@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,75 @@
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 Type 3 user-defined font typing per ISO 32000-2:2020 §9.6.4.
8
+ *
9
+ * Unlike Type1/TrueType/CIDFont (which delegate to `@awacloud/fonts`),
10
+ * Type 3 fonts are **PDF-specific** — each glyph is defined by a
11
+ * content stream in the `/CharProcs` dict, not a portable font format.
12
+ *
13
+ * @module pdf/font/type3
14
+ */
15
+
16
+ /**
17
+ * Module factory — worker-safe, self-contained.
18
+ */
19
+ import { pdfErrors } from '../errors.js';
20
+ import { pdfParser } from '../syntax/parser.js';
21
+
22
+ export const pdfType3 = {
23
+ name: 'pdfType3',
24
+ dependencies: ['pdfErrors', 'pdfParser'],
25
+ deps: [pdfErrors, pdfParser],
26
+ factory(errors, parserMod) {
27
+ const { ParseError } = errors;
28
+ const isType = (parserMod && parserMod.isType)
29
+ || ((v, kind) => !!(v && v.type === kind));
30
+
31
+ function toArray(v, n) {
32
+ if (!v || v.type !== 'array' || v.items.length !== n) return null;
33
+ const r = new Array(n);
34
+ for (let i = 0; i < n; i++) {
35
+ const it = v.items[i];
36
+ if (!it || (it.type !== 'int' && it.type !== 'real')) return null;
37
+ r[i] = it.value;
38
+ }
39
+ return r;
40
+ }
41
+
42
+ function typeType3(dict) {
43
+ if (!isType(dict, 'dict')) {
44
+ throw new ParseError('pdf/type3/not-dict',
45
+ 'Type 3 font must be a dictionary',
46
+ { context: { type: dict && dict.type } });
47
+ }
48
+ const e = dict.entries;
49
+ if (!e.Subtype || e.Subtype.type !== 'name' || e.Subtype.value !== 'Type3') {
50
+ throw new ParseError('pdf/type3/wrong-subtype',
51
+ '/Subtype must be /Type3',
52
+ { context: { actual: e.Subtype && e.Subtype.value } });
53
+ }
54
+ return {
55
+ subtype: 'Type3',
56
+ bbox: toArray(e.FontBBox, 4),
57
+ matrix: toArray(e.FontMatrix, 6) || [0.001, 0, 0, 0.001, 0, 0],
58
+ charProcs: e.CharProcs && e.CharProcs.type === 'dict'
59
+ ? { ...e.CharProcs.entries } : {},
60
+ encoding: e.Encoding || null,
61
+ firstChar: e.FirstChar && e.FirstChar.type === 'int' ? e.FirstChar.value : null,
62
+ lastChar: e.LastChar && e.LastChar.type === 'int' ? e.LastChar.value : null,
63
+ widths: e.Widths && e.Widths.type === 'array'
64
+ ? e.Widths.items.filter(it => it.type === 'int' || it.type === 'real')
65
+ .map(it => it.value)
66
+ : null,
67
+ fontDescriptor: e.FontDescriptor || null,
68
+ resources: e.Resources || null,
69
+ raw: dict
70
+ };
71
+ }
72
+
73
+ return { typeType3 };
74
+ }
75
+ };
@@ -0,0 +1,94 @@
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 AcroForm dict typing per ISO 32000-2:2020 §12.7.3.
8
+ *
9
+ * @module pdf/form/acroform
10
+ */
11
+
12
+ /**
13
+ * Module factory — worker-safe, self-contained.
14
+ */
15
+ import { pdfErrors } from '../errors.js';
16
+ import { pdfParser } from '../syntax/parser.js';
17
+
18
+ export const pdfAcroForm = {
19
+ name: 'pdfAcroForm',
20
+ dependencies: ['pdfErrors', 'pdfParser'],
21
+ deps: [pdfErrors, pdfParser],
22
+ factory(errors, parserMod) {
23
+ const { ParseError } = errors;
24
+ const isType = (parserMod && parserMod.isType)
25
+ || ((v, kind) => !!(v && v.type === kind));
26
+
27
+ const KNOWN = new Set([
28
+ 'Fields', 'NeedAppearances', 'SigFlags', 'CO', 'DR', 'DA', 'Q', 'XFA'
29
+ ]);
30
+
31
+ function toBool(v, dflt) {
32
+ if (!v) return dflt;
33
+ if (v.type !== 'bool') return dflt;
34
+ return !!v.value;
35
+ }
36
+
37
+ function toInt(v, dflt) {
38
+ if (!v) return dflt;
39
+ if (v.type !== 'int' && v.type !== 'real') return dflt;
40
+ return v.value | 0;
41
+ }
42
+
43
+ function toRefArray(v, errCode) {
44
+ if (!v) return [];
45
+ if (v.type !== 'array') {
46
+ throw new ParseError(errCode,
47
+ 'expected an array of indirect references',
48
+ { context: { kind: v.type } });
49
+ }
50
+ const out = [];
51
+ for (const it of v.items) {
52
+ if (!it || it.type !== 'ref') {
53
+ throw new ParseError(errCode,
54
+ 'array entry must be an indirect reference',
55
+ { context: { kind: it && it.type } });
56
+ }
57
+ out.push({ num: it.num, gen: it.gen });
58
+ }
59
+ return out;
60
+ }
61
+
62
+ function typeAcroForm(dict) {
63
+ if (!isType(dict, 'dict')) {
64
+ throw new ParseError('pdf/form/acroform/not-dict',
65
+ 'AcroForm must be a dictionary',
66
+ { context: { type: dict && dict.type } });
67
+ }
68
+ const e = dict.entries;
69
+
70
+ const fields = toRefArray(e.Fields, 'pdf/form/acroform/bad-fields');
71
+
72
+ const out = {
73
+ fields,
74
+ needAppearances: toBool(e.NeedAppearances, false),
75
+ sigFlags: toInt(e.SigFlags, 0),
76
+ co: e.CO ? toRefArray(e.CO, 'pdf/form/acroform/bad-co') : [],
77
+ dr: isType(e.DR, 'dict') ? e.DR : null,
78
+ da: isType(e.DA, 'string') ? e.DA.value : null,
79
+ q: toInt(e.Q, 0),
80
+ raw: dict,
81
+ _extras: {}
82
+ };
83
+
84
+ for (const k of Object.keys(e)) {
85
+ if (!KNOWN.has(k)) out._extras[k] = e[k];
86
+ }
87
+ if (e.XFA) out._extras.XFA = e.XFA;
88
+
89
+ return out;
90
+ }
91
+
92
+ return { typeAcroForm };
93
+ }
94
+ };
@@ -0,0 +1,90 @@
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 Appearance-stream dict typing per ISO 32000-2:2020 §12.5.5.
8
+ *
9
+ * @module pdf/form/appearance
10
+ */
11
+
12
+ /**
13
+ * Module factory — worker-safe, self-contained.
14
+ */
15
+ import { pdfErrors } from '../errors.js';
16
+ import { pdfParser } from '../syntax/parser.js';
17
+
18
+ export const pdfAppearance = {
19
+ name: 'pdfAppearance',
20
+ dependencies: ['pdfErrors', 'pdfParser'],
21
+ deps: [pdfErrors, pdfParser],
22
+ factory(errors, parserMod) {
23
+ const { ParseError } = errors;
24
+ const isType = (parserMod && parserMod.isType)
25
+ || ((v, kind) => !!(v && v.type === kind));
26
+
27
+ const SLOTS = ['N', 'R', 'D'];
28
+
29
+ function normalise(v, slot) {
30
+ if (!v) {
31
+ return { default: null, states: {} };
32
+ }
33
+ if (v.type === 'ref' || v.type === 'stream') {
34
+ return { default: v, states: {} };
35
+ }
36
+ if (v.type === 'dict') {
37
+ const states = {};
38
+ for (const k of Object.keys(v.entries)) {
39
+ const sub = v.entries[k];
40
+ if (!sub || (sub.type !== 'ref' && sub.type !== 'stream')) {
41
+ throw new ParseError('pdf/form/ap/bad-state',
42
+ 'appearance-state entry must be a stream or indirect ref',
43
+ { context: { slot, state: k, kind: sub && sub.type } });
44
+ }
45
+ states[k] = sub;
46
+ }
47
+ return { default: null, states };
48
+ }
49
+ throw new ParseError('pdf/form/ap/bad-slot',
50
+ '/AP slot must be a stream, indirect ref, or sub-dict',
51
+ { context: { slot, kind: v.type } });
52
+ }
53
+
54
+ function typeAppearanceStreams(dict) {
55
+ if (!isType(dict, 'dict')) {
56
+ throw new ParseError('pdf/form/ap/not-dict',
57
+ '/AP must be a dictionary',
58
+ { context: { type: dict && dict.type } });
59
+ }
60
+ const e = dict.entries;
61
+
62
+ const out = {
63
+ N: normalise(e.N, 'N'),
64
+ R: e.R ? normalise(e.R, 'R') : null,
65
+ D: e.D ? normalise(e.D, 'D') : null,
66
+ raw: dict,
67
+ _extras: {}
68
+ };
69
+
70
+ if (!e.N) {
71
+ throw new ParseError('pdf/form/ap/missing-n',
72
+ '/AP is missing required /N entry',
73
+ { context: {} });
74
+ }
75
+
76
+ for (const k of Object.keys(e)) {
77
+ if (k !== 'N' && k !== 'R' && k !== 'D') out._extras[k] = e[k];
78
+ }
79
+ return out;
80
+ }
81
+
82
+ function listPopulatedSlots(ap) {
83
+ const out = [];
84
+ for (const s of SLOTS) if (ap[s]) out.push(s);
85
+ return out;
86
+ }
87
+
88
+ return { typeAppearanceStreams, listPopulatedSlots };
89
+ }
90
+ };
@@ -0,0 +1,105 @@
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 Button field typing per ISO 32000-2:2020 §12.7.5.2.
8
+ *
9
+ * @module pdf/form/button
10
+ */
11
+
12
+ /**
13
+ * Module factory — worker-safe, self-contained.
14
+ */
15
+ import { pdfErrors } from '../errors.js';
16
+ import { pdfParser } from '../syntax/parser.js';
17
+
18
+ export const pdfButtonField = {
19
+ name: 'pdfButtonField',
20
+ dependencies: ['pdfErrors', 'pdfParser'],
21
+ deps: [pdfErrors, pdfParser],
22
+ factory(errors, parserMod) {
23
+ const { ParseError } = errors;
24
+ const isType = (parserMod && parserMod.isType)
25
+ || ((v, kind) => !!(v && v.type === kind));
26
+
27
+ const FLAG_NO_TOGGLE_OFF = 1 << 14;
28
+ const FLAG_RADIO = 1 << 15;
29
+ const FLAG_PUSHBUTTON = 1 << 16;
30
+ const FLAG_RADIOS_IN_UNISON = 1 << 25;
31
+
32
+ const KNOWN = new Set([
33
+ 'Type', 'FT', 'Parent', 'Kids', 'T', 'TU', 'TM', 'Ff', 'V', 'DV',
34
+ 'AA', 'DA', 'Q', 'Opt', 'AP', 'AS'
35
+ ]);
36
+
37
+ function intOr(v, dflt) {
38
+ if (!v) return dflt;
39
+ if (v.type !== 'int' && v.type !== 'real') return dflt;
40
+ return v.value | 0;
41
+ }
42
+
43
+ function toOpt(v) {
44
+ if (!v) return [];
45
+ if (v.type !== 'array') {
46
+ throw new ParseError('pdf/form/btn/bad-opt',
47
+ '/Opt must be an array when present',
48
+ { context: { kind: v.type } });
49
+ }
50
+ const out = [];
51
+ for (const it of v.items) {
52
+ if (isType(it, 'string')) out.push(it.value);
53
+ else if (isType(it, 'name')) out.push(it.value);
54
+ else {
55
+ throw new ParseError('pdf/form/btn/bad-opt-entry',
56
+ '/Opt entries must be strings or names',
57
+ { context: { kind: it && it.type } });
58
+ }
59
+ }
60
+ return out;
61
+ }
62
+
63
+ function typeButtonField(dict) {
64
+ if (!isType(dict, 'dict')) {
65
+ throw new ParseError('pdf/form/btn/not-dict',
66
+ 'button field must be a dictionary',
67
+ { context: { type: dict && dict.type } });
68
+ }
69
+ const e = dict.entries;
70
+ if (e.FT && (e.FT.type !== 'name' || e.FT.value !== 'Btn')) {
71
+ throw new ParseError('pdf/form/btn/bad-ft',
72
+ '/FT must be /Btn for a button field',
73
+ { context: { actual: e.FT.value } });
74
+ }
75
+
76
+ const flags = intOr(e.Ff, 0);
77
+ const isPush = (flags & FLAG_PUSHBUTTON) !== 0;
78
+ const isRadio = (flags & FLAG_RADIO) !== 0;
79
+ let kind;
80
+ if (isPush) kind = 'pushbutton';
81
+ else if (isRadio) kind = 'radio';
82
+ else kind = 'checkbox';
83
+
84
+ const out = {
85
+ ft: 'Btn',
86
+ flags,
87
+ kind,
88
+ noToggleToOff: (flags & FLAG_NO_TOGGLE_OFF) !== 0,
89
+ radiosInUnison: (flags & FLAG_RADIOS_IN_UNISON) !== 0,
90
+ v: isType(e.V, 'name') ? e.V.value : null,
91
+ dv: isType(e.DV, 'name') ? e.DV.value : null,
92
+ opt: toOpt(e.Opt),
93
+ t: isType(e.T, 'string') ? e.T.value : null,
94
+ raw: dict,
95
+ _extras: {}
96
+ };
97
+ for (const k of Object.keys(e)) {
98
+ if (!KNOWN.has(k)) out._extras[k] = e[k];
99
+ }
100
+ return out;
101
+ }
102
+
103
+ return { typeButtonField };
104
+ }
105
+ };
@@ -0,0 +1,152 @@
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 Choice field typing per ISO 32000-2:2020 §12.7.5.4.
8
+ *
9
+ * @module pdf/form/choice
10
+ */
11
+
12
+ /**
13
+ * Module factory — worker-safe, self-contained.
14
+ */
15
+ import { pdfErrors } from '../errors.js';
16
+ import { pdfParser } from '../syntax/parser.js';
17
+
18
+ export const pdfChoiceField = {
19
+ name: 'pdfChoiceField',
20
+ dependencies: ['pdfErrors', 'pdfParser'],
21
+ deps: [pdfErrors, pdfParser],
22
+ factory(errors, parserMod) {
23
+ const { ParseError } = errors;
24
+ const isType = (parserMod && parserMod.isType)
25
+ || ((v, kind) => !!(v && v.type === kind));
26
+
27
+ const F_COMBO = 1 << 17;
28
+ const F_EDIT = 1 << 18;
29
+ const F_SORT = 1 << 19;
30
+ const F_MULTI_SELECT = 1 << 21;
31
+ const F_DO_NOT_SPELLCHECK = 1 << 22;
32
+ const F_COMMIT_ON_SEL_CHG = 1 << 26;
33
+
34
+ const KNOWN = new Set([
35
+ 'Type', 'FT', 'Parent', 'Kids', 'T', 'TU', 'TM', 'Ff', 'V', 'DV',
36
+ 'AA', 'DA', 'Q', 'Opt', 'TI', 'I', 'AP'
37
+ ]);
38
+
39
+ function intOr(v, dflt) {
40
+ if (!v) return dflt;
41
+ if (v.type !== 'int' && v.type !== 'real') return dflt;
42
+ return v.value | 0;
43
+ }
44
+
45
+ function isStringy(v) { return !!(v && (v.type === 'string' || v.type === 'name')); }
46
+
47
+ function toOpt(v) {
48
+ if (!v) return [];
49
+ if (v.type !== 'array') {
50
+ throw new ParseError('pdf/form/ch/bad-opt',
51
+ '/Opt must be an array when present',
52
+ { context: { kind: v.type } });
53
+ }
54
+ const out = [];
55
+ for (const it of v.items) {
56
+ if (isType(it, 'string')) {
57
+ out.push({ export: it.value, display: it.value });
58
+ } else if (isType(it, 'name')) {
59
+ out.push({ export: it.value, display: it.value });
60
+ } else if (isType(it, 'array')) {
61
+ if (it.items.length !== 2) {
62
+ throw new ParseError('pdf/form/ch/bad-opt-entry',
63
+ '/Opt sub-array must have exactly two elements',
64
+ { context: { len: it.items.length } });
65
+ }
66
+ const a = it.items[0], b = it.items[1];
67
+ if (!isStringy(a) || !isStringy(b)) {
68
+ throw new ParseError('pdf/form/ch/bad-opt-entry',
69
+ '/Opt sub-array elements must be strings',
70
+ { context: { kinds: [a && a.type, b && b.type] } });
71
+ }
72
+ out.push({ export: a.value, display: b.value });
73
+ } else {
74
+ throw new ParseError('pdf/form/ch/bad-opt-entry',
75
+ '/Opt entries must be strings or [export, display] arrays',
76
+ { context: { kind: it && it.type } });
77
+ }
78
+ }
79
+ return out;
80
+ }
81
+
82
+ function toV(v) {
83
+ if (!v) return null;
84
+ if (isStringy(v)) return [v.value];
85
+ if (v.type === 'array') {
86
+ const out = [];
87
+ for (const it of v.items) {
88
+ if (!isStringy(it)) {
89
+ throw new ParseError('pdf/form/ch/bad-v',
90
+ '/V array entries must be strings',
91
+ { context: { kind: it && it.type } });
92
+ }
93
+ out.push(it.value);
94
+ }
95
+ return out;
96
+ }
97
+ return null;
98
+ }
99
+
100
+ function toIndices(v) {
101
+ if (!v || v.type !== 'array') return [];
102
+ const out = [];
103
+ for (const it of v.items) {
104
+ if (it && (it.type === 'int' || it.type === 'real')) out.push(it.value | 0);
105
+ }
106
+ return out;
107
+ }
108
+
109
+ function typeChoiceField(dict) {
110
+ if (!isType(dict, 'dict')) {
111
+ throw new ParseError('pdf/form/ch/not-dict',
112
+ 'choice field must be a dictionary',
113
+ { context: { type: dict && dict.type } });
114
+ }
115
+ const e = dict.entries;
116
+ if (e.FT && (e.FT.type !== 'name' || e.FT.value !== 'Ch')) {
117
+ throw new ParseError('pdf/form/ch/bad-ft',
118
+ '/FT must be /Ch for a choice field',
119
+ { context: { actual: e.FT.value } });
120
+ }
121
+
122
+ const flags = intOr(e.Ff, 0);
123
+ const combo = (flags & F_COMBO) !== 0;
124
+ const out = {
125
+ ft: 'Ch',
126
+ flags,
127
+ kind: combo ? 'combo' : 'list',
128
+ editable: combo && ((flags & F_EDIT) !== 0),
129
+ sort: (flags & F_SORT) !== 0,
130
+ multiSelect: !combo && ((flags & F_MULTI_SELECT) !== 0),
131
+ doNotSpellCheck: (flags & F_DO_NOT_SPELLCHECK) !== 0,
132
+ commitOnSelChange:(flags & F_COMMIT_ON_SEL_CHG) !== 0,
133
+ opt: toOpt(e.Opt),
134
+ v: toV(e.V),
135
+ dv: toV(e.DV),
136
+ i: toIndices(e.I),
137
+ ti: intOr(e.TI, 0),
138
+ da: isType(e.DA, 'string') ? e.DA.value : null,
139
+ q: intOr(e.Q, 0),
140
+ t: isType(e.T, 'string') ? e.T.value : null,
141
+ raw: dict,
142
+ _extras: {}
143
+ };
144
+ for (const k of Object.keys(e)) {
145
+ if (!KNOWN.has(k)) out._extras[k] = e[k];
146
+ }
147
+ return out;
148
+ }
149
+
150
+ return { typeChoiceField };
151
+ }
152
+ };
@@ -0,0 +1,120 @@
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 Form-field tree walker per ISO 32000-2:2020 §12.7.4.
8
+ *
9
+ * @module pdf/form/fieldTree
10
+ */
11
+
12
+ /**
13
+ * Module factory — worker-safe, self-contained.
14
+ */
15
+ import { pdfErrors } from '../errors.js';
16
+ import { pdfParser } from '../syntax/parser.js';
17
+
18
+ export const pdfFieldTree = {
19
+ name: 'pdfFieldTree',
20
+ dependencies: ['pdfErrors', 'pdfParser'],
21
+ deps: [pdfErrors, pdfParser],
22
+ factory(errors, parserMod) {
23
+ const { ParseError } = errors;
24
+ const isType = (parserMod && parserMod.isType)
25
+ || ((v, kind) => !!(v && v.type === kind));
26
+
27
+ const INHERITABLE = new Set(['FT', 'Ff', 'V', 'DV', 'DA', 'Q', 'MaxLen']);
28
+
29
+ function isFieldKidsArray(kids, resolveRef) {
30
+ if (!kids || kids.type !== 'array' || kids.items.length === 0) return false;
31
+ const first = kids.items[0];
32
+ if (!first || first.type !== 'ref') return false;
33
+ let resolved;
34
+ try { resolved = resolveRef(first); } catch (_e) { return false; }
35
+ if (!isType(resolved, 'dict')) return false;
36
+ return !!resolved.entries.T;
37
+ }
38
+
39
+ function walkFieldTree(rootRefs, resolveRef, opts) {
40
+ const maxDepth = (opts && opts.maxDepth) || 32;
41
+ const maxFields = (opts && opts.maxFields) || 100000;
42
+ if (!Array.isArray(rootRefs)) {
43
+ throw new ParseError('pdf/form/tree/bad-roots',
44
+ 'rootRefs must be an array',
45
+ { context: { 'type': typeof rootRefs } });
46
+ }
47
+ const out = [];
48
+ const visited = new Set();
49
+ for (const r of rootRefs) walk(r, [], 0);
50
+ return out;
51
+
52
+ function walk(ref, ancestors, depth) {
53
+ if (depth > maxDepth) {
54
+ throw new ParseError('pdf/form/tree/max-depth',
55
+ 'field tree depth limit exceeded',
56
+ { context: { depth, maxDepth } });
57
+ }
58
+ if (!ref || typeof ref.num !== 'number') {
59
+ throw new ParseError('pdf/form/tree/bad-ref',
60
+ 'field reference is malformed',
61
+ { context: { ref } });
62
+ }
63
+ const key = ref.num + ':' + ref.gen;
64
+ if (visited.has(key)) {
65
+ throw new ParseError('pdf/form/tree/cycle',
66
+ 'cycle detected in field tree',
67
+ { context: { ref } });
68
+ }
69
+ visited.add(key);
70
+
71
+ const node = resolveRef({ type: 'ref', num: ref.num, gen: ref.gen });
72
+ if (!isType(node, 'dict')) {
73
+ throw new ParseError('pdf/form/tree/not-dict',
74
+ 'field tree node is not a dict',
75
+ { context: { ref } });
76
+ }
77
+
78
+ const kids = node.entries.Kids;
79
+ const isTerminal = !isFieldKidsArray(kids, resolveRef);
80
+
81
+ out.push({
82
+ ref: { num: ref.num, gen: ref.gen },
83
+ node,
84
+ ancestors: ancestors.slice(),
85
+ terminal: isTerminal
86
+ });
87
+ if (out.length > maxFields) {
88
+ throw new ParseError('pdf/form/tree/too-many',
89
+ 'field count limit exceeded',
90
+ { context: { maxFields } });
91
+ }
92
+
93
+ if (isTerminal) return;
94
+ const nextAncestors = ancestors.concat([node]);
95
+ for (const k of kids.items) {
96
+ if (k.type !== 'ref') {
97
+ throw new ParseError('pdf/form/tree/non-ref-kid',
98
+ 'field /Kids entries must be indirect refs',
99
+ { context: { kind: k.type } });
100
+ }
101
+ walk({ num: k.num, gen: k.gen }, nextAncestors, depth + 1);
102
+ }
103
+ }
104
+ }
105
+
106
+ function getInherited(node, ancestors, key) {
107
+ if (!INHERITABLE.has(key)) return undefined;
108
+ if (node && node.entries && node.entries[key] !== undefined) {
109
+ return node.entries[key];
110
+ }
111
+ for (let i = ancestors.length - 1; i >= 0; i--) {
112
+ const a = ancestors[i];
113
+ if (a && a.entries && a.entries[key] !== undefined) return a.entries[key];
114
+ }
115
+ return undefined;
116
+ }
117
+
118
+ return { walkFieldTree, getInherited };
119
+ }
120
+ };