xls-codec 1.0.2 → 2.0.1
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 +128 -23
- package/dist/biff/bof-writer.cjs +18 -0
- package/dist/biff/bof-writer.d.cts +5 -0
- package/dist/biff/bof-writer.d.ts +5 -0
- package/dist/biff/bof-writer.js +17 -0
- package/dist/biff/builder.cjs +48 -0
- package/dist/biff/builder.d.cts +17 -0
- package/dist/biff/builder.d.ts +17 -0
- package/dist/biff/builder.js +47 -0
- package/dist/biff/cursor.cjs +4 -0
- package/dist/biff/cursor.d.cts +1 -27
- package/dist/biff/cursor.d.ts +1 -27
- package/dist/biff/cursor.js +4 -0
- package/dist/biff/errors.cjs +7 -0
- package/dist/biff/errors.d.cts +3 -1
- package/dist/biff/errors.d.ts +3 -1
- package/dist/biff/errors.js +7 -1
- package/dist/biff/print-setup.cjs +108 -0
- package/dist/biff/print-setup.d.cts +2 -0
- package/dist/biff/print-setup.d.ts +2 -0
- package/dist/biff/print-setup.js +103 -0
- package/dist/biff/ptg-functions.cjs +383 -0
- package/dist/biff/ptg-functions.d.cts +7 -0
- package/dist/biff/ptg-functions.d.ts +7 -0
- package/dist/biff/ptg-functions.js +381 -0
- package/dist/biff/ptg.cjs +319 -0
- package/dist/biff/ptg.d.cts +2 -0
- package/dist/biff/ptg.d.ts +2 -0
- package/dist/biff/ptg.js +318 -0
- package/dist/biff/record-types.cjs +60 -0
- package/dist/biff/record-types.d.cts +41 -1
- package/dist/biff/record-types.d.ts +41 -1
- package/dist/biff/record-types.js +41 -1
- package/dist/biff/record-writer.cjs +29 -0
- package/dist/biff/record-writer.d.cts +7 -0
- package/dist/biff/record-writer.d.ts +7 -0
- package/dist/biff/record-writer.js +27 -0
- package/dist/biff/string-writer.cjs +58 -0
- package/dist/biff/string-writer.d.cts +13 -0
- package/dist/biff/string-writer.d.ts +13 -0
- package/dist/biff/string-writer.js +55 -0
- package/dist/biff/strings.d.cts +1 -1
- package/dist/biff/strings.d.ts +1 -1
- package/dist/biff/substreams.d.cts +1 -20
- package/dist/biff/substreams.d.ts +1 -20
- package/dist/biff/write-errors.cjs +10 -0
- package/dist/biff/write-errors.d.cts +6 -0
- package/dist/biff/write-errors.d.ts +6 -0
- package/dist/biff/write-errors.js +9 -0
- package/dist/biff/xf-colors.cjs +374 -0
- package/dist/biff/xf-colors.d.cts +2 -0
- package/dist/biff/xf-colors.d.ts +2 -0
- package/dist/biff/xf-colors.js +339 -0
- package/dist/biff/xf-writer.cjs +96 -0
- package/dist/biff/xf-writer.d.cts +29 -0
- package/dist/biff/xf-writer.d.ts +29 -0
- package/dist/biff/xf-writer.js +90 -0
- package/dist/container.cjs +13 -4
- package/dist/container.d.cts +10 -3
- package/dist/container.d.ts +10 -3
- package/dist/container.js +12 -4
- package/dist/content.cjs +123 -18
- package/dist/content.js +123 -18
- package/dist/cursor-VMtw9uVP.d.cts +30 -0
- package/dist/cursor-VMtw9uVP.d.ts +30 -0
- package/dist/index.cjs +71 -5
- package/dist/index.d.cts +20 -10
- package/dist/index.d.ts +20 -10
- package/dist/index.js +17 -7
- package/dist/metadata.cjs +14 -0
- package/dist/metadata.d.cts +6 -0
- package/dist/metadata.d.ts +6 -0
- package/dist/metadata.js +13 -0
- package/dist/print-names-D-njuzVw.d.cts +41 -0
- package/dist/print-names-DUlpVE00.d.ts +41 -0
- package/dist/print-setup-B_ihDvm5.d.cts +57 -0
- package/dist/print-setup-B_ihDvm5.d.ts +57 -0
- package/dist/ptg-B2K8t3js.d.cts +21 -0
- package/dist/ptg-B2K8t3js.d.ts +21 -0
- package/dist/serial.cjs +41 -0
- package/dist/serial.d.cts +7 -1
- package/dist/serial.d.ts +7 -1
- package/dist/serial.js +39 -1
- package/dist/substreams-D7dQiJbp.d.ts +21 -0
- package/dist/substreams-Ddtvn_Vr.d.cts +21 -0
- package/dist/units.cjs +37 -0
- package/dist/units.d.cts +15 -1
- package/dist/units.d.ts +15 -1
- package/dist/units.js +33 -1
- package/dist/workbook/globals-writer.cjs +122 -0
- package/dist/workbook/globals-writer.d.cts +39 -0
- package/dist/workbook/globals-writer.d.ts +39 -0
- package/dist/workbook/globals-writer.js +120 -0
- package/dist/workbook/globals.cjs +72 -8
- package/dist/workbook/globals.d.cts +26 -2
- package/dist/workbook/globals.d.ts +26 -2
- package/dist/workbook/globals.js +71 -7
- package/dist/workbook/print-names.cjs +241 -0
- package/dist/workbook/print-names.d.cts +2 -0
- package/dist/workbook/print-names.d.ts +2 -0
- package/dist/workbook/print-names.js +238 -0
- package/dist/workbook/sheet-writer.cjs +277 -0
- package/dist/workbook/sheet-writer.d.cts +12 -0
- package/dist/workbook/sheet-writer.d.ts +12 -0
- package/dist/workbook/sheet-writer.js +276 -0
- package/dist/workbook/sheet.cjs +125 -17
- package/dist/workbook/sheet.d.cts +38 -4
- package/dist/workbook/sheet.d.ts +38 -4
- package/dist/workbook/sheet.js +125 -17
- package/dist/write.cjs +311 -0
- package/dist/write.d.cts +13 -0
- package/dist/write.d.ts +13 -0
- package/dist/write.js +309 -0
- package/dist/written-cells.cjs +26 -0
- package/dist/written-cells.d.cts +16 -0
- package/dist/written-cells.d.ts +16 -0
- package/dist/written-cells.js +24 -0
- package/dist/xf-colors-CehHZtBy.d.cts +91 -0
- package/dist/xf-colors-CpykR3B9.d.ts +91 -0
- package/package.json +4 -3
- package/dist/number-format.cjs +0 -298
- package/dist/number-format.d.cts +0 -32
- package/dist/number-format.d.ts +0 -32
- package/dist/number-format.js +0 -296
package/dist/workbook/sheet.cjs
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
require("../biff/record-types.cjs");
|
|
3
3
|
const require_biff_records = require("../biff/records.cjs");
|
|
4
|
+
const require_units = require("../units.cjs");
|
|
5
|
+
const require_biff_print_setup = require("../biff/print-setup.cjs");
|
|
4
6
|
const require_biff_cursor = require("../biff/cursor.cjs");
|
|
5
7
|
const require_biff_strings = require("../biff/strings.cjs");
|
|
6
8
|
const require_biff_errors = require("../biff/errors.cjs");
|
|
9
|
+
const require_biff_ptg = require("../biff/ptg.cjs");
|
|
7
10
|
const require_biff_rk = require("../biff/rk.cjs");
|
|
8
|
-
const require_units = require("../units.cjs");
|
|
9
11
|
//#region src/workbook/sheet.ts
|
|
10
12
|
/** Row record flag bits, in the 32-bit field following unused1 ([MS-XLS] 2.4.221). */
|
|
11
13
|
const ROW_FLAG_HIDDEN = 32;
|
|
@@ -24,13 +26,25 @@ const FORMULA_VALUE_BLANK = 3;
|
|
|
24
26
|
const MUL_FIXED_BYTES = 6;
|
|
25
27
|
const MULRK_ENTRY_BYTES = 6;
|
|
26
28
|
const MULBLANK_ENTRY_BYTES = 2;
|
|
29
|
+
/** No sheets and no resolvable 3D references -- the default a caller with nothing formula-relevant to offer (every existing test fixture that predates formula-text recovery) gets, so a 3D reference simply fails to resolve rather than throwing on an absent context. */
|
|
30
|
+
const EMPTY_FORMULA_SHEET_CONTEXT = {
|
|
31
|
+
sheets: [],
|
|
32
|
+
sheetRanges: []
|
|
33
|
+
};
|
|
27
34
|
/** Reads one worksheet substream's records. */
|
|
28
|
-
function readSheetRecords(records, sharedStrings) {
|
|
35
|
+
function readSheetRecords(records, sharedStrings, formulaSheets = EMPTY_FORMULA_SHEET_CONTEXT) {
|
|
29
36
|
const cells = [];
|
|
30
37
|
const rows = [];
|
|
31
38
|
const columns = [];
|
|
32
39
|
const merges = [];
|
|
33
40
|
let usedRange;
|
|
41
|
+
const marginsPt = {};
|
|
42
|
+
const rowBreaks = [];
|
|
43
|
+
const columnBreaks = [];
|
|
44
|
+
let setup;
|
|
45
|
+
let printGridlines;
|
|
46
|
+
let printHeaders;
|
|
47
|
+
let fitToPage;
|
|
34
48
|
for (let index = 0; index < records.length; index += 1) {
|
|
35
49
|
const record = records[index];
|
|
36
50
|
if (record === void 0) continue;
|
|
@@ -73,22 +87,108 @@ function readSheetRecords(records, sharedStrings) {
|
|
|
73
87
|
case 516:
|
|
74
88
|
cells.push(readLabel(record));
|
|
75
89
|
break;
|
|
76
|
-
case 6:
|
|
90
|
+
case 6:
|
|
91
|
+
cells.push(readFormula(record, stringResultAfter(records, index), formulaSheets));
|
|
92
|
+
break;
|
|
93
|
+
case 161:
|
|
94
|
+
setup = readSetup(record);
|
|
95
|
+
break;
|
|
96
|
+
case 38:
|
|
97
|
+
marginsPt.left = readMargin(record);
|
|
98
|
+
break;
|
|
99
|
+
case 39:
|
|
100
|
+
marginsPt.right = readMargin(record);
|
|
101
|
+
break;
|
|
102
|
+
case 40:
|
|
103
|
+
marginsPt.top = readMargin(record);
|
|
104
|
+
break;
|
|
105
|
+
case 41:
|
|
106
|
+
marginsPt.bottom = readMargin(record);
|
|
107
|
+
break;
|
|
108
|
+
case 43:
|
|
109
|
+
printGridlines = readGridlineBooleanRecord(record);
|
|
110
|
+
break;
|
|
111
|
+
case 42:
|
|
112
|
+
printHeaders = readBooleanRecord(record);
|
|
113
|
+
break;
|
|
114
|
+
case 129:
|
|
115
|
+
fitToPage = (new require_biff_cursor.BlockCursor(record.blocks).u16() & 256) !== 0;
|
|
116
|
+
break;
|
|
117
|
+
case 27:
|
|
118
|
+
rowBreaks.push(...readPageBreaks(record));
|
|
119
|
+
break;
|
|
120
|
+
case 26: columnBreaks.push(...readPageBreaks(record));
|
|
77
121
|
}
|
|
78
122
|
}
|
|
123
|
+
const print = {
|
|
124
|
+
marginsPt,
|
|
125
|
+
rowBreaks: ascendingDistinct(rowBreaks),
|
|
126
|
+
columnBreaks: ascendingDistinct(columnBreaks),
|
|
127
|
+
...setup === void 0 ? {} : { setup },
|
|
128
|
+
...printGridlines === void 0 ? {} : { printGridlines },
|
|
129
|
+
...printHeaders === void 0 ? {} : { printHeaders },
|
|
130
|
+
...fitToPage === void 0 ? {} : { fitToPage }
|
|
131
|
+
};
|
|
79
132
|
return usedRange === void 0 ? {
|
|
80
133
|
cells,
|
|
81
134
|
rows,
|
|
82
135
|
columns,
|
|
83
|
-
merges
|
|
136
|
+
merges,
|
|
137
|
+
print
|
|
84
138
|
} : {
|
|
85
139
|
cells,
|
|
86
140
|
rows,
|
|
87
141
|
columns,
|
|
88
142
|
merges,
|
|
89
|
-
usedRange
|
|
143
|
+
usedRange,
|
|
144
|
+
print
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
/** Setup ([MS-XLS] 2.4.257): iPaperSize, iScale, iPageStart, iFitWidth, iFitHeight, a flags word, iRes, iVRes, an eight-byte header margin, an eight-byte footer margin, and iCopies. The starting page number, the two print resolutions, the header/footer margins, and the copy count are read past: ContentSheetPrintSettings has no field for any of them, and Margins models only the four page edges. */
|
|
148
|
+
function readSetup(record) {
|
|
149
|
+
const cursor = new require_biff_cursor.BlockCursor(record.blocks);
|
|
150
|
+
const paperCode = cursor.u16();
|
|
151
|
+
const scalePercent = cursor.u16();
|
|
152
|
+
cursor.skip(2);
|
|
153
|
+
return {
|
|
154
|
+
paperCode,
|
|
155
|
+
scalePercent,
|
|
156
|
+
fitWidth: cursor.u16(),
|
|
157
|
+
fitHeight: cursor.u16(),
|
|
158
|
+
...require_biff_print_setup.unpackSetupFlags(cursor.u16())
|
|
90
159
|
};
|
|
91
160
|
}
|
|
161
|
+
/** Any of the four margin records ([MS-XLS] 2.4.151, 2.4.219, 2.4.328, 2.4.27): a single Xnum stating that margin in inches. All four share the identical one-field layout, so one reader serves them all. */
|
|
162
|
+
function readMargin(record) {
|
|
163
|
+
return require_units.inchesToPoints(new require_biff_cursor.BlockCursor(record.blocks).f64());
|
|
164
|
+
}
|
|
165
|
+
/** PrintRowCol ([MS-XLS] 2.4.203): a single 16-bit `Boolean` field (2.5.14), whose whole word is the value. [MS-XLS] 2.4.203's own value table states both 0x0000 and 0x0001 as "Row and column headers are not printed", which is a typo in that table rather than two spellings of one meaning -- the record exists precisely to distinguish them, and every real producer writes 1 for printed (confirmed against LibreOffice-written BIFF8, where a sheet with header printing enabled carries 0x0001 and one without carries 0x0000). */
|
|
166
|
+
function readBooleanRecord(record) {
|
|
167
|
+
return new require_biff_cursor.BlockCursor(record.blocks).u16() !== 0;
|
|
168
|
+
}
|
|
169
|
+
/** PrintGrid ([MS-XLS] 2.4.202): unlike PrintRowCol above, its 16-bit field is only ONE bit wide (`fPrintGrid`) with the remaining 15 "Undefined, and MUST be ignored" -- so a producer leaving anything in those bits would read as gridlines-on under a bare `!== 0` test. No real producer does (LibreOffice writes 0x0000/0x0001, confirmed against its own BIFF8 output), but masking to the one bit the spec actually defines is what the field's own layout says to do. */
|
|
170
|
+
function readGridlineBooleanRecord(record) {
|
|
171
|
+
return (new require_biff_cursor.BlockCursor(record.blocks).u16() & 1) !== 0;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* HorizontalPageBreaks ([MS-XLS] 2.4.142) and VerticalPageBreaks ([MS-XLS] 2.4.343): a count then that many six-byte structures, each an index on the break's own axis followed by the start and end of the break's extent on the other one.
|
|
175
|
+
*
|
|
176
|
+
* Only the index is taken. HorzBrk's colStart/colEnd and VertBrk's rowStart/rowEnd say how far along the perpendicular axis the break runs -- a BIFF8 page break can be partial -- and ContentSheetPrintSettings.manualBreaks models a break as a whole-axis index with no extent, so a partial break is carried as a full one rather than dropped. Both structures have the identical three-field shape, so one reader serves both.
|
|
177
|
+
*/
|
|
178
|
+
function readPageBreaks(record) {
|
|
179
|
+
const cursor = new require_biff_cursor.BlockCursor(record.blocks);
|
|
180
|
+
const count = cursor.u16();
|
|
181
|
+
const indices = [];
|
|
182
|
+
for (let index = 0; index < count; index += 1) {
|
|
183
|
+
indices.push(cursor.u16());
|
|
184
|
+
cursor.skip(4);
|
|
185
|
+
}
|
|
186
|
+
return indices;
|
|
187
|
+
}
|
|
188
|
+
/** Page-break indices, ascending and with duplicates collapsed: two records naming the same row are one page break as far as the schema's own index-only model of a break can express. */
|
|
189
|
+
function ascendingDistinct(values) {
|
|
190
|
+
return [...new Set(values)].sort((a, b) => a - b);
|
|
191
|
+
}
|
|
92
192
|
/**
|
|
93
193
|
* Finds the String record carrying a Formula's cached string result, if it has one.
|
|
94
194
|
*
|
|
@@ -314,28 +414,36 @@ function readLabel(record) {
|
|
|
314
414
|
fromFormula: false
|
|
315
415
|
};
|
|
316
416
|
}
|
|
417
|
+
/** The Formula record's own flags field ([MS-XLS] 2.4.127) and calculation cache, between the cached value and the compiled expression -- neither read for its own content; see the two-byte and four-byte skips in readFormula. */
|
|
418
|
+
const FORMULA_FLAGS_BYTES = 2;
|
|
419
|
+
const FORMULA_CALC_CACHE_BYTES = 4;
|
|
317
420
|
/**
|
|
318
|
-
* Formula ([MS-XLS] 2.4.127): a Cell, an eight-byte FormulaValue, flags, a calculation cache, then
|
|
421
|
+
* Formula ([MS-XLS] 2.4.127): a Cell, an eight-byte FormulaValue, flags, a calculation cache, then a CellParsedFormula -- a two-byte cce followed by exactly that many bytes of compiled Ptg tokens ([MS-XLS] 2.5.198.3).
|
|
319
422
|
*
|
|
320
|
-
*
|
|
423
|
+
* Both the cached value and the expression are read: the value from the FormulaValue exactly as before, and the expression by handing the token bytes to biff/ptg.ts's parseFormulaText, which resolves the whole Ptg vocabulary this reader supports and returns undefined for the constructs it does not (a shared formula, an array formula, a defined name, a natural-language reference, a genuinely external 3D reference -- see that module's own boundary). `formula` is attached only when it resolves; a cell it does not resolve for keeps exactly the behaviour this reader always had, its cached value present and `formula` absent.
|
|
321
424
|
*/
|
|
322
|
-
function readFormula(record, next) {
|
|
425
|
+
function readFormula(record, next, formulaSheets) {
|
|
323
426
|
const cursor = new require_biff_cursor.BlockCursor(record.blocks);
|
|
324
427
|
const header = readCellHeader(cursor);
|
|
325
428
|
const bytes = cursor.take(8);
|
|
326
429
|
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
value:
|
|
330
|
-
kind: "number",
|
|
331
|
-
value: view.getFloat64(0, true)
|
|
332
|
-
},
|
|
333
|
-
fromFormula: true
|
|
430
|
+
const value = view.getUint16(6, true) === FORMULA_VALUE_TAGGED ? taggedFormulaValue(view, next) : {
|
|
431
|
+
kind: "number",
|
|
432
|
+
value: view.getFloat64(0, true)
|
|
334
433
|
};
|
|
335
|
-
|
|
434
|
+
cursor.skip(FORMULA_FLAGS_BYTES);
|
|
435
|
+
cursor.skip(FORMULA_CALC_CACHE_BYTES);
|
|
436
|
+
const cce = cursor.u16();
|
|
437
|
+
const formula = require_biff_ptg.parseFormulaText(cursor.take(cce), formulaSheets);
|
|
438
|
+
return formula === void 0 ? {
|
|
336
439
|
...header,
|
|
337
|
-
value
|
|
440
|
+
value,
|
|
338
441
|
fromFormula: true
|
|
442
|
+
} : {
|
|
443
|
+
...header,
|
|
444
|
+
value,
|
|
445
|
+
fromFormula: true,
|
|
446
|
+
formula
|
|
339
447
|
};
|
|
340
448
|
}
|
|
341
449
|
/** The non-numeric readings of a FormulaValue ([MS-XLS] 2.5.133), selected by its first byte. */
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as SetupFields } from "../print-setup-B_ihDvm5.cjs";
|
|
2
|
+
import { t as FormulaSheetContext } from "../ptg-B2K8t3js.cjs";
|
|
3
|
+
import { t as RecordGroup } from "../substreams-Ddtvn_Vr.cjs";
|
|
2
4
|
//#region src/workbook/sheet.d.ts
|
|
3
5
|
/** 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. */
|
|
4
6
|
type RawCellValue = {
|
|
@@ -23,8 +25,10 @@ interface RawCell {
|
|
|
23
25
|
/** The cell's own ixfe ([MS-XLS] 2.5.168): an index into the globals substream's XF table. */
|
|
24
26
|
readonly xfIndex: number;
|
|
25
27
|
readonly value: RawCellValue;
|
|
26
|
-
/** True when the value is a Formula record's CACHED result rather than a literal.
|
|
28
|
+
/** True when the value is a Formula record's CACHED result rather than a literal. */
|
|
27
29
|
readonly fromFormula: boolean;
|
|
30
|
+
/** The formula's own text, recovered from its compiled Ptg token stream ([MS-XLS] 2.5.198), when every token in it is one this reader resolves -- absent for a shared-formula member, an array formula, a defined-name or natural-language reference, or a 3D reference into a genuinely external workbook (see biff/ptg.ts). */
|
|
31
|
+
readonly formula?: string;
|
|
28
32
|
}
|
|
29
33
|
interface RawRow {
|
|
30
34
|
readonly index: number;
|
|
@@ -43,6 +47,34 @@ interface RawRange {
|
|
|
43
47
|
readonly endRow: number;
|
|
44
48
|
readonly endColumn: number;
|
|
45
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* The page-setup half of a sheet's print settings, as the worksheet substream's own records carry them.
|
|
52
|
+
*
|
|
53
|
+
* Every field is optional because every record behind it is: [MS-XLS] 2.1.7.20.6's PAGESETUP production makes each of the four margins and Setup itself optional, and a sheet that never had its page setup touched carries none of them. An absent field is therefore "this file states nothing here", which is a different fact from "this file states the default", and content.ts is where the distinction is resolved into the required ContentSheetPrintSettings fields.
|
|
54
|
+
*
|
|
55
|
+
* The other half -- the print range and the repeated header bands -- is not here at all, because BIFF8 does not put it in the worksheet substream; see workbook/print-names.ts.
|
|
56
|
+
*/
|
|
57
|
+
interface RawPrintSettings {
|
|
58
|
+
/** The Setup record's own fields ([MS-XLS] 2.4.257). */
|
|
59
|
+
readonly setup?: SetupFields;
|
|
60
|
+
/** Each page margin in points, from its own record ([MS-XLS] 2.4.151, 2.4.219, 2.4.328, 2.4.27), which states it in inches. */
|
|
61
|
+
readonly marginsPt: {
|
|
62
|
+
readonly left?: number;
|
|
63
|
+
readonly right?: number;
|
|
64
|
+
readonly top?: number;
|
|
65
|
+
readonly bottom?: number;
|
|
66
|
+
};
|
|
67
|
+
/** PrintGrid's fPrintGrid ([MS-XLS] 2.4.202). */
|
|
68
|
+
readonly printGridlines?: boolean;
|
|
69
|
+
/** PrintRowCol's printRwCol ([MS-XLS] 2.4.203): whether the row and column headers print. */
|
|
70
|
+
readonly printHeaders?: boolean;
|
|
71
|
+
/** WsBool's fFitToPage ([MS-XLS] 2.4.351), which decides whether Setup's iScale or its iFitWidth/iFitHeight pair is the live one. */
|
|
72
|
+
readonly fitToPage?: boolean;
|
|
73
|
+
/** Zero-based row indices an explicit page break falls immediately above ([MS-XLS] 2.4.142), ascending and deduplicated. */
|
|
74
|
+
readonly rowBreaks: readonly number[];
|
|
75
|
+
/** Zero-based column indices an explicit page break falls immediately to the left of ([MS-XLS] 2.4.343). */
|
|
76
|
+
readonly columnBreaks: readonly number[];
|
|
77
|
+
}
|
|
46
78
|
/** One worksheet's records, read but not yet mapped onto the shared schema. */
|
|
47
79
|
interface RawSheet {
|
|
48
80
|
readonly cells: readonly RawCell[];
|
|
@@ -51,8 +83,10 @@ interface RawSheet {
|
|
|
51
83
|
readonly merges: readonly RawRange[];
|
|
52
84
|
/** The used range from the Dimensions record ([MS-XLS] 2.4.90), when the sheet declared one. */
|
|
53
85
|
readonly usedRange?: RawRange;
|
|
86
|
+
/** The sheet's own page setup, as far as its records state it. */
|
|
87
|
+
readonly print: RawPrintSettings;
|
|
54
88
|
}
|
|
55
89
|
/** Reads one worksheet substream's records. */
|
|
56
|
-
declare function readSheetRecords(records: readonly RecordGroup[], sharedStrings: readonly string[]): RawSheet;
|
|
90
|
+
declare function readSheetRecords(records: readonly RecordGroup[], sharedStrings: readonly string[], formulaSheets?: FormulaSheetContext): RawSheet;
|
|
57
91
|
//#endregion
|
|
58
|
-
export { RawCell, RawCellValue, RawColumn, RawRange, RawRow, RawSheet, readSheetRecords };
|
|
92
|
+
export { RawCell, RawCellValue, RawColumn, RawPrintSettings, RawRange, RawRow, RawSheet, readSheetRecords };
|
package/dist/workbook/sheet.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as SetupFields } from "../print-setup-B_ihDvm5.js";
|
|
2
|
+
import { t as FormulaSheetContext } from "../ptg-B2K8t3js.js";
|
|
3
|
+
import { t as RecordGroup } from "../substreams-D7dQiJbp.js";
|
|
2
4
|
//#region src/workbook/sheet.d.ts
|
|
3
5
|
/** 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. */
|
|
4
6
|
type RawCellValue = {
|
|
@@ -23,8 +25,10 @@ interface RawCell {
|
|
|
23
25
|
/** The cell's own ixfe ([MS-XLS] 2.5.168): an index into the globals substream's XF table. */
|
|
24
26
|
readonly xfIndex: number;
|
|
25
27
|
readonly value: RawCellValue;
|
|
26
|
-
/** True when the value is a Formula record's CACHED result rather than a literal.
|
|
28
|
+
/** True when the value is a Formula record's CACHED result rather than a literal. */
|
|
27
29
|
readonly fromFormula: boolean;
|
|
30
|
+
/** The formula's own text, recovered from its compiled Ptg token stream ([MS-XLS] 2.5.198), when every token in it is one this reader resolves -- absent for a shared-formula member, an array formula, a defined-name or natural-language reference, or a 3D reference into a genuinely external workbook (see biff/ptg.ts). */
|
|
31
|
+
readonly formula?: string;
|
|
28
32
|
}
|
|
29
33
|
interface RawRow {
|
|
30
34
|
readonly index: number;
|
|
@@ -43,6 +47,34 @@ interface RawRange {
|
|
|
43
47
|
readonly endRow: number;
|
|
44
48
|
readonly endColumn: number;
|
|
45
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* The page-setup half of a sheet's print settings, as the worksheet substream's own records carry them.
|
|
52
|
+
*
|
|
53
|
+
* Every field is optional because every record behind it is: [MS-XLS] 2.1.7.20.6's PAGESETUP production makes each of the four margins and Setup itself optional, and a sheet that never had its page setup touched carries none of them. An absent field is therefore "this file states nothing here", which is a different fact from "this file states the default", and content.ts is where the distinction is resolved into the required ContentSheetPrintSettings fields.
|
|
54
|
+
*
|
|
55
|
+
* The other half -- the print range and the repeated header bands -- is not here at all, because BIFF8 does not put it in the worksheet substream; see workbook/print-names.ts.
|
|
56
|
+
*/
|
|
57
|
+
interface RawPrintSettings {
|
|
58
|
+
/** The Setup record's own fields ([MS-XLS] 2.4.257). */
|
|
59
|
+
readonly setup?: SetupFields;
|
|
60
|
+
/** Each page margin in points, from its own record ([MS-XLS] 2.4.151, 2.4.219, 2.4.328, 2.4.27), which states it in inches. */
|
|
61
|
+
readonly marginsPt: {
|
|
62
|
+
readonly left?: number;
|
|
63
|
+
readonly right?: number;
|
|
64
|
+
readonly top?: number;
|
|
65
|
+
readonly bottom?: number;
|
|
66
|
+
};
|
|
67
|
+
/** PrintGrid's fPrintGrid ([MS-XLS] 2.4.202). */
|
|
68
|
+
readonly printGridlines?: boolean;
|
|
69
|
+
/** PrintRowCol's printRwCol ([MS-XLS] 2.4.203): whether the row and column headers print. */
|
|
70
|
+
readonly printHeaders?: boolean;
|
|
71
|
+
/** WsBool's fFitToPage ([MS-XLS] 2.4.351), which decides whether Setup's iScale or its iFitWidth/iFitHeight pair is the live one. */
|
|
72
|
+
readonly fitToPage?: boolean;
|
|
73
|
+
/** Zero-based row indices an explicit page break falls immediately above ([MS-XLS] 2.4.142), ascending and deduplicated. */
|
|
74
|
+
readonly rowBreaks: readonly number[];
|
|
75
|
+
/** Zero-based column indices an explicit page break falls immediately to the left of ([MS-XLS] 2.4.343). */
|
|
76
|
+
readonly columnBreaks: readonly number[];
|
|
77
|
+
}
|
|
46
78
|
/** One worksheet's records, read but not yet mapped onto the shared schema. */
|
|
47
79
|
interface RawSheet {
|
|
48
80
|
readonly cells: readonly RawCell[];
|
|
@@ -51,8 +83,10 @@ interface RawSheet {
|
|
|
51
83
|
readonly merges: readonly RawRange[];
|
|
52
84
|
/** The used range from the Dimensions record ([MS-XLS] 2.4.90), when the sheet declared one. */
|
|
53
85
|
readonly usedRange?: RawRange;
|
|
86
|
+
/** The sheet's own page setup, as far as its records state it. */
|
|
87
|
+
readonly print: RawPrintSettings;
|
|
54
88
|
}
|
|
55
89
|
/** Reads one worksheet substream's records. */
|
|
56
|
-
declare function readSheetRecords(records: readonly RecordGroup[], sharedStrings: readonly string[]): RawSheet;
|
|
90
|
+
declare function readSheetRecords(records: readonly RecordGroup[], sharedStrings: readonly string[], formulaSheets?: FormulaSheetContext): RawSheet;
|
|
57
91
|
//#endregion
|
|
58
|
-
export { RawCell, RawCellValue, RawColumn, RawRange, RawRow, RawSheet, readSheetRecords };
|
|
92
|
+
export { RawCell, RawCellValue, RawColumn, RawPrintSettings, RawRange, RawRow, RawSheet, readSheetRecords };
|
package/dist/workbook/sheet.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import "../biff/record-types.js";
|
|
2
2
|
import { BiffFormatError } from "../biff/records.js";
|
|
3
|
+
import { columnWidthToPoints, inchesToPoints, twipsToPoints } from "../units.js";
|
|
4
|
+
import { unpackSetupFlags } from "../biff/print-setup.js";
|
|
3
5
|
import { BlockCursor } from "../biff/cursor.js";
|
|
4
6
|
import { readXLUnicodeString } from "../biff/strings.js";
|
|
5
7
|
import { errorTextOf } from "../biff/errors.js";
|
|
8
|
+
import { parseFormulaText } from "../biff/ptg.js";
|
|
6
9
|
import { decodeRkNumber } from "../biff/rk.js";
|
|
7
|
-
import { columnWidthToPoints, twipsToPoints } from "../units.js";
|
|
8
10
|
//#region src/workbook/sheet.ts
|
|
9
11
|
/** Row record flag bits, in the 32-bit field following unused1 ([MS-XLS] 2.4.221). */
|
|
10
12
|
const ROW_FLAG_HIDDEN = 32;
|
|
@@ -23,13 +25,25 @@ const FORMULA_VALUE_BLANK = 3;
|
|
|
23
25
|
const MUL_FIXED_BYTES = 6;
|
|
24
26
|
const MULRK_ENTRY_BYTES = 6;
|
|
25
27
|
const MULBLANK_ENTRY_BYTES = 2;
|
|
28
|
+
/** No sheets and no resolvable 3D references -- the default a caller with nothing formula-relevant to offer (every existing test fixture that predates formula-text recovery) gets, so a 3D reference simply fails to resolve rather than throwing on an absent context. */
|
|
29
|
+
const EMPTY_FORMULA_SHEET_CONTEXT = {
|
|
30
|
+
sheets: [],
|
|
31
|
+
sheetRanges: []
|
|
32
|
+
};
|
|
26
33
|
/** Reads one worksheet substream's records. */
|
|
27
|
-
function readSheetRecords(records, sharedStrings) {
|
|
34
|
+
function readSheetRecords(records, sharedStrings, formulaSheets = EMPTY_FORMULA_SHEET_CONTEXT) {
|
|
28
35
|
const cells = [];
|
|
29
36
|
const rows = [];
|
|
30
37
|
const columns = [];
|
|
31
38
|
const merges = [];
|
|
32
39
|
let usedRange;
|
|
40
|
+
const marginsPt = {};
|
|
41
|
+
const rowBreaks = [];
|
|
42
|
+
const columnBreaks = [];
|
|
43
|
+
let setup;
|
|
44
|
+
let printGridlines;
|
|
45
|
+
let printHeaders;
|
|
46
|
+
let fitToPage;
|
|
33
47
|
for (let index = 0; index < records.length; index += 1) {
|
|
34
48
|
const record = records[index];
|
|
35
49
|
if (record === void 0) continue;
|
|
@@ -72,22 +86,108 @@ function readSheetRecords(records, sharedStrings) {
|
|
|
72
86
|
case 516:
|
|
73
87
|
cells.push(readLabel(record));
|
|
74
88
|
break;
|
|
75
|
-
case 6:
|
|
89
|
+
case 6:
|
|
90
|
+
cells.push(readFormula(record, stringResultAfter(records, index), formulaSheets));
|
|
91
|
+
break;
|
|
92
|
+
case 161:
|
|
93
|
+
setup = readSetup(record);
|
|
94
|
+
break;
|
|
95
|
+
case 38:
|
|
96
|
+
marginsPt.left = readMargin(record);
|
|
97
|
+
break;
|
|
98
|
+
case 39:
|
|
99
|
+
marginsPt.right = readMargin(record);
|
|
100
|
+
break;
|
|
101
|
+
case 40:
|
|
102
|
+
marginsPt.top = readMargin(record);
|
|
103
|
+
break;
|
|
104
|
+
case 41:
|
|
105
|
+
marginsPt.bottom = readMargin(record);
|
|
106
|
+
break;
|
|
107
|
+
case 43:
|
|
108
|
+
printGridlines = readGridlineBooleanRecord(record);
|
|
109
|
+
break;
|
|
110
|
+
case 42:
|
|
111
|
+
printHeaders = readBooleanRecord(record);
|
|
112
|
+
break;
|
|
113
|
+
case 129:
|
|
114
|
+
fitToPage = (new BlockCursor(record.blocks).u16() & 256) !== 0;
|
|
115
|
+
break;
|
|
116
|
+
case 27:
|
|
117
|
+
rowBreaks.push(...readPageBreaks(record));
|
|
118
|
+
break;
|
|
119
|
+
case 26: columnBreaks.push(...readPageBreaks(record));
|
|
76
120
|
}
|
|
77
121
|
}
|
|
122
|
+
const print = {
|
|
123
|
+
marginsPt,
|
|
124
|
+
rowBreaks: ascendingDistinct(rowBreaks),
|
|
125
|
+
columnBreaks: ascendingDistinct(columnBreaks),
|
|
126
|
+
...setup === void 0 ? {} : { setup },
|
|
127
|
+
...printGridlines === void 0 ? {} : { printGridlines },
|
|
128
|
+
...printHeaders === void 0 ? {} : { printHeaders },
|
|
129
|
+
...fitToPage === void 0 ? {} : { fitToPage }
|
|
130
|
+
};
|
|
78
131
|
return usedRange === void 0 ? {
|
|
79
132
|
cells,
|
|
80
133
|
rows,
|
|
81
134
|
columns,
|
|
82
|
-
merges
|
|
135
|
+
merges,
|
|
136
|
+
print
|
|
83
137
|
} : {
|
|
84
138
|
cells,
|
|
85
139
|
rows,
|
|
86
140
|
columns,
|
|
87
141
|
merges,
|
|
88
|
-
usedRange
|
|
142
|
+
usedRange,
|
|
143
|
+
print
|
|
89
144
|
};
|
|
90
145
|
}
|
|
146
|
+
/** Setup ([MS-XLS] 2.4.257): iPaperSize, iScale, iPageStart, iFitWidth, iFitHeight, a flags word, iRes, iVRes, an eight-byte header margin, an eight-byte footer margin, and iCopies. The starting page number, the two print resolutions, the header/footer margins, and the copy count are read past: ContentSheetPrintSettings has no field for any of them, and Margins models only the four page edges. */
|
|
147
|
+
function readSetup(record) {
|
|
148
|
+
const cursor = new BlockCursor(record.blocks);
|
|
149
|
+
const paperCode = cursor.u16();
|
|
150
|
+
const scalePercent = cursor.u16();
|
|
151
|
+
cursor.skip(2);
|
|
152
|
+
return {
|
|
153
|
+
paperCode,
|
|
154
|
+
scalePercent,
|
|
155
|
+
fitWidth: cursor.u16(),
|
|
156
|
+
fitHeight: cursor.u16(),
|
|
157
|
+
...unpackSetupFlags(cursor.u16())
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/** Any of the four margin records ([MS-XLS] 2.4.151, 2.4.219, 2.4.328, 2.4.27): a single Xnum stating that margin in inches. All four share the identical one-field layout, so one reader serves them all. */
|
|
161
|
+
function readMargin(record) {
|
|
162
|
+
return inchesToPoints(new BlockCursor(record.blocks).f64());
|
|
163
|
+
}
|
|
164
|
+
/** PrintRowCol ([MS-XLS] 2.4.203): a single 16-bit `Boolean` field (2.5.14), whose whole word is the value. [MS-XLS] 2.4.203's own value table states both 0x0000 and 0x0001 as "Row and column headers are not printed", which is a typo in that table rather than two spellings of one meaning -- the record exists precisely to distinguish them, and every real producer writes 1 for printed (confirmed against LibreOffice-written BIFF8, where a sheet with header printing enabled carries 0x0001 and one without carries 0x0000). */
|
|
165
|
+
function readBooleanRecord(record) {
|
|
166
|
+
return new BlockCursor(record.blocks).u16() !== 0;
|
|
167
|
+
}
|
|
168
|
+
/** PrintGrid ([MS-XLS] 2.4.202): unlike PrintRowCol above, its 16-bit field is only ONE bit wide (`fPrintGrid`) with the remaining 15 "Undefined, and MUST be ignored" -- so a producer leaving anything in those bits would read as gridlines-on under a bare `!== 0` test. No real producer does (LibreOffice writes 0x0000/0x0001, confirmed against its own BIFF8 output), but masking to the one bit the spec actually defines is what the field's own layout says to do. */
|
|
169
|
+
function readGridlineBooleanRecord(record) {
|
|
170
|
+
return (new BlockCursor(record.blocks).u16() & 1) !== 0;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* HorizontalPageBreaks ([MS-XLS] 2.4.142) and VerticalPageBreaks ([MS-XLS] 2.4.343): a count then that many six-byte structures, each an index on the break's own axis followed by the start and end of the break's extent on the other one.
|
|
174
|
+
*
|
|
175
|
+
* Only the index is taken. HorzBrk's colStart/colEnd and VertBrk's rowStart/rowEnd say how far along the perpendicular axis the break runs -- a BIFF8 page break can be partial -- and ContentSheetPrintSettings.manualBreaks models a break as a whole-axis index with no extent, so a partial break is carried as a full one rather than dropped. Both structures have the identical three-field shape, so one reader serves both.
|
|
176
|
+
*/
|
|
177
|
+
function readPageBreaks(record) {
|
|
178
|
+
const cursor = new BlockCursor(record.blocks);
|
|
179
|
+
const count = cursor.u16();
|
|
180
|
+
const indices = [];
|
|
181
|
+
for (let index = 0; index < count; index += 1) {
|
|
182
|
+
indices.push(cursor.u16());
|
|
183
|
+
cursor.skip(4);
|
|
184
|
+
}
|
|
185
|
+
return indices;
|
|
186
|
+
}
|
|
187
|
+
/** Page-break indices, ascending and with duplicates collapsed: two records naming the same row are one page break as far as the schema's own index-only model of a break can express. */
|
|
188
|
+
function ascendingDistinct(values) {
|
|
189
|
+
return [...new Set(values)].sort((a, b) => a - b);
|
|
190
|
+
}
|
|
91
191
|
/**
|
|
92
192
|
* Finds the String record carrying a Formula's cached string result, if it has one.
|
|
93
193
|
*
|
|
@@ -313,28 +413,36 @@ function readLabel(record) {
|
|
|
313
413
|
fromFormula: false
|
|
314
414
|
};
|
|
315
415
|
}
|
|
416
|
+
/** The Formula record's own flags field ([MS-XLS] 2.4.127) and calculation cache, between the cached value and the compiled expression -- neither read for its own content; see the two-byte and four-byte skips in readFormula. */
|
|
417
|
+
const FORMULA_FLAGS_BYTES = 2;
|
|
418
|
+
const FORMULA_CALC_CACHE_BYTES = 4;
|
|
316
419
|
/**
|
|
317
|
-
* Formula ([MS-XLS] 2.4.127): a Cell, an eight-byte FormulaValue, flags, a calculation cache, then
|
|
420
|
+
* Formula ([MS-XLS] 2.4.127): a Cell, an eight-byte FormulaValue, flags, a calculation cache, then a CellParsedFormula -- a two-byte cce followed by exactly that many bytes of compiled Ptg tokens ([MS-XLS] 2.5.198.3).
|
|
318
421
|
*
|
|
319
|
-
*
|
|
422
|
+
* Both the cached value and the expression are read: the value from the FormulaValue exactly as before, and the expression by handing the token bytes to biff/ptg.ts's parseFormulaText, which resolves the whole Ptg vocabulary this reader supports and returns undefined for the constructs it does not (a shared formula, an array formula, a defined name, a natural-language reference, a genuinely external 3D reference -- see that module's own boundary). `formula` is attached only when it resolves; a cell it does not resolve for keeps exactly the behaviour this reader always had, its cached value present and `formula` absent.
|
|
320
423
|
*/
|
|
321
|
-
function readFormula(record, next) {
|
|
424
|
+
function readFormula(record, next, formulaSheets) {
|
|
322
425
|
const cursor = new BlockCursor(record.blocks);
|
|
323
426
|
const header = readCellHeader(cursor);
|
|
324
427
|
const bytes = cursor.take(8);
|
|
325
428
|
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
value:
|
|
329
|
-
kind: "number",
|
|
330
|
-
value: view.getFloat64(0, true)
|
|
331
|
-
},
|
|
332
|
-
fromFormula: true
|
|
429
|
+
const value = view.getUint16(6, true) === FORMULA_VALUE_TAGGED ? taggedFormulaValue(view, next) : {
|
|
430
|
+
kind: "number",
|
|
431
|
+
value: view.getFloat64(0, true)
|
|
333
432
|
};
|
|
334
|
-
|
|
433
|
+
cursor.skip(FORMULA_FLAGS_BYTES);
|
|
434
|
+
cursor.skip(FORMULA_CALC_CACHE_BYTES);
|
|
435
|
+
const cce = cursor.u16();
|
|
436
|
+
const formula = parseFormulaText(cursor.take(cce), formulaSheets);
|
|
437
|
+
return formula === void 0 ? {
|
|
335
438
|
...header,
|
|
336
|
-
value
|
|
439
|
+
value,
|
|
337
440
|
fromFormula: true
|
|
441
|
+
} : {
|
|
442
|
+
...header,
|
|
443
|
+
value,
|
|
444
|
+
fromFormula: true,
|
|
445
|
+
formula
|
|
338
446
|
};
|
|
339
447
|
}
|
|
340
448
|
/** The non-numeric readings of a FormulaValue ([MS-XLS] 2.5.133), selected by its first byte. */
|