@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,198 @@
1
+ // Copyright (c) 2026 AwaCloud SAS
2
+ // Author: Matthieu Bouilloux
3
+ // SPDX-License-Identifier: AGPL-3.0-only
4
+ // Dual-licensed; see the NOTICE file for licensing and any additional terms.
5
+
6
+ /**
7
+ * @fileoverview Opt-in: LEGACY deprecated multimedia annotations.
8
+ *
9
+ * Surfaces typed payloads for the multimedia annotation subtypes that
10
+ * PDF 2.0 deprecates in favour of /RichMedia and the /Screen +
11
+ * /Rendition pattern:
12
+ *
13
+ * - /Subtype /Sound (ISO 32000-1 §12.5.6.16): /Sound stream, /Name
14
+ * icon, /Repeat, /Mode flag.
15
+ * - /Subtype /Movie (ISO 32000-1 §12.5.6.17): /Movie dict, /A
16
+ * activation flag/dict, /T title.
17
+ * - /Subtype /Screen (ISO 32000-1 §12.5.6.18): /MK appearance, /A
18
+ * rendition action, /AA additional actions for media playback.
19
+ *
20
+ * @module pdf/extra/legacy-deprecated-annots
21
+ */
22
+
23
+ import { pdfErrors } from '../errors.js';
24
+
25
+ export const pdfLegacyDeprecatedAnnots = {
26
+ name: 'pdfLegacyDeprecatedAnnots',
27
+ dependencies: ['pdfErrors'],
28
+ deps: [pdfErrors],
29
+
30
+ factory(errors) {
31
+ const { ParseError } = errors;
32
+ const SOUND_MODES = new Set(['Mono', 'Stereo']);
33
+ const MOVIE_OP_MODES = new Set(['Once', 'Open', 'Repeat', 'Palindrome']);
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 isStream(v) { return v && v.type === 'stream'; }
39
+ function isBool(v) { return v && v.type === 'bool'; }
40
+ function isNum(v) { return v && (v.type === 'int' || v.type === 'real'); }
41
+
42
+ function expectSubtype(dict, expected) {
43
+ if (!isDict(dict)) {
44
+ throw new ParseError('pdf/legacy-annot/not-dict',
45
+ 'annotation must be a dictionary',
46
+ { context: { expected } });
47
+ }
48
+ const st = dict.entries.Subtype;
49
+ if (!isName(st) || st.value !== expected) {
50
+ throw new ParseError('pdf/legacy-annot/bad-subtype',
51
+ `/Subtype must be /${expected}`,
52
+ { context: { actual: st && st.value, expected } });
53
+ }
54
+ }
55
+
56
+ function typeSoundAnnot(dict) {
57
+ expectSubtype(dict, 'Sound');
58
+ const e = dict.entries;
59
+ const out = { subtype: 'Sound', raw: dict, _extras: {} };
60
+ if (!e.Sound) {
61
+ throw new ParseError('pdf/legacy-annot/sound-missing',
62
+ 'Sound annot requires /Sound stream');
63
+ }
64
+ if (!isStream(e.Sound) && !(e.Sound.type === 'ref')) {
65
+ throw new ParseError('pdf/legacy-annot/sound-bad-stream',
66
+ '/Sound must be a stream or ref',
67
+ { context: { type: e.Sound.type } });
68
+ }
69
+ out.sound = e.Sound;
70
+ if (e.Name) {
71
+ if (!isName(e.Name)) {
72
+ throw new ParseError('pdf/legacy-annot/sound-bad-name',
73
+ '/Name must be a name');
74
+ }
75
+ out.iconName = e.Name.value;
76
+ }
77
+ const KNOWN = new Set(['Type','Subtype','Sound','Name']);
78
+ for (const k of Object.keys(e)) {
79
+ if (!KNOWN.has(k)) out._extras[k] = e[k];
80
+ }
81
+ return out;
82
+ }
83
+
84
+ function typeSoundStream(stream) {
85
+ if (!isStream(stream)) {
86
+ throw new ParseError('pdf/legacy-annot/sound-stream',
87
+ 'sound payload must be a stream');
88
+ }
89
+ const e = stream.dict ? stream.dict.entries : {};
90
+ const out = { raw: stream, _extras: {} };
91
+ if (e.R) { if (!isNum(e.R)) throw new ParseError('pdf/legacy-annot/sound-bad-R', '/R must be numeric'); out.samplingRate = e.R.value; }
92
+ if (e.C) { if (!isNum(e.C)) throw new ParseError('pdf/legacy-annot/sound-bad-C', '/C must be numeric'); out.channels = e.C.value; }
93
+ if (e.B) { if (!isNum(e.B)) throw new ParseError('pdf/legacy-annot/sound-bad-B', '/B must be numeric'); out.bitsPerSample = e.B.value; }
94
+ if (e.E) { if (!isName(e.E)) throw new ParseError('pdf/legacy-annot/sound-bad-E', '/E must be a name'); out.encoding = e.E.value; }
95
+ if (e.CO) { if (!isName(e.CO)) throw new ParseError('pdf/legacy-annot/sound-bad-CO', '/CO must be a name'); out.compression = e.CO.value; }
96
+ if (e.Mode) {
97
+ if (!isName(e.Mode) || !SOUND_MODES.has(e.Mode.value)) {
98
+ throw new ParseError('pdf/legacy-annot/sound-bad-mode',
99
+ '/Mode must be Mono or Stereo');
100
+ }
101
+ out.mode = e.Mode.value;
102
+ }
103
+ const KNOWN = new Set(['Type','R','C','B','E','CO','Mode','Length','Filter','DecodeParms']);
104
+ for (const k of Object.keys(e)) {
105
+ if (!KNOWN.has(k)) out._extras[k] = e[k];
106
+ }
107
+ return out;
108
+ }
109
+
110
+ function typeMovieAnnot(dict) {
111
+ expectSubtype(dict, 'Movie');
112
+ const e = dict.entries;
113
+ const out = { subtype: 'Movie', raw: dict, _extras: {} };
114
+ if (!e.Movie) {
115
+ throw new ParseError('pdf/legacy-annot/movie-missing',
116
+ 'Movie annot requires /Movie dict');
117
+ }
118
+ if (!isDict(e.Movie)) {
119
+ throw new ParseError('pdf/legacy-annot/movie-bad',
120
+ '/Movie must be a dict');
121
+ }
122
+ out.movie = e.Movie;
123
+ if (e.T) {
124
+ if (!isStr(e.T)) {
125
+ throw new ParseError('pdf/legacy-annot/movie-bad-T',
126
+ '/T must be a text string');
127
+ }
128
+ out.title = e.T.value;
129
+ }
130
+ if (e.A) {
131
+ if (!isBool(e.A) && !isDict(e.A)) {
132
+ throw new ParseError('pdf/legacy-annot/movie-bad-A',
133
+ '/A must be bool or dict');
134
+ }
135
+ if (isBool(e.A)) out.activation = { auto: e.A.value };
136
+ else {
137
+ const ae = e.A.entries;
138
+ const act = { raw: e.A };
139
+ if (ae.Mode && isName(ae.Mode)) {
140
+ if (!MOVIE_OP_MODES.has(ae.Mode.value)) {
141
+ throw new ParseError('pdf/legacy-annot/movie-bad-mode',
142
+ '/Mode must be Once, Open, Repeat or Palindrome');
143
+ }
144
+ act.mode = ae.Mode.value;
145
+ }
146
+ out.activation = act;
147
+ }
148
+ }
149
+ const KNOWN = new Set(['Type','Subtype','Movie','T','A']);
150
+ for (const k of Object.keys(e)) {
151
+ if (!KNOWN.has(k)) out._extras[k] = e[k];
152
+ }
153
+ return out;
154
+ }
155
+
156
+ function typeScreenAnnot(dict) {
157
+ expectSubtype(dict, 'Screen');
158
+ const e = dict.entries;
159
+ const out = { subtype: 'Screen', raw: dict, _extras: {} };
160
+ if (e.T) {
161
+ if (!isStr(e.T)) throw new ParseError('pdf/legacy-annot/screen-bad-T', '/T must be a string');
162
+ out.title = e.T.value;
163
+ }
164
+ if (e.MK) {
165
+ if (!isDict(e.MK)) throw new ParseError('pdf/legacy-annot/screen-bad-MK', '/MK must be a dict');
166
+ out.mk = e.MK;
167
+ }
168
+ if (e.A) {
169
+ if (!isDict(e.A)) throw new ParseError('pdf/legacy-annot/screen-bad-A', '/A must be an action dict');
170
+ out.action = e.A;
171
+ }
172
+ if (e.AA) {
173
+ if (!isDict(e.AA)) throw new ParseError('pdf/legacy-annot/screen-bad-AA', '/AA must be a dict');
174
+ out.additionalActions = e.AA;
175
+ }
176
+ if (e.P) {
177
+ if (e.P.type !== 'ref' && !isDict(e.P)) {
178
+ throw new ParseError('pdf/legacy-annot/screen-bad-P', '/P must be a page ref or dict');
179
+ }
180
+ out.page = e.P;
181
+ }
182
+ const KNOWN = new Set(['Type','Subtype','T','MK','A','AA','P','Rect','Contents']);
183
+ for (const k of Object.keys(e)) {
184
+ if (!KNOWN.has(k)) out._extras[k] = e[k];
185
+ }
186
+ return out;
187
+ }
188
+
189
+ return {
190
+ typeSoundAnnot,
191
+ typeSoundStream,
192
+ typeMovieAnnot,
193
+ typeScreenAnnot,
194
+ SOUND_MODES,
195
+ MOVIE_OP_MODES
196
+ };
197
+ }
198
+ };
@@ -0,0 +1,167 @@
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: read side of three legacy or heavyweight filters.
8
+ *
9
+ * Covers the read side of three filters that are still encountered in
10
+ * legacy PDFs but are deprecated or impractical to fully implement in
11
+ * pure JS:
12
+ *
13
+ * - LZWDecode — thin wrapper over @awacloud/fw/io/compress/lzw using
14
+ * the TIFF profile (MSB-first, 8-bit minCodeBits,
15
+ * CLEAR/END markers). The PDF /EarlyChange
16
+ * DecodeParm is supported.
17
+ * - CCITTFaxDecode — validates the `/DecodeParms` dictionary (K, Columns,
18
+ * Rows, EncodedByteAlign, EndOfLine, EndOfBlock, BlackIs1,
19
+ * DamagedRowsBeforeError) and decodes through
20
+ * `pdfCcittFaxDecoder`, a full ITU-T T.4 (Group 3, 1-D and
21
+ * mixed 1-D/2-D) and T.6 (Group 4) decoder.
22
+ * - DCTDecode — passthrough (JPEG bytes are returned as-is).
23
+ * - JPXDecode — passthrough (JPEG 2000 bytes are returned as-is).
24
+ *
25
+ * @module pdf/extra/legacy-deprecated-filters
26
+ */
27
+
28
+ import { pdfErrors } from '../errors.js';
29
+ import { lzw } from '@awacloud/fw/io/compress/lzw.js';
30
+ import { pdfCcittFaxDecoder } from './ccitt-fax-decoder.js';
31
+
32
+ export const pdfLegacyDeprecatedFilters = {
33
+ name: 'pdfLegacyDeprecatedFilters',
34
+ dependencies: ['pdfErrors', 'lzw', 'pdfCcittFaxDecoder'],
35
+ deps: [pdfErrors, lzw, pdfCcittFaxDecoder],
36
+
37
+ factory(errors, lzwImpl, ccittImpl) {
38
+ const { ParseError } = errors;
39
+ const CCITT_KEYS = new Set([
40
+ 'K', 'EndOfLine', 'EncodedByteAlign', 'Columns', 'Rows',
41
+ 'EndOfBlock', 'BlackIs1', 'DamagedRowsBeforeError'
42
+ ]);
43
+ if (!lzwImpl || typeof lzwImpl.decode !== 'function'
44
+ || typeof lzwImpl.encode !== 'function') {
45
+ throw new ParseError('pdf/filters/missing-lzw',
46
+ 'legacy-deprecated-filters requires the @awacloud/fw lzw factory output');
47
+ }
48
+ if (!ccittImpl || typeof ccittImpl.decode !== 'function') {
49
+ throw new ParseError('pdf/filters/missing-ccitt',
50
+ 'legacy-deprecated-filters requires the pdfCcittFaxDecoder factory output');
51
+ }
52
+
53
+ function asBytes(x) {
54
+ if (x instanceof Uint8Array) return x;
55
+ throw new ParseError('pdf/filters/bad-input',
56
+ 'filter input must be Uint8Array',
57
+ { context: { type: typeof x } });
58
+ }
59
+
60
+ function lzwDecode(bytes, decodeParms) {
61
+ asBytes(bytes);
62
+ const earlyChange = decodeParms && decodeParms.EarlyChange != null
63
+ ? decodeParms.EarlyChange : 1;
64
+ if (earlyChange !== 0 && earlyChange !== 1) {
65
+ throw new ParseError('pdf/lzw/bad-earlychange',
66
+ '/EarlyChange must be 0 or 1',
67
+ { context: { value: earlyChange } });
68
+ }
69
+ try {
70
+ return lzwImpl.decode(bytes, {
71
+ bigEndian: true,
72
+ minCodeBits: 8,
73
+ maxBits: 12,
74
+ useClearEnd: true
75
+ });
76
+ } catch (cause) {
77
+ throw new ParseError('pdf/lzw/decode-failed',
78
+ 'LZWDecode failed: ' + cause.message,
79
+ { cause });
80
+ }
81
+ }
82
+
83
+ function lzwEncode(bytes) {
84
+ asBytes(bytes);
85
+ try {
86
+ return lzwImpl.encode(bytes, {
87
+ bigEndian: true,
88
+ minCodeBits: 8,
89
+ maxBits: 12,
90
+ useClearEnd: true
91
+ });
92
+ } catch (cause) {
93
+ throw new ParseError('pdf/lzw/encode-failed',
94
+ 'LZWEncode failed: ' + cause.message,
95
+ { cause });
96
+ }
97
+ }
98
+
99
+ function validateCcittParms(parms) {
100
+ if (parms == null) return { K: 0 };
101
+ if (typeof parms !== 'object') {
102
+ throw new ParseError('pdf/ccitt/bad-parms',
103
+ 'CCITT DecodeParms must be an object');
104
+ }
105
+ for (const k of Object.keys(parms)) {
106
+ if (!CCITT_KEYS.has(k)) {
107
+ throw new ParseError('pdf/ccitt/unknown-parm',
108
+ 'unknown CCITT DecodeParm',
109
+ { context: { key: k } });
110
+ }
111
+ }
112
+ const out = {
113
+ K: parms.K != null ? parms.K | 0 : 0,
114
+ EndOfLine: !!parms.EndOfLine,
115
+ EncodedByteAlign: !!parms.EncodedByteAlign,
116
+ Columns: parms.Columns != null ? parms.Columns | 0 : 1728,
117
+ Rows: parms.Rows != null ? parms.Rows | 0 : 0,
118
+ EndOfBlock: parms.EndOfBlock !== false,
119
+ BlackIs1: !!parms.BlackIs1,
120
+ DamagedRowsBeforeError: parms.DamagedRowsBeforeError | 0
121
+ };
122
+ return out;
123
+ }
124
+
125
+ function ccittFaxDecode(bytes, decodeParms) {
126
+ asBytes(bytes);
127
+ const parms = validateCcittParms(decodeParms);
128
+ // Real T.4/T.6 codec is provided by pdfCcittFaxDecoder.
129
+ // K < 0 : Group 4 (T.6), pure 2D.
130
+ // K == 0 : Group 3 1D (T.4 baseline).
131
+ // K > 0 : Group 3 mixed — each block of K rows starts with
132
+ // a 1D-coded row followed by up to K-1 2D-coded rows.
133
+ return ccittImpl.decode(bytes, parms);
134
+ }
135
+
136
+ function dctDecode(bytes) {
137
+ // JPEG SOI marker check, then passthrough.
138
+ asBytes(bytes);
139
+ if (bytes.length >= 2 && (bytes[0] !== 0xFF || bytes[1] !== 0xD8)) {
140
+ throw new ParseError('pdf/dct/bad-soi',
141
+ 'DCTDecode input missing JPEG SOI marker');
142
+ }
143
+ return bytes;
144
+ }
145
+
146
+ function jpxDecode(bytes) {
147
+ asBytes(bytes);
148
+ // Either codestream (SOC FF4F) or JP2 box stream (sig
149
+ // 0000000C 6A50 2020 0D0A 870A). Accept both.
150
+ if (bytes.length >= 2) {
151
+ if (bytes[0] === 0xFF && bytes[1] === 0x4F) return bytes;
152
+ if (bytes.length >= 12 &&
153
+ bytes[4] === 0x6A && bytes[5] === 0x50 &&
154
+ bytes[6] === 0x20 && bytes[7] === 0x20) return bytes;
155
+ }
156
+ throw new ParseError('pdf/jpx/bad-signature',
157
+ 'JPXDecode input does not look like JPEG 2000');
158
+ }
159
+
160
+ return {
161
+ lzwDecode, lzwEncode,
162
+ validateCcittParms, ccittFaxDecode,
163
+ dctDecode, jpxDecode,
164
+ CCITT_KEYS
165
+ };
166
+ }
167
+ };
@@ -0,0 +1,235 @@
1
+ // Copyright (c) 2026 AwaCloud SAS
2
+ // Author: Matthieu Bouilloux
3
+ // SPDX-License-Identifier: AGPL-3.0-only
4
+ // Dual-licensed; see the NOTICE file for licensing and any additional terms.
5
+
6
+ /**
7
+ * @fileoverview Opt-in: LEGACY RC4 read support.
8
+ *
9
+ * Implements the RC4 stream cipher locally (per the Wikipedia
10
+ * pseudo-code) and exposes the read side of the PDF v2/v3 Standard
11
+ * Security Handler:
12
+ *
13
+ * - 40-bit RC4 (revision 2, V=1) and 128-bit RC4 (revision 3, V=2)
14
+ * - password validation against /O and /U using PDF 1.7 §7.6.3.3-4
15
+ * - per-object key derivation (file-key || objectId/MD5) for /Encrypt
16
+ * V <= 2.
17
+ *
18
+ * PDF 2.0 forbids RC4 on write; this module is intentionally read-only.
19
+ *
20
+ * @module pdf/extra/legacy-rc4-read
21
+ */
22
+
23
+ import { pdfErrors } from '../errors.js';
24
+
25
+ export const pdfLegacyRc4Read = {
26
+ name: 'pdfLegacyRc4Read',
27
+ dependencies: ['pdfErrors'],
28
+ deps: [pdfErrors],
29
+
30
+ factory(errors) {
31
+ const { EncryptionError } = errors;
32
+ const PASSWORD_PADDING = new Uint8Array([
33
+ 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41,
34
+ 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
35
+ 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80,
36
+ 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A
37
+ ]);
38
+ // ---- RC4 ----
39
+ function rc4(key, data) {
40
+ if (!(key instanceof Uint8Array) || !(data instanceof Uint8Array)) {
41
+ throw new EncryptionError('pdf/rc4/bad-input',
42
+ 'RC4 expects Uint8Array key and data');
43
+ }
44
+ const S = new Uint8Array(256);
45
+ for (let i = 0; i < 256; i++) S[i] = i;
46
+ let j = 0;
47
+ for (let i = 0; i < 256; i++) {
48
+ j = (j + S[i] + key[i % key.length]) & 0xFF;
49
+ const t = S[i]; S[i] = S[j]; S[j] = t;
50
+ }
51
+ const out = new Uint8Array(data.length);
52
+ let a = 0, b = 0;
53
+ for (let n = 0; n < data.length; n++) {
54
+ a = (a + 1) & 0xFF;
55
+ b = (b + S[a]) & 0xFF;
56
+ const t = S[a]; S[a] = S[b]; S[b] = t;
57
+ const k = S[(S[a] + S[b]) & 0xFF];
58
+ out[n] = data[n] ^ k;
59
+ }
60
+ return out;
61
+ }
62
+
63
+ // Lightweight MD5 — needed for PDF Standard SH password algos.
64
+ // Public-domain RFC 1321 reference implementation, compacted.
65
+ function md5(bytes) {
66
+ if (!(bytes instanceof Uint8Array)) {
67
+ throw new EncryptionError('pdf/md5/bad-input',
68
+ 'md5 expects Uint8Array');
69
+ }
70
+ function rol(x, n) { return ((x << n) | (x >>> (32 - n))) | 0; }
71
+ const r = [
72
+ 7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,
73
+ 5, 9,14,20,5, 9,14,20,5, 9,14,20,5, 9,14,20,
74
+ 4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,
75
+ 6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21
76
+ ];
77
+ const k = [
78
+ 0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,
79
+ 0x698098d8,0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,0xa679438e,0x49b40821,
80
+ 0xf61e2562,0xc040b340,0x265e5a51,0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
81
+ 0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905,0xfcefa3f8,0x676f02d9,0x8d2a4c8a,
82
+ 0xfffa3942,0x8771f681,0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60,0xbebfbc70,
83
+ 0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,
84
+ 0xf4292244,0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92,0xffeff47d,0x85845dd1,
85
+ 0x6fa87e4f,0xfe2ce6e0,0xa3014314,0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391
86
+ ];
87
+ const msgLenBytes = bytes.length;
88
+ const withOne = new Uint8Array(((msgLenBytes + 8) >> 6 << 6) + 64);
89
+ withOne.set(bytes);
90
+ withOne[msgLenBytes] = 0x80;
91
+ const bitsLo = (msgLenBytes * 8) >>> 0;
92
+ const bitsHi = Math.floor((msgLenBytes * 8) / 0x100000000) >>> 0;
93
+ const dv = new DataView(withOne.buffer);
94
+ dv.setUint32(withOne.length - 8, bitsLo, true);
95
+ dv.setUint32(withOne.length - 4, bitsHi, true);
96
+ let a0 = 0x67452301 | 0, b0 = 0xefcdab89 | 0, c0 = 0x98badcfe | 0, d0 = 0x10325476 | 0;
97
+ for (let off = 0; off < withOne.length; off += 64) {
98
+ const M = new Array(16);
99
+ for (let i = 0; i < 16; i++) M[i] = dv.getUint32(off + i * 4, true);
100
+ let A = a0, B = b0, C = c0, D = d0;
101
+ for (let i = 0; i < 64; i++) {
102
+ let F, g;
103
+ if (i < 16) { F = (B & C) | ((~B) & D); g = i; }
104
+ else if (i < 32) { F = (D & B) | ((~D) & C); g = (5 * i + 1) % 16; }
105
+ else if (i < 48) { F = B ^ C ^ D; g = (3 * i + 5) % 16; }
106
+ else { F = C ^ (B | (~D)); g = (7 * i) % 16; }
107
+ const t = (A + F + k[i] + M[g]) | 0;
108
+ A = D; D = C; C = B; B = (B + rol(t, r[i])) | 0;
109
+ }
110
+ a0 = (a0 + A) | 0; b0 = (b0 + B) | 0; c0 = (c0 + C) | 0; d0 = (d0 + D) | 0;
111
+ }
112
+ const out = new Uint8Array(16);
113
+ const ov = new DataView(out.buffer);
114
+ ov.setUint32(0, a0, true);
115
+ ov.setUint32(4, b0, true);
116
+ ov.setUint32(8, c0, true);
117
+ ov.setUint32(12, d0, true);
118
+ return out;
119
+ }
120
+
121
+ function padPassword(pw) {
122
+ if (typeof pw === 'string') pw = new TextEncoder().encode(pw);
123
+ if (!(pw instanceof Uint8Array)) {
124
+ throw new EncryptionError('pdf/rc4/bad-password',
125
+ 'password must be string or Uint8Array');
126
+ }
127
+ const out = new Uint8Array(32);
128
+ const take = Math.min(32, pw.length);
129
+ out.set(pw.subarray(0, take));
130
+ out.set(PASSWORD_PADDING.subarray(0, 32 - take), take);
131
+ return out;
132
+ }
133
+
134
+ // §7.6.3.3 Algorithm 2 — compute file encryption key.
135
+ function computeFileKey(pw, params) {
136
+ const { O, P, idFirst, revision, keyLength, encryptMetadata } = params;
137
+ if (!(O instanceof Uint8Array) || O.length !== 32) {
138
+ throw new EncryptionError('pdf/rc4/bad-O',
139
+ '/O must be a 32-byte Uint8Array');
140
+ }
141
+ if (!(idFirst instanceof Uint8Array)) {
142
+ throw new EncryptionError('pdf/rc4/bad-id',
143
+ 'first /ID element required');
144
+ }
145
+ const padded = padPassword(pw);
146
+ // padded || O || P (4 bytes LE) || ID || (R>=4 ? FFFFFFFF if !encryptMetadata : nothing)
147
+ const extra = revision >= 4 && encryptMetadata === false ? 4 : 0;
148
+ const buf = new Uint8Array(32 + 32 + 4 + idFirst.length + extra);
149
+ let p = 0;
150
+ buf.set(padded, p); p += 32;
151
+ buf.set(O, p); p += 32;
152
+ const dv = new DataView(buf.buffer);
153
+ dv.setInt32(p, P | 0, true); p += 4;
154
+ buf.set(idFirst, p); p += idFirst.length;
155
+ if (extra) { buf[p++] = 0xFF; buf[p++] = 0xFF; buf[p++] = 0xFF; buf[p] = 0xFF; }
156
+ let h = md5(buf);
157
+ const nBytes = keyLength / 8;
158
+ if (revision >= 3) {
159
+ for (let i = 0; i < 50; i++) h = md5(h.subarray(0, nBytes));
160
+ }
161
+ return h.subarray(0, nBytes);
162
+ }
163
+
164
+ // §7.6.3.4 Algorithm 5 — compute /U for revision >= 3.
165
+ function computeU(fileKey, idFirst, revision) {
166
+ if (revision === 2) {
167
+ return rc4(fileKey, PASSWORD_PADDING);
168
+ }
169
+ const seed = new Uint8Array(PASSWORD_PADDING.length + idFirst.length);
170
+ seed.set(PASSWORD_PADDING, 0);
171
+ seed.set(idFirst, PASSWORD_PADDING.length);
172
+ let h = md5(seed);
173
+ h = rc4(fileKey, h);
174
+ for (let i = 1; i <= 19; i++) {
175
+ const xk = new Uint8Array(fileKey.length);
176
+ for (let n = 0; n < fileKey.length; n++) xk[n] = fileKey[n] ^ i;
177
+ h = rc4(xk, h);
178
+ }
179
+ // Pad to 32 bytes (arbitrary content).
180
+ const out = new Uint8Array(32);
181
+ out.set(h, 0);
182
+ return out;
183
+ }
184
+
185
+ function bytesEqual(a, b, n) {
186
+ const len = n === undefined ? Math.min(a.length, b.length) : n;
187
+ for (let i = 0; i < len; i++) if (a[i] !== b[i]) return false;
188
+ return true;
189
+ }
190
+
191
+ function validateUserPassword(pw, params) {
192
+ const fk = computeFileKey(pw, params);
193
+ const u = computeU(fk, params.idFirst, params.revision);
194
+ const ok = params.revision === 2
195
+ ? bytesEqual(u, params.U, 32)
196
+ : bytesEqual(u, params.U, 16);
197
+ return ok ? { ok: true, fileKey: fk } : { ok: false };
198
+ }
199
+
200
+ // Per-object key for V <= 2, R 2/3: append the object id and gen
201
+ // (low 3 + low 2 bytes LE) to the file key, MD5, truncate.
202
+ function objectKey(fileKey, objNum, gen) {
203
+ const buf = new Uint8Array(fileKey.length + 5);
204
+ buf.set(fileKey, 0);
205
+ buf[fileKey.length + 0] = objNum & 0xFF;
206
+ buf[fileKey.length + 1] = (objNum >> 8) & 0xFF;
207
+ buf[fileKey.length + 2] = (objNum >> 16) & 0xFF;
208
+ buf[fileKey.length + 3] = gen & 0xFF;
209
+ buf[fileKey.length + 4] = (gen >> 8) & 0xFF;
210
+ const h = md5(buf);
211
+ return h.subarray(0, Math.min(fileKey.length + 5, 16));
212
+ }
213
+
214
+ function decryptString(fileKey, objNum, gen, bytes) {
215
+ return rc4(objectKey(fileKey, objNum, gen), bytes);
216
+ }
217
+
218
+ function decryptStream(fileKey, objNum, gen, bytes) {
219
+ return rc4(objectKey(fileKey, objNum, gen), bytes);
220
+ }
221
+
222
+ return {
223
+ rc4,
224
+ md5,
225
+ padPassword,
226
+ computeFileKey,
227
+ computeU,
228
+ validateUserPassword,
229
+ objectKey,
230
+ decryptString,
231
+ decryptStream,
232
+ PASSWORD_PADDING
233
+ };
234
+ }
235
+ };