reamkit 1.31.0 → 1.32.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 (83) hide show
  1. package/README.md +10 -0
  2. package/dist/esm/core/bidi/index.d.ts +10 -0
  3. package/dist/esm/core/bidi/index.js +18 -2
  4. package/dist/esm/core/converter/facade.js +10 -5
  5. package/dist/esm/core/converter/project.js +1 -0
  6. package/dist/esm/core/converter/ream.js +17 -6
  7. package/dist/esm/core/converter/sheet-images.d.ts +17 -0
  8. package/dist/esm/core/converter/sheet-images.js +33 -0
  9. package/dist/esm/core/document-model/index.d.ts +1 -1
  10. package/dist/esm/core/document-model/types.d.ts +112 -0
  11. package/dist/esm/core/drawingml/chart-geometry.d.ts +21 -8
  12. package/dist/esm/core/drawingml/chart-geometry.js +198 -108
  13. package/dist/esm/core/drawingml/chart-parser.js +73 -2
  14. package/dist/esm/core/drawingml/colors.d.ts +6 -0
  15. package/dist/esm/core/drawingml/colors.js +23 -3
  16. package/dist/esm/core/drawingml/shape-render.d.ts +3 -1
  17. package/dist/esm/core/drawingml/shape-render.js +4 -2
  18. package/dist/esm/core/font/measure.d.ts +9 -0
  19. package/dist/esm/core/font/measure.js +9 -1
  20. package/dist/esm/core/fonts/families.js +4 -2
  21. package/dist/esm/core/fonts/scripts.js +4 -2
  22. package/dist/esm/core/ir/flow.d.ts +6 -0
  23. package/dist/esm/core/ir/sheet.d.ts +43 -0
  24. package/dist/esm/core/number-format.js +16 -11
  25. package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
  26. package/dist/esm/core/spreadsheet-model/types.d.ts +96 -0
  27. package/dist/esm/core/style-cascade/resolver.js +4 -2
  28. package/dist/esm/excel/column-bands.d.ts +39 -6
  29. package/dist/esm/excel/column-bands.js +41 -17
  30. package/dist/esm/excel/comments-parser.js +19 -0
  31. package/dist/esm/excel/conditional-format.d.ts +7 -3
  32. package/dist/esm/excel/conditional-format.js +77 -39
  33. package/dist/esm/excel/formula/context.d.ts +18 -0
  34. package/dist/esm/excel/formula/eval.js +51 -0
  35. package/dist/esm/excel/formula/index.d.ts +2 -2
  36. package/dist/esm/excel/formula/lexer.d.ts +1 -1
  37. package/dist/esm/excel/formula/lexer.js +41 -0
  38. package/dist/esm/excel/formula/parser.d.ts +25 -0
  39. package/dist/esm/excel/formula/parser.js +95 -0
  40. package/dist/esm/excel/print-model.d.ts +38 -1
  41. package/dist/esm/excel/print-model.js +522 -80
  42. package/dist/esm/excel/shared-strings-parser.d.ts +9 -0
  43. package/dist/esm/excel/shared-strings-parser.js +11 -1
  44. package/dist/esm/excel/sheet-drawing.d.ts +38 -2
  45. package/dist/esm/excel/sheet-drawing.js +47 -13
  46. package/dist/esm/excel/sheet-shape-parser.d.ts +6 -2
  47. package/dist/esm/excel/sheet-shape-parser.js +18 -6
  48. package/dist/esm/excel/sheet-to-flow.d.ts +17 -0
  49. package/dist/esm/excel/sheet-to-flow.js +434 -47
  50. package/dist/esm/excel/styles-parser.d.ts +9 -0
  51. package/dist/esm/excel/styles-parser.js +80 -26
  52. package/dist/esm/excel/table-parser.js +5 -0
  53. package/dist/esm/excel/table-style-presets.d.ts +26 -0
  54. package/dist/esm/excel/table-style-presets.js +1555 -0
  55. package/dist/esm/excel/table-style.d.ts +13 -0
  56. package/dist/esm/excel/table-style.js +75 -0
  57. package/dist/esm/excel/tint.d.ts +10 -0
  58. package/dist/esm/excel/tint.js +75 -0
  59. package/dist/esm/excel/vml-drawing.d.ts +41 -3
  60. package/dist/esm/excel/vml-drawing.js +143 -10
  61. package/dist/esm/excel/worksheet-parser.js +89 -15
  62. package/dist/esm/excel/xls/biff-reader.js +10 -2
  63. package/dist/esm/excel/xlsx-reader.js +195 -14
  64. package/dist/esm/excel/xlsx-writer.js +8 -7
  65. package/dist/esm/html/html-writer.js +202 -20
  66. package/dist/esm/layout/fit-pages.d.ts +11 -0
  67. package/dist/esm/layout/fit-pages.js +86 -0
  68. package/dist/esm/layout/line-paint.d.ts +58 -0
  69. package/dist/esm/layout/line-paint.js +107 -0
  70. package/dist/esm/layout/page-doc.d.ts +51 -0
  71. package/dist/esm/layout/page-doc.js +141 -1
  72. package/dist/esm/layout/styled-layout.d.ts +7 -0
  73. package/dist/esm/layout/styled-layout.js +274 -51
  74. package/dist/esm/layout/turned-section.js +14 -2
  75. package/dist/esm/markdown/markdown-writer.js +17 -6
  76. package/dist/esm/pdf/styled-page-emitter.js +49 -71
  77. package/dist/esm/pdf/styled-page-renderer.js +1 -1
  78. package/dist/esm/pdf-reader/font.js +3 -3
  79. package/dist/esm/svg/svg-text.d.ts +58 -0
  80. package/dist/esm/svg/svg-text.js +322 -0
  81. package/dist/esm/svg/svg-writer.js +135 -71
  82. package/dist/esm/word/docx-writer.js +11 -2
  83. package/package.json +5 -1
@@ -195,6 +195,8 @@ function readSheet(wb, view, start, blips, resources, sharedStrings, chartData,
195
195
  const hyperlinks = [];
196
196
  const rowHeights = [];
197
197
  let frozen = false;
198
+ let showGrid = true;
199
+ let rightToLeft = false;
198
200
  let paneSplit;
199
201
  let maxRow = -1;
200
202
  let maxColumn = -1;
@@ -300,7 +302,11 @@ function readSheet(wb, view, start, blips, resources, sharedStrings, chartData,
300
302
  }
301
303
  break;
302
304
  case REC.WINDOW2:
303
- if (d.length >= 2) frozen = (readU16(d, 0) & 8) !== 0;
305
+ if (d.length >= 2) {
306
+ frozen = (readU16(d, 0) & 8) !== 0;
307
+ showGrid = (readU16(d, 0) & 2) !== 0;
308
+ rightToLeft = (readU16(d, 0) & 64) !== 0;
309
+ }
304
310
  break;
305
311
  case REC.PANE:
306
312
  if (d.length >= 4) paneSplit = {
@@ -331,7 +337,9 @@ function readSheet(wb, view, start, blips, resources, sharedStrings, chartData,
331
337
  ...parsePrintModel(records),
332
338
  ...dataValidations.length > 0 ? { dataValidations } : {},
333
339
  ...conditionalFormats.length > 0 ? { conditionalFormats } : {},
334
- ...pane ? { pane } : {}
340
+ ...pane ? { pane } : {},
341
+ ...showGrid ? {} : { showGridLines: false },
342
+ ...rightToLeft ? { rightToLeft: true } : {}
335
343
  };
336
344
  const drawing = gatherDrawing(records, REC.MSODRAWING);
337
345
  const place = anchorPlacer(grid);
@@ -1,22 +1,24 @@
1
1
  import { ResourceStore } from "../core/ir/resources.js";
2
2
  import { FEATURES } from "../core/ir/features.js";
3
3
  import { detectImageFormat } from "../core/images.js";
4
+ import { INDEXED_COLORS } from "../core/indexed-colors.js";
4
5
  import { bytesInclude, packageHasPart } from "../core/bytes.js";
5
- import { DEFAULT_THEME_PALETTE, makeColorResolver } from "../core/drawingml/colors.js";
6
+ import { OFFICE_2023_THEME_PALETTE, makeColorResolver } from "../core/drawingml/colors.js";
6
7
  import { parseChart, withChartColorStyle } from "../core/drawingml/chart-parser.js";
7
8
  import { parseTheme, parseThemeEffectStyles, parseThemeFillStyles, parseThemeLineWidths } from "../core/drawingml/theme-parser.js";
8
9
  import { isOoxmlRel } from "../core/opc/relationship-types.js";
9
10
  import { OpcPackage } from "../core/opc/package.js";
10
11
  import { parseCoreProperties } from "../core/opc/core-properties.js";
11
12
  import "../core/opc/index.js";
13
+ import { parseCellRef } from "./cell-reference.js";
12
14
  import { parseAreaRef } from "./defined-name-ref.js";
13
15
  import { parseSharedStrings } from "./shared-strings-parser.js";
14
16
  import { parseWorkbook } from "./workbook-parser.js";
15
17
  import { EMPTY_XLSX_STYLES, parseXlsxStyles } from "./styles-parser.js";
16
18
  import { parseWorksheet } from "./worksheet-parser.js";
17
19
  import "./index.js";
18
- import { resolveCellText } from "./print-model.js";
19
- import { parseSheetDrawing } from "./sheet-drawing.js";
20
+ import { columnUnitTwips, resolveCellText } from "./print-model.js";
21
+ import { makeColWidthPt, makeRowHeightPt, parseSheetDrawing } from "./sheet-drawing.js";
20
22
  import { parseTablePartFull } from "./table-parser.js";
21
23
  import { parsePivotTablePart } from "./pivot-table-parser.js";
22
24
  import { parseSlicerCachePart, parseSlicerPart } from "./slicer-parser.js";
@@ -28,6 +30,7 @@ import { parseRichValueText } from "./rich-value.js";
28
30
  import { activeXBinRelId, activeXType, activeXTypeFromPart, parseActiveX, parseActiveXBin } from "./activex-parser.js";
29
31
  import { parseDiagramFrames, parseDiagramShapes, parseSheetShapes } from "./sheet-shape-parser.js";
30
32
  import { projectSheetDoc } from "./sheet-to-flow.js";
33
+ import { resolveTableStyleFormat } from "./table-style.js";
31
34
  //#region src/excel/xlsx-reader.ts
32
35
  var WORKBOOK_PART = "xl/workbook.xml";
33
36
  var SHARED_STRINGS_PART = "xl/sharedStrings.xml";
@@ -92,6 +95,7 @@ function readXlsxToSheetDoc(xlsx) {
92
95
  const richValueText = parseRichValueText(pkg.getPart(METADATA_PART), pkg.getPart(RICH_VALUE_STRUCTURE_PART), pkg.getPart(RICH_VALUE_PART));
93
96
  const stylesData = pkg.getPart(STYLES_PART);
94
97
  const styles = stylesData ? parseXlsxStyles(stylesData, themePalette) : EMPTY_XLSX_STYLES;
98
+ const colUnit = columnUnitTwips(styles);
95
99
  const persons = /* @__PURE__ */ new Map();
96
100
  for (const rel of workbookRels) {
97
101
  if (!rel.type.endsWith(PERSON_REL_TAIL)) continue;
@@ -118,15 +122,23 @@ function readXlsxToSheetDoc(xlsx) {
118
122
  if (!resolved) continue;
119
123
  let worksheet = parseWorksheet(resolved.data, themePalette);
120
124
  worksheet = withPrinterPageSetup(worksheet, pkg, resolved.path, pkg.getPartRelationships(resolved.path));
125
+ if (worksheet.chartSheet && worksheet.pageSetup?.orientation === void 0) worksheet = {
126
+ ...worksheet,
127
+ pageSetup: {
128
+ ...worksheet.pageSetup,
129
+ orientation: "landscape"
130
+ }
131
+ };
121
132
  worksheet = withRichValues(worksheet, richValueText);
122
133
  const charts = [];
123
134
  const images = [];
124
135
  let shapes;
136
+ const screenOnlyShapes = /* @__PURE__ */ new Set();
125
137
  if (worksheet.drawingRelId) {
126
138
  const drawingRel = pkg.getPartRelationships(resolved.path).find((r) => r.id === worksheet.drawingRelId);
127
139
  const drawing = drawingRel ? pkg.resolveRelatedPart(resolved.path, drawingRel) : void 0;
128
140
  if (drawing) {
129
- const { charts: chartRefs, pictures } = parseSheetDrawing(drawing.data, drawing.path, pkg, worksheet);
141
+ const { charts: chartRefs, pictures } = parseSheetDrawing(drawing.data, drawing.path, pkg, worksheet, colUnit);
130
142
  for (const ref of chartRefs) {
131
143
  if (!chartData.has(ref.chartPartPath)) {
132
144
  const chartXml = pkg.getPart(ref.chartPartPath);
@@ -139,7 +151,8 @@ function readXlsxToSheetDoc(xlsx) {
139
151
  widthPt: ref.widthPt,
140
152
  heightPt: ref.heightPt,
141
153
  xPt: ref.xPt,
142
- yPt: ref.yPt
154
+ yPt: ref.yPt,
155
+ ...ref.screenOnly ? { screenOnly: true } : {}
143
156
  });
144
157
  }
145
158
  for (const pic of pictures) {
@@ -151,14 +164,15 @@ function readXlsxToSheetDoc(xlsx) {
151
164
  widthPt: pic.widthPt,
152
165
  heightPt: pic.heightPt,
153
166
  xPt: pic.xPt,
154
- yPt: pic.yPt
167
+ yPt: pic.yPt,
168
+ ...pic.screenOnly ? { screenOnly: true } : {}
155
169
  });
156
170
  }
157
171
  if (bytesInclude(drawing.data, ":sp>") || bytesInclude(drawing.data, ":sp ")) {
158
- const parsed = parseSheetShapes(drawing.data, worksheet, resolveColor, themeLineWidths, themeFillStyles, themeEffectStyles);
172
+ const parsed = parseSheetShapes(drawing.data, worksheet, resolveColor, themeLineWidths, themeFillStyles, themeEffectStyles, screenOnlyShapes, colUnit);
159
173
  if (parsed.length > 0) shapes = parsed;
160
174
  }
161
- const frames = parseDiagramFrames(drawing.data, worksheet);
175
+ const frames = parseDiagramFrames(drawing.data, worksheet, colUnit);
162
176
  if (frames.length > 0) {
163
177
  const dsp = pkg.getPartRelationships(drawing.path).filter((r) => r.type.endsWith("/diagramDrawing"));
164
178
  const fromDiagrams = [];
@@ -181,7 +195,7 @@ function readXlsxToSheetDoc(xlsx) {
181
195
  const part = rel ? pkg.resolveRelatedPart(resolved.path, rel) : void 0;
182
196
  const full = part ? parseTablePartFull(part.data) : void 0;
183
197
  if (!full) continue;
184
- resolvedTables.push(resolveTableStyle(full.table, palette));
198
+ resolvedTables.push(withTableStyle(resolveTableStyle(full.table, palette), styles, themePalette));
185
199
  if (full.id !== void 0) tableIndex.set(full.id, {
186
200
  cells: worksheet.cells,
187
201
  ref: full.table.ref,
@@ -266,7 +280,8 @@ function readXlsxToSheetDoc(xlsx) {
266
280
  ...props
267
281
  };
268
282
  };
269
- const legacyVml = readLegacyVml(pkg, resolved.path, wsRels, worksheet);
283
+ const legacyVml = readLegacyVml(pkg, resolved.path, wsRels, worksheet, colUnit);
284
+ if (comments && legacyVml.notes.length > 0) comments = withShownNotes(comments, legacyVml.notes, worksheet, colUnit);
270
285
  let formControls;
271
286
  if (worksheet.formControls && worksheet.formControls.length > 0) {
272
287
  const resolvedControls = [];
@@ -275,7 +290,7 @@ function readXlsxToSheetDoc(xlsx) {
275
290
  if (fc.shapeId !== void 0 && legacyVml.nonPrinting.has(fc.shapeId)) continue;
276
291
  const rel = wsRels.find((r) => r.id === fc.relId);
277
292
  const part = rel ? pkg.resolveRelatedPart(resolved.path, rel) : void 0;
278
- const box = fc.shapeId !== void 0 ? legacyVml.boxes.get(fc.shapeId) : void 0;
293
+ const box = fc.shapeId !== void 0 ? legacyVml.anchoredBoxes.get(fc.shapeId) ?? legacyVml.boxes.get(fc.shapeId) : void 0;
279
294
  if (part && ACTIVEX_PART.test(part.path)) {
280
295
  resolvedAx.push({
281
296
  ...activeXState(part),
@@ -320,6 +335,7 @@ function readXlsxToSheetDoc(xlsx) {
320
335
  ...charts.length > 0 ? { charts } : {},
321
336
  ...images.length > 0 ? { images } : {},
322
337
  ...shapes ? { shapes } : {},
338
+ ...shapes && screenOnlyShapes.size > 0 ? { screenOnlyShapes: new Set(shapes.flatMap((shape, i) => screenOnlyShapes.has(shape) ? [i] : [])) } : {},
323
339
  ...hyperlinks ? { hyperlinks } : {},
324
340
  ...comments ? { comments } : {},
325
341
  ...formControls ? { formControls } : {},
@@ -488,10 +504,12 @@ function withPrinterPageSetup(worksheet, pkg, sheetPath, wsRels) {
488
504
  * through the activeX part. What is left is the Forms-toolbar kind, whose
489
505
  * caption and checked state exist nowhere else in the package.
490
506
  */
491
- function readLegacyVml(pkg, sheetPath, wsRels, worksheet) {
507
+ function readLegacyVml(pkg, sheetPath, wsRels, worksheet, colUnitTwips) {
492
508
  const empty = {
493
509
  controls: [],
510
+ notes: [],
494
511
  boxes: /* @__PURE__ */ new Map(),
512
+ anchoredBoxes: /* @__PURE__ */ new Map(),
495
513
  nonPrinting: /* @__PURE__ */ new Set()
496
514
  };
497
515
  const relId = worksheet.legacyDrawingRelId;
@@ -501,9 +519,25 @@ function readLegacyVml(pkg, sheetPath, wsRels, worksheet) {
501
519
  if (!part) return empty;
502
520
  const activeXShapeIds = new Set((worksheet.formControls ?? []).map((fc) => fc.shapeId).filter((id) => id !== void 0));
503
521
  const drawing = parseVmlDrawing(part.data);
522
+ const colWidthPt = makeColWidthPt(worksheet, colUnitTwips);
523
+ const rowHeightPt = makeRowHeightPt(worksheet);
524
+ const anchoredBoxes = /* @__PURE__ */ new Map();
525
+ for (const [id, anchor] of drawing.anchors) {
526
+ const box = anchoredBox(anchor, colWidthPt, rowHeightPt);
527
+ if (box) anchoredBoxes.set(id, box);
528
+ }
529
+ const colOnce = memoTrack(colWidthPt);
530
+ const rowOnce = memoTrack(rowHeightPt);
531
+ const onGrid = (box) => anchoredBox(pixelAnchor(box, colOnce, rowOnce), colOnce, rowOnce);
532
+ for (const [id, box] of drawing.boxes) {
533
+ if (anchoredBoxes.has(id)) continue;
534
+ const placed = onGrid(box);
535
+ if (placed) anchoredBoxes.set(id, placed);
536
+ }
504
537
  const out = [];
505
538
  for (const shape of drawing.controls) {
506
539
  if (shape.shapeId !== void 0 && activeXShapeIds.has(shape.shapeId)) continue;
540
+ const box = (shape.anchor && anchoredBox(shape.anchor, colWidthPt, rowHeightPt)) ?? (shape.box && onGrid(shape.box)) ?? shape.box;
507
541
  out.push({
508
542
  objectType: shape.objectType,
509
543
  ...shape.caption ? {
@@ -511,18 +545,154 @@ function readLegacyVml(pkg, sheetPath, wsRels, worksheet) {
511
545
  caption: shape.caption
512
546
  } : {},
513
547
  ...shape.checked ? { checked: true } : {},
514
- ...shape.box ? { box: shape.box } : {},
548
+ ...box ? { box } : {},
515
549
  ...shape.fontSizePt !== void 0 ? { fontSizePt: shape.fontSizePt } : {}
516
550
  });
517
551
  }
518
552
  return {
519
553
  controls: out,
554
+ notes: drawing.notes,
520
555
  boxes: drawing.boxes,
556
+ anchoredBoxes,
521
557
  nonPrinting: drawing.nonPrinting
522
558
  };
523
559
  }
560
+ /**
561
+ * A legacy VML shape's box by the cells its `<x:Anchor>` names — on the grid
562
+ * we draw, whatever measure of the columns the producer wrote its `style` in.
563
+ *
564
+ * @param anchor The anchor: cells and pixels into them.
565
+ * @param colWidthPt The sheet's column widths.
566
+ * @param rowHeightPt The sheet's row heights.
567
+ * @returns The box, or undefined for an anchor that names no box on the sheet.
568
+ */
569
+ function anchoredBox(anchor, colWidthPt, rowHeightPt) {
570
+ if (anchor[0] < 0 || anchor[2] < 0) return void 0;
571
+ const x0 = trackPt(anchor[0], anchor[1], colWidthPt);
572
+ const x1 = trackPt(anchor[4], anchor[5], colWidthPt);
573
+ const y0 = trackPt(anchor[2], anchor[3], rowHeightPt);
574
+ const y1 = trackPt(anchor[6], anchor[7], rowHeightPt);
575
+ return x1 > x0 && y1 > y0 ? {
576
+ xPt: x0,
577
+ yPt: y0,
578
+ widthPt: x1 - x0,
579
+ heightPt: y1 - y0
580
+ } : void 0;
581
+ }
582
+ /**
583
+ * The cells a box placed by points alone stands on, as a `<x:Anchor>` would
584
+ * name them — counted the way Excel counts its own sheet when it writes such a
585
+ * box: columns in whole pixels, which ours are, and rows in whole pixels too,
586
+ * a row's points truncated (20.1pt is 26px). 45540_form_Footer.xlsx says so
587
+ * itself: a check box anchored at row 7 + 9px stands at `margin-top:184.5pt`,
588
+ * and its seven rows above come to the 237px that leaves only truncated.
589
+ * Taken as points on our grid, whose rows keep their fractions, a box placed
590
+ * that way stood the more rows above its cells the further down it was.
591
+ *
592
+ * @param box The box, in the producer's points.
593
+ * @param colWidthPt The sheet's column widths.
594
+ * @param rowHeightPt The sheet's row heights.
595
+ * @returns The anchor: cells, 0-based, and pixels into them.
596
+ */
597
+ function pixelAnchor(box, colWidthPt, rowHeightPt) {
598
+ const colPx = (i) => Math.round(colWidthPt(i) / PT_PER_PX);
599
+ const rowPx = (i) => Math.trunc(rowHeightPt(i) / PT_PER_PX + 1e-6);
600
+ const locate = (pt, track, limit) => {
601
+ let i = 0;
602
+ let rest = pt / PT_PER_PX;
603
+ while (i < limit && rest >= track(i)) {
604
+ rest -= track(i);
605
+ i++;
606
+ }
607
+ return [i, rest];
608
+ };
609
+ const [c0, dx0] = locate(box.xPt, colPx, MAX_COLUMNS);
610
+ const [r0, dy0] = locate(box.yPt, rowPx, MAX_ROWS);
611
+ const [c1, dx1] = locate(box.xPt + box.widthPt, colPx, MAX_COLUMNS);
612
+ const [r1, dy1] = locate(box.yPt + box.heightPt, rowPx, MAX_ROWS);
613
+ return [
614
+ c0,
615
+ dx0,
616
+ r0,
617
+ dy0,
618
+ c1,
619
+ dx1,
620
+ r1,
621
+ dy1
622
+ ];
623
+ }
624
+ /** A track accessor that answers each index once. */
625
+ function memoTrack(track) {
626
+ const seen = /* @__PURE__ */ new Map();
627
+ return (i) => {
628
+ let v = seen.get(i);
629
+ if (v === void 0) seen.set(i, v = track(i));
630
+ return v;
631
+ };
632
+ }
633
+ /** SpreadsheetML's grid: 16 384 columns, 1 048 576 rows. */
634
+ var MAX_COLUMNS = 16384;
635
+ var MAX_ROWS = 1048576;
636
+ /** Where `offsetPx` pixels into track `index` stands, in points from the first track. */
637
+ function trackPt(index, offsetPx, track) {
638
+ let total = 0;
639
+ for (let i = 0; i < index; i++) total += track(i);
640
+ return total + offsetPx * PT_PER_PX;
641
+ }
642
+ var PT_PER_PX = .75;
643
+ /**
644
+ * §18.7 + the legacy VML drawing — a comment's box, where its shape SHOWS it.
645
+ * The comments part holds what a note says; the VML shape says whether it is
646
+ * shown and where, and the two meet at the cell (`<x:Row>`/`<x:Column>`
647
+ * against the comment's `ref`).
648
+ *
649
+ * The box is placed by its `<x:Anchor>` — cells plus pixels — so it lands on
650
+ * the grid we draw, whatever widths the producer measured with; the shape's
651
+ * `style` is the fallback for an anchor that is missing or unusable
652
+ * (LibreOffice writes negative columns on a sheet that reads from the right).
653
+ *
654
+ * @param comments The sheet's comments, legacy and threaded.
655
+ * @param notes The note shapes of the sheet's legacy VML drawing.
656
+ * @param worksheet The sheet, for its column widths, row heights and merges.
657
+ * @returns The comments, a shown one carrying its box.
658
+ */
659
+ function withShownNotes(comments, notes, worksheet, colUnitTwips) {
660
+ const shown = /* @__PURE__ */ new Map();
661
+ for (const n of notes) if (n.visible) shown.set(`${n.row}:${n.column}`, n);
662
+ if (shown.size === 0) return [...comments];
663
+ const colWidthPt = makeColWidthPt(worksheet, colUnitTwips);
664
+ const rowHeightPt = makeRowHeightPt(worksheet);
665
+ const along = trackPt;
666
+ return comments.map((c) => {
667
+ if (c.threaded) return c;
668
+ let cell;
669
+ try {
670
+ cell = parseCellRef(c.ref);
671
+ } catch {
672
+ return c;
673
+ }
674
+ const note = shown.get(`${cell.row}:${cell.column}`);
675
+ if (!note) return c;
676
+ let box = note.anchor ? anchoredBox(note.anchor, colWidthPt, rowHeightPt) : void 0;
677
+ if (!box && note.box && note.box.xPt >= 0 && note.box.yPt >= 0) box = note.box;
678
+ if (!box) return c;
679
+ const merge = worksheet.merges.find((m) => m.startRow === cell.row && m.startColumn === cell.column);
680
+ return {
681
+ ...c,
682
+ shown: {
683
+ ...box,
684
+ cornerXPt: along((merge?.endColumn ?? cell.column) + 1, 0, colWidthPt),
685
+ cornerYPt: along(cell.row, 0, rowHeightPt),
686
+ fillHex: note.fillHex,
687
+ lineHex: note.lineHex,
688
+ shadow: note.shadow,
689
+ ...note.textAlign ? { textAlign: note.textAlign } : {}
690
+ }
691
+ };
692
+ });
693
+ }
524
694
  function buildThemePalette(pkg, workbookRels) {
525
- const palette = new Map(DEFAULT_THEME_PALETTE);
695
+ const palette = new Map(OFFICE_2023_THEME_PALETTE);
526
696
  for (const rel of workbookRels) {
527
697
  if (!isOoxmlRel(rel.type, "theme")) continue;
528
698
  const resolved = pkg.resolveRelatedPart(WORKBOOK_PART, rel);
@@ -574,6 +744,17 @@ function resolveTableStyle(t, palette) {
574
744
  headerTextHex: "FFFFFF"
575
745
  };
576
746
  }
747
+ function withTableStyle(t, styles, theme) {
748
+ if (t.styleName === void 0) return t;
749
+ const style = resolveTableStyleFormat(t.styleName, styles, {
750
+ ...theme ? { theme } : {},
751
+ indexed: INDEXED_COLORS
752
+ });
753
+ return style ? {
754
+ ...t,
755
+ style
756
+ } : t;
757
+ }
577
758
  function resolvePivotStyle(p, palette) {
578
759
  const m = p.styleName ? /PivotStyle(Light|Medium|Dark)(\d+)/i.exec(p.styleName) : null;
579
760
  if (!m) return p;
@@ -213,22 +213,23 @@ function worksheetXml(grid, tableRelIds = [], drawingRelId) {
213
213
  return `<row r="${r + 1}"${heightAttrs}>${cells.map(cellXml).join("")}</row>`;
214
214
  }).join("");
215
215
  const mergesXml = grid.merges.length > 0 ? `<mergeCells count="${grid.merges.length}">${grid.merges.map((m) => `<mergeCell ref="${cellRef(m.startRow, m.startColumn)}:${cellRef(m.endRow, m.endColumn)}"/>`).join("")}</mergeCells>` : "";
216
- return XML_DECL + `<worksheet xmlns="${MAIN_NS}" xmlns:r="${R_NS}">` + (grid.fitToPage ? "<sheetPr><pageSetUpPr fitToPage=\"1\"/></sheetPr>" : "") + dimension + sheetViewsXml(grid.pane) + colsXml + `<sheetData>${rowsXml}</sheetData>` + mergesXml + conditionalFormattingXml(grid.conditionalFormats) + dataValidationsXml(grid.dataValidations) + printOptionsXml(grid.printOptions) + pageMarginsXml(grid.pageMargins) + pageSetupXml(grid.pageSetup) + breaksXml("rowBreaks", grid.rowBreaks) + breaksXml("colBreaks", grid.colBreaks) + (drawingRelId ? `<drawing r:id="${drawingRelId}"/>` : "") + (tableRelIds.length > 0 ? `<tableParts count="${tableRelIds.length}">${tableRelIds.map((id) => `<tablePart r:id="${id}"/>`).join("")}</tableParts>` : "") + sparklineExtXml(grid.sparklines) + "</worksheet>";
216
+ return XML_DECL + `<worksheet xmlns="${MAIN_NS}" xmlns:r="${R_NS}">` + (grid.fitToPage ? "<sheetPr><pageSetUpPr fitToPage=\"1\"/></sheetPr>" : "") + dimension + sheetViewsXml(grid.pane, grid.showGridLines, grid.rightToLeft) + colsXml + `<sheetData>${rowsXml}</sheetData>` + mergesXml + conditionalFormattingXml(grid.conditionalFormats) + dataValidationsXml(grid.dataValidations) + printOptionsXml(grid.printOptions) + pageMarginsXml(grid.pageMargins) + pageSetupXml(grid.pageSetup) + breaksXml("rowBreaks", grid.rowBreaks) + breaksXml("colBreaks", grid.colBreaks) + (drawingRelId ? `<drawing r:id="${drawingRelId}"/>` : "") + (tableRelIds.length > 0 ? `<tableParts count="${tableRelIds.length}">${tableRelIds.map((id) => `<tablePart r:id="${id}"/>`).join("")}</tableParts>` : "") + sparklineExtXml(grid.sparklines) + "</worksheet>";
217
217
  }
218
- function sheetViewsXml(pane) {
219
- if (!pane || pane.frozenRows <= 0 && pane.frozenCols <= 0) return "";
218
+ function sheetViewsXml(pane, showGridLines, rightToLeft) {
219
+ const grid = (showGridLines === false ? " showGridLines=\"0\"" : "") + (rightToLeft ? " rightToLeft=\"1\"" : "");
220
+ if (!pane || pane.frozenRows <= 0 && pane.frozenCols <= 0) return grid ? `<sheetViews><sheetView${grid} workbookViewId="0"/></sheetViews>` : "";
220
221
  const { frozenRows, frozenCols } = pane;
221
222
  const topLeftCell = cellRef(frozenRows, frozenCols);
222
223
  const activePane = frozenRows > 0 && frozenCols > 0 ? "bottomRight" : frozenRows > 0 ? "bottomLeft" : "topRight";
223
- return `<sheetViews><sheetView workbookViewId="0"><pane${(frozenCols > 0 ? ` xSplit="${frozenCols}"` : "") + (frozenRows > 0 ? ` ySplit="${frozenRows}"` : "") + ` topLeftCell="${topLeftCell}" activePane="${activePane}" state="frozen"`}/></sheetView></sheetViews>`;
224
+ return `<sheetViews><sheetView${grid} workbookViewId="0"><pane${(frozenCols > 0 ? ` xSplit="${frozenCols}"` : "") + (frozenRows > 0 ? ` ySplit="${frozenRows}"` : "") + ` topLeftCell="${topLeftCell}" activePane="${activePane}" state="frozen"`}/></sheetView></sheetViews>`;
224
225
  }
225
226
  function tableXml(t, id) {
226
227
  const ref = rangeRef(t.ref);
227
228
  const name = t.name ?? `Table${id}`;
228
229
  const ncols = t.ref.endColumn - t.ref.startColumn + 1;
229
- const columns = Array.from({ length: ncols }, (_, c) => `<tableColumn id="${c + 1}" name="Column${c + 1}"/>`).join("");
230
+ const columns = Array.from({ length: ncols }, (_, c) => `<tableColumn id="${c + 1}" name="${escapeAttr(t.columns?.[c] ?? `Column${c + 1}`)}"/>`).join("");
230
231
  const styleInfo = t.styleName ? `<tableStyleInfo name="${escapeAttr(t.styleName)}" showFirstColumn="${t.showFirstColumn ? 1 : 0}" showLastColumn="${t.showLastColumn ? 1 : 0}" showRowStripes="${t.showRowStripes ? 1 : 0}" showColumnStripes="${t.showColumnStripes ? 1 : 0}"/>` : "";
231
- return XML_DECL + `<table xmlns="${MAIN_NS}" id="${id}" name="${escapeAttr(name)}" displayName="${escapeAttr(name)}" ref="${ref}"${t.headerRowCount !== 1 ? ` headerRowCount="${t.headerRowCount}"` : ""} totalsRowShown="0">` + (t.autoFilter ? `<autoFilter ref="${ref}"/>` : "") + `<tableColumns count="${ncols}">${columns}</tableColumns>` + styleInfo + "</table>";
232
+ return XML_DECL + `<table xmlns="${MAIN_NS}" id="${id}" name="${escapeAttr(name)}" displayName="${escapeAttr(name)}" ref="${ref}"${t.headerRowCount !== 1 ? ` headerRowCount="${t.headerRowCount}"` : ""}${t.totalsRowCount ? ` totalsRowCount="${t.totalsRowCount}"` : " totalsRowShown=\"0\""}>` + (t.autoFilter ? `<autoFilter ref="${ref}"/>` : "") + `<tableColumns count="${ncols}">${columns}</tableColumns>` + styleInfo + "</table>";
232
233
  }
233
234
  function conditionalFormattingXml(cfs) {
234
235
  if (!cfs || cfs.length === 0) return "";
@@ -346,7 +347,7 @@ function edgeXml(name, edge) {
346
347
  function cellXfXml(xf) {
347
348
  const apply = applyAttr("applyNumberFormat", xf.applyNumberFormat) + applyAttr("applyFont", xf.applyFont) + applyAttr("applyFill", xf.applyFill) + applyAttr("applyBorder", xf.applyBorder) + applyAttr("applyAlignment", xf.applyAlignment);
348
349
  const a = xf.alignment;
349
- const alignment = a ? `<alignment${a.horizontal ? ` horizontal="${a.horizontal}"` : ""}${a.vertical ? ` vertical="${a.vertical}"` : ""}${a.wrapText ? " wrapText=\"1\"" : ""}${a.textRotation !== void 0 ? ` textRotation="${a.textRotation}"` : ""}${a.indent !== void 0 ? ` indent="${a.indent}"` : ""}${a.shrinkToFit ? " shrinkToFit=\"1\"" : ""}/>` : "";
350
+ const alignment = a ? `<alignment${a.horizontal ? ` horizontal="${a.horizontal}"` : ""}${a.vertical ? ` vertical="${a.vertical}"` : ""}${a.wrapText ? " wrapText=\"1\"" : ""}${a.textRotation !== void 0 ? ` textRotation="${a.textRotation}"` : ""}${a.indent !== void 0 ? ` indent="${a.indent}"` : ""}${a.shrinkToFit ? " shrinkToFit=\"1\"" : ""}${a.readingOrder ? ` readingOrder="${a.readingOrder === "rtl" ? 2 : 1}"` : ""}/>` : "";
350
351
  return `<xf numFmtId="${xf.numFmtId}" fontId="${xf.fontId}" fillId="${xf.fillId}" borderId="${xf.borderId}"${apply}>` + alignment + "</xf>";
351
352
  }
352
353
  function applyAttr(name, value) {