pdf-lite 1.0.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/.commitlintrc.cjs +25 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
- package/.github/workflows/docs.yaml +93 -0
- package/.github/workflows/prepare-release.yaml +79 -0
- package/.github/workflows/release.yaml +80 -0
- package/.github/workflows/test.yaml +35 -0
- package/.husky/commit-msg +1 -0
- package/.husky/pre-commit +1 -0
- package/.prettierignore +4 -0
- package/.prettierrc +4 -0
- package/CONTRIBUTING.md +109 -0
- package/EXAMPLES.md +1515 -0
- package/LICENSE +21 -0
- package/README.md +285 -0
- package/examples/001-create-pdf.ts +112 -0
- package/examples/002-create-encrypted-pdf.ts +121 -0
- package/examples/003-sign-pdf.ts +347 -0
- package/examples/004-incremental-update.ts +206 -0
- package/examples/005-modify-acroform.ts +374 -0
- package/examples/006-tokeniser-example.ts +131 -0
- package/examples/007-decoder-example.ts +197 -0
- package/package.json +72 -0
- package/packages/pdf-lite/README.md +3 -0
- package/packages/pdf-lite/package.json +68 -0
- package/packages/pdf-lite/scripts/create-encryption-tests.sh +41 -0
- package/packages/pdf-lite/scripts/gen-signing-keys.sh +290 -0
- package/packages/pdf-lite/scripts/generate-all-signing-keys.sh +70 -0
- package/packages/pdf-lite/src/core/decoder.ts +454 -0
- package/packages/pdf-lite/src/core/generators.ts +128 -0
- package/packages/pdf-lite/src/core/incremental-parser.ts +221 -0
- package/packages/pdf-lite/src/core/index.ts +2 -0
- package/packages/pdf-lite/src/core/objects/pdf-array.ts +54 -0
- package/packages/pdf-lite/src/core/objects/pdf-boolean.ts +19 -0
- package/packages/pdf-lite/src/core/objects/pdf-comment.ts +50 -0
- package/packages/pdf-lite/src/core/objects/pdf-date.ts +74 -0
- package/packages/pdf-lite/src/core/objects/pdf-dictionary.ts +171 -0
- package/packages/pdf-lite/src/core/objects/pdf-hexadecimal.ts +54 -0
- package/packages/pdf-lite/src/core/objects/pdf-indirect-object.ts +137 -0
- package/packages/pdf-lite/src/core/objects/pdf-name.ts +19 -0
- package/packages/pdf-lite/src/core/objects/pdf-null.ts +15 -0
- package/packages/pdf-lite/src/core/objects/pdf-number.ts +98 -0
- package/packages/pdf-lite/src/core/objects/pdf-object-reference.ts +30 -0
- package/packages/pdf-lite/src/core/objects/pdf-object.ts +107 -0
- package/packages/pdf-lite/src/core/objects/pdf-start-xref.ts +39 -0
- package/packages/pdf-lite/src/core/objects/pdf-stream.ts +687 -0
- package/packages/pdf-lite/src/core/objects/pdf-string.ts +38 -0
- package/packages/pdf-lite/src/core/objects/pdf-trailer.ts +57 -0
- package/packages/pdf-lite/src/core/objects/pdf-xref-table.ts +264 -0
- package/packages/pdf-lite/src/core/parser.ts +22 -0
- package/packages/pdf-lite/src/core/ref.ts +102 -0
- package/packages/pdf-lite/src/core/serializer.ts +68 -0
- package/packages/pdf-lite/src/core/streams/object-stream.ts +20 -0
- package/packages/pdf-lite/src/core/tokeniser.ts +687 -0
- package/packages/pdf-lite/src/core/tokens/boolean-token.ts +20 -0
- package/packages/pdf-lite/src/core/tokens/byte-offset-token.ts +20 -0
- package/packages/pdf-lite/src/core/tokens/comment-token.ts +32 -0
- package/packages/pdf-lite/src/core/tokens/end-array-token.ts +10 -0
- package/packages/pdf-lite/src/core/tokens/end-dictionary-token.ts +10 -0
- package/packages/pdf-lite/src/core/tokens/end-object-token.ts +10 -0
- package/packages/pdf-lite/src/core/tokens/end-stream-token.ts +11 -0
- package/packages/pdf-lite/src/core/tokens/hexadecimal-token.ts +22 -0
- package/packages/pdf-lite/src/core/tokens/name-token.ts +19 -0
- package/packages/pdf-lite/src/core/tokens/null-token.ts +9 -0
- package/packages/pdf-lite/src/core/tokens/number-token.ts +164 -0
- package/packages/pdf-lite/src/core/tokens/object-reference-token.ts +24 -0
- package/packages/pdf-lite/src/core/tokens/start-array-token.ts +10 -0
- package/packages/pdf-lite/src/core/tokens/start-dictionary-token.ts +10 -0
- package/packages/pdf-lite/src/core/tokens/start-object-token.ts +28 -0
- package/packages/pdf-lite/src/core/tokens/start-stream-token.ts +52 -0
- package/packages/pdf-lite/src/core/tokens/start-xref-token.ts +10 -0
- package/packages/pdf-lite/src/core/tokens/stream-chunk-token.ts +8 -0
- package/packages/pdf-lite/src/core/tokens/string-token.ts +17 -0
- package/packages/pdf-lite/src/core/tokens/token.ts +43 -0
- package/packages/pdf-lite/src/core/tokens/trailer-token.ts +12 -0
- package/packages/pdf-lite/src/core/tokens/whitespace-token.ts +43 -0
- package/packages/pdf-lite/src/core/tokens/xref-table-entry-token.ts +65 -0
- package/packages/pdf-lite/src/core/tokens/xref-table-section-start-token.ts +31 -0
- package/packages/pdf-lite/src/core/tokens/xref-table-start-token.ts +13 -0
- package/packages/pdf-lite/src/crypto/ciphers/aes128.ts +63 -0
- package/packages/pdf-lite/src/crypto/ciphers/aes256.ts +50 -0
- package/packages/pdf-lite/src/crypto/ciphers/rc4.ts +82 -0
- package/packages/pdf-lite/src/crypto/constants.ts +10 -0
- package/packages/pdf-lite/src/crypto/key-derivation/key-derivation-aes256.ts +213 -0
- package/packages/pdf-lite/src/crypto/key-derivation/key-derivation.ts +122 -0
- package/packages/pdf-lite/src/crypto/key-gen/key-gen-aes256.ts +79 -0
- package/packages/pdf-lite/src/crypto/key-gen/key-gen-rc4-128.ts +190 -0
- package/packages/pdf-lite/src/crypto/key-gen/key-gen-rc4-40.ts +129 -0
- package/packages/pdf-lite/src/crypto/types.ts +6 -0
- package/packages/pdf-lite/src/crypto/utils.ts +81 -0
- package/packages/pdf-lite/src/filters/ascii85.ts +128 -0
- package/packages/pdf-lite/src/filters/asciihex.ts +55 -0
- package/packages/pdf-lite/src/filters/flate.ts +39 -0
- package/packages/pdf-lite/src/filters/lzw.ts +144 -0
- package/packages/pdf-lite/src/filters/pass-through.ts +37 -0
- package/packages/pdf-lite/src/filters/runlength.ts +92 -0
- package/packages/pdf-lite/src/filters/types.ts +21 -0
- package/packages/pdf-lite/src/index.ts +4 -0
- package/packages/pdf-lite/src/pdf/errors.ts +5 -0
- package/packages/pdf-lite/src/pdf/index.ts +4 -0
- package/packages/pdf-lite/src/pdf/pdf-document.ts +924 -0
- package/packages/pdf-lite/src/pdf/pdf-reader.ts +57 -0
- package/packages/pdf-lite/src/pdf/pdf-revision.ts +234 -0
- package/packages/pdf-lite/src/pdf/pdf-xref-lookup.ts +527 -0
- package/packages/pdf-lite/src/security/crypt-filters/aesv2.ts +58 -0
- package/packages/pdf-lite/src/security/crypt-filters/aesv3.ts +56 -0
- package/packages/pdf-lite/src/security/crypt-filters/base.ts +140 -0
- package/packages/pdf-lite/src/security/crypt-filters/identity.ts +40 -0
- package/packages/pdf-lite/src/security/crypt-filters/v2.ts +59 -0
- package/packages/pdf-lite/src/security/handlers/base.ts +625 -0
- package/packages/pdf-lite/src/security/handlers/pubSec.ts +413 -0
- package/packages/pdf-lite/src/security/handlers/utils.ts +304 -0
- package/packages/pdf-lite/src/security/handlers/v1.ts +225 -0
- package/packages/pdf-lite/src/security/handlers/v2.ts +128 -0
- package/packages/pdf-lite/src/security/handlers/v4.ts +379 -0
- package/packages/pdf-lite/src/security/handlers/v5.ts +298 -0
- package/packages/pdf-lite/src/security/types.ts +158 -0
- package/packages/pdf-lite/src/signing/document-security-store.ts +224 -0
- package/packages/pdf-lite/src/signing/index.ts +3 -0
- package/packages/pdf-lite/src/signing/signatures/adbe-pkcs7-detached.ts +154 -0
- package/packages/pdf-lite/src/signing/signatures/adbe-pkcs7-sha1.ts +161 -0
- package/packages/pdf-lite/src/signing/signatures/adbe-x509-rsa-sha1.ts +106 -0
- package/packages/pdf-lite/src/signing/signatures/base.ts +229 -0
- package/packages/pdf-lite/src/signing/signatures/etsi-cades-detached.ts +229 -0
- package/packages/pdf-lite/src/signing/signatures/etsi-rfc3161.ts +92 -0
- package/packages/pdf-lite/src/signing/signatures/index.ts +6 -0
- package/packages/pdf-lite/src/signing/signer.ts +120 -0
- package/packages/pdf-lite/src/signing/types.ts +86 -0
- package/packages/pdf-lite/src/signing/utils.ts +71 -0
- package/packages/pdf-lite/src/types.ts +44 -0
- package/packages/pdf-lite/src/utils/IterableReadableStream.ts +30 -0
- package/packages/pdf-lite/src/utils/algos.ts +446 -0
- package/packages/pdf-lite/src/utils/assert.ts +42 -0
- package/packages/pdf-lite/src/utils/bytesToHex.ts +18 -0
- package/packages/pdf-lite/src/utils/bytesToHexBytes.ts +27 -0
- package/packages/pdf-lite/src/utils/bytesToString.ts +17 -0
- package/packages/pdf-lite/src/utils/concatUint8Arrays.ts +26 -0
- package/packages/pdf-lite/src/utils/escapeString.ts +49 -0
- package/packages/pdf-lite/src/utils/hexBytesToBytes.ts +22 -0
- package/packages/pdf-lite/src/utils/hexBytesToString.ts +21 -0
- package/packages/pdf-lite/src/utils/hexToBytes.ts +18 -0
- package/packages/pdf-lite/src/utils/padBytes.ts +25 -0
- package/packages/pdf-lite/src/utils/predictors.ts +332 -0
- package/packages/pdf-lite/src/utils/replaceInBuffer.ts +56 -0
- package/packages/pdf-lite/src/utils/stringToBytes.ts +22 -0
- package/packages/pdf-lite/src/utils/stringToHexBytes.ts +23 -0
- package/packages/pdf-lite/src/utils/unescapeString.ts +123 -0
- package/packages/pdf-lite/test/acceptance/__snapshots__/versions.node.test.ts.snap +60766 -0
- package/packages/pdf-lite/test/acceptance/fixtures/1.3/basic.pdf +0 -0
- package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic-aes-128.pdf +0 -0
- package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic-aes-256.pdf +0 -0
- package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic-rc4-128.pdf +0 -0
- package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic-rc4-40.pdf +0 -0
- package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic.pdf +0 -0
- package/packages/pdf-lite/test/acceptance/fixtures/1.5/basic.pdf +0 -0
- package/packages/pdf-lite/test/acceptance/fixtures/1.6/basic.pdf +0 -0
- package/packages/pdf-lite/test/acceptance/fixtures/1.7/basic.pdf +0 -0
- package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic-aes-128.pdf +43 -0
- package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic-aes-256.pdf +43 -0
- package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic-rc4-128.pdf +43 -0
- package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic-rc4-40.pdf +44 -0
- package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic.pdf +79 -0
- package/packages/pdf-lite/test/acceptance/versions.node.test.ts +41 -0
- package/packages/pdf-lite/test/unit/__snapshots__/decoder.node.test.ts.snap +86947 -0
- package/packages/pdf-lite/test/unit/__snapshots__/tokeniser.node.test.ts.snap +131829 -0
- package/packages/pdf-lite/test/unit/ciphers.test.ts +61 -0
- package/packages/pdf-lite/test/unit/decoder.node.test.ts +21 -0
- package/packages/pdf-lite/test/unit/decoder.test.ts +567 -0
- package/packages/pdf-lite/test/unit/filters.test.ts +67 -0
- package/packages/pdf-lite/test/unit/fixtures/basic.pdf +0 -0
- package/packages/pdf-lite/test/unit/fixtures/encrypted_v1/basic-aes-128.pdf +0 -0
- package/packages/pdf-lite/test/unit/fixtures/encrypted_v1/basic-aes-256.pdf +0 -0
- package/packages/pdf-lite/test/unit/fixtures/encrypted_v1/basic-rc4-128.pdf +0 -0
- package/packages/pdf-lite/test/unit/fixtures/encrypted_v1/basic-rc4-40.pdf +43 -0
- package/packages/pdf-lite/test/unit/fixtures/protectedAdobeLivecycle.pdf +0 -0
- package/packages/pdf-lite/test/unit/fixtures/rsa-2048/index.ts +187 -0
- package/packages/pdf-lite/test/unit/fixtures/template.pdf +0 -0
- package/packages/pdf-lite/test/unit/incremental-update.test.ts +0 -0
- package/packages/pdf-lite/test/unit/objects.test.ts +0 -0
- package/packages/pdf-lite/test/unit/pdf-document-signing.test.ts +0 -0
- package/packages/pdf-lite/test/unit/pdf-revision.test.ts +195 -0
- package/packages/pdf-lite/test/unit/pdf.browser.test.ts +0 -0
- package/packages/pdf-lite/test/unit/predictors.test.ts +226 -0
- package/packages/pdf-lite/test/unit/ref.test.ts +158 -0
- package/packages/pdf-lite/test/unit/security-handlers.test.ts +645 -0
- package/packages/pdf-lite/test/unit/serializer.test.ts +81 -0
- package/packages/pdf-lite/test/unit/signature-objects.test.ts +814 -0
- package/packages/pdf-lite/test/unit/string-escaping.test.ts +84 -0
- package/packages/pdf-lite/test/unit/tokeniser.node.test.ts +38 -0
- package/packages/pdf-lite/test/unit/tokeniser.test.ts +1213 -0
- package/packages/pdf-lite/test/unit/utils.test.ts +248 -0
- package/packages/pdf-lite/test/unit/xref-lookup.test.ts +72 -0
- package/packages/pdf-lite/tsconfig.json +4 -0
- package/packages/pdf-lite/tsconfig.prod.json +8 -0
- package/packages/pdf-lite/typedoc.json +14 -0
- package/packages/pdf-lite/vitest.config.ts +43 -0
- package/pnpm-workspace.yaml +2 -0
- package/renovate.json +34 -0
- package/scripts/build-examples.ts +30 -0
- package/scripts/bump-version.sh +56 -0
- package/scripts/gen-html-docs.sh +21 -0
- package/scripts/gen-md-docs.sh +15 -0
- package/scripts/prepare-release.sh +33 -0
- package/tsconfig.json +22 -0
- package/tsconfig.prod.json +12 -0
- package/typedoc.json +34 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ByteArray } from '../../types'
|
|
2
|
+
import { bytesToString } from '../../utils/bytesToString'
|
|
3
|
+
import { stringToBytes } from '../../utils/stringToBytes'
|
|
4
|
+
import { PdfStringToken } from '../tokens/string-token'
|
|
5
|
+
import { PdfObject } from './pdf-object'
|
|
6
|
+
|
|
7
|
+
export class PdfString extends PdfObject {
|
|
8
|
+
/**
|
|
9
|
+
* The raw bytes of the PDF string.
|
|
10
|
+
*/
|
|
11
|
+
private _raw: ByteArray
|
|
12
|
+
|
|
13
|
+
constructor(raw: ByteArray | string) {
|
|
14
|
+
super()
|
|
15
|
+
this._raw = typeof raw === 'string' ? stringToBytes(raw) : raw
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
get raw(): ByteArray {
|
|
19
|
+
return this._raw
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
set raw(raw: ByteArray) {
|
|
23
|
+
this.setModified()
|
|
24
|
+
this._raw = raw
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get value(): string {
|
|
28
|
+
return bytesToString(this.raw)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
protected tokenize() {
|
|
32
|
+
return [new PdfStringToken(this.raw)]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
clone(): this {
|
|
36
|
+
return new PdfString(new Uint8Array(this.raw)) as this
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Ref } from '../ref'
|
|
2
|
+
import { PdfByteOffsetToken } from '../tokens/byte-offset-token'
|
|
3
|
+
import { PdfToken } from '../tokens/token'
|
|
4
|
+
import { PdfTrailerToken } from '../tokens/trailer-token'
|
|
5
|
+
import { PdfWhitespaceToken } from '../tokens/whitespace-token'
|
|
6
|
+
import { PdfArray } from './pdf-array'
|
|
7
|
+
import { PdfDictionary } from './pdf-dictionary'
|
|
8
|
+
import { PdfHexadecimal } from './pdf-hexadecimal'
|
|
9
|
+
import { PdfNumber } from './pdf-number'
|
|
10
|
+
import { PdfObject } from './pdf-object'
|
|
11
|
+
import { PdfObjectReference } from './pdf-object-reference'
|
|
12
|
+
|
|
13
|
+
export type PdfTrailerEntries = {
|
|
14
|
+
Size: PdfNumber
|
|
15
|
+
Root?: PdfObjectReference
|
|
16
|
+
Info?: PdfObjectReference
|
|
17
|
+
Prev?: PdfNumber
|
|
18
|
+
XRefStm?: PdfNumber
|
|
19
|
+
Encrypt?: PdfObjectReference
|
|
20
|
+
ID?: PdfArray<PdfHexadecimal>
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type PdfTrailerDictionary = PdfDictionary<PdfTrailerEntries>
|
|
24
|
+
|
|
25
|
+
export class PdfTrailer extends PdfObject {
|
|
26
|
+
dict: PdfTrailerDictionary
|
|
27
|
+
offset: Ref<number> = new Ref(0)
|
|
28
|
+
|
|
29
|
+
constructor(entries: PdfTrailerEntries | PdfDictionary<PdfTrailerEntries>) {
|
|
30
|
+
super()
|
|
31
|
+
this.dict =
|
|
32
|
+
entries instanceof PdfDictionary
|
|
33
|
+
? entries
|
|
34
|
+
: new PdfDictionary(entries)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
protected tokenize(): PdfToken[] {
|
|
38
|
+
return [
|
|
39
|
+
new PdfByteOffsetToken(this.offset),
|
|
40
|
+
new PdfTrailerToken(),
|
|
41
|
+
...(this.dict.preTokens ? [] : [PdfWhitespaceToken.NEWLINE]),
|
|
42
|
+
...this.dict.toTokens(),
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
clone(): this {
|
|
47
|
+
return new PdfTrailer(this.dict.clone()) as this
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
isModified(): boolean {
|
|
51
|
+
return (
|
|
52
|
+
super.isModified() ||
|
|
53
|
+
this.dict.isModified() ||
|
|
54
|
+
this.offset.isModified
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { Ref } from '../ref'
|
|
2
|
+
import { PdfByteOffsetToken } from '../tokens/byte-offset-token'
|
|
3
|
+
import { PdfWhitespaceToken } from '../tokens/whitespace-token'
|
|
4
|
+
import { PdfXRefTableEntryToken } from '../tokens/xref-table-entry-token'
|
|
5
|
+
import { PdfXRefTableSectionStartToken } from '../tokens/xref-table-section-start-token'
|
|
6
|
+
import { PdfXRefTableStartToken } from '../tokens/xref-table-start-token'
|
|
7
|
+
import { PdfIndirectObject } from './pdf-indirect-object'
|
|
8
|
+
import { PdfNumber } from './pdf-number'
|
|
9
|
+
import { PdfObject } from './pdf-object'
|
|
10
|
+
|
|
11
|
+
export class PdfXRefTableEntry extends PdfObject {
|
|
12
|
+
objectNumber: PdfNumber
|
|
13
|
+
generationNumber: PdfNumber
|
|
14
|
+
byteOffset: PdfNumber
|
|
15
|
+
inUse: boolean
|
|
16
|
+
|
|
17
|
+
constructor(options: {
|
|
18
|
+
byteOffset: number | PdfNumber | Ref<number>
|
|
19
|
+
objectNumber: number | PdfNumber
|
|
20
|
+
generationNumber: number | PdfNumber
|
|
21
|
+
inUse: boolean
|
|
22
|
+
}) {
|
|
23
|
+
super()
|
|
24
|
+
|
|
25
|
+
this.byteOffset =
|
|
26
|
+
options.byteOffset instanceof PdfNumber
|
|
27
|
+
? options.byteOffset
|
|
28
|
+
: new PdfNumber({
|
|
29
|
+
value: options.byteOffset,
|
|
30
|
+
padTo: 10,
|
|
31
|
+
})
|
|
32
|
+
this.byteOffset.isByteOffset = true
|
|
33
|
+
|
|
34
|
+
this.objectNumber = new PdfNumber(options.objectNumber)
|
|
35
|
+
this.generationNumber =
|
|
36
|
+
options.generationNumber instanceof PdfNumber
|
|
37
|
+
? options.generationNumber
|
|
38
|
+
: new PdfNumber({
|
|
39
|
+
value: options.generationNumber,
|
|
40
|
+
padTo: 5,
|
|
41
|
+
})
|
|
42
|
+
this.inUse = options.inUse
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
protected tokenize() {
|
|
46
|
+
return [
|
|
47
|
+
new PdfXRefTableEntryToken(
|
|
48
|
+
this.byteOffset.toToken(),
|
|
49
|
+
this.generationNumber.toToken(),
|
|
50
|
+
this.objectNumber.toToken(),
|
|
51
|
+
this.inUse,
|
|
52
|
+
),
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
clone(): this {
|
|
57
|
+
return new PdfXRefTableEntry({
|
|
58
|
+
byteOffset: this.byteOffset.clone(),
|
|
59
|
+
objectNumber: this.objectNumber.clone(),
|
|
60
|
+
generationNumber: this.generationNumber.clone(),
|
|
61
|
+
inUse: this.inUse,
|
|
62
|
+
}) as this
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
isModified(): boolean {
|
|
66
|
+
return (
|
|
67
|
+
super.isModified() ||
|
|
68
|
+
this.byteOffset.isModified() ||
|
|
69
|
+
this.objectNumber.isModified() ||
|
|
70
|
+
this.generationNumber.isModified()
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export class PdfXRefTableSectionHeader extends PdfObject {
|
|
76
|
+
startObjectNumber: PdfNumber
|
|
77
|
+
entryCount: PdfNumber
|
|
78
|
+
|
|
79
|
+
constructor(options: {
|
|
80
|
+
startObjectNumber: number | PdfNumber
|
|
81
|
+
entryCount: number | PdfNumber
|
|
82
|
+
}) {
|
|
83
|
+
super()
|
|
84
|
+
this.startObjectNumber = new PdfNumber(options.startObjectNumber)
|
|
85
|
+
this.entryCount = new PdfNumber(options.entryCount)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
protected tokenize() {
|
|
89
|
+
return [
|
|
90
|
+
new PdfXRefTableSectionStartToken(
|
|
91
|
+
this.startObjectNumber.value,
|
|
92
|
+
this.entryCount.value,
|
|
93
|
+
),
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
clone(): this {
|
|
98
|
+
return new PdfXRefTableSectionHeader({
|
|
99
|
+
startObjectNumber: this.startObjectNumber.clone(),
|
|
100
|
+
entryCount: this.entryCount.clone(),
|
|
101
|
+
}) as this
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export class PdfXRefTable extends PdfObject {
|
|
106
|
+
sections: PdfXRefTableSectionHeader[]
|
|
107
|
+
entries: PdfXRefTableEntry[]
|
|
108
|
+
offset: Ref<number> = new Ref(0)
|
|
109
|
+
|
|
110
|
+
constructor(options?: {
|
|
111
|
+
sections?: PdfXRefTableSectionHeader[]
|
|
112
|
+
entries?: PdfXRefTableEntry[]
|
|
113
|
+
offset?: Ref<number> | number
|
|
114
|
+
}) {
|
|
115
|
+
super()
|
|
116
|
+
this.sections = options?.sections ?? []
|
|
117
|
+
this.entries = options?.entries ?? []
|
|
118
|
+
if (options?.offset) {
|
|
119
|
+
this.offset.update(options.offset)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
isModified(): boolean {
|
|
124
|
+
return (
|
|
125
|
+
super.isModified() ||
|
|
126
|
+
this.sections.some((section) => section.isModified()) ||
|
|
127
|
+
this.entries.some((entry) => entry.isModified()) ||
|
|
128
|
+
this.offset.isModified
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
addEntryForObject(obj: PdfIndirectObject): void {
|
|
133
|
+
const foundEntry = this.entries.find(
|
|
134
|
+
(entry) => entry.objectNumber.value === obj.objectNumber,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
if (foundEntry) {
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const newEntry = new PdfXRefTableEntry({
|
|
142
|
+
objectNumber: obj.objectNumber,
|
|
143
|
+
generationNumber: obj.generationNumber,
|
|
144
|
+
byteOffset: obj.offset,
|
|
145
|
+
inUse: true,
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
this.entries.push(newEntry)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
getEntry(objectNumber: number): PdfXRefTableEntry | undefined {
|
|
152
|
+
return this.entries.find(
|
|
153
|
+
(entry) => entry.objectNumber.value === objectNumber,
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
get lastSection(): PdfXRefTableSectionHeader | null {
|
|
158
|
+
if (this.sections.length === 0) {
|
|
159
|
+
return null
|
|
160
|
+
}
|
|
161
|
+
return this.sections[this.sections.length - 1]
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
protected tokenize() {
|
|
165
|
+
const innerTokens = this.sortEntriesIntoSections().flatMap(
|
|
166
|
+
({ section, entries }) => [
|
|
167
|
+
...section.toTokens(),
|
|
168
|
+
...entries.flatMap((entry) => entry.toTokens()),
|
|
169
|
+
],
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
for (let i = 0; i < innerTokens.length; i++) {
|
|
173
|
+
const previousToken = innerTokens[i - 1]
|
|
174
|
+
const currentToken = innerTokens[i]
|
|
175
|
+
|
|
176
|
+
// Only insert newline if previous token is not whitespace AND current token is not whitespace
|
|
177
|
+
const previousIsWhitespace =
|
|
178
|
+
previousToken instanceof PdfWhitespaceToken
|
|
179
|
+
const currentIsWhitespace =
|
|
180
|
+
currentToken instanceof PdfWhitespaceToken
|
|
181
|
+
|
|
182
|
+
if (!previousIsWhitespace && !currentIsWhitespace) {
|
|
183
|
+
innerTokens.splice(i, 0, PdfWhitespaceToken.NEWLINE)
|
|
184
|
+
i++ // Skip the newly inserted token
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return [
|
|
189
|
+
new PdfByteOffsetToken(this.offset),
|
|
190
|
+
new PdfXRefTableStartToken(),
|
|
191
|
+
...innerTokens,
|
|
192
|
+
]
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private sortEntriesIntoSections(): {
|
|
196
|
+
section: PdfXRefTableSectionHeader
|
|
197
|
+
entries: PdfXRefTableEntry[]
|
|
198
|
+
}[] {
|
|
199
|
+
const sections: {
|
|
200
|
+
section: PdfXRefTableSectionHeader
|
|
201
|
+
entries: PdfXRefTableEntry[]
|
|
202
|
+
}[] = []
|
|
203
|
+
|
|
204
|
+
let currentSection: PdfXRefTableSectionHeader | null = null
|
|
205
|
+
let currentEntries: PdfXRefTableEntry[] = []
|
|
206
|
+
|
|
207
|
+
const sortedEntries = this.entries.sort(
|
|
208
|
+
(a, b) => a.objectNumber.value - b.objectNumber.value,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
for (let i = 0; i < sortedEntries.length; i++) {
|
|
212
|
+
const last = i > 0 ? sortedEntries[i - 1] : null
|
|
213
|
+
const entry = sortedEntries[i]
|
|
214
|
+
|
|
215
|
+
if (
|
|
216
|
+
!last ||
|
|
217
|
+
entry.objectNumber.value !== last.objectNumber.value + 1
|
|
218
|
+
) {
|
|
219
|
+
if (currentSection) {
|
|
220
|
+
sections.push({
|
|
221
|
+
section: currentSection,
|
|
222
|
+
entries: currentEntries,
|
|
223
|
+
})
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
currentSection =
|
|
227
|
+
this.sections.find(
|
|
228
|
+
(x) =>
|
|
229
|
+
x.startObjectNumber.value ===
|
|
230
|
+
entry.objectNumber.value,
|
|
231
|
+
) ??
|
|
232
|
+
new PdfXRefTableSectionHeader({
|
|
233
|
+
startObjectNumber: entry.objectNumber,
|
|
234
|
+
entryCount: 1,
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
currentEntries = [entry]
|
|
238
|
+
} else {
|
|
239
|
+
currentEntries.push(entry)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (currentSection) {
|
|
243
|
+
currentSection.entryCount.value = currentEntries.length
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (currentSection) {
|
|
248
|
+
sections.push({
|
|
249
|
+
section: currentSection,
|
|
250
|
+
entries: currentEntries,
|
|
251
|
+
})
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return sections
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
clone(): this {
|
|
258
|
+
return new PdfXRefTable({
|
|
259
|
+
sections: this.sections.map((s) => s.clone()),
|
|
260
|
+
entries: this.entries.map((e) => e.clone()),
|
|
261
|
+
offset: new Ref(this.offset.resolve()),
|
|
262
|
+
}) as this
|
|
263
|
+
}
|
|
264
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abstract base class for parsers that transform input items to output items.
|
|
3
|
+
* Provides a common interface for feeding input and generating output.
|
|
4
|
+
*
|
|
5
|
+
* @typeParam I - The input item type
|
|
6
|
+
* @typeParam O - The output item type
|
|
7
|
+
*/
|
|
8
|
+
export abstract class Parser<I, O> {
|
|
9
|
+
/**
|
|
10
|
+
* Feeds input items to the parser.
|
|
11
|
+
*
|
|
12
|
+
* @param input - Input items to process
|
|
13
|
+
*/
|
|
14
|
+
abstract feed(...input: I[]): void
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Generates output items from the fed input.
|
|
18
|
+
*
|
|
19
|
+
* @returns A generator yielding output items
|
|
20
|
+
*/
|
|
21
|
+
abstract nextItems(): Generator<O>
|
|
22
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Callback function type for ref update notifications.
|
|
3
|
+
*
|
|
4
|
+
* @typeParam T - The value type of the ref
|
|
5
|
+
*/
|
|
6
|
+
export type RefUpdateCallback<T> = (oldValue: T, newValue: T) => void
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A mutable reference wrapper that supports value updates and change notifications.
|
|
10
|
+
* Can hold a direct value or reference another Ref, forming reference chains.
|
|
11
|
+
*
|
|
12
|
+
* @typeParam T - The value type being referenced
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* const ref = new Ref(42)
|
|
17
|
+
* ref.onUpdate((old, newVal) => console.log(`Changed from ${old} to ${newVal}`))
|
|
18
|
+
* ref.update(100) // Logs: Changed from 42 to 100
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export class Ref<T> {
|
|
22
|
+
/** The current value or a reference to another Ref */
|
|
23
|
+
value: T | Ref<T>
|
|
24
|
+
/** Registered callbacks for update notifications */
|
|
25
|
+
callbacks: Array<RefUpdateCallback<T>> = []
|
|
26
|
+
isModified: boolean = false
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Creates a new Ref with an initial value.
|
|
30
|
+
*
|
|
31
|
+
* @param value - The initial value or another Ref to chain
|
|
32
|
+
* @throws Error if attempting to create a self-referencing Ref
|
|
33
|
+
*/
|
|
34
|
+
constructor(value: T | Ref<T>) {
|
|
35
|
+
if (value === this) {
|
|
36
|
+
throw new Error('Cannot create Ref to itself')
|
|
37
|
+
}
|
|
38
|
+
this.value = value
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Updates the reference to a new value or another Ref.
|
|
43
|
+
* Notifies all registered callbacks of the change.
|
|
44
|
+
*
|
|
45
|
+
* @param newValue - The new value or Ref to point to
|
|
46
|
+
*/
|
|
47
|
+
update(newValue: T | Ref<T>) {
|
|
48
|
+
if (newValue === this) {
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const resolvedNewValue =
|
|
53
|
+
newValue instanceof Ref ? newValue.resolve() : newValue
|
|
54
|
+
|
|
55
|
+
const oldValue = this.resolve()
|
|
56
|
+
if (oldValue !== resolvedNewValue) {
|
|
57
|
+
this.isModified = true
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
this.value = newValue
|
|
61
|
+
this.callbacks.forEach((cb) => cb(oldValue, this.resolve()))
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Resolves the reference chain to get the final value.
|
|
66
|
+
*
|
|
67
|
+
* @returns The resolved value of type T
|
|
68
|
+
*/
|
|
69
|
+
resolve(): T {
|
|
70
|
+
if (this.value instanceof Ref) {
|
|
71
|
+
return this.value.resolve()
|
|
72
|
+
}
|
|
73
|
+
return this.value
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Compares this Ref's resolved value with another value or Ref.
|
|
78
|
+
*
|
|
79
|
+
* @param other - The value or Ref to compare against
|
|
80
|
+
* @returns True if the resolved values are equal
|
|
81
|
+
*/
|
|
82
|
+
equals(other?: Ref<T> | T): boolean {
|
|
83
|
+
if (other === undefined) {
|
|
84
|
+
return false
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (other instanceof Ref) {
|
|
88
|
+
return this.resolve() === other.resolve()
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return this.resolve() === other
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Registers a callback to be notified when the value changes.
|
|
96
|
+
*
|
|
97
|
+
* @param callback - The function to call on value updates
|
|
98
|
+
*/
|
|
99
|
+
onUpdate(callback: RefUpdateCallback<T>): void {
|
|
100
|
+
this.callbacks.push(callback)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { ByteArray } from '../types'
|
|
2
|
+
import { Parser } from './parser'
|
|
3
|
+
import { PdfByteOffsetToken } from './tokens/byte-offset-token'
|
|
4
|
+
import { PdfToken } from './tokens/token'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Serializes PDF tokens into a byte stream.
|
|
8
|
+
* Handles byte offset calculation and token serialization.
|
|
9
|
+
*/
|
|
10
|
+
export class PdfTokenSerializer extends Parser<PdfToken, number> {
|
|
11
|
+
/** Current byte offset in the output stream */
|
|
12
|
+
offset: number = 0
|
|
13
|
+
private buffer: PdfToken[] = []
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Feeds tokens into the serializer buffer.
|
|
17
|
+
*
|
|
18
|
+
* @param input - PDF tokens to serialize
|
|
19
|
+
*/
|
|
20
|
+
feed(...input: PdfToken[]): void {
|
|
21
|
+
this.buffer.push(...input)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Generates bytes from the buffered tokens.
|
|
26
|
+
* Updates byte offset tokens as they are encountered.
|
|
27
|
+
*
|
|
28
|
+
* @returns A generator yielding individual bytes
|
|
29
|
+
*/
|
|
30
|
+
*nextItems(): Generator<number> {
|
|
31
|
+
while (this.buffer.length) {
|
|
32
|
+
const obj = this.buffer.shift()!
|
|
33
|
+
|
|
34
|
+
if (obj instanceof PdfByteOffsetToken) {
|
|
35
|
+
obj.update(this.offset)
|
|
36
|
+
continue
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const bytes = obj.toBytes()
|
|
40
|
+
yield* bytes
|
|
41
|
+
this.offset += bytes.length
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Pre-calculates byte offsets for all byte offset tokens in the buffer.
|
|
47
|
+
* Does not consume the buffer.
|
|
48
|
+
*/
|
|
49
|
+
calculateOffsets(): void {
|
|
50
|
+
let currentOffset = 0
|
|
51
|
+
|
|
52
|
+
for (const token of this.buffer) {
|
|
53
|
+
if (token instanceof PdfByteOffsetToken) {
|
|
54
|
+
token.update(currentOffset)
|
|
55
|
+
}
|
|
56
|
+
currentOffset += token.toBytes().length
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Serializes all buffered tokens to a byte array.
|
|
62
|
+
*
|
|
63
|
+
* @returns The serialized PDF as a Uint8Array
|
|
64
|
+
*/
|
|
65
|
+
toBytes(): ByteArray {
|
|
66
|
+
return new Uint8Array(Array.from(this.nextItems()))
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { PdfObject } from '../objects/pdf-object'
|
|
2
|
+
import { ByteArray } from '../../types'
|
|
3
|
+
import { IterableReadableStream } from '../../utils/IterableReadableStream'
|
|
4
|
+
import { pdfDecoderAsync } from '../generators'
|
|
5
|
+
|
|
6
|
+
export class PdfObjectStream extends IterableReadableStream<PdfObject> {
|
|
7
|
+
constructor(input: AsyncIterable<ByteArray> | Iterable<ByteArray>) {
|
|
8
|
+
super({
|
|
9
|
+
async start(controller) {
|
|
10
|
+
const decoder = pdfDecoderAsync(input)
|
|
11
|
+
|
|
12
|
+
for await (const obj of decoder) {
|
|
13
|
+
controller.enqueue(obj)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
controller.close()
|
|
17
|
+
},
|
|
18
|
+
})
|
|
19
|
+
}
|
|
20
|
+
}
|