David PHAM-VAN

Add document loading

@@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
7 - Add GridPaper widget 7 - Add GridPaper widget
8 - Improve internal sructure 8 - Improve internal sructure
9 - Add some asserts on the TtfParser 9 - Add some asserts on the TtfParser
  10 +- Add document loading
10 11
11 ## 1.13.0 12 ## 1.13.0
12 13
@@ -92,9 +92,10 @@ final file = File("example.pdf"); @@ -92,9 +92,10 @@ final file = File("example.pdf");
92 await file.writeAsBytes(pdf.save()); 92 await file.writeAsBytes(pdf.save());
93 ``` 93 ```
94 94
95 -## Encryption and Digital Signature 95 +## Encryption, Digital Signature, and loading a PDF Document
96 96
97 Encryption using RC4-40, RC4-128, AES-128, and AES-256 is fully supported using a separate library. 97 Encryption using RC4-40, RC4-128, AES-128, and AES-256 is fully supported using a separate library.
98 This library also provides SHA1 or SHA-256 Digital Signature using your x509 certificate. The graphic signature is represented by a clickable widget that shows Digital Signature information. 98 This library also provides SHA1 or SHA-256 Digital Signature using your x509 certificate. The graphic signature is represented by a clickable widget that shows Digital Signature information.
  99 +It implememts a PDF parser to load an existing document and add pages, change pages, and add a signature.
99 100
100 Drop me an email for availability and more information. 101 Drop me an email for availability and more information.
@@ -20,6 +20,7 @@ export 'src/border.dart'; @@ -20,6 +20,7 @@ export 'src/border.dart';
20 export 'src/color.dart'; 20 export 'src/color.dart';
21 export 'src/colors.dart'; 21 export 'src/colors.dart';
22 export 'src/document.dart'; 22 export 'src/document.dart';
  23 +export 'src/document_parser.dart';
23 export 'src/encryption.dart'; 24 export 'src/encryption.dart';
24 export 'src/exif.dart'; 25 export 'src/exif.dart';
25 export 'src/font.dart'; 26 export 'src/font.dart';
@@ -14,6 +14,7 @@ @@ -14,6 +14,7 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
  17 +import 'dart:collection';
17 import 'dart:convert'; 18 import 'dart:convert';
18 import 'dart:typed_data'; 19 import 'dart:typed_data';
19 20
@@ -54,6 +55,18 @@ class PdfBool extends PdfDataType { @@ -54,6 +55,18 @@ class PdfBool extends PdfDataType {
54 void output(PdfStream s) { 55 void output(PdfStream s) {
55 s.putString(value ? 'true' : 'false'); 56 s.putString(value ? 'true' : 'false');
56 } 57 }
  58 +
  59 + @override
  60 + bool operator ==(Object other) {
  61 + if (other is PdfBool) {
  62 + return value == other.value;
  63 + }
  64 +
  65 + return false;
  66 + }
  67 +
  68 + @override
  69 + int get hashCode => value.hashCode;
57 } 70 }
58 71
59 class PdfNum extends PdfDataType { 72 class PdfNum extends PdfDataType {
@@ -86,6 +99,18 @@ class PdfNum extends PdfDataType { @@ -86,6 +99,18 @@ class PdfNum extends PdfDataType {
86 s.putString(r); 99 s.putString(r);
87 } 100 }
88 } 101 }
  102 +
  103 + @override
  104 + bool operator ==(Object other) {
  105 + if (other is PdfNum) {
  106 + return value == other.value;
  107 + }
  108 +
  109 + return false;
  110 + }
  111 +
  112 + @override
  113 + int get hashCode => value.hashCode;
89 } 114 }
90 115
91 class PdfNumList extends PdfDataType { 116 class PdfNumList extends PdfDataType {
@@ -102,6 +127,18 @@ class PdfNumList extends PdfDataType { @@ -102,6 +127,18 @@ class PdfNumList extends PdfDataType {
102 PdfNum(values[n]).output(s); 127 PdfNum(values[n]).output(s);
103 } 128 }
104 } 129 }
  130 +
  131 + @override
  132 + bool operator ==(Object other) {
  133 + if (other is PdfNumList) {
  134 + return values == other.values;
  135 + }
  136 +
  137 + return false;
  138 + }
  139 +
  140 + @override
  141 + int get hashCode => values.hashCode;
105 } 142 }
106 143
107 enum PdfStringFormat { binary, litteral } 144 enum PdfStringFormat { binary, litteral }
@@ -255,6 +292,18 @@ class PdfString extends PdfDataType { @@ -255,6 +292,18 @@ class PdfString extends PdfDataType {
255 void output(PdfStream s) { 292 void output(PdfStream s) {
256 _output(s, value); 293 _output(s, value);
257 } 294 }
  295 +
  296 + @override
  297 + bool operator ==(Object other) {
  298 + if (other is PdfString) {
  299 + return value == other.value;
  300 + }
  301 +
  302 + return false;
  303 + }
  304 +
  305 + @override
  306 + int get hashCode => value.hashCode;
258 } 307 }
259 308
260 class PdfSecString extends PdfString { 309 class PdfSecString extends PdfString {
@@ -337,6 +386,18 @@ class PdfName extends PdfDataType { @@ -337,6 +386,18 @@ class PdfName extends PdfDataType {
337 } 386 }
338 s.putBytes(bytes); 387 s.putBytes(bytes);
339 } 388 }
  389 +
  390 + @override
  391 + bool operator ==(Object other) {
  392 + if (other is PdfName) {
  393 + return value == other.value;
  394 + }
  395 +
  396 + return false;
  397 + }
  398 +
  399 + @override
  400 + int get hashCode => value.hashCode;
340 } 401 }
341 402
342 class PdfNull extends PdfDataType { 403 class PdfNull extends PdfDataType {
@@ -346,6 +407,14 @@ class PdfNull extends PdfDataType { @@ -346,6 +407,14 @@ class PdfNull extends PdfDataType {
346 void output(PdfStream s) { 407 void output(PdfStream s) {
347 s.putString('null'); 408 s.putString('null');
348 } 409 }
  410 +
  411 + @override
  412 + bool operator ==(Object other) {
  413 + return other is PdfNull;
  414 + }
  415 +
  416 + @override
  417 + int get hashCode => null.hashCode;
349 } 418 }
350 419
351 class PdfIndirect extends PdfDataType { 420 class PdfIndirect extends PdfDataType {
@@ -359,6 +428,18 @@ class PdfIndirect extends PdfDataType { @@ -359,6 +428,18 @@ class PdfIndirect extends PdfDataType {
359 void output(PdfStream s) { 428 void output(PdfStream s) {
360 s.putString('$ser $gen R'); 429 s.putString('$ser $gen R');
361 } 430 }
  431 +
  432 + @override
  433 + bool operator ==(Object other) {
  434 + if (other is PdfIndirect) {
  435 + return ser == other.ser && gen == other.gen;
  436 + }
  437 +
  438 + return false;
  439 + }
  440 +
  441 + @override
  442 + int get hashCode => ser.hashCode + gen.hashCode;
362 } 443 }
363 444
364 class PdfArray extends PdfDataType { 445 class PdfArray extends PdfDataType {
@@ -401,6 +482,33 @@ class PdfArray extends PdfDataType { @@ -401,6 +482,33 @@ class PdfArray extends PdfDataType {
401 } 482 }
402 s.putString(']'); 483 s.putString(']');
403 } 484 }
  485 +
  486 + /// Make all values unique, preserving the order
  487 + void uniq() {
  488 + if (values.length <= 1) {
  489 + return;
  490 + }
  491 +
  492 + // ignore: prefer_collection_literals
  493 + final uniques = LinkedHashMap<PdfDataType, bool>();
  494 + for (final s in values) {
  495 + uniques[s] = true;
  496 + }
  497 + values.clear();
  498 + values.addAll(uniques.keys);
  499 + }
  500 +
  501 + @override
  502 + bool operator ==(Object other) {
  503 + if (other is PdfArray) {
  504 + return values == other.values;
  505 + }
  506 +
  507 + return false;
  508 + }
  509 +
  510 + @override
  511 + int get hashCode => values.hashCode;
404 } 512 }
405 513
406 class PdfDict extends PdfDataType { 514 class PdfDict extends PdfDataType {
@@ -427,6 +535,10 @@ class PdfDict extends PdfDataType { @@ -427,6 +535,10 @@ class PdfDict extends PdfDataType {
427 values[k] = v; 535 values[k] = v;
428 } 536 }
429 537
  538 + PdfDataType operator [](String k) {
  539 + return values[k];
  540 + }
  541 +
430 @override 542 @override
431 void output(PdfStream s) { 543 void output(PdfStream s) {
432 s.putBytes(const <int>[0x3c, 0x3c]); 544 s.putBytes(const <int>[0x3c, 0x3c]);
@@ -443,6 +555,39 @@ class PdfDict extends PdfDataType { @@ -443,6 +555,39 @@ class PdfDict extends PdfDataType {
443 bool containsKey(String key) { 555 bool containsKey(String key) {
444 return values.containsKey(key); 556 return values.containsKey(key);
445 } 557 }
  558 +
  559 + void merge(PdfDict other) {
  560 + for (final key in other.values.keys) {
  561 + final value = other[key];
  562 + final current = values[key];
  563 + if (current == null) {
  564 + values[key] = value;
  565 + } else if (value is PdfArray && current is PdfArray) {
  566 + current.values.addAll(value.values);
  567 + current.uniq();
  568 + } else if (value is PdfDict && current is PdfDict) {
  569 + current.merge(value);
  570 + } else {
  571 + values[key] = value;
  572 + }
  573 + }
  574 + }
  575 +
  576 + void addAll(PdfDict other) {
  577 + values.addAll(other.values);
  578 + }
  579 +
  580 + @override
  581 + bool operator ==(Object other) {
  582 + if (other is PdfDict) {
  583 + return values == other.values;
  584 + }
  585 +
  586 + return false;
  587 + }
  588 +
  589 + @override
  590 + int get hashCode => values.hashCode;
446 } 591 }
447 592
448 class PdfColorType extends PdfDataType { 593 class PdfColorType extends PdfDataType {
@@ -468,4 +613,16 @@ class PdfColorType extends PdfDataType { @@ -468,4 +613,16 @@ class PdfColorType extends PdfDataType {
468 ]).output(s); 613 ]).output(s);
469 } 614 }
470 } 615 }
  616 +
  617 + @override
  618 + bool operator ==(Object other) {
  619 + if (other is PdfColorType) {
  620 + return color == other.color;
  621 + }
  622 +
  623 + return false;
  624 + }
  625 +
  626 + @override
  627 + int get hashCode => color.hashCode;
471 } 628 }
@@ -23,6 +23,7 @@ import '../io/interface.dart' @@ -23,6 +23,7 @@ import '../io/interface.dart'
23 if (dart.library.io) '../io/vm.dart' 23 if (dart.library.io) '../io/vm.dart'
24 if (dart.library.js) '../io/js.dart'; 24 if (dart.library.js) '../io/js.dart';
25 import 'catalog.dart'; 25 import 'catalog.dart';
  26 +import 'document_parser.dart';
26 import 'encryption.dart'; 27 import 'encryption.dart';
27 import 'font.dart'; 28 import 'font.dart';
28 import 'graphic_state.dart'; 29 import 'graphic_state.dart';
@@ -70,7 +71,8 @@ class PdfDocument { @@ -70,7 +71,8 @@ class PdfDocument {
70 PdfPageMode pageMode = PdfPageMode.none, 71 PdfPageMode pageMode = PdfPageMode.none,
71 DeflateCallback deflate, 72 DeflateCallback deflate,
72 bool compress = true, 73 bool compress = true,
73 - }) : deflate = compress ? (deflate ?? defaultDeflate) : null { 74 + }) : deflate = compress ? (deflate ?? defaultDeflate) : null,
  75 + prev = null {
74 _objser = 1; 76 _objser = 1;
75 77
76 // Now create some standard objects 78 // Now create some standard objects
@@ -79,9 +81,30 @@ class PdfDocument { @@ -79,9 +81,30 @@ class PdfDocument {
79 catalog = PdfCatalog(this, pdfPageList, pageMode, pdfNames); 81 catalog = PdfCatalog(this, pdfPageList, pageMode, pdfNames);
80 } 82 }
81 83
  84 + PdfDocument.load(
  85 + this.prev, {
  86 + PdfPageMode pageMode = PdfPageMode.none,
  87 + DeflateCallback deflate,
  88 + bool compress = true,
  89 + }) : deflate = compress ? (deflate ?? defaultDeflate) : null {
  90 + _objser = prev.size;
  91 +
  92 + // Now create some standard objects
  93 + pdfPageList = PdfPageList(this);
  94 + pdfNames = PdfNames(this);
  95 + catalog = PdfCatalog(this, pdfPageList, pageMode, pdfNames);
  96 +
  97 + // Import the existing document
  98 + prev.mergeDocument(this);
  99 + }
  100 +
  101 + final PdfDocumentParserBase prev;
  102 +
82 /// This is used to allocate objects a unique serial number in the document. 103 /// This is used to allocate objects a unique serial number in the document.
83 int _objser; 104 int _objser;
84 105
  106 + int get objser => _objser;
  107 +
85 /// This vector contains each indirect object within the document. 108 /// This vector contains each indirect object within the document.
86 final Set<PdfObject> objects = <PdfObject>{}; 109 final Set<PdfObject> objects = <PdfObject>{};
87 110
@@ -146,7 +169,7 @@ class PdfDocument { @@ -146,7 +169,7 @@ class PdfDocument {
146 /// This returns a specific page. It's used mainly when using a 169 /// This returns a specific page. It's used mainly when using a
147 /// Serialized template file. 170 /// Serialized template file.
148 PdfPage page(int page) { 171 PdfPage page(int page) {
149 - return pdfPageList.getPage(page); 172 + return pdfPageList.pages[page];
150 } 173 }
151 174
152 /// The root outline 175 /// The root outline
@@ -182,6 +205,9 @@ class PdfDocument { @@ -182,6 +205,9 @@ class PdfDocument {
182 /// Generate the PDF document as a memory file 205 /// Generate the PDF document as a memory file
183 Uint8List save() { 206 Uint8List save() {
184 final os = PdfStream(); 207 final os = PdfStream();
  208 + if (prev != null) {
  209 + os.putBytes(prev.bytes);
  210 + }
185 _write(os); 211 _write(os);
186 return os.output(); 212 return os.output();
187 } 213 }
  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 +import 'dart:typed_data';
  18 +
  19 +import 'package:pdf/src/document.dart';
  20 +
  21 +/// Base class for loading an existing PDF document.
  22 +abstract class PdfDocumentParserBase {
  23 + /// Create a Document loader instance
  24 + PdfDocumentParserBase(this.bytes);
  25 +
  26 + /// The existing PDF document content
  27 + final Uint8List bytes;
  28 +
  29 + /// The objects size of the existing PDF document
  30 + int get size;
  31 +
  32 + /// The offset of the previous cross reference table
  33 + int get xrefOffset;
  34 +
  35 + /// Import the existing objects into the new PDF document
  36 + void mergeDocument(PdfDocument pdfDocument);
  37 +}
@@ -138,6 +138,14 @@ mixin PdfGraphicStream on PdfObject { @@ -138,6 +138,14 @@ mixin PdfGraphicStream on PdfObject {
138 resources['/ExtGState'] = pdfDocument.graphicStates.ref(); 138 resources['/ExtGState'] = pdfDocument.graphicStates.ref();
139 } 139 }
140 140
  141 + if (params.containsKey('/Resources')) {
  142 + final res = params['/Resources'];
  143 + if (res is PdfDict) {
  144 + res.merge(resources);
  145 + return;
  146 + }
  147 + }
  148 +
141 params['/Resources'] = resources; 149 params['/Resources'] = resources;
142 } 150 }
143 } 151 }
@@ -83,4 +83,7 @@ class PdfObject { @@ -83,4 +83,7 @@ class PdfObject {
83 83
84 /// Returns the unique serial number in Pdf format 84 /// Returns the unique serial number in Pdf format
85 PdfIndirect ref() => PdfIndirect(objser, objgen); 85 PdfIndirect ref() => PdfIndirect(objser, objgen);
  86 +
  87 + @override
  88 + String toString() => '$runtimeType $params';
86 } 89 }
@@ -72,26 +72,23 @@ class PdfOutput { @@ -72,26 +72,23 @@ class PdfOutput {
72 final xref = os.offset; 72 final xref = os.offset;
73 os.putString('xref\n'); 73 os.putString('xref\n');
74 74
75 - // Now scan through the offsets list. The should be in sequence,  
76 - // but just in case: 75 + // Now scan through the offsets list. They should be in sequence.
  76 + offsets.sort((a, b) => a.id.compareTo(b.id));
  77 +
77 var firstid = 0; // First id in block 78 var firstid = 0; // First id in block
78 - var lastid = -1; // The last id used 79 + var lastid = 0; // The last id used
79 final block = <PdfXref>[]; // xrefs in this block 80 final block = <PdfXref>[]; // xrefs in this block
80 81
81 // We need block 0 to exist 82 // We need block 0 to exist
82 block.add(PdfXref(0, 0, generation: 65535)); 83 block.add(PdfXref(0, 0, generation: 65535));
83 84
84 for (var x in offsets) { 85 for (var x in offsets) {
85 - if (firstid == -1) {  
86 - firstid = x.id;  
87 - }  
88 -  
89 - // check to see if block is in range (-1 means empty)  
90 - if (lastid > -1 && x.id != (lastid + 1)) { 86 + // check to see if block is in range
  87 + if (lastid != null && x.id != (lastid + 1)) {
91 // no, so write this block, and reset 88 // no, so write this block, and reset
92 writeblock(firstid, block); 89 writeblock(firstid, block);
93 block.clear(); 90 block.clear();
94 - firstid = -1; 91 + firstid = x.id;
95 } 92 }
96 93
97 // now add to block 94 // now add to block
@@ -100,9 +97,7 @@ class PdfOutput { @@ -100,9 +97,7 @@ class PdfOutput {
100 } 97 }
101 98
102 // now write the last block 99 // now write the last block
103 - if (firstid > -1) {  
104 writeblock(firstid, block); 100 writeblock(firstid, block);
105 - }  
106 101
107 // now the trailer object 102 // now the trailer object
108 os.putString('trailer\n'); 103 os.putString('trailer\n');
@@ -110,7 +105,7 @@ class PdfOutput { @@ -110,7 +105,7 @@ class PdfOutput {
110 final params = PdfDict(); 105 final params = PdfDict();
111 106
112 // the number of entries (REQUIRED) 107 // the number of entries (REQUIRED)
113 - params['/Size'] = PdfNum(offsets.length + 1); 108 + params['/Size'] = PdfNum(rootID.pdfDocument.objser);
114 109
115 // the /Root catalog indirect reference (REQUIRED) 110 // the /Root catalog indirect reference (REQUIRED)
116 if (rootID != null) { 111 if (rootID != null) {
@@ -132,6 +127,10 @@ class PdfOutput { @@ -132,6 +127,10 @@ class PdfOutput {
132 params['/Encrypt'] = encryptID.ref(); 127 params['/Encrypt'] = encryptID.ref();
133 } 128 }
134 129
  130 + if (rootID.pdfDocument.prev != null) {
  131 + params['/Prev'] = PdfNum(rootID.pdfDocument.prev.xrefOffset);
  132 + }
  133 +
135 // end the trailer object 134 // end the trailer object
136 params.output(os); 135 params.output(os);
137 os.putString('\nstartxref\n$xref\n%%EOF\n'); 136 os.putString('\nstartxref\n$xref\n%%EOF\n');
@@ -27,10 +27,17 @@ import 'page_format.dart'; @@ -27,10 +27,17 @@ import 'page_format.dart';
27 class PdfPage extends PdfObject with PdfGraphicStream { 27 class PdfPage extends PdfObject with PdfGraphicStream {
28 /// This constructs a Page object, which will hold any contents for this 28 /// This constructs a Page object, which will hold any contents for this
29 /// page. 29 /// page.
30 - PdfPage(PdfDocument pdfDocument, {this.pageFormat = PdfPageFormat.standard})  
31 - : super(pdfDocument, type: '/Page') { 30 + PdfPage(
  31 + PdfDocument pdfDocument, {
  32 + this.pageFormat = PdfPageFormat.standard,
  33 + int index,
  34 + }) : super(pdfDocument, type: '/Page') {
  35 + if (index != null) {
  36 + pdfDocument.pdfPageList.pages.insert(index, this);
  37 + } else {
32 pdfDocument.pdfPageList.pages.add(this); 38 pdfDocument.pdfPageList.pages.add(this);
33 } 39 }
  40 + }
34 41
35 /// This is this page format, ie the size of the page, margins, and rotation 42 /// This is this page format, ie the size of the page, margins, and rotation
36 PdfPageFormat pageFormat; 43 PdfPageFormat pageFormat;
@@ -38,10 +45,6 @@ class PdfPage extends PdfObject with PdfGraphicStream { @@ -38,10 +45,6 @@ class PdfPage extends PdfObject with PdfGraphicStream {
38 /// This holds the contents of the page. 45 /// This holds the contents of the page.
39 List<PdfObjectStream> contents = <PdfObjectStream>[]; 46 List<PdfObjectStream> contents = <PdfObjectStream>[];
40 47
41 - /// Object ID that contains a thumbnail sketch of the page.  
42 - /// -1 indicates no thumbnail.  
43 - PdfObject thumbnail;  
44 -  
45 /// This holds any Annotations contained within this page. 48 /// This holds any Annotations contained within this page.
46 List<PdfAnnot> annotations = <PdfAnnot>[]; 49 List<PdfAnnot> annotations = <PdfAnnot>[];
47 50
@@ -72,30 +75,38 @@ class PdfPage extends PdfObject with PdfGraphicStream { @@ -72,30 +75,38 @@ class PdfPage extends PdfObject with PdfGraphicStream {
72 params['/MediaBox'] = 75 params['/MediaBox'] =
73 PdfArray.fromNum(<double>[0, 0, pageFormat.width, pageFormat.height]); 76 PdfArray.fromNum(<double>[0, 0, pageFormat.width, pageFormat.height]);
74 77
75 - // Rotation (if not zero)  
76 -// if(rotate!=0) {  
77 -// os.write("/Rotate ");  
78 -// os.write(Integer.toString(rotate).getBytes());  
79 -// os.write("\n");  
80 -// }  
81 -  
82 - // the /Contents pages object 78 + // The graphic operations to draw the page
83 if (contents.isNotEmpty) { 79 if (contents.isNotEmpty) {
84 - if (contents.length == 1) {  
85 - params['/Contents'] = contents.first.ref(); 80 + final contentList = PdfArray.fromObjects(contents);
  81 +
  82 + if (params.containsKey('/Contents')) {
  83 + final prevContent = params['/Contents'];
  84 + if (prevContent is PdfArray) {
  85 + contentList.values.insertAll(0, prevContent.values);
86 } else { 86 } else {
87 - params['/Contents'] = PdfArray.fromObjects(contents); 87 + contentList.values.insert(0, prevContent);
88 } 88 }
89 } 89 }
90 90
91 - // The thumbnail  
92 - if (thumbnail != null) {  
93 - params['/Thumb'] = thumbnail.ref(); 91 + contentList.uniq();
  92 +
  93 + if (contentList.values.length == 1) {
  94 + params['/Contents'] = contentList.values.first;
  95 + } else {
  96 + params['/Contents'] = contentList;
  97 + }
94 } 98 }
95 99
96 // The /Annots object 100 // The /Annots object
97 if (annotations.isNotEmpty) { 101 if (annotations.isNotEmpty) {
  102 + if (params.containsKey('/Annots')) {
  103 + final annotsList = params['/Annots'];
  104 + if (annotsList is PdfArray) {
  105 + annotsList.values.addAll(PdfArray.fromObjects(annotations).values);
  106 + }
  107 + } else {
98 params['/Annots'] = PdfArray.fromObjects(annotations); 108 params['/Annots'] = PdfArray.fromObjects(annotations);
99 } 109 }
100 } 110 }
  111 + }
101 } 112 }
@@ -17,10 +17,10 @@ @@ -17,10 +17,10 @@
17 */ 17 */
18 18
19 import 'package:meta/meta.dart'; 19 import 'package:meta/meta.dart';
20 -import 'package:pdf/pdf.dart';  
21 20
22 import 'data_types.dart'; 21 import 'data_types.dart';
23 import 'document.dart'; 22 import 'document.dart';
  23 +import 'function.dart';
24 import 'graphic_stream.dart'; 24 import 'graphic_stream.dart';
25 import 'graphics.dart'; 25 import 'graphics.dart';
26 import 'rect.dart'; 26 import 'rect.dart';
@@ -39,4 +39,7 @@ class PdfXref { @@ -39,4 +39,7 @@ class PdfXref {
39 } 39 }
40 return rs + ' n '; 40 return rs + ' n ';
41 } 41 }
  42 +
  43 + @override
  44 + String toString() => '$runtimeType $id $generation $offset';
42 } 45 }
@@ -22,8 +22,8 @@ import 'page.dart'; @@ -22,8 +22,8 @@ import 'page.dart';
22 import 'theme.dart'; 22 import 'theme.dart';
23 23
24 class Document { 24 class Document {
25 - Document(  
26 - {PdfPageMode pageMode = PdfPageMode.none, 25 + Document({
  26 + PdfPageMode pageMode = PdfPageMode.none,
27 DeflateCallback deflate, 27 DeflateCallback deflate,
28 bool compress = true, 28 bool compress = true,
29 this.theme, 29 this.theme,
@@ -32,8 +32,8 @@ class Document { @@ -32,8 +32,8 @@ class Document {
32 String creator, 32 String creator,
33 String subject, 33 String subject,
34 String keywords, 34 String keywords,
35 - String producer})  
36 - : document = PdfDocument( 35 + String producer,
  36 + }) : document = PdfDocument(
37 pageMode: pageMode, 37 pageMode: pageMode,
38 deflate: deflate, 38 deflate: deflate,
39 compress: compress, 39 compress: compress,
@@ -44,13 +44,51 @@ class Document { @@ -44,13 +44,51 @@ class Document {
44 subject != null || 44 subject != null ||
45 keywords != null || 45 keywords != null ||
46 producer != null) { 46 producer != null) {
47 - document.info = PdfInfo(document, 47 + document.info = PdfInfo(
  48 + document,
48 title: title, 49 title: title,
49 author: author, 50 author: author,
50 creator: creator, 51 creator: creator,
51 subject: subject, 52 subject: subject,
52 keywords: keywords, 53 keywords: keywords,
53 - producer: producer); 54 + producer: producer,
  55 + );
  56 + }
  57 + }
  58 +
  59 + Document.load(
  60 + PdfDocumentParserBase parser, {
  61 + PdfPageMode pageMode = PdfPageMode.none,
  62 + DeflateCallback deflate,
  63 + bool compress = true,
  64 + this.theme,
  65 + String title,
  66 + String author,
  67 + String creator,
  68 + String subject,
  69 + String keywords,
  70 + String producer,
  71 + }) : document = PdfDocument.load(
  72 + parser,
  73 + pageMode: pageMode,
  74 + deflate: deflate,
  75 + compress: compress,
  76 + ) {
  77 + if (title != null ||
  78 + author != null ||
  79 + creator != null ||
  80 + subject != null ||
  81 + keywords != null ||
  82 + producer != null) {
  83 + document.info = PdfInfo(
  84 + document,
  85 + title: title,
  86 + author: author,
  87 + creator: creator,
  88 + subject: subject,
  89 + keywords: keywords,
  90 + producer: producer,
  91 + );
54 } 92 }
55 } 93 }
56 94
@@ -64,8 +102,13 @@ class Document { @@ -64,8 +102,13 @@ class Document {
64 102
65 bool _paint = false; 103 bool _paint = false;
66 104
67 - void addPage(Page page) {  
68 - page.generate(this); 105 + void addPage(Page page, {int index}) {
  106 + page.generate(this, index: index);
  107 + _pages.add(page);
  108 + }
  109 +
  110 + void editPage(int index, Page page) {
  111 + page.generate(this, index: index, insert: false);
69 _pages.add(page); 112 _pages.add(page);
70 } 113 }
71 114
@@ -150,7 +150,7 @@ class MultiPage extends Page { @@ -150,7 +150,7 @@ class MultiPage extends Page {
150 } 150 }
151 151
152 @override 152 @override
153 - void generate(Document document) { 153 + void generate(Document document, {bool insert = true, int index}) {
154 if (_buildList == null) { 154 if (_buildList == null) {
155 return; 155 return;
156 } 156 }
@@ -178,7 +178,7 @@ class MultiPage extends Page { @@ -178,7 +178,7 @@ class MultiPage extends Page {
178 Context context; 178 Context context;
179 double offsetEnd; 179 double offsetEnd;
180 double offsetStart; 180 double offsetStart;
181 - var index = 0; 181 + var _index = 0;
182 var sameCount = 0; 182 var sameCount = 0;
183 final baseContext = 183 final baseContext =
184 Context(document: document.document).inheritFromAll(<Inherited>[ 184 Context(document: document.document).inheritFromAll(<Inherited>[
@@ -189,8 +189,8 @@ class MultiPage extends Page { @@ -189,8 +189,8 @@ class MultiPage extends Page {
189 final children = _buildList(baseContext); 189 final children = _buildList(baseContext);
190 WidgetContext widgetContext; 190 WidgetContext widgetContext;
191 191
192 - while (index < children.length) {  
193 - final child = children[index]; 192 + while (_index < children.length) {
  193 + final child = children[_index];
194 var canSpan = false; 194 var canSpan = false;
195 if (child is SpanningWidget) { 195 if (child is SpanningWidget) {
196 canSpan = child.canSpan; 196 canSpan = child.canSpan;
@@ -207,7 +207,11 @@ class MultiPage extends Page { @@ -207,7 +207,11 @@ class MultiPage extends Page {
207 207
208 // Create a new page if we don't already have one 208 // Create a new page if we don't already have one
209 if (context == null || child is NewPage) { 209 if (context == null || child is NewPage) {
210 - final pdfPage = PdfPage(document.document, pageFormat: pageFormat); 210 + final pdfPage = PdfPage(
  211 + document.document,
  212 + pageFormat: pageFormat,
  213 + index: index == null ? null : (index++),
  214 + );
211 context = 215 context =
212 baseContext.copyWith(page: pdfPage, canvas: pdfPage.getGraphics()); 216 baseContext.copyWith(page: pdfPage, canvas: pdfPage.getGraphics());
213 217
@@ -293,7 +297,7 @@ class MultiPage extends Page { @@ -293,7 +297,7 @@ class MultiPage extends Page {
293 // Has it finished spanning? 297 // Has it finished spanning?
294 if (!span.hasMoreWidgets) { 298 if (!span.hasMoreWidgets) {
295 sameCount = 0; 299 sameCount = 0;
296 - index++; 300 + _index++;
297 } 301 }
298 302
299 // Schedule a new page 303 // Schedule a new page
@@ -313,7 +317,7 @@ class MultiPage extends Page { @@ -313,7 +317,7 @@ class MultiPage extends Page {
313 317
314 offsetStart -= child.box.height; 318 offsetStart -= child.box.height;
315 sameCount = 0; 319 sameCount = 0;
316 - index++; 320 + _index++;
317 } 321 }
318 } 322 }
319 323
@@ -96,9 +96,18 @@ class Page { @@ -96,9 +96,18 @@ class Page {
96 ..fillPath(); 96 ..fillPath();
97 } 97 }
98 98
99 - void generate(Document document) { 99 + void generate(Document document, {bool insert = true, int index}) {
  100 + if (index != null) {
  101 + if (insert) {
  102 + _pdfPage =
  103 + PdfPage(document.document, pageFormat: pageFormat, index: index);
  104 + } else {
  105 + _pdfPage = document.document.page(index);
  106 + }
  107 + } else {
100 _pdfPage = PdfPage(document.document, pageFormat: pageFormat); 108 _pdfPage = PdfPage(document.document, pageFormat: pageFormat);
101 } 109 }
  110 + }
102 111
103 void postProcess(Document document) { 112 void postProcess(Document document) {
104 final canvas = _pdfPage.getGraphics(); 113 final canvas = _pdfPage.getGraphics();
@@ -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: 1.13.0 7 +version: 1.14.0
8 8
9 environment: 9 environment:
10 sdk: ">=2.3.0 <3.0.0" 10 sdk: ">=2.3.0 <3.0.0"
@@ -183,9 +183,10 @@ final title = 'Flutter Demo'; @@ -183,9 +183,10 @@ final title = 'Flutter Demo';
183 await Printing.layoutPdf(onLayout: (format) => _generatePdf(format, title)); 183 await Printing.layoutPdf(onLayout: (format) => _generatePdf(format, title));
184 ``` 184 ```
185 185
186 -## Encryption and Digital Signature 186 +## Encryption, Digital Signature, and loading a PDF Document
187 187
188 Encryption using RC4-40, RC4-128, AES-128, and AES-256 is fully supported using a separate library. 188 Encryption using RC4-40, RC4-128, AES-128, and AES-256 is fully supported using a separate library.
189 This library also provides SHA1 or SHA-256 Digital Signature using your x509 certificate. The graphic signature is represented by a clickable widget that shows Digital Signature information. 189 This library also provides SHA1 or SHA-256 Digital Signature using your x509 certificate. The graphic signature is represented by a clickable widget that shows Digital Signature information.
  190 +It implememts a PDF parser to load an existing document and add pages, change pages, and add a signature.
190 191
191 Drop me an email for availability and more information. 192 Drop me an email for availability and more information.