pdf-lite 1.3.0 → 1.3.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 (37) hide show
  1. package/dist/acroform/acroform.d.ts +258 -23
  2. package/dist/acroform/acroform.js +986 -174
  3. package/dist/core/decoder.d.ts +1 -1
  4. package/dist/core/decoder.js +1 -1
  5. package/dist/core/index.d.ts +2 -2
  6. package/dist/core/index.js +2 -2
  7. package/dist/core/objects/pdf-array.d.ts +1 -0
  8. package/dist/core/objects/pdf-array.js +4 -0
  9. package/dist/core/objects/pdf-dictionary.d.ts +1 -0
  10. package/dist/core/objects/pdf-dictionary.js +12 -0
  11. package/dist/core/objects/pdf-hexadecimal.d.ts +3 -1
  12. package/dist/core/objects/pdf-hexadecimal.js +14 -2
  13. package/dist/core/objects/pdf-indirect-object.d.ts +5 -3
  14. package/dist/core/objects/pdf-indirect-object.js +23 -5
  15. package/dist/core/objects/pdf-number.js +3 -0
  16. package/dist/core/objects/pdf-object.d.ts +6 -0
  17. package/dist/core/objects/pdf-object.js +10 -0
  18. package/dist/core/objects/pdf-stream.js +3 -0
  19. package/dist/core/objects/pdf-string.js +3 -0
  20. package/dist/core/ref.d.ts +5 -0
  21. package/dist/core/ref.js +14 -0
  22. package/dist/core/serializer.d.ts +1 -1
  23. package/dist/core/serializer.js +1 -1
  24. package/dist/core/tokeniser.d.ts +2 -2
  25. package/dist/core/tokeniser.js +2 -2
  26. package/dist/fonts/font-manager.js +6 -8
  27. package/dist/pdf/pdf-document.d.ts +6 -5
  28. package/dist/pdf/pdf-document.js +29 -21
  29. package/dist/pdf/pdf-revision.d.ts +33 -4
  30. package/dist/pdf/pdf-revision.js +100 -26
  31. package/dist/pdf/pdf-xref-lookup.js +3 -2
  32. package/dist/xfa/manager.js +2 -4
  33. package/package.json +1 -1
  34. /package/dist/core/{incremental-parser.d.ts → parser/incremental-parser.d.ts} +0 -0
  35. /package/dist/core/{incremental-parser.js → parser/incremental-parser.js} +0 -0
  36. /package/dist/core/{parser.d.ts → parser/parser.d.ts} +0 -0
  37. /package/dist/core/{parser.js → parser/parser.js} +0 -0
@@ -1,6 +1,6 @@
1
1
  import { PdfObject } from './objects/pdf-object.js';
2
2
  import { PdfToken } from './tokens/token.js';
3
- import { IncrementalParser } from './incremental-parser.js';
3
+ import { IncrementalParser } from './parser/incremental-parser.js';
4
4
  /**
5
5
  * Decodes PDF tokens into PDF objects.
6
6
  * Handles parsing of all PDF object types including dictionaries, arrays, streams, and xref tables.
@@ -36,7 +36,7 @@ import { PdfComment } from './objects/pdf-comment.js';
36
36
  import { PdfStartXRefToken } from './tokens/start-xref-token.js';
37
37
  import { PdfStartXRef } from './objects/pdf-start-xref.js';
38
38
  import { PdfWhitespaceToken } from './tokens/whitespace-token.js';
39
- import { IncrementalParser } from './incremental-parser.js';
39
+ import { IncrementalParser } from './parser/incremental-parser.js';
40
40
  import { concatUint8Arrays } from '../utils/concatUint8Arrays.js';
41
41
  const DEFAULT_MAX_BUFFER_SIZE_BYTES = 10 * 1024 * 1024; // 10 MB
42
42
  /**
@@ -1,6 +1,6 @@
1
1
  export * from './decoder.js';
2
2
  export * from './generators.js';
3
- export * from './incremental-parser.js';
3
+ export * from './parser/incremental-parser.js';
4
4
  export * from './objects/pdf-array.js';
5
5
  export * from './objects/pdf-boolean.js';
6
6
  export * from './objects/pdf-comment.js';
@@ -18,7 +18,7 @@ export * from './objects/pdf-stream.js';
18
18
  export * from './objects/pdf-string.js';
19
19
  export * from './objects/pdf-trailer.js';
20
20
  export * from './objects/pdf-xref-table.js';
21
- export * from './parser.js';
21
+ export * from './parser/parser.js';
22
22
  export * from './ref.js';
23
23
  export * from './serializer.js';
24
24
  export * from './streams/object-stream.js';
@@ -1,6 +1,6 @@
1
1
  export * from './decoder.js';
2
2
  export * from './generators.js';
3
- export * from './incremental-parser.js';
3
+ export * from './parser/incremental-parser.js';
4
4
  export * from './objects/pdf-array.js';
5
5
  export * from './objects/pdf-boolean.js';
6
6
  export * from './objects/pdf-comment.js';
@@ -18,7 +18,7 @@ export * from './objects/pdf-stream.js';
18
18
  export * from './objects/pdf-string.js';
19
19
  export * from './objects/pdf-trailer.js';
20
20
  export * from './objects/pdf-xref-table.js';
21
- export * from './parser.js';
21
+ export * from './parser/parser.js';
22
22
  export * from './ref.js';
23
23
  export * from './serializer.js';
24
24
  export * from './streams/object-stream.js';
@@ -9,4 +9,5 @@ export declare class PdfArray<T extends PdfObject = PdfObject> extends PdfObject
9
9
  protected tokenize(): import("../index.js").PdfToken[];
10
10
  clone(): this;
11
11
  isModified(): boolean;
12
+ setImmutable(immutable?: boolean): void;
12
13
  }
@@ -39,4 +39,8 @@ export class PdfArray extends PdfObject {
39
39
  isModified() {
40
40
  return (super.isModified() || this.items.some((item) => item.isModified()));
41
41
  }
42
+ setImmutable(immutable) {
43
+ super.setImmutable(immutable);
44
+ this.items.forEach((item) => item.setImmutable(immutable));
45
+ }
42
46
  }
@@ -24,4 +24,5 @@ export declare class PdfDictionary<T extends PdfDictionaryEntries = PdfDictionar
24
24
  copyFrom(other: PdfDictionary<any>): void;
25
25
  clone(): this;
26
26
  isModified(): boolean;
27
+ setImmutable(immutable?: boolean): void;
27
28
  }
@@ -28,6 +28,9 @@ export class PdfDictionary extends PdfObject {
28
28
  }
29
29
  }
30
30
  set(key, value) {
31
+ if (this.isImmutable()) {
32
+ throw new Error('Cannot modify an immutable PdfDictionary');
33
+ }
31
34
  const currentValue = this.get(key);
32
35
  if (currentValue !== value && !currentValue?.equals(value)) {
33
36
  this.modified = true;
@@ -47,6 +50,9 @@ export class PdfDictionary extends PdfObject {
47
50
  this.#entries.set(new PdfName(key), value);
48
51
  }
49
52
  delete(key) {
53
+ if (this.isImmutable()) {
54
+ throw new Error('Cannot modify an immutable PdfDictionary');
55
+ }
50
56
  if (this.has(key)) {
51
57
  this.modified = true;
52
58
  }
@@ -141,4 +147,10 @@ export class PdfDictionary extends PdfObject {
141
147
  return (super.isModified() ||
142
148
  Array.from(this.#entries.values()).some((v) => v?.isModified()));
143
149
  }
150
+ setImmutable(immutable) {
151
+ super.setImmutable(immutable);
152
+ for (const value of this.#entries.values()) {
153
+ value?.setImmutable(immutable);
154
+ }
155
+ }
144
156
  }
@@ -6,13 +6,15 @@ export declare class PdfHexadecimal extends PdfObject {
6
6
  * The raw byte value represented by this hexadecimal object.
7
7
  * NB: This is the hexadecimal representation, not the actual byte values.
8
8
  */
9
- raw: ByteArray;
9
+ private _raw;
10
10
  /**
11
11
  * Original bytes from the PDF file, including angle brackets.
12
12
  * Used to preserve exact formatting for incremental updates.
13
13
  */
14
14
  private _originalBytes?;
15
15
  constructor(value: string | ByteArray, format?: 'hex' | 'bytes', originalBytes?: ByteArray);
16
+ get raw(): ByteArray;
17
+ set raw(raw: ByteArray);
16
18
  static toHexadecimal(data: string | ByteArray): PdfHexadecimal;
17
19
  get bytes(): ByteArray;
18
20
  toHexBytes(): ByteArray;
@@ -9,7 +9,7 @@ export class PdfHexadecimal extends PdfObject {
9
9
  * The raw byte value represented by this hexadecimal object.
10
10
  * NB: This is the hexadecimal representation, not the actual byte values.
11
11
  */
12
- raw;
12
+ _raw;
13
13
  /**
14
14
  * Original bytes from the PDF file, including angle brackets.
15
15
  * Used to preserve exact formatting for incremental updates.
@@ -24,9 +24,21 @@ export class PdfHexadecimal extends PdfObject {
24
24
  else {
25
25
  bytes = value instanceof Uint8Array ? value : stringToBytes(value);
26
26
  }
27
- this.raw = bytes;
27
+ this._raw = bytes;
28
28
  this._originalBytes = originalBytes;
29
29
  }
30
+ get raw() {
31
+ return this._raw;
32
+ }
33
+ set raw(raw) {
34
+ if (this.isImmutable()) {
35
+ throw new Error('Cannot modify an immutable PdfHexadecimal');
36
+ }
37
+ this.setModified();
38
+ this._raw = raw;
39
+ // Clear original bytes when modified
40
+ this._originalBytes = undefined;
41
+ }
30
42
  static toHexadecimal(data) {
31
43
  return new PdfHexadecimal(data, 'bytes');
32
44
  }
@@ -8,20 +8,22 @@ export declare class PdfIndirectObject<T extends PdfObject = PdfObject> extends
8
8
  offset: Ref<number>;
9
9
  encryptable?: boolean;
10
10
  orderIndex?: number;
11
- constructor(options: {
11
+ constructor(options?: {
12
12
  objectNumber?: number;
13
13
  generationNumber?: number;
14
- content: T;
14
+ content?: T;
15
15
  offset?: number | Ref<number>;
16
16
  encryptable?: boolean;
17
- } | T);
17
+ } | T | PdfIndirectObject);
18
18
  get reference(): PdfObjectReference;
19
19
  isEncryptable(): boolean;
20
20
  static createPlaceholder<T extends PdfObject>(objectNumber?: number, generationNumber?: number, content?: T): PdfIndirectObject<T extends unknown ? PdfNull : T>;
21
21
  inPdf(): boolean;
22
22
  matchesReference(ref?: PdfObjectReference): boolean;
23
23
  protected tokenize(): import("../index.js").PdfToken[];
24
+ copyFrom(other: PdfIndirectObject): void;
24
25
  clone(): this;
25
26
  order(): number;
26
27
  isModified(): boolean;
28
+ setImmutable(immutable?: boolean): void;
27
29
  }
@@ -14,19 +14,25 @@ export class PdfIndirectObject extends PdfObjectReference {
14
14
  encryptable;
15
15
  orderIndex;
16
16
  constructor(options) {
17
+ if (options instanceof PdfIndirectObject) {
18
+ super(options.objectNumber, options.generationNumber);
19
+ this.content = options.content.clone();
20
+ this.offset = options.offset.clone();
21
+ return;
22
+ }
17
23
  if (options instanceof PdfObject) {
18
24
  super(-1, 0);
19
25
  this.content = options;
20
26
  this.offset = new Ref(0);
21
27
  return;
22
28
  }
23
- super(options.objectNumber ?? -1, options.generationNumber ?? 0);
24
- this.content = options.content;
29
+ super(options?.objectNumber ?? -1, options?.generationNumber ?? 0);
30
+ this.content = options?.content ?? new PdfNull();
25
31
  this.offset =
26
- options.offset instanceof Ref
32
+ options?.offset instanceof Ref
27
33
  ? options.offset
28
- : new Ref(options.offset ?? 0);
29
- this.encryptable = options.encryptable;
34
+ : new Ref(options?.offset ?? 0);
35
+ this.encryptable = options?.encryptable;
30
36
  }
31
37
  get reference() {
32
38
  return new Proxy(this, {
@@ -81,6 +87,13 @@ export class PdfIndirectObject extends PdfObjectReference {
81
87
  new PdfEndObjectToken(),
82
88
  ];
83
89
  }
90
+ copyFrom(other) {
91
+ this.objectNumber = other.objectNumber;
92
+ this.generationNumber = other.generationNumber;
93
+ this.content = other.content.clone();
94
+ this.offset = other.offset.clone();
95
+ this.modified = true;
96
+ }
84
97
  clone() {
85
98
  return new PdfIndirectObject({
86
99
  objectNumber: this.objectNumber,
@@ -97,4 +110,9 @@ export class PdfIndirectObject extends PdfObjectReference {
97
110
  this.content.isModified() ||
98
111
  this.offset.isModified);
99
112
  }
113
+ setImmutable(immutable) {
114
+ super.setImmutable(immutable);
115
+ this.content.setImmutable(immutable);
116
+ this.offset.setImmutable(immutable);
117
+ }
100
118
  }
@@ -45,6 +45,9 @@ export class PdfNumber extends PdfObject {
45
45
  return this.#value.resolve();
46
46
  }
47
47
  set value(value) {
48
+ if (this.isImmutable()) {
49
+ throw new Error('Cannot modify an immutable PdfNumber');
50
+ }
48
51
  this.#value.update(value);
49
52
  }
50
53
  onChange(callback) {
@@ -7,6 +7,8 @@ export declare abstract class PdfObject {
7
7
  postTokens?: PdfToken[];
8
8
  /** Indicates whether the object has been modified. By default, assume it has been modified because it's a new object */
9
9
  protected modified: boolean;
10
+ /** Indicates whether the object is immutable (cannot be modified) */
11
+ protected immutable: boolean;
10
12
  /** Tokenizes the object into an array of PdfTokens */
11
13
  protected abstract tokenize(): PdfToken[];
12
14
  /** The type of this PDF object */
@@ -15,6 +17,10 @@ export declare abstract class PdfObject {
15
17
  isModified(): boolean;
16
18
  /** Sets the modified state of the object. Override this method if the modified state is determined differently */
17
19
  setModified(modified?: boolean): void;
20
+ /** Indicates whether the object is immutable (cannot be modified) */
21
+ isImmutable(): boolean;
22
+ /** Sets the immutable state of the object */
23
+ setImmutable(immutable?: boolean): void;
18
24
  /** Converts the object to an array of PdfTokens, including any pre or post tokens */
19
25
  toTokens(): PdfToken[];
20
26
  /** Converts the object to a ByteArray, optionally padding to a specified length */
@@ -7,6 +7,8 @@ export class PdfObject {
7
7
  postTokens;
8
8
  /** Indicates whether the object has been modified. By default, assume it has been modified because it's a new object */
9
9
  modified = true;
10
+ /** Indicates whether the object is immutable (cannot be modified) */
11
+ immutable = false;
10
12
  /** The type of this PDF object */
11
13
  get objectType() {
12
14
  return this.constructor.name;
@@ -19,6 +21,14 @@ export class PdfObject {
19
21
  setModified(modified = true) {
20
22
  this.modified = modified;
21
23
  }
24
+ /** Indicates whether the object is immutable (cannot be modified) */
25
+ isImmutable() {
26
+ return this.immutable;
27
+ }
28
+ /** Sets the immutable state of the object */
29
+ setImmutable(immutable = true) {
30
+ this.immutable = immutable;
31
+ }
22
32
  /** Converts the object to an array of PdfTokens, including any pre or post tokens */
23
33
  toTokens() {
24
34
  return [
@@ -44,6 +44,9 @@ export class PdfStream extends PdfObject {
44
44
  return this.original.slice(0, length);
45
45
  }
46
46
  set raw(data) {
47
+ if (this.isImmutable()) {
48
+ throw new Error('Cannot modify an immutable PdfStream');
49
+ }
47
50
  this.setModified();
48
51
  this.original = data;
49
52
  this.header.set('Length', new PdfNumber(data.length));
@@ -37,6 +37,9 @@ export class PdfString extends PdfObject {
37
37
  return this._raw;
38
38
  }
39
39
  set raw(raw) {
40
+ if (this.isImmutable()) {
41
+ throw new Error('Cannot modify an immutable PdfString');
42
+ }
40
43
  this.setModified();
41
44
  this._raw = raw;
42
45
  // Clear original bytes when modified
@@ -23,6 +23,7 @@ export declare class Ref<T> {
23
23
  /** Registered callbacks for update notifications */
24
24
  callbacks: Array<RefUpdateCallback<T>>;
25
25
  isModified: boolean;
26
+ protected immutable: boolean;
26
27
  /**
27
28
  * Creates a new Ref with an initial value.
28
29
  *
@@ -30,6 +31,8 @@ export declare class Ref<T> {
30
31
  * @throws Error if attempting to create a self-referencing Ref
31
32
  */
32
33
  constructor(value: T | Ref<T>);
34
+ setImmutable(immutable?: boolean): void;
35
+ isImmutable(): boolean;
33
36
  /**
34
37
  * Updates the reference to a new value or another Ref.
35
38
  * Notifies all registered callbacks of the change.
@@ -56,4 +59,6 @@ export declare class Ref<T> {
56
59
  * @param callback - The function to call on value updates
57
60
  */
58
61
  onUpdate(callback: RefUpdateCallback<T>): void;
62
+ /** Creates a new Ref with the same resolved value. */
63
+ clone(): Ref<T>;
59
64
  }
package/dist/core/ref.js CHANGED
@@ -17,6 +17,7 @@ export class Ref {
17
17
  /** Registered callbacks for update notifications */
18
18
  callbacks = [];
19
19
  isModified = false;
20
+ immutable = false;
20
21
  /**
21
22
  * Creates a new Ref with an initial value.
22
23
  *
@@ -29,6 +30,12 @@ export class Ref {
29
30
  }
30
31
  this.value = value;
31
32
  }
33
+ setImmutable(immutable = true) {
34
+ this.immutable = immutable;
35
+ }
36
+ isImmutable() {
37
+ return this.immutable;
38
+ }
32
39
  /**
33
40
  * Updates the reference to a new value or another Ref.
34
41
  * Notifies all registered callbacks of the change.
@@ -42,6 +49,9 @@ export class Ref {
42
49
  const resolvedNewValue = newValue instanceof Ref ? newValue.resolve() : newValue;
43
50
  const oldValue = this.resolve();
44
51
  if (oldValue !== resolvedNewValue) {
52
+ if (this.immutable) {
53
+ throw new Error(`Cannot update an immutable Ref (${oldValue} -> ${resolvedNewValue})`);
54
+ }
45
55
  this.isModified = true;
46
56
  }
47
57
  this.value = newValue;
@@ -81,4 +91,8 @@ export class Ref {
81
91
  onUpdate(callback) {
82
92
  this.callbacks.push(callback);
83
93
  }
94
+ /** Creates a new Ref with the same resolved value. */
95
+ clone() {
96
+ return new Ref(this.resolve());
97
+ }
84
98
  }
@@ -1,5 +1,5 @@
1
1
  import { ByteArray } from '../types.js';
2
- import { Parser } from './parser.js';
2
+ import { Parser } from './parser/parser.js';
3
3
  import { PdfToken } from './tokens/token.js';
4
4
  /**
5
5
  * Serializes PDF tokens into a byte stream.
@@ -1,4 +1,4 @@
1
- import { Parser } from './parser.js';
1
+ import { Parser } from './parser/parser.js';
2
2
  import { PdfByteOffsetToken } from './tokens/byte-offset-token.js';
3
3
  /**
4
4
  * Serializes PDF tokens into a byte stream.
@@ -1,7 +1,7 @@
1
- import { IncrementalParser } from './incremental-parser.js';
1
+ import { IncrementalParser } from './parser/incremental-parser.js';
2
2
  import { PdfObject } from './objects/pdf-object.js';
3
3
  import { PdfToken } from './tokens/token.js';
4
- import { Parser } from './parser.js';
4
+ import { Parser } from './parser/parser.js';
5
5
  import { ByteArray } from '../types.js';
6
6
  /**
7
7
  * Type alias for a parser that converts bytes to PDF tokens.
@@ -1,7 +1,7 @@
1
1
  import { assert } from '../utils/assert.js';
2
2
  import { bytesToString } from '../utils/bytesToString.js';
3
3
  import { unescapeString } from '../utils/unescapeString.js';
4
- import { IncrementalParser } from './incremental-parser.js';
4
+ import { IncrementalParser } from './parser/incremental-parser.js';
5
5
  import { PdfBooleanToken } from './tokens/boolean-token.js';
6
6
  import { PdfCommentToken } from './tokens/comment-token.js';
7
7
  import { PdfEndArrayToken } from './tokens/end-array-token.js';
@@ -25,7 +25,7 @@ import { PdfWhitespaceToken } from './tokens/whitespace-token.js';
25
25
  import { PdfXRefTableEntryToken } from './tokens/xref-table-entry-token.js';
26
26
  import { PdfXRefTableSectionStartToken } from './tokens/xref-table-section-start-token.js';
27
27
  import { PdfXRefTableStartToken } from './tokens/xref-table-start-token.js';
28
- import { Parser } from './parser.js';
28
+ import { Parser } from './parser/parser.js';
29
29
  import { concatUint8Arrays } from '../utils/concatUint8Arrays.js';
30
30
  import { stringToBytes } from '../utils/stringToBytes.js';
31
31
  const ByteMap = {
@@ -273,10 +273,10 @@ export class PdfFontManager {
273
273
  */
274
274
  async collectAllFontsFromPdf() {
275
275
  const fonts = new Map();
276
- const catalog = this.document.rootDictionary;
276
+ const catalog = this.document.root;
277
277
  if (!catalog)
278
278
  return fonts;
279
- const pagesRef = catalog.get('Pages');
279
+ const pagesRef = catalog.content.get('Pages');
280
280
  if (!pagesRef)
281
281
  return fonts;
282
282
  const pagesObjRef = pagesRef.as(PdfObjectReference);
@@ -290,11 +290,11 @@ export class PdfFontManager {
290
290
  * This ensures fonts are available to form fields.
291
291
  */
292
292
  async addFontToAcroFormResources(resourceName, fontObject) {
293
- const catalog = this.document.rootDictionary;
293
+ const catalog = this.document.root;
294
294
  if (!catalog)
295
295
  return;
296
296
  // Get AcroForm dictionary
297
- const acroFormRef = catalog.get('AcroForm');
297
+ const acroFormRef = catalog.content.get('AcroForm');
298
298
  if (!acroFormRef)
299
299
  return;
300
300
  let acroFormDict;
@@ -339,10 +339,8 @@ export class PdfFontManager {
339
339
  * This is more efficient than adding to each individual page.
340
340
  */
341
341
  async addFontToPageResources(resourceName, fontObject) {
342
- const catalog = this.document.rootDictionary;
343
- if (!catalog)
344
- return;
345
- const pagesRef = catalog.get('Pages');
342
+ const catalog = this.document.root;
343
+ const pagesRef = catalog.content.get('Pages');
346
344
  if (!pagesRef)
347
345
  return;
348
346
  const pagesObjRef = pagesRef.as(PdfObjectReference);
@@ -33,8 +33,6 @@ import { PdfFontManager } from '../fonts/font-manager.js';
33
33
  * ```
34
34
  */
35
35
  export declare class PdfDocument extends PdfObject {
36
- /** PDF version comment header */
37
- header: PdfComment;
38
36
  /** List of document revisions for incremental updates */
39
37
  revisions: PdfRevision[];
40
38
  /** Signer instance for digital signature operations */
@@ -65,6 +63,8 @@ export declare class PdfDocument extends PdfObject {
65
63
  securityHandler?: PdfSecurityHandler;
66
64
  signer?: PdfSigner;
67
65
  });
66
+ get header(): PdfComment | undefined;
67
+ set header(comment: PdfComment | undefined);
68
68
  /** XFA manager for handling XFA forms */
69
69
  get xfa(): PdfXfaManager;
70
70
  /** AcroForm manager for handling form fields */
@@ -125,13 +125,14 @@ export declare class PdfDocument extends PdfObject {
125
125
  * @throws Error if the encryption dictionary reference points to a non-dictionary object
126
126
  */
127
127
  get encryptionDictionary(): PdfEncryptionDictionaryObject | undefined;
128
+ get rootReference(): PdfObjectReference;
128
129
  /**
129
- * Gets the document catalog (root) dictionary.
130
+ * Gets the document catalog (root) dictionary, or creates one if it doesn't exist.
130
131
  *
131
- * @returns The root dictionary or undefined if not found
132
+ * @returns The root dictionary
132
133
  * @throws Error if the Root reference points to a non-dictionary object
133
134
  */
134
- get rootDictionary(): PdfDictionary | undefined;
135
+ get root(): PdfIndirectObject<PdfDictionary>;
135
136
  /**
136
137
  * Gets the reference to the metadata stream from the document catalog.
137
138
  *
@@ -20,6 +20,7 @@ import { PdfSigner } from '../signing/signer.js';
20
20
  import { PdfXfaManager } from '../xfa/manager.js';
21
21
  import { PdfAcroFormManager } from '../acroform/manager.js';
22
22
  import { PdfFontManager } from '../fonts/font-manager.js';
23
+ import { concatUint8Arrays } from '../utils/concatUint8Arrays.js';
23
24
  /**
24
25
  * Represents a PDF document with support for reading, writing, and modifying PDF files.
25
26
  * Handles document structure, revisions, encryption, and digital signatures.
@@ -38,8 +39,6 @@ import { PdfFontManager } from '../fonts/font-manager.js';
38
39
  * ```
39
40
  */
40
41
  export class PdfDocument extends PdfObject {
41
- /** PDF version comment header */
42
- header = PdfComment.versionComment('1.7');
43
42
  /** List of document revisions for incremental updates */
44
43
  revisions;
45
44
  /** Signer instance for digital signature operations */
@@ -83,6 +82,13 @@ export class PdfDocument extends PdfObject {
83
82
  this.securityHandler =
84
83
  options?.securityHandler ?? this.getSecurityHandler();
85
84
  }
85
+ get header() {
86
+ return this.revisions[0].header;
87
+ }
88
+ set header(comment) {
89
+ if (comment)
90
+ this.revisions[0].header = comment;
91
+ }
86
92
  /** XFA manager for handling XFA forms */
87
93
  get xfa() {
88
94
  if (!this._xfa) {
@@ -157,6 +163,9 @@ export class PdfDocument extends PdfObject {
157
163
  this.startNewRevision();
158
164
  }
159
165
  for (const obj of objects) {
166
+ if (obj.isImmutable()) {
167
+ throw new Error('Risky adding a immutable obj');
168
+ }
160
169
  this.toBeCommitted.push(obj);
161
170
  this.latestRevision.addObject(obj);
162
171
  }
@@ -227,16 +236,24 @@ export class PdfDocument extends PdfObject {
227
236
  encryptionDictObject.encryptable = false;
228
237
  return encryptionDictObject;
229
238
  }
239
+ get rootReference() {
240
+ return this.root.reference;
241
+ }
230
242
  /**
231
- * Gets the document catalog (root) dictionary.
243
+ * Gets the document catalog (root) dictionary, or creates one if it doesn't exist.
232
244
  *
233
- * @returns The root dictionary or undefined if not found
245
+ * @returns The root dictionary
234
246
  * @throws Error if the Root reference points to a non-dictionary object
235
247
  */
236
- get rootDictionary() {
248
+ get root() {
237
249
  const rootRef = this.trailerDict.get('Root')?.as(PdfObjectReference);
238
250
  if (!rootRef) {
239
- return undefined;
251
+ const rootObject = new PdfIndirectObject({
252
+ content: new PdfDictionary(),
253
+ });
254
+ this.add(rootObject);
255
+ this.trailerDict.set('Root', rootObject.reference);
256
+ return rootObject;
240
257
  }
241
258
  const rootObject = this.findUncompressedObject(rootRef);
242
259
  if (!rootObject) {
@@ -245,7 +262,7 @@ export class PdfDocument extends PdfObject {
245
262
  if (!(rootObject?.content instanceof PdfDictionary)) {
246
263
  throw new Error(`Root object ${rootRef.objectNumber} ${rootRef.generationNumber} is not a dictionary, it is a ${rootObject?.content.objectType}`);
247
264
  }
248
- return rootObject.content;
265
+ return rootObject;
249
266
  }
250
267
  /**
251
268
  * Gets the reference to the metadata stream from the document catalog.
@@ -253,11 +270,11 @@ export class PdfDocument extends PdfObject {
253
270
  * @returns The metadata stream reference or undefined if not present
254
271
  */
255
272
  get metadataStreamReference() {
256
- const root = this.rootDictionary;
273
+ const root = this.root;
257
274
  if (!root) {
258
275
  return;
259
276
  }
260
- const metadataRef = root.get('Metadata')?.as(PdfObjectReference);
277
+ const metadataRef = root.content.get('Metadata')?.as(PdfObjectReference);
261
278
  if (!metadataRef) {
262
279
  return;
263
280
  }
@@ -501,9 +518,6 @@ export class PdfDocument extends PdfObject {
501
518
  foundObject = foundObject.clone();
502
519
  await this.securityHandler.decryptObject(foundObject);
503
520
  }
504
- else if (this.isIncremental()) {
505
- foundObject = foundObject.clone(); // Clone to prevent modifications in locked revisions
506
- }
507
521
  return foundObject;
508
522
  }
509
523
  /**
@@ -591,7 +605,7 @@ export class PdfDocument extends PdfObject {
591
605
  * @throws Error if the document has no root dictionary
592
606
  */
593
607
  async setDocumentSecurityStore(dss) {
594
- let rootDictionary = this.rootDictionary;
608
+ let rootDictionary = this.root?.content;
595
609
  if (!rootDictionary) {
596
610
  throw new Error('Cannot set DSS - document has no root dictionary');
597
611
  }
@@ -615,10 +629,6 @@ export class PdfDocument extends PdfObject {
615
629
  }
616
630
  return tokens.map((token) => ({ token, object: obj }));
617
631
  });
618
- const headerTokens = this.header
619
- .toTokens()
620
- .map((token) => ({ token, object: this.header }));
621
- documentTokens.unshift(...headerTokens);
622
632
  return documentTokens;
623
633
  }
624
634
  tokenize() {
@@ -698,9 +708,7 @@ export class PdfDocument extends PdfObject {
698
708
  toBytes() {
699
709
  this.calculateOffsets();
700
710
  this.updateRevisions();
701
- const serializer = new PdfTokenSerializer();
702
- serializer.feedMany(this.toTokens());
703
- return serializer.toBytes();
711
+ return concatUint8Arrays(...this.revisions.map((x) => x.toBytes()));
704
712
  }
705
713
  /**
706
714
  * Serializes the document to a Base64-encoded string.
@@ -725,7 +733,7 @@ export class PdfDocument extends PdfObject {
725
733
  const clonedRevisions = this.revisions.map((rev) => rev.clone());
726
734
  return new PdfDocument({
727
735
  revisions: clonedRevisions,
728
- version: this.header.clone(),
736
+ version: this.header?.clone(),
729
737
  securityHandler: this.securityHandler,
730
738
  });
731
739
  }