David PHAM-VAN

Implement Flutter Web support

@@ -47,3 +47,4 @@ test/diff @@ -47,3 +47,4 @@ test/diff
47 47
48 ref 48 ref
49 !test/golden/*.pdf 49 !test/golden/*.pdf
  50 +.flutter-plugins-dependencies
@@ -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,7 +11,9 @@ import 'example_widgets.dart'; @@ -10,7 +11,9 @@ 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 + final PdfImage profileImage = kIsWeb
  15 + ? null
  16 + : await pdfImageFromImageProvider(
14 pdf: pdf.document, 17 pdf: pdf.document,
15 image: const fw.NetworkImage( 18 image: const fw.NetworkImage(
16 'https://www.gravatar.com/avatar/00000000000000000000000000000000?d=mp&s=200'), 19 'https://www.gravatar.com/avatar/00000000000000000000000000000000?d=mp&s=200'),
  1 +<!DOCTYPE html>
  2 +<html>
  3 +<head>
  4 + <meta charset="UTF-8">
  5 + <title>Printing Example</title>
  6 +</head>
  7 +<body>
  8 + <script src="main.dart.js" type="application/javascript"></script>
  9 +</body>
  10 +</html>
  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 +}
  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