@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
@@ -1,46 +1,10 @@
1
- import type { Cell } from '../../core/cell.ts';
2
- import type { ColumnProperties, RowProperties, Worksheet } from '../../core/worksheet.ts';
1
+ import type { DateEpoch } from '../../core/date.ts';
2
+ import type { Worksheet } from '../../core/worksheet.ts';
3
3
  import { type HyperlinkPlan } from './hyperlinks.ts';
4
- import type { BackgroundPlan, CommentPlan, DrawingPlan, PivotPlan, PreservedReferencePlan, PrinterSettingsPlan, TablePlan, ThreadedCommentPlan } from './package-plan.ts';
5
- import { type SharedFormulaRole } from './shared-formulas.ts';
4
+ import type { SheetPlan, TablePlan } from './package-plan.ts';
5
+ import { type FlushedSheet } from './row-xml.ts';
6
6
  import type { SharedStringTable } from './shared-strings.ts';
7
7
  import type { StyleRegistry } from './styles.ts';
8
- /**
9
- * The used-cell extent of a sheet: the top-left/bottom-right grid bounds that fold into the
10
- * `<dimension>`. Rows carrying only formatting (a row height, an outline level) do not extend the
11
- * used range, matching how Excel records `<dimension>`, so {@link add} ignores them. A fresh extent
12
- * holds the `Infinity`/`-Infinity` sentinels; {@link isEmpty} reports that no used cell has been seen.
13
- */
14
- export declare class Extent {
15
- top: number;
16
- left: number;
17
- bottom: number;
18
- right: number;
19
- constructor(seed?: Extent);
20
- /** Whether no used cell has been folded in yet, in which case the dimension is the lone cell `A1`. */
21
- get isEmpty(): boolean;
22
- /** Fold a rendered row's used-column span into the extent. `minCol` is `Infinity` when the row
23
- * carried no cells (only formatting), which extends nothing. */
24
- add(row: number, minCol: number, maxCol: number): void;
25
- }
26
- /**
27
- * A worksheet's eagerly-serialised rows: each row's `<row>` XML tagged with its number (so it merges
28
- * into ascending order with the sheet's remaining live rows, whatever order it was committed in), plus
29
- * the used-cell {@link Extent} they span. The buffered pass folds that extent into the sheet's dimension.
30
- */
31
- export interface FlushedSheet {
32
- readonly rows: ReadonlyArray<{
33
- readonly number: number;
34
- readonly xml: string;
35
- }>;
36
- readonly extent: Extent;
37
- /**
38
- * The deepest row outline level among the flushed rows. Carried across the eviction because
39
- * `<sheetFormatPr outlineLevelRow>` is derived from every row on the sheet, and a flushed row's
40
- * properties are gone from the model by the time the header is serialised.
41
- */
42
- readonly maxRowOutlineLevel: number;
43
- }
44
8
  export interface SheetReferences {
45
9
  readonly drawingRelId: string | null;
46
10
  readonly legacyDrawingRelId: string | null;
@@ -49,38 +13,27 @@ export interface SheetReferences {
49
13
  readonly legacyDrawingHFRelId: string | null;
50
14
  readonly slicerRelIds: readonly string[];
51
15
  }
52
- export declare function worksheetXml(sheet: Worksheet, tables: readonly TablePlan[], styles: StyleRegistry, references: SheetReferences, hyperlinks: readonly HyperlinkPlan[], sharedStrings: SharedStringTable | null, active: boolean, flushed?: FlushedSheet): string;
53
16
  /**
54
- * A column's style facets are defaults its cells inherit unless they override them; the writer
55
- * composes each cell's full style up front (cell over row over column, per facet) so a cell that
56
- * overrides one facet still carries the column's others, rather than silently dropping them. Frozen
57
- * once by the streaming writer at its first flush so every eagerly-rendered row sees the same defaults.
17
+ * Everything one worksheet part is rendered from: the sheet itself, the parts of the package plan
18
+ * that belong to it, and the two registries a sheet pass interns into.
19
+ *
20
+ * One object for the reason {@link SheetReferences} is one object, applied to the layer above it: a
21
+ * run of positional arguments carrying two `readonly …[]`s, two nullable references and a bare
22
+ * boolean is a run whose order the call site cannot be read against. `active:` is the clearest of
23
+ * them -- as a positional it needed a comment at the call site to say which boolean it was.
58
24
  */
59
- export declare function buildColumnDefaults(sheet: Worksheet): Map<number, ColumnProperties>;
60
- /** The whole-sheet context a single row needs to serialise: the column defaults it inherits, the
61
- * style/string tables it interns into, the shared-formula roles its cells play, and the collapsed
62
- * outline summaries whose toggle it must stamp. The streaming writer supplies empty shared-formula
63
- * and collapsed-summary sets, since those are whole-sheet derivations a flushed row cannot join. */
64
- export interface RowRenderContext {
65
- readonly columnDefaults: ReadonlyMap<number, ColumnProperties>;
25
+ export interface WorksheetXmlInputs {
26
+ readonly sheet: Worksheet;
27
+ readonly tables: readonly TablePlan[];
66
28
  readonly styles: StyleRegistry;
29
+ readonly references: SheetReferences;
30
+ readonly hyperlinks: readonly HyperlinkPlan[];
67
31
  readonly sharedStrings: SharedStringTable | null;
68
- readonly sharedRoles: ReadonlyMap<string, SharedFormulaRole>;
69
- readonly collapsedSummaries: ReadonlySet<number>;
32
+ readonly dateEpoch: DateEpoch;
33
+ /** Whether this is the workbook's one selected sheet. */
34
+ readonly active: boolean;
35
+ /** The rows the streaming writer already serialised and evicted, absent on the buffered path. */
36
+ readonly flushed?: FlushedSheet | undefined;
70
37
  }
71
- /**
72
- * Serialise one row to its `<row>` element, or '' when the row has neither data nor its own
73
- * formatting. Returns the used-column bounds (`Infinity`/`-Infinity` when nothing was rendered) so a
74
- * caller can fold them into the sheet dimension. Shared by the buffered sheet pass and the streaming
75
- * writer's eager flush, so both emit byte-identical rows.
76
- */
77
- export declare function renderRow(entry: {
78
- readonly number: number;
79
- readonly cells: readonly Cell[];
80
- readonly properties: RowProperties | undefined;
81
- }, ctx: RowRenderContext): {
82
- xml: string;
83
- minCol: number;
84
- maxCol: number;
85
- };
86
- export declare function worksheetRelsXml(tables: readonly TablePlan[], drawing: DrawingPlan | null, comments: CommentPlan | null, threadedComments: ThreadedCommentPlan | null, printerSettings: PrinterSettingsPlan | null, background: BackgroundPlan | null, hyperlinks: readonly HyperlinkPlan[], preservedReferences: readonly PreservedReferencePlan[], pivots: readonly PivotPlan[]): string;
38
+ export declare function worksheetXml(inputs: WorksheetXmlInputs): string;
39
+ export declare function worksheetRelsXml(plan: SheetPlan): string;
@@ -1,56 +1,25 @@
1
- import { decodeRange, encodeAddress } from '../../core/address.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, pickStyleFacets } 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 { assertWritableNumber, escapeAttr, escapeSpreadsheetText, escapeText, numberText, textAttr, textElement, XML_DECLARATION, } from '../../xml/xml.js';
9
- import { relativePartPath } from '../opc/part-paths.js';
1
+ import { decodeRange, encodeRect } from '../../core/address.js';
2
+ import { pickStyleFacets } from '../../core/style.js';
3
+ import { AuthoringError, InternalError, quoted } from '../../errors.js';
4
+ import { escapeAttr, numberText, XML_DECLARATION } from '../../xml/xml.js';
10
5
  import { relationship, relationshipsPart } from '../opc/rels.js';
11
- import { conditionalFormattingsExtXml, conditionalFormattingsXml } from './conditional-formatting.js';
6
+ import { conditionalFormattingsExtXml, conditionalFormattingsXml, dataBarExtLinks, } from './conditional-formatting.js';
12
7
  import { dataValidationsExtXml, dataValidationsXml } from './data-validation.js';
13
- import { hyperlinksXml } from './hyperlinks.js';
8
+ import { hyperlinksXml, isExternalHyperlink } from './hyperlinks.js';
14
9
  import { SLICER_LIST_EXT_URI } from './namespaces.js';
10
+ import { commentsPart, drawingPart, mediaPart, pivotTablePart, printerSettingsPart, tablePart, targetFromWorksheet, threadedCommentsPart, vmlDrawingPart, } from './part-names.js';
15
11
  import { NS, REL } from './relationships.js';
16
- import { richTextRunsXml } from './rich-text.js';
12
+ import { assertWritableLevel, buildColumnDefaults, Extent, outlineAttr, renderRow, rowOpenTag, } from './row-xml.js';
17
13
  import { planSharedFormulas } from './shared-formulas.js';
18
14
  import { autoFilterXml, headerFooterXml, pageBreaksXml, pageMarginsXml, pageSetupXml, printOptionsXml, sheetProtectionXml, sheetPrXml, sheetViewsXml, } from './sheet-properties.js';
19
15
  import { x14Ext } from './x14-ext.js';
20
- export class Extent {
21
- top = Infinity;
22
- left = Infinity;
23
- bottom = -Infinity;
24
- right = -Infinity;
25
- constructor(seed) {
26
- if (seed) {
27
- this.top = seed.top;
28
- this.left = seed.left;
29
- this.bottom = seed.bottom;
30
- this.right = seed.right;
31
- }
32
- }
33
- get isEmpty() {
34
- return this.bottom === -Infinity;
35
- }
36
- add(row, minCol, maxCol) {
37
- if (minCol === Infinity)
38
- return;
39
- if (row < this.top)
40
- this.top = row;
41
- if (row > this.bottom)
42
- this.bottom = row;
43
- if (minCol < this.left)
44
- this.left = minCol;
45
- if (maxCol > this.right)
46
- this.right = maxCol;
47
- }
48
- }
49
- export function worksheetXml(sheet, tables, styles, references, hyperlinks, sharedStrings, active, flushed) {
16
+ export function worksheetXml(inputs) {
17
+ const { sheet, tables, styles, references, hyperlinks, sharedStrings, dateEpoch, active, flushed } = inputs;
50
18
  validateMerges(sheet);
51
19
  const columnDefaults = buildColumnDefaults(sheet);
52
20
  const sharedRoles = planSharedFormulas(sheet);
53
- const rowOutline = scanRowOutline(sheet);
21
+ const extLinks = dataBarExtLinks(sheet.conditionalFormattings);
22
+ const rowOutline = scanRowOutline(sheet, flushed);
54
23
  const collapsedSummaries = rowOutline.collapsedSummaries;
55
24
  const context = {
56
25
  columnDefaults,
@@ -58,6 +27,7 @@ export function worksheetXml(sheet, tables, styles, references, hyperlinks, shar
58
27
  sharedStrings,
59
28
  sharedRoles,
60
29
  collapsedSummaries,
30
+ dateEpoch,
61
31
  };
62
32
  const liveRows = [];
63
33
  const extent = new Extent(flushed?.extent);
@@ -68,11 +38,9 @@ export function worksheetXml(sheet, tables, styles, references, hyperlinks, shar
68
38
  liveRows.push({ number: entry.number, xml });
69
39
  extent.add(entry.number, minCol, maxCol);
70
40
  }
71
- const dimensionRef = extent.isEmpty
72
- ? 'A1'
73
- : `${encodeAddress(extent.left, extent.top)}:${encodeAddress(extent.right, extent.bottom)}`;
41
+ const dimensionRef = extent.isEmpty ? 'A1' : encodeRect(extent);
74
42
  const orderedRows = flushed
75
- ? [...flushed.rows, ...liveRows].sort((a, b) => a.number - b.number)
43
+ ? [...flushed.rows.map((row) => completeCollapsed(row, collapsedSummaries)), ...liveRows].sort((a, b) => a.number - b.number)
76
44
  : liveRows;
77
45
  const bodyXml = orderedRows.map((row) => row.xml).join('');
78
46
  const sheetData = bodyXml === '' ? '<sheetData/>' : `<sheetData>${bodyXml}</sheetData>`;
@@ -83,14 +51,14 @@ export function worksheetXml(sheet, tables, styles, references, hyperlinks, shar
83
51
  sheetViewsXml(sheet.view, active) +
84
52
  sheetFormatPr(sheet.properties, {
85
53
  col: maxColumnOutlineLevel(sheet),
86
- row: Math.max(rowOutline.maxLevel, flushed?.maxRowOutlineLevel ?? 0),
54
+ row: rowOutline.maxLevel,
87
55
  }) +
88
56
  colsXml(sheet, styles) +
89
57
  sheetData +
90
58
  sheetProtectionXml(sheet.protection) +
91
59
  autoFilterXml(sheet.autoFilter) +
92
60
  mergeCellsXml(sheet.merges) +
93
- conditionalFormattingsXml(sheet.conditionalFormattings, styles) +
61
+ conditionalFormattingsXml(sheet.conditionalFormattings, styles, extLinks) +
94
62
  dataValidationsXml(sheet.dataValidations) +
95
63
  hyperlinksXml(hyperlinks) +
96
64
  printOptionsXml(sheet.printOptions) +
@@ -104,57 +72,12 @@ export function worksheetXml(sheet, tables, styles, references, hyperlinks, shar
104
72
  refElement('legacyDrawingHF', references.legacyDrawingHFRelId) +
105
73
  refElement('picture', references.backgroundRelId) +
106
74
  tablePartsXml(tables) +
107
- worksheetExtLstXml(sheet, references.slicerRelIds) +
75
+ worksheetExtLstXml(sheet, references.slicerRelIds, extLinks) +
108
76
  '</worksheet>');
109
77
  }
110
- export function buildColumnDefaults(sheet) {
111
- const columnDefaults = new Map();
112
- for (const { index, properties } of sheet.columns())
113
- columnDefaults.set(index, properties ?? {});
114
- return columnDefaults;
115
- }
116
- export function renderRow(entry, ctx) {
117
- const { number, cells, properties } = entry;
118
- const rendered = cells.filter((cell) => cell.value !== null || hasOwnStyle(cell));
119
- const attrs = rowAttrs(properties, ctx.styles, ctx.collapsedSummaries.has(number));
120
- if (rendered.length === 0 && attrs === '')
121
- return { xml: '', minCol: Infinity, maxCol: -Infinity };
122
- const rowFill = properties?.fill;
123
- const cellsXml = rendered
124
- .map((cell) => {
125
- const style = ctx.styles.styleId(composeCellStyle(cell, rowFill, ctx.columnDefaults.get(cell.col)));
126
- return cellXml(cell, style, ctx.sharedRoles.get(cell.address), ctx.sharedStrings);
127
- })
128
- .join('');
129
- let minCol = Infinity;
130
- let maxCol = -Infinity;
131
- for (const cell of rendered) {
132
- if (cell.col < minCol)
133
- minCol = cell.col;
134
- if (cell.col > maxCol)
135
- maxCol = cell.col;
136
- }
137
- return { xml: `<row r="${number}"${attrs}>${cellsXml}</row>`, minCol, maxCol };
138
- }
139
- function composeCellStyle(cell, rowFill, colDef) {
140
- const style = {};
141
- for (const facet of CELL_STYLE_FACETS)
142
- inheritFacet(style, facet, cell, colDef);
143
- style.fill = cell.fill ?? rowFill ?? colDef?.fill;
144
- style.numFmt = cell.numFmt ?? colDef?.numFmt ?? dateDefaultNumFmt(cell.value);
145
- if (cell.quotePrefix !== undefined)
146
- style.quotePrefix = cell.quotePrefix;
147
- const xfId = cell[NAMED_STYLE_ID];
148
- if (xfId !== undefined)
149
- style.xfId = xfId;
150
- return style;
151
- }
152
- function inheritFacet(target, key, cell, colDef) {
153
- target[key] = cell[key] ?? colDef?.[key];
154
- }
155
- function worksheetExtLstXml(sheet, slicerRelIds) {
78
+ function worksheetExtLstXml(sheet, slicerRelIds, extLinks) {
156
79
  const exts = [
157
- conditionalFormattingsExtXml(sheet.conditionalFormattings),
80
+ conditionalFormattingsExtXml(sheet.conditionalFormattings, extLinks),
158
81
  dataValidationsExtXml(sheet.dataValidations),
159
82
  slicerListExtXml(slicerRelIds),
160
83
  ].filter((ext) => ext !== '');
@@ -180,7 +103,7 @@ function validateMerges(sheet) {
180
103
  top <= region.bottom &&
181
104
  bottom >= region.top;
182
105
  if (overlaps) {
183
- throw new AuthoringError(`merged range ${merge} overlaps table "${table.name}" (${table.range}): Excel forbids a merge inside a table`);
106
+ throw new AuthoringError(`merged range ${merge} overlaps table ${quoted(table.name)} (${table.range}): Excel forbids a merge inside a table`);
184
107
  }
185
108
  }
186
109
  }
@@ -202,40 +125,41 @@ function tablePartsXml(tables) {
202
125
  const parts = tables.map(({ relId }) => `<tablePart r:id="${relId}"/>`).join('');
203
126
  return `<tableParts count="${tables.length}">${parts}</tableParts>`;
204
127
  }
205
- export function worksheetRelsXml(tables, drawing, comments, threadedComments, printerSettings, background, hyperlinks, preservedReferences, pivots) {
128
+ export function worksheetRelsXml(plan) {
129
+ const { tables, drawing, comments, threadedComments, printerSettings, background, hyperlinks, preservedRefs: preservedReferences, pivots, } = plan;
206
130
  const rels = [
207
- ...tables.map(({ relId, number }) => relationship(relId, REL.table, `../tables/table${number}.xml`)),
208
- ...pivots.map((pivot) => relationship(pivot.sheetRelId, REL.pivotTable, `../pivotTables/pivotTable${pivot.number}.xml`)),
131
+ ...tables.map(({ relId, number }) => relationship(relId, REL.table, targetFromWorksheet(tablePart(number)))),
132
+ ...pivots.map((pivot) => relationship(pivot.sheetRelId, REL.pivotTable, targetFromWorksheet(pivotTablePart(pivot.number)))),
209
133
  ...(drawing === null
210
134
  ? []
211
- : [relationship(drawing.relId, REL.drawing, `../drawings/drawing${drawing.number}.xml`)]),
135
+ : [
136
+ relationship(drawing.relId, REL.drawing, targetFromWorksheet(drawingPart(drawing.number))),
137
+ ]),
212
138
  ...(comments === null
213
139
  ? []
214
140
  : [
215
- relationship(comments.vmlRelId, REL.vmlDrawing, `../drawings/vmlDrawing${comments.number}.vml`),
216
- relationship(comments.commentsRelId, REL.comments, `../comments${comments.number}.xml`),
141
+ relationship(comments.vmlRelId, REL.vmlDrawing, targetFromWorksheet(vmlDrawingPart(comments.number))),
142
+ relationship(comments.commentsRelId, REL.comments, targetFromWorksheet(commentsPart(comments.number))),
217
143
  ]),
218
144
  ...(threadedComments === null
219
145
  ? []
220
146
  : [
221
- relationship(threadedComments.relId, REL.threadedComment, `../threadedComments/threadedComment${threadedComments.number}.xml`),
147
+ relationship(threadedComments.relId, REL.threadedComment, targetFromWorksheet(threadedCommentsPart(threadedComments.number))),
222
148
  ]),
223
149
  ...(printerSettings === null
224
150
  ? []
225
151
  : [
226
- relationship(printerSettings.relId, REL.printerSettings, `../printerSettings/printerSettings${printerSettings.number}.bin`),
152
+ relationship(printerSettings.relId, REL.printerSettings, targetFromWorksheet(printerSettingsPart(printerSettings.number))),
227
153
  ]),
228
154
  ...(background === null
229
155
  ? []
230
156
  : [
231
- relationship(background.relId, REL.image, `../media/image${background.mediaNumber}.${background.extension}`),
157
+ relationship(background.relId, REL.image, targetFromWorksheet(mediaPart(background.mediaNumber, background.extension))),
232
158
  ]),
233
- ...preservedReferences.map((reference) => relationship(reference.relId, reference.relType, escapeAttr(relativePartPath('xl/worksheets/sheet1.xml', reference.entryPath)))),
159
+ ...preservedReferences.map((reference) => relationship(reference.relId, reference.relType, targetFromWorksheet(reference.entryPath))),
234
160
  ...hyperlinks
235
- .filter((link) => link.relId !== undefined && link.target !== undefined)
236
- .map((link) => relationship(link.relId, REL.hyperlink, link.target, {
237
- external: true,
238
- })),
161
+ .filter(isExternalHyperlink)
162
+ .map((link) => relationship(link.relId, REL.hyperlink, link.target, { external: true })),
239
163
  ];
240
164
  return relationshipsPart(rels);
241
165
  }
@@ -252,12 +176,6 @@ function sheetFormatPr(properties, outlineLevel) {
252
176
  attrs += outlineAttr('outlineLevelRow', outlineLevel.row);
253
177
  return `<sheetFormatPr${attrs}/>`;
254
178
  }
255
- function outlineAttr(name, level) {
256
- if (level === undefined)
257
- return '';
258
- const text = numberText(level);
259
- return level > 0 ? ` ${name}="${text}"` : '';
260
- }
261
179
  function maxColumnOutlineLevel(sheet) {
262
180
  let max = 0;
263
181
  for (const column of sheet.columns())
@@ -310,33 +228,35 @@ function colBody(properties, styles) {
310
228
  }
311
229
  return meaningful ? attrs : null;
312
230
  }
313
- function rowAttrs(properties, styles, collapsedSummary) {
314
- if (properties === undefined)
315
- return collapsedSummary ? ' collapsed="1"' : '';
316
- let attrs = '';
317
- if (properties.height !== undefined)
318
- attrs += ` ht="${numberText(properties.height)}" customHeight="1"`;
319
- if (properties.hidden)
320
- attrs += ' hidden="1"';
321
- attrs += outlineAttr('outlineLevel', properties.outlineLevel);
322
- if (properties.collapsed || collapsedSummary)
323
- attrs += ' collapsed="1"';
324
- const style = styles.styleId({ fill: properties.fill });
325
- if (style !== 0)
326
- attrs += ` s="${style}" customFormat="1"`;
327
- return attrs;
231
+ function completeCollapsed(row, collapsedSummaries) {
232
+ if (!collapsedSummaries.has(row.number) || row.attrs.includes(' collapsed="1"')) {
233
+ return { number: row.number, xml: row.xml };
234
+ }
235
+ const open = rowOpenTag(row.number);
236
+ if (!row.xml.startsWith(open)) {
237
+ throw new InternalError(`flushed row ${row.number} does not open with ${quoted(open)}`);
238
+ }
239
+ return {
240
+ number: row.number,
241
+ xml: `${open} collapsed="1"${row.xml.slice(open.length)}`,
242
+ };
328
243
  }
329
- function scanRowOutline(sheet) {
244
+ function scanRowOutline(sheet, flushed) {
330
245
  const level = new Map();
331
246
  const hidden = new Map();
332
247
  let maxLevel = 0;
333
- for (const { number, properties } of sheet.rows()) {
334
- const rowLevel = properties?.outlineLevel ?? 0;
335
- assertWritableNumber(rowLevel);
248
+ const note = (number, rowLevel, rowHidden) => {
249
+ assertWritableLevel(`row ${number} outlineLevel`, rowLevel);
336
250
  level.set(number, rowLevel);
337
- hidden.set(number, properties?.hidden ?? false);
251
+ hidden.set(number, rowHidden);
338
252
  if (rowLevel > maxLevel)
339
253
  maxLevel = rowLevel;
254
+ };
255
+ for (const [number, entry] of flushed?.rowOutline ?? []) {
256
+ note(number, entry.outlineLevel, entry.hidden);
257
+ }
258
+ for (const { number, properties } of sheet.rows()) {
259
+ note(number, properties?.outlineLevel ?? 0, properties?.hidden ?? false);
340
260
  }
341
261
  const levelOf = (row) => level.get(row) ?? 0;
342
262
  const step = sheet.outline.summaryBelow === false ? 1 : -1;
@@ -345,7 +265,7 @@ function scanRowOutline(sheet) {
345
265
  let detail = summary + step;
346
266
  let sawDetail = false;
347
267
  let allHidden = true;
348
- while (levelOf(detail) > summaryLevel) {
268
+ while (level.get(detail) !== undefined && levelOf(detail) > summaryLevel) {
349
269
  sawDetail = true;
350
270
  if (!hidden.get(detail))
351
271
  allHidden = false;
@@ -356,106 +276,3 @@ function scanRowOutline(sheet) {
356
276
  }
357
277
  return { collapsedSummaries, maxLevel };
358
278
  }
359
- function dateDefaultNumFmt(value) {
360
- const date = value instanceof Date
361
- ? value
362
- : (isFormulaValue(value) || isSharedFormulaValue(value)) && value.result instanceof Date
363
- ? value.result
364
- : undefined;
365
- return date !== undefined && !Number.isNaN(date.getTime()) ? DEFAULT_DATE_NUMFMT : undefined;
366
- }
367
- function cellXml(cell, style, shared, sharedStrings) {
368
- const ref = cell.address;
369
- const value = cell.value;
370
- const s = style !== 0 ? ` s="${style}"` : '';
371
- const formula = cellFormulaXml(ref, s, value, shared);
372
- if (formula !== undefined)
373
- return formula;
374
- if (value instanceof Date) {
375
- if (Number.isNaN(value.getTime()))
376
- return `<c r="${ref}"${s}/>`;
377
- return `<c r="${ref}"${s}><v>${numberText(dateToSerial(value))}</v></c>`;
378
- }
379
- if (typeof value === 'number') {
380
- if (!Number.isFinite(value))
381
- return `<c r="${ref}"${s}/>`;
382
- return `<c r="${ref}"${s}><v>${numberText(value)}</v></c>`;
383
- }
384
- if (typeof value === 'boolean') {
385
- return `<c r="${ref}"${s} t="b"><v>${value ? 1 : 0}</v></c>`;
386
- }
387
- if (typeof value === 'string') {
388
- if (sharedStrings !== null) {
389
- return `<c r="${ref}"${s} t="s"><v>${sharedStrings.intern(value)}</v></c>`;
390
- }
391
- return `<c r="${ref}"${s} t="inlineStr"><is>${textElement(value)}</is></c>`;
392
- }
393
- if (isRichTextValue(value)) {
394
- if (sharedStrings !== null) {
395
- return `<c r="${ref}"${s} t="s"><v>${sharedStrings.intern(value)}</v></c>`;
396
- }
397
- return `<c r="${ref}"${s} t="inlineStr"><is>${richTextRunsXml(value.richText)}</is></c>`;
398
- }
399
- if (isHyperlinkValue(value)) {
400
- const label = typeof value.text === 'string'
401
- ? textElement(value.text)
402
- : richTextRunsXml(value.text.richText);
403
- return `<c r="${ref}"${s} t="inlineStr"><is>${label}</is></c>`;
404
- }
405
- if (isErrorValue(value)) {
406
- return `<c r="${ref}"${s} t="e"><v>${value.error}</v></c>`;
407
- }
408
- if (value === null)
409
- return `<c r="${ref}"${s}/>`;
410
- throw new InternalError(`writing a ${detectValueType(value)} cell value has no arm: every CellValue kind is handled above`);
411
- }
412
- function hasOwnStyle(cell) {
413
- return (CELL_STYLE_FACETS.some((facet) => cell[facet] !== undefined) ||
414
- cell.quotePrefix === true ||
415
- cell[NAMED_STYLE_ID] !== undefined);
416
- }
417
- function cellFormulaXml(ref, s, value, shared) {
418
- if (shared !== undefined) {
419
- if (shared.ref !== undefined && isFormulaValue(value)) {
420
- const f = `<f t="shared" ref="${shared.ref}" si="${shared.si}">${escapeText(mangleFormula(value.formula))}</f>`;
421
- return formulaBodyXml(ref, s, f, value.result);
422
- }
423
- const result = isSharedFormulaValue(value) ? value.result : undefined;
424
- return formulaBodyXml(ref, s, `<f t="shared" si="${shared.si}"/>`, result);
425
- }
426
- if (isDataTableFormulaValue(value)) {
427
- const attrs = `ref="${escapeAttr(value.ref)}"` +
428
- ` dt2D="${value.dataTable2D ? 1 : 0}"` +
429
- ` dtr="${value.dataTableRow ? 1 : 0}"` +
430
- textAttr('r1', value.r1) +
431
- textAttr('r2', value.r2);
432
- return formulaBodyXml(ref, s, `<f t="dataTable" ${attrs}/>`, value.result);
433
- }
434
- if (isFormulaValue(value)) {
435
- return formulaBodyXml(ref, s, `<f>${escapeText(mangleFormula(value.formula))}</f>`, value.result);
436
- }
437
- return undefined;
438
- }
439
- function formulaBodyXml(ref, s, f, result) {
440
- if (result === undefined || (typeof result === 'number' && !Number.isFinite(result))) {
441
- return `<c r="${ref}"${s}>${f}</c>`;
442
- }
443
- if (typeof result === 'number') {
444
- return `<c r="${ref}"${s}>${f}<v>${numberText(result)}</v></c>`;
445
- }
446
- if (typeof result === 'boolean') {
447
- return `<c r="${ref}"${s} t="b">${f}<v>${result ? 1 : 0}</v></c>`;
448
- }
449
- if (typeof result === 'string') {
450
- return `<c r="${ref}"${s} t="str">${f}<v>${escapeSpreadsheetText(result)}</v></c>`;
451
- }
452
- if (isErrorValue(result)) {
453
- return `<c r="${ref}"${s} t="e">${f}<v>${result.error}</v></c>`;
454
- }
455
- if (result instanceof Date) {
456
- if (Number.isNaN(result.getTime()))
457
- return `<c r="${ref}"${s}>${f}</c>`;
458
- return `<c r="${ref}"${s}>${f}<v>${numberText(dateToSerial(result))}</v></c>`;
459
- }
460
- throw new InternalError('writing a non-primitive formula result has no arm: every FormulaResult kind is handled above');
461
- }