xls-codec 4.8.0 → 4.10.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 +6 -5
- package/dist/biff/cursor.cjs +12 -1
- package/dist/biff/cursor.d.cts +1 -1
- package/dist/biff/cursor.d.ts +1 -1
- package/dist/biff/cursor.js +12 -1
- package/dist/biff/ptg.cjs +16 -2
- package/dist/biff/ptg.d.cts +2 -2
- package/dist/biff/ptg.d.ts +2 -2
- package/dist/biff/ptg.js +16 -3
- package/dist/biff/record-types.cjs +4 -1
- package/dist/biff/record-types.d.cts +4 -2
- package/dist/biff/record-types.d.ts +4 -2
- package/dist/biff/record-types.js +4 -2
- package/dist/biff/strings.d.cts +1 -1
- package/dist/biff/strings.d.ts +1 -1
- package/dist/biff/xf-colors.d.cts +1 -1
- package/dist/biff/xf-colors.d.ts +1 -1
- package/dist/biff/xf-writer.d.cts +1 -1
- package/dist/biff/xf-writer.d.ts +1 -1
- package/dist/{conditional-format-12-Xl52VBCj.d.ts → conditional-format-12-C3nwy1f8.d.ts} +9 -4
- package/dist/{conditional-format-12-CZ43-CR4.d.cts → conditional-format-12-D65-ruTq.d.cts} +9 -4
- package/dist/conditional-format-BRMUTRP1.d.ts +41 -0
- package/dist/conditional-format-Cth_afMy.d.cts +41 -0
- package/dist/content.cjs +11 -1
- package/dist/content.d.cts +1 -1
- package/dist/content.d.ts +1 -1
- package/dist/content.js +11 -1
- package/dist/{cursor-VMtw9uVP.d.cts → cursor-CnEuX5qE.d.cts} +3 -1
- package/dist/{cursor-VMtw9uVP.d.ts → cursor-CnEuX5qE.d.ts} +3 -1
- package/dist/{data-validation-lJWM9-AP.d.cts → data-validation-BgUt8CbE.d.cts} +1 -1
- package/dist/{data-validation-BeYpL3Nb.d.ts → data-validation-Cq6iD8lB.d.ts} +1 -1
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/{ptg-CCBbJLZJ.d.cts → ptg-75v7MhaA.d.cts} +7 -1
- package/dist/{ptg-CCBbJLZJ.d.ts → ptg-75v7MhaA.d.ts} +7 -1
- package/dist/workbook/chart.d.cts +1 -1
- package/dist/workbook/chart.d.ts +1 -1
- package/dist/workbook/conditional-format-12.cjs +32 -1
- package/dist/workbook/conditional-format-12.d.cts +2 -2
- package/dist/workbook/conditional-format-12.d.ts +2 -2
- package/dist/workbook/conditional-format-12.js +33 -3
- package/dist/workbook/conditional-format-ex.cjs +53 -0
- package/dist/workbook/conditional-format-ex.d.cts +13 -0
- package/dist/workbook/conditional-format-ex.d.ts +13 -0
- package/dist/workbook/conditional-format-ex.js +52 -0
- package/dist/workbook/conditional-format.cjs +46 -23
- package/dist/workbook/conditional-format.d.cts +2 -2
- package/dist/workbook/conditional-format.d.ts +2 -2
- package/dist/workbook/conditional-format.js +46 -23
- package/dist/workbook/data-validation.d.cts +1 -1
- package/dist/workbook/data-validation.d.ts +1 -1
- package/dist/workbook/drawing.d.cts +1 -1
- package/dist/workbook/drawing.d.ts +1 -1
- package/dist/workbook/encryption.cjs +67 -20
- package/dist/workbook/encryption.d.cts +2 -2
- package/dist/workbook/encryption.d.ts +2 -2
- package/dist/workbook/encryption.js +68 -21
- package/dist/workbook/globals-writer.d.cts +1 -1
- package/dist/workbook/globals-writer.d.ts +1 -1
- package/dist/workbook/globals.d.cts +2 -2
- package/dist/workbook/globals.d.ts +2 -2
- package/dist/workbook/sheet.cjs +11 -0
- package/dist/workbook/sheet.d.cts +4 -4
- package/dist/workbook/sheet.d.ts +4 -4
- package/dist/workbook/sheet.js +12 -1
- package/dist/{xf-colors-mqerVt2O.d.cts → xf-colors-aeH9Ax3Q.d.cts} +1 -1
- package/dist/{xf-colors-d3OvRw76.d.ts → xf-colors-kdNsiNYt.d.ts} +1 -1
- package/package.json +2 -2
- package/dist/conditional-format-Bt1vRWgc.d.cts +0 -29
- package/dist/conditional-format-Cqcjnm7O.d.ts +0 -29
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { CondFmtGroupResult, RawConditionalFormat, RawConditionalFormatFill, RawConditionalFormatStyle, parseDxfStyle, readCondFmtGroup };
|
|
1
|
+
import { a as RawConditionalFormatStyle, i as RawConditionalFormatFill, n as RawCfOperand, o as parseDxfStyle, r as RawConditionalFormat, s as readCondFmtGroup, t as CondFmtGroupResult } from "../conditional-format-BRMUTRP1.js";
|
|
2
|
+
export { CondFmtGroupResult, RawCfOperand, RawConditionalFormat, RawConditionalFormatFill, RawConditionalFormatStyle, parseDxfStyle, readCondFmtGroup };
|
|
@@ -60,27 +60,42 @@ function parseDxfStyle(dxfBytes) {
|
|
|
60
60
|
return;
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
|
-
|
|
63
|
+
/** The shared byte-layout parse both readCf and conditional-format.ts's own readCondFmtGroup need: ct/cp/cce1/cce2, the dxf trailer's own bytes (its length inferred from the record's total size the same way this file always has, since [MS-XLS] never states it directly), and the two formula operands. Returns undefined for a truncated/malformed record rather than throwing, so a caller can fold this into its own try/catch or call it standalone. */
|
|
64
|
+
function parseCfBytes(record) {
|
|
64
65
|
try {
|
|
65
66
|
const cursor = new BlockCursor(record.blocks);
|
|
66
67
|
const ct = cursor.u8();
|
|
67
68
|
const cp = cursor.u8();
|
|
68
69
|
const cce1 = cursor.u16();
|
|
69
70
|
const cce2 = cursor.u16();
|
|
70
|
-
if (ct === 2) return;
|
|
71
|
-
const operator = CP_TO_OPERATOR.get(cp);
|
|
72
|
-
if (operator === void 0) return;
|
|
73
71
|
const dxfLength = recordByteLength(record) - 6 - cce1 - cce2;
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
72
|
+
return {
|
|
73
|
+
ct,
|
|
74
|
+
cp,
|
|
75
|
+
dxfBytes: cursor.take(dxfLength),
|
|
76
|
+
rgce1: cursor.take(cce1),
|
|
77
|
+
rgce2: cursor.take(cce2)
|
|
78
|
+
};
|
|
79
|
+
} catch (err) {
|
|
80
|
+
if (!(err instanceof BiffFormatError)) throw err;
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function readCf(record, ranges, formulaSheets) {
|
|
85
|
+
const parsed = parseCfBytes(record);
|
|
86
|
+
if (parsed === void 0) return;
|
|
87
|
+
const { ct, cp, dxfBytes, rgce1, rgce2 } = parsed;
|
|
88
|
+
if (ct === 2) return;
|
|
89
|
+
const operator = CP_TO_OPERATOR.get(cp);
|
|
90
|
+
if (operator === void 0) return;
|
|
91
|
+
if (rgce1.length === 0) return;
|
|
92
|
+
try {
|
|
78
93
|
const formula1 = parseFormulaText(rgce1, formulaSheets);
|
|
79
94
|
if (formula1 === void 0) return;
|
|
80
95
|
return {
|
|
81
96
|
operator,
|
|
82
97
|
formula1,
|
|
83
|
-
formula2:
|
|
98
|
+
formula2: rgce2.length > 0 ? parseFormulaText(rgce2, formulaSheets) : void 0,
|
|
84
99
|
style: parseDxfStyle(dxfBytes),
|
|
85
100
|
ranges
|
|
86
101
|
};
|
|
@@ -89,16 +104,20 @@ function readCf(record, ranges, formulaSheets) {
|
|
|
89
104
|
return;
|
|
90
105
|
}
|
|
91
106
|
}
|
|
107
|
+
const DEGRADED_CONDFMT_GROUP = {
|
|
108
|
+
formats: [],
|
|
109
|
+
recordsConsumed: 1,
|
|
110
|
+
nID: 0,
|
|
111
|
+
ranges: [],
|
|
112
|
+
rawCfs: []
|
|
113
|
+
};
|
|
92
114
|
function readCondFmtGroup(records, startIndex, formulaSheets) {
|
|
93
115
|
const condFmt = records[startIndex];
|
|
94
|
-
if (condFmt === void 0) return
|
|
95
|
-
formats: [],
|
|
96
|
-
recordsConsumed: 1
|
|
97
|
-
};
|
|
116
|
+
if (condFmt === void 0) return DEGRADED_CONDFMT_GROUP;
|
|
98
117
|
try {
|
|
99
118
|
const cursor = new BlockCursor(condFmt.blocks);
|
|
100
119
|
const ccf = cursor.u16();
|
|
101
|
-
cursor.
|
|
120
|
+
const nID = cursor.u16() >>> 1 & 32767;
|
|
102
121
|
cursor.skip(8);
|
|
103
122
|
const crefCount = cursor.u16();
|
|
104
123
|
const ranges = [];
|
|
@@ -115,25 +134,29 @@ function readCondFmtGroup(records, startIndex, formulaSheets) {
|
|
|
115
134
|
});
|
|
116
135
|
}
|
|
117
136
|
const formats = [];
|
|
137
|
+
const rawCfs = [];
|
|
118
138
|
for (let offset = 1; offset <= ccf; offset += 1) {
|
|
119
139
|
const cfRecord = records[startIndex + offset];
|
|
120
|
-
if (cfRecord?.type !== 433) return
|
|
121
|
-
formats: [],
|
|
122
|
-
recordsConsumed: 1
|
|
123
|
-
};
|
|
140
|
+
if (cfRecord?.type !== 433) return DEGRADED_CONDFMT_GROUP;
|
|
124
141
|
const format = readCf(cfRecord, ranges, formulaSheets);
|
|
125
142
|
if (format !== void 0) formats.push(format);
|
|
143
|
+
const parsed = parseCfBytes(cfRecord);
|
|
144
|
+
rawCfs.push(parsed === void 0 ? void 0 : {
|
|
145
|
+
ct: parsed.ct,
|
|
146
|
+
cp: parsed.cp,
|
|
147
|
+
rgce1: parsed.rgce1
|
|
148
|
+
});
|
|
126
149
|
}
|
|
127
150
|
return {
|
|
128
151
|
formats,
|
|
129
|
-
recordsConsumed: 1 + ccf
|
|
152
|
+
recordsConsumed: 1 + ccf,
|
|
153
|
+
nID,
|
|
154
|
+
ranges,
|
|
155
|
+
rawCfs
|
|
130
156
|
};
|
|
131
157
|
} catch (err) {
|
|
132
158
|
if (!(err instanceof BiffFormatError)) throw err;
|
|
133
|
-
return
|
|
134
|
-
formats: [],
|
|
135
|
-
recordsConsumed: 1
|
|
136
|
-
};
|
|
159
|
+
return DEGRADED_CONDFMT_GROUP;
|
|
137
160
|
}
|
|
138
161
|
}
|
|
139
162
|
//#endregion
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as readDv, t as RawDataValidation } from "../data-validation-
|
|
1
|
+
import { n as readDv, t as RawDataValidation } from "../data-validation-BgUt8CbE.cjs";
|
|
2
2
|
export { RawDataValidation, readDv };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as readDv, t as RawDataValidation } from "../data-validation-
|
|
1
|
+
import { n as readDv, t as RawDataValidation } from "../data-validation-Cq6iD8lB.js";
|
|
2
2
|
export { RawDataValidation, readDv };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as FormulaSheetContext } from "../ptg-
|
|
1
|
+
import { r as FormulaSheetContext } from "../ptg-75v7MhaA.cjs";
|
|
2
2
|
import { n as Substream, t as RecordGroup } from "../substreams-XDNgDvRJ.cjs";
|
|
3
3
|
import { BlipImage } from "../drawing/blips.cjs";
|
|
4
4
|
import { RawColumn, RawRow } from "./sheet.cjs";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as FormulaSheetContext } from "../ptg-
|
|
1
|
+
import { r as FormulaSheetContext } from "../ptg-75v7MhaA.js";
|
|
2
2
|
import { n as Substream, t as RecordGroup } from "../substreams-CgTHx5kX.js";
|
|
3
3
|
import { BlipImage } from "../drawing/blips.js";
|
|
4
4
|
import { RawColumn, RawRow } from "./sheet.js";
|
|
@@ -4,7 +4,8 @@ const require_biff_records = require("../biff/records.cjs");
|
|
|
4
4
|
const require_biff_cursor = require("../biff/cursor.cjs");
|
|
5
5
|
let archive_codec = require("archive-codec");
|
|
6
6
|
//#region src/workbook/encryption.ts
|
|
7
|
-
/** [MS-XLS] 2.4.117's own wEncryptionType
|
|
7
|
+
/** [MS-XLS] 2.4.117's own wEncryptionType. */
|
|
8
|
+
const ENCRYPTION_TYPE_XOR = 0;
|
|
8
9
|
const ENCRYPTION_TYPE_RC4 = 1;
|
|
9
10
|
/** [MS-OFFCRYPTO] 2.3.6.1's own EncryptionVersionInfo: vMajor/vMinor MUST both be 1 for the "RC4 encryption header" this module reads. vMajor 2/3/4 with vMinor 2 names the RC4 CryptoAPI encryption header instead (2.3.5.1), a different, unimplemented header shape. */
|
|
10
11
|
const RC4_HEADER_VERSION_MAJOR = 1;
|
|
@@ -21,29 +22,28 @@ const NEVER_ENCRYPTED_RECORD_TYPES = /* @__PURE__ */ new Set([
|
|
|
21
22
|
406,
|
|
22
23
|
312
|
|
23
24
|
]);
|
|
24
|
-
/** Reads FilePass's own fields, rejecting the
|
|
25
|
-
function
|
|
25
|
+
/** Reads FilePass's own fields, dispatching on wEncryptionType, and rejecting RC4 CryptoAPI (the one scheme this module does not implement) with a specific message rather than a generic parse failure. */
|
|
26
|
+
function readFilePassHeader(filePassRecord) {
|
|
26
27
|
const cursor = new require_biff_cursor.BlockCursor([filePassRecord.data]);
|
|
27
28
|
const encryptionType = cursor.u16();
|
|
28
|
-
if (encryptionType
|
|
29
|
+
if (encryptionType === ENCRYPTION_TYPE_XOR) return {
|
|
30
|
+
kind: "xor",
|
|
31
|
+
key: cursor.u16(),
|
|
32
|
+
verificationBytes: cursor.u16()
|
|
33
|
+
};
|
|
34
|
+
if (encryptionType !== ENCRYPTION_TYPE_RC4) throw new require_biff_records.BiffFormatError(`workbook uses FilePass wEncryptionType 0x${encryptionType.toString(16).padStart(4, "0")}, which this reader does not decrypt`);
|
|
29
35
|
const versionMajor = cursor.u16();
|
|
30
36
|
const versionMinor = cursor.u16();
|
|
31
37
|
if (versionMajor !== RC4_HEADER_VERSION_MAJOR || versionMinor !== RC4_HEADER_VERSION_MINOR) throw new require_biff_records.BiffFormatError(`workbook uses RC4 CryptoAPI encryption (EncryptionVersionInfo ${versionMajor}.${versionMinor}), which this reader does not decrypt`);
|
|
32
38
|
return {
|
|
39
|
+
kind: "rc4",
|
|
33
40
|
salt: cursor.take(archive_codec.OFFICE_RC4_VERIFIER_LENGTH),
|
|
34
41
|
encryptedVerifier: cursor.take(archive_codec.OFFICE_RC4_VERIFIER_LENGTH),
|
|
35
42
|
encryptedVerifierHash: cursor.take(archive_codec.OFFICE_RC4_VERIFIER_LENGTH)
|
|
36
43
|
};
|
|
37
44
|
}
|
|
38
|
-
/**
|
|
39
|
-
function
|
|
40
|
-
const decryptedVerifier = (0, archive_codec.decryptOfficeRc4)(baseHash, 0, header.encryptedVerifier);
|
|
41
|
-
const decryptedVerifierHash = (0, archive_codec.decryptOfficeRc4)(baseHash, archive_codec.OFFICE_RC4_VERIFIER_LENGTH, header.encryptedVerifierHash);
|
|
42
|
-
const computedHash = (0, archive_codec.md5)(decryptedVerifier);
|
|
43
|
-
if (!(computedHash.length === decryptedVerifierHash.length && computedHash.every((byte, index) => byte === decryptedVerifierHash[index]))) throw new require_biff_records.BiffFormatError("incorrect password for RC4-encrypted workbook");
|
|
44
|
-
}
|
|
45
|
-
/** Decrypts one record's own data in place against the derived base hash, honouring every [MS-XLS] 2.2.10 exclusion: a never-encrypted record's data is returned unchanged, BoundSheet8's own lbPlyPos prefix is preserved while the rest of its data is decrypted, and everything else is decrypted whole. `record.offset` is the record's own header start, so its data begins `HEADER_SIZE` bytes further into the stream -- the position [MS-OFFCRYPTO]'s block-keyed keystream is defined against. */
|
|
46
|
-
function decryptRecord(record, baseHash) {
|
|
45
|
+
/** Decrypts one record's own data against the derived RC4 base hash, honouring every [MS-XLS] 2.2.10 exclusion: a never-encrypted record's data is returned unchanged, BoundSheet8's own lbPlyPos prefix is preserved while the rest of its data is decrypted, and everything else is decrypted whole. `record.offset` is the record's own header start, so its data begins `HEADER_SIZE` bytes further into the stream -- the position [MS-OFFCRYPTO]'s block-keyed keystream is defined against. */
|
|
46
|
+
function decryptRecordRc4(record, baseHash) {
|
|
47
47
|
if (NEVER_ENCRYPTED_RECORD_TYPES.has(record.type)) return record;
|
|
48
48
|
const dataOffset = record.offset + 4;
|
|
49
49
|
if (record.type === 133) {
|
|
@@ -62,17 +62,64 @@ function decryptRecord(record, baseHash) {
|
|
|
62
62
|
data: (0, archive_codec.decryptOfficeRc4)(baseHash, dataOffset, record.data)
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
|
+
/** [MS-XLS] 2.2.10's own XorArrayIndex rule for a record's decrypted span starting `spanOffset` bytes into the Workbook stream: `(streamOffset + recordDataLength) % 16`, where `recordDataLength` is the record's own FULL declared data length -- not the length of `spanOffset`'s own remaining span, which for BoundSheet8 is 4 bytes shorter than the record's own declared size. Confirmed against Apache POI's own `XORDecryptor.invokeCipher` comment ("XorArrayIndex = (FileOffset + Data.Length) % 16") and LibreOffice's `XclImpBiff5Decrypter::OnUpdate`, and directly against a real Excel-generated XOR-obfuscated fixture -- see archive-codec's own crypto/xor-obfuscation.test.ts. */
|
|
66
|
+
function xorArrayIndexFor(spanOffset, recordDataLength) {
|
|
67
|
+
return (spanOffset + recordDataLength) % archive_codec.XOR_OBFUSCATION_ARRAY_LENGTH;
|
|
68
|
+
}
|
|
69
|
+
/** Decrypts one record's own data against the derived XOR obfuscation array, honouring the same [MS-XLS] 2.2.10 exclusions decryptRecordRc4 does. */
|
|
70
|
+
function decryptRecordXor(record, array) {
|
|
71
|
+
if (NEVER_ENCRYPTED_RECORD_TYPES.has(record.type)) return record;
|
|
72
|
+
const dataOffset = record.offset + 4;
|
|
73
|
+
if (record.type === 133) {
|
|
74
|
+
const lbPlyPos = record.data.subarray(0, BOUNDSHEET8_LBPLYPOS_SIZE);
|
|
75
|
+
const spanOffset = dataOffset + BOUNDSHEET8_LBPLYPOS_SIZE;
|
|
76
|
+
const decryptedRest = (0, archive_codec.decryptXorObfuscationMethod1)(array, record.data.subarray(BOUNDSHEET8_LBPLYPOS_SIZE), xorArrayIndexFor(spanOffset, record.data.length));
|
|
77
|
+
const data = new Uint8Array(record.data.length);
|
|
78
|
+
data.set(lbPlyPos, 0);
|
|
79
|
+
data.set(decryptedRest, BOUNDSHEET8_LBPLYPOS_SIZE);
|
|
80
|
+
return {
|
|
81
|
+
...record,
|
|
82
|
+
data
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
...record,
|
|
87
|
+
data: (0, archive_codec.decryptXorObfuscationMethod1)(array, record.data, xorArrayIndexFor(dataOffset, record.data.length))
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/** [MS-OFFCRYPTO] 2.3.6.4's own password verification: block 0's key decrypts EncryptedVerifier, and MD5 of the result must equal the same block's decryption of EncryptedVerifierHash. Both verifier fields sit in one continuous keystream starting at position 0, not two independently-reset ones -- though decryptOfficeRc4 regenerates its keystream fresh from block start on every call regardless, so decrypting them as two separate 16-byte calls at offsets 0 and 16 is equivalent to one 32-byte call, not merely close to it. */
|
|
91
|
+
function decryptWorkbookRecordsRc4(records, header, password) {
|
|
92
|
+
const baseHash = (0, archive_codec.deriveOfficeRc4BaseHash)(password, header.salt);
|
|
93
|
+
const decryptedVerifier = (0, archive_codec.decryptOfficeRc4)(baseHash, 0, header.encryptedVerifier);
|
|
94
|
+
const decryptedVerifierHash = (0, archive_codec.decryptOfficeRc4)(baseHash, archive_codec.OFFICE_RC4_VERIFIER_LENGTH, header.encryptedVerifierHash);
|
|
95
|
+
const computedHash = (0, archive_codec.md5)(decryptedVerifier);
|
|
96
|
+
if (!(computedHash.length === decryptedVerifierHash.length && computedHash.every((byte, index) => byte === decryptedVerifierHash[index]))) throw new require_biff_records.BiffFormatError("incorrect password for RC4-encrypted workbook");
|
|
97
|
+
return records.map((record) => decryptRecordRc4(record, baseHash));
|
|
98
|
+
}
|
|
99
|
+
/** Verifies the password against both of XORObfuscation's own fields (its `key` and `verificationBytes`, matching Apache POI's own `XORDecryptor.verifyPassword`, which checks both rather than either alone) before decrypting every record. */
|
|
100
|
+
function decryptWorkbookRecordsXor(records, header, password) {
|
|
101
|
+
let computedKey;
|
|
102
|
+
let computedVerifier;
|
|
103
|
+
try {
|
|
104
|
+
computedKey = (0, archive_codec.createXorObfuscationKey)(password);
|
|
105
|
+
computedVerifier = (0, archive_codec.createXorObfuscationPasswordVerifier)(password);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
if (error instanceof RangeError) throw new require_biff_records.BiffFormatError("incorrect password for XOR-obfuscated workbook");
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
if (computedKey !== header.key || computedVerifier !== header.verificationBytes) throw new require_biff_records.BiffFormatError("incorrect password for XOR-obfuscated workbook");
|
|
111
|
+
const array = (0, archive_codec.createXorObfuscationArray)(password, archive_codec.XOR_OBFUSCATION_ROTATE_DISTANCE_METHOD1);
|
|
112
|
+
return records.map((record) => decryptRecordXor(record, array));
|
|
113
|
+
}
|
|
65
114
|
/**
|
|
66
|
-
* Decrypts every record of a workbook stream protected by [MS-OFFCRYPTO] 2.3.6.1
|
|
115
|
+
* Decrypts every record of a workbook stream protected by [MS-XLS] 2.4.117's FilePass record, under whichever of the two schemes it names (the [MS-OFFCRYPTO] 2.3.6.1 RC4 encryption header, or 2.3.7's XOR obfuscation), given the `FilePass` record already located within `records` and the password to decrypt it with.
|
|
67
116
|
*
|
|
68
|
-
* Throws `BiffFormatError` for a missing password, an incorrect one, or an encryption scheme this module does not implement (RC4 CryptoAPI
|
|
117
|
+
* Throws `BiffFormatError` for a missing password, an incorrect one, or an encryption scheme this module does not implement (RC4 CryptoAPI) -- there is no partial or best-effort result to return in any of those cases.
|
|
69
118
|
*/
|
|
70
119
|
function decryptWorkbookRecords(records, filePassRecord, password) {
|
|
71
|
-
const header =
|
|
72
|
-
if (password === void 0) throw new require_biff_records.BiffFormatError(
|
|
73
|
-
|
|
74
|
-
verifyOfficeRc4Password(baseHash, header);
|
|
75
|
-
return records.map((record) => decryptRecord(record, baseHash));
|
|
120
|
+
const header = readFilePassHeader(filePassRecord);
|
|
121
|
+
if (password === void 0) throw new require_biff_records.BiffFormatError(`workbook is ${header.kind === "xor" ? "XOR-obfuscated" : "RC4-encrypted"} (FilePass record); call readXlsContent with a password to decrypt it`);
|
|
122
|
+
return header.kind === "xor" ? decryptWorkbookRecordsXor(records, header, password) : decryptWorkbookRecordsRc4(records, header, password);
|
|
76
123
|
}
|
|
77
124
|
//#endregion
|
|
78
125
|
exports.decryptWorkbookRecords = decryptWorkbookRecords;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { n as BiffRecord } from "../records-G82UEVq8.cjs";
|
|
2
2
|
//#region src/workbook/encryption.d.ts
|
|
3
3
|
/**
|
|
4
|
-
* Decrypts every record of a workbook stream protected by [MS-OFFCRYPTO] 2.3.6.1
|
|
4
|
+
* Decrypts every record of a workbook stream protected by [MS-XLS] 2.4.117's FilePass record, under whichever of the two schemes it names (the [MS-OFFCRYPTO] 2.3.6.1 RC4 encryption header, or 2.3.7's XOR obfuscation), given the `FilePass` record already located within `records` and the password to decrypt it with.
|
|
5
5
|
*
|
|
6
|
-
* Throws `BiffFormatError` for a missing password, an incorrect one, or an encryption scheme this module does not implement (RC4 CryptoAPI
|
|
6
|
+
* Throws `BiffFormatError` for a missing password, an incorrect one, or an encryption scheme this module does not implement (RC4 CryptoAPI) -- there is no partial or best-effort result to return in any of those cases.
|
|
7
7
|
*/
|
|
8
8
|
declare function decryptWorkbookRecords(records: readonly BiffRecord[], filePassRecord: BiffRecord, password: string | undefined): readonly BiffRecord[];
|
|
9
9
|
//#endregion
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { n as BiffRecord } from "../records-G82UEVq8.js";
|
|
2
2
|
//#region src/workbook/encryption.d.ts
|
|
3
3
|
/**
|
|
4
|
-
* Decrypts every record of a workbook stream protected by [MS-OFFCRYPTO] 2.3.6.1
|
|
4
|
+
* Decrypts every record of a workbook stream protected by [MS-XLS] 2.4.117's FilePass record, under whichever of the two schemes it names (the [MS-OFFCRYPTO] 2.3.6.1 RC4 encryption header, or 2.3.7's XOR obfuscation), given the `FilePass` record already located within `records` and the password to decrypt it with.
|
|
5
5
|
*
|
|
6
|
-
* Throws `BiffFormatError` for a missing password, an incorrect one, or an encryption scheme this module does not implement (RC4 CryptoAPI
|
|
6
|
+
* Throws `BiffFormatError` for a missing password, an incorrect one, or an encryption scheme this module does not implement (RC4 CryptoAPI) -- there is no partial or best-effort result to return in any of those cases.
|
|
7
7
|
*/
|
|
8
8
|
declare function decryptWorkbookRecords(records: readonly BiffRecord[], filePassRecord: BiffRecord, password: string | undefined): readonly BiffRecord[];
|
|
9
9
|
//#endregion
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { RECORD_BOF } from "../biff/record-types.js";
|
|
2
2
|
import { BiffFormatError } from "../biff/records.js";
|
|
3
3
|
import { BlockCursor } from "../biff/cursor.js";
|
|
4
|
-
import { OFFICE_RC4_VERIFIER_LENGTH, decryptOfficeRc4, deriveOfficeRc4BaseHash, md5 } from "archive-codec";
|
|
4
|
+
import { OFFICE_RC4_VERIFIER_LENGTH, XOR_OBFUSCATION_ARRAY_LENGTH, XOR_OBFUSCATION_ROTATE_DISTANCE_METHOD1, createXorObfuscationArray, createXorObfuscationKey, createXorObfuscationPasswordVerifier, decryptOfficeRc4, decryptXorObfuscationMethod1, deriveOfficeRc4BaseHash, md5 } from "archive-codec";
|
|
5
5
|
//#region src/workbook/encryption.ts
|
|
6
|
-
/** [MS-XLS] 2.4.117's own wEncryptionType
|
|
6
|
+
/** [MS-XLS] 2.4.117's own wEncryptionType. */
|
|
7
|
+
const ENCRYPTION_TYPE_XOR = 0;
|
|
7
8
|
const ENCRYPTION_TYPE_RC4 = 1;
|
|
8
9
|
/** [MS-OFFCRYPTO] 2.3.6.1's own EncryptionVersionInfo: vMajor/vMinor MUST both be 1 for the "RC4 encryption header" this module reads. vMajor 2/3/4 with vMinor 2 names the RC4 CryptoAPI encryption header instead (2.3.5.1), a different, unimplemented header shape. */
|
|
9
10
|
const RC4_HEADER_VERSION_MAJOR = 1;
|
|
@@ -20,29 +21,28 @@ const NEVER_ENCRYPTED_RECORD_TYPES = /* @__PURE__ */ new Set([
|
|
|
20
21
|
406,
|
|
21
22
|
312
|
|
22
23
|
]);
|
|
23
|
-
/** Reads FilePass's own fields, rejecting the
|
|
24
|
-
function
|
|
24
|
+
/** Reads FilePass's own fields, dispatching on wEncryptionType, and rejecting RC4 CryptoAPI (the one scheme this module does not implement) with a specific message rather than a generic parse failure. */
|
|
25
|
+
function readFilePassHeader(filePassRecord) {
|
|
25
26
|
const cursor = new BlockCursor([filePassRecord.data]);
|
|
26
27
|
const encryptionType = cursor.u16();
|
|
27
|
-
if (encryptionType
|
|
28
|
+
if (encryptionType === ENCRYPTION_TYPE_XOR) return {
|
|
29
|
+
kind: "xor",
|
|
30
|
+
key: cursor.u16(),
|
|
31
|
+
verificationBytes: cursor.u16()
|
|
32
|
+
};
|
|
33
|
+
if (encryptionType !== ENCRYPTION_TYPE_RC4) throw new BiffFormatError(`workbook uses FilePass wEncryptionType 0x${encryptionType.toString(16).padStart(4, "0")}, which this reader does not decrypt`);
|
|
28
34
|
const versionMajor = cursor.u16();
|
|
29
35
|
const versionMinor = cursor.u16();
|
|
30
36
|
if (versionMajor !== RC4_HEADER_VERSION_MAJOR || versionMinor !== RC4_HEADER_VERSION_MINOR) throw new BiffFormatError(`workbook uses RC4 CryptoAPI encryption (EncryptionVersionInfo ${versionMajor}.${versionMinor}), which this reader does not decrypt`);
|
|
31
37
|
return {
|
|
38
|
+
kind: "rc4",
|
|
32
39
|
salt: cursor.take(OFFICE_RC4_VERIFIER_LENGTH),
|
|
33
40
|
encryptedVerifier: cursor.take(OFFICE_RC4_VERIFIER_LENGTH),
|
|
34
41
|
encryptedVerifierHash: cursor.take(OFFICE_RC4_VERIFIER_LENGTH)
|
|
35
42
|
};
|
|
36
43
|
}
|
|
37
|
-
/**
|
|
38
|
-
function
|
|
39
|
-
const decryptedVerifier = decryptOfficeRc4(baseHash, 0, header.encryptedVerifier);
|
|
40
|
-
const decryptedVerifierHash = decryptOfficeRc4(baseHash, OFFICE_RC4_VERIFIER_LENGTH, header.encryptedVerifierHash);
|
|
41
|
-
const computedHash = md5(decryptedVerifier);
|
|
42
|
-
if (!(computedHash.length === decryptedVerifierHash.length && computedHash.every((byte, index) => byte === decryptedVerifierHash[index]))) throw new BiffFormatError("incorrect password for RC4-encrypted workbook");
|
|
43
|
-
}
|
|
44
|
-
/** Decrypts one record's own data in place against the derived base hash, honouring every [MS-XLS] 2.2.10 exclusion: a never-encrypted record's data is returned unchanged, BoundSheet8's own lbPlyPos prefix is preserved while the rest of its data is decrypted, and everything else is decrypted whole. `record.offset` is the record's own header start, so its data begins `HEADER_SIZE` bytes further into the stream -- the position [MS-OFFCRYPTO]'s block-keyed keystream is defined against. */
|
|
45
|
-
function decryptRecord(record, baseHash) {
|
|
44
|
+
/** Decrypts one record's own data against the derived RC4 base hash, honouring every [MS-XLS] 2.2.10 exclusion: a never-encrypted record's data is returned unchanged, BoundSheet8's own lbPlyPos prefix is preserved while the rest of its data is decrypted, and everything else is decrypted whole. `record.offset` is the record's own header start, so its data begins `HEADER_SIZE` bytes further into the stream -- the position [MS-OFFCRYPTO]'s block-keyed keystream is defined against. */
|
|
45
|
+
function decryptRecordRc4(record, baseHash) {
|
|
46
46
|
if (NEVER_ENCRYPTED_RECORD_TYPES.has(record.type)) return record;
|
|
47
47
|
const dataOffset = record.offset + 4;
|
|
48
48
|
if (record.type === 133) {
|
|
@@ -61,17 +61,64 @@ function decryptRecord(record, baseHash) {
|
|
|
61
61
|
data: decryptOfficeRc4(baseHash, dataOffset, record.data)
|
|
62
62
|
};
|
|
63
63
|
}
|
|
64
|
+
/** [MS-XLS] 2.2.10's own XorArrayIndex rule for a record's decrypted span starting `spanOffset` bytes into the Workbook stream: `(streamOffset + recordDataLength) % 16`, where `recordDataLength` is the record's own FULL declared data length -- not the length of `spanOffset`'s own remaining span, which for BoundSheet8 is 4 bytes shorter than the record's own declared size. Confirmed against Apache POI's own `XORDecryptor.invokeCipher` comment ("XorArrayIndex = (FileOffset + Data.Length) % 16") and LibreOffice's `XclImpBiff5Decrypter::OnUpdate`, and directly against a real Excel-generated XOR-obfuscated fixture -- see archive-codec's own crypto/xor-obfuscation.test.ts. */
|
|
65
|
+
function xorArrayIndexFor(spanOffset, recordDataLength) {
|
|
66
|
+
return (spanOffset + recordDataLength) % XOR_OBFUSCATION_ARRAY_LENGTH;
|
|
67
|
+
}
|
|
68
|
+
/** Decrypts one record's own data against the derived XOR obfuscation array, honouring the same [MS-XLS] 2.2.10 exclusions decryptRecordRc4 does. */
|
|
69
|
+
function decryptRecordXor(record, array) {
|
|
70
|
+
if (NEVER_ENCRYPTED_RECORD_TYPES.has(record.type)) return record;
|
|
71
|
+
const dataOffset = record.offset + 4;
|
|
72
|
+
if (record.type === 133) {
|
|
73
|
+
const lbPlyPos = record.data.subarray(0, BOUNDSHEET8_LBPLYPOS_SIZE);
|
|
74
|
+
const spanOffset = dataOffset + BOUNDSHEET8_LBPLYPOS_SIZE;
|
|
75
|
+
const decryptedRest = decryptXorObfuscationMethod1(array, record.data.subarray(BOUNDSHEET8_LBPLYPOS_SIZE), xorArrayIndexFor(spanOffset, record.data.length));
|
|
76
|
+
const data = new Uint8Array(record.data.length);
|
|
77
|
+
data.set(lbPlyPos, 0);
|
|
78
|
+
data.set(decryptedRest, BOUNDSHEET8_LBPLYPOS_SIZE);
|
|
79
|
+
return {
|
|
80
|
+
...record,
|
|
81
|
+
data
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
...record,
|
|
86
|
+
data: decryptXorObfuscationMethod1(array, record.data, xorArrayIndexFor(dataOffset, record.data.length))
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/** [MS-OFFCRYPTO] 2.3.6.4's own password verification: block 0's key decrypts EncryptedVerifier, and MD5 of the result must equal the same block's decryption of EncryptedVerifierHash. Both verifier fields sit in one continuous keystream starting at position 0, not two independently-reset ones -- though decryptOfficeRc4 regenerates its keystream fresh from block start on every call regardless, so decrypting them as two separate 16-byte calls at offsets 0 and 16 is equivalent to one 32-byte call, not merely close to it. */
|
|
90
|
+
function decryptWorkbookRecordsRc4(records, header, password) {
|
|
91
|
+
const baseHash = deriveOfficeRc4BaseHash(password, header.salt);
|
|
92
|
+
const decryptedVerifier = decryptOfficeRc4(baseHash, 0, header.encryptedVerifier);
|
|
93
|
+
const decryptedVerifierHash = decryptOfficeRc4(baseHash, OFFICE_RC4_VERIFIER_LENGTH, header.encryptedVerifierHash);
|
|
94
|
+
const computedHash = md5(decryptedVerifier);
|
|
95
|
+
if (!(computedHash.length === decryptedVerifierHash.length && computedHash.every((byte, index) => byte === decryptedVerifierHash[index]))) throw new BiffFormatError("incorrect password for RC4-encrypted workbook");
|
|
96
|
+
return records.map((record) => decryptRecordRc4(record, baseHash));
|
|
97
|
+
}
|
|
98
|
+
/** Verifies the password against both of XORObfuscation's own fields (its `key` and `verificationBytes`, matching Apache POI's own `XORDecryptor.verifyPassword`, which checks both rather than either alone) before decrypting every record. */
|
|
99
|
+
function decryptWorkbookRecordsXor(records, header, password) {
|
|
100
|
+
let computedKey;
|
|
101
|
+
let computedVerifier;
|
|
102
|
+
try {
|
|
103
|
+
computedKey = createXorObfuscationKey(password);
|
|
104
|
+
computedVerifier = createXorObfuscationPasswordVerifier(password);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
if (error instanceof RangeError) throw new BiffFormatError("incorrect password for XOR-obfuscated workbook");
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
if (computedKey !== header.key || computedVerifier !== header.verificationBytes) throw new BiffFormatError("incorrect password for XOR-obfuscated workbook");
|
|
110
|
+
const array = createXorObfuscationArray(password, XOR_OBFUSCATION_ROTATE_DISTANCE_METHOD1);
|
|
111
|
+
return records.map((record) => decryptRecordXor(record, array));
|
|
112
|
+
}
|
|
64
113
|
/**
|
|
65
|
-
* Decrypts every record of a workbook stream protected by [MS-OFFCRYPTO] 2.3.6.1
|
|
114
|
+
* Decrypts every record of a workbook stream protected by [MS-XLS] 2.4.117's FilePass record, under whichever of the two schemes it names (the [MS-OFFCRYPTO] 2.3.6.1 RC4 encryption header, or 2.3.7's XOR obfuscation), given the `FilePass` record already located within `records` and the password to decrypt it with.
|
|
66
115
|
*
|
|
67
|
-
* Throws `BiffFormatError` for a missing password, an incorrect one, or an encryption scheme this module does not implement (RC4 CryptoAPI
|
|
116
|
+
* Throws `BiffFormatError` for a missing password, an incorrect one, or an encryption scheme this module does not implement (RC4 CryptoAPI) -- there is no partial or best-effort result to return in any of those cases.
|
|
68
117
|
*/
|
|
69
118
|
function decryptWorkbookRecords(records, filePassRecord, password) {
|
|
70
|
-
const header =
|
|
71
|
-
if (password === void 0) throw new BiffFormatError(
|
|
72
|
-
|
|
73
|
-
verifyOfficeRc4Password(baseHash, header);
|
|
74
|
-
return records.map((record) => decryptRecord(record, baseHash));
|
|
119
|
+
const header = readFilePassHeader(filePassRecord);
|
|
120
|
+
if (password === void 0) throw new BiffFormatError(`workbook is ${header.kind === "xor" ? "XOR-obfuscated" : "RC4-encrypted"} (FilePass record); call readXlsContent with a password to decrypt it`);
|
|
121
|
+
return header.kind === "xor" ? decryptWorkbookRecordsXor(records, header, password) : decryptWorkbookRecordsRc4(records, header, password);
|
|
75
122
|
}
|
|
76
123
|
//#endregion
|
|
77
124
|
export { decryptWorkbookRecords };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { E as XfDecorationFields } from "../xf-colors-
|
|
1
|
+
import { E as XfDecorationFields } from "../xf-colors-aeH9Ax3Q.cjs";
|
|
2
2
|
import { t as PrintNamePlanEntry } from "../print-names-B2OR0fvs.cjs";
|
|
3
3
|
import { Alignment, Color } from "document-schema.js";
|
|
4
4
|
//#region src/workbook/globals-writer.d.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { E as XfDecorationFields } from "../xf-colors-
|
|
1
|
+
import { E as XfDecorationFields } from "../xf-colors-kdNsiNYt.js";
|
|
2
2
|
import { t as PrintNamePlanEntry } from "../print-names-BXcmmwVi.js";
|
|
3
3
|
import { Alignment, Color } from "document-schema.js";
|
|
4
4
|
//#region src/workbook/globals-writer.d.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as SheetRange, t as ExternalSheetLabel } from "../ptg-
|
|
1
|
+
import { a as SheetRange, t as ExternalSheetLabel } from "../ptg-75v7MhaA.cjs";
|
|
2
2
|
import { t as RecordGroup } from "../substreams-XDNgDvRJ.cjs";
|
|
3
|
-
import { E as XfDecorationFields, w as XfAlignmentFields } from "../xf-colors-
|
|
3
|
+
import { E as XfDecorationFields, w as XfAlignmentFields } from "../xf-colors-aeH9Ax3Q.cjs";
|
|
4
4
|
import { n as SheetPrintNames } from "../print-names-B2OR0fvs.cjs";
|
|
5
5
|
import { Color } from "document-schema.js";
|
|
6
6
|
//#region src/workbook/globals.d.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as SheetRange, t as ExternalSheetLabel } from "../ptg-
|
|
1
|
+
import { a as SheetRange, t as ExternalSheetLabel } from "../ptg-75v7MhaA.js";
|
|
2
2
|
import { t as RecordGroup } from "../substreams-CgTHx5kX.js";
|
|
3
|
-
import { E as XfDecorationFields, w as XfAlignmentFields } from "../xf-colors-
|
|
3
|
+
import { E as XfDecorationFields, w as XfAlignmentFields } from "../xf-colors-kdNsiNYt.js";
|
|
4
4
|
import { n as SheetPrintNames } from "../print-names-BXcmmwVi.js";
|
|
5
5
|
import { Color } from "document-schema.js";
|
|
6
6
|
//#region src/workbook/globals.d.ts
|
package/dist/workbook/sheet.cjs
CHANGED
|
@@ -11,6 +11,7 @@ const require_biff_ptg = require("../biff/ptg.cjs");
|
|
|
11
11
|
const require_biff_rk = require("../biff/rk.cjs");
|
|
12
12
|
const require_workbook_conditional_format = require("./conditional-format.cjs");
|
|
13
13
|
const require_workbook_conditional_format_12 = require("./conditional-format-12.cjs");
|
|
14
|
+
const require_workbook_conditional_format_ex = require("./conditional-format-ex.cjs");
|
|
14
15
|
const require_workbook_data_validation = require("./data-validation.cjs");
|
|
15
16
|
//#region src/workbook/sheet.ts
|
|
16
17
|
/** Row record flag bits, in the 32-bit field following unused1 ([MS-XLS] 2.4.221). */
|
|
@@ -116,6 +117,7 @@ function readSheetRecords(records, sharedStrings, formulaSheets = EMPTY_FORMULA_
|
|
|
116
117
|
const dataValidations = [];
|
|
117
118
|
const conditionalFormats = [];
|
|
118
119
|
const conditionalFormats12 = [];
|
|
120
|
+
const legacyCondFmtsByNID = /* @__PURE__ */ new Map();
|
|
119
121
|
let usedRange;
|
|
120
122
|
const marginsPt = {};
|
|
121
123
|
const rowBreaks = [];
|
|
@@ -148,6 +150,10 @@ function readSheetRecords(records, sharedStrings, formulaSheets = EMPTY_FORMULA_
|
|
|
148
150
|
case 432: {
|
|
149
151
|
const group = require_workbook_conditional_format.readCondFmtGroup(records, index, formulaSheets);
|
|
150
152
|
conditionalFormats.push(...group.formats);
|
|
153
|
+
legacyCondFmtsByNID.set(group.nID, {
|
|
154
|
+
ranges: group.ranges,
|
|
155
|
+
cfs: group.rawCfs
|
|
156
|
+
});
|
|
151
157
|
index += group.recordsConsumed - 1;
|
|
152
158
|
break;
|
|
153
159
|
}
|
|
@@ -157,6 +163,11 @@ function readSheetRecords(records, sharedStrings, formulaSheets = EMPTY_FORMULA_
|
|
|
157
163
|
index += group.recordsConsumed - 1;
|
|
158
164
|
break;
|
|
159
165
|
}
|
|
166
|
+
case require_biff_record_types.RECORD_CFEX: {
|
|
167
|
+
const format = require_workbook_conditional_format_ex.readCfEx(record, legacyCondFmtsByNID, formulaSheets);
|
|
168
|
+
if (format !== void 0) conditionalFormats12.push(format);
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
160
171
|
case 513:
|
|
161
172
|
cells.push(readBlank(record));
|
|
162
173
|
break;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { n as SetupFields } from "../print-setup-B_ihDvm5.cjs";
|
|
2
|
-
import { r as FormulaSheetContext } from "../ptg-
|
|
2
|
+
import { r as FormulaSheetContext } from "../ptg-75v7MhaA.cjs";
|
|
3
3
|
import { t as RecordGroup } from "../substreams-XDNgDvRJ.cjs";
|
|
4
|
-
import {
|
|
5
|
-
import { a as RawConditionalFormat12 } from "../conditional-format-12-
|
|
6
|
-
import { t as RawDataValidation } from "../data-validation-
|
|
4
|
+
import { r as RawConditionalFormat } from "../conditional-format-Cth_afMy.cjs";
|
|
5
|
+
import { a as RawConditionalFormat12 } from "../conditional-format-12-D65-ruTq.cjs";
|
|
6
|
+
import { t as RawDataValidation } from "../data-validation-BgUt8CbE.cjs";
|
|
7
7
|
//#region src/workbook/sheet.d.ts
|
|
8
8
|
/** A cell's value as its own record carries it, before number-format classification decides whether a number is really a date, a percentage, or an amount of money. */
|
|
9
9
|
type RawCellValue = {
|
package/dist/workbook/sheet.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { n as SetupFields } from "../print-setup-B_ihDvm5.js";
|
|
2
|
-
import { r as FormulaSheetContext } from "../ptg-
|
|
2
|
+
import { r as FormulaSheetContext } from "../ptg-75v7MhaA.js";
|
|
3
3
|
import { t as RecordGroup } from "../substreams-CgTHx5kX.js";
|
|
4
|
-
import {
|
|
5
|
-
import { a as RawConditionalFormat12 } from "../conditional-format-12-
|
|
6
|
-
import { t as RawDataValidation } from "../data-validation-
|
|
4
|
+
import { r as RawConditionalFormat } from "../conditional-format-BRMUTRP1.js";
|
|
5
|
+
import { a as RawConditionalFormat12 } from "../conditional-format-12-C3nwy1f8.js";
|
|
6
|
+
import { t as RawDataValidation } from "../data-validation-Cq6iD8lB.js";
|
|
7
7
|
//#region src/workbook/sheet.d.ts
|
|
8
8
|
/** A cell's value as its own record carries it, before number-format classification decides whether a number is really a date, a percentage, or an amount of money. */
|
|
9
9
|
type RawCellValue = {
|
package/dist/workbook/sheet.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RECORD_CONDFMT12 } from "../biff/record-types.js";
|
|
1
|
+
import { RECORD_CFEX, RECORD_CONDFMT12 } from "../biff/record-types.js";
|
|
2
2
|
import { BiffFormatError } from "../biff/records.js";
|
|
3
3
|
import { columnWidthToPoints, inchesToPoints, twipsToPoints } from "../units.js";
|
|
4
4
|
import { unpackSetupFlags } from "../biff/print-setup.js";
|
|
@@ -10,6 +10,7 @@ import { parseFormulaText, readPtgExpBase } from "../biff/ptg.js";
|
|
|
10
10
|
import { decodeRkNumber } from "../biff/rk.js";
|
|
11
11
|
import { readCondFmtGroup } from "./conditional-format.js";
|
|
12
12
|
import { readCondFmt12Group } from "./conditional-format-12.js";
|
|
13
|
+
import { readCfEx } from "./conditional-format-ex.js";
|
|
13
14
|
import { readDv } from "./data-validation.js";
|
|
14
15
|
//#region src/workbook/sheet.ts
|
|
15
16
|
/** Row record flag bits, in the 32-bit field following unused1 ([MS-XLS] 2.4.221). */
|
|
@@ -115,6 +116,7 @@ function readSheetRecords(records, sharedStrings, formulaSheets = EMPTY_FORMULA_
|
|
|
115
116
|
const dataValidations = [];
|
|
116
117
|
const conditionalFormats = [];
|
|
117
118
|
const conditionalFormats12 = [];
|
|
119
|
+
const legacyCondFmtsByNID = /* @__PURE__ */ new Map();
|
|
118
120
|
let usedRange;
|
|
119
121
|
const marginsPt = {};
|
|
120
122
|
const rowBreaks = [];
|
|
@@ -147,6 +149,10 @@ function readSheetRecords(records, sharedStrings, formulaSheets = EMPTY_FORMULA_
|
|
|
147
149
|
case 432: {
|
|
148
150
|
const group = readCondFmtGroup(records, index, formulaSheets);
|
|
149
151
|
conditionalFormats.push(...group.formats);
|
|
152
|
+
legacyCondFmtsByNID.set(group.nID, {
|
|
153
|
+
ranges: group.ranges,
|
|
154
|
+
cfs: group.rawCfs
|
|
155
|
+
});
|
|
150
156
|
index += group.recordsConsumed - 1;
|
|
151
157
|
break;
|
|
152
158
|
}
|
|
@@ -156,6 +162,11 @@ function readSheetRecords(records, sharedStrings, formulaSheets = EMPTY_FORMULA_
|
|
|
156
162
|
index += group.recordsConsumed - 1;
|
|
157
163
|
break;
|
|
158
164
|
}
|
|
165
|
+
case RECORD_CFEX: {
|
|
166
|
+
const format = readCfEx(record, legacyCondFmtsByNID, formulaSheets);
|
|
167
|
+
if (format !== void 0) conditionalFormats12.push(format);
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
159
170
|
case 513:
|
|
160
171
|
cells.push(readBlank(record));
|
|
161
172
|
break;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as BlockCursor } from "./cursor-
|
|
1
|
+
import { t as BlockCursor } from "./cursor-CnEuX5qE.cjs";
|
|
2
2
|
import { Alignment, Color, ContentBorder, ContentCellFill, ContentCellPatternType } from "document-schema.js";
|
|
3
3
|
//#region src/biff/xf-colors.d.ts
|
|
4
4
|
/** FLSNULL: no fill pattern -- the cell's fill colour fields carry no meaning. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as BlockCursor } from "./cursor-
|
|
1
|
+
import { t as BlockCursor } from "./cursor-CnEuX5qE.js";
|
|
2
2
|
import { Alignment, Color, ContentBorder, ContentCellFill, ContentCellPatternType } from "document-schema.js";
|
|
3
3
|
//#region src/biff/xf-colors.d.ts
|
|
4
4
|
/** FLSNULL: no fill pattern -- the cell's fill colour fields carry no meaning. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xls-codec",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.10.0",
|
|
4
4
|
"description": "Hand-written reader for the legacy Excel Binary File Format (.xls, BIFF8) as specified by [MS-XLS], mapping a workbook's record stream onto the shared document-schema.js spreadsheet model - the .xls codec for the documents.js family.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
},
|
|
71
71
|
"packageManager": "pnpm@11.6.0",
|
|
72
72
|
"dependencies": {
|
|
73
|
-
"archive-codec": "1.
|
|
73
|
+
"archive-codec": "1.10.0",
|
|
74
74
|
"document-schema.js": "7.5.0",
|
|
75
75
|
"excel-number-format": "1.1.1"
|
|
76
76
|
},
|