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.
Files changed (144) hide show
  1. package/EXAMPLES.md +51 -70
  2. package/README.md +1 -1
  3. package/dist/acroform/appearance/index.d.ts +4 -4
  4. package/dist/acroform/appearance/index.js +4 -4
  5. package/dist/acroform/appearance/{PdfAppearanceStream.d.ts → pdf-appearance-stream.d.ts} +9 -3
  6. package/dist/acroform/appearance/{PdfAppearanceStream.js → pdf-appearance-stream.js} +14 -5
  7. package/dist/acroform/appearance/{PdfButtonAppearanceStream.d.ts → pdf-button-appearance-stream.d.ts} +3 -2
  8. package/dist/acroform/appearance/pdf-button-appearance-stream.js +58 -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/{PdfTextAppearanceStream.d.ts → pdf-text-appearance-stream.d.ts} +7 -2
  14. package/dist/acroform/appearance/pdf-text-appearance-stream.js +104 -0
  15. package/dist/acroform/fields/index.d.ts +7 -7
  16. package/dist/acroform/fields/index.js +7 -7
  17. package/dist/acroform/fields/pdf-button-form-field.d.ts +14 -0
  18. package/dist/acroform/fields/pdf-button-form-field.js +70 -0
  19. package/dist/acroform/fields/pdf-choice-form-field.d.ts +19 -0
  20. package/dist/acroform/fields/pdf-choice-form-field.js +112 -0
  21. package/dist/acroform/fields/{PdfFormFieldFlags.d.ts → pdf-form-field-flags.d.ts} +5 -6
  22. package/dist/acroform/fields/{PdfFormFieldFlags.js → pdf-form-field-flags.js} +12 -18
  23. package/dist/acroform/fields/{PdfFormField.d.ts → pdf-form-field.d.ts} +37 -38
  24. package/dist/acroform/fields/pdf-form-field.js +519 -0
  25. package/dist/acroform/fields/{PdfSignatureFormField.d.ts → pdf-signature-form-field.d.ts} +1 -1
  26. package/dist/acroform/fields/{PdfSignatureFormField.js → pdf-signature-form-field.js} +1 -1
  27. package/dist/acroform/fields/{PdfTextFormField.d.ts → pdf-text-form-field.d.ts} +1 -1
  28. package/dist/acroform/fields/{PdfTextFormField.js → pdf-text-form-field.js} +11 -13
  29. package/dist/acroform/fields/types.d.ts +6 -1
  30. package/dist/acroform/index.d.ts +1 -3
  31. package/dist/acroform/index.js +1 -3
  32. package/dist/acroform/pdf-acro-form.d.ts +45 -0
  33. package/dist/acroform/pdf-acro-form.js +203 -0
  34. package/dist/acroform/xfa/index.d.ts +3 -3
  35. package/dist/acroform/xfa/index.js +2 -2
  36. package/dist/acroform/xfa/{PdfXfaData.d.ts → pdf-xfa-data.d.ts} +4 -3
  37. package/dist/acroform/xfa/{PdfXfaData.js → pdf-xfa-data.js} +16 -12
  38. package/dist/acroform/xfa/pdf-xfa-form.d.ts +16 -0
  39. package/dist/acroform/xfa/pdf-xfa-form.js +34 -0
  40. package/dist/annotations/index.d.ts +3 -4
  41. package/dist/annotations/index.js +3 -4
  42. package/dist/annotations/{PdfAnnotationFlags.d.ts → pdf-annotation-flags.d.ts} +3 -4
  43. package/dist/annotations/{PdfAnnotationFlags.js → pdf-annotation-flags.js} +5 -6
  44. package/dist/annotations/{PdfAnnotation.d.ts → pdf-annotation.d.ts} +31 -5
  45. package/dist/annotations/{PdfAnnotation.js → pdf-annotation.js} +31 -19
  46. package/dist/annotations/pdf-default-resources.d.ts +11 -0
  47. package/dist/annotations/pdf-default-resources.js +3 -0
  48. package/dist/annotations/{PdfWidgetAnnotation.d.ts → pdf-widget-annotation.d.ts} +1 -1
  49. package/dist/annotations/{PdfWidgetAnnotation.js → pdf-widget-annotation.js} +1 -1
  50. package/dist/core/decoder.js +1 -1
  51. package/dist/core/objects/pdf-array.d.ts +8 -1
  52. package/dist/core/objects/pdf-array.js +31 -0
  53. package/dist/core/objects/pdf-dictionary.d.ts +2 -0
  54. package/dist/core/objects/pdf-dictionary.js +14 -7
  55. package/dist/core/objects/pdf-hexadecimal.d.ts +1 -0
  56. package/dist/core/objects/pdf-hexadecimal.js +3 -3
  57. package/dist/core/objects/pdf-indirect-object.d.ts +18 -9
  58. package/dist/core/objects/pdf-indirect-object.js +75 -16
  59. package/dist/core/objects/pdf-number.d.ts +1 -0
  60. package/dist/core/objects/pdf-number.js +5 -4
  61. package/dist/core/objects/pdf-object-reference.d.ts +8 -1
  62. package/dist/core/objects/pdf-object-reference.js +14 -0
  63. package/dist/core/objects/pdf-object.d.ts +14 -0
  64. package/dist/core/objects/pdf-object.js +36 -0
  65. package/dist/core/objects/pdf-start-xref.d.ts +1 -0
  66. package/dist/core/objects/pdf-start-xref.js +4 -0
  67. package/dist/core/objects/pdf-stream.d.ts +44 -7
  68. package/dist/core/objects/pdf-stream.js +284 -26
  69. package/dist/core/objects/pdf-string.d.ts +1 -0
  70. package/dist/core/objects/pdf-string.js +3 -6
  71. package/dist/core/objects/pdf-trailer.d.ts +1 -0
  72. package/dist/core/objects/pdf-trailer.js +6 -3
  73. package/dist/core/objects/pdf-xref-table.js +1 -1
  74. package/dist/core/parser/incremental-parser.d.ts +0 -13
  75. package/dist/core/parser/incremental-parser.js +1 -18
  76. package/dist/core/ref.d.ts +3 -1
  77. package/dist/core/ref.js +8 -5
  78. package/dist/core/streams/object-stream.d.ts +1 -1
  79. package/dist/core/streams/object-stream.js +1 -1
  80. package/dist/core/tokens/token.d.ts +2 -1
  81. package/dist/core/tokens/token.js +3 -0
  82. package/dist/errors.d.ts +22 -0
  83. package/dist/errors.js +24 -0
  84. package/dist/fonts/index.d.ts +0 -1
  85. package/dist/fonts/index.js +0 -1
  86. package/dist/fonts/pdf-font.d.ts +94 -32
  87. package/dist/fonts/pdf-font.js +301 -83
  88. package/dist/index.d.ts +1 -0
  89. package/dist/index.js +1 -0
  90. package/dist/pdf/index.d.ts +2 -1
  91. package/dist/pdf/index.js +2 -1
  92. package/dist/pdf/pdf-document.d.ts +61 -36
  93. package/dist/pdf/pdf-document.js +315 -117
  94. package/dist/pdf/pdf-page.d.ts +50 -0
  95. package/dist/pdf/pdf-page.js +144 -0
  96. package/dist/pdf/pdf-pages.d.ts +28 -0
  97. package/dist/pdf/pdf-pages.js +94 -0
  98. package/dist/pdf/pdf-reader.d.ts +5 -1
  99. package/dist/pdf/pdf-reader.js +36 -2
  100. package/dist/pdf/pdf-revision.d.ts +3 -3
  101. package/dist/pdf/pdf-revision.js +7 -7
  102. package/dist/pdf/pdf-xref-lookup.js +34 -14
  103. package/dist/signing/document-security-store.d.ts +14 -17
  104. package/dist/signing/document-security-store.js +19 -34
  105. package/dist/signing/signer.d.ts +23 -8
  106. package/dist/signing/signer.js +51 -17
  107. package/dist/utils/encodePdfText.d.ts +17 -0
  108. package/dist/utils/encodePdfText.js +61 -0
  109. package/dist/utils/index.d.ts +1 -2
  110. package/dist/utils/index.js +1 -2
  111. package/dist/utils/needsCentralWhitespace.d.ts +10 -0
  112. package/dist/utils/needsCentralWhitespace.js +34 -0
  113. package/package.json +3 -3
  114. package/dist/acroform/PdfAcroForm.d.ts +0 -63
  115. package/dist/acroform/PdfAcroForm.js +0 -279
  116. package/dist/acroform/PdfFontEncodingCache.d.ts +0 -16
  117. package/dist/acroform/PdfFontEncodingCache.js +0 -75
  118. package/dist/acroform/acroform.d.ts +0 -9
  119. package/dist/acroform/acroform.js +0 -7
  120. package/dist/acroform/appearance/PdfButtonAppearanceStream.js +0 -54
  121. package/dist/acroform/appearance/PdfChoiceAppearanceStream.d.ts +0 -15
  122. package/dist/acroform/appearance/PdfChoiceAppearanceStream.js +0 -48
  123. package/dist/acroform/appearance/PdfTextAppearanceStream.js +0 -75
  124. package/dist/acroform/fields/PdfButtonFormField.d.ts +0 -9
  125. package/dist/acroform/fields/PdfButtonFormField.js +0 -35
  126. package/dist/acroform/fields/PdfChoiceFormField.d.ts +0 -9
  127. package/dist/acroform/fields/PdfChoiceFormField.js +0 -46
  128. package/dist/acroform/fields/PdfFormField.js +0 -499
  129. package/dist/acroform/manager.d.ts +0 -33
  130. package/dist/acroform/manager.js +0 -51
  131. package/dist/acroform/xfa/PdfXfaForm.d.ts +0 -12
  132. package/dist/acroform/xfa/PdfXfaForm.js +0 -64
  133. package/dist/annotations/PdfAnnotationWriter.d.ts +0 -20
  134. package/dist/annotations/PdfAnnotationWriter.js +0 -76
  135. package/dist/fonts/font-manager.d.ts +0 -127
  136. package/dist/fonts/font-manager.js +0 -378
  137. package/dist/pdf/errors.d.ts +0 -6
  138. package/dist/pdf/errors.js +0 -6
  139. package/dist/utils/predictors.d.ts +0 -113
  140. package/dist/utils/predictors.js +0 -279
  141. /package/dist/acroform/fields/{PdfDefaultAppearance.d.ts → pdf-default-appearance.d.ts} +0 -0
  142. /package/dist/acroform/fields/{PdfDefaultAppearance.js → pdf-default-appearance.js} +0 -0
  143. /package/dist/utils/{IterableReadableStream.d.ts → iterable-readable-stream.d.ts} +0 -0
  144. /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
+ }
@@ -1,8 +1,10 @@
1
- import { PdfDefaultAppearance } from '../fields/PdfDefaultAppearance.js';
2
- import { PdfAppearanceStream } from './PdfAppearanceStream.js';
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 './PdfFormField.js';
2
- export { PdfFormFieldFlags } from './PdfFormFieldFlags.js';
3
- export { PdfDefaultAppearance } from './PdfDefaultAppearance.js';
4
- export { PdfTextFormField } from './PdfTextFormField.js';
5
- export { PdfButtonFormField } from './PdfButtonFormField.js';
6
- export { PdfChoiceFormField } from './PdfChoiceFormField.js';
7
- export { PdfSignatureFormField } from './PdfSignatureFormField.js';
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 './PdfFormField.js';
2
- export { PdfFormFieldFlags } from './PdfFormFieldFlags.js';
3
- export { PdfDefaultAppearance } from './PdfDefaultAppearance.js';
4
- export { PdfTextFormField } from './PdfTextFormField.js';
5
- export { PdfButtonFormField } from './PdfButtonFormField.js';
6
- export { PdfChoiceFormField } from './PdfChoiceFormField.js';
7
- export { PdfSignatureFormField } from './PdfSignatureFormField.js';
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 { PdfDictionary } from '../../core/objects/pdf-dictionary.js';
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
- dict: PdfDictionary;
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(flags: number);
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
- dict;
8
- parentDict;
9
- constructor(dict, parentDict) {
10
- this.dict = dict;
11
- this.parentDict = parentDict;
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 (this.dict.get('Ff')?.as(PdfNumber)?.value ??
15
- this.parentDict?.get('Ff')?.as(PdfNumber)?.value ??
16
- 0);
15
+ return this.value;
17
16
  }
18
- set flags(flags) {
19
- this.dict.set('Ff', new PdfNumber(flags));
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, value) {
25
- if (value) {
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);