@awacloud/pdf 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (345) hide show
  1. package/CHANGELOG.md +609 -0
  2. package/LICENSE +661 -0
  3. package/NOTICE +77 -0
  4. package/README.md +363 -2
  5. package/dist/build/index.js +21 -0
  6. package/dist/build/pdf-full-rw.js +10972 -0
  7. package/dist/build/pdf-full-rw.meta.json +105 -0
  8. package/dist/build/pdf-full-rw.min.js +53 -0
  9. package/dist/build/pdf-full.js +6078 -0
  10. package/dist/build/pdf-full.meta.json +90 -0
  11. package/dist/build/pdf-full.min.js +32 -0
  12. package/dist/build/pdf-large-rw.js +10367 -0
  13. package/dist/build/pdf-large-rw.meta.json +99 -0
  14. package/dist/build/pdf-large-rw.min.js +53 -0
  15. package/dist/build/pdf-large.js +5473 -0
  16. package/dist/build/pdf-large.meta.json +84 -0
  17. package/dist/build/pdf-large.min.js +32 -0
  18. package/dist/build/pdf-legacy-rw.js +12402 -0
  19. package/dist/build/pdf-legacy-rw.meta.json +110 -0
  20. package/dist/build/pdf-legacy-rw.min.js +53 -0
  21. package/dist/build/pdf-legacy.js +7508 -0
  22. package/dist/build/pdf-legacy.meta.json +95 -0
  23. package/dist/build/pdf-legacy.min.js +32 -0
  24. package/dist/build/pdf-rw.js +7578 -0
  25. package/dist/build/pdf-rw.meta.json +77 -0
  26. package/dist/build/pdf-rw.min.js +53 -0
  27. package/dist/build/pdf.js +2684 -0
  28. package/dist/build/pdf.meta.json +62 -0
  29. package/dist/build/pdf.min.js +32 -0
  30. package/dist/standalone/pdf-full-rw.js +16798 -0
  31. package/dist/standalone/pdf-full-rw.meta.json +78 -0
  32. package/dist/standalone/pdf-full-rw.min.js +56 -0
  33. package/dist/standalone/pdf-full.js +11904 -0
  34. package/dist/standalone/pdf-full.meta.json +63 -0
  35. package/dist/standalone/pdf-full.min.js +35 -0
  36. package/dist/standalone/pdf-large-rw.js +16193 -0
  37. package/dist/standalone/pdf-large-rw.meta.json +72 -0
  38. package/dist/standalone/pdf-large-rw.min.js +56 -0
  39. package/dist/standalone/pdf-large.js +11299 -0
  40. package/dist/standalone/pdf-large.meta.json +57 -0
  41. package/dist/standalone/pdf-large.min.js +35 -0
  42. package/dist/standalone/pdf-legacy-rw.js +18228 -0
  43. package/dist/standalone/pdf-legacy-rw.meta.json +83 -0
  44. package/dist/standalone/pdf-legacy-rw.min.js +56 -0
  45. package/dist/standalone/pdf-legacy.js +13334 -0
  46. package/dist/standalone/pdf-legacy.meta.json +68 -0
  47. package/dist/standalone/pdf-legacy.min.js +35 -0
  48. package/dist/standalone/pdf-rw.js +13404 -0
  49. package/dist/standalone/pdf-rw.meta.json +50 -0
  50. package/dist/standalone/pdf-rw.min.js +56 -0
  51. package/dist/standalone/pdf.js +8510 -0
  52. package/dist/standalone/pdf.meta.json +35 -0
  53. package/dist/standalone/pdf.min.js +35 -0
  54. package/docs/README.md +53 -0
  55. package/docs/api/README.md +38 -0
  56. package/docs/api/_shared/README.md +91 -0
  57. package/docs/api/action/README.md +29 -0
  58. package/docs/api/action/action.md +81 -0
  59. package/docs/api/action/goTo.md +66 -0
  60. package/docs/api/action/launch.md +58 -0
  61. package/docs/api/action/named.md +55 -0
  62. package/docs/api/action/uri.md +54 -0
  63. package/docs/api/annot/README.md +53 -0
  64. package/docs/api/annot/annot.md +114 -0
  65. package/docs/api/annot/fileAttach.md +53 -0
  66. package/docs/api/annot/freeText.md +68 -0
  67. package/docs/api/annot/ink.md +69 -0
  68. package/docs/api/annot/link.md +74 -0
  69. package/docs/api/annot/markup.md +83 -0
  70. package/docs/api/annot/popup.md +52 -0
  71. package/docs/api/annot/projection.md +56 -0
  72. package/docs/api/annot/redact.md +67 -0
  73. package/docs/api/annot/square.md +87 -0
  74. package/docs/api/annot/stamp.md +54 -0
  75. package/docs/api/annot/text.md +69 -0
  76. package/docs/api/annot/widget.md +69 -0
  77. package/docs/api/associatedFiles/README.md +9 -0
  78. package/docs/api/associatedFiles/associatedFiles.md +78 -0
  79. package/docs/api/bundles/README.md +68 -0
  80. package/docs/api/bundles/dist-matrix.md +165 -0
  81. package/docs/api/bundles/pdf-full.md +148 -0
  82. package/docs/api/bundles/pdf-large.md +144 -0
  83. package/docs/api/bundles/pdf-legacy.md +169 -0
  84. package/docs/api/content/README.md +29 -0
  85. package/docs/api/content/color.md +99 -0
  86. package/docs/api/content/graphics.md +114 -0
  87. package/docs/api/content/images.md +124 -0
  88. package/docs/api/content/ops.md +100 -0
  89. package/docs/api/content/stream.md +107 -0
  90. package/docs/api/content/text.md +98 -0
  91. package/docs/api/crypto/README.md +29 -0
  92. package/docs/api/crypto/aesGcm.md +72 -0
  93. package/docs/api/crypto/permissions.md +79 -0
  94. package/docs/api/crypto/security.md +98 -0
  95. package/docs/api/crypto/standardV4.md +104 -0
  96. package/docs/api/crypto/standardV5.md +84 -0
  97. package/docs/api/crypto/standardV6.md +93 -0
  98. package/docs/api/destination/README.md +9 -0
  99. package/docs/api/destination/destination.md +79 -0
  100. package/docs/api/document/README.md +29 -0
  101. package/docs/api/document/builder.md +281 -0
  102. package/docs/api/document/catalog.md +98 -0
  103. package/docs/api/document/document.md +187 -0
  104. package/docs/api/document/encryptedWriter.md +149 -0
  105. package/docs/api/document/incrementalWriter.md +148 -0
  106. package/docs/api/document/page.md +99 -0
  107. package/docs/api/document/pages.md +82 -0
  108. package/docs/api/document/resources.md +102 -0
  109. package/docs/api/document/writer.md +157 -0
  110. package/docs/api/document/xrefStreamWriter.md +122 -0
  111. package/docs/api/embedded/README.md +13 -0
  112. package/docs/api/embedded/collection.md +80 -0
  113. package/docs/api/embedded/embeddedFile.md +86 -0
  114. package/docs/api/embedded/fileSpec.md +87 -0
  115. package/docs/api/errors.md +110 -0
  116. package/docs/api/extra/3d-richmedia.md +76 -0
  117. package/docs/api/extra/README.md +99 -0
  118. package/docs/api/extra/annot-extended.md +71 -0
  119. package/docs/api/extra/associated-files.md +70 -0
  120. package/docs/api/extra/ccitt-fax-decoder.md +74 -0
  121. package/docs/api/extra/color-spaces-extended.md +72 -0
  122. package/docs/api/extra/content-ops-extended.md +82 -0
  123. package/docs/api/extra/document-parts.md +69 -0
  124. package/docs/api/extra/embedded-files-portfolio.md +87 -0
  125. package/docs/api/extra/font-cid-typed.md +77 -0
  126. package/docs/api/extra/font-color-tagging.md +76 -0
  127. package/docs/api/extra/form-actions-extended.md +75 -0
  128. package/docs/api/extra/info-dict-deprecated.md +72 -0
  129. package/docs/api/extra/jbig2-read.md +80 -0
  130. package/docs/api/extra/legacy-deprecated-annots.md +89 -0
  131. package/docs/api/extra/legacy-deprecated-filters.md +78 -0
  132. package/docs/api/extra/legacy-rc4-read.md +74 -0
  133. package/docs/api/extra/legacy-xfa-read.md +65 -0
  134. package/docs/api/extra/linearization-write.md +71 -0
  135. package/docs/api/extra/misc.md +93 -0
  136. package/docs/api/extra/optional-content-extended.md +83 -0
  137. package/docs/api/extra/pdf-a-output-intent.md +65 -0
  138. package/docs/api/extra/pdf-sandbox.md +76 -0
  139. package/docs/api/extra/pdf-ua-tagged.md +63 -0
  140. package/docs/api/extra/pdf-x-prepress.md +65 -0
  141. package/docs/api/extra/redaction-iso32005.md +65 -0
  142. package/docs/api/extra/shading-typed.md +73 -0
  143. package/docs/api/extra/sig-aes-gcm.md +69 -0
  144. package/docs/api/extra/sig-pades.md +103 -0
  145. package/docs/api/extra/tagged-pdf-typed.md +78 -0
  146. package/docs/api/extra/transparency-typed.md +74 -0
  147. package/docs/api/extra/well-tagged-pdf.md +61 -0
  148. package/docs/api/extra/xmp-extended.md +65 -0
  149. package/docs/api/font/README.md +25 -0
  150. package/docs/api/font/embed.md +157 -0
  151. package/docs/api/font/encoding.md +95 -0
  152. package/docs/api/font/font.md +97 -0
  153. package/docs/api/font/type3.md +89 -0
  154. package/docs/api/form/README.md +35 -0
  155. package/docs/api/form/acroform.md +88 -0
  156. package/docs/api/form/appearance.md +87 -0
  157. package/docs/api/form/button.md +97 -0
  158. package/docs/api/form/choice.md +96 -0
  159. package/docs/api/form/fieldTree.md +93 -0
  160. package/docs/api/form/signature.md +90 -0
  161. package/docs/api/form/text.md +88 -0
  162. package/docs/api/linearization/README.md +11 -0
  163. package/docs/api/linearization/linearization.md +81 -0
  164. package/docs/api/main.md +116 -0
  165. package/docs/api/metadata/README.md +10 -0
  166. package/docs/api/metadata/info.md +70 -0
  167. package/docs/api/metadata/xmp.md +62 -0
  168. package/docs/api/ocg/README.md +23 -0
  169. package/docs/api/ocg/config.md +95 -0
  170. package/docs/api/ocg/ocg.md +77 -0
  171. package/docs/api/outline/README.md +11 -0
  172. package/docs/api/outline/outline.md +107 -0
  173. package/docs/api/pdf.md +152 -0
  174. package/docs/api/prepress/README.md +10 -0
  175. package/docs/api/prepress/outputIntent.md +79 -0
  176. package/docs/api/prepress/pageBoundary.md +75 -0
  177. package/docs/api/sig/README.md +32 -0
  178. package/docs/api/sig/byteRange.md +120 -0
  179. package/docs/api/sig/certChain.md +84 -0
  180. package/docs/api/sig/dss.md +111 -0
  181. package/docs/api/sig/oids.md +76 -0
  182. package/docs/api/sig/sha1.md +72 -0
  183. package/docs/api/sig/sign.md +317 -0
  184. package/docs/api/sig/signature.md +178 -0
  185. package/docs/api/sig/timestamp.md +84 -0
  186. package/docs/api/syntax/README.md +29 -0
  187. package/docs/api/syntax/crossRefStream.md +115 -0
  188. package/docs/api/syntax/filters/README.md +50 -0
  189. package/docs/api/syntax/filters/ascii85.md +76 -0
  190. package/docs/api/syntax/filters/asciiHex.md +73 -0
  191. package/docs/api/syntax/filters/dispatch.md +125 -0
  192. package/docs/api/syntax/filters/flate.md +134 -0
  193. package/docs/api/syntax/filters/runLength.md +78 -0
  194. package/docs/api/syntax/objStream.md +88 -0
  195. package/docs/api/syntax/parser-obj.md +97 -0
  196. package/docs/api/syntax/parser.md +151 -0
  197. package/docs/api/syntax/serializer.md +109 -0
  198. package/docs/api/syntax/tokenizer.md +104 -0
  199. package/docs/api/syntax/trailer.md +85 -0
  200. package/docs/api/syntax/xref.md +139 -0
  201. package/docs/api/tagged/README.md +25 -0
  202. package/docs/api/tagged/classMap.md +67 -0
  203. package/docs/api/tagged/markedContent.md +62 -0
  204. package/docs/api/tagged/parentTree.md +67 -0
  205. package/docs/api/tagged/roleMap.md +67 -0
  206. package/docs/api/tagged/structElement.md +76 -0
  207. package/docs/api/tagged/structTree.md +75 -0
  208. package/docs/guide/coverage.md +113 -0
  209. package/docs/guide/crypto.md +121 -0
  210. package/docs/guide/extending.md +76 -0
  211. package/docs/guide/getting-started.md +75 -0
  212. package/docs/guide/legacy-1.7.md +42 -0
  213. package/docs/guide/pades-integration.md +579 -0
  214. package/docs/guide/read-pdf.md +89 -0
  215. package/package.json +97 -4
  216. package/src/_shared/index.js +179 -0
  217. package/src/action/action.js +119 -0
  218. package/src/action/goTo.js +89 -0
  219. package/src/action/launch.js +61 -0
  220. package/src/action/named.js +54 -0
  221. package/src/action/uri.js +51 -0
  222. package/src/annot/annot.js +212 -0
  223. package/src/annot/fileAttach.js +55 -0
  224. package/src/annot/freeText.js +82 -0
  225. package/src/annot/ink.js +77 -0
  226. package/src/annot/link.js +77 -0
  227. package/src/annot/markup.js +91 -0
  228. package/src/annot/popup.js +53 -0
  229. package/src/annot/projection.js +52 -0
  230. package/src/annot/redact.js +87 -0
  231. package/src/annot/square.js +132 -0
  232. package/src/annot/stamp.js +48 -0
  233. package/src/annot/text.js +54 -0
  234. package/src/annot/widget.js +61 -0
  235. package/src/associatedFiles/associatedFiles.js +86 -0
  236. package/src/bundles/pdf-full.js +91 -0
  237. package/src/bundles/pdf-large.js +81 -0
  238. package/src/bundles/pdf-legacy.js +107 -0
  239. package/src/content/color.js +114 -0
  240. package/src/content/graphics.js +192 -0
  241. package/src/content/images.js +160 -0
  242. package/src/content/ops.js +137 -0
  243. package/src/content/stream.js +154 -0
  244. package/src/content/text.js +125 -0
  245. package/src/crypto/aesGcm.js +123 -0
  246. package/src/crypto/permissions.js +112 -0
  247. package/src/crypto/security.js +327 -0
  248. package/src/crypto/standardV4.js +443 -0
  249. package/src/crypto/standardV5.js +306 -0
  250. package/src/crypto/standardV6.js +334 -0
  251. package/src/destination/destination.js +183 -0
  252. package/src/document/builder.js +618 -0
  253. package/src/document/catalog.js +100 -0
  254. package/src/document/document.js +472 -0
  255. package/src/document/encryptedWriter.js +554 -0
  256. package/src/document/incrementalWriter.js +514 -0
  257. package/src/document/page.js +131 -0
  258. package/src/document/pages.js +103 -0
  259. package/src/document/resources.js +146 -0
  260. package/src/document/writer.js +211 -0
  261. package/src/document/xrefStreamWriter.js +353 -0
  262. package/src/embedded/collection.js +102 -0
  263. package/src/embedded/embeddedFile.js +99 -0
  264. package/src/embedded/fileSpec.js +137 -0
  265. package/src/errors.js +78 -0
  266. package/src/extra/3d-richmedia.js +171 -0
  267. package/src/extra/annot-extended.js +200 -0
  268. package/src/extra/associated-files.js +131 -0
  269. package/src/extra/ccitt-fax-decoder.js +776 -0
  270. package/src/extra/color-spaces-extended.js +196 -0
  271. package/src/extra/content-ops-extended.js +153 -0
  272. package/src/extra/document-parts.js +149 -0
  273. package/src/extra/embedded-files-portfolio.js +234 -0
  274. package/src/extra/font-cid-typed.js +185 -0
  275. package/src/extra/font-color-tagging.js +144 -0
  276. package/src/extra/form-actions-extended.js +196 -0
  277. package/src/extra/info-dict-deprecated.js +137 -0
  278. package/src/extra/jbig2-read.js +169 -0
  279. package/src/extra/legacy-deprecated-annots.js +198 -0
  280. package/src/extra/legacy-deprecated-filters.js +167 -0
  281. package/src/extra/legacy-rc4-read.js +235 -0
  282. package/src/extra/legacy-xfa-read.js +104 -0
  283. package/src/extra/linearization-write.js +97 -0
  284. package/src/extra/misc.js +217 -0
  285. package/src/extra/optional-content-extended.js +142 -0
  286. package/src/extra/pdf-a-output-intent.js +112 -0
  287. package/src/extra/pdf-sandbox.js +88 -0
  288. package/src/extra/pdf-ua-tagged.js +116 -0
  289. package/src/extra/pdf-x-prepress.js +114 -0
  290. package/src/extra/redaction-iso32005.js +136 -0
  291. package/src/extra/shading-typed.js +222 -0
  292. package/src/extra/sig-aes-gcm.js +135 -0
  293. package/src/extra/sig-pades.js +242 -0
  294. package/src/extra/tagged-pdf-typed.js +203 -0
  295. package/src/extra/transparency-typed.js +135 -0
  296. package/src/extra/well-tagged-pdf.js +138 -0
  297. package/src/extra/xmp-extended.js +190 -0
  298. package/src/font/embed.js +480 -0
  299. package/src/font/encoding.js +92 -0
  300. package/src/font/font.js +101 -0
  301. package/src/font/type3.js +75 -0
  302. package/src/form/acroform.js +94 -0
  303. package/src/form/appearance.js +90 -0
  304. package/src/form/button.js +105 -0
  305. package/src/form/choice.js +152 -0
  306. package/src/form/fieldTree.js +120 -0
  307. package/src/form/signature.js +100 -0
  308. package/src/form/text.js +101 -0
  309. package/src/linearization/linearization.js +107 -0
  310. package/src/main.js +411 -0
  311. package/src/metadata/info.js +87 -0
  312. package/src/metadata/xmp.js +62 -0
  313. package/src/ocg/config.js +156 -0
  314. package/src/ocg/ocg.js +124 -0
  315. package/src/outline/outline.js +157 -0
  316. package/src/pdf.js +133 -0
  317. package/src/prepress/outputIntent.js +118 -0
  318. package/src/prepress/pageBoundary.js +108 -0
  319. package/src/sig/byteRange.js +306 -0
  320. package/src/sig/certChain.js +247 -0
  321. package/src/sig/dss.js +317 -0
  322. package/src/sig/oids.js +157 -0
  323. package/src/sig/sha1.js +142 -0
  324. package/src/sig/sign.js +1899 -0
  325. package/src/sig/signature.js +1441 -0
  326. package/src/sig/timestamp.js +236 -0
  327. package/src/syntax/crossRefStream.js +133 -0
  328. package/src/syntax/filters/ascii85.js +122 -0
  329. package/src/syntax/filters/asciiHex.js +83 -0
  330. package/src/syntax/filters/dispatch.js +176 -0
  331. package/src/syntax/filters/flate.js +316 -0
  332. package/src/syntax/filters/runLength.js +96 -0
  333. package/src/syntax/objStream.js +99 -0
  334. package/src/syntax/parser-obj.js +52 -0
  335. package/src/syntax/parser.js +321 -0
  336. package/src/syntax/serializer.js +221 -0
  337. package/src/syntax/tokenizer.js +290 -0
  338. package/src/syntax/trailer.js +76 -0
  339. package/src/syntax/xref.js +341 -0
  340. package/src/tagged/classMap.js +81 -0
  341. package/src/tagged/markedContent.js +123 -0
  342. package/src/tagged/parentTree.js +126 -0
  343. package/src/tagged/roleMap.js +107 -0
  344. package/src/tagged/structElement.js +138 -0
  345. package/src/tagged/structTree.js +94 -0
@@ -0,0 +1,104 @@
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: LEGACY read-only /XFA reader.
8
+ *
9
+ * PDF 2.0 forbids XFA on write, but legacy documents still need to be
10
+ * parsed. The /XFA entry on the AcroForm dict comes in two shapes:
11
+ *
12
+ * (a) a single stream containing a complete XDP document, or
13
+ * (b) an array alternating string-keys and streams, e.g.
14
+ * ['preamble', stream, 'config', stream, 'template', stream, …].
15
+ *
16
+ * This extra surfaces the packets as opaque XML byte buffers on a
17
+ * `_legacy.xfa` object — no further interpretation is attempted.
18
+ *
19
+ * @module pdf/extra/legacy-xfa-read
20
+ */
21
+
22
+ import { pdfErrors } from '../errors.js';
23
+
24
+ export const pdfLegacyXfaRead = {
25
+ name: 'pdfLegacyXfaRead',
26
+ dependencies: ['pdfErrors'],
27
+ deps: [pdfErrors],
28
+
29
+ factory(errors) {
30
+ const { ParseError } = errors;
31
+ const KNOWN_PACKETS = new Set([
32
+ 'xdp', 'preamble', 'config', 'template', 'localeSet',
33
+ 'datasets', 'form', 'connectionSet', 'sourceSet',
34
+ 'stylesheet', 'xmpmeta', 'signature', 'postamble', 'pdf'
35
+ ]);
36
+
37
+ function isStream(v) { return v && v.type === 'stream'; }
38
+ function isStr(v) { return v && v.type === 'string'; }
39
+ function isArr(v) { return v && v.type === 'array'; }
40
+
41
+ function streamBytes(s) {
42
+ if (!isStream(s)) {
43
+ throw new ParseError('pdf/xfa/bad-packet',
44
+ 'XFA packet must be a stream',
45
+ { context: { type: s && s.type } });
46
+ }
47
+ return s.raw;
48
+ }
49
+
50
+ function decodeString(v) {
51
+ if (!isStr(v)) {
52
+ throw new ParseError('pdf/xfa/bad-key',
53
+ 'XFA array key must be a PDF string',
54
+ { context: { type: v && v.type } });
55
+ }
56
+ const b = v.value;
57
+ if (b instanceof Uint8Array) return new TextDecoder('latin1').decode(b);
58
+ return String(b);
59
+ }
60
+
61
+ function readXfa(xfa) {
62
+ if (isStream(xfa)) {
63
+ return {
64
+ _legacy: { xfa: { shape: 'stream', xdp: streamBytes(xfa) } }
65
+ };
66
+ }
67
+ if (isArr(xfa)) {
68
+ if (xfa.items.length % 2 !== 0) {
69
+ throw new ParseError('pdf/xfa/odd-array',
70
+ 'XFA array must have even item count (key/stream pairs)',
71
+ { context: { length: xfa.items.length } });
72
+ }
73
+ const packets = {};
74
+ const order = [];
75
+ const unknown = [];
76
+ for (let i = 0; i < xfa.items.length; i += 2) {
77
+ const key = decodeString(xfa.items[i]);
78
+ const bytes = streamBytes(xfa.items[i + 1]);
79
+ packets[key] = bytes;
80
+ order.push(key);
81
+ if (!KNOWN_PACKETS.has(key)) unknown.push(key);
82
+ }
83
+ return {
84
+ _legacy: {
85
+ xfa: { shape: 'array', packets, order, unknownPackets: unknown }
86
+ }
87
+ };
88
+ }
89
+ throw new ParseError('pdf/xfa/bad-shape',
90
+ '/XFA must be a stream or array',
91
+ { context: { type: xfa && xfa.type } });
92
+ }
93
+
94
+ function isKnownPacket(name) {
95
+ return KNOWN_PACKETS.has(String(name));
96
+ }
97
+
98
+ return {
99
+ readXfa,
100
+ isKnownPacket,
101
+ KNOWN_PACKETS
102
+ };
103
+ }
104
+ };
@@ -0,0 +1,97 @@
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: Linearization Parameter Dictionary emitter
8
+ * (builds and validates the dictionary; the hint stream is an empty stub).
9
+ *
10
+ * @module pdf/extra/linearization-write
11
+ */
12
+
13
+ /**
14
+ * Module factory.
15
+ */
16
+ import { pdfErrors } from '../errors.js';
17
+ import { pdfParser } from '../syntax/parser.js';
18
+
19
+ export const pdfLinearizationWrite = {
20
+ name: 'pdfLinearizationWrite',
21
+ dependencies: ['pdfErrors', 'pdfParser'],
22
+ deps: [pdfErrors, pdfParser],
23
+ factory(errors, parser) {
24
+ const { RenderError } = errors;
25
+ const { obj } = parser;
26
+
27
+ /** Frozen list of required entries (§F.2 Table F.1). */
28
+ const LINEARIZED_KEYS = Object.freeze([
29
+ 'Linearized', 'L', 'H', 'O', 'E', 'N', 'T'
30
+ ]);
31
+
32
+ function buildLinearizedDict(p) {
33
+ if (!p || typeof p !== 'object') {
34
+ throw new RenderError('pdf/extra/linwrite/bad-params',
35
+ 'buildLinearizedDict requires a parameters object',
36
+ { context: { type: typeof p } });
37
+ }
38
+ for (const k of ['fileLength', 'firstPageObj', 'firstPageEnd',
39
+ 'pageCount', 'mainXrefOffset']) {
40
+ if (!Number.isFinite(p[k])) {
41
+ throw new RenderError('pdf/extra/linwrite/missing-' + k,
42
+ 'parameter ' + k + ' is required and must be a number',
43
+ { context: { key: k, type: typeof p[k] } });
44
+ }
45
+ }
46
+ if (!Number.isFinite(p.hintOffset) || !Number.isFinite(p.hintLength)) {
47
+ throw new RenderError('pdf/extra/linwrite/missing-hint',
48
+ 'hintOffset and hintLength are required',
49
+ { context: { hintOffset: p.hintOffset, hintLength: p.hintLength } });
50
+ }
51
+ const entries = {
52
+ Linearized: obj.real(p.version != null ? p.version : 1.0),
53
+ L: obj.int(p.fileLength),
54
+ H: obj.array([obj.int(p.hintOffset), obj.int(p.hintLength)]),
55
+ O: obj.int(p.firstPageObj),
56
+ E: obj.int(p.firstPageEnd),
57
+ N: obj.int(p.pageCount),
58
+ T: obj.int(p.mainXrefOffset)
59
+ };
60
+ if (p.firstPage != null) entries.P = obj.int(p.firstPage);
61
+ return obj.dict(entries);
62
+ }
63
+
64
+ function buildHintStreamStub() {
65
+ const dict = obj.dict({
66
+ Length: obj.int(0),
67
+ S: obj.int(0)
68
+ });
69
+ return obj.stream(dict, new Uint8Array(0));
70
+ }
71
+
72
+ function validateLinearizedDict(dict) {
73
+ if (!dict || dict.type !== 'dict') {
74
+ throw new RenderError('pdf/extra/linwrite/validate/not-dict',
75
+ 'expected a dict',
76
+ { context: { type: dict && dict.type } });
77
+ }
78
+ const errs = [];
79
+ for (const k of LINEARIZED_KEYS) {
80
+ if (!dict.entries[k]) errs.push('missing /' + k);
81
+ }
82
+ const h = dict.entries.H;
83
+ if (h && (h.type !== 'array' || h.items.length < 2)) {
84
+ errs.push('/H must be an array of at least 2 integers');
85
+ }
86
+ return { pass: errs.length === 0, errors: errs, warnings: [] };
87
+ }
88
+
89
+ return {
90
+ buildLinearizedDict,
91
+ buildHintStreamStub,
92
+ validateLinearizedDict,
93
+ LINEARIZED_KEYS
94
+ };
95
+ }
96
+ };
97
+
@@ -0,0 +1,217 @@
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: miscellaneous tail typing.
8
+ *
9
+ * Covers the long tail of Catalog-level entries that don't warrant
10
+ * their own L1 module:
11
+ *
12
+ * - /SpiderInfo — Web-Capture provenance (ISO 32000-2 §14.10.2).
13
+ * - /Threads — array of article-thread dicts (§12.4.3).
14
+ * - /Legal — legal-attestation dict (§12.8.5).
15
+ * - /Requirements — array of requirement dicts (§12.10).
16
+ * - /Perms — extra permission flavours beyond /UR3, notably
17
+ * /DocMDP transformation parameters (§12.8.2.2).
18
+ * - /NeedsRendering — boolean indicator for XFA-bearing forms.
19
+ *
20
+ * @module pdf/extra/misc
21
+ */
22
+
23
+ import { pdfErrors } from '../errors.js';
24
+
25
+ export const pdfMisc = {
26
+ name: 'pdfMisc',
27
+ dependencies: ['pdfErrors'],
28
+ deps: [pdfErrors],
29
+
30
+ factory(errors) {
31
+ const { ParseError } = errors;
32
+ const REQUIREMENT_S_VALUES = new Set([
33
+ 'EnableJavaScripts'
34
+ ]);
35
+
36
+ function isDict(v) { return v && v.type === 'dict'; }
37
+ function isName(v) { return v && v.type === 'name'; }
38
+ function isArr(v) { return v && v.type === 'array'; }
39
+ function isBool(v) { return v && v.type === 'bool'; }
40
+ function isInt(v) { return v && v.type === 'int'; }
41
+
42
+ function typeSpiderInfo(dict) {
43
+ if (!isDict(dict)) {
44
+ throw new ParseError('pdf/misc/spider-not-dict',
45
+ '/SpiderInfo must be a dict');
46
+ }
47
+ const e = dict.entries;
48
+ const out = { raw: dict, _extras: {} };
49
+ if (e.V) {
50
+ if (!isInt(e.V) && !(e.V && e.V.type === 'real')) {
51
+ throw new ParseError('pdf/misc/spider-bad-V',
52
+ '/SpiderInfo /V must be numeric');
53
+ }
54
+ out.version = e.V.value;
55
+ }
56
+ if (e.C) {
57
+ if (!isArr(e.C)) {
58
+ throw new ParseError('pdf/misc/spider-bad-C',
59
+ '/SpiderInfo /C must be an array');
60
+ }
61
+ out.commands = e.C.items;
62
+ }
63
+ const KNOWN = new Set(['V', 'C']);
64
+ for (const k of Object.keys(e)) {
65
+ if (!KNOWN.has(k)) out._extras[k] = e[k];
66
+ }
67
+ return out;
68
+ }
69
+
70
+ function typeThreads(arr) {
71
+ if (!isArr(arr)) {
72
+ throw new ParseError('pdf/misc/threads-not-array',
73
+ '/Threads must be an array');
74
+ }
75
+ const out = [];
76
+ for (let i = 0; i < arr.items.length; i++) {
77
+ const it = arr.items[i];
78
+ if (it.type !== 'ref' && !isDict(it)) {
79
+ throw new ParseError('pdf/misc/thread-bad-entry',
80
+ '/Threads entry must be a dict or ref',
81
+ { context: { index: i } });
82
+ }
83
+ out.push(it);
84
+ }
85
+ return out;
86
+ }
87
+
88
+ function typeLegal(dict) {
89
+ if (!isDict(dict)) {
90
+ throw new ParseError('pdf/misc/legal-not-dict',
91
+ '/Legal must be a dict');
92
+ }
93
+ const e = dict.entries;
94
+ const out = { raw: dict, flags: {}, _extras: {} };
95
+ const KNOWN_FLAGS = new Set([
96
+ 'JavaScriptActions', 'LaunchActions', 'URIActions',
97
+ 'MovieActions', 'SoundActions', 'HiddenAnnotations',
98
+ 'NonEmbeddedFonts', 'DevDepGS_OP', 'DevDepGS_HT',
99
+ 'DevDepGS_TR', 'DevDepGS_UCR', 'DevDepGS_FL', 'DevDepGS_BG',
100
+ 'Annotations', 'ExternalRefXobjects', 'ExternalOPIdicts',
101
+ 'ExternalStreams', 'TrueTypeFonts', 'AlternateImages'
102
+ ]);
103
+ for (const [k, v] of Object.entries(e)) {
104
+ if (KNOWN_FLAGS.has(k)) {
105
+ if (!isInt(v)) {
106
+ throw new ParseError('pdf/misc/legal-bad-flag',
107
+ `/Legal /${k} must be an integer count`,
108
+ { context: { key: k } });
109
+ }
110
+ out.flags[k] = v.value;
111
+ } else if (k === 'Attestation') {
112
+ if (v.type !== 'string') {
113
+ throw new ParseError('pdf/misc/legal-bad-attestation',
114
+ '/Attestation must be a string');
115
+ }
116
+ out.attestation = v.value;
117
+ } else {
118
+ out._extras[k] = v;
119
+ }
120
+ }
121
+ return out;
122
+ }
123
+
124
+ function typeRequirements(arr) {
125
+ if (!isArr(arr)) {
126
+ throw new ParseError('pdf/misc/req-not-array',
127
+ '/Requirements must be an array');
128
+ }
129
+ const out = [];
130
+ for (let i = 0; i < arr.items.length; i++) {
131
+ const it = arr.items[i];
132
+ if (!isDict(it)) {
133
+ throw new ParseError('pdf/misc/req-bad-entry',
134
+ '/Requirements entry must be a dict',
135
+ { context: { index: i } });
136
+ }
137
+ const e = it.entries;
138
+ if (!e.S || !isName(e.S)) {
139
+ throw new ParseError('pdf/misc/req-missing-S',
140
+ '/Requirements entry needs /S name',
141
+ { context: { index: i } });
142
+ }
143
+ out.push({
144
+ raw: it,
145
+ s: e.S.value,
146
+ standard: REQUIREMENT_S_VALUES.has(e.S.value),
147
+ rh: e.RH
148
+ });
149
+ }
150
+ return out;
151
+ }
152
+
153
+ function typeDocMdpParams(dict) {
154
+ if (!isDict(dict)) {
155
+ throw new ParseError('pdf/misc/docmdp-not-dict',
156
+ '/DocMDP transform params must be a dict');
157
+ }
158
+ const e = dict.entries;
159
+ const out = { raw: dict, _extras: {} };
160
+ if (e.P) {
161
+ if (!isInt(e.P) || e.P.value < 1 || e.P.value > 3) {
162
+ throw new ParseError('pdf/misc/docmdp-bad-P',
163
+ '/DocMDP /P must be 1, 2, or 3');
164
+ }
165
+ out.permission = e.P.value;
166
+ }
167
+ if (e.V) {
168
+ if (!isName(e.V)) {
169
+ throw new ParseError('pdf/misc/docmdp-bad-V',
170
+ '/DocMDP /V must be a name');
171
+ }
172
+ out.version = e.V.value;
173
+ }
174
+ const KNOWN = new Set(['Type', 'P', 'V']);
175
+ for (const k of Object.keys(e)) {
176
+ if (!KNOWN.has(k)) out._extras[k] = e[k];
177
+ }
178
+ return out;
179
+ }
180
+
181
+ function typePerms(dict) {
182
+ if (!isDict(dict)) {
183
+ throw new ParseError('pdf/misc/perms-not-dict',
184
+ '/Perms must be a dict');
185
+ }
186
+ const e = dict.entries;
187
+ const out = { raw: dict, _extras: {} };
188
+ if (e.DocMDP) out.docMDP = e.DocMDP;
189
+ if (e.UR3) out.ur3 = e.UR3;
190
+ if (e.UR) out.ur = e.UR;
191
+ const KNOWN = new Set(['DocMDP', 'UR3', 'UR']);
192
+ for (const k of Object.keys(e)) {
193
+ if (!KNOWN.has(k)) out._extras[k] = e[k];
194
+ }
195
+ return out;
196
+ }
197
+
198
+ function typeNeedsRendering(v) {
199
+ if (!isBool(v)) {
200
+ throw new ParseError('pdf/misc/needsrendering-bad',
201
+ '/NeedsRendering must be boolean');
202
+ }
203
+ return v.value;
204
+ }
205
+
206
+ return {
207
+ typeSpiderInfo,
208
+ typeThreads,
209
+ typeLegal,
210
+ typeRequirements,
211
+ typeDocMdpParams,
212
+ typePerms,
213
+ typeNeedsRendering,
214
+ REQUIREMENT_S_VALUES
215
+ };
216
+ }
217
+ };
@@ -0,0 +1,142 @@
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 OCG/OCMD support.
8
+ *
9
+ * ISO 32000-2 §8.11.2.4 defines OCMD `/VE` (Visibility Expression) as a
10
+ * nested array `[op, operand, ...]` with ops `And`, `Or`, `Not`. This
11
+ * module evaluates such expressions, types `/Order` trees with embedded
12
+ * sub-headings (string leaves), and surfaces RBGroups + `/Intent`
13
+ * routing (View vs Design).
14
+ *
15
+ * @module pdf/extra/optional-content-extended
16
+ */
17
+
18
+ /**
19
+ * Module factory.
20
+ */
21
+ import { pdfErrors } from '../errors.js';
22
+ import { pdfParser } from '../syntax/parser.js';
23
+
24
+ export const pdfOptionalContentExtended = {
25
+ name: 'pdfOptionalContentExtended',
26
+ dependencies: ['pdfErrors', 'pdfParser'],
27
+ deps: [pdfErrors, pdfParser],
28
+ factory(errors, parser) {
29
+ const { ParseError } = errors;
30
+ const { isType } = parser;
31
+
32
+ const VE_OPERATORS = Object.freeze(['And', 'Or', 'Not']);
33
+
34
+ function evaluateVE(ve, on) {
35
+ if (!ve) {
36
+ throw new ParseError('pdf/extra/ocgx/ve/null',
37
+ 'VE expression is null');
38
+ }
39
+ if (!(on instanceof Set)) {
40
+ throw new ParseError('pdf/extra/ocgx/ve/bad-state',
41
+ 'evaluateVE requires a Set of on refs',
42
+ { context: { type: typeof on } });
43
+ }
44
+ if (ve.type === 'ref') {
45
+ return on.has(ve.num + ' ' + ve.gen);
46
+ }
47
+ if (ve.type !== 'array' || ve.items.length === 0) {
48
+ throw new ParseError('pdf/extra/ocgx/ve/bad-shape',
49
+ 'VE must be an array starting with an operator',
50
+ { context: { type: ve.type } });
51
+ }
52
+ const op = ve.items[0];
53
+ if (!isType(op, 'name') || !VE_OPERATORS.includes(op.value)) {
54
+ throw new ParseError('pdf/extra/ocgx/ve/bad-op',
55
+ 'VE operator must be /And, /Or or /Not',
56
+ { context: { actual: op && op.value } });
57
+ }
58
+ const operands = ve.items.slice(1);
59
+ if (op.value === 'Not') {
60
+ if (operands.length !== 1) {
61
+ throw new ParseError('pdf/extra/ocgx/ve/not-arity',
62
+ '/Not requires exactly one operand');
63
+ }
64
+ return !evaluateVE(operands[0], on);
65
+ }
66
+ if (op.value === 'And') return operands.every(o => evaluateVE(o, on));
67
+ return operands.some(o => evaluateVE(o, on));
68
+ }
69
+
70
+ function typeOrderTree(arr) {
71
+ if (!arr || arr.type !== 'array') {
72
+ throw new ParseError('pdf/extra/ocgx/order/not-array',
73
+ '/Order must be an array',
74
+ { context: { type: arr && arr.type } });
75
+ }
76
+ const out = [];
77
+ for (const it of arr.items) {
78
+ if (!it) continue;
79
+ if (it.type === 'string') {
80
+ out.push({ kind: 'heading', text: it.value });
81
+ } else if (it.type === 'array') {
82
+ out.push({ kind: 'group', children: typeOrderTree(it) });
83
+ } else if (it.type === 'ref') {
84
+ out.push({ kind: 'ocg', ref: { num: it.num, gen: it.gen } });
85
+ } else {
86
+ out.push({ kind: 'other', value: it });
87
+ }
88
+ }
89
+ return out;
90
+ }
91
+
92
+ function typeRBGroups(arr) {
93
+ if (!arr || arr.type !== 'array') {
94
+ throw new ParseError('pdf/extra/ocgx/rbg/not-array',
95
+ '/RBGroups must be an array',
96
+ { context: { type: arr && arr.type } });
97
+ }
98
+ const out = [];
99
+ for (const grp of arr.items) {
100
+ if (!grp || grp.type !== 'array') {
101
+ throw new ParseError('pdf/extra/ocgx/rbg/bad-group',
102
+ '/RBGroups entry must be an array',
103
+ { context: { type: grp && grp.type } });
104
+ }
105
+ const refs = [];
106
+ for (const r of grp.items) {
107
+ if (!r || r.type !== 'ref') continue;
108
+ refs.push({ num: r.num, gen: r.gen });
109
+ }
110
+ out.push(refs);
111
+ }
112
+ return out;
113
+ }
114
+
115
+ function classifyOcgIntent(v) {
116
+ if (!v) return { view: false, design: false, names: [] };
117
+ const names = [];
118
+ if (v.type === 'name') names.push(v.value);
119
+ else if (v.type === 'array') {
120
+ for (const it of v.items) if (isType(it, 'name')) names.push(it.value);
121
+ } else {
122
+ throw new ParseError('pdf/extra/ocgx/intent/bad',
123
+ '/Intent must be name or array of names',
124
+ { context: { type: v.type } });
125
+ }
126
+ return {
127
+ view: names.includes('View'),
128
+ design: names.includes('Design'),
129
+ names
130
+ };
131
+ }
132
+
133
+ return {
134
+ evaluateVE,
135
+ typeOrderTree,
136
+ typeRBGroups,
137
+ classifyOcgIntent,
138
+ VE_OPERATORS
139
+ };
140
+ }
141
+ };
142
+
@@ -0,0 +1,112 @@
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: PDF/A validation helpers.
8
+ *
9
+ * Recognises an `/OutputIntent` whose `/S` is `GTS_PDFA1` (or later
10
+ * profile names introduced by ISO 19005-{2..4}), extracts the
11
+ * conformance level letter (a/b/u/e/f) and maps the
12
+ * `/OutputConditionIdentifier` to a known registry profile.
13
+ *
14
+ * @module pdf/extra/pdf-a-output-intent
15
+ */
16
+
17
+ /**
18
+ * Module factory.
19
+ */
20
+ import { pdfErrors } from '../errors.js';
21
+ import { pdfParser } from '../syntax/parser.js';
22
+
23
+ export const pdfAOutputIntent = {
24
+ name: 'pdfAOutputIntent',
25
+ dependencies: ['pdfErrors', 'pdfParser'],
26
+ deps: [pdfErrors, pdfParser],
27
+ factory(errors, parser) {
28
+ const { ParseError } = errors;
29
+ const { isType } = parser;
30
+
31
+ const PDFA_PROFILES = Object.freeze({
32
+ '1a': { part: 1, level: 'a', iso: 'ISO 19005-1:2005', notes: 'accessible' },
33
+ '1b': { part: 1, level: 'b', iso: 'ISO 19005-1:2005', notes: 'basic' },
34
+ '2a': { part: 2, level: 'a', iso: 'ISO 19005-2:2011', notes: 'accessible' },
35
+ '2b': { part: 2, level: 'b', iso: 'ISO 19005-2:2011', notes: 'basic' },
36
+ '2u': { part: 2, level: 'u', iso: 'ISO 19005-2:2011', notes: 'unicode' },
37
+ '3a': { part: 3, level: 'a', iso: 'ISO 19005-3:2012', notes: 'accessible + embed' },
38
+ '3b': { part: 3, level: 'b', iso: 'ISO 19005-3:2012', notes: 'basic + embed' },
39
+ '3u': { part: 3, level: 'u', iso: 'ISO 19005-3:2012', notes: 'unicode + embed' },
40
+ '4': { part: 4, level: null, iso: 'ISO 19005-4:2020', notes: 'PDF 2.0 base' },
41
+ '4e': { part: 4, level: 'e', iso: 'ISO 19005-4:2020', notes: 'engineering' },
42
+ '4f': { part: 4, level: 'f', iso: 'ISO 19005-4:2020', notes: 'embedded files allowed' }
43
+ });
44
+
45
+ const VALID_LEVELS = new Set(['1a', '1b', '2a', '2b', '2u',
46
+ '3a', '3b', '3u', '4', '4e', '4f']);
47
+
48
+ function detectPdfAProfile(dict) {
49
+ if (!isType(dict, 'dict')) {
50
+ throw new ParseError('pdf/extra/pdfa/not-dict',
51
+ 'OutputIntent must be a dictionary',
52
+ { context: { type: dict && dict.type } });
53
+ }
54
+ const e = dict.entries;
55
+ const s = isType(e.S, 'name') ? e.S.value : null;
56
+ const ident = isType(e.OutputConditionIdentifier, 'string')
57
+ ? new TextDecoder('latin1').decode(e.OutputConditionIdentifier.value)
58
+ : null;
59
+ const isPdfA = s === 'GTS_PDFA1';
60
+ const profile = profileFromIdentifier(ident);
61
+ return {
62
+ isPdfA,
63
+ subtype: s,
64
+ identifier: ident,
65
+ profile,
66
+ part: profile ? PDFA_PROFILES[profile].part : null,
67
+ level: profile ? PDFA_PROFILES[profile].level : null,
68
+ catalog: profile ? PDFA_PROFILES[profile] : null,
69
+ raw: dict
70
+ };
71
+ }
72
+
73
+ function profileFromIdentifier(s) {
74
+ if (!s) return null;
75
+ const m = s.match(/PDF\/A[-\s]?(1[ab]|2[abu]|3[abu]|4[ef]?)/i);
76
+ if (!m) return null;
77
+ const tag = m[1].toLowerCase();
78
+ return VALID_LEVELS.has(tag) ? tag : null;
79
+ }
80
+
81
+ function validatePdfABasics(typedDoc) {
82
+ if (!typedDoc || typeof typedDoc !== 'object') {
83
+ throw new ParseError('pdf/extra/pdfa/lint/bad-doc',
84
+ 'validatePdfABasics requires a typed document object',
85
+ { context: { type: typeof typedDoc } });
86
+ }
87
+ const errors = [];
88
+ const warnings = [];
89
+ const cat = typedDoc.catalog && typedDoc.catalog.entries;
90
+ if (typedDoc.encrypted) errors.push('PDF/A forbids encryption');
91
+ if (!cat) {
92
+ errors.push('catalog missing');
93
+ return { pass: false, errors, warnings };
94
+ }
95
+ if (!cat.Metadata) errors.push('catalog requires /Metadata XMP stream');
96
+ if (cat.MarkInfo && (!isType(cat.MarkInfo, 'dict'))) {
97
+ errors.push('/MarkInfo must be a dictionary');
98
+ }
99
+ if (!cat.OutputIntents) {
100
+ warnings.push('catalog has no /OutputIntents array');
101
+ }
102
+ return { pass: errors.length === 0, errors, warnings };
103
+ }
104
+
105
+ return {
106
+ detectPdfAProfile,
107
+ validatePdfABasics,
108
+ PDFA_PROFILES
109
+ };
110
+ }
111
+ };
112
+