@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,234 @@
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 PDF Portfolio (/Collection) typing.
8
+ *
9
+ * Augments the L1 collection typing with deeper inspection of the
10
+ * sub-dictionaries: /Schema field definitions, /Sort criteria,
11
+ * /Navigator extension, the initial /D entry, the /View mode
12
+ * ('D'|'T'|'H'|'C'), and the /CI (Collection Item) custom icon dict.
13
+ *
14
+ * @module pdf/extra/embedded-files-portfolio
15
+ */
16
+
17
+ import { pdfErrors } from '../errors.js';
18
+
19
+ export const pdfEmbeddedFilesPortfolio = {
20
+ name: 'pdfEmbeddedFilesPortfolio',
21
+ dependencies: ['pdfErrors'],
22
+ deps: [pdfErrors],
23
+
24
+ factory(errors) {
25
+ const { ParseError } = errors;
26
+ const VIEW_MODES = new Set(['D', 'T', 'H', 'C']);
27
+ const SCHEMA_SUBTYPES = new Set([
28
+ 'S', 'D', 'N', 'F', 'Desc', 'ModDate', 'CreationDate', 'Size'
29
+ ]);
30
+ const KNOWN_COLLECTION = new Set([
31
+ 'Type', 'Schema', 'D', 'View', 'Sort', 'Navigator',
32
+ 'Folders', 'Colors', 'Split'
33
+ ]);
34
+
35
+ function isDict(v) { return v && v.type === 'dict'; }
36
+ function isName(v) { return v && v.type === 'name'; }
37
+ function isStr(v) { return v && v.type === 'string'; }
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 typeSchemaField(name, fieldDict) {
43
+ if (!isDict(fieldDict)) {
44
+ throw new ParseError('pdf/portfolio/bad-schema-field',
45
+ '/Schema field must be a dictionary',
46
+ { context: { field: name, type: fieldDict && fieldDict.type } });
47
+ }
48
+ const e = fieldDict.entries;
49
+ const out = { name, raw: fieldDict, _extras: {} };
50
+ if (e.Subtype) {
51
+ if (!isName(e.Subtype) || !SCHEMA_SUBTYPES.has(e.Subtype.value)) {
52
+ throw new ParseError('pdf/portfolio/bad-schema-subtype',
53
+ '/Subtype on /Schema field must be one of S,D,N,F,Desc,ModDate,CreationDate,Size',
54
+ { context: { field: name, actual: e.Subtype.value } });
55
+ }
56
+ out.subtype = e.Subtype.value;
57
+ }
58
+ if (e.N) {
59
+ if (!isStr(e.N)) {
60
+ throw new ParseError('pdf/portfolio/bad-schema-n',
61
+ '/N must be a string', { context: { field: name } });
62
+ }
63
+ out.displayName = e.N.value;
64
+ }
65
+ if (e.O != null) {
66
+ if (!isInt(e.O)) {
67
+ throw new ParseError('pdf/portfolio/bad-schema-o',
68
+ '/O must be an integer', { context: { field: name } });
69
+ }
70
+ out.order = e.O.value;
71
+ }
72
+ if (e.V != null) {
73
+ if (!isBool(e.V)) {
74
+ throw new ParseError('pdf/portfolio/bad-schema-v',
75
+ '/V must be boolean', { context: { field: name } });
76
+ }
77
+ out.visible = e.V.value;
78
+ }
79
+ if (e.E != null) {
80
+ if (!isBool(e.E)) {
81
+ throw new ParseError('pdf/portfolio/bad-schema-e',
82
+ '/E must be boolean', { context: { field: name } });
83
+ }
84
+ out.editable = e.E.value;
85
+ }
86
+ const KNOWN = new Set(['Subtype', 'N', 'O', 'V', 'E']);
87
+ for (const k of Object.keys(e)) {
88
+ if (!KNOWN.has(k)) out._extras[k] = e[k];
89
+ }
90
+ return out;
91
+ }
92
+
93
+ function typeSchema(schemaDict) {
94
+ if (!isDict(schemaDict)) {
95
+ throw new ParseError('pdf/portfolio/bad-schema',
96
+ '/Schema must be a dictionary',
97
+ { context: { type: schemaDict && schemaDict.type } });
98
+ }
99
+ const fields = {};
100
+ for (const [k, v] of Object.entries(schemaDict.entries)) {
101
+ fields[k] = typeSchemaField(k, v);
102
+ }
103
+ return { fields, raw: schemaDict };
104
+ }
105
+
106
+ function typeSort(sortDict) {
107
+ if (!isDict(sortDict)) {
108
+ throw new ParseError('pdf/portfolio/bad-sort',
109
+ '/Sort must be a dictionary');
110
+ }
111
+ const e = sortDict.entries;
112
+ const out = { raw: sortDict, _extras: {} };
113
+ if (e.S) {
114
+ if (isName(e.S)) out.keys = [e.S.value];
115
+ else if (isArr(e.S)) {
116
+ const ks = [];
117
+ for (const it of e.S.items) {
118
+ if (!isName(it)) {
119
+ throw new ParseError('pdf/portfolio/bad-sort-s-item',
120
+ '/Sort /S array items must be names');
121
+ }
122
+ ks.push(it.value);
123
+ }
124
+ out.keys = ks;
125
+ } else {
126
+ throw new ParseError('pdf/portfolio/bad-sort-s',
127
+ '/Sort /S must be a name or array of names');
128
+ }
129
+ }
130
+ if (e.A != null) {
131
+ if (isBool(e.A)) out.ascending = [e.A.value];
132
+ else if (isArr(e.A)) {
133
+ const as = [];
134
+ for (const it of e.A.items) {
135
+ if (!isBool(it)) {
136
+ throw new ParseError('pdf/portfolio/bad-sort-a-item',
137
+ '/Sort /A array items must be booleans');
138
+ }
139
+ as.push(it.value);
140
+ }
141
+ out.ascending = as;
142
+ } else {
143
+ throw new ParseError('pdf/portfolio/bad-sort-a',
144
+ '/Sort /A must be a bool or array of bools');
145
+ }
146
+ }
147
+ for (const k of Object.keys(e)) {
148
+ if (k !== 'S' && k !== 'A') out._extras[k] = e[k];
149
+ }
150
+ return out;
151
+ }
152
+
153
+ function typeNavigator(nav) {
154
+ if (nav && nav.type === 'ref') return { ref: { num: nav.num, gen: nav.gen }, raw: nav };
155
+ if (!isDict(nav)) {
156
+ throw new ParseError('pdf/portfolio/bad-navigator',
157
+ '/Navigator must be a dict or ref');
158
+ }
159
+ return { raw: nav, entries: nav.entries };
160
+ }
161
+
162
+ function typeCustomIcon(ci) {
163
+ if (!isDict(ci) && !(ci && ci.type === 'stream')) {
164
+ throw new ParseError('pdf/portfolio/bad-ci',
165
+ '/CI custom icon must be dict or stream',
166
+ { context: { type: ci && ci.type } });
167
+ }
168
+ return { raw: ci };
169
+ }
170
+
171
+ function typePortfolio(collectionDict) {
172
+ if (!isDict(collectionDict)) {
173
+ throw new ParseError('pdf/portfolio/not-dict',
174
+ 'Collection must be a dictionary',
175
+ { context: { type: collectionDict && collectionDict.type } });
176
+ }
177
+ const e = collectionDict.entries;
178
+ if (e.Type && (!isName(e.Type) || e.Type.value !== 'Collection')) {
179
+ throw new ParseError('pdf/portfolio/bad-type',
180
+ '/Type must be /Collection',
181
+ { context: { actual: e.Type && e.Type.value } });
182
+ }
183
+ const out = { raw: collectionDict, _extras: {} };
184
+ if (e.Schema) out.schema = typeSchema(e.Schema);
185
+ if (e.D) {
186
+ if (!isStr(e.D)) {
187
+ throw new ParseError('pdf/portfolio/bad-d',
188
+ '/D must be a string');
189
+ }
190
+ out.initialDoc = e.D.value;
191
+ }
192
+ if (e.View) {
193
+ if (!isName(e.View) || !VIEW_MODES.has(e.View.value)) {
194
+ throw new ParseError('pdf/portfolio/bad-view',
195
+ '/View must be one of D, T, H, C',
196
+ { context: { actual: e.View && e.View.value } });
197
+ }
198
+ out.view = e.View.value;
199
+ }
200
+ if (e.Sort) out.sort = typeSort(e.Sort);
201
+ if (e.Navigator) out.navigator = typeNavigator(e.Navigator);
202
+ for (const k of Object.keys(e)) {
203
+ if (!KNOWN_COLLECTION.has(k)) out._extras[k] = e[k];
204
+ }
205
+ return out;
206
+ }
207
+
208
+ function typeCollectionItem(ciDict) {
209
+ if (!isDict(ciDict)) {
210
+ throw new ParseError('pdf/portfolio/ci-not-dict',
211
+ '/CI item must be a dict');
212
+ }
213
+ const out = { raw: ciDict, fields: {}, _extras: {} };
214
+ for (const [k, v] of Object.entries(ciDict.entries)) {
215
+ if (k === 'Type') continue;
216
+ if (k === 'CI') out.customIcon = typeCustomIcon(v);
217
+ else out.fields[k] = v;
218
+ }
219
+ return out;
220
+ }
221
+
222
+ return {
223
+ typePortfolio,
224
+ typeSchema,
225
+ typeSchemaField,
226
+ typeSort,
227
+ typeNavigator,
228
+ typeCustomIcon,
229
+ typeCollectionItem,
230
+ VIEW_MODES,
231
+ SCHEMA_SUBTYPES
232
+ };
233
+ }
234
+ };
@@ -0,0 +1,185 @@
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 CIDFont per ISO 32000-2:2020 §9.7.4
8
+ * (CIDFontType0/CIDFontType2), `/CIDSystemInfo` §9.7.3, `/CIDToGIDMap`
9
+ * §9.7.4.3, glyph-metrics arrays `/W /W2 /DW /DW2` §9.7.4.3, and the
10
+ * predefined CMap catalog §9.7.5.2.
11
+ *
12
+ * @module pdf/extra/font-cid-typed
13
+ */
14
+
15
+ /**
16
+ * Module factory.
17
+ */
18
+ import { pdfErrors } from '../errors.js';
19
+ import { pdfParserObj } from '../syntax/parser-obj.js';
20
+
21
+ export const pdfFontCidTyped = {
22
+ name: 'pdfFontCidTyped',
23
+ dependencies: ['pdfErrors', 'pdfParserObj'],
24
+ deps: [pdfErrors, pdfParserObj],
25
+ factory(errors, parserObj) {
26
+ const { ParseError } = errors;
27
+ const { isType } = parserObj;
28
+
29
+ const SUBTYPES = new Set(['CIDFontType0', 'CIDFontType2']);
30
+
31
+ const PREDEFINED_CMAPS = Object.freeze({
32
+ 'Adobe-GB1': Object.freeze([
33
+ 'GB-EUC-H', 'GB-EUC-V', 'GBpc-EUC-H', 'GBpc-EUC-V',
34
+ 'GBK-EUC-H', 'GBK-EUC-V', 'GBKp-EUC-H', 'GBKp-EUC-V',
35
+ 'GBK2K-H', 'GBK2K-V', 'UniGB-UCS2-H', 'UniGB-UCS2-V',
36
+ 'UniGB-UTF16-H', 'UniGB-UTF16-V'
37
+ ]),
38
+ 'Adobe-CNS1': Object.freeze([
39
+ 'B5pc-H', 'B5pc-V', 'HKscs-B5-H', 'HKscs-B5-V',
40
+ 'ETen-B5-H', 'ETen-B5-V', 'ETenms-B5-H', 'ETenms-B5-V',
41
+ 'CNS-EUC-H', 'CNS-EUC-V', 'UniCNS-UCS2-H', 'UniCNS-UCS2-V',
42
+ 'UniCNS-UTF16-H', 'UniCNS-UTF16-V'
43
+ ]),
44
+ 'Adobe-Japan1': Object.freeze([
45
+ '83pv-RKSJ-H', '90ms-RKSJ-H', '90ms-RKSJ-V', '90msp-RKSJ-H',
46
+ '90msp-RKSJ-V', '90pv-RKSJ-H', 'Add-RKSJ-H', 'Add-RKSJ-V',
47
+ 'EUC-H', 'EUC-V', 'Ext-RKSJ-H', 'Ext-RKSJ-V', 'H', 'V',
48
+ 'UniJIS-UCS2-H', 'UniJIS-UCS2-V', 'UniJIS-UCS2-HW-H',
49
+ 'UniJIS-UCS2-HW-V', 'UniJIS-UTF16-H', 'UniJIS-UTF16-V'
50
+ ]),
51
+ 'Adobe-Korea1': Object.freeze([
52
+ 'KSC-EUC-H', 'KSC-EUC-V', 'KSCms-UHC-H', 'KSCms-UHC-V',
53
+ 'KSCms-UHC-HW-H', 'KSCms-UHC-HW-V', 'KSCpc-EUC-H',
54
+ 'UniKS-UCS2-H', 'UniKS-UCS2-V', 'UniKS-UTF16-H', 'UniKS-UTF16-V'
55
+ ]),
56
+ 'Identity': Object.freeze(['Identity-H', 'Identity-V'])
57
+ });
58
+
59
+ const KNOWN = new Set([
60
+ 'Type', 'Subtype', 'BaseFont', 'CIDSystemInfo', 'FontDescriptor',
61
+ 'DW', 'W', 'DW2', 'W2', 'CIDToGIDMap'
62
+ ]);
63
+
64
+ function typeCIDFont(dict) {
65
+ if (!isType(dict, 'dict')) {
66
+ throw new ParseError('pdf/extra/cid-font/not-dict',
67
+ 'CIDFont must be a dictionary',
68
+ { context: { type: dict && dict.type } });
69
+ }
70
+ const e = dict.entries;
71
+ if (e.Type && (e.Type.type !== 'name' || e.Type.value !== 'Font')) {
72
+ throw new ParseError('pdf/extra/cid-font/bad-type',
73
+ '/Type entry must be /Font',
74
+ { context: { actual: e.Type.value } });
75
+ }
76
+ if (!isType(e.Subtype, 'name') || !SUBTYPES.has(e.Subtype.value)) {
77
+ throw new ParseError('pdf/extra/cid-font/bad-subtype',
78
+ '/Subtype must be CIDFontType0 or CIDFontType2',
79
+ { context: { actual: e.Subtype && e.Subtype.value } });
80
+ }
81
+ if (!isType(e.CIDSystemInfo, 'dict') && !(e.CIDSystemInfo && e.CIDSystemInfo.type === 'ref')) {
82
+ throw new ParseError('pdf/extra/cid-font/missing-csi',
83
+ '/CIDSystemInfo is required (dict or ref)',
84
+ { context: { type: e.CIDSystemInfo && e.CIDSystemInfo.type } });
85
+ }
86
+ const out = {
87
+ subtype: e.Subtype.value,
88
+ baseFont: isType(e.BaseFont, 'name') ? e.BaseFont.value : null,
89
+ cidSystemInfo: isType(e.CIDSystemInfo, 'dict') ? typeCIDSystemInfo(e.CIDSystemInfo) : e.CIDSystemInfo,
90
+ fontDescriptor: e.FontDescriptor || null,
91
+ dw: numVal(e.DW, 1000),
92
+ w: isType(e.W, 'array') ? decodeWidthsW(e.W) : null,
93
+ dw2: isType(e.DW2, 'array') ? toNumArray(e.DW2) : [880, -1000],
94
+ w2: isType(e.W2, 'array') ? e.W2.items.map(it => it) : null,
95
+ cidToGIDMap: e.CIDToGIDMap || null,
96
+ raw: dict,
97
+ _extras: {}
98
+ };
99
+ for (const k of Object.keys(e)) {
100
+ if (!KNOWN.has(k)) out._extras[k] = e[k];
101
+ }
102
+ return out;
103
+ }
104
+
105
+ function typeCIDSystemInfo(dict) {
106
+ if (!isType(dict, 'dict')) {
107
+ throw new ParseError('pdf/extra/cid-system-info/not-dict',
108
+ 'CIDSystemInfo must be a dictionary',
109
+ { context: { type: dict && dict.type } });
110
+ }
111
+ const e = dict.entries;
112
+ if (!isType(e.Registry, 'string') || !isType(e.Ordering, 'string')) {
113
+ throw new ParseError('pdf/extra/cid-system-info/incomplete',
114
+ '/Registry and /Ordering are required strings');
115
+ }
116
+ return {
117
+ registry: e.Registry.value,
118
+ ordering: e.Ordering.value,
119
+ supplement: numVal(e.Supplement, 0)
120
+ };
121
+ }
122
+
123
+ function decodeWidthsW(arr) {
124
+ if (!isType(arr, 'array')) {
125
+ throw new ParseError('pdf/extra/cid-font/bad-w',
126
+ '/W must be an array',
127
+ { context: { type: arr && arr.type } });
128
+ }
129
+ const items = arr.items;
130
+ const out = [];
131
+ let i = 0;
132
+ while (i < items.length) {
133
+ const c = items[i];
134
+ if (!c || (c.type !== 'int' && c.type !== 'real')) {
135
+ throw new ParseError('pdf/extra/cid-font/bad-w-c',
136
+ '/W expects numeric CID');
137
+ }
138
+ const first = c.value | 0;
139
+ const nxt = items[i + 1];
140
+ if (nxt && nxt.type === 'array') {
141
+ const widths = [];
142
+ for (const it of nxt.items) {
143
+ if (!it || (it.type !== 'int' && it.type !== 'real')) {
144
+ throw new ParseError('pdf/extra/cid-font/bad-w-item',
145
+ '/W array sub-items must be numeric');
146
+ }
147
+ widths.push(it.value);
148
+ }
149
+ out.push({ first, last: first + widths.length - 1, widths });
150
+ i += 2;
151
+ } else {
152
+ const last = nxt;
153
+ const w = items[i + 2];
154
+ if (!last || (last.type !== 'int' && last.type !== 'real') ||
155
+ !w || (w.type !== 'int' && w.type !== 'real')) {
156
+ throw new ParseError('pdf/extra/cid-font/bad-w-range',
157
+ '/W range form requires c_first c_last w');
158
+ }
159
+ out.push({ first, last: last.value | 0, width: w.value });
160
+ i += 3;
161
+ }
162
+ }
163
+ return out;
164
+ }
165
+
166
+ function numVal(v, dflt) {
167
+ if (!v) return dflt;
168
+ if (v.type !== 'int' && v.type !== 'real') return dflt;
169
+ return v.value;
170
+ }
171
+
172
+ function toNumArray(v) {
173
+ if (!isType(v, 'array')) return null;
174
+ const out = [];
175
+ for (const it of v.items) {
176
+ if (!it || (it.type !== 'int' && it.type !== 'real')) return null;
177
+ out.push(it.value);
178
+ }
179
+ return out;
180
+ }
181
+
182
+ return { typeCIDFont, typeCIDSystemInfo, decodeWidthsW, PREDEFINED_CMAPS };
183
+ }
184
+ };
185
+
@@ -0,0 +1,144 @@
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: color-font detection per ISO 32000-2:2020
8
+ * §9.8.2 (FontDescriptor `/Flags`) plus the OpenType color tables
9
+ * COLR/sbix/SVG via /FontFile3 streams with `/Subtype /OpenType`.
10
+ *
11
+ * Color-font detection is heuristic — the PDF FontDescriptor does NOT
12
+ * carry a single "is-color-font" flag. We expose:
13
+ * - `decodeFontDescriptorFlags(int)` → typed flag record
14
+ * - `detectColorFontTables(stream)` → which color-OT tables are
15
+ * present in an embedded OpenType font program by sfnt-table scan
16
+ * - `typeFontFile3OpenType(stream)` → typed wrapper around a
17
+ * `/FontFile3` stream whose `/Subtype` is `/OpenType`
18
+ *
19
+ * @module pdf/extra/font-color-tagging
20
+ */
21
+
22
+ /**
23
+ * Module factory.
24
+ */
25
+ import { pdfErrors } from '../errors.js';
26
+ import { pdfParserObj } from '../syntax/parser-obj.js';
27
+
28
+ export const pdfFontColorTagging = {
29
+ name: 'pdfFontColorTagging',
30
+ dependencies: ['pdfErrors', 'pdfParserObj'],
31
+ deps: [pdfErrors, pdfParserObj],
32
+ factory(errors, parserObj) {
33
+ const { ParseError } = errors;
34
+ const { isType } = parserObj;
35
+
36
+ const FONT_DESCRIPTOR_FLAGS = Object.freeze({
37
+ FixedPitch: 1 << 0,
38
+ Serif: 1 << 1,
39
+ Symbolic: 1 << 2,
40
+ Script: 1 << 3,
41
+ Nonsymbolic: 1 << 5,
42
+ Italic: 1 << 6,
43
+ AllCap: 1 << 16,
44
+ SmallCap: 1 << 17,
45
+ ForceBold: 1 << 18
46
+ });
47
+
48
+ const COLOR_OT_TABLES = Object.freeze(['COLR', 'CPAL', 'sbix', 'SVG ', 'CBDT', 'CBLC']);
49
+
50
+ function decodeFontDescriptorFlags(flags) {
51
+ if (typeof flags !== 'number' || !Number.isFinite(flags)) {
52
+ throw new ParseError('pdf/extra/font-flags/bad-input',
53
+ 'flags must be a finite number',
54
+ { context: { flags } });
55
+ }
56
+ const f = flags | 0;
57
+ const out = { raw: f };
58
+ for (const [k, mask] of Object.entries(FONT_DESCRIPTOR_FLAGS)) {
59
+ out[k.charAt(0).toLowerCase() + k.slice(1)] = (f & mask) !== 0;
60
+ }
61
+ return out;
62
+ }
63
+
64
+ function detectColorFontTables(bytes) {
65
+ if (!(bytes instanceof Uint8Array)) {
66
+ throw new ParseError('pdf/extra/color-font/bad-input',
67
+ 'expected Uint8Array', { context: { kind: typeof bytes } });
68
+ }
69
+ if (bytes.length < 12) {
70
+ throw new ParseError('pdf/extra/color-font/truncated',
71
+ 'sfnt header too short', { context: { length: bytes.length } });
72
+ }
73
+ const numTables = (bytes[4] << 8) | bytes[5];
74
+ const need = 12 + numTables * 16;
75
+ if (bytes.length < need) {
76
+ throw new ParseError('pdf/extra/color-font/truncated',
77
+ 'sfnt table directory truncated',
78
+ { context: { numTables, length: bytes.length } });
79
+ }
80
+ const tables = [];
81
+ for (let i = 0; i < numTables; i++) {
82
+ const off = 12 + i * 16;
83
+ const tag = String.fromCharCode(bytes[off], bytes[off + 1], bytes[off + 2], bytes[off + 3]);
84
+ tables.push(tag);
85
+ }
86
+ return {
87
+ hasCOLR: tables.includes('COLR'),
88
+ hasSbix: tables.includes('sbix'),
89
+ hasSVG: tables.includes('SVG '),
90
+ hasCBDT: tables.includes('CBDT'),
91
+ tables
92
+ };
93
+ }
94
+
95
+ function typeFontFile3OpenType(stream) {
96
+ if (!isType(stream, 'stream')) {
97
+ throw new ParseError('pdf/extra/fontfile3/not-stream',
98
+ '/FontFile3 must be a stream',
99
+ { context: { type: stream && stream.type } });
100
+ }
101
+ const dict = stream.dict;
102
+ if (!isType(dict, 'dict')) {
103
+ throw new ParseError('pdf/extra/fontfile3/no-dict',
104
+ '/FontFile3 stream missing dict');
105
+ }
106
+ const e = dict.entries;
107
+ if (!isType(e.Subtype, 'name') || e.Subtype.value !== 'OpenType') {
108
+ throw new ParseError('pdf/extra/fontfile3/bad-subtype',
109
+ '/FontFile3 /Subtype must be /OpenType',
110
+ { context: { actual: e.Subtype && e.Subtype.value } });
111
+ }
112
+ let color = null;
113
+ if (stream.raw instanceof Uint8Array && stream.raw.length >= 12) {
114
+ try { color = detectColorFontTables(stream.raw); }
115
+ catch (_e) { color = null; }
116
+ }
117
+ return {
118
+ subtype: 'OpenType',
119
+ length: stream.raw instanceof Uint8Array ? stream.raw.length : null,
120
+ metadata: e.Metadata || null,
121
+ color,
122
+ raw: stream,
123
+ _extras: collectExtras(e, new Set(['Subtype', 'Length', 'Filter', 'DecodeParms', 'Metadata']))
124
+ };
125
+ }
126
+
127
+ function collectExtras(entries, known) {
128
+ const out = {};
129
+ for (const k of Object.keys(entries)) {
130
+ if (!known.has(k)) out[k] = entries[k];
131
+ }
132
+ return out;
133
+ }
134
+
135
+ return {
136
+ decodeFontDescriptorFlags,
137
+ detectColorFontTables,
138
+ typeFontFile3OpenType,
139
+ FONT_DESCRIPTOR_FLAGS,
140
+ COLOR_OT_TABLES
141
+ };
142
+ }
143
+ };
144
+