reamkit 1.31.0 → 1.32.1

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 (98) 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/crypto/offcrypto.js +56 -12
  10. package/dist/esm/core/document-model/index.d.ts +1 -1
  11. package/dist/esm/core/document-model/types.d.ts +112 -0
  12. package/dist/esm/core/drawingml/chart-geometry.d.ts +36 -13
  13. package/dist/esm/core/drawingml/chart-geometry.js +372 -146
  14. package/dist/esm/core/drawingml/chart-parser.d.ts +19 -0
  15. package/dist/esm/core/drawingml/chart-parser.js +126 -18
  16. package/dist/esm/core/drawingml/colors.d.ts +6 -0
  17. package/dist/esm/core/drawingml/colors.js +23 -3
  18. package/dist/esm/core/drawingml/shape-render.d.ts +3 -1
  19. package/dist/esm/core/drawingml/shape-render.js +4 -2
  20. package/dist/esm/core/font/measure.d.ts +9 -0
  21. package/dist/esm/core/font/measure.js +9 -1
  22. package/dist/esm/core/fonts/families.js +4 -2
  23. package/dist/esm/core/fonts/scripts.js +4 -2
  24. package/dist/esm/core/ir/flow.d.ts +6 -0
  25. package/dist/esm/core/ir/sheet.d.ts +43 -0
  26. package/dist/esm/core/number-format.d.ts +40 -15
  27. package/dist/esm/core/number-format.js +295 -83
  28. package/dist/esm/core/opc/tag-scan.d.ts +15 -0
  29. package/dist/esm/core/opc/tag-scan.js +41 -0
  30. package/dist/esm/core/opc/xml-entities.js +39 -2
  31. package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
  32. package/dist/esm/core/spreadsheet-model/types.d.ts +96 -0
  33. package/dist/esm/core/style-cascade/resolver.js +4 -2
  34. package/dist/esm/excel/column-bands.d.ts +39 -6
  35. package/dist/esm/excel/column-bands.js +41 -17
  36. package/dist/esm/excel/comments-parser.js +19 -0
  37. package/dist/esm/excel/conditional-format.d.ts +7 -3
  38. package/dist/esm/excel/conditional-format.js +77 -39
  39. package/dist/esm/excel/formula/context.d.ts +18 -0
  40. package/dist/esm/excel/formula/eval.js +51 -0
  41. package/dist/esm/excel/formula/functions.js +37 -14
  42. package/dist/esm/excel/formula/index.d.ts +2 -2
  43. package/dist/esm/excel/formula/lexer.d.ts +1 -1
  44. package/dist/esm/excel/formula/lexer.js +41 -0
  45. package/dist/esm/excel/formula/parser.d.ts +25 -0
  46. package/dist/esm/excel/formula/parser.js +95 -0
  47. package/dist/esm/excel/print-model.d.ts +38 -1
  48. package/dist/esm/excel/print-model.js +536 -83
  49. package/dist/esm/excel/shared-strings-parser.d.ts +9 -0
  50. package/dist/esm/excel/shared-strings-parser.js +11 -1
  51. package/dist/esm/excel/sheet-drawing.d.ts +38 -2
  52. package/dist/esm/excel/sheet-drawing.js +47 -13
  53. package/dist/esm/excel/sheet-shape-parser.d.ts +6 -2
  54. package/dist/esm/excel/sheet-shape-parser.js +18 -6
  55. package/dist/esm/excel/sheet-to-flow.d.ts +17 -0
  56. package/dist/esm/excel/sheet-to-flow.js +434 -47
  57. package/dist/esm/excel/styles-parser.d.ts +9 -0
  58. package/dist/esm/excel/styles-parser.js +80 -26
  59. package/dist/esm/excel/table-parser.js +5 -0
  60. package/dist/esm/excel/table-style-presets.d.ts +26 -0
  61. package/dist/esm/excel/table-style-presets.js +1555 -0
  62. package/dist/esm/excel/table-style.d.ts +13 -0
  63. package/dist/esm/excel/table-style.js +75 -0
  64. package/dist/esm/excel/tint.d.ts +10 -0
  65. package/dist/esm/excel/tint.js +75 -0
  66. package/dist/esm/excel/vml-drawing.d.ts +41 -3
  67. package/dist/esm/excel/vml-drawing.js +144 -11
  68. package/dist/esm/excel/worksheet-parser.js +89 -15
  69. package/dist/esm/excel/xls/biff-reader.js +10 -2
  70. package/dist/esm/excel/xlsx-reader.js +234 -19
  71. package/dist/esm/excel/xlsx-writer.js +8 -7
  72. package/dist/esm/html/html-writer.js +202 -20
  73. package/dist/esm/layout/fit-pages.d.ts +11 -0
  74. package/dist/esm/layout/fit-pages.js +86 -0
  75. package/dist/esm/layout/line-paint.d.ts +58 -0
  76. package/dist/esm/layout/line-paint.js +107 -0
  77. package/dist/esm/layout/page-doc.d.ts +51 -0
  78. package/dist/esm/layout/page-doc.js +141 -1
  79. package/dist/esm/layout/styled-layout.d.ts +7 -0
  80. package/dist/esm/layout/styled-layout.js +275 -52
  81. package/dist/esm/layout/turned-section.js +14 -2
  82. package/dist/esm/markdown/markdown-writer.js +75 -13
  83. package/dist/esm/pdf/styled-page-emitter.js +49 -71
  84. package/dist/esm/pdf/styled-page-renderer.js +1 -1
  85. package/dist/esm/pdf-reader/annot-draw.js +2 -2
  86. package/dist/esm/pdf-reader/annots.js +3 -1
  87. package/dist/esm/pdf-reader/font.js +4 -4
  88. package/dist/esm/pdf-reader/layout.js +1 -1
  89. package/dist/esm/svg/svg-text.d.ts +58 -0
  90. package/dist/esm/svg/svg-text.js +322 -0
  91. package/dist/esm/svg/svg-writer.js +135 -71
  92. package/dist/esm/word/document-parser.js +1 -1
  93. package/dist/esm/word/docx-reader.js +13 -8
  94. package/dist/esm/word/docx-to-pdf.js +3 -3
  95. package/dist/esm/word/docx-writer.js +11 -2
  96. package/dist/esm/word/font-table.js +89 -10
  97. package/dist/esm/word/numbering-parser.js +1 -1
  98. package/package.json +7 -3
@@ -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 { parseChart, withChartColorStyle } from "../core/drawingml/chart-parser.js";
6
+ import { OFFICE_2023_THEME_PALETTE, makeColorResolver } from "../core/drawingml/colors.js";
7
+ import { parseChart, pointsPerSeries, 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 } : {},
@@ -393,6 +409,11 @@ function readXlsxToSheetDoc(xlsx) {
393
409
  * them, and a reference naming a sheet this workbook does not have resolves to
394
410
  * nothing rather than to zeros.
395
411
  *
412
+ * A reference reads no more cells than the series' share of the chart's points
413
+ * ({@link pointsPerSeries}), and finds each in an index of its sheet. The range
414
+ * is the file's to name and unbounded — `B2:B99999999999` — and every cell of
415
+ * it was searched for through the whole sheet.
416
+ *
396
417
  * @param chart The parsed chart.
397
418
  * @param sheets Every sheet in the workbook, by tab order.
398
419
  * @param styles The style table (for a referenced cell's number format).
@@ -401,13 +422,27 @@ function readXlsxToSheetDoc(xlsx) {
401
422
  * @returns The chart, unchanged when nothing needed resolving.
402
423
  */
403
424
  function withWorkbookData(chart, sheets, styles, sharedStrings, date1904) {
425
+ const most = pointsPerSeries(chart.series.length);
426
+ const indexes = /* @__PURE__ */ new Map();
427
+ const cellAt = (grid, row, column) => {
428
+ let index = indexes.get(grid);
429
+ if (!index) {
430
+ index = /* @__PURE__ */ new Map();
431
+ for (const cell of grid.cells) {
432
+ const key = `${String(cell.row)}:${String(cell.column)}`;
433
+ if (!index.has(key)) index.set(key, cell);
434
+ }
435
+ indexes.set(grid, index);
436
+ }
437
+ return index.get(`${String(row)}:${String(column)}`);
438
+ };
404
439
  const cellsOf = (ref) => {
405
440
  if (ref === void 0) return void 0;
406
441
  const area = resolveChartRef(ref, sheets);
407
442
  if (!area) return void 0;
408
443
  const out = [];
409
- for (let row = area.startRow; row <= area.endRow; row++) for (let col = area.startColumn; col <= area.endColumn; col++) {
410
- const cell = area.grid.cells.find((c) => c.row === row && c.column === col);
444
+ for (let row = area.startRow; row <= area.endRow && out.length < most; row++) for (let col = area.startColumn; col <= area.endColumn && out.length < most; col++) {
445
+ const cell = cellAt(area.grid, row, col);
411
446
  out.push(cell ? resolveCellText(cell, sharedStrings, styles, date1904) : "");
412
447
  }
413
448
  return out;
@@ -488,10 +523,12 @@ function withPrinterPageSetup(worksheet, pkg, sheetPath, wsRels) {
488
523
  * through the activeX part. What is left is the Forms-toolbar kind, whose
489
524
  * caption and checked state exist nowhere else in the package.
490
525
  */
491
- function readLegacyVml(pkg, sheetPath, wsRels, worksheet) {
526
+ function readLegacyVml(pkg, sheetPath, wsRels, worksheet, colUnitTwips) {
492
527
  const empty = {
493
528
  controls: [],
529
+ notes: [],
494
530
  boxes: /* @__PURE__ */ new Map(),
531
+ anchoredBoxes: /* @__PURE__ */ new Map(),
495
532
  nonPrinting: /* @__PURE__ */ new Set()
496
533
  };
497
534
  const relId = worksheet.legacyDrawingRelId;
@@ -501,9 +538,25 @@ function readLegacyVml(pkg, sheetPath, wsRels, worksheet) {
501
538
  if (!part) return empty;
502
539
  const activeXShapeIds = new Set((worksheet.formControls ?? []).map((fc) => fc.shapeId).filter((id) => id !== void 0));
503
540
  const drawing = parseVmlDrawing(part.data);
541
+ const colWidthPt = makeColWidthPt(worksheet, colUnitTwips);
542
+ const rowHeightPt = makeRowHeightPt(worksheet);
543
+ const anchoredBoxes = /* @__PURE__ */ new Map();
544
+ for (const [id, anchor] of drawing.anchors) {
545
+ const box = anchoredBox(anchor, colWidthPt, rowHeightPt);
546
+ if (box) anchoredBoxes.set(id, box);
547
+ }
548
+ const colOnce = memoTrack(colWidthPt);
549
+ const rowOnce = memoTrack(rowHeightPt);
550
+ const onGrid = (box) => anchoredBox(pixelAnchor(box, colOnce, rowOnce), colOnce, rowOnce);
551
+ for (const [id, box] of drawing.boxes) {
552
+ if (anchoredBoxes.has(id)) continue;
553
+ const placed = onGrid(box);
554
+ if (placed) anchoredBoxes.set(id, placed);
555
+ }
504
556
  const out = [];
505
557
  for (const shape of drawing.controls) {
506
558
  if (shape.shapeId !== void 0 && activeXShapeIds.has(shape.shapeId)) continue;
559
+ const box = (shape.anchor && anchoredBox(shape.anchor, colWidthPt, rowHeightPt)) ?? (shape.box && onGrid(shape.box)) ?? shape.box;
507
560
  out.push({
508
561
  objectType: shape.objectType,
509
562
  ...shape.caption ? {
@@ -511,18 +564,154 @@ function readLegacyVml(pkg, sheetPath, wsRels, worksheet) {
511
564
  caption: shape.caption
512
565
  } : {},
513
566
  ...shape.checked ? { checked: true } : {},
514
- ...shape.box ? { box: shape.box } : {},
567
+ ...box ? { box } : {},
515
568
  ...shape.fontSizePt !== void 0 ? { fontSizePt: shape.fontSizePt } : {}
516
569
  });
517
570
  }
518
571
  return {
519
572
  controls: out,
573
+ notes: drawing.notes,
520
574
  boxes: drawing.boxes,
575
+ anchoredBoxes,
521
576
  nonPrinting: drawing.nonPrinting
522
577
  };
523
578
  }
579
+ /**
580
+ * A legacy VML shape's box by the cells its `<x:Anchor>` names — on the grid
581
+ * we draw, whatever measure of the columns the producer wrote its `style` in.
582
+ *
583
+ * @param anchor The anchor: cells and pixels into them.
584
+ * @param colWidthPt The sheet's column widths.
585
+ * @param rowHeightPt The sheet's row heights.
586
+ * @returns The box, or undefined for an anchor that names no box on the sheet.
587
+ */
588
+ function anchoredBox(anchor, colWidthPt, rowHeightPt) {
589
+ if (anchor[0] < 0 || anchor[2] < 0) return void 0;
590
+ const x0 = trackPt(anchor[0], anchor[1], colWidthPt);
591
+ const x1 = trackPt(anchor[4], anchor[5], colWidthPt);
592
+ const y0 = trackPt(anchor[2], anchor[3], rowHeightPt);
593
+ const y1 = trackPt(anchor[6], anchor[7], rowHeightPt);
594
+ return x1 > x0 && y1 > y0 ? {
595
+ xPt: x0,
596
+ yPt: y0,
597
+ widthPt: x1 - x0,
598
+ heightPt: y1 - y0
599
+ } : void 0;
600
+ }
601
+ /**
602
+ * The cells a box placed by points alone stands on, as a `<x:Anchor>` would
603
+ * name them — counted the way Excel counts its own sheet when it writes such a
604
+ * box: columns in whole pixels, which ours are, and rows in whole pixels too,
605
+ * a row's points truncated (20.1pt is 26px). 45540_form_Footer.xlsx says so
606
+ * itself: a check box anchored at row 7 + 9px stands at `margin-top:184.5pt`,
607
+ * and its seven rows above come to the 237px that leaves only truncated.
608
+ * Taken as points on our grid, whose rows keep their fractions, a box placed
609
+ * that way stood the more rows above its cells the further down it was.
610
+ *
611
+ * @param box The box, in the producer's points.
612
+ * @param colWidthPt The sheet's column widths.
613
+ * @param rowHeightPt The sheet's row heights.
614
+ * @returns The anchor: cells, 0-based, and pixels into them.
615
+ */
616
+ function pixelAnchor(box, colWidthPt, rowHeightPt) {
617
+ const colPx = (i) => Math.round(colWidthPt(i) / PT_PER_PX);
618
+ const rowPx = (i) => Math.trunc(rowHeightPt(i) / PT_PER_PX + 1e-6);
619
+ const locate = (pt, track, limit) => {
620
+ let i = 0;
621
+ let rest = pt / PT_PER_PX;
622
+ while (i < limit && rest >= track(i)) {
623
+ rest -= track(i);
624
+ i++;
625
+ }
626
+ return [i, rest];
627
+ };
628
+ const [c0, dx0] = locate(box.xPt, colPx, MAX_COLUMNS);
629
+ const [r0, dy0] = locate(box.yPt, rowPx, MAX_ROWS);
630
+ const [c1, dx1] = locate(box.xPt + box.widthPt, colPx, MAX_COLUMNS);
631
+ const [r1, dy1] = locate(box.yPt + box.heightPt, rowPx, MAX_ROWS);
632
+ return [
633
+ c0,
634
+ dx0,
635
+ r0,
636
+ dy0,
637
+ c1,
638
+ dx1,
639
+ r1,
640
+ dy1
641
+ ];
642
+ }
643
+ /** A track accessor that answers each index once. */
644
+ function memoTrack(track) {
645
+ const seen = /* @__PURE__ */ new Map();
646
+ return (i) => {
647
+ let v = seen.get(i);
648
+ if (v === void 0) seen.set(i, v = track(i));
649
+ return v;
650
+ };
651
+ }
652
+ /** SpreadsheetML's grid: 16 384 columns, 1 048 576 rows. */
653
+ var MAX_COLUMNS = 16384;
654
+ var MAX_ROWS = 1048576;
655
+ /** Where `offsetPx` pixels into track `index` stands, in points from the first track. */
656
+ function trackPt(index, offsetPx, track) {
657
+ let total = 0;
658
+ for (let i = 0; i < index; i++) total += track(i);
659
+ return total + offsetPx * PT_PER_PX;
660
+ }
661
+ var PT_PER_PX = .75;
662
+ /**
663
+ * §18.7 + the legacy VML drawing — a comment's box, where its shape SHOWS it.
664
+ * The comments part holds what a note says; the VML shape says whether it is
665
+ * shown and where, and the two meet at the cell (`<x:Row>`/`<x:Column>`
666
+ * against the comment's `ref`).
667
+ *
668
+ * The box is placed by its `<x:Anchor>` — cells plus pixels — so it lands on
669
+ * the grid we draw, whatever widths the producer measured with; the shape's
670
+ * `style` is the fallback for an anchor that is missing or unusable
671
+ * (LibreOffice writes negative columns on a sheet that reads from the right).
672
+ *
673
+ * @param comments The sheet's comments, legacy and threaded.
674
+ * @param notes The note shapes of the sheet's legacy VML drawing.
675
+ * @param worksheet The sheet, for its column widths, row heights and merges.
676
+ * @returns The comments, a shown one carrying its box.
677
+ */
678
+ function withShownNotes(comments, notes, worksheet, colUnitTwips) {
679
+ const shown = /* @__PURE__ */ new Map();
680
+ for (const n of notes) if (n.visible) shown.set(`${n.row}:${n.column}`, n);
681
+ if (shown.size === 0) return [...comments];
682
+ const colWidthPt = makeColWidthPt(worksheet, colUnitTwips);
683
+ const rowHeightPt = makeRowHeightPt(worksheet);
684
+ const along = trackPt;
685
+ return comments.map((c) => {
686
+ if (c.threaded) return c;
687
+ let cell;
688
+ try {
689
+ cell = parseCellRef(c.ref);
690
+ } catch {
691
+ return c;
692
+ }
693
+ const note = shown.get(`${cell.row}:${cell.column}`);
694
+ if (!note) return c;
695
+ let box = note.anchor ? anchoredBox(note.anchor, colWidthPt, rowHeightPt) : void 0;
696
+ if (!box && note.box && note.box.xPt >= 0 && note.box.yPt >= 0) box = note.box;
697
+ if (!box) return c;
698
+ const merge = worksheet.merges.find((m) => m.startRow === cell.row && m.startColumn === cell.column);
699
+ return {
700
+ ...c,
701
+ shown: {
702
+ ...box,
703
+ cornerXPt: along((merge?.endColumn ?? cell.column) + 1, 0, colWidthPt),
704
+ cornerYPt: along(cell.row, 0, rowHeightPt),
705
+ fillHex: note.fillHex,
706
+ lineHex: note.lineHex,
707
+ shadow: note.shadow,
708
+ ...note.textAlign ? { textAlign: note.textAlign } : {}
709
+ }
710
+ };
711
+ });
712
+ }
524
713
  function buildThemePalette(pkg, workbookRels) {
525
- const palette = new Map(DEFAULT_THEME_PALETTE);
714
+ const palette = new Map(OFFICE_2023_THEME_PALETTE);
526
715
  for (const rel of workbookRels) {
527
716
  if (!isOoxmlRel(rel.type, "theme")) continue;
528
717
  const resolved = pkg.resolveRelatedPart(WORKBOOK_PART, rel);
@@ -574,6 +763,17 @@ function resolveTableStyle(t, palette) {
574
763
  headerTextHex: "FFFFFF"
575
764
  };
576
765
  }
766
+ function withTableStyle(t, styles, theme) {
767
+ if (t.styleName === void 0) return t;
768
+ const style = resolveTableStyleFormat(t.styleName, styles, {
769
+ ...theme ? { theme } : {},
770
+ indexed: INDEXED_COLORS
771
+ });
772
+ return style ? {
773
+ ...t,
774
+ style
775
+ } : t;
776
+ }
577
777
  function resolvePivotStyle(p, palette) {
578
778
  const m = p.styleName ? /PivotStyle(Light|Medium|Dark)(\d+)/i.exec(p.styleName) : null;
579
779
  if (!m) return p;
@@ -629,9 +829,24 @@ function resolveTableSlicerItems(cache, tableIndex, styles, sharedStrings, date1
629
829
  }
630
830
  return out;
631
831
  }
832
+ function slicerStyleNumber(name) {
833
+ const prefix = /SlicerStyle/gi;
834
+ const isLetter = (i) => /[A-Za-z]/.test(name[i] ?? "");
835
+ const isDigit = (i) => /\d/.test(name[i] ?? "");
836
+ for (let found = prefix.exec(name); found; found = prefix.exec(name)) {
837
+ let end = prefix.lastIndex;
838
+ while (isLetter(end)) end++;
839
+ if (isDigit(end)) {
840
+ let digits = end;
841
+ while (isDigit(digits)) digits++;
842
+ return name.slice(end, digits);
843
+ }
844
+ prefix.lastIndex = end;
845
+ }
846
+ }
632
847
  function resolveSlicerStyle(styleName, palette) {
633
- const m = styleName ? /SlicerStyle[A-Za-z]*?(\d+)/i.exec(styleName) : null;
634
- const column = m ? (Number(m[1]) - 1) % 7 : 1;
848
+ const number = styleName ? slicerStyleNumber(styleName) : void 0;
849
+ const column = number !== void 0 ? (Number(number) - 1) % 7 : 1;
635
850
  const base = column === 0 ? "7F7F7F" : palette.get(`accent${column}`) ?? "4472C4";
636
851
  return {
637
852
  headerHex: base,
@@ -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) {