Showing
9 changed files
with
150 additions
and
18 deletions
1 | # Changelog | 1 | # Changelog |
2 | 2 | ||
3 | -## 3.1.1 | 3 | +## 3.2.0 |
4 | 4 | ||
5 | - Fix documentation | 5 | - Fix documentation |
6 | - Add Positioned.fill() | 6 | - Add Positioned.fill() |
7 | - Improve GraphicState | 7 | - Improve GraphicState |
8 | - Add SVG Color filter | 8 | - Add SVG Color filter |
9 | +- Implement Compressed XREF | ||
9 | 10 | ||
10 | ## 3.1.0 | 11 | ## 3.1.0 |
11 | 12 |
@@ -57,7 +57,7 @@ class PdfCatalog extends PdfObjectDict { | @@ -57,7 +57,7 @@ class PdfCatalog extends PdfObjectDict { | ||
57 | super.prepare(); | 57 | super.prepare(); |
58 | 58 | ||
59 | /// the PDF specification version, overrides the header version starting from 1.4 | 59 | /// the PDF specification version, overrides the header version starting from 1.4 |
60 | - params['/Version'] = PdfName('/${pdfDocument.version}'); | 60 | + params['/Version'] = PdfName('/${pdfDocument.versionString}'); |
61 | 61 | ||
62 | params['/Pages'] = pdfPageList.ref(); | 62 | params['/Pages'] = pdfPageList.ref(); |
63 | 63 |
@@ -600,6 +600,7 @@ class PdfDictStream extends PdfDict<PdfDataType> { | @@ -600,6 +600,7 @@ class PdfDictStream extends PdfDict<PdfDataType> { | ||
600 | Map<String, PdfDataType> values = const <String, PdfDataType>{}, | 600 | Map<String, PdfDataType> values = const <String, PdfDataType>{}, |
601 | required this.data, | 601 | required this.data, |
602 | this.isBinary = false, | 602 | this.isBinary = false, |
603 | + this.encrypt = true, | ||
603 | }) : super.values(values); | 604 | }) : super.values(values); |
604 | 605 | ||
605 | final Uint8List data; | 606 | final Uint8List data; |
@@ -608,6 +609,8 @@ class PdfDictStream extends PdfDict<PdfDataType> { | @@ -608,6 +609,8 @@ class PdfDictStream extends PdfDict<PdfDataType> { | ||
608 | 609 | ||
609 | final bool isBinary; | 610 | final bool isBinary; |
610 | 611 | ||
612 | + final bool encrypt; | ||
613 | + | ||
611 | @override | 614 | @override |
612 | void output(PdfStream s) { | 615 | void output(PdfStream s) { |
613 | final _values = PdfDict(values); | 616 | final _values = PdfDict(values); |
@@ -638,7 +641,7 @@ class PdfDictStream extends PdfDict<PdfDataType> { | @@ -638,7 +641,7 @@ class PdfDictStream extends PdfDict<PdfDataType> { | ||
638 | } | 641 | } |
639 | } | 642 | } |
640 | 643 | ||
641 | - if (object.pdfDocument.encryption != null) { | 644 | + if (encrypt && object.pdfDocument.encryption != null) { |
642 | _data = object.pdfDocument.encryption!.encrypt(_data, object); | 645 | _data = object.pdfDocument.encryption!.encrypt(_data, object); |
643 | } | 646 | } |
644 | 647 |
@@ -35,6 +35,15 @@ import 'page_list.dart'; | @@ -35,6 +35,15 @@ import 'page_list.dart'; | ||
35 | import 'signature.dart'; | 35 | import 'signature.dart'; |
36 | import 'stream.dart'; | 36 | import 'stream.dart'; |
37 | 37 | ||
38 | +/// PDF version to generate | ||
39 | +enum PdfVersion { | ||
40 | + /// PDF 1.4 | ||
41 | + pdf_1_4, | ||
42 | + | ||
43 | + /// PDF 1.5 to 1.7 | ||
44 | + pdf_1_5, | ||
45 | +} | ||
46 | + | ||
38 | /// Display hint for the PDF viewer | 47 | /// Display hint for the PDF viewer |
39 | enum PdfPageMode { | 48 | enum PdfPageMode { |
40 | /// This page mode indicates that the document | 49 | /// This page mode indicates that the document |
@@ -69,6 +78,7 @@ class PdfDocument { | @@ -69,6 +78,7 @@ class PdfDocument { | ||
69 | PdfPageMode pageMode = PdfPageMode.none, | 78 | PdfPageMode pageMode = PdfPageMode.none, |
70 | DeflateCallback? deflate, | 79 | DeflateCallback? deflate, |
71 | bool compress = true, | 80 | bool compress = true, |
81 | + this.version = PdfVersion.pdf_1_4, | ||
72 | }) : deflate = compress ? (deflate ?? defaultDeflate) : null, | 82 | }) : deflate = compress ? (deflate ?? defaultDeflate) : null, |
73 | prev = null, | 83 | prev = null, |
74 | _objser = 1 { | 84 | _objser = 1 { |
@@ -84,7 +94,8 @@ class PdfDocument { | @@ -84,7 +94,8 @@ class PdfDocument { | ||
84 | DeflateCallback? deflate, | 94 | DeflateCallback? deflate, |
85 | bool compress = true, | 95 | bool compress = true, |
86 | }) : deflate = compress ? (deflate ?? defaultDeflate) : null, | 96 | }) : deflate = compress ? (deflate ?? defaultDeflate) : null, |
87 | - _objser = prev!.size { | 97 | + _objser = prev!.size, |
98 | + version = prev.version { | ||
88 | // Now create some standard objects | 99 | // Now create some standard objects |
89 | pdfPageList = PdfPageList(this); | 100 | pdfPageList = PdfPageList(this); |
90 | pdfNames = PdfNames(this); | 101 | pdfNames = PdfNames(this); |
@@ -107,6 +118,9 @@ class PdfDocument { | @@ -107,6 +118,9 @@ class PdfDocument { | ||
107 | /// This is the Catalog object, which is required by each Pdf Document | 118 | /// This is the Catalog object, which is required by each Pdf Document |
108 | late PdfCatalog catalog; | 119 | late PdfCatalog catalog; |
109 | 120 | ||
121 | + /// PDF version to generate | ||
122 | + final PdfVersion version; | ||
123 | + | ||
110 | /// This is the info object. Although this is an optional object, we | 124 | /// This is the info object. Although this is an optional object, we |
111 | /// include it. | 125 | /// include it. |
112 | PdfInfo? info; | 126 | PdfInfo? info; |
@@ -139,7 +153,7 @@ class PdfDocument { | @@ -139,7 +153,7 @@ class PdfDocument { | ||
139 | PdfGraphicStates? _graphicStates; | 153 | PdfGraphicStates? _graphicStates; |
140 | 154 | ||
141 | /// The PDF specification version | 155 | /// The PDF specification version |
142 | - final String version = '1.7'; | 156 | + final String versionString = '1.7'; |
143 | 157 | ||
144 | /// This holds the current fonts | 158 | /// This holds the current fonts |
145 | final Set<PdfFont> fonts = <PdfFont>{}; | 159 | final Set<PdfFont> fonts = <PdfFont>{}; |
@@ -188,7 +202,7 @@ class PdfDocument { | @@ -188,7 +202,7 @@ class PdfDocument { | ||
188 | 202 | ||
189 | /// This writes the document to an OutputStream. | 203 | /// This writes the document to an OutputStream. |
190 | Future<void> _write(PdfStream os) async { | 204 | Future<void> _write(PdfStream os) async { |
191 | - final pos = PdfOutput(os); | 205 | + final pos = PdfOutput(os, version); |
192 | 206 | ||
193 | // Write each object to the [PdfStream]. We call via the output | 207 | // Write each object to the [PdfStream]. We call via the output |
194 | // as that builds the xref table | 208 | // as that builds the xref table |
@@ -32,6 +32,8 @@ abstract class PdfDocumentParserBase { | @@ -32,6 +32,8 @@ abstract class PdfDocumentParserBase { | ||
32 | /// The offset of the previous cross reference table | 32 | /// The offset of the previous cross reference table |
33 | int get xrefOffset; | 33 | int get xrefOffset; |
34 | 34 | ||
35 | + PdfVersion get version => PdfVersion.pdf_1_4; | ||
36 | + | ||
35 | /// Import the existing objects into the new PDF document | 37 | /// Import the existing objects into the new PDF document |
36 | void mergeDocument(PdfDocument pdfDocument); | 38 | void mergeDocument(PdfDocument pdfDocument); |
37 | } | 39 | } |
@@ -16,6 +16,7 @@ | @@ -16,6 +16,7 @@ | ||
16 | 16 | ||
17 | import 'catalog.dart'; | 17 | import 'catalog.dart'; |
18 | import 'data_types.dart'; | 18 | import 'data_types.dart'; |
19 | +import 'document.dart'; | ||
19 | import 'encryption.dart'; | 20 | import 'encryption.dart'; |
20 | import 'info.dart'; | 21 | import 'info.dart'; |
21 | import 'object.dart'; | 22 | import 'object.dart'; |
@@ -26,11 +27,24 @@ import 'xref.dart'; | @@ -26,11 +27,24 @@ import 'xref.dart'; | ||
26 | /// PDF document writer | 27 | /// PDF document writer |
27 | class PdfOutput { | 28 | class PdfOutput { |
28 | /// This creates a Pdf [PdfStream] | 29 | /// This creates a Pdf [PdfStream] |
29 | - PdfOutput(this.os) { | ||
30 | - os.putString('%PDF-1.4\n'); | 30 | + PdfOutput(this.os, this.version) { |
31 | + String v; | ||
32 | + switch (version) { | ||
33 | + case PdfVersion.pdf_1_4: | ||
34 | + v = '1.4'; | ||
35 | + break; | ||
36 | + case PdfVersion.pdf_1_5: | ||
37 | + v = '1.5'; | ||
38 | + break; | ||
39 | + } | ||
40 | + | ||
41 | + os.putString('%PDF-$v\n'); | ||
31 | 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]); |
32 | } | 43 | } |
33 | 44 | ||
45 | + /// Pdf version to output | ||
46 | + final PdfVersion version; | ||
47 | + | ||
34 | /// This is the actual [PdfStream] used to write to. | 48 | /// This is the actual [PdfStream] used to write to. |
35 | final PdfStream os; | 49 | final PdfStream os; |
36 | 50 | ||
@@ -49,6 +63,9 @@ class PdfOutput { | @@ -49,6 +63,9 @@ class PdfOutput { | ||
49 | /// This is used to track the /Sign object (signature) | 63 | /// This is used to track the /Sign object (signature) |
50 | PdfSignature? signatureID; | 64 | PdfSignature? signatureID; |
51 | 65 | ||
66 | + /// Generate a compressed cross reference table | ||
67 | + bool get isCompressed => version.index > PdfVersion.pdf_1_4.index; | ||
68 | + | ||
52 | /// This method writes a [PdfObject] to the stream. | 69 | /// This method writes a [PdfObject] to the stream. |
53 | void write(PdfObject ob) { | 70 | void write(PdfObject ob) { |
54 | // Check the object to see if it's one that is needed later | 71 | // Check the object to see if it's one that is needed later |
@@ -73,12 +90,6 @@ class PdfOutput { | @@ -73,12 +90,6 @@ class PdfOutput { | ||
73 | throw Exception('Root object is not present in document'); | 90 | throw Exception('Root object is not present in document'); |
74 | } | 91 | } |
75 | 92 | ||
76 | - final _xref = os.offset; | ||
77 | - xref.output(os); | ||
78 | - | ||
79 | - // now the trailer object | ||
80 | - os.putString('trailer\n'); | ||
81 | - | ||
82 | final params = PdfDict(); | 93 | final params = PdfDict(); |
83 | 94 | ||
84 | // the number of entries (REQUIRED) | 95 | // the number of entries (REQUIRED) |
@@ -104,9 +115,22 @@ class PdfOutput { | @@ -104,9 +115,22 @@ class PdfOutput { | ||
104 | params['/Prev'] = PdfNum(rootID!.pdfDocument.prev!.xrefOffset); | 115 | params['/Prev'] = PdfNum(rootID!.pdfDocument.prev!.xrefOffset); |
105 | } | 116 | } |
106 | 117 | ||
107 | - // end the trailer object | ||
108 | - params.output(os); | ||
109 | - os.putString('\nstartxref\n$_xref\n%%EOF\n'); | 118 | + final _xref = os.offset; |
119 | + if (isCompressed) { | ||
120 | + xref.outputCompressed(rootID!, os, params); | ||
121 | + } else { | ||
122 | + xref.output(os); | ||
123 | + } | ||
124 | + | ||
125 | + if (!isCompressed) { | ||
126 | + // the trailer object | ||
127 | + os.putString('trailer\n'); | ||
128 | + params.output(os); | ||
129 | + os.putByte(0x0a); | ||
130 | + } | ||
131 | + | ||
132 | + // the reference to the xref object | ||
133 | + os.putString('startxref\n$_xref\n%%EOF\n'); | ||
110 | 134 | ||
111 | if (signatureID != null) { | 135 | if (signatureID != null) { |
112 | await signatureID!.writeSignature(os); | 136 | await signatureID!.writeSignature(os); |
@@ -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:pdf/src/pdf/stream.dart'; | 19 | import 'package:pdf/src/pdf/stream.dart'; |
18 | 20 | ||
19 | import 'data_types.dart'; | 21 | import 'data_types.dart'; |
@@ -45,6 +47,24 @@ class PdfXref { | @@ -45,6 +47,24 @@ class PdfXref { | ||
45 | return rs + ' n '; | 47 | return rs + ' n '; |
46 | } | 48 | } |
47 | 49 | ||
50 | + /// The xref in the format of the compressed xref section in the Pdf file | ||
51 | + int cref(ByteData o, int ofs, List<int> w) { | ||
52 | + assert(w.length >= 3); | ||
53 | + | ||
54 | + void setVal(int l, int v) { | ||
55 | + for (var n = 0; n < l; n++) { | ||
56 | + o.setUint8(ofs, (v >> (l - n - 1) * 8) & 0xff); | ||
57 | + ofs++; | ||
58 | + } | ||
59 | + } | ||
60 | + | ||
61 | + setVal(w[0], 1); | ||
62 | + setVal(w[1], offset); | ||
63 | + setVal(w[2], generation); | ||
64 | + | ||
65 | + return ofs; | ||
66 | + } | ||
67 | + | ||
48 | @override | 68 | @override |
49 | bool operator ==(Object other) { | 69 | bool operator ==(Object other) { |
50 | if (other is PdfXref) { | 70 | if (other is PdfXref) { |
@@ -113,4 +133,70 @@ class PdfXrefTable extends PdfDataType { | @@ -113,4 +133,70 @@ class PdfXrefTable extends PdfDataType { | ||
113 | // now write the last block | 133 | // now write the last block |
114 | _writeblock(s, firstid, block); | 134 | _writeblock(s, firstid, block); |
115 | } | 135 | } |
136 | + | ||
137 | + /// Output a compressed cross-reference table | ||
138 | + void outputCompressed(PdfObject object, PdfStream s, PdfDict params) { | ||
139 | + // Write this object too | ||
140 | + final id = offsets.last.id + 1; | ||
141 | + final offset = s.offset; | ||
142 | + offsets.add(PdfXref(id, offset)); | ||
143 | + | ||
144 | + // Sort all references | ||
145 | + offsets.sort((a, b) => a.id.compareTo(b.id)); | ||
146 | + | ||
147 | + s.putString('$id 0 obj\n'); | ||
148 | + | ||
149 | + params['/Type'] = const PdfName('/XRef'); | ||
150 | + params['/Size'] = PdfNum(id + 1); | ||
151 | + | ||
152 | + var firstid = 0; // First id in block | ||
153 | + var lastid = 0; // The last id used | ||
154 | + final blocks = <int>[]; // xrefs in this block first, count | ||
155 | + | ||
156 | + // We need block 0 to exist | ||
157 | + blocks.add(firstid); | ||
158 | + | ||
159 | + for (var x in offsets) { | ||
160 | + // check to see if block is in range | ||
161 | + if (x.id != (lastid + 1)) { | ||
162 | + // no, so store this block, and reset | ||
163 | + blocks.add(lastid - firstid + 1); | ||
164 | + firstid = x.id; | ||
165 | + blocks.add(firstid); | ||
166 | + } | ||
167 | + lastid = x.id; | ||
168 | + } | ||
169 | + blocks.add(lastid - firstid + 1); | ||
170 | + | ||
171 | + if (!(blocks.length == 2 && blocks[0] == 0 && blocks[1] == id + 1)) { | ||
172 | + params['/Index'] = PdfArray.fromNum(blocks); | ||
173 | + } | ||
174 | + | ||
175 | + var bytes = 2; // A pdf less than 256 bytes is unlikely | ||
176 | + while (1 << (bytes * 8) < offset) { | ||
177 | + bytes++; | ||
178 | + } | ||
179 | + | ||
180 | + final w = [1, bytes, 1]; | ||
181 | + params['/W'] = PdfArray.fromNum(w); | ||
182 | + final wl = w.reduce((a, b) => a + b); | ||
183 | + | ||
184 | + final o = ByteData((offsets.length + 2) * wl); | ||
185 | + var ofs = 0; | ||
186 | + // Write offset zero, all zeros | ||
187 | + ofs += wl; | ||
188 | + | ||
189 | + for (var x in offsets) { | ||
190 | + ofs = x.cref(o, ofs, w); | ||
191 | + } | ||
192 | + | ||
193 | + // Write the object | ||
194 | + PdfDictStream( | ||
195 | + object: object, | ||
196 | + data: o.buffer.asUint8List(), | ||
197 | + isBinary: true, | ||
198 | + encrypt: false, | ||
199 | + values: params.values, | ||
200 | + ).output(s); | ||
201 | + } | ||
116 | } | 202 | } |
@@ -26,6 +26,7 @@ class Document { | @@ -26,6 +26,7 @@ class Document { | ||
26 | PdfPageMode pageMode = PdfPageMode.none, | 26 | PdfPageMode pageMode = PdfPageMode.none, |
27 | DeflateCallback? deflate, | 27 | DeflateCallback? deflate, |
28 | bool compress = true, | 28 | bool compress = true, |
29 | + PdfVersion version = PdfVersion.pdf_1_4, | ||
29 | this.theme, | 30 | this.theme, |
30 | String? title, | 31 | String? title, |
31 | String? author, | 32 | String? author, |
@@ -37,6 +38,7 @@ class Document { | @@ -37,6 +38,7 @@ class Document { | ||
37 | pageMode: pageMode, | 38 | pageMode: pageMode, |
38 | deflate: deflate, | 39 | deflate: deflate, |
39 | compress: compress, | 40 | compress: compress, |
41 | + version: version, | ||
40 | ) { | 42 | ) { |
41 | if (title != null || | 43 | if (title != null || |
42 | author != null || | 44 | author != null || |
@@ -4,7 +4,7 @@ description: A pdf producer for Dart. It can create pdf files for both web or fl | @@ -4,7 +4,7 @@ description: A pdf producer for Dart. It can create pdf files for both web or fl | ||
4 | homepage: https://github.com/DavBfr/dart_pdf/tree/master/pdf | 4 | homepage: https://github.com/DavBfr/dart_pdf/tree/master/pdf |
5 | repository: https://github.com/DavBfr/dart_pdf | 5 | repository: https://github.com/DavBfr/dart_pdf |
6 | issue_tracker: https://github.com/DavBfr/dart_pdf/issues | 6 | issue_tracker: https://github.com/DavBfr/dart_pdf/issues |
7 | -version: 3.1.1 | 7 | +version: 3.2.0 |
8 | 8 | ||
9 | environment: | 9 | environment: |
10 | sdk: ">=2.12.0-0 <3.0.0" | 10 | sdk: ">=2.12.0-0 <3.0.0" |
-
Please register or login to post a comment