@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,135 @@
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: typed Transparency Group attributes (§11.6.6),
8
+ * Soft Mask (§11.6.5.2 — Luminosity/Alpha) with `/TR` transfer, and the
9
+ * frozen catalog of standard blend modes (§11.3.5) — ISO 32000-2:2020.
10
+ *
11
+ * @module pdf/extra/transparency-typed
12
+ */
13
+
14
+ /**
15
+ * Module factory.
16
+ */
17
+ import { pdfErrors } from '../errors.js';
18
+ import { pdfParser } from '../syntax/parser.js';
19
+
20
+ export const pdfTransparencyTyped = {
21
+ name: 'pdfTransparencyTyped',
22
+ dependencies: ['pdfErrors', 'pdfParser'],
23
+ deps: [pdfErrors, pdfParser],
24
+ factory(errors, parser) {
25
+ const { ParseError } = errors;
26
+ const { isType } = parser;
27
+
28
+ /** 16 standard blend modes (§11.3.5 Table 136). */
29
+ const BLEND_MODES = Object.freeze([
30
+ 'Normal', 'Compatible', 'Multiply', 'Screen', 'Overlay',
31
+ 'Darken', 'Lighten', 'ColorDodge', 'ColorBurn', 'HardLight',
32
+ 'SoftLight', 'Difference', 'Exclusion',
33
+ 'Hue', 'Saturation', 'Color', 'Luminosity'
34
+ ]);
35
+
36
+ const NUM = (v) => v && (v.type === 'int' || v.type === 'real');
37
+
38
+ function typeTransparencyGroup(dict) {
39
+ if (!isType(dict, 'dict')) {
40
+ throw new ParseError('pdf/extra/transparency-group/not-dict',
41
+ 'Transparency Group must be a dict',
42
+ { context: { type: dict && dict.type } });
43
+ }
44
+ const e = dict.entries;
45
+ if (e.Type && (e.Type.type !== 'name' || e.Type.value !== 'Group')) {
46
+ throw new ParseError('pdf/extra/transparency-group/bad-type',
47
+ '/Type entry must be /Group',
48
+ { context: { actual: e.Type.value } });
49
+ }
50
+ if (!isType(e.S, 'name') || e.S.value !== 'Transparency') {
51
+ throw new ParseError('pdf/extra/transparency-group/bad-s',
52
+ '/S must be /Transparency',
53
+ { context: { actual: e.S && e.S.value } });
54
+ }
55
+ const known = new Set(['Type', 'S', 'CS', 'I', 'K']);
56
+ const out = {
57
+ s: 'Transparency',
58
+ cs: e.CS || null,
59
+ isolated: isType(e.I, 'bool') ? e.I.value : false,
60
+ knockout: isType(e.K, 'bool') ? e.K.value : false,
61
+ raw: dict,
62
+ _extras: {}
63
+ };
64
+ for (const k of Object.keys(e)) if (!known.has(k)) out._extras[k] = e[k];
65
+ return out;
66
+ }
67
+
68
+ function typeSoftMask(dict) {
69
+ if (!isType(dict, 'dict')) {
70
+ throw new ParseError('pdf/extra/soft-mask/not-dict',
71
+ 'Soft Mask must be a dict',
72
+ { context: { type: dict && dict.type } });
73
+ }
74
+ const e = dict.entries;
75
+ if (e.Type && (e.Type.type !== 'name' || e.Type.value !== 'Mask')) {
76
+ throw new ParseError('pdf/extra/soft-mask/bad-type',
77
+ '/Type entry must be /Mask',
78
+ { context: { actual: e.Type.value } });
79
+ }
80
+ if (!isType(e.S, 'name') || (e.S.value !== 'Alpha' && e.S.value !== 'Luminosity')) {
81
+ throw new ParseError('pdf/extra/soft-mask/bad-s',
82
+ '/S must be /Alpha or /Luminosity',
83
+ { context: { actual: e.S && e.S.value } });
84
+ }
85
+ if (!e.G) {
86
+ throw new ParseError('pdf/extra/soft-mask/missing-g',
87
+ 'Soft Mask requires /G (transparency group XObject)');
88
+ }
89
+ const known = new Set(['Type', 'S', 'G', 'BC', 'TR']);
90
+ const out = {
91
+ kind: e.S.value,
92
+ g: e.G,
93
+ backdrop: isType(e.BC, 'array') ? toNumArr(e.BC) : null,
94
+ transfer: e.TR || null,
95
+ raw: dict,
96
+ _extras: {}
97
+ };
98
+ for (const k of Object.keys(e)) if (!known.has(k)) out._extras[k] = e[k];
99
+ return out;
100
+ }
101
+
102
+ function resolveBlendMode(value) {
103
+ if (!value) return null;
104
+ if (isType(value, 'name')) {
105
+ return BLEND_MODES.includes(value.value) ? value.value : null;
106
+ }
107
+ if (isType(value, 'array')) {
108
+ for (const it of value.items) {
109
+ if (isType(it, 'name') && BLEND_MODES.includes(it.value)) return it.value;
110
+ }
111
+ return null;
112
+ }
113
+ throw new ParseError('pdf/extra/blend-mode/bad-type',
114
+ '/BM must be name or array of names',
115
+ { context: { type: value.type } });
116
+ }
117
+
118
+ function toNumArr(v) {
119
+ const out = [];
120
+ for (const it of v.items) {
121
+ if (!NUM(it)) return null;
122
+ out.push(it.value);
123
+ }
124
+ return out;
125
+ }
126
+
127
+ return {
128
+ typeTransparencyGroup,
129
+ typeSoftMask,
130
+ resolveBlendMode,
131
+ BLEND_MODES
132
+ };
133
+ }
134
+ };
135
+
@@ -0,0 +1,138 @@
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: WTPDF 1.0 (Well-Tagged PDF) linter.
8
+ *
9
+ * @module pdf/extra/well-tagged-pdf
10
+ */
11
+
12
+ /**
13
+ * Module factory.
14
+ */
15
+ import { pdfErrors } from '../errors.js';
16
+ import { pdfParser } from '../syntax/parser.js';
17
+
18
+ export const pdfWellTagged = {
19
+ name: 'pdfWellTagged',
20
+ dependencies: ['pdfErrors', 'pdfParser'],
21
+ deps: [pdfErrors, pdfParser],
22
+ factory(errors, _p) {
23
+ const { ParseError } = errors;
24
+
25
+ /** Frozen catalog of rule identifiers used in lint output. */
26
+ const WTPDF_RULES = Object.freeze({
27
+ 'wtpdf/artifact': 'decorative content must be tagged Artifact',
28
+ 'wtpdf/span/empty': 'Span used as pseudo-paragraph wrapper',
29
+ 'wtpdf/table/empty': 'Table must contain at least one TR',
30
+ 'wtpdf/table/row': 'TR must contain TH or TD children',
31
+ 'wtpdf/list/empty': 'L must contain at least one LI',
32
+ 'wtpdf/list/item': 'LI should contain Lbl + LBody',
33
+ 'wtpdf/heading/jump': 'heading level jump (e.g. H1 -> H3) is discouraged'
34
+ });
35
+
36
+ function lintWellTagged(tree) {
37
+ if (!tree || typeof tree !== 'object') {
38
+ throw new ParseError('pdf/extra/wtpdf/bad-tree',
39
+ 'lintWellTagged requires a structure tree object',
40
+ { context: { type: typeof tree } });
41
+ }
42
+ const errs = [];
43
+ const warnings = [];
44
+ let lastHeading = 0;
45
+ function walk(node) {
46
+ if (!node || typeof node !== 'object' || !node.s) return;
47
+ switch (node.s) {
48
+ case 'Table': checkTable(node, errs); break;
49
+ case 'L': checkList(node, errs, warnings); break;
50
+ case 'Span': if (!node.role) {
51
+ warnings.push({ rule: 'wtpdf/span/empty',
52
+ message: WTPDF_RULES['wtpdf/span/empty'] });
53
+ } break;
54
+ case 'Artifact': /* allowed */ break;
55
+ default: break;
56
+ }
57
+ const m = /^H([1-6])$/.exec(node.s);
58
+ if (m) {
59
+ const lvl = Number(m[1]);
60
+ if (lastHeading && lvl > lastHeading + 1) {
61
+ warnings.push({ rule: 'wtpdf/heading/jump',
62
+ message: WTPDF_RULES['wtpdf/heading/jump'],
63
+ from: lastHeading, to: lvl });
64
+ }
65
+ lastHeading = lvl;
66
+ }
67
+ if (Array.isArray(node.k)) {
68
+ for (const c of node.k) {
69
+ if (c && typeof c === 'object') walk(c);
70
+ }
71
+ }
72
+ }
73
+ walk(tree);
74
+ return { pass: errs.length === 0, errors: errs, warnings };
75
+ }
76
+
77
+ function checkTable(node, errs) {
78
+ const rows = (node.k || []).filter(c => c && c.s === 'TR');
79
+ if (rows.length === 0) {
80
+ errs.push({ rule: 'wtpdf/table/empty',
81
+ message: WTPDF_RULES['wtpdf/table/empty'] });
82
+ return;
83
+ }
84
+ for (const tr of rows) {
85
+ const cells = (tr.k || []).filter(c => c && (c.s === 'TH' || c.s === 'TD'));
86
+ if (cells.length === 0) {
87
+ errs.push({ rule: 'wtpdf/table/row',
88
+ message: WTPDF_RULES['wtpdf/table/row'] });
89
+ }
90
+ }
91
+ }
92
+
93
+ function checkList(node, errs, warnings) {
94
+ const items = (node.k || []).filter(c => c && c.s === 'LI');
95
+ if (items.length === 0) {
96
+ errs.push({ rule: 'wtpdf/list/empty',
97
+ message: WTPDF_RULES['wtpdf/list/empty'] });
98
+ return;
99
+ }
100
+ for (const li of items) {
101
+ const has = { Lbl: false, LBody: false };
102
+ for (const c of li.k || []) {
103
+ if (c && c.s === 'Lbl') has.Lbl = true;
104
+ if (c && c.s === 'LBody') has.LBody = true;
105
+ }
106
+ if (!has.Lbl || !has.LBody) {
107
+ warnings.push({ rule: 'wtpdf/list/item',
108
+ message: WTPDF_RULES['wtpdf/list/item'] });
109
+ }
110
+ }
111
+ }
112
+
113
+ function checkArtifactPlacement(regions) {
114
+ if (!Array.isArray(regions)) {
115
+ throw new ParseError('pdf/extra/wtpdf/regions/bad-input',
116
+ 'checkArtifactPlacement requires an array',
117
+ { context: { type: typeof regions } });
118
+ }
119
+ const errs = [];
120
+ for (const r of regions) {
121
+ if (!r || typeof r !== 'object') continue;
122
+ if (r.tagged === false && r.role !== 'Artifact') {
123
+ errs.push({ rule: 'wtpdf/artifact',
124
+ message: WTPDF_RULES['wtpdf/artifact'],
125
+ mcid: r.mcid });
126
+ }
127
+ }
128
+ return { pass: errs.length === 0, errors: errs, warnings: [] };
129
+ }
130
+
131
+ return {
132
+ lintWellTagged,
133
+ checkArtifactPlacement,
134
+ WTPDF_RULES
135
+ };
136
+ }
137
+ };
138
+
@@ -0,0 +1,190 @@
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: minimal XMP packet extractor.
8
+ *
9
+ * Tokenises an XMP RDF/XML packet for a fixed set of well-known
10
+ * namespaces and pulls out tag→value pairs. This is intentionally NOT
11
+ * an RDF parser; it walks the element stream and harvests qualified
12
+ * tag names whose prefix maps to one of the known namespaces.
13
+ *
14
+ * Supported namespaces (by URI):
15
+ * dc → http://purl.org/dc/elements/1.1/
16
+ * xmp → http://ns.adobe.com/xap/1.0/
17
+ * xmpMM → http://ns.adobe.com/xap/1.0/mm/
18
+ * pdf → http://ns.adobe.com/pdf/1.3/
19
+ * pdfaid → http://www.aiim.org/pdfa/ns/id/
20
+ * pdfuaid → http://www.aiim.org/pdfua/ns/id/
21
+ *
22
+ * @module pdf/extra/xmp-extended
23
+ */
24
+
25
+ import { pdfErrors } from '../errors.js';
26
+
27
+ export const pdfXmpExtended = {
28
+ name: 'pdfXmpExtended',
29
+ dependencies: ['pdfErrors'],
30
+ deps: [pdfErrors],
31
+
32
+ factory(errors) {
33
+ const { ParseError } = errors;
34
+ const KNOWN_NAMESPACES = {
35
+ 'http://purl.org/dc/elements/1.1/': 'dc',
36
+ 'http://ns.adobe.com/xap/1.0/': 'xmp',
37
+ 'http://ns.adobe.com/xap/1.0/mm/': 'xmpMM',
38
+ 'http://ns.adobe.com/pdf/1.3/': 'pdf',
39
+ 'http://www.aiim.org/pdfa/ns/id/': 'pdfaid',
40
+ 'http://www.aiim.org/pdfua/ns/id/': 'pdfuaid'
41
+ };
42
+
43
+ function toText(bytes) {
44
+ if (typeof bytes === 'string') return bytes;
45
+ if (bytes instanceof Uint8Array) return new TextDecoder('utf-8').decode(bytes);
46
+ throw new ParseError('pdf/xmp2/bad-input',
47
+ 'XMP input must be string or Uint8Array',
48
+ { context: { type: typeof bytes } });
49
+ }
50
+
51
+ // Parse xmlns:* attributes from a start-tag inner string.
52
+ function parseNamespaces(attrText) {
53
+ const map = {};
54
+ const rx = /xmlns:([\w-]+)\s*=\s*"([^"]*)"/g;
55
+ let m;
56
+ while ((m = rx.exec(attrText))) {
57
+ map[m[1]] = m[2];
58
+ }
59
+ return map;
60
+ }
61
+
62
+ // Parse attr=value pairs.
63
+ function parseAttrs(attrText) {
64
+ const out = {};
65
+ const rx = /([\w:-]+)\s*=\s*"([^"]*)"/g;
66
+ let m;
67
+ while ((m = rx.exec(attrText))) {
68
+ if (!m[1].startsWith('xmlns')) out[m[1]] = m[2];
69
+ }
70
+ return out;
71
+ }
72
+
73
+ function decodeEntities(s) {
74
+ return s.replace(/&lt;/g, '<')
75
+ .replace(/&gt;/g, '>')
76
+ .replace(/&quot;/g, '"')
77
+ .replace(/&apos;/g, "'")
78
+ .replace(/&amp;/g, '&');
79
+ }
80
+
81
+ // Tokenises tag/value pairs. Returns array of
82
+ // { prefix, ns, localName, value, attrs }.
83
+ function extract(input) {
84
+ const text = toText(input);
85
+ const len = text.length;
86
+ // Top-level ns map from rdf:RDF attributes.
87
+ const tagRx = /<\/?([\w:-]+)([^>]*?)\/?>/g;
88
+ const stack = [{ ns: {} }];
89
+ const pairs = [];
90
+
91
+ let i = 0;
92
+ while (i < len) {
93
+ tagRx.lastIndex = i;
94
+ const m = tagRx.exec(text);
95
+ if (!m) break;
96
+ const start = m.index;
97
+ const whole = m[0];
98
+ const qname = m[1];
99
+ const attrText = m[2] || '';
100
+ const isClose = whole.startsWith('</');
101
+ const isSelf = whole.endsWith('/>');
102
+
103
+ if (isClose) {
104
+ stack.pop();
105
+ i = start + whole.length;
106
+ continue;
107
+ }
108
+
109
+ // Open element. Inherit ns from parent, merge new ns
110
+ // declarations.
111
+ const parentNs = stack[stack.length - 1].ns;
112
+ const newNs = Object.assign({}, parentNs, parseNamespaces(attrText));
113
+ const attrs = parseAttrs(attrText);
114
+
115
+ let prefix = null, local = qname;
116
+ const colon = qname.indexOf(':');
117
+ if (colon >= 0) {
118
+ prefix = qname.slice(0, colon);
119
+ local = qname.slice(colon + 1);
120
+ }
121
+ const nsUri = prefix ? newNs[prefix] : null;
122
+ const knownPrefix = nsUri ? KNOWN_NAMESPACES[nsUri] : null;
123
+
124
+ let value = null;
125
+ if (!isSelf) {
126
+ // Find matching close tag (simple — no nesting of
127
+ // same-name tags supported beyond stack).
128
+ const closeTag = `</${qname}>`;
129
+ const closeAt = text.indexOf(closeTag, start + whole.length);
130
+ if (closeAt < 0) {
131
+ throw new ParseError('pdf/xmp2/unclosed-tag',
132
+ `unclosed <${qname}>`,
133
+ { context: { offset: start } });
134
+ }
135
+ const inner = text.slice(start + whole.length, closeAt);
136
+ // If inner has no child element, treat as leaf value.
137
+ if (!/<[\w]/.test(inner)) {
138
+ value = decodeEntities(inner.trim());
139
+ }
140
+ stack.push({ ns: newNs, qname });
141
+ if (value !== null) {
142
+ // Skip past close tag and pop, since we consumed.
143
+ stack.pop();
144
+ i = closeAt + closeTag.length;
145
+ } else {
146
+ i = start + whole.length;
147
+ }
148
+ } else {
149
+ i = start + whole.length;
150
+ }
151
+
152
+ if (knownPrefix && (value !== null || Object.keys(attrs).length > 0)) {
153
+ pairs.push({
154
+ prefix: knownPrefix,
155
+ ns: nsUri,
156
+ localName: local,
157
+ value,
158
+ attrs
159
+ });
160
+ }
161
+ }
162
+ return pairs;
163
+ }
164
+
165
+ // Group pairs into a { ns: { local: value | [values] } } map.
166
+ function group(pairs) {
167
+ const out = {};
168
+ for (const p of pairs) {
169
+ if (!out[p.prefix]) out[p.prefix] = {};
170
+ const bag = out[p.prefix];
171
+ if (bag[p.localName] === undefined) bag[p.localName] = p.value;
172
+ else if (Array.isArray(bag[p.localName])) bag[p.localName].push(p.value);
173
+ else bag[p.localName] = [bag[p.localName], p.value];
174
+ }
175
+ return out;
176
+ }
177
+
178
+ function parsePacket(input) {
179
+ const pairs = extract(input);
180
+ return { pairs, grouped: group(pairs) };
181
+ }
182
+
183
+ return {
184
+ parsePacket,
185
+ extract,
186
+ group,
187
+ KNOWN_NAMESPACES
188
+ };
189
+ }
190
+ };