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,137 @@
1
+ import { PdfEndObjectToken } from '../tokens/end-object-token'
2
+ import { PdfStartObjectToken } from '../tokens/start-object-token'
3
+ import { PdfWhitespaceToken } from '../tokens/whitespace-token'
4
+ import { PdfNull } from './pdf-null'
5
+ import { PdfObject } from './pdf-object'
6
+ import { PdfObjectReference } from './pdf-object-reference'
7
+ import { PdfStream } from './pdf-stream'
8
+ import { Ref } from '../ref'
9
+ import { PdfByteOffsetToken } from '../tokens/byte-offset-token'
10
+
11
+ export class PdfIndirectObject<
12
+ T extends PdfObject = PdfObject,
13
+ > extends PdfObjectReference {
14
+ static readonly MAX_ORDER_INDEX = 2147483647
15
+
16
+ content: T
17
+ offset: Ref<number>
18
+ encryptable?: boolean
19
+ orderIndex?: number
20
+
21
+ constructor(
22
+ options:
23
+ | {
24
+ objectNumber?: number
25
+ generationNumber?: number
26
+ content: T
27
+ offset?: number | Ref<number>
28
+ encryptable?: boolean
29
+ }
30
+ | T,
31
+ ) {
32
+ if (options instanceof PdfObject) {
33
+ super(-1, 0)
34
+ this.content = options
35
+ this.offset = new Ref(0)
36
+ return
37
+ }
38
+
39
+ super(options.objectNumber ?? -1, options.generationNumber ?? 0)
40
+ this.content = options.content
41
+ this.offset =
42
+ options.offset instanceof Ref
43
+ ? options.offset
44
+ : new Ref(options.offset ?? 0)
45
+ this.encryptable = options.encryptable
46
+ }
47
+
48
+ get reference(): PdfObjectReference {
49
+ return new Proxy(this, {
50
+ get: (target, prop) => {
51
+ const value = new PdfObjectReference(
52
+ target.objectNumber,
53
+ target.generationNumber,
54
+ )
55
+ if (prop === 'objectNumber') {
56
+ return target.objectNumber
57
+ } else if (prop === 'generationNumber') {
58
+ return target.generationNumber
59
+ } else {
60
+ return value[prop as keyof PdfObjectReference]
61
+ }
62
+ },
63
+ })
64
+ }
65
+
66
+ isEncryptable() {
67
+ if (this.encryptable === undefined) {
68
+ if (
69
+ this.content instanceof PdfStream &&
70
+ this.content.isType('XRef')
71
+ ) {
72
+ return false
73
+ }
74
+ }
75
+
76
+ return this.encryptable ?? true
77
+ }
78
+
79
+ static createPlaceholder<T extends PdfObject>(
80
+ objectNumber?: number,
81
+ generationNumber?: number,
82
+ content?: T,
83
+ ): PdfIndirectObject<T extends unknown ? PdfNull : T> {
84
+ return new PdfIndirectObject({
85
+ objectNumber: objectNumber ?? -1,
86
+ generationNumber: generationNumber ?? 0,
87
+ content: content ?? (new PdfNull() as any),
88
+ offset: -1,
89
+ })
90
+ }
91
+
92
+ inPdf() {
93
+ return this.objectNumber >= 1
94
+ }
95
+
96
+ matchesReference(ref?: PdfObjectReference) {
97
+ if (!ref) {
98
+ return false
99
+ }
100
+ return (
101
+ this.objectNumber === ref.objectNumber &&
102
+ this.generationNumber === ref.generationNumber
103
+ )
104
+ }
105
+
106
+ protected tokenize() {
107
+ return [
108
+ new PdfByteOffsetToken(this.offset),
109
+ new PdfStartObjectToken(this.objectNumber, this.generationNumber),
110
+ ...(this.content.preTokens ? [] : [PdfWhitespaceToken.NEWLINE]),
111
+ ...this.content.toTokens(),
112
+ ...(this.content.postTokens ? [] : [PdfWhitespaceToken.NEWLINE]),
113
+ new PdfEndObjectToken(),
114
+ ]
115
+ }
116
+
117
+ clone(): this {
118
+ return new PdfIndirectObject({
119
+ objectNumber: this.objectNumber,
120
+ generationNumber: this.generationNumber,
121
+ content: this.content.clone(),
122
+ offset: this.offset.resolve(),
123
+ }) as this
124
+ }
125
+
126
+ order(): number {
127
+ return this.orderIndex ?? 0
128
+ }
129
+
130
+ isModified(): boolean {
131
+ return (
132
+ super.isModified() ||
133
+ this.content.isModified() ||
134
+ this.offset.isModified
135
+ )
136
+ }
137
+ }
@@ -0,0 +1,19 @@
1
+ import { PdfNameToken } from '../tokens/name-token'
2
+ import { PdfObject } from './pdf-object'
3
+
4
+ export class PdfName<T extends string = string> extends PdfObject {
5
+ value: T
6
+
7
+ constructor(value: T) {
8
+ super()
9
+ this.value = value
10
+ }
11
+
12
+ protected tokenize() {
13
+ return [new PdfNameToken(this.value)]
14
+ }
15
+
16
+ clone(): this {
17
+ return new PdfName(this.value) as this
18
+ }
19
+ }
@@ -0,0 +1,15 @@
1
+ import { PdfObject } from './pdf-object'
2
+ import { PdfNullToken } from '../tokens/null-token'
3
+ import { PdfToken } from '../tokens/token'
4
+
5
+ export class PdfNull extends PdfObject {
6
+ static readonly NULL: PdfNull = new PdfNull()
7
+
8
+ protected tokenize() {
9
+ return [new PdfNullToken()]
10
+ }
11
+
12
+ clone(): this {
13
+ return new PdfNull() as this
14
+ }
15
+ }
@@ -0,0 +1,98 @@
1
+ import { Ref } from '../ref'
2
+ import { PdfNumberToken } from '../tokens/number-token'
3
+ import { PdfObject } from './pdf-object'
4
+
5
+ export class PdfNumber extends PdfObject {
6
+ #value: Ref<number>
7
+ padTo: number
8
+ decimalPlaces: number
9
+ isByteOffset: boolean = false
10
+
11
+ constructor(
12
+ options:
13
+ | {
14
+ value: number | Ref<number>
15
+ padTo?: number
16
+ decimalPlaces?: number
17
+ }
18
+ | number
19
+ | Ref<number>
20
+ | PdfNumber = 0,
21
+ ) {
22
+ super()
23
+ if (options instanceof Ref) {
24
+ this.#value = options
25
+ this.padTo = 0
26
+ this.decimalPlaces = 0
27
+ } else if (options instanceof PdfNumber) {
28
+ this.#value = options.ref
29
+ this.padTo = options.padTo
30
+ this.decimalPlaces = options.decimalPlaces
31
+ this.preTokens = options.preTokens
32
+ this.postTokens = options.postTokens
33
+ } else if (typeof options === 'number') {
34
+ this.#value = new Ref(options)
35
+ this.padTo = 0
36
+ this.decimalPlaces = 0
37
+ } else {
38
+ this.#value =
39
+ typeof options.value === 'number'
40
+ ? new Ref(options.value)
41
+ : options.value
42
+ this.padTo = options.padTo ?? 0
43
+ this.decimalPlaces = options.decimalPlaces ?? 0
44
+ }
45
+
46
+ if (
47
+ this.value === Number.POSITIVE_INFINITY ||
48
+ this.value === Number.NEGATIVE_INFINITY
49
+ ) {
50
+ throw new Error('PDF numbers cannot be infinite')
51
+ }
52
+ }
53
+
54
+ get ref() {
55
+ return this.#value
56
+ }
57
+
58
+ get value() {
59
+ return this.#value.resolve()
60
+ }
61
+
62
+ set value(value: number) {
63
+ this.#value.update(value)
64
+ }
65
+
66
+ onChange(callback: (value: number) => void): void {
67
+ this.#value.onUpdate(callback)
68
+ }
69
+
70
+ toToken(): PdfNumberToken {
71
+ return new PdfNumberToken({
72
+ value: this.#value,
73
+ padTo: this.padTo,
74
+ decimalPlaces: this.decimalPlaces,
75
+ isByteToken: this.isByteOffset,
76
+ })
77
+ }
78
+
79
+ protected tokenize() {
80
+ return [this.toToken()]
81
+ }
82
+
83
+ clone(): this {
84
+ return new PdfNumber({
85
+ value: this.value,
86
+ padTo: this.padTo,
87
+ decimalPlaces: this.decimalPlaces,
88
+ }) as this
89
+ }
90
+
91
+ isModified(): boolean {
92
+ return super.isModified() || this.#value.isModified
93
+ }
94
+ }
95
+
96
+ export class PdfByteOffsetNumber extends PdfNumber {
97
+ isByteOffset: boolean = true
98
+ }
@@ -0,0 +1,30 @@
1
+ import { PdfToken } from '../tokens/token'
2
+ import { PdfObjectReferenceToken } from '../tokens/object-reference-token'
3
+ import { PdfObject } from './pdf-object'
4
+
5
+ export class PdfObjectReference extends PdfObject {
6
+ objectNumber: number
7
+ generationNumber: number
8
+
9
+ constructor(objectNumber: number, generationNumber: number) {
10
+ super()
11
+ this.objectNumber = objectNumber
12
+ this.generationNumber = generationNumber
13
+ }
14
+
15
+ protected tokenize(): PdfToken[] {
16
+ return [
17
+ new PdfObjectReferenceToken(
18
+ this.objectNumber,
19
+ this.generationNumber,
20
+ ),
21
+ ]
22
+ }
23
+
24
+ clone(): this {
25
+ return new PdfObjectReference(
26
+ this.objectNumber,
27
+ this.generationNumber,
28
+ ) as this
29
+ }
30
+ }
@@ -0,0 +1,107 @@
1
+ import { ByteArray } from '../../types.js'
2
+ import { concatUint8Arrays } from '../../utils/concatUint8Arrays.js'
3
+ import { PdfToken } from '../tokens/token.js'
4
+ import { PdfWhitespaceToken } from '../tokens/whitespace-token.js'
5
+
6
+ export abstract class PdfObject {
7
+ /** Optional tokens to prepend or append during serialization */
8
+ preTokens?: PdfToken[]
9
+ /** Optional tokens to prepend or append during serialization */
10
+ postTokens?: PdfToken[]
11
+ /** Indicates whether the object has been modified. By default, assume it has been modified because it's a new object */
12
+ protected modified: boolean = true
13
+ /** Tokenizes the object into an array of PdfTokens */
14
+ protected abstract tokenize(): PdfToken[]
15
+
16
+ /** The type of this PDF object */
17
+ get objectType(): string {
18
+ return this.constructor.name
19
+ }
20
+
21
+ /** Indicates whether the object has been modified. Override this method if the modified state is determined differently */
22
+ isModified(): boolean {
23
+ return this.modified
24
+ }
25
+
26
+ /** Sets the modified state of the object. Override this method if the modified state is determined differently */
27
+ setModified(modified: boolean = true): void {
28
+ this.modified = modified
29
+ }
30
+
31
+ /** Converts the object to an array of PdfTokens, including any pre or post tokens */
32
+ toTokens(): PdfToken[] {
33
+ return [
34
+ ...(this.preTokens ?? []),
35
+ ...this.tokenize(),
36
+ ...(this.postTokens ?? []),
37
+ ]
38
+ }
39
+
40
+ /** Converts the object to a ByteArray, optionally padding to a specified length */
41
+ toBytes(padTo?: number): ByteArray {
42
+ const tokens = this.toTokens()
43
+
44
+ const byteArray = concatUint8Arrays(
45
+ ...tokens.map((token) => token.toBytes()),
46
+ )
47
+ if (padTo) {
48
+ const paddedArray = new Uint8Array(padTo)
49
+ paddedArray.set(byteArray)
50
+ return paddedArray
51
+ }
52
+ return byteArray
53
+ }
54
+
55
+ /** Converts the object to a string representation */
56
+ toString(): string {
57
+ let str = ''
58
+ for (const byte of this.toBytes()) {
59
+ str += String.fromCharCode(byte)
60
+ }
61
+ return str
62
+ }
63
+
64
+ /** Attempts to cast the object to a specific PdfObject subclass */
65
+ as<T extends PdfObject>(ctor: new (...args: any[]) => T): T {
66
+ if (this instanceof ctor) {
67
+ return this as T
68
+ }
69
+
70
+ throw new Error(
71
+ `Cannot cast object of type ${this.objectType} to ${ctor.name}`,
72
+ )
73
+ }
74
+
75
+ /** Creates a deep clone of the object */
76
+ abstract clone(): this
77
+
78
+ /** Compares this object to another for equality based on their token representations */
79
+ equals(other?: PdfObject): boolean {
80
+ if (!other) {
81
+ return false
82
+ }
83
+
84
+ const tokensA = this.toTokens().filter(
85
+ (x) => !(x instanceof PdfWhitespaceToken),
86
+ )
87
+
88
+ const tokensB = other
89
+ .toTokens()
90
+ .filter((x) => !(x instanceof PdfWhitespaceToken))
91
+
92
+ if (tokensA.length !== tokensB.length) {
93
+ return false
94
+ }
95
+
96
+ for (let i = 0; i < tokensA.length; i++) {
97
+ const tokenA = tokensA[i]
98
+ const tokenB = tokensB[i]
99
+
100
+ if (!tokenA.equals(tokenB)) {
101
+ return false
102
+ }
103
+ }
104
+
105
+ return true
106
+ }
107
+ }
@@ -0,0 +1,39 @@
1
+ import { Ref } from '../ref'
2
+ import { PdfStartXRefToken } from '../tokens/start-xref-token'
3
+ import { PdfWhitespaceToken } from '../tokens/whitespace-token'
4
+ import { PdfNumber } from './pdf-number'
5
+ import { PdfObject } from './pdf-object'
6
+
7
+ export class PdfStartXRef extends PdfObject {
8
+ offset: PdfNumber
9
+
10
+ constructor(offset: number | PdfNumber | Ref<number> = -1) {
11
+ // default to -1 for unknown offset
12
+ super()
13
+ this.offset =
14
+ offset instanceof PdfNumber
15
+ ? offset
16
+ : new PdfNumber({ value: offset })
17
+ this.offset.isByteOffset = true
18
+ }
19
+
20
+ protected tokenize() {
21
+ const whiteSpaceTokens = this.offset.preTokens
22
+ ? []
23
+ : [PdfWhitespaceToken.NEWLINE]
24
+
25
+ return [
26
+ new PdfStartXRefToken(),
27
+ ...whiteSpaceTokens,
28
+ ...this.offset.toTokens(),
29
+ ]
30
+ }
31
+
32
+ clone(): this {
33
+ return new PdfStartXRef(this.offset.clone()) as this
34
+ }
35
+
36
+ isModified(): boolean {
37
+ return super.isModified() || this.offset.isModified()
38
+ }
39
+ }