@yuanliwei/exceljs 4.4.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/LICENSE +22 -0
- package/README.md +3024 -0
- package/README_zh.md +2878 -0
- package/excel.js +13 -0
- package/index.d.ts +2040 -0
- package/index.ts +2 -0
- package/lib/csv/csv.js +191 -0
- package/lib/csv/line-buffer.js +74 -0
- package/lib/csv/stream-converter.js +135 -0
- package/lib/doc/anchor.js +91 -0
- package/lib/doc/cell.js +1124 -0
- package/lib/doc/column.js +320 -0
- package/lib/doc/data/theme1.json +234 -0
- package/lib/doc/data-validations.js +19 -0
- package/lib/doc/defined-names.js +196 -0
- package/lib/doc/enums.js +48 -0
- package/lib/doc/image.js +59 -0
- package/lib/doc/modelcontainer.js +18 -0
- package/lib/doc/note.js +65 -0
- package/lib/doc/pivot-table.js +132 -0
- package/lib/doc/range.js +257 -0
- package/lib/doc/row.js +415 -0
- package/lib/doc/table.js +465 -0
- package/lib/doc/workbook.js +224 -0
- package/lib/doc/worksheet.js +949 -0
- package/lib/exceljs.bare.js +13 -0
- package/lib/exceljs.browser.js +36 -0
- package/lib/exceljs.nodejs.js +14 -0
- package/lib/stream/xlsx/hyperlink-reader.js +83 -0
- package/lib/stream/xlsx/sheet-comments-writer.js +121 -0
- package/lib/stream/xlsx/sheet-rels-writer.js +119 -0
- package/lib/stream/xlsx/workbook-reader.js +337 -0
- package/lib/stream/xlsx/workbook-writer.js +347 -0
- package/lib/stream/xlsx/worksheet-reader.js +374 -0
- package/lib/stream/xlsx/worksheet-writer.js +717 -0
- package/lib/utils/auto-drain.js +15 -0
- package/lib/utils/browser-buffer-decode.js +14 -0
- package/lib/utils/browser-buffer-encode.js +15 -0
- package/lib/utils/cell-matrix.js +165 -0
- package/lib/utils/col-cache.js +287 -0
- package/lib/utils/copy-style.js +43 -0
- package/lib/utils/encryptor.js +55 -0
- package/lib/utils/iterate-stream.js +48 -0
- package/lib/utils/parse-sax.js +30 -0
- package/lib/utils/shared-formula.js +44 -0
- package/lib/utils/shared-strings.js +35 -0
- package/lib/utils/stream-base64.js +72 -0
- package/lib/utils/stream-buf.js +364 -0
- package/lib/utils/string-buf.js +82 -0
- package/lib/utils/string-builder.js +35 -0
- package/lib/utils/stuttered-pipe.js +67 -0
- package/lib/utils/typed-stack.js +24 -0
- package/lib/utils/under-dash.js +184 -0
- package/lib/utils/utils.js +205 -0
- package/lib/utils/xml-stream.js +169 -0
- package/lib/utils/zip-stream.js +87 -0
- package/lib/xlsx/.rels +11 -0
- package/lib/xlsx/calcChain.xml +6 -0
- package/lib/xlsx/core.xml +7 -0
- package/lib/xlsx/defaultnumformats.js +153 -0
- package/lib/xlsx/rel-type.js +20 -0
- package/lib/xlsx/styles.xml +41 -0
- package/lib/xlsx/workbook.xml +16 -0
- package/lib/xlsx/xform/base-xform.js +145 -0
- package/lib/xlsx/xform/book/defined-name-xform.js +91 -0
- package/lib/xlsx/xform/book/sheet-xform.js +34 -0
- package/lib/xlsx/xform/book/workbook-calc-properties-xform.js +26 -0
- package/lib/xlsx/xform/book/workbook-pivot-cache-xform.js +29 -0
- package/lib/xlsx/xform/book/workbook-properties-xform.js +29 -0
- package/lib/xlsx/xform/book/workbook-view-xform.js +53 -0
- package/lib/xlsx/xform/book/workbook-xform.js +259 -0
- package/lib/xlsx/xform/comment/comment-xform.js +105 -0
- package/lib/xlsx/xform/comment/comments-xform.js +82 -0
- package/lib/xlsx/xform/comment/style/vml-position-xform.js +39 -0
- package/lib/xlsx/xform/comment/style/vml-protection-xform.js +36 -0
- package/lib/xlsx/xform/comment/vml-anchor-xform.js +60 -0
- package/lib/xlsx/xform/comment/vml-client-data-xform.js +95 -0
- package/lib/xlsx/xform/comment/vml-notes-xform.js +107 -0
- package/lib/xlsx/xform/comment/vml-shape-xform.js +95 -0
- package/lib/xlsx/xform/comment/vml-textbox-xform.js +64 -0
- package/lib/xlsx/xform/composite-xform.js +56 -0
- package/lib/xlsx/xform/core/app-heading-pairs-xform.js +32 -0
- package/lib/xlsx/xform/core/app-titles-of-parts-xform.js +28 -0
- package/lib/xlsx/xform/core/app-xform.js +100 -0
- package/lib/xlsx/xform/core/content-types-xform.js +135 -0
- package/lib/xlsx/xform/core/core-xform.js +136 -0
- package/lib/xlsx/xform/core/relationship-xform.js +25 -0
- package/lib/xlsx/xform/core/relationships-xform.js +73 -0
- package/lib/xlsx/xform/drawing/base-cell-anchor-xform.js +48 -0
- package/lib/xlsx/xform/drawing/blip-fill-xform.js +71 -0
- package/lib/xlsx/xform/drawing/blip-xform.js +42 -0
- package/lib/xlsx/xform/drawing/c-nv-pic-pr-xform.js +38 -0
- package/lib/xlsx/xform/drawing/c-nv-pr-xform.js +68 -0
- package/lib/xlsx/xform/drawing/cell-position-xform.js +77 -0
- package/lib/xlsx/xform/drawing/drawing-xform.js +109 -0
- package/lib/xlsx/xform/drawing/ext-lst-xform.js +43 -0
- package/lib/xlsx/xform/drawing/ext-xform.js +44 -0
- package/lib/xlsx/xform/drawing/hlink-click-xform.js +41 -0
- package/lib/xlsx/xform/drawing/nv-pic-pr-xform.js +65 -0
- package/lib/xlsx/xform/drawing/one-cell-anchor-xform.js +63 -0
- package/lib/xlsx/xform/drawing/pic-xform.js +77 -0
- package/lib/xlsx/xform/drawing/sp-pr.js +17 -0
- package/lib/xlsx/xform/drawing/two-cell-anchor-xform.js +62 -0
- package/lib/xlsx/xform/list-xform.js +95 -0
- package/lib/xlsx/xform/pivot-table/cache-field.js +43 -0
- package/lib/xlsx/xform/pivot-table/pivot-cache-definition-xform.js +77 -0
- package/lib/xlsx/xform/pivot-table/pivot-cache-records-xform.js +103 -0
- package/lib/xlsx/xform/pivot-table/pivot-table-xform.js +189 -0
- package/lib/xlsx/xform/sheet/auto-filter-xform.js +38 -0
- package/lib/xlsx/xform/sheet/cell-xform.js +498 -0
- package/lib/xlsx/xform/sheet/cf/cf-rule-xform.js +301 -0
- package/lib/xlsx/xform/sheet/cf/cfvo-xform.js +27 -0
- package/lib/xlsx/xform/sheet/cf/color-scale-xform.js +45 -0
- package/lib/xlsx/xform/sheet/cf/conditional-formatting-xform.js +48 -0
- package/lib/xlsx/xform/sheet/cf/conditional-formattings-xform.js +92 -0
- package/lib/xlsx/xform/sheet/cf/databar-xform.js +49 -0
- package/lib/xlsx/xform/sheet/cf/ext-lst-ref-xform.js +87 -0
- package/lib/xlsx/xform/sheet/cf/formula-xform.js +25 -0
- package/lib/xlsx/xform/sheet/cf/icon-set-xform.js +47 -0
- package/lib/xlsx/xform/sheet/cf-ext/cf-icon-ext-xform.js +27 -0
- package/lib/xlsx/xform/sheet/cf-ext/cf-rule-ext-xform.js +98 -0
- package/lib/xlsx/xform/sheet/cf-ext/cfvo-ext-xform.js +43 -0
- package/lib/xlsx/xform/sheet/cf-ext/conditional-formatting-ext-xform.js +62 -0
- package/lib/xlsx/xform/sheet/cf-ext/conditional-formattings-ext-xform.js +50 -0
- package/lib/xlsx/xform/sheet/cf-ext/databar-ext-xform.js +98 -0
- package/lib/xlsx/xform/sheet/cf-ext/f-ext-xform.js +25 -0
- package/lib/xlsx/xform/sheet/cf-ext/icon-set-ext-xform.js +73 -0
- package/lib/xlsx/xform/sheet/cf-ext/sqref-ext-xform.js +25 -0
- package/lib/xlsx/xform/sheet/col-xform.js +86 -0
- package/lib/xlsx/xform/sheet/data-validations-xform.js +257 -0
- package/lib/xlsx/xform/sheet/dimension-xform.js +29 -0
- package/lib/xlsx/xform/sheet/drawing-xform.js +33 -0
- package/lib/xlsx/xform/sheet/ext-lst-xform.js +86 -0
- package/lib/xlsx/xform/sheet/header-footer-xform.js +146 -0
- package/lib/xlsx/xform/sheet/hyperlink-xform.js +54 -0
- package/lib/xlsx/xform/sheet/merge-cell-xform.js +27 -0
- package/lib/xlsx/xform/sheet/merges.js +56 -0
- package/lib/xlsx/xform/sheet/outline-properties-xform.js +43 -0
- package/lib/xlsx/xform/sheet/page-breaks-xform.js +27 -0
- package/lib/xlsx/xform/sheet/page-margins-xform.js +49 -0
- package/lib/xlsx/xform/sheet/page-setup-properties-xform.js +35 -0
- package/lib/xlsx/xform/sheet/page-setup-xform.js +103 -0
- package/lib/xlsx/xform/sheet/picture-xform.js +33 -0
- package/lib/xlsx/xform/sheet/print-options-xform.js +49 -0
- package/lib/xlsx/xform/sheet/row-breaks-xform.js +39 -0
- package/lib/xlsx/xform/sheet/row-xform.js +142 -0
- package/lib/xlsx/xform/sheet/sheet-format-properties-xform.js +55 -0
- package/lib/xlsx/xform/sheet/sheet-properties-xform.js +90 -0
- package/lib/xlsx/xform/sheet/sheet-protection-xform.js +89 -0
- package/lib/xlsx/xform/sheet/sheet-view-xform.js +202 -0
- package/lib/xlsx/xform/sheet/table-part-xform.js +33 -0
- package/lib/xlsx/xform/sheet/worksheet-xform.js +548 -0
- package/lib/xlsx/xform/simple/boolean-xform.js +31 -0
- package/lib/xlsx/xform/simple/date-xform.js +66 -0
- package/lib/xlsx/xform/simple/float-xform.js +51 -0
- package/lib/xlsx/xform/simple/integer-xform.js +57 -0
- package/lib/xlsx/xform/simple/string-xform.js +51 -0
- package/lib/xlsx/xform/static-xform.js +64 -0
- package/lib/xlsx/xform/strings/phonetic-text-xform.js +98 -0
- package/lib/xlsx/xform/strings/rich-text-xform.js +101 -0
- package/lib/xlsx/xform/strings/shared-string-xform.js +102 -0
- package/lib/xlsx/xform/strings/shared-strings-xform.js +127 -0
- package/lib/xlsx/xform/strings/text-xform.js +44 -0
- package/lib/xlsx/xform/style/alignment-xform.js +172 -0
- package/lib/xlsx/xform/style/border-xform.js +207 -0
- package/lib/xlsx/xform/style/color-xform.js +63 -0
- package/lib/xlsx/xform/style/dxf-xform.js +111 -0
- package/lib/xlsx/xform/style/fill-xform.js +364 -0
- package/lib/xlsx/xform/style/font-xform.js +102 -0
- package/lib/xlsx/xform/style/numfmt-xform.js +63 -0
- package/lib/xlsx/xform/style/protection-xform.js +60 -0
- package/lib/xlsx/xform/style/style-xform.js +125 -0
- package/lib/xlsx/xform/style/styles-xform.js +527 -0
- package/lib/xlsx/xform/style/underline-xform.js +47 -0
- package/lib/xlsx/xform/table/auto-filter-xform.js +81 -0
- package/lib/xlsx/xform/table/custom-filter-xform.js +33 -0
- package/lib/xlsx/xform/table/filter-column-xform.js +96 -0
- package/lib/xlsx/xform/table/filter-xform.js +31 -0
- package/lib/xlsx/xform/table/table-column-xform.js +44 -0
- package/lib/xlsx/xform/table/table-style-info-xform.js +41 -0
- package/lib/xlsx/xform/table/table-xform.js +131 -0
- package/lib/xlsx/xlsx.js +774 -0
- package/lib/xlsx/xml/theme1.js +3 -0
- package/lib/xlsx/xml/theme1.xml +318 -0
- package/package.json +149 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const {EventEmitter} = require('events');
|
|
2
|
+
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// AutoDrain - kind of /dev/null
|
|
5
|
+
class AutoDrain extends EventEmitter {
|
|
6
|
+
write(chunk) {
|
|
7
|
+
this.emit('data', chunk);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
end() {
|
|
11
|
+
this.emit('end');
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = AutoDrain;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// eslint-disable-next-line node/no-unsupported-features/node-builtins
|
|
2
|
+
const textDecoder = typeof TextDecoder === 'undefined' ? null : new TextDecoder('utf-8');
|
|
3
|
+
|
|
4
|
+
function bufferToString(chunk) {
|
|
5
|
+
if (typeof chunk === 'string') {
|
|
6
|
+
return chunk;
|
|
7
|
+
}
|
|
8
|
+
if (textDecoder) {
|
|
9
|
+
return textDecoder.decode(chunk);
|
|
10
|
+
}
|
|
11
|
+
return chunk.toString();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
exports.bufferToString = bufferToString;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// eslint-disable-next-line node/no-unsupported-features/node-builtins
|
|
2
|
+
const textEncoder = typeof TextEncoder === 'undefined' ? null : new TextEncoder('utf-8');
|
|
3
|
+
const {Buffer} = require('buffer');
|
|
4
|
+
|
|
5
|
+
function stringToBuffer(str) {
|
|
6
|
+
if (typeof str !== 'string') {
|
|
7
|
+
return str;
|
|
8
|
+
}
|
|
9
|
+
if (textEncoder) {
|
|
10
|
+
return Buffer.from(textEncoder.encode(str).buffer);
|
|
11
|
+
}
|
|
12
|
+
return Buffer.from(str);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
exports.stringToBuffer = stringToBuffer;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
const _ = require('./under-dash');
|
|
2
|
+
const colCache = require('./col-cache');
|
|
3
|
+
|
|
4
|
+
class CellMatrix {
|
|
5
|
+
constructor(template) {
|
|
6
|
+
this.template = template;
|
|
7
|
+
this.sheets = {};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
addCell(addressStr) {
|
|
11
|
+
this.addCellEx(colCache.decodeEx(addressStr));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
getCell(addressStr) {
|
|
15
|
+
return this.findCellEx(colCache.decodeEx(addressStr), true);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
findCell(addressStr) {
|
|
19
|
+
return this.findCellEx(colCache.decodeEx(addressStr), false);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
findCellAt(sheetName, rowNumber, colNumber) {
|
|
23
|
+
const sheet = this.sheets[sheetName];
|
|
24
|
+
const row = sheet && sheet[rowNumber];
|
|
25
|
+
return row && row[colNumber];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
addCellEx(address) {
|
|
29
|
+
if (address.top) {
|
|
30
|
+
for (let row = address.top; row <= address.bottom; row++) {
|
|
31
|
+
for (let col = address.left; col <= address.right; col++) {
|
|
32
|
+
this.getCellAt(address.sheetName, row, col);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
} else {
|
|
36
|
+
this.findCellEx(address, true);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
getCellEx(address) {
|
|
41
|
+
return this.findCellEx(address, true);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
findCellEx(address, create) {
|
|
45
|
+
const sheet = this.findSheet(address, create);
|
|
46
|
+
const row = this.findSheetRow(sheet, address, create);
|
|
47
|
+
return this.findRowCell(row, address, create);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
getCellAt(sheetName, rowNumber, colNumber) {
|
|
51
|
+
const sheet = this.sheets[sheetName] || (this.sheets[sheetName] = []);
|
|
52
|
+
const row = sheet[rowNumber] || (sheet[rowNumber] = []);
|
|
53
|
+
const cell =
|
|
54
|
+
row[colNumber] ||
|
|
55
|
+
(row[colNumber] = {
|
|
56
|
+
sheetName,
|
|
57
|
+
address: colCache.n2l(colNumber) + rowNumber,
|
|
58
|
+
row: rowNumber,
|
|
59
|
+
col: colNumber,
|
|
60
|
+
});
|
|
61
|
+
return cell;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
removeCellEx(address) {
|
|
65
|
+
const sheet = this.findSheet(address);
|
|
66
|
+
if (!sheet) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const row = this.findSheetRow(sheet, address);
|
|
70
|
+
if (!row) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
delete row[address.col];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
forEachInSheet(sheetName, callback) {
|
|
77
|
+
const sheet = this.sheets[sheetName];
|
|
78
|
+
if (sheet) {
|
|
79
|
+
sheet.forEach((row, rowNumber) => {
|
|
80
|
+
if (row) {
|
|
81
|
+
row.forEach((cell, colNumber) => {
|
|
82
|
+
if (cell) {
|
|
83
|
+
callback(cell, rowNumber, colNumber);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
forEach(callback) {
|
|
92
|
+
_.each(this.sheets, (sheet, sheetName) => {
|
|
93
|
+
this.forEachInSheet(sheetName, callback);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
map(callback) {
|
|
98
|
+
const results = [];
|
|
99
|
+
this.forEach(cell => {
|
|
100
|
+
results.push(callback(cell));
|
|
101
|
+
});
|
|
102
|
+
return results;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
findSheet(address, create) {
|
|
106
|
+
const name = address.sheetName;
|
|
107
|
+
if (this.sheets[name]) {
|
|
108
|
+
return this.sheets[name];
|
|
109
|
+
}
|
|
110
|
+
if (create) {
|
|
111
|
+
return (this.sheets[name] = []);
|
|
112
|
+
}
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
findSheetRow(sheet, address, create) {
|
|
117
|
+
const {row} = address;
|
|
118
|
+
if (sheet && sheet[row]) {
|
|
119
|
+
return sheet[row];
|
|
120
|
+
}
|
|
121
|
+
if (create) {
|
|
122
|
+
return (sheet[row] = []);
|
|
123
|
+
}
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
findRowCell(row, address, create) {
|
|
128
|
+
const {col} = address;
|
|
129
|
+
if (row && row[col]) {
|
|
130
|
+
return row[col];
|
|
131
|
+
}
|
|
132
|
+
if (create) {
|
|
133
|
+
return (row[col] = this.template
|
|
134
|
+
? Object.assign(address, JSON.parse(JSON.stringify(this.template)))
|
|
135
|
+
: address);
|
|
136
|
+
}
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
spliceRows(sheetName, start, numDelete, numInsert) {
|
|
141
|
+
const sheet = this.sheets[sheetName];
|
|
142
|
+
if (sheet) {
|
|
143
|
+
const inserts = [];
|
|
144
|
+
for (let i = 0; i < numInsert; i++) {
|
|
145
|
+
inserts.push([]);
|
|
146
|
+
}
|
|
147
|
+
sheet.splice(start, numDelete, ...inserts);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
spliceColumns(sheetName, start, numDelete, numInsert) {
|
|
152
|
+
const sheet = this.sheets[sheetName];
|
|
153
|
+
if (sheet) {
|
|
154
|
+
const inserts = [];
|
|
155
|
+
for (let i = 0; i < numInsert; i++) {
|
|
156
|
+
inserts.push(null);
|
|
157
|
+
}
|
|
158
|
+
_.each(sheet, row => {
|
|
159
|
+
row.splice(start, numDelete, ...inserts);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
module.exports = CellMatrix;
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
const addressRegex = /^[A-Z]+\d+$/;
|
|
2
|
+
// =========================================================================
|
|
3
|
+
// Column Letter to Number conversion
|
|
4
|
+
const colCache = {
|
|
5
|
+
_dictionary: [
|
|
6
|
+
'A',
|
|
7
|
+
'B',
|
|
8
|
+
'C',
|
|
9
|
+
'D',
|
|
10
|
+
'E',
|
|
11
|
+
'F',
|
|
12
|
+
'G',
|
|
13
|
+
'H',
|
|
14
|
+
'I',
|
|
15
|
+
'J',
|
|
16
|
+
'K',
|
|
17
|
+
'L',
|
|
18
|
+
'M',
|
|
19
|
+
'N',
|
|
20
|
+
'O',
|
|
21
|
+
'P',
|
|
22
|
+
'Q',
|
|
23
|
+
'R',
|
|
24
|
+
'S',
|
|
25
|
+
'T',
|
|
26
|
+
'U',
|
|
27
|
+
'V',
|
|
28
|
+
'W',
|
|
29
|
+
'X',
|
|
30
|
+
'Y',
|
|
31
|
+
'Z',
|
|
32
|
+
],
|
|
33
|
+
_l2nFill: 0,
|
|
34
|
+
_l2n: {},
|
|
35
|
+
_n2l: [],
|
|
36
|
+
_level(n) {
|
|
37
|
+
if (n <= 26) {
|
|
38
|
+
return 1;
|
|
39
|
+
}
|
|
40
|
+
if (n <= 26 * 26) {
|
|
41
|
+
return 2;
|
|
42
|
+
}
|
|
43
|
+
return 3;
|
|
44
|
+
},
|
|
45
|
+
_fill(level) {
|
|
46
|
+
let c;
|
|
47
|
+
let v;
|
|
48
|
+
let l1;
|
|
49
|
+
let l2;
|
|
50
|
+
let l3;
|
|
51
|
+
let n = 1;
|
|
52
|
+
if (level >= 4) {
|
|
53
|
+
throw new Error('Out of bounds. Excel supports columns from 1 to 16384');
|
|
54
|
+
}
|
|
55
|
+
if (this._l2nFill < 1 && level >= 1) {
|
|
56
|
+
while (n <= 26) {
|
|
57
|
+
c = this._dictionary[n - 1];
|
|
58
|
+
this._n2l[n] = c;
|
|
59
|
+
this._l2n[c] = n;
|
|
60
|
+
n++;
|
|
61
|
+
}
|
|
62
|
+
this._l2nFill = 1;
|
|
63
|
+
}
|
|
64
|
+
if (this._l2nFill < 2 && level >= 2) {
|
|
65
|
+
n = 27;
|
|
66
|
+
while (n <= 26 + (26 * 26)) {
|
|
67
|
+
v = n - (26 + 1);
|
|
68
|
+
l1 = v % 26;
|
|
69
|
+
l2 = Math.floor(v / 26);
|
|
70
|
+
c = this._dictionary[l2] + this._dictionary[l1];
|
|
71
|
+
this._n2l[n] = c;
|
|
72
|
+
this._l2n[c] = n;
|
|
73
|
+
n++;
|
|
74
|
+
}
|
|
75
|
+
this._l2nFill = 2;
|
|
76
|
+
}
|
|
77
|
+
if (this._l2nFill < 3 && level >= 3) {
|
|
78
|
+
n = 26 + (26 * 26) + 1;
|
|
79
|
+
while (n <= 16384) {
|
|
80
|
+
v = n - ((26 * 26) + 26 + 1);
|
|
81
|
+
l1 = v % 26;
|
|
82
|
+
l2 = Math.floor(v / 26) % 26;
|
|
83
|
+
l3 = Math.floor(v / (26 * 26));
|
|
84
|
+
c = this._dictionary[l3] + this._dictionary[l2] + this._dictionary[l1];
|
|
85
|
+
this._n2l[n] = c;
|
|
86
|
+
this._l2n[c] = n;
|
|
87
|
+
n++;
|
|
88
|
+
}
|
|
89
|
+
this._l2nFill = 3;
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
l2n(l) {
|
|
93
|
+
if (!this._l2n[l]) {
|
|
94
|
+
this._fill(l.length);
|
|
95
|
+
}
|
|
96
|
+
if (!this._l2n[l]) {
|
|
97
|
+
throw new Error(`Out of bounds. Invalid column letter: ${l}`);
|
|
98
|
+
}
|
|
99
|
+
return this._l2n[l];
|
|
100
|
+
},
|
|
101
|
+
n2l(n) {
|
|
102
|
+
if (n < 1 || n > 16384) {
|
|
103
|
+
throw new Error(`${n} is out of bounds. Excel supports columns from 1 to 16384`);
|
|
104
|
+
}
|
|
105
|
+
if (!this._n2l[n]) {
|
|
106
|
+
this._fill(this._level(n));
|
|
107
|
+
}
|
|
108
|
+
return this._n2l[n];
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
// =========================================================================
|
|
112
|
+
// Address processing
|
|
113
|
+
_hash: {},
|
|
114
|
+
|
|
115
|
+
// check if value looks like an address
|
|
116
|
+
validateAddress(value) {
|
|
117
|
+
if (!addressRegex.test(value)) {
|
|
118
|
+
throw new Error(`Invalid Address: ${value}`);
|
|
119
|
+
}
|
|
120
|
+
return true;
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
// convert address string into structure
|
|
124
|
+
decodeAddress(value) {
|
|
125
|
+
const addr = value.length < 5 && this._hash[value];
|
|
126
|
+
if (addr) {
|
|
127
|
+
return addr;
|
|
128
|
+
}
|
|
129
|
+
let hasCol = false;
|
|
130
|
+
let col = '';
|
|
131
|
+
let colNumber = 0;
|
|
132
|
+
let hasRow = false;
|
|
133
|
+
let row = '';
|
|
134
|
+
let rowNumber = 0;
|
|
135
|
+
for (let i = 0, char; i < value.length; i++) {
|
|
136
|
+
char = value.charCodeAt(i);
|
|
137
|
+
// col should before row
|
|
138
|
+
if (!hasRow && char >= 65 && char <= 90) {
|
|
139
|
+
// 65 = 'A'.charCodeAt(0)
|
|
140
|
+
// 90 = 'Z'.charCodeAt(0)
|
|
141
|
+
hasCol = true;
|
|
142
|
+
col += value[i];
|
|
143
|
+
// colNumber starts from 1
|
|
144
|
+
colNumber = (colNumber * 26) + char - 64;
|
|
145
|
+
} else if (char >= 48 && char <= 57) {
|
|
146
|
+
// 48 = '0'.charCodeAt(0)
|
|
147
|
+
// 57 = '9'.charCodeAt(0)
|
|
148
|
+
hasRow = true;
|
|
149
|
+
row += value[i];
|
|
150
|
+
// rowNumber starts from 0
|
|
151
|
+
rowNumber = (rowNumber * 10) + char - 48;
|
|
152
|
+
} else if (hasRow && hasCol && char !== 36) {
|
|
153
|
+
// 36 = '$'.charCodeAt(0)
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (!hasCol) {
|
|
158
|
+
colNumber = undefined;
|
|
159
|
+
} else if (colNumber > 16384) {
|
|
160
|
+
throw new Error(`Out of bounds. Invalid column letter: ${col}`);
|
|
161
|
+
}
|
|
162
|
+
if (!hasRow) {
|
|
163
|
+
rowNumber = undefined;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// in case $row$col
|
|
167
|
+
value = col + row;
|
|
168
|
+
|
|
169
|
+
const address = {
|
|
170
|
+
address: value,
|
|
171
|
+
col: colNumber,
|
|
172
|
+
row: rowNumber,
|
|
173
|
+
$col$row: `$${col}$${row}`,
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// mem fix - cache only the tl 100x100 square
|
|
177
|
+
if (colNumber <= 100 && rowNumber <= 100) {
|
|
178
|
+
this._hash[value] = address;
|
|
179
|
+
this._hash[address.$col$row] = address;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return address;
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
// convert r,c into structure (if only 1 arg, assume r is address string)
|
|
186
|
+
getAddress(r, c) {
|
|
187
|
+
if (c) {
|
|
188
|
+
const address = this.n2l(c) + r;
|
|
189
|
+
return this.decodeAddress(address);
|
|
190
|
+
}
|
|
191
|
+
return this.decodeAddress(r);
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
// convert [address], [tl:br] into address structures
|
|
195
|
+
decode(value) {
|
|
196
|
+
const parts = value.split(':');
|
|
197
|
+
if (parts.length === 2) {
|
|
198
|
+
const tl = this.decodeAddress(parts[0]);
|
|
199
|
+
const br = this.decodeAddress(parts[1]);
|
|
200
|
+
const result = {
|
|
201
|
+
top: Math.min(tl.row, br.row),
|
|
202
|
+
left: Math.min(tl.col, br.col),
|
|
203
|
+
bottom: Math.max(tl.row, br.row),
|
|
204
|
+
right: Math.max(tl.col, br.col),
|
|
205
|
+
};
|
|
206
|
+
// reconstruct tl, br and dimensions
|
|
207
|
+
result.tl = this.n2l(result.left) + result.top;
|
|
208
|
+
result.br = this.n2l(result.right) + result.bottom;
|
|
209
|
+
result.dimensions = `${result.tl}:${result.br}`;
|
|
210
|
+
return result;
|
|
211
|
+
}
|
|
212
|
+
return this.decodeAddress(value);
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
// convert [sheetName!][$]col[$]row[[$]col[$]row] into address or range structures
|
|
216
|
+
decodeEx(value) {
|
|
217
|
+
const groups = value.match(/(?:(?:(?:'((?:[^']|'')*)')|([^'^ !]*))!)?(.*)/);
|
|
218
|
+
|
|
219
|
+
const sheetName = groups[1] || groups[2]; // Qouted and unqouted groups
|
|
220
|
+
const reference = groups[3]; // Remaining address
|
|
221
|
+
|
|
222
|
+
const parts = reference.split(':');
|
|
223
|
+
if (parts.length > 1) {
|
|
224
|
+
let tl = this.decodeAddress(parts[0]);
|
|
225
|
+
let br = this.decodeAddress(parts[1]);
|
|
226
|
+
const top = Math.min(tl.row, br.row);
|
|
227
|
+
const left = Math.min(tl.col, br.col);
|
|
228
|
+
const bottom = Math.max(tl.row, br.row);
|
|
229
|
+
const right = Math.max(tl.col, br.col);
|
|
230
|
+
|
|
231
|
+
tl = this.n2l(left) + top;
|
|
232
|
+
br = this.n2l(right) + bottom;
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
top,
|
|
236
|
+
left,
|
|
237
|
+
bottom,
|
|
238
|
+
right,
|
|
239
|
+
sheetName,
|
|
240
|
+
tl: {address: tl, col: left, row: top, $col$row: `$${this.n2l(left)}$${top}`, sheetName},
|
|
241
|
+
br: {
|
|
242
|
+
address: br,
|
|
243
|
+
col: right,
|
|
244
|
+
row: bottom,
|
|
245
|
+
$col$row: `$${this.n2l(right)}$${bottom}`,
|
|
246
|
+
sheetName,
|
|
247
|
+
},
|
|
248
|
+
dimensions: `${tl}:${br}`,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
if (reference.startsWith('#')) {
|
|
252
|
+
return sheetName ? {sheetName, error: reference} : {error: reference};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const address = this.decodeAddress(reference);
|
|
256
|
+
return sheetName ? {sheetName, ...address} : address;
|
|
257
|
+
},
|
|
258
|
+
|
|
259
|
+
// convert row,col into address string
|
|
260
|
+
encodeAddress(row, col) {
|
|
261
|
+
return colCache.n2l(col) + row;
|
|
262
|
+
},
|
|
263
|
+
|
|
264
|
+
// convert row,col into string address or t,l,b,r into range
|
|
265
|
+
encode() {
|
|
266
|
+
switch (arguments.length) {
|
|
267
|
+
case 2:
|
|
268
|
+
return colCache.encodeAddress(arguments[0], arguments[1]);
|
|
269
|
+
case 4:
|
|
270
|
+
return `${colCache.encodeAddress(arguments[0], arguments[1])}:${colCache.encodeAddress(
|
|
271
|
+
arguments[2],
|
|
272
|
+
arguments[3]
|
|
273
|
+
)}`;
|
|
274
|
+
default:
|
|
275
|
+
throw new Error('Can only encode with 2 or 4 arguments');
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
|
|
279
|
+
// return true if address is contained within range
|
|
280
|
+
inRange(range, address) {
|
|
281
|
+
const [left, top, , right, bottom] = range;
|
|
282
|
+
const [col, row] = address;
|
|
283
|
+
return col >= left && col <= right && row >= top && row <= bottom;
|
|
284
|
+
},
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
module.exports = colCache;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const oneDepthCopy = (obj, nestKeys) => ({
|
|
2
|
+
...obj,
|
|
3
|
+
...nestKeys.reduce((memo, key) => {
|
|
4
|
+
if (obj[key]) memo[key] = {...obj[key]};
|
|
5
|
+
return memo;
|
|
6
|
+
}, {}),
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const setIfExists = (src, dst, key, nestKeys = []) => {
|
|
10
|
+
if (src[key]) dst[key] = oneDepthCopy(src[key], nestKeys);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const isEmptyObj = obj => Object.keys(obj).length === 0;
|
|
14
|
+
|
|
15
|
+
const copyStyle = style => {
|
|
16
|
+
if (!style) return style;
|
|
17
|
+
if (isEmptyObj(style)) return {};
|
|
18
|
+
|
|
19
|
+
const copied = {...style};
|
|
20
|
+
|
|
21
|
+
setIfExists(style, copied, 'font', ['color']);
|
|
22
|
+
setIfExists(style, copied, 'alignment');
|
|
23
|
+
setIfExists(style, copied, 'protection');
|
|
24
|
+
if (style.border) {
|
|
25
|
+
setIfExists(style, copied, 'border');
|
|
26
|
+
setIfExists(style.border, copied.border, 'top', ['color']);
|
|
27
|
+
setIfExists(style.border, copied.border, 'left', ['color']);
|
|
28
|
+
setIfExists(style.border, copied.border, 'bottom', ['color']);
|
|
29
|
+
setIfExists(style.border, copied.border, 'right', ['color']);
|
|
30
|
+
setIfExists(style.border, copied.border, 'diagonal', ['color']);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (style.fill) {
|
|
34
|
+
setIfExists(style, copied, 'fill', ['fgColor', 'bgColor', 'center']);
|
|
35
|
+
if (style.fill.stops) {
|
|
36
|
+
copied.fill.stops = style.fill.stops.map(s => oneDepthCopy(s, ['color']));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return copied;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
exports.copyStyle = copyStyle;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const crypto = require('crypto');
|
|
4
|
+
|
|
5
|
+
const Encryptor = {
|
|
6
|
+
/**
|
|
7
|
+
* Calculate a hash of the concatenated buffers with the given algorithm.
|
|
8
|
+
* @param {string} algorithm - The hash algorithm.
|
|
9
|
+
* @returns {Buffer} The hash
|
|
10
|
+
*/
|
|
11
|
+
hash(algorithm, ...buffers) {
|
|
12
|
+
const hash = crypto.createHash(algorithm);
|
|
13
|
+
hash.update(Buffer.concat(buffers));
|
|
14
|
+
return hash.digest();
|
|
15
|
+
},
|
|
16
|
+
/**
|
|
17
|
+
* Convert a password into an encryption key
|
|
18
|
+
* @param {string} password - The password
|
|
19
|
+
* @param {string} hashAlgorithm - The hash algoritm
|
|
20
|
+
* @param {string} saltValue - The salt value
|
|
21
|
+
* @param {number} spinCount - The spin count
|
|
22
|
+
* @param {number} keyBits - The length of the key in bits
|
|
23
|
+
* @param {Buffer} blockKey - The block key
|
|
24
|
+
* @returns {Buffer} The encryption key
|
|
25
|
+
*/
|
|
26
|
+
convertPasswordToHash(password, hashAlgorithm, saltValue, spinCount) {
|
|
27
|
+
hashAlgorithm = hashAlgorithm.toLowerCase();
|
|
28
|
+
const hashes = crypto.getHashes();
|
|
29
|
+
if (hashes.indexOf(hashAlgorithm) < 0) {
|
|
30
|
+
throw new Error(`Hash algorithm '${hashAlgorithm}' not supported!`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Password must be in unicode buffer
|
|
34
|
+
const passwordBuffer = Buffer.from(password, 'utf16le');
|
|
35
|
+
// Generate the initial hash
|
|
36
|
+
let key = this.hash(hashAlgorithm, Buffer.from(saltValue, 'base64'), passwordBuffer);
|
|
37
|
+
// Now regenerate until spin count
|
|
38
|
+
for (let i = 0; i < spinCount; i++) {
|
|
39
|
+
const iterator = Buffer.alloc(4);
|
|
40
|
+
// this is the 'special' element of Excel password hashing
|
|
41
|
+
// that stops us from using crypto.pbkdf2()
|
|
42
|
+
iterator.writeUInt32LE(i, 0);
|
|
43
|
+
key = this.hash(hashAlgorithm, key, iterator);
|
|
44
|
+
}
|
|
45
|
+
return key.toString('base64');
|
|
46
|
+
},
|
|
47
|
+
/**
|
|
48
|
+
* Generates cryptographically strong pseudo-random data.
|
|
49
|
+
* @param size The size argument is a number indicating the number of bytes to generate.
|
|
50
|
+
*/
|
|
51
|
+
randomBytes(size) {
|
|
52
|
+
return crypto.randomBytes(size);
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
module.exports = Encryptor;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module.exports = async function* iterateStream(stream) {
|
|
2
|
+
const contents = [];
|
|
3
|
+
stream.on('data', data => contents.push(data));
|
|
4
|
+
|
|
5
|
+
let resolveStreamEndedPromise;
|
|
6
|
+
const streamEndedPromise = new Promise(resolve => (resolveStreamEndedPromise = resolve));
|
|
7
|
+
|
|
8
|
+
let ended = false;
|
|
9
|
+
stream.on('end', () => {
|
|
10
|
+
ended = true;
|
|
11
|
+
resolveStreamEndedPromise();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
let error = false;
|
|
15
|
+
stream.on('error', err => {
|
|
16
|
+
error = err;
|
|
17
|
+
resolveStreamEndedPromise();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
while (!ended || contents.length > 0) {
|
|
21
|
+
if (contents.length === 0) {
|
|
22
|
+
stream.resume();
|
|
23
|
+
// eslint-disable-next-line no-await-in-loop
|
|
24
|
+
await Promise.race([once(stream, 'data'), streamEndedPromise]);
|
|
25
|
+
} else {
|
|
26
|
+
stream.pause();
|
|
27
|
+
const data = contents.shift();
|
|
28
|
+
yield data;
|
|
29
|
+
}
|
|
30
|
+
if (error) throw error;
|
|
31
|
+
}
|
|
32
|
+
resolveStreamEndedPromise();
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
function once(eventEmitter, type) {
|
|
36
|
+
// TODO: Use require('events').once when node v10 is dropped
|
|
37
|
+
return new Promise(resolve => {
|
|
38
|
+
let fired = false;
|
|
39
|
+
const handler = () => {
|
|
40
|
+
if (!fired) {
|
|
41
|
+
fired = true;
|
|
42
|
+
eventEmitter.removeListener(type, handler);
|
|
43
|
+
resolve();
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
eventEmitter.addListener(type, handler);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const {SaxesParser} = require('saxes');
|
|
2
|
+
const {PassThrough} = require('readable-stream');
|
|
3
|
+
const {bufferToString} = require('./browser-buffer-decode');
|
|
4
|
+
|
|
5
|
+
module.exports = async function* (iterable) {
|
|
6
|
+
// TODO: Remove once node v8 is deprecated
|
|
7
|
+
// Detect and upgrade old streams
|
|
8
|
+
if (iterable.pipe && !iterable[Symbol.asyncIterator]) {
|
|
9
|
+
iterable = iterable.pipe(new PassThrough());
|
|
10
|
+
}
|
|
11
|
+
const saxesParser = new SaxesParser();
|
|
12
|
+
let error;
|
|
13
|
+
saxesParser.on('error', err => {
|
|
14
|
+
error = err;
|
|
15
|
+
});
|
|
16
|
+
let events = [];
|
|
17
|
+
saxesParser.on('opentag', value => events.push({eventType: 'opentag', value}));
|
|
18
|
+
saxesParser.on('text', value => events.push({eventType: 'text', value}));
|
|
19
|
+
saxesParser.on('closetag', value => events.push({eventType: 'closetag', value}));
|
|
20
|
+
for await (const chunk of iterable) {
|
|
21
|
+
saxesParser.write(bufferToString(chunk));
|
|
22
|
+
// saxesParser.write and saxesParser.on() are synchronous,
|
|
23
|
+
// so we can only reach the below line once all events have been emitted
|
|
24
|
+
if (error) throw error;
|
|
25
|
+
// As a performance optimization, we gather all events instead of passing
|
|
26
|
+
// them one by one, which would cause each event to go through the event queue
|
|
27
|
+
yield events;
|
|
28
|
+
events = [];
|
|
29
|
+
}
|
|
30
|
+
};
|