@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/src/errors.js ADDED
@@ -0,0 +1,78 @@
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 Typed error hierarchy for `@awacloud/pdf` — exposed as the
8
+ * `pdfErrors` module factory.
9
+ *
10
+ * Five classes inheriting from `PdfError` :
11
+ *
12
+ * - `ParseError` — malformed input encountered while reading
13
+ * (bad header, truncated xref, unbalanced dict,
14
+ * unknown filter, …).
15
+ * - `RenderError` — invalid model encountered while writing
16
+ * (e.g. circular indirect reference, missing
17
+ * required field on Page).
18
+ * - `ContractError` — the consumer violated the API contract
19
+ * (e.g. `.use()` called twice with the same
20
+ * extension, passing a non-Uint8Array to
21
+ * `.read()`).
22
+ * - `EncryptionError` — encryption / decryption failure (bad password,
23
+ * unsupported Security Handler revision,
24
+ * PKCS#7 verification mismatch).
25
+ *
26
+ * Each error carries a kebab-case `code` (e.g. `'pdf/bad-header'`,
27
+ * `'pdf/xref/truncated'`), an optional `context` object, and an
28
+ * optional `cause`.
29
+ *
30
+ * Classes are declared **inside the factory body** so the factory is a
31
+ * self-contained closure that can be serialised to a Worker via
32
+ * `factory.toString()`. Consumers obtain the classes by resolving the
33
+ * `pdfErrors` module via a `ModuleRuntime` (which caches the resolved
34
+ * instance, providing stable class identities for `instanceof`).
35
+ *
36
+ * ```js
37
+ * const { PdfError, ParseError } = runtime.resolve('pdfErrors');
38
+ * try { pdf.read(bytes); }
39
+ * catch (e) { if (e instanceof PdfError) console.log(e.code, e.context); }
40
+ * ```
41
+ *
42
+ * **Migration (passe 1/2/2a/2b/2c/TD-1 — complete)** — top-level error
43
+ * classes are no longer exported as named ESM bindings and no longer
44
+ * live at module scope. All consumers (source and tests) resolve them
45
+ * via `pdfErrors.factory()` (or, preferably, through a `ModuleRuntime`
46
+ * to share class identities). The transitional compatibility shim that
47
+ * re-exported `PdfError`, `ParseError`, `RenderError`, `ContractError`
48
+ * and `EncryptionError` was removed at the end of passe 2c.
49
+ *
50
+ * @module pdf/errors
51
+ */
52
+
53
+ export const pdfErrors = {
54
+ name: 'pdfErrors',
55
+ dependencies: [],
56
+ factory() {
57
+ class PdfError extends Error {
58
+ constructor(code, message, opts) {
59
+ super(message);
60
+ this.name = new.target.name;
61
+ this.code = code;
62
+ if (opts && opts.context) this.context = opts.context;
63
+ if (opts && opts.cause) this.cause = opts.cause;
64
+ }
65
+ }
66
+ class ParseError extends PdfError {}
67
+ class RenderError extends PdfError {}
68
+ class ContractError extends PdfError {}
69
+ class EncryptionError extends PdfError {}
70
+
71
+ function isPdfError(e) { return e instanceof PdfError; }
72
+
73
+ return {
74
+ PdfError, ParseError, RenderError, ContractError, EncryptionError,
75
+ isPdfError
76
+ };
77
+ }
78
+ };
@@ -0,0 +1,171 @@
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 Opt-in: 3D and RichMedia annotation typing.
8
+ *
9
+ * ISO 32000-2:2020 §13.6 defines two interactive multimedia annotation
10
+ * subtypes:
11
+ *
12
+ * - `/Subtype /3D` (§13.6.2) — references a 3D stream via `/3DD`,
13
+ * default view (`/3DV`), activation dict (`/3DA`), interactive
14
+ * dict (`/3DI`).
15
+ *
16
+ * - `/Subtype /RichMedia` (§13.6.3) — `/RichMediaContent` with
17
+ * `/Assets`, `/Configurations`, `/Views`; `/RichMediaSettings`
18
+ * with `/Activation`, `/Deactivation`, instance state.
19
+ *
20
+ * @module pdf/extra/3d-richmedia
21
+ */
22
+
23
+ /**
24
+ * Module factory.
25
+ */
26
+ import { pdfErrors } from '../errors.js';
27
+ import { pdfParser } from '../syntax/parser.js';
28
+
29
+ export const pdf3dRichMedia = {
30
+ name: 'pdf3dRichMedia',
31
+ dependencies: ['pdfErrors', 'pdfParser'],
32
+ deps: [pdfErrors, pdfParser],
33
+ factory(errors, parser) {
34
+ const { ParseError } = errors;
35
+ const { isType } = parser;
36
+
37
+ const THREE_D_KNOWN = new Set([
38
+ 'Type', 'Subtype', 'Rect', '3DD', '3DV', '3DA', '3DI', '3DB'
39
+ ]);
40
+ const RM_KNOWN = new Set([
41
+ 'Type', 'Subtype', 'Rect', 'RichMediaContent', 'RichMediaSettings'
42
+ ]);
43
+
44
+ const RM_INSTANCE_STATES = Object.freeze({
45
+ A: 'active',
46
+ L: 'loaded',
47
+ U: 'uninstantiated'
48
+ });
49
+
50
+ const ACTIVATION_CONDITIONS = Object.freeze({
51
+ XA: 'explicit activate',
52
+ PO: 'page open',
53
+ PV: 'page visible'
54
+ });
55
+
56
+ function type3DAnnot(dict) {
57
+ if (!isType(dict, 'dict')) {
58
+ throw new ParseError('pdf/extra/3d/not-dict',
59
+ '3D annotation must be a dictionary',
60
+ { context: { type: dict && dict.type } });
61
+ }
62
+ const e = dict.entries;
63
+ if (e.Subtype && (e.Subtype.type !== 'name' || e.Subtype.value !== '3D')) {
64
+ throw new ParseError('pdf/extra/3d/bad-subtype',
65
+ '/Subtype must be /3D',
66
+ { context: { actual: e.Subtype.value } });
67
+ }
68
+ if (!e['3DD']) {
69
+ throw new ParseError('pdf/extra/3d/missing-3dd',
70
+ '3D annotation requires /3DD entry');
71
+ }
72
+ const out = {
73
+ threeDD: e['3DD'],
74
+ threeDV: e['3DV'] || null,
75
+ threeDA: isType(e['3DA'], 'dict') ? type3DActivation(e['3DA']) : null,
76
+ threeDI: isType(e['3DI'], 'bool') ? e['3DI'].value : null,
77
+ threeDB: e['3DB'] || null,
78
+ raw: dict,
79
+ _extras: {}
80
+ };
81
+ for (const k of Object.keys(e)) {
82
+ if (!THREE_D_KNOWN.has(k)) out._extras[k] = e[k];
83
+ }
84
+ return out;
85
+ }
86
+
87
+ function type3DActivation(dict) {
88
+ if (!isType(dict, 'dict')) {
89
+ throw new ParseError('pdf/extra/3d/act/not-dict',
90
+ '/3DA must be a dictionary',
91
+ { context: { type: dict && dict.type } });
92
+ }
93
+ const e = dict.entries;
94
+ return {
95
+ a: isType(e.A, 'name') ? e.A.value : null,
96
+ ais: isType(e.AIS, 'name') ? e.AIS.value : null,
97
+ d: isType(e.D, 'name') ? e.D.value : null,
98
+ dis: isType(e.DIS, 'name') ? e.DIS.value : null,
99
+ tb: isType(e.TB, 'bool') ? e.TB.value : null,
100
+ np: isType(e.NP, 'bool') ? e.NP.value : null,
101
+ raw: dict
102
+ };
103
+ }
104
+
105
+ function typeRichMediaAnnot(dict) {
106
+ if (!isType(dict, 'dict')) {
107
+ throw new ParseError('pdf/extra/rm/not-dict',
108
+ 'RichMedia annotation must be a dictionary',
109
+ { context: { type: dict && dict.type } });
110
+ }
111
+ const e = dict.entries;
112
+ if (e.Subtype && (e.Subtype.type !== 'name' || e.Subtype.value !== 'RichMedia')) {
113
+ throw new ParseError('pdf/extra/rm/bad-subtype',
114
+ '/Subtype must be /RichMedia',
115
+ { context: { actual: e.Subtype.value } });
116
+ }
117
+ if (!e.RichMediaContent) {
118
+ throw new ParseError('pdf/extra/rm/missing-content',
119
+ 'RichMedia annotation requires /RichMediaContent');
120
+ }
121
+ const out = {
122
+ content: isType(e.RichMediaContent, 'dict')
123
+ ? typeRichMediaContent(e.RichMediaContent) : e.RichMediaContent,
124
+ settings: isType(e.RichMediaSettings, 'dict')
125
+ ? e.RichMediaSettings : null,
126
+ raw: dict,
127
+ _extras: {}
128
+ };
129
+ for (const k of Object.keys(e)) {
130
+ if (!RM_KNOWN.has(k)) out._extras[k] = e[k];
131
+ }
132
+ return out;
133
+ }
134
+
135
+ function typeRichMediaContent(dict) {
136
+ if (!isType(dict, 'dict')) {
137
+ throw new ParseError('pdf/extra/rm/content/not-dict',
138
+ '/RichMediaContent must be a dictionary',
139
+ { context: { type: dict && dict.type } });
140
+ }
141
+ const e = dict.entries;
142
+ return {
143
+ assets: e.Assets || null,
144
+ configurations: e.Configurations || null,
145
+ views: e.Views || null,
146
+ raw: dict
147
+ };
148
+ }
149
+
150
+ function classifyRmInstanceState(name) {
151
+ if (name == null) return null;
152
+ if (typeof name !== 'string') {
153
+ throw new ParseError('pdf/extra/rm/state/bad',
154
+ 'state must be a string',
155
+ { context: { type: typeof name } });
156
+ }
157
+ return RM_INSTANCE_STATES[name] || null;
158
+ }
159
+
160
+ return {
161
+ type3DAnnot,
162
+ type3DActivation,
163
+ typeRichMediaAnnot,
164
+ typeRichMediaContent,
165
+ classifyRmInstanceState,
166
+ RM_INSTANCE_STATES,
167
+ ACTIVATION_CONDITIONS
168
+ };
169
+ }
170
+ };
171
+
@@ -0,0 +1,200 @@
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 Opt-in: extended annotation subtypes per
8
+ * ISO 32000-2:2020 §12.5.6.
9
+ *
10
+ * Covers: Watermark (§12.5.6.22), 3D (§13.6), RichMedia (§13.6.2),
11
+ * Sound (§12.5.6.16 legacy), Movie (§12.5.6.17 legacy),
12
+ * Screen (§12.5.6.18), PrinterMark (§12.5.6.20), TrapNet (§12.5.6.21),
13
+ * and FreeText-extended (`/RC`, `/DS`, `/LE`, §12.5.6.6).
14
+ *
15
+ * Each typer composes `typeBaseAnnot` from `../annot/annot.js` so the
16
+ * common annotation entries are preserved.
17
+ *
18
+ * @module pdf/extra/annot-extended
19
+ */
20
+
21
+ /**
22
+ * Module factory.
23
+ */
24
+ import { pdfErrors } from '../errors.js';
25
+ import { pdfParserObj } from '../syntax/parser-obj.js';
26
+ import { pdfAnnot } from '../annot/annot.js';
27
+
28
+ export const pdfAnnotExtended = {
29
+ name: 'pdfAnnotExtended',
30
+ dependencies: ['pdfErrors', 'pdfParserObj', 'pdfAnnot'],
31
+ deps: [pdfErrors, pdfParserObj, pdfAnnot],
32
+ factory(errors, parserObj, annot) {
33
+ const { ParseError } = errors;
34
+ const { isType } = parserObj;
35
+ const { typeBaseAnnot, captureExtras } = annot;
36
+
37
+ const SUBTYPES = new Set([
38
+ 'Watermark', '3D', 'RichMedia', 'Sound', 'Movie', 'Screen',
39
+ 'PrinterMark', 'TrapNet', 'FreeText'
40
+ ]);
41
+
42
+ const FREETEXT_LINE_ENDINGS = Object.freeze([
43
+ 'Square', 'Circle', 'Diamond', 'OpenArrow', 'ClosedArrow', 'None',
44
+ 'Butt', 'ROpenArrow', 'RClosedArrow', 'Slash'
45
+ ]);
46
+
47
+ function typeAnnotExtended(dict) {
48
+ if (!isType(dict, 'dict')) {
49
+ throw new ParseError('pdf/extra/annot-ext/not-dict',
50
+ 'annotation must be a dictionary',
51
+ { context: { type: dict && dict.type } });
52
+ }
53
+ const sub = dict.entries.Subtype;
54
+ if (!isType(sub, 'name') || !SUBTYPES.has(sub.value)) {
55
+ throw new ParseError('pdf/extra/annot-ext/unsupported',
56
+ 'unsupported extended annotation subtype',
57
+ { context: { subtype: sub && sub.value } });
58
+ }
59
+ switch (sub.value) {
60
+ case 'Watermark': return typeWatermarkAnnot(dict);
61
+ case '3D': return type3DAnnot(dict);
62
+ case 'RichMedia': return typeRichMediaAnnot(dict);
63
+ case 'Sound': return typeSoundAnnot(dict);
64
+ case 'Movie': return typeMovieAnnot(dict);
65
+ case 'Screen': return typeScreenAnnot(dict);
66
+ case 'PrinterMark': return typePrinterMarkAnnot(dict);
67
+ case 'TrapNet': return typeTrapNetAnnot(dict);
68
+ case 'FreeText': return typeFreeTextExtended(dict);
69
+ }
70
+ /* istanbul ignore next */
71
+ throw new ParseError('pdf/extra/annot-ext/unreachable', 'unreachable');
72
+ }
73
+
74
+ function typeWatermarkAnnot(dict) {
75
+ const base = typeBaseAnnot(dict);
76
+ base.kind = 'Watermark';
77
+ const e = dict.entries;
78
+ base.fixedPrint = isType(e.FixedPrint, 'dict') ? e.FixedPrint : null;
79
+ captureExtras(base, dict, new Set(['FixedPrint']));
80
+ return base;
81
+ }
82
+
83
+ function type3DAnnot(dict) {
84
+ const base = typeBaseAnnot(dict);
85
+ base.kind = '3D';
86
+ const e = dict.entries;
87
+ base['3DD'] = e['3DD'] || null;
88
+ base['3DV'] = e['3DV'] || null;
89
+ base['3DA'] = e['3DA'] || null;
90
+ base['3DI'] = isType(e['3DI'], 'bool') ? e['3DI'].value : null;
91
+ base['3DB'] = e['3DB'] || null;
92
+ captureExtras(base, dict, new Set(['3DD', '3DV', '3DA', '3DI', '3DB']));
93
+ return base;
94
+ }
95
+
96
+ function typeRichMediaAnnot(dict) {
97
+ const base = typeBaseAnnot(dict);
98
+ base.kind = 'RichMedia';
99
+ const e = dict.entries;
100
+ base.richMediaContent = e.RichMediaContent || null;
101
+ base.richMediaSettings = e.RichMediaSettings || null;
102
+ captureExtras(base, dict, new Set(['RichMediaContent', 'RichMediaSettings']));
103
+ return base;
104
+ }
105
+
106
+ function typeSoundAnnot(dict) {
107
+ const base = typeBaseAnnot(dict);
108
+ base.kind = 'Sound';
109
+ const e = dict.entries;
110
+ base.sound = e.Sound || null;
111
+ base.name = isType(e.Name, 'name') ? e.Name.value : null;
112
+ captureExtras(base, dict, new Set(['Sound', 'Name']));
113
+ return base;
114
+ }
115
+
116
+ function typeMovieAnnot(dict) {
117
+ const base = typeBaseAnnot(dict);
118
+ base.kind = 'Movie';
119
+ const e = dict.entries;
120
+ base.t = isType(e.T, 'string') ? e.T.value : null;
121
+ base.movie = e.Movie || null;
122
+ base.activation = e.A || null;
123
+ captureExtras(base, dict, new Set(['T', 'Movie', 'A']));
124
+ return base;
125
+ }
126
+
127
+ function typeScreenAnnot(dict) {
128
+ const base = typeBaseAnnot(dict);
129
+ base.kind = 'Screen';
130
+ const e = dict.entries;
131
+ base.t = isType(e.T, 'string') ? e.T.value : null;
132
+ base.mk = e.MK || null;
133
+ base.a = e.A || null;
134
+ base.aa = e.AA || null;
135
+ captureExtras(base, dict, new Set(['T', 'MK', 'A', 'AA']));
136
+ return base;
137
+ }
138
+
139
+ function typePrinterMarkAnnot(dict) {
140
+ const base = typeBaseAnnot(dict);
141
+ base.kind = 'PrinterMark';
142
+ const e = dict.entries;
143
+ base.mn = isType(e.MN, 'name') ? e.MN.value : null;
144
+ captureExtras(base, dict, new Set(['MN']));
145
+ return base;
146
+ }
147
+
148
+ function typeTrapNetAnnot(dict) {
149
+ const base = typeBaseAnnot(dict);
150
+ base.kind = 'TrapNet';
151
+ const e = dict.entries;
152
+ base.lastModified = isType(e.LastModified, 'string') ? e.LastModified.value : null;
153
+ base.version = e.Version || null;
154
+ base.annotStates = e.AnnotStates || null;
155
+ base.fontFauxing = e.FontFauxing || null;
156
+ captureExtras(base, dict, new Set(['LastModified', 'Version', 'AnnotStates', 'FontFauxing']));
157
+ return base;
158
+ }
159
+
160
+ function typeFreeTextExtended(dict) {
161
+ const base = typeBaseAnnot(dict);
162
+ base.kind = 'FreeText';
163
+ const e = dict.entries;
164
+ base.da = isType(e.DA, 'string') ? e.DA.value : null;
165
+ base.q = (e.Q && (e.Q.type === 'int' || e.Q.type === 'real')) ? (e.Q.value | 0) : null;
166
+ base.rc = isType(e.RC, 'string') ? e.RC.value : (isType(e.RC, 'stream') ? e.RC : null);
167
+ base.ds = isType(e.DS, 'string') ? e.DS.value : null;
168
+ base.cl = isType(e.CL, 'array') ? toNumArray(e.CL) : null;
169
+ base.it = isType(e.IT, 'name') ? e.IT.value : null;
170
+ base.be = e.BE || null;
171
+ base.rd = isType(e.RD, 'array') ? toNumArray(e.RD) : null;
172
+ let le = null;
173
+ if (isType(e.LE, 'name')) le = e.LE.value;
174
+ else if (isType(e.LE, 'array')) {
175
+ le = e.LE.items.filter(it => isType(it, 'name')).map(it => it.value);
176
+ }
177
+ base.le = le;
178
+ captureExtras(base, dict, new Set(['DA', 'Q', 'RC', 'DS', 'CL', 'IT', 'BE', 'RD', 'LE']));
179
+ return base;
180
+ }
181
+
182
+ function toNumArray(v) {
183
+ const out = [];
184
+ for (const it of v.items) {
185
+ if (!it || (it.type !== 'int' && it.type !== 'real')) return null;
186
+ out.push(it.value);
187
+ }
188
+ return out;
189
+ }
190
+
191
+ return {
192
+ typeAnnotExtended,
193
+ typeWatermarkAnnot, type3DAnnot, typeRichMediaAnnot,
194
+ typeSoundAnnot, typeMovieAnnot, typeScreenAnnot,
195
+ typePrinterMarkAnnot, typeTrapNetAnnot, typeFreeTextExtended,
196
+ FREETEXT_LINE_ENDINGS
197
+ };
198
+ }
199
+ };
200
+
@@ -0,0 +1,131 @@
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 Opt-in: extended /AF (Associated Files) typing.
8
+ *
9
+ * PDF 2.0 lets /AF live on the Catalog, Page, XObject (Form), Mark,
10
+ * StructElem, Annot, or DParams. Each item references a File
11
+ * Specification dictionary that MUST carry /AFRelationship.
12
+ *
13
+ * Relationship vocabulary: Source, Data, Alternative, Supplement,
14
+ * EncryptedPayload, FormData, Schema, Unspecified.
15
+ *
16
+ * @module pdf/extra/associated-files
17
+ */
18
+
19
+ import { pdfErrors } from '../errors.js';
20
+
21
+ export const pdfAssociatedFiles2 = {
22
+ name: 'pdfAssociatedFiles2',
23
+ dependencies: ['pdfErrors'],
24
+ deps: [pdfErrors],
25
+
26
+ factory(errors) {
27
+ const { ParseError } = errors;
28
+ const STANDARD_REL = new Set([
29
+ 'Source', 'Data', 'Alternative', 'Supplement',
30
+ 'EncryptedPayload', 'FormData', 'Schema', 'Unspecified'
31
+ ]);
32
+
33
+ const CARRIER_HINTS = new Set([
34
+ 'Catalog', 'Page', 'XObject', 'StructElem', 'Annot', 'Mark', 'DParams'
35
+ ]);
36
+
37
+ function isDict(v) { return v && v.type === 'dict'; }
38
+ function isName(v) { return v && v.type === 'name'; }
39
+ function isArr(v) { return v && v.type === 'array'; }
40
+ function isRef(v) { return v && v.type === 'ref'; }
41
+
42
+ function resolveFileSpec(entry, resolveRef) {
43
+ if (isRef(entry)) {
44
+ if (typeof resolveRef === 'function') {
45
+ const r = resolveRef(entry);
46
+ return { resolved: true, dict: r, ref: { num: entry.num, gen: entry.gen } };
47
+ }
48
+ return { resolved: false, ref: { num: entry.num, gen: entry.gen } };
49
+ }
50
+ if (isDict(entry)) return { resolved: true, dict: entry };
51
+ throw new ParseError('pdf/af2/bad-entry',
52
+ '/AF entry must be a dict or indirect reference',
53
+ { context: { type: entry && entry.type } });
54
+ }
55
+
56
+ function typeAfEntry(entry, resolveRef, index) {
57
+ const res = resolveFileSpec(entry, resolveRef);
58
+ if (!res.resolved) {
59
+ return { ref: res.ref, resolved: false };
60
+ }
61
+ const dict = res.dict;
62
+ if (!isDict(dict)) {
63
+ throw new ParseError('pdf/af2/not-filespec',
64
+ 'resolved /AF entry must be a dict',
65
+ { context: { index } });
66
+ }
67
+ const e = dict.entries;
68
+ if (e.Type && (!isName(e.Type) || e.Type.value !== 'Filespec')) {
69
+ throw new ParseError('pdf/af2/bad-filespec-type',
70
+ 'expected /Type /Filespec',
71
+ { context: { index, actual: e.Type && e.Type.value } });
72
+ }
73
+ let rel = null, standard = false;
74
+ if (e.AFRelationship) {
75
+ if (!isName(e.AFRelationship)) {
76
+ throw new ParseError('pdf/af2/bad-relationship',
77
+ '/AFRelationship must be a name',
78
+ { context: { index, type: e.AFRelationship.type } });
79
+ }
80
+ rel = e.AFRelationship.value;
81
+ standard = STANDARD_REL.has(rel);
82
+ }
83
+ const out = {
84
+ filespec: dict,
85
+ relationship: rel,
86
+ standard,
87
+ resolved: true
88
+ };
89
+ if (res.ref) out.ref = res.ref;
90
+ return out;
91
+ }
92
+
93
+ function typeAf(afArray, opts) {
94
+ const resolveRef = opts && opts.resolveRef;
95
+ const carrier = opts && opts.carrier;
96
+ if (carrier && !CARRIER_HINTS.has(carrier)) {
97
+ throw new ParseError('pdf/af2/bad-carrier',
98
+ 'unknown /AF carrier hint',
99
+ { context: { carrier } });
100
+ }
101
+ if (!isArr(afArray)) {
102
+ throw new ParseError('pdf/af2/not-array',
103
+ '/AF must be an array',
104
+ { context: { type: afArray && afArray.type } });
105
+ }
106
+ const out = [];
107
+ for (let i = 0; i < afArray.items.length; i++) {
108
+ out.push(typeAfEntry(afArray.items[i], resolveRef, i));
109
+ }
110
+ return { entries: out, carrier: carrier || null };
111
+ }
112
+
113
+ function isStandardRelationship(name) {
114
+ return STANDARD_REL.has(String(name));
115
+ }
116
+
117
+ function listStandardRelationships() {
118
+ return Array.from(STANDARD_REL);
119
+ }
120
+
121
+ return {
122
+ typeAf,
123
+ typeAfEntry,
124
+ resolveFileSpec,
125
+ isStandardRelationship,
126
+ listStandardRelationships,
127
+ STANDARD_REL,
128
+ CARRIER_HINTS
129
+ };
130
+ }
131
+ };