@adnsistemas/pdf-lib 2.6.4 → 2.7.1

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 (66) hide show
  1. package/README.md +44 -0
  2. package/cjs/api/PDFDocument.d.ts +22 -0
  3. package/cjs/api/PDFDocument.d.ts.map +1 -1
  4. package/cjs/api/PDFDocument.js +45 -0
  5. package/cjs/api/PDFDocument.js.map +1 -1
  6. package/cjs/api/PDFFont.d.ts +1 -1
  7. package/cjs/api/PDFFont.d.ts.map +1 -1
  8. package/cjs/api/PDFFont.js +3 -5
  9. package/cjs/api/PDFFont.js.map +1 -1
  10. package/cjs/core/PDFContext.d.ts +2 -0
  11. package/cjs/core/PDFContext.d.ts.map +1 -1
  12. package/cjs/core/PDFContext.js +43 -2
  13. package/cjs/core/PDFContext.js.map +1 -1
  14. package/cjs/core/objects/PDFArray.d.ts +1 -0
  15. package/cjs/core/objects/PDFArray.d.ts.map +1 -1
  16. package/cjs/core/objects/PDFArray.js +7 -0
  17. package/cjs/core/objects/PDFArray.js.map +1 -1
  18. package/cjs/core/objects/PDFRawStream.d.ts.map +1 -1
  19. package/cjs/core/objects/PDFRawStream.js +1 -0
  20. package/cjs/core/objects/PDFRawStream.js.map +1 -1
  21. package/cjs/utils/png.js +1 -1
  22. package/cjs/utils/png.js.map +1 -1
  23. package/dist/pdf-lib.esm.js +224 -45
  24. package/dist/pdf-lib.esm.js.map +1 -1
  25. package/dist/pdf-lib.esm.min.js +3 -3
  26. package/dist/pdf-lib.esm.min.js.map +1 -1
  27. package/dist/pdf-lib.js +224 -45
  28. package/dist/pdf-lib.js.map +1 -1
  29. package/dist/pdf-lib.min.js +2 -2
  30. package/dist/pdf-lib.min.js.map +1 -1
  31. package/es/api/PDFDocument.d.ts +22 -0
  32. package/es/api/PDFDocument.d.ts.map +1 -1
  33. package/es/api/PDFDocument.js +45 -0
  34. package/es/api/PDFDocument.js.map +1 -1
  35. package/es/api/PDFFont.d.ts +1 -1
  36. package/es/api/PDFFont.d.ts.map +1 -1
  37. package/es/api/PDFFont.js +3 -5
  38. package/es/api/PDFFont.js.map +1 -1
  39. package/es/core/PDFContext.d.ts +2 -0
  40. package/es/core/PDFContext.d.ts.map +1 -1
  41. package/es/core/PDFContext.js +43 -2
  42. package/es/core/PDFContext.js.map +1 -1
  43. package/es/core/objects/PDFArray.d.ts +1 -0
  44. package/es/core/objects/PDFArray.d.ts.map +1 -1
  45. package/es/core/objects/PDFArray.js +7 -0
  46. package/es/core/objects/PDFArray.js.map +1 -1
  47. package/es/core/objects/PDFRawStream.d.ts.map +1 -1
  48. package/es/core/objects/PDFRawStream.js +1 -0
  49. package/es/core/objects/PDFRawStream.js.map +1 -1
  50. package/es/utils/png.js +1 -1
  51. package/es/utils/png.js.map +1 -1
  52. package/package.json +4 -3
  53. package/src/api/PDFDocument.ts +60 -0
  54. package/src/api/PDFFont.ts +3 -5
  55. package/src/core/PDFContext.ts +42 -1
  56. package/src/core/objects/PDFArray.ts +8 -0
  57. package/src/core/objects/PDFRawStream.ts +1 -0
  58. package/src/utils/png.ts +1 -1
  59. package/ts3.4/cjs/api/PDFDocument.d.ts +22 -0
  60. package/ts3.4/cjs/api/PDFFont.d.ts +1 -1
  61. package/ts3.4/cjs/core/PDFContext.d.ts +2 -0
  62. package/ts3.4/cjs/core/objects/PDFArray.d.ts +1 -0
  63. package/ts3.4/es/api/PDFDocument.d.ts +22 -0
  64. package/ts3.4/es/api/PDFFont.d.ts +1 -1
  65. package/ts3.4/es/core/PDFContext.d.ts +2 -0
  66. package/ts3.4/es/core/objects/PDFArray.d.ts +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adnsistemas/pdf-lib",
3
- "version": "2.6.4",
3
+ "version": "2.7.1",
4
4
  "description": "Create and modify PDF files with JavaScript",
5
5
  "author": "Andrew Dillon <andrew.dillon.j@gmail.com>",
6
6
  "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e",
@@ -42,7 +42,8 @@
42
42
  "Phakorn Kiong (https://github.com/PhakornKiong)",
43
43
  "Donald Ness (https://github.com/programmarchy)",
44
44
  "Remus DRAICA (https://github.com/remdra)",
45
- "David Abdala (https://github.com/adnsistemas)"
45
+ "David Abdala (https://github.com/adnsistemas)",
46
+ "Eran Ohayon (https://github.com/erano067)"
46
47
  ],
47
48
  "scripts": {
48
49
  "release": "yarn release:prep && release-it",
@@ -100,8 +101,8 @@
100
101
  "yarn.lock"
101
102
  ],
102
103
  "dependencies": {
104
+ "@adnsistemas/upng": "^1.0.3",
103
105
  "@pdf-lib/standard-fonts": "^1.0.0",
104
- "@pdf-lib/upng": "^1.0.1",
105
106
  "color": "^4.2.3",
106
107
  "crypto-js": "^4.2.0",
107
108
  "node-html-better-parser": "^1.4.0",
@@ -1697,6 +1697,66 @@ export default class PDFDocument {
1697
1697
  .filter((ov) => ov.actual || ov.previous.length);
1698
1698
  }
1699
1699
 
1700
+ /**
1701
+ * Saves the current changes to the document as an incremental update, returns the full document,
1702
+ * like save method, and modifies the internal state to be able to continue editing the document
1703
+ * for another incremental update.
1704
+ * This allows you to save multiple incremental updates without reloading the PDF.
1705
+ *
1706
+ * For example:
1707
+ * ```js
1708
+ * const pdfDoc = await PDFDocument.load(pdfBytes, { forIncrementalUpdate: true })
1709
+ *
1710
+ * const page = pdfDoc.getPage(0)
1711
+ * page.drawText('First update')
1712
+ * const firstsave = await pdfDoc.saveAndContinue()
1713
+ *
1714
+ * page.drawText('Second update', { y: 100 })
1715
+ * const secondsave = await pdfDoc.saveAndContinue()
1716
+ * ```
1717
+ *
1718
+ * @param options The options to be used when saving changes.
1719
+ * @returns Resolves with the complete PDF bytes including all updates.
1720
+ */
1721
+ async saveAndContinue(
1722
+ options: IncrementalSaveOptions = {},
1723
+ ): Promise<Uint8Array> {
1724
+ if (!this.context.pdfFileDetails.originalBytes || !this.context.snapshot) {
1725
+ throw new Error(
1726
+ 'saveAndContinue() requires the document to be loaded with forIncrementalUpdate: true',
1727
+ );
1728
+ }
1729
+ const originalBytes = this.context.pdfFileDetails.originalBytes;
1730
+ const incrementalBytes = await this.saveIncremental(
1731
+ this.context.snapshot,
1732
+ options,
1733
+ );
1734
+
1735
+ const newPdfBytes = new Uint8Array(
1736
+ originalBytes.byteLength + incrementalBytes.byteLength,
1737
+ );
1738
+ newPdfBytes.set(originalBytes);
1739
+ newPdfBytes.set(incrementalBytes, originalBytes.byteLength);
1740
+
1741
+ this.context.pdfFileDetails.originalBytes = newPdfBytes;
1742
+ this.context.pdfFileDetails.pdfSize = newPdfBytes.byteLength;
1743
+
1744
+ const incrementalStr = new TextDecoder('latin1').decode(incrementalBytes);
1745
+ const startxrefMatch = incrementalStr.match(/startxref\s+(\d+)/);
1746
+ if (startxrefMatch) {
1747
+ this.context.pdfFileDetails.prevStartXRef = parseInt(
1748
+ startxrefMatch[1],
1749
+ 10,
1750
+ );
1751
+ } else {
1752
+ this.context.pdfFileDetails.prevStartXRef = originalBytes.byteLength;
1753
+ }
1754
+
1755
+ this.context.snapshot = this.takeSnapshot();
1756
+
1757
+ return newPdfBytes;
1758
+ }
1759
+
1700
1760
  private async prepareForSave(options: SaveOptions): Promise<void> {
1701
1761
  const { addDefaultPage = true, updateFieldAppearances = true } = options;
1702
1762
 
@@ -38,7 +38,7 @@ export default class PDFFont implements Embeddable {
38
38
  /** The name of this font. */
39
39
  readonly name: string;
40
40
 
41
- private modified = true;
41
+ private alreadyEmbedded = false;
42
42
  private readonly embedder: FontEmbedder;
43
43
 
44
44
  private constructor(ref: PDFRef, doc: PDFDocument, embedder: FontEmbedder) {
@@ -68,7 +68,6 @@ export default class PDFFont implements Embeddable {
68
68
  */
69
69
  encodeText(text: string): PDFHexString {
70
70
  assertIs(text, 'text', ['string']);
71
- this.modified = true;
72
71
  return this.embedder.encodeText(text);
73
72
  }
74
73
 
@@ -145,10 +144,9 @@ export default class PDFFont implements Embeddable {
145
144
  * @returns Resolves when the embedding is complete.
146
145
  */
147
146
  async embed(): Promise<void> {
148
- // TODO: Cleanup orphan embedded objects if a font is embedded multiple times...
149
- if (this.modified) {
147
+ if (!this.alreadyEmbedded) {
150
148
  await this.embedder.embedIntoContext(this.doc.context, this.ref);
151
- this.modified = false;
149
+ this.alreadyEmbedded = true;
152
150
  }
153
151
  }
154
152
  }
@@ -429,7 +429,48 @@ class PDFContext {
429
429
  }
430
430
 
431
431
  registerObjectChange(obj: PDFObject) {
432
- if (this.snapshot) this.snapshot.markObjForSave(obj);
432
+ if (!this.snapshot) return;
433
+
434
+ const ref = this.getObjectRef(obj);
435
+ if (ref) {
436
+ this.snapshot.markRefForSave(ref);
437
+ return;
438
+ }
439
+
440
+ const containingRef = this.findContainingIndirectObject(obj);
441
+ if (containingRef) {
442
+ this.snapshot.markRefForSave(containingRef);
443
+ }
444
+ }
445
+
446
+ private findContainingIndirectObject(target: PDFObject): PDFRef | undefined {
447
+ const entries = Array.from(this.indirectObjects.entries());
448
+ for (let idx = 0, len = entries.length; idx < len; idx++) {
449
+ const [ref, object] = entries[idx];
450
+ if (this.objectContains(object, target)) {
451
+ return ref;
452
+ }
453
+ }
454
+ return undefined;
455
+ }
456
+
457
+ private objectContains(container: PDFObject, target: PDFObject): boolean {
458
+ if (container === target) return true;
459
+
460
+ if (container instanceof PDFDict) {
461
+ const values = container.values();
462
+ for (let i = 0, len = values.length; i < len; i++) {
463
+ if (this.objectContains(values[i], target)) return true;
464
+ }
465
+ } else if (container instanceof PDFArray) {
466
+ for (let i = 0, len = container.size(); i < len; i++) {
467
+ if (this.objectContains(container.get(i), target)) return true;
468
+ }
469
+ } else if (container instanceof PDFStream) {
470
+ if (this.objectContains(container.dict, target)) return true;
471
+ }
472
+
473
+ return false;
433
474
  }
434
475
 
435
476
  getObjectVersions(ref: PDFRef): PDFObject[] {
@@ -30,10 +30,12 @@ class PDFArray extends PDFObject {
30
30
  }
31
31
 
32
32
  push(object: PDFObject): void {
33
+ this.registerChange();
33
34
  this.array.push(object);
34
35
  }
35
36
 
36
37
  insert(index: number, object: PDFObject): void {
38
+ this.registerChange();
37
39
  this.array.splice(index, 0, object);
38
40
  }
39
41
 
@@ -43,10 +45,12 @@ class PDFArray extends PDFObject {
43
45
  }
44
46
 
45
47
  remove(index: number): void {
48
+ this.registerChange();
46
49
  this.array.splice(index, 1);
47
50
  }
48
51
 
49
52
  set(idx: number, object: PDFObject): void {
53
+ this.registerChange();
50
54
  this.array[idx] = object;
51
55
  }
52
56
 
@@ -180,6 +184,10 @@ class PDFArray extends PDFObject {
180
184
  }
181
185
  }
182
186
  }
187
+
188
+ registerChange(): void {
189
+ this.context.registerObjectChange(this);
190
+ }
183
191
  }
184
192
 
185
193
  export default PDFArray;
@@ -45,6 +45,7 @@ class PDFRawStream extends PDFStream {
45
45
  }
46
46
 
47
47
  updateContents(contents: Uint8Array): void {
48
+ this.dict.registerChange();
48
49
  this.contents = contents;
49
50
  }
50
51
  }
package/src/utils/png.ts CHANGED
@@ -1,4 +1,4 @@
1
- import UPNG from '@pdf-lib/upng';
1
+ import UPNG from '@adnsistemas/upng';
2
2
 
3
3
  const getImageType = (ctype: number) => {
4
4
  if (ctype === 0) return PngType.Greyscale;
@@ -842,6 +842,28 @@ export default class PDFDocument {
842
842
  * @returns {PDFObjectVersions[]} Objects modified in the update, and previous versions
843
843
  */
844
844
  getChangedObjects(lastUpdateMinusX?: number): PDFObjectVersions[];
845
+ /**
846
+ * Saves the current changes to the document as an incremental update, returns the full document,
847
+ * like save method, and modifies the internal state to be able to continue editing the document
848
+ * for another incremental update.
849
+ * This allows you to save multiple incremental updates without reloading the PDF.
850
+ *
851
+ * For example:
852
+ * ```js
853
+ * const pdfDoc = await PDFDocument.load(pdfBytes, { forIncrementalUpdate: true })
854
+ *
855
+ * const page = pdfDoc.getPage(0)
856
+ * page.drawText('First update')
857
+ * const firstsave = await pdfDoc.saveAndContinue()
858
+ *
859
+ * page.drawText('Second update', { y: 100 })
860
+ * const secondsave = await pdfDoc.saveAndContinue()
861
+ * ```
862
+ *
863
+ * @param options The options to be used when saving changes.
864
+ * @returns Resolves with the complete PDF bytes including all updates.
865
+ */
866
+ saveAndContinue(options?: IncrementalSaveOptions): Promise<Uint8Array>;
845
867
  private prepareForSave;
846
868
  private embedAll;
847
869
  private updateInfoDict;
@@ -25,7 +25,7 @@ export default class PDFFont implements Embeddable {
25
25
  readonly doc: PDFDocument;
26
26
  /** The name of this font. */
27
27
  readonly name: string;
28
- private modified;
28
+ private alreadyEmbedded;
29
29
  private readonly embedder;
30
30
  private constructor();
31
31
  /**
@@ -119,6 +119,8 @@ declare class PDFContext {
119
119
  getPopGraphicsStateContentStream(): PDFRef;
120
120
  addRandomSuffix(prefix: string, suffixLength?: number): string;
121
121
  registerObjectChange(obj: PDFObject): void;
122
+ private findContainingIndirectObject;
123
+ private objectContains;
122
124
  getObjectVersions(ref: PDFRef): PDFObject[];
123
125
  listXrefEntries(xrefIndex?: number): Entry[];
124
126
  }
@@ -59,6 +59,7 @@ declare class PDFArray extends PDFObject {
59
59
  sizeInBytes(): number;
60
60
  copyBytesInto(buffer: Uint8Array, offset: number): number;
61
61
  scalePDFNumbers(x: number, y: number): void;
62
+ registerChange(): void;
62
63
  }
63
64
  export default PDFArray;
64
65
  //# sourceMappingURL=PDFArray.d.ts.map
@@ -842,6 +842,28 @@ export default class PDFDocument {
842
842
  * @returns {PDFObjectVersions[]} Objects modified in the update, and previous versions
843
843
  */
844
844
  getChangedObjects(lastUpdateMinusX?: number): PDFObjectVersions[];
845
+ /**
846
+ * Saves the current changes to the document as an incremental update, returns the full document,
847
+ * like save method, and modifies the internal state to be able to continue editing the document
848
+ * for another incremental update.
849
+ * This allows you to save multiple incremental updates without reloading the PDF.
850
+ *
851
+ * For example:
852
+ * ```js
853
+ * const pdfDoc = await PDFDocument.load(pdfBytes, { forIncrementalUpdate: true })
854
+ *
855
+ * const page = pdfDoc.getPage(0)
856
+ * page.drawText('First update')
857
+ * const firstsave = await pdfDoc.saveAndContinue()
858
+ *
859
+ * page.drawText('Second update', { y: 100 })
860
+ * const secondsave = await pdfDoc.saveAndContinue()
861
+ * ```
862
+ *
863
+ * @param options The options to be used when saving changes.
864
+ * @returns Resolves with the complete PDF bytes including all updates.
865
+ */
866
+ saveAndContinue(options?: IncrementalSaveOptions): Promise<Uint8Array>;
845
867
  private prepareForSave;
846
868
  private embedAll;
847
869
  private updateInfoDict;
@@ -25,7 +25,7 @@ export default class PDFFont implements Embeddable {
25
25
  readonly doc: PDFDocument;
26
26
  /** The name of this font. */
27
27
  readonly name: string;
28
- private modified;
28
+ private alreadyEmbedded;
29
29
  private readonly embedder;
30
30
  private constructor();
31
31
  /**
@@ -119,6 +119,8 @@ declare class PDFContext {
119
119
  getPopGraphicsStateContentStream(): PDFRef;
120
120
  addRandomSuffix(prefix: string, suffixLength?: number): string;
121
121
  registerObjectChange(obj: PDFObject): void;
122
+ private findContainingIndirectObject;
123
+ private objectContains;
122
124
  getObjectVersions(ref: PDFRef): PDFObject[];
123
125
  listXrefEntries(xrefIndex?: number): Entry[];
124
126
  }
@@ -59,6 +59,7 @@ declare class PDFArray extends PDFObject {
59
59
  sizeInBytes(): number;
60
60
  copyBytesInto(buffer: Uint8Array, offset: number): number;
61
61
  scalePDFNumbers(x: number, y: number): void;
62
+ registerChange(): void;
62
63
  }
63
64
  export default PDFArray;
64
65
  //# sourceMappingURL=PDFArray.d.ts.map