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,61 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { aes128 } from '../../src/crypto/ciphers/aes128.js'
|
|
3
|
+
import { rc4 } from '../../src/crypto/ciphers/rc4.js'
|
|
4
|
+
import { aes256 } from '../../src/crypto/ciphers/aes256.js'
|
|
5
|
+
import { getRandomBytes } from '../../src/utils/algos.js'
|
|
6
|
+
import { stringToBytes } from '../../src/utils/stringToBytes.js'
|
|
7
|
+
|
|
8
|
+
describe('PDF Ciphers', () => {
|
|
9
|
+
describe('AES-128-CBC', () => {
|
|
10
|
+
it('should encrypt and decrypt data correctly', async () => {
|
|
11
|
+
const data = 'Hello World!'
|
|
12
|
+
const key = Uint8Array.from([
|
|
13
|
+
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
|
14
|
+
])
|
|
15
|
+
const cipher = aes128(key)
|
|
16
|
+
const encrypted = await cipher.encrypt(stringToBytes(data))
|
|
17
|
+
const decrypted = await cipher.decrypt(encrypted)
|
|
18
|
+
const decryptedText = new TextDecoder().decode(decrypted)
|
|
19
|
+
expect(decryptedText).toBe(data)
|
|
20
|
+
})
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
describe('AES-256-CBC', () => {
|
|
24
|
+
it('should encrypt and decrypt data correctly', async () => {
|
|
25
|
+
const data = 'Hello World!'
|
|
26
|
+
// 32 bytes for AES-256
|
|
27
|
+
const key = getRandomBytes(32)
|
|
28
|
+
const cipher = aes256(key)
|
|
29
|
+
const encrypted = await cipher.encrypt(stringToBytes(data))
|
|
30
|
+
const decrypted = await cipher.decrypt(encrypted)
|
|
31
|
+
const decryptedText = new TextDecoder().decode(decrypted)
|
|
32
|
+
expect(decryptedText).toBe(data)
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
describe('RC4-40', () => {
|
|
37
|
+
it('should encrypt and decrypt data correctly', async () => {
|
|
38
|
+
const data = 'Hello World!'
|
|
39
|
+
const key = Uint8Array.from([1, 2, 3, 4, 5])
|
|
40
|
+
const cipher = rc4(key)
|
|
41
|
+
const encrypted = await cipher.encrypt(stringToBytes(data))
|
|
42
|
+
const decrypted = await cipher.decrypt(encrypted)
|
|
43
|
+
const decryptedText = new TextDecoder().decode(decrypted)
|
|
44
|
+
expect(decryptedText).toBe(data)
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
describe('RC4-128', () => {
|
|
49
|
+
it('should encrypt and decrypt data correctly', async () => {
|
|
50
|
+
const data = 'Hello World!'
|
|
51
|
+
const key = Uint8Array.from([
|
|
52
|
+
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
|
53
|
+
])
|
|
54
|
+
const cipher = rc4(key)
|
|
55
|
+
const encrypted = await cipher.encrypt(stringToBytes(data))
|
|
56
|
+
const decrypted = await cipher.decrypt(encrypted)
|
|
57
|
+
const decryptedText = new TextDecoder().decode(decrypted)
|
|
58
|
+
expect(decryptedText).toBe(data)
|
|
59
|
+
})
|
|
60
|
+
})
|
|
61
|
+
})
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { pdfDecoder } from '../../src/core/generators.js'
|
|
3
|
+
import { PdfObject } from '../../src/core/objects/pdf-object.js'
|
|
4
|
+
import { readFileSync } from 'fs'
|
|
5
|
+
|
|
6
|
+
describe('PDF decoder', () => {
|
|
7
|
+
it('should read and write a file, ensuring it is byte-for-byte the same', async () => {
|
|
8
|
+
const pdfBuffer = readFileSync(
|
|
9
|
+
'./test/unit/fixtures/protectedAdobeLivecycle.pdf',
|
|
10
|
+
)
|
|
11
|
+
const output: PdfObject[] = []
|
|
12
|
+
|
|
13
|
+
for (const obj of pdfDecoder([pdfBuffer])) {
|
|
14
|
+
output.push(obj)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const outputString = output.map((obj) => obj.toString()).join('')
|
|
18
|
+
expect(outputString).toBe(pdfBuffer.toString('latin1'))
|
|
19
|
+
expect(output).toMatchSnapshot()
|
|
20
|
+
})
|
|
21
|
+
})
|
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { pdfDecoder } from '../../src/core/generators.js'
|
|
3
|
+
import { stringToBytes } from '../../src/utils/stringToBytes.js'
|
|
4
|
+
import { PdfDictionary } from '../../src/core/objects/pdf-dictionary.js'
|
|
5
|
+
import { PdfNumber } from '../../src/core/objects/pdf-number.js'
|
|
6
|
+
import { PdfArray } from '../../src/core/objects/pdf-array.js'
|
|
7
|
+
import { PdfObject } from '../../src/core/objects/pdf-object.js'
|
|
8
|
+
import { PdfName } from '../../src/core/objects/pdf-name.js'
|
|
9
|
+
import { PdfObjectReference } from '../../src/core/objects/pdf-object-reference.js'
|
|
10
|
+
import { PdfStream } from '../../src/core/objects/pdf-stream.js'
|
|
11
|
+
import { PdfIndirectObject } from '../../src/core/objects/pdf-indirect-object.js'
|
|
12
|
+
import { PdfHexadecimal } from '../../src/core/objects/pdf-hexadecimal.js'
|
|
13
|
+
import { PdfString } from '../../src/core/objects/pdf-string.js'
|
|
14
|
+
import { PdfCommentToken } from '../../src/core/tokens/comment-token.js'
|
|
15
|
+
import { PdfTrailer } from '../../src/core/objects/pdf-trailer.js'
|
|
16
|
+
import {
|
|
17
|
+
PdfXRefTable,
|
|
18
|
+
PdfXRefTableEntry,
|
|
19
|
+
PdfXRefTableSectionHeader,
|
|
20
|
+
} from '../../src/core/objects/pdf-xref-table.js'
|
|
21
|
+
import { PdfComment } from '../../src/core/objects/pdf-comment.js'
|
|
22
|
+
import { PdfStartXRef } from '../../src/core/objects/pdf-start-xref.js'
|
|
23
|
+
import { PdfWhitespaceToken } from '../../src/core/tokens/whitespace-token.js'
|
|
24
|
+
import { ByteArray } from '../../src/types.js'
|
|
25
|
+
|
|
26
|
+
function stringToPdfObject(
|
|
27
|
+
str: string,
|
|
28
|
+
index: number = 0,
|
|
29
|
+
ignoreWhitespace: boolean = true,
|
|
30
|
+
): PdfObject {
|
|
31
|
+
const bytes = stringToBytes(str)
|
|
32
|
+
|
|
33
|
+
// Break it into chunks to test that the tokeniser can handle data in chunks
|
|
34
|
+
const chunks: ByteArray[] = []
|
|
35
|
+
const chunkSize = 10
|
|
36
|
+
|
|
37
|
+
for (let i = 0; i < bytes.length; i += chunkSize) {
|
|
38
|
+
chunks.push(bytes.slice(i, i + chunkSize))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const decoded: PdfObject[] = []
|
|
42
|
+
const decoder = pdfDecoder(chunks, {
|
|
43
|
+
ignoreWhitespace: ignoreWhitespace ?? true,
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
const removeWhitespace = (obj: PdfObject) => {
|
|
47
|
+
obj.setModified(true)
|
|
48
|
+
obj.preTokens = undefined
|
|
49
|
+
obj.postTokens = undefined
|
|
50
|
+
|
|
51
|
+
if (obj instanceof PdfStream) {
|
|
52
|
+
obj.preStreamDataTokens = undefined
|
|
53
|
+
obj.postStreamDataTokens = undefined
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
for (const key of Object.keys(obj)) {
|
|
57
|
+
const value = (obj as any)[key]
|
|
58
|
+
if (value instanceof Map) {
|
|
59
|
+
for (const [key, mapValue] of value) {
|
|
60
|
+
if (key instanceof PdfName) {
|
|
61
|
+
removeWhitespace(key)
|
|
62
|
+
}
|
|
63
|
+
if (mapValue instanceof PdfObject) {
|
|
64
|
+
removeWhitespace(mapValue)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (value instanceof PdfObject) {
|
|
69
|
+
removeWhitespace(value)
|
|
70
|
+
} else if (Array.isArray(value)) {
|
|
71
|
+
for (const item of value) {
|
|
72
|
+
if (item instanceof PdfObject) {
|
|
73
|
+
removeWhitespace(item)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
for (const object of decoder) {
|
|
81
|
+
if (ignoreWhitespace) removeWhitespace(object)
|
|
82
|
+
decoded.push(object)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
decoded[index].setModified(true)
|
|
86
|
+
return decoded[index]
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
describe('PDF decoder', () => {
|
|
90
|
+
it('should decode a PDF dictionary', () => {
|
|
91
|
+
expect(
|
|
92
|
+
stringToPdfObject(
|
|
93
|
+
'<</Linearized 1/L 71443/H[548 187]/O 36/E 2636/N 1/T 71078>>',
|
|
94
|
+
),
|
|
95
|
+
).toMatchObject(
|
|
96
|
+
new PdfDictionary({
|
|
97
|
+
Linearized: new PdfNumber(1),
|
|
98
|
+
L: new PdfNumber(71443),
|
|
99
|
+
H: new PdfArray([new PdfNumber(548), new PdfNumber(187)]),
|
|
100
|
+
O: new PdfNumber(36),
|
|
101
|
+
E: new PdfNumber(2636),
|
|
102
|
+
N: new PdfNumber(1),
|
|
103
|
+
T: new PdfNumber(71078),
|
|
104
|
+
}),
|
|
105
|
+
)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('should decode nested PDF dictionaries', () => {
|
|
109
|
+
expect(
|
|
110
|
+
stringToPdfObject(
|
|
111
|
+
'<</Type /Page/Parent 2 0 R/Resources<</Font<</F1 3 0 R>>>>/MediaBox[0 0 612 792]>>',
|
|
112
|
+
),
|
|
113
|
+
).toMatchObject(
|
|
114
|
+
new PdfDictionary({
|
|
115
|
+
Type: new PdfName('Page'),
|
|
116
|
+
Parent: new PdfObjectReference(2, 0),
|
|
117
|
+
Resources: new PdfDictionary({
|
|
118
|
+
Font: new PdfDictionary({
|
|
119
|
+
F1: new PdfObjectReference(3, 0),
|
|
120
|
+
}),
|
|
121
|
+
}),
|
|
122
|
+
MediaBox: new PdfArray([
|
|
123
|
+
new PdfNumber(0),
|
|
124
|
+
new PdfNumber(0),
|
|
125
|
+
new PdfNumber(612),
|
|
126
|
+
new PdfNumber(792),
|
|
127
|
+
]),
|
|
128
|
+
}),
|
|
129
|
+
)
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('should decode PDF arrays', () => {
|
|
133
|
+
expect(
|
|
134
|
+
stringToPdfObject(
|
|
135
|
+
'<< /Type /Page /MediaBox [0 0 612 792] /Contents 4 0 R >>',
|
|
136
|
+
),
|
|
137
|
+
).toMatchObject(
|
|
138
|
+
new PdfDictionary({
|
|
139
|
+
Type: new PdfName('Page'),
|
|
140
|
+
MediaBox: new PdfArray([
|
|
141
|
+
new PdfNumber(0),
|
|
142
|
+
new PdfNumber(0),
|
|
143
|
+
new PdfNumber(612),
|
|
144
|
+
new PdfNumber(792),
|
|
145
|
+
]),
|
|
146
|
+
Contents: new PdfObjectReference(4, 0),
|
|
147
|
+
}),
|
|
148
|
+
)
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it('should decode a blank PDF dictionary', () => {
|
|
152
|
+
const pdfString = '<< /Test << >> >>'
|
|
153
|
+
expect(stringToPdfObject(pdfString, 0, false).toString()).toEqual(
|
|
154
|
+
pdfString,
|
|
155
|
+
)
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it('should decode PDF streams', () => {
|
|
159
|
+
const stream = stringToPdfObject(
|
|
160
|
+
'<< /Length 10 /Filter /FlateDecode >>stream\n...data...\nendstream',
|
|
161
|
+
).as(PdfStream)
|
|
162
|
+
|
|
163
|
+
expect(stream.header).toMatchObject(
|
|
164
|
+
new PdfDictionary({
|
|
165
|
+
Length: new PdfNumber(11),
|
|
166
|
+
Filter: new PdfName('FlateDecode'),
|
|
167
|
+
}),
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
expect(stream.originalAsString).toBe('...data...\n')
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
it('should decode a PDF object stream', () => {
|
|
174
|
+
const stream = stringToPdfObject(
|
|
175
|
+
[
|
|
176
|
+
'0 0 obj',
|
|
177
|
+
'<</Length 1234 /Filter /FlateDecode>>stream',
|
|
178
|
+
'data',
|
|
179
|
+
'endstream',
|
|
180
|
+
'endobj',
|
|
181
|
+
].join('\n'),
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
expect(stream).toMatchObject(
|
|
185
|
+
new PdfIndirectObject({
|
|
186
|
+
objectNumber: 0,
|
|
187
|
+
generationNumber: 0,
|
|
188
|
+
offset: 0,
|
|
189
|
+
content: new PdfStream({
|
|
190
|
+
header: new PdfDictionary({
|
|
191
|
+
Length: new PdfNumber(1234),
|
|
192
|
+
Filter: new PdfName('FlateDecode'),
|
|
193
|
+
}),
|
|
194
|
+
original: stringToBytes('data\n'),
|
|
195
|
+
}),
|
|
196
|
+
}),
|
|
197
|
+
)
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
it('should decode PDF hexadecimal strings', () => {
|
|
201
|
+
expect(
|
|
202
|
+
stringToPdfObject(
|
|
203
|
+
'<</Value <FEFF00000000000000000000> /ID [<FEFF00000000000000000000><FEFF00000000000000000000>]>>',
|
|
204
|
+
),
|
|
205
|
+
).toEqual(
|
|
206
|
+
new PdfDictionary({
|
|
207
|
+
Value: new PdfHexadecimal('FEFF00000000000000000000', 'hex'),
|
|
208
|
+
ID: new PdfArray([
|
|
209
|
+
new PdfHexadecimal('FEFF00000000000000000000', 'hex'),
|
|
210
|
+
new PdfHexadecimal('FEFF00000000000000000000', 'hex'),
|
|
211
|
+
]),
|
|
212
|
+
}),
|
|
213
|
+
)
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
it('should decode PDF strings', () => {
|
|
217
|
+
expect(stringToPdfObject('<</Value (Hello, World!)>>')).toEqual(
|
|
218
|
+
new PdfDictionary({
|
|
219
|
+
Value: new PdfString('Hello, World!'),
|
|
220
|
+
}),
|
|
221
|
+
)
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
it('should decode PDF names', () => {
|
|
225
|
+
expect(stringToPdfObject('<</Value /Name>>')).toEqual(
|
|
226
|
+
new PdfDictionary({
|
|
227
|
+
Value: new PdfName('Name'),
|
|
228
|
+
}),
|
|
229
|
+
)
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
it('should decode empty PDF arrays', () => {
|
|
233
|
+
const pdfString = '<</Value [ ]>>'
|
|
234
|
+
expect(stringToPdfObject(pdfString, 0, false).toString()).toEqual(
|
|
235
|
+
pdfString,
|
|
236
|
+
)
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
it('should ignore comments in objects but should preserve them in the output', () => {
|
|
240
|
+
const input = '<< /Value /Name % This is a comment>>'
|
|
241
|
+
const object = stringToPdfObject(input, 0, false) as PdfDictionary
|
|
242
|
+
|
|
243
|
+
expect(object.toString()).toBe(input)
|
|
244
|
+
expect(
|
|
245
|
+
object.toTokens().some((token) => token instanceof PdfCommentToken),
|
|
246
|
+
).toBe(true)
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
it('should handle nested structures', () => {
|
|
250
|
+
expect(
|
|
251
|
+
stringToPdfObject('<</Value << /Name /Nested >> /Array [1 2 3]>>'),
|
|
252
|
+
).toEqual(
|
|
253
|
+
new PdfDictionary({
|
|
254
|
+
Value: new PdfDictionary({
|
|
255
|
+
Name: new PdfName('Nested'),
|
|
256
|
+
}),
|
|
257
|
+
Array: new PdfArray([
|
|
258
|
+
new PdfNumber(1),
|
|
259
|
+
new PdfNumber(2),
|
|
260
|
+
new PdfNumber(3),
|
|
261
|
+
]),
|
|
262
|
+
}),
|
|
263
|
+
)
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
it('should identify objects', () => {
|
|
267
|
+
expect(
|
|
268
|
+
stringToPdfObject(
|
|
269
|
+
['3 1 obj', '<</Value /Name /Object 1 0 R>>', 'endobj'].join(
|
|
270
|
+
'\n',
|
|
271
|
+
),
|
|
272
|
+
),
|
|
273
|
+
).toEqual(
|
|
274
|
+
new PdfIndirectObject({
|
|
275
|
+
objectNumber: 3,
|
|
276
|
+
generationNumber: 1,
|
|
277
|
+
offset: 0,
|
|
278
|
+
content: new PdfDictionary({
|
|
279
|
+
Value: new PdfName('Name'),
|
|
280
|
+
Object: new PdfObjectReference(1, 0),
|
|
281
|
+
}),
|
|
282
|
+
}),
|
|
283
|
+
)
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
it('should decode XRef tables', () => {
|
|
287
|
+
expect(
|
|
288
|
+
stringToPdfObject(
|
|
289
|
+
[
|
|
290
|
+
'xref',
|
|
291
|
+
'0 4',
|
|
292
|
+
'0000000000 65535 f',
|
|
293
|
+
'0000000010 00001 n',
|
|
294
|
+
'0000000050 00002 n',
|
|
295
|
+
'0000000100 00003 n',
|
|
296
|
+
].join('\n'),
|
|
297
|
+
),
|
|
298
|
+
).toEqual(
|
|
299
|
+
new PdfXRefTable({
|
|
300
|
+
sections: [
|
|
301
|
+
new PdfXRefTableSectionHeader({
|
|
302
|
+
startObjectNumber: 0,
|
|
303
|
+
entryCount: 4,
|
|
304
|
+
}),
|
|
305
|
+
],
|
|
306
|
+
|
|
307
|
+
entries: [
|
|
308
|
+
new PdfXRefTableEntry({
|
|
309
|
+
objectNumber: 0,
|
|
310
|
+
generationNumber: 65535,
|
|
311
|
+
byteOffset: 0,
|
|
312
|
+
inUse: false,
|
|
313
|
+
}),
|
|
314
|
+
new PdfXRefTableEntry({
|
|
315
|
+
objectNumber: 1,
|
|
316
|
+
generationNumber: 1,
|
|
317
|
+
byteOffset: 10,
|
|
318
|
+
inUse: true,
|
|
319
|
+
}),
|
|
320
|
+
new PdfXRefTableEntry({
|
|
321
|
+
objectNumber: 2,
|
|
322
|
+
generationNumber: 2,
|
|
323
|
+
byteOffset: 50,
|
|
324
|
+
inUse: true,
|
|
325
|
+
}),
|
|
326
|
+
new PdfXRefTableEntry({
|
|
327
|
+
objectNumber: 3,
|
|
328
|
+
generationNumber: 3,
|
|
329
|
+
byteOffset: 100,
|
|
330
|
+
inUse: true,
|
|
331
|
+
}),
|
|
332
|
+
],
|
|
333
|
+
}),
|
|
334
|
+
)
|
|
335
|
+
})
|
|
336
|
+
|
|
337
|
+
it('should be able to identify PDF object references', () => {
|
|
338
|
+
expect(stringToPdfObject('<</Value [3 0 R 4 0 R]>>')).toEqual(
|
|
339
|
+
new PdfDictionary({
|
|
340
|
+
Value: new PdfArray([
|
|
341
|
+
new PdfObjectReference(3, 0),
|
|
342
|
+
new PdfObjectReference(4, 0),
|
|
343
|
+
]),
|
|
344
|
+
}),
|
|
345
|
+
)
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
it('should decode a comment', () => {
|
|
349
|
+
const pdfString = ' This is a comment'
|
|
350
|
+
const object = stringToPdfObject(` %${pdfString}`, 0, false)
|
|
351
|
+
const expected = new PdfComment(pdfString)
|
|
352
|
+
expected.postTokens = []
|
|
353
|
+
expected.preTokens = [
|
|
354
|
+
PdfWhitespaceToken.SPACE,
|
|
355
|
+
PdfWhitespaceToken.SPACE,
|
|
356
|
+
]
|
|
357
|
+
expect(object).toEqual(expected)
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
it('should decode a trailer', () => {
|
|
361
|
+
const expected = new PdfTrailer({
|
|
362
|
+
Size: new PdfNumber(5),
|
|
363
|
+
Root: new PdfObjectReference(1, 0),
|
|
364
|
+
Info: new PdfObjectReference(2, 0),
|
|
365
|
+
})
|
|
366
|
+
expect(
|
|
367
|
+
stringToPdfObject(
|
|
368
|
+
['trailer', '<</Size 5 /Root 1 0 R /Info 2 0 R>>'].join('\n'),
|
|
369
|
+
),
|
|
370
|
+
).toEqual(expected)
|
|
371
|
+
})
|
|
372
|
+
|
|
373
|
+
it('should decode a startxref', () => {
|
|
374
|
+
expect(stringToPdfObject('startxref\n1234')).toEqual(
|
|
375
|
+
new PdfStartXRef(1234),
|
|
376
|
+
)
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
it('should decode an object inside an array', () => {
|
|
380
|
+
const pdfString = `
|
|
381
|
+
<<
|
|
382
|
+
/Type /Sig
|
|
383
|
+
/Filter /Adobe.PPKLite
|
|
384
|
+
/SubFilter /adbe.pkcs7.detached
|
|
385
|
+
/Reference [<<
|
|
386
|
+
/Type /SigRef
|
|
387
|
+
/TransformMethod /UR3
|
|
388
|
+
/TransformParams <<
|
|
389
|
+
/Type /TransformParams
|
|
390
|
+
/V 2.2
|
|
391
|
+
/Document [ /FullSave ]
|
|
392
|
+
/Annots [ /Create /Delete /Modify ]
|
|
393
|
+
>>
|
|
394
|
+
>>]
|
|
395
|
+
>>
|
|
396
|
+
`
|
|
397
|
+
|
|
398
|
+
const object = stringToPdfObject(pdfString, 0, false)
|
|
399
|
+
expect(object.toString()).toBe(pdfString)
|
|
400
|
+
expect(object).toMatchInlineSnapshot(`
|
|
401
|
+
PdfDictionary {
|
|
402
|
+
"innerTokens": [
|
|
403
|
+
PdfWhitespaceToken {
|
|
404
|
+
"rawBytes": Uint8Array [
|
|
405
|
+
10,
|
|
406
|
+
],
|
|
407
|
+
},
|
|
408
|
+
PdfWhitespaceToken {
|
|
409
|
+
"rawBytes": Uint8Array [
|
|
410
|
+
32,
|
|
411
|
+
],
|
|
412
|
+
},
|
|
413
|
+
PdfWhitespaceToken {
|
|
414
|
+
"rawBytes": Uint8Array [
|
|
415
|
+
32,
|
|
416
|
+
],
|
|
417
|
+
},
|
|
418
|
+
],
|
|
419
|
+
"modified": true,
|
|
420
|
+
"postTokens": [
|
|
421
|
+
PdfWhitespaceToken {
|
|
422
|
+
"rawBytes": Uint8Array [
|
|
423
|
+
32,
|
|
424
|
+
],
|
|
425
|
+
},
|
|
426
|
+
PdfWhitespaceToken {
|
|
427
|
+
"rawBytes": Uint8Array [
|
|
428
|
+
32,
|
|
429
|
+
],
|
|
430
|
+
},
|
|
431
|
+
PdfWhitespaceToken {
|
|
432
|
+
"rawBytes": Uint8Array [
|
|
433
|
+
32,
|
|
434
|
+
],
|
|
435
|
+
},
|
|
436
|
+
PdfWhitespaceToken {
|
|
437
|
+
"rawBytes": Uint8Array [
|
|
438
|
+
10,
|
|
439
|
+
],
|
|
440
|
+
},
|
|
441
|
+
],
|
|
442
|
+
"preTokens": [
|
|
443
|
+
PdfWhitespaceToken {
|
|
444
|
+
"rawBytes": Uint8Array [
|
|
445
|
+
10,
|
|
446
|
+
],
|
|
447
|
+
},
|
|
448
|
+
],
|
|
449
|
+
}
|
|
450
|
+
`)
|
|
451
|
+
})
|
|
452
|
+
|
|
453
|
+
it('should handle object references inside an object', () => {
|
|
454
|
+
const pdfString = ['51 0 obj', '[52 0 R]', 'endobj'].join('\n')
|
|
455
|
+
|
|
456
|
+
const object = stringToPdfObject(pdfString)
|
|
457
|
+
expect(object).toEqual(
|
|
458
|
+
new PdfIndirectObject({
|
|
459
|
+
objectNumber: 51,
|
|
460
|
+
generationNumber: 0,
|
|
461
|
+
offset: 0,
|
|
462
|
+
content: new PdfArray([new PdfObjectReference(52, 0)]),
|
|
463
|
+
}),
|
|
464
|
+
)
|
|
465
|
+
})
|
|
466
|
+
|
|
467
|
+
it('should handle numbers inside an object', () => {
|
|
468
|
+
const pdfString = ['51 0 obj', '123', 'endobj'].join('\n')
|
|
469
|
+
|
|
470
|
+
const object = stringToPdfObject(pdfString)
|
|
471
|
+
expect(object).toEqual(
|
|
472
|
+
new PdfIndirectObject({
|
|
473
|
+
objectNumber: 51,
|
|
474
|
+
generationNumber: 0,
|
|
475
|
+
offset: 0,
|
|
476
|
+
content: new PdfNumber(123),
|
|
477
|
+
}),
|
|
478
|
+
)
|
|
479
|
+
})
|
|
480
|
+
|
|
481
|
+
it('should decode a basic PDF', () => {
|
|
482
|
+
const content = [
|
|
483
|
+
'%PDF-2.0',
|
|
484
|
+
'1 0 obj',
|
|
485
|
+
'<< /Type /Catalog /Pages 2 0 R >>',
|
|
486
|
+
'endobj',
|
|
487
|
+
'2 0 obj',
|
|
488
|
+
'<< /Type /Pages /Kids [3 0 R] /Count 1 >>',
|
|
489
|
+
'endobj',
|
|
490
|
+
'3 0 obj',
|
|
491
|
+
'<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Contents 4 0 R /Resources << /Font << /F1 5 0 R >> >> >>',
|
|
492
|
+
'endobj',
|
|
493
|
+
'4 0 obj',
|
|
494
|
+
'<< /Length 44 >>',
|
|
495
|
+
'stream',
|
|
496
|
+
`BT /F1 12 Tf 1 0 0 1 220 700 Tm (Hello, World!) Tj ET`,
|
|
497
|
+
'endstream',
|
|
498
|
+
'endobj',
|
|
499
|
+
'5 0 obj',
|
|
500
|
+
'<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>',
|
|
501
|
+
'endobj',
|
|
502
|
+
'xref',
|
|
503
|
+
'0 6',
|
|
504
|
+
'0000000000 65535 f',
|
|
505
|
+
'0000000010 00000 n',
|
|
506
|
+
'0000000060 00000 n',
|
|
507
|
+
'0000000110 00000 n',
|
|
508
|
+
'0000000170 00000 n',
|
|
509
|
+
'0000000344 00000 n',
|
|
510
|
+
'10 5',
|
|
511
|
+
'0000000591 00000 n',
|
|
512
|
+
'0000000591 00000 n',
|
|
513
|
+
'0000000591 00000 n',
|
|
514
|
+
'0000000591 00000 n',
|
|
515
|
+
'0000000591 00000 n',
|
|
516
|
+
'trailer',
|
|
517
|
+
'<< /Size 5 /Root 1 0 R >>',
|
|
518
|
+
'startxref',
|
|
519
|
+
'414',
|
|
520
|
+
'%%EOF',
|
|
521
|
+
'5 0 obj',
|
|
522
|
+
'<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Override true /Length 11 >>stream ',
|
|
523
|
+
'streamdata',
|
|
524
|
+
'endstream',
|
|
525
|
+
'endobj',
|
|
526
|
+
'xref',
|
|
527
|
+
'5 1',
|
|
528
|
+
'0000000591 00000 n',
|
|
529
|
+
'trailer',
|
|
530
|
+
'<< /Size 5 /Root 1 0 R /Prev 414 >>',
|
|
531
|
+
'startxref',
|
|
532
|
+
'703',
|
|
533
|
+
'%%EOF',
|
|
534
|
+
'',
|
|
535
|
+
].join('\n')
|
|
536
|
+
|
|
537
|
+
const pdfBuffer = stringToBytes(content)
|
|
538
|
+
const decoder = pdfDecoder([pdfBuffer])
|
|
539
|
+
|
|
540
|
+
const output: PdfObject[] = []
|
|
541
|
+
|
|
542
|
+
for (const object of decoder) {
|
|
543
|
+
output.push(object)
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
const outputString = output.map((obj) => obj.toString()).join('')
|
|
547
|
+
expect(output.map((x) => x.objectType).join('\n'))
|
|
548
|
+
.toMatchInlineSnapshot(`
|
|
549
|
+
"PdfComment
|
|
550
|
+
PdfIndirectObject
|
|
551
|
+
PdfIndirectObject
|
|
552
|
+
PdfIndirectObject
|
|
553
|
+
PdfIndirectObject
|
|
554
|
+
PdfIndirectObject
|
|
555
|
+
PdfXRefTable
|
|
556
|
+
PdfTrailer
|
|
557
|
+
PdfStartXRef
|
|
558
|
+
PdfComment
|
|
559
|
+
PdfIndirectObject
|
|
560
|
+
PdfXRefTable
|
|
561
|
+
PdfTrailer
|
|
562
|
+
PdfStartXRef
|
|
563
|
+
PdfComment"
|
|
564
|
+
`)
|
|
565
|
+
expect(outputString).toBe(content)
|
|
566
|
+
})
|
|
567
|
+
})
|