pdf-lite 1.4.0 → 1.6.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/EXAMPLES.md +51 -70
- package/README.md +1 -1
- package/dist/acroform/appearance/index.d.ts +4 -4
- package/dist/acroform/appearance/index.js +4 -4
- package/dist/acroform/appearance/{PdfAppearanceStream.d.ts → pdf-appearance-stream.d.ts} +9 -3
- package/dist/acroform/appearance/{PdfAppearanceStream.js → pdf-appearance-stream.js} +14 -5
- package/dist/acroform/appearance/{PdfButtonAppearanceStream.d.ts → pdf-button-appearance-stream.d.ts} +3 -2
- package/dist/acroform/appearance/pdf-button-appearance-stream.js +58 -0
- package/dist/acroform/appearance/pdf-choice-appearance-stream.d.ts +22 -0
- package/dist/acroform/appearance/pdf-choice-appearance-stream.js +75 -0
- package/dist/acroform/appearance/pdf-graphics.d.ts +51 -0
- package/dist/acroform/appearance/pdf-graphics.js +239 -0
- package/dist/acroform/appearance/{PdfTextAppearanceStream.d.ts → pdf-text-appearance-stream.d.ts} +7 -2
- package/dist/acroform/appearance/pdf-text-appearance-stream.js +104 -0
- package/dist/acroform/fields/index.d.ts +7 -7
- package/dist/acroform/fields/index.js +7 -7
- package/dist/acroform/fields/pdf-button-form-field.d.ts +14 -0
- package/dist/acroform/fields/pdf-button-form-field.js +70 -0
- package/dist/acroform/fields/pdf-choice-form-field.d.ts +19 -0
- package/dist/acroform/fields/pdf-choice-form-field.js +112 -0
- package/dist/acroform/fields/{PdfFormFieldFlags.d.ts → pdf-form-field-flags.d.ts} +5 -6
- package/dist/acroform/fields/{PdfFormFieldFlags.js → pdf-form-field-flags.js} +12 -18
- package/dist/acroform/fields/{PdfFormField.d.ts → pdf-form-field.d.ts} +37 -38
- package/dist/acroform/fields/pdf-form-field.js +519 -0
- package/dist/acroform/fields/{PdfSignatureFormField.d.ts → pdf-signature-form-field.d.ts} +1 -1
- package/dist/acroform/fields/{PdfSignatureFormField.js → pdf-signature-form-field.js} +1 -1
- package/dist/acroform/fields/{PdfTextFormField.d.ts → pdf-text-form-field.d.ts} +1 -1
- package/dist/acroform/fields/{PdfTextFormField.js → pdf-text-form-field.js} +11 -13
- package/dist/acroform/fields/types.d.ts +6 -1
- package/dist/acroform/index.d.ts +1 -3
- package/dist/acroform/index.js +1 -3
- package/dist/acroform/pdf-acro-form.d.ts +45 -0
- package/dist/acroform/pdf-acro-form.js +203 -0
- package/dist/acroform/xfa/index.d.ts +3 -3
- package/dist/acroform/xfa/index.js +2 -2
- package/dist/acroform/xfa/{PdfXfaData.d.ts → pdf-xfa-data.d.ts} +4 -3
- package/dist/acroform/xfa/{PdfXfaData.js → pdf-xfa-data.js} +16 -12
- package/dist/acroform/xfa/pdf-xfa-form.d.ts +16 -0
- package/dist/acroform/xfa/pdf-xfa-form.js +34 -0
- package/dist/annotations/index.d.ts +3 -4
- package/dist/annotations/index.js +3 -4
- package/dist/annotations/{PdfAnnotationFlags.d.ts → pdf-annotation-flags.d.ts} +3 -4
- package/dist/annotations/{PdfAnnotationFlags.js → pdf-annotation-flags.js} +5 -6
- package/dist/annotations/{PdfAnnotation.d.ts → pdf-annotation.d.ts} +31 -5
- package/dist/annotations/{PdfAnnotation.js → pdf-annotation.js} +31 -19
- package/dist/annotations/pdf-default-resources.d.ts +11 -0
- package/dist/annotations/pdf-default-resources.js +3 -0
- package/dist/annotations/{PdfWidgetAnnotation.d.ts → pdf-widget-annotation.d.ts} +1 -1
- package/dist/annotations/{PdfWidgetAnnotation.js → pdf-widget-annotation.js} +1 -1
- package/dist/core/decoder.js +1 -1
- package/dist/core/objects/pdf-array.d.ts +8 -1
- package/dist/core/objects/pdf-array.js +31 -0
- package/dist/core/objects/pdf-dictionary.d.ts +2 -0
- package/dist/core/objects/pdf-dictionary.js +14 -7
- package/dist/core/objects/pdf-hexadecimal.d.ts +1 -0
- package/dist/core/objects/pdf-hexadecimal.js +3 -3
- package/dist/core/objects/pdf-indirect-object.d.ts +18 -9
- package/dist/core/objects/pdf-indirect-object.js +75 -16
- package/dist/core/objects/pdf-number.d.ts +1 -0
- package/dist/core/objects/pdf-number.js +5 -4
- package/dist/core/objects/pdf-object-reference.d.ts +8 -1
- package/dist/core/objects/pdf-object-reference.js +14 -0
- package/dist/core/objects/pdf-object.d.ts +14 -0
- package/dist/core/objects/pdf-object.js +36 -0
- package/dist/core/objects/pdf-start-xref.d.ts +1 -0
- package/dist/core/objects/pdf-start-xref.js +4 -0
- package/dist/core/objects/pdf-stream.d.ts +44 -7
- package/dist/core/objects/pdf-stream.js +284 -26
- package/dist/core/objects/pdf-string.d.ts +1 -0
- package/dist/core/objects/pdf-string.js +3 -6
- package/dist/core/objects/pdf-trailer.d.ts +1 -0
- package/dist/core/objects/pdf-trailer.js +6 -3
- package/dist/core/objects/pdf-xref-table.js +1 -1
- package/dist/core/parser/incremental-parser.d.ts +0 -13
- package/dist/core/parser/incremental-parser.js +1 -18
- package/dist/core/ref.d.ts +3 -1
- package/dist/core/ref.js +8 -5
- package/dist/core/streams/object-stream.d.ts +1 -1
- package/dist/core/streams/object-stream.js +1 -1
- package/dist/core/tokens/token.d.ts +2 -1
- package/dist/core/tokens/token.js +3 -0
- package/dist/errors.d.ts +22 -0
- package/dist/errors.js +24 -0
- package/dist/fonts/index.d.ts +0 -1
- package/dist/fonts/index.js +0 -1
- package/dist/fonts/pdf-font.d.ts +94 -32
- package/dist/fonts/pdf-font.js +301 -83
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/pdf/index.d.ts +2 -1
- package/dist/pdf/index.js +2 -1
- package/dist/pdf/pdf-document.d.ts +61 -36
- package/dist/pdf/pdf-document.js +315 -117
- package/dist/pdf/pdf-page.d.ts +50 -0
- package/dist/pdf/pdf-page.js +144 -0
- package/dist/pdf/pdf-pages.d.ts +28 -0
- package/dist/pdf/pdf-pages.js +94 -0
- package/dist/pdf/pdf-reader.d.ts +5 -1
- package/dist/pdf/pdf-reader.js +36 -2
- package/dist/pdf/pdf-revision.d.ts +3 -3
- package/dist/pdf/pdf-revision.js +7 -7
- package/dist/pdf/pdf-xref-lookup.js +34 -14
- package/dist/signing/document-security-store.d.ts +14 -17
- package/dist/signing/document-security-store.js +19 -34
- package/dist/signing/signer.d.ts +23 -8
- package/dist/signing/signer.js +51 -17
- package/dist/utils/encodePdfText.d.ts +17 -0
- package/dist/utils/encodePdfText.js +61 -0
- package/dist/utils/index.d.ts +1 -2
- package/dist/utils/index.js +1 -2
- package/dist/utils/needsCentralWhitespace.d.ts +10 -0
- package/dist/utils/needsCentralWhitespace.js +34 -0
- package/package.json +3 -3
- package/dist/acroform/PdfAcroForm.d.ts +0 -63
- package/dist/acroform/PdfAcroForm.js +0 -279
- package/dist/acroform/PdfFontEncodingCache.d.ts +0 -16
- package/dist/acroform/PdfFontEncodingCache.js +0 -75
- package/dist/acroform/acroform.d.ts +0 -9
- package/dist/acroform/acroform.js +0 -7
- package/dist/acroform/appearance/PdfButtonAppearanceStream.js +0 -54
- package/dist/acroform/appearance/PdfChoiceAppearanceStream.d.ts +0 -15
- package/dist/acroform/appearance/PdfChoiceAppearanceStream.js +0 -48
- package/dist/acroform/appearance/PdfTextAppearanceStream.js +0 -75
- package/dist/acroform/fields/PdfButtonFormField.d.ts +0 -9
- package/dist/acroform/fields/PdfButtonFormField.js +0 -35
- package/dist/acroform/fields/PdfChoiceFormField.d.ts +0 -9
- package/dist/acroform/fields/PdfChoiceFormField.js +0 -46
- package/dist/acroform/fields/PdfFormField.js +0 -499
- package/dist/acroform/manager.d.ts +0 -33
- package/dist/acroform/manager.js +0 -51
- package/dist/acroform/xfa/PdfXfaForm.d.ts +0 -12
- package/dist/acroform/xfa/PdfXfaForm.js +0 -64
- package/dist/annotations/PdfAnnotationWriter.d.ts +0 -20
- package/dist/annotations/PdfAnnotationWriter.js +0 -76
- package/dist/fonts/font-manager.d.ts +0 -127
- package/dist/fonts/font-manager.js +0 -378
- package/dist/pdf/errors.d.ts +0 -6
- package/dist/pdf/errors.js +0 -6
- package/dist/utils/predictors.d.ts +0 -113
- package/dist/utils/predictors.js +0 -279
- /package/dist/acroform/fields/{PdfDefaultAppearance.d.ts → pdf-default-appearance.d.ts} +0 -0
- /package/dist/acroform/fields/{PdfDefaultAppearance.js → pdf-default-appearance.js} +0 -0
- /package/dist/utils/{IterableReadableStream.d.ts → iterable-readable-stream.d.ts} +0 -0
- /package/dist/utils/{IterableReadableStream.js → iterable-readable-stream.js} +0 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { encodePdfText } from '../../utils/encodePdfText.js';
|
|
2
|
+
import { PdfFont } from '../../fonts/pdf-font.js';
|
|
3
|
+
/**
|
|
4
|
+
* Lightweight builder for PDF content streams.
|
|
5
|
+
* Chains PDF operators via a fluent API and emits the final stream with build().
|
|
6
|
+
* Enhanced with text measurement capabilities for layout calculations.
|
|
7
|
+
*/
|
|
8
|
+
export class PdfGraphics {
|
|
9
|
+
lines = [];
|
|
10
|
+
resolvedFonts;
|
|
11
|
+
defaultAppearance;
|
|
12
|
+
constructor(options) {
|
|
13
|
+
this.resolvedFonts = options?.resolvedFonts;
|
|
14
|
+
this.defaultAppearance = options?.defaultAppearance;
|
|
15
|
+
}
|
|
16
|
+
save() {
|
|
17
|
+
this.lines.push('q');
|
|
18
|
+
return this;
|
|
19
|
+
}
|
|
20
|
+
restore() {
|
|
21
|
+
this.lines.push('Q');
|
|
22
|
+
return this;
|
|
23
|
+
}
|
|
24
|
+
beginText() {
|
|
25
|
+
this.lines.push('BT');
|
|
26
|
+
return this;
|
|
27
|
+
}
|
|
28
|
+
endText() {
|
|
29
|
+
this.lines.push('ET');
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
setDefaultAppearance(da) {
|
|
33
|
+
this.lines.push(da.toString());
|
|
34
|
+
this.defaultAppearance = da;
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
moveTo(x, y) {
|
|
38
|
+
this.lines.push(`${x} ${y} Td`);
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
showText(text, isUnicode, reverseEncodingMap) {
|
|
42
|
+
this.lines.push(`${encodePdfText(text, isUnicode, reverseEncodingMap)} Tj`);
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
showLiteralText(text) {
|
|
46
|
+
this.lines.push(`(${text}) Tj`);
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
beginMarkedContent() {
|
|
50
|
+
this.lines.push('/Tx BMC');
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
53
|
+
endMarkedContent() {
|
|
54
|
+
this.lines.push('EMC');
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
raw(op) {
|
|
58
|
+
this.lines.push(op);
|
|
59
|
+
return this;
|
|
60
|
+
}
|
|
61
|
+
setFillRGB(r, g, b) {
|
|
62
|
+
this.lines.push(`${r} ${g} ${b} rg`);
|
|
63
|
+
return this;
|
|
64
|
+
}
|
|
65
|
+
setFillGray(v) {
|
|
66
|
+
this.lines.push(`${v} g`);
|
|
67
|
+
return this;
|
|
68
|
+
}
|
|
69
|
+
setFont(name, size) {
|
|
70
|
+
this.lines.push(`/${name} ${size} Tf`);
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
movePath(x, y) {
|
|
74
|
+
this.lines.push(`${x} ${y} m`);
|
|
75
|
+
return this;
|
|
76
|
+
}
|
|
77
|
+
lineTo(x, y) {
|
|
78
|
+
this.lines.push(`${x} ${y} l`);
|
|
79
|
+
return this;
|
|
80
|
+
}
|
|
81
|
+
curveTo(x1, y1, x2, y2, x3, y3) {
|
|
82
|
+
this.lines.push(`${x1} ${y1} ${x2} ${y2} ${x3} ${y3} c`);
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
fill() {
|
|
86
|
+
this.lines.push('f');
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
stroke() {
|
|
90
|
+
this.lines.push('S');
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
closePath() {
|
|
94
|
+
this.lines.push('h');
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
97
|
+
build() {
|
|
98
|
+
return this.lines.join('\n') + '\n';
|
|
99
|
+
}
|
|
100
|
+
get currentFont() {
|
|
101
|
+
if (!this.defaultAppearance) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
const fontName = this.defaultAppearance.fontName;
|
|
105
|
+
const size = this.defaultAppearance.fontSize;
|
|
106
|
+
const fontObject = this.resolvedFonts?.get(fontName);
|
|
107
|
+
return { fontObject, size };
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Calculate the width of text using the current font and size.
|
|
111
|
+
*/
|
|
112
|
+
measureTextWidth(text, fontSize) {
|
|
113
|
+
if (!this.currentFont) {
|
|
114
|
+
throw new Error('No font set - call setDefaultAppearance() first');
|
|
115
|
+
}
|
|
116
|
+
const { fontObject, size: currentSize } = this.currentFont;
|
|
117
|
+
const size = fontSize ?? currentSize;
|
|
118
|
+
const fontName = this.defaultAppearance.fontName;
|
|
119
|
+
const effectiveFontObject = fontObject ?? PdfFont.getStandardFont(fontName);
|
|
120
|
+
if (effectiveFontObject?.widths &&
|
|
121
|
+
effectiveFontObject.firstChar !== undefined) {
|
|
122
|
+
let width = 0;
|
|
123
|
+
for (const char of text) {
|
|
124
|
+
const charCode = char.charCodeAt(0);
|
|
125
|
+
const charWidth = effectiveFontObject.getCharacterWidth(charCode, size);
|
|
126
|
+
if (charWidth !== null) {
|
|
127
|
+
width += charWidth;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
width += size * 0.6;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return width;
|
|
134
|
+
}
|
|
135
|
+
// Unknown font — use Helvetica as best-effort approximation
|
|
136
|
+
let width = 0;
|
|
137
|
+
for (const char of text) {
|
|
138
|
+
const charWidth = PdfFont.HELVETICA.getCharacterWidth(char.charCodeAt(0), size);
|
|
139
|
+
width += charWidth !== null ? charWidth : size * 0.6;
|
|
140
|
+
}
|
|
141
|
+
return width;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Wrap text to fit within the specified width, breaking at word boundaries.
|
|
145
|
+
*/
|
|
146
|
+
wrapTextToLines(text, maxWidth, fontSize) {
|
|
147
|
+
if (!this.currentFont) {
|
|
148
|
+
throw new Error('No font set - call setDefaultAppearance() first');
|
|
149
|
+
}
|
|
150
|
+
// Handle explicit line breaks first
|
|
151
|
+
const paragraphs = text.split('\n');
|
|
152
|
+
const wrappedLines = [];
|
|
153
|
+
for (const paragraph of paragraphs) {
|
|
154
|
+
if (this.measureTextWidth(paragraph, fontSize) <= maxWidth) {
|
|
155
|
+
wrappedLines.push(paragraph);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
// Word wrapping needed
|
|
159
|
+
const words = paragraph.split(' ');
|
|
160
|
+
let currentLine = '';
|
|
161
|
+
for (const word of words) {
|
|
162
|
+
const testLine = currentLine ? `${currentLine} ${word}` : word;
|
|
163
|
+
if (this.measureTextWidth(testLine, fontSize) <= maxWidth) {
|
|
164
|
+
currentLine = testLine;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
if (currentLine) {
|
|
168
|
+
wrappedLines.push(currentLine);
|
|
169
|
+
currentLine = word;
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
// Single word is too long, break it
|
|
173
|
+
const brokenLines = this.breakLongWord(word, maxWidth, fontSize);
|
|
174
|
+
wrappedLines.push(...brokenLines.slice(0, -1));
|
|
175
|
+
currentLine = brokenLines[brokenLines.length - 1];
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (currentLine) {
|
|
180
|
+
wrappedLines.push(currentLine);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return wrappedLines;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Calculate the minimum font size needed to fit text within given constraints.
|
|
187
|
+
*/
|
|
188
|
+
calculateFittingFontSize(text, maxWidth, maxHeight, lineHeight = 1.2) {
|
|
189
|
+
if (!this.currentFont) {
|
|
190
|
+
throw new Error('No font set - call setDefaultAppearance() first');
|
|
191
|
+
}
|
|
192
|
+
const startSize = this.currentFont.size;
|
|
193
|
+
const minSize = 0.5;
|
|
194
|
+
const fits = (size) => {
|
|
195
|
+
if (this.measureTextWidth(text, size) > maxWidth)
|
|
196
|
+
return false;
|
|
197
|
+
if (maxHeight !== undefined) {
|
|
198
|
+
const lines = this.wrapTextToLines(text, maxWidth, size);
|
|
199
|
+
return lines.length * size * lineHeight <= maxHeight;
|
|
200
|
+
}
|
|
201
|
+
return true;
|
|
202
|
+
};
|
|
203
|
+
if (fits(startSize))
|
|
204
|
+
return startSize;
|
|
205
|
+
if (!fits(minSize))
|
|
206
|
+
return minSize;
|
|
207
|
+
// Binary search on 0.5pt grid: lo fits, hi does not
|
|
208
|
+
let lo = minSize;
|
|
209
|
+
let hi = startSize;
|
|
210
|
+
while (hi - lo > 0.5) {
|
|
211
|
+
const mid = Math.round((lo + hi) / 2 / 0.5) * 0.5;
|
|
212
|
+
if (fits(mid))
|
|
213
|
+
lo = mid;
|
|
214
|
+
else
|
|
215
|
+
hi = mid;
|
|
216
|
+
}
|
|
217
|
+
return lo;
|
|
218
|
+
}
|
|
219
|
+
breakLongWord(word, maxWidth, fontSize) {
|
|
220
|
+
const lines = [];
|
|
221
|
+
let currentLine = '';
|
|
222
|
+
for (const char of word) {
|
|
223
|
+
const testLine = currentLine + char;
|
|
224
|
+
if (this.measureTextWidth(testLine, fontSize) <= maxWidth) {
|
|
225
|
+
currentLine = testLine;
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
if (currentLine) {
|
|
229
|
+
lines.push(currentLine);
|
|
230
|
+
}
|
|
231
|
+
currentLine = char;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (currentLine) {
|
|
235
|
+
lines.push(currentLine);
|
|
236
|
+
}
|
|
237
|
+
return lines.length > 0 ? lines : [word];
|
|
238
|
+
}
|
|
239
|
+
}
|
package/dist/acroform/appearance/{PdfTextAppearanceStream.d.ts → pdf-text-appearance-stream.d.ts}
RENAMED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { PdfDefaultAppearance } from '../fields/
|
|
2
|
-
import { PdfAppearanceStream } from './
|
|
1
|
+
import { PdfDefaultAppearance } from '../fields/pdf-default-appearance.js';
|
|
2
|
+
import { PdfAppearanceStream } from './pdf-appearance-stream.js';
|
|
3
3
|
import type { PdfDictionary } from '../../core/objects/pdf-dictionary.js';
|
|
4
|
+
import type { PdfFont } from '../../fonts/pdf-font.js';
|
|
4
5
|
/**
|
|
5
6
|
* Appearance stream for text fields (single-line, multiline, comb).
|
|
7
|
+
* Enhanced with word wrapping and automatic font scaling.
|
|
6
8
|
*/
|
|
7
9
|
export declare class PdfTextAppearanceStream extends PdfAppearanceStream {
|
|
8
10
|
constructor(ctx: {
|
|
@@ -13,5 +15,8 @@ export declare class PdfTextAppearanceStream extends PdfAppearanceStream {
|
|
|
13
15
|
comb: boolean;
|
|
14
16
|
maxLen: number | null;
|
|
15
17
|
fontResources?: PdfDictionary;
|
|
18
|
+
resolvedFonts?: Map<string, PdfFont>;
|
|
19
|
+
isUnicode?: boolean;
|
|
20
|
+
reverseEncodingMap?: Map<string, number>;
|
|
16
21
|
});
|
|
17
22
|
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { PdfDefaultAppearance } from '../fields/pdf-default-appearance.js';
|
|
2
|
+
import { PdfAppearanceStream } from './pdf-appearance-stream.js';
|
|
3
|
+
import { PdfGraphics } from './pdf-graphics.js';
|
|
4
|
+
/**
|
|
5
|
+
* Appearance stream for text fields (single-line, multiline, comb).
|
|
6
|
+
* Enhanced with word wrapping and automatic font scaling.
|
|
7
|
+
*/
|
|
8
|
+
export class PdfTextAppearanceStream extends PdfAppearanceStream {
|
|
9
|
+
constructor(ctx) {
|
|
10
|
+
const [x1, y1, x2, y2] = ctx.rect;
|
|
11
|
+
const width = x2 - x1;
|
|
12
|
+
const height = y2 - y1;
|
|
13
|
+
const value = ctx.value;
|
|
14
|
+
const isUnicode = ctx.isUnicode ?? false;
|
|
15
|
+
const reverseEncodingMap = ctx.reverseEncodingMap;
|
|
16
|
+
const padding = 2;
|
|
17
|
+
const availableWidth = width - 2 * padding;
|
|
18
|
+
const availableHeight = height - 2 * padding;
|
|
19
|
+
// Create graphics with font context for text measurement
|
|
20
|
+
const g = new PdfGraphics({
|
|
21
|
+
resolvedFonts: ctx.resolvedFonts,
|
|
22
|
+
});
|
|
23
|
+
g.beginMarkedContent();
|
|
24
|
+
g.save();
|
|
25
|
+
// Set initial font to enable measurement
|
|
26
|
+
g.setDefaultAppearance(ctx.da);
|
|
27
|
+
let finalFontSize = ctx.da.fontSize;
|
|
28
|
+
let lines = [];
|
|
29
|
+
if (ctx.multiline) {
|
|
30
|
+
const testLines = g.wrapTextToLines(value, availableWidth);
|
|
31
|
+
const lineHeight = finalFontSize * 1.2;
|
|
32
|
+
if (testLines.length * lineHeight > availableHeight) {
|
|
33
|
+
// Scale font down to fit
|
|
34
|
+
finalFontSize = g.calculateFittingFontSize(value, availableWidth, availableHeight, 1.2);
|
|
35
|
+
const adjustedDA = new PdfDefaultAppearance(ctx.da.fontName, finalFontSize, ctx.da.colorOp);
|
|
36
|
+
g.setDefaultAppearance(adjustedDA);
|
|
37
|
+
lines = g.wrapTextToLines(value, availableWidth);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
lines = testLines;
|
|
41
|
+
}
|
|
42
|
+
const renderLineHeight = finalFontSize * 1.2;
|
|
43
|
+
const startY = height - padding - finalFontSize;
|
|
44
|
+
g.beginText();
|
|
45
|
+
g.moveTo(padding, startY);
|
|
46
|
+
for (let i = 0; i < lines.length; i++) {
|
|
47
|
+
if (i > 0)
|
|
48
|
+
g.moveTo(0, -renderLineHeight);
|
|
49
|
+
g.showText(lines[i].replace(/\r/g, ''), isUnicode, reverseEncodingMap);
|
|
50
|
+
}
|
|
51
|
+
g.endText();
|
|
52
|
+
}
|
|
53
|
+
else if (ctx.comb && ctx.maxLen) {
|
|
54
|
+
const cellWidth = width / ctx.maxLen;
|
|
55
|
+
const chars = [...value];
|
|
56
|
+
// Calculate font size to fit the widest character in its cell
|
|
57
|
+
let maxCharWidth = 0;
|
|
58
|
+
let widestChar = chars[0] ?? '';
|
|
59
|
+
for (const char of chars) {
|
|
60
|
+
const charWidth = g.measureTextWidth(char);
|
|
61
|
+
if (charWidth > maxCharWidth) {
|
|
62
|
+
maxCharWidth = charWidth;
|
|
63
|
+
widestChar = char;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (maxCharWidth > cellWidth) {
|
|
67
|
+
finalFontSize = g.calculateFittingFontSize(widestChar, cellWidth);
|
|
68
|
+
const adjustedDA = new PdfDefaultAppearance(ctx.da.fontName, finalFontSize, ctx.da.colorOp);
|
|
69
|
+
g.setDefaultAppearance(adjustedDA);
|
|
70
|
+
}
|
|
71
|
+
const textY = (height - finalFontSize) / 2 + finalFontSize * 0.2;
|
|
72
|
+
g.beginText();
|
|
73
|
+
for (let i = 0; i < chars.length && i < ctx.maxLen; i++) {
|
|
74
|
+
const cellX = cellWidth * i + cellWidth / 2 - finalFontSize * 0.3;
|
|
75
|
+
g.moveTo(cellX, textY);
|
|
76
|
+
g.showText(chars[i], isUnicode, reverseEncodingMap);
|
|
77
|
+
g.moveTo(-cellX, -textY);
|
|
78
|
+
}
|
|
79
|
+
g.endText();
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
// Single line text
|
|
83
|
+
const textWidth = g.measureTextWidth(value);
|
|
84
|
+
if (textWidth > availableWidth) {
|
|
85
|
+
finalFontSize = g.calculateFittingFontSize(value, availableWidth);
|
|
86
|
+
const adjustedDA = new PdfDefaultAppearance(ctx.da.fontName, finalFontSize, ctx.da.colorOp);
|
|
87
|
+
g.setDefaultAppearance(adjustedDA);
|
|
88
|
+
}
|
|
89
|
+
const textY = (height - finalFontSize) / 2 + finalFontSize * 0.2;
|
|
90
|
+
g.beginText();
|
|
91
|
+
g.moveTo(padding, textY);
|
|
92
|
+
g.showText(value, isUnicode, reverseEncodingMap);
|
|
93
|
+
g.endText();
|
|
94
|
+
}
|
|
95
|
+
g.restore();
|
|
96
|
+
g.endMarkedContent();
|
|
97
|
+
super({
|
|
98
|
+
width,
|
|
99
|
+
height,
|
|
100
|
+
contentStream: g.build(),
|
|
101
|
+
resources: ctx.fontResources,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export { PdfFormField, PdfAcroFormField } from './
|
|
2
|
-
export { PdfFormFieldFlags } from './
|
|
3
|
-
export { PdfDefaultAppearance } from './
|
|
4
|
-
export { PdfTextFormField } from './
|
|
5
|
-
export { PdfButtonFormField } from './
|
|
6
|
-
export { PdfChoiceFormField } from './
|
|
7
|
-
export { PdfSignatureFormField } from './
|
|
1
|
+
export { PdfFormField, PdfAcroFormField } from './pdf-form-field.js';
|
|
2
|
+
export { PdfFormFieldFlags } from './pdf-form-field-flags.js';
|
|
3
|
+
export { PdfDefaultAppearance } from './pdf-default-appearance.js';
|
|
4
|
+
export { PdfTextFormField } from './pdf-text-form-field.js';
|
|
5
|
+
export { PdfButtonFormField } from './pdf-button-form-field.js';
|
|
6
|
+
export { PdfChoiceFormField } from './pdf-choice-form-field.js';
|
|
7
|
+
export { PdfSignatureFormField } from './pdf-signature-form-field.js';
|
|
8
8
|
export { PdfFieldType, type FormContext } from './types.js';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export { PdfFormField, PdfAcroFormField } from './
|
|
2
|
-
export { PdfFormFieldFlags } from './
|
|
3
|
-
export { PdfDefaultAppearance } from './
|
|
4
|
-
export { PdfTextFormField } from './
|
|
5
|
-
export { PdfButtonFormField } from './
|
|
6
|
-
export { PdfChoiceFormField } from './
|
|
7
|
-
export { PdfSignatureFormField } from './
|
|
1
|
+
export { PdfFormField, PdfAcroFormField } from './pdf-form-field.js';
|
|
2
|
+
export { PdfFormFieldFlags } from './pdf-form-field-flags.js';
|
|
3
|
+
export { PdfDefaultAppearance } from './pdf-default-appearance.js';
|
|
4
|
+
export { PdfTextFormField } from './pdf-text-form-field.js';
|
|
5
|
+
export { PdfButtonFormField } from './pdf-button-form-field.js';
|
|
6
|
+
export { PdfChoiceFormField } from './pdf-choice-form-field.js';
|
|
7
|
+
export { PdfSignatureFormField } from './pdf-signature-form-field.js';
|
|
8
8
|
export { PdfFieldType } from './types.js';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { PdfFormField } from './pdf-form-field.js';
|
|
2
|
+
import { PdfString } from '../../core/objects/pdf-string.js';
|
|
3
|
+
/**
|
|
4
|
+
* Button form field subtype (checkboxes, radio buttons, push buttons).
|
|
5
|
+
*/
|
|
6
|
+
export declare class PdfButtonFormField extends PdfFormField {
|
|
7
|
+
set defaultValue(val: string);
|
|
8
|
+
protected _storeValue(val: string | PdfString, fieldParent: PdfFormField | undefined): boolean;
|
|
9
|
+
get checked(): boolean;
|
|
10
|
+
set checked(isChecked: boolean);
|
|
11
|
+
generateAppearance(options?: {
|
|
12
|
+
makeReadOnly?: boolean;
|
|
13
|
+
}): boolean;
|
|
14
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { PdfFormField } from './pdf-form-field.js';
|
|
2
|
+
import { PdfButtonAppearanceStream } from '../appearance/pdf-button-appearance-stream.js';
|
|
3
|
+
import { PdfName } from '../../core/objects/pdf-name.js';
|
|
4
|
+
import { PdfString } from '../../core/objects/pdf-string.js';
|
|
5
|
+
/**
|
|
6
|
+
* Button form field subtype (checkboxes, radio buttons, push buttons).
|
|
7
|
+
*/
|
|
8
|
+
export class PdfButtonFormField extends PdfFormField {
|
|
9
|
+
static {
|
|
10
|
+
PdfFormField.registerFieldType('Btn', PdfButtonFormField);
|
|
11
|
+
}
|
|
12
|
+
set defaultValue(val) {
|
|
13
|
+
this.content.set('DV', new PdfName(val));
|
|
14
|
+
}
|
|
15
|
+
_storeValue(val, fieldParent) {
|
|
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;
|
|
22
|
+
}
|
|
23
|
+
this.content.set('V', new PdfName(strVal));
|
|
24
|
+
fieldParent?.content.set('V', new PdfName(strVal));
|
|
25
|
+
this.content.set('AS', new PdfName(strVal));
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
get checked() {
|
|
29
|
+
const v = this.content.get('V') ?? this.parent?.content.get('V');
|
|
30
|
+
return v instanceof PdfName && v.value === 'Yes';
|
|
31
|
+
}
|
|
32
|
+
set checked(isChecked) {
|
|
33
|
+
const target = this.parent ?? this;
|
|
34
|
+
if (isChecked) {
|
|
35
|
+
target.content.set('V', new PdfName('Yes'));
|
|
36
|
+
this.content.set('AS', new PdfName('Yes'));
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
target.content.set('V', new PdfName('Off'));
|
|
40
|
+
this.content.set('AS', new PdfName('Off'));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
generateAppearance(options) {
|
|
44
|
+
const rect = this.rect;
|
|
45
|
+
if (!rect || rect.length !== 4)
|
|
46
|
+
return false;
|
|
47
|
+
const [x1, y1, x2, y2] = rect;
|
|
48
|
+
const width = x2 - x1;
|
|
49
|
+
const height = y2 - y1;
|
|
50
|
+
// Merge own flags with parent flags so inherited bits (e.g. Radio) are
|
|
51
|
+
// not lost when a child widget has its own Ff entry (even Ff: 0).
|
|
52
|
+
const effectiveFlags = this.flags.flags | (this.parent?.flags?.flags ?? 0);
|
|
53
|
+
const yesAppearance = PdfButtonAppearanceStream.buildYesContent(width, height, effectiveFlags);
|
|
54
|
+
const noAppearance = new PdfButtonAppearanceStream({
|
|
55
|
+
width,
|
|
56
|
+
height,
|
|
57
|
+
contentStream: '',
|
|
58
|
+
});
|
|
59
|
+
this.setAppearanceStream({
|
|
60
|
+
Yes: yesAppearance,
|
|
61
|
+
Off: noAppearance,
|
|
62
|
+
});
|
|
63
|
+
if (options?.makeReadOnly) {
|
|
64
|
+
this.readOnly = true;
|
|
65
|
+
this.print = true;
|
|
66
|
+
this.noZoom = true;
|
|
67
|
+
}
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { PdfFormField } from './pdf-form-field.js';
|
|
2
|
+
import { PdfObjectReference } from '../../core/objects/pdf-object-reference.js';
|
|
3
|
+
/**
|
|
4
|
+
* Choice form field subtype (dropdowns, list boxes).
|
|
5
|
+
*/
|
|
6
|
+
export declare class PdfChoiceFormField extends PdfFormField {
|
|
7
|
+
get selectedIndex(): number;
|
|
8
|
+
get options(): {
|
|
9
|
+
label: string;
|
|
10
|
+
value: string;
|
|
11
|
+
}[];
|
|
12
|
+
set options(values: {
|
|
13
|
+
label: string;
|
|
14
|
+
value: string;
|
|
15
|
+
}[] | string[] | PdfObjectReference | undefined);
|
|
16
|
+
generateAppearance(options?: {
|
|
17
|
+
makeReadOnly?: boolean;
|
|
18
|
+
}): boolean;
|
|
19
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { PdfFormField } from './pdf-form-field.js';
|
|
2
|
+
import { PdfDefaultAppearance } from './pdf-default-appearance.js';
|
|
3
|
+
import { PdfChoiceAppearanceStream } from '../appearance/pdf-choice-appearance-stream.js';
|
|
4
|
+
import { PdfObjectReference } from '../../core/objects/pdf-object-reference.js';
|
|
5
|
+
import { PdfArray } from '../../core/objects/pdf-array.js';
|
|
6
|
+
import { PdfString } from '../../core/objects/pdf-string.js';
|
|
7
|
+
import { PdfIndirectObject } from '../../core/index.js';
|
|
8
|
+
/**
|
|
9
|
+
* Choice form field subtype (dropdowns, list boxes).
|
|
10
|
+
*/
|
|
11
|
+
export class PdfChoiceFormField extends PdfFormField {
|
|
12
|
+
static {
|
|
13
|
+
PdfFormField.registerFieldType('Ch', PdfChoiceFormField);
|
|
14
|
+
}
|
|
15
|
+
get selectedIndex() {
|
|
16
|
+
return this.options.findIndex((opt) => opt.value === this.value);
|
|
17
|
+
}
|
|
18
|
+
get options() {
|
|
19
|
+
let opt = this.content.get('Opt') ?? this.parent?.content.get('Opt');
|
|
20
|
+
if (!opt)
|
|
21
|
+
return [];
|
|
22
|
+
if (opt instanceof PdfObjectReference) {
|
|
23
|
+
opt = opt
|
|
24
|
+
.resolve()
|
|
25
|
+
.as((PdfIndirectObject)).content;
|
|
26
|
+
}
|
|
27
|
+
if (!(opt instanceof PdfArray))
|
|
28
|
+
return [];
|
|
29
|
+
return opt.items.map((item) => {
|
|
30
|
+
if (item instanceof PdfArray && item.items.length >= 2) {
|
|
31
|
+
const label = item.items[1] instanceof PdfString
|
|
32
|
+
? item.items[1].value
|
|
33
|
+
: '';
|
|
34
|
+
const value = item.items[0] instanceof PdfString
|
|
35
|
+
? item.items[0].value
|
|
36
|
+
: '';
|
|
37
|
+
return { label, value };
|
|
38
|
+
}
|
|
39
|
+
else if (item instanceof PdfString) {
|
|
40
|
+
return { label: item.value, value: item.value };
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
return { label: '', value: '' };
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
set options(values) {
|
|
48
|
+
if (values === undefined) {
|
|
49
|
+
this.content.delete('Opt');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (values instanceof PdfObjectReference) {
|
|
53
|
+
this.content.set('Opt', values);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (values.length === 0) {
|
|
57
|
+
this.content.delete('Opt');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const optArray = new PdfArray();
|
|
61
|
+
for (const option of values) {
|
|
62
|
+
if (typeof option === 'string') {
|
|
63
|
+
optArray.items.push(new PdfString(option));
|
|
64
|
+
}
|
|
65
|
+
else if (option.label === option.value) {
|
|
66
|
+
optArray.items.push(new PdfString(option.value));
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
const pair = new PdfArray();
|
|
70
|
+
pair.items.push(new PdfString(option.value));
|
|
71
|
+
pair.items.push(new PdfString(option.label));
|
|
72
|
+
optArray.items.push(pair);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
this.content.set('Opt', optArray);
|
|
76
|
+
}
|
|
77
|
+
generateAppearance(options) {
|
|
78
|
+
const rect = this.rect;
|
|
79
|
+
if (!rect || rect.length !== 4)
|
|
80
|
+
return false;
|
|
81
|
+
const da = this.defaultAppearance;
|
|
82
|
+
if (!da)
|
|
83
|
+
return false;
|
|
84
|
+
const value = this.value;
|
|
85
|
+
if (!value)
|
|
86
|
+
return false;
|
|
87
|
+
const parsed = PdfDefaultAppearance.parse(da);
|
|
88
|
+
if (!parsed)
|
|
89
|
+
return false;
|
|
90
|
+
const font = this.font;
|
|
91
|
+
const fontResources = this.buildFontResources(parsed.fontName);
|
|
92
|
+
const isUnicode = font?.isUnicode ?? false;
|
|
93
|
+
const reverseEncodingMap = font?.reverseEncodingMap;
|
|
94
|
+
this.setAppearanceStream(new PdfChoiceAppearanceStream({
|
|
95
|
+
rect: rect,
|
|
96
|
+
value,
|
|
97
|
+
da: parsed,
|
|
98
|
+
flags: this.flags,
|
|
99
|
+
fontResources,
|
|
100
|
+
isUnicode,
|
|
101
|
+
reverseEncodingMap,
|
|
102
|
+
displayOptions: this.options.map((opt) => opt.label),
|
|
103
|
+
selectedIndex: this.selectedIndex,
|
|
104
|
+
}));
|
|
105
|
+
if (options?.makeReadOnly) {
|
|
106
|
+
this.readOnly = true;
|
|
107
|
+
this.print = true;
|
|
108
|
+
this.noZoom = true;
|
|
109
|
+
}
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PdfNumber } from '../../core/objects/pdf-number.js';
|
|
2
2
|
/**
|
|
3
3
|
* Field-specific Ff flag accessors for form fields.
|
|
4
4
|
* These are separate from annotation flags (F field).
|
|
5
|
+
* Extends PdfNumber so it can be stored directly in a PDF dictionary.
|
|
5
6
|
*/
|
|
6
|
-
export declare class PdfFormFieldFlags {
|
|
7
|
-
|
|
8
|
-
parentDict?: PdfDictionary;
|
|
9
|
-
constructor(dict: PdfDictionary, parentDict?: PdfDictionary);
|
|
7
|
+
export declare class PdfFormFieldFlags extends PdfNumber {
|
|
8
|
+
constructor(value?: number | PdfNumber | PdfFormFieldFlags);
|
|
10
9
|
get flags(): number;
|
|
11
|
-
set flags(
|
|
10
|
+
set flags(v: number | PdfFormFieldFlags);
|
|
12
11
|
private getFlag;
|
|
13
12
|
private setFlag;
|
|
14
13
|
get readOnly(): boolean;
|
|
@@ -2,32 +2,26 @@ import { PdfNumber } from '../../core/objects/pdf-number.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* Field-specific Ff flag accessors for form fields.
|
|
4
4
|
* These are separate from annotation flags (F field).
|
|
5
|
+
* Extends PdfNumber so it can be stored directly in a PDF dictionary.
|
|
5
6
|
*/
|
|
6
|
-
export class PdfFormFieldFlags {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
this.
|
|
11
|
-
|
|
7
|
+
export class PdfFormFieldFlags extends PdfNumber {
|
|
8
|
+
constructor(value = 0) {
|
|
9
|
+
super(value);
|
|
10
|
+
this.preTokens = typeof value === 'number' ? undefined : value.preTokens;
|
|
11
|
+
this.postTokens =
|
|
12
|
+
typeof value === 'number' ? undefined : value.postTokens;
|
|
12
13
|
}
|
|
13
14
|
get flags() {
|
|
14
|
-
return
|
|
15
|
-
this.parentDict?.get('Ff')?.as(PdfNumber)?.value ??
|
|
16
|
-
0);
|
|
15
|
+
return this.value;
|
|
17
16
|
}
|
|
18
|
-
set flags(
|
|
19
|
-
this.
|
|
17
|
+
set flags(v) {
|
|
18
|
+
this.value = v instanceof PdfFormFieldFlags ? v.value : v;
|
|
20
19
|
}
|
|
21
20
|
getFlag(bit) {
|
|
22
21
|
return (this.flags & bit) !== 0;
|
|
23
22
|
}
|
|
24
|
-
setFlag(bit,
|
|
25
|
-
|
|
26
|
-
this.flags = this.flags | bit;
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
this.flags = this.flags & ~bit;
|
|
30
|
-
}
|
|
23
|
+
setFlag(bit, val) {
|
|
24
|
+
this.flags = val ? this.flags | bit : this.flags & ~bit;
|
|
31
25
|
}
|
|
32
26
|
get readOnly() {
|
|
33
27
|
return this.getFlag(1);
|