@adnsistemas/pdf-lib 2.4.5 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/README.md +43 -0
  2. package/cjs/api/PDFDocument.d.ts +14 -0
  3. package/cjs/api/PDFDocument.d.ts.map +1 -1
  4. package/cjs/api/PDFDocument.js +51 -3
  5. package/cjs/api/PDFDocument.js.map +1 -1
  6. package/cjs/api/PDFDocumentOptions.d.ts +1 -0
  7. package/cjs/api/PDFDocumentOptions.d.ts.map +1 -1
  8. package/cjs/api/snapshot/DefaultDocumentSnapshot.d.ts +4 -0
  9. package/cjs/api/snapshot/DefaultDocumentSnapshot.d.ts.map +1 -1
  10. package/cjs/api/snapshot/DefaultDocumentSnapshot.js +10 -0
  11. package/cjs/api/snapshot/DefaultDocumentSnapshot.js.map +1 -1
  12. package/cjs/api/snapshot/DocumentSnapshot.d.ts +4 -0
  13. package/cjs/api/snapshot/DocumentSnapshot.d.ts.map +1 -1
  14. package/cjs/api/snapshot/IncrementalDocumentSnapshot.d.ts +5 -0
  15. package/cjs/api/snapshot/IncrementalDocumentSnapshot.d.ts.map +1 -1
  16. package/cjs/api/snapshot/IncrementalDocumentSnapshot.js +17 -0
  17. package/cjs/api/snapshot/IncrementalDocumentSnapshot.js.map +1 -1
  18. package/cjs/core/PDFContext.d.ts +10 -1
  19. package/cjs/core/PDFContext.d.ts.map +1 -1
  20. package/cjs/core/PDFContext.js +52 -2
  21. package/cjs/core/PDFContext.js.map +1 -1
  22. package/cjs/core/document/PDFCrossRefSection.d.ts +6 -1
  23. package/cjs/core/document/PDFCrossRefSection.d.ts.map +1 -1
  24. package/cjs/core/document/PDFCrossRefSection.js +65 -2
  25. package/cjs/core/document/PDFCrossRefSection.js.map +1 -1
  26. package/cjs/core/parser/PDFParser.d.ts +2 -2
  27. package/cjs/core/parser/PDFParser.d.ts.map +1 -1
  28. package/cjs/core/parser/PDFParser.js +23 -5
  29. package/cjs/core/parser/PDFParser.js.map +1 -1
  30. package/cjs/core/parser/PDFXRefStreamParser.d.ts.map +1 -1
  31. package/cjs/core/parser/PDFXRefStreamParser.js +6 -0
  32. package/cjs/core/parser/PDFXRefStreamParser.js.map +1 -1
  33. package/cjs/core/writers/PDFWriter.d.ts.map +1 -1
  34. package/cjs/core/writers/PDFWriter.js +10 -0
  35. package/cjs/core/writers/PDFWriter.js.map +1 -1
  36. package/dist/pdf-lib.esm.js +232 -12
  37. package/dist/pdf-lib.esm.js.map +1 -1
  38. package/dist/pdf-lib.esm.min.js +3 -3
  39. package/dist/pdf-lib.esm.min.js.map +1 -1
  40. package/dist/pdf-lib.js +232 -12
  41. package/dist/pdf-lib.js.map +1 -1
  42. package/dist/pdf-lib.min.js +3 -3
  43. package/dist/pdf-lib.min.js.map +1 -1
  44. package/es/api/PDFDocument.d.ts +14 -0
  45. package/es/api/PDFDocument.d.ts.map +1 -1
  46. package/es/api/PDFDocument.js +51 -3
  47. package/es/api/PDFDocument.js.map +1 -1
  48. package/es/api/PDFDocumentOptions.d.ts +1 -0
  49. package/es/api/PDFDocumentOptions.d.ts.map +1 -1
  50. package/es/api/snapshot/DefaultDocumentSnapshot.d.ts +4 -0
  51. package/es/api/snapshot/DefaultDocumentSnapshot.d.ts.map +1 -1
  52. package/es/api/snapshot/DefaultDocumentSnapshot.js +10 -0
  53. package/es/api/snapshot/DefaultDocumentSnapshot.js.map +1 -1
  54. package/es/api/snapshot/DocumentSnapshot.d.ts +4 -0
  55. package/es/api/snapshot/DocumentSnapshot.d.ts.map +1 -1
  56. package/es/api/snapshot/IncrementalDocumentSnapshot.d.ts +5 -0
  57. package/es/api/snapshot/IncrementalDocumentSnapshot.d.ts.map +1 -1
  58. package/es/api/snapshot/IncrementalDocumentSnapshot.js +17 -0
  59. package/es/api/snapshot/IncrementalDocumentSnapshot.js.map +1 -1
  60. package/es/core/PDFContext.d.ts +10 -1
  61. package/es/core/PDFContext.d.ts.map +1 -1
  62. package/es/core/PDFContext.js +52 -2
  63. package/es/core/PDFContext.js.map +1 -1
  64. package/es/core/document/PDFCrossRefSection.d.ts +6 -1
  65. package/es/core/document/PDFCrossRefSection.d.ts.map +1 -1
  66. package/es/core/document/PDFCrossRefSection.js +65 -2
  67. package/es/core/document/PDFCrossRefSection.js.map +1 -1
  68. package/es/core/parser/PDFParser.d.ts +2 -2
  69. package/es/core/parser/PDFParser.d.ts.map +1 -1
  70. package/es/core/parser/PDFParser.js +23 -5
  71. package/es/core/parser/PDFParser.js.map +1 -1
  72. package/es/core/parser/PDFXRefStreamParser.d.ts.map +1 -1
  73. package/es/core/parser/PDFXRefStreamParser.js +6 -0
  74. package/es/core/parser/PDFXRefStreamParser.js.map +1 -1
  75. package/es/core/writers/PDFWriter.d.ts.map +1 -1
  76. package/es/core/writers/PDFWriter.js +10 -0
  77. package/es/core/writers/PDFWriter.js.map +1 -1
  78. package/package.json +4 -4
  79. package/src/api/PDFDocument.ts +55 -0
  80. package/src/api/PDFDocumentOptions.ts +1 -0
  81. package/src/api/snapshot/DefaultDocumentSnapshot.ts +13 -0
  82. package/src/api/snapshot/DocumentSnapshot.ts +6 -0
  83. package/src/api/snapshot/IncrementalDocumentSnapshot.ts +20 -0
  84. package/src/core/PDFContext.ts +53 -2
  85. package/src/core/document/PDFCrossRefSection.ts +70 -2
  86. package/src/core/parser/PDFParser.ts +22 -3
  87. package/src/core/parser/PDFXRefStreamParser.ts +8 -0
  88. package/src/core/writers/PDFWriter.ts +11 -0
  89. package/ts3.4/cjs/api/PDFDocument.d.ts +14 -0
  90. package/ts3.4/cjs/api/PDFDocumentOptions.d.ts +1 -0
  91. package/ts3.4/cjs/api/snapshot/DefaultDocumentSnapshot.d.ts +4 -0
  92. package/ts3.4/cjs/api/snapshot/DocumentSnapshot.d.ts +4 -0
  93. package/ts3.4/cjs/api/snapshot/IncrementalDocumentSnapshot.d.ts +5 -0
  94. package/ts3.4/cjs/core/PDFContext.d.ts +10 -1
  95. package/ts3.4/cjs/core/document/PDFCrossRefSection.d.ts +6 -1
  96. package/ts3.4/cjs/core/parser/PDFParser.d.ts +2 -2
  97. package/ts3.4/es/api/PDFDocument.d.ts +14 -0
  98. package/ts3.4/es/api/PDFDocumentOptions.d.ts +1 -0
  99. package/ts3.4/es/api/snapshot/DefaultDocumentSnapshot.d.ts +4 -0
  100. package/ts3.4/es/api/snapshot/DocumentSnapshot.d.ts +4 -0
  101. package/ts3.4/es/api/snapshot/IncrementalDocumentSnapshot.d.ts +5 -0
  102. package/ts3.4/es/core/PDFContext.d.ts +10 -1
  103. package/ts3.4/es/core/document/PDFCrossRefSection.d.ts +6 -1
  104. package/ts3.4/es/core/parser/PDFParser.d.ts +2 -2
@@ -4,6 +4,7 @@ import type { DocumentSnapshot } from './DocumentSnapshot';
4
4
  export class DefaultDocumentSnapshot implements DocumentSnapshot {
5
5
  pdfSize = 0;
6
6
  prevStartXRef = 0;
7
+ deletedCount = 0;
7
8
 
8
9
  shouldSave(_objectNumber: number): boolean {
9
10
  return true;
@@ -24,6 +25,18 @@ export class DefaultDocumentSnapshot implements DocumentSnapshot {
24
25
  markObjsForSave(_objs: PDFObject[]): void {
25
26
  throw new Error('This method should not be called.');
26
27
  }
28
+
29
+ markDeletedObj(_obj: PDFObject): void {
30
+ throw new Error('This method should not be called.');
31
+ }
32
+
33
+ markDeletedRef(_ref: PDFRef): void {
34
+ throw new Error('This method should not be called.');
35
+ }
36
+
37
+ deletedRef(_index: number): PDFRef | null {
38
+ throw new Error('This method should not be called.');
39
+ }
27
40
  }
28
41
 
29
42
  export const defaultDocumentSnapshot = new DefaultDocumentSnapshot();
@@ -3,6 +3,7 @@ import type { PDFObject, PDFRef } from '../../core';
3
3
  export interface DocumentSnapshot {
4
4
  pdfSize: number;
5
5
  prevStartXRef: number;
6
+ deletedCount: number;
6
7
 
7
8
  shouldSave: (objectNumber: number) => boolean;
8
9
 
@@ -11,4 +12,9 @@ export interface DocumentSnapshot {
11
12
 
12
13
  markObjForSave: (obj: PDFObject) => void;
13
14
  markObjsForSave: (objs: PDFObject[]) => void;
15
+
16
+ markDeletedRef: (ref: PDFRef) => void;
17
+ markDeletedObj: (obj: PDFObject) => void;
18
+
19
+ deletedRef: (index: number) => PDFRef | null;
14
20
  }
@@ -4,7 +4,9 @@ import type { DocumentSnapshot } from './DocumentSnapshot';
4
4
  export class IncrementalDocumentSnapshot implements DocumentSnapshot {
5
5
  pdfSize: number;
6
6
  prevStartXRef: number;
7
+ deletedCount: number = 0;
7
8
 
9
+ private deleted: PDFRef[] = [];
8
10
  private lastObjectNumber: number;
9
11
  private changedObjects: number[];
10
12
 
@@ -56,4 +58,22 @@ export class IncrementalDocumentSnapshot implements DocumentSnapshot {
56
58
  .filter((ref) => ref !== undefined) as PDFRef[],
57
59
  );
58
60
  }
61
+
62
+ markDeletedRef(ref: PDFRef): void {
63
+ if (
64
+ this.deleted.findIndex((dref) => dref.objectNumber === ref.objectNumber) <
65
+ 0
66
+ )
67
+ this.deletedCount = this.deleted.push(ref);
68
+ }
69
+
70
+ markDeletedObj(obj: PDFObject): void {
71
+ const oref = this.context.getRef(obj);
72
+ if (oref) this.markDeletedRef(oref);
73
+ }
74
+
75
+ deletedRef(index: number): PDFRef | null {
76
+ if (index < 0 || index >= this.deleted.length) return null;
77
+ return this.deleted[index];
78
+ }
59
79
  }
@@ -21,6 +21,8 @@ import PDFContentStream from './structures/PDFContentStream';
21
21
  import PDFSecurity from './security/PDFSecurity';
22
22
  import { typedArrayFor } from '../utils';
23
23
  import { SimpleRNG } from '../utils/rng';
24
+ import PDFCrossRefSection from './document/PDFCrossRefSection';
25
+ import type { Entry } from './document/PDFCrossRefSection';
24
26
 
25
27
  type LookupKey = PDFRef | PDFObject | undefined;
26
28
 
@@ -55,11 +57,13 @@ const byAscendingObjectNumber = (
55
57
 
56
58
  class PDFContext {
57
59
  isDecrypted = true;
58
- static create = () => new PDFContext();
60
+ static create = (withObjectVersions: boolean = false) =>
61
+ new PDFContext(withObjectVersions);
59
62
 
60
63
  largestObjectNumber: number;
61
64
  header: PDFHeader;
62
65
  trailerInfo: {
66
+ Size?: PDFNumber;
63
67
  Root?: PDFObject;
64
68
  Encrypt?: PDFObject;
65
69
  Info?: PDFObject;
@@ -73,20 +77,30 @@ class PDFContext {
73
77
  originalBytes?: Uint8Array;
74
78
  };
75
79
  snapshot?: DocumentSnapshot;
80
+ xrefs: PDFCrossRefSection[] = [];
81
+ private _preserveObjectsVersions: boolean;
82
+ public get preserveObjectsVersions(): boolean {
83
+ return this._preserveObjectsVersions;
84
+ }
76
85
 
77
86
  security?: PDFSecurity;
78
87
 
79
88
  private readonly indirectObjects: Map<PDFRef, PDFObject>;
89
+ private readonly objectsPreviousVersions: Map<PDFRef, PDFObject[]>;
80
90
 
81
91
  private pushGraphicsStateContentStreamRef?: PDFRef;
82
92
  private popGraphicsStateContentStreamRef?: PDFRef;
83
93
 
84
- private constructor() {
94
+ private constructor(preserveObjectsVersions?: boolean) {
95
+ this._preserveObjectsVersions = preserveObjectsVersions
96
+ ? preserveObjectsVersions
97
+ : false;
85
98
  this.largestObjectNumber = 0;
86
99
  this.header = PDFHeader.forVersion(1, 7);
87
100
  this.trailerInfo = {};
88
101
 
89
102
  this.indirectObjects = new Map();
103
+ this.objectsPreviousVersions = new Map();
90
104
  this.rng = SimpleRNG.withSeed(1);
91
105
  this.pdfFileDetails = {
92
106
  pdfSize: 0,
@@ -96,6 +110,17 @@ class PDFContext {
96
110
  }
97
111
 
98
112
  assign(ref: PDFRef, object: PDFObject): void {
113
+ if (this.preserveObjectsVersions) {
114
+ const prevOV = this.indirectObjects.get(ref);
115
+ if (prevOV) {
116
+ const prevList = this.objectsPreviousVersions.get(ref);
117
+ if (!prevList) {
118
+ this.objectsPreviousVersions.set(ref, [prevOV]);
119
+ } else {
120
+ prevList.unshift(prevOV);
121
+ }
122
+ }
123
+ }
99
124
  this.indirectObjects.set(ref, object);
100
125
  if (ref.objectNumber > this.largestObjectNumber) {
101
126
  this.largestObjectNumber = ref.objectNumber;
@@ -114,6 +139,19 @@ class PDFContext {
114
139
  }
115
140
 
116
141
  delete(ref: PDFRef): boolean {
142
+ if (this.snapshot) this.snapshot.markDeletedRef(ref);
143
+ if (this.preserveObjectsVersions) {
144
+ const object = this.indirectObjects.get(ref);
145
+ if (object) {
146
+ // check is not already deleted
147
+ const verlist = this.objectsPreviousVersions.get(ref);
148
+ if (verlist) {
149
+ verlist.unshift(object);
150
+ } else {
151
+ this.objectsPreviousVersions.set(ref, [object]);
152
+ }
153
+ }
154
+ }
117
155
  return this.indirectObjects.delete(ref);
118
156
  }
119
157
 
@@ -391,6 +429,19 @@ class PDFContext {
391
429
  registerObjectChange(obj: PDFObject) {
392
430
  if (this.snapshot) this.snapshot.markObjForSave(obj);
393
431
  }
432
+
433
+ getObjectVersions(ref: PDFRef): PDFObject[] {
434
+ if (!this.preserveObjectsVersions) return [];
435
+ const list = this.objectsPreviousVersions.get(ref);
436
+ if (list) return list;
437
+ return [];
438
+ }
439
+
440
+ listXrefEntries(xrefIndex: number = -1): Entry[] {
441
+ if (xrefIndex < 0) xrefIndex = this.xrefs.length - 1;
442
+ if (xrefIndex < 0 || xrefIndex >= this.xrefs.length) return [];
443
+ return this.xrefs[xrefIndex].listRefs();
444
+ }
394
445
  }
395
446
 
396
447
  export default PDFContext;
@@ -10,7 +10,7 @@ export interface Entry {
10
10
 
11
11
  /**
12
12
  * Entries should be added using the [[addEntry]] and [[addDeletedEntry]]
13
- * methods **in order of ascending object number**.
13
+ * methods.
14
14
  */
15
15
  class PDFCrossRefSection {
16
16
  static create = () =>
@@ -37,6 +37,22 @@ class PDFCrossRefSection {
37
37
  }
38
38
 
39
39
  addDeletedEntry(ref: PDFRef, nextFreeObjectNumber: number): void {
40
+ // fix the first entry if required
41
+ if (!this.subsections.length) {
42
+ this.subsections = [
43
+ [
44
+ {
45
+ ref: PDFRef.of(0, 65535),
46
+ offset: ref.objectNumber,
47
+ deleted: true,
48
+ },
49
+ ],
50
+ ];
51
+ this.chunkIdx = 0;
52
+ this.chunkLength = 1;
53
+ } else if (!this.subsections[0][0].offset) {
54
+ this.subsections[0][0].offset = ref.objectNumber;
55
+ }
40
56
  this.append({ ref, offset: nextFreeObjectNumber, deleted: true });
41
57
  }
42
58
 
@@ -159,7 +175,35 @@ class PDFCrossRefSection {
159
175
  const chunk = this.subsections[this.chunkIdx];
160
176
  const prevEntry = chunk[this.chunkLength - 1];
161
177
 
162
- if (currEntry.ref.objectNumber - prevEntry.ref.objectNumber > 1) {
178
+ if (currEntry.ref.objectNumber - prevEntry.ref.objectNumber !== 1) {
179
+ // the current chunk is not the right chunk, find the right one, or create a new one
180
+ for (let c = 0; c < this.subsections.length; c++) {
181
+ const first = this.subsections[c][0];
182
+ const last = this.subsections[c][this.subsections[c].length - 1];
183
+ if (first.ref.objectNumber > currEntry.ref.objectNumber) {
184
+ // goes before this subsection, or at the start of it
185
+ if (first.ref.objectNumber - currEntry.ref.objectNumber === 1) {
186
+ // first element of subsection
187
+ this.subsections[c].unshift(currEntry);
188
+ if (c === this.chunkIdx) this.chunkLength += 1;
189
+ return;
190
+ } else {
191
+ // create subsection
192
+ this.subsections.splice(c, 0, [currEntry]);
193
+ this.chunkIdx++;
194
+ return;
195
+ }
196
+ } else if (last.ref.objectNumber > currEntry.ref.objectNumber) {
197
+ // goes in this subsection, find its place..
198
+ const cep = this.subsections[c].findIndex(
199
+ (ee) => ee.ref.objectNumber > currEntry.ref.objectNumber,
200
+ );
201
+ this.subsections[c].splice(cep, 0, currEntry);
202
+ if (c === this.chunkIdx) this.chunkLength += 1;
203
+ }
204
+ // bigger, keep looking
205
+ }
206
+ // if got to here, then a new subsection is required
163
207
  this.subsections.push([currEntry]);
164
208
  this.chunkIdx += 1;
165
209
  this.chunkLength = 1;
@@ -168,6 +212,30 @@ class PDFCrossRefSection {
168
212
  this.chunkLength += 1;
169
213
  }
170
214
  }
215
+
216
+ /**
217
+ * Returns all the entries in the XREF section, except the first one (object == 0)
218
+ * @returns {Entry[]} All the entries in the XREF section
219
+ */
220
+ listRefs(): Entry[] {
221
+ const refList: Entry[] = [];
222
+ for (
223
+ let rangeIdx = 0, rangeLen = this.subsections.length;
224
+ rangeIdx < rangeLen;
225
+ rangeIdx++
226
+ ) {
227
+ const range = this.subsections[rangeIdx];
228
+ for (
229
+ let entryIdx = 0, entryLen = range.length;
230
+ entryIdx < entryLen;
231
+ entryIdx++
232
+ ) {
233
+ const entry = range[entryIdx];
234
+ if (entry.ref.objectNumber) refList.push(entry);
235
+ }
236
+ }
237
+ return refList;
238
+ }
171
239
  }
172
240
 
173
241
  export default PDFCrossRefSection;
@@ -24,6 +24,7 @@ import { Keywords } from '../syntax/Keywords';
24
24
  import { IsDigit } from '../syntax/Numeric';
25
25
  import { waitForTick } from '../../utils';
26
26
  import { CipherTransformFactory } from '../crypto';
27
+ import PDFNumber from '../objects/PDFNumber';
27
28
 
28
29
  class PDFParser extends PDFObjectParser {
29
30
  static forBytesWithOptions = (
@@ -34,6 +35,7 @@ class PDFParser extends PDFObjectParser {
34
35
  capNumbers?: boolean,
35
36
  cryptoFactory?: CipherTransformFactory,
36
37
  forIncrementalUpdate?: boolean,
38
+ preserveObjectsVersions?: boolean,
37
39
  ) =>
38
40
  new PDFParser(
39
41
  pdfBytes,
@@ -43,6 +45,7 @@ class PDFParser extends PDFObjectParser {
43
45
  capNumbers,
44
46
  cryptoFactory,
45
47
  forIncrementalUpdate,
48
+ preserveObjectsVersions,
46
49
  );
47
50
 
48
51
  private readonly objectsPerTick: number;
@@ -59,10 +62,11 @@ class PDFParser extends PDFObjectParser {
59
62
  capNumbers = false,
60
63
  cryptoFactory?: CipherTransformFactory,
61
64
  forIncrementalUpdate = false,
65
+ preserveObjectsVersions = false,
62
66
  ) {
63
67
  super(
64
68
  ByteStream.of(pdfBytes),
65
- PDFContext.create(),
69
+ PDFContext.create(preserveObjectsVersions),
66
70
  capNumbers,
67
71
  cryptoFactory,
68
72
  );
@@ -195,7 +199,15 @@ class PDFParser extends PDFObjectParser {
195
199
  object instanceof PDFRawStream &&
196
200
  object.dict.lookup(PDFName.of('Type')) === PDFName.of('XRef')
197
201
  ) {
198
- PDFXRefStreamParser.forStream(object).parseIntoContext();
202
+ const entries = PDFXRefStreamParser.forStream(object).parseIntoContext();
203
+ if (entries.length) {
204
+ const xref = PDFCrossRefSection.createEmpty();
205
+ for (const entry of entries) {
206
+ if (entry.deleted) xref.addDeletedEntry(entry.ref, entry.offset);
207
+ else xref.addEntry(entry.ref, entry.offset);
208
+ }
209
+ this.context.xrefs.push(xref);
210
+ }
199
211
  }
200
212
  // always register the object and the ref, to properly handle object numeration
201
213
  this.context.assign(ref, object);
@@ -307,11 +319,17 @@ class PDFParser extends PDFObjectParser {
307
319
 
308
320
  const { context } = this;
309
321
  context.trailerInfo = {
322
+ Size:
323
+ dict.lookupMaybe(PDFName.of('Size'), PDFNumber) ||
324
+ context.trailerInfo.Size,
310
325
  Root: dict.get(PDFName.of('Root')) || context.trailerInfo.Root,
311
326
  Encrypt: dict.get(PDFName.of('Encrypt')) || context.trailerInfo.Encrypt,
312
327
  Info: dict.get(PDFName.of('Info')) || context.trailerInfo.Info,
313
328
  ID: dict.get(PDFName.of('ID')) || context.trailerInfo.ID,
314
329
  };
330
+ // if open for incremental update, then deleted objects need to be preserved, and largestObjectNumber has to be Size-1
331
+ if (context.trailerInfo.Size && context.pdfFileDetails.originalBytes)
332
+ context.largestObjectNumber = context.trailerInfo.Size.asNumber() - 1;
315
333
  }
316
334
 
317
335
  private maybeParseTrailer(): PDFTrailer | void {
@@ -333,7 +351,8 @@ class PDFParser extends PDFObjectParser {
333
351
 
334
352
  private async parseDocumentSection(): Promise<void> {
335
353
  await this.parseIndirectObjects();
336
- this.maybeParseCrossRefSection();
354
+ const xref = this.maybeParseCrossRefSection();
355
+ if (xref) this.context.xrefs.push(xref);
337
356
  this.maybeParseTrailerDict();
338
357
  this.maybeParseTrailer();
339
358
 
@@ -66,11 +66,19 @@ class PDFXRefStreamParser {
66
66
  this.alreadyParsed = true;
67
67
 
68
68
  this.context.trailerInfo = {
69
+ Size: this.dict.lookup(PDFName.of('Size'), PDFNumber),
69
70
  Root: this.dict.get(PDFName.of('Root')),
70
71
  Encrypt: this.dict.get(PDFName.of('Encrypt')),
71
72
  Info: this.dict.get(PDFName.of('Info')),
72
73
  ID: this.dict.get(PDFName.of('ID')),
73
74
  };
75
+ // if open for incremental update, make sure next object number doesn't overlap a deleted one
76
+ if (
77
+ this.context.trailerInfo.Size &&
78
+ this.context.pdfFileDetails.originalBytes
79
+ )
80
+ this.context.largestObjectNumber =
81
+ this.context.trailerInfo.Size.asNumber() - 1;
74
82
 
75
83
  const entries = this.parseEntries();
76
84
 
@@ -166,6 +166,17 @@ class PDFWriter {
166
166
  size += this.computeIndirectObjectSize(indirectObject);
167
167
  if (this.shouldWaitForTick(1)) await waitForTick();
168
168
  }
169
+ // deleted objects
170
+ for (let idx = 0; idx < this.snapshot.deletedCount; idx++) {
171
+ const dref = this.snapshot.deletedRef(idx);
172
+ if (!dref) break;
173
+ const nextdref = this.snapshot.deletedRef(idx + 1);
174
+ // add 1 to generation number for deleted ref
175
+ xref.addDeletedEntry(
176
+ PDFRef.of(dref.objectNumber, dref.generationNumber + 1),
177
+ nextdref ? nextdref.objectNumber : 0,
178
+ );
179
+ }
169
180
 
170
181
  const xrefOffset = size;
171
182
  size += xref.sizeInBytes() + 1; // '\n'
@@ -6,6 +6,7 @@ import PDFForm from './form/PDFForm';
6
6
  import { StandardFonts } from './StandardFonts';
7
7
  import { PageBoundingBox, PDFCatalog, PDFContext } from '../core';
8
8
  import { AttachmentOptions, SaveOptions, Base64SaveOptions, LoadOptions, CreateOptions, EmbedFontOptions, SetTitleOptions, IncrementalSaveOptions } from './PDFDocumentOptions';
9
+ import PDFObject from '../core/objects/PDFObject';
9
10
  import PDFRef from '../core/objects/PDFRef';
10
11
  import { Fontkit } from '../types/fontkit';
11
12
  import { TransformationMatrix } from '../types/matrix';
@@ -22,6 +23,11 @@ export type PDFAttachment = {
22
23
  creationDate: Date | undefined;
23
24
  modificationDate: Date | undefined;
24
25
  };
26
+ export type PDFObjectVersions = {
27
+ ref: PDFRef;
28
+ actual: PDFObject | undefined;
29
+ previous: PDFObject[];
30
+ };
25
31
  /**
26
32
  * Represents a PDF document.
27
33
  */
@@ -828,6 +834,14 @@ export default class PDFDocument {
828
834
  saveAsBase64(options?: Base64SaveOptions): Promise<string>;
829
835
  findPageForAnnotationRef(ref: PDFRef): PDFPage | undefined;
830
836
  takeSnapshot(): DocumentSnapshot;
837
+ /**
838
+ * Returns the update version of the object as 'actual', and all the previous versions, of the objects
839
+ * that has changed in the indicated update (or the last one).
840
+ * If document wasn't load to preserve objects versions, an empty array is returned.
841
+ * @param {number} lastUpdateMinusX If not the last update, how many updates before the last.
842
+ * @returns {PDFObjectVersions[]} Objects modified in the update, and previous versions
843
+ */
844
+ getChangedObjects(lastUpdateMinusX?: number): PDFObjectVersions[];
831
845
  private prepareForSave;
832
846
  private embedAll;
833
847
  private updateInfoDict;
@@ -30,6 +30,7 @@ export interface LoadOptions {
30
30
  capNumbers?: boolean;
31
31
  password?: string;
32
32
  forIncrementalUpdate?: boolean;
33
+ preserveObjectsVersions?: boolean;
33
34
  }
34
35
  export interface CreateOptions {
35
36
  updateMetadata?: boolean;
@@ -3,11 +3,15 @@ import { DocumentSnapshot } from './DocumentSnapshot';
3
3
  export declare class DefaultDocumentSnapshot implements DocumentSnapshot {
4
4
  pdfSize: number;
5
5
  prevStartXRef: number;
6
+ deletedCount: number;
6
7
  shouldSave(_objectNumber: number): boolean;
7
8
  markRefForSave(_ref: PDFRef): void;
8
9
  markRefsForSave(_refs: PDFRef[]): void;
9
10
  markObjForSave(_obj: PDFObject): void;
10
11
  markObjsForSave(_objs: PDFObject[]): void;
12
+ markDeletedObj(_obj: PDFObject): void;
13
+ markDeletedRef(_ref: PDFRef): void;
14
+ deletedRef(_index: number): PDFRef | null;
11
15
  }
12
16
  export declare const defaultDocumentSnapshot: DefaultDocumentSnapshot;
13
17
  //# sourceMappingURL=DefaultDocumentSnapshot.d.ts.map
@@ -2,10 +2,14 @@ import { PDFObject, PDFRef } from '../../core';
2
2
  export interface DocumentSnapshot {
3
3
  pdfSize: number;
4
4
  prevStartXRef: number;
5
+ deletedCount: number;
5
6
  shouldSave: (objectNumber: number) => boolean;
6
7
  markRefForSave: (ref: PDFRef) => void;
7
8
  markRefsForSave: (refs: PDFRef[]) => void;
8
9
  markObjForSave: (obj: PDFObject) => void;
9
10
  markObjsForSave: (objs: PDFObject[]) => void;
11
+ markDeletedRef: (ref: PDFRef) => void;
12
+ markDeletedObj: (obj: PDFObject) => void;
13
+ deletedRef: (index: number) => PDFRef | null;
10
14
  }
11
15
  //# sourceMappingURL=DocumentSnapshot.d.ts.map
@@ -3,6 +3,8 @@ import { DocumentSnapshot } from './DocumentSnapshot';
3
3
  export declare class IncrementalDocumentSnapshot implements DocumentSnapshot {
4
4
  pdfSize: number;
5
5
  prevStartXRef: number;
6
+ deletedCount: number;
7
+ private deleted;
6
8
  private lastObjectNumber;
7
9
  private changedObjects;
8
10
  context: PDFContext;
@@ -12,5 +14,8 @@ export declare class IncrementalDocumentSnapshot implements DocumentSnapshot {
12
14
  markRefsForSave(refs: PDFRef[]): void;
13
15
  markObjForSave(obj: PDFObject): void;
14
16
  markObjsForSave(objs: PDFObject[]): void;
17
+ markDeletedRef(ref: PDFRef): void;
18
+ markDeletedObj(obj: PDFObject): void;
19
+ deletedRef(index: number): PDFRef | null;
15
20
  }
16
21
  //# sourceMappingURL=IncrementalDocumentSnapshot.d.ts.map
@@ -16,6 +16,8 @@ import PDFOperator from './operators/PDFOperator';
16
16
  import PDFContentStream from './structures/PDFContentStream';
17
17
  import PDFSecurity from './security/PDFSecurity';
18
18
  import { SimpleRNG } from '../utils/rng';
19
+ import PDFCrossRefSection from './document/PDFCrossRefSection';
20
+ import { Entry } from './document/PDFCrossRefSection';
19
21
  type LookupKey = PDFRef | PDFObject | undefined;
20
22
  interface LiteralObject {
21
23
  [name: string]: Literal | PDFObject;
@@ -32,10 +34,11 @@ interface LiteralConfig {
32
34
  }
33
35
  declare class PDFContext {
34
36
  isDecrypted: boolean;
35
- static create: () => PDFContext;
37
+ static create: (withObjectVersions?: boolean) => PDFContext;
36
38
  largestObjectNumber: number;
37
39
  header: PDFHeader;
38
40
  trailerInfo: {
41
+ Size?: PDFNumber;
39
42
  Root?: PDFObject;
40
43
  Encrypt?: PDFObject;
41
44
  Info?: PDFObject;
@@ -49,8 +52,12 @@ declare class PDFContext {
49
52
  originalBytes?: Uint8Array;
50
53
  };
51
54
  snapshot?: DocumentSnapshot;
55
+ xrefs: PDFCrossRefSection[];
56
+ private _preserveObjectsVersions;
57
+ readonly preserveObjectsVersions: boolean;
52
58
  security?: PDFSecurity;
53
59
  private readonly indirectObjects;
60
+ private readonly objectsPreviousVersions;
54
61
  private pushGraphicsStateContentStreamRef?;
55
62
  private popGraphicsStateContentStreamRef?;
56
63
  private constructor();
@@ -112,6 +119,8 @@ declare class PDFContext {
112
119
  getPopGraphicsStateContentStream(): PDFRef;
113
120
  addRandomSuffix(prefix: string, suffixLength?: number): string;
114
121
  registerObjectChange(obj: PDFObject): void;
122
+ getObjectVersions(ref: PDFRef): PDFObject[];
123
+ listXrefEntries(xrefIndex?: number): Entry[];
115
124
  }
116
125
  export default PDFContext;
117
126
  //# sourceMappingURL=PDFContext.d.ts.map
@@ -6,7 +6,7 @@ export interface Entry {
6
6
  }
7
7
  /**
8
8
  * Entries should be added using the [[addEntry]] and [[addDeletedEntry]]
9
- * methods **in order of ascending object number**.
9
+ * methods.
10
10
  */
11
11
  declare class PDFCrossRefSection {
12
12
  static create: () => PDFCrossRefSection;
@@ -23,6 +23,11 @@ declare class PDFCrossRefSection {
23
23
  private copySubsectionsIntoBuffer;
24
24
  private copyEntriesIntoBuffer;
25
25
  private append;
26
+ /**
27
+ * Returns all the entries in the XREF section, except the first one (object == 0)
28
+ * @returns {Entry[]} All the entries in the XREF section
29
+ */
30
+ listRefs(): Entry[];
26
31
  }
27
32
  export default PDFCrossRefSection;
28
33
  //# sourceMappingURL=PDFCrossRefSection.d.ts.map
@@ -2,13 +2,13 @@ import PDFObjectParser from './PDFObjectParser';
2
2
  import PDFContext from '../PDFContext';
3
3
  import { CipherTransformFactory } from '../crypto';
4
4
  declare class PDFParser extends PDFObjectParser {
5
- static forBytesWithOptions: (pdfBytes: Uint8Array, objectsPerTick?: number, throwOnInvalidObject?: boolean, warnOnInvalidObjects?: boolean, capNumbers?: boolean, cryptoFactory?: CipherTransformFactory, forIncrementalUpdate?: boolean) => PDFParser;
5
+ static forBytesWithOptions: (pdfBytes: Uint8Array, objectsPerTick?: number, throwOnInvalidObject?: boolean, warnOnInvalidObjects?: boolean, capNumbers?: boolean, cryptoFactory?: CipherTransformFactory, forIncrementalUpdate?: boolean, preserveObjectsVersions?: boolean) => PDFParser;
6
6
  private readonly objectsPerTick;
7
7
  private readonly throwOnInvalidObject;
8
8
  private readonly warnOnInvalidObjects;
9
9
  private alreadyParsed;
10
10
  private parsedObjects;
11
- constructor(pdfBytes: Uint8Array, objectsPerTick?: number, throwOnInvalidObject?: boolean, warnOnInvalidObjects?: boolean, capNumbers?: boolean, cryptoFactory?: CipherTransformFactory, forIncrementalUpdate?: boolean);
11
+ constructor(pdfBytes: Uint8Array, objectsPerTick?: number, throwOnInvalidObject?: boolean, warnOnInvalidObjects?: boolean, capNumbers?: boolean, cryptoFactory?: CipherTransformFactory, forIncrementalUpdate?: boolean, preserveObjectsVersions?: boolean);
12
12
  parseDocument(): Promise<PDFContext>;
13
13
  private maybeRecoverRoot;
14
14
  private parseHeader;
@@ -6,6 +6,7 @@ import PDFForm from './form/PDFForm';
6
6
  import { StandardFonts } from './StandardFonts';
7
7
  import { PageBoundingBox, PDFCatalog, PDFContext } from '../core';
8
8
  import { AttachmentOptions, SaveOptions, Base64SaveOptions, LoadOptions, CreateOptions, EmbedFontOptions, SetTitleOptions, IncrementalSaveOptions } from './PDFDocumentOptions';
9
+ import PDFObject from '../core/objects/PDFObject';
9
10
  import PDFRef from '../core/objects/PDFRef';
10
11
  import { Fontkit } from '../types/fontkit';
11
12
  import { TransformationMatrix } from '../types/matrix';
@@ -22,6 +23,11 @@ export type PDFAttachment = {
22
23
  creationDate: Date | undefined;
23
24
  modificationDate: Date | undefined;
24
25
  };
26
+ export type PDFObjectVersions = {
27
+ ref: PDFRef;
28
+ actual: PDFObject | undefined;
29
+ previous: PDFObject[];
30
+ };
25
31
  /**
26
32
  * Represents a PDF document.
27
33
  */
@@ -828,6 +834,14 @@ export default class PDFDocument {
828
834
  saveAsBase64(options?: Base64SaveOptions): Promise<string>;
829
835
  findPageForAnnotationRef(ref: PDFRef): PDFPage | undefined;
830
836
  takeSnapshot(): DocumentSnapshot;
837
+ /**
838
+ * Returns the update version of the object as 'actual', and all the previous versions, of the objects
839
+ * that has changed in the indicated update (or the last one).
840
+ * If document wasn't load to preserve objects versions, an empty array is returned.
841
+ * @param {number} lastUpdateMinusX If not the last update, how many updates before the last.
842
+ * @returns {PDFObjectVersions[]} Objects modified in the update, and previous versions
843
+ */
844
+ getChangedObjects(lastUpdateMinusX?: number): PDFObjectVersions[];
831
845
  private prepareForSave;
832
846
  private embedAll;
833
847
  private updateInfoDict;
@@ -30,6 +30,7 @@ export interface LoadOptions {
30
30
  capNumbers?: boolean;
31
31
  password?: string;
32
32
  forIncrementalUpdate?: boolean;
33
+ preserveObjectsVersions?: boolean;
33
34
  }
34
35
  export interface CreateOptions {
35
36
  updateMetadata?: boolean;
@@ -3,11 +3,15 @@ import { DocumentSnapshot } from './DocumentSnapshot';
3
3
  export declare class DefaultDocumentSnapshot implements DocumentSnapshot {
4
4
  pdfSize: number;
5
5
  prevStartXRef: number;
6
+ deletedCount: number;
6
7
  shouldSave(_objectNumber: number): boolean;
7
8
  markRefForSave(_ref: PDFRef): void;
8
9
  markRefsForSave(_refs: PDFRef[]): void;
9
10
  markObjForSave(_obj: PDFObject): void;
10
11
  markObjsForSave(_objs: PDFObject[]): void;
12
+ markDeletedObj(_obj: PDFObject): void;
13
+ markDeletedRef(_ref: PDFRef): void;
14
+ deletedRef(_index: number): PDFRef | null;
11
15
  }
12
16
  export declare const defaultDocumentSnapshot: DefaultDocumentSnapshot;
13
17
  //# sourceMappingURL=DefaultDocumentSnapshot.d.ts.map
@@ -2,10 +2,14 @@ import { PDFObject, PDFRef } from '../../core';
2
2
  export interface DocumentSnapshot {
3
3
  pdfSize: number;
4
4
  prevStartXRef: number;
5
+ deletedCount: number;
5
6
  shouldSave: (objectNumber: number) => boolean;
6
7
  markRefForSave: (ref: PDFRef) => void;
7
8
  markRefsForSave: (refs: PDFRef[]) => void;
8
9
  markObjForSave: (obj: PDFObject) => void;
9
10
  markObjsForSave: (objs: PDFObject[]) => void;
11
+ markDeletedRef: (ref: PDFRef) => void;
12
+ markDeletedObj: (obj: PDFObject) => void;
13
+ deletedRef: (index: number) => PDFRef | null;
10
14
  }
11
15
  //# sourceMappingURL=DocumentSnapshot.d.ts.map