@adnsistemas/pdf-lib 2.7.1 → 2.7.3

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 (47) hide show
  1. package/README.md +4 -3
  2. package/cjs/api/PDFDocument.js +1 -1
  3. package/cjs/api/PDFDocument.js.map +1 -1
  4. package/cjs/api/snapshot/IncrementalDocumentSnapshot.d.ts +1 -1
  5. package/cjs/api/snapshot/IncrementalDocumentSnapshot.d.ts.map +1 -1
  6. package/cjs/api/snapshot/IncrementalDocumentSnapshot.js +3 -7
  7. package/cjs/api/snapshot/IncrementalDocumentSnapshot.js.map +1 -1
  8. package/cjs/core/objects/PDFString.js +1 -1
  9. package/cjs/core/parser/PDFObjectParser.d.ts.map +1 -1
  10. package/cjs/core/parser/PDFObjectParser.js +2 -1
  11. package/cjs/core/parser/PDFObjectParser.js.map +1 -1
  12. package/cjs/core/writers/PDFWriter.d.ts +20 -0
  13. package/cjs/core/writers/PDFWriter.d.ts.map +1 -1
  14. package/cjs/core/writers/PDFWriter.js +64 -3
  15. package/cjs/core/writers/PDFWriter.js.map +1 -1
  16. package/dist/pdf-lib.esm.js +69 -13
  17. package/dist/pdf-lib.esm.js.map +1 -1
  18. package/dist/pdf-lib.esm.min.js +3 -3
  19. package/dist/pdf-lib.esm.min.js.map +1 -1
  20. package/dist/pdf-lib.js +69 -13
  21. package/dist/pdf-lib.js.map +1 -1
  22. package/dist/pdf-lib.min.js +3 -3
  23. package/dist/pdf-lib.min.js.map +1 -1
  24. package/es/api/PDFDocument.js +1 -1
  25. package/es/api/PDFDocument.js.map +1 -1
  26. package/es/api/snapshot/IncrementalDocumentSnapshot.d.ts +1 -1
  27. package/es/api/snapshot/IncrementalDocumentSnapshot.d.ts.map +1 -1
  28. package/es/api/snapshot/IncrementalDocumentSnapshot.js +3 -7
  29. package/es/api/snapshot/IncrementalDocumentSnapshot.js.map +1 -1
  30. package/es/core/objects/PDFString.js +1 -1
  31. package/es/core/parser/PDFObjectParser.d.ts.map +1 -1
  32. package/es/core/parser/PDFObjectParser.js +3 -2
  33. package/es/core/parser/PDFObjectParser.js.map +1 -1
  34. package/es/core/writers/PDFWriter.d.ts +20 -0
  35. package/es/core/writers/PDFWriter.d.ts.map +1 -1
  36. package/es/core/writers/PDFWriter.js +64 -3
  37. package/es/core/writers/PDFWriter.js.map +1 -1
  38. package/package.json +1 -1
  39. package/src/api/PDFDocument.ts +1 -1
  40. package/src/api/snapshot/IncrementalDocumentSnapshot.ts +5 -11
  41. package/src/core/objects/PDFString.ts +1 -1
  42. package/src/core/parser/PDFObjectParser.ts +5 -2
  43. package/src/core/writers/PDFWriter.ts +72 -3
  44. package/ts3.4/cjs/api/snapshot/IncrementalDocumentSnapshot.d.ts +1 -1
  45. package/ts3.4/cjs/core/writers/PDFWriter.d.ts +23 -0
  46. package/ts3.4/es/api/snapshot/IncrementalDocumentSnapshot.d.ts +1 -1
  47. package/ts3.4/es/core/writers/PDFWriter.d.ts +23 -0
@@ -17,6 +17,8 @@ import { copyStringIntoBuffer, waitForTick } from '../../utils';
17
17
  import PDFNumber from '../objects/PDFNumber';
18
18
  import PDFSecurity from '../security/PDFSecurity';
19
19
  import PDFStream from '../objects/PDFStream';
20
+ import PDFName from '../objects/PDFName';
21
+ import PDFRawStream from '../objects/PDFRawStream';
20
22
 
21
23
  export interface SerializationInfo {
22
24
  size: number;
@@ -53,6 +55,61 @@ class PDFWriter {
53
55
  this.snapshot = snapshot;
54
56
  }
55
57
 
58
+ /**
59
+ * If PDF has an XRef Stream, then the last object will be probably be skipped on saving.
60
+ * If that's the case, this property will have that object number, and the PDF /Size can
61
+ * be corrected, to be accurate.
62
+ */
63
+ protected _largestSkippedObjectNum: number = 0;
64
+
65
+ /**
66
+ * Used to check wheter an object should be saved or not, preserves the object number of the
67
+ * last XRef Stream object, if there is one.
68
+ */
69
+ protected _lastXRefObjectNumber: number = 0;
70
+ /**
71
+ * For incremental saves, defers the decision to the snapshot.
72
+ * For full saves, checks that the object is not the last XRef stream object.
73
+ * @param {boolean} incremental If making an incremental save, or a full save of the PDF
74
+ * @param {number} objNum Object number
75
+ * @param {[PDFRef, PDFObject][]} objects List of objects that form the PDF
76
+ * @returns {boolean} whether the object should be saved or not
77
+ */
78
+ protected shouldSave(
79
+ incremental: boolean,
80
+ objNum: number,
81
+ objects: [PDFRef, PDFObject][],
82
+ ): boolean {
83
+ let should = true;
84
+ if (incremental) {
85
+ should = this.snapshot.shouldSave(objNum);
86
+ } else {
87
+ // only the last XRef Stream will be regenerated on save
88
+ if (!this._lastXRefObjectNumber) {
89
+ // if no XRef Stream, then nothing should be skipped
90
+ this._lastXRefObjectNumber = this.context.largestObjectNumber + 1;
91
+ const checkWatermark = this._lastXRefObjectNumber - 10; // max number of objects in the final part of the PDF to check
92
+ // search the last XRef Stream, if there is one, objects are expected to be in object number order
93
+ for (let idx = objects.length - 1; idx > 0; idx--) {
94
+ // if not in last 'rangeToCheck' objects, there is none that should be skipped, most probably a linearized PDF, or without XRef Streams
95
+ if (objects[idx][0].objectNumber < checkWatermark) break;
96
+ const object = objects[idx][1];
97
+ if (
98
+ object instanceof PDFRawStream &&
99
+ object.dict.lookup(PDFName.of('Type')) === PDFName.of('XRef')
100
+ ) {
101
+ this._lastXRefObjectNumber = objects[idx][0].objectNumber;
102
+ break;
103
+ }
104
+ }
105
+ }
106
+ should = objNum !== this._lastXRefObjectNumber;
107
+ }
108
+ if (!should && this._largestSkippedObjectNum < objNum)
109
+ this._largestSkippedObjectNum = objNum;
110
+ return should;
111
+ }
112
+
56
113
  async serializeToBuffer(): Promise<Uint8Array> {
57
114
  const incremental = !(this.snapshot instanceof DefaultDocumentSnapshot);
58
115
  const { size, header, indirectObjects, xref, trailerDict, trailer } =
@@ -70,7 +127,7 @@ class PDFWriter {
70
127
  for (let idx = 0, len = indirectObjects.length; idx < len; idx++) {
71
128
  const [ref, object] = indirectObjects[idx];
72
129
 
73
- if (!this.snapshot.shouldSave(ref.objectNumber)) {
130
+ if (!this.shouldSave(incremental, ref.objectNumber, indirectObjects)) {
74
131
  continue;
75
132
  }
76
133
 
@@ -130,8 +187,17 @@ class PDFWriter {
130
187
  }
131
188
 
132
189
  protected createTrailerDict(prevStartXRef?: number): PDFDict {
190
+ /**
191
+ * if last object (XRef Stream) is not in the output, then size is one less.
192
+ * An XRef Stream object should always be the largest object number in PDF
193
+ */
194
+ const size =
195
+ this.context.largestObjectNumber +
196
+ (this._largestSkippedObjectNum === this.context.largestObjectNumber
197
+ ? 0
198
+ : 1);
133
199
  return this.context.obj({
134
- Size: this.context.largestObjectNumber + 1,
200
+ Size: size,
135
201
  Root: this.context.trailerInfo.Root,
136
202
  Encrypt: this.context.trailerInfo.Encrypt,
137
203
  Info: this.context.trailerInfo.Info,
@@ -143,6 +209,8 @@ class PDFWriter {
143
209
  protected async computeBufferSize(
144
210
  incremental: boolean,
145
211
  ): Promise<SerializationInfo> {
212
+ this._largestSkippedObjectNum = 0;
213
+ this._lastXRefObjectNumber = 0;
146
214
  const header = PDFHeader.forVersion(1, 7);
147
215
 
148
216
  let size = this.snapshot.pdfSize;
@@ -160,7 +228,8 @@ class PDFWriter {
160
228
  for (let idx = 0, len = indirectObjects.length; idx < len; idx++) {
161
229
  const indirectObject = indirectObjects[idx];
162
230
  const [ref, object] = indirectObject;
163
- if (!this.snapshot.shouldSave(ref.objectNumber)) continue;
231
+ if (!this.shouldSave(incremental, ref.objectNumber, indirectObjects))
232
+ continue;
164
233
  if (security) this.encrypt(ref, object, security);
165
234
  xref.addEntry(ref, size);
166
235
  size += this.computeIndirectObjectSize(indirectObject);
@@ -8,7 +8,7 @@ export declare class IncrementalDocumentSnapshot implements DocumentSnapshot {
8
8
  private lastObjectNumber;
9
9
  private changedObjects;
10
10
  context: PDFContext;
11
- constructor(lastObjectNumber: number, indirectObjects: number[], pdfSize: number, prevStartXRef: number, context: PDFContext);
11
+ constructor(lastObjectNumber: number, indirectObjects: Set<number>, pdfSize: number, prevStartXRef: number, context: PDFContext);
12
12
  shouldSave(objectNumber: number): boolean;
13
13
  markRefForSave(ref: PDFRef): void;
14
14
  markRefsForSave(refs: PDFRef[]): void;
@@ -27,6 +27,29 @@ declare class PDFWriter {
27
27
  protected readonly snapshot: DocumentSnapshot;
28
28
  private parsedObjects;
29
29
  protected constructor(context: PDFContext, objectsPerTick: number, snapshot: DocumentSnapshot);
30
+ /**
31
+ * If PDF has an XRef Stream, then the last object will be probably be skipped on saving.
32
+ * If that's the case, this property will have that object number, and the PDF /Size can
33
+ * be corrected, to be accurate.
34
+ */
35
+ protected _largestSkippedObjectNum: number;
36
+ /**
37
+ * Used to check wheter an object should be saved or not, preserves the object number of the
38
+ * last XRef Stream object, if there is one.
39
+ */
40
+ protected _lastXRefObjectNumber: number;
41
+ /**
42
+ * For incremental saves, defers the decision to the snapshot.
43
+ * For full saves, checks that the object is not the last XRef stream object.
44
+ * @param {boolean} incremental If making an incremental save, or a full save of the PDF
45
+ * @param {number} objNum Object number
46
+ * @param {[PDFRef, PDFObject][]} objects List of objects that form the PDF
47
+ * @returns {boolean} whether the object should be saved or not
48
+ */
49
+ protected shouldSave(incremental: boolean, objNum: number, objects: [
50
+ PDFRef,
51
+ PDFObject
52
+ ][]): boolean;
30
53
  serializeToBuffer(): Promise<Uint8Array>;
31
54
  protected computeIndirectObjectSize([ref, object]: [
32
55
  PDFRef,
@@ -8,7 +8,7 @@ export declare class IncrementalDocumentSnapshot implements DocumentSnapshot {
8
8
  private lastObjectNumber;
9
9
  private changedObjects;
10
10
  context: PDFContext;
11
- constructor(lastObjectNumber: number, indirectObjects: number[], pdfSize: number, prevStartXRef: number, context: PDFContext);
11
+ constructor(lastObjectNumber: number, indirectObjects: Set<number>, pdfSize: number, prevStartXRef: number, context: PDFContext);
12
12
  shouldSave(objectNumber: number): boolean;
13
13
  markRefForSave(ref: PDFRef): void;
14
14
  markRefsForSave(refs: PDFRef[]): void;
@@ -27,6 +27,29 @@ declare class PDFWriter {
27
27
  protected readonly snapshot: DocumentSnapshot;
28
28
  private parsedObjects;
29
29
  protected constructor(context: PDFContext, objectsPerTick: number, snapshot: DocumentSnapshot);
30
+ /**
31
+ * If PDF has an XRef Stream, then the last object will be probably be skipped on saving.
32
+ * If that's the case, this property will have that object number, and the PDF /Size can
33
+ * be corrected, to be accurate.
34
+ */
35
+ protected _largestSkippedObjectNum: number;
36
+ /**
37
+ * Used to check wheter an object should be saved or not, preserves the object number of the
38
+ * last XRef Stream object, if there is one.
39
+ */
40
+ protected _lastXRefObjectNumber: number;
41
+ /**
42
+ * For incremental saves, defers the decision to the snapshot.
43
+ * For full saves, checks that the object is not the last XRef stream object.
44
+ * @param {boolean} incremental If making an incremental save, or a full save of the PDF
45
+ * @param {number} objNum Object number
46
+ * @param {[PDFRef, PDFObject][]} objects List of objects that form the PDF
47
+ * @returns {boolean} whether the object should be saved or not
48
+ */
49
+ protected shouldSave(incremental: boolean, objNum: number, objects: [
50
+ PDFRef,
51
+ PDFObject
52
+ ][]): boolean;
30
53
  serializeToBuffer(): Promise<Uint8Array>;
31
54
  protected computeIndirectObjectSize([ref, object]: [
32
55
  PDFRef,