feat: migrate to whole new data model and update relevant use

cases.
This commit is contained in:
insleker 2025-09-09 21:44:54 +08:00
parent fba880e1be
commit 380be43c05
16 changed files with 184 additions and 113 deletions

2
.gitignore vendored
View File

@ -135,3 +135,5 @@ AppDir/bundle/
appimage-build/ appimage-build/
/*.AppImage /*.AppImage
.vscode/settings.json .vscode/settings.json
*.patch

View File

@ -2,25 +2,27 @@
## user stories ## user stories
The following user stories may not use formal terminology as [meta-arch.md](./meta-arch.md) and use cases(`test/*.feature`), but use oral descriptions.
* name: [PDF browser](../test/features/pdf_browser.feature) * name: [PDF browser](../test/features/pdf_browser.feature)
* role: user * role: user
* functionality: view and navigate PDF documents * functionality: view and navigate PDF documents
* benefit: select page to add signature * benefit: select page to add signature
* name: [load signature picture](../test/features/load_signature_picture.feature) * name: [load signature](../test/features/load_signature.feature)
* role: user * role: user
* functionality: load a signature picture file * functionality: load a signature asset file and create a signature card
* benefit: easily add signature to PDF * benefit: easily add signature to PDF
* name: [geometrically adjust signature picture](../test/features/geometrically_adjust_signature_picture.feature) * name: [geometrically adjust signature picture](../test/features/geometrically_adjust_signature_picture.feature)
* role: user * role: user
* functionality: adjust the size and position of the signature picture * functionality: adjust the scale, rotation and position of the signature placement on the PDF page
* benefit: ensure the signature fits well on the PDF page * benefit: ensure the signature fits well on the PDF page
* name: [graphically adjust signature picture](../test/features/graphically_adjust_signature_picture.feature) * name: [graphically adjust signature picture](../test/features/graphically_adjust_signature_picture.feature)
* role: user * role: user
* functionality: background removal, contrast adjustment... * functionality: background removal, contrast adjustment... to enhance the appearance of the signature asset within the signature card
* benefit: easily improve the appearance of the signature on the PDF without additional software. * benefit: easily improve the appearance of the signature on the PDF without additional software.
* name: [draw signature](../test/features/draw_signature.feature) * name: [draw signature](../test/features/draw_signature.feature)
* role: user * role: user
* functionality: draw a signature using mouse or touch input * functionality: draw a signature asset using mouse or touch input
* benefit: create a custom signature directly on the PDF if no pre-made signature is available. * benefit: create a custom signature directly on the PDF if no pre-made signature is available.
* name: [save signed PDF](../test/features/save_signed_pdf.feature) * name: [save signed PDF](../test/features/save_signed_pdf.feature)
* role: user * role: user
@ -28,7 +30,7 @@
* benefit: easily keep a copy of the signed document for records. * benefit: easily keep a copy of the signed document for records.
* name: [preferences for app](../test/features/app_preferences.feature) * name: [preferences for app](../test/features/app_preferences.feature)
* role: user * role: user
* functionality: configure app preferences such as `theme`, `language`. * functionality: configure app preferences such as `language`, `theme`, `theme-color`.
* benefit: customize the app experience to better fit user needs * benefit: customize the app experience to better fit user needs
* name: [remember preferences](../test/features/remember_preferences.feature) * name: [remember preferences](../test/features/remember_preferences.feature)
* role: user * role: user

View File

@ -11,6 +11,21 @@ The repo structure follows official [Package structure](https://docs.flutter.dev
* `test/widget/` contains UI widget(component) tests which focus on `View` from MVVM of each component. * `test/widget/` contains UI widget(component) tests which focus on `View` from MVVM of each component.
* `integration_test/` for integration tests. They should be volatile to follow UI layout changes. * `integration_test/` for integration tests. They should be volatile to follow UI layout changes.
## Abstraction
### terminology
* signature asset
* image file of a signature, stored in the device or cloud storage
* can drawing from canvas
* signature card
* template of signature placement
* It will include modifications such as brightness, contrast, background removal, rotation of the signature asset.
* signature placement
* placed modified signature asset from signature card on a specific position on a specific page of a specific PDF document
* document
* PDF document to be signed
## key dependencies ## key dependencies
* [pdfrx](https://pub.dev/packages/pdfrx) * [pdfrx](https://pub.dev/packages/pdfrx)

View File

@ -1,32 +1,80 @@
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
/// A simple library of signature images available to the user in the sidebar.
class SignatureAsset {
final String id; // unique id
final Uint8List bytes;
// List<List<Offset>>? strokes;
final String? name; // optional display name (e.g., filename)
const SignatureAsset({required this.id, required this.bytes, this.name});
}
class GraphicAdjust {
final double contrast;
final double brightness;
final bool bgRemoval;
const GraphicAdjust({
this.contrast = 1.0,
this.brightness = 0.0,
this.bgRemoval = false,
});
GraphicAdjust copyWith({
double? contrast,
double? brightness,
bool? bgRemoval,
}) => GraphicAdjust(
contrast: contrast ?? this.contrast,
brightness: brightness ?? this.brightness,
bgRemoval: bgRemoval ?? this.bgRemoval,
);
}
/**
* signature card is template of signature placement
*/
class SignatureCard {
final double rotationDeg;
final SignatureAsset asset;
GraphicAdjust graphicAdjust;
SignatureCard({required this.rotationDeg, required this.asset})
: graphicAdjust = GraphicAdjust();
}
/// Represents a single signature placement on a page combining both the /// Represents a single signature placement on a page combining both the
/// geometric rectangle (UI coordinate space) and the identifier of the /// geometric rectangle (UI coordinate space) and the identifier of the
/// image/signature asset assigned to that placement. /// image/signature asset assigned to that placement.
class SignaturePlacement { class SignaturePlacement {
// The bounding box of this placement in UI coordinate space, implies scaling and position.
final Rect rect; final Rect rect;
/// from `SignatureCard`
/// Rotation in degrees to apply when rendering/exporting this placement. /// Rotation in degrees to apply when rendering/exporting this placement.
final double rotationDeg; final double rotationDeg;
GraphicAdjust graphicAdjust;
final SignatureAsset asset;
/// Identifier of the image (e.g., filename / asset id) assigned to this placement. SignaturePlacement({
/// Nullable to allow a placement reserved before an image is chosen.
final String? imageId;
const SignaturePlacement({
required this.rect, required this.rect,
this.imageId, required this.asset,
this.rotationDeg = 0.0, this.rotationDeg = 0.0,
}); GraphicAdjust graphicAdjust = const GraphicAdjust(),
}) : graphicAdjust = graphicAdjust;
SignaturePlacement copyWith({ SignaturePlacement copyWith({
Rect? rect, required Rect rect,
String? imageId, required SignatureAsset asset,
double? rotationDeg, double rotationDeg = 0.0,
GraphicAdjust graphicAdjust = const GraphicAdjust(),
}) => SignaturePlacement( }) => SignaturePlacement(
rect: rect ?? this.rect, rect: rect,
imageId: imageId ?? this.imageId, asset: asset,
rotationDeg: rotationDeg ?? this.rotationDeg, rotationDeg: rotationDeg,
graphicAdjust: graphicAdjust,
); );
} }

View File

@ -2,6 +2,7 @@ import 'dart:typed_data';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/l10n/app_localizations.dart'; import 'package:pdf_signature/l10n/app_localizations.dart';
import 'package:pdf_signature/data/model/model.dart';
import '../../../../data/services/export_providers.dart'; import '../../../../data/services/export_providers.dart';
import '../../signature/view_model/signature_controller.dart'; import '../../signature/view_model/signature_controller.dart';

View File

@ -1,13 +1,6 @@
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/data/model/model.dart';
/// A simple library of signature images available to the user in the sidebar.
class SignatureAsset {
final String id; // unique id
final Uint8List bytes;
final String? name; // optional display name (e.g., filename)
const SignatureAsset({required this.id, required this.bytes, this.name});
}
class SignatureLibraryController extends StateNotifier<List<SignatureAsset>> { class SignatureLibraryController extends StateNotifier<List<SignatureAsset>> {
SignatureLibraryController() : super(const []); SignatureLibraryController() : super(const []);

View File

@ -1,5 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../view_model/signature_library.dart'; import 'package:pdf_signature/data/model/model.dart';
import 'signature_drag_data.dart'; import 'signature_drag_data.dart';
import 'rotated_signature_image.dart'; import 'rotated_signature_image.dart';
import 'package:pdf_signature/l10n/app_localizations.dart'; import 'package:pdf_signature/l10n/app_localizations.dart';

View File

@ -1,10 +1,10 @@
Feature: draw signature Feature: draw signature asset
Scenario: Draw with mouse or touch and place on page Scenario: Draw with mouse or touch and place on page
Given an empty signature canvas Given an empty signature canvas
When the user draws strokes and confirms When the user draws strokes and confirms
Then a signature image is created Then a signature asset is created
And it is placed on the selected page And signature placement occurs on the selected page
Scenario: Clear and redraw Scenario: Clear and redraw
Given a drawn signature exists in the canvas Given a drawn signature exists in the canvas

View File

@ -1,12 +1,13 @@
Feature: geometrically adjust signature picture Feature: geometrically adjust signature asset
Scenario: Resize and move the signature within page bounds Scenario: Resize and move the signature within page bounds
Given a signature image is placed on the page Given a signature asset is placed on the page
When the user drags handles to resize and drags to reposition When the user drags handles to resize and drags to reposition
Then the size and position update in real time Then the size and position update in real time
And the signature remains within the page area And the signature placement remains within the page area
Scenario: Lock aspect ratio while resizing Scenario: Rotate the signature
Given a signature image is selected Given a signature asset is placed on the page
When the user enables aspect ratio lock and resizes When the user uses rotate controls
Then the image scales proportionally Then the signature placement rotates around its center in real time
And resize to fit within bounding box

View File

@ -1,13 +1,13 @@
Feature: graphically adjust signature picture Feature: graphically adjust signature asset
Scenario: Remove background Scenario: Remove background
Given a signature image is selected Given a signature asset is selected
When the user enables background removal When the user enables background removal
Then near-white background becomes transparent in the preview Then near-white background becomes transparent in the preview
And the user can apply the change And the user can apply the change
Scenario: Adjust contrast and brightness Scenario: Adjust contrast and brightness
Given a signature image is selected Given a signature asset is selected
When the user changes contrast and brightness controls When the user changes contrast and brightness controls
Then the preview updates immediately Then the preview updates immediately
And the user can apply or reset adjustments And the user can apply or reset adjustments

View File

@ -0,0 +1,27 @@
Feature: load signature asset
Scenario Outline: Handle invalid or unsupported files
Given the user selects "<file>"
When the app attempts to load the asset
Then the user is notified of the issue
And the asset is not added to the document
Examples:
| file |
| 'corrupted.png' |
| 'signature.bmp' |
| 'empty.jpg' |
Scenario: Import a signature asset
When the user chooses a image file as a signature asset
Then the asset is loaded and shown as a signature asset
Scenario: Import a signature card
When the user chooses a signature asset to created a signature card
Then the asset is loaded and shown as a signature card
Scenario: Import a signature placement
Given a created signature card
When the user drags this signature card on the page of the document to place a signature placement
Then a signature placement appears on the page based on the signature card

View File

@ -1,18 +0,0 @@
Feature: load signature picture
Scenario: Import a signature image
Given a PDF page is selected for signing
When the user chooses a signature image file
Then the image is loaded and shown as a signature asset
Scenario Outline: Handle invalid or unsupported files
Given the user selects "<file>"
When the app attempts to load the image
Then the user is notified of the issue
And the image is not added to the document
Examples:
| file |
| 'corrupted.png' |
| 'signature.bmp' |
| 'empty.jpg' |

View File

@ -1,9 +1,9 @@
Feature: PDF browser Feature: document browser
Background: Background:
Given a sample multi-page PDF (5 pages) is available Given a sample multi-page document (5 pages) is available
Scenario: Open a PDF and navigate pages Scenario: Open a document and navigate pages
When the user opens the document When the user opens the document
Then the first page is displayed Then the first page is displayed
And the user can move to the next or previous page And the user can move to the next or previous page
@ -47,6 +47,6 @@ Feature: PDF browser
Then the last page is displayed (page {5}) Then the last page is displayed (page {5})
And the page label shows "Page {5} of {5}" And the page label shows "Page {5} of {5}"
Scenario: Go to is disabled when no PDF is loaded Scenario: Go to is disabled when no document is loaded
Given no document is open Given no document is open
Then the Go to input cannot be used Then the Go to input cannot be used

View File

@ -1,26 +1,26 @@
Feature: save signed PDF Feature: save signed document
Scenario: Export the signed document to a new file Scenario: Export the signed document to a new file
Given a PDF is open and contains at least one placed signature Given a document is open and contains at least one signature placement
When the user saves/exports the document When the user saves/exports the document
Then a new PDF file is saved at specified full path, location and file name Then a new document file is saved at specified full path, location and file name
And the signatures appear on the corresponding page in the output And the signature placements appear on the corresponding page in the output
And keep other unchanged content(pages) intact in the output And keep other unchanged content(pages) intact in the output
Scenario: Vector-accurate stamping into PDF page coordinates Scenario: Vector-accurate stamping into PDF page coordinates
Given a signature is placed with a position and size relative to the page Given a signature placement is placed with a position and size relative to the page
When the user saves/exports the document When the user saves/exports the document
Then the signature is stamped at the exact PDF page coordinates and size Then the signature placement is stamped at the exact PDF page coordinates and size
And the stamp remains crisp at any zoom level (not rasterized by the screen) And the stamp remains crisp at any zoom level (not rasterized by the screen)
And other page content remains vector and unaltered And other page content remains vector and unaltered
Scenario: Prevent saving when nothing is placed Scenario: Prevent saving when nothing is placed
Given a PDF is open with no signatures placed Given a document is open with no signature placements placed
When the user attempts to save When the user attempts to save
Then the user is notified there is nothing to save Then the user is notified there is nothing to save
Scenario: Loading sign when exporting/saving files Scenario: Loading sign when exporting/saving files
Given a signature is placed with a position and size relative to the page Given a signature placement is placed with a position and size relative to the page
When the user starts exporting the document When the user starts exporting the document
And the export process is not yet finished And the export process is not yet finished
Then the user is notified that the export is still in progress Then the user is notified that the export is still in progress

View File

@ -1,30 +1,30 @@
Feature: support multiple signature pictures Feature: support multiple signature assets
Scenario: Place signatures on different pages with different images Scenario: Place signature placements on different pages with different assets
Given a multi-page PDF is open Given a multi-page document is open
When the user places a signature from picture <first_image> on page <first_page> When the user places a signature placement from asset <first_asset> on page <first_page>
And the user places a signature from picture <second_image> on page <second_page> And the user places a signature placement from asset <second_asset> on page <second_page>
Then both signatures are shown on their respective pages Then both signature placements are shown on their respective pages
Examples: Examples:
# Same page, same image # Same page, same asset
# Same page, different images # Same page, different assets
# Different pages, same image # Different pages, same asset
# Different pages, different images # Different pages, different assets
| first_image | first_page | second_image | second_page | | first_asset | first_page | second_asset | second_page |
| 'alice.png' | 1 | 'alice.png' | 1 | | 'alice.png' | 1 | 'alice.png' | 1 |
| 'alice.png' | 1 | 'bob.png' | 1 | | 'alice.png' | 1 | 'bob.png' | 1 |
| 'alice.png' | 1 | 'bob.png' | 3 | | 'alice.png' | 1 | 'bob.png' | 3 |
| 'bob.png' | 2 | 'alice.png' | 5 | | 'bob.png' | 2 | 'alice.png' | 5 |
Scenario: Reuse the same image for more than one signature Scenario: Reuse the same asset for more than one signature placement
Given a signature image is loaded or drawn Given a signature asset is loaded or drawn
When the user places it in multiple locations in the document When the user places it in multiple locations in the document
Then identical signature instances appear in each location Then identical signature instances appear in each location
And adjusting one instance does not affect the others And adjusting one instance does not affect the others
Scenario: Save/export uses the assigned image for each signature Scenario: Save/export uses the assigned asset for each signature placement
Given a PDF is open and contains multiple placed signatures across pages Given a document is open and contains multiple placed signature placements across pages
When the user saves/exports the document When the user saves/exports the document
Then all placed signatures appear on their corresponding pages in the output Then all placed signature placements appear on their corresponding pages in the output
And other page content remains unaltered And other page content remains unaltered

View File

@ -1,38 +1,38 @@
Feature: support multiple signatures Feature: support multiple signature placements
Scenario: Place signatures on different pages Scenario: Place signature placements on different pages
Given a multi-page PDF is open Given a multi-page document is open
When the user places a signature on page {1} When the user places a signature placement on page {1}
And the user navigates to page {3} and places another signature And the user navigates to page {3} and places another signature placement
Then both signatures are shown on their respective pages Then both signature placements are shown on their respective pages
Scenario: Place multiple signatures on the same page independently Scenario: Place multiple signature placements on the same page independently
Given a PDF page is selected for signing Given a document page is selected for signing
When the user places two signatures on the same page When the user places two signature placements on the same page
Then each signature can be dragged and resized independently Then each signature placement can be dragged and resized independently
And dragging or resizing one does not change the other And dragging or resizing one does not change the other
Scenario: Reuse the same signature asset in multiple locations Scenario: Reuse the same signature asset in multiple locations
Given a signature image is loaded or drawn Given a signature asset loaded or drawn is wrapped in a signature card
When the user places it in multiple locations in the document When the user drags it on the page of the document to place signature placements in multiple locations in the document
Then identical signature instances appear in each location Then identical signature placements appear in each location
And adjusting one instance does not affect the others And adjusting one of the signature placements does not affect the others
Scenario: Remove one of many signatures Scenario: Remove one of many signature placements
Given three signatures are placed on the current page Given three signature placements are placed on the current page
When the user deletes one selected signature When the user deletes one selected signature placement
Then only the selected signature is removed Then only the selected signature placement is removed
And the other signatures remain unchanged And the other signature placements remain unchanged
Scenario: Keep earlier signatures while navigating between pages Scenario: Keep earlier signature placements while navigating between pages
Given a signature is placed on page {2} Given a signature placement is placed on page {2}
When the user navigates to page {5} and places another signature When the user navigates to page {5} and places another signature placement
Then the signature on page {2} remains Then the signature placement on page {2} remains
And the signature on page {5} is shown on page {5} And the signature placement on page {5} is shown on page {5}
Scenario: Save a document with multiple signatures across pages Scenario: Save a document with multiple signature placements across pages
Given a PDF is open and contains multiple placed signatures across pages Given a document is open and contains multiple placed signature placements across pages
When the user saves/exports the document When the user saves/exports the document
Then all placed signatures appear on their corresponding pages in the output Then all placed signature placements appear on their corresponding pages in the output
And other page content remains unaltered And other page content remains unaltered