@cj-tech-master/excelts 8.0.0 → 8.1.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.
- package/README.md +14 -1
- package/README_zh.md +6 -0
- package/dist/browser/modules/archive/zip/stream.d.ts +4 -0
- package/dist/browser/modules/archive/zip/stream.js +53 -0
- package/dist/browser/modules/pdf/core/crypto.d.ts +65 -0
- package/dist/browser/modules/pdf/core/crypto.js +637 -0
- package/dist/browser/modules/pdf/core/encryption.d.ts +23 -20
- package/dist/browser/modules/pdf/core/encryption.js +88 -261
- package/dist/browser/modules/pdf/core/pdf-writer.d.ts +6 -4
- package/dist/browser/modules/pdf/core/pdf-writer.js +19 -10
- package/dist/browser/modules/pdf/index.d.ts +23 -2
- package/dist/browser/modules/pdf/index.js +21 -3
- package/dist/browser/modules/pdf/reader/annotation-extractor.d.ts +63 -0
- package/dist/browser/modules/pdf/reader/annotation-extractor.js +155 -0
- package/dist/browser/modules/pdf/reader/cmap-parser.d.ts +70 -0
- package/dist/browser/modules/pdf/reader/cmap-parser.js +321 -0
- package/dist/browser/modules/pdf/reader/content-interpreter.d.ts +57 -0
- package/dist/browser/modules/pdf/reader/content-interpreter.js +715 -0
- package/dist/browser/modules/pdf/reader/font-decoder.d.ts +58 -0
- package/dist/browser/modules/pdf/reader/font-decoder.js +1513 -0
- package/dist/browser/modules/pdf/reader/form-extractor.d.ts +48 -0
- package/dist/browser/modules/pdf/reader/form-extractor.js +355 -0
- package/dist/browser/modules/pdf/reader/image-extractor.d.ts +55 -0
- package/dist/browser/modules/pdf/reader/image-extractor.js +220 -0
- package/dist/browser/modules/pdf/reader/metadata-reader.d.ts +56 -0
- package/dist/browser/modules/pdf/reader/metadata-reader.js +275 -0
- package/dist/browser/modules/pdf/reader/pdf-decrypt.d.ts +26 -0
- package/dist/browser/modules/pdf/reader/pdf-decrypt.js +443 -0
- package/dist/browser/modules/pdf/reader/pdf-document.d.ts +191 -0
- package/dist/browser/modules/pdf/reader/pdf-document.js +818 -0
- package/dist/browser/modules/pdf/reader/pdf-parser.d.ts +65 -0
- package/dist/browser/modules/pdf/reader/pdf-parser.js +285 -0
- package/dist/browser/modules/pdf/reader/pdf-reader.d.ts +143 -0
- package/dist/browser/modules/pdf/reader/pdf-reader.js +200 -0
- package/dist/browser/modules/pdf/reader/pdf-tokenizer.d.ts +101 -0
- package/dist/browser/modules/pdf/reader/pdf-tokenizer.js +543 -0
- package/dist/browser/modules/pdf/reader/reader-utils.d.ts +15 -0
- package/dist/browser/modules/pdf/reader/reader-utils.js +27 -0
- package/dist/browser/modules/pdf/reader/stream-filters.d.ts +20 -0
- package/dist/browser/modules/pdf/reader/stream-filters.js +456 -0
- package/dist/browser/modules/pdf/reader/text-reconstruction.d.ts +44 -0
- package/dist/browser/modules/pdf/reader/text-reconstruction.js +463 -0
- package/dist/cjs/modules/archive/zip/stream.js +53 -0
- package/dist/cjs/modules/pdf/core/crypto.js +649 -0
- package/dist/cjs/modules/pdf/core/encryption.js +88 -263
- package/dist/cjs/modules/pdf/core/pdf-writer.js +19 -10
- package/dist/cjs/modules/pdf/index.js +23 -4
- package/dist/cjs/modules/pdf/reader/annotation-extractor.js +158 -0
- package/dist/cjs/modules/pdf/reader/cmap-parser.js +326 -0
- package/dist/cjs/modules/pdf/reader/content-interpreter.js +718 -0
- package/dist/cjs/modules/pdf/reader/font-decoder.js +1518 -0
- package/dist/cjs/modules/pdf/reader/form-extractor.js +358 -0
- package/dist/cjs/modules/pdf/reader/image-extractor.js +223 -0
- package/dist/cjs/modules/pdf/reader/metadata-reader.js +278 -0
- package/dist/cjs/modules/pdf/reader/pdf-decrypt.js +447 -0
- package/dist/cjs/modules/pdf/reader/pdf-document.js +822 -0
- package/dist/cjs/modules/pdf/reader/pdf-parser.js +301 -0
- package/dist/cjs/modules/pdf/reader/pdf-reader.js +203 -0
- package/dist/cjs/modules/pdf/reader/pdf-tokenizer.js +517 -0
- package/dist/cjs/modules/pdf/reader/reader-utils.js +30 -0
- package/dist/cjs/modules/pdf/reader/stream-filters.js +459 -0
- package/dist/cjs/modules/pdf/reader/text-reconstruction.js +467 -0
- package/dist/esm/modules/archive/zip/stream.js +53 -0
- package/dist/esm/modules/pdf/core/crypto.js +637 -0
- package/dist/esm/modules/pdf/core/encryption.js +88 -261
- package/dist/esm/modules/pdf/core/pdf-writer.js +19 -10
- package/dist/esm/modules/pdf/index.js +21 -3
- package/dist/esm/modules/pdf/reader/annotation-extractor.js +155 -0
- package/dist/esm/modules/pdf/reader/cmap-parser.js +321 -0
- package/dist/esm/modules/pdf/reader/content-interpreter.js +715 -0
- package/dist/esm/modules/pdf/reader/font-decoder.js +1513 -0
- package/dist/esm/modules/pdf/reader/form-extractor.js +355 -0
- package/dist/esm/modules/pdf/reader/image-extractor.js +220 -0
- package/dist/esm/modules/pdf/reader/metadata-reader.js +275 -0
- package/dist/esm/modules/pdf/reader/pdf-decrypt.js +443 -0
- package/dist/esm/modules/pdf/reader/pdf-document.js +818 -0
- package/dist/esm/modules/pdf/reader/pdf-parser.js +285 -0
- package/dist/esm/modules/pdf/reader/pdf-reader.js +200 -0
- package/dist/esm/modules/pdf/reader/pdf-tokenizer.js +543 -0
- package/dist/esm/modules/pdf/reader/reader-utils.js +27 -0
- package/dist/esm/modules/pdf/reader/stream-filters.js +456 -0
- package/dist/esm/modules/pdf/reader/text-reconstruction.js +463 -0
- package/dist/iife/excelts.iife.js +703 -267
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +35 -35
- package/dist/types/modules/archive/zip/stream.d.ts +4 -0
- package/dist/types/modules/pdf/core/crypto.d.ts +65 -0
- package/dist/types/modules/pdf/core/encryption.d.ts +23 -20
- package/dist/types/modules/pdf/core/pdf-writer.d.ts +6 -4
- package/dist/types/modules/pdf/index.d.ts +23 -2
- package/dist/types/modules/pdf/reader/annotation-extractor.d.ts +63 -0
- package/dist/types/modules/pdf/reader/cmap-parser.d.ts +70 -0
- package/dist/types/modules/pdf/reader/content-interpreter.d.ts +57 -0
- package/dist/types/modules/pdf/reader/font-decoder.d.ts +58 -0
- package/dist/types/modules/pdf/reader/form-extractor.d.ts +48 -0
- package/dist/types/modules/pdf/reader/image-extractor.d.ts +55 -0
- package/dist/types/modules/pdf/reader/metadata-reader.d.ts +56 -0
- package/dist/types/modules/pdf/reader/pdf-decrypt.d.ts +26 -0
- package/dist/types/modules/pdf/reader/pdf-document.d.ts +191 -0
- package/dist/types/modules/pdf/reader/pdf-parser.d.ts +65 -0
- package/dist/types/modules/pdf/reader/pdf-reader.d.ts +143 -0
- package/dist/types/modules/pdf/reader/pdf-tokenizer.d.ts +101 -0
- package/dist/types/modules/pdf/reader/reader-utils.d.ts +15 -0
- package/dist/types/modules/pdf/reader/stream-filters.d.ts +20 -0
- package/dist/types/modules/pdf/reader/text-reconstruction.d.ts +44 -0
- package/package.json +1 -1
|
@@ -1,316 +1,141 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* PDF encryption support (Standard Security Handler,
|
|
3
|
+
* PDF encryption support (Standard Security Handler, V=5, R=5).
|
|
4
4
|
*
|
|
5
|
-
* Implements
|
|
5
|
+
* Implements AES-256 encryption compatible with PDF 2.0 (ISO 32000-2:2020).
|
|
6
6
|
* Supports:
|
|
7
7
|
* - User password (required to open the document)
|
|
8
8
|
* - Owner password (grants full access)
|
|
9
9
|
* - Permission flags (print, copy, modify, etc.)
|
|
10
10
|
*
|
|
11
|
-
*
|
|
11
|
+
* The file encryption key (FEK) is a random 256-bit key.
|
|
12
|
+
* All streams and strings are encrypted using AES-256-CBC with a random
|
|
13
|
+
* 16-byte IV prepended to each encrypted value.
|
|
14
|
+
*
|
|
15
|
+
* @see ISO 32000-2:2020, §7.6 — Encryption
|
|
12
16
|
*/
|
|
13
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
18
|
exports.initEncryption = initEncryption;
|
|
15
19
|
exports.encryptData = encryptData;
|
|
16
|
-
|
|
17
|
-
exports.md5 = md5;
|
|
18
|
-
// =============================================================================
|
|
19
|
-
// Constants
|
|
20
|
-
// =============================================================================
|
|
21
|
-
/** PDF password padding string (32 bytes) per PDF spec §3.5.2 */
|
|
22
|
-
const PASSWORD_PADDING = new Uint8Array([
|
|
23
|
-
0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41, 0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa, 0x01, 0x08,
|
|
24
|
-
0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80, 0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a
|
|
25
|
-
]);
|
|
20
|
+
const crypto_1 = require("./crypto");
|
|
26
21
|
// =============================================================================
|
|
27
22
|
// Public API
|
|
28
23
|
// =============================================================================
|
|
29
24
|
/**
|
|
30
|
-
* Initialize encryption state
|
|
25
|
+
* Initialize encryption state for AES-256 (V=5, R=5).
|
|
31
26
|
*/
|
|
32
27
|
function initEncryption(options) {
|
|
33
|
-
const userPwd = options.userPassword ?? "";
|
|
34
|
-
const ownerPwd = options.ownerPassword;
|
|
28
|
+
const userPwd = truncatePassword(options.userPassword ?? "");
|
|
29
|
+
const ownerPwd = truncatePassword(options.ownerPassword);
|
|
35
30
|
const perms = computePermissions(options.permissions);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const
|
|
31
|
+
// Step 1: Generate random 32-byte file encryption key
|
|
32
|
+
const encryptionKey = (0, crypto_1.randomBytes)(32);
|
|
33
|
+
// Step 2: Generate random salts
|
|
34
|
+
const uValidationSalt = (0, crypto_1.randomBytes)(8);
|
|
35
|
+
const uKeySalt = (0, crypto_1.randomBytes)(8);
|
|
36
|
+
const oValidationSalt = (0, crypto_1.randomBytes)(8);
|
|
37
|
+
const oKeySalt = (0, crypto_1.randomBytes)(8);
|
|
41
38
|
// Step 3: Compute U value
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
39
|
+
// U hash = SHA-256(userPassword + uValidationSalt)
|
|
40
|
+
const uHash = (0, crypto_1.sha256)((0, crypto_1.concatArrays)(userPwd, uValidationSalt));
|
|
41
|
+
const uValue = (0, crypto_1.concatArrays)(uHash, uValidationSalt, uKeySalt);
|
|
42
|
+
// Step 4: Compute UE value
|
|
43
|
+
// UE = AES-256-CBC-encrypt(encryptionKey, SHA-256(userPassword + uKeySalt), zeroIV)
|
|
44
|
+
// Actually: the key for encrypting UE is SHA-256(password + key_salt),
|
|
45
|
+
// and we encrypt the file encryption key with it.
|
|
46
|
+
const ueKey = (0, crypto_1.sha256)((0, crypto_1.concatArrays)(userPwd, uKeySalt));
|
|
47
|
+
const zeroIv = new Uint8Array(16);
|
|
48
|
+
const ueValue = (0, crypto_1.aesCbcEncryptRaw)(encryptionKey, ueKey, zeroIv);
|
|
49
|
+
// Step 5: Compute O value
|
|
50
|
+
// O hash = SHA-256(ownerPassword + oValidationSalt + U(0..47))
|
|
51
|
+
const oHash = (0, crypto_1.sha256)((0, crypto_1.concatArrays)(ownerPwd, oValidationSalt, uValue));
|
|
52
|
+
const oValue = (0, crypto_1.concatArrays)(oHash, oValidationSalt, oKeySalt);
|
|
53
|
+
// Step 6: Compute OE value
|
|
54
|
+
// OE = AES-256-CBC-encrypt(encryptionKey, SHA-256(ownerPassword + oKeySalt + U(0..47)), zeroIV)
|
|
55
|
+
const oeKey = (0, crypto_1.sha256)((0, crypto_1.concatArrays)(ownerPwd, oKeySalt, uValue));
|
|
56
|
+
const oeValue = (0, crypto_1.aesCbcEncryptRaw)(encryptionKey, oeKey, zeroIv);
|
|
57
|
+
// Step 7: Compute Perms value
|
|
58
|
+
// 16-byte block: P(4 LE bytes) + 0xFF(4 bytes) + 'T' or 'F' (encryptMetadata) + 'a' 'd' 'b' + 0(3 bytes)
|
|
59
|
+
const permsBlock = new Uint8Array(16);
|
|
60
|
+
const permsView = new DataView(permsBlock.buffer);
|
|
61
|
+
permsView.setInt32(0, perms, true); // P value in little-endian
|
|
62
|
+
permsBlock[4] = 0xff;
|
|
63
|
+
permsBlock[5] = 0xff;
|
|
64
|
+
permsBlock[6] = 0xff;
|
|
65
|
+
permsBlock[7] = 0xff;
|
|
66
|
+
permsBlock[8] = 0x54; // 'T' — EncryptMetadata = true
|
|
67
|
+
permsBlock[9] = 0x61; // 'a'
|
|
68
|
+
permsBlock[10] = 0x64; // 'd'
|
|
69
|
+
permsBlock[11] = 0x62; // 'b'
|
|
70
|
+
// bytes 12-15 are zero
|
|
71
|
+
const permsValue = (0, crypto_1.aesEcbEncrypt)(permsBlock, encryptionKey);
|
|
72
|
+
// File ID (random 16 bytes, used in trailer)
|
|
73
|
+
const fileId = (0, crypto_1.randomBytes)(16);
|
|
74
|
+
return {
|
|
75
|
+
encryptionKey,
|
|
76
|
+
oValue,
|
|
77
|
+
uValue,
|
|
78
|
+
oeValue,
|
|
79
|
+
ueValue,
|
|
80
|
+
permsValue,
|
|
81
|
+
permissions: perms,
|
|
82
|
+
fileId
|
|
83
|
+
};
|
|
63
84
|
}
|
|
64
|
-
// =============================================================================
|
|
65
|
-
// RC4 Cipher
|
|
66
|
-
// =============================================================================
|
|
67
85
|
/**
|
|
68
|
-
*
|
|
86
|
+
* Encrypt data for a PDF object using AES-256-CBC.
|
|
87
|
+
*
|
|
88
|
+
* For V=5/R=5, the file encryption key is used directly (no per-object key derivation).
|
|
89
|
+
* A random 16-byte IV is prepended to the ciphertext.
|
|
69
90
|
*/
|
|
70
|
-
function
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
for (let i = 0; i < 256; i++) {
|
|
78
|
-
j = (j + s[i] + key[i % key.length]) & 0xff;
|
|
79
|
-
[s[i], s[j]] = [s[j], s[i]];
|
|
80
|
-
}
|
|
81
|
-
// Pseudo-Random Generation Algorithm (PRGA)
|
|
82
|
-
const result = new Uint8Array(data.length);
|
|
83
|
-
let ii = 0;
|
|
84
|
-
let jj = 0;
|
|
85
|
-
for (let k = 0; k < data.length; k++) {
|
|
86
|
-
ii = (ii + 1) & 0xff;
|
|
87
|
-
jj = (jj + s[ii]) & 0xff;
|
|
88
|
-
[s[ii], s[jj]] = [s[jj], s[ii]];
|
|
89
|
-
result[k] = data[k] ^ s[(s[ii] + s[jj]) & 0xff];
|
|
90
|
-
}
|
|
91
|
+
function encryptData(data, _objectNumber, _generation, encryptionKey) {
|
|
92
|
+
const iv = (0, crypto_1.randomBytes)(16);
|
|
93
|
+
const ciphertext = (0, crypto_1.aesCbcEncrypt)(data, encryptionKey, iv);
|
|
94
|
+
// Prepend IV to ciphertext per PDF spec
|
|
95
|
+
const result = new Uint8Array(16 + ciphertext.length);
|
|
96
|
+
result.set(iv);
|
|
97
|
+
result.set(ciphertext, 16);
|
|
91
98
|
return result;
|
|
92
99
|
}
|
|
93
100
|
// =============================================================================
|
|
94
|
-
//
|
|
95
|
-
// =============================================================================
|
|
96
|
-
/**
|
|
97
|
-
* MD5 hash implementation (RFC 1321).
|
|
98
|
-
* Returns 16-byte digest.
|
|
99
|
-
*/
|
|
100
|
-
function md5(input) {
|
|
101
|
-
// Pre-processing: padding
|
|
102
|
-
const msgLen = input.length;
|
|
103
|
-
const bitLen = msgLen * 8;
|
|
104
|
-
// Pad to 64-byte boundary (56 bytes mod 64, then 8 bytes length)
|
|
105
|
-
const padLen = ((56 - ((msgLen + 1) % 64) + 64) % 64) + 1;
|
|
106
|
-
const padded = new Uint8Array(msgLen + padLen + 8);
|
|
107
|
-
padded.set(input);
|
|
108
|
-
padded[msgLen] = 0x80;
|
|
109
|
-
// Append length in bits as 64-bit little-endian
|
|
110
|
-
const view = new DataView(padded.buffer);
|
|
111
|
-
view.setUint32(padded.length - 8, bitLen >>> 0, true);
|
|
112
|
-
view.setUint32(padded.length - 4, 0, true); // high 32 bits (always 0 for our sizes)
|
|
113
|
-
// Initialize hash values
|
|
114
|
-
let a0 = 0x67452301;
|
|
115
|
-
let b0 = 0xefcdab89;
|
|
116
|
-
let c0 = 0x98badcfe;
|
|
117
|
-
let d0 = 0x10325476;
|
|
118
|
-
// Process each 64-byte block
|
|
119
|
-
for (let i = 0; i < padded.length; i += 64) {
|
|
120
|
-
const M = new Uint32Array(16);
|
|
121
|
-
for (let j = 0; j < 16; j++) {
|
|
122
|
-
M[j] = view.getUint32(i + j * 4, true);
|
|
123
|
-
}
|
|
124
|
-
let A = a0;
|
|
125
|
-
let B = b0;
|
|
126
|
-
let C = c0;
|
|
127
|
-
let D = d0;
|
|
128
|
-
for (let j = 0; j < 64; j++) {
|
|
129
|
-
let F;
|
|
130
|
-
let g;
|
|
131
|
-
if (j < 16) {
|
|
132
|
-
F = (B & C) | (~B & D);
|
|
133
|
-
g = j;
|
|
134
|
-
}
|
|
135
|
-
else if (j < 32) {
|
|
136
|
-
F = (D & B) | (~D & C);
|
|
137
|
-
g = (5 * j + 1) % 16;
|
|
138
|
-
}
|
|
139
|
-
else if (j < 48) {
|
|
140
|
-
F = B ^ C ^ D;
|
|
141
|
-
g = (3 * j + 5) % 16;
|
|
142
|
-
}
|
|
143
|
-
else {
|
|
144
|
-
F = C ^ (B | ~D);
|
|
145
|
-
g = (7 * j) % 16;
|
|
146
|
-
}
|
|
147
|
-
F = (F + A + K[j] + M[g]) >>> 0;
|
|
148
|
-
A = D;
|
|
149
|
-
D = C;
|
|
150
|
-
C = B;
|
|
151
|
-
B = (B + rotl(F, S[j])) >>> 0;
|
|
152
|
-
}
|
|
153
|
-
a0 = (a0 + A) >>> 0;
|
|
154
|
-
b0 = (b0 + B) >>> 0;
|
|
155
|
-
c0 = (c0 + C) >>> 0;
|
|
156
|
-
d0 = (d0 + D) >>> 0;
|
|
157
|
-
}
|
|
158
|
-
// Produce the 128-bit digest
|
|
159
|
-
const digest = new Uint8Array(16);
|
|
160
|
-
const dv = new DataView(digest.buffer);
|
|
161
|
-
dv.setUint32(0, a0, true);
|
|
162
|
-
dv.setUint32(4, b0, true);
|
|
163
|
-
dv.setUint32(8, c0, true);
|
|
164
|
-
dv.setUint32(12, d0, true);
|
|
165
|
-
return digest;
|
|
166
|
-
}
|
|
167
|
-
function rotl(x, n) {
|
|
168
|
-
return ((x << n) | (x >>> (32 - n))) >>> 0;
|
|
169
|
-
}
|
|
170
|
-
// MD5 per-round shift amounts
|
|
171
|
-
const S = [
|
|
172
|
-
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14,
|
|
173
|
-
20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6,
|
|
174
|
-
10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
|
|
175
|
-
];
|
|
176
|
-
// MD5 per-round constants (floor(2^32 × abs(sin(i+1))))
|
|
177
|
-
const K = new Uint32Array([
|
|
178
|
-
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
|
|
179
|
-
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
|
|
180
|
-
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
|
|
181
|
-
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
|
|
182
|
-
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
|
|
183
|
-
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
|
|
184
|
-
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
|
|
185
|
-
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
|
|
186
|
-
]);
|
|
187
|
-
// =============================================================================
|
|
188
|
-
// PDF Password / Key Computation
|
|
101
|
+
// Internal Helpers
|
|
189
102
|
// =============================================================================
|
|
190
103
|
/**
|
|
191
|
-
*
|
|
104
|
+
* Truncate password to 127 bytes (UTF-8) per PDF 2.0 spec.
|
|
192
105
|
*/
|
|
193
|
-
function
|
|
194
|
-
const result = new Uint8Array(32);
|
|
106
|
+
function truncatePassword(password) {
|
|
195
107
|
const bytes = new TextEncoder().encode(password);
|
|
196
|
-
|
|
197
|
-
result.set(bytes.subarray(0, len));
|
|
198
|
-
result.set(PASSWORD_PADDING.subarray(0, 32 - len), len);
|
|
199
|
-
return result;
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* Compute the O (owner) value.
|
|
203
|
-
* Algorithm 3 from PDF spec §3.5.2.
|
|
204
|
-
*/
|
|
205
|
-
function computeOValue(ownerPassword, userPassword) {
|
|
206
|
-
// Step 1: MD5 hash of padded owner password
|
|
207
|
-
let hash = md5(padPassword(ownerPassword));
|
|
208
|
-
// Step 2: For revision 3, hash 50 more times
|
|
209
|
-
for (let i = 0; i < 50; i++) {
|
|
210
|
-
hash = md5(hash);
|
|
211
|
-
}
|
|
212
|
-
// Use first 16 bytes as RC4 key (128-bit / key length = 16)
|
|
213
|
-
const rc4Key = hash.subarray(0, 16);
|
|
214
|
-
// Step 3: RC4-encrypt the padded user password
|
|
215
|
-
let result = rc4(rc4Key, padPassword(userPassword));
|
|
216
|
-
// Step 4: For revision 3, iterate 1-19 with modified key
|
|
217
|
-
for (let i = 1; i <= 19; i++) {
|
|
218
|
-
const modKey = new Uint8Array(16);
|
|
219
|
-
for (let j = 0; j < 16; j++) {
|
|
220
|
-
modKey[j] = rc4Key[j] ^ i;
|
|
221
|
-
}
|
|
222
|
-
result = rc4(modKey, result);
|
|
223
|
-
}
|
|
224
|
-
return result;
|
|
225
|
-
}
|
|
226
|
-
/**
|
|
227
|
-
* Compute the encryption key.
|
|
228
|
-
* Algorithm 2 from PDF spec §3.5.2.
|
|
229
|
-
*/
|
|
230
|
-
function computeEncryptionKey(userPassword, oValue, permissions, fileId) {
|
|
231
|
-
// Concatenate: padded password + O value + P value (4 LE bytes) + file ID
|
|
232
|
-
const paddedPwd = padPassword(userPassword);
|
|
233
|
-
const input = new Uint8Array(32 + 32 + 4 + fileId.length);
|
|
234
|
-
input.set(paddedPwd);
|
|
235
|
-
input.set(oValue, 32);
|
|
236
|
-
const pView = new DataView(input.buffer, input.byteOffset);
|
|
237
|
-
pView.setInt32(64, permissions, true);
|
|
238
|
-
input.set(fileId, 68);
|
|
239
|
-
let hash = md5(input);
|
|
240
|
-
// For revision 3, hash 50 more times
|
|
241
|
-
for (let i = 0; i < 50; i++) {
|
|
242
|
-
hash = md5(hash.subarray(0, 16));
|
|
243
|
-
}
|
|
244
|
-
return hash.subarray(0, 16); // 128-bit key
|
|
245
|
-
}
|
|
246
|
-
/**
|
|
247
|
-
* Compute the U (user) value.
|
|
248
|
-
* Algorithm 5 from PDF spec §3.5.2 (revision 3).
|
|
249
|
-
*/
|
|
250
|
-
function computeUValue(encryptionKey, fileId) {
|
|
251
|
-
// Step 1: MD5 hash of padding + file ID
|
|
252
|
-
const hashInput = new Uint8Array(32 + fileId.length);
|
|
253
|
-
hashInput.set(PASSWORD_PADDING);
|
|
254
|
-
hashInput.set(fileId, 32);
|
|
255
|
-
const hash = md5(hashInput);
|
|
256
|
-
// Step 2: RC4-encrypt with the encryption key
|
|
257
|
-
let result = rc4(encryptionKey, hash);
|
|
258
|
-
// Step 3: Iterate 1-19 with modified key
|
|
259
|
-
for (let i = 1; i <= 19; i++) {
|
|
260
|
-
const modKey = new Uint8Array(16);
|
|
261
|
-
for (let j = 0; j < 16; j++) {
|
|
262
|
-
modKey[j] = encryptionKey[j] ^ i;
|
|
263
|
-
}
|
|
264
|
-
result = rc4(modKey, result);
|
|
265
|
-
}
|
|
266
|
-
// Pad to 32 bytes with arbitrary padding
|
|
267
|
-
const uValue = new Uint8Array(32);
|
|
268
|
-
uValue.set(result);
|
|
269
|
-
return uValue;
|
|
108
|
+
return bytes.length > 127 ? bytes.subarray(0, 127) : bytes;
|
|
270
109
|
}
|
|
271
110
|
/**
|
|
272
111
|
* Compute the permissions integer (P value) from permission flags.
|
|
273
112
|
*/
|
|
274
113
|
function computePermissions(perms) {
|
|
275
|
-
// Start with all bits set
|
|
276
|
-
|
|
277
|
-
let p = 0xfffff000 | 0b11000000; // bits 7-8 = reserved 1, high bits = 1
|
|
114
|
+
// Start with all reserved bits set to 1
|
|
115
|
+
let p = 0xfffff000 | 0b11000000;
|
|
278
116
|
if (perms?.print) {
|
|
279
|
-
p |= 1 << 2;
|
|
117
|
+
p |= 1 << 2;
|
|
280
118
|
}
|
|
281
119
|
if (perms?.modify) {
|
|
282
|
-
p |= 1 << 3;
|
|
120
|
+
p |= 1 << 3;
|
|
283
121
|
}
|
|
284
122
|
if (perms?.copy) {
|
|
285
|
-
p |= 1 << 4;
|
|
123
|
+
p |= 1 << 4;
|
|
286
124
|
}
|
|
287
125
|
if (perms?.annotate) {
|
|
288
|
-
p |= 1 << 5;
|
|
126
|
+
p |= 1 << 5;
|
|
289
127
|
}
|
|
290
128
|
if (perms?.fillForms) {
|
|
291
|
-
p |= 1 << 8;
|
|
129
|
+
p |= 1 << 8;
|
|
292
130
|
}
|
|
293
131
|
if (perms?.accessibility) {
|
|
294
|
-
p |= 1 << 9;
|
|
132
|
+
p |= 1 << 9;
|
|
295
133
|
}
|
|
296
134
|
if (perms?.assemble) {
|
|
297
|
-
p |= 1 << 10;
|
|
135
|
+
p |= 1 << 10;
|
|
298
136
|
}
|
|
299
137
|
if (perms?.printHighQuality) {
|
|
300
|
-
p |= 1 << 11;
|
|
138
|
+
p |= 1 << 11;
|
|
301
139
|
}
|
|
302
|
-
// Convert to signed 32-bit
|
|
303
140
|
return p | 0;
|
|
304
141
|
}
|
|
305
|
-
/**
|
|
306
|
-
* Generate a random file identifier (16 bytes).
|
|
307
|
-
*/
|
|
308
|
-
function generateFileId() {
|
|
309
|
-
// Use MD5 of current timestamp + random for determinism in tests
|
|
310
|
-
const seed = new Uint8Array(16);
|
|
311
|
-
const now = Date.now();
|
|
312
|
-
const view = new DataView(seed.buffer);
|
|
313
|
-
view.setFloat64(0, now, true);
|
|
314
|
-
view.setFloat64(8, Math.random() * 1e15, true);
|
|
315
|
-
return md5(seed);
|
|
316
|
-
}
|
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* PDF file writer.
|
|
4
4
|
*
|
|
5
|
-
* Assembles a complete PDF document from indirect objects.
|
|
5
|
+
* Assembles a complete PDF 2.0 document from indirect objects.
|
|
6
6
|
* Handles the four sections of a PDF file:
|
|
7
|
-
* 1. Header (%PDF-
|
|
7
|
+
* 1. Header (%PDF-2.0)
|
|
8
8
|
* 2. Body (indirect objects)
|
|
9
9
|
* 3. Cross-reference table
|
|
10
10
|
* 4. Trailer (with document catalog reference)
|
|
11
11
|
*
|
|
12
|
-
*
|
|
12
|
+
* Encryption uses AES-256 (V=5, R=5) per ISO 32000-2:2020.
|
|
13
|
+
*
|
|
14
|
+
* @see ISO 32000-2:2020, Chapter 7.5 — File Structure
|
|
13
15
|
*/
|
|
14
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
17
|
exports.PdfWriter = void 0;
|
|
@@ -22,7 +24,7 @@ const encryption_1 = require("./encryption");
|
|
|
22
24
|
// PDF Writer
|
|
23
25
|
// =============================================================================
|
|
24
26
|
/**
|
|
25
|
-
* Constructs a valid PDF
|
|
27
|
+
* Constructs a valid PDF 2.0 file from a set of indirect objects.
|
|
26
28
|
*
|
|
27
29
|
* Usage:
|
|
28
30
|
* 1. Allocate object numbers with allocObject()
|
|
@@ -171,7 +173,7 @@ class PdfWriter {
|
|
|
171
173
|
let byteOffset = 0;
|
|
172
174
|
// --- Header ---
|
|
173
175
|
// Include a comment with high bytes to signal binary content per PDF spec §3.4.1
|
|
174
|
-
const headerStr = "%PDF-
|
|
176
|
+
const headerStr = "%PDF-2.0\n";
|
|
175
177
|
const headerStrBytes = encoder.encode(headerStr);
|
|
176
178
|
chunks.push(headerStrBytes);
|
|
177
179
|
byteOffset += headerStrBytes.length;
|
|
@@ -223,16 +225,23 @@ class PdfWriter {
|
|
|
223
225
|
chunks.push(objFooter);
|
|
224
226
|
byteOffset += objFooter.length;
|
|
225
227
|
}
|
|
226
|
-
// --- Encrypt dictionary (
|
|
228
|
+
// --- Encrypt dictionary (V=5, R=5, AES-256) ---
|
|
227
229
|
if (this.encryption) {
|
|
228
230
|
const encDict = new pdf_object_1.PdfDict()
|
|
229
231
|
.set("Filter", "/Standard")
|
|
230
|
-
.set("V", "
|
|
231
|
-
.set("R", "
|
|
232
|
-
.set("Length", "
|
|
232
|
+
.set("V", "5")
|
|
233
|
+
.set("R", "5")
|
|
234
|
+
.set("Length", "256")
|
|
233
235
|
.set("P", String(this.encryption.permissions))
|
|
234
236
|
.set("O", (0, pdf_object_1.pdfHexString)(this.encryption.oValue))
|
|
235
|
-
.set("U", (0, pdf_object_1.pdfHexString)(this.encryption.uValue))
|
|
237
|
+
.set("U", (0, pdf_object_1.pdfHexString)(this.encryption.uValue))
|
|
238
|
+
.set("OE", (0, pdf_object_1.pdfHexString)(this.encryption.oeValue))
|
|
239
|
+
.set("UE", (0, pdf_object_1.pdfHexString)(this.encryption.ueValue))
|
|
240
|
+
.set("Perms", (0, pdf_object_1.pdfHexString)(this.encryption.permsValue))
|
|
241
|
+
.set("EncryptMetadata", "true")
|
|
242
|
+
.set("CF", "<< /StdCF << /Type /CryptFilter /CFM /AESV3 /AuthEvent /DocOpen /Length 32 >> >>")
|
|
243
|
+
.set("StmF", "/StdCF")
|
|
244
|
+
.set("StrF", "/StdCF");
|
|
236
245
|
const encContent = encDict.toString();
|
|
237
246
|
const encObj = {
|
|
238
247
|
objectNumber: encryptObjNum,
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* PDF module for excelts.
|
|
4
4
|
*
|
|
5
|
-
* A full-featured, zero-dependency PDF engine.
|
|
5
|
+
* A full-featured, zero-dependency PDF engine for both writing and reading.
|
|
6
6
|
*
|
|
7
|
-
* @example Standalone:
|
|
7
|
+
* @example Standalone PDF generation:
|
|
8
8
|
* ```typescript
|
|
9
9
|
* import { pdf } from "excelts/pdf";
|
|
10
10
|
*
|
|
@@ -26,12 +26,25 @@
|
|
|
26
26
|
* const bytes = excelToPdf(workbook);
|
|
27
27
|
* ```
|
|
28
28
|
*
|
|
29
|
+
* @example Read PDF — extract text, images, and metadata:
|
|
30
|
+
* ```typescript
|
|
31
|
+
* import { readPdf } from "excelts/pdf";
|
|
32
|
+
*
|
|
33
|
+
* const result = readPdf(pdfBytes);
|
|
34
|
+
* console.log(result.text); // All text
|
|
35
|
+
* console.log(result.pages[0].text); // Page 1 text
|
|
36
|
+
* console.log(result.pages[0].images); // Page 1 images
|
|
37
|
+
* console.log(result.pages[0].annotations); // Page 1 annotations
|
|
38
|
+
* console.log(result.metadata.title); // Document title
|
|
39
|
+
* console.log(result.formFields); // Form fields
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
29
42
|
* @module pdf
|
|
30
43
|
*/
|
|
31
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
-
exports.isPdfError = exports.PdfStructureError = exports.PdfFontError = exports.PdfRenderError = exports.PdfError = exports.PageSizes = exports.excelToPdf = exports.pdf = void 0;
|
|
45
|
+
exports.isPdfError = exports.PdfStructureError = exports.PdfFontError = exports.PdfRenderError = exports.PdfError = exports.PageSizes = exports.readPdf = exports.excelToPdf = exports.pdf = void 0;
|
|
33
46
|
// =============================================================================
|
|
34
|
-
// Public API
|
|
47
|
+
// Public API — Writing
|
|
35
48
|
// =============================================================================
|
|
36
49
|
/** Standalone PDF generation — accepts plain arrays, sheet objects, or workbooks. */
|
|
37
50
|
var pdf_1 = require("./pdf");
|
|
@@ -39,6 +52,12 @@ Object.defineProperty(exports, "pdf", { enumerable: true, get: function () { ret
|
|
|
39
52
|
/** Excel-to-PDF conversion — accepts an Excel Workbook instance. */
|
|
40
53
|
var excel_bridge_1 = require("./excel-bridge");
|
|
41
54
|
Object.defineProperty(exports, "excelToPdf", { enumerable: true, get: function () { return excel_bridge_1.excelToPdf; } });
|
|
55
|
+
// =============================================================================
|
|
56
|
+
// Public API — Reading
|
|
57
|
+
// =============================================================================
|
|
58
|
+
/** Read a PDF file and extract text, images, and metadata. */
|
|
59
|
+
var pdf_reader_1 = require("./reader/pdf-reader");
|
|
60
|
+
Object.defineProperty(exports, "readPdf", { enumerable: true, get: function () { return pdf_reader_1.readPdf; } });
|
|
42
61
|
var types_1 = require("./types");
|
|
43
62
|
Object.defineProperty(exports, "PageSizes", { enumerable: true, get: function () { return types_1.PageSizes; } });
|
|
44
63
|
// =============================================================================
|