@shbernal/ts-xlsx 1.3.1 → 2.1.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 (186) hide show
  1. package/README.md +20 -10
  2. package/dist/bytes.d.ts +24 -0
  3. package/dist/bytes.js +34 -0
  4. package/dist/core/address.d.ts +72 -3
  5. package/dist/core/address.js +50 -10
  6. package/dist/core/autofilter.d.ts +13 -2
  7. package/dist/core/autofilter.js +35 -10
  8. package/dist/core/cell.d.ts +17 -16
  9. package/dist/core/cell.js +3 -7
  10. package/dist/core/color-resolution.d.ts +8 -8
  11. package/dist/core/color-resolution.js +7 -8
  12. package/dist/core/column.d.ts +8 -9
  13. package/dist/core/column.js +2 -4
  14. package/dist/core/comment-thread.d.ts +32 -23
  15. package/dist/core/comment-thread.js +4 -4
  16. package/dist/core/conditional-formatting-overlay.d.ts +6 -1
  17. package/dist/core/conditional-formatting-overlay.js +11 -0
  18. package/dist/core/conditional-formatting.d.ts +48 -8
  19. package/dist/core/conditional-formatting.js +88 -0
  20. package/dist/core/data-validation-overlay.d.ts +12 -1
  21. package/dist/core/data-validation-overlay.js +21 -2
  22. package/dist/core/data-validation.d.ts +12 -5
  23. package/dist/core/data-validation.js +34 -0
  24. package/dist/core/date.d.ts +3 -3
  25. package/dist/core/formula.d.ts +6 -6
  26. package/dist/core/grid-edits.d.ts +16 -0
  27. package/dist/core/grid-edits.js +29 -11
  28. package/dist/core/grid-shift.d.ts +19 -0
  29. package/dist/core/grid-shift.js +8 -0
  30. package/dist/core/image.d.ts +39 -2
  31. package/dist/core/image.js +10 -0
  32. package/dist/core/internal.d.ts +9 -1
  33. package/dist/core/limits.d.ts +4 -4
  34. package/dist/core/merge.d.ts +17 -12
  35. package/dist/core/merge.js +36 -5
  36. package/dist/core/page-setup.d.ts +20 -6
  37. package/dist/core/page-setup.js +8 -1
  38. package/dist/core/pivot-table.d.ts +4 -4
  39. package/dist/core/pivot-table.js +29 -20
  40. package/dist/core/preserved.d.ts +3 -3
  41. package/dist/core/protection.d.ts +7 -4
  42. package/dist/core/protection.js +20 -14
  43. package/dist/core/range.d.ts +16 -14
  44. package/dist/core/range.js +5 -5
  45. package/dist/core/row-input.d.ts +9 -8
  46. package/dist/core/row-input.js +12 -10
  47. package/dist/core/row.d.ts +7 -8
  48. package/dist/core/row.js +2 -4
  49. package/dist/core/style.d.ts +41 -14
  50. package/dist/core/style.js +82 -67
  51. package/dist/core/table-style.d.ts +9 -9
  52. package/dist/core/table-style.js +1 -1
  53. package/dist/core/table.d.ts +53 -51
  54. package/dist/core/table.js +83 -41
  55. package/dist/core/theme.d.ts +16 -44
  56. package/dist/core/theme.js +6 -148
  57. package/dist/core/value.d.ts +7 -7
  58. package/dist/core/workbook-protection.d.ts +2 -2
  59. package/dist/core/workbook-styles.d.ts +26 -0
  60. package/dist/core/workbook-styles.js +48 -0
  61. package/dist/core/workbook-theme.d.ts +25 -0
  62. package/dist/core/workbook-theme.js +49 -0
  63. package/dist/core/workbook-vba.d.ts +17 -0
  64. package/dist/core/workbook-vba.js +79 -0
  65. package/dist/core/workbook.d.ts +133 -87
  66. package/dist/core/workbook.js +74 -133
  67. package/dist/core/worksheet-comments.d.ts +17 -0
  68. package/dist/core/worksheet-comments.js +70 -0
  69. package/dist/core/worksheet-model.d.ts +4 -4
  70. package/dist/core/worksheet-model.js +1 -0
  71. package/dist/core/worksheet-pictures.d.ts +29 -0
  72. package/dist/core/worksheet-pictures.js +42 -0
  73. package/dist/core/worksheet.d.ts +100 -79
  74. package/dist/core/worksheet.js +63 -142
  75. package/dist/customui/errors.d.ts +1 -1
  76. package/dist/customui/ribbon.d.ts +3 -3
  77. package/dist/customui/ribbon.js +24 -23
  78. package/dist/entries/core.d.ts +7 -7
  79. package/dist/entries/core.js +1 -1
  80. package/dist/entries/csv.d.ts +1 -1
  81. package/dist/entries/node-unavailable.d.ts +15 -0
  82. package/dist/entries/node-unavailable.js +20 -0
  83. package/dist/entries/node.d.ts +1 -0
  84. package/dist/entries/node.js +1 -0
  85. package/dist/entries/vba.d.ts +1 -1
  86. package/dist/entries/vba.js +1 -1
  87. package/dist/entries/xlsx.d.ts +1 -1
  88. package/dist/entries/xlsx.js +1 -1
  89. package/dist/errors.d.ts +12 -12
  90. package/dist/io/csv/read.js +2 -3
  91. package/dist/io/csv/write.d.ts +19 -4
  92. package/dist/io/csv/write.js +34 -4
  93. package/dist/io/opc/errors.d.ts +6 -6
  94. package/dist/io/opc/inflate.d.ts +1 -1
  95. package/dist/io/opc/inflate.js +2 -12
  96. package/dist/io/opc/namespaces.d.ts +1 -1
  97. package/dist/io/opc/read-opc.d.ts +19 -3
  98. package/dist/io/opc/read-opc.js +14 -8
  99. package/dist/io/opc/read-options.d.ts +2 -2
  100. package/dist/io/opc/rels.d.ts +11 -0
  101. package/dist/io/opc/rels.js +3 -2
  102. package/dist/io/opc/sniff-format.d.ts +2 -2
  103. package/dist/io/style/xf-style.d.ts +44 -7
  104. package/dist/io/style/xf-style.js +27 -0
  105. package/dist/io/xlsb/errors.d.ts +1 -1
  106. package/dist/io/xlsb/formula.d.ts +4 -4
  107. package/dist/io/xlsb/primitives.d.ts +8 -8
  108. package/dist/io/xlsb/ptg-functions.d.ts +1 -1
  109. package/dist/io/xlsb/read-styles.js +2 -20
  110. package/dist/io/xlsb/read.d.ts +2 -2
  111. package/dist/io/xlsx/cell-accumulator.d.ts +26 -9
  112. package/dist/io/xlsx/cell-accumulator.js +89 -46
  113. package/dist/io/xlsx/cell-value.d.ts +3 -3
  114. package/dist/io/xlsx/cell-value.js +9 -6
  115. package/dist/io/xlsx/color-xml.d.ts +1 -1
  116. package/dist/io/xlsx/color-xml.js +18 -23
  117. package/dist/io/xlsx/comments.d.ts +5 -5
  118. package/dist/io/xlsx/comments.js +17 -8
  119. package/dist/io/xlsx/conditional-formatting.d.ts +4 -4
  120. package/dist/io/xlsx/conditional-formatting.js +64 -58
  121. package/dist/io/xlsx/data-validation.d.ts +3 -3
  122. package/dist/io/xlsx/data-validation.js +35 -33
  123. package/dist/io/xlsx/errors.d.ts +3 -3
  124. package/dist/io/xlsx/hyperlinks.d.ts +4 -2
  125. package/dist/io/xlsx/hyperlinks.js +10 -18
  126. package/dist/io/xlsx/images.d.ts +1 -1
  127. package/dist/io/xlsx/images.js +25 -28
  128. package/dist/io/xlsx/namespaces.d.ts +2 -2
  129. package/dist/io/xlsx/package-plan.js +5 -2
  130. package/dist/io/xlsx/read-pivot.js +4 -13
  131. package/dist/io/xlsx/read-rows.d.ts +13 -13
  132. package/dist/io/xlsx/read-rows.js +19 -55
  133. package/dist/io/xlsx/read-shared-strings.js +7 -44
  134. package/dist/io/xlsx/read-styles.d.ts +3 -3
  135. package/dist/io/xlsx/read-styles.js +42 -66
  136. package/dist/io/xlsx/read-worksheet.js +73 -129
  137. package/dist/io/xlsx/read.d.ts +4 -6
  138. package/dist/io/xlsx/read.js +104 -142
  139. package/dist/io/xlsx/rich-runs.d.ts +41 -7
  140. package/dist/io/xlsx/rich-runs.js +94 -30
  141. package/dist/io/xlsx/rich-text.d.ts +1 -1
  142. package/dist/io/xlsx/shared-strings.d.ts +2 -2
  143. package/dist/io/xlsx/sheet-properties.d.ts +1 -1
  144. package/dist/io/xlsx/sheet-properties.js +14 -10
  145. package/dist/io/xlsx/styles.d.ts +9 -22
  146. package/dist/io/xlsx/styles.js +65 -89
  147. package/dist/io/xlsx/tables.d.ts +2 -2
  148. package/dist/io/xlsx/tables.js +20 -25
  149. package/dist/io/xlsx/theme-xml.d.ts +37 -0
  150. package/dist/io/xlsx/theme-xml.js +145 -0
  151. package/dist/io/xlsx/threaded-comments.d.ts +18 -27
  152. package/dist/io/xlsx/threaded-comments.js +26 -38
  153. package/dist/io/xlsx/workbook-xml.js +14 -8
  154. package/dist/io/xlsx/worksheet-xml.d.ts +2 -2
  155. package/dist/io/xlsx/worksheet-xml.js +40 -48
  156. package/dist/io/xlsx/write-stream.d.ts +23 -16
  157. package/dist/io/xlsx/write-stream.js +13 -14
  158. package/dist/io/xlsx/write.d.ts +8 -7
  159. package/dist/io/xlsx/write.js +6 -3
  160. package/dist/io/xlsx/x14-ext.d.ts +1 -1
  161. package/dist/sha512.d.ts +2 -0
  162. package/dist/sha512.js +146 -0
  163. package/dist/vba/bytes.d.ts +11 -0
  164. package/dist/vba/bytes.js +29 -0
  165. package/dist/vba/cfb-writer.d.ts +1 -1
  166. package/dist/vba/cfb.d.ts +1 -1
  167. package/dist/vba/cfb.js +15 -51
  168. package/dist/vba/codepage.d.ts +2 -2
  169. package/dist/vba/dir-records.d.ts +53 -0
  170. package/dist/vba/dir-records.js +28 -0
  171. package/dist/vba/errors.d.ts +2 -2
  172. package/dist/vba/index.d.ts +2 -2
  173. package/dist/vba/index.js +2 -2
  174. package/dist/vba/ms-ovba.d.ts +1 -1
  175. package/dist/vba/ms-ovba.js +1 -3
  176. package/dist/vba/project-editor.d.ts +9 -9
  177. package/dist/vba/project-editor.js +6 -44
  178. package/dist/vba/project.d.ts +5 -5
  179. package/dist/vba/project.js +3 -28
  180. package/dist/vba/vba-encoding.d.ts +1 -1
  181. package/dist/xml/errors.d.ts +1 -1
  182. package/dist/xml/xml-read.d.ts +98 -9
  183. package/dist/xml/xml-read.js +70 -0
  184. package/dist/xml/xml.d.ts +74 -10
  185. package/dist/xml/xml.js +39 -7
  186. package/package.json +59 -36
@@ -3,23 +3,40 @@ import type { Worksheet } from '../../core/worksheet.ts';
3
3
  import { type XmlAttributes } from '../../xml/xml-read.ts';
4
4
  import { type XfStyle } from '../style/xf-style.ts';
5
5
  import { type SharedString } from './cell-value.ts';
6
- import { RunAccumulator } from './rich-runs.ts';
7
6
  export declare class CellAccumulator {
8
7
  #private;
8
+ constructor(options: {
9
+ readonly richRuns: boolean;
10
+ });
9
11
  /** This cell's `<c r>` address (`"B3"`), or '' when it carried none. */
10
12
  get ref(): string;
11
13
  /** This cell's own `<c s>` style index, or -1 when it carries none. */
12
14
  get styleIndex(): number;
13
15
  /** This cell's 1-based column, or -1 when its address was absent or unparseable. */
14
16
  get col(): number;
15
- /** The rich-text run accumulator, driven by the surrounding parser's `<r>`/`<rPr>` handling. */
16
- get runs(): RunAccumulator;
17
- beginCell(attrs: XmlAttributes): void;
18
- beginFormula(attrs: XmlAttributes, selfClosing: boolean): void;
19
- setFormula(text: string): void;
20
- setValue(text: string): void;
21
- beginInlineString(): void;
22
- appendText(text: string, inInlineString: boolean): void;
17
+ /**
18
+ * The text gathered since the current element opened. A caller reads it for the elements it
19
+ * captures itself (see {@link capture}); the machine reads it for its own.
20
+ */
21
+ get capturedText(): string;
22
+ /** Gather the current element's text for the caller's own use, the way `<v>` and `<t>` do. */
23
+ capture(): void;
24
+ /**
25
+ * Drive one element open, and return whether it was one of the cell machine's own. Every open
26
+ * resets the capture state first, which is true of both readers and of every element, not just
27
+ * these; a caller that captures its own text calls {@link capture} after this returns.
28
+ */
29
+ openElement(local: string, attrs: XmlAttributes, selfClosing: boolean): boolean;
30
+ /** Feed one run of character data. Ignored unless something is capturing. */
31
+ appendChunk(chunk: string): void;
32
+ /**
33
+ * Drive one element close. `'cell'` means a `</c>` closed and the caller should commit the
34
+ * gathered cell, which is the one step the two readers do differently; `'claimed'` means the
35
+ * machine handled it; `'other'` leaves it to the caller. Capture always ends here, as it does on
36
+ * every close in both readers.
37
+ */
38
+ closeElement(local: string): 'cell' | 'claimed' | 'other';
23
39
  finalize(sheet: Worksheet, sharedStrings: readonly SharedString[], style: XfStyle | undefined): void;
40
+ private cachedResult;
24
41
  decode(sharedStrings: readonly SharedString[], style: XfStyle | undefined): CellValue;
25
42
  }
@@ -1,6 +1,6 @@
1
- import { decodeAddress, encodeAddress } from '../../core/address.js';
1
+ import { encodeAddress, tryDecodeCellRef } from '../../core/address.js';
2
2
  import { translateFormula, unmangleFunctions } from '../../core/formula.js';
3
- import { boolPresent } from '../../xml/xml-read.js';
3
+ import { boolStrict, numInteger } from '../../xml/xml-read.js';
4
4
  import { applyXfToCell } from '../style/xf-style.js';
5
5
  import { decodeCellContent, decodeFormulaResult, } from './cell-value.js';
6
6
  import { RunAccumulator } from './rich-runs.js';
@@ -16,11 +16,15 @@ export class CellAccumulator {
16
16
  #sharedClone = false;
17
17
  #dataTable = null;
18
18
  #valueText = '';
19
- #inlineText = '';
20
19
  #hasFormula = false;
21
20
  #hasValue = false;
22
- #runs = new RunAccumulator();
21
+ #runs;
23
22
  #masters = new Map();
23
+ #capture = false;
24
+ #text = '';
25
+ constructor(options) {
26
+ this.#runs = new RunAccumulator({ container: 'is', readRuns: options.richRuns });
27
+ }
24
28
  get ref() {
25
29
  return this.#ref;
26
30
  }
@@ -30,19 +34,16 @@ export class CellAccumulator {
30
34
  get col() {
31
35
  return this.#col;
32
36
  }
33
- get runs() {
34
- return this.#runs;
35
- }
36
- beginCell(attrs) {
37
- this.#ref = attrs.r ?? '';
37
+ #beginCell(attrs) {
38
38
  this.#type = attrs.t ?? '';
39
- this.#style = attrs.s !== undefined ? Number(attrs.s) : -1;
40
- this.#col = this.#ref === '' ? -1 : (decodeAddress(this.#ref).col ?? -1);
41
- this.#row = this.#ref === '' ? -1 : (decodeAddress(this.#ref).row ?? -1);
39
+ this.#style = numInteger(attrs.s, 0) ?? -1;
40
+ const decoded = tryDecodeCellRef(attrs.r ?? '');
41
+ this.#ref = decoded === undefined ? '' : (attrs.r ?? '');
42
+ this.#col = decoded?.col ?? -1;
43
+ this.#row = decoded?.row ?? -1;
42
44
  this.#formula = '';
43
45
  this.#valueText = '';
44
- this.#inlineText = '';
45
- this.#runs.reset();
46
+ this.#runs.beginContainer();
46
47
  this.#hasFormula = false;
47
48
  this.#hasValue = false;
48
49
  this.#formulaShared = false;
@@ -50,9 +51,9 @@ export class CellAccumulator {
50
51
  this.#sharedClone = false;
51
52
  this.#dataTable = null;
52
53
  }
53
- beginFormula(attrs, selfClosing) {
54
+ #beginFormula(attrs, selfClosing) {
54
55
  this.#formulaShared = attrs.t === 'shared';
55
- this.#formulaSi = attrs.si !== undefined ? Number(attrs.si) : -1;
56
+ this.#formulaSi = numInteger(attrs.si, 0) ?? -1;
56
57
  if (selfClosing && this.#formulaShared)
57
58
  this.#sharedClone = true;
58
59
  if (attrs.t === 'dataTable' && attrs.ref !== undefined) {
@@ -65,41 +66,85 @@ export class CellAccumulator {
65
66
  };
66
67
  }
67
68
  }
68
- setFormula(text) {
69
+ #setFormula(text) {
69
70
  this.#formula = text;
70
71
  this.#hasFormula = true;
71
72
  }
72
- setValue(text) {
73
+ #setValue(text) {
73
74
  this.#valueText = text;
74
75
  this.#hasValue = true;
75
76
  }
76
- beginInlineString() {
77
- this.#inlineText = '';
78
- this.#runs.reset();
77
+ get capturedText() {
78
+ return this.#text;
79
+ }
80
+ capture() {
81
+ this.#capture = true;
82
+ }
83
+ openElement(local, attrs, selfClosing) {
84
+ this.#text = '';
85
+ this.#capture = false;
86
+ if (this.#runs.open(local, attrs, selfClosing))
87
+ return true;
88
+ switch (local) {
89
+ case 'c':
90
+ this.#beginCell(attrs);
91
+ return true;
92
+ case 'f':
93
+ this.#capture = !selfClosing;
94
+ this.#beginFormula(attrs, selfClosing);
95
+ return true;
96
+ case 'v':
97
+ this.#capture = !selfClosing;
98
+ return true;
99
+ default:
100
+ return false;
101
+ }
102
+ }
103
+ appendChunk(chunk) {
104
+ this.#runs.text(chunk);
105
+ if (this.#capture)
106
+ this.#text += chunk;
79
107
  }
80
- appendText(text, inInlineString) {
81
- if (!this.#runs.appendText(text) && inInlineString)
82
- this.#inlineText += text;
108
+ closeElement(local) {
109
+ const verdict = this.#closeElement(local);
110
+ this.#capture = false;
111
+ return verdict;
112
+ }
113
+ #closeElement(local) {
114
+ if (this.#runs.close(local) !== 'other')
115
+ return 'claimed';
116
+ switch (local) {
117
+ case 'f':
118
+ this.#setFormula(this.#text);
119
+ return 'claimed';
120
+ case 'v':
121
+ this.#setValue(this.#text);
122
+ return 'claimed';
123
+ case 'c':
124
+ return 'cell';
125
+ default:
126
+ return 'other';
127
+ }
83
128
  }
84
129
  finalize(sheet, sharedStrings, style) {
85
130
  if (this.#ref === '')
86
131
  return;
132
+ const value = this.#resolveValue(sharedStrings, style);
133
+ const cell = sheet.getCell(this.#ref);
134
+ applyXfToCell(cell, style);
135
+ cell.value = value;
136
+ }
137
+ #resolveValue(sharedStrings, style) {
87
138
  if (this.#dataTable !== null) {
88
- const value = {
139
+ return {
89
140
  shareType: 'dataTable',
90
141
  ref: this.#dataTable.ref,
91
- ...(boolPresent(this.#dataTable.dt2D ?? '0') ? { dataTable2D: true } : {}),
92
- ...(boolPresent(this.#dataTable.dtr ?? '0') ? { dataTableRow: true } : {}),
142
+ ...(boolStrict(this.#dataTable.dt2D) ? { dataTable2D: true } : {}),
143
+ ...(boolStrict(this.#dataTable.dtr) ? { dataTableRow: true } : {}),
93
144
  ...(this.#dataTable.r1 !== undefined ? { r1: this.#dataTable.r1 } : {}),
94
145
  ...(this.#dataTable.r2 !== undefined ? { r2: this.#dataTable.r2 } : {}),
95
- ...(this.#hasValue
96
- ? { result: decodeFormulaResult(this.#type, this.#valueText, style?.numFmt) }
97
- : {}),
146
+ ...this.cachedResult(style),
98
147
  };
99
- const cell = sheet.getCell(this.#ref);
100
- applyXfToCell(cell, style);
101
- cell.value = value;
102
- return;
103
148
  }
104
149
  if (this.#hasFormula && this.#formulaShared && this.#formulaSi >= 0) {
105
150
  this.#masters.set(this.#formulaSi, { formula: this.#formula, col: this.#col, row: this.#row });
@@ -108,22 +153,20 @@ export class CellAccumulator {
108
153
  const master = this.#masters.get(this.#formulaSi);
109
154
  if (master !== undefined) {
110
155
  const translated = translateFormula(master.formula, this.#col - master.col, this.#row - master.row);
111
- const value = {
156
+ return {
112
157
  sharedFormula: encodeAddress(master.col, master.row),
113
158
  formula: unmangleFunctions(translated),
114
- ...(this.#hasValue
115
- ? { result: decodeFormulaResult(this.#type, this.#valueText, style?.numFmt) }
116
- : {}),
159
+ ...this.cachedResult(style),
117
160
  };
118
- const cell = sheet.getCell(this.#ref);
119
- applyXfToCell(cell, style);
120
- cell.value = value;
121
- return;
122
161
  }
123
162
  }
124
- const cell = sheet.getCell(this.#ref);
125
- applyXfToCell(cell, style);
126
- cell.value = this.decode(sharedStrings, style);
163
+ return this.decode(sharedStrings, style);
164
+ }
165
+ cachedResult(style) {
166
+ if (!this.#hasValue)
167
+ return {};
168
+ const result = decodeFormulaResult(this.#type, this.#valueText, style?.numFmt);
169
+ return result === undefined ? {} : { result };
127
170
  }
128
171
  decode(sharedStrings, style) {
129
172
  const raw = {
@@ -132,7 +175,7 @@ export class CellAccumulator {
132
175
  formula: this.#formula,
133
176
  hasValue: this.#hasValue,
134
177
  valueText: this.#valueText,
135
- inlineText: this.#inlineText,
178
+ inlineText: this.#runs.plainText,
136
179
  richTextRuns: this.#runs.runs,
137
180
  };
138
181
  return decodeCellContent(raw, sharedStrings, style?.numFmt);
@@ -1,7 +1,7 @@
1
1
  import { type CellValue, type FormulaResult, type RichTextRun, type RichTextValue } from '../../core/value.ts';
2
2
  /**
3
3
  * One entry of the shared-strings pool. A `<si>` built from a bare `<t>` is a plain string; a `<si>`
4
- * built from `<r>` runs is rich text so a `t="s"` cell can resolve to either kind, and rich text
4
+ * built from `<r>` runs is rich text, so a `t="s"` cell can resolve to either kind, and rich text
5
5
  * that Excel pooled reads back with its per-run formatting intact rather than flattened to text.
6
6
  */
7
7
  export type SharedString = string | RichTextValue;
@@ -26,7 +26,7 @@ export interface RawCell {
26
26
  */
27
27
  export declare function decodeCellContent(raw: RawCell, sharedStrings: readonly SharedString[], numFmt: string | undefined): CellValue;
28
28
  /** Decode a formula's cached `<v>` result by its `t` type, coercing a numeric result under a date
29
- * `numFmt` to a {@link Date} exactly as a bare numeric cell is so a date-valued formula result
29
+ * `numFmt` to a {@link Date} exactly as a bare numeric cell is, so a date-valued formula result
30
30
  * (e.g. `TODAY()`) reads back as a Date, not a serial. Shared by the buffered reader's shared-formula
31
31
  * clone resolution, which caches a result the same way a plain formula cell does. */
32
- export declare function decodeFormulaResult(type: string, valueText: string, numFmt?: string): FormulaResult;
32
+ export declare function decodeFormulaResult(type: string, valueText: string, numFmt?: string): FormulaResult | undefined;
@@ -1,7 +1,7 @@
1
1
  import { isDateFormat, serialToDate } from '../../core/date.js';
2
2
  import { unmangleFunctions } from '../../core/formula.js';
3
3
  import { isErrorCode, } from '../../core/value.js';
4
- import { boolStrict } from '../../xml/xml-read.js';
4
+ import { boolStrict, decodeSpreadsheetText, numFinite } from '../../xml/xml-read.js';
5
5
  export function decodeCellContent(raw, sharedStrings, numFmt) {
6
6
  if (raw.hasFormula) {
7
7
  const stored = unmangleFunctions(raw.formula);
@@ -21,7 +21,7 @@ function decodeValue(type, valueText, inlineText, hasValue, sharedStrings) {
21
21
  case 'inlineStr':
22
22
  return inlineText;
23
23
  case 'str':
24
- return valueText;
24
+ return decodeSpreadsheetText(valueText);
25
25
  case 'd':
26
26
  return valueText === '' ? null : new Date(valueText);
27
27
  case 's': {
@@ -32,8 +32,11 @@ function decodeValue(type, valueText, inlineText, hasValue, sharedStrings) {
32
32
  return boolStrict(valueText);
33
33
  case 'e':
34
34
  return isErrorCode(valueText) ? { error: valueText } : valueText;
35
- default:
36
- return hasValue ? Number(valueText) : null;
35
+ default: {
36
+ if (!hasValue)
37
+ return null;
38
+ return numFinite(valueText) ?? null;
39
+ }
37
40
  }
38
41
  }
39
42
  export function decodeFormulaResult(type, valueText, numFmt) {
@@ -45,12 +48,12 @@ export function decodeFormulaResult(type, valueText, numFmt) {
45
48
  function decodeResult(type, valueText) {
46
49
  switch (type) {
47
50
  case 'str':
48
- return valueText;
51
+ return decodeSpreadsheetText(valueText);
49
52
  case 'b':
50
53
  return boolStrict(valueText);
51
54
  case 'e':
52
55
  return isErrorCode(valueText) ? { error: valueText } : valueText;
53
56
  default:
54
- return Number(valueText);
57
+ return numFinite(valueText);
55
58
  }
56
59
  }
@@ -1,4 +1,4 @@
1
- import type { Color } from '../../core/style.ts';
1
+ import { type Color } from '../../core/style.ts';
2
2
  /** Serialise a {@link Color} as the attribute list a `<color>`-shaped element carries. */
3
3
  export declare function colorAttrs(color: Color): string;
4
4
  export declare function parseColor(attrs: {
@@ -1,9 +1,10 @@
1
- import { AuthoringError } from '../../errors.js';
1
+ import { parseArgb } from '../../core/style.js';
2
+ import { numFinite, numInteger } from '../../xml/xml-read.js';
3
+ import { numberText } from '../../xml/xml.js';
2
4
  function normalizeArgb(argb) {
3
- const hex = argb.startsWith('#') ? argb.slice(1) : argb;
4
- const rgb = hex.length === 6 ? `FF${hex}` : hex;
5
- if (!/^[0-9a-fA-F]{8}$/.test(rgb)) {
6
- throw new AuthoringError(`Invalid ARGB colour ${JSON.stringify(argb)}: expected 6 or 8 hexadecimal digits`);
5
+ const rgb = parseArgb(argb);
6
+ if (rgb === undefined) {
7
+ throw new SyntaxError(`Invalid ARGB colour ${JSON.stringify(argb)}: expected 6 or 8 hexadecimal digits`);
7
8
  }
8
9
  return rgb;
9
10
  }
@@ -12,31 +13,25 @@ export function colorAttrs(color) {
12
13
  if (color.argb !== undefined)
13
14
  parts.push(`rgb="${normalizeArgb(color.argb)}"`);
14
15
  if (color.theme !== undefined)
15
- parts.push(`theme="${color.theme}"`);
16
+ parts.push(`theme="${numberText(color.theme)}"`);
16
17
  if (color.tint !== undefined)
17
- parts.push(`tint="${color.tint}"`);
18
+ parts.push(`tint="${numberText(color.tint)}"`);
18
19
  if (color.indexed !== undefined)
19
- parts.push(`indexed="${color.indexed}"`);
20
+ parts.push(`indexed="${numberText(color.indexed)}"`);
20
21
  return parts.join(' ');
21
22
  }
22
23
  export function parseColor(attrs) {
23
24
  const color = {};
24
25
  if (attrs.rgb !== undefined)
25
26
  color.argb = attrs.rgb;
26
- if (attrs.theme !== undefined) {
27
- const theme = Number(attrs.theme);
28
- if (Number.isInteger(theme))
29
- color.theme = theme;
30
- }
31
- if (attrs.tint !== undefined) {
32
- const tint = Number(attrs.tint);
33
- if (Number.isFinite(tint))
34
- color.tint = tint;
35
- }
36
- if (attrs.indexed !== undefined) {
37
- const indexed = Number(attrs.indexed);
38
- if (Number.isInteger(indexed))
39
- color.indexed = indexed;
40
- }
27
+ const theme = numInteger(attrs.theme, 0);
28
+ if (theme !== undefined)
29
+ color.theme = theme;
30
+ const tint = numFinite(attrs.tint);
31
+ if (tint !== undefined)
32
+ color.tint = tint;
33
+ const indexed = numInteger(attrs.indexed, 0);
34
+ if (indexed !== undefined)
35
+ color.indexed = indexed;
41
36
  return color;
42
37
  }
@@ -20,12 +20,12 @@ export interface CommentCell {
20
20
  * in `threads`. A comment anchors to its cell regardless of the cell's value, so a note (or a thread) on
21
21
  * an otherwise-empty cell is collected too.
22
22
  *
23
- * `threads` is the conversations the *package* will carry, not simply the ones the sheet holds — the
23
+ * `threads` is the conversations the *package* will carry, not simply the ones the sheet holds. The
24
24
  * caller decides, because a fallback beside a thread whose `threadedComment` part is missing is worse
25
25
  * than no fallback at all: verified against desktop Excel, such a comment shows as neither a thread nor
26
26
  * a note, so the text disappears entirely.
27
27
  *
28
- * Ordered by cell, row-major, the way Excel writes the list so a fallback lands interleaved among the
28
+ * Ordered by cell, row-major, the way Excel writes the list, so a fallback lands interleaved among the
29
29
  * notes rather than appended after them, and the VML shapes follow the same order.
30
30
  */
31
31
  export declare function collectComments(sheet: Worksheet, threads: readonly CommentThread[]): CommentCell[];
@@ -35,13 +35,13 @@ export declare function collectComments(sheet: Worksheet, threads: readonly Comm
35
35
  * Authors are laid out the way Excel lays them out: one synthetic `tc={headId}` entry per threaded
36
36
  * conversation first, then a single anonymous author shared by every note (the model carries no note
37
37
  * author). Each comment points at its own author by index, and a fallback additionally carries the
38
- * `xr:uid` naming its thread the pair that keeps Excel treating the cell as threaded.
38
+ * `xr:uid` naming its thread: the pair that keeps Excel treating the cell as threaded.
39
39
  */
40
40
  export declare function commentsXml(comments: readonly CommentCell[]): string;
41
41
  /** The `xl/drawings/vmlDrawing{n}.vml` companion: one hidden text-box shape per comment, in the same
42
42
  * order as the comments part. Anchor coordinates place the box a couple of cells down-and-right of its
43
43
  * owner; Excel refines them on open, so the values are a sensible starting geometry rather than a
44
- * pixel-exact layout. A thread's fallback shape is `ObjectType="Note"` like any other Excel draws the
44
+ * pixel-exact layout. A thread's fallback shape is `ObjectType="Note"` like any other: Excel draws the
45
45
  * threaded-comment card itself and only needs the shape to exist. */
46
46
  export declare function vmlDrawingXml(comments: readonly CommentCell[]): string;
47
47
  /** One `<comment>` read back from a comments part. */
@@ -63,7 +63,7 @@ export declare function parseComments(xml: string): Map<string, ParsedComment>;
63
63
  * Apply a parsed comments part onto a sheet's cells as notes, addressing each by its A1 reference.
64
64
  *
65
65
  * A thread's legacy fallback is not a note and does not become one: its text is boilerplate wrapping a
66
- * copy of the conversation, so surfacing it as `cell.note` hands the caller garbage and on write it
66
+ * copy of the conversation, so surfacing it as `cell.note` hands the caller garbage, and on write it
67
67
  * would be re-emitted as a plain note, destroying the `tc=`/`xr:uid` binding and leaving Excel unable to
68
68
  * see the thread at all.
69
69
  *
@@ -1,6 +1,6 @@
1
- import { decodeAddress } from '../../core/address.js';
2
- import { escapeAttr, escapeText, textElement, XML_DECLARATION } from '../../xml/xml.js';
3
- import { localName, parseXml } from '../../xml/xml-read.js';
1
+ import { tryDecodeCellRef } from '../../core/address.js';
2
+ import { decodeSpreadsheetText, localName, numInteger, parseXml } from '../../xml/xml-read.js';
3
+ import { escapeText, textAttr, textElement, XML_DECLARATION } from '../../xml/xml.js';
4
4
  import { MARKUP_COMPATIBILITY_NS, REVISION_NS, SPREADSHEETML_NS } from './namespaces.js';
5
5
  export function collectComments(sheet, threads) {
6
6
  const fallbacks = threadFallbacks(threads);
@@ -19,10 +19,16 @@ function threadFallbacks(threads) {
19
19
  const fallbacks = [];
20
20
  for (const thread of threads) {
21
21
  const head = thread.comments[0];
22
- const { col, row } = decodeAddress(thread.ref);
23
- if (head === undefined || col === undefined || row === undefined)
22
+ const cell = tryDecodeCellRef(thread.ref);
23
+ if (head === undefined || cell === undefined)
24
24
  continue;
25
- fallbacks.push({ ref: thread.ref, row, col, text: fallbackText(thread), threadId: head.id });
25
+ fallbacks.push({
26
+ ref: thread.ref,
27
+ row: cell.row,
28
+ col: cell.col,
29
+ text: fallbackText(thread),
30
+ threadId: head.id,
31
+ });
26
32
  }
27
33
  return fallbacks;
28
34
  }
@@ -50,7 +56,7 @@ export function commentsXml(comments) {
50
56
  .map((comment) => {
51
57
  const { threadId } = comment;
52
58
  const authorId = threadId === undefined ? noteAuthorId : authorIdByThreadId.get(threadId);
53
- const uid = threadId === undefined ? '' : ` xr:uid="${escapeAttr(threadId)}"`;
59
+ const uid = textAttr('xr:uid', threadId);
54
60
  return (`<comment ref="${comment.ref}" authorId="${authorId}"${uid}>` +
55
61
  `<text><r>${textElement(comment.text)}</r></text>` +
56
62
  '</comment>');
@@ -125,10 +131,11 @@ export function parseComments(xml) {
125
131
  buffer = '';
126
132
  }
127
133
  else if (local === 'text') {
134
+ buffer = decodeSpreadsheetText(buffer);
128
135
  capture = undefined;
129
136
  }
130
137
  else if (local === 'comment' && currentRef !== undefined) {
131
- const threadId = threadIdOf(authors[Number(currentAuthorId)]);
138
+ const threadId = threadIdOf(authors[numInteger(currentAuthorId, 0) ?? -1]);
132
139
  comments.set(currentRef, {
133
140
  text: buffer,
134
141
  ...(threadId !== undefined ? { threadId } : {}),
@@ -154,6 +161,8 @@ export function applyNotes(sheet, comments) {
154
161
  for (const [ref, comment] of comments) {
155
162
  if (comment.threadId !== undefined && headIds.has(comment.threadId))
156
163
  continue;
164
+ if (tryDecodeCellRef(ref) === undefined)
165
+ continue;
157
166
  sheet.getCell(ref).note = comment.text;
158
167
  }
159
168
  }
@@ -1,4 +1,4 @@
1
- import type { ConditionalFormatting } from '../../core/conditional-formatting.ts';
1
+ import { type ConditionalFormatting } from '../../core/conditional-formatting.ts';
2
2
  import type { StyleRegistry } from './styles.ts';
3
3
  /**
4
4
  * Serialise every conditional formatting on a sheet into its `<conditionalFormatting>` blocks, in
@@ -17,15 +17,15 @@ export declare function conditionalFormattingsExtXml(formattings: readonly Condi
17
17
  /**
18
18
  * Parse a worksheet's conditional formatting into the model. The classic `<conditionalFormatting>`
19
19
  * blocks supply every rule; the x14 extension (`<x14:conditionalFormatting>` inside `<extLst>`) is
20
- * read only to enrich a classic data bar with the facets the classic element cannot carry the
21
- * gradient flag and the negative-fill and axis colours matched by the shared id the two ends link
20
+ * read only to enrich a classic data bar with the facets the classic element cannot carry (the
21
+ * gradient flag and the negative-fill and axis colours) matched by the shared id the two ends link
22
22
  * on. An extension rule with no classic counterpart (a rule that lives only in x14) is ignored, so it
23
23
  * is never half-read into a broken classic rule.
24
24
  */
25
25
  export declare function parseConditionalFormattings(xml: string): ConditionalFormatting[];
26
26
  /**
27
27
  * Extract the differential-style (`<dxf>`) fragments from styles.xml, each verbatim. Preserving the
28
- * raw XML rather than reparsing and re-serialising is what keeps a foreign dxf's number format a
28
+ * raw XML, rather than reparsing and re-serialising, is what keeps a foreign dxf's number format a
29
29
  * real format code on re-write instead of a coerced `"[object Object]"`, and keeps every conditional
30
30
  * formatting's `dxfId` index pointing at the same style it did in the source file.
31
31
  */