Showing
7 changed files
with
238 additions
and
7 deletions
@@ -8,6 +8,7 @@ | @@ -8,6 +8,7 @@ | ||
8 | - Use PageTheme in example | 8 | - Use PageTheme in example |
9 | - Save shared pdf in cache on Android | 9 | - Save shared pdf in cache on Android |
10 | - Implement macOS embedding support | 10 | - Implement macOS embedding support |
11 | +- Implement Flutter Web support | ||
11 | 12 | ||
12 | ## 2.1.9 | 13 | ## 2.1.9 |
13 | 14 |
1 | import 'dart:async'; | 1 | import 'dart:async'; |
2 | 2 | ||
3 | +import 'package:flutter/foundation.dart' show kIsWeb; | ||
3 | import 'package:flutter/widgets.dart' as fw; | 4 | import 'package:flutter/widgets.dart' as fw; |
4 | import 'package:pdf/pdf.dart'; | 5 | import 'package:pdf/pdf.dart'; |
5 | import 'package:pdf/widgets.dart'; | 6 | import 'package:pdf/widgets.dart'; |
@@ -10,13 +11,15 @@ import 'example_widgets.dart'; | @@ -10,13 +11,15 @@ import 'example_widgets.dart'; | ||
10 | Future<Document> generateDocument(PdfPageFormat format) async { | 11 | Future<Document> generateDocument(PdfPageFormat format) async { |
11 | final Document pdf = Document(title: 'My Résumé', author: 'David PHAM-VAN'); | 12 | final Document pdf = Document(title: 'My Résumé', author: 'David PHAM-VAN'); |
12 | 13 | ||
13 | - final PdfImage profileImage = await pdfImageFromImageProvider( | ||
14 | - pdf: pdf.document, | ||
15 | - image: const fw.NetworkImage( | ||
16 | - 'https://www.gravatar.com/avatar/00000000000000000000000000000000?d=mp&s=200'), | ||
17 | - onError: (dynamic exception, StackTrace stackTrace) { | ||
18 | - print('Unable to download image'); | ||
19 | - }); | 14 | + final PdfImage profileImage = kIsWeb |
15 | + ? null | ||
16 | + : await pdfImageFromImageProvider( | ||
17 | + pdf: pdf.document, | ||
18 | + image: const fw.NetworkImage( | ||
19 | + 'https://www.gravatar.com/avatar/00000000000000000000000000000000?d=mp&s=200'), | ||
20 | + onError: (dynamic exception, StackTrace stackTrace) { | ||
21 | + print('Unable to download image'); | ||
22 | + }); | ||
20 | 23 | ||
21 | final PageTheme pageTheme = myPageTheme(format); | 24 | final PageTheme pageTheme = myPageTheme(format); |
22 | 25 |
printing/example/web/index.html
0 → 100644
printing/lib/printing_web.dart
0 → 100644
1 | +/* | ||
2 | + * Copyright (C) 2017, David PHAM-VAN <dev.nfet.net@gmail.com> | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | + | ||
17 | +library printing_web; | ||
18 | + | ||
19 | +import 'dart:async'; | ||
20 | +import 'dart:html' as html; | ||
21 | +import 'dart:js' as js; | ||
22 | + | ||
23 | +import 'package:flutter/services.dart'; | ||
24 | +import 'package:flutter_web_plugins/flutter_web_plugins.dart'; | ||
25 | + | ||
26 | +part 'wsrc/print_job.dart'; | ||
27 | + | ||
28 | +class PrintingPlugin { | ||
29 | + PrintingPlugin(this._channel); | ||
30 | + | ||
31 | + static void registerWith(Registrar registrar) { | ||
32 | + final MethodChannel channel = MethodChannel( | ||
33 | + 'net.nfet.printing', | ||
34 | + const StandardMethodCodec(), | ||
35 | + registrar.messenger, | ||
36 | + ); | ||
37 | + final PrintingPlugin instance = PrintingPlugin(channel); | ||
38 | + channel.setMethodCallHandler(instance.handleMethodCall); | ||
39 | + } | ||
40 | + | ||
41 | + final MethodChannel _channel; | ||
42 | + | ||
43 | + Future<dynamic> handleMethodCall(MethodCall call) async { | ||
44 | + switch (call.method) { | ||
45 | + case 'printPdf': | ||
46 | + final String name = call.arguments['name']; | ||
47 | + final double width = call.arguments['width']; | ||
48 | + final double height = call.arguments['height']; | ||
49 | + final double marginLeft = call.arguments['marginLeft']; | ||
50 | + final double marginTop = call.arguments['marginTop']; | ||
51 | + final double marginRight = call.arguments['marginRight']; | ||
52 | + final double marginBottom = call.arguments['marginBottom']; | ||
53 | + | ||
54 | + final _PrintJob printJob = _PrintJob(this, call.arguments['job']); | ||
55 | + return printJob.printPdf(name, width, height, marginLeft, marginTop, | ||
56 | + marginRight, marginBottom); | ||
57 | + case 'sharePdf': | ||
58 | + final List<int> data = call.arguments['doc']; | ||
59 | + final double x = call.arguments['x']; | ||
60 | + final double y = call.arguments['y']; | ||
61 | + final double width = call.arguments['w']; | ||
62 | + final double height = call.arguments['h']; | ||
63 | + final String name = call.arguments['name']; | ||
64 | + return _PrintJob.sharePdf(data, x, y, width, height, name); | ||
65 | + case 'printingInfo': | ||
66 | + return _PrintJob.printingInfo(); | ||
67 | + } | ||
68 | + throw UnimplementedError('Method "${call.method}" not implemented'); | ||
69 | + } | ||
70 | + | ||
71 | + /// Request the Pdf document from flutter | ||
72 | + Future<void> onLayout( | ||
73 | + _PrintJob printJob, | ||
74 | + double width, | ||
75 | + double height, | ||
76 | + double marginLeft, | ||
77 | + double marginTop, | ||
78 | + double marginRight, | ||
79 | + double marginBottom) async { | ||
80 | + final Map<String, dynamic> args = <String, dynamic>{ | ||
81 | + 'width': width, | ||
82 | + 'height': height, | ||
83 | + 'marginLeft': marginLeft, | ||
84 | + 'marginTop': marginTop, | ||
85 | + 'marginRight': marginRight, | ||
86 | + 'marginBottom': marginBottom, | ||
87 | + 'job': printJob.index, | ||
88 | + }; | ||
89 | + | ||
90 | + final dynamic result = | ||
91 | + await _channel.invokeMethod<dynamic>('onLayout', args); | ||
92 | + | ||
93 | + if (result is List<int>) { | ||
94 | + printJob.setDocument(result); | ||
95 | + } else { | ||
96 | + printJob.cancelJob(); | ||
97 | + } | ||
98 | + } | ||
99 | + | ||
100 | + /// send completion status to flutter | ||
101 | + Future<void> onCompleted(_PrintJob printJob, bool completed, | ||
102 | + [String error = '']) async { | ||
103 | + final Map<String, dynamic> data = <String, dynamic>{ | ||
104 | + 'completed': completed, | ||
105 | + 'error': error, | ||
106 | + 'job': printJob.index, | ||
107 | + }; | ||
108 | + | ||
109 | + await _channel.invokeMethod<dynamic>('onCompleted', data); | ||
110 | + } | ||
111 | +} |
printing/lib/wsrc/print_job.dart
0 → 100644
1 | +/* | ||
2 | + * Copyright (C) 2017, David PHAM-VAN <dev.nfet.net@gmail.com> | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | + | ||
17 | +part of printing_web; | ||
18 | + | ||
19 | +class _PrintJob { | ||
20 | + _PrintJob(this.printing, this.index); | ||
21 | + | ||
22 | + final PrintingPlugin printing; | ||
23 | + final int index; | ||
24 | + | ||
25 | + String _jobName; | ||
26 | + | ||
27 | + Future<int> printPdf( | ||
28 | + String name, | ||
29 | + double width, | ||
30 | + double height, | ||
31 | + double marginLeft, | ||
32 | + double marginTop, | ||
33 | + double marginRight, | ||
34 | + double marginBottom) async { | ||
35 | + _jobName = name; | ||
36 | + await printing.onLayout( | ||
37 | + this, width, height, marginLeft, marginTop, marginRight, marginBottom); | ||
38 | + return 1; | ||
39 | + } | ||
40 | + | ||
41 | + static Future<int> sharePdf(List<int> data, double x, double y, double width, | ||
42 | + double height, String name) async { | ||
43 | + final html.Blob pdfFile = html.Blob(<dynamic>[data], 'application/pdf'); | ||
44 | + final String pdfUrl = html.Url.createObjectUrl(pdfFile); | ||
45 | + final html.HtmlDocument doc = js.context['document']; | ||
46 | + final html.AnchorElement link = doc.createElement('a'); | ||
47 | + link.href = pdfUrl; | ||
48 | + link.download = name; | ||
49 | + link.click(); | ||
50 | + return 1; | ||
51 | + } | ||
52 | + | ||
53 | + static Map<String, dynamic> printingInfo() { | ||
54 | + return <String, dynamic>{ | ||
55 | + 'directPrint': false, | ||
56 | + 'dynamicLayout': false, | ||
57 | + 'canPrint': true, | ||
58 | + 'canConvertHtml': false, | ||
59 | + 'canShare': true, | ||
60 | + }; | ||
61 | + } | ||
62 | + | ||
63 | + void setDocument(List<int> result) { | ||
64 | + final bool isChrome = js.context['chrome'] != null; | ||
65 | + | ||
66 | + if (!isChrome) { | ||
67 | + sharePdf(result, 0, 0, 0, 0, _jobName + '.pdf'); | ||
68 | + printing.onCompleted(this, true); | ||
69 | + return; | ||
70 | + } | ||
71 | + | ||
72 | + final html.Blob pdfFile = html.Blob(<dynamic>[result], 'application/pdf'); | ||
73 | + final String pdfUrl = html.Url.createObjectUrl(pdfFile); | ||
74 | + final html.HtmlDocument doc = js.context['document']; | ||
75 | + final html.IFrameElement frame = doc.createElement('iframe'); | ||
76 | + frame.setAttribute('style', | ||
77 | + 'visibility: hidden; height: 0; width: 0; position: absolute;'); | ||
78 | + frame.setAttribute('src', pdfUrl); | ||
79 | + doc.body.append(frame); | ||
80 | + | ||
81 | + frame.addEventListener('load', (html.Event event) { | ||
82 | + final js.JsObject win = | ||
83 | + js.JsObject.fromBrowserObject(frame)['contentWindow']; | ||
84 | + | ||
85 | + win.callMethod('addEventListener', <dynamic>[ | ||
86 | + 'afterprint', | ||
87 | + js.allowInterop<html.EventListener>((html.Event event) { | ||
88 | + frame.remove(); | ||
89 | + printing.onCompleted(this, true); | ||
90 | + }), | ||
91 | + ]); | ||
92 | + | ||
93 | + frame.focus(); | ||
94 | + win.callMethod('print'); | ||
95 | + printing.onCompleted(this, true); | ||
96 | + }); | ||
97 | + } | ||
98 | + | ||
99 | + Future<void> cancelJob() async {} | ||
100 | +} |
@@ -13,6 +13,8 @@ environment: | @@ -13,6 +13,8 @@ environment: | ||
13 | dependencies: | 13 | dependencies: |
14 | flutter: | 14 | flutter: |
15 | sdk: flutter | 15 | sdk: flutter |
16 | + flutter_web_plugins: | ||
17 | + sdk: flutter | ||
16 | pdf: "^1.3.15" | 18 | pdf: "^1.3.15" |
17 | 19 | ||
18 | dev_dependencies: | 20 | dev_dependencies: |
@@ -33,3 +35,6 @@ flutter: | @@ -33,3 +35,6 @@ flutter: | ||
33 | pluginClass: PrintingPlugin | 35 | pluginClass: PrintingPlugin |
34 | macos: | 36 | macos: |
35 | pluginClass: PrintingPlugin | 37 | pluginClass: PrintingPlugin |
38 | + web: | ||
39 | + fileName: printing_web.dart | ||
40 | + pluginClass: PrintingPlugin |
-
Please register or login to post a comment