@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
package/CHANGELOG.md ADDED
@@ -0,0 +1,609 @@
1
+ # Changelog
2
+
3
+ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
4
+
5
+ Spec reference: ISO 32000-2:2020 (PDF 2.0). Legacy read tolerance:
6
+ ISO 32000-1:2008 (PDF 1.7).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [1.0.0] - 2026-10-07
11
+
12
+ ### Added
13
+
14
+ - **Core surface (L0)** — typed object graph + classical xref + page-tree
15
+ walker. `pdfErrors` exposes `PdfError`, `ParseError`, `RenderError`,
16
+ `ContractError`, `EncryptionError`; every throw in the package uses these
17
+ classes with a kebab-case, namespaced `code` and a structured `context`.
18
+ `pdfTokenizer` (binary lexer, ISO 32000-2 §7.2), `pdfParser`/`pdfParserObj`/
19
+ `pdfParserStream` (typed object parser, §7.3 — `null`/`bool`/`int`/`real`/
20
+ `name`/`string`/`array`/`dict`/`ref`/`stream`), `pdfXref` (classical xref,
21
+ §7.5.4), `pdfTrailer`, `pdfCatalog` (§7.7.2), `pdfPages` (balanced
22
+ page-tree walker, cycle + depth detection), `pdfPage` (§7.7.3.3),
23
+ `pdfDocument` (top-level reader, `/Prev` xref chaining). Top-level `pdf`
24
+ factory exposes `.read()`, `.header()`, `.use(...)` (idempotent by name),
25
+ `.usedExtension()`, `.write()`.
26
+ - **Writer + filters (L1)** — `pdfSerializer` (typed-object → bytes,
27
+ canonical real-number formatting, `#xx` name escapes, automatic hex form
28
+ for binary strings), `pdfWriter` (`writeDocument(...)` emits the header it
29
+ is given, `%PDF-2.0` by default),
30
+ `pdfBuilder` (constructive DSL — `addPage`/`addContent`/`addFont`/
31
+ `addMetadata`/`setVersion`/`setId`/`.build()` — builds a document
32
+ from-scratch with no `pdf.read()` upstream). Five filters: `pdfFlate`
33
+ (delegates to `@awacloud/fw/io/compress/zlib`, full `/Predictor` PNG (10–15,
34
+ incl. optimum) + TIFF (2) encode **and** decode), `pdfAsciiHex`,
35
+ `pdfAscii85` (incl. `z` shorthand + `~>` EOD), `pdfRunLength`,
36
+ `pdfFilterDispatch` (`/Filter`+`/DecodeParms` chain walker, abbreviation
37
+ table, DCT/JPX passthrough). Compressed-object readers `pdfObjStream`
38
+ (§7.5.7) and `pdfCrossRefStream` (§7.5.8).
39
+ - **Content streams + fonts + AcroForm (L2)** — `pdfContentStream` (~70
40
+ operators per ISO 32000-2 Table 60, inline-image `BI…ID…EI` capture),
41
+ `pdfContentOps` (operator catalogue), `pdfGraphics` (`GStateStack`,
42
+ `q`/`Q`, CTM), `pdfText` (Td/TD/Tm/T* tracking, `extractText`),
43
+ `pdfColor`, `pdfImages` (XObject typing), `pdfResources` (page-tree
44
+ parent-chain resolution). Font glue to `@awacloud/fonts`: `pdfFont` (every
45
+ ISO subtype), `pdfFontEncoding`, `pdfType3`, `pdfFontEmbed` (single
46
+ adapter to `@awacloud/fonts/embed-pdf` for subset embedding). AcroForm
47
+ baseline: `pdfAcroForm`, `pdfFieldTree`, `pdfButtonField`, `pdfTextField`,
48
+ `pdfChoiceField`, `pdfSignatureField`, `pdfAppearance`.
49
+ - **Annotations, tagged PDF, OCG, outlines/actions/destinations, embedded
50
+ files, linearization, metadata, prepress, associated files (L3)** —
51
+ `pdfAnnot` orchestrator + 12 subtype typers (Text, Link, FreeText,
52
+ shape family via `pdfShapeAnnot`, markup family via `pdfMarkupAnnot`,
53
+ Ink, Stamp, FileAttachment, Widget, Popup, Projection, Redact —
54
+ ISO 32005). `pdfStructTree`/`pdfStructElement`/`pdfRoleMap`/
55
+ `pdfParentTree`/`pdfClassMap`/`pdfMarkedContent` (StructTreeRoot
56
+ walking, heterogeneous `/K` children, RoleMap + Namespaces per Table
57
+ 364–365, MCID resolution + extraction). `pdfOCG`/`pdfOCConfig`.
58
+ `pdfOutline`, `pdfDestination`, `pdfAction` + GoTo/GoToR/GoToE/URI/
59
+ Named/Launch typers. `pdfFileSpec`/`pdfEmbeddedFile`/`pdfCollection`
60
+ (PDF Portfolio). `pdfLinearization` (types the `/Linearized` parameter
61
+ dictionary, read-only). `pdfInfo`/`pdfXmp`.
62
+ `pdfOutputIntent`/`pdfPageBoundary` (PDF/A, PDF/X, PDF/E; effective
63
+ Crop/Trim/Bleed/Art box resolution). `pdfAssociatedFiles` (`/AF` +
64
+ `/AFRelationship`, PDF 2.0).
65
+ - **Encryption, decrypt + encrypt (L3)** — `pdfSecurity` (Security Handler
66
+ dispatcher), `pdfStandardV4` (PDF 1.6/ISO 32000-1 §7.6.3 — AESV2
67
+ AES-128-CBC and V2 RC4-128, `/O`/`/U` derivation, per-object key
68
+ derivation, string/stream/embedded-file encrypt **and** decrypt
69
+ round-trip), `pdfStandardV5` (PDF 1.7 — AES-256-CBC + SHA-256),
70
+ `pdfStandardV6` (PDF 2.0 — Algorithm 2.B hardening loop + Algorithm 8
71
+ FEK unwrap, SHA-256/384/512 selection), `pdfPermissions` (`/Perms`
72
+ Algorithm 13), `pdfAesGcm` (TS 32003 AES-GCM crypt filter, decode
73
+ **and** encode), `pdfEncryptedWriter` (`writeDocument`-style encrypted
74
+ write path — V4/V5 R=5/V6 R=6, `/CFM AESV4` GCM opt-in). All crypto via
75
+ `@awacloud/fw/crypto/*` (pure JS, worker-safe). `read` does not decrypt:
76
+ it refuses an encrypted file unless `allowEncrypted: true` is passed, and
77
+ the handlers are then called by the caller.
78
+ - **Digital signatures, verify + generate** —
79
+ `pdfSignature`/`pdfByteRange`/`pdfTimestamp`/`pdfCertChain`/
80
+ `pdfDssBuilder`: Sig/DocTimeStamp typing, PKCS#7 detached blob locator,
81
+ ByteRange compute/extract, RFC 3161 TSP parser, X.509 cert chain
82
+ extraction (CMS SignedData + PEM), structural chain ordering check, DSS
83
+ dict typing. **`verifySignature(...)` performs full public-key
84
+ verification** — `verifyPk` wires RSA-PSS (PKCS#1 v1.5 refused per fw
85
+ policy), ECDSA (P-256/P-384/P-521), Ed25519; the result carries
86
+ `verified: true` (alias `valid`), `pkVerified: true`, `computedDigest`
87
+ when the digest chain and PK-verify both succeed — the digest-only
88
+ structural pass this superseded is gone (see Security).
89
+ **`pdfSign.sign(pdfBytes, opts)`** generates PAdES signatures at all
90
+ four levels: **B** (single embedded PKCS#7, no TSA), **T** (adds an
91
+ RFC 3161 timestamp token via a caller-supplied
92
+ `opts.tsaSign({ digest, hashAlg })` callback), **LT** (appends a `/DSS`
93
+ dict with certs via `pdfIncrementalWriter`), **LTA** (appends a
94
+ `/DocTimeStamp` on top of the LT bytes). `verifyAllSignatures` /
95
+ `validateBLtaChain` round-trip a full B→T→LT→LTA chain end-to-end
96
+ (`tsaVerified`/`imprintVerified` on the reconstructed `DocTimeStamp`).
97
+ `validateDocMdp(ref)` validates MDP `/P` 1/2/3 + `/DigestMethod` +
98
+ `/V` `1.2`/`2.2` tolerance. Algorithms: RSA-PSS, ECDSA, Ed25519 across
99
+ both verify and sign.
100
+ - **Constructive + incremental writers** — `pdfIncrementalWriter`
101
+ .`appendIncremental(pdfBytes, updates)` emits `originalBytes ‖
102
+ updatedObjs ‖ xref ‖ trailer ‖ %%EOF` with a `/Prev` chain (the
103
+ mechanism `pdfSign`'s LT/LTA levels build on). `pdfXrefStreamWriter`
104
+ (`src/document/xrefStreamWriter.js`) implements
105
+ `writeXrefStreamDocument(model, opts)` — native `/Type /XRef` emission
106
+ (PDF 1.5+/2.0) with an opt-in `useObjStm` grouping non-stream indirects
107
+ into compressed `/Type /ObjStm` wrappers (§7.5.7). It is registered in
108
+ `src/main.js` `modules`, re-exported by name from the root entry, and
109
+ ships in every Read+Write `dist/` root.
110
+ - **Parser hardening** — `parserLimits` (`maxDepth: 200`,
111
+ `maxArrayLen: 1_000_000`, `maxStreamBytes: 256 MiB`), adjustable at
112
+ runtime via `setParserLimits(partial)`; `findEndstream` accepts an
113
+ explicit `maxScanBytes`.
114
+ - **Shared helper factory `pdfShared`** (`src/_shared/index.js`) —
115
+ canonical magic bytes (`HEADER_PREFIX`, `EOF_MARKER`, `BINARY_MARKER`),
116
+ frozen `ASCII` byte-constant table, character-class predicates per
117
+ §7.2 (`isWs`/`isEol`/`isDigit`/`isHex`/`isDelim`/`isRegular`/
118
+ `hexNibble`), the `HEX_LO` lookup table, codec instances and wrappers
119
+ (`encodeAscii`/`decodeUtf8`/`decodeUtf8Lenient`/`decodeLatin1`), byte
120
+ helpers (`pad10`/`hexLit`/`bytesEqual`/`concatBytes`). `pdfTokenizer`
121
+ and the top-level `pdf` declare it as a dependency; the filters, the
122
+ writer and the serializer still carry their own inline copies of some of
123
+ these helpers. `pdfSigOids` consolidates the OID → dispatch-label tables
124
+ (`DIGEST_OIDS`, `SIG_DISPATCH_OIDS`, `KEY_ALG_OIDS`,
125
+ `SIG_ALG_OIDS_VERBOSE`, `OID_TST_INFO`, `OID_AA_TIMESTAMP`, `shortOid`)
126
+ previously duplicated across `signature.js`/`timestamp.js`/`certChain.js`
127
+ into one factory, which all three now declare as a dependency.
128
+ - **Coverage extras** (opt-in, the `extras` array of `src/main.js` — 32
129
+ modules under `src/extra/` — tree-shaken when unused) reach the PDF 2.0
130
+ long-tail + PDF 1.7 read tolerance:
131
+ **P0** (common) — `content-ops-extended`,
132
+ `font-cid-typed`, `font-color-tagging`, `tagged-pdf-typed`,
133
+ `annot-extended`, `pdf-a-output-intent`, `pdf-ua-tagged`. **P1**
134
+ (extended) — `form-actions-extended`, `color-spaces-extended`,
135
+ `shading-typed`, `transparency-typed`, `sig-pades`, `sig-aes-gcm`,
136
+ `document-parts` (ISO TS 32004), `redaction-iso32005`,
137
+ `pdf-x-prepress`, `well-tagged-pdf` (WTPDF 1.0),
138
+ `optional-content-extended`, `embedded-files-portfolio`,
139
+ `associated-files`, `xmp-extended`. **P2** (tail) —
140
+ `linearization-write` (builds the `/Linearized` dictionary and a
141
+ zero-length hint-stream placeholder; it does not produce a linearized
142
+ file), `3d-richmedia`, `jbig2-read` (segment-header enumeration, decode
143
+ intentionally not implemented), `legacy-xfa-read`,
144
+ `legacy-rc4-read` (RC4 known-answer-test verified),
145
+ `legacy-deprecated-filters` (LZWDecode via fw + DCT/JPX passthrough;
146
+ CCITTFax delegates to the sibling `ccitt-fax-decoder` codec — see
147
+ below), `legacy-deprecated-annots` (Sound/Movie/Screen typing). **P3**
148
+ (misc) — `misc` (SpiderInfo/Threads/Legal/Requirements/NeedsRendering),
149
+ `info-dict-deprecated` (Info dict lint). Plus two modules added after
150
+ the initial P0–P3 pass: **`pdf-sandbox`** (`pdfSandbox.lintActions(...)`
151
+ — classifies `/Launch`/`/JavaScript`/`/SubmitForm`/`/ImportData`/`/URI`,
152
+ bundled opt-in via `pdf-full`) and **`ccitt-fax-decoder`** (a **full
153
+ ITU-T T.4/T.6 codec**, encode + decode, for K<0 Group 4, K=0 Group 3
154
+ 1D, K>0 Group 3 mixed — split out once the original header-only stub
155
+ was completed; consumed by `legacy-deprecated-filters` as a
156
+ dependency, and covered by both suites' tests). The legacy decoders are
157
+ called through their extras' own API; they are not registered into
158
+ `pdfFilterDispatch`.
159
+ - **Bundles** — three ergonomic compositions of core + extras, each a
160
+ pure fw descriptor consumed via `ModuleRuntime.resolve(...)`:
161
+ **`pdfLargeBundle`** (`@awacloud/pdf/pdf-large`, P0+P1, the common PDF
162
+ 2.0 features), **`pdfFullBundle`** (`@awacloud/pdf/pdf-full`, +P2+P3,
163
+ every PDF 2.0 extra), **`pdfLegacyBundle`** (`@awacloud/pdf/pdf-legacy`, +
164
+ `legacy-*` family, reads PDF 1.7; `write` still emits a `%PDF-2.0`
165
+ header and re-emits legacy content — XFA, RC4 encryption, LZW streams,
166
+ Sound/Movie annotations — as it was read, without converting it). Each
167
+ resolved bundle exposes `.read`, `.write`, `.use`, `.usedExtension`,
168
+ `.header`, and every wired extra under its factory name; re-applying a
169
+ bundle is a no-op. See `docs/api/bundles/README.md` for the per-bundle
170
+ extras breakdown.
171
+ - **Pre-built bundles — two-surface `dist/`, Read and Read+Write families.**
172
+ `tools/generate-bundles.mjs` (`bun run gen:bundles`, a thin wrapper
173
+ around `@awacloud/tool-prebuild-generator`) emits two path-discriminated
174
+ surfaces side by side under `dist/`: `dist/standalone/<root>.
175
+ {js,min.js,meta.json}` (`dependencies: []`, every fw + pdf-local
176
+ factory inlined, zero runtime registration) and
177
+ `dist/build/<root>.{js,min.js,meta.json}` (declares the fw modules as
178
+ dependencies, inlines only the pdf-local factories, smallest payload).
179
+ The roots form a two-family × size matrix: the four assembly roots
180
+ (`pdf`, `pdf-large`, `pdf-full`, `pdf-legacy`) are the **Read** family,
181
+ and four `-rw` roots (`pdf-rw`, `pdf-large-rw`, `pdf-full-rw`,
182
+ `pdf-legacy-rw`) form the **Read+Write** family — each the Read root's
183
+ segment plus the write inventory, with `pdfXrefStreamWriter` shipping in
184
+ every `-rw` root — on both surfaces, 8 roots × 2 surfaces. Strictly
185
+ additive: adding the `-rw` roots left the four Read roots unchanged.
186
+ `dist/build/index.js` is a barrel re-exporting the whole `@awacloud/pdf`
187
+ namespace. `package.json` exposes `./build/*` and `./standalone/*`.
188
+ Output is byte-deterministic across runs (no build stamp); the root
189
+ `.gitignore`'s blanket `dist/` exclusion is re-included via the
190
+ package's own `.gitignore` (`!dist/`+`!dist/**`). See
191
+ [`docs/api/bundles/dist-matrix.md`](docs/api/bundles/dist-matrix.md).
192
+ - **Documentation** — `docs/README.md` top-level index; `docs/api/` one
193
+ page per source module (core, `_shared/`, `extra/`, `bundles/`),
194
+ following the `@awacloud/fw` module-page format; `docs/guide/` — getting
195
+ started, read pipeline, extension hook, coverage (with `parserLimits`),
196
+ crypto (AES-CBC malleability risk, `verified` vs `valid` semantics,
197
+ `auditByteRange`, `pdfSandbox`), PDF 1.7 legacy reading, and PAdES
198
+ signing and verification.
199
+ - **Tests + integration** — one sibling test file per source module,
200
+ co-located in `src/`; integration suite under `tests/`:
201
+ `roundtrip.integration.test.js` (wires the full stack through
202
+ `@awacloud/fw` `ModuleRuntime`, read → write → read on fixtures sized 1–10
203
+ pages, every `factory.toString()` transportability assertion),
204
+ `legacy-conversion.test.js` (`%PDF-1.x` header tolerance),
205
+ `fuzz.test.js` (empty/garbage/truncated/no-xref/bad-header inputs →
206
+ typed `PdfError`, never a bare `Error`), `_helpers/build.js` (shared
207
+ fixture builder). The package's coverage floor is `awa.coverageFloor` in
208
+ `package.json`.
209
+ - **Architecture** — clean binary format (no ZIP; `%PDF-2.0` header +
210
+ indirect objects + xref + trailer); worker-safe factories
211
+ (`factory.toString()` serializable, no closure on mutable
212
+ module-level state — see Changed); zero external dependency beyond
213
+ `@awacloud/fw` + `@awacloud/fonts` (both workspace); content stream parsed to
214
+ an operator list, not interpreted (rendering / coordinate math is
215
+ left to the caller); fonts always delegated to `@awacloud/fonts` (even
216
+ standard-14 metrics); AcroForm field inheritance resolved at lookup
217
+ time, never collapsed onto children, to preserve roundtrip fidelity;
218
+ bundle composition is layered (`pdf-full ⊃ pdf-large`,
219
+ `pdf-legacy ⊃ pdf-full`).
220
+ - **Package surface** — `package.json` exposes:
221
+
222
+ ```
223
+ . src/main.js 5 descriptor arrays + every core descriptor by name
224
+ ./pdf src/pdf.js top-level orchestrator only
225
+ ./errors src/errors.js pdfErrors
226
+ ./serializer src/syntax/serializer.js
227
+ ./filters/* src/syntax/filters/*.js
228
+ ./annot/* src/annot/*.js
229
+ ./tagged/* src/tagged/*.js
230
+ ./crypto/* src/crypto/*.js
231
+ ./sig/* src/sig/*.js
232
+ ./ocg/* src/ocg/*.js
233
+ ./action/* src/action/*.js
234
+ ./embedded/* src/embedded/*.js
235
+ ./metadata/* src/metadata/*.js
236
+ ./prepress/* src/prepress/*.js
237
+ ./extra/* src/extra/*.js the opt-in extras
238
+ ./bundles/* src/bundles/*.js 3 compositions
239
+ ./pdf-large src/bundles/pdf-large.js
240
+ ./pdf-full src/bundles/pdf-full.js
241
+ ./pdf-legacy src/bundles/pdf-legacy.js
242
+ ./build/* dist/build/* two-surface prebuilt (fw-DI variant)
243
+ ./standalone/* dist/standalone/* two-surface prebuilt (framework-free)
244
+ ```
245
+
246
+ Note: `src/document/*` (writer, builder, incremental/xref-stream
247
+ writers, catalog, pages, resources) has no dedicated subpath export —
248
+ reachable only through the root entry's additive named re-exports
249
+ (below). `awa.maturity: "L4"` (the initial core surface shipped at
250
+ L0, then progressed L0 → L1 → L2 → L3 → L4 through the factory-only
251
+ refactor and the read/write gap closure).
252
+ - **`addFont` `encoding` option.** A non-embedded `addFont`
253
+ spec accepts an optional `encoding` — `WinAnsiEncoding`,
254
+ `MacRomanEncoding` or `StandardEncoding` — emitted as `/Encoding /<name>`
255
+ after the existing keys, so a Standard-14 simple font can declare how its
256
+ codes map to glyphs. Absent (or `undefined`) emits nothing and the bytes
257
+ are unchanged; an unknown value, or `encoding` combined with `embedded`,
258
+ throws `pdf/builder/bad-font` with `{ name, encoding }` in its `context`.
259
+ - **`./<family>/*.js` export twins.** All twelve wildcard families
260
+ of the `exports` map (`./filters/*`, `./annot/*`, `./tagged/*`,
261
+ `./crypto/*`, `./sig/*`, `./ocg/*`, `./action/*`, `./embedded/*`,
262
+ `./metadata/*`, `./prepress/*`, `./extra/*`, `./bundles/*`) gain a
263
+ `./<family>/*.js` twin, so a specifier written with the `.js` suffix
264
+ (e.g. `@awacloud/pdf/extra/sig-pades.js`) resolves to the same file under
265
+ Node and under a browser prefix import map. The existing forms are
266
+ unchanged.
267
+ - **`pdf.write(model, opts)` options.** `strict` forwards to
268
+ `assembleIndirects`; the lenient skip list is exposed as `skippedObjects`
269
+ and through `onSkipped`.
270
+ - **`sign()` signs encrypted bases.** The signature field's `/T` is
271
+ encrypted with the document key derived from `opts.password` (standard
272
+ security handler, AESV2/AESV3); RC4 and AES-GCM bases are refused with
273
+ typed errors. This supersedes the encrypted-base exception (signature-only
274
+ update, no field) noted under Fixed.
275
+ - **PAdES LT and LTA on encrypted bases.** The DSS streams, the VRI
276
+ strings and the document-timestamp field are encrypted with the document
277
+ key; the signature `/Contents` stay clear, as the standard requires.
278
+ - **External-oracle tests.** OpenSSL-produced ECDSA and Ed25519 CMS
279
+ signatures verify, and `sign()` output matches their structure.
280
+ - **Ed25519 signatures declare ISO/TS 32002.** The Ed25519 signing update
281
+ re-emits the Catalog with `/Extensions` declaring the `ISO_` developer
282
+ extension (`/ExtensionLevel 32002`, ISO/TS 32002 §4), merged with any
283
+ existing extensions dictionary, and sets `/Version /2.0` on a document
284
+ below PDF 2.0. A malformed `/Extensions` is refused
285
+ (`pdf/sign/bad-extensions`). ECDSA and RSA-PSS output is unchanged.
286
+
287
+ ### Changed
288
+
289
+ - **API reference pages and source comments describe each opt-in module by
290
+ what it covers** — internal milestone labels are removed from the `extra/`
291
+ pages and the module, crypto and signature comments.
292
+ - **Standard 14 font dictionaries are shared across pages.**
293
+ `pdfBuilder.addFont` (non-embedded) writes one font dictionary per
294
+ distinct `(baseFont, subtype, encoding)` and every page that uses it
295
+ references that object. A document that repeats a font on several pages
296
+ gets smaller: a 3-page document using 4 faces drops from 12 font
297
+ dictionaries to 4. Documents without a repeated font are unchanged.
298
+ - **Truncated FlateDecode streams decode to their prefix.**
299
+ `pdfFlate.decode` returns the bytes decoded before a FlateDecode stream
300
+ ends without its final block, flagged by a non-enumerable
301
+ `truncated: true`, instead of throwing `pdf/flate/inflate-failed`.
302
+ Content streams of such files now extract. Other inflate errors still
303
+ throw `pdf/flate/inflate-failed`.
304
+ - **Documentation pass.** The README follows the published-package
305
+ layout (installation, quick start, sub-path table with targets, maturity,
306
+ licence, project links) with executed quick-start snippets and no
307
+ hand-typed counts; guides open with their purpose and prerequisites; the
308
+ coverage and bundle pages state what the filter dispatch, the legacy
309
+ extras and `write` actually do; reference pages were checked against the
310
+ resolved module surfaces; a `pdfShared` reference page was added; links
311
+ to the historical audit pages, which no longer ship, were removed.
312
+ - **The Read+Write prebuilt bundles include signature verification.**
313
+ Every `-rw` root under `dist/build/` and `dist/standalone/` ships
314
+ `pdfSignature` beside `pdfSign`, so an invoked bundle exposes
315
+ `verifySignature` and `verifyAllSignatures`. Each `-rw` `.min.js` grows by
316
+ about 21.8 KB (6.4 KB gzipped); the Read bundles are unchanged.
317
+ - **Dist** — dist regenerated with the licence banner: every committed
318
+ `dist/**/*.js` / `.min.js` opens with the package's `/*! … */` legal block
319
+ (content from the source repository's licence matrix), each `*.meta.json`
320
+ `bytes` entry is measured on the final bytes, and there is no `builtAt`
321
+ timestamp — `bun run gen:bundles` is byte-deterministic; the fixed
322
+ `sign.js` (no hard-coded `/Root 1 0 R` trailer; whole-token `/ByteRange`
323
+ gap) ships in every `-rw` bundle.
324
+ - **`/ByteRange` gap: the signer emits the whole `<…>` token; the verifier
325
+ accepts exactly two forms.** `pdfSign.sign` (the `/Sig`, and the
326
+ LTA `/DocTimeStamp` through the same emitter) now leaves the whole
327
+ `/Contents <…>` token out of the signed ranges, delimiters included —
328
+ ISO 32000-2 §12.8.3.3.1 requires the string to "fit precisely in the space
329
+ between the ranges", and PDFBox and pyHanko emit and check that form (b).
330
+ It previously left out the hex digits only (form a).
331
+ `computeByteRange` takes the token span as an optional fourth argument
332
+ (`opts.token`, new code `pdf/sig/byterange/bad-token`); its three-argument
333
+ call is unchanged. `auditByteRange` accepts a gap that is exactly the token
334
+ or exactly the digits, and reports which in the additive `gapForm` field
335
+ (`'token' | 'digits' | null`); before, it accepted the token only and
336
+ flagged `pdfSign`'s own output. `verifySignature` / `verifyAllSignatures`
337
+ now apply that rule to every `/Sig`: any other gap gives `verified: false`
338
+ with `gap-start-mismatch` / `gap-end-mismatch` in `errors`. This is a
339
+ tightening — measured on 2026-09-23, the verify path had no gap check at
340
+ all: a gap of `<` + digits, or digits + `>`, re-signed over its own ranges
341
+ verified `true`; both are now refused. Signatures written in form (a) —
342
+ every signed PDF committed in the repo — are not re-signed and keep
343
+ verifying.
344
+ - **`/DocTimeStamp` `/ByteRange` gap check.** `verifyAllSignatures` applies
345
+ the same rule to every `/DocTimeStamp`: a gap that is not exactly the
346
+ `<…>` token or exactly the hex digits gives `verified: false` with
347
+ `gap-start-mismatch` / `gap-end-mismatch` in `errors` (`imprintVerified`
348
+ still reports the imprint alone), and each `timestamps[]` entry gains the
349
+ additive `gapForm` field (`'token' | 'digits' | null`, present on every
350
+ entry). A tightening: an off-by-one DocTimeStamp gap re-stamped over its
351
+ own ranges verified `true` before.
352
+ - **Package contents** — the npm tarball ships `NOTICE` (dual licence +
353
+ trademark notice, commercial-licence contact) next to `LICENSE`; the pre-publication
354
+ checklist no longer ships. Package `description` corrected: the shipped
355
+ signature surface covers PKCS#7/PAdES sign **and** verify.
356
+ - **`main.js` restructured as a declarative manifest** — no runtime
357
+ bootstrap, no re-export of resolved instances, no import of built
358
+ bundles. Exports `fw_require` (the `@awacloud/fw` factories consumed by
359
+ fw-bound modules, completed with every provider's transitive deps, e.g.
360
+ `zlib`→`deflate`→`bitstream`/`huffman`, `pem`→`b64`,
361
+ `rsa`/`ecc`→`bn`/`random`/`hex`/`hmac`), `pkg_require` (cross-package
362
+ bridge re-exporting `@awacloud/fonts`' own `fw_require`+`modules` plus 4
363
+ internal `embed-pdf/subsetForPdf/*` helper descriptors it doesn't itself
364
+ export, so `pdfFontEmbed`'s full dependency graph resolves through a bare
365
+ `@awacloud/pdf` registration), `modules` (the core factories,
366
+ topologically ordered), `extras` (opt-in), `bundle` (3 descriptors).
367
+ Every `modules` descriptor is additionally re-exported by binding name so
368
+ a sibling composer (e.g. `@awacloud/oconv`, `@awacloud/facturx`) can
369
+ resolve any pdf dependency through the bare `@awacloud/pdf` specifier —
370
+ purely additive, the five arrays stay byte-unchanged; the `extras` and
371
+ `bundle` descriptors are not re-exported by name.
372
+ `tools/generate-bundles.mjs` is a thin wrapper over
373
+ `@awacloud/tool-prebuild-generator` (see Added — two-surface `dist/`).
374
+ - **Factory-only strict** — the five error classes
375
+ (`PdfError`/`ParseError`/`RenderError`/`ContractError`/
376
+ `EncryptionError`) are declared **inside** `pdfErrors`'s factory body;
377
+ every consumer declares `'pdfErrors'` as a dependency and destructures
378
+ the classes from the injected `errors` parameter instead of a
379
+ top-level `import { ParseError } from '../errors.js'`. Each
380
+ `pdfErrors.factory()` call therefore creates its own classes; a
381
+ `ModuleRuntime` caches the resolved instance, so the modules of one
382
+ runtime share them. The transitional ESM compatibility shim that
383
+ temporarily re-exported the five classes as named bindings is fully
384
+ retired (see Removed). `pdfSigOids` likewise moved its 8 top-level
385
+ `export const`/`export function` OID tables into its factory body, with
386
+ `signature.js`/`timestamp.js`/`certChain.js` receiving them via DI.
387
+ Across the refactor, every factory in the package is worker-safe:
388
+ module-level constants/helpers a factory body referenced are relocated
389
+ or inlined into that factory, so `factory.toString()` rehydrates in a
390
+ Web Worker without resolving an external module symbol (pinned by
391
+ `tests/roundtrip.integration.test.js`'s worker-safety assertion over
392
+ every registered factory).
393
+ - **Bundles simplified to pure fw descriptors** — `pdfLargeBundle`/
394
+ `pdfFullBundle`/`pdfLegacyBundle` are `{ name, dependencies, factory }`
395
+ descriptors whose factory wires each resolved extra into the core
396
+ `pdf` via `.use({ name, register })` and returns the enriched
397
+ instance; consumption is exclusively `ModuleRuntime.resolve(...)`
398
+ (see Removed for the retired imperative builders).
399
+ - **`verifySignature(...)` result shape (breaking)** — carries
400
+ `verified`, `valid` (alias, mirrors `verified`), `pkVerified`,
401
+ `computedDigest`, in place of the earlier structural-only
402
+ `{ valid: errors.length === 0, errors, signerCerts, hashAlg,
403
+ signatureAlg }`. See Security for the vulnerability this closes.
404
+ `pdfSignature.dependencies` gained `'bitArray'`.
405
+ - **Error codes namespaced by origin** — granular kebab-case codes
406
+ (`pdf/flate/bad-predictor`, `pdf/sig/byterange/*`, `pdf/parser/*`, …)
407
+ replace earlier placeholder-style codes; every raised error carries a
408
+ structured `context`. `pdf/ts/parse` and `pdf/ts/parse-failed` records
409
+ keep the underlying error as `cause`; a `pdf/sig/digest-failed` record is
410
+ `{ code, message }`, with the underlying error's message appended to
411
+ `message`.
412
+ - **Hash streaming for `/ByteRange`** — `verifySignature` uses
413
+ `hashMod.fn` + `update`/`finalize` when `bitArray` is available,
414
+ avoiding an `O(document size)` intermediate buffer allocation;
415
+ falls back to `hashMod.hash(...)` otherwise.
416
+ - **`standardV6` scratch buffer** — the hardening loop (Algorithm 2.B)
417
+ reuses a scratch buffer for AES-128 key scheduling instead of
418
+ allocating per round.
419
+ - **Packaging** — `awa.maturity` progressed `L0` → `L1` → `L2` → `L3` →
420
+ `L4`; `package.json` carries `description`, `keywords`, `engines`,
421
+ `sideEffects: false`, and the legal and project metadata (`license`
422
+ `AGPL-3.0-only`, `author`, `repository`, `bugs`, `homepage`); `LICENSE`
423
+ and `NOTICE` ship in the tarball.
424
+ - **`sign()` documentation.** The `sign()` JSDoc lists every option the
425
+ body reads.
426
+ - **Ed25519 viewer support documented.** Adobe Acrobat Reader does not
427
+ validate Ed25519 (EdDSA) signatures, including OpenSSL-produced ones;
428
+ OpenSSL 3.5 and later verify them. The PAdES guide recommends ECDSA P-256
429
+ where Acrobat must validate the signature, and its algorithm claim row
430
+ carries that caveat.
431
+
432
+ ### Deprecated
433
+
434
+ - **`valid` on signature verification results.** The `valid` field of
435
+ `verifySignature` / `verifyAllSignatures` results (signatures and
436
+ document timestamps) is deprecated: read `verified`. It carries the same
437
+ value and is kept for compatibility.
438
+
439
+ ### Fixed
440
+
441
+ - **Identity crypt filters on V=5 mean no encryption.** On V=5 (R=5 and
442
+ R=6) files, a `/StmF`, `/StrF` or `/EFF` named `Identity`, or absent,
443
+ now resolves to `Identity`: those strings and streams are left as they
444
+ are instead of being decrypted as AES-256, and `sign()` no longer
445
+ encrypts its new strings on such files.
446
+ - **The PAdES guide's external-TSA example runs.** The `tsaSign` example
447
+ is executed by the test suite, and the guide's verification-report
448
+ sample shows SHA-512 for Ed25519.
449
+ - **Source comments match the code.** The comments of the legacy-filters
450
+ extra, the writer, the filter dispatcher, the legacy bundle, `pdfShared`
451
+ and the font-embed adapter describe the current behaviour: CCITT decoder
452
+ delegation, header bytes, the dispatch map, no conversion on write, and
453
+ the codec helpers.
454
+ - **Documentation links resolve from the npm tarball.** Links that pointed
455
+ outside the package now point at the public repository at this release's
456
+ tag, so they resolve from the tarball; references to sources that are not
457
+ published are plain-text citations.
458
+
459
+ - **`readDocument` opens PDF 1.5+ cross-reference streams and object
460
+ streams.** The top-level reader now picks a cross-reference form per
461
+ section: an `xref` keyword takes the classical table path, anything
462
+ else is parsed as a `/Type /XRef` stream (§7.5.8). Mixed `/Prev`
463
+ chains (a classical incremental section over an xref-stream base, or
464
+ the reverse) and hybrid-reference files (a classical trailer carrying
465
+ `/XRefStm`) resolve end to end, and objects held in a `/Type /ObjStm`
466
+ container (§7.5.7) are materialised on demand by `doc._raw.resolve`,
467
+ each container decoded once per document. `xref.sections[i]` gains
468
+ `kind` (`'table' | 'stream'`) and an indirect materialised from a
469
+ container carries `objStm`; the public `readDocument` signature and
470
+ return shape are otherwise unchanged.
471
+ - **`/DecodeParms` reach the decoders as plain values.** Filter dispatch
472
+ now marshals the typed dictionary (or array of dictionaries) into plain
473
+ parameters, so a `/Predictor` (e.g. PNG predictor 12 on cross-reference
474
+ streams) is actually applied instead of silently skipped.
475
+ - **`readDocument` survives two real-world shapes.** `/Root` is resolved
476
+ across a chain of cross-reference streams whose sections carry it in
477
+ different trailers (merged newest first), and a non-catalog object marked
478
+ free but still referenced degrades to a recorded loss instead of a throw;
479
+ the returned document gains a `losses` array for these.
480
+ A document whose catalog itself is free is still refused.
481
+ - **Indirect page resources are resolved.** `typeResources` /
482
+ `resolvePageResources` accept an optional `resolveRef`, so a page whose
483
+ `/ExtGState` (or another resource category) is an indirect reference no
484
+ longer drops the whole resource map, fonts included.
485
+ - **`appendIncremental` extends cross-reference-stream bases** with an
486
+ uncompressed `/Type /XRef` section carrying `/Prev` and `/Root`; the
487
+ classical-table path is byte-unchanged. A hybrid-reference base (a
488
+ classical trailer with `/XRefStm`) is refused with
489
+ `pdf/incremental/hybrid-base`, and a `startxref` that designates neither
490
+ form with `pdf/incremental/unsupported-base`. `pdfXref` gains
491
+ `readXrefStreamDict` and `buildXrefStream`.
492
+ - **Signing over cross-reference-stream and object-stream bases now yields
493
+ a document that re-reads.** `pdfSign.sign()` appends the signature (and
494
+ the LTA DocTimeStamp) through `pdfIncrementalWriter`, so the signature
495
+ section follows the base's cross-reference form (table or stream) and
496
+ carries `/Root`, `/Info` and `/ID` from the merged trailer instead of a
497
+ hard-coded `/Root 1 0 R`. The signature takes the first object number at
498
+ or past the merged `/Size`, so it no longer overwrites an object held in
499
+ an object stream. At LT/LTA the Catalog is resolved through
500
+ `readDocument` from `/Root`, whatever its number or container, instead
501
+ of a byte scan for `1 0 obj`. A hybrid-reference base is refused
502
+ with `pdf/incremental/hybrid-base`, and nothing is written.
503
+ `pdfIncrementalWriter` gains `appendIncrementalWithOffsets` and
504
+ `readBaseTrailer`, and `appendIncremental`'s output is unchanged.
505
+ `pdfSign` gains a `pdfDocument` dependency, appended last. It is required
506
+ only at LT/LTA and reported as `pdf/sign/no-document-reader`. Every level
507
+ now requires `pdfIncrementalWriter`.
508
+
509
+ - **`pdfSign.sign()` registers the signature in a signature
510
+ field.** The `/Sig` update now also writes an invisible `/FT /Sig`
511
+ field merged with its widget (`/T Signature<n>`, `/V`, `/Rect [0 0 0 0]`,
512
+ `/F 132`, `/P` page 1), the page 1 `/Annots` entry and the Catalog
513
+ `/AcroForm` (`/Fields`, `/SigFlags 3`), per ISO 32000-2 §12.7.5.5, so
514
+ viewers list the signature. The LTA `/DocTimeStamp` gets its own field.
515
+ `pdf/sign/no-document-reader` now fires at **every** level, because the
516
+ field needs the Catalog and page 1 — this supersedes the LT/LTA-only rule
517
+ above. An encrypted base keeps the signature-only update (no field).
518
+ - **`assembleIndirects` reports the objects it could not
519
+ resolve.** Read-then-write no longer drops an unresolvable object
520
+ without a trace: every skipped `{ num, gen, code }` is listed on the
521
+ returned array's non-enumerable `skippedObjects` property, and
522
+ `assembleIndirects(model, { strict: true })` throws a `RenderError` coded
523
+ `pdf/writer/unresolvable-objects` whose `context.objects` is that list.
524
+ The default stays lenient and the output for a fully resolvable model is
525
+ unchanged.
526
+ - **ECDSA signature value is DER.** The ECDSA CMS signature value is now
527
+ the DER `ECDSA-Sig-Value`; the verifier accepts the DER form and the
528
+ earlier raw r||s.
529
+ - **Ed25519 signs over SHA-512.** Ed25519 signatures use SHA-512 as
530
+ RFC 8419 requires; `hashAlg` defaults to `sha512` for Ed25519 and any
531
+ other value is refused (`pdf/sign/ed25519-requires-sha512`).
532
+ - **One-byte `/ToUnicode` codespace for simple fonts.** `embedSimple`
533
+ writes a one-byte `/ToUnicode` codespace matching its WinAnsi codes.
534
+ - **Update trailers repeat `/Encrypt`.** Incremental updates written by
535
+ `pdfIncrementalWriter` / `pdfXref.buildXrefStream` can repeat the base's
536
+ `/Encrypt` (`opts.encrypt`); `sign()` does so over encrypted bases.
537
+ - **CMS signed attributes in DER order.** CMS signed attributes are written
538
+ in DER SET OF order, so verifiers that re-encode them (OpenSSL with
539
+ Ed25519) accept the signature.
540
+
541
+ ### Removed
542
+
543
+ - **`pdfParserStream`**, the stream-body helpers module. No module depended
544
+ on it and `pdfParser` carries its own helpers. It is no longer in
545
+ `modules` and no longer a named export of `@awacloud/pdf`.
546
+ - **The `prebuilt/` bundle directory** that used to sit under
547
+ `src/bundles/`, and `tools/generate-prebuilds.mjs`/
548
+ `bun run gen:prebuilds` — retired in favour of the two-surface
549
+ `dist/build/` + `dist/standalone/` convention (see Added). Exported
550
+ factory names and resolve keys are unchanged; only the on-disk
551
+ location and generator moved (`tools/generate-bundles.mjs`/
552
+ `bun run gen:bundles`).
553
+ - **Imperative bundle builders** `buildPdfLarge`, `buildPdfFull`,
554
+ `buildPdfLegacy` and the constants `PDF_LARGE_EXTRAS`,
555
+ `PDF_FULL_EXTRAS`, `PDF_LEGACY_EXTRAS` — consumption is now
556
+ exclusively `ModuleRuntime.resolve('pdfLargeBundle' | 'pdfFullBundle'
557
+ | 'pdfLegacyBundle')`; the extras list lives in
558
+ `bundleDescriptor.dependencies`.
559
+ - **Named re-exports of extras from the bundle entry points** — import
560
+ an extra from its canonical module (`@awacloud/pdf/extra/...`), or
561
+ register the `extras` array exported by `@awacloud/pdf` (the root entry
562
+ does not re-export extras by name).
563
+ - **Top-level error-class named exports** (`PdfError`, `ParseError`,
564
+ `RenderError`, `ContractError`, `EncryptionError`) from `@awacloud/pdf`/
565
+ `@awacloud/pdf/errors`, and the transitional ESM compatibility shim that
566
+ temporarily preserved them during the factory-only migration.
567
+ Consumers resolve via `pdfErrors`:
568
+
569
+ ```js
570
+ import { pdfErrors } from '@awacloud/pdf';
571
+ const { PdfError, ParseError, isPdfError } = pdfErrors.factory();
572
+ ```
573
+
574
+ or `runtime.resolve('pdfErrors')`.
575
+
576
+ ### Security
577
+
578
+ - **The encrypted writer uses cryptographic randomness only.**
579
+ `pdfEncryptedWriter` no longer falls back to `Math.random` for keys,
580
+ salts and IVs. It uses `encrypt.randomBytes` when given, else
581
+ `crypto.getRandomValues`; with neither it throws an `EncryptionError`
582
+ with code `pdf/crypto/enc-writer/no-random`.
583
+ - **Signature verification — end of the silent false-positive.**
584
+ Before public-key wiring, `verifySignature(...)` returned
585
+ `valid: errors.length === 0` without ever invoking `rsa.pssVerify` /
586
+ `ecc.verify` / `ed25519.verify` — a PKCS#7 structurally valid but
587
+ cryptographically forged signature reported `valid: true` to a naïve
588
+ consumer. `verified`/`valid` are now `true` only once the digest
589
+ chain **and** PK-verify both succeed (see Changed).
590
+ - **`auditByteRange(...)`** (`src/sig/byteRange.js`) — public helper
591
+ detecting six `/ByteRange` attack vectors: `self-overlap`,
592
+ `gap-start-mismatch`, `gap-end-mismatch`, `cross-overlap`,
593
+ `incomplete-coverage` (opt-in `requireFullCoverage`),
594
+ `non-zero-start`. Exposed via `runtime.resolve('pdfByteRange')`.
595
+ - **Sandboxed action flag** — every typer for `/Launch`, `/JavaScript`,
596
+ `/SubmitForm`, `/ImportData`, `/URI` adds `sandboxed: true` to its
597
+ returned record, alongside the existing `securityWarning`, so a naïve
598
+ consumer can't mistake a typed action record for an execution
599
+ instruction. The opt-in `pdfSandbox` module (`pdf-full` bundle)
600
+ additionally exposes `lintActions([...])` → `{ hasErrors,
601
+ hasActiveContent, issues }`.
602
+ - **Parser hardening** — see Added (`parserLimits`): depth cap, array
603
+ literal cap, stream fallback-scan quota, each raising a typed
604
+ `pdf/parser/*` error.
605
+ - **Encryption** — Standard Security Handler V4/V5/V6 decrypt + encrypt
606
+ (AES-128/256-CBC, and RC4-128 through the V4 handler's `V2` crypt
607
+ filter), AES-GCM (TS 32003) decrypt + encrypt. The older RC4 revisions
608
+ (40-bit R2, 128-bit R3) are read-only, through the opt-in
609
+ `legacy-rc4-read` extra of the `pdf-legacy` bundle.