diff --git a/lib/data/repositories/document_repository.dart b/lib/data/repositories/document_repository.dart index 98fdc4c..94c4ba8 100644 --- a/lib/data/repositories/document_repository.dart +++ b/lib/data/repositories/document_repository.dart @@ -182,6 +182,7 @@ class DocumentStateNotifier extends StateNotifier { ); if (result != null) return result; } catch (_) { + debugPrint('Warning: export in isolate failed'); // Fall back to main-isolate export if isolate fails (e.g., engine limitations). } diff --git a/lib/data/services/export_service.dart b/lib/data/services/export_service.dart index c2a1802..25d37c5 100644 --- a/lib/data/services/export_service.dart +++ b/lib/data/services/export_service.dart @@ -6,6 +6,7 @@ import 'package:image/image.dart' as img; import 'package:pdf/widgets.dart' as pw; import 'package:pdf/pdf.dart' as pdf; import 'package:pdfrx_engine/pdfrx_engine.dart' as engine; +import 'package:pdfrx/pdfrx.dart' show pdfrxFlutterInitialize; import '../../domain/models/model.dart'; import '../../utils/rotation_utils.dart' as rot; import '../../utils/background_removal.dart' as br; @@ -104,15 +105,14 @@ class ExportService { } // Initialize engine (safe to call multiple times) - try { - await engine.pdfrxInitialize(); - } catch (_) {} + pdfrxFlutterInitialize(); // Open source document from memory; if not supported, write temp file engine.PdfDocument? doc; try { doc = await engine.PdfDocument.openData(srcBytes); } catch (_) { + debugPrint('Warning: pdfrx openData failed'); final tmp = File( '${Directory.systemTemp.path}/pdfrx_src_${DateTime.now().millisecondsSinceEpoch}.pdf', ); @@ -120,7 +120,9 @@ class ExportService { doc = await engine.PdfDocument.openFile(tmp.path); try { tmp.deleteSync(); - } catch (_) {} + } catch (_) { + debugPrint('Warning: temp file delete failed'); + } } // doc is guaranteed to be assigned by either openData or openFile above @@ -221,6 +223,7 @@ class ExportService { final bytes = await out.save(); doc.dispose(); + debugPrint('exportSignedPdfFromBytes succeeded'); return bytes; } @@ -233,6 +236,7 @@ class ExportService { await file.writeAsBytes(bytes, flush: true); return true; } catch (_) { + debugPrint('Error: saveBytesToFile failed'); return false; } } diff --git a/lib/ui/features/pdf/widgets/pdf_screen.dart b/lib/ui/features/pdf/widgets/pdf_screen.dart index 59da41d..e214a20 100644 --- a/lib/ui/features/pdf/widgets/pdf_screen.dart +++ b/lib/ui/features/pdf/widgets/pdf_screen.dart @@ -221,6 +221,8 @@ class _PdfSignatureHomePageState extends ConsumerState { if (out != null) { ok = await downloadBytes(out, filename: suggested); savedPath = suggested; + } else { + debugPrint('_saveSignedPdf: export to bytes failed'); } } if (!kIsWeb) { @@ -235,7 +237,6 @@ class _PdfSignatureHomePageState extends ConsumerState { ), ), ); - // ignore: avoid_print debugPrint('_saveSignedPdf: SnackBar shown ok=' + ok.toString()); } else { messenger.showSnackBar( diff --git a/lib/utils/download.dart b/lib/utils/download.dart index 90d88e7..7cb6759 100644 --- a/lib/utils/download.dart +++ b/lib/utils/download.dart @@ -1,11 +1,17 @@ -import 'dart:typed_data'; +import 'package:flutter/foundation.dart'; -import 'download_stub.dart' if (dart.library.html) 'download_web.dart' as impl; +// On modern Flutter Web (Wasm GC, e.g., Chromium), dart:html is not available. +// Use js_interop capability to select the web implementation that relies on +// package:web instead of dart:html. +import 'download_stub.dart' + if (dart.library.js_interop) 'download_web.dart' + as impl; /// Initiates a platform-appropriate download/save operation. /// /// On Web: triggers a browser download with the provided filename. /// On non-Web: returns false (no-op). Use your existing IO save flow instead. Future downloadBytes(Uint8List bytes, {required String filename}) { + debugPrint('downloadBytes: initiating download'); return impl.downloadBytes(bytes, filename: filename); } diff --git a/lib/utils/download_stub.dart b/lib/utils/download_stub.dart index 654d280..4540c9c 100644 --- a/lib/utils/download_stub.dart +++ b/lib/utils/download_stub.dart @@ -1,6 +1,9 @@ import 'dart:typed_data'; +import 'package:flutter/widgets.dart'; + Future downloadBytes(Uint8List bytes, {required String filename}) async { // Not supported on non-web. Return false so caller can fallback to file save. + debugPrint('downloadBytes: not supported on this platform'); return false; } diff --git a/lib/utils/download_web.dart b/lib/utils/download_web.dart index 5088023..3a8aa57 100644 --- a/lib/utils/download_web.dart +++ b/lib/utils/download_web.dart @@ -1,23 +1,28 @@ -// ignore_for_file: deprecated_member_use -// ignore: avoid_web_libraries_in_flutter -import 'dart:html' as html; -import 'dart:typed_data'; +// Implementation for Web using package:web to support Wasm GC (Chromium) +// without importing dart:html directly. +import 'dart:convert'; +import 'package:flutter/foundation.dart'; +import 'package:web/web.dart' as web; Future downloadBytes(Uint8List bytes, {required String filename}) async { try { - final blob = html.Blob([bytes], 'application/pdf'); - final url = html.Url.createObjectUrlFromBlob(blob); + // Use a data URL to avoid Blob/typed array interop issues under Wasm GC. + final url = 'data:application/pdf;base64,${base64Encode(bytes)}'; + + // Create an anchor element and trigger a click to download final anchor = - html.document.createElement('a') as html.AnchorElement + web.HTMLAnchorElement() ..href = url ..download = filename ..style.display = 'none'; - html.document.body?.children.add(anchor); + + web.document.body?.append(anchor); anchor.click(); anchor.remove(); - html.Url.revokeObjectUrl(url); + return true; - } catch (_) { + } catch (e, st) { + debugPrint('Error: downloadBytes failed: $e\n$st'); return false; } } diff --git a/pubspec.yaml b/pubspec.yaml index b13e7d1..1a88d5a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -62,6 +62,7 @@ dependencies: responsive_framework: ^1.5.1 # disable_web_context_menu: ^1.1.0 # ml_linalg: ^13.12.6 + web: ^1.1.1 dev_dependencies: flutter_test: