@shbernal/ts-xlsx 3.0.0 → 3.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 +4 -1
- package/dist/bytes.d.ts +7 -0
- package/dist/bytes.js +22 -0
- package/dist/core/address.d.ts +60 -2
- package/dist/core/address.js +40 -24
- package/dist/core/autofilter.d.ts +4 -3
- package/dist/core/autofilter.js +31 -30
- package/dist/core/axis-handle.d.ts +2 -0
- package/dist/core/axis-handle.js +5 -2
- package/dist/core/cell.d.ts +4 -3
- package/dist/core/cell.js +4 -6
- package/dist/core/clone.d.ts +29 -0
- package/dist/core/clone.js +24 -0
- package/dist/core/color-resolution.js +2 -4
- package/dist/core/column.d.ts +1 -0
- package/dist/core/column.js +3 -0
- package/dist/core/comment-thread.js +2 -1
- package/dist/core/conditional-formatting-overlay.d.ts +2 -1
- package/dist/core/conditional-formatting-overlay.js +2 -2
- package/dist/core/conditional-formatting.d.ts +9 -1
- package/dist/core/conditional-formatting.js +34 -18
- package/dist/core/containers.d.ts +12 -0
- package/dist/core/containers.js +5 -0
- package/dist/core/data-validation-overlay.d.ts +2 -1
- package/dist/core/data-validation-overlay.js +4 -4
- package/dist/core/data-validation.d.ts +6 -0
- package/dist/core/data-validation.js +15 -4
- package/dist/core/date-format.d.ts +20 -0
- package/dist/core/date-format.js +160 -0
- package/dist/core/date.d.ts +43 -8
- package/dist/core/date.js +18 -2
- package/dist/core/formula.d.ts +26 -0
- package/dist/core/formula.js +20 -4
- package/dist/core/grid-edits.d.ts +2 -3
- package/dist/core/grid-edits.js +69 -70
- package/dist/core/grid-shift.d.ts +58 -9
- package/dist/core/grid-shift.js +26 -4
- package/dist/core/image.d.ts +13 -9
- package/dist/core/image.js +7 -5
- package/dist/core/internal.d.ts +8 -2
- package/dist/core/limits.d.ts +23 -0
- package/dist/core/limits.js +4 -0
- package/dist/core/merge.d.ts +5 -4
- package/dist/core/merge.js +18 -19
- package/dist/core/pivot-table.js +63 -45
- package/dist/core/protection.js +2 -11
- package/dist/core/range.js +34 -30
- package/dist/core/row-input.js +2 -2
- package/dist/core/row.d.ts +1 -0
- package/dist/core/row.js +3 -0
- package/dist/core/style.d.ts +85 -70
- package/dist/core/style.js +92 -28
- package/dist/core/table-style.d.ts +1 -1
- package/dist/core/table-style.js +4 -4
- package/dist/core/table.d.ts +27 -9
- package/dist/core/table.js +51 -34
- package/dist/core/theme.js +6 -5
- package/dist/core/value.d.ts +9 -0
- package/dist/core/value.js +36 -34
- package/dist/core/workbook-media.d.ts +34 -0
- package/dist/core/workbook-media.js +44 -0
- package/dist/core/workbook-styles.d.ts +67 -1
- package/dist/core/workbook-vba.js +3 -2
- package/dist/core/workbook.d.ts +23 -2
- package/dist/core/workbook.js +24 -40
- package/dist/core/worksheet-comments.d.ts +2 -1
- package/dist/core/worksheet-comments.js +20 -13
- package/dist/core/worksheet-merges.d.ts +44 -0
- package/dist/core/worksheet-merges.js +59 -0
- package/dist/core/worksheet-model.d.ts +9 -1
- package/dist/core/worksheet-model.js +24 -9
- package/dist/core/worksheet.d.ts +55 -6
- package/dist/core/worksheet.js +36 -44
- package/dist/customui/ribbon.js +2 -1
- package/dist/entries/core.d.ts +18 -13
- package/dist/entries/core.js +12 -6
- package/dist/entries/xlsx.d.ts +1 -1
- package/dist/errors.d.ts +64 -0
- package/dist/errors.js +22 -0
- package/dist/hex.d.ts +8 -0
- package/dist/hex.js +3 -0
- package/dist/io/csv/delimiter.d.ts +9 -0
- package/dist/io/csv/delimiter.js +6 -0
- package/dist/io/csv/read.js +11 -4
- package/dist/io/csv/write.d.ts +9 -2
- package/dist/io/csv/write.js +9 -38
- package/dist/io/opc/inflate.js +14 -0
- package/dist/io/opc/namespaces.d.ts +16 -0
- package/dist/io/opc/namespaces.js +4 -0
- package/dist/io/opc/part-paths.d.ts +9 -0
- package/dist/io/opc/part-paths.js +1 -1
- package/dist/io/opc/read-opc.d.ts +54 -14
- package/dist/io/opc/read-opc.js +42 -20
- package/dist/io/opc/read-options.d.ts +5 -1
- package/dist/io/opc/sniff-format.d.ts +3 -3
- package/dist/io/opc/sniff-format.js +2 -2
- package/dist/io/style/xf-style.d.ts +23 -1
- package/dist/io/style/xf-style.js +8 -0
- package/dist/io/xlsb/formula.d.ts +2 -2
- package/dist/io/xlsb/formula.js +49 -34
- package/dist/io/xlsb/primitives.js +8 -14
- package/dist/io/xlsb/ptg-functions.d.ts +19 -10
- package/dist/io/xlsb/ptg-functions.js +385 -596
- package/dist/io/xlsb/read-styles.js +45 -81
- package/dist/io/xlsb/read-worksheet.d.ts +20 -1
- package/dist/io/xlsb/read-worksheet.js +73 -89
- package/dist/io/xlsb/read.d.ts +6 -3
- package/dist/io/xlsb/read.js +44 -23
- package/dist/io/xlsb/record-stream.d.ts +30 -0
- package/dist/io/xlsb/record-stream.js +20 -0
- package/dist/io/xlsb/record-types.d.ts +1 -5
- package/dist/io/xlsb/record-types.js +1 -5
- package/dist/io/xlsx/cell-accumulator.d.ts +8 -1
- package/dist/io/xlsx/cell-accumulator.js +32 -9
- package/dist/io/xlsx/cell-style-resolution.d.ts +25 -0
- package/dist/io/xlsx/cell-style-resolution.js +29 -0
- package/dist/io/xlsx/cell-value.d.ts +5 -3
- package/dist/io/xlsx/cell-value.js +13 -15
- package/dist/io/xlsx/color-xml.js +3 -2
- package/dist/io/xlsx/column-budget.d.ts +26 -0
- package/dist/io/xlsx/column-budget.js +22 -0
- package/dist/io/xlsx/comments.d.ts +6 -2
- package/dist/io/xlsx/comments.js +36 -32
- package/dist/io/xlsx/conditional-formatting.d.ts +6 -3
- package/dist/io/xlsx/conditional-formatting.js +30 -26
- package/dist/io/xlsx/data-validation.js +29 -27
- package/dist/io/xlsx/edit-vba.d.ts +7 -2
- package/dist/io/xlsx/edit-vba.js +14 -21
- package/dist/io/xlsx/font-xml.d.ts +7 -0
- package/dist/io/xlsx/font-xml.js +104 -0
- package/dist/io/xlsx/hyperlinks.d.ts +37 -5
- package/dist/io/xlsx/hyperlinks.js +22 -13
- package/dist/io/xlsx/images.js +11 -13
- package/dist/io/xlsx/namespaces.d.ts +16 -0
- package/dist/io/xlsx/namespaces.js +7 -0
- package/dist/io/xlsx/package-plan.d.ts +14 -2
- package/dist/io/xlsx/package-plan.js +39 -11
- package/dist/io/xlsx/pivot.js +8 -7
- package/dist/io/xlsx/read-parts.d.ts +59 -0
- package/dist/io/xlsx/read-parts.js +252 -0
- package/dist/io/xlsx/read-pivot.js +2 -1
- package/dist/io/xlsx/read-repair.d.ts +26 -0
- package/dist/io/xlsx/read-repair.js +39 -0
- package/dist/io/xlsx/{rich-runs.js → read-rich-runs.js} +6 -3
- package/dist/io/xlsx/read-rows.d.ts +18 -5
- package/dist/io/xlsx/read-rows.js +45 -31
- package/dist/io/xlsx/read-shared-strings.js +3 -2
- package/dist/io/xlsx/read-styles.d.ts +1 -6
- package/dist/io/xlsx/read-styles.js +6 -71
- package/dist/io/xlsx/read-workbook-xml.d.ts +36 -0
- package/dist/io/xlsx/read-workbook-xml.js +170 -0
- package/dist/io/xlsx/read-worksheet.d.ts +2 -1
- package/dist/io/xlsx/read-worksheet.js +35 -164
- package/dist/io/xlsx/read.d.ts +5 -12
- package/dist/io/xlsx/read.js +73 -406
- package/dist/io/xlsx/rich-text.js +1 -1
- package/dist/io/xlsx/row-position.d.ts +18 -0
- package/dist/io/xlsx/row-position.js +11 -0
- package/dist/io/xlsx/row-xml.d.ts +128 -0
- package/dist/io/xlsx/row-xml.js +203 -0
- package/dist/io/xlsx/shared-formulas.js +2 -2
- package/dist/io/xlsx/sheet-properties.d.ts +10 -1
- package/dist/io/xlsx/sheet-properties.js +145 -9
- package/dist/io/xlsx/style-elements.d.ts +43 -0
- package/dist/io/xlsx/style-elements.js +156 -0
- package/dist/io/xlsx/styles.d.ts +12 -3
- package/dist/io/xlsx/styles.js +5 -207
- package/dist/io/xlsx/tables.js +10 -14
- package/dist/io/xlsx/theme-xml.d.ts +26 -4
- package/dist/io/xlsx/theme-xml.js +107 -69
- package/dist/io/xlsx/threaded-comments.d.ts +8 -10
- package/dist/io/xlsx/threaded-comments.js +2 -1
- package/dist/io/xlsx/workbook-xml.d.ts +54 -4
- package/dist/io/xlsx/workbook-xml.js +60 -34
- package/dist/io/xlsx/worksheet-xml.d.ts +23 -70
- package/dist/io/xlsx/worksheet-xml.js +48 -229
- package/dist/io/xlsx/write-stream.d.ts +65 -16
- package/dist/io/xlsx/write-stream.js +50 -20
- package/dist/io/xlsx/write.d.ts +1 -2
- package/dist/io/xlsx/write.js +114 -74
- package/dist/rel-type.d.ts +16 -0
- package/dist/rel-type.js +6 -0
- package/dist/token-set.d.ts +13 -0
- package/dist/token-set.js +4 -1
- package/dist/vba/bytes.d.ts +14 -5
- package/dist/vba/bytes.js +16 -6
- package/dist/vba/cfb-format.d.ts +8 -0
- package/dist/vba/cfb-format.js +1 -0
- package/dist/vba/cfb-writer.d.ts +9 -0
- package/dist/vba/cfb-writer.js +12 -9
- package/dist/vba/cfb.d.ts +13 -1
- package/dist/vba/cfb.js +73 -51
- package/dist/vba/codepage.js +2 -1
- package/dist/vba/errors.d.ts +7 -0
- package/dist/vba/ms-ovba.js +51 -16
- package/dist/vba/project-editor.js +35 -40
- package/dist/vba/project.d.ts +11 -1
- package/dist/vba/project.js +27 -8
- package/dist/vba/vba-encoding.js +4 -5
- package/dist/xml/xml-attrs.d.ts +37 -0
- package/dist/xml/xml-attrs.js +30 -0
- package/dist/xml/xml-chars.d.ts +40 -0
- package/dist/xml/xml-chars.js +12 -0
- package/dist/xml/xml-namespaces.d.ts +31 -0
- package/dist/xml/xml-namespaces.js +50 -0
- package/dist/xml/xml-read.d.ts +72 -10
- package/dist/xml/xml-read.js +94 -18
- package/dist/xml/xml-scan.d.ts +15 -37
- package/dist/xml/xml-scan.js +12 -40
- package/dist/xml/xml.d.ts +28 -27
- package/dist/xml/xml.js +24 -24
- package/package.json +12 -7
- /package/dist/io/xlsx/{rich-runs.d.ts → read-rich-runs.d.ts} +0 -0
package/dist/core/grid-edits.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { decodeRange, encodeAddress, tryDecodeCellRef } from './address.js';
|
|
1
|
+
import { boundedRect, decodeRange, encodeAddress, encodeRect, tryDecodeCellRef } from './address.js';
|
|
2
2
|
import { shiftAutoFilter } from './autofilter.js';
|
|
3
3
|
import { Cell, copyCellContent } from './cell.js';
|
|
4
4
|
import { replaceContents } from './containers.js';
|
|
5
|
-
import { isDeletedSpan, shiftIndex } from './grid-shift.js';
|
|
5
|
+
import { isDeletedSpan, shiftIndex, shiftRect } from './grid-shift.js';
|
|
6
6
|
import { isOneCellAnchor } from './image.js';
|
|
7
|
+
import { positionalPlacements } from './row-input.js';
|
|
7
8
|
import { isSharedFormulaValue } from './value.js';
|
|
8
9
|
export class GridEdits {
|
|
9
10
|
#rows;
|
|
10
11
|
#rowProperties;
|
|
11
12
|
#columns;
|
|
12
13
|
#merges;
|
|
13
|
-
#mergeRects;
|
|
14
14
|
#tables;
|
|
15
15
|
#images;
|
|
16
16
|
#dataValidations;
|
|
@@ -22,7 +22,6 @@ export class GridEdits {
|
|
|
22
22
|
this.#rowProperties = storage.rowProperties;
|
|
23
23
|
this.#columns = storage.columns;
|
|
24
24
|
this.#merges = storage.merges;
|
|
25
|
-
this.#mergeRects = storage.mergeRects;
|
|
26
25
|
this.#tables = storage.tables;
|
|
27
26
|
this.#images = storage.images;
|
|
28
27
|
this.#dataValidations = storage.dataValidations;
|
|
@@ -31,13 +30,15 @@ export class GridEdits {
|
|
|
31
30
|
this.#autoFilter = storage.autoFilter;
|
|
32
31
|
}
|
|
33
32
|
spliceRows(start, count, inserted) {
|
|
34
|
-
const
|
|
33
|
+
const splice = { axis: 'row', start, count, delta: inserted.length - count };
|
|
35
34
|
const shifted = new Map();
|
|
36
35
|
for (const [row, cols] of this.#rows) {
|
|
37
36
|
if (row < start)
|
|
38
37
|
shifted.set(row, cols);
|
|
39
|
-
else if (row >= start + count)
|
|
40
|
-
|
|
38
|
+
else if (row >= start + count) {
|
|
39
|
+
const dest = shiftIndex(row, splice);
|
|
40
|
+
shifted.set(dest, this.#relocateRow(cols, dest));
|
|
41
|
+
}
|
|
41
42
|
}
|
|
42
43
|
inserted.forEach((cols, i) => {
|
|
43
44
|
shifted.set(start + i, this.#relocateRow(cols, start + i));
|
|
@@ -45,15 +46,16 @@ export class GridEdits {
|
|
|
45
46
|
this.#rows.clear();
|
|
46
47
|
for (const [row, cols] of shifted)
|
|
47
48
|
this.#rows.set(row, cols);
|
|
48
|
-
this.#shiftLineProperties(this.#rowProperties,
|
|
49
|
-
this.#shiftMerges(
|
|
50
|
-
this.#shiftTables(
|
|
51
|
-
this.#shiftImages(
|
|
52
|
-
this.#reanchorSharedFormulas(
|
|
53
|
-
this.#shiftRangeBoundOverlays(
|
|
49
|
+
this.#shiftLineProperties(this.#rowProperties, splice);
|
|
50
|
+
this.#shiftMerges(splice);
|
|
51
|
+
this.#shiftTables(splice);
|
|
52
|
+
this.#shiftImages(splice);
|
|
53
|
+
this.#reanchorSharedFormulas(splice);
|
|
54
|
+
this.#shiftRangeBoundOverlays(splice);
|
|
54
55
|
}
|
|
55
56
|
spliceColumns(start, count, inserts) {
|
|
56
|
-
const
|
|
57
|
+
const splice = { axis: 'col', start, count, delta: inserts.length - count };
|
|
58
|
+
const shiftedRows = new Map();
|
|
57
59
|
for (const [row, cols] of this.#rows) {
|
|
58
60
|
const shifted = new Map();
|
|
59
61
|
for (const [col, cell] of cols) {
|
|
@@ -61,37 +63,42 @@ export class GridEdits {
|
|
|
61
63
|
shifted.set(col, cell);
|
|
62
64
|
}
|
|
63
65
|
else if (col >= start + count) {
|
|
64
|
-
const dest = col
|
|
66
|
+
const dest = shiftIndex(col, splice);
|
|
65
67
|
const moved = new Cell(row, dest);
|
|
66
68
|
copyCellContent(cell, moved);
|
|
67
69
|
shifted.set(dest, moved);
|
|
68
70
|
}
|
|
69
71
|
}
|
|
70
|
-
|
|
71
|
-
const value = values[row - 1];
|
|
72
|
-
if (value !== undefined) {
|
|
73
|
-
const cell = new Cell(row, start + i);
|
|
74
|
-
cell.value = value;
|
|
75
|
-
shifted.set(start + i, cell);
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
this.#rows.set(row, shifted);
|
|
72
|
+
shiftedRows.set(row, shifted);
|
|
79
73
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
74
|
+
inserts.forEach((values, i) => {
|
|
75
|
+
for (const [row, value] of positionalPlacements(values)) {
|
|
76
|
+
const cell = new Cell(row, start + i);
|
|
77
|
+
cell.value = value;
|
|
78
|
+
let cols = shiftedRows.get(row);
|
|
79
|
+
if (cols === undefined) {
|
|
80
|
+
cols = new Map();
|
|
81
|
+
shiftedRows.set(row, cols);
|
|
82
|
+
}
|
|
83
|
+
cols.set(start + i, cell);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
for (const [row, cols] of shiftedRows)
|
|
87
|
+
this.#rows.set(row, cols);
|
|
88
|
+
this.#shiftLineProperties(this.#columns, splice);
|
|
89
|
+
this.#shiftMerges(splice);
|
|
90
|
+
this.#shiftTables(splice);
|
|
91
|
+
this.#shiftImages(splice);
|
|
92
|
+
this.#reanchorSharedFormulas(splice);
|
|
93
|
+
this.#shiftRangeBoundOverlays(splice);
|
|
86
94
|
}
|
|
87
|
-
#shiftRangeBoundOverlays(
|
|
88
|
-
this.#dataValidations.shift(
|
|
89
|
-
this.#conditionalFormattings.shift(
|
|
90
|
-
this.#comments.shift(
|
|
95
|
+
#shiftRangeBoundOverlays(splice) {
|
|
96
|
+
this.#dataValidations.shift(splice);
|
|
97
|
+
this.#conditionalFormattings.shift(splice);
|
|
98
|
+
this.#comments.shift(splice);
|
|
91
99
|
const filter = this.#autoFilter.get();
|
|
92
|
-
if (filter !== undefined)
|
|
93
|
-
this.#autoFilter.set(shiftAutoFilter(filter,
|
|
94
|
-
}
|
|
100
|
+
if (filter !== undefined)
|
|
101
|
+
this.#autoFilter.set(shiftAutoFilter(filter, splice));
|
|
95
102
|
}
|
|
96
103
|
#relocateRow(cols, destRow) {
|
|
97
104
|
const moved = new Map();
|
|
@@ -107,7 +114,7 @@ export class GridEdits {
|
|
|
107
114
|
}
|
|
108
115
|
return moved;
|
|
109
116
|
}
|
|
110
|
-
#reanchorSharedFormulas(
|
|
117
|
+
#reanchorSharedFormulas(splice) {
|
|
111
118
|
for (const cols of this.#rows.values()) {
|
|
112
119
|
for (const cell of cols.values()) {
|
|
113
120
|
const value = cell.value;
|
|
@@ -116,9 +123,9 @@ export class GridEdits {
|
|
|
116
123
|
const master = tryDecodeCellRef(value.sharedFormula);
|
|
117
124
|
if (master === undefined)
|
|
118
125
|
continue;
|
|
119
|
-
const anchored = axis === 'row'
|
|
120
|
-
? encodeAddress(master.col, shiftIndex(master.row,
|
|
121
|
-
: encodeAddress(shiftIndex(master.col,
|
|
126
|
+
const anchored = splice.axis === 'row'
|
|
127
|
+
? encodeAddress(master.col, shiftIndex(master.row, splice))
|
|
128
|
+
: encodeAddress(shiftIndex(master.col, splice), master.row);
|
|
122
129
|
if (anchored === value.sharedFormula)
|
|
123
130
|
continue;
|
|
124
131
|
const reanchored = { ...value, sharedFormula: anchored };
|
|
@@ -126,59 +133,51 @@ export class GridEdits {
|
|
|
126
133
|
}
|
|
127
134
|
}
|
|
128
135
|
}
|
|
129
|
-
#shiftLineProperties(map,
|
|
136
|
+
#shiftLineProperties(map, splice) {
|
|
130
137
|
const shifted = new Map();
|
|
131
138
|
for (const [index, value] of map) {
|
|
132
|
-
if (index
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
shifted.set(index + delta, value);
|
|
139
|
+
if (isDeletedSpan(index, index, splice))
|
|
140
|
+
continue;
|
|
141
|
+
shifted.set(shiftIndex(index, splice), value);
|
|
136
142
|
}
|
|
137
143
|
map.clear();
|
|
138
144
|
for (const [index, value] of shifted)
|
|
139
145
|
map.set(index, value);
|
|
140
146
|
}
|
|
141
|
-
#shiftMerges(
|
|
142
|
-
const shift = (v) => shiftIndex(v, start, count, delta, axis);
|
|
147
|
+
#shiftMerges(splice) {
|
|
143
148
|
const merges = [];
|
|
144
149
|
const rects = [];
|
|
145
|
-
for (const range of this.#merges) {
|
|
146
|
-
const
|
|
147
|
-
if (
|
|
150
|
+
for (const range of this.#merges.ranges) {
|
|
151
|
+
const decoded = boundedRect(decodeRange(range));
|
|
152
|
+
if (decoded === undefined) {
|
|
148
153
|
merges.push(range);
|
|
149
154
|
continue;
|
|
150
155
|
}
|
|
151
|
-
const
|
|
152
|
-
if (
|
|
156
|
+
const rect = shiftRect(decoded, splice);
|
|
157
|
+
if (rect === undefined)
|
|
153
158
|
continue;
|
|
154
|
-
const rect = axis === 'row'
|
|
155
|
-
? { top: shift(top), left, bottom: shift(bottom), right }
|
|
156
|
-
: { top, left: shift(left), bottom, right: shift(right) };
|
|
157
159
|
rects.push(rect);
|
|
158
|
-
merges.push(
|
|
160
|
+
merges.push(encodeRect(rect));
|
|
159
161
|
}
|
|
160
|
-
|
|
161
|
-
replaceContents(this.#mergeRects, rects);
|
|
162
|
+
this.#merges.replaceAll(merges, rects);
|
|
162
163
|
}
|
|
163
|
-
#shiftTables(
|
|
164
|
-
const survivors = this.#tables.filter((table) => axis === 'row'
|
|
165
|
-
? table.shiftRows(start, count, delta)
|
|
166
|
-
: table.shiftColumns(start, count, delta));
|
|
164
|
+
#shiftTables(splice) {
|
|
165
|
+
const survivors = this.#tables.filter((table) => splice.axis === 'row' ? table.shiftRows(splice) : table.shiftColumns(splice));
|
|
167
166
|
replaceContents(this.#tables, survivors);
|
|
168
167
|
}
|
|
169
|
-
#shiftImages(
|
|
170
|
-
const
|
|
171
|
-
const zeroBased = axis === 'row' ? point.row : point.col;
|
|
172
|
-
const shifted = shiftIndex(zeroBased + 1,
|
|
168
|
+
#shiftImages(splice) {
|
|
169
|
+
const shiftAnchor = (point) => {
|
|
170
|
+
const zeroBased = splice.axis === 'row' ? point.row : point.col;
|
|
171
|
+
const shifted = shiftIndex(zeroBased + 1, splice) - 1;
|
|
173
172
|
if (shifted === zeroBased)
|
|
174
173
|
return point;
|
|
175
|
-
return axis === 'row' ? { ...point, row: shifted } : { ...point, col: shifted };
|
|
174
|
+
return splice.axis === 'row' ? { ...point, row: shifted } : { ...point, col: shifted };
|
|
176
175
|
};
|
|
177
176
|
const moved = this.#images.map((image) => {
|
|
178
|
-
const from =
|
|
177
|
+
const from = shiftAnchor(image.anchor.from);
|
|
179
178
|
const anchor = isOneCellAnchor(image.anchor)
|
|
180
179
|
? { ...image.anchor, from }
|
|
181
|
-
: { ...image.anchor, from, to:
|
|
180
|
+
: { ...image.anchor, from, to: shiftAnchor(image.anchor.to) };
|
|
182
181
|
return { imageId: image.imageId, anchor };
|
|
183
182
|
});
|
|
184
183
|
replaceContents(this.#images, moved);
|
|
@@ -1,19 +1,68 @@
|
|
|
1
|
+
import { type CellPosition, type GridRect } from './address.ts';
|
|
1
2
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
3
|
+
* One structural edit to an axis: `count` lines removed at the 1-based `start`, with everything
|
|
4
|
+
* after the removed span moved by `delta`. `delta` is the *net* movement, so a pure insert of two
|
|
5
|
+
* lines is `{count: 0, delta: 2}` and a pure delete of three is `{count: 3, delta: -3}`.
|
|
6
|
+
*
|
|
7
|
+
* The four travel as one value because they describe one edit, and because three of them are
|
|
8
|
+
* `number`: passed positionally, two adjacent ones transposed compiles, typechecks, lints, and moves
|
|
9
|
+
* a merge, a dropdown, a highlight or a comment anchor onto cells the author never chose -- silently,
|
|
10
|
+
* since nothing about the resulting file is malformed. A named field cannot be transposed.
|
|
11
|
+
*/
|
|
12
|
+
export interface AxisSplice {
|
|
13
|
+
/** The axis the lines were spliced on. */
|
|
14
|
+
readonly axis: 'row' | 'col';
|
|
15
|
+
/** 1-based first line of the removed span. */
|
|
16
|
+
readonly start: number;
|
|
17
|
+
/** How many lines were removed. */
|
|
18
|
+
readonly count: number;
|
|
19
|
+
/** Net movement of every line after the removed span. */
|
|
20
|
+
readonly delta: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Where a 1-based coordinate on the spliced axis lands: before the edit it stays put, at or after
|
|
24
|
+
* the edited span it shifts by `delta`, and inside a deleted span it clamps to the cut line. The
|
|
25
|
+
* clamp is the best effort for a geometry straddling the cut; a caller that must instead *drop*
|
|
26
|
+
* what a delete swallowed whole tests {@link isDeletedSpan} first.
|
|
7
27
|
*
|
|
8
28
|
* A shift never leaves the grid: the result is clamped to the axis's last line, so a region already
|
|
9
29
|
* touching the bottom (or the right edge) keeps its edge there instead of naming a line the format
|
|
10
30
|
* has no room for. That shrinks such a region by what it could not move, which is the lesser of the
|
|
11
31
|
* two evils and is what Excel does to the same region on the same edit.
|
|
12
32
|
*/
|
|
13
|
-
export declare function shiftIndex(v: number,
|
|
33
|
+
export declare function shiftIndex(v: number, splice: AxisSplice): number;
|
|
14
34
|
/**
|
|
15
|
-
* Whether the inclusive span `lo..hi` lies entirely within the
|
|
16
|
-
*
|
|
35
|
+
* Whether the inclusive span `lo..hi` lies entirely within the deleted lines: the test that
|
|
36
|
+
* separates "this moved" from "this is gone". A single coordinate is the degenerate span
|
|
17
37
|
* `lo === hi`.
|
|
18
38
|
*/
|
|
19
|
-
export declare function isDeletedSpan(lo: number, hi: number,
|
|
39
|
+
export declare function isDeletedSpan(lo: number, hi: number, splice: AxisSplice): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Both edges of a region's span on the spliced axis, moved together, or `undefined` when the delete
|
|
42
|
+
* swallowed the span whole. The two answers are exclusive on purpose: a span straddling the cut
|
|
43
|
+
* clamps and survives, one wholly inside it is dropped, and a caller must not clamp its way out of
|
|
44
|
+
* a drop.
|
|
45
|
+
*
|
|
46
|
+
* The one-axis form, for a region whose *other* axis is not a number the caller holds -- a `sqref`
|
|
47
|
+
* area may be unbounded across the axis it is not being spliced on, and re-encoding it as bounded
|
|
48
|
+
* would rewrite the file's own spelling. Callers holding a real rectangle want {@link shiftRect}.
|
|
49
|
+
*/
|
|
50
|
+
export declare function shiftSpan(lo: number, hi: number, splice: AxisSplice): {
|
|
51
|
+
lo: number;
|
|
52
|
+
hi: number;
|
|
53
|
+
} | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* A rectangle re-anchored through the splice, or `undefined` when the delete swallowed it whole.
|
|
56
|
+
* The unspliced axis passes through untouched; both edges of the spliced axis move and clamp.
|
|
57
|
+
*
|
|
58
|
+
* This projection -- pick the spliced axis's two edges out of the rectangle, ask whether they
|
|
59
|
+
* survived, move them, put the rectangle back together -- is the shape every range-bound thing in
|
|
60
|
+
* the model re-anchors by, and writing it per caller is how the axis ternary gets inverted in one
|
|
61
|
+
* of them.
|
|
62
|
+
*/
|
|
63
|
+
export declare function shiftRect(rect: GridRect, splice: AxisSplice): GridRect | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* The degenerate one-coordinate form of {@link shiftRect}: a cell that moves, or `undefined` when
|
|
66
|
+
* the delete took the line it sat on. The coordinate off the spliced axis passes through.
|
|
67
|
+
*/
|
|
68
|
+
export declare function shiftPoint(point: CellPosition, splice: AxisSplice): CellPosition | undefined;
|
package/dist/core/grid-shift.js
CHANGED
|
@@ -1,8 +1,30 @@
|
|
|
1
1
|
import { MAX_COLUMN, MAX_ROW } from './address.js';
|
|
2
|
-
export function shiftIndex(v,
|
|
2
|
+
export function shiftIndex(v, splice) {
|
|
3
|
+
const { start, count, delta } = splice;
|
|
3
4
|
const moved = v < start ? v : v >= start + count ? v + delta : start;
|
|
4
|
-
return Math.min(moved, axis === 'row' ? MAX_ROW : MAX_COLUMN);
|
|
5
|
+
return Math.min(moved, splice.axis === 'row' ? MAX_ROW : MAX_COLUMN);
|
|
5
6
|
}
|
|
6
|
-
export function isDeletedSpan(lo, hi,
|
|
7
|
-
return lo >= start && hi < start + count;
|
|
7
|
+
export function isDeletedSpan(lo, hi, splice) {
|
|
8
|
+
return lo >= splice.start && hi < splice.start + splice.count;
|
|
9
|
+
}
|
|
10
|
+
export function shiftSpan(lo, hi, splice) {
|
|
11
|
+
if (isDeletedSpan(lo, hi, splice))
|
|
12
|
+
return undefined;
|
|
13
|
+
return { lo: shiftIndex(lo, splice), hi: shiftIndex(hi, splice) };
|
|
14
|
+
}
|
|
15
|
+
export function shiftRect(rect, splice) {
|
|
16
|
+
const rowAxis = splice.axis === 'row';
|
|
17
|
+
const moved = shiftSpan(rowAxis ? rect.top : rect.left, rowAxis ? rect.bottom : rect.right, splice);
|
|
18
|
+
if (moved === undefined)
|
|
19
|
+
return undefined;
|
|
20
|
+
return rowAxis
|
|
21
|
+
? { ...rect, top: moved.lo, bottom: moved.hi }
|
|
22
|
+
: { ...rect, left: moved.lo, right: moved.hi };
|
|
23
|
+
}
|
|
24
|
+
export function shiftPoint(point, splice) {
|
|
25
|
+
const line = splice.axis === 'row' ? point.row : point.col;
|
|
26
|
+
if (isDeletedSpan(line, line, splice))
|
|
27
|
+
return undefined;
|
|
28
|
+
const moved = shiftIndex(line, splice);
|
|
29
|
+
return splice.axis === 'row' ? { col: point.col, row: moved } : { col: moved, row: point.row };
|
|
8
30
|
}
|
package/dist/core/image.d.ts
CHANGED
|
@@ -99,15 +99,19 @@ export interface WorksheetImages {
|
|
|
99
99
|
readonly background: WorkbookImage | undefined;
|
|
100
100
|
}
|
|
101
101
|
/**
|
|
102
|
-
*
|
|
102
|
+
* A picture's content identity as a map key: its kind, its length, and a digest of its bytes.
|
|
103
103
|
*
|
|
104
|
-
* Content-addressed
|
|
105
|
-
* however they reached the registry.
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
* workbook would never satisfy.
|
|
104
|
+
* **Content-addressed, not reference-addressed.** Two byte-identical pictures are one picture,
|
|
105
|
+
* however they reached the registry. That is what keeps repeated imports from growing the media list
|
|
106
|
+
* without bound, so the same logo carried onto twenty sheets registers once, and it is why identity
|
|
107
|
+
* cannot be object identity: a picture arriving from another workbook would never satisfy that.
|
|
109
108
|
*
|
|
110
|
-
*
|
|
111
|
-
* byte
|
|
109
|
+
* **A key rather than a scan**, which is the part that changed. The comparison used to walk the whole
|
|
110
|
+
* media list byte by byte per candidate, and importing a sheet's pictures asks it once per anchored
|
|
111
|
+
* image, so merging a workbook of fifty distinct megabyte images compared bytes fifty times over
|
|
112
|
+
* fifty candidates. The workbook indexes each picture as it is registered instead.
|
|
113
|
+
*
|
|
114
|
+
* SHA-512 rather than a cheap checksum because a collision here silently substitutes one picture for
|
|
115
|
+
* another; the length is in the key as well, so a collision would have to match that too.
|
|
112
116
|
*/
|
|
113
|
-
export declare function
|
|
117
|
+
export declare function imageContentKey(image: WorkbookImage): string;
|
package/dist/core/image.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { hex } from '../hex.js';
|
|
2
|
+
import { sha512 } from '../sha512.js';
|
|
1
3
|
import { tokenSet } from '../token-set.js';
|
|
2
4
|
export const PX_TO_EMU = 9525;
|
|
3
5
|
export const isImageEditAs = tokenSet({ oneCell: true, twoCell: true, absolute: true });
|
|
@@ -40,9 +42,9 @@ export function normalizeImageExtension(extension, data) {
|
|
|
40
42
|
}
|
|
41
43
|
return sniffImageExtension(data);
|
|
42
44
|
}
|
|
43
|
-
export function
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return
|
|
45
|
+
export function imageContentKey(image) {
|
|
46
|
+
let digest = '';
|
|
47
|
+
for (const byte of sha512(image.data))
|
|
48
|
+
digest += hex(byte, 2);
|
|
49
|
+
return `${image.extension}:${image.data.length}:${digest}`;
|
|
48
50
|
}
|
package/dist/core/internal.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Keys the
|
|
3
|
-
* `WorksheetInternals`, declared beside their classes).
|
|
2
|
+
* Keys the operations the library's own machinery may perform on a published class and a caller may
|
|
3
|
+
* not (see `WorkbookInternals` / `WorksheetInternals`, declared beside their classes).
|
|
4
|
+
*
|
|
5
|
+
* Not only the model: `WorksheetStreamWriter` hangs its construction and its row-flush plumbing off
|
|
6
|
+
* the same key, on the static side and the instance side respectively, because that class has the
|
|
7
|
+
* identical problem one layer up. Its constructor took the writer's style registry and its
|
|
8
|
+
* `flushedSheet()` returned the writer's flushed-row record, so seven internal types were named by a
|
|
9
|
+
* published signature and none of them was a type a consumer could write down.
|
|
4
10
|
*/
|
|
5
11
|
export declare const INTERNAL: unique symbol;
|
|
6
12
|
/**
|
package/dist/core/limits.d.ts
CHANGED
|
@@ -28,3 +28,26 @@ export declare const MAX_ROW_HEIGHT = 409.5;
|
|
|
28
28
|
* value for it is a bug rather than a shortcut.
|
|
29
29
|
*/
|
|
30
30
|
export declare const MAX_COLUMN_WIDTH = 255;
|
|
31
|
+
/**
|
|
32
|
+
* The longest sheet name Excel accepts, in UTF-16 code units. A longer one is refused outright rather
|
|
33
|
+
* than truncated: a truncated name silently collides with its neighbours.
|
|
34
|
+
*/
|
|
35
|
+
export declare const MAX_SHEET_NAME_LENGTH = 31;
|
|
36
|
+
/**
|
|
37
|
+
* The characters Excel forbids anywhere in a sheet name. A name may also not begin or end with an
|
|
38
|
+
* apostrophe, which this pattern does not express because the position is what makes it illegal: a
|
|
39
|
+
* sheet-qualified reference quotes the name with apostrophes, so one at either edge cannot be told
|
|
40
|
+
* from the quoting.
|
|
41
|
+
*/
|
|
42
|
+
export declare const INVALID_SHEET_NAME_CHARS: RegExp;
|
|
43
|
+
/** The longest table name Excel accepts, in UTF-16 code units. */
|
|
44
|
+
export declare const MAX_TABLE_NAME_LENGTH = 255;
|
|
45
|
+
/**
|
|
46
|
+
* Excel's table-name grammar: start with a letter, underscore, or backslash; every later character a
|
|
47
|
+
* letter, digit, period, or underscore. Unicode letters and digits are allowed.
|
|
48
|
+
*
|
|
49
|
+
* Excel additionally forbids a name that *is* a cell reference (`A1`, `R1C1`), which this pattern
|
|
50
|
+
* deliberately does not: the regression corpus treats cell-reference-shaped names like `T1` as valid
|
|
51
|
+
* table names, so enforcing that rule would reject a fixture the contract accepts.
|
|
52
|
+
*/
|
|
53
|
+
export declare const TABLE_NAME_PATTERN: RegExp;
|
package/dist/core/limits.js
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
export const MAX_ROW_HEIGHT = 409.5;
|
|
2
2
|
export const MAX_COLUMN_WIDTH = 255;
|
|
3
|
+
export const MAX_SHEET_NAME_LENGTH = 31;
|
|
4
|
+
export const INVALID_SHEET_NAME_CHARS = /[*?:\\/[\]]/;
|
|
5
|
+
export const MAX_TABLE_NAME_LENGTH = 255;
|
|
6
|
+
export const TABLE_NAME_PATTERN = /^[\p{L}\\_][\p{L}\p{N}._]*$/u;
|
package/dist/core/merge.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type GridRect } from './address.ts';
|
|
2
2
|
import type { Cell } from './cell.ts';
|
|
3
|
+
import { type AxisSplice } from './grid-shift.ts';
|
|
3
4
|
/** A merged region, as the {@link GridRect} every range-shaped thing in the library is. */
|
|
4
5
|
export type MergeRect = GridRect;
|
|
5
6
|
/**
|
|
@@ -15,12 +16,12 @@ export declare function clearCoveredValues(rows: Map<number, Map<number, Cell>>,
|
|
|
15
16
|
* that can exist contributes no rectangle, the reader's rule for every other foreign attribute. */
|
|
16
17
|
export declare function decodeSqrefRects(sqref: string): MergeRect[];
|
|
17
18
|
/**
|
|
18
|
-
* Re-anchor an OOXML `sqref` through a splice
|
|
19
|
-
*
|
|
20
|
-
*
|
|
19
|
+
* Re-anchor an OOXML `sqref` through a splice, the inverse of {@link decodeSqrefRects}. Returns
|
|
20
|
+
* `undefined` when the splice deleted every area the `sqref` named: an empty `sqref` is not
|
|
21
|
+
* writable, so the entry holding it goes too.
|
|
21
22
|
*
|
|
22
23
|
* Each space-separated area shifts on its own, and one the splice does not move is returned as the
|
|
23
24
|
* *original text*. That matters for a file the library did not author: `B:B` and `B1:B1048576` decode
|
|
24
25
|
* identically, so a re-encode would rewrite a foreign spelling and cost the byte-clean round trip.
|
|
25
26
|
*/
|
|
26
|
-
export declare function shiftSqref(sqref: string,
|
|
27
|
+
export declare function shiftSqref(sqref: string, splice: AxisSplice): string | undefined;
|
package/dist/core/merge.js
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import { encodeCornerRef, tryDecodeRange } from './address.js';
|
|
2
|
-
import {
|
|
2
|
+
import { shiftSpan } from './grid-shift.js';
|
|
3
3
|
export function clearCoveredValues(rows, rect) {
|
|
4
|
-
for (
|
|
5
|
-
|
|
6
|
-
if (cols === undefined)
|
|
4
|
+
for (const [row, cols] of rows) {
|
|
5
|
+
if (row < rect.top || row > rect.bottom)
|
|
7
6
|
continue;
|
|
8
|
-
for (
|
|
7
|
+
for (const [col, covered] of cols) {
|
|
8
|
+
if (col < rect.left || col > rect.right)
|
|
9
|
+
continue;
|
|
9
10
|
if (row === rect.top && col === rect.left)
|
|
10
11
|
continue;
|
|
11
|
-
|
|
12
|
-
if (covered !== undefined)
|
|
13
|
-
covered.value = null;
|
|
12
|
+
covered.value = null;
|
|
14
13
|
}
|
|
15
14
|
}
|
|
16
15
|
}
|
|
@@ -32,33 +31,33 @@ export function decodeSqrefRects(sqref) {
|
|
|
32
31
|
}
|
|
33
32
|
return rects;
|
|
34
33
|
}
|
|
35
|
-
export function shiftSqref(sqref,
|
|
34
|
+
export function shiftSqref(sqref, splice) {
|
|
36
35
|
const areas = [];
|
|
37
36
|
for (const area of sqref.split(/\s+/)) {
|
|
38
37
|
if (area === '')
|
|
39
38
|
continue;
|
|
40
|
-
const shifted = shiftSqrefArea(area,
|
|
39
|
+
const shifted = shiftSqrefArea(area, splice);
|
|
41
40
|
if (shifted !== undefined)
|
|
42
41
|
areas.push(shifted);
|
|
43
42
|
}
|
|
44
43
|
return areas.length > 0 ? areas.join(' ') : undefined;
|
|
45
44
|
}
|
|
46
|
-
function shiftSqrefArea(area,
|
|
45
|
+
function shiftSqrefArea(area, splice) {
|
|
47
46
|
const decoded = tryDecodeRange(area);
|
|
48
47
|
if (decoded === undefined)
|
|
49
48
|
return area;
|
|
50
49
|
const { top, left, bottom, right } = decoded;
|
|
51
|
-
const
|
|
50
|
+
const rowAxis = splice.axis === 'row';
|
|
51
|
+
const [lo, hi] = rowAxis ? [top, bottom] : [left, right];
|
|
52
52
|
if (lo === undefined || hi === undefined)
|
|
53
53
|
return area;
|
|
54
|
-
|
|
54
|
+
const moved = shiftSpan(lo, hi, splice);
|
|
55
|
+
if (moved === undefined)
|
|
55
56
|
return undefined;
|
|
56
|
-
|
|
57
|
-
const movedHi = shiftIndex(hi, start, count, delta, axis);
|
|
58
|
-
if (movedLo === lo && movedHi === hi)
|
|
57
|
+
if (moved.lo === lo && moved.hi === hi)
|
|
59
58
|
return area;
|
|
60
|
-
const [tl, br] =
|
|
61
|
-
? [encodeCornerRef(left,
|
|
62
|
-
: [encodeCornerRef(
|
|
59
|
+
const [tl, br] = rowAxis
|
|
60
|
+
? [encodeCornerRef(left, moved.lo), encodeCornerRef(right, moved.hi)]
|
|
61
|
+
: [encodeCornerRef(moved.lo, top), encodeCornerRef(moved.hi, bottom)];
|
|
63
62
|
return area.includes(':') ? `${tl}:${br}` : tl;
|
|
64
63
|
}
|