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
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
 
@@ -1,4 +1,4 @@
1
- export { PdfAppearanceStream } from './PdfAppearanceStream.js';
2
- export { PdfTextAppearanceStream } from './PdfTextAppearanceStream.js';
3
- export { PdfButtonAppearanceStream } from './PdfButtonAppearanceStream.js';
4
- export { PdfChoiceAppearanceStream } from './PdfChoiceAppearanceStream.js';
1
+ export { PdfAppearanceStream } from './pdf-appearance-stream.js';
2
+ export { PdfTextAppearanceStream } from './pdf-text-appearance-stream.js';
3
+ export { PdfButtonAppearanceStream } from './pdf-button-appearance-stream.js';
4
+ export { PdfChoiceAppearanceStream } from './pdf-choice-appearance-stream.js';
@@ -1,4 +1,4 @@
1
- export { PdfAppearanceStream } from './PdfAppearanceStream.js';
2
- export { PdfTextAppearanceStream } from './PdfTextAppearanceStream.js';
3
- export { PdfButtonAppearanceStream } from './PdfButtonAppearanceStream.js';
4
- export { PdfChoiceAppearanceStream } from './PdfChoiceAppearanceStream.js';
1
+ export { PdfAppearanceStream } from './pdf-appearance-stream.js';
2
+ export { PdfTextAppearanceStream } from './pdf-text-appearance-stream.js';
3
+ export { PdfButtonAppearanceStream } from './pdf-button-appearance-stream.js';
4
+ export { PdfChoiceAppearanceStream } from './pdf-choice-appearance-stream.js';
@@ -1,15 +1,21 @@
1
1
  import { PdfDictionary } from '../../core/objects/pdf-dictionary.js';
2
2
  import { PdfStream } from '../../core/objects/pdf-stream.js';
3
3
  import { PdfIndirectObject } from '../../core/objects/pdf-indirect-object.js';
4
+ import { PdfGraphics } from './pdf-graphics.js';
4
5
  /**
5
6
  * Base class for PDF appearance streams (Form XObjects).
6
7
  * Wraps a PdfStream as a PdfIndirectObject so it can be added directly to a document.
7
8
  */
8
9
  export declare class PdfAppearanceStream extends PdfIndirectObject<PdfStream> {
9
10
  constructor(options: {
10
- width: number;
11
- height: number;
12
- contentStream: string;
11
+ x?: number;
12
+ y?: number;
13
+ width?: number;
14
+ height?: number;
15
+ contentStream?: string;
13
16
  resources?: PdfDictionary;
14
17
  });
18
+ get contentStream(): string;
19
+ set contentStream(newContent: string);
20
+ set graphics(g: PdfGraphics);
15
21
  }
@@ -15,18 +15,27 @@ export class PdfAppearanceStream extends PdfIndirectObject {
15
15
  appearanceDict.set('Subtype', new PdfName('Form'));
16
16
  appearanceDict.set('FormType', new PdfNumber(1));
17
17
  appearanceDict.set('BBox', new PdfArray([
18
- new PdfNumber(0),
19
- new PdfNumber(0),
20
- new PdfNumber(options.width),
21
- new PdfNumber(options.height),
18
+ new PdfNumber(options.x ?? 0),
19
+ new PdfNumber(options.y ?? 0),
20
+ new PdfNumber(options.width ?? 100),
21
+ new PdfNumber(options.height ?? 100),
22
22
  ]));
23
23
  if (options.resources) {
24
24
  appearanceDict.set('Resources', options.resources);
25
25
  }
26
26
  const stream = new PdfStream({
27
27
  header: appearanceDict,
28
- original: options.contentStream,
28
+ original: options.contentStream ?? '',
29
29
  });
30
30
  super({ content: stream });
31
31
  }
32
+ get contentStream() {
33
+ return this.content.rawAsString;
34
+ }
35
+ set contentStream(newContent) {
36
+ this.content.rawAsString = newContent;
37
+ }
38
+ set graphics(g) {
39
+ this.contentStream = g.build();
40
+ }
32
41
  }
@@ -1,4 +1,5 @@
1
- import { PdfAppearanceStream } from './PdfAppearanceStream.js';
1
+ import { PdfAppearanceStream } from './pdf-appearance-stream.js';
2
+ import { PdfFormFieldFlags } from '../fields/pdf-form-field-flags.js';
2
3
  /**
3
4
  * Appearance stream for button fields (checkboxes, radio buttons).
4
5
  */
@@ -8,5 +9,5 @@ export declare class PdfButtonAppearanceStream extends PdfAppearanceStream {
8
9
  height: number;
9
10
  contentStream: string;
10
11
  });
11
- static buildYesContent(width: number, height: number, flags: number): string;
12
+ static buildYesContent(width: number, height: number, flags: number | PdfFormFieldFlags): PdfButtonAppearanceStream;
12
13
  }
@@ -0,0 +1,58 @@
1
+ import { PdfDictionary } from '../../core/objects/pdf-dictionary.js';
2
+ import { PdfAppearanceStream } from './pdf-appearance-stream.js';
3
+ import { PdfGraphics } from './pdf-graphics.js';
4
+ import { PdfFont } from '../../fonts/pdf-font.js';
5
+ import { PdfFormFieldFlags } from '../fields/pdf-form-field-flags.js';
6
+ /**
7
+ * Appearance stream for button fields (checkboxes, radio buttons).
8
+ */
9
+ export class PdfButtonAppearanceStream extends PdfAppearanceStream {
10
+ constructor(ctx) {
11
+ const resources = new PdfDictionary();
12
+ const fonts = new PdfDictionary();
13
+ fonts.set('ZaDb', PdfFont.ZAPF_DINGBATS.dict.clone());
14
+ resources.set('Font', fonts);
15
+ super({
16
+ width: ctx.width,
17
+ height: ctx.height,
18
+ contentStream: ctx.contentStream,
19
+ resources,
20
+ });
21
+ }
22
+ static buildYesContent(width, height, flags) {
23
+ const size = Math.min(width, height);
24
+ const isRadio = new PdfFormFieldFlags(flags).radio;
25
+ const g = new PdfGraphics();
26
+ if (isRadio) {
27
+ const center = size / 2;
28
+ const radius = size * 0.35;
29
+ const k = 0.5522847498;
30
+ const kRadius = k * radius;
31
+ g.save();
32
+ g.setFillRGB(0, 0, 0);
33
+ g.movePath(center, center + radius);
34
+ g.curveTo(center + kRadius, center + radius, center + radius, center + kRadius, center + radius, center);
35
+ g.curveTo(center + radius, center - kRadius, center + kRadius, center - radius, center, center - radius);
36
+ g.curveTo(center - kRadius, center - radius, center - radius, center - kRadius, center - radius, center);
37
+ g.curveTo(center - radius, center + kRadius, center - kRadius, center + radius, center, center + radius);
38
+ g.fill();
39
+ g.restore();
40
+ }
41
+ else {
42
+ const checkSize = size * 0.8;
43
+ const offset = (size - checkSize) / 2;
44
+ g.save();
45
+ g.beginText();
46
+ g.setFont('ZaDb', checkSize);
47
+ g.moveTo(offset, offset);
48
+ g.showLiteralText('4'); // Checkmark character in Zapf Dingbats
49
+ g.endText();
50
+ g.restore();
51
+ }
52
+ return new PdfButtonAppearanceStream({
53
+ width,
54
+ height,
55
+ contentStream: g.build(),
56
+ });
57
+ }
58
+ }
@@ -0,0 +1,22 @@
1
+ import { PdfDefaultAppearance } from '../fields/pdf-default-appearance.js';
2
+ import { PdfAppearanceStream } from './pdf-appearance-stream.js';
3
+ import type { PdfDictionary } from '../../core/objects/pdf-dictionary.js';
4
+ import type { PdfFont } from '../../fonts/pdf-font.js';
5
+ import { PdfFormFieldFlags } from '../fields/pdf-form-field-flags.js';
6
+ /**
7
+ * Appearance stream for choice fields (dropdowns, list boxes).
8
+ */
9
+ export declare class PdfChoiceAppearanceStream extends PdfAppearanceStream {
10
+ constructor(ctx: {
11
+ rect: [number, number, number, number];
12
+ value: string;
13
+ da: PdfDefaultAppearance;
14
+ flags: number | PdfFormFieldFlags;
15
+ fontResources?: PdfDictionary;
16
+ resolvedFonts?: Map<string, PdfFont>;
17
+ isUnicode?: boolean;
18
+ reverseEncodingMap?: Map<string, number>;
19
+ displayOptions?: string[];
20
+ selectedIndex?: number;
21
+ });
22
+ }
@@ -0,0 +1,75 @@
1
+ import { PdfAppearanceStream } from './pdf-appearance-stream.js';
2
+ import { PdfGraphics } from './pdf-graphics.js';
3
+ import { PdfFormFieldFlags } from '../fields/pdf-form-field-flags.js';
4
+ /**
5
+ * Appearance stream for choice fields (dropdowns, list boxes).
6
+ */
7
+ export class PdfChoiceAppearanceStream extends PdfAppearanceStream {
8
+ constructor(ctx) {
9
+ const [x1, y1, x2, y2] = ctx.rect;
10
+ const width = x2 - x1;
11
+ const height = y2 - y1;
12
+ super({
13
+ width,
14
+ height,
15
+ resources: ctx.fontResources,
16
+ });
17
+ const isUnicode = ctx.isUnicode ?? false;
18
+ const reverseEncodingMap = ctx.reverseEncodingMap;
19
+ const padding = 2;
20
+ const isCombo = new PdfFormFieldFlags(ctx.flags).combo;
21
+ const g = new PdfGraphics({ resolvedFonts: ctx.resolvedFonts });
22
+ g.beginMarkedContent();
23
+ g.save();
24
+ if (!isCombo && ctx.displayOptions && ctx.displayOptions.length > 0) {
25
+ // Listbox: render all items, highlight the selected one
26
+ const lineHeight = ctx.da.fontSize + 4;
27
+ const selectedIndex = ctx.selectedIndex ?? -1;
28
+ for (let i = 0; i < ctx.displayOptions.length; i++) {
29
+ const itemY = height - (i + 1) * lineHeight;
30
+ // Stop rendering if we've gone below the visible area
31
+ if (itemY + lineHeight < 0)
32
+ break;
33
+ if (i === selectedIndex) {
34
+ // Draw highlight background using the PDF re (rectangle) operator
35
+ g.save();
36
+ g.setFillRGB(0.376, 0.62, 0.671);
37
+ g.raw(`0 ${itemY} ${width} ${lineHeight} re`);
38
+ g.fill();
39
+ g.restore();
40
+ }
41
+ const textY = itemY + lineHeight * 0.25;
42
+ g.beginText();
43
+ g.setDefaultAppearance(ctx.da);
44
+ g.moveTo(padding, textY);
45
+ g.showText(ctx.displayOptions[i], isUnicode, reverseEncodingMap);
46
+ g.endText();
47
+ }
48
+ }
49
+ else {
50
+ // Combo (dropdown) or no options: show selected value only
51
+ const textY = (height - ctx.da.fontSize) / 2 + ctx.da.fontSize * 0.2;
52
+ g.beginText();
53
+ g.setDefaultAppearance(ctx.da);
54
+ g.moveTo(padding, textY);
55
+ g.showText(ctx.value, isUnicode, reverseEncodingMap);
56
+ g.endText();
57
+ if (isCombo) {
58
+ const arrowWidth = height * 0.8;
59
+ const arrowX = width - arrowWidth - 2;
60
+ const arrowY = height / 2;
61
+ const arrowSize = height * 0.3;
62
+ g.save();
63
+ g.setFillGray(0.5);
64
+ g.movePath(arrowX + arrowWidth / 2, arrowY - arrowSize / 3);
65
+ g.lineTo(arrowX + arrowWidth / 2 - arrowSize / 2, arrowY + arrowSize / 3);
66
+ g.lineTo(arrowX + arrowWidth / 2 + arrowSize / 2, arrowY + arrowSize / 3);
67
+ g.fill();
68
+ g.restore();
69
+ }
70
+ }
71
+ g.restore();
72
+ g.endMarkedContent();
73
+ this.graphics = g;
74
+ }
75
+ }
@@ -0,0 +1,51 @@
1
+ import type { PdfDefaultAppearance } from '../fields/pdf-default-appearance.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 declare class PdfGraphics {
9
+ private lines;
10
+ private resolvedFonts?;
11
+ private defaultAppearance?;
12
+ constructor(options?: {
13
+ resolvedFonts?: Map<string, PdfFont>;
14
+ defaultAppearance?: PdfDefaultAppearance;
15
+ });
16
+ save(): this;
17
+ restore(): this;
18
+ beginText(): this;
19
+ endText(): this;
20
+ setDefaultAppearance(da: PdfDefaultAppearance): this;
21
+ moveTo(x: number, y: number): this;
22
+ showText(text: string, isUnicode: boolean, reverseEncodingMap?: Map<string, number>): this;
23
+ showLiteralText(text: string): this;
24
+ beginMarkedContent(): this;
25
+ endMarkedContent(): this;
26
+ raw(op: string): this;
27
+ setFillRGB(r: number, g: number, b: number): this;
28
+ setFillGray(v: number): this;
29
+ setFont(name: string, size: number): this;
30
+ movePath(x: number, y: number): this;
31
+ lineTo(x: number, y: number): this;
32
+ curveTo(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number): this;
33
+ fill(): this;
34
+ stroke(): this;
35
+ closePath(): this;
36
+ build(): string;
37
+ private get currentFont();
38
+ /**
39
+ * Calculate the width of text using the current font and size.
40
+ */
41
+ measureTextWidth(text: string, fontSize?: number): number;
42
+ /**
43
+ * Wrap text to fit within the specified width, breaking at word boundaries.
44
+ */
45
+ wrapTextToLines(text: string, maxWidth: number, fontSize?: number): string[];
46
+ /**
47
+ * Calculate the minimum font size needed to fit text within given constraints.
48
+ */
49
+ calculateFittingFontSize(text: string, maxWidth: number, maxHeight?: number, lineHeight?: number): number;
50
+ private breakLongWord;
51
+ }