@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.
Files changed (185) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +3024 -0
  3. package/README_zh.md +2878 -0
  4. package/excel.js +13 -0
  5. package/index.d.ts +2040 -0
  6. package/index.ts +2 -0
  7. package/lib/csv/csv.js +191 -0
  8. package/lib/csv/line-buffer.js +74 -0
  9. package/lib/csv/stream-converter.js +135 -0
  10. package/lib/doc/anchor.js +91 -0
  11. package/lib/doc/cell.js +1124 -0
  12. package/lib/doc/column.js +320 -0
  13. package/lib/doc/data/theme1.json +234 -0
  14. package/lib/doc/data-validations.js +19 -0
  15. package/lib/doc/defined-names.js +196 -0
  16. package/lib/doc/enums.js +48 -0
  17. package/lib/doc/image.js +59 -0
  18. package/lib/doc/modelcontainer.js +18 -0
  19. package/lib/doc/note.js +65 -0
  20. package/lib/doc/pivot-table.js +132 -0
  21. package/lib/doc/range.js +257 -0
  22. package/lib/doc/row.js +415 -0
  23. package/lib/doc/table.js +465 -0
  24. package/lib/doc/workbook.js +224 -0
  25. package/lib/doc/worksheet.js +949 -0
  26. package/lib/exceljs.bare.js +13 -0
  27. package/lib/exceljs.browser.js +36 -0
  28. package/lib/exceljs.nodejs.js +14 -0
  29. package/lib/stream/xlsx/hyperlink-reader.js +83 -0
  30. package/lib/stream/xlsx/sheet-comments-writer.js +121 -0
  31. package/lib/stream/xlsx/sheet-rels-writer.js +119 -0
  32. package/lib/stream/xlsx/workbook-reader.js +337 -0
  33. package/lib/stream/xlsx/workbook-writer.js +347 -0
  34. package/lib/stream/xlsx/worksheet-reader.js +374 -0
  35. package/lib/stream/xlsx/worksheet-writer.js +717 -0
  36. package/lib/utils/auto-drain.js +15 -0
  37. package/lib/utils/browser-buffer-decode.js +14 -0
  38. package/lib/utils/browser-buffer-encode.js +15 -0
  39. package/lib/utils/cell-matrix.js +165 -0
  40. package/lib/utils/col-cache.js +287 -0
  41. package/lib/utils/copy-style.js +43 -0
  42. package/lib/utils/encryptor.js +55 -0
  43. package/lib/utils/iterate-stream.js +48 -0
  44. package/lib/utils/parse-sax.js +30 -0
  45. package/lib/utils/shared-formula.js +44 -0
  46. package/lib/utils/shared-strings.js +35 -0
  47. package/lib/utils/stream-base64.js +72 -0
  48. package/lib/utils/stream-buf.js +364 -0
  49. package/lib/utils/string-buf.js +82 -0
  50. package/lib/utils/string-builder.js +35 -0
  51. package/lib/utils/stuttered-pipe.js +67 -0
  52. package/lib/utils/typed-stack.js +24 -0
  53. package/lib/utils/under-dash.js +184 -0
  54. package/lib/utils/utils.js +205 -0
  55. package/lib/utils/xml-stream.js +169 -0
  56. package/lib/utils/zip-stream.js +87 -0
  57. package/lib/xlsx/.rels +11 -0
  58. package/lib/xlsx/calcChain.xml +6 -0
  59. package/lib/xlsx/core.xml +7 -0
  60. package/lib/xlsx/defaultnumformats.js +153 -0
  61. package/lib/xlsx/rel-type.js +20 -0
  62. package/lib/xlsx/styles.xml +41 -0
  63. package/lib/xlsx/workbook.xml +16 -0
  64. package/lib/xlsx/xform/base-xform.js +145 -0
  65. package/lib/xlsx/xform/book/defined-name-xform.js +91 -0
  66. package/lib/xlsx/xform/book/sheet-xform.js +34 -0
  67. package/lib/xlsx/xform/book/workbook-calc-properties-xform.js +26 -0
  68. package/lib/xlsx/xform/book/workbook-pivot-cache-xform.js +29 -0
  69. package/lib/xlsx/xform/book/workbook-properties-xform.js +29 -0
  70. package/lib/xlsx/xform/book/workbook-view-xform.js +53 -0
  71. package/lib/xlsx/xform/book/workbook-xform.js +259 -0
  72. package/lib/xlsx/xform/comment/comment-xform.js +105 -0
  73. package/lib/xlsx/xform/comment/comments-xform.js +82 -0
  74. package/lib/xlsx/xform/comment/style/vml-position-xform.js +39 -0
  75. package/lib/xlsx/xform/comment/style/vml-protection-xform.js +36 -0
  76. package/lib/xlsx/xform/comment/vml-anchor-xform.js +60 -0
  77. package/lib/xlsx/xform/comment/vml-client-data-xform.js +95 -0
  78. package/lib/xlsx/xform/comment/vml-notes-xform.js +107 -0
  79. package/lib/xlsx/xform/comment/vml-shape-xform.js +95 -0
  80. package/lib/xlsx/xform/comment/vml-textbox-xform.js +64 -0
  81. package/lib/xlsx/xform/composite-xform.js +56 -0
  82. package/lib/xlsx/xform/core/app-heading-pairs-xform.js +32 -0
  83. package/lib/xlsx/xform/core/app-titles-of-parts-xform.js +28 -0
  84. package/lib/xlsx/xform/core/app-xform.js +100 -0
  85. package/lib/xlsx/xform/core/content-types-xform.js +135 -0
  86. package/lib/xlsx/xform/core/core-xform.js +136 -0
  87. package/lib/xlsx/xform/core/relationship-xform.js +25 -0
  88. package/lib/xlsx/xform/core/relationships-xform.js +73 -0
  89. package/lib/xlsx/xform/drawing/base-cell-anchor-xform.js +48 -0
  90. package/lib/xlsx/xform/drawing/blip-fill-xform.js +71 -0
  91. package/lib/xlsx/xform/drawing/blip-xform.js +42 -0
  92. package/lib/xlsx/xform/drawing/c-nv-pic-pr-xform.js +38 -0
  93. package/lib/xlsx/xform/drawing/c-nv-pr-xform.js +68 -0
  94. package/lib/xlsx/xform/drawing/cell-position-xform.js +77 -0
  95. package/lib/xlsx/xform/drawing/drawing-xform.js +109 -0
  96. package/lib/xlsx/xform/drawing/ext-lst-xform.js +43 -0
  97. package/lib/xlsx/xform/drawing/ext-xform.js +44 -0
  98. package/lib/xlsx/xform/drawing/hlink-click-xform.js +41 -0
  99. package/lib/xlsx/xform/drawing/nv-pic-pr-xform.js +65 -0
  100. package/lib/xlsx/xform/drawing/one-cell-anchor-xform.js +63 -0
  101. package/lib/xlsx/xform/drawing/pic-xform.js +77 -0
  102. package/lib/xlsx/xform/drawing/sp-pr.js +17 -0
  103. package/lib/xlsx/xform/drawing/two-cell-anchor-xform.js +62 -0
  104. package/lib/xlsx/xform/list-xform.js +95 -0
  105. package/lib/xlsx/xform/pivot-table/cache-field.js +43 -0
  106. package/lib/xlsx/xform/pivot-table/pivot-cache-definition-xform.js +77 -0
  107. package/lib/xlsx/xform/pivot-table/pivot-cache-records-xform.js +103 -0
  108. package/lib/xlsx/xform/pivot-table/pivot-table-xform.js +189 -0
  109. package/lib/xlsx/xform/sheet/auto-filter-xform.js +38 -0
  110. package/lib/xlsx/xform/sheet/cell-xform.js +498 -0
  111. package/lib/xlsx/xform/sheet/cf/cf-rule-xform.js +301 -0
  112. package/lib/xlsx/xform/sheet/cf/cfvo-xform.js +27 -0
  113. package/lib/xlsx/xform/sheet/cf/color-scale-xform.js +45 -0
  114. package/lib/xlsx/xform/sheet/cf/conditional-formatting-xform.js +48 -0
  115. package/lib/xlsx/xform/sheet/cf/conditional-formattings-xform.js +92 -0
  116. package/lib/xlsx/xform/sheet/cf/databar-xform.js +49 -0
  117. package/lib/xlsx/xform/sheet/cf/ext-lst-ref-xform.js +87 -0
  118. package/lib/xlsx/xform/sheet/cf/formula-xform.js +25 -0
  119. package/lib/xlsx/xform/sheet/cf/icon-set-xform.js +47 -0
  120. package/lib/xlsx/xform/sheet/cf-ext/cf-icon-ext-xform.js +27 -0
  121. package/lib/xlsx/xform/sheet/cf-ext/cf-rule-ext-xform.js +98 -0
  122. package/lib/xlsx/xform/sheet/cf-ext/cfvo-ext-xform.js +43 -0
  123. package/lib/xlsx/xform/sheet/cf-ext/conditional-formatting-ext-xform.js +62 -0
  124. package/lib/xlsx/xform/sheet/cf-ext/conditional-formattings-ext-xform.js +50 -0
  125. package/lib/xlsx/xform/sheet/cf-ext/databar-ext-xform.js +98 -0
  126. package/lib/xlsx/xform/sheet/cf-ext/f-ext-xform.js +25 -0
  127. package/lib/xlsx/xform/sheet/cf-ext/icon-set-ext-xform.js +73 -0
  128. package/lib/xlsx/xform/sheet/cf-ext/sqref-ext-xform.js +25 -0
  129. package/lib/xlsx/xform/sheet/col-xform.js +86 -0
  130. package/lib/xlsx/xform/sheet/data-validations-xform.js +257 -0
  131. package/lib/xlsx/xform/sheet/dimension-xform.js +29 -0
  132. package/lib/xlsx/xform/sheet/drawing-xform.js +33 -0
  133. package/lib/xlsx/xform/sheet/ext-lst-xform.js +86 -0
  134. package/lib/xlsx/xform/sheet/header-footer-xform.js +146 -0
  135. package/lib/xlsx/xform/sheet/hyperlink-xform.js +54 -0
  136. package/lib/xlsx/xform/sheet/merge-cell-xform.js +27 -0
  137. package/lib/xlsx/xform/sheet/merges.js +56 -0
  138. package/lib/xlsx/xform/sheet/outline-properties-xform.js +43 -0
  139. package/lib/xlsx/xform/sheet/page-breaks-xform.js +27 -0
  140. package/lib/xlsx/xform/sheet/page-margins-xform.js +49 -0
  141. package/lib/xlsx/xform/sheet/page-setup-properties-xform.js +35 -0
  142. package/lib/xlsx/xform/sheet/page-setup-xform.js +103 -0
  143. package/lib/xlsx/xform/sheet/picture-xform.js +33 -0
  144. package/lib/xlsx/xform/sheet/print-options-xform.js +49 -0
  145. package/lib/xlsx/xform/sheet/row-breaks-xform.js +39 -0
  146. package/lib/xlsx/xform/sheet/row-xform.js +142 -0
  147. package/lib/xlsx/xform/sheet/sheet-format-properties-xform.js +55 -0
  148. package/lib/xlsx/xform/sheet/sheet-properties-xform.js +90 -0
  149. package/lib/xlsx/xform/sheet/sheet-protection-xform.js +89 -0
  150. package/lib/xlsx/xform/sheet/sheet-view-xform.js +202 -0
  151. package/lib/xlsx/xform/sheet/table-part-xform.js +33 -0
  152. package/lib/xlsx/xform/sheet/worksheet-xform.js +548 -0
  153. package/lib/xlsx/xform/simple/boolean-xform.js +31 -0
  154. package/lib/xlsx/xform/simple/date-xform.js +66 -0
  155. package/lib/xlsx/xform/simple/float-xform.js +51 -0
  156. package/lib/xlsx/xform/simple/integer-xform.js +57 -0
  157. package/lib/xlsx/xform/simple/string-xform.js +51 -0
  158. package/lib/xlsx/xform/static-xform.js +64 -0
  159. package/lib/xlsx/xform/strings/phonetic-text-xform.js +98 -0
  160. package/lib/xlsx/xform/strings/rich-text-xform.js +101 -0
  161. package/lib/xlsx/xform/strings/shared-string-xform.js +102 -0
  162. package/lib/xlsx/xform/strings/shared-strings-xform.js +127 -0
  163. package/lib/xlsx/xform/strings/text-xform.js +44 -0
  164. package/lib/xlsx/xform/style/alignment-xform.js +172 -0
  165. package/lib/xlsx/xform/style/border-xform.js +207 -0
  166. package/lib/xlsx/xform/style/color-xform.js +63 -0
  167. package/lib/xlsx/xform/style/dxf-xform.js +111 -0
  168. package/lib/xlsx/xform/style/fill-xform.js +364 -0
  169. package/lib/xlsx/xform/style/font-xform.js +102 -0
  170. package/lib/xlsx/xform/style/numfmt-xform.js +63 -0
  171. package/lib/xlsx/xform/style/protection-xform.js +60 -0
  172. package/lib/xlsx/xform/style/style-xform.js +125 -0
  173. package/lib/xlsx/xform/style/styles-xform.js +527 -0
  174. package/lib/xlsx/xform/style/underline-xform.js +47 -0
  175. package/lib/xlsx/xform/table/auto-filter-xform.js +81 -0
  176. package/lib/xlsx/xform/table/custom-filter-xform.js +33 -0
  177. package/lib/xlsx/xform/table/filter-column-xform.js +96 -0
  178. package/lib/xlsx/xform/table/filter-xform.js +31 -0
  179. package/lib/xlsx/xform/table/table-column-xform.js +44 -0
  180. package/lib/xlsx/xform/table/table-style-info-xform.js +41 -0
  181. package/lib/xlsx/xform/table/table-xform.js +131 -0
  182. package/lib/xlsx/xlsx.js +774 -0
  183. package/lib/xlsx/xml/theme1.js +3 -0
  184. package/lib/xlsx/xml/theme1.xml +318 -0
  185. 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;