aves/lib/widgets/viewer/action/printer.dart
2025-04-03 22:58:23 +02:00

130 lines
3.9 KiB
Dart

import 'dart:async';
import 'dart:convert';
import 'dart:math';
import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/entry/extensions/images.dart';
import 'package:aves/model/entry/extensions/multipage.dart';
import 'package:aves/model/entry/extensions/props.dart';
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<void> print(BuildContext context) async {
final documentName = entry.bestTitle ?? context.l10n.appName;
final imageWidgets = await _buildImageWidgets(context);
if (imageWidgets.isNotEmpty) {
unawaited(Printing.layoutPdf(
onLayout: (pageFormat) {
final doc = pdf.Document(title: documentName);
imageWidgets.map((v) => _buildPdfPage(v, pageFormat)).forEach(doc.addPage);
return doc.save();
},
name: documentName,
));
}
}
pdf.Page _buildPdfPage(pdf.Widget imageWidget, PdfPageFormat pageFormat) {
final displaySize = entry.displaySize;
final pageTheme = pdf.PageTheme(
pageFormat: pageFormat,
orientation: displaySize.aspectRatio < 1 ? pdf.PageOrientation.portrait : pdf.PageOrientation.landscape,
margin: pdf.EdgeInsets.zero,
theme: null,
clip: false,
textDirection: null,
);
return pdf.Page(
pageTheme: pageTheme,
build: (context) => pdf.FullPage(
ignoreMargins: true,
child: pdf.Center(
child: pdf.Transform.scale(
scale: min(pageFormat.availableWidth / displaySize.width, pageFormat.availableHeight / displaySize.height),
child: pdf.Transform.rotateBox(
angle: 0,
unconstrained: true,
child: imageWidget,
),
),
),
),
);
}
Future<List<pdf.Widget>> _buildImageWidgets(BuildContext context) async {
final widgets = <pdf.Widget>[];
if (entry.isMultiPage && !entry.isMotionPhoto) {
final multiPageInfo = await entry.getMultiPageInfo();
if (multiPageInfo != null) {
final pageCount = multiPageInfo.pageCount;
if (pageCount > 1) {
final streamController = StreamController<AvesEntry>.broadcast();
unawaited(showOpReport<AvesEntry>(
context: context,
opStream: streamController.stream,
itemCount: pageCount,
));
for (var page = 0; page < pageCount; page++) {
final pageEntry = multiPageInfo.getPageEntryByIndex(page);
final widget = await _buildPageImage(pageEntry);
if (widget != null) {
widgets.add(widget);
}
streamController.add(pageEntry);
}
await streamController.close();
}
}
}
if (widgets.isEmpty) {
final widget = await _buildPageImage(entry);
if (widget != null) {
widgets.add(widget);
}
}
return widgets;
}
Future<pdf.Widget?> _buildPageImage(AvesEntry entry) async {
try {
if (entry.isSvg) {
final data = await mediaFetchService.getSvg(
entry.uri,
entry.mimeType,
sizeBytes: entry.sizeBytes,
);
if (data.isNotEmpty) {
return pdf.SvgImage(
svg: utf8.decode(data),
fit: _fit,
);
}
} else {
return pdf.Image(
await flutterImageProvider(entry.fullImage),
fit: _fit,
);
}
} catch (error) {
debugPrint('failed to load image for entry=$entry, error=$error');
}
return null;
}
}