David PHAM-VAN

Add PdfDictStream

@@ -20,6 +20,7 @@ import 'dart:typed_data'; @@ -20,6 +20,7 @@ import 'dart:typed_data';
20 20
21 import 'package:meta/meta.dart'; 21 import 'package:meta/meta.dart';
22 22
  23 +import 'ascii85.dart';
23 import 'color.dart'; 24 import 'color.dart';
24 import 'object.dart'; 25 import 'object.dart';
25 import 'stream.dart'; 26 import 'stream.dart';
@@ -113,7 +114,7 @@ class PdfNum extends PdfDataType { @@ -113,7 +114,7 @@ class PdfNum extends PdfDataType {
113 } 114 }
114 115
115 class PdfNumList extends PdfDataType { 116 class PdfNumList extends PdfDataType {
116 - PdfNumList(this.values); 117 + const PdfNumList(this.values);
117 118
118 final List<num> values; 119 final List<num> values;
119 120
@@ -511,12 +512,16 @@ class PdfArray<T extends PdfDataType> extends PdfDataType { @@ -511,12 +512,16 @@ class PdfArray<T extends PdfDataType> extends PdfDataType {
511 } 512 }
512 513
513 class PdfDict<T extends PdfDataType> extends PdfDataType { 514 class PdfDict<T extends PdfDataType> extends PdfDataType {
514 - PdfDict([Map<String, T>? values]) { 515 + factory PdfDict([Map<String, T>? values]) {
  516 + final _values = <String, T>{};
515 if (values != null) { 517 if (values != null) {
516 - this.values.addAll(values); 518 + _values.addAll(values);
517 } 519 }
  520 + return PdfDict.values(_values);
518 } 521 }
519 522
  523 + const PdfDict.values([this.values = const {}]);
  524 +
520 static PdfDict<PdfIndirect> fromObjectMap(Map<String, PdfObject> objects) { 525 static PdfDict<PdfIndirect> fromObjectMap(Map<String, PdfObject> objects) {
521 return PdfDict( 526 return PdfDict(
522 objects.map<String, PdfIndirect>( 527 objects.map<String, PdfIndirect>(
@@ -526,7 +531,7 @@ class PdfDict<T extends PdfDataType> extends PdfDataType { @@ -526,7 +531,7 @@ class PdfDict<T extends PdfDataType> extends PdfDataType {
526 ); 531 );
527 } 532 }
528 533
529 - final values = <String, T>{}; 534 + final Map<String, T> values;
530 535
531 bool get isNotEmpty => values.isNotEmpty; 536 bool get isNotEmpty => values.isNotEmpty;
532 537
@@ -589,6 +594,63 @@ class PdfDict<T extends PdfDataType> extends PdfDataType { @@ -589,6 +594,63 @@ class PdfDict<T extends PdfDataType> extends PdfDataType {
589 int get hashCode => values.hashCode; 594 int get hashCode => values.hashCode;
590 } 595 }
591 596
  597 +class PdfDictStream extends PdfDict<PdfDataType> {
  598 + const PdfDictStream({
  599 + required this.object,
  600 + Map<String, PdfDataType> values = const <String, PdfDataType>{},
  601 + required this.data,
  602 + this.isBinary = false,
  603 + }) : super.values(values);
  604 +
  605 + final Uint8List data;
  606 +
  607 + final PdfObject object;
  608 +
  609 + final bool isBinary;
  610 +
  611 + @override
  612 + void output(PdfStream s) {
  613 + final _values = PdfDict(values);
  614 +
  615 + Uint8List? _data;
  616 +
  617 + if (_values.containsKey('/Filter')) {
  618 + // The data is already in the right format
  619 + _data = data;
  620 + } else if (object.pdfDocument.deflate != null) {
  621 + // Compress the data
  622 + final newData = Uint8List.fromList(object.pdfDocument.deflate!(data));
  623 + if (newData.lengthInBytes < data.lengthInBytes) {
  624 + _values['/Filter'] = const PdfName('/FlateDecode');
  625 + _data = newData;
  626 + }
  627 + }
  628 +
  629 + if (_data == null) {
  630 + if (isBinary) {
  631 + // This is an Ascii85 stream
  632 + final e = Ascii85Encoder();
  633 + _data = e.convert(data);
  634 + _values['/Filter'] = const PdfName('/ASCII85Decode');
  635 + } else {
  636 + // This is a non-deflated stream
  637 + _data = data;
  638 + }
  639 + }
  640 +
  641 + if (object.pdfDocument.encryption != null) {
  642 + _data = object.pdfDocument.encryption!.encrypt(_data, object);
  643 + }
  644 +
  645 + _values['/Length'] = PdfNum(_data.length);
  646 +
  647 + _values.output(s);
  648 + s.putString('stream\n');
  649 + s.putBytes(_data);
  650 + s.putString('\nendstream\n');
  651 + }
  652 +}
  653 +
592 class PdfColorType extends PdfDataType { 654 class PdfColorType extends PdfDataType {
593 const PdfColorType(this.color); 655 const PdfColorType(this.color);
594 656
@@ -38,6 +38,10 @@ void main() { @@ -38,6 +38,10 @@ void main() {
38 expect(const PdfNum(50.1).toString(), '50.1'); 38 expect(const PdfNum(50.1).toString(), '50.1');
39 }); 39 });
40 40
  41 + test('PdfDataTypes NumList', () {
  42 + expect(const PdfNumList([0, 1, 2, 3]).toString(), '0 1 2 3');
  43 + });
  44 +
41 test('PdfDataTypes String', () { 45 test('PdfDataTypes String', () {
42 expect(PdfString.fromString('test').toString(), '(test)'); 46 expect(PdfString.fromString('test').toString(), '(test)');
43 expect(PdfString.fromString('Zoé').toString(), '(Zoé)'); 47 expect(PdfString.fromString('Zoé').toString(), '(Zoé)');
@@ -105,8 +109,8 @@ void main() { @@ -105,8 +109,8 @@ void main() {
105 '/String': PdfString.fromString('hello'), 109 '/String': PdfString.fromString('hello'),
106 '/Null': const PdfNull(), 110 '/Null': const PdfNull(),
107 '/Indirect': const PdfIndirect(55, 0), 111 '/Indirect': const PdfIndirect(55, 0),
108 - '/Array': PdfArray<PdfDataType>([]),  
109 - '/Dict': PdfDict<PdfDataType>({}), 112 + '/Array': PdfArray(),
  113 + '/Dict': PdfDict(),
110 }).toString(), 114 }).toString(),
111 '<</Name/Value/Bool true/Num 42/String(hello)/Null null/Indirect 55 0 R/Array[]/Dict<<>>>>', 115 '<</Name/Value/Bool true/Num 42/String(hello)/Null null/Indirect 55 0 R/Array[]/Dict<<>>>>',
112 ); 116 );