Showing
10 changed files
with
435 additions
and
266 deletions
@@ -16,7 +16,8 @@ | @@ -16,7 +16,8 @@ | ||
16 | 16 | ||
17 | export 'src/asset_utils.dart'; | 17 | export 'src/asset_utils.dart'; |
18 | export 'src/callback.dart'; | 18 | export 'src/callback.dart'; |
19 | -export 'src/pdf_preview.dart'; | 19 | +export 'src/preview/pdf_preview.dart'; |
20 | +export 'src/preview/pdf_preview_action.dart'; | ||
20 | export 'src/printer.dart'; | 21 | export 'src/printer.dart'; |
21 | export 'src/printing.dart'; | 22 | export 'src/printing.dart'; |
22 | export 'src/printing_info.dart'; | 23 | export 'src/printing_info.dart'; |
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 | + | ||
1 | import 'dart:async'; | 17 | import 'dart:async'; |
2 | import 'dart:math'; | 18 | import 'dart:math'; |
3 | -import 'dart:typed_data'; | ||
4 | 19 | ||
5 | import 'package:flutter/foundation.dart'; | 20 | import 'package:flutter/foundation.dart'; |
6 | import 'package:flutter/material.dart'; | 21 | import 'package:flutter/material.dart'; |
7 | import 'package:pdf/pdf.dart'; | 22 | import 'package:pdf/pdf.dart'; |
8 | import 'package:pdf/widgets.dart' as pw; | 23 | import 'package:pdf/widgets.dart' as pw; |
9 | 24 | ||
10 | -import 'callback.dart'; | ||
11 | -import 'printing.dart'; | ||
12 | -import 'printing_info.dart'; | ||
13 | -import 'raster.dart'; | 25 | +import '../callback.dart'; |
26 | +import '../printing.dart'; | ||
27 | +import '../printing_info.dart'; | ||
28 | +import 'pdf_preview_action.dart'; | ||
29 | +import 'pdf_preview_raster.dart'; | ||
14 | 30 | ||
15 | /// Flutter widget that uses the rasterized pdf pages to display a document. | 31 | /// Flutter widget that uses the rasterized pdf pages to display a document. |
16 | class PdfPreview extends StatefulWidget { | 32 | class PdfPreview extends StatefulWidget { |
@@ -25,7 +41,7 @@ class PdfPreview extends StatefulWidget { | @@ -25,7 +41,7 @@ class PdfPreview extends StatefulWidget { | ||
25 | this.canChangePageFormat = true, | 41 | this.canChangePageFormat = true, |
26 | this.canChangeOrientation = true, | 42 | this.canChangeOrientation = true, |
27 | this.actions, | 43 | this.actions, |
28 | - this.pageFormats, | 44 | + this.pageFormats = _defaultPageFormats, |
29 | this.onError, | 45 | this.onError, |
30 | this.onPrinted, | 46 | this.onPrinted, |
31 | this.onPrintError, | 47 | this.onPrintError, |
@@ -44,6 +60,11 @@ class PdfPreview extends StatefulWidget { | @@ -44,6 +60,11 @@ class PdfPreview extends StatefulWidget { | ||
44 | this.shouldRepaint = false, | 60 | this.shouldRepaint = false, |
45 | }) : super(key: key); | 61 | }) : super(key: key); |
46 | 62 | ||
63 | + static const _defaultPageFormats = <String, PdfPageFormat>{ | ||
64 | + 'A4': PdfPageFormat.a4, | ||
65 | + 'Letter': PdfPageFormat.letter, | ||
66 | + }; | ||
67 | + | ||
47 | /// Called when a pdf document is needed | 68 | /// Called when a pdf document is needed |
48 | final LayoutCallback build; | 69 | final LayoutCallback build; |
49 | 70 | ||
@@ -72,10 +93,10 @@ class PdfPreview extends StatefulWidget { | @@ -72,10 +93,10 @@ class PdfPreview extends StatefulWidget { | ||
72 | final List<PdfPreviewAction>? actions; | 93 | final List<PdfPreviewAction>? actions; |
73 | 94 | ||
74 | /// List of page formats the user can choose | 95 | /// List of page formats the user can choose |
75 | - final Map<String, PdfPageFormat>? pageFormats; | 96 | + final Map<String, PdfPageFormat> pageFormats; |
76 | 97 | ||
77 | /// Widget to display if the PDF document cannot be displayed | 98 | /// Widget to display if the PDF document cannot be displayed |
78 | - final Widget Function(BuildContext context)? onError; | 99 | + final Widget Function(BuildContext context, Object error)? onError; |
79 | 100 | ||
80 | /// Called if the user prints the pdf document | 101 | /// Called if the user prints the pdf document |
81 | final void Function(BuildContext context)? onPrinted; | 102 | final void Function(BuildContext context)? onPrinted; |
@@ -129,22 +150,40 @@ class PdfPreview extends StatefulWidget { | @@ -129,22 +150,40 @@ class PdfPreview extends StatefulWidget { | ||
129 | _PdfPreviewState createState() => _PdfPreviewState(); | 150 | _PdfPreviewState createState() => _PdfPreviewState(); |
130 | } | 151 | } |
131 | 152 | ||
132 | -class _PdfPreviewState extends State<PdfPreview> { | 153 | +class _PdfPreviewState extends State<PdfPreview> with PdfPreviewRaster { |
133 | final GlobalKey<State<StatefulWidget>> shareWidget = GlobalKey(); | 154 | final GlobalKey<State<StatefulWidget>> shareWidget = GlobalKey(); |
134 | final GlobalKey<State<StatefulWidget>> listView = GlobalKey(); | 155 | final GlobalKey<State<StatefulWidget>> listView = GlobalKey(); |
135 | 156 | ||
136 | - final List<_PdfPreviewPage> pages = <_PdfPreviewPage>[]; | 157 | + PdfPageFormat? _pageFormat; |
137 | 158 | ||
138 | - late PdfPageFormat pageFormat; | 159 | + String get localPageFormat { |
160 | + final locale = WidgetsBinding.instance!.window.locale; | ||
161 | + // ignore: unnecessary_cast | ||
162 | + final cc = (locale as Locale?)?.countryCode ?? 'US'; | ||
139 | 163 | ||
140 | - bool? horizontal; | 164 | + if (cc == 'US' || cc == 'CA' || cc == 'MX') { |
165 | + return 'Letter'; | ||
166 | + } | ||
167 | + return 'A4'; | ||
168 | + } | ||
141 | 169 | ||
142 | - PrintingInfo info = PrintingInfo.unavailable; | ||
143 | - bool infoLoaded = false; | 170 | + @override |
171 | + PdfPageFormat get pageFormat { | ||
172 | + _pageFormat ??= widget.initialPageFormat == null | ||
173 | + ? widget.pageFormats[localPageFormat] | ||
174 | + : _pageFormat = widget.initialPageFormat!; | ||
175 | + | ||
176 | + if (!widget.pageFormats.containsValue(_pageFormat)) { | ||
177 | + _pageFormat = widget.initialPageFormat ?? | ||
178 | + (widget.pageFormats.isNotEmpty | ||
179 | + ? widget.pageFormats.values.first | ||
180 | + : PdfPreview._defaultPageFormats[localPageFormat]); | ||
181 | + } | ||
144 | 182 | ||
145 | - double dpi = 10; | 183 | + return _pageFormat!; |
184 | + } | ||
146 | 185 | ||
147 | - Object? error; | 186 | + bool infoLoaded = false; |
148 | 187 | ||
149 | int? preview; | 188 | int? preview; |
150 | 189 | ||
@@ -158,125 +197,7 @@ class _PdfPreviewState extends State<PdfPreview> { | @@ -158,125 +197,7 @@ class _PdfPreviewState extends State<PdfPreview> { | ||
158 | 197 | ||
159 | Timer? previewUpdate; | 198 | Timer? previewUpdate; |
160 | 199 | ||
161 | - var _rastering = false; | ||
162 | - | ||
163 | - static const defaultPageFormats = <String, PdfPageFormat>{ | ||
164 | - 'A4': PdfPageFormat.a4, | ||
165 | - 'Letter': PdfPageFormat.letter, | ||
166 | - }; | ||
167 | - | ||
168 | - PdfPageFormat get computedPageFormat => horizontal != null | ||
169 | - ? (horizontal! ? pageFormat.landscape : pageFormat.portrait) | ||
170 | - : pageFormat; | ||
171 | - | ||
172 | - Future<void> _raster() async { | ||
173 | - if (_rastering) { | ||
174 | - return; | ||
175 | - } | ||
176 | - _rastering = true; | ||
177 | - | ||
178 | - Uint8List _doc; | ||
179 | - | ||
180 | - if (!info.canRaster) { | ||
181 | - assert(() { | ||
182 | - if (kIsWeb) { | ||
183 | - FlutterError.reportError(FlutterErrorDetails( | ||
184 | - exception: Exception( | ||
185 | - 'Unable to find the `pdf.js` library.\nPlease follow the installation instructions at https://github.com/DavBfr/dart_pdf/tree/master/printing#installing'), | ||
186 | - library: 'printing', | ||
187 | - context: ErrorDescription('while rendering a PDF'), | ||
188 | - )); | ||
189 | - } | ||
190 | - | ||
191 | - return true; | ||
192 | - }()); | ||
193 | - | ||
194 | - _rastering = false; | ||
195 | - return; | ||
196 | - } | ||
197 | - | ||
198 | - try { | ||
199 | - _doc = await widget.build(computedPageFormat); | ||
200 | - } catch (exception, stack) { | ||
201 | - InformationCollector? collector; | ||
202 | - | ||
203 | - assert(() { | ||
204 | - collector = () sync* { | ||
205 | - yield StringProperty('PageFormat', computedPageFormat.toString()); | ||
206 | - }; | ||
207 | - return true; | ||
208 | - }()); | ||
209 | - | ||
210 | - FlutterError.reportError(FlutterErrorDetails( | ||
211 | - exception: exception, | ||
212 | - stack: stack, | ||
213 | - library: 'printing', | ||
214 | - context: ErrorDescription('while generating a PDF'), | ||
215 | - informationCollector: collector, | ||
216 | - )); | ||
217 | - error = exception; | ||
218 | - _rastering = false; | ||
219 | - return; | ||
220 | - } | ||
221 | - | ||
222 | - if (error != null) { | ||
223 | - setState(() { | ||
224 | - error = null; | ||
225 | - }); | ||
226 | - } | ||
227 | - | ||
228 | - try { | ||
229 | - var pageNum = 0; | ||
230 | - await for (final PdfRaster page in Printing.raster( | ||
231 | - _doc, | ||
232 | - dpi: dpi, | ||
233 | - pages: widget.pages, | ||
234 | - )) { | ||
235 | - if (!mounted) { | ||
236 | - _rastering = false; | ||
237 | - return; | ||
238 | - } | ||
239 | - setState(() { | ||
240 | - if (pages.length <= pageNum) { | ||
241 | - pages.add(_PdfPreviewPage( | ||
242 | - page: page, | ||
243 | - pdfPreviewPageDecoration: widget.pdfPreviewPageDecoration, | ||
244 | - pageMargin: widget.previewPageMargin, | ||
245 | - )); | ||
246 | - } else { | ||
247 | - pages[pageNum] = _PdfPreviewPage( | ||
248 | - page: page, | ||
249 | - pdfPreviewPageDecoration: widget.pdfPreviewPageDecoration, | ||
250 | - pageMargin: widget.previewPageMargin, | ||
251 | - ); | ||
252 | - } | ||
253 | - }); | ||
254 | - | ||
255 | - pageNum++; | ||
256 | - pages.removeRange(pageNum, pages.length); | ||
257 | - } | ||
258 | - } catch (exception, stack) { | ||
259 | - InformationCollector? collector; | ||
260 | - | ||
261 | - assert(() { | ||
262 | - collector = () sync* { | ||
263 | - yield StringProperty('PageFormat', computedPageFormat.toString()); | ||
264 | - }; | ||
265 | - return true; | ||
266 | - }()); | ||
267 | - | ||
268 | - FlutterError.reportError(FlutterErrorDetails( | ||
269 | - exception: exception, | ||
270 | - stack: stack, | ||
271 | - library: 'printing', | ||
272 | - context: ErrorDescription('while generating a PDF'), | ||
273 | - informationCollector: collector, | ||
274 | - )); | ||
275 | - error = exception; | ||
276 | - } | ||
277 | - | ||
278 | - _rastering = false; | ||
279 | - } | 200 | + static const _errorMessage = 'Unable to display the document'; |
280 | 201 | ||
281 | @override | 202 | @override |
282 | void initState() { | 203 | void initState() { |
@@ -286,17 +207,17 @@ class _PdfPreviewState extends State<PdfPreview> { | @@ -286,17 +207,17 @@ class _PdfPreviewState extends State<PdfPreview> { | ||
286 | final cc = (locale as Locale?)?.countryCode ?? 'US'; | 207 | final cc = (locale as Locale?)?.countryCode ?? 'US'; |
287 | 208 | ||
288 | if (cc == 'US' || cc == 'CA' || cc == 'MX') { | 209 | if (cc == 'US' || cc == 'CA' || cc == 'MX') { |
289 | - pageFormat = PdfPageFormat.letter; | 210 | + _pageFormat = PdfPageFormat.letter; |
290 | } else { | 211 | } else { |
291 | - pageFormat = PdfPageFormat.a4; | 212 | + _pageFormat = PdfPageFormat.a4; |
292 | } | 213 | } |
293 | } else { | 214 | } else { |
294 | - pageFormat = widget.initialPageFormat!; | 215 | + _pageFormat = widget.initialPageFormat!; |
295 | } | 216 | } |
296 | 217 | ||
297 | - final _pageFormats = widget.pageFormats ?? defaultPageFormats; | 218 | + final _pageFormats = widget.pageFormats; |
298 | if (!_pageFormats.containsValue(pageFormat)) { | 219 | if (!_pageFormats.containsValue(pageFormat)) { |
299 | - pageFormat = _pageFormats.values.first; | 220 | + _pageFormat = _pageFormats.values.first; |
300 | } | 221 | } |
301 | 222 | ||
302 | super.initState(); | 223 | super.initState(); |
@@ -310,16 +231,19 @@ class _PdfPreviewState extends State<PdfPreview> { | @@ -310,16 +231,19 @@ class _PdfPreviewState extends State<PdfPreview> { | ||
310 | 231 | ||
311 | @override | 232 | @override |
312 | void reassemble() { | 233 | void reassemble() { |
313 | - _raster(); | 234 | + raster(); |
314 | super.reassemble(); | 235 | super.reassemble(); |
315 | } | 236 | } |
316 | 237 | ||
317 | @override | 238 | @override |
318 | void didUpdateWidget(covariant PdfPreview oldWidget) { | 239 | void didUpdateWidget(covariant PdfPreview oldWidget) { |
319 | - if (oldWidget.build != widget.build || widget.shouldRepaint) { | 240 | + if (oldWidget.build != widget.build || |
241 | + widget.shouldRepaint || | ||
242 | + widget.pageFormats != oldWidget.pageFormats) { | ||
320 | preview = null; | 243 | preview = null; |
321 | updatePosition = null; | 244 | updatePosition = null; |
322 | - _raster(); | 245 | + |
246 | + raster(); | ||
323 | } | 247 | } |
324 | super.didUpdateWidget(oldWidget); | 248 | super.didUpdateWidget(oldWidget); |
325 | } | 249 | } |
@@ -327,54 +251,35 @@ class _PdfPreviewState extends State<PdfPreview> { | @@ -327,54 +251,35 @@ class _PdfPreviewState extends State<PdfPreview> { | ||
327 | @override | 251 | @override |
328 | void didChangeDependencies() { | 252 | void didChangeDependencies() { |
329 | if (!infoLoaded) { | 253 | if (!infoLoaded) { |
254 | + infoLoaded = true; | ||
330 | Printing.info().then((PrintingInfo _info) { | 255 | Printing.info().then((PrintingInfo _info) { |
331 | setState(() { | 256 | setState(() { |
332 | - infoLoaded = true; | ||
333 | info = _info; | 257 | info = _info; |
258 | + raster(); | ||
334 | }); | 259 | }); |
335 | }); | 260 | }); |
336 | } | 261 | } |
337 | 262 | ||
338 | - previewUpdate?.cancel(); | ||
339 | - previewUpdate = Timer(const Duration(seconds: 1), () { | ||
340 | - final mq = MediaQuery.of(context); | ||
341 | - dpi = (min(mq.size.width - 16, widget.maxPageWidth ?? double.infinity)) * | ||
342 | - mq.devicePixelRatio / | ||
343 | - computedPageFormat.width * | ||
344 | - 72; | ||
345 | - | ||
346 | - _raster(); | ||
347 | - }); | 263 | + raster(); |
348 | super.didChangeDependencies(); | 264 | super.didChangeDependencies(); |
349 | } | 265 | } |
350 | 266 | ||
351 | - Widget _showError() { | 267 | + Widget _showError(Object error) { |
352 | if (widget.onError != null) { | 268 | if (widget.onError != null) { |
353 | - return widget.onError!(context); | 269 | + return widget.onError!(context, error); |
354 | } | 270 | } |
355 | 271 | ||
356 | - return const Center( | ||
357 | - child: Text( | ||
358 | - 'Unable to display the document', | ||
359 | - style: TextStyle( | ||
360 | - fontSize: 20, | ||
361 | - ), | ||
362 | - ), | ||
363 | - ); | 272 | + return ErrorWidget(error); |
364 | } | 273 | } |
365 | 274 | ||
366 | Widget _createPreview() { | 275 | Widget _createPreview() { |
367 | if (error != null) { | 276 | if (error != null) { |
368 | - var content = _showError(); | ||
369 | - assert(() { | ||
370 | - content = ErrorWidget(error!); | ||
371 | - return true; | ||
372 | - }()); | ||
373 | - return content; | 277 | + return _showError(error!); |
374 | } | 278 | } |
375 | 279 | ||
376 | - if (!info.canRaster) { | ||
377 | - return _showError(); | 280 | + final _info = info; |
281 | + if (_info != null && !_info.canRaster) { | ||
282 | + return _showError(_errorMessage); | ||
378 | } | 283 | } |
379 | 284 | ||
380 | if (pages.isEmpty) { | 285 | if (pages.isEmpty) { |
@@ -454,7 +359,7 @@ class _PdfPreviewState extends State<PdfPreview> { | @@ -454,7 +359,7 @@ class _PdfPreviewState extends State<PdfPreview> { | ||
454 | 359 | ||
455 | final actions = <Widget>[]; | 360 | final actions = <Widget>[]; |
456 | 361 | ||
457 | - if (widget.allowPrinting && info.canPrint) { | 362 | + if (widget.allowPrinting && info?.canPrint == true) { |
458 | actions.add( | 363 | actions.add( |
459 | IconButton( | 364 | IconButton( |
460 | icon: const Icon(Icons.print), | 365 | icon: const Icon(Icons.print), |
@@ -463,7 +368,7 @@ class _PdfPreviewState extends State<PdfPreview> { | @@ -463,7 +368,7 @@ class _PdfPreviewState extends State<PdfPreview> { | ||
463 | ); | 368 | ); |
464 | } | 369 | } |
465 | 370 | ||
466 | - if (widget.allowSharing && info.canShare) { | 371 | + if (widget.allowSharing && info?.canShare == true) { |
467 | actions.add( | 372 | actions.add( |
468 | IconButton( | 373 | IconButton( |
469 | key: shareWidget, | 374 | key: shareWidget, |
@@ -474,8 +379,7 @@ class _PdfPreviewState extends State<PdfPreview> { | @@ -474,8 +379,7 @@ class _PdfPreviewState extends State<PdfPreview> { | ||
474 | } | 379 | } |
475 | 380 | ||
476 | if (widget.canChangePageFormat) { | 381 | if (widget.canChangePageFormat) { |
477 | - final _pageFormats = widget.pageFormats ?? defaultPageFormats; | ||
478 | - final keys = _pageFormats.keys.toList(); | 382 | + final keys = widget.pageFormats.keys.toList(); |
479 | actions.add( | 383 | actions.add( |
480 | DropdownButton<PdfPageFormat>( | 384 | DropdownButton<PdfPageFormat>( |
481 | dropdownColor: theme.primaryColor, | 385 | dropdownColor: theme.primaryColor, |
@@ -485,21 +389,21 @@ class _PdfPreviewState extends State<PdfPreview> { | @@ -485,21 +389,21 @@ class _PdfPreviewState extends State<PdfPreview> { | ||
485 | ), | 389 | ), |
486 | value: pageFormat, | 390 | value: pageFormat, |
487 | items: List<DropdownMenuItem<PdfPageFormat>>.generate( | 391 | items: List<DropdownMenuItem<PdfPageFormat>>.generate( |
488 | - _pageFormats.length, | 392 | + widget.pageFormats.length, |
489 | (int index) { | 393 | (int index) { |
490 | final key = keys[index]; | 394 | final key = keys[index]; |
491 | - final val = _pageFormats[key]; | 395 | + final val = widget.pageFormats[key]; |
492 | return DropdownMenuItem<PdfPageFormat>( | 396 | return DropdownMenuItem<PdfPageFormat>( |
493 | value: val, | 397 | value: val, |
494 | child: Text(key, style: TextStyle(color: iconColor)), | 398 | child: Text(key, style: TextStyle(color: iconColor)), |
495 | ); | 399 | ); |
496 | }, | 400 | }, |
497 | ), | 401 | ), |
498 | - onChanged: (PdfPageFormat? _pageFormat) { | 402 | + onChanged: (PdfPageFormat? pageFormat) { |
499 | setState(() { | 403 | setState(() { |
500 | - if (_pageFormat != null) { | ||
501 | - pageFormat = _pageFormat; | ||
502 | - _raster(); | 404 | + if (pageFormat != null) { |
405 | + _pageFormat = pageFormat; | ||
406 | + raster(); | ||
503 | } | 407 | } |
504 | }); | 408 | }); |
505 | }, | 409 | }, |
@@ -520,7 +424,7 @@ class _PdfPreviewState extends State<PdfPreview> { | @@ -520,7 +424,7 @@ class _PdfPreviewState extends State<PdfPreview> { | ||
520 | onPressed: (int index) { | 424 | onPressed: (int index) { |
521 | setState(() { | 425 | setState(() { |
522 | horizontal = index == 1; | 426 | horizontal = index == 1; |
523 | - _raster(); | 427 | + raster(); |
524 | }); | 428 | }); |
525 | }, | 429 | }, |
526 | isSelected: <bool>[horizontal == false, horizontal == true], | 430 | isSelected: <bool>[horizontal == false, horizontal == true], |
@@ -561,7 +465,7 @@ class _PdfPreviewState extends State<PdfPreview> { | @@ -561,7 +465,7 @@ class _PdfPreviewState extends State<PdfPreview> { | ||
561 | setState( | 465 | setState( |
562 | () { | 466 | () { |
563 | pw.Document.debug = value; | 467 | pw.Document.debug = value; |
564 | - _raster(); | 468 | + raster(); |
565 | }, | 469 | }, |
566 | ); | 470 | ); |
567 | }, | 471 | }, |
@@ -624,9 +528,26 @@ class _PdfPreviewState extends State<PdfPreview> { | @@ -624,9 +528,26 @@ class _PdfPreviewState extends State<PdfPreview> { | ||
624 | if (result && widget.onPrinted != null) { | 528 | if (result && widget.onPrinted != null) { |
625 | widget.onPrinted!(context); | 529 | widget.onPrinted!(context); |
626 | } | 530 | } |
627 | - } catch (e) { | 531 | + } catch (exception, stack) { |
532 | + InformationCollector? collector; | ||
533 | + | ||
534 | + assert(() { | ||
535 | + collector = () sync* { | ||
536 | + yield StringProperty('PageFormat', computedPageFormat.toString()); | ||
537 | + }; | ||
538 | + return true; | ||
539 | + }()); | ||
540 | + | ||
541 | + FlutterError.reportError(FlutterErrorDetails( | ||
542 | + exception: exception, | ||
543 | + stack: stack, | ||
544 | + library: 'printing', | ||
545 | + context: ErrorDescription('while printing a PDF'), | ||
546 | + informationCollector: collector, | ||
547 | + )); | ||
548 | + | ||
628 | if (widget.onPrintError != null) { | 549 | if (widget.onPrintError != null) { |
629 | - widget.onPrintError!(context, e); | 550 | + widget.onPrintError!(context, exception); |
630 | } | 551 | } |
631 | } | 552 | } |
632 | } | 553 | } |
@@ -656,76 +577,3 @@ class _PdfPreviewState extends State<PdfPreview> { | @@ -656,76 +577,3 @@ class _PdfPreviewState extends State<PdfPreview> { | ||
656 | } | 577 | } |
657 | } | 578 | } |
658 | } | 579 | } |
659 | - | ||
660 | -class _PdfPreviewPage extends StatelessWidget { | ||
661 | - const _PdfPreviewPage({ | ||
662 | - Key? key, | ||
663 | - this.page, | ||
664 | - this.pdfPreviewPageDecoration, | ||
665 | - this.pageMargin, | ||
666 | - }) : super(key: key); | ||
667 | - | ||
668 | - final PdfRaster? page; | ||
669 | - final Decoration? pdfPreviewPageDecoration; | ||
670 | - final EdgeInsets? pageMargin; | ||
671 | - | ||
672 | - @override | ||
673 | - Widget build(BuildContext context) { | ||
674 | - final im = PdfRasterImage(page!); | ||
675 | - final scrollbarTrack = Theme.of(context) | ||
676 | - .scrollbarTheme | ||
677 | - .thickness | ||
678 | - ?.resolve({MaterialState.hovered}) ?? | ||
679 | - 12; | ||
680 | - | ||
681 | - return Container( | ||
682 | - margin: pageMargin ?? | ||
683 | - EdgeInsets.only( | ||
684 | - left: 8 + scrollbarTrack, | ||
685 | - top: 8, | ||
686 | - right: 8 + scrollbarTrack, | ||
687 | - bottom: 12, | ||
688 | - ), | ||
689 | - decoration: pdfPreviewPageDecoration ?? | ||
690 | - const BoxDecoration( | ||
691 | - color: Colors.white, | ||
692 | - boxShadow: <BoxShadow>[ | ||
693 | - BoxShadow( | ||
694 | - offset: Offset(0, 3), | ||
695 | - blurRadius: 5, | ||
696 | - color: Color(0xFF000000), | ||
697 | - ), | ||
698 | - ], | ||
699 | - ), | ||
700 | - child: AspectRatio( | ||
701 | - aspectRatio: page!.width / page!.height, | ||
702 | - child: Image( | ||
703 | - image: im, | ||
704 | - fit: BoxFit.cover, | ||
705 | - ), | ||
706 | - ), | ||
707 | - ); | ||
708 | - } | ||
709 | -} | ||
710 | - | ||
711 | -/// Action callback | ||
712 | -typedef OnPdfPreviewActionPressed = void Function( | ||
713 | - BuildContext context, | ||
714 | - LayoutCallback build, | ||
715 | - PdfPageFormat pageFormat, | ||
716 | -); | ||
717 | - | ||
718 | -/// Action to add the the [PdfPreview] widget | ||
719 | -class PdfPreviewAction { | ||
720 | - /// Represents an icon to add to [PdfPreview] | ||
721 | - const PdfPreviewAction({ | ||
722 | - required this.icon, | ||
723 | - required this.onPressed, | ||
724 | - }); | ||
725 | - | ||
726 | - /// The icon to display | ||
727 | - final Icon icon; | ||
728 | - | ||
729 | - /// The callback called when the user tap on the icon | ||
730 | - final OnPdfPreviewActionPressed? onPressed; | ||
731 | -} |
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 | +import 'package:flutter/material.dart'; | ||
18 | +import 'package:pdf/pdf.dart'; | ||
19 | + | ||
20 | +import '../callback.dart'; | ||
21 | + | ||
22 | +/// Base Action callback | ||
23 | +typedef OnPdfPreviewActionPressed = void Function( | ||
24 | + BuildContext context, | ||
25 | + LayoutCallback build, | ||
26 | + PdfPageFormat pageFormat, | ||
27 | +); | ||
28 | + | ||
29 | +/// Action to add the the [PdfPreview] widget | ||
30 | +class PdfPreviewAction { | ||
31 | + /// Represents an icon to add to [PdfPreview] | ||
32 | + const PdfPreviewAction({ | ||
33 | + required this.icon, | ||
34 | + required this.onPressed, | ||
35 | + }); | ||
36 | + | ||
37 | + /// The icon to display | ||
38 | + final Icon icon; | ||
39 | + | ||
40 | + /// The callback called when the user tap on the icon | ||
41 | + final OnPdfPreviewActionPressed? onPressed; | ||
42 | +} |
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 | +import 'package:flutter/foundation.dart'; | ||
18 | +import 'package:flutter/material.dart'; | ||
19 | + | ||
20 | +import '../raster.dart'; | ||
21 | + | ||
22 | +/// Represents one PDF page | ||
23 | +class PdfPreviewPage extends StatelessWidget { | ||
24 | + /// Create a PDF page widget | ||
25 | + const PdfPreviewPage({ | ||
26 | + Key? key, | ||
27 | + this.page, | ||
28 | + this.pdfPreviewPageDecoration, | ||
29 | + this.pageMargin, | ||
30 | + }) : super(key: key); | ||
31 | + | ||
32 | + /// Image representing the content of the page | ||
33 | + final PdfRaster? page; | ||
34 | + | ||
35 | + /// Decoration around the page | ||
36 | + final Decoration? pdfPreviewPageDecoration; | ||
37 | + | ||
38 | + /// Margin | ||
39 | + final EdgeInsets? pageMargin; | ||
40 | + | ||
41 | + @override | ||
42 | + Widget build(BuildContext context) { | ||
43 | + final im = PdfRasterImage(page!); | ||
44 | + final scrollbarTrack = Theme.of(context) | ||
45 | + .scrollbarTheme | ||
46 | + .thickness | ||
47 | + ?.resolve({MaterialState.hovered}) ?? | ||
48 | + 12; | ||
49 | + | ||
50 | + return Container( | ||
51 | + margin: pageMargin ?? | ||
52 | + EdgeInsets.only( | ||
53 | + left: 8 + scrollbarTrack, | ||
54 | + top: 8, | ||
55 | + right: 8 + scrollbarTrack, | ||
56 | + bottom: 12, | ||
57 | + ), | ||
58 | + decoration: pdfPreviewPageDecoration ?? | ||
59 | + const BoxDecoration( | ||
60 | + color: Colors.white, | ||
61 | + boxShadow: <BoxShadow>[ | ||
62 | + BoxShadow( | ||
63 | + offset: Offset(0, 3), | ||
64 | + blurRadius: 5, | ||
65 | + color: Color(0xFF000000), | ||
66 | + ), | ||
67 | + ], | ||
68 | + ), | ||
69 | + child: AspectRatio( | ||
70 | + aspectRatio: page!.width / page!.height, | ||
71 | + child: Image( | ||
72 | + image: im, | ||
73 | + fit: BoxFit.cover, | ||
74 | + ), | ||
75 | + ), | ||
76 | + ); | ||
77 | + } | ||
78 | +} |
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 | +import 'dart:async'; | ||
18 | +import 'dart:math'; | ||
19 | +import 'dart:typed_data'; | ||
20 | + | ||
21 | +import 'package:flutter/foundation.dart'; | ||
22 | +import 'package:flutter/material.dart'; | ||
23 | +import 'package:pdf/pdf.dart'; | ||
24 | + | ||
25 | +import '../printing.dart'; | ||
26 | +import '../printing_info.dart'; | ||
27 | +import '../raster.dart'; | ||
28 | +import 'pdf_preview.dart'; | ||
29 | +import 'pdf_preview_page.dart'; | ||
30 | + | ||
31 | +/// Raster PDF documents | ||
32 | +mixin PdfPreviewRaster on State<PdfPreview> { | ||
33 | + static const _updateTime = Duration(milliseconds: 300); | ||
34 | + | ||
35 | + /// Configured page format | ||
36 | + PdfPageFormat get pageFormat; | ||
37 | + | ||
38 | + /// Is the print horizontal | ||
39 | + bool? horizontal; | ||
40 | + | ||
41 | + /// Resulting pages | ||
42 | + final List<PdfPreviewPage> pages = <PdfPreviewPage>[]; | ||
43 | + | ||
44 | + /// Printing subsystem information | ||
45 | + PrintingInfo? info; | ||
46 | + | ||
47 | + /// Error message | ||
48 | + Object? error; | ||
49 | + | ||
50 | + /// Dots per inch | ||
51 | + double dpi = 10; | ||
52 | + | ||
53 | + var _rastering = false; | ||
54 | + | ||
55 | + Timer? _previewUpdate; | ||
56 | + | ||
57 | + @override | ||
58 | + void dispose() { | ||
59 | + _previewUpdate?.cancel(); | ||
60 | + super.dispose(); | ||
61 | + } | ||
62 | + | ||
63 | + /// Computed page format | ||
64 | + PdfPageFormat get computedPageFormat => horizontal != null | ||
65 | + ? (horizontal! ? pageFormat.landscape : pageFormat.portrait) | ||
66 | + : pageFormat; | ||
67 | + | ||
68 | + /// Rasterize the document | ||
69 | + void raster() { | ||
70 | + _previewUpdate?.cancel(); | ||
71 | + _previewUpdate = Timer(_updateTime, () { | ||
72 | + final mq = MediaQuery.of(context); | ||
73 | + dpi = (min(mq.size.width - 16, widget.maxPageWidth ?? double.infinity)) * | ||
74 | + mq.devicePixelRatio / | ||
75 | + computedPageFormat.width * | ||
76 | + 72; | ||
77 | + | ||
78 | + _raster(); | ||
79 | + }); | ||
80 | + } | ||
81 | + | ||
82 | + Future<void> _raster() async { | ||
83 | + if (_rastering) { | ||
84 | + return; | ||
85 | + } | ||
86 | + _rastering = true; | ||
87 | + | ||
88 | + Uint8List _doc; | ||
89 | + | ||
90 | + final _info = info; | ||
91 | + if (_info != null && !_info.canRaster) { | ||
92 | + assert(() { | ||
93 | + if (kIsWeb) { | ||
94 | + FlutterError.reportError(FlutterErrorDetails( | ||
95 | + exception: Exception( | ||
96 | + 'Unable to find the `pdf.js` library.\nPlease follow the installation instructions at https://github.com/DavBfr/dart_pdf/tree/master/printing#installing'), | ||
97 | + library: 'printing', | ||
98 | + context: ErrorDescription('while rendering a PDF'), | ||
99 | + )); | ||
100 | + } | ||
101 | + | ||
102 | + return true; | ||
103 | + }()); | ||
104 | + | ||
105 | + _rastering = false; | ||
106 | + return; | ||
107 | + } | ||
108 | + | ||
109 | + try { | ||
110 | + _doc = await widget.build(computedPageFormat); | ||
111 | + } catch (exception, stack) { | ||
112 | + InformationCollector? collector; | ||
113 | + | ||
114 | + assert(() { | ||
115 | + collector = () sync* { | ||
116 | + yield StringProperty('PageFormat', computedPageFormat.toString()); | ||
117 | + }; | ||
118 | + return true; | ||
119 | + }()); | ||
120 | + | ||
121 | + FlutterError.reportError(FlutterErrorDetails( | ||
122 | + exception: exception, | ||
123 | + stack: stack, | ||
124 | + library: 'printing', | ||
125 | + context: ErrorDescription('while generating a PDF'), | ||
126 | + informationCollector: collector, | ||
127 | + )); | ||
128 | + setState(() { | ||
129 | + error = exception; | ||
130 | + _rastering = false; | ||
131 | + }); | ||
132 | + return; | ||
133 | + } | ||
134 | + | ||
135 | + if (error != null) { | ||
136 | + setState(() { | ||
137 | + error = null; | ||
138 | + }); | ||
139 | + } | ||
140 | + | ||
141 | + try { | ||
142 | + var pageNum = 0; | ||
143 | + await for (final PdfRaster page in Printing.raster( | ||
144 | + _doc, | ||
145 | + dpi: dpi, | ||
146 | + pages: widget.pages, | ||
147 | + )) { | ||
148 | + if (!mounted) { | ||
149 | + _rastering = false; | ||
150 | + return; | ||
151 | + } | ||
152 | + setState(() { | ||
153 | + if (pages.length <= pageNum) { | ||
154 | + pages.add(PdfPreviewPage( | ||
155 | + page: page, | ||
156 | + pdfPreviewPageDecoration: widget.pdfPreviewPageDecoration, | ||
157 | + pageMargin: widget.previewPageMargin, | ||
158 | + )); | ||
159 | + } else { | ||
160 | + pages[pageNum] = PdfPreviewPage( | ||
161 | + page: page, | ||
162 | + pdfPreviewPageDecoration: widget.pdfPreviewPageDecoration, | ||
163 | + pageMargin: widget.previewPageMargin, | ||
164 | + ); | ||
165 | + } | ||
166 | + }); | ||
167 | + | ||
168 | + pageNum++; | ||
169 | + } | ||
170 | + | ||
171 | + pages.removeRange(pageNum, pages.length); | ||
172 | + } catch (exception, stack) { | ||
173 | + InformationCollector? collector; | ||
174 | + | ||
175 | + assert(() { | ||
176 | + collector = () sync* { | ||
177 | + yield StringProperty('PageFormat', computedPageFormat.toString()); | ||
178 | + }; | ||
179 | + return true; | ||
180 | + }()); | ||
181 | + | ||
182 | + FlutterError.reportError(FlutterErrorDetails( | ||
183 | + exception: exception, | ||
184 | + stack: stack, | ||
185 | + library: 'printing', | ||
186 | + context: ErrorDescription('while rastering a PDF'), | ||
187 | + informationCollector: collector, | ||
188 | + )); | ||
189 | + | ||
190 | + setState(() { | ||
191 | + error = exception; | ||
192 | + }); | ||
193 | + } | ||
194 | + | ||
195 | + _rastering = false; | ||
196 | + } | ||
197 | +} |
@@ -7,7 +7,7 @@ description: > | @@ -7,7 +7,7 @@ description: > | ||
7 | homepage: https://github.com/DavBfr/dart_pdf/tree/master/printing | 7 | homepage: https://github.com/DavBfr/dart_pdf/tree/master/printing |
8 | repository: https://github.com/DavBfr/dart_pdf | 8 | repository: https://github.com/DavBfr/dart_pdf |
9 | issue_tracker: https://github.com/DavBfr/dart_pdf/issues | 9 | issue_tracker: https://github.com/DavBfr/dart_pdf/issues |
10 | -version: 5.2.2 | 10 | +version: 5.3.0 |
11 | 11 | ||
12 | environment: | 12 | environment: |
13 | sdk: ">=2.12.0-0 <3.0.0" | 13 | sdk: ">=2.12.0-0 <3.0.0" |
@@ -347,7 +347,7 @@ void PrintJob::rasterPdf(std::vector<uint8_t> data, | @@ -347,7 +347,7 @@ void PrintJob::rasterPdf(std::vector<uint8_t> data, | ||
347 | 347 | ||
348 | FPDF_DestroyLibrary(); | 348 | FPDF_DestroyLibrary(); |
349 | 349 | ||
350 | - printing->onPageRasterEnd(this, nullptr); | 350 | + printing->onPageRasterEnd(this, ""); |
351 | } | 351 | } |
352 | 352 | ||
353 | std::map<std::string, bool> PrintJob::printingInfo() { | 353 | std::map<std::string, bool> PrintJob::printingInfo() { |
@@ -44,7 +44,7 @@ void Printing::onPageRasterized(std::vector<uint8_t> data, | @@ -44,7 +44,7 @@ void Printing::onPageRasterized(std::vector<uint8_t> data, | ||
44 | }))); | 44 | }))); |
45 | } | 45 | } |
46 | 46 | ||
47 | -void Printing::onPageRasterEnd(PrintJob* job, const char* error) { | 47 | +void Printing::onPageRasterEnd(PrintJob* job, const std::string error) { |
48 | auto map = flutter::EncodableMap{ | 48 | auto map = flutter::EncodableMap{ |
49 | {flutter::EncodableValue("job"), flutter::EncodableValue(job->id())}, | 49 | {flutter::EncodableValue("job"), flutter::EncodableValue(job->id())}, |
50 | }; | 50 | }; |
@@ -40,7 +40,7 @@ class Printing { | @@ -40,7 +40,7 @@ class Printing { | ||
40 | int height, | 40 | int height, |
41 | PrintJob* job); | 41 | PrintJob* job); |
42 | 42 | ||
43 | - void onPageRasterEnd(PrintJob* job, const char* error); | 43 | + void onPageRasterEnd(PrintJob* job, const std::string error); |
44 | 44 | ||
45 | void onLayout(PrintJob* job, | 45 | void onLayout(PrintJob* job, |
46 | double pageWidth, | 46 | double pageWidth, |
@@ -50,7 +50,9 @@ class Printing { | @@ -50,7 +50,9 @@ class Printing { | ||
50 | double marginRight, | 50 | double marginRight, |
51 | double marginBottom); | 51 | double marginBottom); |
52 | 52 | ||
53 | - void Printing::onCompleted(PrintJob* job, bool completed, std::string error); | 53 | + void Printing::onCompleted(PrintJob* job, |
54 | + bool completed, | ||
55 | + const std::string error); | ||
54 | }; | 56 | }; |
55 | 57 | ||
56 | } // namespace nfet | 58 | } // namespace nfet |
-
Please register or login to post a comment