@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,527 @@
1
+ /* eslint-disable max-classes-per-file */
2
+ const Enums = require('../../../doc/enums');
3
+ const XmlStream = require('../../../utils/xml-stream');
4
+
5
+ const BaseXform = require('../base-xform');
6
+ const StaticXform = require('../static-xform');
7
+ const ListXform = require('../list-xform');
8
+ const FontXform = require('./font-xform');
9
+ const FillXform = require('./fill-xform');
10
+ const BorderXform = require('./border-xform');
11
+ const NumFmtXform = require('./numfmt-xform');
12
+ const StyleXform = require('./style-xform');
13
+ const DxfXform = require('./dxf-xform');
14
+
15
+ // custom numfmt ids start here
16
+ const NUMFMT_BASE = 164;
17
+
18
+ // =============================================================================
19
+ // StylesXform is used to generate and parse the styles.xml file
20
+ // it manages the collections of fonts, number formats, alignments, etc
21
+ class StylesXform extends BaseXform {
22
+ constructor(initialise) {
23
+ super();
24
+
25
+ this.map = {
26
+ numFmts: new ListXform({tag: 'numFmts', count: true, childXform: new NumFmtXform()}),
27
+ fonts: new ListXform({
28
+ tag: 'fonts',
29
+ count: true,
30
+ childXform: new FontXform(),
31
+ $: {'x14ac:knownFonts': 1},
32
+ }),
33
+ fills: new ListXform({tag: 'fills', count: true, childXform: new FillXform()}),
34
+ borders: new ListXform({tag: 'borders', count: true, childXform: new BorderXform()}),
35
+ cellStyleXfs: new ListXform({tag: 'cellStyleXfs', count: true, childXform: new StyleXform()}),
36
+ cellXfs: new ListXform({
37
+ tag: 'cellXfs',
38
+ count: true,
39
+ childXform: new StyleXform({xfId: true}),
40
+ }),
41
+ dxfs: new ListXform({tag: 'dxfs', always: true, count: true, childXform: new DxfXform()}),
42
+
43
+ // for style manager
44
+ numFmt: new NumFmtXform(),
45
+ font: new FontXform(),
46
+ fill: new FillXform(),
47
+ border: new BorderXform(),
48
+ style: new StyleXform({xfId: true}),
49
+
50
+ cellStyles: StylesXform.STATIC_XFORMS.cellStyles,
51
+ tableStyles: StylesXform.STATIC_XFORMS.tableStyles,
52
+ extLst: StylesXform.STATIC_XFORMS.extLst,
53
+ };
54
+
55
+ if (initialise) {
56
+ // StylesXform also acts as style manager and is used to build up styles-model during worksheet processing
57
+ this.init();
58
+ }
59
+ }
60
+
61
+ initIndex() {
62
+ this.index = {
63
+ style: {},
64
+ numFmt: {},
65
+ numFmtNextId: 164, // start custom format ids here
66
+ font: {},
67
+ border: {},
68
+ fill: {},
69
+ };
70
+ }
71
+
72
+ init() {
73
+ // Prepare for Style Manager role
74
+ this.model = {
75
+ styles: [],
76
+ numFmts: [],
77
+ fonts: [],
78
+ borders: [],
79
+ fills: [],
80
+ dxfs: [],
81
+ };
82
+
83
+ this.initIndex();
84
+
85
+ // default (zero) border
86
+ this._addBorder({});
87
+
88
+ // add default (all zero) style
89
+ this._addStyle({numFmtId: 0, fontId: 0, fillId: 0, borderId: 0, xfId: 0});
90
+
91
+ // add default fills
92
+ this._addFill({type: 'pattern', pattern: 'none'});
93
+ this._addFill({type: 'pattern', pattern: 'gray125'});
94
+
95
+ this.weakMap = new WeakMap();
96
+ }
97
+
98
+ render(xmlStream, model) {
99
+ model = model || this.model;
100
+ //
101
+ // <fonts count="2" x14ac:knownFonts="1">
102
+ xmlStream.openXml(XmlStream.StdDocAttributes);
103
+
104
+ xmlStream.openNode('styleSheet', StylesXform.STYLESHEET_ATTRIBUTES);
105
+
106
+ if (this.index) {
107
+ // model has been built by style manager role (contains xml)
108
+ if (model.numFmts && model.numFmts.length) {
109
+ xmlStream.openNode('numFmts', {count: model.numFmts.length});
110
+ model.numFmts.forEach(numFmtXml => {
111
+ xmlStream.writeXml(numFmtXml);
112
+ });
113
+ xmlStream.closeNode();
114
+ }
115
+
116
+ if (!model.fonts.length) {
117
+ // default (zero) font
118
+ this._addFont({size: 11, color: {theme: 1}, name: 'Calibri', family: 2, scheme: 'minor'});
119
+ }
120
+ xmlStream.openNode('fonts', {count: model.fonts.length, 'x14ac:knownFonts': 1});
121
+ model.fonts.forEach(fontXml => {
122
+ xmlStream.writeXml(fontXml);
123
+ });
124
+ xmlStream.closeNode();
125
+
126
+ xmlStream.openNode('fills', {count: model.fills.length});
127
+ model.fills.forEach(fillXml => {
128
+ xmlStream.writeXml(fillXml);
129
+ });
130
+ xmlStream.closeNode();
131
+
132
+ xmlStream.openNode('borders', {count: model.borders.length});
133
+ model.borders.forEach(borderXml => {
134
+ xmlStream.writeXml(borderXml);
135
+ });
136
+ xmlStream.closeNode();
137
+
138
+ this.map.cellStyleXfs.render(xmlStream, [{numFmtId: 0, fontId: 0, fillId: 0, borderId: 0, xfId: 0}]);
139
+
140
+ xmlStream.openNode('cellXfs', {count: model.styles.length});
141
+ model.styles.forEach(styleXml => {
142
+ xmlStream.writeXml(styleXml);
143
+ });
144
+ xmlStream.closeNode();
145
+ } else {
146
+ // model is plain JSON and needs to be xformed
147
+ this.map.numFmts.render(xmlStream, model.numFmts);
148
+ this.map.fonts.render(xmlStream, model.fonts);
149
+ this.map.fills.render(xmlStream, model.fills);
150
+ this.map.borders.render(xmlStream, model.borders);
151
+ this.map.cellStyleXfs.render(xmlStream, [{numFmtId: 0, fontId: 0, fillId: 0, borderId: 0, xfId: 0}]);
152
+ this.map.cellXfs.render(xmlStream, model.styles);
153
+ }
154
+
155
+ StylesXform.STATIC_XFORMS.cellStyles.render(xmlStream);
156
+
157
+ this.map.dxfs.render(xmlStream, model.dxfs);
158
+
159
+ StylesXform.STATIC_XFORMS.tableStyles.render(xmlStream);
160
+ StylesXform.STATIC_XFORMS.extLst.render(xmlStream);
161
+
162
+ xmlStream.closeNode();
163
+ }
164
+
165
+ parseOpen(node) {
166
+ if (this.parser) {
167
+ this.parser.parseOpen(node);
168
+ return true;
169
+ }
170
+ switch (node.name) {
171
+ case 'styleSheet':
172
+ this.initIndex();
173
+ return true;
174
+ default:
175
+ this.parser = this.map[node.name];
176
+ if (this.parser) {
177
+ this.parser.parseOpen(node);
178
+ }
179
+ return true;
180
+ }
181
+ }
182
+
183
+ parseText(text) {
184
+ if (this.parser) {
185
+ this.parser.parseText(text);
186
+ }
187
+ }
188
+
189
+ parseClose(name) {
190
+ if (this.parser) {
191
+ if (!this.parser.parseClose(name)) {
192
+ this.parser = undefined;
193
+ }
194
+ return true;
195
+ }
196
+ switch (name) {
197
+ case 'styleSheet': {
198
+ this.model = {};
199
+ const add = (propName, xform) => {
200
+ if (xform.model && xform.model.length) {
201
+ this.model[propName] = xform.model;
202
+ }
203
+ };
204
+ add('numFmts', this.map.numFmts);
205
+ add('fonts', this.map.fonts);
206
+ add('fills', this.map.fills);
207
+ add('borders', this.map.borders);
208
+ add('styles', this.map.cellXfs);
209
+ add('dxfs', this.map.dxfs);
210
+
211
+ // index numFmts
212
+ this.index = {
213
+ model: [],
214
+ numFmt: [],
215
+ };
216
+ if (this.model.numFmts) {
217
+ const numFmtIndex = this.index.numFmt;
218
+ this.model.numFmts.forEach(numFmt => {
219
+ numFmtIndex[numFmt.id] = numFmt.formatCode;
220
+ });
221
+ }
222
+
223
+ return false;
224
+ }
225
+ default:
226
+ // not quite sure how we get here!
227
+ return true;
228
+ }
229
+ }
230
+
231
+ // add a cell's style model to the collection
232
+ // each style property is processed and cross-referenced, etc.
233
+ // the styleId is returned. Note: cellType is used when numFmt not defined
234
+ addStyleModel(model, cellType) {
235
+ if (!model) {
236
+ return 0;
237
+ }
238
+
239
+ // if we have no default font, add it here now
240
+ if (!this.model.fonts.length) {
241
+ // default (zero) font
242
+ this._addFont({size: 11, color: {theme: 1}, name: 'Calibri', family: 2, scheme: 'minor'});
243
+ }
244
+
245
+ // if we have seen this style object before, assume it has the same styleId
246
+ if (this.weakMap && this.weakMap.has(model)) {
247
+ return this.weakMap.get(model);
248
+ }
249
+
250
+ const style = {};
251
+ cellType = cellType || Enums.ValueType.Number;
252
+
253
+ if (model.numFmt) {
254
+ style.numFmtId = this._addNumFmtStr(model.numFmt);
255
+ } else {
256
+ switch (cellType) {
257
+ case Enums.ValueType.Number:
258
+ style.numFmtId = this._addNumFmtStr('General');
259
+ break;
260
+ case Enums.ValueType.Date:
261
+ style.numFmtId = this._addNumFmtStr('mm-dd-yy');
262
+ break;
263
+ default:
264
+ break;
265
+ }
266
+ }
267
+
268
+ if (model.font) {
269
+ style.fontId = this._addFont(model.font);
270
+ }
271
+
272
+ if (model.border) {
273
+ style.borderId = this._addBorder(model.border);
274
+ }
275
+
276
+ if (model.fill) {
277
+ style.fillId = this._addFill(model.fill);
278
+ }
279
+
280
+ if (model.alignment) {
281
+ style.alignment = model.alignment;
282
+ }
283
+
284
+ if (model.protection) {
285
+ style.protection = model.protection;
286
+ }
287
+
288
+ const styleId = this._addStyle(style);
289
+ if (this.weakMap) {
290
+ this.weakMap.set(model, styleId);
291
+ }
292
+ return styleId;
293
+ }
294
+
295
+ // given a styleId (i.e. s="n"), get the cell's style model
296
+ // objects are shared where possible.
297
+ getStyleModel(id) {
298
+ // if the style doesn't exist return null
299
+ const style = this.model.styles[id];
300
+ if (!style) return null;
301
+
302
+ // have we built this model before?
303
+ let model = this.index.model[id];
304
+ if (model) return model;
305
+
306
+ // build a new model
307
+ model = this.index.model[id] = {};
308
+
309
+ // -------------------------------------------------------
310
+ // number format
311
+ if (style.numFmtId) {
312
+ const numFmt = this.index.numFmt[style.numFmtId] || NumFmtXform.getDefaultFmtCode(style.numFmtId);
313
+ if (numFmt) {
314
+ model.numFmt = numFmt;
315
+ }
316
+ }
317
+
318
+ function addStyle(name, group, styleId) {
319
+ if (styleId || styleId === 0) {
320
+ const part = group[styleId];
321
+ if (part) {
322
+ model[name] = part;
323
+ }
324
+ }
325
+ }
326
+
327
+ addStyle('font', this.model.fonts, style.fontId);
328
+ addStyle('border', this.model.borders, style.borderId);
329
+ addStyle('fill', this.model.fills, style.fillId);
330
+
331
+ // -------------------------------------------------------
332
+ // alignment
333
+ if (style.alignment) {
334
+ model.alignment = style.alignment;
335
+ }
336
+
337
+ // -------------------------------------------------------
338
+ // protection
339
+ if (style.protection) {
340
+ model.protection = style.protection;
341
+ }
342
+
343
+ return model;
344
+ }
345
+
346
+ addDxfStyle(style) {
347
+ if (style.numFmt) {
348
+ // register numFmtId to use it during dxf-xform rendering
349
+ style.numFmtId = this._addNumFmtStr(style.numFmt);
350
+ }
351
+
352
+ this.model.dxfs.push(style);
353
+ return this.model.dxfs.length - 1;
354
+ }
355
+
356
+ getDxfStyle(id) {
357
+ return this.model.dxfs[id];
358
+ }
359
+
360
+ // =========================================================================
361
+ // Private Interface
362
+ _addStyle(style) {
363
+ const xml = this.map.style.toXml(style);
364
+ let index = this.index.style[xml];
365
+ if (index === undefined) {
366
+ index = this.index.style[xml] = this.model.styles.length;
367
+ this.model.styles.push(xml);
368
+ }
369
+ return index;
370
+ }
371
+
372
+ // =========================================================================
373
+ // Number Formats
374
+ _addNumFmtStr(formatCode) {
375
+ // check if default format
376
+ let index = NumFmtXform.getDefaultFmtId(formatCode);
377
+ if (index !== undefined) return index;
378
+
379
+ // check if already in
380
+ index = this.index.numFmt[formatCode];
381
+ if (index !== undefined) return index;
382
+
383
+ index = this.index.numFmt[formatCode] = NUMFMT_BASE + this.model.numFmts.length;
384
+ const xml = this.map.numFmt.toXml({id: index, formatCode});
385
+ this.model.numFmts.push(xml);
386
+ return index;
387
+ }
388
+
389
+ // =========================================================================
390
+ // Fonts
391
+ _addFont(font) {
392
+ const xml = this.map.font.toXml(font);
393
+ let index = this.index.font[xml];
394
+ if (index === undefined) {
395
+ index = this.index.font[xml] = this.model.fonts.length;
396
+ this.model.fonts.push(xml);
397
+ }
398
+ return index;
399
+ }
400
+
401
+ // =========================================================================
402
+ // Borders
403
+ _addBorder(border) {
404
+ const xml = this.map.border.toXml(border);
405
+ let index = this.index.border[xml];
406
+ if (index === undefined) {
407
+ index = this.index.border[xml] = this.model.borders.length;
408
+ this.model.borders.push(xml);
409
+ }
410
+ return index;
411
+ }
412
+
413
+ // =========================================================================
414
+ // Fills
415
+ _addFill(fill) {
416
+ const xml = this.map.fill.toXml(fill);
417
+ let index = this.index.fill[xml];
418
+ if (index === undefined) {
419
+ index = this.index.fill[xml] = this.model.fills.length;
420
+ this.model.fills.push(xml);
421
+ }
422
+ return index;
423
+ }
424
+
425
+ // =========================================================================
426
+ }
427
+
428
+ StylesXform.STYLESHEET_ATTRIBUTES = {
429
+ xmlns: 'http://schemas.openxmlformats.org/spreadsheetml/2006/main',
430
+ 'xmlns:mc': 'http://schemas.openxmlformats.org/markup-compatibility/2006',
431
+ 'mc:Ignorable': 'x14ac x16r2',
432
+ 'xmlns:x14ac': 'http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac',
433
+ 'xmlns:x16r2': 'http://schemas.microsoft.com/office/spreadsheetml/2015/02/main',
434
+ };
435
+ StylesXform.STATIC_XFORMS = {
436
+ cellStyles: new StaticXform({
437
+ tag: 'cellStyles',
438
+ $: {count: 1},
439
+ c: [{tag: 'cellStyle', $: {name: 'Normal', xfId: 0, builtinId: 0}}],
440
+ }),
441
+ dxfs: new StaticXform({tag: 'dxfs', $: {count: 0}}),
442
+ tableStyles: new StaticXform({
443
+ tag: 'tableStyles',
444
+ $: {count: 0, defaultTableStyle: 'TableStyleMedium2', defaultPivotStyle: 'PivotStyleLight16'},
445
+ }),
446
+ extLst: new StaticXform({
447
+ tag: 'extLst',
448
+ c: [
449
+ {
450
+ tag: 'ext',
451
+ $: {
452
+ uri: '{EB79DEF2-80B8-43e5-95BD-54CBDDF9020C}',
453
+ 'xmlns:x14': 'http://schemas.microsoft.com/office/spreadsheetml/2009/9/main',
454
+ },
455
+ c: [{tag: 'x14:slicerStyles', $: {defaultSlicerStyle: 'SlicerStyleLight1'}}],
456
+ },
457
+ {
458
+ tag: 'ext',
459
+ $: {
460
+ uri: '{9260A510-F301-46a8-8635-F512D64BE5F5}',
461
+ 'xmlns:x15': 'http://schemas.microsoft.com/office/spreadsheetml/2010/11/main',
462
+ },
463
+ c: [{tag: 'x15:timelineStyles', $: {defaultTimelineStyle: 'TimeSlicerStyleLight1'}}],
464
+ },
465
+ ],
466
+ }),
467
+ };
468
+
469
+ // the stylemanager mock acts like StyleManager except that it always returns 0 or {}
470
+ class StylesXformMock extends StylesXform {
471
+ constructor() {
472
+ super();
473
+
474
+ this.model = {
475
+ styles: [{numFmtId: 0, fontId: 0, fillId: 0, borderId: 0, xfId: 0}],
476
+ numFmts: [],
477
+ fonts: [{size: 11, color: {theme: 1}, name: 'Calibri', family: 2, scheme: 'minor'}],
478
+ borders: [{}],
479
+ fills: [
480
+ {type: 'pattern', pattern: 'none'},
481
+ {type: 'pattern', pattern: 'gray125'},
482
+ ],
483
+ };
484
+ }
485
+
486
+ // =========================================================================
487
+ // Style Manager Interface
488
+
489
+ // override normal behaviour - consume and dispose
490
+ parseStream(stream) {
491
+ stream.autodrain();
492
+ return Promise.resolve();
493
+ }
494
+
495
+ // add a cell's style model to the collection
496
+ // each style property is processed and cross-referenced, etc.
497
+ // the styleId is returned. Note: cellType is used when numFmt not defined
498
+ addStyleModel(model, cellType) {
499
+ switch (cellType) {
500
+ case Enums.ValueType.Date:
501
+ return this.dateStyleId;
502
+ default:
503
+ return 0;
504
+ }
505
+ }
506
+
507
+ get dateStyleId() {
508
+ if (!this._dateStyleId) {
509
+ const dateStyle = {
510
+ numFmtId: NumFmtXform.getDefaultFmtId('mm-dd-yy'),
511
+ };
512
+ this._dateStyleId = this.model.styles.length;
513
+ this.model.styles.push(dateStyle);
514
+ }
515
+ return this._dateStyleId;
516
+ }
517
+
518
+ // given a styleId (i.e. s="n"), get the cell's style model
519
+ // objects are shared where possible.
520
+ getStyleModel(/* id */) {
521
+ return {};
522
+ }
523
+ }
524
+
525
+ StylesXform.Mock = StylesXformMock;
526
+
527
+ module.exports = StylesXform;
@@ -0,0 +1,47 @@
1
+ const BaseXform = require('../base-xform');
2
+
3
+ class UnderlineXform extends BaseXform {
4
+ constructor(model) {
5
+ super();
6
+
7
+ this.model = model;
8
+ }
9
+
10
+ get tag() {
11
+ return 'u';
12
+ }
13
+
14
+ render(xmlStream, model) {
15
+ model = model || this.model;
16
+
17
+ if (model === true) {
18
+ xmlStream.leafNode('u');
19
+ } else {
20
+ const attr = UnderlineXform.Attributes[model];
21
+ if (attr) {
22
+ xmlStream.leafNode('u', attr);
23
+ }
24
+ }
25
+ }
26
+
27
+ parseOpen(node) {
28
+ if (node.name === 'u') {
29
+ this.model = node.attributes.val || true;
30
+ }
31
+ }
32
+
33
+ parseText() {}
34
+
35
+ parseClose() {
36
+ return false;
37
+ }
38
+ }
39
+
40
+ UnderlineXform.Attributes = {
41
+ single: {},
42
+ double: {val: 'double'},
43
+ singleAccounting: {val: 'singleAccounting'},
44
+ doubleAccounting: {val: 'doubleAccounting'},
45
+ };
46
+
47
+ module.exports = UnderlineXform;
@@ -0,0 +1,81 @@
1
+ const BaseXform = require('../base-xform');
2
+
3
+ const FilterColumnXform = require('./filter-column-xform');
4
+
5
+ class AutoFilterXform extends BaseXform {
6
+ constructor() {
7
+ super();
8
+
9
+ this.map = {
10
+ filterColumn: new FilterColumnXform(),
11
+ };
12
+ }
13
+
14
+ get tag() {
15
+ return 'autoFilter';
16
+ }
17
+
18
+ prepare(model) {
19
+ model.columns.forEach((column, index) => {
20
+ this.map.filterColumn.prepare(column, {index});
21
+ });
22
+ }
23
+
24
+ render(xmlStream, model) {
25
+ xmlStream.openNode(this.tag, {ref: model.autoFilterRef});
26
+
27
+ model.columns.forEach(column => {
28
+ this.map.filterColumn.render(xmlStream, column);
29
+ });
30
+
31
+ xmlStream.closeNode();
32
+ return true;
33
+ }
34
+
35
+ parseOpen(node) {
36
+ if (this.parser) {
37
+ this.parser.parseOpen(node);
38
+ return true;
39
+ }
40
+ switch (node.name) {
41
+ case this.tag:
42
+ this.model = {
43
+ autoFilterRef: node.attributes.ref,
44
+ columns: [],
45
+ };
46
+ return true;
47
+
48
+ default:
49
+ this.parser = this.map[node.name];
50
+ if (this.parser) {
51
+ this.parseOpen(node);
52
+ return true;
53
+ }
54
+ throw new Error(`Unexpected xml node in parseOpen: ${JSON.stringify(node)}`);
55
+ }
56
+ }
57
+
58
+ parseText(text) {
59
+ if (this.parser) {
60
+ this.parser.parseText(text);
61
+ }
62
+ }
63
+
64
+ parseClose(name) {
65
+ if (this.parser) {
66
+ if (!this.parser.parseClose(name)) {
67
+ this.model.columns.push(this.parser.model);
68
+ this.parser = undefined;
69
+ }
70
+ return true;
71
+ }
72
+ switch (name) {
73
+ case this.tag:
74
+ return false;
75
+ default:
76
+ throw new Error(`Unexpected xml node in parseClose: ${name}`);
77
+ }
78
+ }
79
+ }
80
+
81
+ module.exports = AutoFilterXform;
@@ -0,0 +1,33 @@
1
+ const BaseXform = require('../base-xform');
2
+
3
+ class CustomFilterXform extends BaseXform {
4
+ get tag() {
5
+ return 'customFilter';
6
+ }
7
+
8
+ render(xmlStream, model) {
9
+ xmlStream.leafNode(this.tag, {
10
+ val: model.val,
11
+ operator: model.operator,
12
+ });
13
+ }
14
+
15
+ parseOpen(node) {
16
+ if (node.name === this.tag) {
17
+ this.model = {
18
+ val: node.attributes.val,
19
+ operator: node.attributes.operator,
20
+ };
21
+ return true;
22
+ }
23
+ return false;
24
+ }
25
+
26
+ parseText() {}
27
+
28
+ parseClose() {
29
+ return false;
30
+ }
31
+ }
32
+
33
+ module.exports = CustomFilterXform;