pdf-lite 1.3.3 → 1.5.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 (124) hide show
  1. package/dist/acroform/acroform.d.ts +7 -454
  2. package/dist/acroform/acroform.js +5 -1664
  3. package/dist/acroform/appearance/index.d.ts +4 -0
  4. package/dist/acroform/appearance/index.js +4 -0
  5. package/dist/acroform/appearance/pdf-appearance-stream.d.ts +21 -0
  6. package/dist/acroform/appearance/pdf-appearance-stream.js +41 -0
  7. package/dist/acroform/appearance/pdf-button-appearance-stream.d.ts +13 -0
  8. package/dist/acroform/appearance/pdf-button-appearance-stream.js +54 -0
  9. package/dist/acroform/appearance/pdf-choice-appearance-stream.d.ts +22 -0
  10. package/dist/acroform/appearance/pdf-choice-appearance-stream.js +75 -0
  11. package/dist/acroform/appearance/pdf-graphics.d.ts +51 -0
  12. package/dist/acroform/appearance/pdf-graphics.js +239 -0
  13. package/dist/acroform/appearance/pdf-text-appearance-stream.d.ts +22 -0
  14. package/dist/acroform/appearance/pdf-text-appearance-stream.js +104 -0
  15. package/dist/acroform/fields/index.d.ts +8 -0
  16. package/dist/acroform/fields/index.js +8 -0
  17. package/dist/acroform/fields/pdf-button-form-field.d.ts +23 -0
  18. package/dist/acroform/fields/pdf-button-form-field.js +102 -0
  19. package/dist/acroform/fields/pdf-choice-form-field.d.ts +18 -0
  20. package/dist/acroform/fields/pdf-choice-form-field.js +131 -0
  21. package/dist/acroform/fields/pdf-default-appearance.d.ts +23 -0
  22. package/dist/acroform/fields/pdf-default-appearance.js +68 -0
  23. package/dist/acroform/fields/pdf-form-field-flags.d.ts +45 -0
  24. package/dist/acroform/fields/pdf-form-field-flags.js +122 -0
  25. package/dist/acroform/fields/pdf-form-field.d.ts +123 -0
  26. package/dist/acroform/fields/pdf-form-field.js +433 -0
  27. package/dist/acroform/fields/pdf-signature-form-field.d.ts +7 -0
  28. package/dist/acroform/fields/pdf-signature-form-field.js +12 -0
  29. package/dist/acroform/fields/pdf-text-form-field.d.ts +10 -0
  30. package/dist/acroform/fields/pdf-text-form-field.js +77 -0
  31. package/dist/acroform/fields/types.d.ts +26 -0
  32. package/dist/acroform/fields/types.js +9 -0
  33. package/dist/acroform/index.d.ts +5 -1
  34. package/dist/acroform/index.js +5 -1
  35. package/dist/acroform/manager.d.ts +12 -1
  36. package/dist/acroform/manager.js +20 -2
  37. package/dist/acroform/pdf-acro-form.d.ts +69 -0
  38. package/dist/acroform/pdf-acro-form.js +293 -0
  39. package/dist/acroform/pdf-font-encoding-cache.d.ts +27 -0
  40. package/dist/acroform/pdf-font-encoding-cache.js +188 -0
  41. package/dist/acroform/xfa/index.d.ts +3 -0
  42. package/dist/acroform/xfa/index.js +2 -0
  43. package/dist/acroform/xfa/pdf-xfa-data.d.ts +20 -0
  44. package/dist/acroform/xfa/pdf-xfa-data.js +68 -0
  45. package/dist/acroform/xfa/pdf-xfa-form.d.ts +11 -0
  46. package/dist/acroform/xfa/pdf-xfa-form.js +56 -0
  47. package/dist/annotations/index.d.ts +4 -0
  48. package/dist/annotations/index.js +4 -0
  49. package/dist/annotations/pdf-annotation-flags.d.ts +24 -0
  50. package/dist/annotations/pdf-annotation-flags.js +93 -0
  51. package/dist/annotations/pdf-annotation-writer.d.ts +20 -0
  52. package/dist/annotations/pdf-annotation-writer.js +76 -0
  53. package/dist/annotations/pdf-annotation.d.ts +61 -0
  54. package/dist/annotations/pdf-annotation.js +106 -0
  55. package/dist/annotations/pdf-widget-annotation.d.ts +15 -0
  56. package/dist/annotations/pdf-widget-annotation.js +37 -0
  57. package/dist/core/objects/pdf-array.d.ts +1 -1
  58. package/dist/core/objects/pdf-array.js +3 -2
  59. package/dist/core/objects/pdf-boolean.d.ts +1 -1
  60. package/dist/core/objects/pdf-boolean.js +3 -2
  61. package/dist/core/objects/pdf-comment.d.ts +1 -1
  62. package/dist/core/objects/pdf-comment.js +1 -1
  63. package/dist/core/objects/pdf-dictionary.d.ts +1 -1
  64. package/dist/core/objects/pdf-dictionary.js +3 -2
  65. package/dist/core/objects/pdf-hexadecimal.d.ts +1 -1
  66. package/dist/core/objects/pdf-hexadecimal.js +3 -2
  67. package/dist/core/objects/pdf-indirect-object.d.ts +1 -1
  68. package/dist/core/objects/pdf-indirect-object.js +1 -1
  69. package/dist/core/objects/pdf-name.d.ts +1 -1
  70. package/dist/core/objects/pdf-name.js +3 -2
  71. package/dist/core/objects/pdf-null.d.ts +1 -1
  72. package/dist/core/objects/pdf-null.js +3 -2
  73. package/dist/core/objects/pdf-number.d.ts +1 -1
  74. package/dist/core/objects/pdf-number.js +3 -2
  75. package/dist/core/objects/pdf-object-reference.d.ts +1 -1
  76. package/dist/core/objects/pdf-object-reference.js +3 -2
  77. package/dist/core/objects/pdf-object.d.ts +3 -1
  78. package/dist/core/objects/pdf-object.js +6 -0
  79. package/dist/core/objects/pdf-start-xref.d.ts +1 -1
  80. package/dist/core/objects/pdf-start-xref.js +3 -2
  81. package/dist/core/objects/pdf-stream.d.ts +4 -3
  82. package/dist/core/objects/pdf-stream.js +45 -16
  83. package/dist/core/objects/pdf-string.d.ts +2 -1
  84. package/dist/core/objects/pdf-string.js +17 -2
  85. package/dist/core/objects/pdf-trailer.d.ts +1 -1
  86. package/dist/core/objects/pdf-trailer.js +3 -2
  87. package/dist/core/objects/pdf-xref-table.d.ts +3 -3
  88. package/dist/core/objects/pdf-xref-table.js +3 -3
  89. package/dist/core/parser/incremental-parser.d.ts +0 -13
  90. package/dist/core/parser/incremental-parser.js +1 -18
  91. package/dist/core/streams/object-stream.d.ts +1 -1
  92. package/dist/core/streams/object-stream.js +1 -1
  93. package/dist/errors.d.ts +22 -0
  94. package/dist/errors.js +24 -0
  95. package/dist/fonts/index.d.ts +1 -1
  96. package/dist/fonts/index.js +1 -1
  97. package/dist/fonts/pdf-font.d.ts +64 -7
  98. package/dist/fonts/pdf-font.js +188 -8
  99. package/dist/index.d.ts +2 -0
  100. package/dist/index.js +2 -0
  101. package/dist/pdf/index.d.ts +0 -1
  102. package/dist/pdf/index.js +0 -1
  103. package/dist/pdf/pdf-document.d.ts +16 -12
  104. package/dist/pdf/pdf-document.js +51 -37
  105. package/dist/pdf/pdf-revision.d.ts +1 -1
  106. package/dist/pdf/pdf-revision.js +1 -1
  107. package/dist/pdf/pdf-xref-lookup.d.ts +8 -0
  108. package/dist/pdf/pdf-xref-lookup.js +12 -0
  109. package/dist/security/handlers/base.js +3 -0
  110. package/dist/utils/encodePdfText.d.ts +17 -0
  111. package/dist/utils/encodePdfText.js +61 -0
  112. package/dist/utils/index.d.ts +1 -1
  113. package/dist/utils/index.js +1 -1
  114. package/package.json +1 -1
  115. package/dist/pdf/errors.d.ts +0 -6
  116. package/dist/pdf/errors.js +0 -6
  117. package/dist/xfa/index.d.ts +0 -1
  118. package/dist/xfa/index.js +0 -1
  119. package/dist/xfa/manager.d.ts +0 -44
  120. package/dist/xfa/manager.js +0 -136
  121. /package/dist/fonts/{font-manager.d.ts → manager.d.ts} +0 -0
  122. /package/dist/fonts/{font-manager.js → manager.js} +0 -0
  123. /package/dist/utils/{IterableReadableStream.d.ts → iterable-readable-stream.d.ts} +0 -0
  124. /package/dist/utils/{IterableReadableStream.js → iterable-readable-stream.js} +0 -0
@@ -0,0 +1,69 @@
1
+ import { PdfDocument } from '../pdf/pdf-document.js';
2
+ import { PdfDictionary } from '../core/objects/pdf-dictionary.js';
3
+ import { PdfArray } from '../core/objects/pdf-array.js';
4
+ import { PdfString } from '../core/objects/pdf-string.js';
5
+ import { PdfObjectReference } from '../core/objects/pdf-object-reference.js';
6
+ import { PdfIndirectObject } from '../core/objects/pdf-indirect-object.js';
7
+ import { PdfBoolean } from '../core/objects/pdf-boolean.js';
8
+ import { PdfNumber } from '../core/objects/pdf-number.js';
9
+ import { PdfXfaForm } from './xfa/pdf-xfa-form.js';
10
+ import { PdfFormField } from './fields/pdf-form-field.js';
11
+ import './fields/pdf-text-form-field.js';
12
+ import './fields/pdf-button-form-field.js';
13
+ import './fields/pdf-choice-form-field.js';
14
+ import './fields/pdf-signature-form-field.js';
15
+ import type { FormContext } from './fields/types.js';
16
+ export type PdfDefaultResourcesDictionary = PdfDictionary<{
17
+ Font?: PdfDictionary;
18
+ ProcSet?: PdfArray;
19
+ ExtGState?: PdfDictionary;
20
+ ColorSpace?: PdfDictionary;
21
+ Pattern?: PdfDictionary;
22
+ Shading?: PdfDictionary;
23
+ XObject?: PdfDictionary;
24
+ }>;
25
+ export declare class PdfAcroForm<T extends Record<string, string> = Record<string, string>> extends PdfIndirectObject<PdfDictionary<{
26
+ Fields: PdfArray<PdfObjectReference>;
27
+ NeedAppearances?: PdfBoolean;
28
+ SigFlags?: PdfNumber;
29
+ CO?: PdfArray<PdfObjectReference>;
30
+ DR?: PdfDefaultResourcesDictionary;
31
+ DA?: PdfString;
32
+ Q?: PdfNumber;
33
+ XFA?: PdfDictionary;
34
+ }>> implements FormContext<PdfFormField> {
35
+ fields: PdfFormField[];
36
+ private _fontEncodingCache?;
37
+ private document?;
38
+ private _xfa?;
39
+ constructor(options?: {
40
+ other?: PdfIndirectObject;
41
+ fields?: PdfFormField[];
42
+ document?: PdfDocument;
43
+ });
44
+ private get fontEncodingCache();
45
+ get fontEncodingMaps(): Map<string, Map<number, string>>;
46
+ get fontRefs(): Map<string, PdfObjectReference>;
47
+ setXfa(xfa: PdfXfaForm | null): void;
48
+ isModified(): boolean;
49
+ get needAppearances(): boolean;
50
+ set needAppearances(value: boolean);
51
+ get signatureFlags(): number;
52
+ set signatureFlags(flags: number);
53
+ get defaultAppearance(): string | null;
54
+ set defaultAppearance(da: string);
55
+ get defaultQuadding(): number;
56
+ set defaultQuadding(q: number);
57
+ get defaultResources(): PdfDefaultResourcesDictionary | null;
58
+ set defaultResources(resources: PdfDefaultResourcesDictionary | null);
59
+ setValues(values: Partial<T>): void;
60
+ importData(fields: T): void;
61
+ exportData(): Partial<T>;
62
+ getFontEncodingMap(fontName: string): Promise<Map<number, string> | null>;
63
+ isFontUnicode(fontName: string): boolean;
64
+ static fromDocument(document: PdfDocument): Promise<PdfAcroForm | null>;
65
+ private cacheAllFontEncodings;
66
+ private updatePageAnnotations;
67
+ getXfa(document?: PdfDocument): Promise<PdfXfaForm | null>;
68
+ write(document?: PdfDocument): Promise<void>;
69
+ }
@@ -0,0 +1,293 @@
1
+ import { PdfDictionary } from '../core/objects/pdf-dictionary.js';
2
+ import { PdfArray } from '../core/objects/pdf-array.js';
3
+ import { PdfString } from '../core/objects/pdf-string.js';
4
+ import { PdfObjectReference } from '../core/objects/pdf-object-reference.js';
5
+ import { PdfIndirectObject } from '../core/objects/pdf-indirect-object.js';
6
+ import { PdfBoolean } from '../core/objects/pdf-boolean.js';
7
+ import { PdfNumber } from '../core/objects/pdf-number.js';
8
+ import { PdfFontEncodingCache } from './pdf-font-encoding-cache.js';
9
+ import { PdfAnnotationWriter } from '../annotations/pdf-annotation-writer.js';
10
+ import { PdfXfaForm } from './xfa/pdf-xfa-form.js';
11
+ import { PdfFormField } from './fields/pdf-form-field.js';
12
+ // Import subclasses to trigger static registration blocks
13
+ import './fields/pdf-text-form-field.js';
14
+ import './fields/pdf-button-form-field.js';
15
+ import './fields/pdf-choice-form-field.js';
16
+ import './fields/pdf-signature-form-field.js';
17
+ export class PdfAcroForm extends PdfIndirectObject {
18
+ fields;
19
+ _fontEncodingCache;
20
+ document;
21
+ _xfa; // undefined = not set; null = explicitly no XFA
22
+ constructor(options) {
23
+ super(options?.other ??
24
+ new PdfIndirectObject({
25
+ content: new PdfDictionary(),
26
+ }));
27
+ this.fields = options?.fields ?? [];
28
+ this.document = options?.document;
29
+ }
30
+ get fontEncodingCache() {
31
+ if (!this._fontEncodingCache) {
32
+ this._fontEncodingCache = new PdfFontEncodingCache(this.document, this.defaultResources);
33
+ }
34
+ return this._fontEncodingCache;
35
+ }
36
+ get fontEncodingMaps() {
37
+ return this.fontEncodingCache.fontEncodingMaps;
38
+ }
39
+ get fontRefs() {
40
+ return this.fontEncodingCache.fontRefs;
41
+ }
42
+ setXfa(xfa) {
43
+ this._xfa = xfa;
44
+ }
45
+ isModified() {
46
+ return this.content.isModified();
47
+ }
48
+ get needAppearances() {
49
+ return (this.content.get('NeedAppearances')?.as(PdfBoolean)?.value ?? false);
50
+ }
51
+ set needAppearances(value) {
52
+ this.content.set('NeedAppearances', new PdfBoolean(value));
53
+ }
54
+ get signatureFlags() {
55
+ return this.content.get('SigFlags')?.as(PdfNumber)?.value ?? 0;
56
+ }
57
+ set signatureFlags(flags) {
58
+ this.content.set('SigFlags', new PdfNumber(flags));
59
+ }
60
+ get defaultAppearance() {
61
+ return this.content.get('DA')?.as(PdfString)?.value ?? null;
62
+ }
63
+ set defaultAppearance(da) {
64
+ this.content.set('DA', new PdfString(da));
65
+ }
66
+ get defaultQuadding() {
67
+ return this.content.get('Q')?.as(PdfNumber)?.value ?? 0;
68
+ }
69
+ set defaultQuadding(q) {
70
+ this.content.set('Q', new PdfNumber(q));
71
+ }
72
+ get defaultResources() {
73
+ return this.content.get('DR')?.as(PdfDictionary) ?? null;
74
+ }
75
+ set defaultResources(resources) {
76
+ if (resources === null) {
77
+ this.content.delete('DR');
78
+ }
79
+ else {
80
+ this.content.set('DR', resources);
81
+ }
82
+ }
83
+ setValues(values) {
84
+ for (const field of this.fields) {
85
+ const name = field.name;
86
+ if (name in values && values[name] !== undefined) {
87
+ field.value = values[name];
88
+ }
89
+ }
90
+ }
91
+ importData(fields) {
92
+ for (const field of this.fields) {
93
+ const name = field.name;
94
+ if (name && name in fields) {
95
+ field.value = fields[name];
96
+ }
97
+ }
98
+ }
99
+ exportData() {
100
+ const result = {};
101
+ for (const field of this.fields) {
102
+ const name = field.name;
103
+ if (name) {
104
+ result[name] = field.value;
105
+ }
106
+ }
107
+ return result;
108
+ }
109
+ async getFontEncodingMap(fontName) {
110
+ return this.fontEncodingCache.getFontEncodingMap(fontName);
111
+ }
112
+ isFontUnicode(fontName) {
113
+ return this.fontEncodingCache.isFontUnicode(fontName);
114
+ }
115
+ static async fromDocument(document) {
116
+ const catalog = document.root;
117
+ if (!catalog)
118
+ return null;
119
+ const acroFormRef = catalog.content.get('AcroForm');
120
+ if (!acroFormRef)
121
+ return null;
122
+ let acroFormDict;
123
+ let acroFormContainer;
124
+ if (acroFormRef instanceof PdfObjectReference) {
125
+ const acroFormObject = await document.readObject(acroFormRef);
126
+ if (!acroFormObject)
127
+ return null;
128
+ if (!(acroFormObject.content instanceof PdfDictionary))
129
+ throw new Error('AcroForm content must be a dictionary');
130
+ acroFormDict = acroFormObject.content;
131
+ acroFormContainer = acroFormObject;
132
+ }
133
+ else if (acroFormRef instanceof PdfDictionary) {
134
+ acroFormDict = acroFormRef;
135
+ acroFormContainer = new PdfIndirectObject({ content: acroFormDict });
136
+ }
137
+ else {
138
+ return null;
139
+ }
140
+ const acroForm = new PdfAcroForm({ other: acroFormContainer, document });
141
+ const fields = new Map();
142
+ const getFields = async (fieldRefs, parent) => {
143
+ for (const fieldRef of fieldRefs) {
144
+ const refKey = fieldRef.toString().trim();
145
+ if (fields.has(refKey)) {
146
+ fields.get(refKey).parent = parent;
147
+ continue;
148
+ }
149
+ const fieldObject = await document.readObject(fieldRef);
150
+ if (!fieldObject)
151
+ continue;
152
+ if (!(fieldObject.content instanceof PdfDictionary))
153
+ continue;
154
+ const field = PdfFormField.create({
155
+ other: fieldObject,
156
+ form: acroForm,
157
+ parent,
158
+ });
159
+ const kids = field.kids;
160
+ if (kids.length > 0) {
161
+ await getFields(kids, field);
162
+ }
163
+ acroForm.fields.push(field);
164
+ fields.set(refKey, field);
165
+ }
166
+ };
167
+ const fieldsArray = new PdfArray();
168
+ if (acroForm.content.get('Fields') instanceof PdfArray) {
169
+ fieldsArray.items.push(...acroForm.content
170
+ .get('Fields')
171
+ .as((PdfArray)).items);
172
+ }
173
+ else if (acroForm.content.get('Fields') instanceof PdfObjectReference) {
174
+ const fieldsObj = await document.readObject(acroForm.content.get('Fields').as(PdfObjectReference));
175
+ if (fieldsObj && fieldsObj.content instanceof PdfArray) {
176
+ fieldsArray.items.push(...fieldsObj.content.as((PdfArray)).items);
177
+ }
178
+ }
179
+ await getFields(fieldsArray.items);
180
+ // Pre-cache font encoding maps now that this.fields is populated
181
+ await acroForm.cacheAllFontEncodings();
182
+ // Reset field-level modified flag so only explicitly changed fields are detected
183
+ for (const field of acroForm.fields) {
184
+ field.setModified(false);
185
+ }
186
+ return acroForm;
187
+ }
188
+ async cacheAllFontEncodings() {
189
+ await this.fontEncodingCache.cacheAllFontEncodings(this.fields);
190
+ }
191
+ async updatePageAnnotations(document, fieldsByPage) {
192
+ await PdfAnnotationWriter.updatePageAnnotations(document, fieldsByPage);
193
+ }
194
+ async getXfa(document) {
195
+ if (this._xfa) {
196
+ return this._xfa;
197
+ }
198
+ const doc = document || this.document;
199
+ if (!doc) {
200
+ throw new Error('No document available to load XFA form. Provide a document or ensure this AcroForm is associated with a document.');
201
+ }
202
+ this._xfa = await PdfXfaForm.fromDocument(doc);
203
+ return this._xfa;
204
+ }
205
+ async write(document) {
206
+ document ||= this.document;
207
+ if (!document) {
208
+ throw new Error('No document associated with this AcroForm');
209
+ }
210
+ const catalog = document.root;
211
+ const isIncremental = document.isIncremental();
212
+ document.setIncremental(true);
213
+ const xfaForm = this._xfa !== undefined
214
+ ? this._xfa
215
+ : await PdfXfaForm.fromDocument(document);
216
+ if (xfaForm) {
217
+ // Only send fields whose value was explicitly changed (field.isModified())
218
+ const xfaFields = this.fields
219
+ .filter((f) => f.isModified() && f.fieldType !== 'Signature' && f.name)
220
+ .map((f) => ({ name: f.name, value: f.value }));
221
+ if (xfaFields.length > 0) {
222
+ xfaForm.datasets?.updateFields(xfaFields);
223
+ }
224
+ }
225
+ const fieldsArray = new PdfArray();
226
+ this.content.set('Fields', fieldsArray);
227
+ const fieldsByPage = new Map();
228
+ // Phase 1: add appearance streams standalone (PdfStream — cannot go in ObjStm)
229
+ // and collect field dicts for ObjStm batching.
230
+ const compressibleFields = [];
231
+ for (const field of this.fields) {
232
+ if (field.content.isModified()) {
233
+ const appearances = field.getAppearanceStreamsForWriting();
234
+ if (appearances) {
235
+ document.add(appearances.primary);
236
+ if (appearances.secondary) {
237
+ document.add(appearances.secondary);
238
+ }
239
+ field.setAppearanceReference(appearances.primary.reference, appearances.secondary?.reference);
240
+ if (!field.print) {
241
+ field.print = true;
242
+ }
243
+ }
244
+ compressibleFields.push(field);
245
+ }
246
+ }
247
+ // Phase 2: pack field dicts into ObjStm — pre-assigns their object numbers.
248
+ document.addObjectsAsStream(compressibleFields);
249
+ // Phase 3: build fieldsArray now that field object numbers are stable.
250
+ for (const field of this.fields) {
251
+ let fieldReference;
252
+ if (field.content.isModified()) {
253
+ fieldReference = new PdfObjectReference(field.objectNumber, field.generationNumber);
254
+ const parentRef = field.parentRef;
255
+ const isWidget = field.isWidget;
256
+ if (parentRef && isWidget) {
257
+ const pageKey = `${parentRef.objectNumber}_${parentRef.generationNumber}`;
258
+ if (!fieldsByPage.has(pageKey)) {
259
+ fieldsByPage.set(pageKey, {
260
+ pageRef: parentRef,
261
+ fieldRefs: [],
262
+ });
263
+ }
264
+ fieldsByPage.get(pageKey).fieldRefs.push(fieldReference);
265
+ }
266
+ }
267
+ else {
268
+ fieldReference = field.reference;
269
+ }
270
+ fieldsArray.push(fieldReference);
271
+ }
272
+ await this.updatePageAnnotations(document, fieldsByPage);
273
+ // Phase 4: pack AcroForm dict into ObjStm AFTER fieldsArray is populated,
274
+ // so the dict is serialized with the correct Fields array.
275
+ if (this.isModified()) {
276
+ document.addObjectsAsStream([this]);
277
+ if (!catalog.content.has('AcroForm')) {
278
+ let updatableCatalog = catalog;
279
+ if (catalog.isImmutable()) {
280
+ updatableCatalog = catalog.clone();
281
+ document.add(updatableCatalog);
282
+ }
283
+ updatableCatalog.content.set('AcroForm', this.reference);
284
+ }
285
+ }
286
+ // XFA datasets stream — PdfXfaData extends PdfIndirectObject<PdfStream>, must stay standalone
287
+ if (xfaForm?.datasets?.isModified()) {
288
+ document.add(xfaForm.datasets);
289
+ }
290
+ await document.commit();
291
+ document.setIncremental(isIncremental);
292
+ }
293
+ }
@@ -0,0 +1,27 @@
1
+ import { PdfDocument } from '../pdf/pdf-document.js';
2
+ import { PdfDictionary } from '../core/objects/pdf-dictionary.js';
3
+ import { PdfObjectReference } from '../core/objects/pdf-object-reference.js';
4
+ import type { PdfDefaultResourcesDictionary } from './acroform.js';
5
+ /**
6
+ * Resolves and caches font encoding maps from the form's default resources.
7
+ */
8
+ export declare class PdfFontEncodingCache {
9
+ readonly fontEncodingMaps: Map<string, Map<number, string>>;
10
+ readonly fontTypes: Map<string, string>;
11
+ /** Object references for all resolved fonts, keyed by resource name. */
12
+ readonly fontRefs: Map<string, PdfObjectReference>;
13
+ private document?;
14
+ private defaultResources;
15
+ constructor(document: PdfDocument | undefined, defaultResources: PdfDefaultResourcesDictionary | null);
16
+ getFontEncodingMap(fontName: string): Promise<Map<number, string> | null>;
17
+ cacheAllFontEncodings(fields: Array<{
18
+ content: PdfDictionary;
19
+ }>): Promise<void>;
20
+ /**
21
+ * Walks the page-tree (page → parent → … → Pages root), looking for the
22
+ * font in each node's /Resources/Font dict. Stops as soon as it finds the
23
+ * font. PDF resource inheritance means any ancestor can supply the font.
24
+ */
25
+ private resolveFontFromPage;
26
+ isFontUnicode(fontName: string): boolean;
27
+ }
@@ -0,0 +1,188 @@
1
+ import { PdfDictionary } from '../core/objects/pdf-dictionary.js';
2
+ import { PdfArray } from '../core/objects/pdf-array.js';
3
+ import { PdfName } from '../core/objects/pdf-name.js';
4
+ import { PdfString } from '../core/objects/pdf-string.js';
5
+ import { PdfObjectReference } from '../core/objects/pdf-object-reference.js';
6
+ import { buildEncodingMap } from '../utils/decodeWithFontEncoding.js';
7
+ /**
8
+ * Resolves and caches font encoding maps from the form's default resources.
9
+ */
10
+ export class PdfFontEncodingCache {
11
+ fontEncodingMaps = new Map();
12
+ fontTypes = new Map();
13
+ /** Object references for all resolved fonts, keyed by resource name. */
14
+ fontRefs = new Map();
15
+ document;
16
+ defaultResources;
17
+ constructor(document, defaultResources) {
18
+ this.document = document;
19
+ this.defaultResources = defaultResources;
20
+ }
21
+ async getFontEncodingMap(fontName) {
22
+ if (this.fontEncodingMaps.has(fontName)) {
23
+ return this.fontEncodingMaps.get(fontName);
24
+ }
25
+ const dr = this.defaultResources;
26
+ if (!dr)
27
+ return null;
28
+ const fontDictEntry = dr.get('Font');
29
+ const fonts = fontDictEntry instanceof PdfDictionary ? fontDictEntry : null;
30
+ if (!fonts)
31
+ return null;
32
+ const fontEntry = fonts.get(fontName);
33
+ const fontRef = fontEntry instanceof PdfObjectReference ? fontEntry : null;
34
+ if (!fontRef)
35
+ return null;
36
+ this.fontRefs.set(fontName, fontRef);
37
+ const fontObj = await this.document?.readObject(fontRef);
38
+ if (!fontObj)
39
+ return null;
40
+ const fontDict = fontObj.content instanceof PdfDictionary ? fontObj.content : null;
41
+ if (!fontDict)
42
+ return null;
43
+ // Cache font subtype (Type0, TrueType, Type1, etc.)
44
+ const subtypeEntry = fontDict.get('Subtype');
45
+ const subtype = subtypeEntry instanceof PdfName ? subtypeEntry.value : undefined;
46
+ if (subtype) {
47
+ this.fontTypes.set(fontName, subtype);
48
+ }
49
+ const encoding = fontDict.get('Encoding');
50
+ let encodingDict = null;
51
+ if (encoding instanceof PdfObjectReference) {
52
+ const encodingObj = await this.document?.readObject(encoding);
53
+ encodingDict =
54
+ encodingObj?.content instanceof PdfDictionary
55
+ ? encodingObj.content
56
+ : null;
57
+ }
58
+ else if (encoding instanceof PdfDictionary) {
59
+ encodingDict = encoding;
60
+ }
61
+ if (!encodingDict)
62
+ return null;
63
+ const differences = encodingDict.get('Differences')?.as(PdfArray);
64
+ if (!differences)
65
+ return null;
66
+ const encodingMap = buildEncodingMap(differences);
67
+ if (!encodingMap)
68
+ return null;
69
+ this.fontEncodingMaps.set(fontName, encodingMap);
70
+ return encodingMap;
71
+ }
72
+ async cacheAllFontEncodings(fields) {
73
+ // Collect font names with an associated page ref for the first field
74
+ // that uses them (needed to fall back to page resources).
75
+ const fontPageRefs = new Map();
76
+ for (const field of fields) {
77
+ const daEntry = field.content.get('DA');
78
+ const da = daEntry instanceof PdfString ? daEntry.value : undefined;
79
+ if (da) {
80
+ const fontMatch = da.match(/\/(\w+)\s+[\d.]+\s+Tf/);
81
+ if (fontMatch) {
82
+ const fontName = fontMatch[1];
83
+ if (!fontPageRefs.has(fontName)) {
84
+ const pageEntry = field.content.get('P');
85
+ const pageRef = pageEntry instanceof PdfObjectReference
86
+ ? pageEntry
87
+ : null;
88
+ fontPageRefs.set(fontName, pageRef);
89
+ }
90
+ }
91
+ }
92
+ }
93
+ for (const [fontName, pageRef] of fontPageRefs) {
94
+ await this.getFontEncodingMap(fontName);
95
+ // If the font was not in the AcroForm DR, try the field's page resources
96
+ if (!this.fontRefs.has(fontName) && pageRef) {
97
+ await this.resolveFontFromPage(fontName, pageRef);
98
+ }
99
+ }
100
+ }
101
+ /**
102
+ * Walks the page-tree (page → parent → … → Pages root), looking for the
103
+ * font in each node's /Resources/Font dict. Stops as soon as it finds the
104
+ * font. PDF resource inheritance means any ancestor can supply the font.
105
+ */
106
+ async resolveFontFromPage(fontName, pageRef) {
107
+ if (!this.document)
108
+ return;
109
+ // Walk up the parent chain (max 16 levels to avoid infinite loops)
110
+ let currentRef = pageRef;
111
+ for (let depth = 0; depth < 16 && currentRef; depth++) {
112
+ const nodeObj = await this.document.readObject({
113
+ objectNumber: currentRef.objectNumber,
114
+ generationNumber: currentRef.generationNumber,
115
+ });
116
+ const nodeDict = nodeObj?.content instanceof PdfDictionary
117
+ ? nodeObj.content
118
+ : null;
119
+ if (!nodeDict)
120
+ break;
121
+ // Try Resources.Font in this node
122
+ const resourcesEntry = nodeDict.get('Resources');
123
+ const resources = resourcesEntry instanceof PdfDictionary ? resourcesEntry : null;
124
+ if (resources) {
125
+ const fontsEntry = resources.get('Font');
126
+ const fonts = fontsEntry instanceof PdfDictionary ? fontsEntry : null;
127
+ if (fonts) {
128
+ const fontEntry = fonts.get(fontName);
129
+ const fontRef = fontEntry instanceof PdfObjectReference
130
+ ? fontEntry
131
+ : null;
132
+ if (fontRef) {
133
+ // Found — cache and finish
134
+ currentRef = fontRef;
135
+ break;
136
+ }
137
+ }
138
+ }
139
+ // Walk to parent
140
+ const parentEntry = nodeDict.get('Parent');
141
+ currentRef =
142
+ parentEntry instanceof PdfObjectReference ? parentEntry : null;
143
+ }
144
+ const fontRef = currentRef;
145
+ if (!fontRef)
146
+ return;
147
+ this.fontRefs.set(fontName, fontRef);
148
+ const fontObj = await this.document.readObject({
149
+ objectNumber: fontRef.objectNumber,
150
+ generationNumber: fontRef.generationNumber,
151
+ });
152
+ const fontDict = fontObj?.content instanceof PdfDictionary ? fontObj.content : null;
153
+ if (!fontDict)
154
+ return;
155
+ const subtypeEntry = fontDict.get('Subtype');
156
+ const subtype = subtypeEntry instanceof PdfName ? subtypeEntry.value : undefined;
157
+ if (subtype)
158
+ this.fontTypes.set(fontName, subtype);
159
+ // Also resolve encoding map if present
160
+ const encoding = fontDict.get('Encoding');
161
+ let encodingDict = null;
162
+ if (encoding instanceof PdfObjectReference) {
163
+ const encodingObj = await this.document.readObject({
164
+ objectNumber: encoding.objectNumber,
165
+ generationNumber: encoding.generationNumber,
166
+ });
167
+ encodingDict =
168
+ encodingObj?.content instanceof PdfDictionary
169
+ ? encodingObj.content
170
+ : null;
171
+ }
172
+ else if (encoding instanceof PdfDictionary) {
173
+ encodingDict = encoding;
174
+ }
175
+ if (!encodingDict)
176
+ return;
177
+ const differencesEntry = encodingDict.get('Differences');
178
+ if (!(differencesEntry instanceof PdfArray))
179
+ return;
180
+ const encodingMap = buildEncodingMap(differencesEntry);
181
+ if (encodingMap) {
182
+ this.fontEncodingMaps.set(fontName, encodingMap);
183
+ }
184
+ }
185
+ isFontUnicode(fontName) {
186
+ return this.fontTypes.get(fontName) === 'Type0';
187
+ }
188
+ }
@@ -0,0 +1,3 @@
1
+ export { PdfXfaForm } from './pdf-xfa-form.js';
2
+ export { PdfXfaData } from './pdf-xfa-data.js';
3
+ export type { XfaFieldData } from './pdf-xfa-data.js';
@@ -0,0 +1,2 @@
1
+ export { PdfXfaForm } from './pdf-xfa-form.js';
2
+ export { PdfXfaData } from './pdf-xfa-data.js';
@@ -0,0 +1,20 @@
1
+ import { PdfIndirectObject } from '../../core/objects/pdf-indirect-object.js';
2
+ import { PdfStream } from '../../core/objects/pdf-stream.js';
3
+ export interface XfaFieldData {
4
+ name: string;
5
+ value: string;
6
+ }
7
+ /**
8
+ * Wraps an XFA datasets stream as a typed PDF indirect object.
9
+ * Provides methods to read/write XML and update individual field values.
10
+ */
11
+ export declare class PdfXfaData extends PdfIndirectObject<PdfStream> {
12
+ constructor(stream: PdfIndirectObject<PdfStream>);
13
+ readXml(): string;
14
+ writeXml(xml: string): void;
15
+ updateField(name: string, value: string): void;
16
+ updateFields(fields: XfaFieldData[]): void;
17
+ getFieldValue(name: string): string | null;
18
+ private static updateFieldValue;
19
+ private static escapeRegex;
20
+ }
@@ -0,0 +1,68 @@
1
+ import { PdfIndirectObject } from '../../core/objects/pdf-indirect-object.js';
2
+ import { PdfStream } from '../../core/objects/pdf-stream.js';
3
+ /**
4
+ * Wraps an XFA datasets stream as a typed PDF indirect object.
5
+ * Provides methods to read/write XML and update individual field values.
6
+ */
7
+ export class PdfXfaData extends PdfIndirectObject {
8
+ constructor(stream) {
9
+ super(stream);
10
+ }
11
+ readXml() {
12
+ const decompressed = this.content.decode();
13
+ return new TextDecoder().decode(decompressed);
14
+ }
15
+ writeXml(xml) {
16
+ this.content = PdfStream.fromString(xml);
17
+ }
18
+ updateField(name, value) {
19
+ let xml = this.readXml();
20
+ xml = PdfXfaData.updateFieldValue(xml, name, value);
21
+ this.writeXml(xml);
22
+ }
23
+ updateFields(fields) {
24
+ if (fields.length === 0)
25
+ return;
26
+ let xml = this.readXml();
27
+ for (const { name, value } of fields) {
28
+ xml = PdfXfaData.updateFieldValue(xml, name, value);
29
+ }
30
+ this.writeXml(xml);
31
+ }
32
+ getFieldValue(name) {
33
+ const xml = this.readXml();
34
+ const segments = name.split('.');
35
+ const leafSegment = segments[segments.length - 1];
36
+ const leafName = leafSegment.replace(/\[\d+\]$/, '');
37
+ if (leafName.startsWith('#'))
38
+ return null;
39
+ const contentRegex = new RegExp(`<${PdfXfaData.escapeRegex(leafName)}(?:\\s[^>]*)?>([\\s\\S]*?)</${PdfXfaData.escapeRegex(leafName)}\\s*>`);
40
+ const match = xml.match(contentRegex);
41
+ return match ? match[1] : null;
42
+ }
43
+ static updateFieldValue(xml, fieldName, value) {
44
+ const segments = fieldName.split('.');
45
+ const leafSegment = segments[segments.length - 1];
46
+ const leafName = leafSegment.replace(/\[\d+\]$/, '');
47
+ if (leafName.startsWith('#'))
48
+ return xml;
49
+ const escapedValue = value
50
+ .replace(/&/g, '&amp;')
51
+ .replace(/</g, '&lt;')
52
+ .replace(/>/g, '&gt;')
53
+ .replace(/"/g, '&quot;')
54
+ .replace(/'/g, '&apos;');
55
+ const selfClosingRegex = new RegExp(`<${PdfXfaData.escapeRegex(leafName)}\\s*/>`);
56
+ if (selfClosingRegex.test(xml)) {
57
+ return xml.replace(selfClosingRegex, `<${leafName}>${escapedValue}</${leafName}>`);
58
+ }
59
+ const contentRegex = new RegExp(`(<${PdfXfaData.escapeRegex(leafName)}(?:\\s[^>]*)?>)[\\s\\S]*?(</${PdfXfaData.escapeRegex(leafName)}\\s*>)`);
60
+ if (contentRegex.test(xml)) {
61
+ return xml.replace(contentRegex, `$1${escapedValue}$2`);
62
+ }
63
+ return xml;
64
+ }
65
+ static escapeRegex(str) {
66
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
67
+ }
68
+ }