@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,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;
|