diff --git a/integration_test/data/sample-local-pdf.pdf b/integration_test/data/sample-local-pdf.pdf new file mode 100644 index 0000000..4603bd3 Binary files /dev/null and b/integration_test/data/sample-local-pdf.pdf differ diff --git a/integration_test/export_flow_test.dart b/integration_test/export_flow_test.dart index f3b5eaf..5f3b7aa 100644 --- a/integration_test/export_flow_test.dart +++ b/integration_test/export_flow_test.dart @@ -42,10 +42,12 @@ void main() { ), documentRepositoryProvider.overrideWith( (ref) => - DocumentStateNotifier() - ..openPicked(path: 'test.pdf', pageCount: 5), + DocumentStateNotifier()..openPicked( + path: 'integration_test/data/sample-local-pdf.pdf', + pageCount: 5, + ), ), - useMockViewerProvider.overrideWith((ref) => true), + useMockViewerProvider.overrideWith((ref) => false), exportServiceProvider.overrideWith((_) => fake), savePathPickerProvider.overrideWith( (_) => () async => 'C:/tmp/output.pdf', @@ -99,16 +101,17 @@ void main() { ), documentRepositoryProvider.overrideWith( (ref) => - DocumentStateNotifier() - ..openPicked(path: 'test.pdf', pageCount: 5), + DocumentStateNotifier()..openPicked( + path: 'integration_test/data/sample-local-pdf.pdf', + pageCount: 5, + ), ), signatureAssetRepositoryProvider.overrideWith((ref) { final c = SignatureAssetRepository(); c.add(sigBytes, name: 'image'); return c; }), - // Keep mock viewer for determinism on CI/desktop devices - useMockViewerProvider.overrideWithValue(true), + useMockViewerProvider.overrideWithValue(false), ], child: const MaterialApp( localizationsDelegates: AppLocalizations.localizationsDelegates, diff --git a/lib/data/repositories/signature_card_repository.dart b/lib/data/repositories/signature_card_repository.dart index ee169f5..833b4a0 100644 --- a/lib/data/repositories/signature_card_repository.dart +++ b/lib/data/repositories/signature_card_repository.dart @@ -4,16 +4,20 @@ import '../../domain/models/model.dart'; class SignatureCardStateNotifier extends StateNotifier> { SignatureCardStateNotifier() : super(const []); - add({required SignatureAsset asset, double rotationDeg = 0.0}) { + void add(SignatureCard card) { + state = List.of(state)..add(card); + } + + void addWithAsset(SignatureAsset asset, double rotationDeg) { state = List.of(state) ..add(SignatureCard(asset: asset, rotationDeg: rotationDeg)); } - void update({ - required SignatureCard card, + void update( + SignatureCard card, double? rotationDeg, GraphicAdjust? graphicAdjust, - }) { + ) { final list = List.of(state); for (var i = 0; i < list.length; i++) { final c = list[i]; diff --git a/lib/ui/features/pdf/widgets/pdf_mock_continuous_list.dart b/lib/ui/features/pdf/widgets/pdf_mock_continuous_list.dart index 6eaf9fa..ffdd554 100644 --- a/lib/ui/features/pdf/widgets/pdf_mock_continuous_list.dart +++ b/lib/ui/features/pdf/widgets/pdf_mock_continuous_list.dart @@ -7,6 +7,8 @@ import 'pdf_page_overlays.dart'; import 'pdf_providers.dart'; import 'package:pdf_signature/data/repositories/signature_asset_repository.dart'; // using only adjusted overlay, no direct model imports needed +import '../../signature/widgets/signature_drag_data.dart'; +import 'package:pdf_signature/data/repositories/document_repository.dart'; /// Mocked continuous viewer for tests or platforms without real viewer. @visibleForTesting @@ -81,29 +83,73 @@ class _PdfMockContinuousListState extends ConsumerState { child: Stack( key: ValueKey('page_stack_$pageNum'), children: [ - Container( - color: Colors.grey.shade200, - child: Center( - child: Builder( - builder: (ctx) { - String label; - try { - label = AppLocalizations.of( - ctx, - ).pageInfo(pageNum, count); - } catch (_) { - label = 'Page $pageNum of $count'; - } - return Text( - label, - style: const TextStyle( - fontSize: 24, - color: Colors.black54, - ), - ); - }, - ), - ), + DragTarget( + onAcceptWithDetails: (details) { + final dragData = details.data; + final offset = details.offset; + final renderBox = + context.findRenderObject() as RenderBox?; + if (renderBox != null) { + final localPosition = renderBox.globalToLocal(offset); + final normalizedX = + localPosition.dx / renderBox.size.width; + final normalizedY = + localPosition.dy / renderBox.size.height; + + // Create a default rect for the signature (can be adjusted later) + final rect = Rect.fromLTWH( + (normalizedX - 0.1).clamp( + 0.0, + 0.8, + ), // Center horizontally with some margin + (normalizedY - 0.05).clamp( + 0.0, + 0.9, + ), // Center vertically with some margin + 0.2, // Default width + 0.1, // Default height + ); + + // Add placement to the document + ref + .read(documentRepositoryProvider.notifier) + .addPlacement( + page: pageNum, + rect: rect, + asset: dragData.card?.asset, + rotationDeg: dragData.card?.rotationDeg ?? 0.0, + ); + } + }, + builder: (context, candidateData, rejectedData) { + return Container( + color: + candidateData.isNotEmpty + ? Colors.blue.withOpacity(0.3) + : Colors.grey.shade200, + child: Center( + child: Builder( + builder: (ctx) { + String label; + try { + label = AppLocalizations.of( + ctx, + ).pageInfo(pageNum, count); + } catch (_) { + label = 'Page $pageNum of $count'; + } + return Text( + label, + style: const TextStyle( + fontSize: 24, + color: Colors.black54, + ), + ); + }, + ), + ), + ); + }, ), visible ? Stack( diff --git a/lib/ui/features/pdf/widgets/pdf_viewer_widget.dart b/lib/ui/features/pdf/widgets/pdf_viewer_widget.dart index 40b1ea6..e9f8d46 100644 --- a/lib/ui/features/pdf/widgets/pdf_viewer_widget.dart +++ b/lib/ui/features/pdf/widgets/pdf_viewer_widget.dart @@ -6,6 +6,7 @@ import 'package:pdf_signature/l10n/app_localizations.dart'; import 'pdf_page_overlays.dart'; import 'pdf_providers.dart'; import './pdf_mock_continuous_list.dart'; +import '../../signature/widgets/signature_drag_data.dart'; class PdfViewerWidget extends ConsumerStatefulWidget { const PdfViewerWidget({ @@ -115,6 +116,41 @@ class _PdfViewerWidgetState extends ConsumerState { }, ), ), + // Drag target for dropping signatures + Positioned.fill( + child: DragTarget( + onAcceptWithDetails: (details) { + final dragData = details.data; + + // For real PDF viewer, we need to calculate which page was dropped on + // This is a simplified implementation - in a real app you'd need to + // determine the exact page and position within that page + final currentPage = + ref.read(documentRepositoryProvider).currentPage; + + // Create a default rect for the signature (can be adjusted later) + final rect = const Rect.fromLTWH(0.1, 0.1, 0.2, 0.1); + + // Add placement to the document + ref + .read(documentRepositoryProvider.notifier) + .addPlacement( + page: currentPage, + rect: rect, + asset: dragData.card?.asset, + rotationDeg: dragData.card?.rotationDeg ?? 0.0, + ); + }, + builder: (context, candidateData, rejectedData) { + return Container( + color: + candidateData.isNotEmpty + ? Colors.blue.withOpacity(0.1) + : Colors.transparent, + ); + }, + ), + ), // Add signature overlays on top Positioned.fill( child: Consumer( diff --git a/lib/ui/features/signature/widgets/signature_card.dart b/lib/ui/features/signature/widgets/signature_card.dart index 23597a5..0dfed93 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 'package:pdf_signature/domain/models/model.dart'; +import 'package:pdf_signature/domain/models/model.dart' as domain; import 'signature_drag_data.dart'; import 'rotated_signature_image.dart'; import 'package:pdf_signature/l10n/app_localizations.dart'; @@ -15,7 +15,7 @@ class SignatureCard extends StatelessWidget { this.useCurrentBytesForDrag = false, this.rotationDeg = 0.0, }); - final SignatureAsset asset; + final domain.SignatureAsset asset; final bool disabled; final VoidCallback onDelete; final VoidCallback? onTap; @@ -142,7 +142,12 @@ class SignatureCard extends StatelessWidget { data: useCurrentBytesForDrag ? const SignatureDragData() - : SignatureDragData(asset: asset), + : SignatureDragData( + card: domain.SignatureCard( + asset: asset, + rotationDeg: rotationDeg, + ), + ), feedback: Opacity( opacity: 0.9, child: ConstrainedBox( diff --git a/lib/ui/features/signature/widgets/signature_drag_data.dart b/lib/ui/features/signature/widgets/signature_drag_data.dart index c21acbb..12facf6 100644 --- a/lib/ui/features/signature/widgets/signature_drag_data.dart +++ b/lib/ui/features/signature/widgets/signature_drag_data.dart @@ -1,6 +1,6 @@ import 'package:pdf_signature/domain/models/model.dart'; class SignatureDragData { - final SignatureAsset? asset; // null means use current processed signature - const SignatureDragData({this.asset}); + final SignatureCard? card; // null means use current processed signature + const SignatureDragData({this.card}); } diff --git a/test/features/step/the_user_drags_this_signature_card_on_the_page_of_the_document_to_place_a_signature_placement.dart b/test/features/step/the_user_drags_this_signature_card_on_the_page_of_the_document_to_place_a_signature_placement.dart index b85e2ed..864bd10 100644 --- a/test/features/step/the_user_drags_this_signature_card_on_the_page_of_the_document_to_place_a_signature_placement.dart +++ b/test/features/step/the_user_drags_this_signature_card_on_the_page_of_the_document_to_place_a_signature_placement.dart @@ -4,6 +4,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:pdf_signature/data/repositories/document_repository.dart'; import 'package:pdf_signature/data/repositories/signature_asset_repository.dart'; +import 'package:pdf_signature/data/repositories/signature_card_repository.dart'; import 'package:pdf_signature/domain/models/model.dart'; import '_world.dart'; @@ -37,6 +38,14 @@ theUserDragsThisSignatureCardOnThePageOfTheDocumentToPlaceASignaturePlacement( .firstWhere((a) => a.name == 'placement.png'); } + // create a signature card + final temp_card = SignatureCard(asset: asset, rotationDeg: 0); + container + .read(signatureCardRepositoryProvider.notifier) + .addWithAsset(temp_card.asset, temp_card.rotationDeg); + // drag and drop (DragTarget, `onAccept`) it on document page + final drop_card = temp_card; + // Place it on the current page final pdf = container.read(documentRepositoryProvider); container @@ -44,6 +53,7 @@ theUserDragsThisSignatureCardOnThePageOfTheDocumentToPlaceASignaturePlacement( .addPlacement( page: pdf.currentPage, rect: Rect.fromLTWH(100, 100, 100, 50), - asset: asset, + asset: drop_card.asset, + rotationDeg: drop_card.rotationDeg, ); } diff --git a/test/features/step/the_user_places_a_signature_placement_from_asset_on_page.dart b/test/features/step/the_user_places_a_signature_placement_from_asset_on_page.dart index 9c634ab..0bce005 100644 --- a/test/features/step/the_user_places_a_signature_placement_from_asset_on_page.dart +++ b/test/features/step/the_user_places_a_signature_placement_from_asset_on_page.dart @@ -6,7 +6,7 @@ import 'package:pdf_signature/data/repositories/document_repository.dart'; import 'package:pdf_signature/data/repositories/signature_asset_repository.dart'; import '_world.dart'; -/// Usage: the user places a signature placement from asset on page +/// Usage: the user places a signature placement from asset on page Future theUserPlacesASignaturePlacementFromAssetOnPage( WidgetTester tester, String assetName, diff --git a/test/features/support_multiple_signature_pictures.feature b/test/features/support_multiple_signature_pictures.feature index 9a8a8eb..3952463 100644 --- a/test/features/support_multiple_signature_pictures.feature +++ b/test/features/support_multiple_signature_pictures.feature @@ -2,15 +2,11 @@ Feature: support multiple signature assets 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 + 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 asset - # Same page, different assets - # Different pages, same asset - # Different pages, different assets - | first_asset | first_page | second_asset | second_page | + | firstAsset | firstPage | secondAsset | secondPage | | 'alice.png' | 1 | 'alice.png' | 1 | | 'alice.png' | 1 | 'bob.png' | 1 | | 'alice.png' | 1 | 'bob.png' | 3 | diff --git a/test/widgets/rotated_signature_image_test.dart b/test/widget/rotated_signature_image_test.dart similarity index 100% rename from test/widgets/rotated_signature_image_test.dart rename to test/widget/rotated_signature_image_test.dart