@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,57 @@
1
+ const BaseXform = require('../base-xform');
2
+
3
+ class IntegerXform extends BaseXform {
4
+ constructor(options) {
5
+ super();
6
+
7
+ this.tag = options.tag;
8
+ this.attr = options.attr;
9
+ this.attrs = options.attrs;
10
+
11
+ // option to render zero
12
+ this.zero = options.zero;
13
+ }
14
+
15
+ render(xmlStream, model) {
16
+ // int is different to float in that zero is not rendered
17
+ if (model || this.zero) {
18
+ xmlStream.openNode(this.tag);
19
+ if (this.attrs) {
20
+ xmlStream.addAttributes(this.attrs);
21
+ }
22
+ if (this.attr) {
23
+ xmlStream.addAttribute(this.attr, model);
24
+ } else {
25
+ xmlStream.writeText(model);
26
+ }
27
+ xmlStream.closeNode();
28
+ }
29
+ }
30
+
31
+ parseOpen(node) {
32
+ if (node.name === this.tag) {
33
+ if (this.attr) {
34
+ this.model = parseInt(node.attributes[this.attr], 10);
35
+ } else {
36
+ this.text = [];
37
+ }
38
+ return true;
39
+ }
40
+ return false;
41
+ }
42
+
43
+ parseText(text) {
44
+ if (!this.attr) {
45
+ this.text.push(text);
46
+ }
47
+ }
48
+
49
+ parseClose() {
50
+ if (!this.attr) {
51
+ this.model = parseInt(this.text.join('') || 0, 10);
52
+ }
53
+ return false;
54
+ }
55
+ }
56
+
57
+ module.exports = IntegerXform;
@@ -0,0 +1,51 @@
1
+ const BaseXform = require('../base-xform');
2
+
3
+ class StringXform extends BaseXform {
4
+ constructor(options) {
5
+ super();
6
+
7
+ this.tag = options.tag;
8
+ this.attr = options.attr;
9
+ this.attrs = options.attrs;
10
+ }
11
+
12
+ render(xmlStream, model) {
13
+ if (model !== undefined) {
14
+ xmlStream.openNode(this.tag);
15
+ if (this.attrs) {
16
+ xmlStream.addAttributes(this.attrs);
17
+ }
18
+ if (this.attr) {
19
+ xmlStream.addAttribute(this.attr, model);
20
+ } else {
21
+ xmlStream.writeText(model);
22
+ }
23
+ xmlStream.closeNode();
24
+ }
25
+ }
26
+
27
+ parseOpen(node) {
28
+ if (node.name === this.tag) {
29
+ if (this.attr) {
30
+ this.model = node.attributes[this.attr];
31
+ } else {
32
+ this.text = [];
33
+ }
34
+ }
35
+ }
36
+
37
+ parseText(text) {
38
+ if (!this.attr) {
39
+ this.text.push(text);
40
+ }
41
+ }
42
+
43
+ parseClose() {
44
+ if (!this.attr) {
45
+ this.model = this.text.join('');
46
+ }
47
+ return false;
48
+ }
49
+ }
50
+
51
+ module.exports = StringXform;
@@ -0,0 +1,64 @@
1
+ const BaseXform = require('./base-xform');
2
+ const XmlStream = require('../../utils/xml-stream');
3
+
4
+ // const model = {
5
+ // tag: 'name',
6
+ // $: {attr: 'value'},
7
+ // c: [
8
+ // { tag: 'child' }
9
+ // ],
10
+ // t: 'some text'
11
+ // };
12
+
13
+ function build(xmlStream, model) {
14
+ xmlStream.openNode(model.tag, model.$);
15
+ if (model.c) {
16
+ model.c.forEach(child => {
17
+ build(xmlStream, child);
18
+ });
19
+ }
20
+ if (model.t) {
21
+ xmlStream.writeText(model.t);
22
+ }
23
+ xmlStream.closeNode();
24
+ }
25
+
26
+ class StaticXform extends BaseXform {
27
+ constructor(model) {
28
+ super();
29
+
30
+ // This class is an optimisation for static (unimportant and unchanging) xml
31
+ // It is stateless - apart from its static model and so can be used as a singleton
32
+ // Being stateless - it will only track entry to and exit from it's root xml tag during parsing and nothing else
33
+ // Known issues:
34
+ // since stateless - parseOpen always returns true. Parent xform must know when to start using this xform
35
+ // if the root tag is recursive, the parsing will behave unpredictably
36
+ this._model = model;
37
+ }
38
+
39
+ render(xmlStream) {
40
+ if (!this._xml) {
41
+ const stream = new XmlStream();
42
+ build(stream, this._model);
43
+ this._xml = stream.xml;
44
+ }
45
+ xmlStream.writeXml(this._xml);
46
+ }
47
+
48
+ parseOpen() {
49
+ return true;
50
+ }
51
+
52
+ parseText() {}
53
+
54
+ parseClose(name) {
55
+ switch (name) {
56
+ case this._model.tag:
57
+ return false;
58
+ default:
59
+ return true;
60
+ }
61
+ }
62
+ }
63
+
64
+ module.exports = StaticXform;
@@ -0,0 +1,98 @@
1
+ const TextXform = require('./text-xform');
2
+ const RichTextXform = require('./rich-text-xform');
3
+
4
+ const BaseXform = require('../base-xform');
5
+
6
+ // <rPh sb="0" eb="1">
7
+ // <t>(its pronounciation in KATAKANA)</t>
8
+ // </rPh>
9
+
10
+ class PhoneticTextXform extends BaseXform {
11
+ constructor() {
12
+ super();
13
+
14
+ this.map = {
15
+ r: new RichTextXform(),
16
+ t: new TextXform(),
17
+ };
18
+ }
19
+
20
+ get tag() {
21
+ return 'rPh';
22
+ }
23
+
24
+ render(xmlStream, model) {
25
+ xmlStream.openNode(this.tag, {
26
+ sb: model.sb || 0,
27
+ eb: model.eb || 0,
28
+ });
29
+ if (model && model.hasOwnProperty('richText') && model.richText) {
30
+ const {r} = this.map;
31
+ model.richText.forEach(text => {
32
+ r.render(xmlStream, text);
33
+ });
34
+ } else if (model) {
35
+ this.map.t.render(xmlStream, model.text);
36
+ }
37
+ xmlStream.closeNode();
38
+ }
39
+
40
+ parseOpen(node) {
41
+ const {name} = node;
42
+ if (this.parser) {
43
+ this.parser.parseOpen(node);
44
+ return true;
45
+ }
46
+ if (name === this.tag) {
47
+ this.model = {
48
+ sb: parseInt(node.attributes.sb, 10),
49
+ eb: parseInt(node.attributes.eb, 10),
50
+ };
51
+ return true;
52
+ }
53
+ this.parser = this.map[name];
54
+ if (this.parser) {
55
+ this.parser.parseOpen(node);
56
+ return true;
57
+ }
58
+ return false;
59
+ }
60
+
61
+ parseText(text) {
62
+ if (this.parser) {
63
+ this.parser.parseText(text);
64
+ }
65
+ }
66
+
67
+ parseClose(name) {
68
+ if (this.parser) {
69
+ if (!this.parser.parseClose(name)) {
70
+ switch (name) {
71
+ case 'r': {
72
+ let rt = this.model.richText;
73
+ if (!rt) {
74
+ rt = this.model.richText = [];
75
+ }
76
+ rt.push(this.parser.model);
77
+ break;
78
+ }
79
+ case 't':
80
+ this.model.text = this.parser.model;
81
+ break;
82
+ default:
83
+ break;
84
+ }
85
+ this.parser = undefined;
86
+ }
87
+ return true;
88
+ }
89
+ switch (name) {
90
+ case this.tag:
91
+ return false;
92
+ default:
93
+ return true;
94
+ }
95
+ }
96
+ }
97
+
98
+ module.exports = PhoneticTextXform;
@@ -0,0 +1,101 @@
1
+ const TextXform = require('./text-xform');
2
+ const FontXform = require('../style/font-xform');
3
+
4
+ const BaseXform = require('../base-xform');
5
+
6
+ // <r>
7
+ // <rPr>
8
+ // <sz val="11"/>
9
+ // <color theme="1" tint="5"/>
10
+ // <rFont val="Calibri"/>
11
+ // <family val="2"/>
12
+ // <scheme val="minor"/>
13
+ // </rPr>
14
+ // <t xml:space="preserve"> is </t>
15
+ // </r>
16
+
17
+ class RichTextXform extends BaseXform {
18
+ constructor(model) {
19
+ super();
20
+
21
+ this.model = model;
22
+ }
23
+
24
+ get tag() {
25
+ return 'r';
26
+ }
27
+
28
+ get textXform() {
29
+ return this._textXform || (this._textXform = new TextXform());
30
+ }
31
+
32
+ get fontXform() {
33
+ return this._fontXform || (this._fontXform = new FontXform(RichTextXform.FONT_OPTIONS));
34
+ }
35
+
36
+ render(xmlStream, model) {
37
+ model = model || this.model;
38
+
39
+ xmlStream.openNode('r');
40
+ if (model.font) {
41
+ this.fontXform.render(xmlStream, model.font);
42
+ }
43
+ this.textXform.render(xmlStream, model.text);
44
+ xmlStream.closeNode();
45
+ }
46
+
47
+ parseOpen(node) {
48
+ if (this.parser) {
49
+ this.parser.parseOpen(node);
50
+ return true;
51
+ }
52
+ switch (node.name) {
53
+ case 'r':
54
+ this.model = {};
55
+ return true;
56
+ case 't':
57
+ this.parser = this.textXform;
58
+ this.parser.parseOpen(node);
59
+ return true;
60
+ case 'rPr':
61
+ this.parser = this.fontXform;
62
+ this.parser.parseOpen(node);
63
+ return true;
64
+ default:
65
+ return false;
66
+ }
67
+ }
68
+
69
+ parseText(text) {
70
+ if (this.parser) {
71
+ this.parser.parseText(text);
72
+ }
73
+ }
74
+
75
+ parseClose(name) {
76
+ switch (name) {
77
+ case 'r':
78
+ return false;
79
+ case 't':
80
+ this.model.text = this.parser.model;
81
+ this.parser = undefined;
82
+ return true;
83
+ case 'rPr':
84
+ this.model.font = this.parser.model;
85
+ this.parser = undefined;
86
+ return true;
87
+ default:
88
+ if (this.parser) {
89
+ this.parser.parseClose(name);
90
+ }
91
+ return true;
92
+ }
93
+ }
94
+ }
95
+
96
+ RichTextXform.FONT_OPTIONS = {
97
+ tagName: 'rPr',
98
+ fontNameTag: 'rFont',
99
+ };
100
+
101
+ module.exports = RichTextXform;
@@ -0,0 +1,102 @@
1
+ const TextXform = require('./text-xform');
2
+ const RichTextXform = require('./rich-text-xform');
3
+ const PhoneticTextXform = require('./phonetic-text-xform');
4
+
5
+ const BaseXform = require('../base-xform');
6
+
7
+ // <si>
8
+ // <r></r><r></r>...
9
+ // </si>
10
+ // <si>
11
+ // <t></t>
12
+ // </si>
13
+
14
+ class SharedStringXform extends BaseXform {
15
+ constructor(model) {
16
+ super();
17
+
18
+ this.model = model;
19
+
20
+ this.map = {
21
+ r: new RichTextXform(),
22
+ t: new TextXform(),
23
+ rPh: new PhoneticTextXform(),
24
+ };
25
+ }
26
+
27
+ get tag() {
28
+ return 'si';
29
+ }
30
+
31
+ render(xmlStream, model) {
32
+ xmlStream.openNode(this.tag);
33
+ if (model && model.hasOwnProperty('richText') && model.richText) {
34
+ if (model.richText.length) {
35
+ model.richText.forEach(text => {
36
+ this.map.r.render(xmlStream, text);
37
+ });
38
+ } else {
39
+ this.map.t.render(xmlStream, '');
40
+ }
41
+ } else if (model !== undefined && model !== null) {
42
+ this.map.t.render(xmlStream, model);
43
+ }
44
+ xmlStream.closeNode();
45
+ }
46
+
47
+ parseOpen(node) {
48
+ const {name} = node;
49
+ if (this.parser) {
50
+ this.parser.parseOpen(node);
51
+ return true;
52
+ }
53
+ if (name === this.tag) {
54
+ this.model = {};
55
+ return true;
56
+ }
57
+ this.parser = this.map[name];
58
+ if (this.parser) {
59
+ this.parser.parseOpen(node);
60
+ return true;
61
+ }
62
+ return false;
63
+ }
64
+
65
+ parseText(text) {
66
+ if (this.parser) {
67
+ this.parser.parseText(text);
68
+ }
69
+ }
70
+
71
+ parseClose(name) {
72
+ if (this.parser) {
73
+ if (!this.parser.parseClose(name)) {
74
+ switch (name) {
75
+ case 'r': {
76
+ let rt = this.model.richText;
77
+ if (!rt) {
78
+ rt = this.model.richText = [];
79
+ }
80
+ rt.push(this.parser.model);
81
+ break;
82
+ }
83
+ case 't':
84
+ this.model = this.parser.model || {};
85
+ break;
86
+ default:
87
+ break;
88
+ }
89
+ this.parser = undefined;
90
+ }
91
+ return true;
92
+ }
93
+ switch (name) {
94
+ case this.tag:
95
+ return false;
96
+ default:
97
+ return true;
98
+ }
99
+ }
100
+ }
101
+
102
+ module.exports = SharedStringXform;
@@ -0,0 +1,127 @@
1
+ const XmlStream = require('../../../utils/xml-stream');
2
+ const BaseXform = require('../base-xform');
3
+ const SharedStringXform = require('./shared-string-xform');
4
+
5
+ class SharedStringsXform extends BaseXform {
6
+ constructor(model) {
7
+ super();
8
+
9
+ this.model = model || {
10
+ values: [],
11
+ count: 0,
12
+ };
13
+ this.hash = Object.create(null);
14
+ this.rich = Object.create(null);
15
+ }
16
+
17
+ get sharedStringXform() {
18
+ return this._sharedStringXform || (this._sharedStringXform = new SharedStringXform());
19
+ }
20
+
21
+ get values() {
22
+ return this.model.values;
23
+ }
24
+
25
+ get uniqueCount() {
26
+ return this.model.values.length;
27
+ }
28
+
29
+ get count() {
30
+ return this.model.count;
31
+ }
32
+
33
+ getString(index) {
34
+ return this.model.values[index];
35
+ }
36
+
37
+ add(value) {
38
+ return value.richText ? this.addRichText(value) : this.addText(value);
39
+ }
40
+
41
+ addText(value) {
42
+ let index = this.hash[value];
43
+ if (index === undefined) {
44
+ index = this.hash[value] = this.model.values.length;
45
+ this.model.values.push(value);
46
+ }
47
+ this.model.count++;
48
+ return index;
49
+ }
50
+
51
+ addRichText(value) {
52
+ // TODO: add WeakMap here
53
+ const xml = this.sharedStringXform.toXml(value);
54
+ let index = this.rich[xml];
55
+ if (index === undefined) {
56
+ index = this.rich[xml] = this.model.values.length;
57
+ this.model.values.push(value);
58
+ }
59
+ this.model.count++;
60
+ return index;
61
+ }
62
+
63
+ // <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
64
+ // <sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="<%=totalRefs%>" uniqueCount="<%=count%>">
65
+ // <si><t><%=text%></t></si>
66
+ // <si><r><rPr></rPr><t></t></r></si>
67
+ // </sst>
68
+
69
+ render(xmlStream, model) {
70
+ model = model || this._values;
71
+ xmlStream.openXml(XmlStream.StdDocAttributes);
72
+
73
+ xmlStream.openNode('sst', {
74
+ xmlns: 'http://schemas.openxmlformats.org/spreadsheetml/2006/main',
75
+ count: model.count,
76
+ uniqueCount: model.values.length,
77
+ });
78
+
79
+ const sx = this.sharedStringXform;
80
+ model.values.forEach(sharedString => {
81
+ sx.render(xmlStream, sharedString);
82
+ });
83
+ xmlStream.closeNode();
84
+ }
85
+
86
+ parseOpen(node) {
87
+ if (this.parser) {
88
+ this.parser.parseOpen(node);
89
+ return true;
90
+ }
91
+ switch (node.name) {
92
+ case 'sst':
93
+ return true;
94
+ case 'si':
95
+ this.parser = this.sharedStringXform;
96
+ this.parser.parseOpen(node);
97
+ return true;
98
+ default:
99
+ throw new Error(`Unexpected xml node in parseOpen: ${JSON.stringify(node)}`);
100
+ }
101
+ }
102
+
103
+ parseText(text) {
104
+ if (this.parser) {
105
+ this.parser.parseText(text);
106
+ }
107
+ }
108
+
109
+ parseClose(name) {
110
+ if (this.parser) {
111
+ if (!this.parser.parseClose(name)) {
112
+ this.model.values.push(this.parser.model);
113
+ this.model.count++;
114
+ this.parser = undefined;
115
+ }
116
+ return true;
117
+ }
118
+ switch (name) {
119
+ case 'sst':
120
+ return false;
121
+ default:
122
+ throw new Error(`Unexpected xml node in parseClose: ${name}`);
123
+ }
124
+ }
125
+ }
126
+
127
+ module.exports = SharedStringsXform;
@@ -0,0 +1,44 @@
1
+ const BaseXform = require('../base-xform');
2
+
3
+ // <t xml:space="preserve"> is </t>
4
+
5
+ class TextXform extends BaseXform {
6
+ get tag() {
7
+ return 't';
8
+ }
9
+
10
+ render(xmlStream, model) {
11
+ xmlStream.openNode('t');
12
+ if (/^\s|\n|\s$/.test(model)) {
13
+ xmlStream.addAttribute('xml:space', 'preserve');
14
+ }
15
+ xmlStream.writeText(model);
16
+ xmlStream.closeNode();
17
+ }
18
+
19
+ get model() {
20
+ return this._text
21
+ .join('')
22
+ .replace(/_x([0-9A-F]{4})_/g, ($0, $1) => String.fromCharCode(parseInt($1, 16)));
23
+ }
24
+
25
+ parseOpen(node) {
26
+ switch (node.name) {
27
+ case 't':
28
+ this._text = [];
29
+ return true;
30
+ default:
31
+ return false;
32
+ }
33
+ }
34
+
35
+ parseText(text) {
36
+ this._text.push(text);
37
+ }
38
+
39
+ parseClose() {
40
+ return false;
41
+ }
42
+ }
43
+
44
+ module.exports = TextXform;