@shbernal/ts-xlsx 1.3.1 → 2.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 +20 -10
- package/dist/bytes.d.ts +24 -0
- package/dist/bytes.js +34 -0
- package/dist/core/address.d.ts +72 -3
- package/dist/core/address.js +50 -10
- package/dist/core/autofilter.d.ts +13 -2
- package/dist/core/autofilter.js +35 -10
- package/dist/core/cell.d.ts +17 -16
- package/dist/core/cell.js +3 -7
- package/dist/core/color-resolution.d.ts +8 -8
- package/dist/core/color-resolution.js +7 -8
- package/dist/core/column.d.ts +8 -9
- package/dist/core/column.js +2 -4
- package/dist/core/comment-thread.d.ts +32 -23
- package/dist/core/comment-thread.js +4 -4
- package/dist/core/conditional-formatting-overlay.d.ts +6 -1
- package/dist/core/conditional-formatting-overlay.js +11 -0
- package/dist/core/conditional-formatting.d.ts +48 -8
- package/dist/core/conditional-formatting.js +88 -0
- package/dist/core/data-validation-overlay.d.ts +12 -1
- package/dist/core/data-validation-overlay.js +21 -2
- package/dist/core/data-validation.d.ts +12 -5
- package/dist/core/data-validation.js +34 -0
- package/dist/core/date.d.ts +3 -3
- package/dist/core/formula.d.ts +6 -6
- package/dist/core/grid-edits.d.ts +16 -0
- package/dist/core/grid-edits.js +29 -11
- package/dist/core/grid-shift.d.ts +19 -0
- package/dist/core/grid-shift.js +8 -0
- package/dist/core/image.d.ts +39 -2
- package/dist/core/image.js +10 -0
- package/dist/core/internal.d.ts +9 -1
- package/dist/core/limits.d.ts +4 -4
- package/dist/core/merge.d.ts +17 -12
- package/dist/core/merge.js +36 -5
- package/dist/core/page-setup.d.ts +20 -6
- package/dist/core/page-setup.js +8 -1
- package/dist/core/pivot-table.d.ts +4 -4
- package/dist/core/pivot-table.js +29 -20
- package/dist/core/preserved.d.ts +3 -3
- package/dist/core/protection.d.ts +7 -4
- package/dist/core/protection.js +20 -14
- package/dist/core/range.d.ts +16 -14
- package/dist/core/range.js +5 -5
- package/dist/core/row-input.d.ts +9 -8
- package/dist/core/row-input.js +12 -10
- package/dist/core/row.d.ts +7 -8
- package/dist/core/row.js +2 -4
- package/dist/core/style.d.ts +41 -14
- package/dist/core/style.js +82 -67
- package/dist/core/table-style.d.ts +9 -9
- package/dist/core/table-style.js +1 -1
- package/dist/core/table.d.ts +53 -51
- package/dist/core/table.js +83 -41
- package/dist/core/theme.d.ts +16 -44
- package/dist/core/theme.js +6 -148
- package/dist/core/value.d.ts +7 -7
- package/dist/core/workbook-protection.d.ts +2 -2
- package/dist/core/workbook-styles.d.ts +26 -0
- package/dist/core/workbook-styles.js +48 -0
- package/dist/core/workbook-theme.d.ts +25 -0
- package/dist/core/workbook-theme.js +49 -0
- package/dist/core/workbook-vba.d.ts +17 -0
- package/dist/core/workbook-vba.js +79 -0
- package/dist/core/workbook.d.ts +133 -87
- package/dist/core/workbook.js +74 -133
- package/dist/core/worksheet-comments.d.ts +17 -0
- package/dist/core/worksheet-comments.js +70 -0
- package/dist/core/worksheet-model.d.ts +4 -4
- package/dist/core/worksheet-model.js +1 -0
- package/dist/core/worksheet-pictures.d.ts +29 -0
- package/dist/core/worksheet-pictures.js +42 -0
- package/dist/core/worksheet.d.ts +100 -79
- package/dist/core/worksheet.js +63 -142
- package/dist/customui/errors.d.ts +1 -1
- package/dist/customui/ribbon.d.ts +3 -3
- package/dist/customui/ribbon.js +24 -23
- package/dist/entries/core.d.ts +7 -7
- package/dist/entries/core.js +1 -1
- package/dist/entries/csv.d.ts +1 -1
- package/dist/entries/node-unavailable.d.ts +15 -0
- package/dist/entries/node-unavailable.js +20 -0
- package/dist/entries/node.d.ts +1 -0
- package/dist/entries/node.js +1 -0
- package/dist/entries/vba.d.ts +1 -1
- package/dist/entries/vba.js +1 -1
- package/dist/entries/xlsx.d.ts +1 -1
- package/dist/entries/xlsx.js +1 -1
- package/dist/errors.d.ts +12 -12
- package/dist/io/csv/read.js +2 -3
- package/dist/io/csv/write.d.ts +19 -4
- package/dist/io/csv/write.js +34 -4
- package/dist/io/opc/errors.d.ts +6 -6
- package/dist/io/opc/inflate.d.ts +1 -1
- package/dist/io/opc/inflate.js +2 -12
- package/dist/io/opc/namespaces.d.ts +1 -1
- package/dist/io/opc/read-opc.d.ts +19 -3
- package/dist/io/opc/read-opc.js +14 -8
- package/dist/io/opc/read-options.d.ts +2 -2
- package/dist/io/opc/rels.d.ts +11 -0
- package/dist/io/opc/rels.js +3 -2
- package/dist/io/opc/sniff-format.d.ts +2 -2
- package/dist/io/style/xf-style.d.ts +44 -7
- package/dist/io/style/xf-style.js +27 -0
- package/dist/io/xlsb/errors.d.ts +1 -1
- package/dist/io/xlsb/formula.d.ts +4 -4
- package/dist/io/xlsb/primitives.d.ts +8 -8
- package/dist/io/xlsb/ptg-functions.d.ts +1 -1
- package/dist/io/xlsb/read-styles.js +2 -20
- package/dist/io/xlsb/read.d.ts +2 -2
- package/dist/io/xlsx/cell-accumulator.d.ts +26 -9
- package/dist/io/xlsx/cell-accumulator.js +89 -46
- package/dist/io/xlsx/cell-value.d.ts +3 -3
- package/dist/io/xlsx/cell-value.js +9 -6
- package/dist/io/xlsx/color-xml.d.ts +1 -1
- package/dist/io/xlsx/color-xml.js +18 -23
- package/dist/io/xlsx/comments.d.ts +5 -5
- package/dist/io/xlsx/comments.js +17 -8
- package/dist/io/xlsx/conditional-formatting.d.ts +4 -4
- package/dist/io/xlsx/conditional-formatting.js +64 -58
- package/dist/io/xlsx/data-validation.d.ts +3 -3
- package/dist/io/xlsx/data-validation.js +35 -33
- package/dist/io/xlsx/errors.d.ts +3 -3
- package/dist/io/xlsx/hyperlinks.d.ts +4 -2
- package/dist/io/xlsx/hyperlinks.js +10 -18
- package/dist/io/xlsx/images.d.ts +1 -1
- package/dist/io/xlsx/images.js +25 -28
- package/dist/io/xlsx/namespaces.d.ts +2 -2
- package/dist/io/xlsx/package-plan.js +5 -2
- package/dist/io/xlsx/read-pivot.js +4 -13
- package/dist/io/xlsx/read-rows.d.ts +13 -13
- package/dist/io/xlsx/read-rows.js +19 -55
- package/dist/io/xlsx/read-shared-strings.js +7 -44
- package/dist/io/xlsx/read-styles.d.ts +3 -3
- package/dist/io/xlsx/read-styles.js +42 -66
- package/dist/io/xlsx/read-worksheet.js +73 -129
- package/dist/io/xlsx/read.d.ts +4 -6
- package/dist/io/xlsx/read.js +104 -142
- package/dist/io/xlsx/rich-runs.d.ts +41 -7
- package/dist/io/xlsx/rich-runs.js +94 -30
- package/dist/io/xlsx/rich-text.d.ts +1 -1
- package/dist/io/xlsx/shared-strings.d.ts +2 -2
- package/dist/io/xlsx/sheet-properties.d.ts +1 -1
- package/dist/io/xlsx/sheet-properties.js +14 -10
- package/dist/io/xlsx/styles.d.ts +9 -22
- package/dist/io/xlsx/styles.js +65 -89
- package/dist/io/xlsx/tables.d.ts +2 -2
- package/dist/io/xlsx/tables.js +20 -25
- package/dist/io/xlsx/theme-xml.d.ts +37 -0
- package/dist/io/xlsx/theme-xml.js +145 -0
- package/dist/io/xlsx/threaded-comments.d.ts +18 -27
- package/dist/io/xlsx/threaded-comments.js +26 -38
- package/dist/io/xlsx/workbook-xml.js +14 -8
- package/dist/io/xlsx/worksheet-xml.d.ts +2 -2
- package/dist/io/xlsx/worksheet-xml.js +40 -48
- package/dist/io/xlsx/write-stream.d.ts +23 -16
- package/dist/io/xlsx/write-stream.js +13 -14
- package/dist/io/xlsx/write.d.ts +8 -7
- package/dist/io/xlsx/write.js +6 -3
- package/dist/io/xlsx/x14-ext.d.ts +1 -1
- package/dist/sha512.d.ts +2 -0
- package/dist/sha512.js +146 -0
- package/dist/vba/bytes.d.ts +11 -0
- package/dist/vba/bytes.js +29 -0
- package/dist/vba/cfb-writer.d.ts +1 -1
- package/dist/vba/cfb.d.ts +1 -1
- package/dist/vba/cfb.js +15 -51
- package/dist/vba/codepage.d.ts +2 -2
- package/dist/vba/dir-records.d.ts +53 -0
- package/dist/vba/dir-records.js +28 -0
- package/dist/vba/errors.d.ts +2 -2
- package/dist/vba/index.d.ts +2 -2
- package/dist/vba/index.js +2 -2
- package/dist/vba/ms-ovba.d.ts +1 -1
- package/dist/vba/ms-ovba.js +1 -3
- package/dist/vba/project-editor.d.ts +9 -9
- package/dist/vba/project-editor.js +6 -44
- package/dist/vba/project.d.ts +5 -5
- package/dist/vba/project.js +3 -28
- package/dist/vba/vba-encoding.d.ts +1 -1
- package/dist/xml/errors.d.ts +1 -1
- package/dist/xml/xml-read.d.ts +98 -9
- package/dist/xml/xml-read.js +70 -0
- package/dist/xml/xml.d.ts +74 -10
- package/dist/xml/xml.js +39 -7
- package/package.json +59 -36
|
@@ -12,12 +12,12 @@ import type { StyleRegistry } from './styles.ts';
|
|
|
12
12
|
import { type FlushedSheet } from './write.ts';
|
|
13
13
|
/** Calculation settings applied to the streamed workbook. Mirrors the {@link Workbook} flags. */
|
|
14
14
|
export interface CalcProperties {
|
|
15
|
-
/** Ask the consumer to recalculate every formula on open
|
|
15
|
+
/** Ask the consumer to recalculate every formula on open: the OOXML `fullCalcOnLoad` flag. */
|
|
16
16
|
fullCalcOnLoad?: boolean;
|
|
17
17
|
}
|
|
18
18
|
type SinkOptions = {
|
|
19
19
|
/**
|
|
20
|
-
* Write the package to a caller-owned {@link Writable} sink
|
|
20
|
+
* Write the package to a caller-owned {@link Writable} sink: an outbound upload, a cloud-SDK
|
|
21
21
|
* stream, any destination the caller controls. {@link WorkbookStreamWriter.commit} pushes every
|
|
22
22
|
* chunk into it and settles only once the sink has finished (or rejects if it errors), so a
|
|
23
23
|
* caller can deterministically sequence work after the upload completes. Mutually exclusive
|
|
@@ -41,7 +41,7 @@ type SinkOptions = {
|
|
|
41
41
|
/** Options fixed at construction that shape the whole streamed package. */
|
|
42
42
|
export type WorkbookStreamWriterOptions = SinkOptions & {
|
|
43
43
|
/**
|
|
44
|
-
* Pool plain string cell values into a shared-strings table rather than storing each inline
|
|
44
|
+
* Pool plain string cell values into a shared-strings table rather than storing each inline: the
|
|
45
45
|
* same {@link WriteOptions.useSharedStrings} the buffered writer exposes. Off by default.
|
|
46
46
|
*/
|
|
47
47
|
readonly useSharedStrings?: boolean;
|
|
@@ -58,12 +58,12 @@ export declare class StreamedRow {
|
|
|
58
58
|
/** The cells this row materialised, for styling before it is committed. */
|
|
59
59
|
get cells(): readonly Cell[];
|
|
60
60
|
/** Finalise the row: an eager writer serialises it now and releases its cells; otherwise a no-op.
|
|
61
|
-
* Committing twice is harmless
|
|
61
|
+
* Committing twice is harmless: the second call does nothing rather than re-emitting the row. */
|
|
62
62
|
commit(): void;
|
|
63
63
|
}
|
|
64
64
|
/**
|
|
65
65
|
* A worksheet being written incrementally. Append rows with {@link addRow}/{@link addRows}, style
|
|
66
|
-
* cells through {@link getCell}, then {@link commit} to freeze it
|
|
66
|
+
* cells through {@link getCell}, then {@link commit} to freeze it, after which any further mutation
|
|
67
67
|
* is rejected with a legible error rather than silently accepted or crashing.
|
|
68
68
|
*/
|
|
69
69
|
export declare class WorksheetStreamWriter {
|
|
@@ -71,7 +71,7 @@ export declare class WorksheetStreamWriter {
|
|
|
71
71
|
constructor(sheet: Worksheet, eager: boolean, styles: StyleRegistry);
|
|
72
72
|
/** The sheet's name. */
|
|
73
73
|
get name(): string;
|
|
74
|
-
/** The number of rows written so far
|
|
74
|
+
/** The number of rows written so far. Spans gaps and formatted-only rows, like the model, and
|
|
75
75
|
* survives the eviction of eagerly-flushed rows. */
|
|
76
76
|
get rowCount(): number;
|
|
77
77
|
/** Append one row of values after the last used row; the cells are returned for styling. */
|
|
@@ -83,32 +83,39 @@ export declare class WorksheetStreamWriter {
|
|
|
83
83
|
* {@link StreamedRow.commit}; the row's `<row>` XML is retained (interned into the workbook's live
|
|
84
84
|
* style registry so its ids stay valid) and the cell graph is dropped, bounding peak memory.
|
|
85
85
|
*
|
|
86
|
-
* @throws {AuthoringError} if the row carries a shared-formula cell
|
|
86
|
+
* @throws {AuthoringError} if the row carries a shared-formula cell: a finished row cannot join the
|
|
87
87
|
* whole-sheet formula planning, so shared formulas must be authored through {@link getCell}.
|
|
88
88
|
*/
|
|
89
89
|
flushRow(number: number, cells: readonly Cell[]): void;
|
|
90
90
|
flushedSheet(): FlushedSheet | undefined;
|
|
91
|
-
/**
|
|
91
|
+
/**
|
|
92
|
+
* Address a cell by its A1 reference to read or style it before the sheet is committed. The row it
|
|
93
|
+
* names must still be live: a row whose {@link StreamedRow.commit} has run is finished.
|
|
94
|
+
*
|
|
95
|
+
* @throws {AuthoringError} if the reference names an already-committed row. That row's `<row>` is
|
|
96
|
+
* rendered and its cells are released, so the cell this would materialise could only be written
|
|
97
|
+
* as a second row carrying the same number.
|
|
98
|
+
*/
|
|
92
99
|
getCell(reference: string): Cell;
|
|
93
100
|
/**
|
|
94
101
|
* Attach a data validation to a range before the sheet is committed. Delegates to the model, so the
|
|
95
|
-
* streamed package emits the `<dataValidations>` block in its CT_Worksheet position
|
|
96
|
-
* `<hyperlinks
|
|
102
|
+
* streamed package emits the `<dataValidations>` block in its CT_Worksheet position, before
|
|
103
|
+
* `<hyperlinks>`, because both writers share one worksheet serializer.
|
|
97
104
|
*/
|
|
98
105
|
addDataValidation(sqref: string, rule: DataValidation, options?: {
|
|
99
106
|
extended?: boolean;
|
|
100
107
|
}): void;
|
|
101
108
|
/**
|
|
102
109
|
* Attach a conditional formatting to a range before the sheet is committed. Like every other block,
|
|
103
|
-
* it lands in its schema-mandated slot
|
|
104
|
-
* `<hyperlinks
|
|
110
|
+
* it lands in its schema-mandated slot, after `<mergeCells>` and before `<dataValidations>` and
|
|
111
|
+
* `<hyperlinks>`, since the streamed sheet is serialized through the same path as a buffered write.
|
|
105
112
|
*/
|
|
106
113
|
addConditionalFormatting(formatting: ConditionalFormatting): void;
|
|
107
114
|
/**
|
|
108
115
|
* Anchor a workbook image (the id from {@link WorkbookStreamWriter.addImage}) to this sheet,
|
|
109
116
|
* spanning the rectangle from the top-left grid point `tl` to the bottom-right `br`. The streamed
|
|
110
117
|
* package emits the drawing part, its media relationship, and the sheet's `<drawing>` reference
|
|
111
|
-
* exactly as a buffered write does
|
|
118
|
+
* exactly as a buffered write does: both writers share `buildPackageParts`.
|
|
112
119
|
*/
|
|
113
120
|
addImage(imageId: number, anchor: {
|
|
114
121
|
readonly tl: AnchorPoint;
|
|
@@ -116,7 +123,7 @@ export declare class WorksheetStreamWriter {
|
|
|
116
123
|
}): void;
|
|
117
124
|
/**
|
|
118
125
|
* Apply the sheet's autofilter before it is committed; mirrors {@link Worksheet.autoFilter}. The
|
|
119
|
-
* streamed package emits `<autoFilter>` in its CT_Worksheet slot
|
|
126
|
+
* streamed package emits `<autoFilter>` in its CT_Worksheet slot, after `<sheetProtection>`, and
|
|
120
127
|
* contributes the hidden `_FilterDatabase` defined name, exactly as a buffered write does.
|
|
121
128
|
*/
|
|
122
129
|
set autoFilter(filter: string | AutoFilter | undefined);
|
|
@@ -147,8 +154,8 @@ export declare class WorkbookStreamWriter {
|
|
|
147
154
|
/** Document-level metadata written to the package's core properties. */
|
|
148
155
|
get properties(): Workbook['properties'];
|
|
149
156
|
/**
|
|
150
|
-
* The output stream carrying the package bytes. A caller drives it with Node's standard idiom
|
|
151
|
-
* `writer.stream.pipe(out)
|
|
157
|
+
* The output stream carrying the package bytes. A caller drives it with Node's standard idiom,
|
|
158
|
+
* `writer.stream.pipe(out)`, which composes because `pipe` returns its destination. The stream is
|
|
152
159
|
* created lazily on first access so a caller handing the writer its own sink is still free to
|
|
153
160
|
* ignore this one.
|
|
154
161
|
*/
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { createWriteStream } from 'node:fs';
|
|
2
2
|
import { PassThrough } from 'node:stream';
|
|
3
3
|
import { Zip, ZipDeflate } from 'fflate';
|
|
4
|
-
import {
|
|
4
|
+
import { concat } from '../../bytes.js';
|
|
5
|
+
import { encodeAddress, tryDecodeCellRef } from '../../core/address.js';
|
|
5
6
|
import { INTERNAL } from '../../core/internal.js';
|
|
6
7
|
import { isSharedFormulaValue } from '../../core/value.js';
|
|
7
8
|
import { Workbook } from '../../core/workbook.js';
|
|
@@ -36,6 +37,7 @@ export class WorksheetStreamWriter {
|
|
|
36
37
|
#lastRow = 0;
|
|
37
38
|
#columnDefaults;
|
|
38
39
|
#flushedRows = [];
|
|
40
|
+
#flushedNumbers = new Set();
|
|
39
41
|
#extent = new Extent();
|
|
40
42
|
#maxRowOutlineLevel = 0;
|
|
41
43
|
constructor(sheet, eager, styles) {
|
|
@@ -97,6 +99,7 @@ export class WorksheetStreamWriter {
|
|
|
97
99
|
this.#flushedRows.push({ number, xml });
|
|
98
100
|
this.#extent.add(number, minCol, maxCol);
|
|
99
101
|
}
|
|
102
|
+
this.#flushedNumbers.add(number);
|
|
100
103
|
this.#sheet[INTERNAL].evictRow(number);
|
|
101
104
|
}
|
|
102
105
|
flushedSheet() {
|
|
@@ -110,6 +113,12 @@ export class WorksheetStreamWriter {
|
|
|
110
113
|
}
|
|
111
114
|
getCell(reference) {
|
|
112
115
|
this.#assertOpen();
|
|
116
|
+
const target = tryDecodeCellRef(reference);
|
|
117
|
+
if (target !== undefined && this.#flushedNumbers.has(target.row)) {
|
|
118
|
+
throw new AuthoringError(`cell ${reference} of streamed sheet "${this.#sheet.name}" is in row ${target.row}, which is ` +
|
|
119
|
+
'already committed: its bytes are written and its cells released, so this value could only ' +
|
|
120
|
+
'be emitted as a second row with that number. Style a row before committing it.');
|
|
121
|
+
}
|
|
113
122
|
return this.#sheet.getCell(reference);
|
|
114
123
|
}
|
|
115
124
|
addDataValidation(sqref, rule, options = {}) {
|
|
@@ -146,7 +155,7 @@ export class WorksheetStreamWriter {
|
|
|
146
155
|
}
|
|
147
156
|
#assertOpen() {
|
|
148
157
|
if (this.#committed) {
|
|
149
|
-
throw new AuthoringError(`worksheet "${this.#sheet.name}" is already committed
|
|
158
|
+
throw new AuthoringError(`worksheet "${this.#sheet.name}" is already committed: its rows are finalised and no more can be added`);
|
|
150
159
|
}
|
|
151
160
|
}
|
|
152
161
|
}
|
|
@@ -179,13 +188,13 @@ export class WorkbookStreamWriter {
|
|
|
179
188
|
}
|
|
180
189
|
addImage(options) {
|
|
181
190
|
if (this.#committed) {
|
|
182
|
-
throw new AuthoringError('the workbook is already committed
|
|
191
|
+
throw new AuthoringError('the workbook is already committed: no more images can be registered');
|
|
183
192
|
}
|
|
184
193
|
return this.#workbook.addImage(options);
|
|
185
194
|
}
|
|
186
195
|
addWorksheet(name, options = {}) {
|
|
187
196
|
if (this.#committed) {
|
|
188
|
-
throw new AuthoringError('the workbook is already committed
|
|
197
|
+
throw new AuthoringError('the workbook is already committed: no more worksheets can be added');
|
|
189
198
|
}
|
|
190
199
|
const sheet = new WorksheetStreamWriter(this.#workbook.addWorksheet(name, options), this.#eager, this.#styles);
|
|
191
200
|
this.#sheets.push(sheet);
|
|
@@ -252,13 +261,3 @@ function streamZipPackage(parts, onChunk) {
|
|
|
252
261
|
zip.end();
|
|
253
262
|
});
|
|
254
263
|
}
|
|
255
|
-
function concat(chunks) {
|
|
256
|
-
const total = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
257
|
-
const out = new Uint8Array(total);
|
|
258
|
-
let offset = 0;
|
|
259
|
-
for (const chunk of chunks) {
|
|
260
|
-
out.set(chunk, offset);
|
|
261
|
-
offset += chunk.length;
|
|
262
|
-
}
|
|
263
|
-
return out;
|
|
264
|
-
}
|
package/dist/io/xlsx/write.d.ts
CHANGED
|
@@ -16,13 +16,14 @@ export interface WriteOptions {
|
|
|
16
16
|
/**
|
|
17
17
|
* {@link WriteOptions} plus the streaming writer's internal wiring, so a buffered caller's options
|
|
18
18
|
* object can never carry fields meant only for {@link WorkbookStreamWriter}'s own use. Not exported
|
|
19
|
-
* from the public barrel
|
|
19
|
+
* from the public barrel: {@link WorkbookStreamWriter} is the only caller that fills these fields,
|
|
20
|
+
* and it reaches them through {@link buildPackageParts}.
|
|
20
21
|
*/
|
|
21
22
|
export interface InternalWriteOptions extends WriteOptions {
|
|
22
23
|
/**
|
|
23
24
|
* The style registry to intern into, in place of a freshly-seeded one. The streaming writer
|
|
24
25
|
* serialises each committed row eagerly (freeing its cells), so those rows' style ids must be
|
|
25
|
-
* assigned by the very same registry that later emits `xl/styles.xml
|
|
26
|
+
* assigned by the very same registry that later emits `xl/styles.xml`; otherwise the ids in the
|
|
26
27
|
* pre-rendered rows would not match the styles part. When omitted the buffered path seeds its own,
|
|
27
28
|
* so its output is unchanged.
|
|
28
29
|
*/
|
|
@@ -48,19 +49,19 @@ export declare function writeXlsx(workbook: Workbook, options?: WriteOptions): U
|
|
|
48
49
|
/**
|
|
49
50
|
* Serialise a workbook into an `.xlsx` package, deflating off the calling thread.
|
|
50
51
|
*
|
|
51
|
-
* Produces the same package {@link writeXlsx} does
|
|
52
|
+
* Produces the same package {@link writeXlsx} does, byte for byte and entry timestamps included, and
|
|
52
53
|
* exists for one reason: DEFLATE dominates the cost of writing a large workbook, and {@link writeXlsx}
|
|
53
54
|
* spends all of it on the caller's thread. Here `fflate` deflates each part in a worker, so the event
|
|
54
55
|
* loop keeps turning (stalls drop from the whole write to tens of milliseconds) and parts compress in
|
|
55
56
|
* parallel, which on a multi-sheet workbook also finishes sooner. On a single-sheet workbook there is
|
|
56
57
|
* only one part to deflate, so expect responsiveness rather than speed.
|
|
57
58
|
*
|
|
58
|
-
* Building the parts still happens on the calling thread
|
|
59
|
+
* Building the parts still happens on the calling thread; only compression moves. That is why there
|
|
59
60
|
* is no `readXlsxAsync` mirroring this: reading is dominated by XML parsing and model building, which
|
|
60
61
|
* no worker can take, and the reader's zip-bomb ceiling is enforced by counting output between
|
|
61
62
|
* synchronous input slices. See ADR-0024.
|
|
62
63
|
*
|
|
63
|
-
* @throws {AuthoringError}
|
|
64
|
+
* @throws {AuthoringError}, as a rejection, under the same conditions as {@link writeXlsx};
|
|
64
65
|
* the part-building it shares happens before any worker is involved. A failure raised by the zip
|
|
65
66
|
* layer itself (including an environment that cannot spawn a worker) propagates unwrapped, exactly
|
|
66
67
|
* as it does from {@link writeXlsx}.
|
|
@@ -75,8 +76,8 @@ export declare function writeXlsxAsync(workbook: Workbook, options?: WriteOption
|
|
|
75
76
|
export declare function createStyleRegistry(workbook: Workbook): StyleRegistry;
|
|
76
77
|
/**
|
|
77
78
|
* Assemble a workbook into the map of OPC package parts (part name → bytes) that make up an `.xlsx`,
|
|
78
|
-
* short of zipping them. This is the whole serialisation
|
|
79
|
-
* per-sheet XML, styles, theme, media, tables, and props
|
|
79
|
+
* short of zipping them. This is the whole serialisation (content types, relationships, workbook,
|
|
80
|
+
* per-sheet XML, styles, theme, media, tables, and props) factored out of {@link writeXlsx} so the
|
|
80
81
|
* streaming writer can drive the identical parts through a streamed zip container rather than
|
|
81
82
|
* `zipSync`. Neither writer duplicates a byte of serialisation.
|
|
82
83
|
*
|
package/dist/io/xlsx/write.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { strToU8, zip, zipSync } from 'fflate';
|
|
2
|
-
import { DEFAULT_THEME_XML } from '../../core/theme.js';
|
|
3
2
|
import { AuthoringError } from '../../errors.js';
|
|
4
3
|
import { THEME_PART_PATH } from '../opc/part-paths.js';
|
|
5
4
|
import { relsPartXml } from '../opc/rels.js';
|
|
@@ -13,6 +12,7 @@ import { REL } from './relationships.js';
|
|
|
13
12
|
import { SharedStringTable } from './shared-strings.js';
|
|
14
13
|
import { StyleRegistry } from './styles.js';
|
|
15
14
|
import { tableXml } from './tables.js';
|
|
15
|
+
import { applyThemeOverrides, DEFAULT_THEME_XML } from './theme-xml.js';
|
|
16
16
|
import { personsXml, threadedCommentsXml } from './threaded-comments.js';
|
|
17
17
|
import { appPropsXml, contentTypesXml, corePropsXml, FIXED_WORKBOOK_REL_COUNT, rootRelsXml, workbookRelsXml, workbookXml, } from './workbook-xml.js';
|
|
18
18
|
import { worksheetRelsXml, worksheetXml, } from './worksheet-xml.js';
|
|
@@ -66,7 +66,7 @@ function resolveSheetReferences(plan) {
|
|
|
66
66
|
export function buildPackageParts(workbook, options = {}) {
|
|
67
67
|
const sheets = workbook.worksheets;
|
|
68
68
|
if (sheets.length === 0) {
|
|
69
|
-
throw new AuthoringError('cannot write a workbook with no worksheets
|
|
69
|
+
throw new AuthoringError('cannot write a workbook with no worksheets: a zero-sheet package is corrupt to Excel');
|
|
70
70
|
}
|
|
71
71
|
const sharedStrings = options.useSharedStrings ? new SharedStringTable() : null;
|
|
72
72
|
const media = planMedia(workbook, sheets);
|
|
@@ -184,7 +184,10 @@ export function buildPackageParts(workbook, options = {}) {
|
|
|
184
184
|
'xl/styles.xml': strToU8(styles.toXml()),
|
|
185
185
|
};
|
|
186
186
|
if (!preserved.themeEmitted) {
|
|
187
|
-
|
|
187
|
+
const overrides = workbook.themeOverrides;
|
|
188
|
+
files[THEME_PART_PATH] = strToU8(overrides === undefined
|
|
189
|
+
? DEFAULT_THEME_XML
|
|
190
|
+
: applyThemeOverrides(DEFAULT_THEME_XML, overrides));
|
|
188
191
|
}
|
|
189
192
|
if (hasSharedStrings) {
|
|
190
193
|
files['xl/sharedStrings.xml'] = strToU8(sharedStrings.toXml());
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Wrap an x14 extension body in the `<ext>` envelope every worksheet- and workbook-level x14
|
|
3
3
|
* extension shares: a `uri` scoping the extension to its feature (a fixed GUID from
|
|
4
|
-
* {@link namespaces.ts}) plus the `xmlns:x14` binding the extension namespace on the element itself
|
|
4
|
+
* {@link namespaces.ts}) plus the `xmlns:x14` binding the extension namespace on the element itself.
|
|
5
5
|
* Excel declares it per `<ext>`, never at the part root. Callers needing an `<extLst>` wrapper add it
|
|
6
6
|
* around the result; the producers the worksheet serialiser gathers return this bare so several exts
|
|
7
7
|
* compose under a single shared `<extLst>`.
|
package/dist/sha512.d.ts
ADDED
package/dist/sha512.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
const BLOCK_BYTES = 128;
|
|
2
|
+
const DIGEST_BYTES = 64;
|
|
3
|
+
const K = new Uint32Array([
|
|
4
|
+
0x428a2f98, 0xd728ae22, 0x71374491, 0x23ef65cd, 0xb5c0fbcf, 0xec4d3b2f, 0xe9b5dba5, 0x8189dbbc,
|
|
5
|
+
0x3956c25b, 0xf348b538, 0x59f111f1, 0xb605d019, 0x923f82a4, 0xaf194f9b, 0xab1c5ed5, 0xda6d8118,
|
|
6
|
+
0xd807aa98, 0xa3030242, 0x12835b01, 0x45706fbe, 0x243185be, 0x4ee4b28c, 0x550c7dc3, 0xd5ffb4e2,
|
|
7
|
+
0x72be5d74, 0xf27b896f, 0x80deb1fe, 0x3b1696b1, 0x9bdc06a7, 0x25c71235, 0xc19bf174, 0xcf692694,
|
|
8
|
+
0xe49b69c1, 0x9ef14ad2, 0xefbe4786, 0x384f25e3, 0x0fc19dc6, 0x8b8cd5b5, 0x240ca1cc, 0x77ac9c65,
|
|
9
|
+
0x2de92c6f, 0x592b0275, 0x4a7484aa, 0x6ea6e483, 0x5cb0a9dc, 0xbd41fbd4, 0x76f988da, 0x831153b5,
|
|
10
|
+
0x983e5152, 0xee66dfab, 0xa831c66d, 0x2db43210, 0xb00327c8, 0x98fb213f, 0xbf597fc7, 0xbeef0ee4,
|
|
11
|
+
0xc6e00bf3, 0x3da88fc2, 0xd5a79147, 0x930aa725, 0x06ca6351, 0xe003826f, 0x14292967, 0x0a0e6e70,
|
|
12
|
+
0x27b70a85, 0x46d22ffc, 0x2e1b2138, 0x5c26c926, 0x4d2c6dfc, 0x5ac42aed, 0x53380d13, 0x9d95b3df,
|
|
13
|
+
0x650a7354, 0x8baf63de, 0x766a0abb, 0x3c77b2a8, 0x81c2c92e, 0x47edaee6, 0x92722c85, 0x1482353b,
|
|
14
|
+
0xa2bfe8a1, 0x4cf10364, 0xa81a664b, 0xbc423001, 0xc24b8b70, 0xd0f89791, 0xc76c51a3, 0x0654be30,
|
|
15
|
+
0xd192e819, 0xd6ef5218, 0xd6990624, 0x5565a910, 0xf40e3585, 0x5771202a, 0x106aa070, 0x32bbd1b8,
|
|
16
|
+
0x19a4c116, 0xb8d2d0c8, 0x1e376c08, 0x5141ab53, 0x2748774c, 0xdf8eeb99, 0x34b0bcb5, 0xe19b48a8,
|
|
17
|
+
0x391c0cb3, 0xc5c95a63, 0x4ed8aa4a, 0xe3418acb, 0x5b9cca4f, 0x7763e373, 0x682e6ff3, 0xd6b2b8a3,
|
|
18
|
+
0x748f82ee, 0x5defb2fc, 0x78a5636f, 0x43172f60, 0x84c87814, 0xa1f0ab72, 0x8cc70208, 0x1a6439ec,
|
|
19
|
+
0x90befffa, 0x23631e28, 0xa4506ceb, 0xde82bde9, 0xbef9a3f7, 0xb2c67915, 0xc67178f2, 0xe372532b,
|
|
20
|
+
0xca273ece, 0xea26619c, 0xd186b8c7, 0x21c0c207, 0xeada7dd6, 0xcde0eb1e, 0xf57d4f7f, 0xee6ed178,
|
|
21
|
+
0x06f067aa, 0x72176fba, 0x0a637dc5, 0xa2c898a6, 0x113f9804, 0xbef90dae, 0x1b710b35, 0x131c471b,
|
|
22
|
+
0x28db77f5, 0x23047d84, 0x32caab7b, 0x40c72493, 0x3c9ebe0a, 0x15c9bebc, 0x431d67c4, 0x9c100d4c,
|
|
23
|
+
0x4cc5d4be, 0xcb3e42b6, 0x597f299c, 0xfc657e2a, 0x5fcb6fab, 0x3ad6faec, 0x6c44198c, 0x4a475817,
|
|
24
|
+
]);
|
|
25
|
+
const INITIAL = new Uint32Array([
|
|
26
|
+
0x6a09e667, 0xf3bcc908, 0xbb67ae85, 0x84caa73b, 0x3c6ef372, 0xfe94f82b, 0xa54ff53a, 0x5f1d36f1,
|
|
27
|
+
0x510e527f, 0xade682d1, 0x9b05688c, 0x2b3e6c1f, 0x1f83d9ab, 0xfb41bd6b, 0x5be0cd19, 0x137e2179,
|
|
28
|
+
]);
|
|
29
|
+
function word(words, index) {
|
|
30
|
+
return words[index] ?? 0;
|
|
31
|
+
}
|
|
32
|
+
export function sha512(message) {
|
|
33
|
+
const blocks = Math.ceil((message.length + 17) / BLOCK_BYTES);
|
|
34
|
+
const padded = new Uint8Array(blocks * BLOCK_BYTES);
|
|
35
|
+
padded.set(message);
|
|
36
|
+
padded[message.length] = 0x80;
|
|
37
|
+
const view = new DataView(padded.buffer);
|
|
38
|
+
const bits = message.length * 8;
|
|
39
|
+
view.setUint32(padded.length - 8, Math.floor(bits / 0x1_0000_0000), false);
|
|
40
|
+
view.setUint32(padded.length - 4, bits >>> 0, false);
|
|
41
|
+
const h = Uint32Array.from(INITIAL);
|
|
42
|
+
const schedule = new Uint32Array(160);
|
|
43
|
+
for (let block = 0; block < blocks; block++)
|
|
44
|
+
compress(h, view, block * BLOCK_BYTES, schedule);
|
|
45
|
+
const digest = new Uint8Array(DIGEST_BYTES);
|
|
46
|
+
const out = new DataView(digest.buffer);
|
|
47
|
+
for (let i = 0; i < 16; i++)
|
|
48
|
+
out.setUint32(i * 4, word(h, i), false);
|
|
49
|
+
return digest;
|
|
50
|
+
}
|
|
51
|
+
function compress(h, input, offset, schedule) {
|
|
52
|
+
for (let t = 0; t < 16; t++) {
|
|
53
|
+
schedule[t * 2] = input.getUint32(offset + t * 8, false);
|
|
54
|
+
schedule[t * 2 + 1] = input.getUint32(offset + t * 8 + 4, false);
|
|
55
|
+
}
|
|
56
|
+
for (let t = 16; t < 80; t++) {
|
|
57
|
+
const xHi = word(schedule, (t - 15) * 2);
|
|
58
|
+
const xLo = word(schedule, (t - 15) * 2 + 1);
|
|
59
|
+
const s0Hi = ((xHi >>> 1) | (xLo << 31)) ^ ((xHi >>> 8) | (xLo << 24)) ^ (xHi >>> 7);
|
|
60
|
+
const s0Lo = ((xLo >>> 1) | (xHi << 31)) ^ ((xLo >>> 8) | (xHi << 24)) ^ ((xLo >>> 7) | (xHi << 25));
|
|
61
|
+
const yHi = word(schedule, (t - 2) * 2);
|
|
62
|
+
const yLo = word(schedule, (t - 2) * 2 + 1);
|
|
63
|
+
const s1Hi = ((yHi >>> 19) | (yLo << 13)) ^ ((yLo >>> 29) | (yHi << 3)) ^ (yHi >>> 6);
|
|
64
|
+
const s1Lo = ((yLo >>> 19) | (yHi << 13)) ^ ((yHi >>> 29) | (yLo << 3)) ^ ((yLo >>> 6) | (yHi << 26));
|
|
65
|
+
const lo = (s1Lo >>> 0) +
|
|
66
|
+
word(schedule, (t - 7) * 2 + 1) +
|
|
67
|
+
(s0Lo >>> 0) +
|
|
68
|
+
word(schedule, (t - 16) * 2 + 1);
|
|
69
|
+
const hi = (s1Hi >>> 0) +
|
|
70
|
+
word(schedule, (t - 7) * 2) +
|
|
71
|
+
(s0Hi >>> 0) +
|
|
72
|
+
word(schedule, (t - 16) * 2) +
|
|
73
|
+
Math.floor(lo / 0x1_0000_0000);
|
|
74
|
+
schedule[t * 2] = hi >>> 0;
|
|
75
|
+
schedule[t * 2 + 1] = lo >>> 0;
|
|
76
|
+
}
|
|
77
|
+
let aHi = word(h, 0);
|
|
78
|
+
let aLo = word(h, 1);
|
|
79
|
+
let bHi = word(h, 2);
|
|
80
|
+
let bLo = word(h, 3);
|
|
81
|
+
let cHi = word(h, 4);
|
|
82
|
+
let cLo = word(h, 5);
|
|
83
|
+
let dHi = word(h, 6);
|
|
84
|
+
let dLo = word(h, 7);
|
|
85
|
+
let eHi = word(h, 8);
|
|
86
|
+
let eLo = word(h, 9);
|
|
87
|
+
let fHi = word(h, 10);
|
|
88
|
+
let fLo = word(h, 11);
|
|
89
|
+
let gHi = word(h, 12);
|
|
90
|
+
let gLo = word(h, 13);
|
|
91
|
+
let hHi = word(h, 14);
|
|
92
|
+
let hLo = word(h, 15);
|
|
93
|
+
for (let t = 0; t < 80; t++) {
|
|
94
|
+
const bigS1Hi = ((eHi >>> 14) | (eLo << 18)) ^ ((eHi >>> 18) | (eLo << 14)) ^ ((eLo >>> 9) | (eHi << 23));
|
|
95
|
+
const bigS1Lo = ((eLo >>> 14) | (eHi << 18)) ^ ((eLo >>> 18) | (eHi << 14)) ^ ((eHi >>> 9) | (eLo << 23));
|
|
96
|
+
const chHi = (eHi & fHi) ^ (~eHi & gHi);
|
|
97
|
+
const chLo = (eLo & fLo) ^ (~eLo & gLo);
|
|
98
|
+
const t1Lo = hLo + (bigS1Lo >>> 0) + (chLo >>> 0) + word(K, t * 2 + 1) + word(schedule, t * 2 + 1);
|
|
99
|
+
const t1Hi = (hHi +
|
|
100
|
+
(bigS1Hi >>> 0) +
|
|
101
|
+
(chHi >>> 0) +
|
|
102
|
+
word(K, t * 2) +
|
|
103
|
+
word(schedule, t * 2) +
|
|
104
|
+
Math.floor(t1Lo / 0x1_0000_0000)) >>>
|
|
105
|
+
0;
|
|
106
|
+
const t1LoWrapped = t1Lo >>> 0;
|
|
107
|
+
const bigS0Hi = ((aHi >>> 28) | (aLo << 4)) ^ ((aLo >>> 2) | (aHi << 30)) ^ ((aLo >>> 7) | (aHi << 25));
|
|
108
|
+
const bigS0Lo = ((aLo >>> 28) | (aHi << 4)) ^ ((aHi >>> 2) | (aLo << 30)) ^ ((aHi >>> 7) | (aLo << 25));
|
|
109
|
+
const majHi = (aHi & bHi) ^ (aHi & cHi) ^ (bHi & cHi);
|
|
110
|
+
const majLo = (aLo & bLo) ^ (aLo & cLo) ^ (bLo & cLo);
|
|
111
|
+
const t2Lo = (bigS0Lo >>> 0) + (majLo >>> 0);
|
|
112
|
+
const t2Hi = ((bigS0Hi >>> 0) + (majHi >>> 0) + Math.floor(t2Lo / 0x1_0000_0000)) >>> 0;
|
|
113
|
+
const t2LoWrapped = t2Lo >>> 0;
|
|
114
|
+
hHi = gHi;
|
|
115
|
+
hLo = gLo;
|
|
116
|
+
gHi = fHi;
|
|
117
|
+
gLo = fLo;
|
|
118
|
+
fHi = eHi;
|
|
119
|
+
fLo = eLo;
|
|
120
|
+
const nextELo = dLo + t1LoWrapped;
|
|
121
|
+
eHi = (dHi + t1Hi + Math.floor(nextELo / 0x1_0000_0000)) >>> 0;
|
|
122
|
+
eLo = nextELo >>> 0;
|
|
123
|
+
dHi = cHi;
|
|
124
|
+
dLo = cLo;
|
|
125
|
+
cHi = bHi;
|
|
126
|
+
cLo = bLo;
|
|
127
|
+
bHi = aHi;
|
|
128
|
+
bLo = aLo;
|
|
129
|
+
const nextALo = t1LoWrapped + t2LoWrapped;
|
|
130
|
+
aHi = (t1Hi + t2Hi + Math.floor(nextALo / 0x1_0000_0000)) >>> 0;
|
|
131
|
+
aLo = nextALo >>> 0;
|
|
132
|
+
}
|
|
133
|
+
addInto(h, 0, aHi, aLo);
|
|
134
|
+
addInto(h, 2, bHi, bLo);
|
|
135
|
+
addInto(h, 4, cHi, cLo);
|
|
136
|
+
addInto(h, 6, dHi, dLo);
|
|
137
|
+
addInto(h, 8, eHi, eLo);
|
|
138
|
+
addInto(h, 10, fHi, fLo);
|
|
139
|
+
addInto(h, 12, gHi, gLo);
|
|
140
|
+
addInto(h, 14, hHi, hLo);
|
|
141
|
+
}
|
|
142
|
+
function addInto(h, index, hi, lo) {
|
|
143
|
+
const sumLo = word(h, index + 1) + lo;
|
|
144
|
+
h[index] = (word(h, index) + hi + Math.floor(sumLo / 0x1_0000_0000)) >>> 0;
|
|
145
|
+
h[index + 1] = sumLo >>> 0;
|
|
146
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { concat } from '../bytes.ts';
|
|
2
|
+
/** Read a little-endian `uint16`. @throws {VbaParseError} if the two bytes are not both in `buf`. */
|
|
3
|
+
export declare function readU16(buf: Uint8Array, at: number): number;
|
|
4
|
+
/** Read a little-endian `uint32`. @throws {VbaParseError} if the four bytes are not all in `buf`. */
|
|
5
|
+
export declare function readU32(buf: Uint8Array, at: number): number;
|
|
6
|
+
/**
|
|
7
|
+
* Decode UTF-16LE code units: the encoding [MS-CFB] uses for directory-entry names and [MS-OVBA] for
|
|
8
|
+
* every "Unicode" name field. A trailing odd byte is dropped: these fields are length-prefixed by the
|
|
9
|
+
* producer and a half code unit carries nothing to decode.
|
|
10
|
+
*/
|
|
11
|
+
export declare function decodeUtf16le(bytes: Uint8Array): string;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export { concat } from '../bytes.js';
|
|
2
|
+
import { VbaParseError } from './errors.js';
|
|
3
|
+
function truncated(at, need, length) {
|
|
4
|
+
return new VbaParseError(`read of ${need} bytes at offset ${at} runs past the end of a ${length}-byte buffer`);
|
|
5
|
+
}
|
|
6
|
+
export function readU16(buf, at) {
|
|
7
|
+
const b0 = buf[at];
|
|
8
|
+
const b1 = buf[at + 1];
|
|
9
|
+
if (b0 === undefined || b1 === undefined)
|
|
10
|
+
throw truncated(at, 2, buf.length);
|
|
11
|
+
return b0 | (b1 << 8);
|
|
12
|
+
}
|
|
13
|
+
export function readU32(buf, at) {
|
|
14
|
+
const b0 = buf[at];
|
|
15
|
+
const b1 = buf[at + 1];
|
|
16
|
+
const b2 = buf[at + 2];
|
|
17
|
+
const b3 = buf[at + 3];
|
|
18
|
+
if (b0 === undefined || b1 === undefined || b2 === undefined || b3 === undefined) {
|
|
19
|
+
throw truncated(at, 4, buf.length);
|
|
20
|
+
}
|
|
21
|
+
return (b0 | (b1 << 8) | (b2 << 16) | (b3 << 24)) >>> 0;
|
|
22
|
+
}
|
|
23
|
+
export function decodeUtf16le(bytes) {
|
|
24
|
+
let s = '';
|
|
25
|
+
for (let i = 0; i + 1 < bytes.length; i += 2) {
|
|
26
|
+
s += String.fromCharCode(readU16(bytes, i));
|
|
27
|
+
}
|
|
28
|
+
return s;
|
|
29
|
+
}
|
package/dist/vba/cfb-writer.d.ts
CHANGED
|
@@ -14,6 +14,6 @@ export type CfbNode = CfbStream | CfbStorage;
|
|
|
14
14
|
* by linear scan and by tree navigation.
|
|
15
15
|
*
|
|
16
16
|
* @throws {VbaAuthorError} if any name is empty or exceeds 31 characters, sibling names collide, or the
|
|
17
|
-
* project is so large it would need more than 109 FAT sectors (~7 MB
|
|
17
|
+
* project is so large it would need more than 109 FAT sectors (~7 MB, far beyond any real project).
|
|
18
18
|
*/
|
|
19
19
|
export declare function writeCompoundFile(root: readonly CfbNode[]): Uint8Array;
|
package/dist/vba/cfb.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export declare class CompoundFile {
|
|
|
8
8
|
readStream(name: string): Uint8Array | undefined;
|
|
9
9
|
/**
|
|
10
10
|
* Reconstruct the container's top-level children as the writer's node shape, recursing into every
|
|
11
|
-
* storage
|
|
11
|
+
* storage, so a caller can swap one stream and re-emit the whole hierarchy with {@link writeCompoundFile}.
|
|
12
12
|
* Walks the red-black sibling tree each storage navigates (not the linear directory scan), so any part
|
|
13
13
|
* a host reaches is carried through. Cycle- and bounds-guarded like every other chain walk here.
|
|
14
14
|
*/
|
package/dist/vba/cfb.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { concat, decodeUtf16le, readU16, readU32 } from './bytes.js';
|
|
1
2
|
import { VbaParseError } from './errors.js';
|
|
2
3
|
const MAX_REGULAR_SECTOR = 0xfffffffa;
|
|
3
4
|
const NOSTREAM = 0xffffffff;
|
|
@@ -10,7 +11,6 @@ const CFB_SIGNATURE_HI = 0xe11ab1a1;
|
|
|
10
11
|
const DIR_ENTRY_SIZE = 128;
|
|
11
12
|
export class CompoundFile {
|
|
12
13
|
#buf;
|
|
13
|
-
#view;
|
|
14
14
|
#sectorSize;
|
|
15
15
|
#miniSectorSize;
|
|
16
16
|
#miniCutoff;
|
|
@@ -21,14 +21,13 @@ export class CompoundFile {
|
|
|
21
21
|
#miniStream;
|
|
22
22
|
constructor(buf) {
|
|
23
23
|
this.#buf = buf;
|
|
24
|
-
this.#view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
25
24
|
if (buf.length < 512)
|
|
26
25
|
throw new VbaParseError('compound file shorter than its 512-byte header');
|
|
27
|
-
if (this.#
|
|
26
|
+
if (readU32(this.#buf, 0) !== CFB_SIGNATURE_LO || readU32(this.#buf, 4) !== CFB_SIGNATURE_HI) {
|
|
28
27
|
throw new VbaParseError('not a compound file (bad OLE2 signature)');
|
|
29
28
|
}
|
|
30
|
-
const sectorShift = this.#
|
|
31
|
-
const miniSectorShift = this.#
|
|
29
|
+
const sectorShift = readU16(this.#buf, 30);
|
|
30
|
+
const miniSectorShift = readU16(this.#buf, 32);
|
|
32
31
|
if (sectorShift !== 9 && sectorShift !== 12) {
|
|
33
32
|
throw new VbaParseError(`unsupported sector shift ${sectorShift}`);
|
|
34
33
|
}
|
|
@@ -37,12 +36,12 @@ export class CompoundFile {
|
|
|
37
36
|
this.#sectorSize = 1 << sectorShift;
|
|
38
37
|
this.#miniSectorSize = 1 << miniSectorShift;
|
|
39
38
|
this.#maxSector = Math.floor(buf.length / this.#sectorSize);
|
|
40
|
-
const numFatSectors = this.#
|
|
41
|
-
const firstDirSector = this.#
|
|
42
|
-
this.#miniCutoff = this.#
|
|
43
|
-
const firstMiniFatSector = this.#
|
|
44
|
-
const firstDifatSector = this.#
|
|
45
|
-
const numDifatSectors = this.#
|
|
39
|
+
const numFatSectors = readU32(this.#buf, 44);
|
|
40
|
+
const firstDirSector = readU32(this.#buf, 48);
|
|
41
|
+
this.#miniCutoff = readU32(this.#buf, 56);
|
|
42
|
+
const firstMiniFatSector = readU32(this.#buf, 60);
|
|
43
|
+
const firstDifatSector = readU32(this.#buf, 68);
|
|
44
|
+
const numDifatSectors = readU32(this.#buf, 72);
|
|
46
45
|
const fatSectorIds = this.#readDifat(numFatSectors, firstDifatSector, numDifatSectors);
|
|
47
46
|
this.#fat = this.#readFat(fatSectorIds);
|
|
48
47
|
this.#miniFat = this.#readChainValues(firstMiniFatSector);
|
|
@@ -103,7 +102,7 @@ export class CompoundFile {
|
|
|
103
102
|
#readDifat(numFatSectors, firstDifat, numDifat) {
|
|
104
103
|
const ids = [];
|
|
105
104
|
for (let i = 0; i < 109 && ids.length < numFatSectors; i++) {
|
|
106
|
-
const v = this.#
|
|
105
|
+
const v = readU32(this.#buf, 76 + i * 4);
|
|
107
106
|
if (v >= MAX_REGULAR_SECTOR)
|
|
108
107
|
break;
|
|
109
108
|
ids.push(v);
|
|
@@ -117,11 +116,11 @@ export class CompoundFile {
|
|
|
117
116
|
seen.add(sector);
|
|
118
117
|
const base = this.#dataSectorOffset(sector);
|
|
119
118
|
for (let i = 0; i < perSector; i++) {
|
|
120
|
-
const v = this.#
|
|
119
|
+
const v = readU32(this.#buf, base + i * 4);
|
|
121
120
|
if (v < MAX_REGULAR_SECTOR)
|
|
122
121
|
ids.push(v);
|
|
123
122
|
}
|
|
124
|
-
sector = this.#
|
|
123
|
+
sector = readU32(this.#buf, base + perSector * 4);
|
|
125
124
|
}
|
|
126
125
|
return ids;
|
|
127
126
|
}
|
|
@@ -131,7 +130,7 @@ export class CompoundFile {
|
|
|
131
130
|
for (const sid of fatSectorIds) {
|
|
132
131
|
const base = this.#dataSectorOffset(sid);
|
|
133
132
|
for (let i = 0; i < perSector; i++)
|
|
134
|
-
fat.push(this.#
|
|
133
|
+
fat.push(readU32(this.#buf, base + i * 4));
|
|
135
134
|
}
|
|
136
135
|
return fat;
|
|
137
136
|
}
|
|
@@ -146,7 +145,7 @@ export class CompoundFile {
|
|
|
146
145
|
seen.add(sector);
|
|
147
146
|
const base = this.#dataSectorOffset(sector);
|
|
148
147
|
for (let i = 0; i < perSector; i++)
|
|
149
|
-
values.push(this.#
|
|
148
|
+
values.push(readU32(this.#buf, base + i * 4));
|
|
150
149
|
sector = this.#nextInFat(sector);
|
|
151
150
|
}
|
|
152
151
|
return values;
|
|
@@ -256,39 +255,4 @@ export class CompoundFile {
|
|
|
256
255
|
}
|
|
257
256
|
return base;
|
|
258
257
|
}
|
|
259
|
-
#u16(at) {
|
|
260
|
-
return this.#view.getUint16(at, true);
|
|
261
|
-
}
|
|
262
|
-
#u32(at) {
|
|
263
|
-
return this.#view.getUint32(at, true);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
function readU16(buf, at) {
|
|
267
|
-
return buf[at] | (buf[at + 1] << 8);
|
|
268
|
-
}
|
|
269
|
-
function readU32(buf, at) {
|
|
270
|
-
return ((buf[at] |
|
|
271
|
-
(buf[at + 1] << 8) |
|
|
272
|
-
(buf[at + 2] << 16) |
|
|
273
|
-
(buf[at + 3] << 24)) >>>
|
|
274
|
-
0);
|
|
275
|
-
}
|
|
276
|
-
function decodeUtf16le(bytes) {
|
|
277
|
-
let s = '';
|
|
278
|
-
for (let i = 0; i + 1 < bytes.length; i += 2) {
|
|
279
|
-
s += String.fromCharCode(bytes[i] | (bytes[i + 1] << 8));
|
|
280
|
-
}
|
|
281
|
-
return s;
|
|
282
|
-
}
|
|
283
|
-
function concat(chunks) {
|
|
284
|
-
let total = 0;
|
|
285
|
-
for (const c of chunks)
|
|
286
|
-
total += c.length;
|
|
287
|
-
const out = new Uint8Array(total);
|
|
288
|
-
let off = 0;
|
|
289
|
-
for (const c of chunks) {
|
|
290
|
-
out.set(c, off);
|
|
291
|
-
off += c.length;
|
|
292
|
-
}
|
|
293
|
-
return out;
|
|
294
258
|
}
|
package/dist/vba/codepage.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export type Decoder = InstanceType<typeof TextDecoder>;
|
|
|
3
3
|
export type Encoder = (text: string) => Uint8Array;
|
|
4
4
|
/**
|
|
5
5
|
* A `TextDecoder` for the given VBA project code page. Non-fatal (malformed bytes become U+FFFD rather
|
|
6
|
-
* than throwing) because recovered source is for reading, not re-encoding
|
|
6
|
+
* than throwing) because recovered source is for reading, not re-encoding: a stray byte must not sink
|
|
7
7
|
* the whole extraction. An unknown code page falls back to windows-1252.
|
|
8
8
|
*/
|
|
9
9
|
export declare function decoderForCodePage(codePage: number): Decoder;
|
|
@@ -12,7 +12,7 @@ export declare function decoderForCodePage(codePage: number): Decoder;
|
|
|
12
12
|
* decoding every byte 0x00..0xFF through the matching decoder and inverting the map. This is exact for
|
|
13
13
|
* the single-byte code pages a VBA project realistically declares. Under a multi-byte code page (Shift
|
|
14
14
|
* JIS, GBK, …) the high bytes are lead bytes, not standalone characters, so only ASCII inverts and any
|
|
15
|
-
* non-ASCII character is rejected
|
|
15
|
+
* non-ASCII character is rejected, failing closed rather than emitting a corrupt byte. A character the chosen
|
|
16
16
|
* code page cannot represent throws {@link VbaAuthorError}.
|
|
17
17
|
*/
|
|
18
18
|
export declare function encoderForCodePage(codePage: number): Encoder;
|