diff --git a/CHANGELOG.md b/CHANGELOG.md index cfc91f4cb..b7fc825d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ All notable changes to this project will be documented in this file. - rendering of SVG with large header - stopping video playback when changing device orientation on Android >=13 +- printing content orientation according to page format ## [v1.10.9] - 2024-04-14 diff --git a/lib/widgets/viewer/action/printer.dart b/lib/widgets/viewer/action/printer.dart index d2969c953..e8ed87d54 100644 --- a/lib/widgets/viewer/action/printer.dart +++ b/lib/widgets/viewer/action/printer.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:convert'; +import 'dart:math'; import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/extensions/images.dart'; @@ -9,44 +10,65 @@ import 'package:aves/services/common/services.dart'; import 'package:aves/widgets/common/action_mixins/feedback.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:flutter/widgets.dart'; +import 'package:pdf/pdf.dart'; import 'package:pdf/widgets.dart' as pdf; import 'package:printing/printing.dart'; class EntryPrinter with FeedbackMixin { final AvesEntry entry; + static const _fit = pdf.BoxFit.contain; + EntryPrinter(this.entry); Future print(BuildContext context) async { final documentName = entry.bestTitle ?? context.l10n.appName; - final doc = pdf.Document(title: documentName); - final pages = await _buildPages(context); - if (pages.isNotEmpty) { - pages.forEach(doc.addPage); + final imageWidgets = await _buildImageWidgets(context); + if (imageWidgets.isNotEmpty) { unawaited(Printing.layoutPdf( - onLayout: (format) => doc.save(), + onLayout: (pageFormat) { + final doc = pdf.Document(title: documentName); + imageWidgets.map((v) => _buildPdfPage(v, pageFormat)).forEach(doc.addPage); + return doc.save(); + }, name: documentName, )); } } - Future> _buildPages(BuildContext context) async { - final pages = []; - - void _addPdfPage(pdf.Widget? pdfChild) { - if (pdfChild == null) return; - final displaySize = entry.displaySize; - pages.add(pdf.Page( - orientation: displaySize.height > displaySize.width ? pdf.PageOrientation.portrait : pdf.PageOrientation.landscape, - build: (context) => pdf.FullPage( - ignoreMargins: true, - child: pdf.Center( - child: pdfChild, + pdf.Page _buildPdfPage(pdf.Widget imageWidget, PdfPageFormat pageFormat) { + final displaySize = entry.displaySize; + final pageTheme = pdf.PageTheme( + pageFormat: pageFormat, + orientation: displaySize.height > displaySize.width ? pdf.PageOrientation.portrait : pdf.PageOrientation.landscape, + margin: pdf.EdgeInsets.zero, + theme: null, + clip: false, + textDirection: null, + ); + final mustRotate = pageTheme.mustRotate; + final childSize = mustRotate ? Size(displaySize.height, displaySize.width) : displaySize; + return pdf.Page( + pageTheme: pageTheme, + build: (context) => pdf.FullPage( + ignoreMargins: true, + child: pdf.Center( + child: pdf.Transform.scale( + scale: min(pageFormat.availableWidth / childSize.width, pageFormat.availableHeight / childSize.height), + child: pdf.Transform.rotateBox( + angle: pageTheme.mustRotate ? -0.5 * pi : 0, + unconstrained: true, + child: imageWidget, + ), ), ), - )); - } + ), + ); + } + + Future> _buildImageWidgets(BuildContext context) async { + final widgets = []; if (entry.isMultiPage && !entry.isMotionPhoto) { final multiPageInfo = await entry.getMultiPageInfo(); @@ -61,17 +83,25 @@ class EntryPrinter with FeedbackMixin { )); for (var page = 0; page < pageCount; page++) { final pageEntry = multiPageInfo.getPageEntryByIndex(page); - _addPdfPage(await _buildPageImage(pageEntry)); + final widget = await _buildPageImage(pageEntry); + if (widget != null) { + widgets.add(widget); + } streamController.add(pageEntry); } await streamController.close(); } } } - if (pages.isEmpty) { - _addPdfPage(await _buildPageImage(entry)); + + if (widgets.isEmpty) { + final widget = await _buildPageImage(entry); + if (widget != null) { + widgets.add(widget); + } } - return pages; + + return widgets; } Future _buildPageImage(AvesEntry entry) async { @@ -82,10 +112,16 @@ class EntryPrinter with FeedbackMixin { sizeBytes: entry.sizeBytes, ); if (data.isNotEmpty) { - return pdf.SvgImage(svg: utf8.decode(data)); + return pdf.SvgImage( + svg: utf8.decode(data), + fit: _fit, + ); } } else { - return pdf.Image(await flutterImageProvider(entry.uriImage)); + return pdf.Image( + await flutterImageProvider(entry.uriImage), + fit: _fit, + ); } return null; }