@xlsxflow/core 1.1.2 → 1.1.3
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/CHANGELOG.md +122 -108
- package/README.md +2 -0
- package/dist/index.cjs +74 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.mjs +74 -54
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,108 +1,122 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## 1.1.
|
|
4
|
-
|
|
5
|
-
### Fixed
|
|
6
|
-
|
|
7
|
-
- `
|
|
8
|
-
- `
|
|
9
|
-
- `
|
|
10
|
-
-
|
|
11
|
-
- `
|
|
12
|
-
|
|
13
|
-
### Added
|
|
14
|
-
|
|
15
|
-
- `
|
|
16
|
-
|
|
17
|
-
## 1.1.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
- `
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
- `
|
|
55
|
-
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
-
|
|
62
|
-
- The
|
|
63
|
-
-
|
|
64
|
-
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
-
|
|
84
|
-
-
|
|
85
|
-
- `
|
|
86
|
-
-
|
|
87
|
-
-
|
|
88
|
-
-
|
|
89
|
-
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
- `
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
-
|
|
99
|
-
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
-
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.1.3
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- `SheetEditor.setCells` refuses addresses past column XFD or row 1,048,576, instead of writing a file Excel reports as damaged.
|
|
8
|
+
- `SheetWriter` and `SheetEditor` refuse text longer than Excel's 32,767 characters per cell.
|
|
9
|
+
- Colours are checked: ARGB (`FFFF0000`) and RGB (`FF0000`, `#FF0000`) hex are accepted, anything else (`'red'`) throws instead of making Excel repair the file.
|
|
10
|
+
- Picture anchors past the last column or row throw; a range given the wrong way round (`F20:A2`) is turned round.
|
|
11
|
+
- `SheetReader`: a shared string index past the end of the table reads as an empty cell, not as the index.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- `checkCellText` is exported, for `@xlsxflow/pro`.
|
|
16
|
+
|
|
17
|
+
## 1.1.2
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- `SheetWriter` throws an error for a row longer than 16,384 cells or past row 1,048,576, instead of writing a file Excel reports as damaged.
|
|
22
|
+
- `SheetEditor.setCells`: a date in a cell without a date format gets `yyyy-mm-dd` (or `yyyy-mm-dd hh:mm:ss` with a time), as in `SheetWriter`, instead of showing as a serial number.
|
|
23
|
+
- `OdsWriter` stores formula results, so readers that don't recalculate show them.
|
|
24
|
+
- `sheetToJson` keeps every column when headers repeat: the second `Name` becomes `Name_2`.
|
|
25
|
+
- `parseCsv` returns `null` for empty unquoted fields with `{ convert: false }` too.
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- `StylePatcher` is exported, for `@xlsxflow/pro`.
|
|
30
|
+
|
|
31
|
+
## 1.1.1
|
|
32
|
+
|
|
33
|
+
- README: the XlsxFlow website address, and Pro at a flat $5.
|
|
34
|
+
- More npm keywords: `xls` and `ods`.
|
|
35
|
+
|
|
36
|
+
## 1.1.0
|
|
37
|
+
|
|
38
|
+
### Added
|
|
39
|
+
|
|
40
|
+
- `SheetWriter`: workbook properties (title, author, company and so on), defined names, hidden and very hidden sheets, and sheet views (zoom, gridlines, headings, right-to-left). `SheetReader.readWorkbook` reads them back.
|
|
41
|
+
- `{ formatted: true }` reports each cell's text as Excel shows it, from its number format.
|
|
42
|
+
- `SheetEditor.insertRows`, `deleteRows`, `insertColumns` and `deleteColumns`. Everything that points at the moved cells moves with them: formulas on all sheets, defined names, merges, conditional formats, validations, hyperlinks, filters, page breaks, column widths, tables, pictures, notes, sparklines, data tables, chart series and pivot sources.
|
|
43
|
+
- `SheetEditor.setCells` edits and restyles cells of existing files; `addSheet` and `deleteSheet` add and remove sheets.
|
|
44
|
+
- `parseCsv` streams CSV rows, which `SheetWriter.addSheet` turns into a sheet.
|
|
45
|
+
- `SheetReader` reads Excel 97-2003 `.xls` files and OpenDocument `.ods` files: values, dates, formula results, merges, hidden rows, columns and sheets, frozen panes, defined names and document properties. `.ods` also gives formulas, hyperlinks and notes.
|
|
46
|
+
- `OdsWriter` writes `.ods` files: values, dates, formulas, merges, column widths, frozen panes and hidden sheets.
|
|
47
|
+
- Password-protected `.xlsx` files are rejected with an error that points to `decryptWorkbook` in `@xlsxflow/pro`, instead of a ZIP error.
|
|
48
|
+
- Cell notes on write (plain or formatted text) and `getComments()` on read.
|
|
49
|
+
- Conditional formats `cellIs`, `expression`, `top10`, `aboveAverage`, text rules, `duplicateValues`/`uniqueValues` and `iconSet`.
|
|
50
|
+
- Excel tables, sheet protection, page setup (margins, header and footer, print area and titles), row heights, hidden rows and columns, outline grouping, tab colour, and validation operators and messages.
|
|
51
|
+
- `Date` cell values, hyperlinks (URLs and locations in the workbook), `autoFilter`, and an optional shared string table on write.
|
|
52
|
+
- Reading returns formulas (`formulas: true`, shared formulas expanded), styles (`styles: true`) and rich text (`richText: true`). Theme and indexed colours are resolved to ARGB.
|
|
53
|
+
- PNG, JPEG and GIF images (`images` sheet option), anchored to a cell or stretched over a range. `getImages()` reads them back.
|
|
54
|
+
- Low-level parts are exported for add-ons: the ZIP reader and writer, `resolveWorkbookParts`, `readSharedStrings`, and `mapFormulaRefs`/`shiftFormula`.
|
|
55
|
+
- Tested: `.xlsm` files keep their macros through `SheetEditor`; the library runs in Cloudflare Workers without `nodejs_compat` (`scripts/workers` checks it).
|
|
56
|
+
|
|
57
|
+
### Security
|
|
58
|
+
|
|
59
|
+
Fixed crashes, hangs and memory blowups caused by crafted files:
|
|
60
|
+
|
|
61
|
+
- A ZIP directory pointing outside the file crashed Node.
|
|
62
|
+
- Unclosed tags and elements made the parser quadratic. The XML tokenizer is now linear on giant tags, text nodes and CDATA.
|
|
63
|
+
- Repeated hidden columns, far-right cells, fraction formats with long denominators and large style tables were slow or used unbounded memory.
|
|
64
|
+
- One deflated entry could be read many times under different names.
|
|
65
|
+
|
|
66
|
+
Also:
|
|
67
|
+
|
|
68
|
+
- `SheetEditor.edit` takes `maxUncompressedBytes`.
|
|
69
|
+
- Option values typed as enums are escaped, and appended rows encode control characters.
|
|
70
|
+
- `sheetToJson` keeps a `__proto__` header as an ordinary key.
|
|
71
|
+
- In-memory parts are capped at 1 GiB uncompressed by default.
|
|
72
|
+
|
|
73
|
+
### Fixed
|
|
74
|
+
|
|
75
|
+
- Sheets with frozen panes were all marked as selected, so Excel opened them grouped.
|
|
76
|
+
- The reader dropped or corrupted cells at stream chunk boundaries.
|
|
77
|
+
- Styles pointed at the wrong font, fill or border, and styles used only by `AsyncIterable` rows were missing from `styles.xml`.
|
|
78
|
+
- Number formats with quoted text, escapes or colours (`#,##0.00 "USD"`, `[Red]0.0`) were taken for date formats.
|
|
79
|
+
- Reading a corrupt file without calling `getMetadata()` caused an unhandled promise rejection, which is fatal in Node.
|
|
80
|
+
- `addSheet` accepted sheet names Excel refuses to open. It now throws.
|
|
81
|
+
- Files Excel would repair:
|
|
82
|
+
- gradient stops were written as `<gradientStop>`;
|
|
83
|
+
- `vertical: 'middle'` was written as is instead of `center`;
|
|
84
|
+
- conditional formats came after validations.
|
|
85
|
+
- Data bar `minValue`/`maxValue` were ignored, every conditional format had priority 1, and colours and ranges were not XML-escaped.
|
|
86
|
+
- Strings with `_xHHHH_`, control characters, CR, or leading and trailing spaces now survive a round trip. `NaN` and `Infinity` are written as `#NUM!`.
|
|
87
|
+
- Reader:
|
|
88
|
+
- Strict OOXML and non-standard part names work;
|
|
89
|
+
- empty `<v/>` reads as empty and `-0` as `0`;
|
|
90
|
+
- out-of-order cells land in the right column;
|
|
91
|
+
- time-only values are no longer a day off, and datetimes keep milliseconds.
|
|
92
|
+
- frozen panes saved by Excel as `frozenSplit` (frozen after a split) are reported.
|
|
93
|
+
- `sheetToJson` ignored its `headerRowIndex` argument.
|
|
94
|
+
|
|
95
|
+
### Changed
|
|
96
|
+
|
|
97
|
+
- The package ships compiled ESM and CommonJS builds with type declarations.
|
|
98
|
+
- Browser bundles no longer try to resolve Node's `fs`.
|
|
99
|
+
- `[Content_Types].xml` is written last in the ZIP.
|
|
100
|
+
- Real backpressure in the ZIP writer and worksheet stream. Producer errors now error the output stream instead of hanging it.
|
|
101
|
+
- About 2.5× faster reads, and a table-driven CRC-32 for faster writes.
|
|
102
|
+
- Styles, formulas and conditional formats are part of the MIT core. Paid add-ons are in the separate `@xlsxflow/pro` package.
|
|
103
|
+
- Benchmarks corrected: the earlier ExcelJS write time came from a cold first run, and write-excel-file was run with an old API.
|
|
104
|
+
|
|
105
|
+
## 1.0.0
|
|
106
|
+
|
|
107
|
+
- `SheetWriter` and `SheetEditor` stream the ZIP with data descriptors instead of buffering the file in memory.
|
|
108
|
+
- Dates are returned as ISO-8601 strings, detected from each cell's number format.
|
|
109
|
+
|
|
110
|
+
## 0.3.0-beta
|
|
111
|
+
|
|
112
|
+
- Workbooks with several sheets.
|
|
113
|
+
- Fixed a buffer bug that made parsing quadratic.
|
|
114
|
+
|
|
115
|
+
## 0.2.0-beta
|
|
116
|
+
|
|
117
|
+
- Deflate compression with `CompressionStream`: a 1M-cell file went from 30 MB to 2.9 MB.
|
|
118
|
+
- Inline and rich text strings are read.
|
|
119
|
+
|
|
120
|
+
## 0.1.0-beta
|
|
121
|
+
|
|
122
|
+
- First release: streaming reader, writer, and shared strings.
|
package/README.md
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -464,6 +464,21 @@ function decodeXString(s) {
|
|
|
464
464
|
// Excel's sheet size (A..XFD, 1..1048576)
|
|
465
465
|
const MAX_COLUMNS$1 = 16384;
|
|
466
466
|
const MAX_ROWS$1 = 1048576;
|
|
467
|
+
// Excel's limit on the characters in one cell
|
|
468
|
+
const MAX_CELL_TEXT = 32767;
|
|
469
|
+
function checkCellText(s, ref) {
|
|
470
|
+
if (s.length > MAX_CELL_TEXT)
|
|
471
|
+
throw new Error(`Cell ${ref} has ${s.length} characters, more than Excel's ${MAX_CELL_TEXT}.`);
|
|
472
|
+
return s;
|
|
473
|
+
}
|
|
474
|
+
// Colours are ARGB hex ("FFFF0000"); "FF0000" and "#FF0000" get an opaque alpha. Anything else
|
|
475
|
+
// makes Excel repair the file.
|
|
476
|
+
function argb(c) {
|
|
477
|
+
const m = /^#?([0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/.exec(String(c));
|
|
478
|
+
if (!m)
|
|
479
|
+
throw new Error(`Invalid colour "${c}": use ARGB hex such as "FFFF0000".`);
|
|
480
|
+
return (m[1].length === 6 ? 'FF' + m[1] : m[1]).toUpperCase();
|
|
481
|
+
}
|
|
467
482
|
// 0-based column of letters like "BC" (or a reference like "BC12"); -1 when there are none
|
|
468
483
|
function colIndex(ref) {
|
|
469
484
|
const match = ref.match(/[A-Za-z]+/);
|
|
@@ -1461,7 +1476,8 @@ function parseWorksheet(xmlTokenStream, sharedStrings, styles, is1904 = false, o
|
|
|
1461
1476
|
}
|
|
1462
1477
|
else if (currentCellType === 's') {
|
|
1463
1478
|
const index = parseInt(currentCellValue, 10);
|
|
1464
|
-
|
|
1479
|
+
// An index past the table points nowhere: the cell is empty
|
|
1480
|
+
resolvedValue = sharedStrings.get(index) ?? null;
|
|
1465
1481
|
}
|
|
1466
1482
|
else if (currentCellType === 'b') {
|
|
1467
1483
|
resolvedValue = currentCellValue === '1' || currentCellValue === 'true';
|
|
@@ -1635,9 +1651,10 @@ async function readSheetImages(readText, readBytes, sheetPath) {
|
|
|
1635
1651
|
const escapeXml$4 = (s) => s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
1636
1652
|
const cellPos = (ref) => {
|
|
1637
1653
|
const m = /^\$?([A-Za-z]{1,3})\$?(\d+)$/.exec(ref.trim());
|
|
1638
|
-
|
|
1654
|
+
const col = m ? colIndex(m[1]) : -1, row = m ? parseInt(m[2], 10) - 1 : -1;
|
|
1655
|
+
if (col < 0 || col >= MAX_COLUMNS$1 || row < 0 || row >= MAX_ROWS$1)
|
|
1639
1656
|
throw new Error(`Invalid anchor cell "${ref}".`);
|
|
1640
|
-
return { col
|
|
1657
|
+
return { col, row };
|
|
1641
1658
|
};
|
|
1642
1659
|
const marker = (tag, { col, row }) => `<xdr:${tag}><xdr:col>${col}</xdr:col><xdr:colOff>0</xdr:colOff><xdr:row>${row}</xdr:row><xdr:rowOff>0</xdr:rowOff></xdr:${tag}>`;
|
|
1643
1660
|
const DRAWING_NS = 'xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing" ' +
|
|
@@ -1649,9 +1666,11 @@ const DRAWING_NS = 'xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/
|
|
|
1649
1666
|
function anchorXml(place, natural, body, attrs = '') {
|
|
1650
1667
|
if ('range' in place) {
|
|
1651
1668
|
const [from, to = from] = place.range.split(':');
|
|
1652
|
-
const
|
|
1653
|
-
// The object fills the range: it ends at the far edge of the last cell
|
|
1654
|
-
|
|
1669
|
+
const a = cellPos(from), b = cellPos(to);
|
|
1670
|
+
// The object fills the range, given either way round: it ends at the far edge of the last cell
|
|
1671
|
+
const start = { col: Math.min(a.col, b.col), row: Math.min(a.row, b.row) };
|
|
1672
|
+
const end = { col: Math.max(a.col, b.col) + 1, row: Math.max(a.row, b.row) + 1 };
|
|
1673
|
+
return `<xdr:twoCellAnchor editAs="oneCell"${attrs}>${marker('from', start)}${marker('to', end)}${body}<xdr:clientData/></xdr:twoCellAnchor>`;
|
|
1655
1674
|
}
|
|
1656
1675
|
const { width: w, height: h } = natural;
|
|
1657
1676
|
const width = place.width ?? (place.height ? w * place.height / h : w);
|
|
@@ -2636,22 +2655,22 @@ function fontXml(font, nameTag = 'name') {
|
|
|
2636
2655
|
if (font.size)
|
|
2637
2656
|
xml += `<sz val="${escapeXml$3(font.size)}"/>`;
|
|
2638
2657
|
if (font.color)
|
|
2639
|
-
xml += `<color rgb="${
|
|
2658
|
+
xml += `<color rgb="${argb(font.color)}"/>`;
|
|
2640
2659
|
if (font.name)
|
|
2641
2660
|
xml += `<${nameTag} val="${escapeXml$3(font.name)}"/>`;
|
|
2642
2661
|
return xml;
|
|
2643
2662
|
}
|
|
2644
|
-
const borderSideXml = (tag, s) => s ? `<${tag} style="${escapeXml$3(s.style)}">${s.color ? `<color rgb="${
|
|
2663
|
+
const borderSideXml = (tag, s) => s ? `<${tag} style="${escapeXml$3(s.style)}">${s.color ? `<color rgb="${argb(s.color)}"/>` : ''}</${tag}>` : `<${tag}/>`;
|
|
2645
2664
|
function borderXml(b) {
|
|
2646
2665
|
const side = borderSideXml;
|
|
2647
2666
|
return `<border>${side('left', b.left)}${side('right', b.right)}${side('top', b.top)}${side('bottom', b.bottom)}<diagonal/></border>`;
|
|
2648
2667
|
}
|
|
2649
2668
|
function fillXml(fill) {
|
|
2650
2669
|
if (fill.type === 'gradient') {
|
|
2651
|
-
const stops = fill.stops.map(s => `<stop position="${s.position}"><color rgb="${
|
|
2670
|
+
const stops = fill.stops.map(s => `<stop position="${s.position}"><color rgb="${argb(s.color)}"/></stop>`).join('');
|
|
2652
2671
|
return `<fill><gradientFill degree="${escapeXml$3(fill.degree ?? 0)}">${stops}</gradientFill></fill>`;
|
|
2653
2672
|
}
|
|
2654
|
-
return `<fill><patternFill patternType="solid"><fgColor rgb="${
|
|
2673
|
+
return `<fill><patternFill patternType="solid"><fgColor rgb="${argb(fill.fgColor)}"/></patternFill></fill>`;
|
|
2655
2674
|
}
|
|
2656
2675
|
// Attributes of an <alignment> element
|
|
2657
2676
|
function alignmentAttrs(alignment) {
|
|
@@ -2693,7 +2712,7 @@ class StyleEngine {
|
|
|
2693
2712
|
xml += `<numFmt numFmtId="${this.registerNumFmt(style.numFmt)}" formatCode="${escapeXml$3(style.numFmt)}"/>`;
|
|
2694
2713
|
if (style.fill?.type === 'solid') {
|
|
2695
2714
|
// A differential solid fill takes its colour from bgColor
|
|
2696
|
-
const c =
|
|
2715
|
+
const c = argb(style.fill.fgColor);
|
|
2697
2716
|
xml += `<fill><patternFill patternType="solid"><fgColor rgb="${c}"/><bgColor rgb="${c}"/></patternFill></fill>`;
|
|
2698
2717
|
}
|
|
2699
2718
|
if (style.border)
|
|
@@ -2821,7 +2840,7 @@ ${this.dxfs.size ? `<dxfs count="${this.dxfs.size}">${[...this.dxfs.keys()].map(
|
|
|
2821
2840
|
function escapeXml$2(val) {
|
|
2822
2841
|
return String(val).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
2823
2842
|
}
|
|
2824
|
-
const color = (rgb) => `<color rgb="${
|
|
2843
|
+
const color = (rgb) => `<color rgb="${argb(rgb)}"/>`;
|
|
2825
2844
|
const bound = (type, value) => value === undefined ? `<cfvo type="${type}"/>` : `<cfvo type="num" val="${escapeXml$2(value)}"/>`;
|
|
2826
2845
|
const formula = (f) => `<formula>${escapeXml$2(String(f).replace(/^=/, ''))}</formula>`;
|
|
2827
2846
|
// Excel string literal inside a formula
|
|
@@ -3399,13 +3418,13 @@ const W3CDTF = (d) => d.toISOString().replace(/\.\d{3}Z$/, 'Z');
|
|
|
3399
3418
|
const propXml = (tag, v) => v === undefined ? '' : `<${tag}>${escapeXml$1(v)}</${tag}>`;
|
|
3400
3419
|
function corePropsXml(p) {
|
|
3401
3420
|
const created = p.created ? `<dcterms:created xsi:type="dcterms:W3CDTF">${W3CDTF(p.created)}</dcterms:created>` : '';
|
|
3402
|
-
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
3421
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
3403
3422
|
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">` +
|
|
3404
3423
|
propXml('dc:title', p.title) + propXml('dc:subject', p.subject) + propXml('dc:creator', p.creator) + propXml('cp:keywords', p.keywords) +
|
|
3405
3424
|
propXml('dc:description', p.description) + propXml('cp:category', p.category) + created + '</cp:coreProperties>';
|
|
3406
3425
|
}
|
|
3407
3426
|
function appPropsXml(p) {
|
|
3408
|
-
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
3427
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
3409
3428
|
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"><Application>XlsxFlow</Application>${propXml('Manager', p.manager)}${propXml('Company', p.company)}</Properties>`;
|
|
3410
3429
|
}
|
|
3411
3430
|
// A name Excel accepts: letters, digits, _ . and \, not a cell reference (A1, R1C1) and not reserved
|
|
@@ -3573,7 +3592,7 @@ class SheetWriter {
|
|
|
3573
3592
|
});
|
|
3574
3593
|
await this.streamString(zip, `xl/drawings/drawing${n}.xml`, drawingXml(images, images.map(img => this.media.get(img.data).info), rIds));
|
|
3575
3594
|
const rels = targets.map((t, k) => `<Relationship Id="rId${k + 1}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="${t}"/>`).join('');
|
|
3576
|
-
await this.streamString(zip, `xl/drawings/_rels/drawing${n}.xml.rels`, `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
3595
|
+
await this.streamString(zip, `xl/drawings/_rels/drawing${n}.xml.rels`, `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
3577
3596
|
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">${rels}</Relationships>`);
|
|
3578
3597
|
}
|
|
3579
3598
|
applyAutoFit(rows, options) {
|
|
@@ -3650,7 +3669,7 @@ class SheetWriter {
|
|
|
3650
3669
|
const page = options.pageSetup;
|
|
3651
3670
|
const fitToPage = page && (page.fitToWidth !== undefined || page.fitToHeight !== undefined);
|
|
3652
3671
|
const sheetPr = options.tabColor || fitToPage
|
|
3653
|
-
? `<sheetPr>${options.tabColor ? `<tabColor rgb="${
|
|
3672
|
+
? `<sheetPr>${options.tabColor ? `<tabColor rgb="${argb(options.tabColor)}"/>` : ''}${fitToPage ? '<pageSetUpPr fitToPage="1"/>' : ''}</sheetPr>`
|
|
3654
3673
|
: '';
|
|
3655
3674
|
const sheetFormatPr = outlineRow || outlineCol
|
|
3656
3675
|
? `<sheetFormatPr defaultRowHeight="15"${outlineRow ? ` outlineLevelRow="${outlineRow}"` : ''}${outlineCol ? ` outlineLevelCol="${outlineCol}"` : ''}/>`
|
|
@@ -3754,6 +3773,7 @@ class SheetWriter {
|
|
|
3754
3773
|
return `<c r="${colRef}"${sAttr}><v>${dateToSerial(val)}</v></c>`;
|
|
3755
3774
|
}
|
|
3756
3775
|
if (typeof val === 'string') {
|
|
3776
|
+
checkCellText(val, colRef);
|
|
3757
3777
|
if (this.writerOptions.sharedStrings) {
|
|
3758
3778
|
let index = this.sharedStrings.get(val);
|
|
3759
3779
|
if (index === undefined)
|
|
@@ -3841,7 +3861,7 @@ class SheetWriter {
|
|
|
3841
3861
|
let items = '';
|
|
3842
3862
|
for (const s of this.sharedStrings.keys())
|
|
3843
3863
|
items += `<si>${stringXml(s)}</si>`;
|
|
3844
|
-
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
3864
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
3845
3865
|
<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="${this.sharedStringRefs}" uniqueCount="${this.sharedStrings.size}">${items}</sst>`;
|
|
3846
3866
|
}
|
|
3847
3867
|
// '' when the sheet has no relationships
|
|
@@ -3856,7 +3876,7 @@ class SheetWriter {
|
|
|
3856
3876
|
tableIds.forEach((id, k) => { rels += `<Relationship Id="rIdTable${k + 1}" Type="${REL}/table" Target="../tables/table${id}.xml"/>`; });
|
|
3857
3877
|
if (!rels)
|
|
3858
3878
|
return '';
|
|
3859
|
-
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
3879
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
3860
3880
|
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">${rels}</Relationships>`;
|
|
3861
3881
|
}
|
|
3862
3882
|
buildWorkbookXml() {
|
|
@@ -3881,14 +3901,14 @@ class SheetWriter {
|
|
|
3881
3901
|
}
|
|
3882
3902
|
return names;
|
|
3883
3903
|
}).join('');
|
|
3884
|
-
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
3885
|
-
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
|
|
3886
|
-
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
|
|
3887
|
-
${active > 0 ? `<bookViews><workbookView firstSheet="${active}" activeTab="${active}"/></bookViews>` : ''}
|
|
3888
|
-
<sheets>
|
|
3889
|
-
${sheetsXml}
|
|
3890
|
-
</sheets>
|
|
3891
|
-
${filterNames || userNames ? `<definedNames>${filterNames}${userNames}</definedNames>` : ''}
|
|
3904
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
3905
|
+
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
|
|
3906
|
+
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
|
|
3907
|
+
${active > 0 ? `<bookViews><workbookView firstSheet="${active}" activeTab="${active}"/></bookViews>` : ''}
|
|
3908
|
+
<sheets>
|
|
3909
|
+
${sheetsXml}
|
|
3910
|
+
</sheets>
|
|
3911
|
+
${filterNames || userNames ? `<definedNames>${filterNames}${userNames}</definedNames>` : ''}
|
|
3892
3912
|
</workbook>`;
|
|
3893
3913
|
}
|
|
3894
3914
|
// The first visible sheet, which Excel opens on
|
|
@@ -3899,19 +3919,19 @@ class SheetWriter {
|
|
|
3899
3919
|
const sheetRels = this.sheets.map((_, i) => `<Relationship Id="rId${i + 1}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet${i + 1}.xml"/>`).join('\n ');
|
|
3900
3920
|
const ssId = this.sheets.length + 1;
|
|
3901
3921
|
const stylesId = this.sheets.length + 2;
|
|
3902
|
-
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
3903
|
-
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
3904
|
-
${sheetRels}
|
|
3905
|
-
<Relationship Id="rId${ssId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/>
|
|
3906
|
-
<Relationship Id="rId${stylesId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>
|
|
3922
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
3923
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
3924
|
+
${sheetRels}
|
|
3925
|
+
<Relationship Id="rId${ssId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/>
|
|
3926
|
+
<Relationship Id="rId${stylesId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>
|
|
3907
3927
|
</Relationships>`;
|
|
3908
3928
|
}
|
|
3909
3929
|
buildRootRels() {
|
|
3910
|
-
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
3911
|
-
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
3912
|
-
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>${this.writerOptions.properties ? `
|
|
3913
|
-
<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
|
|
3914
|
-
<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>` : ''}
|
|
3930
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
3931
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
3932
|
+
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>${this.writerOptions.properties ? `
|
|
3933
|
+
<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
|
|
3934
|
+
<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>` : ''}
|
|
3915
3935
|
</Relationships>`;
|
|
3916
3936
|
}
|
|
3917
3937
|
buildContentTypes(drawings, commentSheets, tables) {
|
|
@@ -3922,17 +3942,17 @@ class SheetWriter {
|
|
|
3922
3942
|
commentSheets.map(n => `<Override PartName="/xl/comments${n}.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml"/>`).join('') +
|
|
3923
3943
|
Array.from({ length: tables }, (_, i) => `<Override PartName="/xl/tables/table${i + 1}.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml"/>`).join('') +
|
|
3924
3944
|
(commentSheets.length ? '<Default Extension="vml" ContentType="application/vnd.openxmlformats-officedocument.vmlDrawing"/>' : '');
|
|
3925
|
-
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
3926
|
-
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
|
3927
|
-
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
|
|
3928
|
-
<Default Extension="xml" ContentType="application/xml"/>
|
|
3929
|
-
${imageTypes}${drawingOverrides}
|
|
3930
|
-
<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
|
|
3931
|
-
${sheetOverrides}
|
|
3932
|
-
<Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>
|
|
3933
|
-
<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>${this.writerOptions.properties ? `
|
|
3934
|
-
<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
|
|
3935
|
-
<Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>` : ''}
|
|
3945
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
3946
|
+
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
|
3947
|
+
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
|
|
3948
|
+
<Default Extension="xml" ContentType="application/xml"/>
|
|
3949
|
+
${imageTypes}${drawingOverrides}
|
|
3950
|
+
<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
|
|
3951
|
+
${sheetOverrides}
|
|
3952
|
+
<Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>
|
|
3953
|
+
<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>${this.writerOptions.properties ? `
|
|
3954
|
+
<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
|
|
3955
|
+
<Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>` : ''}
|
|
3936
3956
|
</Types>`;
|
|
3937
3957
|
}
|
|
3938
3958
|
}
|
|
@@ -3964,7 +3984,7 @@ function pageSetupXml(page) {
|
|
|
3964
3984
|
function commentsXml(comments) {
|
|
3965
3985
|
const authors = [...new Set(comments.map(c => c.comment.author ?? ''))];
|
|
3966
3986
|
const list = comments.map(c => `<comment ref="${c.ref}" authorId="${authors.indexOf(c.comment.author ?? '')}"><text>${runsXml(c.comment.text)}</text></comment>`).join('');
|
|
3967
|
-
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
3987
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
3968
3988
|
<comments xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><authors>${authors.map(a => `<author>${escapeXml$1(a)}</author>`).join('')}</authors><commentList>${list}</commentList></comments>`;
|
|
3969
3989
|
}
|
|
3970
3990
|
// The legacy VML shapes Excel needs to show the note boxes (hidden until the cell is hovered)
|
|
@@ -3984,7 +4004,7 @@ function tableXml(table, id, columns) {
|
|
|
3984
4004
|
const ref = table.ref.replace(/\$/g, '');
|
|
3985
4005
|
const filter = table.autoFilter === false ? '' : `<autoFilter ref="${ref}"/>`;
|
|
3986
4006
|
const cols = columns.map((c, k) => `<tableColumn id="${k + 1}" name="${escapeXml$1(c)}"/>`).join('');
|
|
3987
|
-
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
4007
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
3988
4008
|
<table xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" id="${id}" name="${escapeXml$1(table.name)}" displayName="${escapeXml$1(table.name)}" ref="${ref}" totalsRowShown="0">` +
|
|
3989
4009
|
`${filter}<tableColumns count="${columns.length}">${cols}</tableColumns>` +
|
|
3990
4010
|
`<tableStyleInfo name="${escapeXml$1(table.style ?? 'TableStyleMedium2')}" showFirstColumn="0" showLastColumn="0" showRowStripes="${table.showRowStripes === false ? 0 : 1}" showColumnStripes="0"/></table>`;
|
|
@@ -4724,7 +4744,7 @@ function editedCell(p, ref, attrs, edit) {
|
|
|
4724
4744
|
}
|
|
4725
4745
|
if (typeof edit === 'object')
|
|
4726
4746
|
return `<${p}c r="${ref}"${attrs}><${p}f>${escapeXml$5(edit.formula.replace(/^=/, ''))}</${p}f></${p}c>`;
|
|
4727
|
-
const text = escapeXml$5(encodeXString(edit));
|
|
4747
|
+
const text = escapeXml$5(encodeXString(checkCellText(edit, ref)));
|
|
4728
4748
|
const t = /^\s|\s$/.test(edit) ? `<${p}t xml:space="preserve">${text}</${p}t>` : `<${p}t>${text}</${p}t>`;
|
|
4729
4749
|
return `<${p}c r="${ref}"${attrs} t="inlineStr"><${p}is>${t}</${p}is></${p}c>`;
|
|
4730
4750
|
}
|
|
@@ -4775,9 +4795,9 @@ class SheetEditor {
|
|
|
4775
4795
|
this.cellEdits.set(sheetName, rows = new Map());
|
|
4776
4796
|
for (const [ref, edit] of Object.entries(cells)) {
|
|
4777
4797
|
const m = /^\$?([A-Za-z]{1,3})\$?(\d+)$/.exec(ref);
|
|
4778
|
-
|
|
4798
|
+
const r = m ? parseInt(m[2], 10) : 0;
|
|
4799
|
+
if (!m || r < 1 || r > MAX_ROWS$1 || colIndex(m[1]) >= MAX_COLUMNS$1)
|
|
4779
4800
|
throw new Error(`Invalid cell reference "${ref}".`);
|
|
4780
|
-
const r = parseInt(m[2], 10);
|
|
4781
4801
|
if (!rows.has(r))
|
|
4782
4802
|
rows.set(r, new Map());
|
|
4783
4803
|
rows.get(r).set(colIndex(m[1]), edit);
|
|
@@ -5196,7 +5216,7 @@ class SheetEditor {
|
|
|
5196
5216
|
if (typeof val === 'number')
|
|
5197
5217
|
return `<c r="${colRef}"><v>${val}</v></c>`;
|
|
5198
5218
|
if (typeof val === 'string') {
|
|
5199
|
-
return `<c r="${colRef}" t="inlineStr"><is><t xml:space="preserve">${escapeXml$5(encodeXString(val))}</t></is></c>`;
|
|
5219
|
+
return `<c r="${colRef}" t="inlineStr"><is><t xml:space="preserve">${escapeXml$5(encodeXString(checkCellText(val, colRef)))}</t></is></c>`;
|
|
5200
5220
|
}
|
|
5201
5221
|
return `<c r="${colRef}"/>`;
|
|
5202
5222
|
}).join('');
|
|
@@ -6156,6 +6176,7 @@ exports.XlsxFlow = XlsxFlow;
|
|
|
6156
6176
|
exports.ZipRandomAccessParser = ZipRandomAccessParser;
|
|
6157
6177
|
exports.ZipStreamWriter = ZipStreamWriter;
|
|
6158
6178
|
exports.anchorXml = anchorXml;
|
|
6179
|
+
exports.checkCellText = checkCellText;
|
|
6159
6180
|
exports.colIndex = colIndex;
|
|
6160
6181
|
exports.colLetter = colLetter;
|
|
6161
6182
|
exports.crc32 = crc32;
|