@shbernal/ts-xlsx 3.0.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 (213) 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 +4 -3
  7. package/dist/core/autofilter.js +31 -30
  8. package/dist/core/axis-handle.d.ts +2 -0
  9. package/dist/core/axis-handle.js +5 -2
  10. package/dist/core/cell.d.ts +4 -3
  11. package/dist/core/cell.js +4 -6
  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 +1 -0
  16. package/dist/core/column.js +3 -0
  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 +9 -1
  21. package/dist/core/conditional-formatting.js +34 -18
  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 +6 -0
  27. package/dist/core/data-validation.js +15 -4
  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 +13 -9
  39. package/dist/core/image.js +7 -5
  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.d.ts +5 -4
  44. package/dist/core/merge.js +18 -19
  45. package/dist/core/pivot-table.js +63 -45
  46. package/dist/core/protection.js +2 -11
  47. package/dist/core/range.js +34 -30
  48. package/dist/core/row-input.js +2 -2
  49. package/dist/core/row.d.ts +1 -0
  50. package/dist/core/row.js +3 -0
  51. package/dist/core/style.d.ts +85 -70
  52. package/dist/core/style.js +92 -28
  53. package/dist/core/table-style.d.ts +1 -1
  54. package/dist/core/table-style.js +4 -4
  55. package/dist/core/table.d.ts +27 -9
  56. package/dist/core/table.js +51 -34
  57. package/dist/core/theme.js +6 -5
  58. package/dist/core/value.d.ts +9 -0
  59. package/dist/core/value.js +36 -34
  60. package/dist/core/workbook-media.d.ts +34 -0
  61. package/dist/core/workbook-media.js +44 -0
  62. package/dist/core/workbook-styles.d.ts +67 -1
  63. package/dist/core/workbook-vba.js +3 -2
  64. package/dist/core/workbook.d.ts +23 -2
  65. package/dist/core/workbook.js +24 -40
  66. package/dist/core/worksheet-comments.d.ts +2 -1
  67. package/dist/core/worksheet-comments.js +20 -13
  68. package/dist/core/worksheet-merges.d.ts +44 -0
  69. package/dist/core/worksheet-merges.js +59 -0
  70. package/dist/core/worksheet-model.d.ts +9 -1
  71. package/dist/core/worksheet-model.js +24 -9
  72. package/dist/core/worksheet.d.ts +55 -6
  73. package/dist/core/worksheet.js +36 -44
  74. package/dist/customui/ribbon.js +2 -1
  75. package/dist/entries/core.d.ts +18 -13
  76. package/dist/entries/core.js +12 -6
  77. package/dist/entries/xlsx.d.ts +1 -1
  78. package/dist/errors.d.ts +64 -0
  79. package/dist/errors.js +22 -0
  80. package/dist/hex.d.ts +8 -0
  81. package/dist/hex.js +3 -0
  82. package/dist/io/csv/delimiter.d.ts +9 -0
  83. package/dist/io/csv/delimiter.js +6 -0
  84. package/dist/io/csv/read.js +11 -4
  85. package/dist/io/csv/write.d.ts +9 -2
  86. package/dist/io/csv/write.js +9 -38
  87. package/dist/io/opc/inflate.js +14 -0
  88. package/dist/io/opc/namespaces.d.ts +16 -0
  89. package/dist/io/opc/namespaces.js +4 -0
  90. package/dist/io/opc/part-paths.d.ts +9 -0
  91. package/dist/io/opc/part-paths.js +1 -1
  92. package/dist/io/opc/read-opc.d.ts +54 -14
  93. package/dist/io/opc/read-opc.js +42 -20
  94. package/dist/io/opc/read-options.d.ts +5 -1
  95. package/dist/io/opc/sniff-format.d.ts +3 -3
  96. package/dist/io/opc/sniff-format.js +2 -2
  97. package/dist/io/style/xf-style.d.ts +23 -1
  98. package/dist/io/style/xf-style.js +8 -0
  99. package/dist/io/xlsb/formula.d.ts +2 -2
  100. package/dist/io/xlsb/formula.js +49 -34
  101. package/dist/io/xlsb/primitives.js +8 -14
  102. package/dist/io/xlsb/ptg-functions.d.ts +19 -10
  103. package/dist/io/xlsb/ptg-functions.js +385 -596
  104. package/dist/io/xlsb/read-styles.js +45 -81
  105. package/dist/io/xlsb/read-worksheet.d.ts +20 -1
  106. package/dist/io/xlsb/read-worksheet.js +73 -89
  107. package/dist/io/xlsb/read.d.ts +6 -3
  108. package/dist/io/xlsb/read.js +44 -23
  109. package/dist/io/xlsb/record-stream.d.ts +30 -0
  110. package/dist/io/xlsb/record-stream.js +20 -0
  111. package/dist/io/xlsb/record-types.d.ts +1 -5
  112. package/dist/io/xlsb/record-types.js +1 -5
  113. package/dist/io/xlsx/cell-accumulator.d.ts +8 -1
  114. package/dist/io/xlsx/cell-accumulator.js +32 -9
  115. package/dist/io/xlsx/cell-style-resolution.d.ts +25 -0
  116. package/dist/io/xlsx/cell-style-resolution.js +29 -0
  117. package/dist/io/xlsx/cell-value.d.ts +5 -3
  118. package/dist/io/xlsx/cell-value.js +13 -15
  119. package/dist/io/xlsx/color-xml.js +3 -2
  120. package/dist/io/xlsx/column-budget.d.ts +26 -0
  121. package/dist/io/xlsx/column-budget.js +22 -0
  122. package/dist/io/xlsx/comments.d.ts +6 -2
  123. package/dist/io/xlsx/comments.js +36 -32
  124. package/dist/io/xlsx/conditional-formatting.d.ts +6 -3
  125. package/dist/io/xlsx/conditional-formatting.js +30 -26
  126. package/dist/io/xlsx/data-validation.js +29 -27
  127. package/dist/io/xlsx/edit-vba.d.ts +7 -2
  128. package/dist/io/xlsx/edit-vba.js +14 -21
  129. package/dist/io/xlsx/font-xml.d.ts +7 -0
  130. package/dist/io/xlsx/font-xml.js +104 -0
  131. package/dist/io/xlsx/hyperlinks.d.ts +37 -5
  132. package/dist/io/xlsx/hyperlinks.js +22 -13
  133. package/dist/io/xlsx/images.js +11 -13
  134. package/dist/io/xlsx/namespaces.d.ts +16 -0
  135. package/dist/io/xlsx/namespaces.js +7 -0
  136. package/dist/io/xlsx/package-plan.d.ts +14 -2
  137. package/dist/io/xlsx/package-plan.js +39 -11
  138. package/dist/io/xlsx/pivot.js +8 -7
  139. package/dist/io/xlsx/read-parts.d.ts +59 -0
  140. package/dist/io/xlsx/read-parts.js +252 -0
  141. package/dist/io/xlsx/read-pivot.js +2 -1
  142. package/dist/io/xlsx/read-repair.d.ts +26 -0
  143. package/dist/io/xlsx/read-repair.js +39 -0
  144. package/dist/io/xlsx/{rich-runs.js → read-rich-runs.js} +6 -3
  145. package/dist/io/xlsx/read-rows.d.ts +18 -5
  146. package/dist/io/xlsx/read-rows.js +45 -31
  147. package/dist/io/xlsx/read-shared-strings.js +3 -2
  148. package/dist/io/xlsx/read-styles.d.ts +1 -6
  149. package/dist/io/xlsx/read-styles.js +6 -71
  150. package/dist/io/xlsx/read-workbook-xml.d.ts +36 -0
  151. package/dist/io/xlsx/read-workbook-xml.js +170 -0
  152. package/dist/io/xlsx/read-worksheet.d.ts +2 -1
  153. package/dist/io/xlsx/read-worksheet.js +35 -164
  154. package/dist/io/xlsx/read.d.ts +5 -12
  155. package/dist/io/xlsx/read.js +73 -406
  156. package/dist/io/xlsx/rich-text.js +1 -1
  157. package/dist/io/xlsx/row-position.d.ts +18 -0
  158. package/dist/io/xlsx/row-position.js +11 -0
  159. package/dist/io/xlsx/row-xml.d.ts +128 -0
  160. package/dist/io/xlsx/row-xml.js +203 -0
  161. package/dist/io/xlsx/shared-formulas.js +2 -2
  162. package/dist/io/xlsx/sheet-properties.d.ts +10 -1
  163. package/dist/io/xlsx/sheet-properties.js +145 -9
  164. package/dist/io/xlsx/style-elements.d.ts +43 -0
  165. package/dist/io/xlsx/style-elements.js +156 -0
  166. package/dist/io/xlsx/styles.d.ts +12 -3
  167. package/dist/io/xlsx/styles.js +5 -207
  168. package/dist/io/xlsx/tables.js +10 -14
  169. package/dist/io/xlsx/theme-xml.d.ts +26 -4
  170. package/dist/io/xlsx/theme-xml.js +107 -69
  171. package/dist/io/xlsx/threaded-comments.d.ts +8 -10
  172. package/dist/io/xlsx/threaded-comments.js +2 -1
  173. package/dist/io/xlsx/workbook-xml.d.ts +54 -4
  174. package/dist/io/xlsx/workbook-xml.js +60 -34
  175. package/dist/io/xlsx/worksheet-xml.d.ts +23 -70
  176. package/dist/io/xlsx/worksheet-xml.js +48 -229
  177. package/dist/io/xlsx/write-stream.d.ts +65 -16
  178. package/dist/io/xlsx/write-stream.js +50 -20
  179. package/dist/io/xlsx/write.d.ts +1 -2
  180. package/dist/io/xlsx/write.js +114 -74
  181. package/dist/rel-type.d.ts +16 -0
  182. package/dist/rel-type.js +6 -0
  183. package/dist/token-set.d.ts +13 -0
  184. package/dist/token-set.js +4 -1
  185. package/dist/vba/bytes.d.ts +14 -5
  186. package/dist/vba/bytes.js +16 -6
  187. package/dist/vba/cfb-format.d.ts +8 -0
  188. package/dist/vba/cfb-format.js +1 -0
  189. package/dist/vba/cfb-writer.d.ts +9 -0
  190. package/dist/vba/cfb-writer.js +12 -9
  191. package/dist/vba/cfb.d.ts +13 -1
  192. package/dist/vba/cfb.js +73 -51
  193. package/dist/vba/codepage.js +2 -1
  194. package/dist/vba/errors.d.ts +7 -0
  195. package/dist/vba/ms-ovba.js +51 -16
  196. package/dist/vba/project-editor.js +35 -40
  197. package/dist/vba/project.d.ts +11 -1
  198. package/dist/vba/project.js +27 -8
  199. package/dist/vba/vba-encoding.js +4 -5
  200. package/dist/xml/xml-attrs.d.ts +37 -0
  201. package/dist/xml/xml-attrs.js +30 -0
  202. package/dist/xml/xml-chars.d.ts +40 -0
  203. package/dist/xml/xml-chars.js +12 -0
  204. package/dist/xml/xml-namespaces.d.ts +31 -0
  205. package/dist/xml/xml-namespaces.js +50 -0
  206. package/dist/xml/xml-read.d.ts +72 -10
  207. package/dist/xml/xml-read.js +94 -18
  208. package/dist/xml/xml-scan.d.ts +15 -37
  209. package/dist/xml/xml-scan.js +12 -40
  210. package/dist/xml/xml.d.ts +28 -27
  211. package/dist/xml/xml.js +24 -24
  212. package/package.json +12 -7
  213. /package/dist/io/xlsx/{rich-runs.d.ts → read-rich-runs.d.ts} +0 -0
@@ -0,0 +1,128 @@
1
+ import { type Cell } from '../../core/cell.ts';
2
+ import { type DateEpoch } from '../../core/date.ts';
3
+ import type { ColumnProperties, RowProperties, Worksheet } from '../../core/worksheet.ts';
4
+ import type { CommentCell } from './comments.ts';
5
+ import type { CollectedHyperlink } from './hyperlinks.ts';
6
+ import type { SharedFormulaRole } from './shared-formulas.ts';
7
+ import type { SharedStringTable } from './shared-strings.ts';
8
+ import type { StyleRegistry } from './styles.ts';
9
+ /**
10
+ * The used-cell extent of a sheet: the top-left/bottom-right grid bounds that fold into the
11
+ * `<dimension>`. Rows carrying only formatting (a row height, an outline level) do not extend the
12
+ * used range, matching how Excel records `<dimension>`, so {@link add} ignores them. A fresh extent
13
+ * holds the `Infinity`/`-Infinity` sentinels; {@link isEmpty} reports that no used cell has been seen.
14
+ */
15
+ export declare class Extent {
16
+ top: number;
17
+ left: number;
18
+ bottom: number;
19
+ right: number;
20
+ constructor(seed?: Extent);
21
+ /** Whether no used cell has been folded in yet, in which case the dimension is the lone cell `A1`. */
22
+ get isEmpty(): boolean;
23
+ /** Fold a rendered row's used-column span into the extent. `minCol` is `Infinity` when the row
24
+ * carried no cells (only formatting), which extends nothing. */
25
+ add(row: number, minCol: number, maxCol: number): void;
26
+ }
27
+ /**
28
+ * A row the streaming writer already serialised, kept with the attribute string it was built from.
29
+ *
30
+ * The attributes are carried rather than re-read out of `xml`, and that is the whole point of the
31
+ * field. `collapsed="1"` is decided after the row is gone (see {@link FlushedSheet.rowOutline}), so
32
+ * the summary row has to be patched, and the patcher used to ask `xml.includes(' collapsed="1"')`
33
+ * whether the attribute was already there. The `<row>` tag's shape is known, but the *cell text*
34
+ * embedded in it is not: `escapeText` leaves a double quote verbatim, so a cell whose value was the
35
+ * literal string ` collapsed="1"` answered that question for the row and left the outline group
36
+ * rendering expanded. A file cannot forge a field.
37
+ */
38
+ export interface FlushedRow {
39
+ readonly number: number;
40
+ readonly xml: string;
41
+ /** The `<row>` element's own attributes, exactly as {@link renderRow} emitted them. */
42
+ readonly attrs: string;
43
+ }
44
+ /**
45
+ * A worksheet's eagerly-serialised rows: each row's `<row>` XML tagged with its number (so it merges
46
+ * into ascending order with the sheet's remaining live rows, whatever order it was committed in), plus
47
+ * the used-cell {@link Extent} they span. The buffered pass folds that extent into the sheet's dimension.
48
+ */
49
+ export interface FlushedSheet {
50
+ readonly rows: readonly FlushedRow[];
51
+ readonly extent: Extent;
52
+ /**
53
+ * Each flushed row's outline level and hidden flag. Carried across the eviction because both feed
54
+ * whole-sheet derivations made long after the row is gone from the model: `<sheetFormatPr
55
+ * outlineLevelRow>` is the deepest level on the sheet, and `collapsed="1"` rides a summary row only
56
+ * when its whole detail group is hidden, which is a question about *other* rows.
57
+ *
58
+ * Carried as the two inputs rather than the derived flag, because the derivation is a look-ahead a
59
+ * row being finalised cannot do, while its inputs are per-row and known exactly when the row flushes.
60
+ */
61
+ readonly rowOutline: ReadonlyMap<number, {
62
+ readonly outlineLevel: number;
63
+ readonly hidden: boolean;
64
+ }>;
65
+ /**
66
+ * The hyperlinks and notes the flushed rows carried, on the same terms and for the same reason.
67
+ * Both are serialised outside the `<row>`: a hyperlink into the sheet's `<hyperlinks>` element plus
68
+ * an external relationship, a note into the comments and VML parts. The buffered pass gathers them
69
+ * by walking the sheet's rows at commit time, which finds nothing on a row whose cells have already
70
+ * been evicted, so a streamed row's link kept its visible label and silently lost its destination.
71
+ */
72
+ readonly hyperlinks: readonly CollectedHyperlink[];
73
+ readonly notes: readonly CommentCell[];
74
+ }
75
+ /**
76
+ * A column's style facets are defaults its cells inherit unless they override them; the writer
77
+ * composes each cell's full style up front (cell over row over column, per facet) so a cell that
78
+ * overrides one facet still carries the column's others, rather than silently dropping them. Frozen
79
+ * once by the streaming writer at its first flush so every eagerly-rendered row sees the same defaults.
80
+ */
81
+ export declare function buildColumnDefaults(sheet: Worksheet): Map<number, ColumnProperties>;
82
+ /** The whole-sheet context a single row needs to serialise: the column defaults it inherits, the
83
+ * style/string tables it interns into, the shared-formula roles its cells play, and the collapsed
84
+ * outline summaries whose toggle it must stamp. The streaming writer supplies empty shared-formula
85
+ * and collapsed-summary sets, since those are whole-sheet derivations a flushed row cannot join. */
86
+ export interface RowRenderContext {
87
+ readonly columnDefaults: ReadonlyMap<number, ColumnProperties>;
88
+ readonly styles: StyleRegistry;
89
+ readonly sharedStrings: SharedStringTable | null;
90
+ readonly sharedRoles: ReadonlyMap<string, SharedFormulaRole>;
91
+ readonly collapsedSummaries: ReadonlySet<number>;
92
+ /** The workbook's date system, which is what a `Date` cell's serial counts from. */
93
+ readonly dateEpoch: DateEpoch;
94
+ }
95
+ /**
96
+ * Serialise one row to its `<row>` element, or '' when the row has neither data nor its own
97
+ * formatting. Returns the used-column bounds (`Infinity`/`-Infinity` when nothing was rendered) so a
98
+ * caller can fold them into the sheet dimension, and the attribute string separately, so a later pass
99
+ * can ask what this row declared without reading it back out of markup that also holds cell text.
100
+ * Shared by the buffered sheet pass and the streaming writer's eager flush, so both emit
101
+ * byte-identical rows.
102
+ */
103
+ export declare function renderRow(entry: {
104
+ readonly number: number;
105
+ readonly cells: readonly Cell[];
106
+ readonly properties: RowProperties | undefined;
107
+ }, ctx: RowRenderContext): {
108
+ xml: string;
109
+ attrs: string;
110
+ minCol: number;
111
+ maxCol: number;
112
+ };
113
+ /**
114
+ * The `<row>` element's opening tag up to its first attribute: the one thing a later pass may rely on
115
+ * about a rendered row's shape, spelled here so it is the emitter's own string rather than a
116
+ * re-parse of the emitter's output.
117
+ */
118
+ export declare function rowOpenTag(number: number): string;
119
+ /**
120
+ * Refuse an outline depth that is not a non-negative integer, naming what carried it.
121
+ *
122
+ * `xsd:unsignedInt` is the attribute's type, so this is what the schema already says. It is asserted
123
+ * rather than clamped because a negative level is also a hang: the writer finds a group's end by
124
+ * walking outwards while the neighbouring level exceeds the summary's, and every unmapped row
125
+ * answers `0`, which exceeds `-1` for as long as there are rows to walk.
126
+ */
127
+ export declare function assertWritableLevel(name: string, level: number): void;
128
+ export declare function outlineAttr(name: string, level: number | undefined): string;
@@ -0,0 +1,203 @@
1
+ import { cellHasOwnStyle } from '../../core/cell.js';
2
+ import { DEFAULT_DATE_NUMFMT, dateToSerial } from '../../core/date.js';
3
+ import { mangleFormula } from '../../core/formula.js';
4
+ import { NAMED_STYLE_ID } from '../../core/internal.js';
5
+ import { CELL_STYLE_FACETS } from '../../core/style.js';
6
+ import { detectValueType, isDataTableFormulaValue, isErrorValue, isFormulaValue, isHyperlinkValue, isRichTextValue, isSharedFormulaValue, } from '../../core/value.js';
7
+ import { AuthoringError, InternalError } from '../../errors.js';
8
+ import { boolAttr, escapeAttr, escapeSpreadsheetText, escapeText, numberText, textAttr, textElement, } from '../../xml/xml.js';
9
+ import { richTextRunsXml } from './rich-text.js';
10
+ export class Extent {
11
+ top = Infinity;
12
+ left = Infinity;
13
+ bottom = -Infinity;
14
+ right = -Infinity;
15
+ constructor(seed) {
16
+ if (seed) {
17
+ this.top = seed.top;
18
+ this.left = seed.left;
19
+ this.bottom = seed.bottom;
20
+ this.right = seed.right;
21
+ }
22
+ }
23
+ get isEmpty() {
24
+ return this.bottom === -Infinity;
25
+ }
26
+ add(row, minCol, maxCol) {
27
+ if (minCol === Infinity)
28
+ return;
29
+ if (row < this.top)
30
+ this.top = row;
31
+ if (row > this.bottom)
32
+ this.bottom = row;
33
+ if (minCol < this.left)
34
+ this.left = minCol;
35
+ if (maxCol > this.right)
36
+ this.right = maxCol;
37
+ }
38
+ }
39
+ export function buildColumnDefaults(sheet) {
40
+ const columnDefaults = new Map();
41
+ for (const { index, properties } of sheet.columns())
42
+ columnDefaults.set(index, properties ?? {});
43
+ return columnDefaults;
44
+ }
45
+ export function renderRow(entry, ctx) {
46
+ const { number, cells, properties } = entry;
47
+ const rendered = cells.filter((cell) => cell.value !== null || cellHasOwnStyle(cell));
48
+ const attrs = rowAttrs(properties, ctx.styles, ctx.collapsedSummaries.has(number));
49
+ if (rendered.length === 0 && attrs === '') {
50
+ return { xml: '', attrs: '', minCol: Infinity, maxCol: -Infinity };
51
+ }
52
+ const rowFill = properties?.fill;
53
+ const cellsXml = rendered
54
+ .map((cell) => {
55
+ const style = ctx.styles.styleId(composeCellStyle(cell, rowFill, ctx.columnDefaults.get(cell.col)));
56
+ return cellXml(cell, style, ctx.sharedRoles.get(cell.address), ctx.sharedStrings, ctx.dateEpoch);
57
+ })
58
+ .join('');
59
+ let minCol = Infinity;
60
+ let maxCol = -Infinity;
61
+ for (const cell of rendered) {
62
+ if (cell.col < minCol)
63
+ minCol = cell.col;
64
+ if (cell.col > maxCol)
65
+ maxCol = cell.col;
66
+ }
67
+ return { xml: `${rowOpenTag(number)}${attrs}>${cellsXml}</row>`, attrs, minCol, maxCol };
68
+ }
69
+ export function rowOpenTag(number) {
70
+ return `<row r="${number}"`;
71
+ }
72
+ function composeCellStyle(cell, rowFill, colDef) {
73
+ const style = {};
74
+ for (const facet of CELL_STYLE_FACETS)
75
+ inheritFacet(style, facet, cell, colDef);
76
+ style.fill = cell.fill ?? rowFill ?? colDef?.fill;
77
+ style.numFmt = cell.numFmt ?? colDef?.numFmt ?? dateDefaultNumFmt(cell.value);
78
+ if (cell.quotePrefix !== undefined)
79
+ style.quotePrefix = cell.quotePrefix;
80
+ const xfId = cell[NAMED_STYLE_ID];
81
+ if (xfId !== undefined)
82
+ style.xfId = xfId;
83
+ return style;
84
+ }
85
+ function inheritFacet(target, key, cell, colDef) {
86
+ target[key] = cell[key] ?? colDef?.[key];
87
+ }
88
+ function rowAttrs(properties, styles, collapsedSummary) {
89
+ if (properties === undefined)
90
+ return collapsedSummary ? ' collapsed="1"' : '';
91
+ let attrs = '';
92
+ if (properties.height !== undefined)
93
+ attrs += ` ht="${numberText(properties.height)}" customHeight="1"`;
94
+ if (properties.hidden)
95
+ attrs += ' hidden="1"';
96
+ attrs += outlineAttr('outlineLevel', properties.outlineLevel);
97
+ if (properties.collapsed || collapsedSummary)
98
+ attrs += ' collapsed="1"';
99
+ const style = styles.styleId({ fill: properties.fill });
100
+ if (style !== 0)
101
+ attrs += ` s="${style}" customFormat="1"`;
102
+ return attrs;
103
+ }
104
+ function dateDefaultNumFmt(value) {
105
+ const date = value instanceof Date
106
+ ? value
107
+ : (isFormulaValue(value) || isSharedFormulaValue(value)) && value.result instanceof Date
108
+ ? value.result
109
+ : undefined;
110
+ return date !== undefined && !Number.isNaN(date.getTime()) ? DEFAULT_DATE_NUMFMT : undefined;
111
+ }
112
+ function cellElement(ref, s, type, body) {
113
+ const t = type === '' ? '' : ` t="${type}"`;
114
+ return body === '' ? `<c r="${ref}"${s}${t}/>` : `<c r="${ref}"${s}${t}>${body}</c>`;
115
+ }
116
+ const UNWRITABLE = { type: '', v: null };
117
+ const vElement = (v) => (v === null ? '' : `<v>${v}</v>`);
118
+ function valueBody(value, epoch) {
119
+ if (typeof value === 'number') {
120
+ return Number.isFinite(value) ? { type: '', v: numberText(value) } : UNWRITABLE;
121
+ }
122
+ if (typeof value === 'boolean')
123
+ return { type: 'b', v: value ? '1' : '0' };
124
+ if (value instanceof Date) {
125
+ return Number.isNaN(value.getTime())
126
+ ? UNWRITABLE
127
+ : { type: '', v: numberText(dateToSerial(value, epoch)) };
128
+ }
129
+ if (isErrorValue(value))
130
+ return { type: 'e', v: value.error };
131
+ return undefined;
132
+ }
133
+ function cellXml(cell, style, shared, sharedStrings, epoch) {
134
+ const ref = cell.address;
135
+ const value = cell.value;
136
+ const s = style !== 0 ? ` s="${style}"` : '';
137
+ const formula = cellFormulaXml(ref, s, value, shared, epoch);
138
+ if (formula !== undefined)
139
+ return formula;
140
+ const body = valueBody(value, epoch);
141
+ if (body !== undefined)
142
+ return cellElement(ref, s, body.type, vElement(body.v));
143
+ if (typeof value === 'string' || isRichTextValue(value)) {
144
+ if (sharedStrings !== null) {
145
+ return cellElement(ref, s, 's', vElement(`${sharedStrings.intern(value)}`));
146
+ }
147
+ const inline = typeof value === 'string' ? textElement(value) : richTextRunsXml(value.richText);
148
+ return cellElement(ref, s, 'inlineStr', `<is>${inline}</is>`);
149
+ }
150
+ if (isHyperlinkValue(value)) {
151
+ const label = typeof value.text === 'string'
152
+ ? textElement(value.text)
153
+ : richTextRunsXml(value.text.richText);
154
+ return cellElement(ref, s, 'inlineStr', `<is>${label}</is>`);
155
+ }
156
+ if (value === null)
157
+ return cellElement(ref, s, '', '');
158
+ throw new InternalError(`writing a ${detectValueType(value)} cell value has no arm: every CellValue kind is handled above`);
159
+ }
160
+ function cellFormulaXml(ref, s, value, shared, epoch) {
161
+ if (shared !== undefined) {
162
+ if (shared.ref !== undefined && isFormulaValue(value)) {
163
+ const f = `<f t="shared" ref="${shared.ref}" si="${shared.si}">${escapeText(mangleFormula(value.formula))}</f>`;
164
+ return formulaBodyXml(ref, s, f, value.result, epoch);
165
+ }
166
+ const result = isSharedFormulaValue(value) ? value.result : undefined;
167
+ return formulaBodyXml(ref, s, `<f t="shared" si="${shared.si}"/>`, result, epoch);
168
+ }
169
+ if (isDataTableFormulaValue(value)) {
170
+ const attrs = `ref="${escapeAttr(value.ref)}"` +
171
+ boolAttr('dt2D', value.dataTable2D) +
172
+ boolAttr('dtr', value.dataTableRow) +
173
+ textAttr('r1', value.r1) +
174
+ textAttr('r2', value.r2);
175
+ return formulaBodyXml(ref, s, `<f t="dataTable" ${attrs}/>`, value.result, epoch);
176
+ }
177
+ if (isFormulaValue(value)) {
178
+ return formulaBodyXml(ref, s, `<f>${escapeText(mangleFormula(value.formula))}</f>`, value.result, epoch);
179
+ }
180
+ return undefined;
181
+ }
182
+ function formulaBodyXml(ref, s, f, result, epoch) {
183
+ if (result === undefined)
184
+ return cellElement(ref, s, '', f);
185
+ const body = valueBody(result, epoch);
186
+ if (body !== undefined)
187
+ return cellElement(ref, s, body.type, f + vElement(body.v));
188
+ if (typeof result === 'string') {
189
+ return cellElement(ref, s, 'str', f + vElement(escapeSpreadsheetText(result)));
190
+ }
191
+ throw new InternalError('writing a non-primitive formula result has no arm: every FormulaResult kind is handled above');
192
+ }
193
+ export function assertWritableLevel(name, level) {
194
+ if (Number.isInteger(level) && level >= 0)
195
+ return;
196
+ throw new AuthoringError(`${name} must be a non-negative integer, not ${level}`);
197
+ }
198
+ export function outlineAttr(name, level) {
199
+ if (level === undefined)
200
+ return '';
201
+ assertWritableLevel(name, level);
202
+ return level > 0 ? ` ${name}="${numberText(level)}"` : '';
203
+ }
@@ -1,4 +1,4 @@
1
- import { encodeAddress } from '../../core/address.js';
1
+ import { encodeRect } from '../../core/address.js';
2
2
  import { isFormulaValue, isSharedFormulaValue } from '../../core/value.js';
3
3
  import { AuthoringError } from '../../errors.js';
4
4
  export function planSharedFormulas(sheet) {
@@ -35,7 +35,7 @@ export function planSharedFormulas(sheet) {
35
35
  }
36
36
  roles.set(masterAddress, {
37
37
  si,
38
- ref: `${encodeAddress(master.col, master.row)}:${encodeAddress(maxCol, maxRow)}`,
38
+ ref: encodeRect({ top: master.row, left: master.col, bottom: maxRow, right: maxCol }),
39
39
  });
40
40
  for (const clone of clones)
41
41
  roles.set(clone.address, { si });
@@ -1,7 +1,8 @@
1
- import type { AutoFilter } from '../../core/autofilter.ts';
1
+ import { type AutoFilter } from '../../core/autofilter.ts';
2
2
  import { type HeaderFooter, type PageBreak, type PageMargins, type PageSetup, type PrintOptions } from '../../core/page-setup.ts';
3
3
  import { type SheetProtection } from '../../core/protection.ts';
4
4
  import type { SheetView, Worksheet } from '../../core/worksheet.ts';
5
+ import { type XmlAttributes } from '../../xml/xml-scan.ts';
5
6
  export declare function sheetViewsXml(view: SheetView, active: boolean): string;
6
7
  export declare function sheetPrXml(sheet: Worksheet): string;
7
8
  export declare function sheetProtectionXml(protection: SheetProtection | undefined): string;
@@ -11,3 +12,11 @@ export declare function printOptionsXml(printOptions: PrintOptions): string;
11
12
  export declare function pageMarginsXml(margins: PageMargins): string;
12
13
  export declare function pageSetupXml(pageSetup: PageSetup, printerSettingsRelId: string | null): string;
13
14
  export declare function pageBreaksXml(breaks: readonly PageBreak[], element: 'rowBreaks' | 'colBreaks'): string;
15
+ export declare function parseSheetProtection(attrs: XmlAttributes): SheetProtection | undefined;
16
+ export declare class PageBreakAccumulator {
17
+ #private;
18
+ begin(target: PageBreak[]): void;
19
+ end(): void;
20
+ add(attrs: XmlAttributes): void;
21
+ }
22
+ export declare function applySheetProperties(local: string, attrs: XmlAttributes, sheet: Worksheet): void;
@@ -1,8 +1,11 @@
1
1
  import { encodeAddress } from '../../core/address.js';
2
+ import { isCustomFilterOperator, } from '../../core/autofilter.js';
2
3
  import { HEADER_FOOTER_ELEMENTS, MARGIN_SIDES, PAGE_SETUP_FACETS, PRINT_OPTION_FLAGS, } from '../../core/page-setup.js';
3
- import { SHEET_PROTECTION_FLAGS } from '../../core/protection.js';
4
+ import { SHEET_PROTECTION_FLAGS, } from '../../core/protection.js';
5
+ import { numFinite, numInteger } from '../../xml/xml-attrs.js';
6
+ import { boolPresent, boolStrict, boolTristate } from '../../xml/xml-scan.js';
4
7
  import { boolAttr, checkedToken, escapeAttr, escapeSpreadsheetText, numAttr, numberText, } from '../../xml/xml.js';
5
- import { colorAttrs } from './color-xml.js';
8
+ import { colorAttrs, parseColor } from './color-xml.js';
6
9
  export function sheetViewsXml(view, active) {
7
10
  const selected = active ? ' tabSelected="1"' : '';
8
11
  const gridLines = view.showGridLines === false ? ' showGridLines="0"' : '';
@@ -21,10 +24,13 @@ export function sheetViewsXml(view, active) {
21
24
  return `<sheetViews><sheetView${gridLines}${selected} workbookViewId="0">${pane}${selection}</sheetView></sheetViews>`;
22
25
  }
23
26
  export function sheetPrXml(sheet) {
27
+ const attrs = sheet.codeName === undefined ? '' : ` codeName="${escapeAttr(sheet.codeName)}"`;
24
28
  const children = (sheet.tabColor !== undefined ? `<tabColor ${colorAttrs(sheet.tabColor)}/>` : '') +
25
29
  outlinePrXml(sheet.outline) +
26
30
  pageSetUpPrXml(sheet.pageSetup);
27
- return children === '' ? '' : `<sheetPr>${children}</sheetPr>`;
31
+ if (attrs === '' && children === '')
32
+ return '';
33
+ return children === '' ? `<sheetPr${attrs}/>` : `<sheetPr${attrs}>${children}</sheetPr>`;
28
34
  }
29
35
  function pageSetUpPrXml(pageSetup) {
30
36
  return pageSetup.fitToPage ? '<pageSetUpPr fitToPage="1"/>' : '';
@@ -40,10 +46,10 @@ export function sheetProtectionXml(protection) {
40
46
  let attrs = '';
41
47
  if (credential !== undefined) {
42
48
  attrs +=
43
- ` algorithmName="${credential.algorithmName}"` +
44
- ` hashValue="${credential.hashValue}"` +
45
- ` saltValue="${credential.saltValue}"` +
46
- ` spinCount="${credential.spinCount}"`;
49
+ ` algorithmName="${escapeAttr(credential.algorithmName)}"` +
50
+ ` hashValue="${escapeAttr(credential.hashValue)}"` +
51
+ ` saltValue="${escapeAttr(credential.saltValue)}"` +
52
+ numAttr('spinCount', credential.spinCount);
47
53
  }
48
54
  attrs += ' sheet="1"';
49
55
  for (const { key, defaultForbidden } of SHEET_PROTECTION_FLAGS) {
@@ -66,7 +72,7 @@ export function autoFilterXml(filter) {
66
72
  return `<autoFilter ref="${ref}">${filter.columns.map(filterColumnXml).join('')}</autoFilter>`;
67
73
  }
68
74
  function filterColumnXml(column) {
69
- return `<filterColumn colId="${column.colId}">${filterCriteriaXml(column.criteria)}</filterColumn>`;
75
+ return `<filterColumn colId="${numberText(column.colId)}">${filterCriteriaXml(column.criteria)}</filterColumn>`;
70
76
  }
71
77
  function filterCriteriaXml(criteria) {
72
78
  if (criteria.kind === 'values') {
@@ -76,7 +82,7 @@ function filterCriteriaXml(criteria) {
76
82
  }
77
83
  const andAttr = criteria.and ? ' and="1"' : '';
78
84
  const predicates = criteria.predicates
79
- .map((p) => `<customFilter operator="${p.operator}" val="${escapeAttr(p.val)}"/>`)
85
+ .map((p) => `<customFilter operator="${checkedToken(p.operator, isCustomFilterOperator, 'custom filter operator')}" val="${escapeAttr(p.val)}"/>`)
80
86
  .join('');
81
87
  return `<customFilters${andAttr}>${predicates}</customFilters>`;
82
88
  }
@@ -144,3 +150,133 @@ export function pageBreaksXml(breaks, element) {
144
150
  .join('');
145
151
  return `<${element} count="${breaks.length}" manualBreakCount="${breaks.length}">${brks}</${element}>`;
146
152
  }
153
+ export function parseSheetProtection(attrs) {
154
+ if (boolTristate(attrs.sheet) === false)
155
+ return undefined;
156
+ const flags = {};
157
+ for (const { key } of SHEET_PROTECTION_FLAGS) {
158
+ const raw = attrs[key];
159
+ if (raw !== undefined)
160
+ flags[key] = !boolStrict(raw);
161
+ }
162
+ const { algorithmName, hashValue, saltValue, spinCount } = attrs;
163
+ const spin = numInteger(spinCount, 0);
164
+ if (algorithmName !== undefined &&
165
+ hashValue !== undefined &&
166
+ saltValue !== undefined &&
167
+ spin !== undefined) {
168
+ const credential = {
169
+ algorithmName,
170
+ hashValue,
171
+ saltValue,
172
+ spinCount: spin,
173
+ };
174
+ return { flags, credential };
175
+ }
176
+ return { flags };
177
+ }
178
+ export class PageBreakAccumulator {
179
+ #target = null;
180
+ begin(target) {
181
+ this.#target = target;
182
+ }
183
+ end() {
184
+ this.#target = null;
185
+ }
186
+ add(attrs) {
187
+ if (this.#target === null)
188
+ return;
189
+ const id = numInteger(attrs.id, 1);
190
+ if (id === undefined)
191
+ return;
192
+ const brk = { id };
193
+ const max = numInteger(attrs.max, 0);
194
+ if (max !== undefined)
195
+ brk.max = max;
196
+ if (boolStrict(attrs.man))
197
+ brk.man = true;
198
+ this.#target.push(brk);
199
+ }
200
+ }
201
+ export function applySheetProperties(local, attrs, sheet) {
202
+ switch (local) {
203
+ case 'sheetPr':
204
+ if (attrs.codeName !== undefined)
205
+ sheet.codeName = attrs.codeName;
206
+ break;
207
+ case 'tabColor':
208
+ sheet.tabColor = parseColor(attrs);
209
+ break;
210
+ case 'outlinePr':
211
+ if (attrs.summaryBelow !== undefined)
212
+ sheet.outline.summaryBelow = boolPresent(attrs.summaryBelow);
213
+ if (attrs.summaryRight !== undefined)
214
+ sheet.outline.summaryRight = boolPresent(attrs.summaryRight);
215
+ break;
216
+ case 'sheetView':
217
+ if (attrs.showGridLines !== undefined && !boolPresent(attrs.showGridLines)) {
218
+ sheet.view.showGridLines = false;
219
+ }
220
+ break;
221
+ case 'pane':
222
+ if (attrs.state === 'frozen' || attrs.state === 'frozenSplit') {
223
+ sheet.view.state = 'frozen';
224
+ const xSplit = numInteger(attrs.xSplit, 0);
225
+ const ySplit = numInteger(attrs.ySplit, 0);
226
+ if (xSplit !== undefined)
227
+ sheet.view.xSplit = xSplit;
228
+ if (ySplit !== undefined)
229
+ sheet.view.ySplit = ySplit;
230
+ if (attrs.topLeftCell !== undefined)
231
+ sheet.view.topLeftCell = attrs.topLeftCell;
232
+ }
233
+ break;
234
+ case 'pageSetUpPr':
235
+ if (attrs.fitToPage !== undefined)
236
+ sheet.pageSetup.fitToPage = boolPresent(attrs.fitToPage);
237
+ break;
238
+ case 'printOptions':
239
+ applyPrintOptions(sheet.printOptions, attrs);
240
+ break;
241
+ case 'pageMargins':
242
+ applyMargins(sheet.pageMargins, attrs);
243
+ break;
244
+ case 'pageSetup':
245
+ applyPageSetup(sheet.pageSetup, attrs);
246
+ break;
247
+ }
248
+ }
249
+ function applyPrintOptions(printOptions, attrs) {
250
+ for (const flag of PRINT_OPTION_FLAGS) {
251
+ const value = boolTristate(attrs[flag]);
252
+ if (value !== undefined)
253
+ printOptions[flag] = value;
254
+ }
255
+ }
256
+ function applyMargins(margins, attrs) {
257
+ for (const side of MARGIN_SIDES) {
258
+ const value = numFinite(attrs[side]);
259
+ if (value !== undefined)
260
+ margins[side] = value;
261
+ }
262
+ }
263
+ function applyPageSetup(pageSetup, attrs) {
264
+ for (const facet of PAGE_SETUP_FACETS) {
265
+ const raw = attrs[facet.key];
266
+ switch (facet.kind) {
267
+ case 'count': {
268
+ const value = numInteger(raw, 0);
269
+ if (value !== undefined)
270
+ pageSetup[facet.key] = value;
271
+ break;
272
+ }
273
+ case 'token':
274
+ if (raw !== undefined && facet.isValid(raw))
275
+ assignPageSetupToken(pageSetup, facet.key, raw);
276
+ break;
277
+ }
278
+ }
279
+ }
280
+ function assignPageSetupToken(pageSetup, key, raw) {
281
+ pageSetup[key] = raw;
282
+ }
@@ -0,0 +1,43 @@
1
+ import { type Alignment, type Border, type Fill, type Protection } from '../../core/style.ts';
2
+ import type { DifferentialStyle } from '../../core/workbook-styles.ts';
3
+ export declare const CUSTOM_NUMFMT_BASE = 164;
4
+ export declare const DEFAULT_FONT_BODY = "<sz val=\"11\"/><color theme=\"1\"/><name val=\"Calibri\"/><family val=\"2\"/><scheme val=\"minor\"/>";
5
+ export declare const DEFAULT_BORDER = "<border><left/><right/><top/><bottom/><diagonal/></border>";
6
+ export interface CellFormat {
7
+ readonly fillId: number;
8
+ readonly numFmtId: number;
9
+ readonly fontId: number;
10
+ readonly borderId: number;
11
+ readonly alignment: string;
12
+ readonly protection: string;
13
+ readonly quotePrefix: boolean;
14
+ readonly xfId: number;
15
+ }
16
+ export declare const DEFAULT_FORMAT: CellFormat;
17
+ export declare function formatSignature(format: CellFormat): string;
18
+ /**
19
+ * Whether a format is the do-nothing default: no facet, General number format, no quote prefix, and
20
+ * linked to the Normal named style. Such a cellXfs entry adds nothing, so its owner needs no `s`.
21
+ *
22
+ * Asked of the signature rather than field by field, because the two questions have to be answered
23
+ * from the same field set and both answer silently when they are not. A facet the signature forgets
24
+ * makes two distinct formats intern to one entry, and every cell carrying the facet quietly loses it;
25
+ * a facet *this* forgets forces a spurious entry and an `s` attribute onto every cell in the book.
26
+ * Neither is a type error, and neither is visible in the file until someone compares two of them.
27
+ */
28
+ export declare function isDefaultFormat(format: CellFormat): boolean;
29
+ export declare function xfXml(format: CellFormat, xfId: number | null): string;
30
+ export declare function cellStyleTag(entry: {
31
+ name: string;
32
+ builtinId?: number;
33
+ xfId: number;
34
+ }): string;
35
+ export declare function alignmentAttrs(alignment: Alignment): string;
36
+ export declare function protectionAttrs(protection: Protection): string;
37
+ export declare function tableStyleName(fragment: string): string;
38
+ export declare function dxfXml(style: DifferentialStyle): string;
39
+ export declare function borderXml(border: Border): string;
40
+ export declare function fillSignature(fill: Fill): string;
41
+ export declare function patternFillXml(fill: Fill, { solidBgFallback }: {
42
+ solidBgFallback: boolean;
43
+ }): string;