fix: download is not work under web/ wasm/ chromium env
This commit is contained in:
parent
b2bf489af0
commit
5673f9a0e7
|
|
@ -182,6 +182,7 @@ class DocumentStateNotifier extends StateNotifier<Document> {
|
||||||
);
|
);
|
||||||
if (result != null) return result;
|
if (result != null) return result;
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
|
debugPrint('Warning: export in isolate failed');
|
||||||
// Fall back to main-isolate export if isolate fails (e.g., engine limitations).
|
// Fall back to main-isolate export if isolate fails (e.g., engine limitations).
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import 'package:image/image.dart' as img;
|
||||||
import 'package:pdf/widgets.dart' as pw;
|
import 'package:pdf/widgets.dart' as pw;
|
||||||
import 'package:pdf/pdf.dart' as pdf;
|
import 'package:pdf/pdf.dart' as pdf;
|
||||||
import 'package:pdfrx_engine/pdfrx_engine.dart' as engine;
|
import 'package:pdfrx_engine/pdfrx_engine.dart' as engine;
|
||||||
|
import 'package:pdfrx/pdfrx.dart' show pdfrxFlutterInitialize;
|
||||||
import '../../domain/models/model.dart';
|
import '../../domain/models/model.dart';
|
||||||
import '../../utils/rotation_utils.dart' as rot;
|
import '../../utils/rotation_utils.dart' as rot;
|
||||||
import '../../utils/background_removal.dart' as br;
|
import '../../utils/background_removal.dart' as br;
|
||||||
|
|
@ -104,15 +105,14 @@ class ExportService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize engine (safe to call multiple times)
|
// Initialize engine (safe to call multiple times)
|
||||||
try {
|
pdfrxFlutterInitialize();
|
||||||
await engine.pdfrxInitialize();
|
|
||||||
} catch (_) {}
|
|
||||||
|
|
||||||
// Open source document from memory; if not supported, write temp file
|
// Open source document from memory; if not supported, write temp file
|
||||||
engine.PdfDocument? doc;
|
engine.PdfDocument? doc;
|
||||||
try {
|
try {
|
||||||
doc = await engine.PdfDocument.openData(srcBytes);
|
doc = await engine.PdfDocument.openData(srcBytes);
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
|
debugPrint('Warning: pdfrx openData failed');
|
||||||
final tmp = File(
|
final tmp = File(
|
||||||
'${Directory.systemTemp.path}/pdfrx_src_${DateTime.now().millisecondsSinceEpoch}.pdf',
|
'${Directory.systemTemp.path}/pdfrx_src_${DateTime.now().millisecondsSinceEpoch}.pdf',
|
||||||
);
|
);
|
||||||
|
|
@ -120,7 +120,9 @@ class ExportService {
|
||||||
doc = await engine.PdfDocument.openFile(tmp.path);
|
doc = await engine.PdfDocument.openFile(tmp.path);
|
||||||
try {
|
try {
|
||||||
tmp.deleteSync();
|
tmp.deleteSync();
|
||||||
} catch (_) {}
|
} catch (_) {
|
||||||
|
debugPrint('Warning: temp file delete failed');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// doc is guaranteed to be assigned by either openData or openFile above
|
// doc is guaranteed to be assigned by either openData or openFile above
|
||||||
|
|
||||||
|
|
@ -221,6 +223,7 @@ class ExportService {
|
||||||
|
|
||||||
final bytes = await out.save();
|
final bytes = await out.save();
|
||||||
doc.dispose();
|
doc.dispose();
|
||||||
|
debugPrint('exportSignedPdfFromBytes succeeded');
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -233,6 +236,7 @@ class ExportService {
|
||||||
await file.writeAsBytes(bytes, flush: true);
|
await file.writeAsBytes(bytes, flush: true);
|
||||||
return true;
|
return true;
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
|
debugPrint('Error: saveBytesToFile failed');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -221,6 +221,8 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
|
||||||
if (out != null) {
|
if (out != null) {
|
||||||
ok = await downloadBytes(out, filename: suggested);
|
ok = await downloadBytes(out, filename: suggested);
|
||||||
savedPath = suggested;
|
savedPath = suggested;
|
||||||
|
} else {
|
||||||
|
debugPrint('_saveSignedPdf: export to bytes failed');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!kIsWeb) {
|
if (!kIsWeb) {
|
||||||
|
|
@ -235,7 +237,6 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
// ignore: avoid_print
|
|
||||||
debugPrint('_saveSignedPdf: SnackBar shown ok=' + ok.toString());
|
debugPrint('_saveSignedPdf: SnackBar shown ok=' + ok.toString());
|
||||||
} else {
|
} else {
|
||||||
messenger.showSnackBar(
|
messenger.showSnackBar(
|
||||||
|
|
|
||||||
|
|
@ -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.
|
/// Initiates a platform-appropriate download/save operation.
|
||||||
///
|
///
|
||||||
/// On Web: triggers a browser download with the provided filename.
|
/// On Web: triggers a browser download with the provided filename.
|
||||||
/// On non-Web: returns false (no-op). Use your existing IO save flow instead.
|
/// On non-Web: returns false (no-op). Use your existing IO save flow instead.
|
||||||
Future<bool> downloadBytes(Uint8List bytes, {required String filename}) {
|
Future<bool> downloadBytes(Uint8List bytes, {required String filename}) {
|
||||||
|
debugPrint('downloadBytes: initiating download');
|
||||||
return impl.downloadBytes(bytes, filename: filename);
|
return impl.downloadBytes(bytes, filename: filename);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
Future<bool> downloadBytes(Uint8List bytes, {required String filename}) async {
|
Future<bool> downloadBytes(Uint8List bytes, {required String filename}) async {
|
||||||
// Not supported on non-web. Return false so caller can fallback to file save.
|
// Not supported on non-web. Return false so caller can fallback to file save.
|
||||||
|
debugPrint('downloadBytes: not supported on this platform');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,28 @@
|
||||||
// ignore_for_file: deprecated_member_use
|
// Implementation for Web using package:web to support Wasm GC (Chromium)
|
||||||
// ignore: avoid_web_libraries_in_flutter
|
// without importing dart:html directly.
|
||||||
import 'dart:html' as html;
|
import 'dart:convert';
|
||||||
import 'dart:typed_data';
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:web/web.dart' as web;
|
||||||
|
|
||||||
Future<bool> downloadBytes(Uint8List bytes, {required String filename}) async {
|
Future<bool> downloadBytes(Uint8List bytes, {required String filename}) async {
|
||||||
try {
|
try {
|
||||||
final blob = html.Blob([bytes], 'application/pdf');
|
// Use a data URL to avoid Blob/typed array interop issues under Wasm GC.
|
||||||
final url = html.Url.createObjectUrlFromBlob(blob);
|
final url = 'data:application/pdf;base64,${base64Encode(bytes)}';
|
||||||
|
|
||||||
|
// Create an anchor element and trigger a click to download
|
||||||
final anchor =
|
final anchor =
|
||||||
html.document.createElement('a') as html.AnchorElement
|
web.HTMLAnchorElement()
|
||||||
..href = url
|
..href = url
|
||||||
..download = filename
|
..download = filename
|
||||||
..style.display = 'none';
|
..style.display = 'none';
|
||||||
html.document.body?.children.add(anchor);
|
|
||||||
|
web.document.body?.append(anchor);
|
||||||
anchor.click();
|
anchor.click();
|
||||||
anchor.remove();
|
anchor.remove();
|
||||||
html.Url.revokeObjectUrl(url);
|
|
||||||
return true;
|
return true;
|
||||||
} catch (_) {
|
} catch (e, st) {
|
||||||
|
debugPrint('Error: downloadBytes failed: $e\n$st');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@ dependencies:
|
||||||
responsive_framework: ^1.5.1
|
responsive_framework: ^1.5.1
|
||||||
# disable_web_context_menu: ^1.1.0
|
# disable_web_context_menu: ^1.1.0
|
||||||
# ml_linalg: ^13.12.6
|
# ml_linalg: ^13.12.6
|
||||||
|
web: ^1.1.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue