@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,717 @@
|
|
|
1
|
+
const _ = require('../../utils/under-dash');
|
|
2
|
+
|
|
3
|
+
const RelType = require('../../xlsx/rel-type');
|
|
4
|
+
|
|
5
|
+
const colCache = require('../../utils/col-cache');
|
|
6
|
+
const Encryptor = require('../../utils/encryptor');
|
|
7
|
+
const Dimensions = require('../../doc/range');
|
|
8
|
+
const StringBuf = require('../../utils/string-buf');
|
|
9
|
+
|
|
10
|
+
const Row = require('../../doc/row');
|
|
11
|
+
const Column = require('../../doc/column');
|
|
12
|
+
|
|
13
|
+
const SheetRelsWriter = require('./sheet-rels-writer');
|
|
14
|
+
const SheetCommentsWriter = require('./sheet-comments-writer');
|
|
15
|
+
const DataValidations = require('../../doc/data-validations');
|
|
16
|
+
|
|
17
|
+
const xmlBuffer = new StringBuf();
|
|
18
|
+
|
|
19
|
+
// ============================================================================================
|
|
20
|
+
// Xforms
|
|
21
|
+
const ListXform = require('../../xlsx/xform/list-xform');
|
|
22
|
+
const DataValidationsXform = require('../../xlsx/xform/sheet/data-validations-xform');
|
|
23
|
+
const SheetPropertiesXform = require('../../xlsx/xform/sheet/sheet-properties-xform');
|
|
24
|
+
const SheetFormatPropertiesXform = require('../../xlsx/xform/sheet/sheet-format-properties-xform');
|
|
25
|
+
const ColXform = require('../../xlsx/xform/sheet/col-xform');
|
|
26
|
+
const RowXform = require('../../xlsx/xform/sheet/row-xform');
|
|
27
|
+
const HyperlinkXform = require('../../xlsx/xform/sheet/hyperlink-xform');
|
|
28
|
+
const SheetViewXform = require('../../xlsx/xform/sheet/sheet-view-xform');
|
|
29
|
+
const SheetProtectionXform = require('../../xlsx/xform/sheet/sheet-protection-xform');
|
|
30
|
+
const PageMarginsXform = require('../../xlsx/xform/sheet/page-margins-xform');
|
|
31
|
+
const PageSetupXform = require('../../xlsx/xform/sheet/page-setup-xform');
|
|
32
|
+
const AutoFilterXform = require('../../xlsx/xform/sheet/auto-filter-xform');
|
|
33
|
+
const PictureXform = require('../../xlsx/xform/sheet/picture-xform');
|
|
34
|
+
const ConditionalFormattingsXform = require('../../xlsx/xform/sheet/cf/conditional-formattings-xform');
|
|
35
|
+
const HeaderFooterXform = require('../../xlsx/xform/sheet/header-footer-xform');
|
|
36
|
+
const RowBreaksXform = require('../../xlsx/xform/sheet/row-breaks-xform');
|
|
37
|
+
|
|
38
|
+
// since prepare and render are functional, we can use singletons
|
|
39
|
+
const xform = {
|
|
40
|
+
dataValidations: new DataValidationsXform(),
|
|
41
|
+
sheetProperties: new SheetPropertiesXform(),
|
|
42
|
+
sheetFormatProperties: new SheetFormatPropertiesXform(),
|
|
43
|
+
columns: new ListXform({tag: 'cols', length: false, childXform: new ColXform()}),
|
|
44
|
+
row: new RowXform(),
|
|
45
|
+
hyperlinks: new ListXform({tag: 'hyperlinks', length: false, childXform: new HyperlinkXform()}),
|
|
46
|
+
sheetViews: new ListXform({tag: 'sheetViews', length: false, childXform: new SheetViewXform()}),
|
|
47
|
+
sheetProtection: new SheetProtectionXform(),
|
|
48
|
+
pageMargins: new PageMarginsXform(),
|
|
49
|
+
pageSeteup: new PageSetupXform(),
|
|
50
|
+
autoFilter: new AutoFilterXform(),
|
|
51
|
+
picture: new PictureXform(),
|
|
52
|
+
conditionalFormattings: new ConditionalFormattingsXform(),
|
|
53
|
+
headerFooter: new HeaderFooterXform(),
|
|
54
|
+
rowBreaks: new RowBreaksXform(),
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// ============================================================================================
|
|
58
|
+
|
|
59
|
+
class WorksheetWriter {
|
|
60
|
+
constructor(options) {
|
|
61
|
+
// in a workbook, each sheet will have a number
|
|
62
|
+
this.id = options.id;
|
|
63
|
+
|
|
64
|
+
// and a name
|
|
65
|
+
this.name = options.name || `Sheet${this.id}`;
|
|
66
|
+
|
|
67
|
+
// add a state
|
|
68
|
+
this.state = options.state || 'visible';
|
|
69
|
+
|
|
70
|
+
// rows are stored here while they need to be worked on.
|
|
71
|
+
// when they are committed, they will be deleted.
|
|
72
|
+
this._rows = [];
|
|
73
|
+
|
|
74
|
+
// column definitions
|
|
75
|
+
this._columns = null;
|
|
76
|
+
|
|
77
|
+
// column keys (addRow convenience): key ==> this._columns index
|
|
78
|
+
this._keys = {};
|
|
79
|
+
|
|
80
|
+
// keep a record of all row and column pageBreaks
|
|
81
|
+
this._merges = [];
|
|
82
|
+
this._merges.add = function() {}; // ignore cell instruction
|
|
83
|
+
|
|
84
|
+
// keep record of all hyperlinks
|
|
85
|
+
this._sheetRelsWriter = new SheetRelsWriter(options);
|
|
86
|
+
|
|
87
|
+
this._sheetCommentsWriter = new SheetCommentsWriter(this, this._sheetRelsWriter, options);
|
|
88
|
+
|
|
89
|
+
// keep a record of dimensions
|
|
90
|
+
this._dimensions = new Dimensions();
|
|
91
|
+
|
|
92
|
+
// first uncommitted row
|
|
93
|
+
this._rowZero = 1;
|
|
94
|
+
|
|
95
|
+
// committed flag
|
|
96
|
+
this.committed = false;
|
|
97
|
+
|
|
98
|
+
// for data validations
|
|
99
|
+
this.dataValidations = new DataValidations();
|
|
100
|
+
|
|
101
|
+
// for sharing formulae
|
|
102
|
+
this._formulae = {};
|
|
103
|
+
this._siFormulae = 0;
|
|
104
|
+
|
|
105
|
+
// keep a record of conditionalFormattings
|
|
106
|
+
this.conditionalFormatting = [];
|
|
107
|
+
|
|
108
|
+
// keep a record of all row and column pageBreaks
|
|
109
|
+
this.rowBreaks = [];
|
|
110
|
+
|
|
111
|
+
// for default row height, outline levels, etc
|
|
112
|
+
this.properties = Object.assign(
|
|
113
|
+
{},
|
|
114
|
+
{
|
|
115
|
+
defaultRowHeight: 15,
|
|
116
|
+
dyDescent: 55,
|
|
117
|
+
outlineLevelCol: 0,
|
|
118
|
+
outlineLevelRow: 0,
|
|
119
|
+
},
|
|
120
|
+
options.properties
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
this.headerFooter = Object.assign(
|
|
124
|
+
{},
|
|
125
|
+
{
|
|
126
|
+
differentFirst: false,
|
|
127
|
+
differentOddEven: false,
|
|
128
|
+
oddHeader: null,
|
|
129
|
+
oddFooter: null,
|
|
130
|
+
evenHeader: null,
|
|
131
|
+
evenFooter: null,
|
|
132
|
+
firstHeader: null,
|
|
133
|
+
firstFooter: null,
|
|
134
|
+
},
|
|
135
|
+
options.headerFooter
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
// for all things printing
|
|
139
|
+
this.pageSetup = Object.assign(
|
|
140
|
+
{},
|
|
141
|
+
{
|
|
142
|
+
margins: {left: 0.7, right: 0.7, top: 0.75, bottom: 0.75, header: 0.3, footer: 0.3},
|
|
143
|
+
orientation: 'portrait',
|
|
144
|
+
horizontalDpi: 4294967295,
|
|
145
|
+
verticalDpi: 4294967295,
|
|
146
|
+
fitToPage: !!(
|
|
147
|
+
options.pageSetup &&
|
|
148
|
+
(options.pageSetup.fitToWidth || options.pageSetup.fitToHeight) &&
|
|
149
|
+
!options.pageSetup.scale
|
|
150
|
+
),
|
|
151
|
+
pageOrder: 'downThenOver',
|
|
152
|
+
blackAndWhite: false,
|
|
153
|
+
draft: false,
|
|
154
|
+
cellComments: 'None',
|
|
155
|
+
errors: 'displayed',
|
|
156
|
+
scale: 100,
|
|
157
|
+
fitToWidth: 1,
|
|
158
|
+
fitToHeight: 1,
|
|
159
|
+
paperSize: undefined,
|
|
160
|
+
showRowColHeaders: false,
|
|
161
|
+
showGridLines: false,
|
|
162
|
+
horizontalCentered: false,
|
|
163
|
+
verticalCentered: false,
|
|
164
|
+
rowBreaks: null,
|
|
165
|
+
colBreaks: null,
|
|
166
|
+
},
|
|
167
|
+
options.pageSetup
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
// using shared strings creates a smaller xlsx file but may use more memory
|
|
171
|
+
this.useSharedStrings = options.useSharedStrings || false;
|
|
172
|
+
|
|
173
|
+
this._workbook = options.workbook;
|
|
174
|
+
|
|
175
|
+
this.hasComments = false;
|
|
176
|
+
|
|
177
|
+
// views
|
|
178
|
+
this._views = options.views || [];
|
|
179
|
+
|
|
180
|
+
// auto filter
|
|
181
|
+
this.autoFilter = options.autoFilter || null;
|
|
182
|
+
|
|
183
|
+
this._media = [];
|
|
184
|
+
|
|
185
|
+
// worksheet protection
|
|
186
|
+
this.sheetProtection = null;
|
|
187
|
+
|
|
188
|
+
// start writing to stream now
|
|
189
|
+
this._writeOpenWorksheet();
|
|
190
|
+
|
|
191
|
+
this.startedData = false;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
get workbook() {
|
|
195
|
+
return this._workbook;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
get stream() {
|
|
199
|
+
if (!this._stream) {
|
|
200
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
201
|
+
this._stream = this._workbook._openStream(`/xl/worksheets/sheet${this.id}.xml`);
|
|
202
|
+
|
|
203
|
+
// pause stream to prevent 'data' events
|
|
204
|
+
this._stream.pause();
|
|
205
|
+
}
|
|
206
|
+
return this._stream;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// destroy - not a valid operation for a streaming writer
|
|
210
|
+
// even though some streamers might be able to, it's a bad idea.
|
|
211
|
+
destroy() {
|
|
212
|
+
throw new Error('Invalid Operation: destroy');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
commit() {
|
|
216
|
+
if (this.committed) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
// commit all rows
|
|
220
|
+
this._rows.forEach(cRow => {
|
|
221
|
+
if (cRow) {
|
|
222
|
+
// write the row to the stream
|
|
223
|
+
this._writeRow(cRow);
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// we _cannot_ accept new rows from now on
|
|
228
|
+
this._rows = null;
|
|
229
|
+
|
|
230
|
+
if (!this.startedData) {
|
|
231
|
+
this._writeOpenSheetData();
|
|
232
|
+
}
|
|
233
|
+
this._writeCloseSheetData();
|
|
234
|
+
this._writeAutoFilter();
|
|
235
|
+
this._writeMergeCells();
|
|
236
|
+
|
|
237
|
+
// for some reason, Excel can't handle dimensions at the bottom of the file
|
|
238
|
+
// this._writeDimensions();
|
|
239
|
+
|
|
240
|
+
this._writeHyperlinks();
|
|
241
|
+
this._writeConditionalFormatting();
|
|
242
|
+
this._writeDataValidations();
|
|
243
|
+
this._writeSheetProtection();
|
|
244
|
+
this._writePageMargins();
|
|
245
|
+
this._writePageSetup();
|
|
246
|
+
this._writeBackground();
|
|
247
|
+
this._writeHeaderFooter();
|
|
248
|
+
this._writeRowBreaks();
|
|
249
|
+
|
|
250
|
+
// Legacy Data tag for comments
|
|
251
|
+
this._writeLegacyData();
|
|
252
|
+
|
|
253
|
+
this._writeCloseWorksheet();
|
|
254
|
+
// signal end of stream to workbook
|
|
255
|
+
this.stream.end();
|
|
256
|
+
|
|
257
|
+
this._sheetCommentsWriter.commit();
|
|
258
|
+
// also commit the hyperlinks if any
|
|
259
|
+
this._sheetRelsWriter.commit();
|
|
260
|
+
|
|
261
|
+
this.committed = true;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// return the current dimensions of the writer
|
|
265
|
+
get dimensions() {
|
|
266
|
+
return this._dimensions;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
get views() {
|
|
270
|
+
return this._views;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// =========================================================================
|
|
274
|
+
// Columns
|
|
275
|
+
|
|
276
|
+
// get the current columns array.
|
|
277
|
+
get columns() {
|
|
278
|
+
return this._columns;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// set the columns from an array of column definitions.
|
|
282
|
+
// Note: any headers defined will overwrite existing values.
|
|
283
|
+
set columns(value) {
|
|
284
|
+
// calculate max header row count
|
|
285
|
+
this._headerRowCount = value.reduce((pv, cv) => {
|
|
286
|
+
const headerCount = (cv.header && 1) || (cv.headers && cv.headers.length) || 0;
|
|
287
|
+
return Math.max(pv, headerCount);
|
|
288
|
+
}, 0);
|
|
289
|
+
|
|
290
|
+
// construct Column objects
|
|
291
|
+
let count = 1;
|
|
292
|
+
const columns = (this._columns = []);
|
|
293
|
+
value.forEach(defn => {
|
|
294
|
+
const column = new Column(this, count++, false);
|
|
295
|
+
columns.push(column);
|
|
296
|
+
column.defn = defn;
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
getColumnKey(key) {
|
|
301
|
+
return this._keys[key];
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
setColumnKey(key, value) {
|
|
305
|
+
this._keys[key] = value;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
deleteColumnKey(key) {
|
|
309
|
+
delete this._keys[key];
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
eachColumnKey(f) {
|
|
313
|
+
_.each(this._keys, f);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// get a single column by col number. If it doesn't exist, it and any gaps before it
|
|
317
|
+
// are created.
|
|
318
|
+
getColumn(c) {
|
|
319
|
+
if (typeof c === 'string') {
|
|
320
|
+
// if it matches a key'd column, return that
|
|
321
|
+
const col = this._keys[c];
|
|
322
|
+
if (col) return col;
|
|
323
|
+
|
|
324
|
+
// otherwise, assume letter
|
|
325
|
+
c = colCache.l2n(c);
|
|
326
|
+
}
|
|
327
|
+
if (!this._columns) {
|
|
328
|
+
this._columns = [];
|
|
329
|
+
}
|
|
330
|
+
if (c > this._columns.length) {
|
|
331
|
+
let n = this._columns.length + 1;
|
|
332
|
+
while (n <= c) {
|
|
333
|
+
this._columns.push(new Column(this, n++));
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return this._columns[c - 1];
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// =========================================================================
|
|
340
|
+
// Rows
|
|
341
|
+
get _nextRow() {
|
|
342
|
+
return this._rowZero + this._rows.length;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// iterate over every uncommitted row in the worksheet, including maybe empty rows
|
|
346
|
+
eachRow(options, iteratee) {
|
|
347
|
+
if (!iteratee) {
|
|
348
|
+
iteratee = options;
|
|
349
|
+
options = undefined;
|
|
350
|
+
}
|
|
351
|
+
if (options && options.includeEmpty) {
|
|
352
|
+
const n = this._nextRow;
|
|
353
|
+
for (let i = this._rowZero; i < n; i++) {
|
|
354
|
+
iteratee(this.getRow(i), i);
|
|
355
|
+
}
|
|
356
|
+
} else {
|
|
357
|
+
this._rows.forEach(row => {
|
|
358
|
+
if (row.hasValues) {
|
|
359
|
+
iteratee(row, row.number);
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
_commitRow(cRow) {
|
|
366
|
+
// since rows must be written in order, we commit all rows up till and including cRow
|
|
367
|
+
let found = false;
|
|
368
|
+
while (this._rows.length && !found) {
|
|
369
|
+
const row = this._rows.shift();
|
|
370
|
+
this._rowZero++;
|
|
371
|
+
if (row) {
|
|
372
|
+
this._writeRow(row);
|
|
373
|
+
found = row.number === cRow.number;
|
|
374
|
+
this._rowZero = row.number + 1;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
get lastRow() {
|
|
380
|
+
// returns last uncommitted row
|
|
381
|
+
if (this._rows.length) {
|
|
382
|
+
return this._rows[this._rows.length - 1];
|
|
383
|
+
}
|
|
384
|
+
return undefined;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// find a row (if exists) by row number
|
|
388
|
+
findRow(rowNumber) {
|
|
389
|
+
const index = rowNumber - this._rowZero;
|
|
390
|
+
return this._rows[index];
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
getRow(rowNumber) {
|
|
394
|
+
const index = rowNumber - this._rowZero;
|
|
395
|
+
|
|
396
|
+
// may fail if rows have been comitted
|
|
397
|
+
if (index < 0) {
|
|
398
|
+
throw new Error('Out of bounds: this row has been committed');
|
|
399
|
+
}
|
|
400
|
+
let row = this._rows[index];
|
|
401
|
+
if (!row) {
|
|
402
|
+
this._rows[index] = row = new Row(this, rowNumber);
|
|
403
|
+
}
|
|
404
|
+
return row;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
addRow(value) {
|
|
408
|
+
const row = new Row(this, this._nextRow);
|
|
409
|
+
this._rows[row.number - this._rowZero] = row;
|
|
410
|
+
row.values = value;
|
|
411
|
+
return row;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// ================================================================================
|
|
415
|
+
// Cells
|
|
416
|
+
|
|
417
|
+
// returns the cell at [r,c] or address given by r. If not found, return undefined
|
|
418
|
+
findCell(r, c) {
|
|
419
|
+
const address = colCache.getAddress(r, c);
|
|
420
|
+
const row = this.findRow(address.row);
|
|
421
|
+
return row ? row.findCell(address.column) : undefined;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// return the cell at [r,c] or address given by r. If not found, create a new one.
|
|
425
|
+
getCell(r, c) {
|
|
426
|
+
const address = colCache.getAddress(r, c);
|
|
427
|
+
const row = this.getRow(address.row);
|
|
428
|
+
return row.getCellEx(address);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
mergeCells(...cells) {
|
|
432
|
+
// may fail if rows have been comitted
|
|
433
|
+
const dimensions = new Dimensions(cells);
|
|
434
|
+
|
|
435
|
+
// check cells aren't already merged
|
|
436
|
+
this._merges.forEach(merge => {
|
|
437
|
+
if (merge.intersects(dimensions)) {
|
|
438
|
+
throw new Error('Cannot merge already merged cells');
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
// apply merge
|
|
443
|
+
const master = this.getCell(dimensions.top, dimensions.left);
|
|
444
|
+
for (let i = dimensions.top; i <= dimensions.bottom; i++) {
|
|
445
|
+
for (let j = dimensions.left; j <= dimensions.right; j++) {
|
|
446
|
+
if (i > dimensions.top || j > dimensions.left) {
|
|
447
|
+
this.getCell(i, j).merge(master);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// index merge
|
|
453
|
+
this._merges.push(dimensions);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// ===========================================================================
|
|
457
|
+
// Conditional Formatting
|
|
458
|
+
addConditionalFormatting(cf) {
|
|
459
|
+
this.conditionalFormatting.push(cf);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
removeConditionalFormatting(filter) {
|
|
463
|
+
if (typeof filter === 'number') {
|
|
464
|
+
this.conditionalFormatting.splice(filter, 1);
|
|
465
|
+
} else if (filter instanceof Function) {
|
|
466
|
+
this.conditionalFormatting = this.conditionalFormatting.filter(filter);
|
|
467
|
+
} else {
|
|
468
|
+
this.conditionalFormatting = [];
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// =========================================================================
|
|
473
|
+
|
|
474
|
+
addBackgroundImage(imageId) {
|
|
475
|
+
this._background = {
|
|
476
|
+
imageId,
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
getBackgroundImageId() {
|
|
481
|
+
return this._background && this._background.imageId;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// =========================================================================
|
|
485
|
+
// Worksheet Protection
|
|
486
|
+
protect(password, options) {
|
|
487
|
+
// TODO: make this function truly async
|
|
488
|
+
// perhaps marshal to worker thread or something
|
|
489
|
+
return new Promise(resolve => {
|
|
490
|
+
this.sheetProtection = {
|
|
491
|
+
sheet: true,
|
|
492
|
+
};
|
|
493
|
+
if (options && 'spinCount' in options) {
|
|
494
|
+
// force spinCount to be integer >= 0
|
|
495
|
+
options.spinCount = Number.isFinite(options.spinCount) ? Math.round(Math.max(0, options.spinCount)) : 100000;
|
|
496
|
+
}
|
|
497
|
+
if (password) {
|
|
498
|
+
this.sheetProtection.algorithmName = 'SHA-512';
|
|
499
|
+
this.sheetProtection.saltValue = Encryptor.randomBytes(16).toString('base64');
|
|
500
|
+
this.sheetProtection.spinCount = options && 'spinCount' in options ? options.spinCount : 100000; // allow user specified spinCount
|
|
501
|
+
this.sheetProtection.hashValue = Encryptor.convertPasswordToHash(
|
|
502
|
+
password,
|
|
503
|
+
'SHA512',
|
|
504
|
+
this.sheetProtection.saltValue,
|
|
505
|
+
this.sheetProtection.spinCount
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
if (options) {
|
|
509
|
+
this.sheetProtection = Object.assign(this.sheetProtection, options);
|
|
510
|
+
if (!password && 'spinCount' in options) {
|
|
511
|
+
delete this.sheetProtection.spinCount;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
resolve();
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
unprotect() {
|
|
519
|
+
this.sheetProtection = null;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// ================================================================================
|
|
523
|
+
|
|
524
|
+
_write(text) {
|
|
525
|
+
xmlBuffer.reset();
|
|
526
|
+
xmlBuffer.addText(text);
|
|
527
|
+
this.stream.write(xmlBuffer);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
_writeSheetProperties(xmlBuf, properties, pageSetup) {
|
|
531
|
+
const sheetPropertiesModel = {
|
|
532
|
+
outlineProperties: properties && properties.outlineProperties,
|
|
533
|
+
tabColor: properties && properties.tabColor,
|
|
534
|
+
pageSetup:
|
|
535
|
+
pageSetup && pageSetup.fitToPage
|
|
536
|
+
? {
|
|
537
|
+
fitToPage: pageSetup.fitToPage,
|
|
538
|
+
}
|
|
539
|
+
: undefined,
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
xmlBuf.addText(xform.sheetProperties.toXml(sheetPropertiesModel));
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
_writeSheetFormatProperties(xmlBuf, properties) {
|
|
546
|
+
const sheetFormatPropertiesModel = properties
|
|
547
|
+
? {
|
|
548
|
+
defaultRowHeight: properties.defaultRowHeight,
|
|
549
|
+
dyDescent: properties.dyDescent,
|
|
550
|
+
outlineLevelCol: properties.outlineLevelCol,
|
|
551
|
+
outlineLevelRow: properties.outlineLevelRow,
|
|
552
|
+
}
|
|
553
|
+
: undefined;
|
|
554
|
+
if (properties.defaultColWidth) {
|
|
555
|
+
sheetFormatPropertiesModel.defaultColWidth = properties.defaultColWidth;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
xmlBuf.addText(xform.sheetFormatProperties.toXml(sheetFormatPropertiesModel));
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
_writeOpenWorksheet() {
|
|
562
|
+
xmlBuffer.reset();
|
|
563
|
+
|
|
564
|
+
xmlBuffer.addText('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>');
|
|
565
|
+
xmlBuffer.addText(
|
|
566
|
+
'<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"' +
|
|
567
|
+
' xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"' +
|
|
568
|
+
' xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"' +
|
|
569
|
+
' mc:Ignorable="x14ac"' +
|
|
570
|
+
' xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">'
|
|
571
|
+
);
|
|
572
|
+
|
|
573
|
+
this._writeSheetProperties(xmlBuffer, this.properties, this.pageSetup);
|
|
574
|
+
|
|
575
|
+
xmlBuffer.addText(xform.sheetViews.toXml(this.views));
|
|
576
|
+
|
|
577
|
+
this._writeSheetFormatProperties(xmlBuffer, this.properties);
|
|
578
|
+
|
|
579
|
+
this.stream.write(xmlBuffer);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
_writeColumns() {
|
|
583
|
+
const cols = Column.toModel(this.columns);
|
|
584
|
+
if (cols) {
|
|
585
|
+
xform.columns.prepare(cols, {styles: this._workbook.styles});
|
|
586
|
+
this.stream.write(xform.columns.toXml(cols));
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
_writeOpenSheetData() {
|
|
591
|
+
this._write('<sheetData>');
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
_writeRow(row) {
|
|
595
|
+
if (!this.startedData) {
|
|
596
|
+
this._writeColumns();
|
|
597
|
+
this._writeOpenSheetData();
|
|
598
|
+
this.startedData = true;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
if (row.hasValues || row.height) {
|
|
602
|
+
const {model} = row;
|
|
603
|
+
const options = {
|
|
604
|
+
styles: this._workbook.styles,
|
|
605
|
+
sharedStrings: this.useSharedStrings ? this._workbook.sharedStrings : undefined,
|
|
606
|
+
hyperlinks: this._sheetRelsWriter.hyperlinksProxy,
|
|
607
|
+
merges: this._merges,
|
|
608
|
+
formulae: this._formulae,
|
|
609
|
+
siFormulae: this._siFormulae,
|
|
610
|
+
comments: [],
|
|
611
|
+
};
|
|
612
|
+
xform.row.prepare(model, options);
|
|
613
|
+
this.stream.write(xform.row.toXml(model));
|
|
614
|
+
|
|
615
|
+
if (options.comments.length) {
|
|
616
|
+
this.hasComments = true;
|
|
617
|
+
this._sheetCommentsWriter.addComments(options.comments);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
_writeCloseSheetData() {
|
|
623
|
+
this._write('</sheetData>');
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
_writeMergeCells() {
|
|
627
|
+
if (this._merges.length) {
|
|
628
|
+
xmlBuffer.reset();
|
|
629
|
+
xmlBuffer.addText(`<mergeCells count="${this._merges.length}">`);
|
|
630
|
+
this._merges.forEach(merge => {
|
|
631
|
+
xmlBuffer.addText(`<mergeCell ref="${merge}"/>`);
|
|
632
|
+
});
|
|
633
|
+
xmlBuffer.addText('</mergeCells>');
|
|
634
|
+
|
|
635
|
+
this.stream.write(xmlBuffer);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
_writeHyperlinks() {
|
|
640
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
641
|
+
this.stream.write(xform.hyperlinks.toXml(this._sheetRelsWriter._hyperlinks));
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
_writeConditionalFormatting() {
|
|
645
|
+
const options = {
|
|
646
|
+
styles: this._workbook.styles,
|
|
647
|
+
};
|
|
648
|
+
xform.conditionalFormattings.prepare(this.conditionalFormatting, options);
|
|
649
|
+
this.stream.write(xform.conditionalFormattings.toXml(this.conditionalFormatting));
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
_writeRowBreaks() {
|
|
653
|
+
this.stream.write(xform.rowBreaks.toXml(this.rowBreaks));
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
_writeDataValidations() {
|
|
657
|
+
this.stream.write(xform.dataValidations.toXml(this.dataValidations.model));
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
_writeSheetProtection() {
|
|
661
|
+
this.stream.write(xform.sheetProtection.toXml(this.sheetProtection));
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
_writePageMargins() {
|
|
665
|
+
this.stream.write(xform.pageMargins.toXml(this.pageSetup.margins));
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
_writePageSetup() {
|
|
669
|
+
this.stream.write(xform.pageSeteup.toXml(this.pageSetup));
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
_writeHeaderFooter() {
|
|
673
|
+
this.stream.write(xform.headerFooter.toXml(this.headerFooter));
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
_writeAutoFilter() {
|
|
677
|
+
this.stream.write(xform.autoFilter.toXml(this.autoFilter));
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
_writeBackground() {
|
|
681
|
+
if (this._background) {
|
|
682
|
+
if (this._background.imageId !== undefined) {
|
|
683
|
+
const image = this._workbook.getImage(this._background.imageId);
|
|
684
|
+
const pictureId = this._sheetRelsWriter.addMedia({
|
|
685
|
+
Target: `../media/${image.name}`,
|
|
686
|
+
Type: RelType.Image,
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
this._background = {
|
|
690
|
+
...this._background,
|
|
691
|
+
rId: pictureId,
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
this.stream.write(xform.picture.toXml({rId: this._background.rId}));
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
_writeLegacyData() {
|
|
699
|
+
if (this.hasComments) {
|
|
700
|
+
xmlBuffer.reset();
|
|
701
|
+
xmlBuffer.addText(`<legacyDrawing r:id="${this._sheetCommentsWriter.vmlRelId}"/>`);
|
|
702
|
+
this.stream.write(xmlBuffer);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
_writeDimensions() {
|
|
707
|
+
// for some reason, Excel can't handle dimensions at the bottom of the file
|
|
708
|
+
// and we don't know the dimensions until the commit, so don't write them.
|
|
709
|
+
// this._write('<dimension ref="' + this._dimensions + '"/>');
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
_writeCloseWorksheet() {
|
|
713
|
+
this._write('</worksheet>');
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
module.exports = WorksheetWriter;
|