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.
Files changed (206) hide show
  1. package/.commitlintrc.cjs +25 -0
  2. package/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
  3. package/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
  4. package/.github/workflows/docs.yaml +93 -0
  5. package/.github/workflows/prepare-release.yaml +79 -0
  6. package/.github/workflows/release.yaml +80 -0
  7. package/.github/workflows/test.yaml +35 -0
  8. package/.husky/commit-msg +1 -0
  9. package/.husky/pre-commit +1 -0
  10. package/.prettierignore +4 -0
  11. package/.prettierrc +4 -0
  12. package/CONTRIBUTING.md +109 -0
  13. package/EXAMPLES.md +1515 -0
  14. package/LICENSE +21 -0
  15. package/README.md +285 -0
  16. package/examples/001-create-pdf.ts +112 -0
  17. package/examples/002-create-encrypted-pdf.ts +121 -0
  18. package/examples/003-sign-pdf.ts +347 -0
  19. package/examples/004-incremental-update.ts +206 -0
  20. package/examples/005-modify-acroform.ts +374 -0
  21. package/examples/006-tokeniser-example.ts +131 -0
  22. package/examples/007-decoder-example.ts +197 -0
  23. package/package.json +72 -0
  24. package/packages/pdf-lite/README.md +3 -0
  25. package/packages/pdf-lite/package.json +68 -0
  26. package/packages/pdf-lite/scripts/create-encryption-tests.sh +41 -0
  27. package/packages/pdf-lite/scripts/gen-signing-keys.sh +290 -0
  28. package/packages/pdf-lite/scripts/generate-all-signing-keys.sh +70 -0
  29. package/packages/pdf-lite/src/core/decoder.ts +454 -0
  30. package/packages/pdf-lite/src/core/generators.ts +128 -0
  31. package/packages/pdf-lite/src/core/incremental-parser.ts +221 -0
  32. package/packages/pdf-lite/src/core/index.ts +2 -0
  33. package/packages/pdf-lite/src/core/objects/pdf-array.ts +54 -0
  34. package/packages/pdf-lite/src/core/objects/pdf-boolean.ts +19 -0
  35. package/packages/pdf-lite/src/core/objects/pdf-comment.ts +50 -0
  36. package/packages/pdf-lite/src/core/objects/pdf-date.ts +74 -0
  37. package/packages/pdf-lite/src/core/objects/pdf-dictionary.ts +171 -0
  38. package/packages/pdf-lite/src/core/objects/pdf-hexadecimal.ts +54 -0
  39. package/packages/pdf-lite/src/core/objects/pdf-indirect-object.ts +137 -0
  40. package/packages/pdf-lite/src/core/objects/pdf-name.ts +19 -0
  41. package/packages/pdf-lite/src/core/objects/pdf-null.ts +15 -0
  42. package/packages/pdf-lite/src/core/objects/pdf-number.ts +98 -0
  43. package/packages/pdf-lite/src/core/objects/pdf-object-reference.ts +30 -0
  44. package/packages/pdf-lite/src/core/objects/pdf-object.ts +107 -0
  45. package/packages/pdf-lite/src/core/objects/pdf-start-xref.ts +39 -0
  46. package/packages/pdf-lite/src/core/objects/pdf-stream.ts +687 -0
  47. package/packages/pdf-lite/src/core/objects/pdf-string.ts +38 -0
  48. package/packages/pdf-lite/src/core/objects/pdf-trailer.ts +57 -0
  49. package/packages/pdf-lite/src/core/objects/pdf-xref-table.ts +264 -0
  50. package/packages/pdf-lite/src/core/parser.ts +22 -0
  51. package/packages/pdf-lite/src/core/ref.ts +102 -0
  52. package/packages/pdf-lite/src/core/serializer.ts +68 -0
  53. package/packages/pdf-lite/src/core/streams/object-stream.ts +20 -0
  54. package/packages/pdf-lite/src/core/tokeniser.ts +687 -0
  55. package/packages/pdf-lite/src/core/tokens/boolean-token.ts +20 -0
  56. package/packages/pdf-lite/src/core/tokens/byte-offset-token.ts +20 -0
  57. package/packages/pdf-lite/src/core/tokens/comment-token.ts +32 -0
  58. package/packages/pdf-lite/src/core/tokens/end-array-token.ts +10 -0
  59. package/packages/pdf-lite/src/core/tokens/end-dictionary-token.ts +10 -0
  60. package/packages/pdf-lite/src/core/tokens/end-object-token.ts +10 -0
  61. package/packages/pdf-lite/src/core/tokens/end-stream-token.ts +11 -0
  62. package/packages/pdf-lite/src/core/tokens/hexadecimal-token.ts +22 -0
  63. package/packages/pdf-lite/src/core/tokens/name-token.ts +19 -0
  64. package/packages/pdf-lite/src/core/tokens/null-token.ts +9 -0
  65. package/packages/pdf-lite/src/core/tokens/number-token.ts +164 -0
  66. package/packages/pdf-lite/src/core/tokens/object-reference-token.ts +24 -0
  67. package/packages/pdf-lite/src/core/tokens/start-array-token.ts +10 -0
  68. package/packages/pdf-lite/src/core/tokens/start-dictionary-token.ts +10 -0
  69. package/packages/pdf-lite/src/core/tokens/start-object-token.ts +28 -0
  70. package/packages/pdf-lite/src/core/tokens/start-stream-token.ts +52 -0
  71. package/packages/pdf-lite/src/core/tokens/start-xref-token.ts +10 -0
  72. package/packages/pdf-lite/src/core/tokens/stream-chunk-token.ts +8 -0
  73. package/packages/pdf-lite/src/core/tokens/string-token.ts +17 -0
  74. package/packages/pdf-lite/src/core/tokens/token.ts +43 -0
  75. package/packages/pdf-lite/src/core/tokens/trailer-token.ts +12 -0
  76. package/packages/pdf-lite/src/core/tokens/whitespace-token.ts +43 -0
  77. package/packages/pdf-lite/src/core/tokens/xref-table-entry-token.ts +65 -0
  78. package/packages/pdf-lite/src/core/tokens/xref-table-section-start-token.ts +31 -0
  79. package/packages/pdf-lite/src/core/tokens/xref-table-start-token.ts +13 -0
  80. package/packages/pdf-lite/src/crypto/ciphers/aes128.ts +63 -0
  81. package/packages/pdf-lite/src/crypto/ciphers/aes256.ts +50 -0
  82. package/packages/pdf-lite/src/crypto/ciphers/rc4.ts +82 -0
  83. package/packages/pdf-lite/src/crypto/constants.ts +10 -0
  84. package/packages/pdf-lite/src/crypto/key-derivation/key-derivation-aes256.ts +213 -0
  85. package/packages/pdf-lite/src/crypto/key-derivation/key-derivation.ts +122 -0
  86. package/packages/pdf-lite/src/crypto/key-gen/key-gen-aes256.ts +79 -0
  87. package/packages/pdf-lite/src/crypto/key-gen/key-gen-rc4-128.ts +190 -0
  88. package/packages/pdf-lite/src/crypto/key-gen/key-gen-rc4-40.ts +129 -0
  89. package/packages/pdf-lite/src/crypto/types.ts +6 -0
  90. package/packages/pdf-lite/src/crypto/utils.ts +81 -0
  91. package/packages/pdf-lite/src/filters/ascii85.ts +128 -0
  92. package/packages/pdf-lite/src/filters/asciihex.ts +55 -0
  93. package/packages/pdf-lite/src/filters/flate.ts +39 -0
  94. package/packages/pdf-lite/src/filters/lzw.ts +144 -0
  95. package/packages/pdf-lite/src/filters/pass-through.ts +37 -0
  96. package/packages/pdf-lite/src/filters/runlength.ts +92 -0
  97. package/packages/pdf-lite/src/filters/types.ts +21 -0
  98. package/packages/pdf-lite/src/index.ts +4 -0
  99. package/packages/pdf-lite/src/pdf/errors.ts +5 -0
  100. package/packages/pdf-lite/src/pdf/index.ts +4 -0
  101. package/packages/pdf-lite/src/pdf/pdf-document.ts +924 -0
  102. package/packages/pdf-lite/src/pdf/pdf-reader.ts +57 -0
  103. package/packages/pdf-lite/src/pdf/pdf-revision.ts +234 -0
  104. package/packages/pdf-lite/src/pdf/pdf-xref-lookup.ts +527 -0
  105. package/packages/pdf-lite/src/security/crypt-filters/aesv2.ts +58 -0
  106. package/packages/pdf-lite/src/security/crypt-filters/aesv3.ts +56 -0
  107. package/packages/pdf-lite/src/security/crypt-filters/base.ts +140 -0
  108. package/packages/pdf-lite/src/security/crypt-filters/identity.ts +40 -0
  109. package/packages/pdf-lite/src/security/crypt-filters/v2.ts +59 -0
  110. package/packages/pdf-lite/src/security/handlers/base.ts +625 -0
  111. package/packages/pdf-lite/src/security/handlers/pubSec.ts +413 -0
  112. package/packages/pdf-lite/src/security/handlers/utils.ts +304 -0
  113. package/packages/pdf-lite/src/security/handlers/v1.ts +225 -0
  114. package/packages/pdf-lite/src/security/handlers/v2.ts +128 -0
  115. package/packages/pdf-lite/src/security/handlers/v4.ts +379 -0
  116. package/packages/pdf-lite/src/security/handlers/v5.ts +298 -0
  117. package/packages/pdf-lite/src/security/types.ts +158 -0
  118. package/packages/pdf-lite/src/signing/document-security-store.ts +224 -0
  119. package/packages/pdf-lite/src/signing/index.ts +3 -0
  120. package/packages/pdf-lite/src/signing/signatures/adbe-pkcs7-detached.ts +154 -0
  121. package/packages/pdf-lite/src/signing/signatures/adbe-pkcs7-sha1.ts +161 -0
  122. package/packages/pdf-lite/src/signing/signatures/adbe-x509-rsa-sha1.ts +106 -0
  123. package/packages/pdf-lite/src/signing/signatures/base.ts +229 -0
  124. package/packages/pdf-lite/src/signing/signatures/etsi-cades-detached.ts +229 -0
  125. package/packages/pdf-lite/src/signing/signatures/etsi-rfc3161.ts +92 -0
  126. package/packages/pdf-lite/src/signing/signatures/index.ts +6 -0
  127. package/packages/pdf-lite/src/signing/signer.ts +120 -0
  128. package/packages/pdf-lite/src/signing/types.ts +86 -0
  129. package/packages/pdf-lite/src/signing/utils.ts +71 -0
  130. package/packages/pdf-lite/src/types.ts +44 -0
  131. package/packages/pdf-lite/src/utils/IterableReadableStream.ts +30 -0
  132. package/packages/pdf-lite/src/utils/algos.ts +446 -0
  133. package/packages/pdf-lite/src/utils/assert.ts +42 -0
  134. package/packages/pdf-lite/src/utils/bytesToHex.ts +18 -0
  135. package/packages/pdf-lite/src/utils/bytesToHexBytes.ts +27 -0
  136. package/packages/pdf-lite/src/utils/bytesToString.ts +17 -0
  137. package/packages/pdf-lite/src/utils/concatUint8Arrays.ts +26 -0
  138. package/packages/pdf-lite/src/utils/escapeString.ts +49 -0
  139. package/packages/pdf-lite/src/utils/hexBytesToBytes.ts +22 -0
  140. package/packages/pdf-lite/src/utils/hexBytesToString.ts +21 -0
  141. package/packages/pdf-lite/src/utils/hexToBytes.ts +18 -0
  142. package/packages/pdf-lite/src/utils/padBytes.ts +25 -0
  143. package/packages/pdf-lite/src/utils/predictors.ts +332 -0
  144. package/packages/pdf-lite/src/utils/replaceInBuffer.ts +56 -0
  145. package/packages/pdf-lite/src/utils/stringToBytes.ts +22 -0
  146. package/packages/pdf-lite/src/utils/stringToHexBytes.ts +23 -0
  147. package/packages/pdf-lite/src/utils/unescapeString.ts +123 -0
  148. package/packages/pdf-lite/test/acceptance/__snapshots__/versions.node.test.ts.snap +60766 -0
  149. package/packages/pdf-lite/test/acceptance/fixtures/1.3/basic.pdf +0 -0
  150. package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic-aes-128.pdf +0 -0
  151. package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic-aes-256.pdf +0 -0
  152. package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic-rc4-128.pdf +0 -0
  153. package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic-rc4-40.pdf +0 -0
  154. package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic.pdf +0 -0
  155. package/packages/pdf-lite/test/acceptance/fixtures/1.5/basic.pdf +0 -0
  156. package/packages/pdf-lite/test/acceptance/fixtures/1.6/basic.pdf +0 -0
  157. package/packages/pdf-lite/test/acceptance/fixtures/1.7/basic.pdf +0 -0
  158. package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic-aes-128.pdf +43 -0
  159. package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic-aes-256.pdf +43 -0
  160. package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic-rc4-128.pdf +43 -0
  161. package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic-rc4-40.pdf +44 -0
  162. package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic.pdf +79 -0
  163. package/packages/pdf-lite/test/acceptance/versions.node.test.ts +41 -0
  164. package/packages/pdf-lite/test/unit/__snapshots__/decoder.node.test.ts.snap +86947 -0
  165. package/packages/pdf-lite/test/unit/__snapshots__/tokeniser.node.test.ts.snap +131829 -0
  166. package/packages/pdf-lite/test/unit/ciphers.test.ts +61 -0
  167. package/packages/pdf-lite/test/unit/decoder.node.test.ts +21 -0
  168. package/packages/pdf-lite/test/unit/decoder.test.ts +567 -0
  169. package/packages/pdf-lite/test/unit/filters.test.ts +67 -0
  170. package/packages/pdf-lite/test/unit/fixtures/basic.pdf +0 -0
  171. package/packages/pdf-lite/test/unit/fixtures/encrypted_v1/basic-aes-128.pdf +0 -0
  172. package/packages/pdf-lite/test/unit/fixtures/encrypted_v1/basic-aes-256.pdf +0 -0
  173. package/packages/pdf-lite/test/unit/fixtures/encrypted_v1/basic-rc4-128.pdf +0 -0
  174. package/packages/pdf-lite/test/unit/fixtures/encrypted_v1/basic-rc4-40.pdf +43 -0
  175. package/packages/pdf-lite/test/unit/fixtures/protectedAdobeLivecycle.pdf +0 -0
  176. package/packages/pdf-lite/test/unit/fixtures/rsa-2048/index.ts +187 -0
  177. package/packages/pdf-lite/test/unit/fixtures/template.pdf +0 -0
  178. package/packages/pdf-lite/test/unit/incremental-update.test.ts +0 -0
  179. package/packages/pdf-lite/test/unit/objects.test.ts +0 -0
  180. package/packages/pdf-lite/test/unit/pdf-document-signing.test.ts +0 -0
  181. package/packages/pdf-lite/test/unit/pdf-revision.test.ts +195 -0
  182. package/packages/pdf-lite/test/unit/pdf.browser.test.ts +0 -0
  183. package/packages/pdf-lite/test/unit/predictors.test.ts +226 -0
  184. package/packages/pdf-lite/test/unit/ref.test.ts +158 -0
  185. package/packages/pdf-lite/test/unit/security-handlers.test.ts +645 -0
  186. package/packages/pdf-lite/test/unit/serializer.test.ts +81 -0
  187. package/packages/pdf-lite/test/unit/signature-objects.test.ts +814 -0
  188. package/packages/pdf-lite/test/unit/string-escaping.test.ts +84 -0
  189. package/packages/pdf-lite/test/unit/tokeniser.node.test.ts +38 -0
  190. package/packages/pdf-lite/test/unit/tokeniser.test.ts +1213 -0
  191. package/packages/pdf-lite/test/unit/utils.test.ts +248 -0
  192. package/packages/pdf-lite/test/unit/xref-lookup.test.ts +72 -0
  193. package/packages/pdf-lite/tsconfig.json +4 -0
  194. package/packages/pdf-lite/tsconfig.prod.json +8 -0
  195. package/packages/pdf-lite/typedoc.json +14 -0
  196. package/packages/pdf-lite/vitest.config.ts +43 -0
  197. package/pnpm-workspace.yaml +2 -0
  198. package/renovate.json +34 -0
  199. package/scripts/build-examples.ts +30 -0
  200. package/scripts/bump-version.sh +56 -0
  201. package/scripts/gen-html-docs.sh +21 -0
  202. package/scripts/gen-md-docs.sh +15 -0
  203. package/scripts/prepare-release.sh +33 -0
  204. package/tsconfig.json +22 -0
  205. package/tsconfig.prod.json +12 -0
  206. package/typedoc.json +34 -0
@@ -0,0 +1,20 @@
1
+ import { Ref } from '../ref'
2
+ import { PdfToken } from './token'
3
+
4
+ export class PdfByteOffsetToken extends PdfToken {
5
+ value: Ref<number>
6
+
7
+ constructor(value: Ref<number> = new Ref(0)) {
8
+ super(new Uint8Array())
9
+
10
+ this.value = value
11
+ }
12
+
13
+ resolve(): number {
14
+ return this.value.resolve()
15
+ }
16
+
17
+ update(offset: number | Ref<number>): void {
18
+ this.value.update(offset)
19
+ }
20
+ }
@@ -0,0 +1,32 @@
1
+ import { stringToBytes } from '../../utils/stringToBytes'
2
+ import { concatUint8Arrays } from '../../utils/concatUint8Arrays'
3
+ import { PdfToken } from './token'
4
+ import { ByteArray } from '../../types'
5
+ import { bytesToString } from '../../utils/bytesToString'
6
+
7
+ export class PdfCommentToken extends PdfToken {
8
+ static EOF = new PdfCommentToken('%EOF')
9
+ comment: ByteArray
10
+
11
+ constructor(comment: ByteArray | string) {
12
+ super(PdfCommentToken.toBytes(comment))
13
+ this.comment =
14
+ typeof comment === 'string' ? stringToBytes(comment) : comment
15
+ }
16
+
17
+ get innerComment(): string {
18
+ return bytesToString(this.comment)
19
+ }
20
+
21
+ private static toBytes(comment: ByteArray | string): ByteArray {
22
+ const tokenBytes =
23
+ typeof comment === 'string' ? stringToBytes(comment) : comment
24
+ return concatUint8Arrays(stringToBytes('%'), tokenBytes)
25
+ }
26
+
27
+ static isEofCommentToken(token: PdfToken): boolean {
28
+ if (!(token instanceof PdfCommentToken)) return false
29
+ const tokenStr = token.innerComment.trim()
30
+ return tokenStr.toLowerCase() === '%eof'
31
+ }
32
+ }
@@ -0,0 +1,10 @@
1
+ import { stringToBytes } from '../../utils/stringToBytes'
2
+ import { PdfToken } from './token'
3
+
4
+ const END_ARRAY_BYTES = stringToBytes(']')
5
+
6
+ export class PdfEndArrayToken extends PdfToken {
7
+ constructor() {
8
+ super(END_ARRAY_BYTES)
9
+ }
10
+ }
@@ -0,0 +1,10 @@
1
+ import { stringToBytes } from '../../utils/stringToBytes'
2
+ import { PdfToken } from './token'
3
+
4
+ const END_DICTIONARY_BYTES = stringToBytes('>>')
5
+
6
+ export class PdfEndDictionaryToken extends PdfToken {
7
+ constructor() {
8
+ super(END_DICTIONARY_BYTES)
9
+ }
10
+ }
@@ -0,0 +1,10 @@
1
+ import { stringToBytes } from '../../utils/stringToBytes'
2
+ import { PdfToken } from './token'
3
+
4
+ const END_OBJECT_BYTES = stringToBytes('endobj')
5
+
6
+ export class PdfEndObjectToken extends PdfToken {
7
+ constructor() {
8
+ super(END_OBJECT_BYTES)
9
+ }
10
+ }
@@ -0,0 +1,11 @@
1
+ import { ByteArray } from '../../types'
2
+ import { stringToBytes } from '../../utils/stringToBytes'
3
+ import { PdfToken } from './token'
4
+
5
+ const END_STREAM_BYTES = stringToBytes('endstream')
6
+
7
+ export class PdfEndStreamToken extends PdfToken {
8
+ constructor(bytes?: ByteArray) {
9
+ super(bytes ?? END_STREAM_BYTES)
10
+ }
11
+ }
@@ -0,0 +1,22 @@
1
+ import { ByteArray } from '../../types'
2
+ import { stringToBytes } from '../../utils/stringToBytes'
3
+ import { PdfToken } from './token'
4
+
5
+ export class PdfHexadecimalToken extends PdfToken {
6
+ raw: ByteArray
7
+
8
+ constructor(hexadecimal: string | ByteArray) {
9
+ super(PdfHexadecimalToken.toBytes(hexadecimal))
10
+
11
+ this.raw =
12
+ typeof hexadecimal === 'string'
13
+ ? stringToBytes(hexadecimal)
14
+ : hexadecimal
15
+ }
16
+
17
+ private static toBytes(hexadecimal: string | ByteArray): ByteArray {
18
+ const bytes = stringToBytes(hexadecimal)
19
+
20
+ return new Uint8Array([0x3c, ...bytes, 0x3e])
21
+ }
22
+ }
@@ -0,0 +1,19 @@
1
+ import { ByteArray } from '../../types'
2
+ import { stringToBytes } from '../../utils/stringToBytes'
3
+ import { PdfToken } from './token'
4
+
5
+ export class PdfNameToken extends PdfToken {
6
+ name: string
7
+
8
+ constructor(name: string) {
9
+ if (typeof name !== 'string' || name.length === 0) {
10
+ throw new Error('PdfNameToken name must be a non-empty string')
11
+ }
12
+ super(PdfNameToken.toBytes(name))
13
+ this.name = name
14
+ }
15
+
16
+ private static toBytes(name: string): ByteArray {
17
+ return stringToBytes(`/${name}`)
18
+ }
19
+ }
@@ -0,0 +1,9 @@
1
+ import { stringToBytes } from '../../utils/stringToBytes'
2
+ import { PdfToken } from './token'
3
+
4
+ const NULL = stringToBytes('null')
5
+ export class PdfNullToken extends PdfToken {
6
+ constructor() {
7
+ super(NULL)
8
+ }
9
+ }
@@ -0,0 +1,164 @@
1
+ import { ByteArray } from '../../types'
2
+ import { stringToBytes } from '../../utils/stringToBytes'
3
+ import { Ref } from '../ref'
4
+ import { PdfToken } from './token'
5
+
6
+ export class PdfNumberToken extends PdfToken {
7
+ #value: Ref<number>
8
+ padTo: number
9
+ decimalPlaces: number
10
+ isByteToken: boolean = false
11
+
12
+ constructor(
13
+ options:
14
+ | {
15
+ value: number | ByteArray | PdfNumberToken | Ref<number>
16
+ padTo?: number
17
+ decimalPlaces?: number
18
+ isByteToken?: boolean
19
+ }
20
+ | number
21
+ | ByteArray,
22
+ padTo?: number,
23
+ decimalPlaces?: number,
24
+ ) {
25
+ super()
26
+
27
+ if (
28
+ typeof options === 'number' ||
29
+ options instanceof Uint8Array ||
30
+ options instanceof Ref ||
31
+ options instanceof PdfNumberToken
32
+ ) {
33
+ this.#value = PdfNumberToken.getValue(options)
34
+ this.padTo = padTo ?? 0
35
+ this.decimalPlaces = decimalPlaces ?? 0
36
+ this.isByteToken = false
37
+ return
38
+ }
39
+
40
+ this.#value = PdfNumberToken.getValue(options.value)
41
+ this.padTo = options.padTo ?? 0
42
+
43
+ this.decimalPlaces =
44
+ options.decimalPlaces ??
45
+ PdfNumberToken.getDecimalPlaces(options.value)
46
+ this.isByteToken = options.isByteToken ?? false
47
+ }
48
+
49
+ toBytes(): ByteArray {
50
+ return PdfNumberToken.toBytes(
51
+ this.#value,
52
+ this.padTo,
53
+ this.decimalPlaces,
54
+ )
55
+ }
56
+
57
+ get ref(): Ref<number> {
58
+ return this.#value
59
+ }
60
+
61
+ set ref(newRef: Ref<number>) {
62
+ this.#value = newRef
63
+ }
64
+
65
+ get value(): number {
66
+ return this.#value.resolve()
67
+ }
68
+
69
+ set value(newValue: number) {
70
+ this.#value.update(newValue)
71
+ }
72
+
73
+ static getValue(
74
+ bytes: ByteArray | PdfNumberToken | number | Ref<number>,
75
+ ): Ref<number> {
76
+ if (bytes instanceof Ref) {
77
+ return bytes
78
+ }
79
+
80
+ if (bytes instanceof PdfNumberToken) {
81
+ return bytes.ref
82
+ }
83
+
84
+ if (typeof bytes === 'number') {
85
+ return new Ref(bytes)
86
+ }
87
+
88
+ const str = new TextDecoder().decode(bytes)
89
+ return new Ref(parseFloat(str))
90
+ }
91
+
92
+ static getPadding(
93
+ bytes: ByteArray | PdfNumberToken | number | Ref<number>,
94
+ ): number {
95
+ if (bytes instanceof PdfNumberToken) {
96
+ return bytes.padTo
97
+ }
98
+
99
+ if (bytes instanceof Ref) {
100
+ bytes = PdfNumberToken.toBytes(bytes.resolve())
101
+ }
102
+
103
+ if (typeof bytes === 'number') {
104
+ bytes = PdfNumberToken.toBytes(bytes)
105
+ }
106
+
107
+ let padding = 0
108
+ while (bytes.length && bytes[0] === 0x30) {
109
+ bytes = bytes.slice(1)
110
+ padding++
111
+ }
112
+
113
+ return padding
114
+ }
115
+
116
+ static getDecimalPlaces(
117
+ bytes: ByteArray | PdfNumberToken | number | Ref<number>,
118
+ ): number {
119
+ if (bytes instanceof PdfNumberToken) {
120
+ return bytes.decimalPlaces
121
+ }
122
+
123
+ if (bytes instanceof Ref) {
124
+ bytes = PdfNumberToken.toBytes(bytes.resolve())
125
+ }
126
+
127
+ if (typeof bytes === 'number') {
128
+ bytes = PdfNumberToken.toBytes(bytes)
129
+ }
130
+
131
+ const str = new TextDecoder().decode(bytes)
132
+ const match = str.match(/\.(\d+)/)
133
+
134
+ if (match) {
135
+ return match[1].length
136
+ }
137
+
138
+ return 0
139
+ }
140
+
141
+ static toBytes(
142
+ value: number | PdfNumberToken | Ref<number> | ByteArray,
143
+ padTo?: number,
144
+ decimalPlaces?: number,
145
+ ): ByteArray {
146
+ if (value instanceof Uint8Array) {
147
+ return value
148
+ }
149
+
150
+ if (value instanceof PdfNumberToken) {
151
+ return value.toBytes()
152
+ }
153
+
154
+ const numberValue = value instanceof Ref ? value.resolve() : value
155
+
156
+ const valueString = decimalPlaces
157
+ ? numberValue.toFixed(decimalPlaces)
158
+ : numberValue.toString()
159
+
160
+ const tokenString = valueString.padStart(padTo ?? 0, '0')
161
+
162
+ return stringToBytes(tokenString)
163
+ }
164
+ }
@@ -0,0 +1,24 @@
1
+ import { ByteArray } from '../../types'
2
+ import { stringToBytes } from '../../utils/stringToBytes'
3
+ import { PdfToken } from './token'
4
+
5
+ export class PdfObjectReferenceToken extends PdfToken {
6
+ objectNumber: number
7
+ generationNumber: number
8
+
9
+ constructor(objectNumber: number, generationNumber: number) {
10
+ super(PdfObjectReferenceToken.toBytes(objectNumber, generationNumber))
11
+ this.objectNumber = objectNumber
12
+ this.generationNumber = generationNumber
13
+ }
14
+
15
+ private static toBytes(
16
+ objectNumber: number,
17
+ generationNumber: number,
18
+ ): ByteArray {
19
+ const objNumStr = objectNumber.toString()
20
+ const genNumStr = generationNumber.toString()
21
+ const refStr = `${objNumStr} ${genNumStr} R`
22
+ return stringToBytes(refStr)
23
+ }
24
+ }
@@ -0,0 +1,10 @@
1
+ import { stringToBytes } from '../../utils/stringToBytes'
2
+ import { PdfToken } from './token'
3
+
4
+ const START_ARRAY_BYTES = stringToBytes('[')
5
+
6
+ export class PdfStartArrayToken extends PdfToken {
7
+ constructor() {
8
+ super(START_ARRAY_BYTES)
9
+ }
10
+ }
@@ -0,0 +1,10 @@
1
+ import { stringToBytes } from '../../utils/stringToBytes'
2
+ import { PdfToken } from './token'
3
+
4
+ const START_DICTIONARY_BYTES = stringToBytes('<<')
5
+
6
+ export class PdfStartDictionaryToken extends PdfToken {
7
+ constructor() {
8
+ super(START_DICTIONARY_BYTES)
9
+ }
10
+ }
@@ -0,0 +1,28 @@
1
+ import { ByteArray } from '../../types'
2
+ import { stringToBytes } from '../../utils/stringToBytes'
3
+ import { PdfToken } from './token'
4
+
5
+ export class PdfStartObjectToken extends PdfToken {
6
+ objectNumber: number
7
+ generationNumber: number
8
+ byteOffset?: number
9
+
10
+ constructor(
11
+ objectNumber: number,
12
+ generationNumber: number,
13
+ byteOffset?: number,
14
+ ) {
15
+ super(PdfStartObjectToken.toBytes(objectNumber, generationNumber))
16
+ this.objectNumber = objectNumber
17
+ this.generationNumber = generationNumber
18
+ this.byteOffset = byteOffset
19
+ }
20
+
21
+ private static toBytes(
22
+ objectNumber: number,
23
+ generationNumber: number,
24
+ ): ByteArray {
25
+ const tokenString = `${objectNumber} ${generationNumber} obj`
26
+ return stringToBytes(tokenString)
27
+ }
28
+ }
@@ -0,0 +1,52 @@
1
+ import { ByteArray } from '../../types'
2
+ import { stringToBytes } from '../../utils/stringToBytes'
3
+ import { PdfToken } from './token'
4
+ import { PdfWhitespaceToken } from './whitespace-token'
5
+
6
+ const START_STREAM_WITHOUT_WHITESPACE_BYTES = stringToBytes('stream')
7
+ const START_STREAM_BYTES = stringToBytes('stream\n')
8
+
9
+ export class PdfStartStreamToken extends PdfToken {
10
+ constructor(bytes?: ByteArray) {
11
+ super(bytes ?? START_STREAM_BYTES)
12
+ }
13
+
14
+ getTrailingWhitespaceTokens(): PdfWhitespaceToken[] {
15
+ const tokens: PdfWhitespaceToken[] = []
16
+ const bytes = this.toBytes()
17
+
18
+ for (let i = bytes.length - 1; i >= 0; i--) {
19
+ const byte = bytes[i]
20
+ if (!PdfWhitespaceToken.isWhitespaceByte(byte)) {
21
+ continue
22
+ }
23
+
24
+ tokens.push(new PdfWhitespaceToken(byte))
25
+ }
26
+ return tokens.reverse()
27
+ }
28
+
29
+ static withTrailingWhitespace(
30
+ whitespaceTokens: PdfToken[] | undefined,
31
+ ): PdfStartStreamToken {
32
+ if (!whitespaceTokens || whitespaceTokens.length === 0) {
33
+ return new PdfStartStreamToken()
34
+ }
35
+
36
+ const totalLength =
37
+ START_STREAM_WITHOUT_WHITESPACE_BYTES.length +
38
+ whitespaceTokens.reduce((sum, token) => sum + token.byteLength, 0)
39
+
40
+ const bytes = new Uint8Array(totalLength)
41
+ bytes.set(START_STREAM_WITHOUT_WHITESPACE_BYTES, 0)
42
+
43
+ let offset = START_STREAM_WITHOUT_WHITESPACE_BYTES.length
44
+ for (const token of whitespaceTokens) {
45
+ const tokenBytes = token.toBytes()
46
+ bytes.set(tokenBytes, offset)
47
+ offset += tokenBytes.length
48
+ }
49
+
50
+ return new PdfStartStreamToken(bytes)
51
+ }
52
+ }
@@ -0,0 +1,10 @@
1
+ import { stringToBytes } from '../../utils/stringToBytes'
2
+ import { PdfToken } from './token'
3
+
4
+ const START_XREF = stringToBytes('startxref')
5
+
6
+ export class PdfStartXRefToken extends PdfToken {
7
+ constructor() {
8
+ super(START_XREF)
9
+ }
10
+ }
@@ -0,0 +1,8 @@
1
+ import { ByteArray } from '../../types'
2
+ import { PdfToken } from './token'
3
+
4
+ export class PdfStreamChunkToken extends PdfToken {
5
+ constructor(bytes: ByteArray) {
6
+ super(bytes)
7
+ }
8
+ }
@@ -0,0 +1,17 @@
1
+ import { ByteArray } from '../../types'
2
+ import { escapeString } from '../../utils/escapeString'
3
+ import { stringToBytes } from '../../utils/stringToBytes'
4
+ import { PdfToken } from './token'
5
+
6
+ export class PdfStringToken extends PdfToken {
7
+ value: ByteArray
8
+
9
+ constructor(value: string | ByteArray) {
10
+ super(PdfStringToken.toBytes(value))
11
+ this.value = typeof value === 'string' ? stringToBytes(value) : value
12
+ }
13
+
14
+ private static toBytes(value: string | ByteArray): ByteArray {
15
+ return new Uint8Array([0x28, ...escapeString(value), 0x29])
16
+ }
17
+ }
@@ -0,0 +1,43 @@
1
+ import { ByteArray } from '../../types'
2
+ import { bytesToString } from '../../utils/bytesToString'
3
+
4
+ export abstract class PdfToken {
5
+ protected rawBytes: ByteArray
6
+
7
+ constructor(bytes?: ByteArray) {
8
+ this.rawBytes = bytes ?? new Uint8Array()
9
+ }
10
+
11
+ toBytes(): ByteArray {
12
+ return this.rawBytes
13
+ }
14
+
15
+ toString(): string {
16
+ return bytesToString(this.toBytes())
17
+ }
18
+
19
+ get type(): string {
20
+ return this.constructor.name
21
+ }
22
+
23
+ get byteLength(): number {
24
+ return this.toBytes().length
25
+ }
26
+
27
+ equals(other: PdfToken): boolean {
28
+ if (this.byteLength !== other.byteLength) {
29
+ return false
30
+ }
31
+
32
+ const bytes = this.toBytes()
33
+ const otherBytes = other.toBytes()
34
+
35
+ for (let i = 0; i < this.byteLength; i++) {
36
+ if (bytes[i] !== otherBytes[i]) {
37
+ return false
38
+ }
39
+ }
40
+
41
+ return true
42
+ }
43
+ }
@@ -0,0 +1,12 @@
1
+ import { stringToBytes } from '../../utils/stringToBytes'
2
+ import { PdfToken } from './token'
3
+
4
+ const TRAILER = stringToBytes('trailer')
5
+
6
+ export class PdfTrailerToken extends PdfToken {
7
+ byteOffset?: number
8
+ constructor(byteOffset?: number) {
9
+ super(TRAILER)
10
+ this.byteOffset = byteOffset
11
+ }
12
+ }
@@ -0,0 +1,43 @@
1
+ import { ByteArray } from '../../types'
2
+ import { PdfToken } from './token'
3
+
4
+ export class PdfWhitespaceToken extends PdfToken {
5
+ public static NEWLINE = new PdfWhitespaceToken('\n')
6
+ public static SPACE = new PdfWhitespaceToken(' ')
7
+ public static TAB = new PdfWhitespaceToken('\t')
8
+ public static CARRIAGE_RETURN = new PdfWhitespaceToken('\r')
9
+
10
+ constructor(value: string | ByteArray | number) {
11
+ super(PdfWhitespaceToken.toBytes(value))
12
+ }
13
+
14
+ private static toBytes(value: string | ByteArray | number): ByteArray {
15
+ if (value instanceof Uint8Array) {
16
+ return value
17
+ }
18
+
19
+ if (typeof value === 'number') {
20
+ return new Uint8Array([value])
21
+ }
22
+
23
+ switch (value) {
24
+ case ' ':
25
+ return new Uint8Array([0x20]) // space character
26
+ case '\t':
27
+ return new Uint8Array([0x09]) // tab character
28
+ case '\n':
29
+ return new Uint8Array([0x0a]) // newline character
30
+ case '\r':
31
+ return new Uint8Array([0x0d]) // carriage return character
32
+ default:
33
+ throw new Error(`Invalid whitespace character: ${value}`)
34
+ }
35
+ }
36
+
37
+ static isWhitespaceByte(byte: number | null | undefined): boolean {
38
+ if (byte === null || byte === undefined) {
39
+ return false
40
+ }
41
+ return byte === 0x20 || byte === 0x0a || byte === 0x0d || byte === 0x09
42
+ }
43
+ }
@@ -0,0 +1,65 @@
1
+ import { ByteArray } from '../../types'
2
+ import { stringToBytes } from '../../utils/stringToBytes'
3
+ import { Ref } from '../ref'
4
+ import { PdfNumberToken } from './number-token'
5
+ import { PdfToken } from './token.js'
6
+
7
+ export class PdfXRefTableEntryToken extends PdfToken {
8
+ objectNumber: PdfNumberToken
9
+ generationNumber: PdfNumberToken
10
+ offset: PdfNumberToken
11
+ inUse: boolean
12
+
13
+ constructor(
14
+ offset: number | Ref<number> | PdfNumberToken,
15
+ generationNumber: number | PdfNumberToken,
16
+ objectNumber: number | PdfNumberToken,
17
+ inUse: boolean,
18
+ ) {
19
+ super()
20
+ this.objectNumber =
21
+ objectNumber instanceof PdfNumberToken
22
+ ? objectNumber
23
+ : new PdfNumberToken({ value: objectNumber })
24
+ this.generationNumber =
25
+ generationNumber instanceof PdfNumberToken
26
+ ? generationNumber
27
+ : new PdfNumberToken({ value: generationNumber })
28
+ this.offset =
29
+ offset instanceof PdfNumberToken
30
+ ? offset
31
+ : new PdfNumberToken({ value: offset })
32
+ this.inUse = inUse
33
+ }
34
+
35
+ toBytes(): ByteArray {
36
+ return PdfXRefTableEntryToken.toBytes(
37
+ this.generationNumber,
38
+ this.offset,
39
+ this.inUse,
40
+ )
41
+ }
42
+
43
+ private static toBytes(
44
+ generationNumber: number | PdfNumberToken,
45
+ offset: number | PdfNumberToken | Ref<number>,
46
+ inUse: boolean,
47
+ ): ByteArray {
48
+ const offsetString =
49
+ offset instanceof PdfNumberToken
50
+ ? offset.toString()
51
+ : new PdfNumberToken({ value: offset, padTo: 10 }).toString()
52
+
53
+ const generationNumberString =
54
+ generationNumber instanceof PdfNumberToken
55
+ ? generationNumber.toString()
56
+ : new PdfNumberToken({
57
+ value: generationNumber,
58
+ padTo: 5,
59
+ }).toString()
60
+
61
+ return stringToBytes(
62
+ `${offsetString} ${generationNumberString} ${inUse ? 'n' : 'f'}`,
63
+ )
64
+ }
65
+ }