David PHAM-VAN

Implement ImageProvider

@@ -91,7 +91,7 @@ class Invoice { @@ -91,7 +91,7 @@ class Invoice {
91 91
92 double get _grandTotal => _total * (1 + tax); 92 double get _grandTotal => _total * (1 + tax);
93 93
94 - PdfImage _logo; 94 + pw.MemoryImage _logo;
95 95
96 Future<Uint8List> buildPdf(PdfPageFormat pageFormat) async { 96 Future<Uint8List> buildPdf(PdfPageFormat pageFormat) async {
97 // Create a PDF document. 97 // Create a PDF document.
@@ -101,9 +101,8 @@ class Invoice { @@ -101,9 +101,8 @@ class Invoice {
101 final font2 = await rootBundle.load('assets/roboto2.ttf'); 101 final font2 = await rootBundle.load('assets/roboto2.ttf');
102 final font3 = await rootBundle.load('assets/roboto3.ttf'); 102 final font3 = await rootBundle.load('assets/roboto3.ttf');
103 103
104 - _logo = PdfImage.file(  
105 - doc.document,  
106 - bytes: (await rootBundle.load('assets/logo.png')).buffer.asUint8List(), 104 + _logo = pw.MemoryImage(
  105 + (await rootBundle.load('assets/logo.png')).buffer.asUint8List(),
107 ); 106 );
108 107
109 // Add page to the PDF 108 // Add page to the PDF
@@ -191,7 +190,8 @@ class Invoice { @@ -191,7 +190,8 @@ class Invoice {
191 alignment: pw.Alignment.topRight, 190 alignment: pw.Alignment.topRight,
192 padding: const pw.EdgeInsets.only(bottom: 8, left: 30), 191 padding: const pw.EdgeInsets.only(bottom: 8, left: 30),
193 height: 72, 192 height: 72,
194 - child: _logo != null ? pw.Image(_logo) : pw.PdfLogo(), 193 + child:
  194 + _logo != null ? pw.Image.provider(_logo) : pw.PdfLogo(),
195 ), 195 ),
196 // pw.Container( 196 // pw.Container(
197 // color: baseColor, 197 // color: baseColor,
@@ -29,9 +29,8 @@ const sep = 120.0; @@ -29,9 +29,8 @@ const sep = 120.0;
29 Future<Uint8List> generateResume(PdfPageFormat format) async { 29 Future<Uint8List> generateResume(PdfPageFormat format) async {
30 final doc = pw.Document(title: 'My Résumé', author: 'David PHAM-VAN'); 30 final doc = pw.Document(title: 'My Résumé', author: 'David PHAM-VAN');
31 31
32 - final profileImage = PdfImage.file(  
33 - doc.document,  
34 - bytes: (await rootBundle.load('assets/profile.jpg')).buffer.asUint8List(), 32 + final profileImage = pw.MemoryImage(
  33 + (await rootBundle.load('assets/profile.jpg')).buffer.asUint8List(),
35 ); 34 );
36 35
37 final pageTheme = await _myPageTheme(format); 36 final pageTheme = await _myPageTheme(format);
@@ -121,7 +120,7 @@ Future<Uint8List> generateResume(PdfPageFormat format) async { @@ -121,7 +120,7 @@ Future<Uint8List> generateResume(PdfPageFormat format) async {
121 width: 100, 120 width: 100,
122 height: 100, 121 height: 100,
123 color: lightGreen, 122 color: lightGreen,
124 - child: pw.Image(profileImage), 123 + child: pw.Image.provider(profileImage),
125 ), 124 ),
126 ), 125 ),
127 pw.Column(children: <pw.Widget>[ 126 pw.Column(children: <pw.Widget>[
@@ -79,6 +79,7 @@ @@ -79,6 +79,7 @@
79 - Add Chart Widget [Marco Papula] 79 - Add Chart Widget [Marco Papula]
80 - Add Divider and VerticalDivider Widget 80 - Add Divider and VerticalDivider Widget
81 - Replace Theme with ThemeData 81 - Replace Theme with ThemeData
  82 +- Implement ImageProvider
82 83
83 ## 1.6.2 84 ## 1.6.2
84 85
@@ -56,17 +56,15 @@ pdf.addPage(pw.Page( @@ -56,17 +56,15 @@ pdf.addPage(pw.Page(
56 To load an image from a file: 56 To load an image from a file:
57 57
58 ```dart 58 ```dart
59 -final image = PdfImage.file(  
60 - pdf.document,  
61 - bytes: File('test.webp').readAsBytesSync(), 59 +final image = pw.MemoryImage(
  60 + File('test.webp').readAsBytesSync(),
62 ); 61 );
63 62
64 -pdf.addPage(pw.Page(  
65 - build: (pw.Context context) {  
66 - return pw.Center(  
67 - child: pw.Image(image),  
68 - ); // Center  
69 - })); // Page 63 +pdf.addPage(pw.Page(build: (pw.Context context) {
  64 + return pw.Center(
  65 + child: pw.Image.provider(image),
  66 + ); // Center
  67 +})); // Page
70 ``` 68 ```
71 69
72 To use a TrueType font: 70 To use a TrueType font:
@@ -50,6 +50,7 @@ part 'widgets/geometry.dart'; @@ -50,6 +50,7 @@ part 'widgets/geometry.dart';
50 part 'widgets/grid_view.dart'; 50 part 'widgets/grid_view.dart';
51 part 'widgets/icon.dart'; 51 part 'widgets/icon.dart';
52 part 'widgets/image.dart'; 52 part 'widgets/image.dart';
  53 +part 'widgets/image_provider.dart';
53 part 'widgets/multi_page.dart'; 54 part 'widgets/multi_page.dart';
54 part 'widgets/page.dart'; 55 part 'widgets/page.dart';
55 part 'widgets/page_theme.dart'; 56 part 'widgets/page_theme.dart';
@@ -144,20 +144,36 @@ class BoxBorder { @@ -144,20 +144,36 @@ class BoxBorder {
144 144
145 @immutable 145 @immutable
146 class DecorationImage { 146 class DecorationImage {
147 - const DecorationImage(  
148 - {@required this.image,  
149 - this.fit = BoxFit.cover,  
150 - this.alignment = Alignment.center})  
151 - : assert(image != null), 147 + @Deprecated('Use DecorationImage.provider()')
  148 + DecorationImage({
  149 + @required PdfImage image,
  150 + this.fit = BoxFit.cover,
  151 + this.alignment = Alignment.center,
  152 + }) : assert(image != null),
  153 + assert(fit != null),
  154 + assert(alignment != null),
  155 + image = ImageProxy(image),
  156 + dpi = null;
  157 +
  158 + const DecorationImage.provider({
  159 + @required this.image,
  160 + this.fit = BoxFit.cover,
  161 + this.alignment = Alignment.center,
  162 + this.dpi,
  163 + }) : assert(image != null),
152 assert(fit != null), 164 assert(fit != null),
153 assert(alignment != null); 165 assert(alignment != null);
154 166
155 - final PdfImage image; 167 + final ImageProvider image;
156 final BoxFit fit; 168 final BoxFit fit;
157 final Alignment alignment; 169 final Alignment alignment;
  170 + final double dpi;
158 171
159 void paint(Context context, PdfRect box) { 172 void paint(Context context, PdfRect box) {
160 - final imageSize = PdfPoint(image.width.toDouble(), image.height.toDouble()); 173 + final _image = image.resolve(context, box.size, dpi: dpi);
  174 +
  175 + final imageSize =
  176 + PdfPoint(_image.width.toDouble(), _image.height.toDouble());
161 final sizes = applyBoxFit(fit, imageSize, box.size); 177 final sizes = applyBoxFit(fit, imageSize, box.size);
162 final scaleX = sizes.destination.x / sizes.source.x; 178 final scaleX = sizes.destination.x / sizes.source.x;
163 final scaleY = sizes.destination.y / sizes.source.y; 179 final scaleY = sizes.destination.y / sizes.source.y;
@@ -174,7 +190,7 @@ class DecorationImage { @@ -174,7 +190,7 @@ class DecorationImage {
174 ..drawRect(box.x, box.y, box.width, box.height) 190 ..drawRect(box.x, box.y, box.width, box.height)
175 ..clipPath() 191 ..clipPath()
176 ..setTransform(mat) 192 ..setTransform(mat)
177 - ..drawImage(image, 0, 0, imageSize.x, imageSize.y) 193 + ..drawImage(_image, 0, 0, imageSize.x, imageSize.y)
178 ..restoreContext(); 194 ..restoreContext();
179 } 195 }
180 } 196 }
@@ -75,18 +75,27 @@ void _drawImageRect(PdfGraphics canvas, PdfImage image, PdfRect sourceRect, @@ -75,18 +75,27 @@ void _drawImageRect(PdfGraphics canvas, PdfImage image, PdfRect sourceRect,
75 } 75 }
76 76
77 class Image extends Widget { 77 class Image extends Widget {
  78 + @Deprecated('Use Image.provider instead')
78 Image( 79 Image(
79 - this.image, { 80 + PdfImage image, {
80 this.fit = BoxFit.contain, 81 this.fit = BoxFit.contain,
81 this.alignment = Alignment.center, 82 this.alignment = Alignment.center,
82 this.width, 83 this.width,
83 this.height, 84 this.height,
84 }) : assert(image != null), 85 }) : assert(image != null),
85 - aspectRatio = image.height.toDouble() / image.width.toDouble(); 86 + image = ImageProxy(image),
  87 + dpi = null;
86 88
87 - final PdfImage image; 89 + Image.provider(
  90 + this.image, {
  91 + this.fit = BoxFit.contain,
  92 + this.alignment = Alignment.center,
  93 + this.width,
  94 + this.height,
  95 + this.dpi,
  96 + }) : assert(image != null);
88 97
89 - final double aspectRatio; 98 + final ImageProvider image;
90 99
91 final BoxFit fit; 100 final BoxFit fit;
92 101
@@ -96,6 +105,8 @@ class Image extends Widget { @@ -96,6 +105,8 @@ class Image extends Widget {
96 105
97 final double height; 106 final double height;
98 107
  108 + final double dpi;
  109 +
99 @override 110 @override
100 void layout(Context context, BoxConstraints constraints, 111 void layout(Context context, BoxConstraints constraints,
101 {bool parentUsesSize = false}) { 112 {bool parentUsesSize = false}) {
@@ -119,9 +130,11 @@ class Image extends Widget { @@ -119,9 +130,11 @@ class Image extends Widget {
119 void paint(Context context) { 130 void paint(Context context) {
120 super.paint(context); 131 super.paint(context);
121 132
  133 + final rect = context.localToGlobal(box);
  134 +
122 _paintImage( 135 _paintImage(
123 canvas: context.canvas, 136 canvas: context.canvas,
124 - image: image, 137 + image: image.resolve(context, rect.size, dpi: dpi),
125 rect: box, 138 rect: box,
126 alignment: alignment, 139 alignment: alignment,
127 fit: fit, 140 fit: fit,
  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 +part of widget;
  18 +
  19 +/// Identifies an image without committing to the precise final asset
  20 +abstract class ImageProvider {
  21 + ImageProvider(
  22 + this._width,
  23 + this._height,
  24 + this.orientation,
  25 + this.dpi,
  26 + );
  27 +
  28 + final double dpi;
  29 +
  30 + final int _width;
  31 +
  32 + /// Image width
  33 + int get width => orientation.index >= 4 ? _height : _width;
  34 +
  35 + final int _height;
  36 +
  37 + /// Image height
  38 + int get height => orientation.index < 4 ? _height : _width;
  39 +
  40 + /// The internal orientation of the image
  41 + final PdfImageOrientation orientation;
  42 +
  43 + final _cache = <int, PdfImage>{};
  44 +
  45 + PdfImage buildImage(Context context, {int width, int height});
  46 +
  47 + /// Resolves this image provider using the given context, returning a PdfImage
  48 + /// The image is automatically added to the document
  49 + PdfImage resolve(Context context, PdfPoint size, {double dpi}) {
  50 + assert(size != null);
  51 + final effectiveDpi = dpi ?? this.dpi;
  52 +
  53 + if (effectiveDpi == null || _cache[0] != null) {
  54 + _cache[0] ??= buildImage(context);
  55 +
  56 + assert(_cache[0].pdfDocument == context.document,
  57 + 'Do not reuse an ImageProvider object across multiple documents');
  58 + return _cache[0];
  59 + }
  60 +
  61 + final width = (size.x / PdfPageFormat.inch * effectiveDpi).toInt();
  62 + final height = (size.y / PdfPageFormat.inch * effectiveDpi).toInt();
  63 +
  64 + if (!_cache.containsKey(width)) {
  65 + _cache[width] ??= buildImage(context, width: width, height: height);
  66 + }
  67 +
  68 + assert(_cache[width].pdfDocument == context.document,
  69 + 'Do not reuse an ImageProvider object across multiple documents');
  70 + return _cache[width];
  71 + }
  72 +}
  73 +
  74 +class ImageProxy extends ImageProvider {
  75 + ImageProxy(
  76 + this._image, {
  77 + double dpi,
  78 + }) : super(_image.width, _image.height, _image.orientation, dpi);
  79 +
  80 + /// The proxy image
  81 + final PdfImage _image;
  82 +
  83 + @override
  84 + PdfImage buildImage(Context context, {int width, int height}) => _image;
  85 +}
  86 +
  87 +class MemoryImage extends ImageProvider {
  88 + factory MemoryImage(
  89 + Uint8List bytes, {
  90 + PdfImageOrientation orientation,
  91 + double dpi,
  92 + }) {
  93 + final decoder = im.findDecoderForData(bytes);
  94 + if (decoder is im.JpegDecoder) {
  95 + final info = PdfJpegInfo(bytes);
  96 +
  97 + return MemoryImage._(
  98 + bytes,
  99 + info.width,
  100 + info.height,
  101 + orientation ?? info.orientation,
  102 + dpi,
  103 + );
  104 + }
  105 +
  106 + final info = decoder.startDecode(bytes);
  107 + return MemoryImage._(
  108 + bytes,
  109 + info.width,
  110 + info.height,
  111 + orientation ?? PdfImageOrientation.topLeft,
  112 + dpi,
  113 + );
  114 + }
  115 +
  116 + MemoryImage._(
  117 + this.bytes,
  118 + int width,
  119 + int height,
  120 + PdfImageOrientation orientation,
  121 + double dpi,
  122 + ) : super(width, height, orientation, dpi);
  123 +
  124 + /// The image data
  125 + final Uint8List bytes;
  126 +
  127 + @override
  128 + PdfImage buildImage(Context context, {int width, int height}) {
  129 + if (width == null) {
  130 + return PdfImage.file(context.document, bytes: bytes);
  131 + }
  132 +
  133 + final image = im.decodeImage(bytes);
  134 + print(width);
  135 + final resized = im.copyResize(image, width: width);
  136 + return PdfImage.fromImage(context.document, image: resized);
  137 + }
  138 +}
  139 +
  140 +class ImageImage extends ImageProvider {
  141 + ImageImage(
  142 + this._image, {
  143 + double dpi,
  144 + PdfImageOrientation orientation,
  145 + }) : super(_image.width, _image.height,
  146 + orientation ?? PdfImageOrientation.topLeft, dpi);
  147 +
  148 + /// The image data
  149 + final im.Image _image;
  150 +
  151 + @override
  152 + PdfImage buildImage(Context context, {int width, int height}) {
  153 + if (width == null) {
  154 + return PdfImage.fromImage(context.document, image: _image);
  155 + }
  156 +
  157 + final resized = im.copyResize(_image, width: width);
  158 + return PdfImage.fromImage(context.document, image: resized);
  159 + }
  160 +}
  161 +
  162 +class RawImage extends ImageImage {
  163 + RawImage({
  164 + @required Uint8List bytes,
  165 + @required int width,
  166 + @required int height,
  167 + PdfImageOrientation orientation,
  168 + double dpi,
  169 + }) : super(im.Image.fromBytes(width, height, bytes),
  170 + orientation: orientation, dpi: dpi);
  171 +}
@@ -19,7 +19,6 @@ import 'dart:io'; @@ -19,7 +19,6 @@ import 'dart:io';
19 import 'dart:isolate'; 19 import 'dart:isolate';
20 import 'dart:typed_data'; 20 import 'dart:typed_data';
21 21
22 -import 'package:pdf/pdf.dart';  
23 import 'package:pdf/widgets.dart'; 22 import 'package:pdf/widgets.dart';
24 import 'package:test/test.dart'; 23 import 'package:test/test.dart';
25 24
@@ -35,12 +34,12 @@ class Message { @@ -35,12 +34,12 @@ class Message {
35 void compute(Message message) { 34 void compute(Message message) {
36 final pdf = Document(); 35 final pdf = Document();
37 36
38 - final image = PdfImage.jpeg(  
39 - pdf.document,  
40 - image: message.image, 37 + final image = MemoryImage(
  38 + message.image,
41 ); 39 );
42 40
43 - pdf.addPage(Page(build: (Context context) => Center(child: Image(image)))); 41 + pdf.addPage(
  42 + Page(build: (Context context) => Center(child: Image.provider(image))));
44 43
45 message.sendPort.send(pdf.save()); 44 message.sendPort.send(pdf.save());
46 } 45 }
@@ -17,29 +17,27 @@ @@ -17,29 +17,27 @@
17 import 'dart:convert'; 17 import 'dart:convert';
18 import 'dart:io'; 18 import 'dart:io';
19 19
20 -import 'package:pdf/pdf.dart';  
21 import 'package:pdf/widgets.dart'; 20 import 'package:pdf/widgets.dart';
22 import 'package:test/test.dart'; 21 import 'package:test/test.dart';
23 22
24 import 'utils.dart'; 23 import 'utils.dart';
25 24
26 Document pdf; 25 Document pdf;
27 -PdfImage image; 26 +MemoryImage image;
28 27
29 void main() { 28 void main() {
30 setUpAll(() async { 29 setUpAll(() async {
31 Document.debug = true; 30 Document.debug = true;
32 pdf = Document(); 31 pdf = Document();
33 32
34 - image = PdfImage.jpeg(  
35 - pdf.document,  
36 - image: await download('https://www.nfet.net/nfet.jpg'), 33 + image = MemoryImage(
  34 + await download('https://www.nfet.net/nfet.jpg'),
37 ); 35 );
38 }); 36 });
39 37
40 test('Pdf Jpeg Download', () async { 38 test('Pdf Jpeg Download', () async {
41 pdf.addPage(Page( 39 pdf.addPage(Page(
42 - build: (Context context) => Center(child: Image(image)), 40 + build: (Context context) => Center(child: Image.provider(image)),
43 )); 41 ));
44 }); 42 });
45 43
@@ -51,10 +49,9 @@ void main() { @@ -51,10 +49,9 @@ void main() {
51 crossAxisSpacing: 10, 49 crossAxisSpacing: 10,
52 children: List<Widget>.generate( 50 children: List<Widget>.generate(
53 images.length, 51 images.length,
54 - (int index) => Image(  
55 - PdfImage.jpeg(  
56 - pdf.document,  
57 - image: base64.decode(images[index]), 52 + (int index) => Image.provider(
  53 + MemoryImage(
  54 + base64.decode(images[index]),
58 ), 55 ),
59 ), 56 ),
60 ), 57 ),
@@ -72,7 +69,7 @@ void main() { @@ -72,7 +69,7 @@ void main() {
72 return SizedBox( 69 return SizedBox(
73 width: 200, 70 width: 200,
74 height: 100, 71 height: 100,
75 - child: Image( 72 + child: Image.provider(
76 image, 73 image,
77 fit: fit, 74 fit: fit,
78 ), 75 ),
@@ -85,10 +82,9 @@ void main() { @@ -85,10 +82,9 @@ void main() {
85 test('Pdf Image decode', () { 82 test('Pdf Image decode', () {
86 final imageWidgets = imageFiles.map<Widget>( 83 final imageWidgets = imageFiles.map<Widget>(
87 (String image) => SizedBox( 84 (String image) => SizedBox(
88 - child: Image(  
89 - PdfImage.file(  
90 - pdf.document,  
91 - bytes: gzip.decode(base64.decode(image)), 85 + child: Image.provider(
  86 + MemoryImage(
  87 + gzip.decode(base64.decode(image)),
92 ), 88 ),
93 ), 89 ),
94 width: 200, 90 width: 200,
@@ -19,7 +19,6 @@ import 'dart:io'; @@ -19,7 +19,6 @@ import 'dart:io';
19 import 'dart:math' as math; 19 import 'dart:math' as math;
20 import 'dart:typed_data'; 20 import 'dart:typed_data';
21 21
22 -import 'package:pdf/pdf.dart';  
23 import 'package:pdf/widgets.dart'; 22 import 'package:pdf/widgets.dart';
24 23
25 Future<Uint8List> download( 24 Future<Uint8List> download(
@@ -56,7 +55,7 @@ Future<Uint8List> download( @@ -56,7 +55,7 @@ Future<Uint8List> download(
56 return Uint8List.fromList(data); 55 return Uint8List.fromList(data);
57 } 56 }
58 57
59 -PdfImage generateBitmap(PdfDocument pdf, int w, int h) { 58 +ImageProvider generateBitmap(int w, int h) {
60 final bm = Uint32List(w * h); 59 final bm = Uint32List(w * h);
61 final dw = w.toDouble(); 60 final dw = w.toDouble();
62 final dh = h.toDouble(); 61 final dh = h.toDouble();
@@ -69,9 +68,8 @@ PdfImage generateBitmap(PdfDocument pdf, int w, int h) { @@ -69,9 +68,8 @@ PdfImage generateBitmap(PdfDocument pdf, int w, int h) {
69 } 68 }
70 } 69 }
71 70
72 - return PdfImage(  
73 - pdf,  
74 - image: bm.buffer.asUint8List(), 71 + return RawImage(
  72 + bytes: bm.buffer.asUint8List(),
75 width: w, 73 width: w,
76 height: h, 74 height: h,
77 ); 75 );
@@ -55,7 +55,7 @@ void main() { @@ -55,7 +55,7 @@ void main() {
55 }); 55 });
56 56
57 test('Container Widgets Image', () { 57 test('Container Widgets Image', () {
58 - final image = generateBitmap(pdf.document, 100, 200); 58 + final image = generateBitmap(100, 200);
59 59
60 final widgets = <Widget>[]; 60 final widgets = <Widget>[];
61 for (var shape in BoxShape.values) { 61 for (var shape in BoxShape.values) {
@@ -66,7 +66,7 @@ void main() { @@ -66,7 +66,7 @@ void main() {
66 decoration: BoxDecoration( 66 decoration: BoxDecoration(
67 shape: shape, 67 shape: shape,
68 borderRadiusEx: const BorderRadius.all(Radius.circular(10)), 68 borderRadiusEx: const BorderRadius.all(Radius.circular(10)),
69 - image: DecorationImage(image: image, fit: fit), 69 + image: DecorationImage.provider(image: image, fit: fit),
70 ), 70 ),
71 width: 100, 71 width: 100,
72 height: 100, 72 height: 100,
@@ -37,7 +37,7 @@ void main() { @@ -37,7 +37,7 @@ void main() {
37 MultiPage( 37 MultiPage(
38 theme: ThemeData.withFont(icons: icons), 38 theme: ThemeData.withFont(icons: icons),
39 build: (Context context) { 39 build: (Context context) {
40 - final iconList = List<IconData>(); 40 + final iconList = <IconData>[];
41 final pdfFont = icons.getFont(context); 41 final pdfFont = icons.getFont(context);
42 if (pdfFont is PdfTtfFont) { 42 if (pdfFont is PdfTtfFont) {
43 iconList.addAll( 43 iconList.addAll(
@@ -24,7 +24,7 @@ import 'package:test/test.dart'; @@ -24,7 +24,7 @@ import 'package:test/test.dart';
24 void main() { 24 void main() {
25 Document pdf; 25 Document pdf;
26 TextStyle symbol; 26 TextStyle symbol;
27 - PdfImage im; 27 + ImageProvider im;
28 28
29 setUpAll(() { 29 setUpAll(() {
30 Document.debug = true; 30 Document.debug = true;
@@ -43,7 +43,7 @@ void main() { @@ -43,7 +43,7 @@ void main() {
43 43
44 final imData = zlib.decode(base64.decode( 44 final imData = zlib.decode(base64.decode(
45 'eJz7//8/w388uOTCT6a4Ez96Q47++I+OI479mEVALyNU7z9seuNP/mAm196Ekz8YR+0dWHtBmJC9S+7/Zog89iMIKLYaHQPVJGLTD7MXpDfq+I9goNhPdPPDjv3YlnH6Jye6+2H21l/6yeB/4HsSDr1bQXrRwq8HqHcGyF6QXp9933N0tn/7Y7vn+/9gLPaih0PDlV9MIAzVm6ez7dsfzW3f/oMwzAx0e7FhoJutdbcj9MKw9frnL2J2POfBpxeEg478YLba/X0Wsl6lBXf+s0bP/s8ePXeWePJCvPEJNYMRZIYWSO/cq/9Z/Nv+M4bO+M8YDjFDJGkhzvSE7A6jRTdnsQR2wfXCMLHuMC5byyidvGgWE5JeZDOIcYdR+TpmkBno+mFmAAC+DGhl')); 45 'eJz7//8/w388uOTCT6a4Ez96Q47++I+OI479mEVALyNU7z9seuNP/mAm196Ekz8YR+0dWHtBmJC9S+7/Zog89iMIKLYaHQPVJGLTD7MXpDfq+I9goNhPdPPDjv3YlnH6Jye6+2H21l/6yeB/4HsSDr1bQXrRwq8HqHcGyF6QXp9933N0tn/7Y7vn+/9gLPaih0PDlV9MIAzVm6ez7dsfzW3f/oMwzAx0e7FhoJutdbcj9MKw9frnL2J2POfBpxeEg478YLba/X0Wsl6lBXf+s0bP/s8ePXeWePJCvPEJNYMRZIYWSO/cq/9Z/Nv+M4bO+M8YDjFDJGkhzvSE7A6jRTdnsQR2wfXCMLHuMC5byyidvGgWE5JeZDOIcYdR+TpmkBno+mFmAAC+DGhl'));
46 - im = PdfImage(pdf.document, image: imData, width: 16, height: 20); 46 + im = RawImage(bytes: imData, width: 16, height: 20);
47 }); 47 });
48 48
49 test('Pdf Widgets page 1', () { 49 test('Pdf Widgets page 1', () {
@@ -85,7 +85,7 @@ void main() { @@ -85,7 +85,7 @@ void main() {
85 Row( 85 Row(
86 mainAxisAlignment: MainAxisAlignment.spaceEvenly, 86 mainAxisAlignment: MainAxisAlignment.spaceEvenly,
87 children: <Widget>[ 87 children: <Widget>[
88 - Image(im), 88 + Image.provider(im),
89 PdfLogo(), 89 PdfLogo(),
90 Column( 90 Column(
91 children: <Widget>[ 91 children: <Widget>[
@@ -55,12 +55,12 @@ To load an image from an ImageProvider: @@ -55,12 +55,12 @@ To load an image from an ImageProvider:
55 55
56 ```dart 56 ```dart
57 const imageProvider = const AssetImage('assets/image.png'); 57 const imageProvider = const AssetImage('assets/image.png');
58 -final PdfImage image = await pdfImageFromImageProvider(pdf: doc.document, image: imageProvider); 58 +final image = await flutterImageProvider(imageProvider);
59 59
60 doc.addPage(pw.Page( 60 doc.addPage(pw.Page(
61 build: (pw.Context context) { 61 build: (pw.Context context) {
62 return pw.Center( 62 return pw.Center(
63 - child: pw.Image(image), 63 + child: pw.Image.provider(image),
64 ); // Center 64 ); // Center
65 })); // Page 65 })); // Page
66 ``` 66 ```
@@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
17 import 'dart:async'; 17 import 'dart:async';
18 import 'dart:ui' as ui; 18 import 'dart:ui' as ui;
19 19
20 -import 'package:flutter/rendering.dart'; 20 +import 'package:flutter/rendering.dart' as rdr;
21 import 'package:flutter/services.dart'; 21 import 'package:flutter/services.dart';
22 import 'package:meta/meta.dart'; 22 import 'package:meta/meta.dart';
23 import 'package:pdf/pdf.dart'; 23 import 'package:pdf/pdf.dart';
@@ -25,6 +25,7 @@ import 'package:pdf/widgets.dart'; @@ -25,6 +25,7 @@ import 'package:pdf/widgets.dart';
25 25
26 /// Loads an image from a Flutter [ui.Image] 26 /// Loads an image from a Flutter [ui.Image]
27 /// into a [PdfImage] instance 27 /// into a [PdfImage] instance
  28 +@Deprecated('Use flutterImageProvider')
28 Future<PdfImage> pdfImageFromImage( 29 Future<PdfImage> pdfImageFromImage(
29 {@required PdfDocument pdf, @required ui.Image image}) async { 30 {@required PdfDocument pdf, @required ui.Image image}) async {
30 final bytes = await image.toByteData(format: ui.ImageByteFormat.rawRgba); 31 final bytes = await image.toByteData(format: ui.ImageByteFormat.rawRgba);
@@ -37,16 +38,17 @@ Future<PdfImage> pdfImageFromImage( @@ -37,16 +38,17 @@ Future<PdfImage> pdfImageFromImage(
37 38
38 /// Loads an image from a Flutter [ImageProvider] 39 /// Loads an image from a Flutter [ImageProvider]
39 /// into a [PdfImage] instance 40 /// into a [PdfImage] instance
  41 +@Deprecated('Use flutterImageProvider')
40 Future<PdfImage> pdfImageFromImageProvider( 42 Future<PdfImage> pdfImageFromImageProvider(
41 {@required PdfDocument pdf, 43 {@required PdfDocument pdf,
42 - @required ImageProvider image,  
43 - ImageConfiguration configuration,  
44 - ImageErrorListener onError}) async { 44 + @required rdr.ImageProvider image,
  45 + rdr.ImageConfiguration configuration,
  46 + rdr.ImageErrorListener onError}) async {
45 final completer = Completer<PdfImage>(); 47 final completer = Completer<PdfImage>();
46 - final stream = image.resolve(configuration ?? ImageConfiguration.empty); 48 + final stream = image.resolve(configuration ?? rdr.ImageConfiguration.empty);
47 49
48 - ImageStreamListener listener;  
49 - listener = ImageStreamListener((ImageInfo image, bool sync) async { 50 + rdr.ImageStreamListener listener;
  51 + listener = rdr.ImageStreamListener((rdr.ImageInfo image, bool sync) async {
50 final result = await pdfImageFromImage(pdf: pdf, image: image.image); 52 final result = await pdfImageFromImage(pdf: pdf, image: image.image);
51 if (!completer.isCompleted) { 53 if (!completer.isCompleted) {
52 completer.complete(result); 54 completer.complete(result);
@@ -68,6 +70,46 @@ Future<PdfImage> pdfImageFromImageProvider( @@ -68,6 +70,46 @@ Future<PdfImage> pdfImageFromImageProvider(
68 return completer.future; 70 return completer.future;
69 } 71 }
70 72
  73 +/// Loads an image from a Flutter [ImageProvider]
  74 +/// into an [ImageProvider] instance
  75 +Future<ImageProvider> flutterImageProvider(
  76 + rdr.ImageProvider image, {
  77 + rdr.ImageConfiguration configuration,
  78 + rdr.ImageErrorListener onError,
  79 +}) async {
  80 + final completer = Completer<ImageProvider>();
  81 + final stream = image.resolve(configuration ?? rdr.ImageConfiguration.empty);
  82 +
  83 + rdr.ImageStreamListener listener;
  84 + listener = rdr.ImageStreamListener((rdr.ImageInfo image, bool sync) async {
  85 + final bytes =
  86 + await image.image.toByteData(format: ui.ImageByteFormat.rawRgba);
  87 +
  88 + final result = RawImage(
  89 + bytes: bytes.buffer.asUint8List(),
  90 + width: image.image.width,
  91 + height: image.image.height);
  92 +
  93 + if (!completer.isCompleted) {
  94 + completer.complete(result);
  95 + }
  96 + stream.removeListener(listener);
  97 + }, onError: (dynamic exception, StackTrace stackTrace) {
  98 + if (!completer.isCompleted) {
  99 + completer.complete(null);
  100 + }
  101 + if (onError != null) {
  102 + onError(exception, stackTrace);
  103 + } else {
  104 + // https://groups.google.com/forum/#!topic/flutter-announce/hp1RNIgej38
  105 + assert(false, 'image failed to load');
  106 + }
  107 + });
  108 +
  109 + stream.addListener(listener);
  110 + return completer.future;
  111 +}
  112 +
71 /// Loads a font from an asset bundle key. If used multiple times with the same font name, 113 /// Loads a font from an asset bundle key. If used multiple times with the same font name,
72 /// it will be included multiple times in the pdf file 114 /// it will be included multiple times in the pdf file
73 Future<TtfFont> fontFromAssetBundle(String key, AssetBundle bundle) async { 115 Future<TtfFont> fontFromAssetBundle(String key, AssetBundle bundle) async {
@@ -18,7 +18,7 @@ dependencies: @@ -18,7 +18,7 @@ dependencies:
18 image: ^2.1.4 18 image: ^2.1.4
19 js: ^0.6.1 19 js: ^0.6.1
20 meta: ^1.1.5 20 meta: ^1.1.5
21 - pdf: ^1.11.1 21 + pdf: ^1.13.0
22 plugin_platform_interface: ^1.0.2 22 plugin_platform_interface: ^1.0.2
23 23
24 dev_dependencies: 24 dev_dependencies:
@@ -45,15 +45,15 @@ void main() { @@ -45,15 +45,15 @@ void main() {
45 // expect(await Printing.platformVersion, '42'); 45 // expect(await Printing.platformVersion, '42');
46 }); 46 });
47 47
48 - test('pdfImageFromImageProvider(FileImage)', () async {  
49 - final image = await pdfImageFromImageProvider(  
50 - pdf: doc.document, image: FileImage(File('$path/example.png'))); 48 + test('flutterImageProvider(FileImage)', () async {
  49 + final image =
  50 + await flutterImageProvider(FileImage(File('$path/example.png')));
51 51
52 doc.addPage( 52 doc.addPage(
53 pw.Page( 53 pw.Page(
54 build: (pw.Context context) => pw.Center( 54 build: (pw.Context context) => pw.Center(
55 child: pw.Container( 55 child: pw.Container(
56 - child: pw.Image(image), 56 + child: pw.Image.provider(image),
57 ), 57 ),
58 ), 58 ),
59 ), 59 ),