David PHAM-VAN

Add WidgetWraper.fromWidget()

1 # Changelog 1 # Changelog
2 2
3 -## 5.5.1 3 +## 5.6.0
4 4
5 - Update Google fonts 5 - Update Google fonts
6 - Fix typo in README 6 - Fix typo in README
@@ -9,6 +9,7 @@ @@ -9,6 +9,7 @@
9 - Fix error while loading shared libraries on Linux 9 - Fix error while loading shared libraries on Linux
10 - Update pdfium library to 4627 10 - Update pdfium library to 4627
11 - Apply Flutter 2.5 coding style 11 - Apply Flutter 2.5 coding style
  12 +- Add WidgetWraper.fromWidget()
12 13
13 ## 5.5.0 14 ## 5.5.0
14 15
@@ -20,6 +20,7 @@ import 'dart:ui' as ui; @@ -20,6 +20,7 @@ import 'dart:ui' as ui;
20 20
21 import 'package:flutter/material.dart'; 21 import 'package:flutter/material.dart';
22 import 'package:flutter/rendering.dart'; 22 import 'package:flutter/rendering.dart';
  23 +import 'package:flutter/widgets.dart';
23 import 'package:pdf/pdf.dart'; 24 import 'package:pdf/pdf.dart';
24 import 'package:pdf/widgets.dart' as pw; 25 import 'package:pdf/widgets.dart' as pw;
25 26
@@ -100,6 +101,118 @@ class WidgetWraper extends pw.ImageProvider { @@ -100,6 +101,118 @@ class WidgetWraper extends pw.ImageProvider {
100 ); 101 );
101 } 102 }
102 103
  104 + /// Wrap a Flutter Widget to an ImageProvider.
  105 + ///
  106 + /// ```
  107 + /// final wrapped = await WidgetWraper.fromWidget(
  108 + /// widget: Container(
  109 + /// color: Colors.white,
  110 + /// child: Text(
  111 + /// 'Hello world !',
  112 + /// style: TextStyle(color: Colors.amber),
  113 + /// ),
  114 + /// ),
  115 + /// constraints: BoxConstraints(maxWidth: 100, maxHeight: 400),
  116 + /// pixelRatio: 3,
  117 + /// );
  118 + ///
  119 + /// pdf.addPage(
  120 + /// pw.Page(
  121 + /// pageFormat: format,
  122 + /// build: (context) {
  123 + /// return pw.Image(wrapped, width: 100);
  124 + /// },
  125 + /// ),
  126 + /// );
  127 + /// ```
  128 + static Future<WidgetWraper> fromWidget({
  129 + required Widget widget,
  130 + required BoxConstraints constraints,
  131 + double pixelRatio = 1.0,
  132 + PdfImageOrientation? orientation,
  133 + double? dpi,
  134 + }) async {
  135 + assert(pixelRatio > 0);
  136 +
  137 + if (!constraints.hasBoundedHeight || !constraints.hasBoundedHeight) {
  138 + throw Exception(
  139 + 'Unable to convert an unbounded widget. Add maxWidth and maxHeight to the constraints.');
  140 + }
  141 +
  142 + widget = ConstrainedBox(
  143 + constraints: constraints,
  144 + child: widget,
  145 + );
  146 +
  147 + final _properties = DiagnosticPropertiesBuilder();
  148 + widget.debugFillProperties(_properties);
  149 +
  150 + if (_properties.properties.isEmpty) {
  151 + throw ErrorDescription('Unable to get the widget properties');
  152 + }
  153 +
  154 + final _constraints = _properties.properties
  155 + .whereType<DiagnosticsProperty<BoxConstraints>>()
  156 + .first
  157 + .value;
  158 +
  159 + if (_constraints == null ||
  160 + !_constraints.hasBoundedWidth ||
  161 + !_constraints.hasBoundedWidth) {
  162 + throw Exception('Unable to convert an unbounded widget.');
  163 + }
  164 +
  165 + final _repaintBoundary = RenderRepaintBoundary();
  166 +
  167 + final renderView = RenderView(
  168 + child: RenderPositionedBox(
  169 + alignment: Alignment.center, child: _repaintBoundary),
  170 + configuration: ViewConfiguration(
  171 + size: Size(_constraints.maxWidth, _constraints.maxHeight),
  172 + devicePixelRatio: ui.window.devicePixelRatio),
  173 + window: _FlutterView(
  174 + configuration: ui.ViewConfiguration(
  175 + devicePixelRatio: ui.window.devicePixelRatio,
  176 + ),
  177 + ),
  178 + );
  179 +
  180 + final pipelineOwner = PipelineOwner()..rootNode = renderView;
  181 + renderView.prepareInitialFrame();
  182 +
  183 + final buildOwner = BuildOwner(focusManager: FocusManager());
  184 + final _rootElement = RenderObjectToWidgetAdapter<RenderBox>(
  185 + container: _repaintBoundary,
  186 + child: Directionality(
  187 + textDirection: TextDirection.ltr,
  188 + child: IntrinsicHeight(child: IntrinsicWidth(child: widget)),
  189 + ),
  190 + ).attachToRenderTree(buildOwner);
  191 +
  192 + buildOwner
  193 + ..buildScope(_rootElement)
  194 + ..finalizeTree();
  195 +
  196 + pipelineOwner
  197 + ..flushLayout()
  198 + ..flushCompositingBits()
  199 + ..flushPaint();
  200 +
  201 + final image = await _repaintBoundary.toImage(pixelRatio: pixelRatio);
  202 + final bytes = await image.toByteData(format: ui.ImageByteFormat.rawRgba);
  203 + if (bytes == null) {
  204 + throw Exception('Unable to read image data');
  205 + }
  206 +
  207 + return WidgetWraper._(
  208 + bytes.buffer.asUint8List(),
  209 + image.width,
  210 + image.height,
  211 + orientation ?? PdfImageOrientation.topLeft,
  212 + dpi,
  213 + );
  214 + }
  215 +
103 /// The image data 216 /// The image data
104 final Uint8List bytes; 217 final Uint8List bytes;
105 218
@@ -114,3 +227,16 @@ class WidgetWraper extends pw.ImageProvider { @@ -114,3 +227,16 @@ class WidgetWraper extends pw.ImageProvider {
114 ); 227 );
115 } 228 }
116 } 229 }
  230 +
  231 +class _FlutterView extends ui.FlutterView {
  232 + _FlutterView({required this.configuration});
  233 +
  234 + final ui.ViewConfiguration configuration;
  235 +
  236 + @override
  237 + ui.PlatformDispatcher get platformDispatcher =>
  238 + ui.PlatformDispatcher.instance;
  239 +
  240 + @override
  241 + ui.ViewConfiguration get viewConfiguration => configuration;
  242 +}
@@ -6,7 +6,7 @@ description: > @@ -6,7 +6,7 @@ description: >
6 homepage: https://github.com/DavBfr/dart_pdf/tree/master/printing 6 homepage: https://github.com/DavBfr/dart_pdf/tree/master/printing
7 repository: https://github.com/DavBfr/dart_pdf 7 repository: https://github.com/DavBfr/dart_pdf
8 issue_tracker: https://github.com/DavBfr/dart_pdf/issues 8 issue_tracker: https://github.com/DavBfr/dart_pdf/issues
9 -version: 5.5.1 9 +version: 5.6.0
10 10
11 environment: 11 environment:
12 sdk: ">=2.12.0 <3.0.0" 12 sdk: ">=2.12.0 <3.0.0"