pdf-lite 1.2.1 → 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 (52) hide show
  1. package/EXAMPLES.md +1 -1
  2. package/dist/acroform/acroform.d.ts +272 -16
  3. package/dist/acroform/acroform.js +1084 -144
  4. package/dist/acroform/manager.d.ts +2 -2
  5. package/dist/acroform/manager.js +3 -3
  6. package/dist/core/decoder.d.ts +1 -1
  7. package/dist/core/decoder.js +3 -3
  8. package/dist/core/index.d.ts +2 -2
  9. package/dist/core/index.js +2 -2
  10. package/dist/core/objects/pdf-array.d.ts +1 -0
  11. package/dist/core/objects/pdf-array.js +4 -0
  12. package/dist/core/objects/pdf-dictionary.d.ts +1 -0
  13. package/dist/core/objects/pdf-dictionary.js +12 -0
  14. package/dist/core/objects/pdf-hexadecimal.d.ts +9 -2
  15. package/dist/core/objects/pdf-hexadecimal.js +25 -5
  16. package/dist/core/objects/pdf-indirect-object.d.ts +5 -3
  17. package/dist/core/objects/pdf-indirect-object.js +23 -5
  18. package/dist/core/objects/pdf-number.js +3 -0
  19. package/dist/core/objects/pdf-object.d.ts +6 -0
  20. package/dist/core/objects/pdf-object.js +10 -0
  21. package/dist/core/objects/pdf-stream.js +3 -0
  22. package/dist/core/objects/pdf-string.d.ts +11 -1
  23. package/dist/core/objects/pdf-string.js +24 -6
  24. package/dist/core/ref.d.ts +5 -0
  25. package/dist/core/ref.js +14 -0
  26. package/dist/core/serializer.d.ts +1 -1
  27. package/dist/core/serializer.js +1 -1
  28. package/dist/core/tokeniser.d.ts +2 -2
  29. package/dist/core/tokeniser.js +37 -75
  30. package/dist/core/tokens/hexadecimal-token.d.ts +8 -1
  31. package/dist/core/tokens/hexadecimal-token.js +20 -2
  32. package/dist/core/tokens/name-token.js +0 -3
  33. package/dist/core/tokens/string-token.d.ts +8 -1
  34. package/dist/core/tokens/string-token.js +20 -2
  35. package/dist/fonts/font-manager.js +6 -8
  36. package/dist/pdf/pdf-document.d.ts +12 -11
  37. package/dist/pdf/pdf-document.js +50 -42
  38. package/dist/pdf/pdf-revision.d.ts +33 -4
  39. package/dist/pdf/pdf-revision.js +100 -26
  40. package/dist/pdf/pdf-xref-lookup.js +3 -2
  41. package/dist/utils/decodeWithFontEncoding.d.ts +20 -0
  42. package/dist/utils/decodeWithFontEncoding.js +67 -0
  43. package/dist/utils/escapeString.d.ts +1 -1
  44. package/dist/utils/escapeString.js +12 -3
  45. package/dist/utils/glyphNameToUnicode.d.ts +10 -0
  46. package/dist/utils/glyphNameToUnicode.js +4292 -0
  47. package/dist/xfa/manager.js +2 -4
  48. package/package.json +1 -1
  49. /package/dist/core/{incremental-parser.d.ts → parser/incremental-parser.d.ts} +0 -0
  50. /package/dist/core/{incremental-parser.js → parser/incremental-parser.js} +0 -0
  51. /package/dist/core/{parser.d.ts → parser/parser.d.ts} +0 -0
  52. /package/dist/core/{parser.js → parser/parser.js} +0 -0
@@ -11,12 +11,12 @@ export declare class PdfAcroFormManager {
11
11
  * Checks if the document contains AcroForm fields.
12
12
  * @returns True if the document has AcroForm fields, false otherwise
13
13
  */
14
- hasAcroForm(): Promise<boolean>;
14
+ exists(): Promise<boolean>;
15
15
  /**
16
16
  * Gets the AcroForm object from the document catalog.
17
17
  * @returns The AcroForm object or null if not found
18
18
  */
19
- getAcroForm(): Promise<PdfAcroForm | null>;
19
+ read(): Promise<PdfAcroForm | null>;
20
20
  /**
21
21
  * Writes the provided AcroForm to the associated PDF document.
22
22
  * @param acroForm The AcroForm instance to serialize into the document.
@@ -12,9 +12,9 @@ export class PdfAcroFormManager {
12
12
  * Checks if the document contains AcroForm fields.
13
13
  * @returns True if the document has AcroForm fields, false otherwise
14
14
  */
15
- async hasAcroForm() {
15
+ async exists() {
16
16
  try {
17
- const acroForm = await this.getAcroForm();
17
+ const acroForm = await this.read();
18
18
  return acroForm !== null;
19
19
  }
20
20
  catch {
@@ -25,7 +25,7 @@ export class PdfAcroFormManager {
25
25
  * Gets the AcroForm object from the document catalog.
26
26
  * @returns The AcroForm object or null if not found
27
27
  */
28
- async getAcroForm() {
28
+ async read() {
29
29
  return await PdfAcroForm.fromDocument(this.document);
30
30
  }
31
31
  /**
@@ -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
  /**
@@ -179,7 +179,7 @@ export class PdfDecoder extends IncrementalParser {
179
179
  out = new PdfBoolean(token.value);
180
180
  }
181
181
  else if (token instanceof PdfHexadecimalToken) {
182
- out = new PdfHexadecimal(token.raw, 'hex');
182
+ out = new PdfHexadecimal(token.raw, 'hex', token.originalBytes);
183
183
  }
184
184
  else if (token instanceof PdfNullToken) {
185
185
  out = new PdfNull();
@@ -188,7 +188,7 @@ export class PdfDecoder extends IncrementalParser {
188
188
  out = new PdfObjectReference(token.objectNumber, token.generationNumber);
189
189
  }
190
190
  else if (token instanceof PdfStringToken) {
191
- out = new PdfString(token.value);
191
+ out = new PdfString(token.value, token.originalBytes);
192
192
  }
193
193
  else {
194
194
  throw new Error(`Unknown primitive token type: ${token.type}`);
@@ -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,8 +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;
10
- constructor(value: string | ByteArray, format?: 'hex' | 'bytes');
9
+ private _raw;
10
+ /**
11
+ * Original bytes from the PDF file, including angle brackets.
12
+ * Used to preserve exact formatting for incremental updates.
13
+ */
14
+ private _originalBytes?;
15
+ constructor(value: string | ByteArray, format?: 'hex' | 'bytes', originalBytes?: ByteArray);
16
+ get raw(): ByteArray;
17
+ set raw(raw: ByteArray);
11
18
  static toHexadecimal(data: string | ByteArray): PdfHexadecimal;
12
19
  get bytes(): ByteArray;
13
20
  toHexBytes(): ByteArray;
@@ -9,8 +9,13 @@ 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;
13
- constructor(value, format = 'hex') {
12
+ _raw;
13
+ /**
14
+ * Original bytes from the PDF file, including angle brackets.
15
+ * Used to preserve exact formatting for incremental updates.
16
+ */
17
+ _originalBytes;
18
+ constructor(value, format = 'hex', originalBytes) {
14
19
  super();
15
20
  let bytes;
16
21
  if (format === 'bytes') {
@@ -19,7 +24,20 @@ export class PdfHexadecimal extends PdfObject {
19
24
  else {
20
25
  bytes = value instanceof Uint8Array ? value : stringToBytes(value);
21
26
  }
22
- this.raw = bytes;
27
+ this._raw = bytes;
28
+ this._originalBytes = originalBytes;
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;
23
41
  }
24
42
  static toHexadecimal(data) {
25
43
  return new PdfHexadecimal(data, 'bytes');
@@ -34,9 +52,11 @@ export class PdfHexadecimal extends PdfObject {
34
52
  return bytesToString(this.toHexBytes());
35
53
  }
36
54
  tokenize() {
37
- return [new PdfHexadecimalToken(this.raw)];
55
+ return [new PdfHexadecimalToken(this.raw, this._originalBytes)];
38
56
  }
39
57
  clone() {
40
- return new PdfHexadecimal(new Uint8Array(this.raw));
58
+ return new PdfHexadecimal(new Uint8Array(this.raw), 'hex', this._originalBytes
59
+ ? new Uint8Array(this._originalBytes)
60
+ : undefined);
41
61
  }
42
62
  }
@@ -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));
@@ -6,9 +6,19 @@ export declare class PdfString extends PdfObject {
6
6
  * The raw bytes of the PDF string.
7
7
  */
8
8
  private _raw;
9
- constructor(raw: ByteArray | string);
9
+ /**
10
+ * Original bytes from the PDF file, including parentheses and escape sequences.
11
+ * Used to preserve exact formatting for incremental updates.
12
+ */
13
+ private _originalBytes?;
14
+ constructor(raw: ByteArray | string, originalBytes?: ByteArray);
10
15
  get raw(): ByteArray;
11
16
  set raw(raw: ByteArray);
17
+ /**
18
+ * Checks if this string is UTF-16BE encoded (has UTF-16BE BOM).
19
+ * UTF-16BE strings start with the byte order mark 0xFE 0xFF.
20
+ */
21
+ get isUTF16BE(): boolean;
12
22
  get value(): string;
13
23
  protected tokenize(): PdfStringToken[];
14
24
  clone(): this;
@@ -10,7 +10,12 @@ export class PdfString extends PdfObject {
10
10
  * The raw bytes of the PDF string.
11
11
  */
12
12
  _raw;
13
- constructor(raw) {
13
+ /**
14
+ * Original bytes from the PDF file, including parentheses and escape sequences.
15
+ * Used to preserve exact formatting for incremental updates.
16
+ */
17
+ _originalBytes;
18
+ constructor(raw, originalBytes) {
14
19
  super();
15
20
  if (typeof raw === 'string') {
16
21
  // Check if the string contains non-ASCII characters
@@ -26,28 +31,41 @@ export class PdfString extends PdfObject {
26
31
  else {
27
32
  this._raw = raw;
28
33
  }
34
+ this._originalBytes = originalBytes;
29
35
  }
30
36
  get raw() {
31
37
  return this._raw;
32
38
  }
33
39
  set raw(raw) {
40
+ if (this.isImmutable()) {
41
+ throw new Error('Cannot modify an immutable PdfString');
42
+ }
34
43
  this.setModified();
35
44
  this._raw = raw;
45
+ // Clear original bytes when modified
46
+ this._originalBytes = undefined;
47
+ }
48
+ /**
49
+ * Checks if this string is UTF-16BE encoded (has UTF-16BE BOM).
50
+ * UTF-16BE strings start with the byte order mark 0xFE 0xFF.
51
+ */
52
+ get isUTF16BE() {
53
+ return (this.raw.length >= 2 && this.raw[0] === 0xfe && this.raw[1] === 0xff);
36
54
  }
37
55
  get value() {
38
56
  // Check for UTF-16BE BOM (0xFE 0xFF)
39
- if (this.raw.length >= 2 &&
40
- this.raw[0] === 0xfe &&
41
- this.raw[1] === 0xff) {
57
+ if (this.isUTF16BE) {
42
58
  return decodeFromUTF16BE(this.raw);
43
59
  }
44
60
  // Default: use PDFDocEncoding
45
61
  return decodeFromPDFDocEncoding(this.raw);
46
62
  }
47
63
  tokenize() {
48
- return [new PdfStringToken(this.raw)];
64
+ return [new PdfStringToken(this.raw, this._originalBytes)];
49
65
  }
50
66
  clone() {
51
- return new PdfString(new Uint8Array(this.raw));
67
+ return new PdfString(new Uint8Array(this.raw), this._originalBytes
68
+ ? new Uint8Array(this._originalBytes)
69
+ : undefined);
52
70
  }
53
71
  }
@@ -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.