From 380be43c05275378d6f2473b3832d7f3d355e179 Mon Sep 17 00:00:00 2001 From: insleker Date: Tue, 9 Sep 2025 21:44:54 +0800 Subject: [PATCH] feat: migrate to whole new data model and update relevant use cases. --- .gitignore | 2 + docs/FRs.md | 14 ++-- docs/meta-arch.md | 15 ++++ lib/data/model/model.dart | 72 +++++++++++++++---- .../pdf/widgets/signature_drawer.dart | 1 + .../view_model/signature_library.dart | 9 +-- .../signature/widgets/signature_card.dart | 2 +- test/features/draw_signature.feature | 6 +- ...etrically_adjust_signature_picture.feature | 15 ++-- ...aphically_adjust_signature_picture.feature | 6 +- test/features/load_signature.feature | 27 +++++++ test/features/load_signature_picture.feature | 18 ----- test/features/pdf_browser.feature | 8 +-- test/features/save_signed_pdf.feature | 16 ++--- ...upport_multiple_signature_pictures.feature | 32 ++++----- .../support_multiple_signatures.feature | 54 +++++++------- 16 files changed, 184 insertions(+), 113 deletions(-) create mode 100644 test/features/load_signature.feature delete mode 100644 test/features/load_signature_picture.feature diff --git a/.gitignore b/.gitignore index d30f692..397e47a 100644 --- a/.gitignore +++ b/.gitignore @@ -135,3 +135,5 @@ AppDir/bundle/ appimage-build/ /*.AppImage .vscode/settings.json + +*.patch diff --git a/docs/FRs.md b/docs/FRs.md index 5792e98..c2b83c2 100644 --- a/docs/FRs.md +++ b/docs/FRs.md @@ -2,25 +2,27 @@ ## 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) * role: user * functionality: view and navigate PDF documents * 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 - * functionality: load a signature picture file + * functionality: load a signature asset file and create a signature card * benefit: easily add signature to PDF * name: [geometrically adjust signature picture](../test/features/geometrically_adjust_signature_picture.feature) * 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 * name: [graphically adjust signature picture](../test/features/graphically_adjust_signature_picture.feature) * 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. * name: [draw signature](../test/features/draw_signature.feature) * 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. * name: [save signed PDF](../test/features/save_signed_pdf.feature) * role: user @@ -28,7 +30,7 @@ * benefit: easily keep a copy of the signed document for records. * name: [preferences for app](../test/features/app_preferences.feature) * 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 * name: [remember preferences](../test/features/remember_preferences.feature) * role: user diff --git a/docs/meta-arch.md b/docs/meta-arch.md index 1dd7752..ca7a133 100644 --- a/docs/meta-arch.md +++ b/docs/meta-arch.md @@ -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. * `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 * [pdfrx](https://pub.dev/packages/pdfrx) diff --git a/lib/data/model/model.dart b/lib/data/model/model.dart index df11209..6fab8d3 100644 --- a/lib/data/model/model.dart +++ b/lib/data/model/model.dart @@ -1,32 +1,80 @@ import 'dart:typed_data'; 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>? 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 /// geometric rectangle (UI coordinate space) and the identifier of the /// image/signature asset assigned to that placement. class SignaturePlacement { + // The bounding box of this placement in UI coordinate space, implies scaling and position. final Rect rect; + /// from `SignatureCard` /// Rotation in degrees to apply when rendering/exporting this placement. final double rotationDeg; + GraphicAdjust graphicAdjust; + final SignatureAsset asset; - /// Identifier of the image (e.g., filename / asset id) assigned to this placement. - /// Nullable to allow a placement reserved before an image is chosen. - final String? imageId; - const SignaturePlacement({ + SignaturePlacement({ required this.rect, - this.imageId, + required this.asset, this.rotationDeg = 0.0, - }); + GraphicAdjust graphicAdjust = const GraphicAdjust(), + }) : graphicAdjust = graphicAdjust; SignaturePlacement copyWith({ - Rect? rect, - String? imageId, - double? rotationDeg, + required Rect rect, + required SignatureAsset asset, + double rotationDeg = 0.0, + GraphicAdjust graphicAdjust = const GraphicAdjust(), }) => SignaturePlacement( - rect: rect ?? this.rect, - imageId: imageId ?? this.imageId, - rotationDeg: rotationDeg ?? this.rotationDeg, + rect: rect, + asset: asset, + rotationDeg: rotationDeg, + graphicAdjust: graphicAdjust, ); } diff --git a/lib/ui/features/pdf/widgets/signature_drawer.dart b/lib/ui/features/pdf/widgets/signature_drawer.dart index 34fbedc..3bca4b2 100644 --- a/lib/ui/features/pdf/widgets/signature_drawer.dart +++ b/lib/ui/features/pdf/widgets/signature_drawer.dart @@ -2,6 +2,7 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:pdf_signature/l10n/app_localizations.dart'; +import 'package:pdf_signature/data/model/model.dart'; import '../../../../data/services/export_providers.dart'; import '../../signature/view_model/signature_controller.dart'; diff --git a/lib/ui/features/signature/view_model/signature_library.dart b/lib/ui/features/signature/view_model/signature_library.dart index 768eb0a..efe5a35 100644 --- a/lib/ui/features/signature/view_model/signature_library.dart +++ b/lib/ui/features/signature/view_model/signature_library.dart @@ -1,13 +1,6 @@ import 'dart:typed_data'; import 'package:flutter_riverpod/flutter_riverpod.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}); -} +import 'package:pdf_signature/data/model/model.dart'; class SignatureLibraryController extends StateNotifier> { SignatureLibraryController() : super(const []); diff --git a/lib/ui/features/signature/widgets/signature_card.dart b/lib/ui/features/signature/widgets/signature_card.dart index 00d586f..be5dfed 100644 --- a/lib/ui/features/signature/widgets/signature_card.dart +++ b/lib/ui/features/signature/widgets/signature_card.dart @@ -1,5 +1,5 @@ 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 'rotated_signature_image.dart'; import 'package:pdf_signature/l10n/app_localizations.dart'; diff --git a/test/features/draw_signature.feature b/test/features/draw_signature.feature index 894ee9c..0a7d3b3 100644 --- a/test/features/draw_signature.feature +++ b/test/features/draw_signature.feature @@ -1,10 +1,10 @@ -Feature: draw signature +Feature: draw signature asset Scenario: Draw with mouse or touch and place on page Given an empty signature canvas When the user draws strokes and confirms - Then a signature image is created - And it is placed on the selected page + Then a signature asset is created + And signature placement occurs on the selected page Scenario: Clear and redraw Given a drawn signature exists in the canvas diff --git a/test/features/geometrically_adjust_signature_picture.feature b/test/features/geometrically_adjust_signature_picture.feature index 0cab7aa..d79fa8f 100644 --- a/test/features/geometrically_adjust_signature_picture.feature +++ b/test/features/geometrically_adjust_signature_picture.feature @@ -1,12 +1,13 @@ -Feature: geometrically adjust signature picture +Feature: geometrically adjust signature asset 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 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 - Given a signature image is selected - When the user enables aspect ratio lock and resizes - Then the image scales proportionally + Scenario: Rotate the signature + Given a signature asset is placed on the page + When the user uses rotate controls + Then the signature placement rotates around its center in real time + And resize to fit within bounding box \ No newline at end of file diff --git a/test/features/graphically_adjust_signature_picture.feature b/test/features/graphically_adjust_signature_picture.feature index e118740..802c5db 100644 --- a/test/features/graphically_adjust_signature_picture.feature +++ b/test/features/graphically_adjust_signature_picture.feature @@ -1,13 +1,13 @@ -Feature: graphically adjust signature picture +Feature: graphically adjust signature asset Scenario: Remove background - Given a signature image is selected + Given a signature asset is selected When the user enables background removal Then near-white background becomes transparent in the preview And the user can apply the change 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 Then the preview updates immediately And the user can apply or reset adjustments diff --git a/test/features/load_signature.feature b/test/features/load_signature.feature new file mode 100644 index 0000000..fed2cf5 --- /dev/null +++ b/test/features/load_signature.feature @@ -0,0 +1,27 @@ +Feature: load signature asset + + Scenario Outline: Handle invalid or unsupported files + Given the user selects "" + 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 + diff --git a/test/features/load_signature_picture.feature b/test/features/load_signature_picture.feature deleted file mode 100644 index 7a15876..0000000 --- a/test/features/load_signature_picture.feature +++ /dev/null @@ -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 "" - 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' | diff --git a/test/features/pdf_browser.feature b/test/features/pdf_browser.feature index 8b5b2ad..8a3bbd5 100644 --- a/test/features/pdf_browser.feature +++ b/test/features/pdf_browser.feature @@ -1,9 +1,9 @@ -Feature: PDF browser +Feature: document browser 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 Then the first page is displayed 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}) 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 Then the Go to input cannot be used diff --git a/test/features/save_signed_pdf.feature b/test/features/save_signed_pdf.feature index 3360d4a..de046cf 100644 --- a/test/features/save_signed_pdf.feature +++ b/test/features/save_signed_pdf.feature @@ -1,26 +1,26 @@ -Feature: save signed PDF +Feature: save signed document 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 - Then a new PDF file is saved at specified full path, location and file name - And the signatures appear on the corresponding page in the output + Then a new document file is saved at specified full path, location and file name + And the signature placements appear on the corresponding page in the output And keep other unchanged content(pages) intact in the output 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 - 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 other page content remains vector and unaltered 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 Then the user is notified there is nothing to save 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 And the export process is not yet finished Then the user is notified that the export is still in progress diff --git a/test/features/support_multiple_signature_pictures.feature b/test/features/support_multiple_signature_pictures.feature index a3303fe..9a8a8eb 100644 --- a/test/features/support_multiple_signature_pictures.feature +++ b/test/features/support_multiple_signature_pictures.feature @@ -1,30 +1,30 @@ -Feature: support multiple signature pictures +Feature: support multiple signature assets - Scenario: Place signatures on different pages with different images - Given a multi-page PDF is open - When the user places a signature from picture on page - And the user places a signature from picture on page - Then both signatures are shown on their respective pages + Scenario: Place signature placements on different pages with different assets + Given a multi-page document is open + When the user places a signature placement from asset on page + And the user places a signature placement from asset on page + Then both signature placements are shown on their respective pages Examples: - # Same page, same image - # Same page, different images - # Different pages, same image - # Different pages, different images - | first_image | first_page | second_image | second_page | + # Same page, same asset + # Same page, different assets + # Different pages, same asset + # Different pages, different assets + | first_asset | first_page | second_asset | second_page | | 'alice.png' | 1 | 'alice.png' | 1 | | 'alice.png' | 1 | 'bob.png' | 1 | | 'alice.png' | 1 | 'bob.png' | 3 | | 'bob.png' | 2 | 'alice.png' | 5 | - Scenario: Reuse the same image for more than one signature - Given a signature image is loaded or drawn + Scenario: Reuse the same asset for more than one signature placement + Given a signature asset is loaded or drawn When the user places it in multiple locations in the document Then identical signature instances appear in each location And adjusting one instance does not affect the others - Scenario: Save/export uses the assigned image for each signature - Given a PDF is open and contains multiple placed signatures across pages + Scenario: Save/export uses the assigned asset for each signature placement + Given a document is open and contains multiple placed signature placements across pages 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 diff --git a/test/features/support_multiple_signatures.feature b/test/features/support_multiple_signatures.feature index 18fec8d..5d04876 100644 --- a/test/features/support_multiple_signatures.feature +++ b/test/features/support_multiple_signatures.feature @@ -1,38 +1,38 @@ -Feature: support multiple signatures +Feature: support multiple signature placements - Scenario: Place signatures on different pages - Given a multi-page PDF is open - When the user places a signature on page {1} - And the user navigates to page {3} and places another signature - Then both signatures are shown on their respective pages + Scenario: Place signature placements on different pages + Given a multi-page document is open + When the user places a signature placement on page {1} + And the user navigates to page {3} and places another signature placement + Then both signature placements are shown on their respective pages - Scenario: Place multiple signatures on the same page independently - Given a PDF page is selected for signing - When the user places two signatures on the same page - Then each signature can be dragged and resized independently + Scenario: Place multiple signature placements on the same page independently + Given a document page is selected for signing + When the user places two signature placements on the same page + Then each signature placement can be dragged and resized independently And dragging or resizing one does not change the other Scenario: Reuse the same signature asset in multiple locations - Given a signature image is loaded or drawn - When the user places it in multiple locations in the document - Then identical signature instances appear in each location - And adjusting one instance does not affect the others + Given a signature asset loaded or drawn is wrapped in a signature card + When the user drags it on the page of the document to place signature placements in multiple locations in the document + Then identical signature placements appear in each location + And adjusting one of the signature placements does not affect the others - Scenario: Remove one of many signatures - Given three signatures are placed on the current page - When the user deletes one selected signature - Then only the selected signature is removed - And the other signatures remain unchanged + Scenario: Remove one of many signature placements + Given three signature placements are placed on the current page + When the user deletes one selected signature placement + Then only the selected signature placement is removed + And the other signature placements remain unchanged - Scenario: Keep earlier signatures while navigating between pages - Given a signature is placed on page {2} - When the user navigates to page {5} and places another signature - Then the signature on page {2} remains - And the signature on page {5} is shown on page {5} + Scenario: Keep earlier signature placements while navigating between pages + Given a signature placement is placed on page {2} + When the user navigates to page {5} and places another signature placement + Then the signature placement on page {2} remains + And the signature placement on page {5} is shown on page {5} - Scenario: Save a document with multiple signatures across pages - Given a PDF is open and contains multiple placed signatures across pages + Scenario: Save a document with multiple signature placements across pages + Given a document is open and contains multiple placed signature placements across pages 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