pdf-parser.js 5.2.27 → 5.2.29

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 (145) hide show
  1. package/dist/{afm-widths-BYiX2L9D.cjs → afm-widths-BHD2HZIL.cjs} +553 -642
  2. package/dist/{afm-widths-blUDDjlI.js → afm-widths-B_IGbGky.js} +553 -642
  3. package/dist/afm-widths.cjs +1 -1
  4. package/dist/afm-widths.js +1 -1
  5. package/dist/annotations.cjs +5 -3
  6. package/dist/annotations.js +5 -3
  7. package/dist/builtin-encoding.cjs +17 -6
  8. package/dist/builtin-encoding.js +17 -6
  9. package/dist/bytes/crc32.cjs +5 -3
  10. package/dist/bytes/crc32.js +5 -3
  11. package/dist/bytes/flate.cjs +0 -1
  12. package/dist/bytes/flate.js +0 -1
  13. package/dist/cff-bounds.cjs +42 -24
  14. package/dist/cff-bounds.js +42 -24
  15. package/dist/cff.cjs +13 -6
  16. package/dist/cff.js +13 -6
  17. package/dist/cmap-table.cjs +54 -26
  18. package/dist/cmap-table.js +54 -26
  19. package/dist/cmap.cjs +5 -3
  20. package/dist/cmap.js +5 -3
  21. package/dist/content-write.cjs +3 -2
  22. package/dist/content-write.js +3 -2
  23. package/dist/crypto/aes.cjs +76 -55
  24. package/dist/crypto/aes.js +76 -55
  25. package/dist/crypto/md5.cjs +345 -149
  26. package/dist/crypto/md5.js +345 -149
  27. package/dist/crypto/rc4.cjs +5 -4
  28. package/dist/crypto/rc4.js +5 -4
  29. package/dist/crypto/sha2.cjs +92 -43
  30. package/dist/crypto/sha2.js +92 -43
  31. package/dist/embedded-font.cjs +4 -2
  32. package/dist/embedded-font.js +4 -2
  33. package/dist/encoding.cjs +1 -1
  34. package/dist/encoding.js +1 -1
  35. package/dist/encrypt-write.cjs +25 -21
  36. package/dist/encrypt-write.js +25 -21
  37. package/dist/encrypt.cjs +43 -64
  38. package/dist/encrypt.js +43 -64
  39. package/dist/filters.cjs +40 -20
  40. package/dist/filters.js +40 -20
  41. package/dist/font-face.cjs +2 -1
  42. package/dist/font-face.js +2 -1
  43. package/dist/font-read.cjs +12 -6
  44. package/dist/font-read.js +12 -6
  45. package/dist/font-tables.cjs +55 -28
  46. package/dist/font-tables.js +55 -28
  47. package/dist/gdef-table.cjs +6 -3
  48. package/dist/gdef-table.js +6 -3
  49. package/dist/glyf-contours.cjs +3 -1
  50. package/dist/glyf-contours.js +3 -1
  51. package/dist/glyf.cjs +22 -11
  52. package/dist/glyf.js +22 -11
  53. package/dist/gpos-table.cjs +37 -19
  54. package/dist/gpos-table.js +37 -19
  55. package/dist/gsub-table.cjs +28 -17
  56. package/dist/gsub-table.js +28 -17
  57. package/dist/hmtx-table.cjs +5 -3
  58. package/dist/hmtx-table.js +5 -3
  59. package/dist/image/ccitt-encode.cjs +8 -5
  60. package/dist/image/ccitt-encode.js +8 -5
  61. package/dist/image/ccitt.cjs +801 -209
  62. package/dist/image/ccitt.js +801 -209
  63. package/dist/image/jbig2-arith.cjs +340 -306
  64. package/dist/image/jbig2-arith.js +340 -306
  65. package/dist/image/jbig2-bitmap.cjs +8 -5
  66. package/dist/image/jbig2-bitmap.js +8 -5
  67. package/dist/image/jbig2-generic.cjs +240 -69
  68. package/dist/image/jbig2-generic.js +240 -69
  69. package/dist/image/jbig2-text.cjs +4 -2
  70. package/dist/image/jbig2-text.js +4 -2
  71. package/dist/image/jbig2.cjs +69 -31
  72. package/dist/image/jbig2.js +69 -31
  73. package/dist/image/jp2-boxes.cjs +52 -27
  74. package/dist/image/jp2-boxes.js +52 -27
  75. package/dist/image/jpeg-info.cjs +29 -24
  76. package/dist/image/jpeg-info.js +29 -24
  77. package/dist/image/jpeg2000-codestream.cjs +44 -24
  78. package/dist/image/jpeg2000-codestream.js +44 -24
  79. package/dist/image/jpeg2000-dwt.cjs +2 -1
  80. package/dist/image/jpeg2000-dwt.js +2 -1
  81. package/dist/image/jpeg2000-t1.cjs +21 -11
  82. package/dist/image/jpeg2000-t1.js +21 -11
  83. package/dist/image/jpeg2000-t2.cjs +12 -5
  84. package/dist/image/jpeg2000-t2.js +12 -5
  85. package/dist/image/jpeg2000-tagtree.cjs +8 -4
  86. package/dist/image/jpeg2000-tagtree.js +8 -4
  87. package/dist/image/jpeg2000.cjs +11 -6
  88. package/dist/image/jpeg2000.js +11 -6
  89. package/dist/image/png-decode.cjs +62 -39
  90. package/dist/image/png-decode.d.cts +3 -2
  91. package/dist/image/png-decode.d.ts +3 -2
  92. package/dist/image/png-decode.js +62 -39
  93. package/dist/image/png-filter.cjs +23 -15
  94. package/dist/image/png-filter.js +23 -15
  95. package/dist/images-read.cjs +41 -36
  96. package/dist/images-read.js +41 -36
  97. package/dist/index.cjs +1 -1
  98. package/dist/index.js +1 -1
  99. package/dist/interpret.cjs +39 -24
  100. package/dist/interpret.js +39 -24
  101. package/dist/lexer.cjs +86 -51
  102. package/dist/lexer.js +86 -51
  103. package/dist/math-content-write.cjs +9 -5
  104. package/dist/math-content-write.js +9 -5
  105. package/dist/math-font-write.cjs +2 -1
  106. package/dist/math-font-write.js +2 -1
  107. package/dist/math-font.cjs +16 -7
  108. package/dist/math-font.js +16 -7
  109. package/dist/math-table.cjs +25 -13
  110. package/dist/math-table.js +25 -13
  111. package/dist/matrix.cjs +3 -2
  112. package/dist/matrix.js +3 -2
  113. package/dist/measure.cjs +1 -1
  114. package/dist/measure.js +1 -1
  115. package/dist/navigation.cjs +10 -5
  116. package/dist/navigation.js +10 -5
  117. package/dist/ot-layout-common.cjs +4 -2
  118. package/dist/ot-layout-common.js +4 -2
  119. package/dist/parse.cjs +7 -5
  120. package/dist/parse.js +7 -5
  121. package/dist/pdf-text.cjs +5 -2
  122. package/dist/pdf-text.js +5 -2
  123. package/dist/predictors.cjs +14 -9
  124. package/dist/predictors.js +14 -9
  125. package/dist/raster.cjs +30 -19
  126. package/dist/raster.js +30 -19
  127. package/dist/read.cjs +17 -8
  128. package/dist/read.js +17 -8
  129. package/dist/serialize.cjs +6 -2
  130. package/dist/serialize.js +6 -2
  131. package/dist/sfnt-subset.cjs +25 -13
  132. package/dist/sfnt-subset.js +25 -13
  133. package/dist/sfnt.cjs +20 -10
  134. package/dist/sfnt.js +20 -10
  135. package/dist/text-group.cjs +0 -1
  136. package/dist/text-group.js +0 -1
  137. package/dist/tounicode.cjs +4 -2
  138. package/dist/tounicode.js +4 -2
  139. package/dist/winansi.cjs +1 -1
  140. package/dist/winansi.js +1 -1
  141. package/dist/write.cjs +20 -12
  142. package/dist/write.js +20 -12
  143. package/dist/xref.cjs +5 -2
  144. package/dist/xref.js +5 -2
  145. package/package.json +2 -2
@@ -14,6 +14,8 @@ var PdfEncryptionError = class extends Error {
14
14
  };
15
15
  const RC4_128_KEY_BYTES = 16;
16
16
  const R6_PASSWORD_MAX_BYTES = 127;
17
+ const BITS_PER_BYTE = 8;
18
+ const MAX_PERMISSION_BITS = 32;
17
19
  const RESERVED_BIT_7_AND_8 = 192;
18
20
  const DEPRECATED_ALWAYS_ONE_BIT_10 = 512;
19
21
  const SCHEME_SPECS = {
@@ -42,11 +44,12 @@ const SCHEME_SPECS = {
42
44
  method: "aes"
43
45
  }
44
46
  };
47
+ const MAX_PRINTABLE_ASCII = 127;
45
48
  function legacyPasswordBytes(password) {
46
49
  const bytes = new Uint8Array(password.length);
47
50
  for (let i = 0; i < password.length; i++) {
48
51
  const code = password.charCodeAt(i);
49
- if (code > 127) throw new PdfEncryptionError(`password contains character code ${String(code)}, outside the printable-ASCII range this writer supports for the rc4-40/rc4-128/aes-128 schemes; use a plain-ASCII password, or the aes-256 scheme, whose revision-6 passwords are Unicode`);
52
+ if (code > MAX_PRINTABLE_ASCII) throw new PdfEncryptionError(`password contains character code ${String(code)}, outside the printable-ASCII range this writer supports for the rc4-40/rc4-128/aes-128 schemes; use a plain-ASCII password, or the aes-256 scheme, whose revision-6 passwords are Unicode`);
50
53
  bytes[i] = code;
51
54
  }
52
55
  return bytes;
@@ -65,15 +68,16 @@ function permissionsToP(permissions) {
65
68
  p |= DEPRECATED_ALWAYS_ONE_BIT_10;
66
69
  if (permissions?.assemble ?? true) p |= 1024;
67
70
  if (permissions?.printHighRes ?? true) p |= 2048;
68
- for (let bit = 13; bit <= 32; bit++) p |= 1 << bit - 1;
71
+ for (let bit = 13; bit <= MAX_PERMISSION_BITS; bit++) p |= 1 << bit - 1;
69
72
  return p;
70
73
  }
74
+ const ITERATED_REVISION_MIN = 3;
71
75
  function computeLegacyOwnerValue(ownerPassword, userPassword, revision, keyBytes) {
72
76
  let digest = md5(padOrTruncatePassword(ownerPassword));
73
- if (revision >= 3) for (let i = 0; i < 50; i++) digest = md5(digest);
77
+ if (revision >= ITERATED_REVISION_MIN) for (let i = 0; i < 50; i++) digest = md5(digest);
74
78
  const ownerKey = digest.subarray(0, keyBytes);
75
79
  let value = rc4(ownerKey, padOrTruncatePassword(userPassword));
76
- if (revision >= 3) for (let i = 1; i <= 19; i++) {
80
+ if (revision >= ITERATED_REVISION_MIN) for (let i = 1; i <= 19; i++) {
77
81
  const roundKey = Uint8Array.from(ownerKey, (byte) => byte ^ i);
78
82
  value = rc4(roundKey, value);
79
83
  }
@@ -124,9 +128,9 @@ function buildLegacyEncryptor(spec, options, fileId) {
124
128
  ["O", pdfHexString(owner)],
125
129
  ["U", pdfHexString(user)],
126
130
  ["P", pdfNum(p)],
127
- ["Length", pdfNum(spec.keyBytes * 8)]
131
+ ["Length", pdfNum(spec.keyBytes * BITS_PER_BYTE)]
128
132
  ]);
129
- if (spec.v === 4) {
133
+ if (spec.v === SCHEME_SPECS["aes-128"].v) {
130
134
  entries.set("CF", pdfDict({ StdCF: pdfDict({
131
135
  AuthEvent: pdfName("DocOpen"),
132
136
  CFM: pdfName("AESV2"),
@@ -165,21 +169,21 @@ function buildAes256Encryptor(spec, options) {
165
169
  ]);
166
170
  const ownerIntermediateKey = hardenedHash(ownerPassword, ownerKeySalt, u, spec.r);
167
171
  const oe = aesCbcEncrypt(ownerIntermediateKey, ZERO_IV, fileKey);
172
+ const PERMS_P_BYTES = 4;
173
+ const PERMS_SIGN_EXTENSION_BYTES = new Uint8Array(PERMS_P_BYTES).fill(255);
174
+ const PERMS_METADATA_FLAG_OFFSET = 8;
175
+ const ENCRYPT_METADATA_TRUE_ASCII = 84;
176
+ const ENCRYPT_METADATA_FALSE_ASCII = 70;
177
+ const ADB_MARKER_OFFSET = 9;
178
+ const ADB_MARKER_BYTES = Uint8Array.from("adb", (c) => c.charCodeAt(0));
179
+ const PERMS_RANDOM_BYTES = 4;
180
+ const PERMS_RANDOM_OFFSET = ADB_MARKER_OFFSET + ADB_MARKER_BYTES.length;
168
181
  const permsBlock = /* @__PURE__ */ new Uint8Array(16);
169
182
  permsBlock.set(permissionsBytes(p), 0);
170
- permsBlock.set([
171
- 255,
172
- 255,
173
- 255,
174
- 255
175
- ], 4);
176
- permsBlock[8] = encryptMetadata ? 84 : 70;
177
- permsBlock.set([
178
- 97,
179
- 100,
180
- 98
181
- ], 9);
182
- permsBlock.set(randomBytes(4), 12);
183
+ permsBlock.set(PERMS_SIGN_EXTENSION_BYTES, PERMS_P_BYTES);
184
+ permsBlock[PERMS_METADATA_FLAG_OFFSET] = encryptMetadata ? ENCRYPT_METADATA_TRUE_ASCII : ENCRYPT_METADATA_FALSE_ASCII;
185
+ permsBlock.set(ADB_MARKER_BYTES, ADB_MARKER_OFFSET);
186
+ permsBlock.set(randomBytes(PERMS_RANDOM_BYTES), PERMS_RANDOM_OFFSET);
183
187
  const perms = aesCbcEncrypt(fileKey, ZERO_IV, permsBlock);
184
188
  const entries = /* @__PURE__ */ new Map([
185
189
  ["Filter", pdfName("Standard")],
@@ -191,7 +195,7 @@ function buildAes256Encryptor(spec, options) {
191
195
  ["UE", pdfHexString(ue)],
192
196
  ["P", pdfNum(p)],
193
197
  ["Perms", pdfHexString(perms)],
194
- ["Length", pdfNum(spec.keyBytes * 8)],
198
+ ["Length", pdfNum(spec.keyBytes * BITS_PER_BYTE)],
195
199
  ["CF", pdfDict({ StdCF: pdfDict({
196
200
  AuthEvent: pdfName("DocOpen"),
197
201
  CFM: pdfName("AESV3"),
@@ -206,7 +210,7 @@ function buildAes256Encryptor(spec, options) {
206
210
  function createStandardEncryptor(options, fileId) {
207
211
  const scheme = options.scheme ?? "aes-256";
208
212
  const spec = SCHEME_SPECS[scheme];
209
- return spec.v === 5 ? buildAes256Encryptor(spec, options) : buildLegacyEncryptor(spec, options, fileId);
213
+ return spec.v === SCHEME_SPECS["aes-256"].v ? buildAes256Encryptor(spec, options) : buildLegacyEncryptor(spec, options, fileId);
210
214
  }
211
215
  function encryptIndirectObject(value, num, gen, encryptor) {
212
216
  if (value.kind === "string") return {
package/dist/encrypt.cjs CHANGED
@@ -7,46 +7,18 @@ const require_crypto_md5 = require("./crypto/md5.cjs");
7
7
  const require_crypto_rc4 = require("./crypto/rc4.cjs");
8
8
  const require_crypto_sha2 = require("./crypto/sha2.cjs");
9
9
  //#region src/encrypt.ts
10
- const PASSWORD_PADDING = new Uint8Array([
11
- 40,
12
- 191,
13
- 78,
14
- 94,
15
- 78,
16
- 117,
17
- 138,
18
- 65,
19
- 100,
20
- 0,
21
- 78,
22
- 86,
23
- 255,
24
- 250,
25
- 1,
26
- 8,
27
- 46,
28
- 46,
29
- 0,
30
- 182,
31
- 208,
32
- 104,
33
- 62,
34
- 128,
35
- 47,
36
- 12,
37
- 169,
38
- 254,
39
- 100,
40
- 83,
41
- 105,
42
- 122
43
- ]);
44
- const AES_OBJECT_KEY_SALT = new Uint8Array([
45
- 115,
46
- 65,
47
- 108,
48
- 84
49
- ]);
10
+ const HEX_RADIX = 16;
11
+ const HEX_CHARS_PER_BYTE = 2;
12
+ function bytesFromHex(hex) {
13
+ const bytes = new Uint8Array(hex.length / HEX_CHARS_PER_BYTE);
14
+ for (let i = 0; i < bytes.length; i++) {
15
+ const start = i * HEX_CHARS_PER_BYTE;
16
+ bytes[i] = parseInt(hex.substring(start, start + HEX_CHARS_PER_BYTE), HEX_RADIX);
17
+ }
18
+ return bytes;
19
+ }
20
+ const PASSWORD_PADDING = bytesFromHex("28bf4e5e4e758a4164004e56fffa01082e2e00b6d0683e802f0ca9fe6453697a");
21
+ const AES_OBJECT_KEY_SALT = new TextEncoder().encode("sAlT");
50
22
  const MD5_DIGEST_BYTES = 16;
51
23
  const OBJECT_KEY_EXTRA_BYTES = 5;
52
24
  const LEGACY_KEY_ITERATIONS = 50;
@@ -60,6 +32,9 @@ const RC4_40_KEY_BYTES = 5;
60
32
  const AESV2_KEY_BYTES = 16;
61
33
  const DEFAULT_KEY_BITS = 40;
62
34
  const BITS_PER_BYTE = 8;
35
+ const BYTE_MASK = 255;
36
+ const UINT32_BYTE_2_SHIFT = 16;
37
+ const UINT32_BYTE_3_SHIFT = 24;
63
38
  const MAX_RC4_KEY_BYTES = 16;
64
39
  const ZERO_IV = /* @__PURE__ */ new Uint8Array(16);
65
40
  function requireStringBytes(dict, key) {
@@ -74,10 +49,10 @@ function bytesEqual(a, b, length) {
74
49
  function permissionsBytes(p) {
75
50
  const unsigned = Math.trunc(p) >>> 0;
76
51
  return new Uint8Array([
77
- unsigned & 255,
78
- unsigned >>> 8 & 255,
79
- unsigned >>> 16 & 255,
80
- unsigned >>> 24 & 255
52
+ unsigned & BYTE_MASK,
53
+ unsigned >>> BITS_PER_BYTE & BYTE_MASK,
54
+ unsigned >>> UINT32_BYTE_2_SHIFT & BYTE_MASK,
55
+ unsigned >>> UINT32_BYTE_3_SHIFT & BYTE_MASK
81
56
  ]);
82
57
  }
83
58
  function padOrTruncatePassword(password) {
@@ -85,6 +60,9 @@ function padOrTruncatePassword(password) {
85
60
  padded.set(password.subarray(0, PASSWORD_PADDING.length));
86
61
  return padded;
87
62
  }
63
+ const METADATA_FLAG_MIN_REVISION = 4;
64
+ const METADATA_NOT_ENCRYPTED_FLAG_BYTES = 4;
65
+ const ITERATED_REVISION_MIN = 3;
88
66
  function computeLegacyFileKeyFromPaddedPassword(paddedPassword, owner, permissions, fileId, revision, keyBytes, encryptMetadata) {
89
67
  const parts = [
90
68
  paddedPassword,
@@ -92,14 +70,9 @@ function computeLegacyFileKeyFromPaddedPassword(paddedPassword, owner, permissio
92
70
  permissionsBytes(permissions),
93
71
  fileId
94
72
  ];
95
- if (revision >= 4 && !encryptMetadata) parts.push(new Uint8Array([
96
- 255,
97
- 255,
98
- 255,
99
- 255
100
- ]));
73
+ if (revision >= METADATA_FLAG_MIN_REVISION && !encryptMetadata) parts.push(new Uint8Array(METADATA_NOT_ENCRYPTED_FLAG_BYTES).fill(BYTE_MASK));
101
74
  let digest = require_crypto_md5.md5(require_bytes_writer.concatBytes(parts));
102
- if (revision >= 3) for (let i = 0; i < 50; i++) digest = require_crypto_md5.md5(digest.subarray(0, keyBytes));
75
+ if (revision >= ITERATED_REVISION_MIN) for (let i = 0; i < 50; i++) digest = require_crypto_md5.md5(digest.subarray(0, keyBytes));
103
76
  return digest.subarray(0, keyBytes);
104
77
  }
105
78
  function computeLegacyFileKey(encryptDict, fileId, revision, keyBytes, encryptMetadata) {
@@ -120,13 +93,16 @@ function legacyUserPasswordVerifies(encryptDict, fileId, revision, fileKey) {
120
93
  const storedUser = requireStringBytes(encryptDict, "U");
121
94
  return bytesEqual(legacyUserValueCore(fileKey, fileId, revision), storedUser, revision === 2 ? PASSWORD_PADDING.length : 16);
122
95
  }
96
+ const HARDENED_HASH_SHA256_ONLY_REVISION = 5;
97
+ const STANDARD_AESV3_REVISION = 6;
98
+ const HASH_SELECTOR_MODULUS = 3;
123
99
  function hardenedHash(password, salt, userData, revision) {
124
100
  let k = require_crypto_sha2.sha256(require_bytes_writer.concatBytes([
125
101
  password,
126
102
  salt,
127
103
  userData
128
104
  ]));
129
- if (revision === 5) return k;
105
+ if (revision === HARDENED_HASH_SHA256_ONLY_REVISION) return k;
130
106
  let e;
131
107
  let round = 0;
132
108
  do {
@@ -140,7 +116,7 @@ function hardenedHash(password, salt, userData, revision) {
140
116
  e = require_crypto_aes.aesCbcEncrypt(k.subarray(0, 16), k.subarray(16, 32), k1);
141
117
  let sum = 0;
142
118
  for (let i = 0; i < 16; i++) sum += e[i];
143
- const selector = sum % 3;
119
+ const selector = sum % HASH_SELECTOR_MODULUS;
144
120
  k = selector === 0 ? require_crypto_sha2.sha256(e) : selector === 1 ? require_crypto_sha2.sha384(e) : require_crypto_sha2.sha512(e);
145
121
  round++;
146
122
  } while (round < R6_MINIMUM_ROUNDS || e[e.length - 1] > round - R6_TERMINATION_MARGIN);
@@ -180,14 +156,17 @@ function cryptFilterKeyBytes(encryptDict, declaredBits) {
180
156
  if (declared === void 0) return Math.floor(declaredBits / BITS_PER_BYTE);
181
157
  return declared >= DEFAULT_KEY_BITS ? Math.floor(declared / BITS_PER_BYTE) : declared;
182
158
  }
159
+ const V5_HANDLER_VERSION = 5;
160
+ const V4_HANDLER_VERSION = 4;
161
+ const LEGACY_MAX_REVISION = 4;
183
162
  function setUpHandler(encryptDict, fileId) {
184
163
  const version = require_objects.asNumber(require_objects.dictGet(encryptDict, "V")) ?? 0;
185
164
  const revision = require_objects.asNumber(require_objects.dictGet(encryptDict, "R")) ?? 0;
186
165
  const declaredBits = require_objects.asNumber(require_objects.dictGet(encryptDict, "Length")) ?? DEFAULT_KEY_BITS;
187
166
  const encryptMetadataEntry = require_objects.dictGet(encryptDict, "EncryptMetadata");
188
167
  const encryptMetadata = encryptMetadataEntry?.kind === "bool" ? encryptMetadataEntry.value : true;
189
- if (version === 5) {
190
- if (revision !== 5 && revision !== 6) throw new require_diagnostics.PdfEncryptedError(`/V 5 encryption with an unsupported revision /R ${String(revision)}`);
168
+ if (version === V5_HANDLER_VERSION) {
169
+ if (revision !== HARDENED_HASH_SHA256_ONLY_REVISION && revision !== STANDARD_AESV3_REVISION) throw new require_diagnostics.PdfEncryptedError(`/V 5 encryption with an unsupported revision /R ${String(revision)}`);
191
170
  return {
192
171
  fileKey: computeAesV3FileKey(encryptDict, revision),
193
172
  streamMethod: methodFromCryptFilterName(encryptDict, "StmF"),
@@ -196,10 +175,10 @@ function setUpHandler(encryptDict, fileId) {
196
175
  encryptMetadata
197
176
  };
198
177
  }
199
- if (version !== 1 && version !== 2 && version !== 4) throw new require_diagnostics.PdfEncryptedError(`unsupported standard security handler version /V ${String(version)}`);
200
- if (revision < 2 || revision > 4) throw new require_diagnostics.PdfEncryptedError(`unsupported standard security handler revision /R ${String(revision)}`);
201
- const streamMethod = version === 4 ? methodFromCryptFilterName(encryptDict, "StmF") : "rc4";
202
- const stringMethod = version === 4 ? methodFromCryptFilterName(encryptDict, "StrF") : "rc4";
178
+ if (version !== 1 && version !== 2 && version !== V4_HANDLER_VERSION) throw new require_diagnostics.PdfEncryptedError(`unsupported standard security handler version /V ${String(version)}`);
179
+ if (revision < 2 || revision > LEGACY_MAX_REVISION) throw new require_diagnostics.PdfEncryptedError(`unsupported standard security handler revision /R ${String(revision)}`);
180
+ const streamMethod = version === V4_HANDLER_VERSION ? methodFromCryptFilterName(encryptDict, "StmF") : "rc4";
181
+ const stringMethod = version === V4_HANDLER_VERSION ? methodFromCryptFilterName(encryptDict, "StrF") : "rc4";
203
182
  const keyBytes = version === 1 ? 5 : version === 2 ? Math.floor(declaredBits / BITS_PER_BYTE) : cryptFilterKeyBytes(encryptDict, declaredBits);
204
183
  if (keyBytes < 5 || keyBytes > MAX_RC4_KEY_BYTES) throw new require_diagnostics.PdfEncryptedError(`the /Encrypt dictionary declares a ${String(keyBytes * BITS_PER_BYTE)}-bit file key, outside the 40-128 bit range /V ${String(version)} permits`);
205
184
  const fileKey = computeLegacyFileKey(encryptDict, fileId, revision, keyBytes, encryptMetadata);
@@ -214,11 +193,11 @@ function setUpHandler(encryptDict, fileId) {
214
193
  }
215
194
  function objectKey(fileKey, num, gen, method) {
216
195
  const extra = new Uint8Array(OBJECT_KEY_EXTRA_BYTES);
217
- extra[0] = num & 255;
218
- extra[1] = num >>> 8 & 255;
219
- extra[2] = num >>> 16 & 255;
220
- extra[3] = gen & 255;
221
- extra[4] = gen >>> 8 & 255;
196
+ extra[0] = num & BYTE_MASK;
197
+ extra[1] = num >>> BITS_PER_BYTE & BYTE_MASK;
198
+ extra[2] = num >>> UINT32_BYTE_2_SHIFT & BYTE_MASK;
199
+ extra[3] = gen & BYTE_MASK;
200
+ extra[4] = gen >>> BITS_PER_BYTE & BYTE_MASK;
222
201
  return require_crypto_md5.md5(require_bytes_writer.concatBytes(method === "aes" ? [
223
202
  fileKey,
224
203
  extra,
package/dist/encrypt.js CHANGED
@@ -6,46 +6,18 @@ import { md5 } from "./crypto/md5.js";
6
6
  import { rc4 } from "./crypto/rc4.js";
7
7
  import { sha256, sha384, sha512 } from "./crypto/sha2.js";
8
8
  //#region src/encrypt.ts
9
- const PASSWORD_PADDING = new Uint8Array([
10
- 40,
11
- 191,
12
- 78,
13
- 94,
14
- 78,
15
- 117,
16
- 138,
17
- 65,
18
- 100,
19
- 0,
20
- 78,
21
- 86,
22
- 255,
23
- 250,
24
- 1,
25
- 8,
26
- 46,
27
- 46,
28
- 0,
29
- 182,
30
- 208,
31
- 104,
32
- 62,
33
- 128,
34
- 47,
35
- 12,
36
- 169,
37
- 254,
38
- 100,
39
- 83,
40
- 105,
41
- 122
42
- ]);
43
- const AES_OBJECT_KEY_SALT = new Uint8Array([
44
- 115,
45
- 65,
46
- 108,
47
- 84
48
- ]);
9
+ const HEX_RADIX = 16;
10
+ const HEX_CHARS_PER_BYTE = 2;
11
+ function bytesFromHex(hex) {
12
+ const bytes = new Uint8Array(hex.length / HEX_CHARS_PER_BYTE);
13
+ for (let i = 0; i < bytes.length; i++) {
14
+ const start = i * HEX_CHARS_PER_BYTE;
15
+ bytes[i] = parseInt(hex.substring(start, start + HEX_CHARS_PER_BYTE), HEX_RADIX);
16
+ }
17
+ return bytes;
18
+ }
19
+ const PASSWORD_PADDING = bytesFromHex("28bf4e5e4e758a4164004e56fffa01082e2e00b6d0683e802f0ca9fe6453697a");
20
+ const AES_OBJECT_KEY_SALT = new TextEncoder().encode("sAlT");
49
21
  const MD5_DIGEST_BYTES = 16;
50
22
  const OBJECT_KEY_EXTRA_BYTES = 5;
51
23
  const LEGACY_KEY_ITERATIONS = 50;
@@ -59,6 +31,9 @@ const RC4_40_KEY_BYTES = 5;
59
31
  const AESV2_KEY_BYTES = 16;
60
32
  const DEFAULT_KEY_BITS = 40;
61
33
  const BITS_PER_BYTE = 8;
34
+ const BYTE_MASK = 255;
35
+ const UINT32_BYTE_2_SHIFT = 16;
36
+ const UINT32_BYTE_3_SHIFT = 24;
62
37
  const MAX_RC4_KEY_BYTES = 16;
63
38
  const ZERO_IV = /* @__PURE__ */ new Uint8Array(16);
64
39
  function requireStringBytes(dict, key) {
@@ -73,10 +48,10 @@ function bytesEqual(a, b, length) {
73
48
  function permissionsBytes(p) {
74
49
  const unsigned = Math.trunc(p) >>> 0;
75
50
  return new Uint8Array([
76
- unsigned & 255,
77
- unsigned >>> 8 & 255,
78
- unsigned >>> 16 & 255,
79
- unsigned >>> 24 & 255
51
+ unsigned & BYTE_MASK,
52
+ unsigned >>> BITS_PER_BYTE & BYTE_MASK,
53
+ unsigned >>> UINT32_BYTE_2_SHIFT & BYTE_MASK,
54
+ unsigned >>> UINT32_BYTE_3_SHIFT & BYTE_MASK
80
55
  ]);
81
56
  }
82
57
  function padOrTruncatePassword(password) {
@@ -84,6 +59,9 @@ function padOrTruncatePassword(password) {
84
59
  padded.set(password.subarray(0, PASSWORD_PADDING.length));
85
60
  return padded;
86
61
  }
62
+ const METADATA_FLAG_MIN_REVISION = 4;
63
+ const METADATA_NOT_ENCRYPTED_FLAG_BYTES = 4;
64
+ const ITERATED_REVISION_MIN = 3;
87
65
  function computeLegacyFileKeyFromPaddedPassword(paddedPassword, owner, permissions, fileId, revision, keyBytes, encryptMetadata) {
88
66
  const parts = [
89
67
  paddedPassword,
@@ -91,14 +69,9 @@ function computeLegacyFileKeyFromPaddedPassword(paddedPassword, owner, permissio
91
69
  permissionsBytes(permissions),
92
70
  fileId
93
71
  ];
94
- if (revision >= 4 && !encryptMetadata) parts.push(new Uint8Array([
95
- 255,
96
- 255,
97
- 255,
98
- 255
99
- ]));
72
+ if (revision >= METADATA_FLAG_MIN_REVISION && !encryptMetadata) parts.push(new Uint8Array(METADATA_NOT_ENCRYPTED_FLAG_BYTES).fill(BYTE_MASK));
100
73
  let digest = md5(concatBytes(parts));
101
- if (revision >= 3) for (let i = 0; i < 50; i++) digest = md5(digest.subarray(0, keyBytes));
74
+ if (revision >= ITERATED_REVISION_MIN) for (let i = 0; i < 50; i++) digest = md5(digest.subarray(0, keyBytes));
102
75
  return digest.subarray(0, keyBytes);
103
76
  }
104
77
  function computeLegacyFileKey(encryptDict, fileId, revision, keyBytes, encryptMetadata) {
@@ -119,13 +92,16 @@ function legacyUserPasswordVerifies(encryptDict, fileId, revision, fileKey) {
119
92
  const storedUser = requireStringBytes(encryptDict, "U");
120
93
  return bytesEqual(legacyUserValueCore(fileKey, fileId, revision), storedUser, revision === 2 ? PASSWORD_PADDING.length : 16);
121
94
  }
95
+ const HARDENED_HASH_SHA256_ONLY_REVISION = 5;
96
+ const STANDARD_AESV3_REVISION = 6;
97
+ const HASH_SELECTOR_MODULUS = 3;
122
98
  function hardenedHash(password, salt, userData, revision) {
123
99
  let k = sha256(concatBytes([
124
100
  password,
125
101
  salt,
126
102
  userData
127
103
  ]));
128
- if (revision === 5) return k;
104
+ if (revision === HARDENED_HASH_SHA256_ONLY_REVISION) return k;
129
105
  let e;
130
106
  let round = 0;
131
107
  do {
@@ -139,7 +115,7 @@ function hardenedHash(password, salt, userData, revision) {
139
115
  e = aesCbcEncrypt(k.subarray(0, 16), k.subarray(16, 32), k1);
140
116
  let sum = 0;
141
117
  for (let i = 0; i < 16; i++) sum += e[i];
142
- const selector = sum % 3;
118
+ const selector = sum % HASH_SELECTOR_MODULUS;
143
119
  k = selector === 0 ? sha256(e) : selector === 1 ? sha384(e) : sha512(e);
144
120
  round++;
145
121
  } while (round < R6_MINIMUM_ROUNDS || e[e.length - 1] > round - R6_TERMINATION_MARGIN);
@@ -179,14 +155,17 @@ function cryptFilterKeyBytes(encryptDict, declaredBits) {
179
155
  if (declared === void 0) return Math.floor(declaredBits / BITS_PER_BYTE);
180
156
  return declared >= DEFAULT_KEY_BITS ? Math.floor(declared / BITS_PER_BYTE) : declared;
181
157
  }
158
+ const V5_HANDLER_VERSION = 5;
159
+ const V4_HANDLER_VERSION = 4;
160
+ const LEGACY_MAX_REVISION = 4;
182
161
  function setUpHandler(encryptDict, fileId) {
183
162
  const version = asNumber(dictGet(encryptDict, "V")) ?? 0;
184
163
  const revision = asNumber(dictGet(encryptDict, "R")) ?? 0;
185
164
  const declaredBits = asNumber(dictGet(encryptDict, "Length")) ?? DEFAULT_KEY_BITS;
186
165
  const encryptMetadataEntry = dictGet(encryptDict, "EncryptMetadata");
187
166
  const encryptMetadata = encryptMetadataEntry?.kind === "bool" ? encryptMetadataEntry.value : true;
188
- if (version === 5) {
189
- if (revision !== 5 && revision !== 6) throw new PdfEncryptedError(`/V 5 encryption with an unsupported revision /R ${String(revision)}`);
167
+ if (version === V5_HANDLER_VERSION) {
168
+ if (revision !== HARDENED_HASH_SHA256_ONLY_REVISION && revision !== STANDARD_AESV3_REVISION) throw new PdfEncryptedError(`/V 5 encryption with an unsupported revision /R ${String(revision)}`);
190
169
  return {
191
170
  fileKey: computeAesV3FileKey(encryptDict, revision),
192
171
  streamMethod: methodFromCryptFilterName(encryptDict, "StmF"),
@@ -195,10 +174,10 @@ function setUpHandler(encryptDict, fileId) {
195
174
  encryptMetadata
196
175
  };
197
176
  }
198
- if (version !== 1 && version !== 2 && version !== 4) throw new PdfEncryptedError(`unsupported standard security handler version /V ${String(version)}`);
199
- if (revision < 2 || revision > 4) throw new PdfEncryptedError(`unsupported standard security handler revision /R ${String(revision)}`);
200
- const streamMethod = version === 4 ? methodFromCryptFilterName(encryptDict, "StmF") : "rc4";
201
- const stringMethod = version === 4 ? methodFromCryptFilterName(encryptDict, "StrF") : "rc4";
177
+ if (version !== 1 && version !== 2 && version !== V4_HANDLER_VERSION) throw new PdfEncryptedError(`unsupported standard security handler version /V ${String(version)}`);
178
+ if (revision < 2 || revision > LEGACY_MAX_REVISION) throw new PdfEncryptedError(`unsupported standard security handler revision /R ${String(revision)}`);
179
+ const streamMethod = version === V4_HANDLER_VERSION ? methodFromCryptFilterName(encryptDict, "StmF") : "rc4";
180
+ const stringMethod = version === V4_HANDLER_VERSION ? methodFromCryptFilterName(encryptDict, "StrF") : "rc4";
202
181
  const keyBytes = version === 1 ? 5 : version === 2 ? Math.floor(declaredBits / BITS_PER_BYTE) : cryptFilterKeyBytes(encryptDict, declaredBits);
203
182
  if (keyBytes < 5 || keyBytes > MAX_RC4_KEY_BYTES) throw new PdfEncryptedError(`the /Encrypt dictionary declares a ${String(keyBytes * BITS_PER_BYTE)}-bit file key, outside the 40-128 bit range /V ${String(version)} permits`);
204
183
  const fileKey = computeLegacyFileKey(encryptDict, fileId, revision, keyBytes, encryptMetadata);
@@ -213,11 +192,11 @@ function setUpHandler(encryptDict, fileId) {
213
192
  }
214
193
  function objectKey(fileKey, num, gen, method) {
215
194
  const extra = new Uint8Array(OBJECT_KEY_EXTRA_BYTES);
216
- extra[0] = num & 255;
217
- extra[1] = num >>> 8 & 255;
218
- extra[2] = num >>> 16 & 255;
219
- extra[3] = gen & 255;
220
- extra[4] = gen >>> 8 & 255;
195
+ extra[0] = num & BYTE_MASK;
196
+ extra[1] = num >>> BITS_PER_BYTE & BYTE_MASK;
197
+ extra[2] = num >>> UINT32_BYTE_2_SHIFT & BYTE_MASK;
198
+ extra[3] = gen & BYTE_MASK;
199
+ extra[4] = gen >>> BITS_PER_BYTE & BYTE_MASK;
221
200
  return md5(concatBytes(method === "aes" ? [
222
201
  fileKey,
223
202
  extra,
package/dist/filters.cjs CHANGED
@@ -6,6 +6,8 @@ const require_image_ccitt = require("./image/ccitt.cjs");
6
6
  const require_image_jbig2 = require("./image/jbig2.cjs");
7
7
  const require_predictors = require("./predictors.cjs");
8
8
  //#region src/filters.ts
9
+ const BITS_PER_BYTE = 8;
10
+ const BYTE_MASK = 255;
9
11
  function decodeStream(raw, dict, sink, resolve) {
10
12
  const filters = filterNames(dict);
11
13
  const parms = decodeParmsList(dict, filters.length);
@@ -103,7 +105,7 @@ function jbig2Decode(data, parm, dict, sink, resolve) {
103
105
  }
104
106
  });
105
107
  return {
106
- bytes: Uint8Array.from(image.bytes, (byte) => byte ^ 255),
108
+ bytes: Uint8Array.from(image.bytes, (byte) => byte ^ BYTE_MASK),
107
109
  globals
108
110
  };
109
111
  } catch (error) {
@@ -146,6 +148,9 @@ function decodeParmsList(dict, count) {
146
148
  const LZW_CLEAR_TABLE = 256;
147
149
  const LZW_EOD = 257;
148
150
  const LZW_INITIAL_CODE_WIDTH = 9;
151
+ const LZW_CODE_WIDTH_10 = 10;
152
+ const LZW_CODE_WIDTH_11 = 11;
153
+ const LZW_CODE_WIDTH_12 = 12;
149
154
  const LZW_FIRST_NEW_CODE = 258;
150
155
  function initialLzwDictionary() {
151
156
  return Array.from({ length: 256 }, (_, i) => new Uint8Array([i]));
@@ -169,9 +174,9 @@ function lzwDecode(data, earlyChange, sink) {
169
174
  const readCode = () => {
170
175
  while (bitCount < codeWidth) {
171
176
  if (pos >= data.length) return;
172
- bitBuffer = bitBuffer << 8 | data[pos];
177
+ bitBuffer = bitBuffer << BITS_PER_BYTE | data[pos];
173
178
  pos++;
174
- bitCount += 8;
179
+ bitCount += BITS_PER_BYTE;
175
180
  }
176
181
  const value = bitBuffer >>> bitCount - codeWidth & (1 << codeWidth) - 1;
177
182
  bitCount -= codeWidth;
@@ -203,9 +208,9 @@ function lzwDecode(data, earlyChange, sink) {
203
208
  if (prevEntry !== void 0) {
204
209
  dict[nextCode] = concatTwo(prevEntry, new Uint8Array([entry[0] ?? 0]));
205
210
  nextCode++;
206
- if (nextCode + bias === 512) codeWidth = 10;
207
- else if (nextCode + bias === 1024) codeWidth = 11;
208
- else if (nextCode + bias === 2048) codeWidth = 12;
211
+ if (nextCode + bias === 2 ** LZW_INITIAL_CODE_WIDTH) codeWidth = LZW_CODE_WIDTH_10;
212
+ else if (nextCode + bias === 2 ** LZW_CODE_WIDTH_10) codeWidth = LZW_CODE_WIDTH_11;
213
+ else if (nextCode + bias === 2 ** LZW_CODE_WIDTH_11) codeWidth = LZW_CODE_WIDTH_12;
209
214
  }
210
215
  prevEntry = entry;
211
216
  }
@@ -217,21 +222,26 @@ const ASCII85_END_MARKER = 126;
217
222
  const ASCII85_MIN_DIGIT = 33;
218
223
  const ASCII85_MAX_DIGIT = 117;
219
224
  const ASCII85_MAX_DIGIT_VALUE = 84;
225
+ const ASCII85_RADIX = 85;
226
+ const ASCII85_GROUP_DIGITS = 5;
227
+ const ASCII85_OPTIONAL_PREFIX_LT = 60;
228
+ const ASCII85_BYTE_3_SHIFT = 24;
229
+ const ASCII85_BYTE_2_SHIFT = 16;
220
230
  function ascii85GroupBytes(digits, byteCount) {
221
231
  let value = 0;
222
- for (const digit of digits) value = value * 85 + digit;
232
+ for (const digit of digits) value = value * ASCII85_RADIX + digit;
223
233
  return [
224
- value >>> 24 & 255,
225
- value >>> 16 & 255,
226
- value >>> 8 & 255,
227
- value & 255
234
+ value >>> ASCII85_BYTE_3_SHIFT & BYTE_MASK,
235
+ value >>> ASCII85_BYTE_2_SHIFT & BYTE_MASK,
236
+ value >>> BITS_PER_BYTE & BYTE_MASK,
237
+ value & BYTE_MASK
228
238
  ].slice(0, byteCount);
229
239
  }
230
240
  function ascii85Decode(data) {
231
241
  const out = [];
232
242
  let tuple = [];
233
243
  let i = 0;
234
- if (data.length >= 2 && data[0] === 60 && data[1] === 126) i = 2;
244
+ if (data.length >= 2 && data[0] === ASCII85_OPTIONAL_PREFIX_LT && data[1] === ASCII85_END_MARKER) i = 2;
235
245
  for (; i < data.length; i++) {
236
246
  const byte = data[i];
237
247
  if (byte === ASCII85_END_MARKER) break;
@@ -242,36 +252,46 @@ function ascii85Decode(data) {
242
252
  }
243
253
  if (byte < ASCII85_MIN_DIGIT || byte > ASCII85_MAX_DIGIT) continue;
244
254
  tuple.push(byte - ASCII85_MIN_DIGIT);
245
- if (tuple.length === 5) {
255
+ if (tuple.length === ASCII85_GROUP_DIGITS) {
246
256
  out.push(...ascii85GroupBytes(tuple, ASCII85_GROUP_BYTES));
247
257
  tuple = [];
248
258
  }
249
259
  }
250
260
  if (tuple.length > 1) {
251
261
  const padded = tuple.slice();
252
- while (padded.length < 5) padded.push(ASCII85_MAX_DIGIT_VALUE);
262
+ while (padded.length < ASCII85_GROUP_DIGITS) padded.push(ASCII85_MAX_DIGIT_VALUE);
253
263
  out.push(...ascii85GroupBytes(padded, tuple.length - 1));
254
264
  }
255
265
  return Uint8Array.from(out);
256
266
  }
267
+ const ASCII_DIGIT_ZERO = 48;
268
+ const ASCII_DIGIT_NINE = 57;
269
+ const ASCII_UPPER_A = 65;
270
+ const ASCII_UPPER_F = 70;
271
+ const ASCII_LOWER_A = 97;
272
+ const ASCII_LOWER_F = 102;
273
+ const HEX_LETTER_DIGIT_OFFSET = 10;
274
+ const ASCII_HEX_TERMINATOR = 62;
275
+ const HEX_NIBBLE_BITS = 4;
257
276
  function hexDigitValue(byte) {
258
- if (byte >= 48 && byte <= 57) return byte - 48;
259
- if (byte >= 65 && byte <= 70) return byte - 65 + 10;
260
- if (byte >= 97 && byte <= 102) return byte - 97 + 10;
277
+ if (byte >= ASCII_DIGIT_ZERO && byte <= ASCII_DIGIT_NINE) return byte - ASCII_DIGIT_ZERO;
278
+ if (byte >= ASCII_UPPER_A && byte <= ASCII_UPPER_F) return byte - ASCII_UPPER_A + HEX_LETTER_DIGIT_OFFSET;
279
+ if (byte >= ASCII_LOWER_A && byte <= ASCII_LOWER_F) return byte - ASCII_LOWER_A + HEX_LETTER_DIGIT_OFFSET;
261
280
  }
262
281
  function asciiHexDecode(data) {
263
282
  const digits = [];
264
283
  for (const byte of data) {
265
- if (byte === 62) break;
284
+ if (byte === ASCII_HEX_TERMINATOR) break;
266
285
  const value = hexDigitValue(byte);
267
286
  if (value !== void 0) digits.push(value);
268
287
  }
269
288
  if (digits.length % 2 === 1) digits.push(0);
270
289
  const out = new Uint8Array(digits.length / 2);
271
- for (let i = 0; i < out.length; i++) out[i] = digits[i * 2] << 4 | digits[i * 2 + 1];
290
+ for (let i = 0; i < out.length; i++) out[i] = digits[i * 2] << HEX_NIBBLE_BITS | digits[i * 2 + 1];
272
291
  return out;
273
292
  }
274
293
  const RUN_LENGTH_EOD = 128;
294
+ const RUN_LENGTH_REPEAT_COUNT_BASE = 257;
275
295
  function runLengthDecode(data) {
276
296
  const out = [];
277
297
  let i = 0;
@@ -283,7 +303,7 @@ function runLengthDecode(data) {
283
303
  const count = length + 1;
284
304
  for (let j = 0; j < count && i < data.length; j++, i++) out.push(data[i]);
285
305
  } else {
286
- const count = 257 - length;
306
+ const count = RUN_LENGTH_REPEAT_COUNT_BASE - length;
287
307
  const byte = data[i] ?? 0;
288
308
  i++;
289
309
  for (let j = 0; j < count; j++) out.push(byte);