pdf-lite 1.4.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/EXAMPLES.md +51 -70
- package/README.md +1 -1
- package/dist/acroform/appearance/index.d.ts +4 -4
- package/dist/acroform/appearance/index.js +4 -4
- package/dist/acroform/appearance/{PdfAppearanceStream.d.ts → pdf-appearance-stream.d.ts} +9 -3
- package/dist/acroform/appearance/{PdfAppearanceStream.js → pdf-appearance-stream.js} +14 -5
- package/dist/acroform/appearance/{PdfButtonAppearanceStream.d.ts → pdf-button-appearance-stream.d.ts} +3 -2
- package/dist/acroform/appearance/pdf-button-appearance-stream.js +58 -0
- package/dist/acroform/appearance/pdf-choice-appearance-stream.d.ts +22 -0
- package/dist/acroform/appearance/pdf-choice-appearance-stream.js +75 -0
- package/dist/acroform/appearance/pdf-graphics.d.ts +51 -0
- package/dist/acroform/appearance/pdf-graphics.js +239 -0
- package/dist/acroform/appearance/{PdfTextAppearanceStream.d.ts → pdf-text-appearance-stream.d.ts} +7 -2
- package/dist/acroform/appearance/pdf-text-appearance-stream.js +104 -0
- package/dist/acroform/fields/index.d.ts +7 -7
- package/dist/acroform/fields/index.js +7 -7
- package/dist/acroform/fields/pdf-button-form-field.d.ts +14 -0
- package/dist/acroform/fields/pdf-button-form-field.js +70 -0
- package/dist/acroform/fields/pdf-choice-form-field.d.ts +19 -0
- package/dist/acroform/fields/pdf-choice-form-field.js +112 -0
- package/dist/acroform/fields/{PdfFormFieldFlags.d.ts → pdf-form-field-flags.d.ts} +5 -6
- package/dist/acroform/fields/{PdfFormFieldFlags.js → pdf-form-field-flags.js} +12 -18
- package/dist/acroform/fields/{PdfFormField.d.ts → pdf-form-field.d.ts} +37 -38
- package/dist/acroform/fields/pdf-form-field.js +519 -0
- package/dist/acroform/fields/{PdfSignatureFormField.d.ts → pdf-signature-form-field.d.ts} +1 -1
- package/dist/acroform/fields/{PdfSignatureFormField.js → pdf-signature-form-field.js} +1 -1
- package/dist/acroform/fields/{PdfTextFormField.d.ts → pdf-text-form-field.d.ts} +1 -1
- package/dist/acroform/fields/{PdfTextFormField.js → pdf-text-form-field.js} +11 -13
- package/dist/acroform/fields/types.d.ts +6 -1
- package/dist/acroform/index.d.ts +1 -3
- package/dist/acroform/index.js +1 -3
- package/dist/acroform/pdf-acro-form.d.ts +45 -0
- package/dist/acroform/pdf-acro-form.js +203 -0
- package/dist/acroform/xfa/index.d.ts +3 -3
- package/dist/acroform/xfa/index.js +2 -2
- package/dist/acroform/xfa/{PdfXfaData.d.ts → pdf-xfa-data.d.ts} +4 -3
- package/dist/acroform/xfa/{PdfXfaData.js → pdf-xfa-data.js} +16 -12
- package/dist/acroform/xfa/pdf-xfa-form.d.ts +16 -0
- package/dist/acroform/xfa/pdf-xfa-form.js +34 -0
- package/dist/annotations/index.d.ts +3 -4
- package/dist/annotations/index.js +3 -4
- package/dist/annotations/{PdfAnnotationFlags.d.ts → pdf-annotation-flags.d.ts} +3 -4
- package/dist/annotations/{PdfAnnotationFlags.js → pdf-annotation-flags.js} +5 -6
- package/dist/annotations/{PdfAnnotation.d.ts → pdf-annotation.d.ts} +31 -5
- package/dist/annotations/{PdfAnnotation.js → pdf-annotation.js} +31 -19
- package/dist/annotations/pdf-default-resources.d.ts +11 -0
- package/dist/annotations/pdf-default-resources.js +3 -0
- package/dist/annotations/{PdfWidgetAnnotation.d.ts → pdf-widget-annotation.d.ts} +1 -1
- package/dist/annotations/{PdfWidgetAnnotation.js → pdf-widget-annotation.js} +1 -1
- package/dist/core/decoder.js +1 -1
- package/dist/core/objects/pdf-array.d.ts +8 -1
- package/dist/core/objects/pdf-array.js +31 -0
- package/dist/core/objects/pdf-dictionary.d.ts +2 -0
- package/dist/core/objects/pdf-dictionary.js +14 -7
- package/dist/core/objects/pdf-hexadecimal.d.ts +1 -0
- package/dist/core/objects/pdf-hexadecimal.js +3 -3
- package/dist/core/objects/pdf-indirect-object.d.ts +18 -9
- package/dist/core/objects/pdf-indirect-object.js +75 -16
- package/dist/core/objects/pdf-number.d.ts +1 -0
- package/dist/core/objects/pdf-number.js +5 -4
- package/dist/core/objects/pdf-object-reference.d.ts +8 -1
- package/dist/core/objects/pdf-object-reference.js +14 -0
- package/dist/core/objects/pdf-object.d.ts +14 -0
- package/dist/core/objects/pdf-object.js +36 -0
- package/dist/core/objects/pdf-start-xref.d.ts +1 -0
- package/dist/core/objects/pdf-start-xref.js +4 -0
- package/dist/core/objects/pdf-stream.d.ts +44 -7
- package/dist/core/objects/pdf-stream.js +284 -26
- package/dist/core/objects/pdf-string.d.ts +1 -0
- package/dist/core/objects/pdf-string.js +3 -6
- package/dist/core/objects/pdf-trailer.d.ts +1 -0
- package/dist/core/objects/pdf-trailer.js +6 -3
- package/dist/core/objects/pdf-xref-table.js +1 -1
- package/dist/core/parser/incremental-parser.d.ts +0 -13
- package/dist/core/parser/incremental-parser.js +1 -18
- package/dist/core/ref.d.ts +3 -1
- package/dist/core/ref.js +8 -5
- package/dist/core/streams/object-stream.d.ts +1 -1
- package/dist/core/streams/object-stream.js +1 -1
- package/dist/core/tokens/token.d.ts +2 -1
- package/dist/core/tokens/token.js +3 -0
- package/dist/errors.d.ts +22 -0
- package/dist/errors.js +24 -0
- package/dist/fonts/index.d.ts +0 -1
- package/dist/fonts/index.js +0 -1
- package/dist/fonts/pdf-font.d.ts +94 -32
- package/dist/fonts/pdf-font.js +301 -83
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/pdf/index.d.ts +2 -1
- package/dist/pdf/index.js +2 -1
- package/dist/pdf/pdf-document.d.ts +61 -36
- package/dist/pdf/pdf-document.js +315 -117
- package/dist/pdf/pdf-page.d.ts +50 -0
- package/dist/pdf/pdf-page.js +144 -0
- package/dist/pdf/pdf-pages.d.ts +28 -0
- package/dist/pdf/pdf-pages.js +94 -0
- package/dist/pdf/pdf-reader.d.ts +5 -1
- package/dist/pdf/pdf-reader.js +36 -2
- package/dist/pdf/pdf-revision.d.ts +3 -3
- package/dist/pdf/pdf-revision.js +7 -7
- package/dist/pdf/pdf-xref-lookup.js +34 -14
- package/dist/signing/document-security-store.d.ts +14 -17
- package/dist/signing/document-security-store.js +19 -34
- package/dist/signing/signer.d.ts +23 -8
- package/dist/signing/signer.js +51 -17
- package/dist/utils/encodePdfText.d.ts +17 -0
- package/dist/utils/encodePdfText.js +61 -0
- package/dist/utils/index.d.ts +1 -2
- package/dist/utils/index.js +1 -2
- package/dist/utils/needsCentralWhitespace.d.ts +10 -0
- package/dist/utils/needsCentralWhitespace.js +34 -0
- package/package.json +3 -3
- package/dist/acroform/PdfAcroForm.d.ts +0 -63
- package/dist/acroform/PdfAcroForm.js +0 -279
- package/dist/acroform/PdfFontEncodingCache.d.ts +0 -16
- package/dist/acroform/PdfFontEncodingCache.js +0 -75
- package/dist/acroform/acroform.d.ts +0 -9
- package/dist/acroform/acroform.js +0 -7
- package/dist/acroform/appearance/PdfButtonAppearanceStream.js +0 -54
- package/dist/acroform/appearance/PdfChoiceAppearanceStream.d.ts +0 -15
- package/dist/acroform/appearance/PdfChoiceAppearanceStream.js +0 -48
- package/dist/acroform/appearance/PdfTextAppearanceStream.js +0 -75
- package/dist/acroform/fields/PdfButtonFormField.d.ts +0 -9
- package/dist/acroform/fields/PdfButtonFormField.js +0 -35
- package/dist/acroform/fields/PdfChoiceFormField.d.ts +0 -9
- package/dist/acroform/fields/PdfChoiceFormField.js +0 -46
- package/dist/acroform/fields/PdfFormField.js +0 -499
- package/dist/acroform/manager.d.ts +0 -33
- package/dist/acroform/manager.js +0 -51
- package/dist/acroform/xfa/PdfXfaForm.d.ts +0 -12
- package/dist/acroform/xfa/PdfXfaForm.js +0 -64
- package/dist/annotations/PdfAnnotationWriter.d.ts +0 -20
- package/dist/annotations/PdfAnnotationWriter.js +0 -76
- package/dist/fonts/font-manager.d.ts +0 -127
- package/dist/fonts/font-manager.js +0 -378
- package/dist/pdf/errors.d.ts +0 -6
- package/dist/pdf/errors.js +0 -6
- package/dist/utils/predictors.d.ts +0 -113
- package/dist/utils/predictors.js +0 -279
- /package/dist/acroform/fields/{PdfDefaultAppearance.d.ts → pdf-default-appearance.d.ts} +0 -0
- /package/dist/acroform/fields/{PdfDefaultAppearance.js → pdf-default-appearance.js} +0 -0
- /package/dist/utils/{IterableReadableStream.d.ts → iterable-readable-stream.d.ts} +0 -0
- /package/dist/utils/{IterableReadableStream.js → iterable-readable-stream.js} +0 -0
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
|
-
|
|
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 =
|
|
94
|
-
|
|
95
|
-
page.
|
|
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 =
|
|
100
|
-
|
|
101
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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 =
|
|
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.
|
|
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.
|
|
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
|
-
//
|
|
1934
|
-
const helveticaBold =
|
|
1935
|
-
|
|
1936
|
-
const timesRoman =
|
|
1937
|
-
|
|
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
|
-
//
|
|
1973
|
-
|
|
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.
|
|
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 './
|
|
2
|
-
export { PdfTextAppearanceStream } from './
|
|
3
|
-
export { PdfButtonAppearanceStream } from './
|
|
4
|
-
export { PdfChoiceAppearanceStream } from './
|
|
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 './
|
|
2
|
-
export { PdfTextAppearanceStream } from './
|
|
3
|
-
export { PdfButtonAppearanceStream } from './
|
|
4
|
-
export { PdfChoiceAppearanceStream } from './
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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 './
|
|
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):
|
|
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
|
+
}
|