David PHAM-VAN

Use proper print dialog on Firefox

1 # Changelog 1 # Changelog
2 2
  3 +## 5.4.2
  4 +
  5 +- Use proper print dialog on Firefox
  6 +
3 ## 5.4.1 7 ## 5.4.1
4 8
5 - Always use HTTPS to download Google Fonts 9 - Always use HTTPS to download Google Fonts
@@ -37,8 +37,7 @@ for documentation. @@ -37,8 +37,7 @@ for documentation.
37 4. For MacOS add printing capability by opening macos directory in XCode 37 4. For MacOS add printing capability by opening macos directory in XCode
38 38
39 5. For the web, a javascript library and a small script has to be added to 39 5. For the web, a javascript library and a small script has to be added to
40 - your `web/index.html` file, just before  
41 - `<script src="main.dart.js" type="application/javascript"></script>`: 40 + your `web/index.html` file, just before `</head>`:
42 41
43 ```html 42 ```html
44 <script src="//cdnjs.cloudflare.com/ajax/libs/pdf.js/2.8.335/pdf.min.js"></script> 43 <script src="//cdnjs.cloudflare.com/ajax/libs/pdf.js/2.8.335/pdf.min.js"></script>
@@ -43,6 +43,8 @@ class PrintingPlugin extends PrintingPlatform { @@ -43,6 +43,8 @@ class PrintingPlugin extends PrintingPlatform {
43 PrintingPlatform.instance = PrintingPlugin(); 43 PrintingPlatform.instance = PrintingPlugin();
44 } 44 }
45 45
  46 + static const String _scriptId = '__net_nfet_printing_s__';
  47 +
46 static const String _frameId = '__net_nfet_printing__'; 48 static const String _frameId = '__net_nfet_printing__';
47 49
48 @override 50 @override
@@ -99,59 +101,76 @@ class PrintingPlugin extends PrintingPlatform { @@ -99,59 +101,76 @@ class PrintingPlugin extends PrintingPlatform {
99 final isChrome = js.context['chrome'] != null; 101 final isChrome = js.context['chrome'] != null;
100 final isSafari = js.context['safari'] != null; 102 final isSafari = js.context['safari'] != null;
101 final isMobile = userAgent.contains('Mobile'); 103 final isMobile = userAgent.contains('Mobile');
102 - // Maybe Firefox will support iframe printing  
103 - // https://bugzilla.mozilla.org/show_bug.cgi?id=911444  
104 - // final isFirefox = userAgent.contains('Firefox'); 104 + final isFirefox = userAgent.contains('Firefox');
105 105
106 - // Chrome and Safari on a desktop computer  
107 - if ((isChrome || isSafari) && !isMobile) { 106 + // Chrome, Safari, and Firefox on a desktop computer
  107 + if ((isChrome || isSafari || isFirefox) && !isMobile) {
108 final completer = Completer<bool>(); 108 final completer = Completer<bool>();
109 final pdfFile = html.Blob( 109 final pdfFile = html.Blob(
110 - <Uint8List>[Uint8List.fromList(result)], 110 + <Uint8List>[result],
111 'application/pdf', 111 'application/pdf',
112 ); 112 );
113 final pdfUrl = html.Url.createObjectUrl(pdfFile); 113 final pdfUrl = html.Url.createObjectUrl(pdfFile);
114 final html.HtmlDocument doc = js.context['document']; 114 final html.HtmlDocument doc = js.context['document'];
115 115
  116 + final script =
  117 + doc.getElementById(_scriptId) ?? doc.createElement('script');
  118 + script.setAttribute('id', _scriptId);
  119 + script.setAttribute('type', 'text/javascript');
  120 + script.innerText =
  121 + '''function ${_frameId}_print(){var f=document.getElementById('$_frameId');f.focus();f.contentWindow.print();}''';
  122 + doc.body!.append(script);
  123 +
116 final frame = doc.getElementById(_frameId) ?? doc.createElement('iframe'); 124 final frame = doc.getElementById(_frameId) ?? doc.createElement('iframe');
  125 + if (isFirefox) {
  126 + // Set the iframe to be is visible on the page (guaranteed by fixed position) but hidden using opacity 0, because
  127 + // this works in Firefox. The height needs to be sufficient for some part of the document other than the PDF
  128 + // viewer's toolbar to be visible in the page
  129 + frame.setAttribute('style',
  130 + 'width: 1px; height: 100px; position: fixed; left: 0; top: 0; opacity: 0; border-width: 0; margin: 0; padding: 0');
  131 + } else {
  132 + // Hide the iframe in other browsers
117 frame.setAttribute( 133 frame.setAttribute(
118 'style', 134 'style',
119 'visibility: hidden; height: 0; width: 0; position: absolute;', 135 'visibility: hidden; height: 0; width: 0; position: absolute;',
120 // 'height: 400px; width: 600px; position: absolute; z-index: 1000', 136 // 'height: 400px; width: 600px; position: absolute; z-index: 1000',
121 ); 137 );
  138 + }
122 139
123 frame.setAttribute('id', _frameId); 140 frame.setAttribute('id', _frameId);
124 frame.setAttribute('src', pdfUrl); 141 frame.setAttribute('src', pdfUrl);
  142 + final stopWatch = Stopwatch();
125 143
126 html.EventListener? load; 144 html.EventListener? load;
127 load = (html.Event event) { 145 load = (html.Event event) {
128 frame.removeEventListener('load', load); 146 frame.removeEventListener('load', load);
129 - final js.JsObject win =  
130 - js.JsObject.fromBrowserObject(frame)['contentWindow'];  
131 - frame.focus();  
132 - win.callMethod('print'); 147 + Timer(Duration(milliseconds: isSafari ? 500 : 0), () {
  148 + try {
  149 + stopWatch.start();
  150 + js.context.callMethod('${_frameId}_print');
  151 + stopWatch.stop();
133 completer.complete(true); 152 completer.complete(true);
  153 + } catch (e) {
  154 + print(e);
  155 + completer.complete(_getPdf(result));
  156 + }
  157 + });
134 }; 158 };
135 159
136 frame.addEventListener('load', load); 160 frame.addEventListener('load', load);
137 161
138 doc.body!.append(frame); 162 doc.body!.append(frame);
139 - return completer.future; 163 +
  164 + final res = await completer.future;
  165 + // If print() is synchronous
  166 + if (stopWatch.elapsedMilliseconds > 1000) {
  167 + frame.remove();
  168 + script.remove();
  169 + }
  170 + return res;
140 } 171 }
141 172
142 - // All the others  
143 - final pdfFile = html.Blob(  
144 - <Uint8List>[Uint8List.fromList(result)],  
145 - 'application/pdf',  
146 - );  
147 - final pdfUrl = html.Url.createObjectUrl(pdfFile);  
148 - final html.HtmlDocument doc = js.context['document'];  
149 - final link = html.AnchorElement(href: pdfUrl);  
150 - link.target = '_blank';  
151 - doc.body?.append(link);  
152 - link.click();  
153 - link.remove();  
154 - return true; 173 + return _getPdf(result);
155 } 174 }
156 175
157 @override 176 @override
@@ -163,14 +182,22 @@ class PrintingPlugin extends PrintingPlatform { @@ -163,14 +182,22 @@ class PrintingPlugin extends PrintingPlatform {
163 String? body, 182 String? body,
164 List<String>? emails, 183 List<String>? emails,
165 ) async { 184 ) async {
  185 + return _getPdf(bytes, filename: filename);
  186 + }
  187 +
  188 + Future<bool> _getPdf(Uint8List bytes, {String? filename}) async {
166 final pdfFile = html.Blob( 189 final pdfFile = html.Blob(
167 - <Uint8List>[Uint8List.fromList(bytes)], 190 + <Uint8List>[bytes],
168 'application/pdf', 191 'application/pdf',
169 ); 192 );
170 final pdfUrl = html.Url.createObjectUrl(pdfFile); 193 final pdfUrl = html.Url.createObjectUrl(pdfFile);
171 final html.HtmlDocument doc = js.context['document']; 194 final html.HtmlDocument doc = js.context['document'];
172 final link = html.AnchorElement(href: pdfUrl); 195 final link = html.AnchorElement(href: pdfUrl);
  196 + if (filename != null) {
173 link.download = filename; 197 link.download = filename;
  198 + } else {
  199 + link.target = '_blank';
  200 + }
174 doc.body?.append(link); 201 doc.body?.append(link);
175 link.click(); 202 link.click();
176 link.remove(); 203 link.remove();