pdf-lite 1.5.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 (92) hide show
  1. package/EXAMPLES.md +51 -70
  2. package/README.md +1 -1
  3. package/dist/acroform/appearance/pdf-button-appearance-stream.d.ts +1 -1
  4. package/dist/acroform/appearance/pdf-button-appearance-stream.js +6 -2
  5. package/dist/acroform/fields/pdf-button-form-field.d.ts +0 -9
  6. package/dist/acroform/fields/pdf-button-form-field.js +7 -39
  7. package/dist/acroform/fields/pdf-choice-form-field.d.ts +2 -1
  8. package/dist/acroform/fields/pdf-choice-form-field.js +19 -38
  9. package/dist/acroform/fields/pdf-form-field.d.ts +27 -32
  10. package/dist/acroform/fields/pdf-form-field.js +180 -94
  11. package/dist/acroform/fields/pdf-text-form-field.js +6 -33
  12. package/dist/acroform/fields/types.d.ts +1 -1
  13. package/dist/acroform/index.d.ts +0 -2
  14. package/dist/acroform/index.js +0 -2
  15. package/dist/acroform/pdf-acro-form.d.ts +12 -36
  16. package/dist/acroform/pdf-acro-form.js +111 -201
  17. package/dist/acroform/xfa/pdf-xfa-data.d.ts +4 -3
  18. package/dist/acroform/xfa/pdf-xfa-data.js +16 -12
  19. package/dist/acroform/xfa/pdf-xfa-form.d.ts +9 -4
  20. package/dist/acroform/xfa/pdf-xfa-form.js +17 -39
  21. package/dist/annotations/index.d.ts +0 -1
  22. package/dist/annotations/index.js +0 -1
  23. package/dist/annotations/pdf-annotation.d.ts +7 -2
  24. package/dist/annotations/pdf-annotation.js +30 -19
  25. package/dist/annotations/pdf-default-resources.d.ts +11 -0
  26. package/dist/annotations/pdf-default-resources.js +3 -0
  27. package/dist/core/decoder.js +1 -1
  28. package/dist/core/objects/pdf-array.d.ts +8 -1
  29. package/dist/core/objects/pdf-array.js +31 -0
  30. package/dist/core/objects/pdf-dictionary.d.ts +2 -0
  31. package/dist/core/objects/pdf-dictionary.js +14 -7
  32. package/dist/core/objects/pdf-hexadecimal.d.ts +1 -0
  33. package/dist/core/objects/pdf-hexadecimal.js +3 -3
  34. package/dist/core/objects/pdf-indirect-object.d.ts +18 -9
  35. package/dist/core/objects/pdf-indirect-object.js +75 -16
  36. package/dist/core/objects/pdf-number.d.ts +1 -0
  37. package/dist/core/objects/pdf-number.js +5 -4
  38. package/dist/core/objects/pdf-object-reference.d.ts +8 -1
  39. package/dist/core/objects/pdf-object-reference.js +14 -0
  40. package/dist/core/objects/pdf-object.d.ts +14 -0
  41. package/dist/core/objects/pdf-object.js +36 -0
  42. package/dist/core/objects/pdf-start-xref.d.ts +1 -0
  43. package/dist/core/objects/pdf-start-xref.js +4 -0
  44. package/dist/core/objects/pdf-stream.d.ts +43 -7
  45. package/dist/core/objects/pdf-stream.js +278 -24
  46. package/dist/core/objects/pdf-string.d.ts +1 -0
  47. package/dist/core/objects/pdf-string.js +3 -6
  48. package/dist/core/objects/pdf-trailer.d.ts +1 -0
  49. package/dist/core/objects/pdf-trailer.js +6 -3
  50. package/dist/core/objects/pdf-xref-table.js +1 -1
  51. package/dist/core/ref.d.ts +3 -1
  52. package/dist/core/ref.js +8 -5
  53. package/dist/core/tokens/token.d.ts +2 -1
  54. package/dist/core/tokens/token.js +3 -0
  55. package/dist/fonts/index.d.ts +0 -1
  56. package/dist/fonts/index.js +0 -1
  57. package/dist/fonts/pdf-font.d.ts +32 -27
  58. package/dist/fonts/pdf-font.js +115 -77
  59. package/dist/pdf/index.d.ts +2 -0
  60. package/dist/pdf/index.js +2 -0
  61. package/dist/pdf/pdf-document.d.ts +61 -37
  62. package/dist/pdf/pdf-document.js +314 -120
  63. package/dist/pdf/pdf-page.d.ts +50 -0
  64. package/dist/pdf/pdf-page.js +144 -0
  65. package/dist/pdf/pdf-pages.d.ts +28 -0
  66. package/dist/pdf/pdf-pages.js +94 -0
  67. package/dist/pdf/pdf-reader.d.ts +5 -1
  68. package/dist/pdf/pdf-reader.js +36 -2
  69. package/dist/pdf/pdf-revision.d.ts +3 -3
  70. package/dist/pdf/pdf-revision.js +7 -7
  71. package/dist/pdf/pdf-xref-lookup.js +34 -14
  72. package/dist/signing/document-security-store.d.ts +14 -17
  73. package/dist/signing/document-security-store.js +19 -34
  74. package/dist/signing/signer.d.ts +23 -8
  75. package/dist/signing/signer.js +51 -17
  76. package/dist/utils/index.d.ts +0 -1
  77. package/dist/utils/index.js +0 -1
  78. package/dist/utils/needsCentralWhitespace.d.ts +10 -0
  79. package/dist/utils/needsCentralWhitespace.js +34 -0
  80. package/package.json +3 -3
  81. package/dist/acroform/acroform.d.ts +0 -9
  82. package/dist/acroform/acroform.js +0 -7
  83. package/dist/acroform/manager.d.ts +0 -37
  84. package/dist/acroform/manager.js +0 -57
  85. package/dist/acroform/pdf-font-encoding-cache.d.ts +0 -27
  86. package/dist/acroform/pdf-font-encoding-cache.js +0 -188
  87. package/dist/annotations/pdf-annotation-writer.d.ts +0 -20
  88. package/dist/annotations/pdf-annotation-writer.js +0 -76
  89. package/dist/fonts/manager.d.ts +0 -127
  90. package/dist/fonts/manager.js +0 -378
  91. package/dist/utils/predictors.d.ts +0 -113
  92. package/dist/utils/predictors.js +0 -279
package/EXAMPLES.md CHANGED
@@ -10,38 +10,11 @@ import { PdfArray } from 'pdf-lite/core/objects/pdf-array'
10
10
  import { PdfDictionary } from 'pdf-lite/core/objects/pdf-dictionary'
11
11
  import { PdfIndirectObject } from 'pdf-lite/core/objects/pdf-indirect-object'
12
12
  import { PdfName } from 'pdf-lite/core/objects/pdf-name'
13
- import { PdfNumber } from 'pdf-lite/core/objects/pdf-number'
14
13
  import { PdfObjectReference } from 'pdf-lite/core/objects/pdf-object-reference'
15
14
  import { PdfStream } from 'pdf-lite/core/objects/pdf-stream'
16
15
  import { PdfDocument } from 'pdf-lite/pdf/pdf-document'
17
-
18
- function createPage(
19
- contentStreamRef: PdfObjectReference,
20
- ): PdfIndirectObject<PdfDictionary> {
21
- const pageDict = new PdfDictionary()
22
- pageDict.set('Type', new PdfName('Page'))
23
- pageDict.set(
24
- 'MediaBox',
25
- new PdfArray([
26
- new PdfNumber(0),
27
- new PdfNumber(0),
28
- new PdfNumber(612),
29
- new PdfNumber(792),
30
- ]),
31
- )
32
- pageDict.set('Contents', contentStreamRef)
33
- return new PdfIndirectObject({ content: pageDict })
34
- }
35
-
36
- function createPages(
37
- pages: PdfIndirectObject<PdfDictionary>[],
38
- ): PdfIndirectObject<PdfDictionary> {
39
- const pagesDict = new PdfDictionary()
40
- pagesDict.set('Type', new PdfName('Pages'))
41
- pagesDict.set('Kids', new PdfArray(pages.map((x) => x.reference)))
42
- pagesDict.set('Count', new PdfNumber(pages.length))
43
- return new PdfIndirectObject({ content: pagesDict })
44
- }
16
+ import { PdfPage } from 'pdf-lite/pdf/pdf-page'
17
+ import { PdfPages } from 'pdf-lite/pdf/pdf-pages'
45
18
 
46
19
  function createCatalog(
47
20
  pagesRef: PdfObjectReference,
@@ -89,16 +62,18 @@ const contentStream = new PdfIndirectObject({
89
62
  }),
90
63
  })
91
64
 
92
- // Create a page
93
- const page = createPage(contentStream.reference)
94
- // Add resources to the page
95
- page.content.set('Resources', resources.reference)
65
+ // Create a page using PdfPage
66
+ const page = new PdfPage()
67
+ page.mediaBox = [0, 0, 612, 792]
68
+ page.contents = contentStream.reference
69
+ page.resources = resources.reference
96
70
  document.add(page)
97
71
 
98
- // Create pages collection
99
- const pages = createPages([page])
100
- // Set parent reference for the page
101
- page.content.set('Parent', pages.reference)
72
+ // Create pages collection using PdfPages
73
+ const pages = new PdfPages()
74
+ pages.kids = new PdfArray([page.reference])
75
+ pages.count = 1
76
+ page.parent = pages
102
77
  document.add(pages)
103
78
 
104
79
  // Create catalog
@@ -109,7 +84,7 @@ document.add(catalog)
109
84
  document.trailerDict.set('Root', catalog.reference)
110
85
 
111
86
  document.add(contentStream)
112
- await document.commit()
87
+ await document.finalize()
113
88
 
114
89
  const file = `${import.meta.dirname}/tmp/created.pdf`
115
90
  console.log(`Writing PDF to: ${file}`)
@@ -225,7 +200,7 @@ document.add(catalog)
225
200
  document.trailerDict.set('Root', catalog.reference)
226
201
 
227
202
  document.add(contentStream)
228
- await document.commit()
203
+ await document.finalize()
229
204
 
230
205
  document.securityHandler = new PdfV2SecurityHandler({
231
206
  password: 'up',
@@ -577,14 +552,7 @@ document.add(catalog)
577
552
  // Set the catalog as the root
578
553
  document.trailerDict.set('Root', catalog.reference)
579
554
 
580
- // IMPORTANT: Add all signatures LAST - after all other objects
581
- // This ensures the ByteRange is calculated correctly for each signature
582
- allSignatures.forEach((sig) => {
583
- document.startNewRevision()
584
- document.add(sig)
585
- })
586
-
587
- await document.commit()
555
+ await document.finalize()
588
556
 
589
557
  const tmpFolder = `${import.meta.dirname}/tmp`
590
558
  await fs.mkdir(tmpFolder, { recursive: true })
@@ -694,7 +662,7 @@ document.add(catalog)
694
662
  document.trailerDict.set('Root', catalog.reference)
695
663
  document.add(contentStream)
696
664
 
697
- await document.commit()
665
+ await document.finalize()
698
666
  // Save the original PDF
699
667
  const originalPdfPath = `${tmpFolder}/original.pdf`
700
668
  await fs.writeFile(originalPdfPath, document.toBytes())
@@ -706,7 +674,9 @@ console.log('\nStep 2: Loading PDF and performing incremental update...')
706
674
 
707
675
  // Read the existing PDF
708
676
  const existingPdfBytes = await fs.readFile(originalPdfPath)
709
- const loadedDocument = await PdfDocument.fromBytes([existingPdfBytes])
677
+ const loadedDocument = await PdfDocument.fromBytes([existingPdfBytes], {
678
+ incremental: true,
679
+ })
710
680
 
711
681
  // Lock existing revisions to enable incremental mode
712
682
  // This ensures changes are added as new revisions instead of modifying existing ones
@@ -726,7 +696,7 @@ const newContentStream = new PdfIndirectObject({
726
696
 
727
697
  // Add the new content to the document
728
698
  loadedDocument.add(newContentStream)
729
- await loadedDocument.commit()
699
+ await loadedDocument.finalize()
730
700
 
731
701
  // Save the incrementally updated PDF
732
702
  const updatedPdfPath = `${tmpFolder}/incremental-update.pdf`
@@ -768,7 +738,9 @@ console.log(`Original content preserved: ${originalBytesMatch ? 'Yes' : 'No'}`)
768
738
  // Step 4: Add another incremental revision
769
739
  console.log('\nStep 4: Adding another incremental revision...')
770
740
 
771
- const secondUpdate = await PdfDocument.fromBytes([updatedPdfBytes])
741
+ const secondUpdate = await PdfDocument.fromBytes([updatedPdfBytes], {
742
+ incremental: true,
743
+ })
772
744
  secondUpdate.setIncremental(true)
773
745
 
774
746
  const thirdRevisionContent = new PdfIndirectObject({
@@ -780,7 +752,7 @@ const thirdRevisionContent = new PdfIndirectObject({
780
752
  })
781
753
 
782
754
  secondUpdate.add(thirdRevisionContent)
783
- await secondUpdate.commit()
755
+ await secondUpdate.finalize()
784
756
 
785
757
  const multiRevisionPdfPath = `${tmpFolder}/multi-revision.pdf`
786
758
  await fs.writeFile(multiRevisionPdfPath, secondUpdate.toBytes())
@@ -1092,7 +1064,7 @@ document.add(catalog)
1092
1064
  // Set the catalog as the root
1093
1065
  document.trailerDict.set('Root', catalog.reference)
1094
1066
 
1095
- await document.commit()
1067
+ await document.finalize()
1096
1068
 
1097
1069
  // Save the empty form
1098
1070
  // This demonstrates creating a blank form that users can fill in
@@ -1109,7 +1081,7 @@ console.log('Created form-empty.pdf with empty form fields')
1109
1081
  const emptyFormBytes = await fs.readFile(`${tmpFolder}/form-empty.pdf`)
1110
1082
  const filledDocument = await PdfDocument.fromBytes([emptyFormBytes])
1111
1083
 
1112
- const acroform = await filledDocument.acroForm.read()
1084
+ const acroform = filledDocument.acroform
1113
1085
  if (!acroform) {
1114
1086
  throw new Error('No AcroForm found in the document')
1115
1087
  }
@@ -1119,7 +1091,6 @@ acroform.importData({
1119
1091
  phone: '+1 (555) 123-4567',
1120
1092
  subscribe: 'Off', // For checkbox, use the "Yes/Off" value
1121
1093
  })
1122
- await filledDocument.acroForm.write(acroform)
1123
1094
 
1124
1095
  // Save the filled form
1125
1096
  await fs.writeFile(`${tmpFolder}/form-filled.pdf`, filledDocument.toBytes())
@@ -1808,7 +1779,7 @@ allSignatures.forEach((sig) => {
1808
1779
  document.add(sig)
1809
1780
  })
1810
1781
 
1811
- await document.commit()
1782
+ await document.finalize()
1812
1783
 
1813
1784
  const documentBytes = document.toBytes()
1814
1785
  const newDocument = await PdfDocument.fromBytes([documentBytes])
@@ -1885,7 +1856,7 @@ async function main() {
1885
1856
 
1886
1857
  document.add(pages, catalog)
1887
1858
  document.trailerDict.set('Root', catalog.reference)
1888
- await document.commit()
1859
+ await document.finalize()
1889
1860
 
1890
1861
  // Build content stream with different fonts
1891
1862
  // F1=Helvetica-Bold, F2=Times-Roman, F3=Courier, F4=Roboto
@@ -1930,11 +1901,13 @@ async function main() {
1930
1901
  }),
1931
1902
  })
1932
1903
 
1933
- // Embed fonts - FontManager will automatically add them to the /Pages Resources
1934
- const helveticaBold =
1935
- await document.fonts.embedStandardFont('Helvetica-Bold')
1936
- const timesRoman = await document.fonts.embedStandardFont('Times-Roman')
1937
- const courier = await document.fonts.embedStandardFont('Courier')
1904
+ // Create standard fonts and assign resource names to match the content stream
1905
+ const helveticaBold = PdfFont.fromStandardFont('Helvetica-Bold')
1906
+ helveticaBold.resourceName = 'F1'
1907
+ const timesRoman = PdfFont.fromStandardFont('Times-Roman')
1908
+ timesRoman.resourceName = 'F2'
1909
+ const courier = PdfFont.fromStandardFont('Courier')
1910
+ courier.resourceName = 'F3'
1938
1911
 
1939
1912
  console.log(
1940
1913
  `Embedded standard fonts: ${helveticaBold}, ${timesRoman}, ${courier}`,
@@ -1960,6 +1933,7 @@ async function main() {
1960
1933
  // - Creates a ready-to-use PdfFont instance
1961
1934
  // - Throws descriptive errors for unsupported formats (WOFF2, CFF-based OTF)
1962
1935
  const robotoFont = PdfFont.fromBytes(fontData)
1936
+ robotoFont.resourceName = 'F4'
1963
1937
 
1964
1938
  console.log(
1965
1939
  `Created PdfFont from bytes - Font name: ${robotoFont.fontName}`,
@@ -1969,15 +1943,22 @@ async function main() {
1969
1943
  ` Embedded font data preserved: ${robotoFont.fontData ? 'Yes' : 'No'}`,
1970
1944
  )
1971
1945
 
1972
- // Write the font to the document
1973
- // FontManager.write() automatically:
1974
- // - Assigns a resource name (F1, F2, F3, etc.)
1975
- // - Creates the container indirect object
1976
- // - Adds the font to the /Pages node Resources dictionary
1977
- // - All child pages inherit these fonts (even pages added later!)
1978
- await document.fonts.write(robotoFont)
1946
+ // Add all fonts to the document
1947
+ document.add(helveticaBold, timesRoman, courier, robotoFont)
1979
1948
  console.log(`Embedded custom font in PDF: ${robotoFont}`)
1980
1949
 
1950
+ // Build a Font resource dictionary mapping resource names to font references
1951
+ const fontDict = new PdfDictionary()
1952
+ fontDict.set(helveticaBold.resourceName, helveticaBold.reference)
1953
+ fontDict.set(timesRoman.resourceName, timesRoman.reference)
1954
+ fontDict.set(courier.resourceName, courier.reference)
1955
+ fontDict.set(robotoFont.resourceName, robotoFont.reference)
1956
+
1957
+ // Add font resources to the /Pages node so all child pages inherit them
1958
+ const resourcesDict = new PdfDictionary()
1959
+ resourcesDict.set('Font', fontDict)
1960
+ pages.content.set('Resources', resourcesDict)
1961
+
1981
1962
  console.log(`\nFont resource mappings:`)
1982
1963
  console.log(` ${helveticaBold.resourceName} = Helvetica-Bold`)
1983
1964
  console.log(` ${timesRoman.resourceName} = Times-Roman`)
@@ -1992,7 +1973,7 @@ async function main() {
1992
1973
  pages.content.set('Count', new PdfNumber(1))
1993
1974
 
1994
1975
  document.add(contentStream, page)
1995
- await document.commit()
1976
+ await document.finalize()
1996
1977
 
1997
1978
  console.log(`\nFont resource mappings:`)
1998
1979
  console.log(` ${helveticaBold.resourceName} = Helvetica-Bold`)
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  A low-level, minimal-dependency, type-safe PDF library that works in the browser and Node.js.
6
6
 
7
- > **Note**: This library is actively developed and may not support all PDF features yet. However, it is designed to be extensible and can be improved over time.
7
+ > **Note**: This library is actively developed and may not support all PDF features yet. However, it is designed to be extensible and can be improved over time. I would also not expect the API to be stable until at least version 2.0 as a lot of features are still being added and the API is evolving.
8
8
 
9
9
  PRs and issues are welcome!
10
10
 
@@ -9,5 +9,5 @@ export declare class PdfButtonAppearanceStream extends PdfAppearanceStream {
9
9
  height: number;
10
10
  contentStream: string;
11
11
  });
12
- static buildYesContent(width: number, height: number, flags: number | PdfFormFieldFlags): string;
12
+ static buildYesContent(width: number, height: number, flags: number | PdfFormFieldFlags): PdfButtonAppearanceStream;
13
13
  }
@@ -10,7 +10,7 @@ export class PdfButtonAppearanceStream extends PdfAppearanceStream {
10
10
  constructor(ctx) {
11
11
  const resources = new PdfDictionary();
12
12
  const fonts = new PdfDictionary();
13
- fonts.set('ZaDb', PdfFont.ZAPF_DINGBATS);
13
+ fonts.set('ZaDb', PdfFont.ZAPF_DINGBATS.dict.clone());
14
14
  resources.set('Font', fonts);
15
15
  super({
16
16
  width: ctx.width,
@@ -49,6 +49,10 @@ export class PdfButtonAppearanceStream extends PdfAppearanceStream {
49
49
  g.endText();
50
50
  g.restore();
51
51
  }
52
- return g.build();
52
+ return new PdfButtonAppearanceStream({
53
+ width,
54
+ height,
55
+ contentStream: g.build(),
56
+ });
53
57
  }
54
58
  }
@@ -1,8 +1,5 @@
1
1
  import { PdfFormField } from './pdf-form-field.js';
2
2
  import { PdfString } from '../../core/objects/pdf-string.js';
3
- import { PdfObjectReference } from '../../core/objects/pdf-object-reference.js';
4
- import type { PdfAppearanceStream } from '../appearance/pdf-appearance-stream.js';
5
- import type { PdfStream } from '../../core/objects/pdf-stream.js';
6
3
  /**
7
4
  * Button form field subtype (checkboxes, radio buttons, push buttons).
8
5
  */
@@ -11,12 +8,6 @@ export declare class PdfButtonFormField extends PdfFormField {
11
8
  protected _storeValue(val: string | PdfString, fieldParent: PdfFormField | undefined): boolean;
12
9
  get checked(): boolean;
13
10
  set checked(isChecked: boolean);
14
- getAppearanceStream(): PdfStream | undefined;
15
- getAppearanceStreamsForWriting(): {
16
- primary: PdfAppearanceStream;
17
- secondary?: PdfAppearanceStream;
18
- } | undefined;
19
- setAppearanceReference(appearanceStreamRef: PdfObjectReference, appearanceStreamYesRef?: PdfObjectReference): void;
20
11
  generateAppearance(options?: {
21
12
  makeReadOnly?: boolean;
22
13
  }): boolean;
@@ -2,7 +2,6 @@ import { PdfFormField } from './pdf-form-field.js';
2
2
  import { PdfButtonAppearanceStream } from '../appearance/pdf-button-appearance-stream.js';
3
3
  import { PdfName } from '../../core/objects/pdf-name.js';
4
4
  import { PdfString } from '../../core/objects/pdf-string.js';
5
- import { PdfDictionary } from '../../core/objects/pdf-dictionary.js';
6
5
  /**
7
6
  * Button form field subtype (checkboxes, radio buttons, push buttons).
8
7
  */
@@ -41,36 +40,6 @@ export class PdfButtonFormField extends PdfFormField {
41
40
  this.content.set('AS', new PdfName('Off'));
42
41
  }
43
42
  }
44
- getAppearanceStream() {
45
- if (this.checked && this._appearanceStreamYes) {
46
- return this._appearanceStreamYes.content;
47
- }
48
- return this._appearanceStream?.content;
49
- }
50
- getAppearanceStreamsForWriting() {
51
- if (!this._appearanceStream)
52
- return undefined;
53
- return {
54
- primary: this._appearanceStream,
55
- secondary: this._appearanceStreamYes,
56
- };
57
- }
58
- setAppearanceReference(appearanceStreamRef, appearanceStreamYesRef) {
59
- let apDict = this.appearanceStreamDict;
60
- if (!apDict) {
61
- apDict = new PdfDictionary();
62
- this.appearanceStreamDict = apDict;
63
- }
64
- if (appearanceStreamYesRef) {
65
- const stateDict = new PdfDictionary();
66
- stateDict.set('Off', appearanceStreamRef);
67
- stateDict.set('Yes', appearanceStreamYesRef);
68
- apDict.set('N', stateDict);
69
- }
70
- else {
71
- apDict.set('N', appearanceStreamRef);
72
- }
73
- }
74
43
  generateAppearance(options) {
75
44
  const rect = this.rect;
76
45
  if (!rect || rect.length !== 4)
@@ -78,19 +47,18 @@ export class PdfButtonFormField extends PdfFormField {
78
47
  const [x1, y1, x2, y2] = rect;
79
48
  const width = x2 - x1;
80
49
  const height = y2 - y1;
81
- this._appearanceStream = new PdfButtonAppearanceStream({
82
- width,
83
- height,
84
- contentStream: '',
85
- });
86
50
  // Merge own flags with parent flags so inherited bits (e.g. Radio) are
87
51
  // not lost when a child widget has its own Ff entry (even Ff: 0).
88
52
  const effectiveFlags = this.flags.flags | (this.parent?.flags?.flags ?? 0);
89
- const yesContent = PdfButtonAppearanceStream.buildYesContent(width, height, effectiveFlags);
90
- this._appearanceStreamYes = new PdfButtonAppearanceStream({
53
+ const yesAppearance = PdfButtonAppearanceStream.buildYesContent(width, height, effectiveFlags);
54
+ const noAppearance = new PdfButtonAppearanceStream({
91
55
  width,
92
56
  height,
93
- contentStream: yesContent,
57
+ contentStream: '',
58
+ });
59
+ this.setAppearanceStream({
60
+ Yes: yesAppearance,
61
+ Off: noAppearance,
94
62
  });
95
63
  if (options?.makeReadOnly) {
96
64
  this.readOnly = true;
@@ -1,4 +1,5 @@
1
1
  import { PdfFormField } from './pdf-form-field.js';
2
+ import { PdfObjectReference } from '../../core/objects/pdf-object-reference.js';
2
3
  /**
3
4
  * Choice form field subtype (dropdowns, list boxes).
4
5
  */
@@ -11,7 +12,7 @@ export declare class PdfChoiceFormField extends PdfFormField {
11
12
  set options(values: {
12
13
  label: string;
13
14
  value: string;
14
- }[] | string[] | undefined);
15
+ }[] | string[] | PdfObjectReference | undefined);
15
16
  generateAppearance(options?: {
16
17
  makeReadOnly?: boolean;
17
18
  }): boolean;
@@ -1,10 +1,10 @@
1
1
  import { PdfFormField } from './pdf-form-field.js';
2
2
  import { PdfDefaultAppearance } from './pdf-default-appearance.js';
3
3
  import { PdfChoiceAppearanceStream } from '../appearance/pdf-choice-appearance-stream.js';
4
- import { PdfDictionary } from '../../core/objects/pdf-dictionary.js';
5
4
  import { PdfObjectReference } from '../../core/objects/pdf-object-reference.js';
6
5
  import { PdfArray } from '../../core/objects/pdf-array.js';
7
6
  import { PdfString } from '../../core/objects/pdf-string.js';
7
+ import { PdfIndirectObject } from '../../core/index.js';
8
8
  /**
9
9
  * Choice form field subtype (dropdowns, list boxes).
10
10
  */
@@ -16,14 +16,16 @@ export class PdfChoiceFormField extends PdfFormField {
16
16
  return this.options.findIndex((opt) => opt.value === this.value);
17
17
  }
18
18
  get options() {
19
- const opt = this.content
20
- .get('Opt')
21
- ?.as((PdfArray)) ??
22
- this.parent?.content
23
- .get('Opt')
24
- ?.as((PdfArray));
19
+ let opt = this.content.get('Opt') ?? this.parent?.content.get('Opt');
25
20
  if (!opt)
26
21
  return [];
22
+ if (opt instanceof PdfObjectReference) {
23
+ opt = opt
24
+ .resolve()
25
+ .as((PdfIndirectObject)).content;
26
+ }
27
+ if (!(opt instanceof PdfArray))
28
+ return [];
27
29
  return opt.items.map((item) => {
28
30
  if (item instanceof PdfArray && item.items.length >= 2) {
29
31
  const label = item.items[1] instanceof PdfString
@@ -47,6 +49,10 @@ export class PdfChoiceFormField extends PdfFormField {
47
49
  this.content.delete('Opt');
48
50
  return;
49
51
  }
52
+ if (values instanceof PdfObjectReference) {
53
+ this.content.set('Opt', values);
54
+ return;
55
+ }
50
56
  if (values.length === 0) {
51
57
  this.content.delete('Opt');
52
58
  return;
@@ -81,36 +87,11 @@ export class PdfChoiceFormField extends PdfFormField {
81
87
  const parsed = PdfDefaultAppearance.parse(da);
82
88
  if (!parsed)
83
89
  return false;
84
- let fontResources;
85
- const drFontValue = this.form?.defaultResources?.get('Font');
86
- const drFonts = drFontValue instanceof PdfDictionary ? drFontValue : undefined;
87
- const daFontRef = this.form?.fontRefs?.get(parsed.fontName);
88
- if (drFonts || daFontRef) {
89
- // Build a fresh font dict using clean PdfObjectReferences (no
90
- // pre/postTokens inherited from the original parse context).
91
- const fontDict = new PdfDictionary();
92
- if (drFonts) {
93
- for (const [key, val] of drFonts.entries()) {
94
- if (val instanceof PdfObjectReference) {
95
- fontDict.set(key, new PdfObjectReference(val.objectNumber, val.generationNumber));
96
- }
97
- else if (val != null) {
98
- fontDict.set(key, val);
99
- }
100
- }
101
- }
102
- if (daFontRef && !fontDict.has(parsed.fontName)) {
103
- fontDict.set(parsed.fontName, new PdfObjectReference(daFontRef.objectNumber, daFontRef.generationNumber));
104
- }
105
- fontResources = new PdfDictionary();
106
- fontResources.set('Font', fontDict);
107
- }
108
- const isUnicode = this.form?.isFontUnicode(parsed.fontName) ?? false;
109
- const encodingMap = this.form?.fontEncodingMaps?.get(parsed.fontName);
110
- const reverseEncodingMap = encodingMap
111
- ? new Map(Array.from(encodingMap, ([code, char]) => [char, code]))
112
- : undefined;
113
- this._appearanceStream = new PdfChoiceAppearanceStream({
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({
114
95
  rect: rect,
115
96
  value,
116
97
  da: parsed,
@@ -120,7 +101,7 @@ export class PdfChoiceFormField extends PdfFormField {
120
101
  reverseEncodingMap,
121
102
  displayOptions: this.options.map((opt) => opt.label),
122
103
  selectedIndex: this.selectedIndex,
123
- });
104
+ }));
124
105
  if (options?.makeReadOnly) {
125
106
  this.readOnly = true;
126
107
  this.print = true;
@@ -1,34 +1,43 @@
1
+ import { PdfDictionary } from '../../core/objects/pdf-dictionary.js';
2
+ import { PdfArray } from '../../core/objects/pdf-array.js';
1
3
  import { PdfString } from '../../core/objects/pdf-string.js';
2
4
  import { PdfObjectReference } from '../../core/objects/pdf-object-reference.js';
3
5
  import { PdfIndirectObject } from '../../core/objects/pdf-indirect-object.js';
4
6
  import { PdfFont } from '../../fonts/pdf-font.js';
5
7
  import { PdfStream } from '../../core/objects/pdf-stream.js';
6
8
  import { PdfWidgetAnnotation } from '../../annotations/pdf-widget-annotation.js';
7
- import type { PdfAppearanceStream } from '../appearance/pdf-appearance-stream.js';
8
- import type { FormContext, PdfFieldType } from './types.js';
9
+ import type { PdfFieldType } from './types.js';
9
10
  import { PdfFormFieldFlags } from './pdf-form-field-flags.js';
11
+ import { PdfDefaultResourcesDictionary } from '../../annotations/pdf-default-resources.js';
12
+ import type { PdfAcroForm } from '../pdf-acro-form.js';
10
13
  /**
11
14
  * Abstract base form field class. Extends PdfWidgetAnnotation with form-specific properties:
12
15
  * FT, V, DA, Ff, T (name), field hierarchy (parent/children/siblings).
13
16
  * Subclasses must implement generateAppearance().
14
17
  */
15
18
  export declare abstract class PdfFormField extends PdfWidgetAnnotation {
16
- private _parent?;
17
19
  defaultGenerateAppearance: boolean;
18
- protected _appearanceStream?: PdfAppearanceStream;
19
- protected _appearanceStreamYes?: PdfAppearanceStream;
20
- form?: FormContext<PdfFormField>;
21
- constructor(options?: {
22
- other?: PdfIndirectObject;
23
- form?: FormContext<PdfFormField>;
24
- parent?: PdfFormField;
20
+ /** @internal */
21
+ _form?: PdfAcroForm;
22
+ constructor(other?: PdfIndirectObject | {
23
+ form?: PdfAcroForm;
25
24
  });
25
+ set form(f: PdfAcroForm);
26
+ static create(other?: PdfIndirectObject): PdfFormField;
26
27
  get parent(): PdfFormField | undefined;
27
- set parent(field: PdfFormField | undefined);
28
+ set parent(field: PdfFormField | PdfIndirectObject | undefined);
28
29
  get children(): PdfFormField[];
29
30
  set children(fields: PdfFormField[]);
30
31
  get siblings(): PdfFormField[];
31
- get encodingMap(): Map<number, string> | undefined;
32
+ get font(): PdfFont | null;
33
+ get defaultResources(): PdfDefaultResourcesDictionary | null;
34
+ set defaultResources(resources: PdfDefaultResourcesDictionary | null);
35
+ /**
36
+ * Builds a Resources dictionary containing the font entry for `fontName`,
37
+ * resolved from DR (handling indirect references) or from a loaded font.
38
+ * Returns undefined if neither source provides the font.
39
+ */
40
+ buildFontResources(fontName: string): PdfDictionary | undefined;
32
41
  get fieldType(): PdfFieldType | null;
33
42
  set fieldType(type: PdfFieldType | null);
34
43
  get name(): string;
@@ -43,9 +52,6 @@ export declare abstract class PdfFormField extends PdfWidgetAnnotation {
43
52
  * Override in subclasses to change the stored representation.
44
53
  */
45
54
  protected _storeValue(val: string | PdfString, fieldParent: PdfFormField | undefined): boolean;
46
- protected tryGenerateAppearance(field: PdfFormField): void;
47
- get checked(): boolean;
48
- set checked(_isChecked: boolean);
49
55
  get fontSize(): number | null;
50
56
  set fontSize(size: number);
51
57
  get fontName(): string | null;
@@ -92,32 +98,21 @@ export declare abstract class PdfFormField extends PdfWidgetAnnotation {
92
98
  set defaultAppearance(da: string);
93
99
  get maxLen(): number | null;
94
100
  set maxLen(maxLen: number | null);
95
- get kids(): PdfObjectReference[];
101
+ get kids(): PdfArray<PdfObjectReference> | undefined;
96
102
  set kids(kids: PdfObjectReference[]);
97
103
  abstract generateAppearance(options?: {
98
104
  makeReadOnly?: boolean;
99
105
  textYOffset?: number;
100
106
  }): boolean;
101
- getAppearanceStream(): PdfStream | undefined;
102
- getAppearanceStreamsForWriting(): {
103
- primary: PdfAppearanceStream;
104
- secondary?: PdfAppearanceStream;
105
- } | undefined;
106
- setAppearanceReference(appearanceStreamRef: PdfObjectReference, _appearanceStreamYesRef?: PdfObjectReference): void;
107
+ setAppearanceStream(stream: PdfIndirectObject | {
108
+ [key: string]: PdfIndirectObject;
109
+ }): void;
110
+ getAppearanceStream(setting?: string): PdfIndirectObject<PdfStream> | null;
107
111
  private static _fallbackCtor?;
108
112
  private static _registry;
109
- static registerFieldType(ft: 'Sig' | 'Btn' | 'Tx' | 'Ch', ctor: new (options?: {
110
- other?: PdfIndirectObject;
111
- form?: FormContext<PdfFormField>;
112
- parent?: PdfFormField;
113
- }) => PdfFormField, options?: {
113
+ static registerFieldType(ft: 'Sig' | 'Btn' | 'Tx' | 'Ch', ctor: new (other?: PdfIndirectObject) => PdfFormField, options?: {
114
114
  fallback?: boolean;
115
115
  }): void;
116
- static create(options: {
117
- other: PdfIndirectObject;
118
- form: FormContext<PdfFormField>;
119
- parent?: PdfFormField;
120
- }): PdfFormField;
121
116
  }
122
117
  /** Backward compatible alias */
123
118
  export { PdfFormField as PdfAcroFormField };