pdf-lite 1.3.1 → 1.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/acroform/acroform.d.ts +6 -1
- package/dist/acroform/acroform.js +125 -23
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,10 +13,10 @@ PRs and issues are welcome!
|
|
|
13
13
|
|
|
14
14
|
## Features
|
|
15
15
|
|
|
16
|
-
- **Zero dependencies**: No external libraries are required, making it lightweight and easy to integrate.
|
|
17
16
|
- **Type-safe**: Built with TypeScript, ensuring type safety and reducing runtime errors.
|
|
18
17
|
- **Browser and Node.js support**: Works seamlessly in both environments, allowing for versatile usage.
|
|
19
18
|
- **Low-level API**: Provides a low-level API for advanced users who want to manipulate PDF files directly, as well as a higher-level API for easier usage.
|
|
19
|
+
- **Minimal dependencies**: A small number external libraries are required, making it lightweight and easy to integrate.
|
|
20
20
|
|
|
21
21
|
## Installation
|
|
22
22
|
|
|
@@ -62,7 +62,7 @@ export declare class PdfAcroFormField extends PdfIndirectObject<PdfDictionary<{
|
|
|
62
62
|
MaxLen?: PdfNumber;
|
|
63
63
|
Opt?: PdfArray<PdfString>;
|
|
64
64
|
}>> {
|
|
65
|
-
|
|
65
|
+
private _parent?;
|
|
66
66
|
defaultGenerateAppearance: boolean;
|
|
67
67
|
private _appearanceStream?;
|
|
68
68
|
private _appearanceStreamYes?;
|
|
@@ -71,6 +71,11 @@ export declare class PdfAcroFormField extends PdfIndirectObject<PdfDictionary<{
|
|
|
71
71
|
other?: PdfIndirectObject;
|
|
72
72
|
form?: PdfAcroForm;
|
|
73
73
|
});
|
|
74
|
+
get parent(): PdfAcroFormField | undefined;
|
|
75
|
+
set parent(field: PdfAcroFormField | undefined);
|
|
76
|
+
get children(): PdfAcroFormField[];
|
|
77
|
+
set children(fields: PdfAcroFormField[]);
|
|
78
|
+
get siblings(): PdfAcroFormField[];
|
|
74
79
|
get encodingMap(): Map<number, string> | undefined;
|
|
75
80
|
/**
|
|
76
81
|
* Convenience method to check if field dictionary is modified
|
|
@@ -18,7 +18,7 @@ export const PdfFieldType = {
|
|
|
18
18
|
Signature: 'Sig',
|
|
19
19
|
};
|
|
20
20
|
export class PdfAcroFormField extends PdfIndirectObject {
|
|
21
|
-
|
|
21
|
+
_parent;
|
|
22
22
|
defaultGenerateAppearance = true;
|
|
23
23
|
_appearanceStream;
|
|
24
24
|
_appearanceStreamYes; // For button fields: checked state
|
|
@@ -28,6 +28,50 @@ export class PdfAcroFormField extends PdfIndirectObject {
|
|
|
28
28
|
new PdfIndirectObject({ content: new PdfDictionary() }));
|
|
29
29
|
this.form = options?.form;
|
|
30
30
|
}
|
|
31
|
+
get parent() {
|
|
32
|
+
if (this._parent)
|
|
33
|
+
return this._parent;
|
|
34
|
+
if (!this.form)
|
|
35
|
+
return undefined;
|
|
36
|
+
return this.form.fields.find((f) => f !== this &&
|
|
37
|
+
f.kids.some((k) => k.objectNumber === this.objectNumber &&
|
|
38
|
+
k.generationNumber === this.generationNumber));
|
|
39
|
+
}
|
|
40
|
+
set parent(field) {
|
|
41
|
+
// Remove from old parent's Kids
|
|
42
|
+
if (this._parent) {
|
|
43
|
+
this._parent.kids = this._parent.kids.filter((k) => k.objectNumber !== this.objectNumber ||
|
|
44
|
+
k.generationNumber !== this.generationNumber);
|
|
45
|
+
}
|
|
46
|
+
this._parent = field;
|
|
47
|
+
// Add to new parent's Kids
|
|
48
|
+
if (field) {
|
|
49
|
+
const alreadyInKids = field.kids.some((k) => k.objectNumber === this.objectNumber &&
|
|
50
|
+
k.generationNumber === this.generationNumber);
|
|
51
|
+
if (!alreadyInKids) {
|
|
52
|
+
field.kids = [...field.kids, this.reference];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
get children() {
|
|
57
|
+
if (!this.form)
|
|
58
|
+
return [];
|
|
59
|
+
return this.form.fields.filter((f) => f.parent === this);
|
|
60
|
+
}
|
|
61
|
+
set children(fields) {
|
|
62
|
+
// Clear existing children's parent
|
|
63
|
+
for (const child of this.children) {
|
|
64
|
+
child._parent = undefined;
|
|
65
|
+
}
|
|
66
|
+
// Set new children and update Kids array
|
|
67
|
+
this.kids = fields.map((f) => f.reference);
|
|
68
|
+
for (const child of fields) {
|
|
69
|
+
child._parent = this;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
get siblings() {
|
|
73
|
+
return this.parent?.children ?? [];
|
|
74
|
+
}
|
|
31
75
|
get encodingMap() {
|
|
32
76
|
const fontName = this.fontName;
|
|
33
77
|
if (!fontName)
|
|
@@ -44,7 +88,8 @@ export class PdfAcroFormField extends PdfIndirectObject {
|
|
|
44
88
|
* Gets the field type
|
|
45
89
|
*/
|
|
46
90
|
get fieldType() {
|
|
47
|
-
const ft = this.content.get('FT')?.value
|
|
91
|
+
const ft = this.content.get('FT')?.value ??
|
|
92
|
+
this.parent?.content.get('FT')?.value;
|
|
48
93
|
switch (ft) {
|
|
49
94
|
case 'Tx':
|
|
50
95
|
return 'Text';
|
|
@@ -128,7 +173,7 @@ export class PdfAcroFormField extends PdfIndirectObject {
|
|
|
128
173
|
* Gets the default value
|
|
129
174
|
*/
|
|
130
175
|
get defaultValue() {
|
|
131
|
-
const dv = this.content.get('DV');
|
|
176
|
+
const dv = this.content.get('DV') ?? this.parent?.content.get('DV');
|
|
132
177
|
if (dv instanceof PdfString) {
|
|
133
178
|
return dv.value;
|
|
134
179
|
}
|
|
@@ -150,7 +195,8 @@ export class PdfAcroFormField extends PdfIndirectObject {
|
|
|
150
195
|
}
|
|
151
196
|
}
|
|
152
197
|
get value() {
|
|
153
|
-
|
|
198
|
+
// V may be on this field or inherited from parent (parent/kids split)
|
|
199
|
+
const v = this.content.get('V') ?? this.parent?.content.get('V');
|
|
154
200
|
if (v instanceof PdfString) {
|
|
155
201
|
// UTF-16BE strings should always use UTF-16BE decoding regardless of font encoding
|
|
156
202
|
if (v.isUTF16BE) {
|
|
@@ -170,45 +216,56 @@ export class PdfAcroFormField extends PdfIndirectObject {
|
|
|
170
216
|
if (this.value === val) {
|
|
171
217
|
return;
|
|
172
218
|
}
|
|
219
|
+
// In a parent/kids split, V should be set on the parent field
|
|
220
|
+
const target = this.parent ?? this;
|
|
173
221
|
const fieldType = this.fieldType;
|
|
174
222
|
if (fieldType === 'Button') {
|
|
175
223
|
val = val instanceof PdfString ? val.value : val;
|
|
176
224
|
if (val.trim() === '') {
|
|
177
|
-
|
|
225
|
+
target.content.delete('V');
|
|
178
226
|
this.content.delete('AS');
|
|
179
227
|
return;
|
|
180
228
|
}
|
|
181
|
-
|
|
229
|
+
target.content.set('V', new PdfName(val));
|
|
182
230
|
this.content.set('AS', new PdfName(val));
|
|
183
231
|
}
|
|
184
232
|
else {
|
|
185
|
-
|
|
233
|
+
target.content.set('V', val instanceof PdfString ? val : new PdfString(val));
|
|
186
234
|
}
|
|
187
235
|
if (this.defaultGenerateAppearance) {
|
|
188
236
|
this.generateAppearance();
|
|
237
|
+
// If this is a child widget with siblings, regenerate their appearances too
|
|
238
|
+
for (const sibling of this.siblings) {
|
|
239
|
+
if (sibling !== this &&
|
|
240
|
+
sibling.rect &&
|
|
241
|
+
sibling.defaultGenerateAppearance) {
|
|
242
|
+
sibling.generateAppearance();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
189
245
|
}
|
|
190
246
|
}
|
|
191
247
|
get checked() {
|
|
192
248
|
if (this.fieldType === 'Button') {
|
|
193
|
-
const v = this.content.get('V');
|
|
249
|
+
const v = this.content.get('V') ?? this.parent?.content.get('V');
|
|
194
250
|
return v instanceof PdfName && v.value === 'Yes';
|
|
195
251
|
}
|
|
196
252
|
return false;
|
|
197
253
|
}
|
|
198
254
|
set checked(isChecked) {
|
|
199
255
|
if (this.fieldType === 'Button') {
|
|
256
|
+
const target = this.parent ?? this;
|
|
200
257
|
if (isChecked) {
|
|
201
|
-
|
|
258
|
+
target.content.set('V', new PdfName('Yes'));
|
|
202
259
|
this.content.set('AS', new PdfName('Yes'));
|
|
203
260
|
}
|
|
204
261
|
else {
|
|
205
|
-
|
|
262
|
+
target.content.set('V', new PdfName('Off'));
|
|
206
263
|
this.content.set('AS', new PdfName('Off'));
|
|
207
264
|
}
|
|
208
265
|
}
|
|
209
266
|
}
|
|
210
267
|
get fontSize() {
|
|
211
|
-
const da = this.
|
|
268
|
+
const da = this.defaultAppearance || '';
|
|
212
269
|
const match = da.match(/\/[A-Za-z0-9_-]+\s+([\d.]+)\s+Tf/);
|
|
213
270
|
if (match) {
|
|
214
271
|
return parseFloat(match[1]);
|
|
@@ -216,7 +273,7 @@ export class PdfAcroFormField extends PdfIndirectObject {
|
|
|
216
273
|
return null;
|
|
217
274
|
}
|
|
218
275
|
set fontSize(size) {
|
|
219
|
-
const da = this.
|
|
276
|
+
const da = this.defaultAppearance || '';
|
|
220
277
|
if (!da) {
|
|
221
278
|
this.content.set('DA', new PdfString(`/F1 ${size} Tf 0 g`));
|
|
222
279
|
return;
|
|
@@ -225,7 +282,7 @@ export class PdfAcroFormField extends PdfIndirectObject {
|
|
|
225
282
|
this.content.set('DA', new PdfString(updatedDa));
|
|
226
283
|
}
|
|
227
284
|
get fontName() {
|
|
228
|
-
const da = this.
|
|
285
|
+
const da = this.defaultAppearance || '';
|
|
229
286
|
const match = da.match(/\/([A-Za-z0-9_-]+)\s+[\d.]+\s+Tf/);
|
|
230
287
|
if (match) {
|
|
231
288
|
return match[1];
|
|
@@ -233,7 +290,7 @@ export class PdfAcroFormField extends PdfIndirectObject {
|
|
|
233
290
|
return null;
|
|
234
291
|
}
|
|
235
292
|
set fontName(fontName) {
|
|
236
|
-
const da = this.
|
|
293
|
+
const da = this.defaultAppearance || '';
|
|
237
294
|
if (!da) {
|
|
238
295
|
this.content.set('DA', new PdfString(`/${fontName} 12 Tf 0 g`));
|
|
239
296
|
return;
|
|
@@ -253,7 +310,7 @@ export class PdfAcroFormField extends PdfIndirectObject {
|
|
|
253
310
|
}
|
|
254
311
|
const resourceName = font.resourceName;
|
|
255
312
|
const currentSize = this.fontSize ?? 12;
|
|
256
|
-
const da = this.
|
|
313
|
+
const da = this.defaultAppearance || '';
|
|
257
314
|
if (!da) {
|
|
258
315
|
this.content.set('DA', new PdfString(`/${resourceName} ${currentSize} Tf 0 g`));
|
|
259
316
|
return;
|
|
@@ -265,7 +322,9 @@ export class PdfAcroFormField extends PdfIndirectObject {
|
|
|
265
322
|
* Gets field flags (bitwise combination of field attributes)
|
|
266
323
|
*/
|
|
267
324
|
get flags() {
|
|
268
|
-
return this.content.get('Ff')?.as(PdfNumber)?.value ??
|
|
325
|
+
return (this.content.get('Ff')?.as(PdfNumber)?.value ??
|
|
326
|
+
this.parent?.content.get('Ff')?.as(PdfNumber)?.value ??
|
|
327
|
+
0);
|
|
269
328
|
}
|
|
270
329
|
/**
|
|
271
330
|
* Sets field flags
|
|
@@ -364,7 +423,9 @@ export class PdfAcroFormField extends PdfIndirectObject {
|
|
|
364
423
|
* 0 = left-justified, 1 = centered, 2 = right-justified
|
|
365
424
|
*/
|
|
366
425
|
get quadding() {
|
|
367
|
-
return this.content.get('Q')?.as(PdfNumber)?.value ??
|
|
426
|
+
return (this.content.get('Q')?.as(PdfNumber)?.value ??
|
|
427
|
+
this.parent?.content.get('Q')?.as(PdfNumber)?.value ??
|
|
428
|
+
0);
|
|
368
429
|
}
|
|
369
430
|
/**
|
|
370
431
|
* Sets the quadding (text alignment) for this field.
|
|
@@ -378,7 +439,8 @@ export class PdfAcroFormField extends PdfIndirectObject {
|
|
|
378
439
|
* Returns an array of option strings.
|
|
379
440
|
*/
|
|
380
441
|
get options() {
|
|
381
|
-
const opt = this.content.get('Opt')?.as((PdfArray))
|
|
442
|
+
const opt = this.content.get('Opt')?.as((PdfArray)) ??
|
|
443
|
+
this.parent?.content.get('Opt')?.as((PdfArray));
|
|
382
444
|
if (!opt)
|
|
383
445
|
return [];
|
|
384
446
|
return opt.items.map((item) => item.value);
|
|
@@ -396,7 +458,9 @@ export class PdfAcroFormField extends PdfIndirectObject {
|
|
|
396
458
|
this.content.set('Opt', optArray);
|
|
397
459
|
}
|
|
398
460
|
get defaultAppearance() {
|
|
399
|
-
return this.content.get('DA')?.as(PdfString)?.value ??
|
|
461
|
+
return (this.content.get('DA')?.as(PdfString)?.value ??
|
|
462
|
+
this.parent?.content.get('DA')?.as(PdfString)?.value ??
|
|
463
|
+
null);
|
|
400
464
|
}
|
|
401
465
|
set defaultAppearance(da) {
|
|
402
466
|
this.content.set('DA', new PdfString(da));
|
|
@@ -747,8 +811,9 @@ export class PdfAcroFormField extends PdfIndirectObject {
|
|
|
747
811
|
const [x1, y1, x2, y2] = rect;
|
|
748
812
|
const width = x2 - x1;
|
|
749
813
|
const height = y2 - y1;
|
|
750
|
-
// Get the default appearance string
|
|
751
|
-
const da = this.content.get('DA')?.as(PdfString)?.value
|
|
814
|
+
// Get the default appearance string (may be inherited from parent)
|
|
815
|
+
const da = this.content.get('DA')?.as(PdfString)?.value ??
|
|
816
|
+
this.parent?.content.get('DA')?.as(PdfString)?.value;
|
|
752
817
|
if (!da)
|
|
753
818
|
return false;
|
|
754
819
|
// Get the field value
|
|
@@ -858,6 +923,25 @@ EMC
|
|
|
858
923
|
new PdfNumber(width),
|
|
859
924
|
new PdfNumber(height),
|
|
860
925
|
]));
|
|
926
|
+
// Add font resources so Acrobat can resolve the font name.
|
|
927
|
+
// Prefer the field's own DR (which has correctly resolved refs in incremental updates),
|
|
928
|
+
// then fall back to the AcroForm-level DR.
|
|
929
|
+
const fieldDR = this.content
|
|
930
|
+
.get('DR')
|
|
931
|
+
?.as(PdfDictionary);
|
|
932
|
+
const acroformDR = this.form?.defaultResources;
|
|
933
|
+
const fontSource = fieldDR?.get('Font')?.as(PdfDictionary) ??
|
|
934
|
+
acroformDR?.get('Font')?.as(PdfDictionary);
|
|
935
|
+
if (fontSource && fontName) {
|
|
936
|
+
const fontRef = fontSource.get(fontName);
|
|
937
|
+
if (fontRef) {
|
|
938
|
+
const resourceFontDict = new PdfDictionary();
|
|
939
|
+
resourceFontDict.set(fontName, fontRef);
|
|
940
|
+
const resourcesDict = new PdfDictionary();
|
|
941
|
+
resourcesDict.set('Font', resourceFontDict);
|
|
942
|
+
appearanceDict.set('Resources', resourcesDict);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
861
945
|
const stream = new PdfStream({
|
|
862
946
|
header: appearanceDict,
|
|
863
947
|
original: contentStream,
|
|
@@ -978,8 +1062,9 @@ Q
|
|
|
978
1062
|
const [x1, y1, x2, y2] = rect;
|
|
979
1063
|
const width = x2 - x1;
|
|
980
1064
|
const height = y2 - y1;
|
|
981
|
-
// Get the default appearance string
|
|
982
|
-
const da = this.content.get('DA')?.as(PdfString)?.value
|
|
1065
|
+
// Get the default appearance string (may be inherited from parent)
|
|
1066
|
+
const da = this.content.get('DA')?.as(PdfString)?.value ??
|
|
1067
|
+
this.parent?.content.get('DA')?.as(PdfString)?.value;
|
|
983
1068
|
if (!da)
|
|
984
1069
|
return false;
|
|
985
1070
|
const value = this.value;
|
|
@@ -1045,6 +1130,23 @@ EMC
|
|
|
1045
1130
|
new PdfNumber(width),
|
|
1046
1131
|
new PdfNumber(height),
|
|
1047
1132
|
]));
|
|
1133
|
+
// Add font resources so Acrobat can resolve the font name.
|
|
1134
|
+
const fieldDR = this.content
|
|
1135
|
+
.get('DR')
|
|
1136
|
+
?.as(PdfDictionary);
|
|
1137
|
+
const acroformDR = this.form?.defaultResources;
|
|
1138
|
+
const fontSource = fieldDR?.get('Font')?.as(PdfDictionary) ??
|
|
1139
|
+
acroformDR?.get('Font')?.as(PdfDictionary);
|
|
1140
|
+
if (fontSource && fontName) {
|
|
1141
|
+
const fontRef = fontSource.get(fontName);
|
|
1142
|
+
if (fontRef) {
|
|
1143
|
+
const resourceFontDict = new PdfDictionary();
|
|
1144
|
+
resourceFontDict.set(fontName, fontRef);
|
|
1145
|
+
const resourcesDict = new PdfDictionary();
|
|
1146
|
+
resourcesDict.set('Font', resourceFontDict);
|
|
1147
|
+
appearanceDict.set('Resources', resourcesDict);
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1048
1150
|
const stream = new PdfStream({
|
|
1049
1151
|
header: appearanceDict,
|
|
1050
1152
|
original: contentStream,
|