@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,236 @@
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 RFC 3161 / PAdES B-T timestamp token parser.
8
+ *
9
+ * A document-level timestamp token (PAdES B-T, ISO/TS 32001 §5.4) is a
10
+ * RFC 3161 `TimeStampToken`, i.e. a CMS `SignedData` whose
11
+ * `encapContentInfo` carries `id-ct-TSTInfo`
12
+ * (`1.2.840.113549.1.9.16.1.4`). Inside `TSTInfo` (RFC 3161 §2.4.2)
13
+ * we recover the policy, `messageImprint`, serial number, generation
14
+ * time and optional nonce / TSA identity.
15
+ *
16
+ * @module pdf/sig/timestamp
17
+ */
18
+
19
+ /**
20
+ * Module factory.
21
+ */
22
+ import { pdfErrors } from '../errors.js';
23
+ import { pdfSigOids } from './oids.js';
24
+ import { asn1 } from '@awacloud/fw/crypto/utils/asn1.js';
25
+ import { rsa } from '@awacloud/fw/crypto/pkc/rsa.js';
26
+ import { ecc } from '@awacloud/fw/crypto/pkc/ecc.js';
27
+ import { ed25519 } from '@awacloud/fw/crypto/pkc/ed25519.js';
28
+ import { sha256 } from '@awacloud/fw/crypto/hash/sha256.js';
29
+ import { sha384 } from '@awacloud/fw/crypto/hash/sha384.js';
30
+ import { sha512 } from '@awacloud/fw/crypto/hash/sha512.js';
31
+
32
+ export const pdfTimestamp = {
33
+ name: 'pdfTimestamp',
34
+ dependencies: ['pdfErrors', 'pdfSigOids',
35
+ 'asn1', 'rsa', 'ecc', 'ed25519',
36
+ 'sha256', 'sha384', 'sha512'],
37
+ deps: [pdfErrors, pdfSigOids, asn1, rsa, ecc, ed25519, sha256, sha384, sha512],
38
+ factory(errors, sigOids, asn1, rsa, ecc, ed25519,
39
+ sha256, sha384, sha512) {
40
+ const { EncryptionError, ParseError } = errors;
41
+ const {
42
+ DIGEST_OIDS,
43
+ SIG_DISPATCH_OIDS: SIG_OIDS,
44
+ OID_TST_INFO,
45
+ OID_AA_TIMESTAMP
46
+ } = sigOids;
47
+ const bundle = { asn1, rsa, ecc, ed25519, sha256, sha384, sha512 };
48
+
49
+ function _parseGeneralizedTime(bytes) {
50
+ if (!bytes || bytes.length < 14) return null;
51
+ const s = String.fromCharCode.apply(null, Array.from(bytes));
52
+ const m = /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/.exec(s);
53
+ if (!m) return null;
54
+ const d = new Date(Date.UTC(
55
+ +m[1], +m[2] - 1, +m[3], +m[4], +m[5], +m[6]));
56
+ return isNaN(d.getTime()) ? null : d;
57
+ }
58
+
59
+ function _sliceTLV(buf) {
60
+ if (!buf || buf.length < 2) return null;
61
+ let lenLen = 1;
62
+ let len = buf[1];
63
+ if (len & 0x80) {
64
+ const n = len & 0x7F;
65
+ len = 0;
66
+ for (let i = 0; i < n; i++) len = (len << 8) | buf[2 + i];
67
+ lenLen = 1 + n;
68
+ }
69
+ return buf.subarray(0, 1 + lenLen + len);
70
+ }
71
+
72
+ function parseTimestampToken(blob, asn1Mod) {
73
+ const a1 = asn1Mod || asn1;
74
+ if (!(blob instanceof Uint8Array)) {
75
+ throw new ParseError('pdf/ts/bad-input',
76
+ 'parseTimestampToken expects a Uint8Array');
77
+ }
78
+ if (!a1 || typeof a1.parseOne !== 'function') {
79
+ throw new EncryptionError('pdf/ts/missing-fw',
80
+ 'parseTimestampToken requires the fw asn1 module');
81
+ }
82
+ const ci = a1.parseOne(blob, 0);
83
+ if (!ci) {
84
+ throw new ParseError('pdf/ts/malformed',
85
+ 'ContentInfo parse failed');
86
+ }
87
+ const ciChildren = a1.parseChildren(ci.value);
88
+ if (!ciChildren || ciChildren.length < 2) {
89
+ throw new ParseError('pdf/ts/malformed',
90
+ 'ContentInfo missing signedData');
91
+ }
92
+ const sdNode = a1.parseOne(ciChildren[1].value, 0);
93
+ if (!sdNode) {
94
+ throw new ParseError('pdf/ts/malformed',
95
+ 'SignedData parse failed');
96
+ }
97
+ const sd = a1.parseChildren(sdNode.value);
98
+ if (!sd) {
99
+ throw new ParseError('pdf/ts/malformed',
100
+ 'SignedData children parse failed');
101
+ }
102
+ const eci = sd[2] && a1.parseChildren(sd[2].value);
103
+ if (!eci || eci.length < 2) {
104
+ throw new ParseError('pdf/ts/no-econtent',
105
+ 'TSTInfo encapContentInfo missing');
106
+ }
107
+ const eciTypeOid = a1.readOid(eci[0]);
108
+ if (eciTypeOid !== OID_TST_INFO) {
109
+ throw new ParseError('pdf/ts/wrong-econtent',
110
+ 'eContentType is not id-ct-TSTInfo',
111
+ { context: { oid: eciTypeOid } });
112
+ }
113
+ const inner = a1.parseOne(eci[1].value, 0);
114
+ if (!inner) {
115
+ throw new ParseError('pdf/ts/no-tstinfo',
116
+ 'TSTInfo OCTET STRING missing');
117
+ }
118
+ const tst = a1.parseOne(inner.value, 0);
119
+ if (!tst) {
120
+ throw new ParseError('pdf/ts/bad-tstinfo',
121
+ 'TSTInfo parse failed');
122
+ }
123
+ const tstChildren = a1.parseChildren(tst.value);
124
+ if (!tstChildren || tstChildren.length < 4) {
125
+ throw new ParseError('pdf/ts/bad-tstinfo',
126
+ 'TSTInfo too short');
127
+ }
128
+ const out = {
129
+ version: tstChildren[0].value[0] || 1,
130
+ policy: a1.readOid(tstChildren[1]),
131
+ serialNumber: tstChildren[3].value,
132
+ genTime: null,
133
+ messageImprint: null
134
+ };
135
+ const mi = a1.parseChildren(tstChildren[2].value);
136
+ if (mi && mi.length >= 2) {
137
+ const algChildren = a1.parseChildren(mi[0].value);
138
+ const algOid = algChildren && algChildren[0]
139
+ ? a1.readOid(algChildren[0]) : null;
140
+ out.messageImprint = {
141
+ hashAlg: DIGEST_OIDS[algOid] || algOid,
142
+ hashedMessage: mi[1].value
143
+ };
144
+ }
145
+ if (tstChildren[4]) {
146
+ out.genTime = _parseGeneralizedTime(tstChildren[4].value);
147
+ }
148
+ for (let i = 5; i < tstChildren.length; i++) {
149
+ const n = tstChildren[i];
150
+ if (n.tag === 0x02) {
151
+ out.nonce = n.value;
152
+ } else if (n.tag === 0xA0) {
153
+ out.tsa = n.value;
154
+ }
155
+ }
156
+ return out;
157
+ }
158
+
159
+ function verifyTimestamp(tokenBlob, fwBundle) {
160
+ const fb = fwBundle || bundle;
161
+ if (!fb || !fb.asn1) {
162
+ throw new EncryptionError('pdf/ts/missing-fw',
163
+ 'verifyTimestamp requires fwBundle.asn1');
164
+ }
165
+ const errs = [];
166
+ let parsed;
167
+ try {
168
+ parsed = parseTimestampToken(tokenBlob, fb.asn1);
169
+ } catch (e) {
170
+ errs.push({ code: e.code || 'pdf/ts/parse',
171
+ message: e.message,
172
+ cause: e });
173
+ return { valid: false, errors: errs, tstInfo: null,
174
+ hashAlg: null, signatureAlg: null };
175
+ }
176
+ const ci = fb.asn1.parseOne(tokenBlob, 0);
177
+ const ciKids = ci ? fb.asn1.parseChildren(ci.value) : null;
178
+ const sdNode = ciKids && ciKids[1]
179
+ ? fb.asn1.parseOne(ciKids[1].value, 0) : null;
180
+ const sd = sdNode ? fb.asn1.parseChildren(sdNode.value) : null;
181
+ let signatureAlg = null;
182
+ if (sd) {
183
+ let siSet = null;
184
+ for (let i = sd.length - 1; i >= 0; i--) {
185
+ if (sd[i].tag === 0x31) { siSet = sd[i]; break; }
186
+ }
187
+ if (siSet) {
188
+ const signers = fb.asn1.parseChildren(siSet.value);
189
+ const si = signers && signers[0]
190
+ ? fb.asn1.parseChildren(signers[0].value) : null;
191
+ if (si) {
192
+ for (let i = 0; i < si.length - 1; i++) {
193
+ if (si[i].tag === 0x30 && si[i + 1].tag === 0x04) {
194
+ const algChildren =
195
+ fb.asn1.parseChildren(si[i].value);
196
+ if (algChildren && algChildren[0]) {
197
+ const oid = fb.asn1.readOid(algChildren[0]);
198
+ signatureAlg = SIG_OIDS[oid] || null;
199
+ }
200
+ break;
201
+ }
202
+ }
203
+ }
204
+ }
205
+ }
206
+ return {
207
+ valid: errs.length === 0,
208
+ errors: errs, tstInfo: parsed,
209
+ hashAlg: parsed.messageImprint && parsed.messageImprint.hashAlg,
210
+ signatureAlg
211
+ };
212
+ }
213
+
214
+ function extractTimestampFromUnsignedAttrs(signedAttrs, asn1Mod) {
215
+ const a1 = asn1Mod || asn1;
216
+ if (!Array.isArray(signedAttrs)) return null;
217
+ for (let i = 0; i < signedAttrs.length; i++) {
218
+ const attr = a1.parseChildren(signedAttrs[i].value);
219
+ if (!attr || attr.length < 2) continue;
220
+ const oid = a1.readOid(attr[0]);
221
+ if (oid !== OID_AA_TIMESTAMP) continue;
222
+ const set = a1.parseChildren(attr[1].value);
223
+ if (!set || !set.length) continue;
224
+ return _sliceTLV(attr[1].value);
225
+ }
226
+ return null;
227
+ }
228
+
229
+ return {
230
+ parseTimestampToken,
231
+ verifyTimestamp,
232
+ extractTimestampFromUnsignedAttrs,
233
+ OID_TST_INFO, OID_AA_TIMESTAMP
234
+ };
235
+ }
236
+ };
@@ -0,0 +1,133 @@
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 Cross-reference stream parser per ISO 32000-2:2020 §7.5.8.
8
+ *
9
+ * @module pdf/syntax/crossRefStream
10
+ */
11
+
12
+ /**
13
+ * Module factory — worker-safe, self-contained.
14
+ */
15
+ import { pdfErrors } from '../errors.js';
16
+ import { pdfParserObj } from './parser-obj.js';
17
+
18
+ export const pdfCrossRefStream = {
19
+ name: 'pdfCrossRefStream',
20
+ dependencies: ['pdfErrors', 'pdfParserObj'],
21
+ deps: [pdfErrors, pdfParserObj],
22
+ factory(errors, parserObj) {
23
+ const { ParseError } = errors;
24
+ const { isType } = parserObj;
25
+
26
+ function readBE(bytes, off, len) {
27
+ let v = 0;
28
+ for (let i = 0; i < len; i++) v = (v * 256) + bytes[off + i];
29
+ return v;
30
+ }
31
+
32
+ function readInt(entry, label) {
33
+ if (!entry || entry.type !== 'int') {
34
+ throw new ParseError('pdf/xrefstm/missing-int',
35
+ `XRef stream dict missing required /${label}`,
36
+ { context: { label } });
37
+ }
38
+ return entry.value;
39
+ }
40
+
41
+ function parseCrossRefStream(decoded, dict) {
42
+ if (!(decoded instanceof Uint8Array)) {
43
+ throw new ParseError('pdf/xrefstm/bad-input',
44
+ 'parseCrossRefStream expects Uint8Array');
45
+ }
46
+ if (!isType(dict, 'dict')) {
47
+ throw new ParseError('pdf/xrefstm/bad-dict',
48
+ 'XRef stream dict must be a typed dict');
49
+ }
50
+ const e = dict.entries;
51
+ if (!e.Type || e.Type.type !== 'name' || e.Type.value !== 'XRef') {
52
+ throw new ParseError('pdf/xrefstm/wrong-type',
53
+ '/Type must be /XRef',
54
+ { context: { actual: e.Type && e.Type.value } });
55
+ }
56
+
57
+ const W = e.W;
58
+ if (!W || W.type !== 'array' || W.items.length !== 3) {
59
+ throw new ParseError('pdf/xrefstm/bad-W',
60
+ '/W must be a 3-element array', { context: { items: W && W.items && W.items.length } });
61
+ }
62
+ const w = W.items.map(it => {
63
+ if (it.type !== 'int' || it.value < 0) {
64
+ throw new ParseError('pdf/xrefstm/bad-W-entry',
65
+ '/W entries must be non-negative ints');
66
+ }
67
+ return it.value;
68
+ });
69
+ const recordSize = w[0] + w[1] + w[2];
70
+ if (recordSize <= 0) {
71
+ throw new ParseError('pdf/xrefstm/bad-W-zero',
72
+ '/W must yield at least one non-zero field');
73
+ }
74
+
75
+ let index;
76
+ if (e.Index) {
77
+ if (e.Index.type !== 'array' || (e.Index.items.length & 1) !== 0) {
78
+ throw new ParseError('pdf/xrefstm/bad-Index',
79
+ '/Index must be an array of (first, count) pairs');
80
+ }
81
+ index = [];
82
+ for (let i = 0; i < e.Index.items.length; i += 2) {
83
+ const f = e.Index.items[i], c = e.Index.items[i + 1];
84
+ if (f.type !== 'int' || c.type !== 'int') {
85
+ throw new ParseError('pdf/xrefstm/bad-Index-entry',
86
+ '/Index entries must be ints');
87
+ }
88
+ index.push([f.value, c.value]);
89
+ }
90
+ } else {
91
+ const size = readInt(e.Size, 'Size');
92
+ index = [[0, size]];
93
+ }
94
+
95
+ const entries = {};
96
+ let pos = 0;
97
+ for (const [first, count] of index) {
98
+ for (let k = 0; k < count; k++) {
99
+ if (pos + recordSize > decoded.length) {
100
+ throw new ParseError('pdf/xrefstm/truncated',
101
+ 'truncated xref stream payload',
102
+ { context: { pos, recordSize, length: decoded.length } });
103
+ }
104
+ const t = w[0] > 0 ? readBE(decoded, pos, w[0]) : 1;
105
+ const f2 = w[1] > 0 ? readBE(decoded, pos + w[0], w[1]) : 0;
106
+ const f3 = w[2] > 0 ? readBE(decoded, pos + w[0] + w[1], w[2]) : 0;
107
+ pos += recordSize;
108
+ const num = first + k;
109
+ if (t === 0) {
110
+ entries[num] = { type: 0, offset: f2, gen: f3, free: true };
111
+ } else if (t === 1) {
112
+ entries[num] = { type: 1, offset: f2, gen: f3, free: false };
113
+ } else if (t === 2) {
114
+ entries[num] = {
115
+ type: 2, objStm: f2, index: f3,
116
+ free: false, offset: 0, gen: 0
117
+ };
118
+ } else {
119
+ entries[num] = { type: t, offset: 0, gen: 0, free: true };
120
+ }
121
+ }
122
+ }
123
+
124
+ return {
125
+ entries,
126
+ size: e.Size && e.Size.type === 'int' ? e.Size.value : Object.keys(entries).length,
127
+ trailer: dict
128
+ };
129
+ }
130
+
131
+ return { parseCrossRefStream };
132
+ }
133
+ };
@@ -0,0 +1,122 @@
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 ASCII85Decode filter per ISO 32000-2:2020 §7.4.3.
8
+ *
9
+ * 5 ASCII characters in the range `!`..`u` decode to 4 binary bytes
10
+ * encoding a 32-bit big-endian integer (base-85). The special
11
+ * character `z` represents 4 zero bytes. The stream is terminated by
12
+ * `~>`. The final group may be 2..4 characters, decoding to 1..3
13
+ * trailing bytes.
14
+ *
15
+ * @module pdf/syntax/filters/ascii85
16
+ */
17
+
18
+ /**
19
+ * Module factory — worker-safe, self-contained.
20
+ */
21
+ import { pdfErrors } from '../../errors.js';
22
+
23
+ export const pdfAscii85 = {
24
+ name: 'pdfAscii85',
25
+ dependencies: ['pdfErrors'],
26
+ deps: [pdfErrors],
27
+ factory(errors) {
28
+ const { ParseError } = errors;
29
+ const TILDE = 0x7E, GT = 0x3E, Z = 0x7A, BANG = 0x21, U = 0x75;
30
+ const SP = 0x20, HT = 0x09, LF = 0x0A, CR = 0x0D, FF = 0x0C, NUL = 0x00;
31
+ function isWs(b) {
32
+ return b === SP || b === HT || b === LF || b === CR || b === FF || b === NUL;
33
+ }
34
+
35
+ function decode(bytes) {
36
+ if (!(bytes instanceof Uint8Array)) {
37
+ throw new ParseError('pdf/ascii85/bad-input',
38
+ 'ASCII85Decode expects Uint8Array');
39
+ }
40
+ const out = [];
41
+ let group = 0, count = 0;
42
+ for (let i = 0; i < bytes.length; i++) {
43
+ const b = bytes[i];
44
+ if (isWs(b)) continue;
45
+ if (b === TILDE) {
46
+ if (i + 1 < bytes.length && bytes[i + 1] === GT) break;
47
+ throw new ParseError('pdf/ascii85/bad-eod',
48
+ '~ not followed by >', { context: { offset: i } });
49
+ }
50
+ if (b === Z) {
51
+ if (count !== 0) {
52
+ throw new ParseError('pdf/ascii85/bad-z',
53
+ 'z must appear at group boundary', { context: { offset: i } });
54
+ }
55
+ out.push(0, 0, 0, 0);
56
+ continue;
57
+ }
58
+ if (b < BANG || b > U) {
59
+ throw new ParseError('pdf/ascii85/bad-digit',
60
+ 'character outside ! .. u',
61
+ { context: { offset: i, byte: b } });
62
+ }
63
+ group = group * 85 + (b - BANG);
64
+ count++;
65
+ if (count === 5) {
66
+ out.push((group >>> 24) & 0xFF, (group >>> 16) & 0xFF,
67
+ (group >>> 8) & 0xFF, group & 0xFF);
68
+ group = 0; count = 0;
69
+ }
70
+ }
71
+ if (count > 0) {
72
+ for (let k = count; k < 5; k++) group = group * 85 + 84;
73
+ const decoded = [
74
+ (group >>> 24) & 0xFF, (group >>> 16) & 0xFF,
75
+ (group >>> 8) & 0xFF, group & 0xFF
76
+ ];
77
+ for (let k = 0; k < count - 1; k++) out.push(decoded[k]);
78
+ }
79
+ return Uint8Array.from(out);
80
+ }
81
+
82
+ function encode(bytes) {
83
+ if (!(bytes instanceof Uint8Array)) {
84
+ throw new ParseError('pdf/ascii85/bad-input',
85
+ 'ASCII85Encode expects Uint8Array');
86
+ }
87
+ const chars = [];
88
+ let i = 0;
89
+ while (i + 4 <= bytes.length) {
90
+ const v = (bytes[i] * 0x1000000) + (bytes[i + 1] << 16)
91
+ + (bytes[i + 2] << 8) + bytes[i + 3];
92
+ if (v === 0) chars.push(Z);
93
+ else {
94
+ const g = [0, 0, 0, 0, 0];
95
+ let n = v;
96
+ for (let k = 4; k >= 0; k--) {
97
+ g[k] = (n % 85) + BANG; n = Math.floor(n / 85);
98
+ }
99
+ chars.push(g[0], g[1], g[2], g[3], g[4]);
100
+ }
101
+ i += 4;
102
+ }
103
+ const rem = bytes.length - i;
104
+ if (rem > 0) {
105
+ const padded = new Uint8Array(4);
106
+ padded.set(bytes.subarray(i));
107
+ const v = (padded[0] * 0x1000000) + (padded[1] << 16)
108
+ + (padded[2] << 8) + padded[3];
109
+ const g = [0, 0, 0, 0, 0];
110
+ let n = v;
111
+ for (let k = 4; k >= 0; k--) {
112
+ g[k] = (n % 85) + BANG; n = Math.floor(n / 85);
113
+ }
114
+ for (let k = 0; k < rem + 1; k++) chars.push(g[k]);
115
+ }
116
+ chars.push(TILDE, GT);
117
+ return Uint8Array.from(chars);
118
+ }
119
+
120
+ return { decode, encode };
121
+ }
122
+ };
@@ -0,0 +1,83 @@
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 ASCIIHexDecode filter per ISO 32000-2:2020 §7.4.2.
8
+ *
9
+ * Each pair of ASCII hex digits decodes to one byte. Whitespace is
10
+ * ignored. The terminator `>` ends the stream ; an odd number of hex
11
+ * digits is treated as if a trailing `0` were present.
12
+ *
13
+ * @module pdf/syntax/filters/asciiHex
14
+ */
15
+
16
+ /**
17
+ * Module factory — worker-safe, self-contained.
18
+ */
19
+ import { pdfErrors } from '../../errors.js';
20
+
21
+ export const pdfAsciiHex = {
22
+ name: 'pdfAsciiHex',
23
+ dependencies: ['pdfErrors'],
24
+ deps: [pdfErrors],
25
+ factory(errors) {
26
+ const { ParseError } = errors;
27
+ const HEX_LO = [
28
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
29
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
30
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
31
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
32
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
33
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
34
+ -1, 10, 11, 12, 13, 14, 15, -1
35
+ ];
36
+ const SP = 0x20, HT = 0x09, LF = 0x0A, CR = 0x0D, FF = 0x0C, NUL = 0x00;
37
+ function isWs(b) {
38
+ return b === SP || b === HT || b === LF || b === CR || b === FF || b === NUL;
39
+ }
40
+
41
+ function decode(bytes) {
42
+ if (!(bytes instanceof Uint8Array)) {
43
+ throw new ParseError('pdf/asciiHex/bad-input',
44
+ 'ASCIIHexDecode expects Uint8Array');
45
+ }
46
+ const out = [];
47
+ let pending = -1;
48
+ for (let i = 0; i < bytes.length; i++) {
49
+ const b = bytes[i];
50
+ if (b === 0x3E) break;
51
+ if (isWs(b)) continue;
52
+ const v = b < HEX_LO.length ? HEX_LO[b] : -1;
53
+ if (v < 0) {
54
+ throw new ParseError('pdf/asciiHex/bad-digit',
55
+ 'invalid hex digit',
56
+ { context: { offset: i, byte: b } });
57
+ }
58
+ if (pending < 0) pending = v;
59
+ else { out.push((pending << 4) | v); pending = -1; }
60
+ }
61
+ if (pending >= 0) out.push(pending << 4);
62
+ return Uint8Array.from(out);
63
+ }
64
+
65
+ function encode(bytes) {
66
+ if (!(bytes instanceof Uint8Array)) {
67
+ throw new ParseError('pdf/asciiHex/bad-input',
68
+ 'ASCIIHexEncode expects Uint8Array');
69
+ }
70
+ const H = '0123456789ABCDEF';
71
+ let s = '';
72
+ for (let i = 0; i < bytes.length; i++) {
73
+ if (i > 0 && (i & 31) === 0) s += '\n';
74
+ const b = bytes[i];
75
+ s += H[b >> 4] + H[b & 0xF];
76
+ }
77
+ s += '>';
78
+ return new TextEncoder().encode(s);
79
+ }
80
+
81
+ return { decode, encode };
82
+ }
83
+ };