@awacloud/pdf 0.0.0-stage → 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 (345) hide show
  1. package/CHANGELOG.md +609 -0
  2. package/LICENSE +661 -0
  3. package/NOTICE +77 -0
  4. package/README.md +363 -2
  5. package/dist/build/index.js +21 -0
  6. package/dist/build/pdf-full-rw.js +10972 -0
  7. package/dist/build/pdf-full-rw.meta.json +105 -0
  8. package/dist/build/pdf-full-rw.min.js +53 -0
  9. package/dist/build/pdf-full.js +6078 -0
  10. package/dist/build/pdf-full.meta.json +90 -0
  11. package/dist/build/pdf-full.min.js +32 -0
  12. package/dist/build/pdf-large-rw.js +10367 -0
  13. package/dist/build/pdf-large-rw.meta.json +99 -0
  14. package/dist/build/pdf-large-rw.min.js +53 -0
  15. package/dist/build/pdf-large.js +5473 -0
  16. package/dist/build/pdf-large.meta.json +84 -0
  17. package/dist/build/pdf-large.min.js +32 -0
  18. package/dist/build/pdf-legacy-rw.js +12402 -0
  19. package/dist/build/pdf-legacy-rw.meta.json +110 -0
  20. package/dist/build/pdf-legacy-rw.min.js +53 -0
  21. package/dist/build/pdf-legacy.js +7508 -0
  22. package/dist/build/pdf-legacy.meta.json +95 -0
  23. package/dist/build/pdf-legacy.min.js +32 -0
  24. package/dist/build/pdf-rw.js +7578 -0
  25. package/dist/build/pdf-rw.meta.json +77 -0
  26. package/dist/build/pdf-rw.min.js +53 -0
  27. package/dist/build/pdf.js +2684 -0
  28. package/dist/build/pdf.meta.json +62 -0
  29. package/dist/build/pdf.min.js +32 -0
  30. package/dist/standalone/pdf-full-rw.js +16798 -0
  31. package/dist/standalone/pdf-full-rw.meta.json +78 -0
  32. package/dist/standalone/pdf-full-rw.min.js +56 -0
  33. package/dist/standalone/pdf-full.js +11904 -0
  34. package/dist/standalone/pdf-full.meta.json +63 -0
  35. package/dist/standalone/pdf-full.min.js +35 -0
  36. package/dist/standalone/pdf-large-rw.js +16193 -0
  37. package/dist/standalone/pdf-large-rw.meta.json +72 -0
  38. package/dist/standalone/pdf-large-rw.min.js +56 -0
  39. package/dist/standalone/pdf-large.js +11299 -0
  40. package/dist/standalone/pdf-large.meta.json +57 -0
  41. package/dist/standalone/pdf-large.min.js +35 -0
  42. package/dist/standalone/pdf-legacy-rw.js +18228 -0
  43. package/dist/standalone/pdf-legacy-rw.meta.json +83 -0
  44. package/dist/standalone/pdf-legacy-rw.min.js +56 -0
  45. package/dist/standalone/pdf-legacy.js +13334 -0
  46. package/dist/standalone/pdf-legacy.meta.json +68 -0
  47. package/dist/standalone/pdf-legacy.min.js +35 -0
  48. package/dist/standalone/pdf-rw.js +13404 -0
  49. package/dist/standalone/pdf-rw.meta.json +50 -0
  50. package/dist/standalone/pdf-rw.min.js +56 -0
  51. package/dist/standalone/pdf.js +8510 -0
  52. package/dist/standalone/pdf.meta.json +35 -0
  53. package/dist/standalone/pdf.min.js +35 -0
  54. package/docs/README.md +53 -0
  55. package/docs/api/README.md +38 -0
  56. package/docs/api/_shared/README.md +91 -0
  57. package/docs/api/action/README.md +29 -0
  58. package/docs/api/action/action.md +81 -0
  59. package/docs/api/action/goTo.md +66 -0
  60. package/docs/api/action/launch.md +58 -0
  61. package/docs/api/action/named.md +55 -0
  62. package/docs/api/action/uri.md +54 -0
  63. package/docs/api/annot/README.md +53 -0
  64. package/docs/api/annot/annot.md +114 -0
  65. package/docs/api/annot/fileAttach.md +53 -0
  66. package/docs/api/annot/freeText.md +68 -0
  67. package/docs/api/annot/ink.md +69 -0
  68. package/docs/api/annot/link.md +74 -0
  69. package/docs/api/annot/markup.md +83 -0
  70. package/docs/api/annot/popup.md +52 -0
  71. package/docs/api/annot/projection.md +56 -0
  72. package/docs/api/annot/redact.md +67 -0
  73. package/docs/api/annot/square.md +87 -0
  74. package/docs/api/annot/stamp.md +54 -0
  75. package/docs/api/annot/text.md +69 -0
  76. package/docs/api/annot/widget.md +69 -0
  77. package/docs/api/associatedFiles/README.md +9 -0
  78. package/docs/api/associatedFiles/associatedFiles.md +78 -0
  79. package/docs/api/bundles/README.md +68 -0
  80. package/docs/api/bundles/dist-matrix.md +165 -0
  81. package/docs/api/bundles/pdf-full.md +148 -0
  82. package/docs/api/bundles/pdf-large.md +144 -0
  83. package/docs/api/bundles/pdf-legacy.md +169 -0
  84. package/docs/api/content/README.md +29 -0
  85. package/docs/api/content/color.md +99 -0
  86. package/docs/api/content/graphics.md +114 -0
  87. package/docs/api/content/images.md +124 -0
  88. package/docs/api/content/ops.md +100 -0
  89. package/docs/api/content/stream.md +107 -0
  90. package/docs/api/content/text.md +98 -0
  91. package/docs/api/crypto/README.md +29 -0
  92. package/docs/api/crypto/aesGcm.md +72 -0
  93. package/docs/api/crypto/permissions.md +79 -0
  94. package/docs/api/crypto/security.md +98 -0
  95. package/docs/api/crypto/standardV4.md +104 -0
  96. package/docs/api/crypto/standardV5.md +84 -0
  97. package/docs/api/crypto/standardV6.md +93 -0
  98. package/docs/api/destination/README.md +9 -0
  99. package/docs/api/destination/destination.md +79 -0
  100. package/docs/api/document/README.md +29 -0
  101. package/docs/api/document/builder.md +281 -0
  102. package/docs/api/document/catalog.md +98 -0
  103. package/docs/api/document/document.md +187 -0
  104. package/docs/api/document/encryptedWriter.md +149 -0
  105. package/docs/api/document/incrementalWriter.md +148 -0
  106. package/docs/api/document/page.md +99 -0
  107. package/docs/api/document/pages.md +82 -0
  108. package/docs/api/document/resources.md +102 -0
  109. package/docs/api/document/writer.md +157 -0
  110. package/docs/api/document/xrefStreamWriter.md +122 -0
  111. package/docs/api/embedded/README.md +13 -0
  112. package/docs/api/embedded/collection.md +80 -0
  113. package/docs/api/embedded/embeddedFile.md +86 -0
  114. package/docs/api/embedded/fileSpec.md +87 -0
  115. package/docs/api/errors.md +110 -0
  116. package/docs/api/extra/3d-richmedia.md +76 -0
  117. package/docs/api/extra/README.md +99 -0
  118. package/docs/api/extra/annot-extended.md +71 -0
  119. package/docs/api/extra/associated-files.md +70 -0
  120. package/docs/api/extra/ccitt-fax-decoder.md +74 -0
  121. package/docs/api/extra/color-spaces-extended.md +72 -0
  122. package/docs/api/extra/content-ops-extended.md +82 -0
  123. package/docs/api/extra/document-parts.md +69 -0
  124. package/docs/api/extra/embedded-files-portfolio.md +87 -0
  125. package/docs/api/extra/font-cid-typed.md +77 -0
  126. package/docs/api/extra/font-color-tagging.md +76 -0
  127. package/docs/api/extra/form-actions-extended.md +75 -0
  128. package/docs/api/extra/info-dict-deprecated.md +72 -0
  129. package/docs/api/extra/jbig2-read.md +80 -0
  130. package/docs/api/extra/legacy-deprecated-annots.md +89 -0
  131. package/docs/api/extra/legacy-deprecated-filters.md +78 -0
  132. package/docs/api/extra/legacy-rc4-read.md +74 -0
  133. package/docs/api/extra/legacy-xfa-read.md +65 -0
  134. package/docs/api/extra/linearization-write.md +71 -0
  135. package/docs/api/extra/misc.md +93 -0
  136. package/docs/api/extra/optional-content-extended.md +83 -0
  137. package/docs/api/extra/pdf-a-output-intent.md +65 -0
  138. package/docs/api/extra/pdf-sandbox.md +76 -0
  139. package/docs/api/extra/pdf-ua-tagged.md +63 -0
  140. package/docs/api/extra/pdf-x-prepress.md +65 -0
  141. package/docs/api/extra/redaction-iso32005.md +65 -0
  142. package/docs/api/extra/shading-typed.md +73 -0
  143. package/docs/api/extra/sig-aes-gcm.md +69 -0
  144. package/docs/api/extra/sig-pades.md +103 -0
  145. package/docs/api/extra/tagged-pdf-typed.md +78 -0
  146. package/docs/api/extra/transparency-typed.md +74 -0
  147. package/docs/api/extra/well-tagged-pdf.md +61 -0
  148. package/docs/api/extra/xmp-extended.md +65 -0
  149. package/docs/api/font/README.md +25 -0
  150. package/docs/api/font/embed.md +157 -0
  151. package/docs/api/font/encoding.md +95 -0
  152. package/docs/api/font/font.md +97 -0
  153. package/docs/api/font/type3.md +89 -0
  154. package/docs/api/form/README.md +35 -0
  155. package/docs/api/form/acroform.md +88 -0
  156. package/docs/api/form/appearance.md +87 -0
  157. package/docs/api/form/button.md +97 -0
  158. package/docs/api/form/choice.md +96 -0
  159. package/docs/api/form/fieldTree.md +93 -0
  160. package/docs/api/form/signature.md +90 -0
  161. package/docs/api/form/text.md +88 -0
  162. package/docs/api/linearization/README.md +11 -0
  163. package/docs/api/linearization/linearization.md +81 -0
  164. package/docs/api/main.md +116 -0
  165. package/docs/api/metadata/README.md +10 -0
  166. package/docs/api/metadata/info.md +70 -0
  167. package/docs/api/metadata/xmp.md +62 -0
  168. package/docs/api/ocg/README.md +23 -0
  169. package/docs/api/ocg/config.md +95 -0
  170. package/docs/api/ocg/ocg.md +77 -0
  171. package/docs/api/outline/README.md +11 -0
  172. package/docs/api/outline/outline.md +107 -0
  173. package/docs/api/pdf.md +152 -0
  174. package/docs/api/prepress/README.md +10 -0
  175. package/docs/api/prepress/outputIntent.md +79 -0
  176. package/docs/api/prepress/pageBoundary.md +75 -0
  177. package/docs/api/sig/README.md +32 -0
  178. package/docs/api/sig/byteRange.md +120 -0
  179. package/docs/api/sig/certChain.md +84 -0
  180. package/docs/api/sig/dss.md +111 -0
  181. package/docs/api/sig/oids.md +76 -0
  182. package/docs/api/sig/sha1.md +72 -0
  183. package/docs/api/sig/sign.md +317 -0
  184. package/docs/api/sig/signature.md +178 -0
  185. package/docs/api/sig/timestamp.md +84 -0
  186. package/docs/api/syntax/README.md +29 -0
  187. package/docs/api/syntax/crossRefStream.md +115 -0
  188. package/docs/api/syntax/filters/README.md +50 -0
  189. package/docs/api/syntax/filters/ascii85.md +76 -0
  190. package/docs/api/syntax/filters/asciiHex.md +73 -0
  191. package/docs/api/syntax/filters/dispatch.md +125 -0
  192. package/docs/api/syntax/filters/flate.md +134 -0
  193. package/docs/api/syntax/filters/runLength.md +78 -0
  194. package/docs/api/syntax/objStream.md +88 -0
  195. package/docs/api/syntax/parser-obj.md +97 -0
  196. package/docs/api/syntax/parser.md +151 -0
  197. package/docs/api/syntax/serializer.md +109 -0
  198. package/docs/api/syntax/tokenizer.md +104 -0
  199. package/docs/api/syntax/trailer.md +85 -0
  200. package/docs/api/syntax/xref.md +139 -0
  201. package/docs/api/tagged/README.md +25 -0
  202. package/docs/api/tagged/classMap.md +67 -0
  203. package/docs/api/tagged/markedContent.md +62 -0
  204. package/docs/api/tagged/parentTree.md +67 -0
  205. package/docs/api/tagged/roleMap.md +67 -0
  206. package/docs/api/tagged/structElement.md +76 -0
  207. package/docs/api/tagged/structTree.md +75 -0
  208. package/docs/guide/coverage.md +113 -0
  209. package/docs/guide/crypto.md +121 -0
  210. package/docs/guide/extending.md +76 -0
  211. package/docs/guide/getting-started.md +75 -0
  212. package/docs/guide/legacy-1.7.md +42 -0
  213. package/docs/guide/pades-integration.md +579 -0
  214. package/docs/guide/read-pdf.md +89 -0
  215. package/package.json +97 -4
  216. package/src/_shared/index.js +179 -0
  217. package/src/action/action.js +119 -0
  218. package/src/action/goTo.js +89 -0
  219. package/src/action/launch.js +61 -0
  220. package/src/action/named.js +54 -0
  221. package/src/action/uri.js +51 -0
  222. package/src/annot/annot.js +212 -0
  223. package/src/annot/fileAttach.js +55 -0
  224. package/src/annot/freeText.js +82 -0
  225. package/src/annot/ink.js +77 -0
  226. package/src/annot/link.js +77 -0
  227. package/src/annot/markup.js +91 -0
  228. package/src/annot/popup.js +53 -0
  229. package/src/annot/projection.js +52 -0
  230. package/src/annot/redact.js +87 -0
  231. package/src/annot/square.js +132 -0
  232. package/src/annot/stamp.js +48 -0
  233. package/src/annot/text.js +54 -0
  234. package/src/annot/widget.js +61 -0
  235. package/src/associatedFiles/associatedFiles.js +86 -0
  236. package/src/bundles/pdf-full.js +91 -0
  237. package/src/bundles/pdf-large.js +81 -0
  238. package/src/bundles/pdf-legacy.js +107 -0
  239. package/src/content/color.js +114 -0
  240. package/src/content/graphics.js +192 -0
  241. package/src/content/images.js +160 -0
  242. package/src/content/ops.js +137 -0
  243. package/src/content/stream.js +154 -0
  244. package/src/content/text.js +125 -0
  245. package/src/crypto/aesGcm.js +123 -0
  246. package/src/crypto/permissions.js +112 -0
  247. package/src/crypto/security.js +327 -0
  248. package/src/crypto/standardV4.js +443 -0
  249. package/src/crypto/standardV5.js +306 -0
  250. package/src/crypto/standardV6.js +334 -0
  251. package/src/destination/destination.js +183 -0
  252. package/src/document/builder.js +618 -0
  253. package/src/document/catalog.js +100 -0
  254. package/src/document/document.js +472 -0
  255. package/src/document/encryptedWriter.js +554 -0
  256. package/src/document/incrementalWriter.js +514 -0
  257. package/src/document/page.js +131 -0
  258. package/src/document/pages.js +103 -0
  259. package/src/document/resources.js +146 -0
  260. package/src/document/writer.js +211 -0
  261. package/src/document/xrefStreamWriter.js +353 -0
  262. package/src/embedded/collection.js +102 -0
  263. package/src/embedded/embeddedFile.js +99 -0
  264. package/src/embedded/fileSpec.js +137 -0
  265. package/src/errors.js +78 -0
  266. package/src/extra/3d-richmedia.js +171 -0
  267. package/src/extra/annot-extended.js +200 -0
  268. package/src/extra/associated-files.js +131 -0
  269. package/src/extra/ccitt-fax-decoder.js +776 -0
  270. package/src/extra/color-spaces-extended.js +196 -0
  271. package/src/extra/content-ops-extended.js +153 -0
  272. package/src/extra/document-parts.js +149 -0
  273. package/src/extra/embedded-files-portfolio.js +234 -0
  274. package/src/extra/font-cid-typed.js +185 -0
  275. package/src/extra/font-color-tagging.js +144 -0
  276. package/src/extra/form-actions-extended.js +196 -0
  277. package/src/extra/info-dict-deprecated.js +137 -0
  278. package/src/extra/jbig2-read.js +169 -0
  279. package/src/extra/legacy-deprecated-annots.js +198 -0
  280. package/src/extra/legacy-deprecated-filters.js +167 -0
  281. package/src/extra/legacy-rc4-read.js +235 -0
  282. package/src/extra/legacy-xfa-read.js +104 -0
  283. package/src/extra/linearization-write.js +97 -0
  284. package/src/extra/misc.js +217 -0
  285. package/src/extra/optional-content-extended.js +142 -0
  286. package/src/extra/pdf-a-output-intent.js +112 -0
  287. package/src/extra/pdf-sandbox.js +88 -0
  288. package/src/extra/pdf-ua-tagged.js +116 -0
  289. package/src/extra/pdf-x-prepress.js +114 -0
  290. package/src/extra/redaction-iso32005.js +136 -0
  291. package/src/extra/shading-typed.js +222 -0
  292. package/src/extra/sig-aes-gcm.js +135 -0
  293. package/src/extra/sig-pades.js +242 -0
  294. package/src/extra/tagged-pdf-typed.js +203 -0
  295. package/src/extra/transparency-typed.js +135 -0
  296. package/src/extra/well-tagged-pdf.js +138 -0
  297. package/src/extra/xmp-extended.js +190 -0
  298. package/src/font/embed.js +480 -0
  299. package/src/font/encoding.js +92 -0
  300. package/src/font/font.js +101 -0
  301. package/src/font/type3.js +75 -0
  302. package/src/form/acroform.js +94 -0
  303. package/src/form/appearance.js +90 -0
  304. package/src/form/button.js +105 -0
  305. package/src/form/choice.js +152 -0
  306. package/src/form/fieldTree.js +120 -0
  307. package/src/form/signature.js +100 -0
  308. package/src/form/text.js +101 -0
  309. package/src/linearization/linearization.js +107 -0
  310. package/src/main.js +411 -0
  311. package/src/metadata/info.js +87 -0
  312. package/src/metadata/xmp.js +62 -0
  313. package/src/ocg/config.js +156 -0
  314. package/src/ocg/ocg.js +124 -0
  315. package/src/outline/outline.js +157 -0
  316. package/src/pdf.js +133 -0
  317. package/src/prepress/outputIntent.js +118 -0
  318. package/src/prepress/pageBoundary.js +108 -0
  319. package/src/sig/byteRange.js +306 -0
  320. package/src/sig/certChain.js +247 -0
  321. package/src/sig/dss.js +317 -0
  322. package/src/sig/oids.js +157 -0
  323. package/src/sig/sha1.js +142 -0
  324. package/src/sig/sign.js +1899 -0
  325. package/src/sig/signature.js +1441 -0
  326. package/src/sig/timestamp.js +236 -0
  327. package/src/syntax/crossRefStream.js +133 -0
  328. package/src/syntax/filters/ascii85.js +122 -0
  329. package/src/syntax/filters/asciiHex.js +83 -0
  330. package/src/syntax/filters/dispatch.js +176 -0
  331. package/src/syntax/filters/flate.js +316 -0
  332. package/src/syntax/filters/runLength.js +96 -0
  333. package/src/syntax/objStream.js +99 -0
  334. package/src/syntax/parser-obj.js +52 -0
  335. package/src/syntax/parser.js +321 -0
  336. package/src/syntax/serializer.js +221 -0
  337. package/src/syntax/tokenizer.js +290 -0
  338. package/src/syntax/trailer.js +76 -0
  339. package/src/syntax/xref.js +341 -0
  340. package/src/tagged/classMap.js +81 -0
  341. package/src/tagged/markedContent.js +123 -0
  342. package/src/tagged/parentTree.js +126 -0
  343. package/src/tagged/roleMap.js +107 -0
  344. package/src/tagged/structElement.js +138 -0
  345. package/src/tagged/structTree.js +94 -0
@@ -0,0 +1,290 @@
1
+ // Copyright (c) 2026 AwaCloud SAS
2
+ // Author: Matthieu Bouilloux
3
+ // SPDX-License-Identifier: AGPL-3.0-only
4
+ // Dual-licensed; see the NOTICE file for licensing and any additional terms.
5
+
6
+ /**
7
+ * @fileoverview PDF tokenizer — splits a `Uint8Array` of PDF source into
8
+ * a stream of tokens per ISO 32000-2:2020 §7.2 (Lexical conventions).
9
+ *
10
+ * Tokens emitted (kind → payload):
11
+ *
12
+ * | kind | payload |
13
+ * |--------------|------------------------------------------------|
14
+ * | `'ws'` | whitespace run (only when `keepWhitespace`) |
15
+ * | `'comment'` | comment body (bytes between `%` and EOL) |
16
+ * | `'name'` | name string, `#xx` hex-escapes decoded |
17
+ * | `'int'` | parsed JS number (safe integer) |
18
+ * | `'real'` | parsed JS number (float) |
19
+ * | `'string'` | literal string `(...)` decoded → Uint8Array |
20
+ * | `'hex'` | hex string `<...>` decoded → Uint8Array |
21
+ * | `'open_arr'` | `[` |
22
+ * | `'close_arr'`| `]` |
23
+ * | `'open_dict'`| `<<` |
24
+ * | `'close_dict'`| `>>` |
25
+ * | `'kw'` | keyword bytes as ASCII string |
26
+ * | `'eof_marker'`| `%%EOF` |
27
+ *
28
+ * @module pdf/syntax/tokenizer
29
+ */
30
+
31
+ /**
32
+ * Module factory — worker-safe, self-contained.
33
+ */
34
+ import { pdfErrors } from '../errors.js';
35
+ import { pdfShared } from '../_shared/index.js';
36
+
37
+ export const pdfTokenizer = {
38
+ name: 'pdfTokenizer',
39
+ dependencies: ['pdfErrors', 'pdfShared'],
40
+ deps: [pdfErrors, pdfShared],
41
+ factory(errors, shared) {
42
+ const { ParseError } = errors;
43
+ const { ASCII, isWs, isEol, isDigit, isRegular, hexNibble } = shared;
44
+ const {
45
+ HT, LF, CR, FF,
46
+ LPAREN, RPAREN, LANGLE, RANGLE, LBRACK, RBRACK,
47
+ SLASH, PERCENT, BACKSLASH,
48
+ PLUS, MINUS, DOT,
49
+ ZERO,
50
+ HASH
51
+ } = ASCII;
52
+
53
+ function tokenize(bytes, opts) {
54
+ if (!(bytes instanceof Uint8Array)) {
55
+ throw new ParseError('pdf/tokenizer/bad-input',
56
+ 'tokenize() expects a Uint8Array', { context: { typeof: typeof bytes } });
57
+ }
58
+ const keepWs = !!(opts && opts.keepWhitespace);
59
+ let i = (opts && opts.start | 0) || 0;
60
+ const N = (opts && Number.isFinite(opts.end))
61
+ ? Math.min(opts.end | 0, bytes.length) : bytes.length;
62
+ let lookahead = null;
63
+
64
+ function pos() { return i; }
65
+ function seek(n) {
66
+ if (n < 0 || n > bytes.length) {
67
+ throw new ParseError('pdf/tokenizer/bad-seek',
68
+ 'seek out of range', { context: { offset: n, total: bytes.length } });
69
+ }
70
+ i = n; lookahead = null;
71
+ }
72
+
73
+ function readName() {
74
+ const start = i;
75
+ i++;
76
+ const out = [];
77
+ while (i < N) {
78
+ const b = bytes[i];
79
+ if (!isRegular(b)) break;
80
+ if (b === HASH) {
81
+ if (i + 2 >= N) {
82
+ throw new ParseError('pdf/tokenizer/bad-name-escape',
83
+ 'truncated #xx escape in name', { context: { offset: i } });
84
+ }
85
+ const hi = hexNibble(bytes[i + 1]);
86
+ const lo = hexNibble(bytes[i + 2]);
87
+ if (hi < 0 || lo < 0) {
88
+ throw new ParseError('pdf/tokenizer/bad-name-escape',
89
+ 'invalid hex digits in name escape', { context: { offset: i } });
90
+ }
91
+ out.push((hi << 4) | lo);
92
+ i += 3;
93
+ } else {
94
+ out.push(b); i++;
95
+ }
96
+ }
97
+ const value = new TextDecoder('utf-8', { fatal: false }).decode(Uint8Array.from(out));
98
+ return { kind: 'name', value, offset: start, end: i };
99
+ }
100
+
101
+ function readNumber() {
102
+ const start = i;
103
+ let isReal = false;
104
+ if (bytes[i] === PLUS || bytes[i] === MINUS) i++;
105
+ while (i < N) {
106
+ const b = bytes[i];
107
+ if (isDigit(b)) { i++; continue; }
108
+ if (b === DOT && !isReal) { isReal = true; i++; continue; }
109
+ break;
110
+ }
111
+ const raw = new TextDecoder('latin1').decode(bytes.subarray(start, i));
112
+ if (raw === '' || raw === '+' || raw === '-' || raw === '.') {
113
+ throw new ParseError('pdf/tokenizer/bad-number',
114
+ 'empty number token', { context: { offset: start } });
115
+ }
116
+ const n = Number(raw);
117
+ if (!Number.isFinite(n)) {
118
+ throw new ParseError('pdf/tokenizer/bad-number',
119
+ 'cannot parse number', { context: { raw, offset: start } });
120
+ }
121
+ return { kind: isReal ? 'real' : 'int', value: n, offset: start, end: i, raw };
122
+ }
123
+
124
+ function readLiteralString() {
125
+ const start = i;
126
+ i++;
127
+ const out = [];
128
+ let depth = 1;
129
+ while (i < N && depth > 0) {
130
+ const b = bytes[i];
131
+ if (b === LPAREN) { depth++; out.push(b); i++; continue; }
132
+ if (b === RPAREN) { depth--; if (depth === 0) { i++; break; } out.push(b); i++; continue; }
133
+ if (b === BACKSLASH) {
134
+ if (i + 1 >= N) {
135
+ throw new ParseError('pdf/tokenizer/bad-string',
136
+ 'trailing backslash in literal string', { context: { offset: i } });
137
+ }
138
+ const nxt = bytes[i + 1];
139
+ if (nxt === LF) { i += 2; continue; }
140
+ if (nxt === CR) {
141
+ i += 2;
142
+ if (i < N && bytes[i] === LF) i++;
143
+ continue;
144
+ }
145
+ if (nxt === 0x6E) { out.push(LF); i += 2; continue; }
146
+ if (nxt === 0x72) { out.push(CR); i += 2; continue; }
147
+ if (nxt === 0x74) { out.push(HT); i += 2; continue; }
148
+ if (nxt === 0x62) { out.push(0x08); i += 2; continue; }
149
+ if (nxt === 0x66) { out.push(FF); i += 2; continue; }
150
+ if (nxt === LPAREN || nxt === RPAREN || nxt === BACKSLASH) {
151
+ out.push(nxt); i += 2; continue;
152
+ }
153
+ if (nxt >= ZERO && nxt <= ZERO + 7) {
154
+ let v = 0, k = 0;
155
+ i++;
156
+ while (k < 3 && i < N && bytes[i] >= ZERO && bytes[i] <= ZERO + 7) {
157
+ v = (v << 3) | (bytes[i] - ZERO);
158
+ i++; k++;
159
+ }
160
+ out.push(v & 0xFF);
161
+ continue;
162
+ }
163
+ i++;
164
+ continue;
165
+ }
166
+ if (b === CR) {
167
+ out.push(LF);
168
+ i++;
169
+ if (i < N && bytes[i] === LF) i++;
170
+ continue;
171
+ }
172
+ out.push(b); i++;
173
+ }
174
+ if (depth !== 0) {
175
+ throw new ParseError('pdf/tokenizer/unterminated-string',
176
+ 'literal string was not closed', { context: { offset: start } });
177
+ }
178
+ return { kind: 'string', value: Uint8Array.from(out), offset: start, end: i };
179
+ }
180
+
181
+ function readHexString() {
182
+ const start = i;
183
+ i++;
184
+ const nibbles = [];
185
+ while (i < N) {
186
+ const b = bytes[i];
187
+ if (b === RANGLE) { i++; break; }
188
+ if (isWs(b)) { i++; continue; }
189
+ const v = hexNibble(b);
190
+ if (v < 0) {
191
+ throw new ParseError('pdf/tokenizer/bad-hex',
192
+ 'non-hex byte in hex string', { context: { offset: i, byte: b } });
193
+ }
194
+ nibbles.push(v); i++;
195
+ }
196
+ if (nibbles.length % 2 === 1) nibbles.push(0);
197
+ const out = new Uint8Array(nibbles.length / 2);
198
+ for (let k = 0; k < out.length; k++)
199
+ out[k] = (nibbles[2 * k] << 4) | nibbles[2 * k + 1];
200
+ return { kind: 'hex', value: out, offset: start, end: i };
201
+ }
202
+
203
+ function readKeyword() {
204
+ const start = i;
205
+ while (i < N && isRegular(bytes[i])) i++;
206
+ const raw = new TextDecoder('latin1').decode(bytes.subarray(start, i));
207
+ if (raw === '') {
208
+ throw new ParseError('pdf/tokenizer/empty-keyword',
209
+ 'empty keyword', { context: { offset: start } });
210
+ }
211
+ return { kind: 'kw', value: raw, offset: start, end: i };
212
+ }
213
+
214
+ function next() {
215
+ if (lookahead) { const t = lookahead; lookahead = null; return t; }
216
+ while (i < N) {
217
+ const b = bytes[i];
218
+ if (isWs(b)) {
219
+ if (keepWs) {
220
+ const start = i;
221
+ while (i < N && isWs(bytes[i])) i++;
222
+ return { kind: 'ws', offset: start, end: i };
223
+ }
224
+ i++; continue;
225
+ }
226
+ if (b === PERCENT) {
227
+ if (i + 4 < N
228
+ && bytes[i + 1] === PERCENT
229
+ && bytes[i + 2] === 0x45
230
+ && bytes[i + 3] === 0x4F
231
+ && bytes[i + 4] === 0x46) {
232
+ const start = i;
233
+ i += 5;
234
+ return { kind: 'eof_marker', offset: start, end: i };
235
+ }
236
+ while (i < N && !isEol(bytes[i])) i++;
237
+ if (i < N && bytes[i] === CR) i++;
238
+ if (i < N && bytes[i] === LF) i++;
239
+ continue;
240
+ }
241
+ break;
242
+ }
243
+ if (i >= N) return null;
244
+
245
+ const b = bytes[i];
246
+ if (b === SLASH) return readName();
247
+ if (b === LBRACK) { const off = i++; return { kind: 'open_arr', offset: off, end: i }; }
248
+ if (b === RBRACK) { const off = i++; return { kind: 'close_arr', offset: off, end: i }; }
249
+ if (b === LANGLE) {
250
+ if (i + 1 < N && bytes[i + 1] === LANGLE) {
251
+ const off = i; i += 2;
252
+ return { kind: 'open_dict', offset: off, end: i };
253
+ }
254
+ return readHexString();
255
+ }
256
+ if (b === RANGLE) {
257
+ if (i + 1 < N && bytes[i + 1] === RANGLE) {
258
+ const off = i; i += 2;
259
+ return { kind: 'close_dict', offset: off, end: i };
260
+ }
261
+ throw new ParseError('pdf/tokenizer/unexpected-rangle',
262
+ 'lone > outside hex string', { context: { offset: i } });
263
+ }
264
+ if (b === LPAREN) return readLiteralString();
265
+ if (b === PLUS || b === MINUS || b === DOT || isDigit(b)) return readNumber();
266
+ return readKeyword();
267
+ }
268
+
269
+ function peek() {
270
+ if (!lookahead) lookahead = next();
271
+ return lookahead;
272
+ }
273
+
274
+ return { next, peek, pos, seek, bytes };
275
+ }
276
+
277
+ function lastIndexOfBytes(bytes, needle, from) {
278
+ const N = bytes.length, M = needle.length;
279
+ if (M === 0 || M > N) return -1;
280
+ let start = Number.isFinite(from) ? Math.min(from | 0, N - M) : N - M;
281
+ outer: for (let i = start; i >= 0; i--) {
282
+ for (let k = 0; k < M; k++) if (bytes[i + k] !== needle[k]) continue outer;
283
+ return i;
284
+ }
285
+ return -1;
286
+ }
287
+
288
+ return { tokenize, lastIndexOfBytes };
289
+ }
290
+ };
@@ -0,0 +1,76 @@
1
+ // Copyright (c) 2026 AwaCloud SAS
2
+ // Author: Matthieu Bouilloux
3
+ // SPDX-License-Identifier: AGPL-3.0-only
4
+ // Dual-licensed; see the NOTICE file for licensing and any additional terms.
5
+
6
+ /**
7
+ * @fileoverview Trailer typing per ISO 32000-2:2020 §7.5.5.
8
+ *
9
+ * @module pdf/syntax/trailer
10
+ */
11
+
12
+ /**
13
+ * Module factory — worker-safe, self-contained.
14
+ */
15
+ import { pdfErrors } from '../errors.js';
16
+ import { pdfParserObj } from './parser-obj.js';
17
+
18
+ export const pdfTrailer = {
19
+ name: 'pdfTrailer',
20
+ dependencies: ['pdfErrors', 'pdfParserObj'],
21
+ deps: [pdfErrors, pdfParserObj],
22
+ factory(errors, parserObj) {
23
+ const { ParseError } = errors;
24
+ const { isType } = parserObj;
25
+
26
+ function typeTrailer(dict) {
27
+ if (!isType(dict, 'dict')) {
28
+ throw new ParseError('pdf/trailer/not-dict',
29
+ 'trailer is not a dictionary',
30
+ { context: { type: dict && dict.type } });
31
+ }
32
+ const e = dict.entries;
33
+
34
+ const size = e.Size;
35
+ if (!size || size.type !== 'int' || size.value < 0) {
36
+ throw new ParseError('pdf/trailer/missing-size',
37
+ 'trailer is missing required /Size entry',
38
+ { context: { hasSize: !!size } });
39
+ }
40
+
41
+ const root = e.Root;
42
+ if (!root || root.type !== 'ref') {
43
+ throw new ParseError('pdf/trailer/missing-root',
44
+ 'trailer is missing required /Root indirect reference',
45
+ { context: { hasRoot: !!root, type: root && root.type } });
46
+ }
47
+
48
+ const out = {
49
+ size: size.value,
50
+ root: { num: root.num, gen: root.gen },
51
+ raw: dict
52
+ };
53
+
54
+ if (e.Info && e.Info.type === 'ref') {
55
+ out.info = { num: e.Info.num, gen: e.Info.gen };
56
+ }
57
+ if (e.Prev && e.Prev.type === 'int') {
58
+ out.prev = e.Prev.value;
59
+ }
60
+ if (e.Encrypt) {
61
+ out.encrypt = e.Encrypt.type === 'ref'
62
+ ? { num: e.Encrypt.num, gen: e.Encrypt.gen }
63
+ : e.Encrypt;
64
+ }
65
+ if (e.ID && e.ID.type === 'array' && e.ID.items.length === 2
66
+ && e.ID.items[0].type === 'string'
67
+ && e.ID.items[1].type === 'string') {
68
+ out.id = [e.ID.items[0].value, e.ID.items[1].value];
69
+ }
70
+
71
+ return out;
72
+ }
73
+
74
+ return { typeTrailer };
75
+ }
76
+ };