David PHAM-VAN

Optimize memory footprint

@@ -12,6 +12,7 @@ @@ -12,6 +12,7 @@
12 - Fix context painting empty Table 12 - Fix context painting empty Table
13 - Fix Text decoration placements 13 - Fix Text decoration placements
14 - Improve image buffer management 14 - Improve image buffer management
  15 +- Optimize memory footprint
15 16
16 ## 1.5.0 17 ## 1.5.0
17 18
@@ -18,9 +18,9 @@ @@ -18,9 +18,9 @@
18 18
19 part of pdf; 19 part of pdf;
20 20
21 -class Ascii85Encoder extends Converter<List<int>, List<int>> { 21 +class Ascii85Encoder extends Converter<Uint8List, Uint8List> {
22 @override 22 @override
23 - List<int> convert(List<int> input) { 23 + Uint8List convert(Uint8List input) {
24 final Uint8List output = Uint8List(_maxEncodedLen(input.length) + 2); 24 final Uint8List output = Uint8List(_maxEncodedLen(input.length) + 2);
25 25
26 int outputOffset = 0; 26 int outputOffset = 0;
@@ -35,7 +35,7 @@ abstract class PdfDataType { @@ -35,7 +35,7 @@ abstract class PdfDataType {
35 return String.fromCharCodes(toStream().output()); 35 return String.fromCharCodes(toStream().output());
36 } 36 }
37 37
38 - List<int> toList() { 38 + Uint8List toList() {
39 return toStream().output(); 39 return toStream().output();
40 } 40 }
41 } 41 }
@@ -153,8 +153,8 @@ class PdfSecString extends PdfString { @@ -153,8 +153,8 @@ class PdfSecString extends PdfString {
153 return super.output(s); 153 return super.output(s);
154 } 154 }
155 155
156 - final List<int> enc = object.pdfDocument.encryption.encrypt(value, object);  
157 - _output(s, Uint8List.fromList(enc)); 156 + final Uint8List enc = object.pdfDocument.encryption.encrypt(value, object);
  157 + _output(s, enc);
158 } 158 }
159 } 159 }
160 160
@@ -116,14 +116,14 @@ class PdfDocument { @@ -116,14 +116,14 @@ class PdfDocument {
116 final Set<PdfFont> fonts = <PdfFont>{}; 116 final Set<PdfFont> fonts = <PdfFont>{};
117 117
118 /// Generates the document ID 118 /// Generates the document ID
119 - List<int> _documentID;  
120 - List<int> get documentID { 119 + Uint8List _documentID;
  120 + Uint8List get documentID {
121 if (_documentID == null) { 121 if (_documentID == null) {
122 final math.Random rnd = math.Random(); 122 final math.Random rnd = math.Random();
123 - _documentID = sha256 123 + _documentID = Uint8List.fromList(sha256
124 .convert(DateTime.now().toIso8601String().codeUnits + 124 .convert(DateTime.now().toIso8601String().codeUnits +
125 List<int>.generate(32, (_) => rnd.nextInt(256))) 125 List<int>.generate(32, (_) => rnd.nextInt(256)))
126 - .bytes; 126 + .bytes);
127 } 127 }
128 128
129 return _documentID; 129 return _documentID;
@@ -182,7 +182,7 @@ class PdfDocument { @@ -182,7 +182,7 @@ class PdfDocument {
182 pos.close(); 182 pos.close();
183 } 183 }
184 184
185 - List<int> save() { 185 + Uint8List save() {
186 final PdfStream os = PdfStream(); 186 final PdfStream os = PdfStream();
187 _write(os); 187 _write(os);
188 return os.output(); 188 return os.output();
@@ -19,5 +19,5 @@ part of pdf; @@ -19,5 +19,5 @@ part of pdf;
19 abstract class PdfEncryption extends PdfObject { 19 abstract class PdfEncryption extends PdfObject {
20 PdfEncryption(PdfDocument pdfDocument) : super(pdfDocument, null); 20 PdfEncryption(PdfDocument pdfDocument) : super(pdfDocument, null);
21 21
22 - List<int> encrypt(List<int> input, PdfObject object); 22 + Uint8List encrypt(Uint8List input, PdfObject object);
23 } 23 }
@@ -35,7 +35,7 @@ class PdfObjectStream extends PdfObject { @@ -35,7 +35,7 @@ class PdfObjectStream extends PdfObject {
35 /// defines if the stream needs to be converted to ascii85 35 /// defines if the stream needs to be converted to ascii85
36 final bool isBinary; 36 final bool isBinary;
37 37
38 - List<int> _data; 38 + Uint8List _data;
39 39
40 @override 40 @override
41 void _prepare() { 41 void _prepare() {
@@ -20,30 +20,53 @@ part of pdf; @@ -20,30 +20,53 @@ part of pdf;
20 20
21 class PdfStream { 21 class PdfStream {
22 static const int precision = 5; 22 static const int precision = 5;
23 - final List<int> _stream = <int>[]; 23 +
  24 + static const int _grow = 65536;
  25 +
  26 + Uint8List _stream = Uint8List(_grow);
  27 +
  28 + int _offset = 0;
  29 +
  30 + void _ensureCapacity(int size) {
  31 + if (_stream.length - _offset >= size) {
  32 + return;
  33 + }
  34 +
  35 + final int newSize = math.max(_offset + size + _grow, _offset * 2);
  36 + final Uint8List newBuffer = Uint8List(newSize);
  37 + newBuffer.setAll(0, _stream);
  38 + _stream = newBuffer;
  39 + }
  40 +
  41 + void putByte(int s) {
  42 + _ensureCapacity(1);
  43 + _stream[_offset++] = s;
  44 + }
  45 +
  46 + void putBytes(List<int> s) {
  47 + _ensureCapacity(s.length);
  48 + _stream.setAll(_offset, s);
  49 + _offset += s.length;
  50 + }
24 51
25 void putStream(PdfStream s) { 52 void putStream(PdfStream s) {
26 - _stream.addAll(s._stream); 53 + putBytes(s._stream);
27 } 54 }
28 55
  56 + int get offset => _offset;
  57 +
  58 + Uint8List output() => _stream.sublist(0, _offset);
  59 +
29 void putString(String s) { 60 void putString(String s) {
30 for (int codeUnit in s.codeUnits) { 61 for (int codeUnit in s.codeUnits) {
31 if (codeUnit <= 0x7f) { 62 if (codeUnit <= 0x7f) {
32 - _stream.add(codeUnit); 63 + putByte(codeUnit);
33 } else { 64 } else {
34 - _stream.add(0x20); 65 + putByte(0x20);
35 } 66 }
36 } 67 }
37 } 68 }
38 69
39 - void putByte(int s) {  
40 - _stream.add(s);  
41 - }  
42 -  
43 - void putBytes(List<int> s) {  
44 - _stream.addAll(s);  
45 - }  
46 -  
47 void putNum(double d) { 70 void putNum(double d) {
48 assert(d != double.infinity); 71 assert(d != double.infinity);
49 putString(d.toStringAsFixed(precision)); 72 putString(d.toStringAsFixed(precision));
@@ -62,44 +85,40 @@ class PdfStream { @@ -62,44 +85,40 @@ class PdfStream {
62 for (int c in s) { 85 for (int c in s) {
63 switch (c) { 86 switch (c) {
64 case 0x0a: // \n Line feed (LF) 87 case 0x0a: // \n Line feed (LF)
65 - _stream.add(0x5c);  
66 - _stream.add(0x6e); 88 + putByte(0x5c);
  89 + putByte(0x6e);
67 break; 90 break;
68 case 0x0d: // \r Carriage return (CR) 91 case 0x0d: // \r Carriage return (CR)
69 - _stream.add(0x5c);  
70 - _stream.add(0x72); 92 + putByte(0x5c);
  93 + putByte(0x72);
71 break; 94 break;
72 case 0x09: // \t Horizontal tab (HT) 95 case 0x09: // \t Horizontal tab (HT)
73 - _stream.add(0x5c);  
74 - _stream.add(0x74); 96 + putByte(0x5c);
  97 + putByte(0x74);
75 break; 98 break;
76 case 0x08: // \b Backspace (BS) 99 case 0x08: // \b Backspace (BS)
77 - _stream.add(0x5c);  
78 - _stream.add(0x62); 100 + putByte(0x5c);
  101 + putByte(0x62);
79 break; 102 break;
80 case 0x0c: // \f Form feed (FF) 103 case 0x0c: // \f Form feed (FF)
81 - _stream.add(0x5c);  
82 - _stream.add(0x66); 104 + putByte(0x5c);
  105 + putByte(0x66);
83 break; 106 break;
84 case 0x28: // \( Left parenthesis 107 case 0x28: // \( Left parenthesis
85 - _stream.add(0x5c);  
86 - _stream.add(0x28); 108 + putByte(0x5c);
  109 + putByte(0x28);
87 break; 110 break;
88 case 0x29: // \) Right parenthesis 111 case 0x29: // \) Right parenthesis
89 - _stream.add(0x5c);  
90 - _stream.add(0x29); 112 + putByte(0x5c);
  113 + putByte(0x29);
91 break; 114 break;
92 case 0x5c: // \\ Backslash 115 case 0x5c: // \\ Backslash
93 - _stream.add(0x5c);  
94 - _stream.add(0x5c); 116 + putByte(0x5c);
  117 + putByte(0x5c);
95 break; 118 break;
96 default: 119 default:
97 - _stream.add(c); 120 + putByte(c);
98 } 121 }
99 } 122 }
100 } 123 }
101 -  
102 - int get offset => _stream.length;  
103 -  
104 - List<int> output() => _stream;  
105 } 124 }
@@ -66,7 +66,7 @@ class Document { @@ -66,7 +66,7 @@ class Document {
66 _pages.add(page); 66 _pages.add(page);
67 } 67 }
68 68
69 - List<int> save() { 69 + Uint8List save() {
70 if (!_paint) { 70 if (!_paint) {
71 for (Page page in _pages) { 71 for (Page page in _pages) {
72 page.postProcess(this); 72 page.postProcess(this);
@@ -61,7 +61,7 @@ void main() { @@ -61,7 +61,7 @@ void main() {
61 final ReceivePort receivePort = ReceivePort(); 61 final ReceivePort receivePort = ReceivePort();
62 62
63 receivePort.listen((dynamic data) async { 63 receivePort.listen((dynamic data) async {
64 - if (data is List<int>) { 64 + if (data is Uint8List) {
65 print('Received a ${data.length} bytes PDF'); 65 print('Received a ${data.length} bytes PDF');
66 final File file = File('isolate.pdf'); 66 final File file = File('isolate.pdf');
67 await file.writeAsBytes(data); 67 await file.writeAsBytes(data);
@@ -9,6 +9,7 @@ @@ -9,6 +9,7 @@
9 - Add Unit tests 9 - Add Unit tests
10 - Update example tab 10 - Update example tab
11 - Uniformize examples 11 - Uniformize examples
  12 +- Optimize memory footprint
12 13
13 ## 3.1.0 14 ## 3.1.0
14 15
@@ -15,9 +15,10 @@ @@ -15,9 +15,10 @@
15 */ 15 */
16 16
17 import 'dart:async'; 17 import 'dart:async';
  18 +import 'dart:typed_data';
18 19
19 import 'package:pdf/pdf.dart'; 20 import 'package:pdf/pdf.dart';
20 21
21 /// Callback used to generate the Pdf document dynamically when the user 22 /// Callback used to generate the Pdf document dynamically when the user
22 /// changes the page settings: size and margins 23 /// changes the page settings: size and margins
23 -typedef LayoutCallback = FutureOr<List<int>> Function(PdfPageFormat format); 24 +typedef LayoutCallback = FutureOr<Uint8List> Function(PdfPageFormat format);
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 16
17 import 'dart:async'; 17 import 'dart:async';
  18 +import 'dart:typed_data';
18 19
19 import 'package:flutter/rendering.dart' show Rect; 20 import 'package:flutter/rendering.dart' show Rect;
20 import 'package:pdf/pdf.dart'; 21 import 'package:pdf/pdf.dart';
@@ -81,13 +82,13 @@ abstract class PrintingPlatform extends PlatformInterface { @@ -81,13 +82,13 @@ abstract class PrintingPlatform extends PlatformInterface {
81 82
82 /// Displays a platform popup to share the Pdf document to another application 83 /// Displays a platform popup to share the Pdf document to another application
83 Future<bool> sharePdf( 84 Future<bool> sharePdf(
84 - List<int> bytes, 85 + Uint8List bytes,
85 String filename, 86 String filename,
86 Rect bounds, 87 Rect bounds,
87 ); 88 );
88 89
89 /// Convert an html document to a pdf data 90 /// Convert an html document to a pdf data
90 - Future<List<int>> convertHtml( 91 + Future<Uint8List> convertHtml(
91 String html, 92 String html,
92 String baseUrl, 93 String baseUrl,
93 PdfPageFormat format, 94 PdfPageFormat format,
@@ -95,7 +96,7 @@ abstract class PrintingPlatform extends PlatformInterface { @@ -95,7 +96,7 @@ abstract class PrintingPlatform extends PlatformInterface {
95 96
96 /// Convert a Pdf document to bitmap images 97 /// Convert a Pdf document to bitmap images
97 Stream<PdfRaster> raster( 98 Stream<PdfRaster> raster(
98 - List<int> document, 99 + Uint8List document,
99 List<int> pages, 100 List<int> pages,
100 double dpi, 101 double dpi,
101 ); 102 );
@@ -54,7 +54,7 @@ class MethodChannelPrinting extends PrintingPlatform { @@ -54,7 +54,7 @@ class MethodChannelPrinting extends PrintingPlatform {
54 marginBottom: call.arguments['marginBottom'], 54 marginBottom: call.arguments['marginBottom'],
55 ); 55 );
56 56
57 - final List<int> bytes = await job.onLayout(format); 57 + final Uint8List bytes = await job.onLayout(format);
58 58
59 if (bytes == null) { 59 if (bytes == null) {
60 throw 'onLayout returned null'; 60 throw 'onLayout returned null';
@@ -186,7 +186,7 @@ class MethodChannelPrinting extends PrintingPlatform { @@ -186,7 +186,7 @@ class MethodChannelPrinting extends PrintingPlatform {
186 onCompleted: Completer<bool>(), 186 onCompleted: Completer<bool>(),
187 )); 187 ));
188 188
189 - final List<int> bytes = await onLayout(format); 189 + final Uint8List bytes = await onLayout(format);
190 if (bytes == null) { 190 if (bytes == null) {
191 return false; 191 return false;
192 } 192 }
@@ -205,7 +205,7 @@ class MethodChannelPrinting extends PrintingPlatform { @@ -205,7 +205,7 @@ class MethodChannelPrinting extends PrintingPlatform {
205 205
206 @override 206 @override
207 Future<bool> sharePdf( 207 Future<bool> sharePdf(
208 - List<int> bytes, 208 + Uint8List bytes,
209 String filename, 209 String filename,
210 Rect bounds, 210 Rect bounds,
211 ) async { 211 ) async {
@@ -221,10 +221,10 @@ class MethodChannelPrinting extends PrintingPlatform { @@ -221,10 +221,10 @@ class MethodChannelPrinting extends PrintingPlatform {
221 } 221 }
222 222
223 @override 223 @override
224 - Future<List<int>> convertHtml( 224 + Future<Uint8List> convertHtml(
225 String html, String baseUrl, PdfPageFormat format) async { 225 String html, String baseUrl, PdfPageFormat format) async {
226 final PrintJob job = _newPrintJob(PrintJob( 226 final PrintJob job = _newPrintJob(PrintJob(
227 - onHtmlRendered: Completer<List<int>>(), 227 + onHtmlRendered: Completer<Uint8List>(),
228 )); 228 ));
229 229
230 final Map<String, dynamic> params = <String, dynamic>{ 230 final Map<String, dynamic> params = <String, dynamic>{
@@ -240,14 +240,14 @@ class MethodChannelPrinting extends PrintingPlatform { @@ -240,14 +240,14 @@ class MethodChannelPrinting extends PrintingPlatform {
240 }; 240 };
241 241
242 await _channel.invokeMethod<void>('convertHtml', params); 242 await _channel.invokeMethod<void>('convertHtml', params);
243 - final List<int> result = await job.onHtmlRendered.future; 243 + final Uint8List result = await job.onHtmlRendered.future;
244 _printJobs.remove(job.index); 244 _printJobs.remove(job.index);
245 return result; 245 return result;
246 } 246 }
247 247
248 @override 248 @override
249 Stream<PdfRaster> raster( 249 Stream<PdfRaster> raster(
250 - List<int> document, 250 + Uint8List document,
251 List<int> pages, 251 List<int> pages,
252 double dpi, 252 double dpi,
253 ) { 253 ) {
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 16
17 import 'dart:async'; 17 import 'dart:async';
  18 +import 'dart:typed_data';
18 19
19 import 'callback.dart'; 20 import 'callback.dart';
20 import 'raster.dart'; 21 import 'raster.dart';
@@ -28,7 +29,7 @@ class PrintJob { @@ -28,7 +29,7 @@ class PrintJob {
28 }); 29 });
29 30
30 final LayoutCallback onLayout; 31 final LayoutCallback onLayout;
31 - final Completer<List<int>> onHtmlRendered; 32 + final Completer<Uint8List> onHtmlRendered;
32 final Completer<bool> onCompleted; 33 final Completer<bool> onCompleted;
33 final StreamController<PdfRaster> onPageRasterized; 34 final StreamController<PdfRaster> onPageRasterized;
34 35
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 16
17 import 'dart:async'; 17 import 'dart:async';
  18 +import 'dart:typed_data';
18 19
19 import 'package:flutter/rendering.dart' show Rect, Offset; 20 import 'package:flutter/rendering.dart' show Rect, Offset;
20 import 'package:meta/meta.dart'; 21 import 'package:meta/meta.dart';
@@ -83,7 +84,7 @@ mixin Printing { @@ -83,7 +84,7 @@ mixin Printing {
83 /// Displays a platform popup to share the Pdf document to another application 84 /// Displays a platform popup to share the Pdf document to another application
84 static Future<bool> sharePdf({ 85 static Future<bool> sharePdf({
85 @Deprecated('use bytes with document.save()') PdfDocument document, 86 @Deprecated('use bytes with document.save()') PdfDocument document,
86 - List<int> bytes, 87 + Uint8List bytes,
87 String filename = 'document.pdf', 88 String filename = 'document.pdf',
88 Rect bounds, 89 Rect bounds,
89 }) { 90 }) {
@@ -105,7 +106,7 @@ mixin Printing { @@ -105,7 +106,7 @@ mixin Printing {
105 } 106 }
106 107
107 /// Convert an html document to a pdf data 108 /// Convert an html document to a pdf data
108 - static Future<List<int>> convertHtml({ 109 + static Future<Uint8List> convertHtml({
109 @required String html, 110 @required String html,
110 String baseUrl, 111 String baseUrl,
111 PdfPageFormat format = PdfPageFormat.standard, 112 PdfPageFormat format = PdfPageFormat.standard,
@@ -134,7 +135,7 @@ mixin Printing { @@ -134,7 +135,7 @@ mixin Printing {
134 } 135 }
135 136
136 static Stream<PdfRaster> raster( 137 static Stream<PdfRaster> raster(
137 - List<int> document, { 138 + Uint8List document, {
138 List<int> pages, 139 List<int> pages,
139 double dpi = PdfPageFormat.inch, 140 double dpi = PdfPageFormat.inch,
140 }) { 141 }) {
@@ -150,7 +151,7 @@ mixin Printing { @@ -150,7 +151,7 @@ mixin Printing {
150 @Deprecated('use Printing.layoutPdf(onLayout: (_) => document.save());') 151 @Deprecated('use Printing.layoutPdf(onLayout: (_) => document.save());')
151 static Future<void> printPdf({ 152 static Future<void> printPdf({
152 @Deprecated('use bytes with document.save()') PdfDocument document, 153 @Deprecated('use bytes with document.save()') PdfDocument document,
153 - List<int> bytes, 154 + Uint8List bytes,
154 }) async { 155 }) async {
155 assert(document != null || bytes != null); 156 assert(document != null || bytes != null);
156 assert(!(document == null && bytes == null)); 157 assert(!(document == null && bytes == null));
@@ -56,7 +56,7 @@ class PrintingPlugin extends PrintingPlatform { @@ -56,7 +56,7 @@ class PrintingPlugin extends PrintingPlatform {
56 String name, 56 String name,
57 PdfPageFormat format, 57 PdfPageFormat format,
58 ) async { 58 ) async {
59 - final List<int> result = await onLayout(format); 59 + final Uint8List result = await onLayout(format);
60 60
61 if (result == null || result.isEmpty) { 61 if (result == null || result.isEmpty) {
62 return false; 62 return false;
@@ -83,7 +83,7 @@ class PrintingPlugin extends PrintingPlatform { @@ -83,7 +83,7 @@ class PrintingPlugin extends PrintingPlatform {
83 final String pdfUrl = html.Url.createObjectUrl(pdfFile); 83 final String pdfUrl = html.Url.createObjectUrl(pdfFile);
84 final html.HtmlDocument doc = js.context['document']; 84 final html.HtmlDocument doc = js.context['document'];
85 85
86 - final html.IFrameElement frame = 86 + final html.Element frame =
87 doc.getElementById(_frameId) ?? doc.createElement('iframe'); 87 doc.getElementById(_frameId) ?? doc.createElement('iframe');
88 frame.setAttribute( 88 frame.setAttribute(
89 'style', 89 'style',
@@ -112,7 +112,7 @@ class PrintingPlugin extends PrintingPlatform { @@ -112,7 +112,7 @@ class PrintingPlugin extends PrintingPlatform {
112 112
113 @override 113 @override
114 Future<bool> sharePdf( 114 Future<bool> sharePdf(
115 - List<int> bytes, 115 + Uint8List bytes,
116 String filename, 116 String filename,
117 Rect bounds, 117 Rect bounds,
118 ) async { 118 ) async {
@@ -130,7 +130,7 @@ class PrintingPlugin extends PrintingPlatform { @@ -130,7 +130,7 @@ class PrintingPlugin extends PrintingPlatform {
130 } 130 }
131 131
132 @override 132 @override
133 - Future<List<int>> convertHtml( 133 + Future<Uint8List> convertHtml(
134 String html, 134 String html,
135 String baseUrl, 135 String baseUrl,
136 PdfPageFormat format, 136 PdfPageFormat format,
@@ -157,7 +157,7 @@ class PrintingPlugin extends PrintingPlatform { @@ -157,7 +157,7 @@ class PrintingPlugin extends PrintingPlatform {
157 157
158 @override 158 @override
159 Stream<PdfRaster> raster( 159 Stream<PdfRaster> raster(
160 - List<int> document, 160 + Uint8List document,
161 List<int> pages, 161 List<int> pages,
162 double dpi, 162 double dpi,
163 ) { 163 ) {
@@ -15,7 +15,7 @@ dependencies: @@ -15,7 +15,7 @@ dependencies:
15 sdk: flutter 15 sdk: flutter
16 flutter_web_plugins: 16 flutter_web_plugins:
17 sdk: flutter 17 sdk: flutter
18 - pdf: ^1.3.15 18 + pdf: ^1.6.0
19 plugin_platform_interface: ^1.0.2 19 plugin_platform_interface: ^1.0.2
20 20
21 dev_dependencies: 21 dev_dependencies:
@@ -14,6 +14,8 @@ @@ -14,6 +14,8 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
  17 +import 'dart:typed_data';
  18 +
17 import 'package:flutter_test/flutter_test.dart'; 19 import 'package:flutter_test/flutter_test.dart';
18 import 'package:mockito/mockito.dart'; 20 import 'package:mockito/mockito.dart';
19 import 'package:pdf/pdf.dart'; 21 import 'package:pdf/pdf.dart';
@@ -56,7 +58,7 @@ void main() { @@ -56,7 +58,7 @@ void main() {
56 58
57 expect( 59 expect(
58 await Printing.sharePdf( 60 await Printing.sharePdf(
59 - bytes: <int>[], 61 + bytes: Uint8List(0),
60 ), 62 ),
61 null, 63 null,
62 ); 64 );
@@ -106,7 +108,7 @@ void main() { @@ -106,7 +108,7 @@ void main() {
106 ); 108 );
107 109
108 expect( 110 expect(
109 - Printing.raster(<int>[]), 111 + Printing.raster(Uint8List(0)),
110 null, 112 null,
111 ); 113 );
112 }); 114 });