@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,176 @@
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 Filter dispatch — applies a PDF filter chain
8
+ * (`/Filter` + `/DecodeParms`) on raw stream bytes per ISO 32000-2:2020
9
+ * §7.4.
10
+ *
11
+ * The dispatch map ships Flate/AHx/A85/RL (+ DCT/JPX/Crypt passthrough);
12
+ * LZW, CCITTFax and JBIG2 are not in the map; their implementations live on
13
+ * extras and are wired with `register(name, impl)` — no extra registers itself.
14
+ *
15
+ * @module pdf/syntax/filters/dispatch
16
+ */
17
+
18
+ /**
19
+ * Module factory — collects all registered decoders into one map.
20
+ * Worker-safe: factory body is self-contained.
21
+ */
22
+ import { pdfErrors } from '../../errors.js';
23
+ import { pdfFlate } from './flate.js';
24
+ import { pdfAsciiHex } from './asciiHex.js';
25
+ import { pdfAscii85 } from './ascii85.js';
26
+ import { pdfRunLength } from './runLength.js';
27
+
28
+ export const pdfFilterDispatch = {
29
+ name: 'pdfFilterDispatch',
30
+ dependencies: ['pdfErrors', 'pdfFlate', 'pdfAsciiHex', 'pdfAscii85', 'pdfRunLength'],
31
+ deps: [pdfErrors, pdfFlate, pdfAsciiHex, pdfAscii85, pdfRunLength],
32
+ factory(errors, flate, asciiHex, ascii85, runLength) {
33
+ const { ParseError } = errors;
34
+ function isType(node, kind) { return !!(node && node.type === kind); }
35
+
36
+ const ABBREV = {
37
+ Fl: 'FlateDecode',
38
+ AHx: 'ASCIIHexDecode',
39
+ A85: 'ASCII85Decode',
40
+ RL: 'RunLengthDecode',
41
+ LZW: 'LZWDecode',
42
+ DCT: 'DCTDecode',
43
+ JPX: 'JPXDecode',
44
+ CCF: 'CCITTFaxDecode'
45
+ };
46
+ function resolveAbbrev(name) { return ABBREV[name] || name; }
47
+ function passthrough() {
48
+ return {
49
+ decode(bytes) { return bytes; },
50
+ encode(bytes) { return bytes; }
51
+ };
52
+ }
53
+
54
+ function normalizeFilterList(filterEntry) {
55
+ if (!filterEntry) return [];
56
+ if (filterEntry.type === 'name') {
57
+ return [resolveAbbrev(filterEntry.value)];
58
+ }
59
+ if (filterEntry.type === 'array') {
60
+ const out = [];
61
+ for (const it of filterEntry.items) {
62
+ if (it.type !== 'name') {
63
+ throw new ParseError('pdf/filter/non-name',
64
+ '/Filter array entries must be names',
65
+ { context: { kind: it.type } });
66
+ }
67
+ out.push(resolveAbbrev(it.value));
68
+ }
69
+ return out;
70
+ }
71
+ throw new ParseError('pdf/filter/bad-type',
72
+ '/Filter must be a name or array of names',
73
+ { context: { type: filterEntry.type } });
74
+ }
75
+
76
+ function applyDecodeChain(bytes, filters, params, decoders) {
77
+ let cur = bytes;
78
+ for (let i = 0; i < filters.length; i++) {
79
+ const name = filters[i];
80
+ const p = params ? (params[i] || null) : null;
81
+ const dec = decoders[name];
82
+ if (!dec) {
83
+ throw new ParseError('pdf/filter/unsupported',
84
+ `filter "${name}" not registered`,
85
+ { context: { name, available: Object.keys(decoders) } });
86
+ }
87
+ cur = dec.decode(cur, p);
88
+ }
89
+ return cur;
90
+ }
91
+
92
+ function applyEncodeChain(bytes, filters, params, decoders) {
93
+ let cur = bytes;
94
+ for (let i = 0; i < filters.length; i++) {
95
+ const name = filters[i];
96
+ const p = params ? (params[i] || null) : null;
97
+ const enc = decoders[name];
98
+ if (!enc || typeof enc.encode !== 'function') {
99
+ throw new ParseError('pdf/filter/no-encoder',
100
+ `filter "${name}" has no encoder`,
101
+ { context: { name } });
102
+ }
103
+ cur = enc.encode(cur, p);
104
+ }
105
+ return cur;
106
+ }
107
+
108
+ // Marshal a typed dict node ({type:'dict', entries:{K: {type,value}}})
109
+ // to a plain key -> value object, per ISO 32000-2:2020 §7.4 — the
110
+ // decoders (e.g. pdfFlate's predictor) expect plain values, not
111
+ // typed AST nodes (a typed node read as plain silently no-ops the
112
+ // predictor).
113
+ function plainParams(dictNode) {
114
+ const entries = dictNode.entries || {};
115
+ const out = {};
116
+ for (const key of Object.keys(entries)) {
117
+ const v = entries[key];
118
+ out[key] = v ? v.value : undefined;
119
+ }
120
+ return out;
121
+ }
122
+
123
+ function decodeParmsList(entry, count) {
124
+ if (!entry) return null;
125
+ if (entry.type === 'null') return null;
126
+ if (entry.type === 'dict') {
127
+ const arr = new Array(count).fill(null);
128
+ if (count > 0) arr[0] = plainParams(entry);
129
+ return arr;
130
+ }
131
+ if (entry.type === 'array') {
132
+ return entry.items.map(it => (it.type === 'dict' ? plainParams(it) : null));
133
+ }
134
+ throw new ParseError('pdf/filter/bad-decodeparms',
135
+ '/DecodeParms must be a dict, array, or null',
136
+ { context: { type: entry.type } });
137
+ }
138
+
139
+ function decodeStream(streamObj, decoders) {
140
+ if (!isType(streamObj, 'stream')) {
141
+ throw new ParseError('pdf/filter/not-stream',
142
+ 'decodeStream expects a stream object');
143
+ }
144
+ const dict = streamObj.dict;
145
+ const filters = normalizeFilterList(dict.entries.Filter);
146
+ const params = decodeParmsList(dict.entries.DecodeParms, filters.length);
147
+ return applyDecodeChain(streamObj.raw, filters, params, decoders);
148
+ }
149
+
150
+ const decoders = {
151
+ FlateDecode: flate,
152
+ ASCIIHexDecode: asciiHex,
153
+ ASCII85Decode: ascii85,
154
+ RunLengthDecode: runLength,
155
+ DCTDecode: passthrough(),
156
+ JPXDecode: passthrough(),
157
+ Crypt: passthrough()
158
+ };
159
+ function decode(streamObj) { return decodeStream(streamObj, decoders); }
160
+ function decodeChain(bytes, filters, params) {
161
+ return applyDecodeChain(bytes, filters, params, decoders);
162
+ }
163
+ function encodeChain(bytes, filters, params) {
164
+ return applyEncodeChain(bytes, filters, params, decoders);
165
+ }
166
+ function register(name, impl) { decoders[name] = impl; }
167
+ function names() { return Object.keys(decoders); }
168
+ return {
169
+ decode, decodeChain, encodeChain, register, names, decoders,
170
+ normalizeFilterList,
171
+ applyDecodeChain,
172
+ applyEncodeChain,
173
+ decodeStream
174
+ };
175
+ }
176
+ };
@@ -0,0 +1,316 @@
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 FlateDecode filter wrapper.
8
+ *
9
+ * PDF FlateDecode (ISO 32000-2:2020 §7.4.4) uses zlib framing (RFC 1950).
10
+ * `@awacloud/fw/io/compress/zlib` produces and consumes this format.
11
+ *
12
+ * Supports the optional `Predictor` DecodeParms:
13
+ * - 1 : None (default)
14
+ * - 2 : TIFF Predictor 2
15
+ * - 10, 11, 12, 13, 14 : PNG None/Sub/Up/Average/Paeth (per row)
16
+ * - 15 : PNG optimum (encoder picks per row;
17
+ * decoder reads tag byte regardless)
18
+ *
19
+ * @module pdf/syntax/filters/flate
20
+ */
21
+
22
+ /**
23
+ * Module factory — worker-safe, self-contained.
24
+ *
25
+ * `fwZlib` is injected via the runtime dependency resolution.
26
+ */
27
+ import { pdfErrors } from '../../errors.js';
28
+ import { zlib } from '@awacloud/fw/io/compress/zlib.js';
29
+
30
+ export const pdfFlate = {
31
+ name: 'pdfFlate',
32
+ dependencies: ['pdfErrors', 'zlib'],
33
+ deps: [pdfErrors, zlib],
34
+ factory(errors, fwZlib) {
35
+ const { ParseError } = errors;
36
+ if (!fwZlib || typeof fwZlib.unzlibSync !== 'function'
37
+ || typeof fwZlib.zlibSync !== 'function') {
38
+ throw new ParseError('pdf/flate/missing-fw',
39
+ 'pdfFlate requires the @awacloud/fw zlib module',
40
+ { context: { keys: fwZlib && Object.keys(fwZlib) } });
41
+ }
42
+
43
+ function paeth(a, b, c) {
44
+ const p = a + b - c;
45
+ const pa = Math.abs(p - a);
46
+ const pb = Math.abs(p - b);
47
+ const pc = Math.abs(p - c);
48
+ if (pa <= pb && pa <= pc) return a;
49
+ if (pb <= pc) return b;
50
+ return c;
51
+ }
52
+
53
+ function predictorParams(params) {
54
+ const predictor = (params && params.predictor != null)
55
+ ? params.predictor | 0 : ((params && params.Predictor != null)
56
+ ? params.Predictor | 0 : 1);
57
+ const columns = (params && params.columns != null)
58
+ ? params.columns | 0 : ((params && params.Columns != null)
59
+ ? params.Columns | 0 : 1);
60
+ const colors = (params && params.colors != null)
61
+ ? params.colors | 0 : ((params && params.Colors != null)
62
+ ? params.Colors | 0 : 1);
63
+ const bpc = (params && params.bitsPerComponent != null)
64
+ ? params.bitsPerComponent | 0
65
+ : ((params && params.BitsPerComponent != null)
66
+ ? params.BitsPerComponent | 0 : 8);
67
+ if (predictor !== 1 && predictor !== 2
68
+ && (predictor < 10 || predictor > 15)) {
69
+ throw new ParseError('pdf/flate/bad-predictor',
70
+ 'unsupported Predictor value',
71
+ { context: { predictor } });
72
+ }
73
+ if (columns <= 0 || colors <= 0 || bpc <= 0) {
74
+ throw new ParseError('pdf/flate/bad-predictor-params',
75
+ 'Columns, Colors and BitsPerComponent must be > 0',
76
+ { context: { columns, colors, bpc } });
77
+ }
78
+ // Stride/row size in bytes — we support byte-aligned configs
79
+ // (bpc multiple of 8) for the predictor codec. Sub-byte
80
+ // configurations decode the raw bytes per row but apply
81
+ // byte-wise PNG filters (TIFF Predictor 2 requires bpc=8).
82
+ const bitsPerPixel = colors * bpc;
83
+ const bytesPerPixel = Math.max(1, (bitsPerPixel + 7) >> 3);
84
+ const rowBytes = ((columns * bitsPerPixel) + 7) >> 3;
85
+ return { predictor, columns, colors, bpc, bytesPerPixel, rowBytes };
86
+ }
87
+
88
+ function pngUnfilterRow(filter, row, prevRow, bpp) {
89
+ const out = new Uint8Array(row.length);
90
+ for (let i = 0; i < row.length; i++) {
91
+ const left = i >= bpp ? out[i - bpp] : 0;
92
+ const up = prevRow ? prevRow[i] : 0;
93
+ const upLeft = (prevRow && i >= bpp) ? prevRow[i - bpp] : 0;
94
+ let v = row[i];
95
+ switch (filter) {
96
+ case 0: break; // None
97
+ case 1: v = (v + left) & 0xFF; break; // Sub
98
+ case 2: v = (v + up) & 0xFF; break; // Up
99
+ case 3: v = (v + ((left + up) >> 1)) & 0xFF; break; // Avg
100
+ case 4: v = (v + paeth(left, up, upLeft)) & 0xFF; break;
101
+ default:
102
+ throw new ParseError('pdf/flate/bad-png-filter',
103
+ 'unknown PNG filter tag',
104
+ { context: { tag: filter } });
105
+ }
106
+ out[i] = v;
107
+ }
108
+ return out;
109
+ }
110
+
111
+ function pngFilterRow(filter, row, prevRow, bpp) {
112
+ const out = new Uint8Array(row.length);
113
+ for (let i = 0; i < row.length; i++) {
114
+ const left = i >= bpp ? row[i - bpp] : 0;
115
+ const up = prevRow ? prevRow[i] : 0;
116
+ const upLeft = (prevRow && i >= bpp) ? prevRow[i - bpp] : 0;
117
+ const v = row[i];
118
+ switch (filter) {
119
+ case 0: out[i] = v; break;
120
+ case 1: out[i] = (v - left) & 0xFF; break;
121
+ case 2: out[i] = (v - up) & 0xFF; break;
122
+ case 3: out[i] = (v - ((left + up) >> 1)) & 0xFF; break;
123
+ case 4: out[i] = (v - paeth(left, up, upLeft)) & 0xFF; break;
124
+ default:
125
+ throw new ParseError('pdf/flate/bad-png-filter',
126
+ 'unknown PNG filter tag',
127
+ { context: { tag: filter } });
128
+ }
129
+ }
130
+ return out;
131
+ }
132
+
133
+ function tiff2Decode(bytes, p) {
134
+ // TIFF Predictor 2: each sample = previous-sample-in-row + cur.
135
+ // Operates per row, per component, byte-aligned (bpc=8).
136
+ if (p.bpc !== 8) {
137
+ throw new ParseError('pdf/flate/tiff-bpc-unsupported',
138
+ 'TIFF Predictor 2 requires BitsPerComponent=8',
139
+ { context: { bpc: p.bpc } });
140
+ }
141
+ const out = new Uint8Array(bytes.length);
142
+ const stride = p.colors;
143
+ const rowBytes = p.rowBytes;
144
+ const rows = (bytes.length / rowBytes) | 0;
145
+ for (let r = 0; r < rows; r++) {
146
+ const base = r * rowBytes;
147
+ for (let i = 0; i < rowBytes; i++) {
148
+ const left = i >= stride ? out[base + i - stride] : 0;
149
+ out[base + i] = (bytes[base + i] + left) & 0xFF;
150
+ }
151
+ }
152
+ return out;
153
+ }
154
+
155
+ function tiff2Encode(bytes, p) {
156
+ if (p.bpc !== 8) {
157
+ throw new ParseError('pdf/flate/tiff-bpc-unsupported',
158
+ 'TIFF Predictor 2 requires BitsPerComponent=8',
159
+ { context: { bpc: p.bpc } });
160
+ }
161
+ const out = new Uint8Array(bytes.length);
162
+ const stride = p.colors;
163
+ const rowBytes = p.rowBytes;
164
+ const rows = (bytes.length / rowBytes) | 0;
165
+ for (let r = 0; r < rows; r++) {
166
+ const base = r * rowBytes;
167
+ for (let i = 0; i < rowBytes; i++) {
168
+ const left = i >= stride ? bytes[base + i - stride] : 0;
169
+ out[base + i] = (bytes[base + i] - left) & 0xFF;
170
+ }
171
+ }
172
+ return out;
173
+ }
174
+
175
+ function pngDecode(bytes, p) {
176
+ const rowBytes = p.rowBytes;
177
+ const stride = rowBytes + 1; // +1 for filter tag
178
+ if (bytes.length % stride !== 0) {
179
+ throw new ParseError('pdf/flate/png-row-mismatch',
180
+ 'predicted stream length is not a multiple of (rowBytes+1)',
181
+ { context: { length: bytes.length, stride } });
182
+ }
183
+ const rows = bytes.length / stride;
184
+ const out = new Uint8Array(rows * rowBytes);
185
+ let prev = null;
186
+ for (let r = 0; r < rows; r++) {
187
+ const tag = bytes[r * stride];
188
+ const row = bytes.subarray(r * stride + 1, r * stride + stride);
189
+ const unfiltered = pngUnfilterRow(tag, row, prev, p.bytesPerPixel);
190
+ out.set(unfiltered, r * rowBytes);
191
+ prev = unfiltered;
192
+ }
193
+ return out;
194
+ }
195
+
196
+ function pngEncode(bytes, p) {
197
+ // For predictors 10..14, force the single filter type
198
+ // (Predictor - 10). For Predictor 15 (optimum), use Up for
199
+ // rows after the first, None for the first — a simple and
200
+ // deterministic heuristic; downstream decoders read the tag.
201
+ const rowBytes = p.rowBytes;
202
+ if (bytes.length % rowBytes !== 0) {
203
+ throw new ParseError('pdf/flate/png-row-mismatch',
204
+ 'input length is not a multiple of rowBytes',
205
+ { context: { length: bytes.length, rowBytes } });
206
+ }
207
+ const rows = bytes.length / rowBytes;
208
+ const outStride = rowBytes + 1;
209
+ const out = new Uint8Array(rows * outStride);
210
+ const forced = p.predictor === 15 ? -1 : (p.predictor - 10);
211
+ let prev = null;
212
+ for (let r = 0; r < rows; r++) {
213
+ const row = bytes.subarray(r * rowBytes, (r + 1) * rowBytes);
214
+ let tag = forced;
215
+ if (tag < 0) tag = r === 0 ? 0 : 2; // optimum heuristic
216
+ const filtered = pngFilterRow(tag, row, prev, p.bytesPerPixel);
217
+ out[r * outStride] = tag;
218
+ out.set(filtered, r * outStride + 1);
219
+ prev = row;
220
+ }
221
+ return out;
222
+ }
223
+
224
+ // fw's inflate reports "input exhausted before the final block" as
225
+ // error code 0 ('unexpected EOF'); every other failure has its own code.
226
+ function endedBeforeFinalBlock(e) {
227
+ return !!e && e.code === 0;
228
+ }
229
+
230
+ // Inflate `bytes` through the streaming decoder WITHOUT signalling the
231
+ // end of input, and return everything it emitted (possibly empty).
232
+ function inflatePrefix(bytes) {
233
+ const chunks = [];
234
+ let total = 0;
235
+ const stream = new fwZlib.UnzlibStream((chunk) => {
236
+ chunks.push(chunk);
237
+ total += chunk.length;
238
+ });
239
+ stream.push(bytes, false);
240
+ const out = new Uint8Array(total);
241
+ let off = 0;
242
+ for (const chunk of chunks) { out.set(chunk, off); off += chunk.length; }
243
+ return out;
244
+ }
245
+
246
+ function markTruncated(out) {
247
+ Object.defineProperty(out, 'truncated', { value: true, enumerable: false });
248
+ return out;
249
+ }
250
+
251
+ function decode(bytes, params) {
252
+ if (!(bytes instanceof Uint8Array)) {
253
+ throw new ParseError('pdf/flate/bad-input',
254
+ 'FlateDecode expects Uint8Array');
255
+ }
256
+ let raw;
257
+ let truncated = false;
258
+ try {
259
+ raw = fwZlib.unzlibSync(bytes);
260
+ } catch (e) {
261
+ const inflateFailed = () => new ParseError('pdf/flate/inflate-failed',
262
+ 'inflate failed: ' + (e && e.message),
263
+ { context: { length: bytes.length }, cause: e });
264
+ if (!endedBeforeFinalBlock(e)
265
+ || typeof fwZlib.UnzlibStream !== 'function') {
266
+ throw inflateFailed();
267
+ }
268
+ // A stream that ends before its final deflate block yields the
269
+ // bytes decoded so far, flagged `truncated` (viewers do the same).
270
+ let prefix;
271
+ try { prefix = inflatePrefix(bytes); } catch { throw inflateFailed(); }
272
+ if (prefix.length === 0) throw inflateFailed();
273
+ raw = prefix;
274
+ truncated = true;
275
+ }
276
+ const done = (out) => (truncated ? markTruncated(out) : out);
277
+ if (!params) return done(raw);
278
+ const hasPredictor = (params.predictor != null && params.predictor !== 1)
279
+ || (params.Predictor != null && params.Predictor !== 1);
280
+ if (!hasPredictor) return done(raw);
281
+ const p = predictorParams(params);
282
+ if (p.predictor === 1) return done(raw);
283
+ // A truncated prefix keeps only its whole predicted rows; a complete
284
+ // stream keeps the strict row check.
285
+ const stride = p.predictor === 2 ? p.rowBytes : p.rowBytes + 1;
286
+ if (truncated) raw = raw.subarray(0, raw.length - (raw.length % stride));
287
+ if (p.predictor === 2) return done(tiff2Decode(raw, p));
288
+ return done(pngDecode(raw, p));
289
+ }
290
+
291
+ function encode(bytes, params) {
292
+ if (!(bytes instanceof Uint8Array)) {
293
+ throw new ParseError('pdf/flate/bad-input',
294
+ 'FlateEncode expects Uint8Array');
295
+ }
296
+ let payload = bytes;
297
+ if (params) {
298
+ const hasPredictor = (params.predictor != null && params.predictor !== 1)
299
+ || (params.Predictor != null && params.Predictor !== 1);
300
+ if (hasPredictor) {
301
+ const p = predictorParams(params);
302
+ if (p.predictor === 2) payload = tiff2Encode(bytes, p);
303
+ else if (p.predictor >= 10) payload = pngEncode(bytes, p);
304
+ }
305
+ }
306
+ try {
307
+ return fwZlib.zlibSync(payload);
308
+ } catch (e) {
309
+ throw new ParseError('pdf/flate/deflate-failed',
310
+ 'deflate failed: ' + (e && e.message),
311
+ { context: { length: payload.length }, cause: e });
312
+ }
313
+ }
314
+ return { decode, encode };
315
+ }
316
+ };
@@ -0,0 +1,96 @@
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 RunLengthDecode filter per ISO 32000-2:2020 §7.4.5.
8
+ *
9
+ * Each control byte `n` is interpreted as:
10
+ * - `0..127` → copy the next `n + 1` bytes literally,
11
+ * - `128` → end-of-data marker (stop decoding),
12
+ * - `129..255` → repeat the next byte `257 - n` times (2..128 times).
13
+ *
14
+ * @module pdf/syntax/filters/runLength
15
+ */
16
+
17
+ /**
18
+ * Module factory — worker-safe.
19
+ */
20
+ import { pdfErrors } from '../../errors.js';
21
+
22
+ export const pdfRunLength = {
23
+ name: 'pdfRunLength',
24
+ dependencies: ['pdfErrors'],
25
+ deps: [pdfErrors],
26
+ factory(errors) {
27
+ const { ParseError } = errors;
28
+ function decode(bytes) {
29
+ if (!(bytes instanceof Uint8Array)) {
30
+ throw new ParseError('pdf/runLength/bad-input',
31
+ 'RunLengthDecode expects Uint8Array');
32
+ }
33
+ const out = [];
34
+ let i = 0;
35
+ while (i < bytes.length) {
36
+ const n = bytes[i++];
37
+ if (n === 128) break;
38
+ if (n < 128) {
39
+ const count = n + 1;
40
+ if (i + count > bytes.length) {
41
+ throw new ParseError('pdf/runLength/truncated-literal',
42
+ 'truncated literal run',
43
+ { context: { offset: i - 1, want: count, have: bytes.length - i } });
44
+ }
45
+ for (let k = 0; k < count; k++) out.push(bytes[i + k]);
46
+ i += count;
47
+ } else {
48
+ if (i >= bytes.length) {
49
+ throw new ParseError('pdf/runLength/truncated-repeat',
50
+ 'truncated repeat run',
51
+ { context: { offset: i - 1 } });
52
+ }
53
+ const v = bytes[i++];
54
+ const count = 257 - n;
55
+ for (let k = 0; k < count; k++) out.push(v);
56
+ }
57
+ }
58
+ return Uint8Array.from(out);
59
+ }
60
+
61
+ function encode(bytes) {
62
+ if (!(bytes instanceof Uint8Array)) {
63
+ throw new ParseError('pdf/runLength/bad-input',
64
+ 'RunLengthEncode expects Uint8Array');
65
+ }
66
+ const out = [];
67
+ let i = 0;
68
+ while (i < bytes.length) {
69
+ const v = bytes[i];
70
+ let runLen = 1;
71
+ while (runLen < 128 && i + runLen < bytes.length && bytes[i + runLen] === v) {
72
+ runLen++;
73
+ }
74
+ if (runLen >= 3) {
75
+ out.push(257 - runLen, v);
76
+ i += runLen;
77
+ continue;
78
+ }
79
+ let litStart = i;
80
+ let litLen = 0;
81
+ while (litLen < 128 && i < bytes.length) {
82
+ let look = 1;
83
+ while (look < 3 && i + look < bytes.length && bytes[i + look] === bytes[i]) look++;
84
+ if (look >= 3 && litLen > 0) break;
85
+ i++; litLen++;
86
+ }
87
+ out.push(litLen - 1);
88
+ for (let k = 0; k < litLen; k++) out.push(bytes[litStart + k]);
89
+ }
90
+ out.push(128);
91
+ return Uint8Array.from(out);
92
+ }
93
+
94
+ return { decode, encode };
95
+ }
96
+ };
@@ -0,0 +1,99 @@
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 Object Stream parser per ISO 32000-2:2020 §7.5.7.
8
+ *
9
+ * @module pdf/syntax/objStream
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
+ import { pdfTokenizer } from './tokenizer.js';
18
+ import { pdfParser } from './parser.js';
19
+
20
+ export const pdfObjStream = {
21
+ name: 'pdfObjStream',
22
+ dependencies: ['pdfErrors', 'pdfParserObj', 'pdfTokenizer', 'pdfParser'],
23
+ deps: [pdfErrors, pdfParserObj, pdfTokenizer, pdfParser],
24
+ factory(errors, parserObj, tokenizerMod, parserMod) {
25
+ const { ParseError } = errors;
26
+ const { isType } = parserObj;
27
+ const tokenize = tokenizerMod.tokenize;
28
+ const parseObject = parserMod.parseObject;
29
+
30
+ function readInt(entry, label) {
31
+ if (!entry || entry.type !== 'int') {
32
+ throw new ParseError('pdf/objstm/missing-int',
33
+ `ObjStm dict missing required /${label}`,
34
+ { context: { label, hasEntry: !!entry, type: entry && entry.type } });
35
+ }
36
+ return entry.value;
37
+ }
38
+
39
+ function parseObjectStream(decoded, dict) {
40
+ if (!(decoded instanceof Uint8Array)) {
41
+ throw new ParseError('pdf/objstm/bad-input',
42
+ 'parseObjectStream expects Uint8Array',
43
+ { context: { typeof: typeof decoded } });
44
+ }
45
+ if (!isType(dict, 'dict')) {
46
+ throw new ParseError('pdf/objstm/bad-dict',
47
+ 'ObjStm dict must be a typed dict');
48
+ }
49
+ const e = dict.entries;
50
+ if (e.Type && (e.Type.type !== 'name' || e.Type.value !== 'ObjStm')) {
51
+ throw new ParseError('pdf/objstm/wrong-type',
52
+ '/Type must be /ObjStm', { context: { actual: e.Type.value } });
53
+ }
54
+ const N = readInt(e.N, 'N');
55
+ const First = readInt(e.First, 'First');
56
+ if (N < 0) {
57
+ throw new ParseError('pdf/objstm/bad-N',
58
+ '/N must be non-negative', { context: { N } });
59
+ }
60
+ if (First < 0 || First > decoded.length) {
61
+ throw new ParseError('pdf/objstm/bad-First',
62
+ '/First out of payload range',
63
+ { context: { First, length: decoded.length } });
64
+ }
65
+
66
+ const header = tokenize(decoded, { start: 0, end: First });
67
+ const nums = new Array(N);
68
+ const offs = new Array(N);
69
+ for (let i = 0; i < N; i++) {
70
+ const tNum = header.next();
71
+ const tOff = header.next();
72
+ if (!tNum || tNum.kind !== 'int' || !tOff || tOff.kind !== 'int') {
73
+ throw new ParseError('pdf/objstm/bad-pair',
74
+ 'ObjStm header expects N pairs of integers',
75
+ { context: { index: i } });
76
+ }
77
+ nums[i] = tNum.value;
78
+ offs[i] = tOff.value;
79
+ }
80
+
81
+ const out = new Array(N);
82
+ for (let i = 0; i < N; i++) {
83
+ const start = First + offs[i];
84
+ const end = (i + 1 < N) ? (First + offs[i + 1]) : decoded.length;
85
+ if (start < First || end > decoded.length || start > end) {
86
+ throw new ParseError('pdf/objstm/bad-offset',
87
+ 'ObjStm member offset out of range',
88
+ { context: { index: i, start, end, length: decoded.length } });
89
+ }
90
+ const tok = tokenize(decoded, { start, end });
91
+ const value = parseObject(tok);
92
+ out[i] = { num: nums[i], gen: 0, value };
93
+ }
94
+ return out;
95
+ }
96
+
97
+ return { parseObjectStream };
98
+ }
99
+ };