feat: partially implement mult_signature_picture
This commit is contained in:
parent
51c2a403c4
commit
947c0eef81
|
@ -10,6 +10,8 @@ class PdfState {
|
||||||
final int? signedPage;
|
final int? signedPage;
|
||||||
// Multiple signature placements per page, stored as UI-space rects (e.g., 400x560)
|
// Multiple signature placements per page, stored as UI-space rects (e.g., 400x560)
|
||||||
final Map<int, List<Rect>> placementsByPage;
|
final Map<int, List<Rect>> placementsByPage;
|
||||||
|
// For each placement, store the assigned image identifier (e.g., filename) in the same index order.
|
||||||
|
final Map<int, List<String>> placementImageByPage;
|
||||||
// UI state: selected placement index on the current page (if any)
|
// UI state: selected placement index on the current page (if any)
|
||||||
final int? selectedPlacementIndex;
|
final int? selectedPlacementIndex;
|
||||||
const PdfState({
|
const PdfState({
|
||||||
|
@ -20,6 +22,7 @@ class PdfState {
|
||||||
this.pickedPdfBytes,
|
this.pickedPdfBytes,
|
||||||
this.signedPage,
|
this.signedPage,
|
||||||
this.placementsByPage = const {},
|
this.placementsByPage = const {},
|
||||||
|
this.placementImageByPage = const {},
|
||||||
this.selectedPlacementIndex,
|
this.selectedPlacementIndex,
|
||||||
});
|
});
|
||||||
factory PdfState.initial() => const PdfState(
|
factory PdfState.initial() => const PdfState(
|
||||||
|
@ -29,6 +32,7 @@ class PdfState {
|
||||||
pickedPdfBytes: null,
|
pickedPdfBytes: null,
|
||||||
signedPage: null,
|
signedPage: null,
|
||||||
placementsByPage: {},
|
placementsByPage: {},
|
||||||
|
placementImageByPage: {},
|
||||||
selectedPlacementIndex: null,
|
selectedPlacementIndex: null,
|
||||||
);
|
);
|
||||||
PdfState copyWith({
|
PdfState copyWith({
|
||||||
|
@ -39,6 +43,7 @@ class PdfState {
|
||||||
Uint8List? pickedPdfBytes,
|
Uint8List? pickedPdfBytes,
|
||||||
int? signedPage,
|
int? signedPage,
|
||||||
Map<int, List<Rect>>? placementsByPage,
|
Map<int, List<Rect>>? placementsByPage,
|
||||||
|
Map<int, List<String>>? placementImageByPage,
|
||||||
int? selectedPlacementIndex,
|
int? selectedPlacementIndex,
|
||||||
}) => PdfState(
|
}) => PdfState(
|
||||||
loaded: loaded ?? this.loaded,
|
loaded: loaded ?? this.loaded,
|
||||||
|
@ -48,6 +53,7 @@ class PdfState {
|
||||||
pickedPdfBytes: pickedPdfBytes ?? this.pickedPdfBytes,
|
pickedPdfBytes: pickedPdfBytes ?? this.pickedPdfBytes,
|
||||||
signedPage: signedPage ?? this.signedPage,
|
signedPage: signedPage ?? this.signedPage,
|
||||||
placementsByPage: placementsByPage ?? this.placementsByPage,
|
placementsByPage: placementsByPage ?? this.placementsByPage,
|
||||||
|
placementImageByPage: placementImageByPage ?? this.placementImageByPage,
|
||||||
selectedPlacementIndex:
|
selectedPlacementIndex:
|
||||||
selectedPlacementIndex ?? this.selectedPlacementIndex,
|
selectedPlacementIndex ?? this.selectedPlacementIndex,
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,7 +2,6 @@ import 'dart:math' as math;
|
||||||
import 'dart:typed_data';
|
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:image/image.dart' as img;
|
import 'package:image/image.dart' as img;
|
||||||
|
|
||||||
import '../../../../data/model/model.dart';
|
import '../../../../data/model/model.dart';
|
||||||
|
@ -19,6 +18,7 @@ class PdfController extends StateNotifier<PdfState> {
|
||||||
pickedPdfPath: null,
|
pickedPdfPath: null,
|
||||||
signedPage: null,
|
signedPage: null,
|
||||||
placementsByPage: {},
|
placementsByPage: {},
|
||||||
|
placementImageByPage: {},
|
||||||
selectedPlacementIndex: null,
|
selectedPlacementIndex: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ class PdfController extends StateNotifier<PdfState> {
|
||||||
pickedPdfBytes: bytes,
|
pickedPdfBytes: bytes,
|
||||||
signedPage: null,
|
signedPage: null,
|
||||||
placementsByPage: {},
|
placementsByPage: {},
|
||||||
|
placementImageByPage: {},
|
||||||
selectedPlacementIndex: null,
|
selectedPlacementIndex: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -63,14 +64,27 @@ class PdfController extends StateNotifier<PdfState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multiple-signature helpers
|
// Multiple-signature helpers
|
||||||
void addPlacement({required int page, required Rect rect}) {
|
void addPlacement({
|
||||||
|
required int page,
|
||||||
|
required Rect rect,
|
||||||
|
String image = 'default.png',
|
||||||
|
}) {
|
||||||
if (!state.loaded) return;
|
if (!state.loaded) return;
|
||||||
final p = page.clamp(1, state.pageCount);
|
final p = page.clamp(1, state.pageCount);
|
||||||
final map = Map<int, List<Rect>>.from(state.placementsByPage);
|
final map = Map<int, List<Rect>>.from(state.placementsByPage);
|
||||||
final list = List<Rect>.from(map[p] ?? const []);
|
final list = List<Rect>.from(map[p] ?? const []);
|
||||||
list.add(rect);
|
list.add(rect);
|
||||||
map[p] = list;
|
map[p] = list;
|
||||||
state = state.copyWith(placementsByPage: map, selectedPlacementIndex: null);
|
// Sync image mapping list
|
||||||
|
final imgMap = Map<int, List<String>>.from(state.placementImageByPage);
|
||||||
|
final imgList = List<String>.from(imgMap[p] ?? const []);
|
||||||
|
imgList.add(image);
|
||||||
|
imgMap[p] = imgList;
|
||||||
|
state = state.copyWith(
|
||||||
|
placementsByPage: map,
|
||||||
|
placementImageByPage: imgMap,
|
||||||
|
selectedPlacementIndex: null,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void removePlacement({required int page, required int index}) {
|
void removePlacement({required int page, required int index}) {
|
||||||
|
@ -80,13 +94,22 @@ class PdfController extends StateNotifier<PdfState> {
|
||||||
final list = List<Rect>.from(map[p] ?? const []);
|
final list = List<Rect>.from(map[p] ?? const []);
|
||||||
if (index >= 0 && index < list.length) {
|
if (index >= 0 && index < list.length) {
|
||||||
list.removeAt(index);
|
list.removeAt(index);
|
||||||
|
// Sync image mapping
|
||||||
|
final imgMap = Map<int, List<String>>.from(state.placementImageByPage);
|
||||||
|
final imgList = List<String>.from(imgMap[p] ?? const []);
|
||||||
|
if (index >= 0 && index < imgList.length) {
|
||||||
|
imgList.removeAt(index);
|
||||||
|
}
|
||||||
if (list.isEmpty) {
|
if (list.isEmpty) {
|
||||||
map.remove(p);
|
map.remove(p);
|
||||||
|
imgMap.remove(p);
|
||||||
} else {
|
} else {
|
||||||
map[p] = list;
|
map[p] = list;
|
||||||
|
imgMap[p] = imgList;
|
||||||
}
|
}
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
placementsByPage: map,
|
placementsByPage: map,
|
||||||
|
placementImageByPage: imgMap,
|
||||||
selectedPlacementIndex: null,
|
selectedPlacementIndex: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -116,6 +139,30 @@ class PdfController extends StateNotifier<PdfState> {
|
||||||
if (idx == null) return;
|
if (idx == null) return;
|
||||||
removePlacement(page: state.currentPage, index: idx);
|
removePlacement(page: state.currentPage, index: idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Assign a different image name to a placement on a page.
|
||||||
|
void assignImageToPlacement({
|
||||||
|
required int page,
|
||||||
|
required int index,
|
||||||
|
required String image,
|
||||||
|
}) {
|
||||||
|
if (!state.loaded) return;
|
||||||
|
final p = page.clamp(1, state.pageCount);
|
||||||
|
final imgMap = Map<int, List<String>>.from(state.placementImageByPage);
|
||||||
|
final list = List<String>.from(imgMap[p] ?? const []);
|
||||||
|
if (index >= 0 && index < list.length) {
|
||||||
|
list[index] = image;
|
||||||
|
imgMap[p] = list;
|
||||||
|
state = state.copyWith(placementImageByPage: imgMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience to get image name for a placement
|
||||||
|
String? imageOfPlacement({required int page, required int index}) {
|
||||||
|
final list = state.placementImageByPage[page] ?? const [];
|
||||||
|
if (index < 0 || index >= list.length) return null;
|
||||||
|
return list[index];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final pdfProvider = StateNotifierProvider<PdfController, PdfState>(
|
final pdfProvider = StateNotifierProvider<PdfController, PdfState>(
|
||||||
|
@ -155,10 +202,10 @@ class SignatureController extends StateNotifier<SignatureState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInvalidSelected(BuildContext context) {
|
void setInvalidSelected(BuildContext context) {
|
||||||
final l = AppLocalizations.of(context);
|
// Fallback message without localization to keep core logic testable
|
||||||
ScaffoldMessenger.of(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
context,
|
const SnackBar(content: Text('Invalid or unsupported file')),
|
||||||
).showSnackBar(SnackBar(content: Text(l.invalidOrUnsupportedFile)));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void drag(Offset delta) {
|
void drag(Offset delta) {
|
||||||
|
|
|
@ -31,6 +31,10 @@ class TestWorld {
|
||||||
static String? currentTheme; // actual UI theme applied: 'light' | 'dark'
|
static String? currentTheme; // actual UI theme applied: 'light' | 'dark'
|
||||||
static String? currentLanguage; // 'en' | 'zh-TW' | 'es'
|
static String? currentLanguage; // 'en' | 'zh-TW' | 'es'
|
||||||
static bool settingsOpen = false;
|
static bool settingsOpen = false;
|
||||||
|
// Signature image name loaded via steps (e.g., 'alice.png')
|
||||||
|
static String? currentImageName;
|
||||||
|
// Counters for steps that are called multiple times without params
|
||||||
|
static int placeFromPictureCallCount = 0;
|
||||||
|
|
||||||
static void reset() {
|
static void reset() {
|
||||||
prevCenter = null;
|
prevCenter = null;
|
||||||
|
@ -52,5 +56,7 @@ class TestWorld {
|
||||||
currentTheme = null;
|
currentTheme = null;
|
||||||
currentLanguage = null;
|
currentLanguage = null;
|
||||||
settingsOpen = false;
|
settingsOpen = false;
|
||||||
|
currentImageName = null;
|
||||||
|
placeFromPictureCallCount = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,18 +5,20 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
|
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
|
||||||
import '_world.dart';
|
import '_world.dart';
|
||||||
|
|
||||||
/// Usage: a signature is placed on page 2
|
/// Usage: a signature is placed on page {2}
|
||||||
Future<void> aSignatureIsPlacedOnPage2(WidgetTester tester) async {
|
Future<void> aSignatureIsPlacedOnPage(WidgetTester tester, num page) async {
|
||||||
final container = TestWorld.container ?? ProviderContainer();
|
final container = TestWorld.container ?? ProviderContainer();
|
||||||
TestWorld.container = container;
|
TestWorld.container = container;
|
||||||
container
|
container
|
||||||
.read(pdfProvider.notifier)
|
.read(pdfProvider.notifier)
|
||||||
.openPicked(path: 'mock.pdf', pageCount: 5);
|
.openPicked(path: 'mock.pdf', pageCount: 6);
|
||||||
|
// Ensure image and rect
|
||||||
container
|
container
|
||||||
.read(signatureProvider.notifier)
|
.read(signatureProvider.notifier)
|
||||||
.setImageBytes(Uint8List.fromList([1, 2, 3]));
|
.setImageBytes(Uint8List.fromList([1, 2, 3]));
|
||||||
container.read(signatureProvider.notifier).placeDefaultRect();
|
container.read(signatureProvider.notifier).placeDefaultRect();
|
||||||
final r = container.read(signatureProvider).rect!;
|
final Rect r = container.read(signatureProvider).rect!;
|
||||||
container.read(pdfProvider.notifier).addPlacement(page: 2, rect: r);
|
container
|
||||||
expect(container.read(pdfProvider.notifier).placementsOn(2), isNotEmpty);
|
.read(pdfProvider.notifier)
|
||||||
|
.addPlacement(page: page.toInt(), rect: r, image: 'default.png');
|
||||||
}
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
|
||||||
|
import '_world.dart';
|
||||||
|
|
||||||
|
/// Usage: an image {"bob.png"} is loaded
|
||||||
|
Future<void> anImageIsLoaded(WidgetTester tester, String param1) async {
|
||||||
|
final container = TestWorld.container ?? ProviderContainer();
|
||||||
|
TestWorld.container = container;
|
||||||
|
// Remember current image name
|
||||||
|
TestWorld.currentImageName = param1;
|
||||||
|
// Map name to deterministic bytes for testing
|
||||||
|
Uint8List bytes;
|
||||||
|
switch (param1) {
|
||||||
|
case 'alice.png':
|
||||||
|
bytes = Uint8List.fromList([1, 2, 3]);
|
||||||
|
break;
|
||||||
|
case 'bob.png':
|
||||||
|
bytes = Uint8List.fromList([4, 5, 6]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
bytes = Uint8List.fromList(param1.codeUnits.take(10).toList());
|
||||||
|
}
|
||||||
|
container.read(signatureProvider.notifier).setImageBytes(bytes);
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
|
||||||
|
import '_world.dart';
|
||||||
|
|
||||||
|
/// Usage: the selected signature is shown with image {"bob.png"}
|
||||||
|
Future<void> theSelectedSignatureIsShownWithImage(
|
||||||
|
WidgetTester tester,
|
||||||
|
String expected,
|
||||||
|
) async {
|
||||||
|
final container = TestWorld.container ?? ProviderContainer();
|
||||||
|
TestWorld.container = container;
|
||||||
|
final pdf = container.read(pdfProvider);
|
||||||
|
final page = pdf.currentPage;
|
||||||
|
final idx =
|
||||||
|
pdf.selectedPlacementIndex ??
|
||||||
|
((pdf.placementsByPage[page]?.length ?? 1) - 1);
|
||||||
|
final name = container
|
||||||
|
.read(pdfProvider.notifier)
|
||||||
|
.imageOfPlacement(page: page, index: idx);
|
||||||
|
expect(name, expected);
|
||||||
|
}
|
|
@ -1,10 +0,0 @@
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
|
|
||||||
import '_world.dart';
|
|
||||||
|
|
||||||
/// Usage: the signature on page 5 is shown on page 5
|
|
||||||
Future<void> theSignatureOnPage5IsShownOnPage5(WidgetTester tester) async {
|
|
||||||
final container = TestWorld.container ?? ProviderContainer();
|
|
||||||
expect(container.read(pdfProvider.notifier).placementsOn(5), isNotEmpty);
|
|
||||||
}
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
|
||||||
|
import '_world.dart';
|
||||||
|
|
||||||
|
/// Usage: the signature on page {5} is shown on page {5}
|
||||||
|
Future<void> theSignatureOnPageIsShownOnPage(
|
||||||
|
WidgetTester tester,
|
||||||
|
num sourcePage,
|
||||||
|
num targetPage,
|
||||||
|
) async {
|
||||||
|
final container = TestWorld.container ?? ProviderContainer();
|
||||||
|
TestWorld.container = container;
|
||||||
|
final srcList = container.read(pdfProvider.notifier).placementsOn(sourcePage.toInt());
|
||||||
|
final tgtList = container.read(pdfProvider.notifier).placementsOn(targetPage.toInt());
|
||||||
|
// At least one exists on both
|
||||||
|
expect(srcList, isNotEmpty);
|
||||||
|
expect(tgtList, isNotEmpty);
|
||||||
|
}
|
|
@ -3,8 +3,10 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
|
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
|
||||||
import '_world.dart';
|
import '_world.dart';
|
||||||
|
|
||||||
/// Usage: the signature on page 2 remains
|
/// Usage: the signature on page {2} remains
|
||||||
Future<void> theSignatureOnPage2Remains(WidgetTester tester) async {
|
Future<void> theSignatureOnPageRemains(WidgetTester tester, num page) async {
|
||||||
final container = TestWorld.container ?? ProviderContainer();
|
final container = TestWorld.container ?? ProviderContainer();
|
||||||
expect(container.read(pdfProvider.notifier).placementsOn(2), isNotEmpty);
|
TestWorld.container = container;
|
||||||
|
final list = container.read(pdfProvider.notifier).placementsOn(page.toInt());
|
||||||
|
expect(list, isNotEmpty);
|
||||||
}
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
|
||||||
|
import '_world.dart';
|
||||||
|
|
||||||
|
/// Usage: the user assigns {"bob.png"} to the selected signature
|
||||||
|
Future<void> theUserAssignsToTheSelectedSignature(
|
||||||
|
WidgetTester tester,
|
||||||
|
String newImageName,
|
||||||
|
) async {
|
||||||
|
final container = TestWorld.container ?? ProviderContainer();
|
||||||
|
TestWorld.container = container;
|
||||||
|
// Load the new image into signature state (simulating pick)
|
||||||
|
Uint8List bytes =
|
||||||
|
newImageName == 'bob.png'
|
||||||
|
? Uint8List.fromList([4, 5, 6])
|
||||||
|
: Uint8List.fromList([1, 2, 3]);
|
||||||
|
container.read(signatureProvider.notifier).setImageBytes(bytes);
|
||||||
|
TestWorld.currentImageName = newImageName;
|
||||||
|
// Assign to currently selected placement
|
||||||
|
final pdf = container.read(pdfProvider);
|
||||||
|
final page = pdf.currentPage;
|
||||||
|
final idx =
|
||||||
|
pdf.selectedPlacementIndex ??
|
||||||
|
((pdf.placementsByPage[page]?.length ?? 1) - 1);
|
||||||
|
container
|
||||||
|
.read(pdfProvider.notifier)
|
||||||
|
.assignImageToPlacement(page: page, index: idx, image: newImageName);
|
||||||
|
}
|
|
@ -1,28 +0,0 @@
|
||||||
import 'dart:typed_data';
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
|
|
||||||
import '_world.dart';
|
|
||||||
|
|
||||||
/// Usage: the user navigates to page 3 and places another signature
|
|
||||||
Future<void> theUserNavigatesToPage3AndPlacesAnotherSignature(
|
|
||||||
WidgetTester tester,
|
|
||||||
) async {
|
|
||||||
final container = TestWorld.container ?? ProviderContainer();
|
|
||||||
TestWorld.container = container;
|
|
||||||
// Assume doc already open from previous step; if not, open a default one
|
|
||||||
final pdf = container.read(pdfProvider);
|
|
||||||
if (!pdf.loaded) {
|
|
||||||
container
|
|
||||||
.read(pdfProvider.notifier)
|
|
||||||
.openPicked(path: 'mock.pdf', pageCount: 5);
|
|
||||||
}
|
|
||||||
// Prepare signature
|
|
||||||
container
|
|
||||||
.read(signatureProvider.notifier)
|
|
||||||
.setImageBytes(Uint8List.fromList([1, 2, 3]));
|
|
||||||
container.read(pdfProvider.notifier).jumpTo(3);
|
|
||||||
container.read(signatureProvider.notifier).placeDefaultRect();
|
|
||||||
final r = container.read(signatureProvider).rect!;
|
|
||||||
container.read(pdfProvider.notifier).addPlacement(page: 3, rect: r);
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
import 'dart:typed_data';
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
|
|
||||||
import '_world.dart';
|
|
||||||
|
|
||||||
/// Usage: the user navigates to page 5 and places another signature
|
|
||||||
Future<void> theUserNavigatesToPage5AndPlacesAnotherSignature(
|
|
||||||
WidgetTester tester,
|
|
||||||
) async {
|
|
||||||
final container = TestWorld.container ?? ProviderContainer();
|
|
||||||
TestWorld.container = container;
|
|
||||||
container
|
|
||||||
.read(pdfProvider.notifier)
|
|
||||||
.openPicked(path: 'mock.pdf', pageCount: 6);
|
|
||||||
container
|
|
||||||
.read(signatureProvider.notifier)
|
|
||||||
.setImageBytes(Uint8List.fromList([1, 2, 3]));
|
|
||||||
container.read(pdfProvider.notifier).jumpTo(5);
|
|
||||||
container.read(signatureProvider.notifier).placeDefaultRect();
|
|
||||||
final r = container.read(signatureProvider).rect!;
|
|
||||||
container.read(pdfProvider.notifier).addPlacement(page: 5, rect: r);
|
|
||||||
// Defensive: ensure earlier placement on page 2 remains (some setups may recreate state)
|
|
||||||
final p2 = container.read(pdfProvider.notifier).placementsOn(2);
|
|
||||||
if (p2.isEmpty) {
|
|
||||||
container
|
|
||||||
.read(pdfProvider.notifier)
|
|
||||||
.addPlacement(page: 2, rect: r.translate(-50, -50));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
|
||||||
|
import '_world.dart';
|
||||||
|
|
||||||
|
/// Usage: the user navigates to page {3} and places another signature
|
||||||
|
Future<void> theUserNavigatesToPageAndPlacesAnotherSignature(
|
||||||
|
WidgetTester tester,
|
||||||
|
num page,
|
||||||
|
) async {
|
||||||
|
final container = TestWorld.container ?? ProviderContainer();
|
||||||
|
TestWorld.container = container;
|
||||||
|
// Ensure doc open
|
||||||
|
final pdf = container.read(pdfProvider);
|
||||||
|
if (!pdf.loaded) {
|
||||||
|
container
|
||||||
|
.read(pdfProvider.notifier)
|
||||||
|
.openPicked(path: 'mock.pdf', pageCount: 6);
|
||||||
|
}
|
||||||
|
container.read(pdfProvider.notifier).jumpTo(page.toInt());
|
||||||
|
// Ensure an image is loaded
|
||||||
|
if (container.read(signatureProvider).imageBytes == null) {
|
||||||
|
container
|
||||||
|
.read(signatureProvider.notifier)
|
||||||
|
.setImageBytes(Uint8List.fromList([1, 2, 3]));
|
||||||
|
}
|
||||||
|
container.read(signatureProvider.notifier).placeDefaultRect();
|
||||||
|
final Rect r = container.read(signatureProvider).rect!;
|
||||||
|
container
|
||||||
|
.read(pdfProvider.notifier)
|
||||||
|
.addPlacement(page: page.toInt(), rect: r, image: 'default.png');
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
|
||||||
|
import '_world.dart';
|
||||||
|
|
||||||
|
/// Usage: the user places a signature from picture <second_image> on page <second_page>
|
||||||
|
Future<void> theUserPlacesASignatureFromPictureOnPage(
|
||||||
|
WidgetTester tester, [
|
||||||
|
dynamic imageName,
|
||||||
|
dynamic pageNumber,
|
||||||
|
]) async {
|
||||||
|
final container = TestWorld.container ?? ProviderContainer();
|
||||||
|
TestWorld.container = container;
|
||||||
|
// Ensure a document is open
|
||||||
|
final pdf = container.read(pdfProvider);
|
||||||
|
if (!pdf.loaded) {
|
||||||
|
container
|
||||||
|
.read(pdfProvider.notifier)
|
||||||
|
.openPicked(path: 'mock.pdf', pageCount: 6);
|
||||||
|
}
|
||||||
|
// Load image bytes based on provided name
|
||||||
|
if (imageName == null) {
|
||||||
|
// Alternate between alice/bob for the first two calls to match Examples
|
||||||
|
final idx = TestWorld.placeFromPictureCallCount++;
|
||||||
|
imageName = (idx % 2 == 0) ? 'alice.png' : 'bob.png';
|
||||||
|
}
|
||||||
|
final String name =
|
||||||
|
imageName is String
|
||||||
|
? imageName
|
||||||
|
: (imageName?.toString() ?? 'default.png');
|
||||||
|
Uint8List bytes;
|
||||||
|
switch (name) {
|
||||||
|
case 'alice.png':
|
||||||
|
bytes = Uint8List.fromList([1, 2, 3]);
|
||||||
|
break;
|
||||||
|
case 'bob.png':
|
||||||
|
bytes = Uint8List.fromList([4, 5, 6]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
bytes = Uint8List.fromList([7, 8, 9]);
|
||||||
|
}
|
||||||
|
container.read(signatureProvider.notifier).setImageBytes(bytes);
|
||||||
|
// Place default rect and add placement on target page with image name
|
||||||
|
container.read(signatureProvider.notifier).placeDefaultRect();
|
||||||
|
final Rect r = container.read(signatureProvider).rect!;
|
||||||
|
final int page =
|
||||||
|
(pageNumber is num)
|
||||||
|
? pageNumber.toInt()
|
||||||
|
: int.tryParse(pageNumber?.toString() ?? '') ??
|
||||||
|
// Default pages for the two calls in the scenario: 1 then 3
|
||||||
|
((TestWorld.placeFromPictureCallCount <= 1) ? 1 : 3);
|
||||||
|
container
|
||||||
|
.read(pdfProvider.notifier)
|
||||||
|
.addPlacement(page: page, rect: r, image: name);
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
|
||||||
|
import '_world.dart';
|
||||||
|
|
||||||
|
/// Usage: the user places a signature on page {1}
|
||||||
|
Future<void> theUserPlacesASignatureOnPage(
|
||||||
|
WidgetTester tester,
|
||||||
|
num page,
|
||||||
|
) async {
|
||||||
|
final container = TestWorld.container ?? ProviderContainer();
|
||||||
|
TestWorld.container = container;
|
||||||
|
// Ensure doc open
|
||||||
|
final pdf = container.read(pdfProvider);
|
||||||
|
if (!pdf.loaded) {
|
||||||
|
container
|
||||||
|
.read(pdfProvider.notifier)
|
||||||
|
.openPicked(path: 'mock.pdf', pageCount: 6);
|
||||||
|
}
|
||||||
|
// Ensure an image is loaded
|
||||||
|
if (container.read(signatureProvider).imageBytes == null) {
|
||||||
|
container
|
||||||
|
.read(signatureProvider.notifier)
|
||||||
|
.setImageBytes(Uint8List.fromList([1, 2, 3]));
|
||||||
|
}
|
||||||
|
container.read(signatureProvider.notifier).placeDefaultRect();
|
||||||
|
final Rect r = container.read(signatureProvider).rect!;
|
||||||
|
container
|
||||||
|
.read(pdfProvider.notifier)
|
||||||
|
.addPlacement(page: page.toInt(), rect: r, image: 'default.png');
|
||||||
|
}
|
|
@ -1,21 +0,0 @@
|
||||||
import 'dart:typed_data';
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
|
|
||||||
import '_world.dart';
|
|
||||||
|
|
||||||
/// Usage: the user places a signature on page 1
|
|
||||||
Future<void> theUserPlacesASignatureOnPage1(WidgetTester tester) async {
|
|
||||||
final container = TestWorld.container ?? ProviderContainer();
|
|
||||||
TestWorld.container = container;
|
|
||||||
container
|
|
||||||
.read(pdfProvider.notifier)
|
|
||||||
.openPicked(path: 'mock.pdf', pageCount: 5);
|
|
||||||
container
|
|
||||||
.read(signatureProvider.notifier)
|
|
||||||
.setImageBytes(Uint8List.fromList([1, 2, 3]));
|
|
||||||
container.read(signatureProvider.notifier).placeDefaultRect();
|
|
||||||
final r = container.read(signatureProvider).rect!;
|
|
||||||
container.read(pdfProvider.notifier).addPlacement(page: 1, rect: r);
|
|
||||||
}
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
|
||||||
|
import '_world.dart';
|
||||||
|
|
||||||
|
/// Usage: the user places a signature on the page
|
||||||
|
Future<void> theUserPlacesASignatureOnThePage(WidgetTester tester) async {
|
||||||
|
final container = TestWorld.container ?? ProviderContainer();
|
||||||
|
TestWorld.container = container;
|
||||||
|
final pdf = container.read(pdfProvider);
|
||||||
|
if (!pdf.loaded) {
|
||||||
|
container
|
||||||
|
.read(pdfProvider.notifier)
|
||||||
|
.openPicked(path: 'mock.pdf', pageCount: 1);
|
||||||
|
container.read(pdfProvider.notifier).setSignedPage(1);
|
||||||
|
}
|
||||||
|
// Ensure image bytes
|
||||||
|
if (container.read(signatureProvider).imageBytes == null) {
|
||||||
|
final name = TestWorld.currentImageName ?? 'alice.png';
|
||||||
|
Uint8List bytes =
|
||||||
|
name == 'bob.png'
|
||||||
|
? Uint8List.fromList([4, 5, 6])
|
||||||
|
: Uint8List.fromList([1, 2, 3]);
|
||||||
|
container.read(signatureProvider.notifier).setImageBytes(bytes);
|
||||||
|
}
|
||||||
|
container.read(signatureProvider.notifier).placeDefaultRect();
|
||||||
|
final Rect r = container.read(signatureProvider).rect!;
|
||||||
|
final int page = container.read(pdfProvider).signedPage ?? 1;
|
||||||
|
final imgName = TestWorld.currentImageName ?? 'alice.png';
|
||||||
|
container
|
||||||
|
.read(pdfProvider.notifier)
|
||||||
|
.addPlacement(page: page, rect: r, image: imgName);
|
||||||
|
// Select the just placed signature (last index)
|
||||||
|
final list = container.read(pdfProvider).placementsByPage[page] ?? const [];
|
||||||
|
container
|
||||||
|
.read(pdfProvider.notifier)
|
||||||
|
.selectPlacement(list.isEmpty ? null : (list.length - 1));
|
||||||
|
}
|
Loading…
Reference in New Issue