@shbernal/ts-xlsx 2.1.0 → 3.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 (225) hide show
  1. package/README.md +4 -1
  2. package/dist/bytes.d.ts +7 -0
  3. package/dist/bytes.js +22 -0
  4. package/dist/core/address.d.ts +60 -2
  5. package/dist/core/address.js +40 -24
  6. package/dist/core/autofilter.d.ts +5 -4
  7. package/dist/core/autofilter.js +34 -35
  8. package/dist/core/axis-handle.d.ts +10 -0
  9. package/dist/core/axis-handle.js +17 -0
  10. package/dist/core/cell.d.ts +25 -0
  11. package/dist/core/cell.js +9 -3
  12. package/dist/core/clone.d.ts +29 -0
  13. package/dist/core/clone.js +24 -0
  14. package/dist/core/color-resolution.js +2 -4
  15. package/dist/core/column.d.ts +5 -1
  16. package/dist/core/column.js +34 -35
  17. package/dist/core/comment-thread.js +2 -1
  18. package/dist/core/conditional-formatting-overlay.d.ts +2 -1
  19. package/dist/core/conditional-formatting-overlay.js +2 -2
  20. package/dist/core/conditional-formatting.d.ts +14 -6
  21. package/dist/core/conditional-formatting.js +45 -43
  22. package/dist/core/containers.d.ts +12 -0
  23. package/dist/core/containers.js +5 -0
  24. package/dist/core/data-validation-overlay.d.ts +2 -1
  25. package/dist/core/data-validation-overlay.js +4 -4
  26. package/dist/core/data-validation.d.ts +9 -3
  27. package/dist/core/data-validation.js +21 -18
  28. package/dist/core/date-format.d.ts +20 -0
  29. package/dist/core/date-format.js +160 -0
  30. package/dist/core/date.d.ts +43 -8
  31. package/dist/core/date.js +18 -2
  32. package/dist/core/formula.d.ts +26 -0
  33. package/dist/core/formula.js +20 -4
  34. package/dist/core/grid-edits.d.ts +2 -3
  35. package/dist/core/grid-edits.js +69 -70
  36. package/dist/core/grid-shift.d.ts +58 -9
  37. package/dist/core/grid-shift.js +26 -4
  38. package/dist/core/image.d.ts +14 -10
  39. package/dist/core/image.js +9 -9
  40. package/dist/core/internal.d.ts +8 -2
  41. package/dist/core/limits.d.ts +23 -0
  42. package/dist/core/limits.js +4 -0
  43. package/dist/core/merge-index.d.ts +31 -0
  44. package/dist/core/merge-index.js +60 -0
  45. package/dist/core/merge.d.ts +5 -14
  46. package/dist/core/merge.js +18 -27
  47. package/dist/core/page-setup.d.ts +72 -2
  48. package/dist/core/page-setup.js +34 -8
  49. package/dist/core/pivot-table.d.ts +1 -1
  50. package/dist/core/pivot-table.js +67 -53
  51. package/dist/core/protection.js +2 -11
  52. package/dist/core/range.js +34 -30
  53. package/dist/core/row-input.js +2 -2
  54. package/dist/core/row.d.ts +5 -1
  55. package/dist/core/row.js +22 -23
  56. package/dist/core/style.d.ts +134 -75
  57. package/dist/core/style.js +112 -53
  58. package/dist/core/table-style.d.ts +1 -1
  59. package/dist/core/table-style.js +4 -4
  60. package/dist/core/table.d.ts +28 -10
  61. package/dist/core/table.js +54 -39
  62. package/dist/core/theme.js +6 -5
  63. package/dist/core/used-extent.d.ts +44 -0
  64. package/dist/core/used-extent.js +102 -0
  65. package/dist/core/value.d.ts +9 -0
  66. package/dist/core/value.js +36 -34
  67. package/dist/core/workbook-media.d.ts +34 -0
  68. package/dist/core/workbook-media.js +44 -0
  69. package/dist/core/workbook-styles.d.ts +67 -1
  70. package/dist/core/workbook-vba.js +3 -2
  71. package/dist/core/workbook.d.ts +23 -2
  72. package/dist/core/workbook.js +24 -40
  73. package/dist/core/worksheet-comments.d.ts +2 -1
  74. package/dist/core/worksheet-comments.js +20 -13
  75. package/dist/core/worksheet-merges.d.ts +44 -0
  76. package/dist/core/worksheet-merges.js +59 -0
  77. package/dist/core/worksheet-model.d.ts +9 -1
  78. package/dist/core/worksheet-model.js +24 -9
  79. package/dist/core/worksheet.d.ts +72 -12
  80. package/dist/core/worksheet.js +68 -70
  81. package/dist/customui/ribbon.js +6 -7
  82. package/dist/entries/core.d.ts +18 -13
  83. package/dist/entries/core.js +12 -6
  84. package/dist/entries/xlsx.d.ts +1 -1
  85. package/dist/errors.d.ts +64 -0
  86. package/dist/errors.js +22 -0
  87. package/dist/hex.d.ts +8 -0
  88. package/dist/hex.js +3 -0
  89. package/dist/io/csv/delimiter.d.ts +9 -0
  90. package/dist/io/csv/delimiter.js +6 -0
  91. package/dist/io/csv/read.js +11 -4
  92. package/dist/io/csv/write.d.ts +9 -2
  93. package/dist/io/csv/write.js +9 -38
  94. package/dist/io/opc/inflate.js +15 -1
  95. package/dist/io/opc/namespaces.d.ts +16 -0
  96. package/dist/io/opc/namespaces.js +4 -0
  97. package/dist/io/opc/part-paths.d.ts +10 -0
  98. package/dist/io/opc/part-paths.js +16 -1
  99. package/dist/io/opc/read-opc.d.ts +69 -10
  100. package/dist/io/opc/read-opc.js +49 -55
  101. package/dist/io/opc/read-options.d.ts +5 -1
  102. package/dist/io/opc/rels.d.ts +1 -6
  103. package/dist/io/opc/rels.js +1 -4
  104. package/dist/io/opc/sniff-format.d.ts +3 -3
  105. package/dist/io/opc/sniff-format.js +2 -2
  106. package/dist/io/style/xf-style.d.ts +48 -2
  107. package/dist/io/style/xf-style.js +14 -0
  108. package/dist/io/xlsb/formula.d.ts +2 -2
  109. package/dist/io/xlsb/formula.js +49 -34
  110. package/dist/io/xlsb/primitives.js +8 -14
  111. package/dist/io/xlsb/ptg-functions.d.ts +19 -10
  112. package/dist/io/xlsb/ptg-functions.js +385 -596
  113. package/dist/io/xlsb/read-styles.js +50 -83
  114. package/dist/io/xlsb/read-worksheet.d.ts +20 -1
  115. package/dist/io/xlsb/read-worksheet.js +73 -89
  116. package/dist/io/xlsb/read.d.ts +6 -3
  117. package/dist/io/xlsb/read.js +47 -29
  118. package/dist/io/xlsb/record-stream.d.ts +30 -0
  119. package/dist/io/xlsb/record-stream.js +20 -0
  120. package/dist/io/xlsb/record-types.d.ts +1 -5
  121. package/dist/io/xlsb/record-types.js +1 -5
  122. package/dist/io/xlsx/cell-accumulator.d.ts +9 -2
  123. package/dist/io/xlsx/cell-accumulator.js +32 -9
  124. package/dist/io/xlsx/cell-style-resolution.d.ts +25 -0
  125. package/dist/io/xlsx/cell-style-resolution.js +29 -0
  126. package/dist/io/xlsx/cell-value.d.ts +5 -3
  127. package/dist/io/xlsx/cell-value.js +13 -15
  128. package/dist/io/xlsx/color-xml.js +3 -2
  129. package/dist/io/xlsx/column-budget.d.ts +26 -0
  130. package/dist/io/xlsx/column-budget.js +22 -0
  131. package/dist/io/xlsx/comments.d.ts +6 -2
  132. package/dist/io/xlsx/comments.js +36 -31
  133. package/dist/io/xlsx/conditional-formatting.d.ts +9 -5
  134. package/dist/io/xlsx/conditional-formatting.js +51 -46
  135. package/dist/io/xlsx/data-validation.d.ts +9 -7
  136. package/dist/io/xlsx/data-validation.js +38 -35
  137. package/dist/io/xlsx/edit-vba.d.ts +7 -2
  138. package/dist/io/xlsx/edit-vba.js +15 -29
  139. package/dist/io/xlsx/font-xml.d.ts +7 -0
  140. package/dist/io/xlsx/font-xml.js +104 -0
  141. package/dist/io/xlsx/hyperlinks.d.ts +41 -7
  142. package/dist/io/xlsx/hyperlinks.js +40 -28
  143. package/dist/io/xlsx/images.js +24 -25
  144. package/dist/io/xlsx/namespaces.d.ts +16 -0
  145. package/dist/io/xlsx/namespaces.js +7 -0
  146. package/dist/io/xlsx/package-plan.d.ts +23 -3
  147. package/dist/io/xlsx/package-plan.js +66 -25
  148. package/dist/io/xlsx/part-names.d.ts +41 -0
  149. package/dist/io/xlsx/part-names.js +46 -0
  150. package/dist/io/xlsx/pivot.js +8 -7
  151. package/dist/io/xlsx/read-parts.d.ts +59 -0
  152. package/dist/io/xlsx/read-parts.js +252 -0
  153. package/dist/io/xlsx/read-pivot.js +3 -1
  154. package/dist/io/xlsx/read-repair.d.ts +26 -0
  155. package/dist/io/xlsx/read-repair.js +39 -0
  156. package/dist/io/xlsx/{rich-runs.d.ts → read-rich-runs.d.ts} +1 -1
  157. package/dist/io/xlsx/{rich-runs.js → read-rich-runs.js} +7 -3
  158. package/dist/io/xlsx/read-rows.d.ts +18 -5
  159. package/dist/io/xlsx/read-rows.js +50 -37
  160. package/dist/io/xlsx/read-shared-strings.js +5 -3
  161. package/dist/io/xlsx/read-styles.d.ts +1 -6
  162. package/dist/io/xlsx/read-styles.js +65 -109
  163. package/dist/io/xlsx/read-workbook-xml.d.ts +36 -0
  164. package/dist/io/xlsx/read-workbook-xml.js +170 -0
  165. package/dist/io/xlsx/read-worksheet.d.ts +11 -1
  166. package/dist/io/xlsx/read-worksheet.js +51 -193
  167. package/dist/io/xlsx/read.d.ts +5 -13
  168. package/dist/io/xlsx/read.js +111 -436
  169. package/dist/io/xlsx/rich-text.js +1 -1
  170. package/dist/io/xlsx/row-position.d.ts +18 -0
  171. package/dist/io/xlsx/row-position.js +11 -0
  172. package/dist/io/xlsx/row-xml.d.ts +128 -0
  173. package/dist/io/xlsx/row-xml.js +203 -0
  174. package/dist/io/xlsx/shared-formulas.js +2 -2
  175. package/dist/io/xlsx/sheet-properties.d.ts +10 -1
  176. package/dist/io/xlsx/sheet-properties.js +166 -37
  177. package/dist/io/xlsx/style-elements.d.ts +43 -0
  178. package/dist/io/xlsx/style-elements.js +156 -0
  179. package/dist/io/xlsx/styles.d.ts +12 -3
  180. package/dist/io/xlsx/styles.js +5 -204
  181. package/dist/io/xlsx/tables.js +11 -14
  182. package/dist/io/xlsx/theme-xml.d.ts +26 -4
  183. package/dist/io/xlsx/theme-xml.js +107 -69
  184. package/dist/io/xlsx/threaded-comments.d.ts +8 -10
  185. package/dist/io/xlsx/threaded-comments.js +3 -1
  186. package/dist/io/xlsx/workbook-xml.d.ts +54 -4
  187. package/dist/io/xlsx/workbook-xml.js +84 -57
  188. package/dist/io/xlsx/worksheet-xml.d.ts +23 -70
  189. package/dist/io/xlsx/worksheet-xml.js +59 -242
  190. package/dist/io/xlsx/write-stream.d.ts +71 -16
  191. package/dist/io/xlsx/write-stream.js +79 -41
  192. package/dist/io/xlsx/write.d.ts +6 -2
  193. package/dist/io/xlsx/write.js +217 -153
  194. package/dist/rel-type.d.ts +16 -0
  195. package/dist/rel-type.js +6 -0
  196. package/dist/token-set.d.ts +28 -0
  197. package/dist/token-set.js +7 -0
  198. package/dist/vba/bytes.d.ts +14 -5
  199. package/dist/vba/bytes.js +16 -6
  200. package/dist/vba/cfb-format.d.ts +32 -0
  201. package/dist/vba/cfb-format.js +13 -0
  202. package/dist/vba/cfb-writer.d.ts +9 -0
  203. package/dist/vba/cfb-writer.js +28 -25
  204. package/dist/vba/cfb.d.ts +13 -1
  205. package/dist/vba/cfb.js +73 -57
  206. package/dist/vba/codepage.js +2 -1
  207. package/dist/vba/errors.d.ts +7 -0
  208. package/dist/vba/ms-ovba.js +79 -23
  209. package/dist/vba/project-editor.js +35 -40
  210. package/dist/vba/project.d.ts +11 -1
  211. package/dist/vba/project.js +40 -21
  212. package/dist/vba/vba-encoding.js +5 -6
  213. package/dist/xml/xml-attrs.d.ts +37 -0
  214. package/dist/xml/xml-attrs.js +30 -0
  215. package/dist/xml/xml-chars.d.ts +40 -0
  216. package/dist/xml/xml-chars.js +12 -0
  217. package/dist/xml/xml-namespaces.d.ts +31 -0
  218. package/dist/xml/xml-namespaces.js +50 -0
  219. package/dist/xml/xml-read.d.ts +128 -81
  220. package/dist/xml/xml-read.js +161 -178
  221. package/dist/xml/xml-scan.d.ts +78 -0
  222. package/dist/xml/xml-scan.js +180 -0
  223. package/dist/xml/xml.d.ts +29 -28
  224. package/dist/xml/xml.js +24 -24
  225. package/package.json +13 -9
@@ -16,7 +16,6 @@ export declare const BRT: {
16
16
  readonly ColInfo: 60;
17
17
  readonly MergeCell: 176;
18
18
  readonly SSTItem: 19;
19
- readonly BeginSst: 159;
20
19
  readonly EndSst: 160;
21
20
  readonly Fmt: 44;
22
21
  readonly Font: 43;
@@ -38,6 +37,7 @@ export declare const BRT: {
38
37
  readonly EndCellXFs: 618;
39
38
  readonly BeginStyles: 619;
40
39
  readonly EndStyles: 620;
40
+ readonly WbProp: 153;
41
41
  readonly BundleSh: 156;
42
42
  readonly BeginBundleShs: 143;
43
43
  readonly EndBundleShs: 144;
@@ -46,10 +46,6 @@ export declare const BRT: {
46
46
  readonly EndExternals: 354;
47
47
  readonly SupSelf: 357;
48
48
  readonly ExternSheet: 362;
49
- readonly WsProp: 147;
50
- readonly WsDim: 148;
51
49
  readonly WsFmtInfo: 485;
52
- readonly BeginSheetData: 145;
53
- readonly EndSheetData: 146;
54
50
  readonly ArrFmla: 426;
55
51
  };
@@ -15,7 +15,6 @@ export const BRT = {
15
15
  ColInfo: 60,
16
16
  MergeCell: 176,
17
17
  SSTItem: 19,
18
- BeginSst: 159,
19
18
  EndSst: 160,
20
19
  Fmt: 44,
21
20
  Font: 43,
@@ -37,6 +36,7 @@ export const BRT = {
37
36
  EndCellXFs: 618,
38
37
  BeginStyles: 619,
39
38
  EndStyles: 620,
39
+ WbProp: 153,
40
40
  BundleSh: 156,
41
41
  BeginBundleShs: 143,
42
42
  EndBundleShs: 144,
@@ -45,10 +45,6 @@ export const BRT = {
45
45
  EndExternals: 354,
46
46
  SupSelf: 357,
47
47
  ExternSheet: 362,
48
- WsProp: 147,
49
- WsDim: 148,
50
48
  WsFmtInfo: 485,
51
- BeginSheetData: 145,
52
- EndSheetData: 146,
53
49
  ArrFmla: 426,
54
50
  };
@@ -1,19 +1,26 @@
1
+ import type { DateEpoch } from '../../core/date.ts';
1
2
  import type { CellValue } from '../../core/value.ts';
2
3
  import type { Worksheet } from '../../core/worksheet.ts';
3
- import { type XmlAttributes } from '../../xml/xml-read.ts';
4
+ import { type XmlAttributes } from '../../xml/xml-scan.ts';
4
5
  import { type XfStyle } from '../style/xf-style.ts';
5
6
  import { type SharedString } from './cell-value.ts';
6
7
  export declare class CellAccumulator {
7
8
  #private;
8
9
  constructor(options: {
9
10
  readonly richRuns: boolean;
11
+ readonly dateEpoch: DateEpoch;
10
12
  });
11
13
  /** This cell's `<c r>` address (`"B3"`), or '' when it carried none. */
12
14
  get ref(): string;
13
15
  /** This cell's own `<c s>` style index, or -1 when it carries none. */
14
16
  get styleIndex(): number;
15
- /** This cell's 1-based column, or -1 when its address was absent or unparseable. */
17
+ /** This cell's 1-based column, or -1 when its address was unparseable. */
16
18
  get col(): number;
19
+ /**
20
+ * Open a `<row>`: the cells that follow belong to it, and the next one with no `r` of its own is
21
+ * its first column. Both readers call this where they already tell the style resolver a row opened.
22
+ */
23
+ openRow(number: number): void;
17
24
  /**
18
25
  * The text gathered since the current element opened. A caller reads it for the elements it
19
26
  * captures itself (see {@link capture}); the machine reads it for its own.
@@ -1,15 +1,17 @@
1
- import { encodeAddress, tryDecodeCellRef } from '../../core/address.js';
1
+ import { encodeAddress, MAX_COLUMN, tryDecodeCellRef } from '../../core/address.js';
2
2
  import { translateFormula, unmangleFunctions } from '../../core/formula.js';
3
- import { boolStrict, numInteger } from '../../xml/xml-read.js';
3
+ import { numInteger } from '../../xml/xml-attrs.js';
4
+ import { boolStrict } from '../../xml/xml-scan.js';
4
5
  import { applyXfToCell } from '../style/xf-style.js';
5
6
  import { decodeCellContent, decodeFormulaResult, } from './cell-value.js';
6
- import { RunAccumulator } from './rich-runs.js';
7
+ import { RunAccumulator } from './read-rich-runs.js';
7
8
  export class CellAccumulator {
8
9
  #ref = '';
9
10
  #type = '';
10
11
  #style = -1;
11
12
  #col = -1;
12
13
  #row = -1;
14
+ #nextCol = 1;
13
15
  #formula = '';
14
16
  #formulaShared = false;
15
17
  #formulaSi = -1;
@@ -22,8 +24,10 @@ export class CellAccumulator {
22
24
  #masters = new Map();
23
25
  #capture = false;
24
26
  #text = '';
27
+ #dateEpoch;
25
28
  constructor(options) {
26
29
  this.#runs = new RunAccumulator({ container: 'is', readRuns: options.richRuns });
30
+ this.#dateEpoch = options.dateEpoch;
27
31
  }
28
32
  get ref() {
29
33
  return this.#ref;
@@ -34,13 +38,14 @@ export class CellAccumulator {
34
38
  get col() {
35
39
  return this.#col;
36
40
  }
41
+ openRow(number) {
42
+ this.#row = number;
43
+ this.#nextCol = 1;
44
+ }
37
45
  #beginCell(attrs) {
38
46
  this.#type = attrs.t ?? '';
39
47
  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;
48
+ this.#placeCell(attrs.r);
44
49
  this.#formula = '';
45
50
  this.#valueText = '';
46
51
  this.#runs.beginContainer();
@@ -51,6 +56,24 @@ export class CellAccumulator {
51
56
  this.#sharedClone = false;
52
57
  this.#dataTable = null;
53
58
  }
59
+ #placeCell(ref) {
60
+ const decoded = ref === undefined ? undefined : tryDecodeCellRef(ref);
61
+ if (decoded !== undefined) {
62
+ this.#ref = ref ?? '';
63
+ this.#col = decoded.col;
64
+ this.#row = decoded.row;
65
+ }
66
+ else if (ref === undefined && this.#row > 0 && this.#nextCol <= MAX_COLUMN) {
67
+ this.#col = this.#nextCol;
68
+ this.#ref = encodeAddress(this.#col, this.#row);
69
+ }
70
+ else {
71
+ this.#ref = '';
72
+ this.#col = -1;
73
+ }
74
+ if (this.#col > 0)
75
+ this.#nextCol = this.#col + 1;
76
+ }
54
77
  #beginFormula(attrs, selfClosing) {
55
78
  this.#formulaShared = attrs.t === 'shared';
56
79
  this.#formulaSi = numInteger(attrs.si, 0) ?? -1;
@@ -165,7 +188,7 @@ export class CellAccumulator {
165
188
  cachedResult(style) {
166
189
  if (!this.#hasValue)
167
190
  return {};
168
- const result = decodeFormulaResult(this.#type, this.#valueText, style?.numFmt);
191
+ const result = decodeFormulaResult(this.#type, this.#valueText, style?.numFmt, this.#dateEpoch);
169
192
  return result === undefined ? {} : { result };
170
193
  }
171
194
  decode(sharedStrings, style) {
@@ -178,6 +201,6 @@ export class CellAccumulator {
178
201
  inlineText: this.#runs.plainText,
179
202
  richTextRuns: this.#runs.runs,
180
203
  };
181
- return decodeCellContent(raw, sharedStrings, style?.numFmt);
204
+ return decodeCellContent(raw, sharedStrings, style?.numFmt, this.#dateEpoch);
182
205
  }
183
206
  }
@@ -0,0 +1,25 @@
1
+ /** The attributes this resolver reads, the subset both readers' scanners already hand it. */
2
+ interface StyleAttributes {
3
+ readonly [key: string]: string | undefined;
4
+ }
5
+ /**
6
+ * The cell → row → column style resolution for one worksheet, fed by the same elements in the same
7
+ * order both readers already visit them: `<col>` before any cell references it, `<row>` before its
8
+ * own cells.
9
+ */
10
+ export declare class CellStyleResolver {
11
+ #private;
12
+ /** Record a `<col min max style>` span's default for the columns it covers. */
13
+ noteColumnSpan(first: number, last: number, attrs: StyleAttributes): void;
14
+ /** Open a `<row>`, taking the format it declares for its own bare cells. */
15
+ openRow(attrs: StyleAttributes): void;
16
+ /** Close the row, so a cell outside one never inherits the last row's format. */
17
+ closeRow(): void;
18
+ /**
19
+ * The `xfStyles` index a cell's format resolves to, or `-1` when nothing declares one. A row's
20
+ * format applies only when the row is marked `customFormat`; without that flag the `s` on a `<row>`
21
+ * describes the row itself and is not inherited by its cells.
22
+ */
23
+ indexFor(col: number, cellStyleIndex: number): number;
24
+ }
25
+ export {};
@@ -0,0 +1,29 @@
1
+ import { numInteger } from '../../xml/xml-attrs.js';
2
+ import { boolStrict } from '../../xml/xml-scan.js';
3
+ export class CellStyleResolver {
4
+ #columnStyle = new Map();
5
+ #rowStyle = -1;
6
+ #rowCustomFormat = false;
7
+ noteColumnSpan(first, last, attrs) {
8
+ const styleIndex = numInteger(attrs.style, 0) ?? -1;
9
+ if (styleIndex < 0)
10
+ return;
11
+ for (let index = first; index <= last; index++)
12
+ this.#columnStyle.set(index, styleIndex);
13
+ }
14
+ openRow(attrs) {
15
+ this.#rowStyle = numInteger(attrs.s, 0) ?? -1;
16
+ this.#rowCustomFormat = boolStrict(attrs.customFormat);
17
+ }
18
+ closeRow() {
19
+ this.#rowStyle = -1;
20
+ this.#rowCustomFormat = false;
21
+ }
22
+ indexFor(col, cellStyleIndex) {
23
+ if (cellStyleIndex >= 0)
24
+ return cellStyleIndex;
25
+ if (this.#rowCustomFormat && this.#rowStyle >= 0)
26
+ return this.#rowStyle;
27
+ return this.#columnStyle.get(col) ?? -1;
28
+ }
29
+ }
@@ -1,3 +1,4 @@
1
+ import { type DateEpoch } from '../../core/date.ts';
1
2
  import { type CellValue, type FormulaResult, type RichTextRun, type RichTextValue } from '../../core/value.ts';
2
3
  /**
3
4
  * One entry of the shared-strings pool. A `<si>` built from a bare `<t>` is a plain string; a `<si>`
@@ -22,11 +23,12 @@ export interface RawCell {
22
23
  * Decode a gathered cell into its model value. A formula cell becomes a `{formula, result?}`
23
24
  * object (the on-disk `_xlfn.`/`_xlpm.` mangling stripped back to the readable name); a plain
24
25
  * numeric cell under a date number format becomes a {@link Date}; everything else decodes by its
25
- * `t` type. `numFmt` is the cell's resolved number-format code, used only for date detection.
26
+ * `t` type. `numFmt` is the cell's resolved number-format code, used only for date detection, and
27
+ * `epoch` the workbook's date system, which is what a serial under such a format counts from.
26
28
  */
27
- export declare function decodeCellContent(raw: RawCell, sharedStrings: readonly SharedString[], numFmt: string | undefined): CellValue;
29
+ export declare function decodeCellContent(raw: RawCell, sharedStrings: readonly SharedString[], numFmt: string | undefined, epoch: DateEpoch): CellValue;
28
30
  /** Decode a formula's cached `<v>` result by its `t` type, coercing a numeric result under a date
29
31
  * `numFmt` to a {@link Date} exactly as a bare numeric cell is, so a date-valued formula result
30
32
  * (e.g. `TODAY()`) reads back as a Date, not a serial. Shared by the buffered reader's shared-formula
31
33
  * 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 | undefined;
34
+ export declare function decodeFormulaResult(type: string, valueText: string, numFmt: string | undefined, epoch: DateEpoch): FormulaResult | undefined;
@@ -1,20 +1,21 @@
1
- import { isDateFormat, serialToDate } from '../../core/date.js';
1
+ import { coerceDateSerial, parseDateText } from '../../core/date.js';
2
2
  import { unmangleFunctions } from '../../core/formula.js';
3
3
  import { isErrorCode, } from '../../core/value.js';
4
- import { boolStrict, decodeSpreadsheetText, numFinite } from '../../xml/xml-read.js';
5
- export function decodeCellContent(raw, sharedStrings, numFmt) {
4
+ import { decodeSpreadsheetText, numFinite, numInteger } from '../../xml/xml-attrs.js';
5
+ import { boolStrict } from '../../xml/xml-scan.js';
6
+ export function decodeCellContent(raw, sharedStrings, numFmt, epoch) {
6
7
  if (raw.hasFormula) {
7
8
  const stored = unmangleFunctions(raw.formula);
8
- const result = raw.hasValue ? decodeFormulaResult(raw.type, raw.valueText, numFmt) : undefined;
9
+ const result = raw.hasValue
10
+ ? decodeFormulaResult(raw.type, raw.valueText, numFmt, epoch)
11
+ : undefined;
9
12
  return result === undefined ? { formula: stored } : { formula: stored, result };
10
13
  }
11
14
  if (raw.type === 'inlineStr' && raw.richTextRuns !== undefined && raw.richTextRuns.length > 0) {
12
15
  return { richText: raw.richTextRuns };
13
16
  }
14
17
  const value = decodeValue(raw.type, raw.valueText, raw.inlineText, raw.hasValue, sharedStrings);
15
- return typeof value === 'number' && numFmt !== undefined && isDateFormat(numFmt)
16
- ? serialToDate(value)
17
- : value;
18
+ return coerceDateSerial(value, numFmt, epoch);
18
19
  }
19
20
  function decodeValue(type, valueText, inlineText, hasValue, sharedStrings) {
20
21
  switch (type) {
@@ -23,10 +24,10 @@ function decodeValue(type, valueText, inlineText, hasValue, sharedStrings) {
23
24
  case 'str':
24
25
  return decodeSpreadsheetText(valueText);
25
26
  case 'd':
26
- return valueText === '' ? null : new Date(valueText);
27
+ return parseDateText(valueText);
27
28
  case 's': {
28
- const index = Number(valueText);
29
- return Number.isInteger(index) ? (sharedStrings[index] ?? '') : '';
29
+ const index = numInteger(valueText, 0);
30
+ return index === undefined ? '' : (sharedStrings[index] ?? '');
30
31
  }
31
32
  case 'b':
32
33
  return boolStrict(valueText);
@@ -39,11 +40,8 @@ function decodeValue(type, valueText, inlineText, hasValue, sharedStrings) {
39
40
  }
40
41
  }
41
42
  }
42
- export function decodeFormulaResult(type, valueText, numFmt) {
43
- const result = decodeResult(type, valueText);
44
- return typeof result === 'number' && numFmt !== undefined && isDateFormat(numFmt)
45
- ? serialToDate(result)
46
- : result;
43
+ export function decodeFormulaResult(type, valueText, numFmt, epoch) {
44
+ return coerceDateSerial(decodeResult(type, valueText), numFmt, epoch);
47
45
  }
48
46
  function decodeResult(type, valueText) {
49
47
  switch (type) {
@@ -1,10 +1,11 @@
1
1
  import { parseArgb } from '../../core/style.js';
2
- import { numFinite, numInteger } from '../../xml/xml-read.js';
2
+ import { quoted } from '../../errors.js';
3
+ import { numFinite, numInteger } from '../../xml/xml-attrs.js';
3
4
  import { numberText } from '../../xml/xml.js';
4
5
  function normalizeArgb(argb) {
5
6
  const rgb = parseArgb(argb);
6
7
  if (rgb === undefined) {
7
- throw new SyntaxError(`Invalid ARGB colour ${JSON.stringify(argb)}: expected 6 or 8 hexadecimal digits`);
8
+ throw new SyntaxError(`Invalid ARGB colour ${quoted(argb)}: expected 6 or 8 hexadecimal digits`);
8
9
  }
9
10
  return rgb;
10
11
  }
@@ -0,0 +1,26 @@
1
+ import { type XmlAttributes } from '../../xml/xml-scan.ts';
2
+ /**
3
+ * A per-sheet allowance of column touches, spent by each `<col>` span the reader applies.
4
+ *
5
+ * A span past the allowance is truncated, and one starting past it is dropped, rather than the read
6
+ * being refused: that is the stance both readers already take on an out-of-grid span, and it means a
7
+ * hostile file loses formatting it could not have meant while a real one is untouched.
8
+ */
9
+ export declare class ColumnRecordBudget {
10
+ #private;
11
+ /** The last column of `[first, last]` this sheet can still afford, or `undefined` when none is. */
12
+ take(first: number, last: number): number | undefined;
13
+ }
14
+ /**
15
+ * The columns a `<col min max>` element actually applies to, or `undefined` for one that applies to
16
+ * none: unreadable bounds, a span starting past the grid, or a budget already spent.
17
+ *
18
+ * Beside the budget because the two decisions are one decision. Both readers had their own copy, and
19
+ * the copies had already drifted: one tested `min > MAX_COLUMN` and the other did not, which happened
20
+ * not to matter only because `budget.take` returns `undefined` when `first > last` - an accident of
21
+ * this class's contract rather than an agreement between the readers.
22
+ */
23
+ export declare function takeColumnSpan(attrs: XmlAttributes, budget: ColumnRecordBudget): {
24
+ first: number;
25
+ last: number;
26
+ } | undefined;
@@ -0,0 +1,22 @@
1
+ import { MAX_COLUMN } from '../../core/address.js';
2
+ import { numInteger } from '../../xml/xml-attrs.js';
3
+ import {} from '../../xml/xml-scan.js';
4
+ const MAX_COLUMN_RECORD_TOUCHES = MAX_COLUMN * 4;
5
+ export class ColumnRecordBudget {
6
+ #remaining = MAX_COLUMN_RECORD_TOUCHES;
7
+ take(first, last) {
8
+ if (this.#remaining <= 0 || first > last)
9
+ return undefined;
10
+ const affordable = Math.min(last, first + this.#remaining - 1);
11
+ this.#remaining -= affordable - first + 1;
12
+ return affordable;
13
+ }
14
+ }
15
+ export function takeColumnSpan(attrs, budget) {
16
+ const first = numInteger(attrs.min, 1);
17
+ const declared = numInteger(attrs.max, 1);
18
+ if (first === undefined || declared === undefined || first > MAX_COLUMN)
19
+ return undefined;
20
+ const last = budget.take(first, Math.min(declared, MAX_COLUMN));
21
+ return last === undefined ? undefined : { first, last };
22
+ }
@@ -1,6 +1,8 @@
1
+ import type { Cell } from '../../core/cell.ts';
1
2
  import type { CommentThread } from '../../core/comment-thread.ts';
2
3
  import type { Worksheet } from '../../core/worksheet.ts';
3
- /** A comment bound for `comments{n}.xml`, paired with the coordinates the VML anchor needs. */
4
+ /** A comment bound for `comments{n}.xml`, paired with the coordinates the VML anchor needs.
5
+ */
4
6
  export interface CommentCell {
5
7
  readonly ref: string;
6
8
  /** 1-based row of the commented cell. */
@@ -28,7 +30,9 @@ export interface CommentCell {
28
30
  * Ordered by cell, row-major, the way Excel writes the list, so a fallback lands interleaved among the
29
31
  * notes rather than appended after them, and the VML shapes follow the same order.
30
32
  */
31
- export declare function collectComments(sheet: Worksheet, threads: readonly CommentThread[]): CommentCell[];
33
+ export declare function collectComments(cells: Iterable<Cell>, threads: readonly CommentThread[], alreadyCollected?: readonly CommentCell[]): CommentCell[];
34
+ /** The notes among a run of cells, for a writer that must ask before the cells are evicted. */
35
+ export declare function collectNotes(cells: Iterable<Cell>): CommentCell[];
32
36
  /**
33
37
  * The `xl/comments{n}.xml` part.
34
38
  *
@@ -1,19 +1,32 @@
1
1
  import { tryDecodeCellRef } from '../../core/address.js';
2
- import { decodeSpreadsheetText, localName, numInteger, parseXml } from '../../xml/xml-read.js';
2
+ import { decodeSpreadsheetText, numInteger } from '../../xml/xml-attrs.js';
3
+ import { parseXml, TextCapture } from '../../xml/xml-read.js';
4
+ import { localName } from '../../xml/xml-scan.js';
3
5
  import { escapeText, textAttr, textElement, XML_DECLARATION } from '../../xml/xml.js';
4
6
  import { MARKUP_COMPATIBILITY_NS, REVISION_NS, SPREADSHEETML_NS } from './namespaces.js';
5
- export function collectComments(sheet, threads) {
7
+ export function collectComments(cells, threads, alreadyCollected = []) {
6
8
  const fallbacks = threadFallbacks(threads);
7
9
  const anchored = new Set(fallbacks.map((fallback) => fallback.ref));
8
- const comments = [...fallbacks];
9
- for (const { cells } of sheet.rows()) {
10
- for (const cell of cells) {
11
- if (cell.note === undefined || anchored.has(cell.address))
12
- continue;
13
- comments.push({ ref: cell.address, row: cell.row, col: cell.col, text: cell.note });
14
- }
10
+ const comments = [...fallbacks, ...alreadyCollected];
11
+ for (const cell of cells) {
12
+ if (cell.note === undefined || anchored.has(cell.address))
13
+ continue;
14
+ comments.push(noteOn(cell, cell.note));
15
+ }
16
+ return comments
17
+ .filter((comment) => comment.threadId !== undefined || !anchored.has(comment.ref))
18
+ .sort((a, b) => a.row - b.row || a.col - b.col);
19
+ }
20
+ export function collectNotes(cells) {
21
+ const notes = [];
22
+ for (const cell of cells) {
23
+ if (cell.note !== undefined)
24
+ notes.push(noteOn(cell, cell.note));
15
25
  }
16
- return comments.sort((a, b) => a.row - b.row || a.col - b.col);
26
+ return notes;
27
+ }
28
+ function noteOn(cell, text) {
29
+ return { ref: cell.address, row: cell.row, col: cell.col, text };
17
30
  }
18
31
  function threadFallbacks(threads) {
19
32
  const fallbacks = [];
@@ -75,13 +88,14 @@ const VML_HEADER = '<xml xmlns:v="urn:schemas-microsoft-com:vml" ' +
75
88
  '<v:shapetype id="_x0000_t202" coordsize="21600,21600" o:spt="202" ' +
76
89
  'path="m,l,21600r21600,l21600,xe"><v:stroke joinstyle="miter"/>' +
77
90
  '<v:path gradientshapeok="t" o:connecttype="rect"/></v:shapetype>';
91
+ const FIRST_SHAPE_ID = 1025;
78
92
  export function vmlDrawingXml(comments) {
79
93
  const shapes = comments
80
94
  .map((comment, i) => {
81
95
  const row0 = comment.row - 1;
82
96
  const col0 = comment.col - 1;
83
97
  const anchor = `${col0 + 1}, 15, ${row0}, 2, ${col0 + 3}, 15, ${row0 + 4}, 4`;
84
- return (`<v:shape id="_x0000_s${1025 + i}" type="#_x0000_t202" ` +
98
+ return (`<v:shape id="_x0000_s${FIRST_SHAPE_ID + i}" type="#_x0000_t202" ` +
85
99
  'style="position:absolute;margin-left:59.25pt;margin-top:1.5pt;width:108pt;height:59.25pt;' +
86
100
  `z-index:${i + 1};visibility:hidden" fillcolor="#ffffe1" o:insetmode="auto">` +
87
101
  '<v:fill color2="#ffffe1"/><v:shadow on="t" color="black" obscured="t"/>' +
@@ -94,50 +108,41 @@ export function vmlDrawingXml(comments) {
94
108
  .join('');
95
109
  return `${VML_HEADER}${shapes}</xml>`;
96
110
  }
97
- const COMMENT_EMPTY_CLOSES = new Set(['author']);
111
+ const COMMENT_EMPTY_CLOSES = new Set(['author', 'text']);
98
112
  const THREAD_AUTHOR_PREFIX = 'tc=';
99
113
  export function parseComments(xml) {
100
114
  const comments = new Map();
101
115
  const authors = [];
102
116
  let currentRef;
103
117
  let currentAuthorId;
104
- let capture;
105
- let buffer = '';
118
+ const capture = new TextCapture(['author', 'text']);
119
+ let body = '';
106
120
  parseXml(xml, {
107
- onOpen(name, attrs) {
121
+ onOpen(name, attrs, selfClosing) {
108
122
  const local = localName(name);
109
123
  if (local === 'comment') {
110
124
  currentRef = attrs.ref;
111
125
  currentAuthorId = attrs.authorId;
112
- buffer = '';
113
- }
114
- else if (local === 'author') {
115
- capture = 'author';
116
- buffer = '';
117
- }
118
- else if (local === 'text') {
119
- capture = 'text';
126
+ body = '';
120
127
  }
128
+ capture.open(local, selfClosing);
121
129
  },
122
130
  onText(text) {
123
- if (capture !== undefined)
124
- buffer += text;
131
+ capture.text(text);
125
132
  },
126
133
  onClose(name) {
127
134
  const local = localName(name);
135
+ const text = capture.close(local);
128
136
  if (local === 'author') {
129
- authors.push(buffer);
130
- capture = undefined;
131
- buffer = '';
137
+ authors.push(text ?? '');
132
138
  }
133
139
  else if (local === 'text') {
134
- buffer = decodeSpreadsheetText(buffer);
135
- capture = undefined;
140
+ body = decodeSpreadsheetText(text ?? '');
136
141
  }
137
142
  else if (local === 'comment' && currentRef !== undefined) {
138
143
  const threadId = threadIdOf(authors[numInteger(currentAuthorId, 0) ?? -1]);
139
144
  comments.set(currentRef, {
140
- text: buffer,
145
+ text: body,
141
146
  ...(threadId !== undefined ? { threadId } : {}),
142
147
  });
143
148
  currentRef = undefined;
@@ -1,11 +1,15 @@
1
- import { type ConditionalFormatting } from '../../core/conditional-formatting.ts';
1
+ import { type ConditionalFormatting, type ConditionalFormattingRule } from '../../core/conditional-formatting.ts';
2
+ import { type CollectingPass } from '../../xml/xml-read.ts';
2
3
  import type { StyleRegistry } from './styles.ts';
4
+ /** Each extended data bar's link id, keyed by rule identity: the one map both passes are handed. */
5
+ export type DataBarExtLinks = ReadonlyMap<ConditionalFormattingRule, string>;
6
+ export declare function dataBarExtLinks(formattings: readonly ConditionalFormatting[]): DataBarExtLinks;
3
7
  /**
4
8
  * Serialise every conditional formatting on a sheet into its `<conditionalFormatting>` blocks, in
5
9
  * insertion order. Returns '' when the sheet has none. A rule missing a `priority` is assigned the
6
10
  * next free one, so the output always satisfies Excel's requirement that every cfRule carry one.
7
11
  */
8
- export declare function conditionalFormattingsXml(formattings: readonly ConditionalFormatting[], styles: StyleRegistry): string;
12
+ export declare function conditionalFormattingsXml(formattings: readonly ConditionalFormatting[], styles: StyleRegistry, extLinks: DataBarExtLinks): string;
9
13
  /**
10
14
  * The worksheet `<extLst>` `<ext>` carrying the x14 data-bar extensions, or '' when no data bar needs
11
15
  * one. Each extension's `<x14:cfRule id>` is read from the same {@link dataBarExtLinks} map the classic
@@ -13,16 +17,16 @@ export declare function conditionalFormattingsXml(formattings: readonly Conditio
13
17
  * (no `<extLst>` wrapper) so the worksheet serialiser can gather it into a single `<extLst>` beside the
14
18
  * data-validation extension.
15
19
  */
16
- export declare function conditionalFormattingsExtXml(formattings: readonly ConditionalFormatting[]): string;
20
+ export declare function conditionalFormattingsExtXml(formattings: readonly ConditionalFormatting[], extLinks: DataBarExtLinks): string;
17
21
  /**
18
- * Parse a worksheet's conditional formatting into the model. The classic `<conditionalFormatting>`
22
+ * A pass reading a worksheet's conditional formatting into the model. The classic `<conditionalFormatting>`
19
23
  * blocks supply every rule; the x14 extension (`<x14:conditionalFormatting>` inside `<extLst>`) is
20
24
  * read only to enrich a classic data bar with the facets the classic element cannot carry (the
21
25
  * gradient flag and the negative-fill and axis colours) matched by the shared id the two ends link
22
26
  * on. An extension rule with no classic counterpart (a rule that lives only in x14) is ignored, so it
23
27
  * is never half-read into a broken classic rule.
24
28
  */
25
- export declare function parseConditionalFormattings(xml: string): ConditionalFormatting[];
29
+ export declare function conditionalFormattingPass(): CollectingPass<ConditionalFormatting[]>;
26
30
  /**
27
31
  * Extract the differential-style (`<dxf>`) fragments from styles.xml, each verbatim. Preserving the
28
32
  * raw XML, rather than reparsing and re-serialising, is what keeps a foreign dxf's number format a