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
@@ -3,6 +3,7 @@ import { scriptForCodepoint } from "../core/fonts/scripts.js";
3
3
  import { shapeText } from "../core/font/opentype-layout.js";
4
4
  import { hasSubstitutable, substituteLetters } from "../core/font/ligatures.js";
5
5
  import { createFontMeasure } from "../core/font/measure.js";
6
+ import { pageItemBounds } from "./page-doc.js";
6
7
  import { halfPtToPt, pt } from "../core/ir/units.js";
7
8
  import { ResourceStore } from "../core/ir/resources.js";
8
9
  import { headingLevelOf } from "../core/outline.js";
@@ -1841,7 +1842,7 @@ function metafileDrawing(pic, widthPt, heightPt, options, fontResources, colorCh
1841
1842
  function layoutChartBlock(block, options, fontResources, contentWidth, maxHeight, imageResources) {
1842
1843
  let widthPt = block.width;
1843
1844
  let heightPt = block.height;
1844
- if (widthPt > contentWidth && widthPt > 0) {
1845
+ if (widthPt > contentWidth && widthPt > 0 && !isOutOfFlowFloat(block.float)) {
1845
1846
  const scale = contentWidth / widthPt;
1846
1847
  widthPt = contentWidth;
1847
1848
  heightPt *= scale;
@@ -1911,8 +1912,13 @@ function rectAtPath(x, y, w, h) {
1911
1912
  return new PathBuilder().moveTo(x, y).lineTo(x + w, y).lineTo(x + w, y + h).lineTo(x, y + h).close().build();
1912
1913
  }
1913
1914
  var CF_ICON_SIZE_PT = 9;
1915
+ /** How far inside its cell Excel keeps a data bar, on every side — 2px. */
1916
+ var DATA_BAR_INSET_PT = 1.5;
1914
1917
  var CF_ICON_GUTTER_PT = 12;
1915
1918
  var CF_ICON_EMPTY_HEX = "BFBFBF";
1919
+ var NOTE_FLAG_PT = 4.5;
1920
+ var NOTE_FLAG_HEX = "FF0000";
1921
+ var NOTE_THREAD_HEX = "7030A0";
1916
1922
  var DROPDOWN_BUTTON_PT = 11;
1917
1923
  var DROPDOWN_FILL_HEX = "F0F0F0";
1918
1924
  var DROPDOWN_BORDER_HEX = "B0B0B0";
@@ -2138,8 +2144,10 @@ function collectImageResources(body, headersFooters, options) {
2138
2144
  if (el.image.resource) seen.add(el.image.resource);
2139
2145
  } else if (el.kind === "paragraph") {
2140
2146
  for (const run of el.paragraph.runs) if (run.inlineImage?.resource) seen.add(run.inlineImage.resource);
2141
- } else if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
2142
- else if (el.kind === "shape") {
2147
+ } else if (el.kind === "table") {
2148
+ for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
2149
+ visit(el.table.overlay?.drawings ?? []);
2150
+ } else if (el.kind === "shape") {
2143
2151
  const shapeImages = (sh) => {
2144
2152
  if (sh.fill.imageResource) seen.add(sh.fill.imageResource);
2145
2153
  if (sh.text) visit(sh.text.content);
@@ -2467,28 +2475,47 @@ function emitCellFloat(cf, ctx, frame) {
2467
2475
  if (!float) return;
2468
2476
  const x = floatXIn(frame, float, cf.block.widthPt);
2469
2477
  const topYUp = floatTopYUpIn(frame, float, cf.block.heightPt);
2470
- const sink = ctx.sink(float.behind === true, float.zOrder);
2471
- if (cf.block.kind === "shape") emitShapeItems(cf.block, x, topYUp - cf.block.heightPt, sink, ctx.pageHeight, void 0);
2472
- else {
2473
- const ins = cf.block.drawInset;
2474
- sink.push({
2475
- type: "image",
2476
- x: pt(x + (ins?.dxPt ?? 0)),
2477
- y: pt(ctx.pageHeight - (topYUp - (ins?.dyTopPt ?? 0))),
2478
- width: pt(ins?.widthPt ?? cf.block.widthPt),
2479
- height: pt(ins?.heightPt ?? cf.block.heightPt),
2480
- imageResourceName: cf.block.resourceName,
2481
- ...cf.block.crop ? { crop: cf.block.crop } : {},
2482
- ...cf.block.wash ? { wash: cf.block.wash } : {},
2483
- ...cf.block.alpha !== void 0 ? { alpha: cf.block.alpha } : {},
2484
- ...cf.block.rotationDeg ? { rotationDeg: cf.block.rotationDeg } : {},
2485
- ...cf.block.flipH ? { flipH: true } : {},
2486
- ...cf.block.flipV ? { flipV: true } : {}
2487
- });
2488
- if (cf.block.outline) sink.push(...pictureOutlineItems(cf.block.outline, x + (ins?.dxPt ?? 0), ctx.pageHeight - (topYUp - (ins?.dyTopPt ?? 0)), ins?.widthPt ?? cf.block.widthPt, ins?.heightPt ?? cf.block.heightPt, void 0));
2489
- }
2478
+ emitDrawing(cf.block, x, topYUp, ctx.sink(float.behind === true, float.zOrder), ctx.pageHeight, void 0);
2490
2479
  if (float.wrap !== "none") ctx.exclude(float, x, topYUp, cf.block.widthPt, cf.block.heightPt);
2491
2480
  }
2481
+ /**
2482
+ * A drawing out of the flow — a picture, a shape or a chart — in the items it
2483
+ * paints, its top-left corner at `(x, topYUp)` in the y-up cursor frame.
2484
+ *
2485
+ * @param block The drawing, laid out.
2486
+ * @param x Its left edge on the page.
2487
+ * @param topYUp Its top, y-up.
2488
+ * @param sink Where its items go.
2489
+ * @param pageHeight The page's height, for the flip into the page frame.
2490
+ * @param figId The tagged-PDF Figure its items belong to, if any.
2491
+ */
2492
+ function emitDrawing(block, x, topYUp, sink, pageHeight, figId) {
2493
+ if (block.kind === "shape") {
2494
+ emitShapeItems(block, x, topYUp - block.heightPt, sink, pageHeight, figId);
2495
+ return;
2496
+ }
2497
+ if (block.kind === "chart") {
2498
+ sink.push(...chartPageItems(block, x, topYUp - block.heightPt, pageHeight, figId));
2499
+ return;
2500
+ }
2501
+ const ins = block.drawInset;
2502
+ sink.push({
2503
+ type: "image",
2504
+ x: pt(x + (ins?.dxPt ?? 0)),
2505
+ y: pt(pageHeight - (topYUp - (ins?.dyTopPt ?? 0))),
2506
+ width: pt(ins?.widthPt ?? block.widthPt),
2507
+ height: pt(ins?.heightPt ?? block.heightPt),
2508
+ imageResourceName: block.resourceName,
2509
+ ...block.crop ? { crop: block.crop } : {},
2510
+ ...block.wash ? { wash: block.wash } : {},
2511
+ ...block.alpha !== void 0 ? { alpha: block.alpha } : {},
2512
+ ...block.rotationDeg ? { rotationDeg: block.rotationDeg } : {},
2513
+ ...block.flipH ? { flipH: true } : {},
2514
+ ...block.flipV ? { flipV: true } : {},
2515
+ ...figId !== void 0 ? { structId: figId } : {}
2516
+ });
2517
+ if (block.outline) sink.push(...pictureOutlineItems(block.outline, x + (ins?.dxPt ?? 0), pageHeight - (topYUp - (ins?.dyTopPt ?? 0)), ins?.widthPt ?? block.widthPt, ins?.heightPt ?? block.heightPt, figId));
2518
+ }
2492
2519
  function emitShapeItems(sh, x, bottomYUp, sink, pageHeight, figId) {
2493
2520
  const castsUnderPicture = sh.fillImageResourceName !== void 0 ? sh.shadow : void 0;
2494
2521
  const shadowedFill = castsUnderPicture !== void 0;
@@ -3107,17 +3134,19 @@ function collectFontResources(body, headersFooters, options, imageResources) {
3107
3134
  }
3108
3135
  addRun(run, el.paragraph);
3109
3136
  }
3110
- } else if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) {
3111
- if (cell.properties.hashOnOverflow === true) for (const inner of cell.content) {
3112
- if (inner.kind !== "paragraph") continue;
3113
- for (const run of inner.paragraph.runs) addRun({
3114
- text: "#",
3115
- properties: run.properties
3116
- }, inner.paragraph);
3137
+ } else if (el.kind === "table") {
3138
+ for (const row of el.table.rows) for (const cell of row.cells) {
3139
+ if (cell.properties.hashOnOverflow === true) for (const inner of cell.content) {
3140
+ if (inner.kind !== "paragraph") continue;
3141
+ for (const run of inner.paragraph.runs) addRun({
3142
+ text: "#",
3143
+ properties: run.properties
3144
+ }, inner.paragraph);
3145
+ }
3146
+ visit(cell.content);
3117
3147
  }
3118
- visit(cell.content);
3119
- }
3120
- else if (el.kind === "shape") {
3148
+ visit(el.table.overlay?.drawings ?? []);
3149
+ } else if (el.kind === "shape") {
3121
3150
  const shapeText = (sh) => {
3122
3151
  if (sh.text) visit(sh.text.content);
3123
3152
  for (const child of sh.children ?? []) shapeText(child.shape);
@@ -4417,16 +4446,18 @@ function layoutTableBlock(table, options, fontResources, imageResources, content
4417
4446
  }
4418
4447
  aboveBordersByCol = nextAbove;
4419
4448
  const rp = table.rows[r].properties;
4420
- rows.push(rp.isHeader || rp.pageBreakBefore ? {
4449
+ rows.push(rp.isHeader || rp.pageBreakBefore || rp.sheetTopPt !== void 0 ? {
4421
4450
  ...rl,
4422
4451
  ...rp.isHeader ? { isHeader: true } : {},
4423
- ...rp.pageBreakBefore ? { breakBefore: true } : {}
4452
+ ...rp.pageBreakBefore ? { breakBefore: true } : {},
4453
+ ...rp.sheetTopPt !== void 0 ? { sheetTopPt: rp.sheetTopPt } : {}
4424
4454
  } : rl);
4425
4455
  }
4426
4456
  growRowsForMerges(rows);
4427
4457
  const heightPt = rows.reduce((s, r) => s + r.heightPt, 0);
4428
4458
  const totalWidthPt = columnWidthsPt.reduce((s, w) => s + w, 0);
4429
4459
  const xOffsetPt = (table.properties.indentPt ?? 0) + tableXOffset(table.properties.alignment, contentWidth, totalWidthPt) - legacyTableOutdent(table, rows, options);
4460
+ const overlay = table.overlay ? layoutOverlay(table.overlay, options, fontResources, imageResources, contentWidth) : void 0;
4430
4461
  return {
4431
4462
  kind: "table",
4432
4463
  ...table.properties.float ? { float: table.properties.float } : {},
@@ -4434,7 +4465,40 @@ function layoutTableBlock(table, options, fontResources, imageResources, content
4434
4465
  heightPt,
4435
4466
  totalWidthPt,
4436
4467
  colCount,
4437
- xOffsetPt
4468
+ xOffsetPt,
4469
+ ...overlay ? { overlay } : {}
4470
+ };
4471
+ }
4472
+ /**
4473
+ * The drawings over a spreadsheet grid (TableOverlay), each laid out as the
4474
+ * float it is and placed by its corner in the sheet's own frame.
4475
+ *
4476
+ * @param overlay The drawings and the part of the sheet the table prints.
4477
+ * @param options The render options.
4478
+ * @param fontResources The fonts the drawings' text is set in.
4479
+ * @param imageResources The pictures they show.
4480
+ * @param contentWidth The width between the margins.
4481
+ * @returns The overlay laid out, or undefined when none of it draws.
4482
+ */
4483
+ function layoutOverlay(overlay, options, fontResources, imageResources, contentWidth) {
4484
+ const drawings = [];
4485
+ for (const el of overlay.drawings) {
4486
+ if (el.kind !== "image" && el.kind !== "shape" && el.kind !== "chart") continue;
4487
+ const block = layoutBodyElement(el, options, fontResources, imageResources, contentWidth);
4488
+ if (block.kind !== "image" && block.kind !== "shape" && block.kind !== "chart") continue;
4489
+ drawings.push({
4490
+ block,
4491
+ xPt: block.float?.posH?.offsetPt ?? 0,
4492
+ yPt: block.float?.posV?.offsetPt ?? 0
4493
+ });
4494
+ }
4495
+ if (drawings.length === 0) return void 0;
4496
+ return {
4497
+ sheetLeftPt: overlay.sheetLeftPt,
4498
+ widthPt: overlay.widthPt,
4499
+ leftPt: overlay.leftPt,
4500
+ mirrored: overlay.mirrored === true,
4501
+ drawings
4438
4502
  };
4439
4503
  }
4440
4504
  /**
@@ -4684,15 +4748,19 @@ function layoutTableRow(row, rowIdx, rowCount, colCount, tableProps, columnWidth
4684
4748
  let widthPt = 0;
4685
4749
  for (let k = 0; k < span && colIdx + k < columnWidthsPt.length; k++) widthPt += columnWidthsPt[colIdx + k];
4686
4750
  const paintSpan = Math.min(span, Math.max(1, cell.properties.paintColumns ?? span));
4751
+ const paintFrom = cell.properties.paintAtEnd === true ? span - paintSpan : 0;
4687
4752
  let paintWidthPt = 0;
4688
- for (let k = 0; k < paintSpan && colIdx + k < columnWidthsPt.length; k++) paintWidthPt += columnWidthsPt[colIdx + k];
4753
+ let paintOffsetPt = 0;
4754
+ for (let k = 0; k < paintFrom + paintSpan && colIdx + k < columnWidthsPt.length; k++) if (k < paintFrom) paintOffsetPt += columnWidthsPt[colIdx + k];
4755
+ else paintWidthPt += columnWidthsPt[colIdx + k];
4689
4756
  columnXOffsets.push(cursorX);
4690
4757
  const mergeRole = rowMergeRoles[cellIdx] ?? "standalone";
4691
4758
  const leftNeighbor = cellIdx > 0 ? row.cells[cellIdx - 1].properties.borders : void 0;
4692
4759
  const cellLayout = layoutTableCell(cell, tableProps, widthPt, options, fontResources, imageResources, rowIdx, colIdx, span, rowCount, colCount, mergeRole, leftNeighbor, aboveOfSpan(aboveBordersByCol, colIdx, span));
4693
4760
  cells.push(paintWidthPt < widthPt ? {
4694
4761
  ...cellLayout,
4695
- paintWidthPt
4762
+ paintWidthPt,
4763
+ ...paintOffsetPt > 0 ? { paintOffsetPt } : {}
4696
4764
  } : cellLayout);
4697
4765
  cursorX += widthPt;
4698
4766
  colIdx += span;
@@ -4784,14 +4852,16 @@ function clipCellToHeight(cell, heightPt) {
4784
4852
  let used = 0;
4785
4853
  for (const line of cell.lines) {
4786
4854
  const h = computeLineHeight(line, line.resolved);
4787
- if (kept.length > 0 && used + h > room) break;
4855
+ const below = line.metricDescentPt ?? line.maxFontSizePt * .2;
4856
+ if (kept.length > 0 && used + h - below > room) break;
4788
4857
  kept.push(line);
4789
4858
  used += h;
4790
4859
  }
4791
4860
  return {
4792
4861
  ...cell,
4793
4862
  lines: kept,
4794
- contentHeightPt: used
4863
+ contentHeightPt: used,
4864
+ ...used > room ? { clipped: true } : {}
4795
4865
  };
4796
4866
  }
4797
4867
  /**
@@ -4900,6 +4970,13 @@ function layoutTableCell(cell, tableProps, widthPt, options, fontResources, imag
4900
4970
  }
4901
4971
  const colEnd = colStart + colSpan - 1;
4902
4972
  const borders = resolveCellBorders(cell.properties.borders, tableProps.borders, rowIdx, colStart, colEnd, rowCount, colCount, mergeRole, leftNeighborBorders, aboveNeighborBorders);
4973
+ const grid = tableProps.gridlines;
4974
+ const ruled = grid && !cell.properties.shading?.colorHex ? {
4975
+ ...borders.top || mergeRole === "middle" || mergeRole === "end" ? {} : { top: grid },
4976
+ ...borders.left ? {} : { left: grid },
4977
+ ...borders.bottom || rowIdx !== rowCount - 1 || mergeRole === "start" || mergeRole === "middle" ? {} : { bottom: grid },
4978
+ ...borders.right || colEnd !== colCount - 1 ? {} : { right: grid }
4979
+ } : void 0;
4903
4980
  const alongPt = turned ? lines.reduce((a, l) => Math.max(a, l.contentWidthPt), 0) : 0;
4904
4981
  const heightPt = turned ? alongPt : contentHeightPt;
4905
4982
  const totalHeightPt = mergeRole === "middle" || mergeRole === "end" ? 0 : padTopPt + heightPt + padBottomPt;
@@ -4909,11 +4986,15 @@ function layoutTableCell(cell, tableProps, widthPt, options, fontResources, imag
4909
4986
  padRightPt,
4910
4987
  padBottomPt,
4911
4988
  padLeftPt,
4912
- borders,
4989
+ borders: ruled ? {
4990
+ ...ruled,
4991
+ ...borders
4992
+ } : borders,
4913
4993
  ...cell.properties.shading?.colorHex ? { shadingColorHex: cell.properties.shading.colorHex } : {},
4914
4994
  ...cell.properties.dataBar ? { dataBar: cell.properties.dataBar } : {},
4915
4995
  ...cell.properties.icon ? { icon: cell.properties.icon } : {},
4916
4996
  ...cell.properties.sparkline ? { sparkline: cell.properties.sparkline } : {},
4997
+ ...cell.properties.noteFlag ? { noteFlag: cell.properties.noteFlag } : {},
4917
4998
  lines,
4918
4999
  ...lineGaps.size > 0 ? { lineGaps } : {},
4919
5000
  ...nestedTables.length > 0 ? { nestedTables } : {},
@@ -5298,6 +5379,17 @@ var PageAssembler = class PageAssembler {
5298
5379
  floatsBehind = [];
5299
5380
  floatsFront = [];
5300
5381
  /**
5382
+ * The front floats as the page paints them: over its text, when the
5383
+ * document's floats cover what is under them (StyledRenderOptions
5384
+ * floatsOverText); in the ordinary passes, under it, otherwise.
5385
+ */
5386
+ inFront(items) {
5387
+ return this.ctx.options?.floatsOverText === true ? items.map((item) => ({
5388
+ ...item,
5389
+ over: true
5390
+ })) : items;
5391
+ }
5392
+ /**
5301
5393
  * Side-wrapping floats (wrapSquare/tight/through): rectangles the body text
5302
5394
  * must flow around. Page-scoped, like the float graphics.
5303
5395
  */
@@ -5575,7 +5667,7 @@ var PageAssembler = class PageAssembler {
5575
5667
  behind: true
5576
5668
  }))),
5577
5669
  ...onSheet(this.current),
5578
- ...onSheet(PageAssembler.byZ(this.floatsFront)),
5670
+ ...onSheet(this.inFront(PageAssembler.byZ(this.floatsFront))),
5579
5671
  ...onSheet(this.renderNotesBand()),
5580
5672
  ...footer.commands
5581
5673
  ],
@@ -5791,6 +5883,105 @@ var PageAssembler = class PageAssembler {
5791
5883
  };
5792
5884
  };
5793
5885
  /**
5886
+ * §20.5 — one spreadsheet table's drawings as its pages show them
5887
+ * (TableBlock.overlay). The table lays its rows down page by page, and each
5888
+ * stretch of sheet rows a page takes unbroken is a window onto the sheet:
5889
+ * every drawing over that stretch is drawn where it stands relative to those
5890
+ * rows and cut off at the window's edges. A chart across a page break so
5891
+ * prints in two pieces, one on each page, and a drawing in the title rows on
5892
+ * every page that repeats them — which is what Excel prints.
5893
+ */
5894
+ var SheetView = class {
5895
+ runs = [];
5896
+ figures = /* @__PURE__ */ new Map();
5897
+ /**
5898
+ * @param overlay The table's drawings and the part of the sheet it prints.
5899
+ * @param tableX The table's left edge on the page.
5900
+ * @param builder The tagged-PDF structure, when the document is tagged.
5901
+ */
5902
+ constructor(overlay, tableX, builder) {
5903
+ this.overlay = overlay;
5904
+ this.tableX = tableX;
5905
+ this.builder = builder;
5906
+ }
5907
+ /**
5908
+ * A row, or a piece of one, laid down on the page in hand.
5909
+ *
5910
+ * @param sheetTop Where its top stands on the sheet; undefined for a row the sheet does not have.
5911
+ * @param topYUp Where its top stands on the page, y-up.
5912
+ * @param heightPt How tall it was laid out.
5913
+ */
5914
+ lay(sheetTop, topYUp, heightPt) {
5915
+ if (sheetTop === void 0) return;
5916
+ const last = this.runs[this.runs.length - 1];
5917
+ if (last && Math.abs(last.bottomYUp - topYUp) < .01 && Math.abs(last.sheetBottom - sheetTop) < .5) {
5918
+ last.sheetBottom = sheetTop + heightPt;
5919
+ last.bottomYUp = topYUp - heightPt;
5920
+ return;
5921
+ }
5922
+ this.runs.push({
5923
+ sheetTop,
5924
+ sheetBottom: sheetTop + heightPt,
5925
+ topYUp,
5926
+ bottomYUp: topYUp - heightPt
5927
+ });
5928
+ }
5929
+ /**
5930
+ * Draw what the page's rows show of the drawings, and start over for the
5931
+ * next page.
5932
+ *
5933
+ * @param asm The page assembler, still on the page the rows were laid on.
5934
+ */
5935
+ show(asm) {
5936
+ const o = this.overlay;
5937
+ const pageHeight = asm.ctx.pageHeight;
5938
+ for (const run of this.runs) {
5939
+ const window = {
5940
+ x: pt(this.tableX + o.leftPt),
5941
+ y: pt(pageHeight - run.topYUp),
5942
+ width: pt(o.widthPt),
5943
+ height: pt(run.topYUp - run.bottomYUp)
5944
+ };
5945
+ o.drawings.forEach((d, i) => {
5946
+ const { block } = d;
5947
+ if (d.yPt > run.sheetBottom || d.yPt + block.heightPt < run.sheetTop) return;
5948
+ const left = d.xPt - o.sheetLeftPt;
5949
+ if (left > o.widthPt || left + block.widthPt < 0) return;
5950
+ const x = this.tableX + o.leftPt + (o.mirrored ? o.widthPt - left - block.widthPt : left);
5951
+ const items = [];
5952
+ emitDrawing(block, x, run.topYUp - (d.yPt - run.sheetTop), items, pageHeight, void 0);
5953
+ const shown = items.filter((item) => {
5954
+ const box = pageItemBounds(item);
5955
+ return box.x1 >= window.x && box.x0 <= window.x + window.width && box.y1 >= window.y && box.y0 <= window.y + window.height;
5956
+ });
5957
+ if (shown.length === 0) return;
5958
+ const figId = this.figure(i, block);
5959
+ seenThrough(PageAssembler.zSink(asm.floatsFront, block.float?.zOrder), window).push(...figId === void 0 ? shown : shown.map((item) => ({
5960
+ ...item,
5961
+ structId: figId
5962
+ })));
5963
+ });
5964
+ }
5965
+ this.runs = [];
5966
+ }
5967
+ /** The tagged-PDF Figure a drawing is, made once however many pages show it. */
5968
+ figure(i, block) {
5969
+ if (!this.builder) return void 0;
5970
+ let id = this.figures.get(i);
5971
+ if (id === void 0) {
5972
+ const role = block.kind === "chart" ? block.figureRole ?? "Chart" : block.kind === "image" ? "Image" : "Shape";
5973
+ id = createFigure(this.builder, block.altText, role);
5974
+ this.figures.set(i, id);
5975
+ }
5976
+ return id;
5977
+ }
5978
+ };
5979
+ /** A sink that marks each item it takes as seen through `window` (PageItemBase.window). */
5980
+ var seenThrough = (target, window) => ({ push: (...items) => target.push(...items.map((item) => ({
5981
+ ...item,
5982
+ window
5983
+ }))) });
5984
+ /**
5794
5985
  * §17.3.1.14/15 — the height a paragraph needs in one column: all of its lines
5795
5986
  * where it keeps them together, and where it keeps with the next, the whole of
5796
5987
  * every paragraph in the chain it heads and as much of the block after as has
@@ -6143,6 +6334,7 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
6143
6334
  const headerHeightPt = headerRows.reduce((s, r) => s + r.heightPt, 0);
6144
6335
  const tableNode = builder ? builder.create("Table", builder.root) : void 0;
6145
6336
  const tableX = asm.colLeft() + block.xOffsetPt;
6337
+ const sheetView = block.overlay ? new SheetView(block.overlay, tableX, builder) : void 0;
6146
6338
  for (let ri = 0; ri < block.rows.length; ri++) {
6147
6339
  const row = block.rows[ri];
6148
6340
  const isLeadingHeader = ri < headerRows.length;
@@ -6163,20 +6355,25 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
6163
6355
  const room = asm.cursorY - asm.bottomLimit();
6164
6356
  const page = asm.ctx.pageContentHeight;
6165
6357
  const chunks = !row.cantSplit && row.heightPt > room && asm.colHasContent() && room > 0 ? splitRowIntoChunks(row, room, page) : row.heightPt > page ? splitRowIntoChunks(row, page) : [row];
6358
+ let chunkOffsetPt = 0;
6166
6359
  for (let ci = 0; ci < chunks.length; ci++) {
6167
6360
  const chunk = chunks[ci];
6168
- const forcedBreak = ci === 0 && row.breakBefore && !isLeadingHeader && asm.pageHasContent();
6361
+ const forcedBreak = ci === 0 && row.breakBefore && (ri === 0 || !isLeadingHeader) && asm.pageHasContent();
6169
6362
  const overflow = asm.cursorY - chunk.heightPt < asm.bottomLimit() && asm.colHasContent();
6170
6363
  if (forcedBreak || overflow) {
6364
+ sheetView?.show(asm);
6171
6365
  if (forcedBreak) asm.flushPage();
6172
6366
  else asm.advanceColumn();
6173
6367
  if (!isLeadingHeader && headerRows.length > 0 && asm.cursorY - headerHeightPt - chunk.heightPt >= asm.bottomLimit()) for (const hr of headerRows) {
6174
6368
  emitRowChunk(asm.current, hr, tableX, asm.cursorY, asm.ctx.pageHeight, colCount, void 0);
6369
+ sheetView?.lay(hr.sheetTopPt, asm.cursorY, hr.heightPt);
6175
6370
  asm.cursorY -= hr.heightPt;
6176
6371
  }
6177
6372
  }
6178
6373
  const mergedHeights = chunks.length === 1 && row.cells.some((c) => c.mergeRole === "start") ? row.cells.map((c) => c.mergeRole === "start" ? mergedBoxHeightPt(block.rows, ri, c.colStart, asm.cursorY - asm.bottomLimit()) : void 0) : void 0;
6179
6374
  emitRowChunk(asm.current, chunk, tableX, asm.cursorY, asm.ctx.pageHeight, colCount, cellStructIds, mergedHeights, cellAnchorCtx(asm));
6375
+ sheetView?.lay(row.sheetTopPt !== void 0 ? row.sheetTopPt + chunkOffsetPt : void 0, asm.cursorY, chunk.heightPt);
6376
+ chunkOffsetPt += chunk.heightPt;
6180
6377
  asm.cursorY -= chunk.heightPt;
6181
6378
  if (ci === chunks.length - 1 && ri < block.rows.length - 1) {
6182
6379
  const next = block.rows[ri + 1];
@@ -6187,6 +6384,7 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
6187
6384
  }
6188
6385
  }
6189
6386
  }
6387
+ sheetView?.show(asm);
6190
6388
  }
6191
6389
  }
6192
6390
  asm.flushPage();
@@ -6267,7 +6465,7 @@ function emitRowChunk(out, row, marginLeft, cursorY, pageHeight, colCount, cellS
6267
6465
  }
6268
6466
  out.push({
6269
6467
  type: "fill",
6270
- x: pt(marginLeft + (row.columnXOffsets[i] ?? 0)),
6468
+ x: pt(marginLeft + (row.columnXOffsets[i] ?? 0) + (cell.paintOffsetPt ?? 0)),
6271
6469
  y: pt(pageHeight - rowBottom - row.heightPt - FILL_SEAM_PT),
6272
6470
  width: pt(width),
6273
6471
  height: pt(row.heightPt + FILL_SEAM_PT),
@@ -6282,16 +6480,19 @@ function emitRowChunk(out, row, marginLeft, cursorY, pageHeight, colCount, cellS
6282
6480
  const structId = cellStructIds?.[i];
6283
6481
  if (cell.dataBar && cell.mergeRole !== "middle" && cell.mergeRole !== "end") {
6284
6482
  const start = Math.max(0, Math.min(1, cell.dataBar.startFraction ?? 0));
6285
- const barWidth = cell.widthPt * Math.max(0, Math.min(1, cell.dataBar.fraction));
6483
+ const inner = Math.max(0, cell.widthPt - 2 * DATA_BAR_INSET_PT);
6484
+ const barWidth = inner * Math.max(0, Math.min(1, cell.dataBar.fraction));
6286
6485
  if (barWidth > 0) {
6287
- const barX = cellX + cell.widthPt * start;
6288
- const barBottomYUp = rowBottom;
6486
+ const barX = cellX + DATA_BAR_INSET_PT + inner * start;
6487
+ const boxHeightPt = Math.max(row.heightPt, mergedHeights?.[i] ?? row.heightPt);
6488
+ const barHeightPt = Math.max(0, boxHeightPt - 2 * DATA_BAR_INSET_PT);
6489
+ const barBottomYUp = rowBottom + row.heightPt - boxHeightPt + DATA_BAR_INSET_PT;
6289
6490
  out.push({
6290
6491
  type: "shape",
6291
6492
  shape: {
6292
- paths: [rectAtPath(0, 0, barWidth, row.heightPt)],
6493
+ paths: [rectAtPath(0, 0, barWidth, barHeightPt)],
6293
6494
  fillColorHex: cell.dataBar.colorHex,
6294
- fillGradient: {
6495
+ ...cell.dataBar.solid ? {} : { fillGradient: {
6295
6496
  kind: "linear",
6296
6497
  angle: cell.dataBar.negative ? 180 : 0,
6297
6498
  stops: [{
@@ -6301,7 +6502,7 @@ function emitRowChunk(out, row, marginLeft, cursorY, pageHeight, colCount, cellS
6301
6502
  offset: 1,
6302
6503
  colorHex: "FFFFFF"
6303
6504
  }]
6304
- },
6505
+ } },
6305
6506
  transform: flipTransform([
6306
6507
  1,
6307
6508
  0,
@@ -6358,6 +6559,28 @@ function emitRowChunk(out, row, marginLeft, cursorY, pageHeight, colCount, cellS
6358
6559
  });
6359
6560
  }
6360
6561
  }
6562
+ if (cell.noteFlag && cell.mergeRole !== "middle" && cell.mergeRole !== "end") {
6563
+ const left = cell.paintOffsetPt ?? 0;
6564
+ const right = left + (cell.paintWidthPt ?? cell.widthPt);
6565
+ const side = Math.min(NOTE_FLAG_PT, row.heightPt, right - left);
6566
+ const top = row.heightPt;
6567
+ const corner = cell.noteFlag.atLeft ? new PathBuilder().moveTo(left, top).lineTo(left + side, top).lineTo(left, top - side) : new PathBuilder().moveTo(right - side, top).lineTo(right, top).lineTo(right, top - side);
6568
+ out.push({
6569
+ type: "shape",
6570
+ shape: {
6571
+ paths: [corner.close().build()],
6572
+ fillColorHex: cell.noteFlag.kind === "thread" ? NOTE_THREAD_HEX : NOTE_FLAG_HEX,
6573
+ transform: flipTransform([
6574
+ 1,
6575
+ 0,
6576
+ 0,
6577
+ 1,
6578
+ cellX,
6579
+ rowBottom
6580
+ ], pageHeight)
6581
+ }
6582
+ });
6583
+ }
6361
6584
  if (cell.sparkline && cell.sparkline.values.length > 0 && cell.mergeRole !== "middle" && cell.mergeRole !== "end") {
6362
6585
  const inset = 1.5;
6363
6586
  const sw = cell.widthPt - 2 * inset;
@@ -6609,7 +6832,7 @@ function emitCellBorders(out, cell, cellX, cellY, rowHeight, pageHeight, rowIdx,
6609
6832
  out.push({
6610
6833
  type: "border",
6611
6834
  side,
6612
- x: pt(cellX),
6835
+ x: pt(side === "left" ? cellX : cellX + (cell.paintOffsetPt ?? 0)),
6613
6836
  y: pt(pageHeight - cellY - rowHeight + lowered),
6614
6837
  width: pt(cell.paintWidthPt ?? cell.widthPt),
6615
6838
  height: pt(rowHeight - lowered - raised),
@@ -13,12 +13,24 @@ var QUARTER_60K = 90 * 6e4;
13
13
  * @returns The same item on the sheet, turned a quarter clockwise.
14
14
  */
15
15
  function turnOntoSheet(item, sheetWidth) {
16
- const box = (x, y, w, h) => ({
16
+ const turned = turnItem(item, sheetWidth);
17
+ const w = item.window;
18
+ return w ? {
19
+ ...turned,
20
+ window: turnBox(w.x, w.y, w.width, w.height, sheetWidth)
21
+ } : turned;
22
+ }
23
+ /** A top-left-frame box of the frame, as it stands on the sheet once turned a quarter clockwise. */
24
+ function turnBox(x, y, w, h, sheetWidth) {
25
+ return {
17
26
  x: pt(sheetWidth - y - h),
18
27
  y: pt(x),
19
28
  width: pt(h),
20
29
  height: pt(w)
21
- });
30
+ };
31
+ }
32
+ function turnItem(item, sheetWidth) {
33
+ const box = (x, y, w, h) => turnBox(x, y, w, h, sheetWidth);
22
34
  switch (item.type) {
23
35
  case "line": {
24
36
  const quarter = withinTurn((item.rotationDeg ?? 0) - 90);
@@ -32,6 +32,7 @@ function writeMarkdown(flow, options = {}) {
32
32
  anchors: referencedAnchors(flow.body),
33
33
  notes: noteNumbers(flow),
34
34
  mediaNames: /* @__PURE__ */ new Map(),
35
+ drawn: /* @__PURE__ */ new Set(),
35
36
  ...flow.numbering ? { numbering: flow.numbering } : {}
36
37
  };
37
38
  if (flow.headersFooters && flow.headersFooters.size > 0) lose(ctx, "dropped", FEATURES.headersFooters, "markdown has no pages to band headers onto");
@@ -98,8 +99,14 @@ function emitBlock(out, el, ctx) {
98
99
  return;
99
100
  }
100
101
  ctx.list.length = 0;
101
- if (el.kind === "table") emitTable(out, el.table, ctx);
102
- else if (el.kind === "image") {
102
+ if (el.kind === "table") {
103
+ emitTable(out, el.table, ctx);
104
+ for (const drawing of el.table.overlay?.drawings ?? []) {
105
+ if (ctx.drawn.has(drawing)) continue;
106
+ ctx.drawn.add(drawing);
107
+ emitBlock(out, drawing, ctx);
108
+ }
109
+ } else if (el.kind === "image") {
103
110
  const img = pictureMarkdown(el.image.resource, el.image.altText, ctx);
104
111
  if (img.length > 0) out.push(img);
105
112
  } else if (el.kind === "chart") lose(ctx, "dropped", FEATURES.charts, "markdown cannot draw a chart");
@@ -275,8 +282,10 @@ function noteNumbers(flow) {
275
282
  add(r.endnoteRef, endnotes, flow.endnotes);
276
283
  add(r.commentRef, comments, flow.comments);
277
284
  }
278
- else if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
279
- else if (el.kind === "shape") visitShape(el.shape);
285
+ else if (el.kind === "table") {
286
+ for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
287
+ visit(el.table.overlay?.drawings ?? []);
288
+ } else if (el.kind === "shape") visitShape(el.shape);
280
289
  };
281
290
  visit(flow.body);
282
291
  return {
@@ -370,8 +379,10 @@ function referencedAnchors(body) {
370
379
  const visit = (els) => {
371
380
  for (const el of els) if (el.kind === "paragraph") {
372
381
  for (const r of el.paragraph.runs) if (r.anchor !== void 0) out.add(r.anchor);
373
- } else if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
374
- else if (el.kind === "shape") visitShape(el.shape);
382
+ } else if (el.kind === "table") {
383
+ for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
384
+ visit(el.table.overlay?.drawings ?? []);
385
+ } else if (el.kind === "shape") visitShape(el.shape);
375
386
  };
376
387
  visit(body);
377
388
  return out;