pdf-lite 1.6.3 → 1.7.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.
- package/dist/acroform/appearance/pdf-button-appearance-stream.js +1 -1
- package/dist/acroform/appearance/pdf-text-appearance-stream.js +42 -25
- package/dist/acroform/fields/pdf-button-form-field.d.ts +11 -2
- package/dist/acroform/fields/pdf-button-form-field.js +76 -37
- package/dist/acroform/fields/pdf-choice-form-field.js +2 -2
- package/dist/acroform/fields/pdf-form-field.d.ts +15 -9
- package/dist/acroform/fields/pdf-form-field.js +124 -47
- package/dist/acroform/fields/pdf-text-form-field.js +2 -2
- package/dist/acroform/pdf-acro-form.d.ts +1 -0
- package/dist/acroform/pdf-acro-form.js +15 -4
- package/dist/acroform/xfa/pdf-xfa-data.d.ts +2 -1
- package/dist/acroform/xfa/pdf-xfa-data.js +36 -28
- package/dist/annotations/pdf-annotation.d.ts +1 -1
- package/dist/annotations/pdf-annotation.js +16 -2
- package/dist/core/generators.js +12 -6
- package/dist/core/objects/pdf-array.d.ts +6 -1
- package/dist/core/objects/pdf-array.js +3 -0
- package/dist/core/objects/pdf-boolean.d.ts +6 -2
- package/dist/core/objects/pdf-boolean.js +3 -0
- package/dist/core/objects/pdf-comment.d.ts +6 -2
- package/dist/core/objects/pdf-comment.js +3 -0
- package/dist/core/objects/pdf-date.d.ts +4 -0
- package/dist/core/objects/pdf-date.js +3 -0
- package/dist/core/objects/pdf-dictionary.d.ts +6 -0
- package/dist/core/objects/pdf-dictionary.js +19 -0
- package/dist/core/objects/pdf-hexadecimal.d.ts +6 -2
- package/dist/core/objects/pdf-hexadecimal.js +3 -0
- package/dist/core/objects/pdf-indirect-object.d.ts +8 -1
- package/dist/core/objects/pdf-indirect-object.js +14 -0
- package/dist/core/objects/pdf-name.d.ts +6 -2
- package/dist/core/objects/pdf-name.js +3 -0
- package/dist/core/objects/pdf-null.d.ts +5 -2
- package/dist/core/objects/pdf-null.js +3 -0
- package/dist/core/objects/pdf-number.d.ts +6 -1
- package/dist/core/objects/pdf-number.js +3 -0
- package/dist/core/objects/pdf-object-reference.d.ts +5 -0
- package/dist/core/objects/pdf-object-reference.js +7 -0
- package/dist/core/objects/pdf-object.d.ts +1 -0
- package/dist/core/objects/pdf-start-xref.d.ts +6 -1
- package/dist/core/objects/pdf-start-xref.js +3 -0
- package/dist/core/objects/pdf-stream.d.ts +8 -0
- package/dist/core/objects/pdf-stream.js +7 -0
- package/dist/core/objects/pdf-string.d.ts +8 -2
- package/dist/core/objects/pdf-string.js +9 -0
- package/dist/core/objects/pdf-trailer.d.ts +7 -0
- package/dist/core/objects/pdf-trailer.js +3 -0
- package/dist/core/objects/pdf-xref-table.d.ts +31 -5
- package/dist/core/objects/pdf-xref-table.js +23 -0
- package/dist/core/parser/incremental-parser.d.ts +7 -2
- package/dist/core/parser/incremental-parser.js +9 -3
- package/dist/pdf/pdf-document.d.ts +7 -0
- package/dist/pdf/pdf-document.js +6 -0
- package/dist/pdf/pdf-revision.d.ts +4 -0
- package/dist/pdf/pdf-revision.js +6 -0
- package/dist/utils/iterable-readable-stream.d.ts +2 -0
- package/dist/utils/iterable-readable-stream.js +8 -1
- package/dist/utils/xml.d.ts +9 -0
- package/dist/utils/xml.js +59 -0
- package/package.json +2 -2
|
@@ -28,8 +28,8 @@ export class PdfTextAppearanceStream extends PdfAppearanceStream {
|
|
|
28
28
|
g.setDefaultAppearance(new PdfDefaultAppearance(ctx.da.fontName, DEFAULT_FONT_SIZE, ctx.da.colorOp));
|
|
29
29
|
// ── Determine font size ──────────────────────────────────────
|
|
30
30
|
let finalFontSize;
|
|
31
|
-
if (autoSize) {
|
|
32
|
-
//
|
|
31
|
+
if (autoSize && ctx.multiline) {
|
|
32
|
+
// Multiline auto-size: default to 12pt with wrapping, then
|
|
33
33
|
// shrink only if the wrapped text still doesn't fit.
|
|
34
34
|
finalFontSize = DEFAULT_FONT_SIZE;
|
|
35
35
|
const testLines = g.wrapTextToLines(value, availableWidth);
|
|
@@ -38,6 +38,17 @@ export class PdfTextAppearanceStream extends PdfAppearanceStream {
|
|
|
38
38
|
}
|
|
39
39
|
finalFontSize = Math.max(finalFontSize, 0.5);
|
|
40
40
|
}
|
|
41
|
+
else if (autoSize) {
|
|
42
|
+
// Single-line auto-size: fit to available height, then
|
|
43
|
+
// shrink further if text overflows the width.
|
|
44
|
+
finalFontSize = Math.min(DEFAULT_FONT_SIZE, availableHeight);
|
|
45
|
+
g.setDefaultAppearance(new PdfDefaultAppearance(ctx.da.fontName, finalFontSize, ctx.da.colorOp));
|
|
46
|
+
const textWidth = g.measureTextWidth(value);
|
|
47
|
+
if (textWidth > availableWidth) {
|
|
48
|
+
finalFontSize = g.calculateFittingFontSize(value, availableWidth);
|
|
49
|
+
}
|
|
50
|
+
finalFontSize = Math.max(finalFontSize, 0.5);
|
|
51
|
+
}
|
|
41
52
|
else {
|
|
42
53
|
finalFontSize = ctx.da.fontSize;
|
|
43
54
|
}
|
|
@@ -45,30 +56,14 @@ export class PdfTextAppearanceStream extends PdfAppearanceStream {
|
|
|
45
56
|
const finalDA = new PdfDefaultAppearance(ctx.da.fontName, finalFontSize, ctx.da.colorOp);
|
|
46
57
|
g.setDefaultAppearance(finalDA);
|
|
47
58
|
let lines = [];
|
|
48
|
-
if (ctx.
|
|
49
|
-
if (!autoSize) {
|
|
50
|
-
const testLines = g.wrapTextToLines(value, availableWidth);
|
|
51
|
-
const lineHeight = finalFontSize * 1.2;
|
|
52
|
-
if (testLines.length * lineHeight > availableHeight) {
|
|
53
|
-
finalFontSize = g.calculateFittingFontSize(value, availableWidth, availableHeight, 1.2);
|
|
54
|
-
g.setDefaultAppearance(new PdfDefaultAppearance(ctx.da.fontName, finalFontSize, ctx.da.colorOp));
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
lines = g.wrapTextToLines(value, availableWidth);
|
|
58
|
-
const renderLineHeight = finalFontSize * 1.2;
|
|
59
|
-
const startY = height - padding - finalFontSize;
|
|
60
|
-
g.beginText();
|
|
61
|
-
g.moveTo(padding, startY);
|
|
62
|
-
for (let i = 0; i < lines.length; i++) {
|
|
63
|
-
if (i > 0)
|
|
64
|
-
g.moveTo(0, -renderLineHeight);
|
|
65
|
-
g.showText(lines[i].replace(/\r/g, ''), isUnicode, reverseEncodingMap);
|
|
66
|
-
}
|
|
67
|
-
g.endText();
|
|
68
|
-
}
|
|
69
|
-
else if (ctx.comb && ctx.maxLen) {
|
|
59
|
+
if (ctx.comb && ctx.maxLen) {
|
|
70
60
|
const cellWidth = width / ctx.maxLen;
|
|
71
61
|
const chars = [...value];
|
|
62
|
+
// For auto-size comb, fit font to cell height
|
|
63
|
+
if (autoSize) {
|
|
64
|
+
finalFontSize = Math.min(height - 2 * padding, cellWidth);
|
|
65
|
+
g.setDefaultAppearance(new PdfDefaultAppearance(ctx.da.fontName, finalFontSize, ctx.da.colorOp));
|
|
66
|
+
}
|
|
72
67
|
let maxCharWidth = 0;
|
|
73
68
|
let widestChar = chars[0] ?? '';
|
|
74
69
|
for (const char of chars) {
|
|
@@ -85,13 +80,35 @@ export class PdfTextAppearanceStream extends PdfAppearanceStream {
|
|
|
85
80
|
const textY = (height - finalFontSize) / 2 + finalFontSize * 0.2;
|
|
86
81
|
g.beginText();
|
|
87
82
|
for (let i = 0; i < chars.length && i < ctx.maxLen; i++) {
|
|
88
|
-
const
|
|
83
|
+
const charWidth = g.measureTextWidth(chars[i]);
|
|
84
|
+
const cellX = cellWidth * i + (cellWidth - charWidth) / 2;
|
|
89
85
|
g.moveTo(cellX, textY);
|
|
90
86
|
g.showText(chars[i], isUnicode, reverseEncodingMap);
|
|
91
87
|
g.moveTo(-cellX, -textY);
|
|
92
88
|
}
|
|
93
89
|
g.endText();
|
|
94
90
|
}
|
|
91
|
+
else if (ctx.multiline) {
|
|
92
|
+
if (!autoSize) {
|
|
93
|
+
const testLines = g.wrapTextToLines(value, availableWidth);
|
|
94
|
+
const lineHeight = finalFontSize * 1.2;
|
|
95
|
+
if (testLines.length * lineHeight > availableHeight) {
|
|
96
|
+
finalFontSize = g.calculateFittingFontSize(value, availableWidth, availableHeight, 1.2);
|
|
97
|
+
g.setDefaultAppearance(new PdfDefaultAppearance(ctx.da.fontName, finalFontSize, ctx.da.colorOp));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
lines = g.wrapTextToLines(value, availableWidth);
|
|
101
|
+
const renderLineHeight = finalFontSize * 1.2;
|
|
102
|
+
const startY = height - padding - finalFontSize;
|
|
103
|
+
g.beginText();
|
|
104
|
+
g.moveTo(padding, startY);
|
|
105
|
+
for (let i = 0; i < lines.length; i++) {
|
|
106
|
+
if (i > 0)
|
|
107
|
+
g.moveTo(0, -renderLineHeight);
|
|
108
|
+
g.showText(lines[i].replace(/\r/g, ''), isUnicode, reverseEncodingMap);
|
|
109
|
+
}
|
|
110
|
+
g.endText();
|
|
111
|
+
}
|
|
95
112
|
else {
|
|
96
113
|
// Single line — for non-auto-size, shrink if text overflows
|
|
97
114
|
if (!autoSize) {
|
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
import { PdfFormField } from './pdf-form-field.js';
|
|
2
2
|
import { PdfString } from '../../core/objects/pdf-string.js';
|
|
3
|
+
import type { PdfAcroForm } from '../pdf-acro-form.js';
|
|
4
|
+
import type { PdfIndirectObject } from '../../core/objects/pdf-indirect-object.js';
|
|
3
5
|
/**
|
|
4
6
|
* Button form field subtype (checkboxes, radio buttons, push buttons).
|
|
5
7
|
*/
|
|
6
8
|
export declare class PdfButtonFormField extends PdfFormField {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
constructor(other?: PdfIndirectObject | {
|
|
10
|
+
form?: PdfAcroForm;
|
|
11
|
+
});
|
|
12
|
+
private initWidget;
|
|
13
|
+
set isWidget(val: boolean);
|
|
14
|
+
get isGroup(): boolean;
|
|
15
|
+
get value(): string;
|
|
16
|
+
set value(val: string | PdfString);
|
|
9
17
|
get checked(): boolean;
|
|
10
18
|
set checked(isChecked: boolean);
|
|
11
19
|
generateAppearance(options?: {
|
|
12
20
|
makeReadOnly?: boolean;
|
|
21
|
+
onStateName?: string;
|
|
13
22
|
}): boolean;
|
|
14
23
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { PdfFormField } from './pdf-form-field.js';
|
|
2
2
|
import { PdfButtonAppearanceStream } from '../appearance/pdf-button-appearance-stream.js';
|
|
3
|
-
import { PdfName } from '../../core/objects/pdf-name.js';
|
|
4
3
|
import { PdfString } from '../../core/objects/pdf-string.js';
|
|
5
4
|
/**
|
|
6
5
|
* Button form field subtype (checkboxes, radio buttons, push buttons).
|
|
@@ -9,50 +8,80 @@ export class PdfButtonFormField extends PdfFormField {
|
|
|
9
8
|
static {
|
|
10
9
|
PdfFormField.registerFieldType('Btn', PdfButtonFormField);
|
|
11
10
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const strVal = val instanceof PdfString ? val.value : val;
|
|
17
|
-
if (strVal.trim() === '') {
|
|
18
|
-
this.content.delete('V');
|
|
19
|
-
fieldParent?.content.delete('V');
|
|
20
|
-
this.content.delete('AS');
|
|
21
|
-
return false;
|
|
11
|
+
constructor(other) {
|
|
12
|
+
super(other);
|
|
13
|
+
if (this.isWidget && this.appearanceStates.length === 0) {
|
|
14
|
+
this.initWidget();
|
|
22
15
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
16
|
+
}
|
|
17
|
+
initWidget() {
|
|
18
|
+
this.rect ||= [0, 0, 50, 50];
|
|
19
|
+
this.generateAppearance({
|
|
20
|
+
onStateName: this.onState ?? 'Yes',
|
|
21
|
+
});
|
|
22
|
+
this.appearanceState ??= 'Off';
|
|
23
|
+
}
|
|
24
|
+
set isWidget(val) {
|
|
25
|
+
super.isWidget = val;
|
|
26
|
+
// Only initialize the widget if it has no existing appearances.
|
|
27
|
+
// `appearanceStream` is write-only on PdfFormField, so instead check
|
|
28
|
+
// the underlying appearance data structures.
|
|
29
|
+
if (val &&
|
|
30
|
+
!this.appearanceStreamDict &&
|
|
31
|
+
this.appearanceStates.length === 0) {
|
|
32
|
+
this.initWidget();
|
|
30
33
|
}
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
}
|
|
35
|
+
get isGroup() {
|
|
36
|
+
return this.children.length > 0;
|
|
37
|
+
}
|
|
38
|
+
get value() {
|
|
39
|
+
return super.value;
|
|
40
|
+
}
|
|
41
|
+
set value(val) {
|
|
42
|
+
const strVal = val instanceof PdfString ? val.value : val;
|
|
43
|
+
this.setRawValue(new PdfString(strVal));
|
|
44
|
+
if (this.isGroup) {
|
|
45
|
+
const children = this.children;
|
|
46
|
+
// 'Off' means explicitly uncheck all children
|
|
47
|
+
if (strVal === 'Off') {
|
|
48
|
+
for (const child of children) {
|
|
49
|
+
child.appearanceState = 'Off';
|
|
50
|
+
if (this._form)
|
|
51
|
+
child.form = this._form;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
let wasSet = false;
|
|
56
|
+
for (const child of children) {
|
|
57
|
+
const foundState = child.onStates.includes(strVal);
|
|
58
|
+
if (!wasSet && foundState) {
|
|
59
|
+
wasSet = true;
|
|
60
|
+
}
|
|
61
|
+
child.appearanceState = foundState ? strVal : 'Off';
|
|
62
|
+
if (this._form)
|
|
63
|
+
child.form = this._form;
|
|
64
|
+
}
|
|
65
|
+
if (!wasSet && children.length > 0) {
|
|
66
|
+
// If value doesn't match any on-state, check first child by default
|
|
67
|
+
children[0].appearanceState =
|
|
68
|
+
children[0].onStates[0] ?? 'Off';
|
|
69
|
+
}
|
|
70
|
+
}
|
|
33
71
|
}
|
|
34
72
|
else {
|
|
35
|
-
|
|
73
|
+
this.appearanceState = strVal;
|
|
36
74
|
}
|
|
37
|
-
this.content.set('V', new PdfName(resolved));
|
|
38
|
-
fieldParent?.content.set('V', new PdfName(resolved));
|
|
39
|
-
this.content.set('AS', new PdfName(resolved));
|
|
40
|
-
return true;
|
|
41
75
|
}
|
|
42
76
|
get checked() {
|
|
43
|
-
|
|
44
|
-
return v instanceof PdfName && v.value !== 'Off';
|
|
77
|
+
return !(this.appearanceState === 'Off' || this.value === 'Off');
|
|
45
78
|
}
|
|
46
79
|
set checked(isChecked) {
|
|
47
|
-
const target = this.parent ?? this;
|
|
48
80
|
if (isChecked) {
|
|
49
|
-
|
|
50
|
-
target.content.set('V', new PdfName(onState));
|
|
51
|
-
this.content.set('AS', new PdfName(onState));
|
|
81
|
+
this.value = this.onState ?? 'Yes';
|
|
52
82
|
}
|
|
53
83
|
else {
|
|
54
|
-
|
|
55
|
-
this.content.set('AS', new PdfName('Off'));
|
|
84
|
+
this.value = 'Off';
|
|
56
85
|
}
|
|
57
86
|
}
|
|
58
87
|
generateAppearance(options) {
|
|
@@ -71,11 +100,21 @@ export class PdfButtonFormField extends PdfFormField {
|
|
|
71
100
|
height,
|
|
72
101
|
contentStream: '',
|
|
73
102
|
});
|
|
74
|
-
const
|
|
75
|
-
this.
|
|
76
|
-
|
|
103
|
+
const existingOnState = this.onState;
|
|
104
|
+
const as = this.appearanceState;
|
|
105
|
+
const onKey = options?.onStateName ??
|
|
106
|
+
(as && as !== 'Off' ? as : (existingOnState ?? 'Yes'));
|
|
107
|
+
if (onKey === 'Off') {
|
|
108
|
+
throw new Error("Invalid on-state name 'Off' for button field appearance stream");
|
|
109
|
+
}
|
|
110
|
+
this.appearanceStream = {
|
|
111
|
+
[onKey]: yesAppearance,
|
|
77
112
|
Off: noAppearance,
|
|
78
|
-
}
|
|
113
|
+
};
|
|
114
|
+
this.downAppearanceStream = {
|
|
115
|
+
[onKey]: yesAppearance,
|
|
116
|
+
Off: noAppearance,
|
|
117
|
+
};
|
|
79
118
|
if (options?.makeReadOnly) {
|
|
80
119
|
this.readOnly = true;
|
|
81
120
|
this.print = true;
|
|
@@ -91,7 +91,7 @@ export class PdfChoiceFormField extends PdfFormField {
|
|
|
91
91
|
const fontResources = this.buildFontResources(parsed.fontName);
|
|
92
92
|
const isUnicode = font?.isUnicode ?? false;
|
|
93
93
|
const reverseEncodingMap = font?.reverseEncodingMap;
|
|
94
|
-
this.
|
|
94
|
+
this.appearanceStream = new PdfChoiceAppearanceStream({
|
|
95
95
|
rect: rect,
|
|
96
96
|
value,
|
|
97
97
|
da: parsed,
|
|
@@ -101,7 +101,7 @@ export class PdfChoiceFormField extends PdfFormField {
|
|
|
101
101
|
reverseEncodingMap,
|
|
102
102
|
displayOptions: this.options.map((opt) => opt.label),
|
|
103
103
|
selectedIndex: this.selectedIndex,
|
|
104
|
-
})
|
|
104
|
+
});
|
|
105
105
|
if (options?.makeReadOnly) {
|
|
106
106
|
this.readOnly = true;
|
|
107
107
|
this.print = true;
|
|
@@ -25,6 +25,7 @@ export declare abstract class PdfFormField extends PdfWidgetAnnotation {
|
|
|
25
25
|
form?: PdfAcroForm;
|
|
26
26
|
});
|
|
27
27
|
set form(f: PdfAcroForm);
|
|
28
|
+
static getFieldType(other: PdfIndirectObject): 'Btn' | 'Sig' | 'Tx' | 'Ch' | null;
|
|
28
29
|
static create(other?: PdfIndirectObject): PdfFormField;
|
|
29
30
|
get parent(): PdfFormField | undefined;
|
|
30
31
|
set parent(field: PdfFormField | PdfIndirectObject | undefined);
|
|
@@ -46,14 +47,12 @@ export declare abstract class PdfFormField extends PdfWidgetAnnotation {
|
|
|
46
47
|
set name(name: string);
|
|
47
48
|
get defaultValue(): string;
|
|
48
49
|
set defaultValue(val: string);
|
|
50
|
+
get onStates(): string[];
|
|
51
|
+
get onState(): string | null;
|
|
52
|
+
set onState(state: string);
|
|
49
53
|
get value(): string;
|
|
54
|
+
protected setRawValue(val: string | PdfString): void;
|
|
50
55
|
set value(val: string | PdfString);
|
|
51
|
-
/**
|
|
52
|
-
* Writes the value to the dictionary. Returns true if appearance generation
|
|
53
|
-
* should proceed, false to skip it (e.g. when value was cleared).
|
|
54
|
-
* Override in subclasses to change the stored representation.
|
|
55
|
-
*/
|
|
56
|
-
protected _storeValue(val: string | PdfString, fieldParent: PdfFormField | undefined): boolean;
|
|
57
56
|
get fontSize(): number | null;
|
|
58
57
|
set fontSize(size: number);
|
|
59
58
|
get fontName(): string | null;
|
|
@@ -108,16 +107,23 @@ export declare abstract class PdfFormField extends PdfWidgetAnnotation {
|
|
|
108
107
|
abstract generateAppearance(options?: {
|
|
109
108
|
makeReadOnly?: boolean;
|
|
110
109
|
textYOffset?: number;
|
|
110
|
+
onStateName?: string;
|
|
111
111
|
}): boolean;
|
|
112
|
-
|
|
112
|
+
set appearanceStream(stream: PdfIndirectObject | {
|
|
113
113
|
[key: string]: PdfIndirectObject;
|
|
114
|
-
})
|
|
114
|
+
});
|
|
115
|
+
set downAppearanceStream(stream: PdfIndirectObject | {
|
|
116
|
+
[key: string]: PdfIndirectObject;
|
|
117
|
+
});
|
|
118
|
+
get appearanceState(): string | null;
|
|
119
|
+
set appearanceState(state: string | null);
|
|
115
120
|
/**
|
|
116
121
|
* Returns the list of appearance state names from the normal appearance
|
|
117
122
|
* dictionary (e.g. ["Yes", "Off"] for a checkbox).
|
|
118
123
|
*/
|
|
119
|
-
get appearanceStates(): string
|
|
124
|
+
get appearanceStates(): ReadonlyArray<string>;
|
|
120
125
|
getAppearanceStream(setting?: string): PdfIndirectObject<PdfStream> | null;
|
|
126
|
+
hasAppearanceStream(setting: string): boolean;
|
|
121
127
|
private static _fallbackCtor?;
|
|
122
128
|
private static _registry;
|
|
123
129
|
static registerFieldType(ft: 'Sig' | 'Btn' | 'Tx' | 'Ch', ctor: new (other?: PdfIndirectObject) => PdfFormField, options?: {
|
|
@@ -32,10 +32,25 @@ export class PdfFormField extends PdfWidgetAnnotation {
|
|
|
32
32
|
set form(f) {
|
|
33
33
|
this._form = f;
|
|
34
34
|
}
|
|
35
|
+
static getFieldType(other) {
|
|
36
|
+
if (!(other.content instanceof PdfDictionary))
|
|
37
|
+
return null;
|
|
38
|
+
const ft = other.content.get('FT')?.as(PdfName)?.value;
|
|
39
|
+
if (ft)
|
|
40
|
+
return ft;
|
|
41
|
+
const parentRef = other.content.get('Parent');
|
|
42
|
+
if (parentRef instanceof PdfObjectReference) {
|
|
43
|
+
const parentResolved = parentRef.resolve();
|
|
44
|
+
if (parentResolved?.content instanceof PdfDictionary) {
|
|
45
|
+
return (parentResolved.content.get('FT')?.as(PdfName)?.value ?? null);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
35
50
|
static create(other) {
|
|
36
51
|
if (!(other?.content instanceof PdfDictionary))
|
|
37
52
|
throw new Error('Invalid form field object');
|
|
38
|
-
const ft =
|
|
53
|
+
const ft = PdfFormField.getFieldType(other);
|
|
39
54
|
const cls = ft ? PdfFormField._registry.get(ft) : undefined;
|
|
40
55
|
if (!cls) {
|
|
41
56
|
if (PdfFormField._fallbackCtor) {
|
|
@@ -60,14 +75,23 @@ export class PdfFormField extends PdfWidgetAnnotation {
|
|
|
60
75
|
if (field instanceof PdfFormField) {
|
|
61
76
|
field.children = [...field.children, this];
|
|
62
77
|
}
|
|
78
|
+
// Auto-add widget to page's Annots array
|
|
79
|
+
const page = this.page;
|
|
80
|
+
if (page) {
|
|
81
|
+
const annots = page.annotations;
|
|
82
|
+
const ref = this.reference;
|
|
83
|
+
const key = ref.key;
|
|
84
|
+
const alreadyPresent = annots.items.some((r) => r instanceof PdfObjectReference && r.key === key);
|
|
85
|
+
if (!alreadyPresent) {
|
|
86
|
+
annots.items.push(ref);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
63
89
|
}
|
|
64
90
|
get children() {
|
|
65
91
|
const kids = this.content.get('Kids')?.items ?? [];
|
|
66
92
|
const result = [];
|
|
67
93
|
for (const ref of kids) {
|
|
68
94
|
const resolved = ref.resolve();
|
|
69
|
-
if (!resolved || !(resolved.content instanceof PdfDictionary))
|
|
70
|
-
continue;
|
|
71
95
|
result.push(PdfFormField.create(resolved));
|
|
72
96
|
}
|
|
73
97
|
return result;
|
|
@@ -157,8 +181,7 @@ export class PdfFormField extends PdfWidgetAnnotation {
|
|
|
157
181
|
return undefined;
|
|
158
182
|
}
|
|
159
183
|
get fieldType() {
|
|
160
|
-
const ft =
|
|
161
|
-
this.parent?.content.get('FT')?.as(PdfName)?.value;
|
|
184
|
+
const ft = PdfFormField.getFieldType(this);
|
|
162
185
|
switch (ft) {
|
|
163
186
|
case 'Tx':
|
|
164
187
|
return 'Text';
|
|
@@ -202,6 +225,27 @@ export class PdfFormField extends PdfWidgetAnnotation {
|
|
|
202
225
|
set defaultValue(val) {
|
|
203
226
|
this.content.set('DV', new PdfString(val));
|
|
204
227
|
}
|
|
228
|
+
get onStates() {
|
|
229
|
+
return this.appearanceStates.filter((s) => s !== 'Off');
|
|
230
|
+
}
|
|
231
|
+
get onState() {
|
|
232
|
+
return this.appearanceStates.find((s) => s !== 'Off') || null;
|
|
233
|
+
}
|
|
234
|
+
set onState(state) {
|
|
235
|
+
if (!this.appearanceStates.includes(state)) {
|
|
236
|
+
const currentOnState = this.onState;
|
|
237
|
+
if (currentOnState) {
|
|
238
|
+
this.appearanceStreamDict
|
|
239
|
+
?.get('N')
|
|
240
|
+
?.as(PdfDictionary)
|
|
241
|
+
?.move(currentOnState, state);
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
// No existing on-state; generate a new appearance stream for the new state
|
|
245
|
+
this.generateAppearance({ onStateName: state });
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
205
249
|
get value() {
|
|
206
250
|
const v = this.content.get('V') ?? this.parent?.content.get('V');
|
|
207
251
|
if (v instanceof PdfString) {
|
|
@@ -221,48 +265,51 @@ export class PdfFormField extends PdfWidgetAnnotation {
|
|
|
221
265
|
}
|
|
222
266
|
return '';
|
|
223
267
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
sibling.defaultGenerateAppearance) {
|
|
237
|
-
sibling.generateAppearance();
|
|
238
|
-
}
|
|
268
|
+
setRawValue(val) {
|
|
269
|
+
const targets = [this];
|
|
270
|
+
const parent = this.parent;
|
|
271
|
+
if (parent?.fieldType) {
|
|
272
|
+
targets.push(parent);
|
|
273
|
+
}
|
|
274
|
+
const pdfVal = val instanceof PdfString ? val : new PdfString(val);
|
|
275
|
+
const isEmpty = pdfVal.length === 0;
|
|
276
|
+
for (const target of targets) {
|
|
277
|
+
if (isEmpty) {
|
|
278
|
+
target.content.delete('V');
|
|
279
|
+
target.appearanceState = null;
|
|
239
280
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
if (!this.rect) {
|
|
243
|
-
for (const child of this.children) {
|
|
244
|
-
if (child.rect && child.defaultGenerateAppearance) {
|
|
245
|
-
if (this._form)
|
|
246
|
-
child.form = this._form;
|
|
247
|
-
child.generateAppearance();
|
|
248
|
-
}
|
|
249
|
-
}
|
|
281
|
+
else {
|
|
282
|
+
target.content.set('V', pdfVal);
|
|
250
283
|
}
|
|
251
284
|
}
|
|
252
|
-
if (
|
|
253
|
-
this._form
|
|
285
|
+
if (isEmpty) {
|
|
286
|
+
this._form?.xfa?.datasets?.updateField(this.name, '');
|
|
287
|
+
}
|
|
288
|
+
if (this.defaultGenerateAppearance) {
|
|
289
|
+
this.generateAppearance();
|
|
290
|
+
}
|
|
291
|
+
for (const sibling of this.siblings) {
|
|
292
|
+
if (sibling !== this && sibling.defaultGenerateAppearance) {
|
|
293
|
+
sibling.generateAppearance();
|
|
294
|
+
}
|
|
254
295
|
}
|
|
296
|
+
// Separated field/widget structure: field has no Rect but its Kids
|
|
297
|
+
// are widget annotations that do. Clear stale V entries on children
|
|
298
|
+
// so they inherit the parent's value, then generate appearances.
|
|
299
|
+
for (const child of this.children) {
|
|
300
|
+
if (child.content.has('V')) {
|
|
301
|
+
child.content.delete('V');
|
|
302
|
+
}
|
|
303
|
+
if (child.defaultGenerateAppearance) {
|
|
304
|
+
if (this._form)
|
|
305
|
+
child.form = this._form;
|
|
306
|
+
child.generateAppearance();
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
this._form?.xfa?.datasets?.updateField(this.name, this.value);
|
|
255
310
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
* should proceed, false to skip it (e.g. when value was cleared).
|
|
259
|
-
* Override in subclasses to change the stored representation.
|
|
260
|
-
*/
|
|
261
|
-
_storeValue(val, fieldParent) {
|
|
262
|
-
const pdfVal = val instanceof PdfString ? val : new PdfString(val);
|
|
263
|
-
this.content.set('V', pdfVal);
|
|
264
|
-
fieldParent?.content.set('V', pdfVal);
|
|
265
|
-
return true;
|
|
311
|
+
set value(val) {
|
|
312
|
+
this.setRawValue(val);
|
|
266
313
|
}
|
|
267
314
|
get fontSize() {
|
|
268
315
|
const da = this.defaultAppearance || '';
|
|
@@ -499,7 +546,7 @@ export class PdfFormField extends PdfWidgetAnnotation {
|
|
|
499
546
|
engine: this._form?.jsEngine,
|
|
500
547
|
});
|
|
501
548
|
}
|
|
502
|
-
|
|
549
|
+
set appearanceStream(stream) {
|
|
503
550
|
this.appearanceStreamDict ||= new PdfDictionary();
|
|
504
551
|
if (stream instanceof PdfIndirectObject) {
|
|
505
552
|
this.appearanceStreamDict.set('N', stream.reference);
|
|
@@ -512,6 +559,35 @@ export class PdfFormField extends PdfWidgetAnnotation {
|
|
|
512
559
|
this.appearanceStreamDict.set('N', dict);
|
|
513
560
|
}
|
|
514
561
|
}
|
|
562
|
+
set downAppearanceStream(stream) {
|
|
563
|
+
this.appearanceStreamDict ||= new PdfDictionary();
|
|
564
|
+
if (stream instanceof PdfIndirectObject) {
|
|
565
|
+
this.appearanceStreamDict.set('D', stream.reference);
|
|
566
|
+
}
|
|
567
|
+
else {
|
|
568
|
+
const dict = new PdfDictionary();
|
|
569
|
+
for (const key in stream) {
|
|
570
|
+
dict.set(key, stream[key].reference);
|
|
571
|
+
}
|
|
572
|
+
this.appearanceStreamDict.set('D', dict);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
get appearanceState() {
|
|
576
|
+
return this.content.get('AS')?.as(PdfName)?.value ?? null;
|
|
577
|
+
}
|
|
578
|
+
set appearanceState(state) {
|
|
579
|
+
if (state === null) {
|
|
580
|
+
this.content.delete('AS');
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
else {
|
|
584
|
+
this.content.set('AS', new PdfName(state));
|
|
585
|
+
}
|
|
586
|
+
if (this.defaultGenerateAppearance &&
|
|
587
|
+
!this.hasAppearanceStream(state)) {
|
|
588
|
+
this.generateAppearance();
|
|
589
|
+
}
|
|
590
|
+
}
|
|
515
591
|
/**
|
|
516
592
|
* Returns the list of appearance state names from the normal appearance
|
|
517
593
|
* dictionary (e.g. ["Yes", "Off"] for a checkbox).
|
|
@@ -519,7 +595,7 @@ export class PdfFormField extends PdfWidgetAnnotation {
|
|
|
519
595
|
get appearanceStates() {
|
|
520
596
|
const n = this.appearanceStreamDict?.get('N');
|
|
521
597
|
if (n instanceof PdfDictionary) {
|
|
522
|
-
return
|
|
598
|
+
return n.keys().map((k) => k.value);
|
|
523
599
|
}
|
|
524
600
|
return [];
|
|
525
601
|
}
|
|
@@ -534,9 +610,7 @@ export class PdfFormField extends PdfWidgetAnnotation {
|
|
|
534
610
|
}
|
|
535
611
|
}
|
|
536
612
|
else if (n instanceof PdfDictionary) {
|
|
537
|
-
const key = setting ??
|
|
538
|
-
this.content.get('AS')?.as(PdfName)?.value ??
|
|
539
|
-
undefined;
|
|
613
|
+
const key = setting ?? this.appearanceState ?? undefined;
|
|
540
614
|
if (key) {
|
|
541
615
|
const entry = n.get(key);
|
|
542
616
|
if (entry instanceof PdfObjectReference) {
|
|
@@ -549,6 +623,9 @@ export class PdfFormField extends PdfWidgetAnnotation {
|
|
|
549
623
|
}
|
|
550
624
|
return null;
|
|
551
625
|
}
|
|
626
|
+
hasAppearanceStream(setting) {
|
|
627
|
+
return this.appearanceStates.includes(setting);
|
|
628
|
+
}
|
|
552
629
|
static _fallbackCtor;
|
|
553
630
|
static _registry = new Map();
|
|
554
631
|
static registerFieldType(ft, ctor, options) {
|
|
@@ -24,7 +24,7 @@ export class PdfTextFormField extends PdfFormField {
|
|
|
24
24
|
const fontResources = this.buildFontResources(parsed.fontName);
|
|
25
25
|
const isUnicode = font?.isUnicode ?? false;
|
|
26
26
|
const reverseEncodingMap = font?.reverseEncodingMap;
|
|
27
|
-
this.
|
|
27
|
+
this.appearanceStream = new PdfTextAppearanceStream({
|
|
28
28
|
rect: rect,
|
|
29
29
|
value: this.value,
|
|
30
30
|
da: parsed,
|
|
@@ -34,7 +34,7 @@ export class PdfTextFormField extends PdfFormField {
|
|
|
34
34
|
fontResources,
|
|
35
35
|
isUnicode,
|
|
36
36
|
reverseEncodingMap,
|
|
37
|
-
})
|
|
37
|
+
});
|
|
38
38
|
if (options?.makeReadOnly) {
|
|
39
39
|
this.readOnly = true;
|
|
40
40
|
if (!this.print)
|
|
@@ -36,6 +36,7 @@ export declare class PdfAcroForm<T extends Record<string, string> = Record<strin
|
|
|
36
36
|
set defaultResources(resources: PdfDefaultResourcesDictionary | null);
|
|
37
37
|
get fields(): ReadonlyArray<PdfFormField>;
|
|
38
38
|
addField(...fields: PdfFormField[]): void;
|
|
39
|
+
private _addWidgetToPage;
|
|
39
40
|
set fields(newFields: PdfFormField[]);
|
|
40
41
|
setValues(values: Partial<T>): void;
|
|
41
42
|
importData(fields: T): void;
|
|
@@ -108,13 +108,24 @@ export class PdfAcroForm extends PdfIndirectObject {
|
|
|
108
108
|
}
|
|
109
109
|
for (const field of fields) {
|
|
110
110
|
fieldsArray.items.push(field.reference);
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
this._addWidgetToPage(field);
|
|
112
|
+
// Also register any child widgets (e.g. radio button group kids)
|
|
113
|
+
for (const child of field.children) {
|
|
114
|
+
this._addWidgetToPage(child);
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
|
+
_addWidgetToPage(field) {
|
|
119
|
+
const page = field.page;
|
|
120
|
+
if (!page)
|
|
121
|
+
return;
|
|
122
|
+
const ref = field.reference;
|
|
123
|
+
const key = ref.key;
|
|
124
|
+
const alreadyPresent = page.annotations.items.some((r) => r instanceof PdfObjectReference && r.key === key);
|
|
125
|
+
if (!alreadyPresent) {
|
|
126
|
+
page.annotations.items.push(ref);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
118
129
|
set fields(newFields) {
|
|
119
130
|
this.content.set('Fields', PdfArray.refs(newFields));
|
|
120
131
|
}
|