David PHAM-VAN

Add debugging information

@@ -8,6 +8,7 @@ @@ -8,6 +8,7 @@
8 - Implement fallback font 8 - Implement fallback font
9 - Implement Emoji support 9 - Implement Emoji support
10 - Improve outlines containing non-sequential level increments [Roel Spilker] 10 - Improve outlines containing non-sequential level increments [Roel Spilker]
  11 +- Add debugging information
11 12
12 ## 3.6.5 13 ## 3.6.5
13 14
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 16
17 import 'dart:collection'; 17 import 'dart:collection';
18 import 'dart:convert'; 18 import 'dart:convert';
  19 +import 'dart:math' as math;
19 import 'dart:typed_data'; 20 import 'dart:typed_data';
20 21
21 import 'package:meta/meta.dart'; 22 import 'package:meta/meta.dart';
@@ -25,10 +26,12 @@ import 'color.dart'; @@ -25,10 +26,12 @@ import 'color.dart';
25 import 'obj/object.dart'; 26 import 'obj/object.dart';
26 import 'stream.dart'; 27 import 'stream.dart';
27 28
  29 +const _kIndentSize = 2;
  30 +
28 abstract class PdfDataType { 31 abstract class PdfDataType {
29 const PdfDataType(); 32 const PdfDataType();
30 33
31 - void output(PdfStream s); 34 + void output(PdfStream s, [int? indent]);
32 35
33 PdfStream _toStream() { 36 PdfStream _toStream() {
34 final s = PdfStream(); 37 final s = PdfStream();
@@ -53,7 +56,7 @@ class PdfBool extends PdfDataType { @@ -53,7 +56,7 @@ class PdfBool extends PdfDataType {
53 final bool value; 56 final bool value;
54 57
55 @override 58 @override
56 - void output(PdfStream s) { 59 + void output(PdfStream s, [int? indent]) {
57 s.putString(value ? 'true' : 'false'); 60 s.putString(value ? 'true' : 'false');
58 } 61 }
59 62
@@ -81,7 +84,7 @@ class PdfNum extends PdfDataType { @@ -81,7 +84,7 @@ class PdfNum extends PdfDataType {
81 final num value; 84 final num value;
82 85
83 @override 86 @override
84 - void output(PdfStream s) { 87 + void output(PdfStream s, [int? indent]) {
85 if (value is int) { 88 if (value is int) {
86 s.putString(value.toInt().toString()); 89 s.putString(value.toInt().toString());
87 } else { 90 } else {
@@ -119,12 +122,12 @@ class PdfNumList extends PdfDataType { @@ -119,12 +122,12 @@ class PdfNumList extends PdfDataType {
119 final List<num> values; 122 final List<num> values;
120 123
121 @override 124 @override
122 - void output(PdfStream s) { 125 + void output(PdfStream s, [int? indent]) {
123 for (var n = 0; n < values.length; n++) { 126 for (var n = 0; n < values.length; n++) {
124 if (n > 0) { 127 if (n > 0) {
125 s.putByte(0x20); 128 s.putByte(0x20);
126 } 129 }
127 - PdfNum(values[n]).output(s); 130 + PdfNum(values[n]).output(s, indent);
128 } 131 }
129 } 132 }
130 133
@@ -289,7 +292,7 @@ class PdfString extends PdfDataType { @@ -289,7 +292,7 @@ class PdfString extends PdfDataType {
289 } 292 }
290 293
291 @override 294 @override
292 - void output(PdfStream s) { 295 + void output(PdfStream s, [int? indent]) {
293 _output(s, value); 296 _output(s, value);
294 } 297 }
295 298
@@ -346,9 +349,9 @@ class PdfSecString extends PdfString { @@ -346,9 +349,9 @@ class PdfSecString extends PdfString {
346 final PdfObject object; 349 final PdfObject object;
347 350
348 @override 351 @override
349 - void output(PdfStream s) { 352 + void output(PdfStream s, [int? indent]) {
350 if (object.pdfDocument.encryption == null) { 353 if (object.pdfDocument.encryption == null) {
351 - return super.output(s); 354 + return super.output(s, indent);
352 } 355 }
353 356
354 final enc = object.pdfDocument.encryption!.encrypt(value, object); 357 final enc = object.pdfDocument.encryption!.encrypt(value, object);
@@ -362,7 +365,7 @@ class PdfName extends PdfDataType { @@ -362,7 +365,7 @@ class PdfName extends PdfDataType {
362 final String value; 365 final String value;
363 366
364 @override 367 @override
365 - void output(PdfStream s) { 368 + void output(PdfStream s, [int? indent]) {
366 assert(value[0] == '/'); 369 assert(value[0] == '/');
367 final bytes = <int>[]; 370 final bytes = <int>[];
368 for (final c in value.codeUnits) { 371 for (final c in value.codeUnits) {
@@ -404,7 +407,7 @@ class PdfNull extends PdfDataType { @@ -404,7 +407,7 @@ class PdfNull extends PdfDataType {
404 const PdfNull(); 407 const PdfNull();
405 408
406 @override 409 @override
407 - void output(PdfStream s) { 410 + void output(PdfStream s, [int? indent]) {
408 s.putString('null'); 411 s.putString('null');
409 } 412 }
410 413
@@ -425,7 +428,7 @@ class PdfIndirect extends PdfDataType { @@ -425,7 +428,7 @@ class PdfIndirect extends PdfDataType {
425 final int gen; 428 final int gen;
426 429
427 @override 430 @override
428 - void output(PdfStream s) { 431 + void output(PdfStream s, [int? indent]) {
429 s.putString('$ser $gen R'); 432 s.putString('$ser $gen R');
430 } 433 }
431 434
@@ -465,11 +468,21 @@ class PdfArray<T extends PdfDataType> extends PdfDataType { @@ -465,11 +468,21 @@ class PdfArray<T extends PdfDataType> extends PdfDataType {
465 } 468 }
466 469
467 @override 470 @override
468 - void output(PdfStream s) { 471 + void output(PdfStream s, [int? indent]) {
  472 + if (indent != null) {
  473 + s.putBytes(List<int>.filled(indent, 0x20));
  474 + indent += _kIndentSize;
  475 + }
469 s.putString('['); 476 s.putString('[');
470 if (values.isNotEmpty) { 477 if (values.isNotEmpty) {
471 for (var n = 0; n < values.length; n++) { 478 for (var n = 0; n < values.length; n++) {
472 final val = values[n]; 479 final val = values[n];
  480 + if (indent != null) {
  481 + s.putByte(0x0a);
  482 + if (val is! PdfDict && val is! PdfArray) {
  483 + s.putBytes(List<int>.filled(indent, 0x20));
  484 + }
  485 + } else {
473 if (n > 0 && 486 if (n > 0 &&
474 !(val is PdfName || 487 !(val is PdfName ||
475 val is PdfString || 488 val is PdfString ||
@@ -477,8 +490,16 @@ class PdfArray<T extends PdfDataType> extends PdfDataType { @@ -477,8 +490,16 @@ class PdfArray<T extends PdfDataType> extends PdfDataType {
477 val is PdfDict)) { 490 val is PdfDict)) {
478 s.putByte(0x20); 491 s.putByte(0x20);
479 } 492 }
480 - val.output(s);  
481 } 493 }
  494 + val.output(s, indent);
  495 + }
  496 + if (indent != null) {
  497 + s.putByte(0x0a);
  498 + }
  499 + }
  500 + if (indent != null) {
  501 + indent -= _kIndentSize;
  502 + s.putBytes(List<int>.filled(indent, 0x20));
482 } 503 }
483 s.putString(']'); 504 s.putString(']');
484 } 505 }
@@ -544,15 +565,44 @@ class PdfDict<T extends PdfDataType> extends PdfDataType { @@ -544,15 +565,44 @@ class PdfDict<T extends PdfDataType> extends PdfDataType {
544 } 565 }
545 566
546 @override 567 @override
547 - void output(PdfStream s) { 568 + void output(PdfStream s, [int? indent]) {
  569 + if (indent != null) {
  570 + s.putBytes(List<int>.filled(indent, 0x20));
  571 + }
548 s.putBytes(const <int>[0x3c, 0x3c]); 572 s.putBytes(const <int>[0x3c, 0x3c]);
  573 + var len = 0;
  574 + var n = 1;
  575 + if (indent != null) {
  576 + s.putByte(0x0a);
  577 + indent += _kIndentSize;
  578 + len = values.keys.fold<int>(0, (p, e) => math.max(p, e.length));
  579 + }
549 values.forEach((String k, T v) { 580 values.forEach((String k, T v) {
  581 + if (indent != null) {
  582 + s.putBytes(List<int>.filled(indent, 0x20));
  583 + n = len - k.length + 1;
  584 + }
550 s.putString(k); 585 s.putString(k);
  586 + if (indent != null) {
  587 + if (v is PdfDict || v is PdfArray) {
  588 + s.putByte(0x0a);
  589 + } else {
  590 + s.putBytes(List<int>.filled(n, 0x20));
  591 + }
  592 + } else {
551 if (v is PdfNum || v is PdfBool || v is PdfNull || v is PdfIndirect) { 593 if (v is PdfNum || v is PdfBool || v is PdfNull || v is PdfIndirect) {
552 s.putByte(0x20); 594 s.putByte(0x20);
553 } 595 }
554 - v.output(s); 596 + }
  597 + v.output(s, indent);
  598 + if (indent != null) {
  599 + s.putByte(0x0a);
  600 + }
555 }); 601 });
  602 + if (indent != null) {
  603 + indent -= _kIndentSize;
  604 + s.putBytes(List<int>.filled(indent, 0x20));
  605 + }
556 s.putBytes(const <int>[0x3e, 0x3e]); 606 s.putBytes(const <int>[0x3e, 0x3e]);
557 } 607 }
558 608
@@ -633,7 +683,7 @@ class PdfDictStream extends PdfDict<PdfDataType> { @@ -633,7 +683,7 @@ class PdfDictStream extends PdfDict<PdfDataType> {
633 final bool compress; 683 final bool compress;
634 684
635 @override 685 @override
636 - void output(PdfStream s) { 686 + void output(PdfStream s, [int? indent]) {
637 final _values = PdfDict(values); 687 final _values = PdfDict(values);
638 688
639 Uint8List? _data; 689 Uint8List? _data;
@@ -641,7 +691,7 @@ class PdfDictStream extends PdfDict<PdfDataType> { @@ -641,7 +691,7 @@ class PdfDictStream extends PdfDict<PdfDataType> {
641 if (_values.containsKey('/Filter')) { 691 if (_values.containsKey('/Filter')) {
642 // The data is already in the right format 692 // The data is already in the right format
643 _data = data; 693 _data = data;
644 - } else if (compress && object.pdfDocument.deflate != null) { 694 + } else if (compress && object.pdfDocument.compress) {
645 // Compress the data 695 // Compress the data
646 final newData = Uint8List.fromList(object.pdfDocument.deflate!(data)); 696 final newData = Uint8List.fromList(object.pdfDocument.deflate!(data));
647 if (newData.lengthInBytes < data.lengthInBytes) { 697 if (newData.lengthInBytes < data.lengthInBytes) {
@@ -668,7 +718,10 @@ class PdfDictStream extends PdfDict<PdfDataType> { @@ -668,7 +718,10 @@ class PdfDictStream extends PdfDict<PdfDataType> {
668 718
669 _values['/Length'] = PdfNum(_data.length); 719 _values['/Length'] = PdfNum(_data.length);
670 720
671 - _values.output(s); 721 + _values.output(s, indent);
  722 + if (indent != null) {
  723 + s.putByte(0x0a);
  724 + }
672 s.putString('stream\n'); 725 s.putString('stream\n');
673 s.putBytes(_data); 726 s.putBytes(_data);
674 s.putString('\nendstream\n'); 727 s.putString('\nendstream\n');
@@ -681,7 +734,7 @@ class PdfColorType extends PdfDataType { @@ -681,7 +734,7 @@ class PdfColorType extends PdfDataType {
681 final PdfColor color; 734 final PdfColor color;
682 735
683 @override 736 @override
684 - void output(PdfStream s) { 737 + void output(PdfStream s, [int? indent]) {
685 if (color is PdfColorCmyk) { 738 if (color is PdfColorCmyk) {
686 final k = color as PdfColorCmyk; 739 final k = color as PdfColorCmyk;
687 PdfArray.fromNum(<double>[ 740 PdfArray.fromNum(<double>[
@@ -689,13 +742,13 @@ class PdfColorType extends PdfDataType { @@ -689,13 +742,13 @@ class PdfColorType extends PdfDataType {
689 k.magenta, 742 k.magenta,
690 k.yellow, 743 k.yellow,
691 k.black, 744 k.black,
692 - ]).output(s); 745 + ]).output(s, indent);
693 } else { 746 } else {
694 PdfArray.fromNum(<double>[ 747 PdfArray.fromNum(<double>[
695 color.red, 748 color.red,
696 color.green, 749 color.green,
697 color.blue, 750 color.blue,
698 - ]).output(s); 751 + ]).output(s, indent);
699 } 752 }
700 } 753 }
701 754
@@ -161,6 +161,8 @@ class PdfDocument { @@ -161,6 +161,8 @@ class PdfDocument {
161 161
162 Uint8List? _documentID; 162 Uint8List? _documentID;
163 163
  164 + bool get compress => deflate != null;
  165 +
164 /// Generates the document ID 166 /// Generates the document ID
165 Uint8List get documentID { 167 Uint8List get documentID {
166 if (_documentID == null) { 168 if (_documentID == null) {
@@ -203,7 +205,7 @@ class PdfDocument { @@ -203,7 +205,7 @@ class PdfDocument {
203 205
204 /// This writes the document to an OutputStream. 206 /// This writes the document to an OutputStream.
205 Future<void> _write(PdfStream os) async { 207 Future<void> _write(PdfStream os) async {
206 - final pos = PdfOutput(os, version); 208 + final pos = PdfOutput(os, version, compress);
207 209
208 // Write each object to the [PdfStream]. We call via the output 210 // Write each object to the [PdfStream]. We call via the output
209 // as that builds the xref table 211 // as that builds the xref table
@@ -109,6 +109,10 @@ class PdfGraphicState { @@ -109,6 +109,10 @@ class PdfGraphicState {
109 /// Color transfer function 109 /// Color transfer function
110 final PdfFunction? transferFunction; 110 final PdfFunction? transferFunction;
111 111
  112 + @override
  113 + String toString() =>
  114 + '$runtimeType fillOpacity:$fillOpacity strokeOpacity:$strokeOpacity blendMode:$blendMode softMask:$softMask transferFunction:$transferFunction';
  115 +
112 PdfDict output() { 116 PdfDict output() {
113 final params = PdfDict(); 117 final params = PdfDict();
114 118
@@ -122,33 +122,75 @@ class PdfGraphics { @@ -122,33 +122,75 @@ class PdfGraphics {
122 /// Draw a surface on the previously defined shape 122 /// Draw a surface on the previously defined shape
123 /// set evenOdd to false to use the nonzero winding number rule to determine the region to fill and to true to use the even-odd rule to determine the region to fill 123 /// set evenOdd to false to use the nonzero winding number rule to determine the region to fill and to true to use the even-odd rule to determine the region to fill
124 void fillPath({bool evenOdd = false}) { 124 void fillPath({bool evenOdd = false}) {
  125 + assert(() {
  126 + if (!_page.pdfDocument.compress) {
  127 + _buf.putComment('fillPath evenOdd:$evenOdd');
  128 + }
  129 + return true;
  130 + }());
  131 +
125 _buf.putString('f${evenOdd ? '*' : ''}\n'); 132 _buf.putString('f${evenOdd ? '*' : ''}\n');
126 } 133 }
127 134
128 /// Draw the contour of the previously defined shape 135 /// Draw the contour of the previously defined shape
129 void strokePath({bool close = false}) { 136 void strokePath({bool close = false}) {
  137 + assert(() {
  138 + if (!_page.pdfDocument.compress) {
  139 + _buf.putComment('strokePath close:$close');
  140 + }
  141 + return true;
  142 + }());
  143 +
130 _buf.putString('${close ? 's' : 'S'}\n'); 144 _buf.putString('${close ? 's' : 'S'}\n');
131 } 145 }
132 146
133 /// Close the path with a line 147 /// Close the path with a line
134 void closePath() { 148 void closePath() {
  149 + assert(() {
  150 + if (!_page.pdfDocument.compress) {
  151 + _buf.putComment('closePath');
  152 + }
  153 + return true;
  154 + }());
  155 +
135 _buf.putString('h\n'); 156 _buf.putString('h\n');
136 } 157 }
137 158
138 /// Create a clipping surface from the previously defined shape, 159 /// Create a clipping surface from the previously defined shape,
139 /// to prevent any further drawing outside 160 /// to prevent any further drawing outside
140 void clipPath({bool evenOdd = false, bool end = true}) { 161 void clipPath({bool evenOdd = false, bool end = true}) {
  162 + assert(() {
  163 + if (!_page.pdfDocument.compress) {
  164 + _buf.putComment('clipPath evenOdd:$evenOdd end:$end');
  165 + }
  166 + return true;
  167 + }());
  168 +
141 _buf.putString('W${evenOdd ? '*' : ''}${end ? ' n' : ''}\n'); 169 _buf.putString('W${evenOdd ? '*' : ''}${end ? ' n' : ''}\n');
142 } 170 }
143 171
144 /// Draw a surface on the previously defined shape and then draw the contour 172 /// Draw a surface on the previously defined shape and then draw the contour
145 /// set evenOdd to false to use the nonzero winding number rule to determine the region to fill and to true to use the even-odd rule to determine the region to fill 173 /// set evenOdd to false to use the nonzero winding number rule to determine the region to fill and to true to use the even-odd rule to determine the region to fill
146 void fillAndStrokePath({bool evenOdd = false, bool close = false}) { 174 void fillAndStrokePath({bool evenOdd = false, bool close = false}) {
  175 + assert(() {
  176 + if (!_page.pdfDocument.compress) {
  177 + _buf.putComment('fillAndStrokePath evenOdd:$evenOdd close:$close');
  178 + }
  179 + return true;
  180 + }());
  181 +
147 _buf.putString('${close ? 'b' : 'B'}${evenOdd ? '*' : ''}\n'); 182 _buf.putString('${close ? 'b' : 'B'}${evenOdd ? '*' : ''}\n');
148 } 183 }
149 184
150 /// Apply a shader 185 /// Apply a shader
151 void applyShader(PdfShading shader) { 186 void applyShader(PdfShading shader) {
  187 + assert(() {
  188 + if (!_page.pdfDocument.compress) {
  189 + _buf.putComment('applyShader');
  190 + }
  191 + return true;
  192 + }());
  193 +
152 // The shader needs to be registered in the page resources 194 // The shader needs to be registered in the page resources
153 _page.addShader(shader); 195 _page.addShader(shader);
154 _buf.putString('${shader.name} sh\n'); 196 _buf.putString('${shader.name} sh\n');
@@ -160,6 +202,13 @@ class PdfGraphics { @@ -160,6 +202,13 @@ class PdfGraphics {
160 /// When using [PdfPage], you can create another fresh Graphics instance, 202 /// When using [PdfPage], you can create another fresh Graphics instance,
161 /// which will draw over this one. 203 /// which will draw over this one.
162 void restoreContext() { 204 void restoreContext() {
  205 + assert(() {
  206 + if (!_page.pdfDocument.compress) {
  207 + _buf.putComment('restoreContext');
  208 + }
  209 + return true;
  210 + }());
  211 +
163 if (_contextQueue.isNotEmpty) { 212 if (_contextQueue.isNotEmpty) {
164 // restore graphics context 213 // restore graphics context
165 _buf.putString('Q\n'); 214 _buf.putString('Q\n');
@@ -169,12 +218,26 @@ class PdfGraphics { @@ -169,12 +218,26 @@ class PdfGraphics {
169 218
170 /// Save the graphc context 219 /// Save the graphc context
171 void saveContext() { 220 void saveContext() {
  221 + assert(() {
  222 + if (!_page.pdfDocument.compress) {
  223 + _buf.putComment('saveContext');
  224 + }
  225 + return true;
  226 + }());
  227 +
172 _buf.putString('q\n'); 228 _buf.putString('q\n');
173 _contextQueue.addLast(_context.copy()); 229 _contextQueue.addLast(_context.copy());
174 } 230 }
175 231
176 /// Draws an image onto the page. 232 /// Draws an image onto the page.
177 void drawImage(PdfImage img, double x, double y, [double? w, double? h]) { 233 void drawImage(PdfImage img, double x, double y, [double? w, double? h]) {
  234 + assert(() {
  235 + if (!_page.pdfDocument.compress) {
  236 + _buf.putComment('drawImage x:$x y:$y');
  237 + }
  238 + return true;
  239 + }());
  240 +
178 w ??= img.width.toDouble(); 241 w ??= img.width.toDouble();
179 h ??= img.height.toDouble() * w / img.width.toDouble(); 242 h ??= img.height.toDouble() * w / img.width.toDouble();
180 243
@@ -215,6 +278,13 @@ class PdfGraphics { @@ -215,6 +278,13 @@ class PdfGraphics {
215 278
216 /// Draws a line between two coordinates. 279 /// Draws a line between two coordinates.
217 void drawLine(double x1, double y1, double x2, double y2) { 280 void drawLine(double x1, double y1, double x2, double y2) {
  281 + assert(() {
  282 + if (!_page.pdfDocument.compress) {
  283 + _buf.putComment('drawLine x1:$x1 y1:$y1 x2:$x2 y2:$y2');
  284 + }
  285 + return true;
  286 + }());
  287 +
218 moveTo(x1, y1); 288 moveTo(x1, y1);
219 lineTo(x2, y2); 289 lineTo(x2, y2);
220 } 290 }
@@ -224,6 +294,13 @@ class PdfGraphics { @@ -224,6 +294,13 @@ class PdfGraphics {
224 /// Use clockwise=false to draw the inside of a donnnut 294 /// Use clockwise=false to draw the inside of a donnnut
225 void drawEllipse(double x, double y, double r1, double r2, 295 void drawEllipse(double x, double y, double r1, double r2,
226 {bool clockwise = true}) { 296 {bool clockwise = true}) {
  297 + assert(() {
  298 + if (!_page.pdfDocument.compress) {
  299 + _buf.putComment('drawEllipse x:$x y:$y r1:$r1 r2:$r2');
  300 + }
  301 + return true;
  302 + }());
  303 +
227 moveTo(x, y - r2); 304 moveTo(x, y - r2);
228 if (clockwise) { 305 if (clockwise) {
229 curveTo(x + _m4 * r1, y - r2, x + r1, y - _m4 * r2, x + r1, y); 306 curveTo(x + _m4 * r1, y - r2, x + r1, y - _m4 * r2, x + r1, y);
@@ -245,6 +322,13 @@ class PdfGraphics { @@ -245,6 +322,13 @@ class PdfGraphics {
245 double w, 322 double w,
246 double h, 323 double h,
247 ) { 324 ) {
  325 + assert(() {
  326 + if (!_page.pdfDocument.compress) {
  327 + _buf.putComment('drawRect x:$x y:$y w:$w h:$h');
  328 + }
  329 + return true;
  330 + }());
  331 +
248 PdfNumList([x, y, w, h]).output(_buf); 332 PdfNumList([x, y, w, h]).output(_buf);
249 _buf.putString(' re\n'); 333 _buf.putString(' re\n');
250 } 334 }
@@ -256,6 +340,13 @@ class PdfGraphics { @@ -256,6 +340,13 @@ class PdfGraphics {
256 340
257 /// Draws a Rounded Rectangle 341 /// Draws a Rounded Rectangle
258 void drawRRect(double x, double y, double w, double h, double rv, double rh) { 342 void drawRRect(double x, double y, double w, double h, double rv, double rh) {
  343 + assert(() {
  344 + if (!_page.pdfDocument.compress) {
  345 + _buf.putComment('drawRRect x:$x y:$y w:$w h:$h rv:$rv rh:$rh');
  346 + }
  347 + return true;
  348 + }());
  349 +
259 moveTo(x, y + rv); 350 moveTo(x, y + rv);
260 curveTo(x, y - _m4 * rv + rv, x - _m4 * rh + rh, y, x + rh, y); 351 curveTo(x, y - _m4 * rv + rv, x - _m4 * rh + rh, y, x + rh, y);
261 lineTo(x + w - rh, y); 352 lineTo(x + w - rh, y);
@@ -278,6 +369,13 @@ class PdfGraphics { @@ -278,6 +369,13 @@ class PdfGraphics {
278 PdfTextRenderingMode? mode = PdfTextRenderingMode.fill, 369 PdfTextRenderingMode? mode = PdfTextRenderingMode.fill,
279 double? rise, 370 double? rise,
280 }) { 371 }) {
  372 + assert(() {
  373 + if (!_page.pdfDocument.compress) {
  374 + _buf.putComment('setFont');
  375 + }
  376 + return true;
  377 + }());
  378 +
281 _buf.putString('${font.name} '); 379 _buf.putString('${font.name} ');
282 PdfNum(size).output(_buf); 380 PdfNum(size).output(_buf);
283 _buf.putString(' Tf\n'); 381 _buf.putString(' Tf\n');
@@ -315,6 +413,13 @@ class PdfGraphics { @@ -315,6 +413,13 @@ class PdfGraphics {
315 PdfTextRenderingMode mode = PdfTextRenderingMode.fill, 413 PdfTextRenderingMode mode = PdfTextRenderingMode.fill,
316 double rise = 0, 414 double rise = 0,
317 }) { 415 }) {
  416 + assert(() {
  417 + if (!_page.pdfDocument.compress) {
  418 + _buf.putComment('drawString x:$x y:$y size:$size "$s"');
  419 + }
  420 + return true;
  421 + }());
  422 +
318 _page.addFont(font); 423 _page.addFont(font);
319 424
320 _buf.putString('BT '); 425 _buf.putString('BT ');
@@ -332,6 +437,13 @@ class PdfGraphics { @@ -332,6 +437,13 @@ class PdfGraphics {
332 } 437 }
333 438
334 void reset() { 439 void reset() {
  440 + assert(() {
  441 + if (!_page.pdfDocument.compress) {
  442 + _buf.putComment('reset');
  443 + }
  444 + return true;
  445 + }());
  446 +
335 _buf.putString('0 Tr\n'); 447 _buf.putString('0 Tr\n');
336 } 448 }
337 449
@@ -343,6 +455,13 @@ class PdfGraphics { @@ -343,6 +455,13 @@ class PdfGraphics {
343 455
344 /// Sets the fill color for drawing 456 /// Sets the fill color for drawing
345 void setFillColor(PdfColor? color) { 457 void setFillColor(PdfColor? color) {
  458 + assert(() {
  459 + if (!_page.pdfDocument.compress) {
  460 + _buf.putComment('setFillColor ${color?.toHex()}');
  461 + }
  462 + return true;
  463 + }());
  464 +
346 if (color is PdfColorCmyk) { 465 if (color is PdfColorCmyk) {
347 PdfNumList(<double>[color.cyan, color.magenta, color.yellow, color.black]) 466 PdfNumList(<double>[color.cyan, color.magenta, color.yellow, color.black])
348 .output(_buf); 467 .output(_buf);
@@ -355,6 +474,13 @@ class PdfGraphics { @@ -355,6 +474,13 @@ class PdfGraphics {
355 474
356 /// Sets the stroke color for drawing 475 /// Sets the stroke color for drawing
357 void setStrokeColor(PdfColor? color) { 476 void setStrokeColor(PdfColor? color) {
  477 + assert(() {
  478 + if (!_page.pdfDocument.compress) {
  479 + _buf.putComment('setStrokeColor ${color?.toHex()}');
  480 + }
  481 + return true;
  482 + }());
  483 +
358 if (color is PdfColorCmyk) { 484 if (color is PdfColorCmyk) {
359 PdfNumList(<double>[color.cyan, color.magenta, color.yellow, color.black]) 485 PdfNumList(<double>[color.cyan, color.magenta, color.yellow, color.black])
360 .output(_buf); 486 .output(_buf);
@@ -367,6 +493,13 @@ class PdfGraphics { @@ -367,6 +493,13 @@ class PdfGraphics {
367 493
368 /// Sets the fill pattern for drawing 494 /// Sets the fill pattern for drawing
369 void setFillPattern(PdfPattern pattern) { 495 void setFillPattern(PdfPattern pattern) {
  496 + assert(() {
  497 + if (!_page.pdfDocument.compress) {
  498 + _buf.putComment('setFillPattern');
  499 + }
  500 + return true;
  501 + }());
  502 +
370 // The shader needs to be registered in the page resources 503 // The shader needs to be registered in the page resources
371 _page.addPattern(pattern); 504 _page.addPattern(pattern);
372 _buf.putString('/Pattern cs${pattern.name} scn\n'); 505 _buf.putString('/Pattern cs${pattern.name} scn\n');
@@ -374,6 +507,13 @@ class PdfGraphics { @@ -374,6 +507,13 @@ class PdfGraphics {
374 507
375 /// Sets the stroke pattern for drawing 508 /// Sets the stroke pattern for drawing
376 void setStrokePattern(PdfPattern pattern) { 509 void setStrokePattern(PdfPattern pattern) {
  510 + assert(() {
  511 + if (!_page.pdfDocument.compress) {
  512 + _buf.putComment('setStrokePattern');
  513 + }
  514 + return true;
  515 + }());
  516 +
377 // The shader needs to be registered in the page resources 517 // The shader needs to be registered in the page resources
378 _page.addPattern(pattern); 518 _page.addPattern(pattern);
379 _buf.putString('/Pattern CS${pattern.name} SCN\n'); 519 _buf.putString('/Pattern CS${pattern.name} SCN\n');
@@ -381,12 +521,26 @@ class PdfGraphics { @@ -381,12 +521,26 @@ class PdfGraphics {
381 521
382 /// Set the graphic state for drawing 522 /// Set the graphic state for drawing
383 void setGraphicState(PdfGraphicState state) { 523 void setGraphicState(PdfGraphicState state) {
  524 + assert(() {
  525 + if (!_page.pdfDocument.compress) {
  526 + _buf.putComment('setGraphicState $state');
  527 + }
  528 + return true;
  529 + }());
  530 +
384 final name = _page.stateName(state); 531 final name = _page.stateName(state);
385 _buf.putString('$name gs\n'); 532 _buf.putString('$name gs\n');
386 } 533 }
387 534
388 /// Set the transformation Matrix 535 /// Set the transformation Matrix
389 void setTransform(Matrix4 t) { 536 void setTransform(Matrix4 t) {
  537 + assert(() {
  538 + if (!_page.pdfDocument.compress) {
  539 + _buf.putComment('setTransform\n$t');
  540 + }
  541 + return true;
  542 + }());
  543 +
390 final s = t.storage; 544 final s = t.storage;
391 PdfNumList(<double>[s[0], s[1], s[4], s[5], s[12], s[13]]).output(_buf); 545 PdfNumList(<double>[s[0], s[1], s[4], s[5], s[12], s[13]]).output(_buf);
392 _buf.putString(' cm\n'); 546 _buf.putString(' cm\n');
@@ -400,12 +554,26 @@ class PdfGraphics { @@ -400,12 +554,26 @@ class PdfGraphics {
400 554
401 /// This adds a line segment to the current path 555 /// This adds a line segment to the current path
402 void lineTo(double x, double y) { 556 void lineTo(double x, double y) {
  557 + assert(() {
  558 + if (!_page.pdfDocument.compress) {
  559 + _buf.putComment('lineTo x:$x y:$y');
  560 + }
  561 + return true;
  562 + }());
  563 +
403 PdfNumList([x, y]).output(_buf); 564 PdfNumList([x, y]).output(_buf);
404 _buf.putString(' l\n'); 565 _buf.putString(' l\n');
405 } 566 }
406 567
407 /// This moves the current drawing point. 568 /// This moves the current drawing point.
408 void moveTo(double x, double y) { 569 void moveTo(double x, double y) {
  570 + assert(() {
  571 + if (!_page.pdfDocument.compress) {
  572 + _buf.putComment('moveTo x:$x y:$y');
  573 + }
  574 + return true;
  575 + }());
  576 +
409 PdfNumList([x, y]).output(_buf); 577 PdfNumList([x, y]).output(_buf);
410 _buf.putString(' m\n'); 578 _buf.putString(' m\n');
411 } 579 }
@@ -415,6 +583,13 @@ class PdfGraphics { @@ -415,6 +583,13 @@ class PdfGraphics {
415 /// and (x2,y2) as the control point at the end of the curve. 583 /// and (x2,y2) as the control point at the end of the curve.
416 void curveTo( 584 void curveTo(
417 double x1, double y1, double x2, double y2, double x3, double y3) { 585 double x1, double y1, double x2, double y2, double x3, double y3) {
  586 + assert(() {
  587 + if (!_page.pdfDocument.compress) {
  588 + _buf.putComment('curveTo x1:$x1 y1:$y1 x2:$x2 y2:$y2 x3:$x3 y3:$y3');
  589 + }
  590 + return true;
  591 + }());
  592 +
418 PdfNumList([x1, y1, x2, y2, x3, y3]).output(_buf); 593 PdfNumList([x1, y1, x2, y2, x3, y3]).output(_buf);
419 _buf.putString(' c\n'); 594 _buf.putString(' c\n');
420 } 595 }
@@ -576,22 +751,50 @@ class PdfGraphics { @@ -576,22 +751,50 @@ class PdfGraphics {
576 751
577 /// Set line starting and ending cap type 752 /// Set line starting and ending cap type
578 void setLineCap(PdfLineCap cap) { 753 void setLineCap(PdfLineCap cap) {
  754 + assert(() {
  755 + if (!_page.pdfDocument.compress) {
  756 + _buf.putComment('setLineCap $cap');
  757 + }
  758 + return true;
  759 + }());
  760 +
579 _buf.putString('${cap.index} J\n'); 761 _buf.putString('${cap.index} J\n');
580 } 762 }
581 763
582 /// Set line join type 764 /// Set line join type
583 void setLineJoin(PdfLineJoin join) { 765 void setLineJoin(PdfLineJoin join) {
  766 + assert(() {
  767 + if (!_page.pdfDocument.compress) {
  768 + _buf.putComment('setLineJoin $join');
  769 + }
  770 + return true;
  771 + }());
  772 +
584 _buf.putString('${join.index} j\n'); 773 _buf.putString('${join.index} j\n');
585 } 774 }
586 775
587 /// Set line width 776 /// Set line width
588 void setLineWidth(double width) { 777 void setLineWidth(double width) {
  778 + assert(() {
  779 + if (!_page.pdfDocument.compress) {
  780 + _buf.putComment('setLineWidth $width');
  781 + }
  782 + return true;
  783 + }());
  784 +
589 PdfNum(width).output(_buf); 785 PdfNum(width).output(_buf);
590 _buf.putString(' w\n'); 786 _buf.putString(' w\n');
591 } 787 }
592 788
593 /// Set line joint miter limit, applies if the 789 /// Set line joint miter limit, applies if the
594 void setMiterLimit(double limit) { 790 void setMiterLimit(double limit) {
  791 + assert(() {
  792 + if (!_page.pdfDocument.compress) {
  793 + _buf.putComment('setMiterLimit $limit');
  794 + }
  795 + return true;
  796 + }());
  797 +
595 assert(limit >= 1.0); 798 assert(limit >= 1.0);
596 PdfNum(limit).output(_buf); 799 PdfNum(limit).output(_buf);
597 _buf.putString(' M\n'); 800 _buf.putString(' M\n');
@@ -602,16 +805,37 @@ class PdfGraphics { @@ -602,16 +805,37 @@ class PdfGraphics {
602 /// 805 ///
603 /// Example: [2 1] will create a dash pattern with 2 on, 1 off, 2 on, 1 off, ... 806 /// Example: [2 1] will create a dash pattern with 2 on, 1 off, 2 on, 1 off, ...
604 void setLineDashPattern([List<num> array = const <num>[], int phase = 0]) { 807 void setLineDashPattern([List<num> array = const <num>[], int phase = 0]) {
  808 + assert(() {
  809 + if (!_page.pdfDocument.compress) {
  810 + _buf.putComment('setLineDashPattern $array phase:$phase');
  811 + }
  812 + return true;
  813 + }());
  814 +
605 PdfArray.fromNum(array).output(_buf); 815 PdfArray.fromNum(array).output(_buf);
606 _buf.putString(' $phase d\n'); 816 _buf.putString(' $phase d\n');
607 } 817 }
608 818
609 void markContentBegin(PdfName tag) { 819 void markContentBegin(PdfName tag) {
  820 + assert(() {
  821 + if (!_page.pdfDocument.compress) {
  822 + _buf.putComment('markContentBegin');
  823 + }
  824 + return true;
  825 + }());
  826 +
610 tag.output(_buf); 827 tag.output(_buf);
611 _buf.putString(' BMC\n'); 828 _buf.putString(' BMC\n');
612 } 829 }
613 830
614 void markContentEnd() { 831 void markContentEnd() {
  832 + assert(() {
  833 + if (!_page.pdfDocument.compress) {
  834 + _buf.putComment('markContentEnd');
  835 + }
  836 + return true;
  837 + }());
  838 +
615 _buf.putString('EMC\n'); 839 _buf.putString('EMC\n');
616 } 840 }
617 } 841 }
@@ -60,11 +60,20 @@ abstract class PdfObject<T extends PdfDataType> { @@ -60,11 +60,20 @@ abstract class PdfObject<T extends PdfDataType> {
60 /// The write method should call this before writing anything to the 60 /// The write method should call this before writing anything to the
61 /// OutputStream. This will send the standard header for each object. 61 /// OutputStream. This will send the standard header for each object.
62 void _writeStart(PdfStream os) { 62 void _writeStart(PdfStream os) {
  63 + assert(() {
  64 + if (!pdfDocument.compress) {
  65 + os.putComment('');
  66 + os.putComment('-' * 78);
  67 + os.putComment('$runtimeType');
  68 + }
  69 + return true;
  70 + }());
  71 +
63 os.putString('$objser $objgen obj\n'); 72 os.putString('$objser $objgen obj\n');
64 } 73 }
65 74
66 void writeContent(PdfStream os) { 75 void writeContent(PdfStream os) {
67 - params.output(os); 76 + params.output(os, pdfDocument.compress ? null : 0);
68 os.putByte(0x0a); 77 os.putByte(0x0a);
69 } 78 }
70 79
@@ -37,7 +37,7 @@ class PdfObjectDict extends PdfObject<PdfDict> { @@ -37,7 +37,7 @@ class PdfObjectDict extends PdfObject<PdfDict> {
37 @override 37 @override
38 void writeContent(PdfStream os) { 38 void writeContent(PdfStream os) {
39 if (params.isNotEmpty) { 39 if (params.isNotEmpty) {
40 - params.output(os); 40 + params.output(os, pdfDocument.compress ? null : 0);
41 os.putByte(0x0a); 41 os.putByte(0x0a);
42 } 42 }
43 } 43 }
@@ -41,6 +41,6 @@ class PdfObjectStream extends PdfObjectDict { @@ -41,6 +41,6 @@ class PdfObjectStream extends PdfObjectDict {
41 isBinary: isBinary, 41 isBinary: isBinary,
42 values: params.values, 42 values: params.values,
43 data: buf.output(), 43 data: buf.output(),
44 - ).output(os); 44 + ).output(os, pdfDocument.compress ? null : 0);
45 } 45 }
46 } 46 }
@@ -62,6 +62,9 @@ class PdfSoftMask { @@ -62,6 +62,9 @@ class PdfSoftMask {
62 62
63 PdfBaseFunction? _tr; 63 PdfBaseFunction? _tr;
64 64
  65 + @override
  66 + String toString() => '$runtimeType';
  67 +
65 PdfDict output() { 68 PdfDict output() {
66 final params = PdfDict({ 69 final params = PdfDict({
67 '/S': const PdfName('/Luminosity'), 70 '/S': const PdfName('/Luminosity'),
@@ -34,7 +34,7 @@ class PdfUnicodeCmap extends PdfObjectStream { @@ -34,7 +34,7 @@ class PdfUnicodeCmap extends PdfObjectStream {
34 cmap.fillRange(1, cmap.length, 0x20); 34 cmap.fillRange(1, cmap.length, 0x20);
35 } 35 }
36 36
37 - buf.putString('/CIDInit/ProcSet findresource begin\n' 37 + buf.putString('/CIDInit/ProcSet\nfindresource begin\n'
38 '12 dict begin\n' 38 '12 dict begin\n'
39 'begincmap\n' 39 'begincmap\n'
40 '/CIDSystemInfo<<\n' 40 '/CIDSystemInfo<<\n'
@@ -27,7 +27,7 @@ import 'xref.dart'; @@ -27,7 +27,7 @@ import 'xref.dart';
27 /// PDF document writer 27 /// PDF document writer
28 class PdfOutput { 28 class PdfOutput {
29 /// This creates a Pdf [PdfStream] 29 /// This creates a Pdf [PdfStream]
30 - PdfOutput(this.os, this.version) { 30 + PdfOutput(this.os, this.version, bool compress) {
31 String v; 31 String v;
32 switch (version) { 32 switch (version) {
33 case PdfVersion.pdf_1_4: 33 case PdfVersion.pdf_1_4:
@@ -40,7 +40,21 @@ class PdfOutput { @@ -40,7 +40,21 @@ class PdfOutput {
40 40
41 os.putString('%PDF-$v\n'); 41 os.putString('%PDF-$v\n');
42 os.putBytes(const <int>[0x25, 0xC2, 0xA5, 0xC2, 0xB1, 0xC3, 0xAB, 0x0A]); 42 os.putBytes(const <int>[0x25, 0xC2, 0xA5, 0xC2, 0xB1, 0xC3, 0xAB, 0x0A]);
  43 + assert(() {
  44 + if (!compress) {
  45 + _stopwatch = Stopwatch()..start();
  46 + os.putComment('');
  47 + os.putComment('Verbose dart_pdf');
  48 + os.putComment('Creation date: ${DateTime.now()}');
  49 + _comment = os.offset;
  50 + os.putBytes(List<int>.filled(120, 0x20));
43 } 51 }
  52 + return true;
  53 + }());
  54 + }
  55 +
  56 + late final Stopwatch _stopwatch;
  57 + var _comment = 0;
44 58
45 /// Pdf version to output 59 /// Pdf version to output
46 final PdfVersion version; 60 final PdfVersion version;
@@ -129,9 +143,32 @@ class PdfOutput { @@ -129,9 +143,32 @@ class PdfOutput {
129 os.putByte(0x0a); 143 os.putByte(0x0a);
130 } 144 }
131 145
  146 + assert(() {
  147 + if (!rootID!.pdfDocument.compress) {
  148 + os.putComment('');
  149 + os.putComment('-' * 78);
  150 + }
  151 + return true;
  152 + }());
  153 +
132 // the reference to the xref object 154 // the reference to the xref object
133 os.putString('startxref\n$_xref\n%%EOF\n'); 155 os.putString('startxref\n$_xref\n%%EOF\n');
134 156
  157 + assert(() {
  158 + if (!rootID!.pdfDocument.compress) {
  159 + _stopwatch.stop();
  160 + final h = PdfStream();
  161 + h.putComment(
  162 + 'Creation time: ${_stopwatch.elapsed.inMicroseconds / Duration.microsecondsPerSecond} seconds');
  163 + h.putComment('File size: ${os.offset} bytes');
  164 + h.putComment('Pages: ${rootID!.pdfDocument.pdfPageList.pages.length}');
  165 + h.putComment('Objects: ${xref.offsets.length}');
  166 +
  167 + os.setBytes(_comment, h.output());
  168 + }
  169 + return true;
  170 + }());
  171 +
135 if (signatureID != null) { 172 if (signatureID != null) {
136 await signatureID!.writeSignature(os); 173 await signatureID!.writeSignature(os);
137 } 174 }
@@ -68,4 +68,16 @@ class PdfStream { @@ -68,4 +68,16 @@ class PdfStream {
68 }()); 68 }());
69 putBytes(s!.codeUnits); 69 putBytes(s!.codeUnits);
70 } 70 }
  71 +
  72 + void putComment(String s) {
  73 + if (s.isEmpty) {
  74 + putByte(0x0a);
  75 + } else {
  76 + for (final l in s.split('\n')) {
  77 + if (l.isNotEmpty) {
  78 + putBytes('% $l\n'.codeUnits);
  79 + }
  80 + }
  81 + }
  82 + }
71 } 83 }
@@ -107,7 +107,7 @@ class PdfXrefTable extends PdfDataType { @@ -107,7 +107,7 @@ class PdfXrefTable extends PdfDataType {
107 } 107 }
108 108
109 @override 109 @override
110 - void output(PdfStream s) { 110 + void output(PdfStream s, [int? indent]) {
111 s.putString('xref\n'); 111 s.putString('xref\n');
112 112
113 // Now scan through the offsets list. They should be in sequence. 113 // Now scan through the offsets list. They should be in sequence.
@@ -153,8 +153,6 @@ class PdfXrefTable extends PdfDataType { @@ -153,8 +153,6 @@ class PdfXrefTable extends PdfDataType {
153 // Sort all references 153 // Sort all references
154 offsets.sort((a, b) => a.id.compareTo(b.id)); 154 offsets.sort((a, b) => a.id.compareTo(b.id));
155 155
156 - s.putString('$id 0 obj\n');  
157 -  
158 params['/Type'] = const PdfName('/XRef'); 156 params['/Type'] = const PdfName('/XRef');
159 params['/Size'] = PdfNum(id + 1); 157 params['/Size'] = PdfNum(id + 1);
160 158
@@ -200,12 +198,22 @@ class PdfXrefTable extends PdfDataType { @@ -200,12 +198,22 @@ class PdfXrefTable extends PdfDataType {
200 } 198 }
201 199
202 // Write the object 200 // Write the object
  201 + assert(() {
  202 + if (!object.pdfDocument.compress) {
  203 + s.putComment('');
  204 + s.putComment('-' * 78);
  205 + s.putComment('$runtimeType $this');
  206 + }
  207 + return true;
  208 + }());
  209 + s.putString('$id 0 obj\n');
  210 +
203 PdfDictStream( 211 PdfDictStream(
204 object: object, 212 object: object,
205 data: o.buffer.asUint8List(), 213 data: o.buffer.asUint8List(),
206 isBinary: false, 214 isBinary: false,
207 encrypt: false, 215 encrypt: false,
208 values: params.values, 216 values: params.values,
209 - ).output(s); 217 + ).output(s, object.pdfDocument.compress ? null : 0);
210 } 218 }
211 } 219 }