ooxml-core 1.9.0 → 1.10.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 (126) hide show
  1. package/dist/PptxHandler-5UURGJEV.mjs +2 -0
  2. package/dist/{PptxHandler-EX4WBIQQ.mjs → PptxHandler-ADEFY476.mjs} +2 -2
  3. package/dist/PptxHandler-U5XB2HUE.cjs +11 -0
  4. package/dist/automation/index.cjs +744 -192
  5. package/dist/automation/index.mjs +744 -192
  6. package/dist/{chunk-SWF7KVL5.cjs → chunk-3N6XMMTT.cjs} +1365 -1062
  7. package/dist/{chunk-FIELY5PI.mjs → chunk-5NVJN4E6.mjs} +30 -2
  8. package/dist/{chunk-IGOA57LE.mjs → chunk-A6P7G3UK.mjs} +1 -1
  9. package/dist/{chunk-CPKNMQLX.cjs → chunk-AWIDASUW.cjs} +25 -2
  10. package/dist/{chunk-SVQZXP3F.mjs → chunk-CBNKBVX5.mjs} +25 -2
  11. package/dist/{chunk-OXHMZS7B.mjs → chunk-CCHYFCPD.mjs} +649 -172
  12. package/dist/{chunk-OB277ZGE.mjs → chunk-R6KSBNKX.mjs} +27 -1
  13. package/dist/{chunk-AT66SA5M.cjs → chunk-S6DOCMQC.cjs} +28 -0
  14. package/dist/{chunk-WSO4T2LJ.mjs → chunk-WUE5ZHU3.mjs} +26 -3
  15. package/dist/{chunk-LCS3J3QI.mjs → chunk-ZE53K7DH.mjs} +474 -171
  16. package/dist/{chunk-M76PHYRR.mjs → chunk-ZOUGMAZG.mjs} +1 -1
  17. package/dist/docx/layout/adapt-table.d.ts +2 -2
  18. package/dist/docx/layout/adapt-table.d.ts.map +1 -1
  19. package/dist/docx/layout/flow-table.d.ts.map +1 -1
  20. package/dist/docx/layout/index.cjs +477 -75
  21. package/dist/docx/layout/index.mjs +477 -76
  22. package/dist/docx/layout/nested-frame-profile.d.ts +9 -0
  23. package/dist/docx/layout/nested-frame-profile.d.ts.map +1 -0
  24. package/dist/docx/layout/page-cursor.d.ts +2 -0
  25. package/dist/docx/layout/page-cursor.d.ts.map +1 -1
  26. package/dist/docx/layout/result.d.ts +13 -0
  27. package/dist/docx/layout/result.d.ts.map +1 -1
  28. package/dist/docx/layout/table-border-layout.d.ts +16 -0
  29. package/dist/docx/layout/table-border-layout.d.ts.map +1 -0
  30. package/dist/docx/layout/table-cell-layout.d.ts +2 -1
  31. package/dist/docx/layout/table-cell-layout.d.ts.map +1 -1
  32. package/dist/docx/layout/table-content.d.ts +4 -0
  33. package/dist/docx/layout/table-content.d.ts.map +1 -0
  34. package/dist/docx/layout/table-footnotes.d.ts +5 -0
  35. package/dist/docx/layout/table-footnotes.d.ts.map +1 -1
  36. package/dist/docx/layout/table-frames.d.ts +4 -0
  37. package/dist/docx/layout/table-frames.d.ts.map +1 -0
  38. package/dist/docx/layout/table-layout.d.ts +8 -3
  39. package/dist/docx/layout/table-layout.d.ts.map +1 -1
  40. package/dist/docx/layout/table-note-completion.d.ts +5 -0
  41. package/dist/docx/layout/table-note-completion.d.ts.map +1 -0
  42. package/dist/docx/layout/table-note-stories.d.ts +6 -0
  43. package/dist/docx/layout/table-note-stories.d.ts.map +1 -0
  44. package/dist/pptx/automation/index.cjs +535 -182
  45. package/dist/pptx/automation/index.d.ts +4 -4
  46. package/dist/pptx/automation/index.mjs +5 -5
  47. package/dist/pptx/chunks/{index-BZh-IMkN.d.ts → index-Cuxtr76Y.d.ts} +17 -3
  48. package/dist/pptx/chunks/index-Cuxtr76Y.d.ts.map +1 -0
  49. package/dist/pptx/chunks/{index-B4cnSPNz.d.ts → index-DUCGeZh7.d.ts} +65 -5
  50. package/dist/pptx/chunks/index-DUCGeZh7.d.ts.map +1 -0
  51. package/dist/pptx/chunks/{smart-art-Q2kZQtHh.d.ts → smart-art-D-sXa_Nb.d.ts} +11 -1
  52. package/dist/pptx/chunks/smart-art-D-sXa_Nb.d.ts.map +1 -0
  53. package/dist/pptx/chunks/{text-operations-BLodkuXE.d.ts → text-operations-BqHmLaLA.d.ts} +26 -2
  54. package/dist/pptx/chunks/text-operations-BqHmLaLA.d.ts.map +1 -0
  55. package/dist/pptx/cli/index.cjs +535 -182
  56. package/dist/pptx/cli/index.d.ts +1 -1
  57. package/dist/pptx/cli/index.mjs +4 -4
  58. package/dist/pptx/converter/index.cjs +1 -0
  59. package/dist/pptx/converter/index.d.ts +1 -1
  60. package/dist/pptx/converter/index.mjs +2 -2
  61. package/dist/pptx/editor/render/collaboration-live-patch-target.cjs +2 -2
  62. package/dist/pptx/editor/render/collaboration-live-patch-target.mjs +1 -1
  63. package/dist/pptx/editor/render/find-replace.cjs +18 -18
  64. package/dist/pptx/editor/render/find-replace.d.ts +2 -2
  65. package/dist/pptx/editor/render/find-replace.mjs +2 -2
  66. package/dist/pptx/editor/render/remap-text.cjs +2 -2
  67. package/dist/pptx/editor/render/remap-text.d.ts.map +1 -1
  68. package/dist/pptx/editor/render/remap-text.mjs +1 -1
  69. package/dist/pptx/editor/render/slide-operations.cjs +8 -8
  70. package/dist/pptx/editor/render/slide-operations.mjs +2 -2
  71. package/dist/pptx/index.cjs +762 -182
  72. package/dist/pptx/index.d.ts +5 -5
  73. package/dist/pptx/index.mjs +5 -5
  74. package/dist/pptx/smartart-layouts/index.d.ts +1 -1
  75. package/dist/teams/index.cjs +486 -114
  76. package/dist/teams/index.mjs +486 -114
  77. package/dist/visio/date-cache.d.ts +3 -0
  78. package/dist/visio/date-cache.d.ts.map +1 -0
  79. package/dist/visio/edit-text-field.d.ts.map +1 -1
  80. package/dist/visio/index.cjs +131 -22
  81. package/dist/visio/index.mjs +131 -22
  82. package/dist/visio/shape-metadata.d.ts.map +1 -1
  83. package/dist/visio/text-field-display.d.ts.map +1 -1
  84. package/dist/visio/text-field-fraction.d.ts +3 -0
  85. package/dist/visio/text-field-fraction.d.ts.map +1 -0
  86. package/dist/visio/text-field-numeric.d.ts +1 -1
  87. package/dist/visio/text-field-numeric.d.ts.map +1 -1
  88. package/dist/visio/text-field-system-date.d.ts +4 -0
  89. package/dist/visio/text-field-system-date.d.ts.map +1 -0
  90. package/dist/visio/text-fields.d.ts +1 -0
  91. package/dist/visio/text-fields.d.ts.map +1 -1
  92. package/dist/visio/ui/index.cjs +132 -22
  93. package/dist/visio/ui/index.mjs +132 -22
  94. package/dist/visio/ui/text-field-catalog.d.ts.map +1 -1
  95. package/dist/xlsx/edit/pivots.d.ts.map +1 -1
  96. package/dist/xlsx/index.cjs +614 -170
  97. package/dist/xlsx/index.mjs +614 -170
  98. package/dist/xlsx/load/index.cjs +575 -163
  99. package/dist/xlsx/load/index.mjs +575 -163
  100. package/dist/xlsx/model-pivots.d.ts +23 -2
  101. package/dist/xlsx/model-pivots.d.ts.map +1 -1
  102. package/dist/xlsx/pivot/admission.d.ts.map +1 -1
  103. package/dist/xlsx/pivot/aggregate.d.ts +3 -1
  104. package/dist/xlsx/pivot/aggregate.d.ts.map +1 -1
  105. package/dist/xlsx/pivot/data.d.ts +1 -1
  106. package/dist/xlsx/pivot/data.d.ts.map +1 -1
  107. package/dist/xlsx/pivot/hierarchy-first.d.ts +21 -0
  108. package/dist/xlsx/pivot/hierarchy-first.d.ts.map +1 -0
  109. package/dist/xlsx/pivot/hierarchy.d.ts +2 -4
  110. package/dist/xlsx/pivot/hierarchy.d.ts.map +1 -1
  111. package/dist/xlsx/pivot/measures.d.ts +13 -0
  112. package/dist/xlsx/pivot/measures.d.ts.map +1 -0
  113. package/dist/xlsx/pivot/read.d.ts.map +1 -1
  114. package/dist/xlsx/pivot/table-xml.d.ts.map +1 -1
  115. package/dist/xlsx/pivot/values-axis.d.ts +9 -0
  116. package/dist/xlsx/pivot/values-axis.d.ts.map +1 -0
  117. package/dist/xlsx/ui/commands/i18n/pivots.d.ts.map +1 -1
  118. package/dist/xlsx/ui/index.cjs +547 -127
  119. package/dist/xlsx/ui/index.mjs +547 -127
  120. package/package.json +1 -1
  121. package/dist/PptxHandler-AT7FVG5Q.mjs +0 -2
  122. package/dist/PptxHandler-VFSCECPL.cjs +0 -11
  123. package/dist/pptx/chunks/index-B4cnSPNz.d.ts.map +0 -1
  124. package/dist/pptx/chunks/index-BZh-IMkN.d.ts.map +0 -1
  125. package/dist/pptx/chunks/smart-art-Q2kZQtHh.d.ts.map +0 -1
  126. package/dist/pptx/chunks/text-operations-BLodkuXE.d.ts.map +0 -1
@@ -4425,6 +4425,12 @@ function appendBreakMarker(tokens, runIndex, sourceStart, kind) {
4425
4425
  tokens.push({ kind: kind === "page" ? "pageBreak" : "columnBreak", runIndex, sourceStart });
4426
4426
  }
4427
4427
 
4428
+ // docx/layout/result.ts
4429
+ function cellVerticalOffset(geometry, rowHeightPx) {
4430
+ const free = Math.max(0, (geometry.alignmentHeightPx ?? rowHeightPx) - geometry.contentHeightPx);
4431
+ return geometry.verticalAlign === "center" ? free / 2 : geometry.verticalAlign === "bottom" ? free : 0;
4432
+ }
4433
+
4428
4434
  // docx/layout/page-fields.ts
4429
4435
  function fieldDisplayText(run, values) {
4430
4436
  if (!run.field || isFieldLocked(run)) return run.text;
@@ -4889,6 +4895,79 @@ function widowControlEnabled(value) {
4889
4895
  return value !== false;
4890
4896
  }
4891
4897
 
4898
+ // docx/layout/table-content.ts
4899
+ function cellContentBottom(boxes, paddingBottom = 0) {
4900
+ return Math.max(
4901
+ 0,
4902
+ ...boxes.map(
4903
+ (box) => box.yPx + box.heightPx + (box.tableFrameInsets?.bottom ?? 0) + paddingBottom
4904
+ )
4905
+ );
4906
+ }
4907
+
4908
+ // docx/layout/table-frames.ts
4909
+ function clipTableFrames(tables, start, end, inset = 0) {
4910
+ return tables.flatMap((table) => {
4911
+ const top = Math.max(start, table.yPx);
4912
+ const bottom = Math.min(end, table.yPx + table.heightPx);
4913
+ if (bottom <= top) return [];
4914
+ const rows = table.rows.flatMap((row) => {
4915
+ const rowTop = table.yPx + row.yPx;
4916
+ const a = Math.max(top, rowTop), b = Math.min(bottom, rowTop + row.heightPx);
4917
+ if (b <= a) return [];
4918
+ return [
4919
+ {
4920
+ ...row,
4921
+ yPx: a - top,
4922
+ heightPx: b - a,
4923
+ ...row.cellTables ? {
4924
+ cellTables: row.cellTables.map(
4925
+ (nested) => clipTableFrames(nested, a - rowTop, b - rowTop)
4926
+ )
4927
+ } : {}
4928
+ }
4929
+ ];
4930
+ });
4931
+ return [{ ...table, yPx: top - start + inset, heightPx: bottom - top, rows }];
4932
+ });
4933
+ }
4934
+
4935
+ // docx/layout/nested-frame-profile.ts
4936
+ var equal = (a, b) => a !== void 0 && b !== void 0 && a.style === b.style && a.color === b.color && Math.abs(a.widthPx - b.widthPx) < 1e-6;
4937
+ function nestedFrameProfile(table, tableWidthPx = table.widthPx) {
4938
+ const sides = table.rows.flatMap(
4939
+ (row) => row.cells.flatMap(
4940
+ (cell) => ["top", "right", "bottom", "left"].map((side) => cell.borders?.[side])
4941
+ )
4942
+ );
4943
+ if (sides.every((side) => !side)) return { leftPx: 0, rightPx: 0, bottomPx: 0 };
4944
+ if (!sides.every((side) => side?.style === "solid" && side.widthPx > 0)) return void 0;
4945
+ const first3 = table.rows[0], left = first3.cells[0].borders.left, right = first3.cells.at(-1).borders.right;
4946
+ for (const [index, row] of table.rows.entries()) {
4947
+ if (row.cells.length !== first3.cells.length || !equal(row.cells[0]?.borders?.left, left) || !equal(row.cells.at(-1)?.borders?.right, right))
4948
+ return void 0;
4949
+ for (const [column, cell] of row.cells.entries()) {
4950
+ const sourceWidth = cell.widthPx ?? (tableWidthPx === void 0 ? void 0 : tableWidthPx / row.cells.length);
4951
+ const paintWidth = sourceWidth === void 0 ? void 0 : sourceWidth + (cell.borders.left.widthPx - cell.borders.right.widthPx) / 2 + (column === row.cells.length - 1 ? cell.borders.right.widthPx : 0);
4952
+ if (paintWidth !== void 0 && paintWidth <= 0) return void 0;
4953
+ if (!equal(cell.borders?.top, row.cells[0]?.borders?.top) || !equal(cell.borders?.bottom, row.cells[0]?.borders?.bottom))
4954
+ return void 0;
4955
+ if (column && !equal(row.cells[column - 1]?.borders?.right, cell.borders?.left))
4956
+ return void 0;
4957
+ if (index) {
4958
+ const previous = table.rows[index - 1].cells[column];
4959
+ if (previous.xPx !== cell.xPx || previous.widthPx !== cell.widthPx || !equal(previous.borders?.bottom, cell.borders?.top))
4960
+ return void 0;
4961
+ }
4962
+ }
4963
+ }
4964
+ return {
4965
+ leftPx: left.widthPx,
4966
+ rightPx: right.widthPx,
4967
+ bottomPx: table.rows.at(-1).cells[0].borders.bottom.widthPx
4968
+ };
4969
+ }
4970
+
4892
4971
  // docx/layout/table-cell-layout.ts
4893
4972
  function cellParagraphs(cell) {
4894
4973
  const walk = (blocks) => blocks.flatMap(
@@ -4908,6 +4987,7 @@ function stackCellBlocks(cell, width, measurer, note, top, starts) {
4908
4987
  );
4909
4988
  let y = top;
4910
4989
  const boxes = [];
4990
+ const tables = [];
4911
4991
  for (const block of cell.blocks) {
4912
4992
  if (block.kind === "paragraph") {
4913
4993
  if (starts && !starts.has(block.id)) continue;
@@ -4917,27 +4997,72 @@ function stackCellBlocks(cell, width, measurer, note, top, starts) {
4917
4997
  continue;
4918
4998
  }
4919
4999
  const tableWidth = block.widthPx ?? width;
4920
- const x = block.alignment === "center" ? Math.max(0, (width - tableWidth) / 2) : block.alignment === "right" ? Math.max(0, width - tableWidth) : block.indentPx ?? 0;
5000
+ const profile = nestedFrameProfile(block, tableWidth);
5001
+ const reserveBorders = profile !== void 0;
5002
+ if (!reserveBorders)
5003
+ note(
5004
+ "Nested table border occupancy is approximate for missing edges, non-solid or conflicting shared borders and changing row grids."
5005
+ );
5006
+ const externalRight = profile ? (profile.leftPx + profile.rightPx) / 2 : 0;
5007
+ const paintedWidth = tableWidth + externalRight;
5008
+ const x = block.alignment === "center" ? Math.max(0, (width - paintedWidth) / 2) : block.alignment === "right" ? Math.max(0, width - paintedWidth) : block.indentPx ?? 0;
5009
+ const frame = {
5010
+ kind: "table",
5011
+ blockId: block.id,
5012
+ xPx: x,
5013
+ widthPx: paintedWidth,
5014
+ yPx: y,
5015
+ heightPx: 0,
5016
+ rows: []
5017
+ };
4921
5018
  for (const row of block.rows) {
4922
5019
  if (starts && !row.cells.some((nested) => cellParagraphs(nested).some((p) => starts.has(p.id))))
4923
5020
  continue;
4924
- const layout = layoutRow(row, tableWidth, measurer, note, void 0, starts);
5021
+ const layout = layoutRow(
5022
+ row,
5023
+ tableWidth,
5024
+ measurer,
5025
+ note,
5026
+ void 0,
5027
+ starts,
5028
+ reserveBorders,
5029
+ profile?.leftPx
5030
+ );
5031
+ frame.rows.push({
5032
+ yPx: y - frame.yPx,
5033
+ heightPx: layout.heightPx,
5034
+ repeated: false,
5035
+ cells: layout.cells,
5036
+ geometry: layout.geometry,
5037
+ ...layout.cellTables ? { cellTables: layout.cellTables } : {}
5038
+ });
4925
5039
  layout.cells.forEach((paragraphs, index) => {
4926
5040
  const geometry = layout.geometry[index];
4927
5041
  for (const paragraph of paragraphs)
4928
5042
  boxes.push({
4929
5043
  ...paragraph,
4930
- yPx: y + paragraph.yPx,
5044
+ yPx: y + paragraph.yPx + cellVerticalOffset(geometry, layout.heightPx),
4931
5045
  xPx: x + geometry.xPx + geometry.paddingLeftPx + (paragraph.xPx ?? 0),
4932
5046
  widthPx: paragraph.widthPx ?? Math.max(1, geometry.widthPx - geometry.paddingLeftPx - geometry.paddingRightPx),
4933
- readOnly: true
5047
+ readOnly: true,
5048
+ tableFrameInsets: {
5049
+ top: (paragraph.tableFrameInsets?.top ?? 0) + (reserveBorders ? geometry.borders?.top?.widthPx ?? 0 : 0),
5050
+ bottom: (paragraph.tableFrameInsets?.bottom ?? 0) + (reserveBorders ? geometry.borders?.bottom?.widthPx ?? 0 : 0)
5051
+ }
4934
5052
  });
4935
5053
  });
4936
5054
  y += layout.heightPx;
4937
5055
  }
5056
+ const bottomBorder = profile?.bottomPx ?? 0;
5057
+ if (frame.rows.length) {
5058
+ frame.rows.at(-1).heightPx += bottomBorder;
5059
+ y += bottomBorder;
5060
+ frame.heightPx = y - frame.yPx;
5061
+ tables.push(frame);
5062
+ }
4938
5063
  }
4939
5064
  boxes.sort((a, b) => a.yPx - b.yPx);
4940
- return { heightPx: y, boxes };
5065
+ return { heightPx: y, boxes, tables };
4941
5066
  }
4942
5067
 
4943
5068
  // docx/layout/table-layout.ts
@@ -4968,7 +5093,7 @@ function stackParagraphs(paragraphs, widthPx, measurer, note, topPx = 0, startTo
4968
5093
  }
4969
5094
  return { heightPx: y, boxes };
4970
5095
  }
4971
- function layoutRow(row, tableWidthPx, measurer, note, remaining, startTokens) {
5096
+ function layoutRow(row, tableWidthPx, measurer, note, remaining, startTokens, frameBorders = false, frameLeftBorderPx) {
4972
5097
  const count = row.cells.length || 1;
4973
5098
  let x = 0;
4974
5099
  const geometry = [];
@@ -4976,10 +5101,21 @@ function layoutRow(row, tableWidthPx, measurer, note, remaining, startTokens) {
4976
5101
  const continuation = remaining?.[geometry.length];
4977
5102
  const starts = continuation ? new Map(continuation.map((box) => [box.blockId, box.lines[0]?.firstToken ?? 0])) : startTokens;
4978
5103
  const widthPx = cell.widthPx ?? tableWidthPx / count;
4979
- const padding = cell.padding ?? { top: 0, right: 0, bottom: 0, left: 0 };
5104
+ const margins = cell.padding ?? { top: 0, right: 0, bottom: 0, left: 0 };
5105
+ const leftBorder = frameBorders ? cell.borders?.left?.widthPx ?? 0 : 0;
5106
+ const rightBorder = frameBorders ? cell.borders?.right?.widthPx ?? 0 : 0;
5107
+ const padding = frameBorders ? {
5108
+ top: margins.top + (cell.borders?.top?.widthPx ?? 0),
5109
+ bottom: margins.bottom,
5110
+ left: leftBorder / 2 + Math.max(margins.left, leftBorder / 2),
5111
+ right: Math.max(margins.right, rightBorder / 2) - rightBorder / 2
5112
+ } : margins;
5113
+ const externalRight = frameBorders && geometry.length === row.cells.length - 1 ? rightBorder : 0;
5114
+ const paintWidth = widthPx + (frameBorders ? (leftBorder - rightBorder) / 2 + externalRight : 0);
5115
+ const frameX = frameBorders ? ((frameLeftBorderPx ?? leftBorder) - leftBorder) / 2 : 0;
4980
5116
  const content2 = stackCellBlocks(
4981
5117
  cell,
4982
- Math.max(1, widthPx - padding.left - padding.right),
5118
+ Math.max(1, paintWidth - padding.left - padding.right - externalRight),
4983
5119
  measurer,
4984
5120
  note,
4985
5121
  padding.top,
@@ -4987,48 +5123,58 @@ function layoutRow(row, tableWidthPx, measurer, note, remaining, startTokens) {
4987
5123
  );
4988
5124
  const heightPx2 = content2.heightPx + padding.bottom;
4989
5125
  geometry.push({
4990
- xPx: cell.xPx ?? x,
4991
- widthPx,
5126
+ xPx: (cell.xPx ?? x) + frameX,
5127
+ widthPx: paintWidth,
5128
+ ...frameBorders ? { borderInsetsReserved: true } : {},
4992
5129
  paddingLeftPx: padding.left,
4993
- paddingRightPx: padding.right,
5130
+ paddingRightPx: padding.right + externalRight,
4994
5131
  contentHeightPx: heightPx2,
4995
5132
  ...cell.borders ? { borders: cell.borders } : {},
4996
5133
  ...cell.shading ? { shading: cell.shading } : {},
4997
5134
  ...cell.verticalAlign ? { verticalAlign: cell.verticalAlign } : {}
4998
5135
  });
4999
5136
  x = (cell.xPx ?? x) + widthPx;
5000
- return { heightPx: heightPx2, boxes: content2.boxes };
5137
+ return { heightPx: heightPx2, boxes: content2.boxes, tables: content2.tables ?? [] };
5001
5138
  });
5002
5139
  const content = Math.max(0, ...laidOut.map((cell) => cell.heightPx));
5003
5140
  const heightPx = row.heightPx === void 0 ? content : row.heightRule === "exact" ? row.heightPx : Math.max(content, row.heightPx);
5141
+ if (frameBorders) for (const cell of geometry) cell.alignmentHeightPx = heightPx;
5004
5142
  return {
5005
5143
  heightPx,
5006
5144
  cells: laidOut.map((cell) => cell.boxes),
5007
5145
  geometry,
5146
+ ...laidOut.some((cell) => cell.tables.length) ? { cellTables: laidOut.map((cell) => cell.tables) } : {},
5008
5147
  cellPadding: row.cells.map((cell) => ({
5009
5148
  top: cell.padding?.top ?? 0,
5010
5149
  bottom: cell.padding?.bottom ?? 0
5011
5150
  }))
5012
5151
  };
5013
5152
  }
5014
- function splitCellAtHeight(boxes, cutHeightPx) {
5153
+ function splitCellAtHeight(boxes, cutHeightPx, paragraphCuts) {
5015
5154
  const before = [];
5016
5155
  const after = [];
5017
5156
  let beforeHeightPx = 0;
5018
5157
  for (const box of boxes) {
5019
- if (box.yPx + box.heightPx <= cutHeightPx) {
5158
+ const cut = paragraphCuts?.get(box.blockId) ?? cutHeightPx;
5159
+ if (box.yPx + box.heightPx + (box.tableFrameInsets?.bottom ?? 0) <= cut) {
5020
5160
  before.push(box);
5021
- beforeHeightPx = Math.max(beforeHeightPx, box.yPx + box.heightPx);
5161
+ beforeHeightPx = Math.max(
5162
+ beforeHeightPx,
5163
+ box.yPx + box.heightPx + (box.tableFrameInsets?.bottom ?? 0)
5164
+ );
5022
5165
  continue;
5023
5166
  }
5024
5167
  const beforeLines = box.lines.filter(
5025
- (line2) => box.yPx + line2.yPx + line2.heightPx <= cutHeightPx
5168
+ (line2) => box.yPx + line2.yPx + line2.heightPx + (box.tableFrameInsets?.bottom ?? 0) <= cut
5026
5169
  );
5027
5170
  const afterLines = box.lines.slice(beforeLines.length);
5028
5171
  if (beforeLines.length) {
5029
5172
  const bottom = beforeLines.at(-1).yPx + beforeLines.at(-1).heightPx;
5030
5173
  before.push({ ...box, heightPx: bottom, lines: beforeLines });
5031
- beforeHeightPx = Math.max(beforeHeightPx, box.yPx + bottom);
5174
+ beforeHeightPx = Math.max(
5175
+ beforeHeightPx,
5176
+ box.yPx + bottom + (box.tableFrameInsets?.bottom ?? 0)
5177
+ );
5032
5178
  }
5033
5179
  if (afterLines.length) {
5034
5180
  const shift = expectDefined(afterLines[0], "first line after split").yPx;
@@ -5040,43 +5186,81 @@ function splitCellAtHeight(boxes, cutHeightPx) {
5040
5186
  });
5041
5187
  }
5042
5188
  }
5043
- const afterOrigin = Math.min(...after.map((box) => box.yPx));
5189
+ const afterOrigin = Math.min(...after.map((box) => box.yPx - (box.tableFrameInsets?.top ?? 0)));
5044
5190
  for (const box of after) box.yPx -= afterOrigin;
5045
- return { before, after, beforeHeightPx };
5191
+ return { before, after, beforeHeightPx, ...after.length ? { afterOrigin } : {} };
5046
5192
  }
5047
- function splitRowAtHeight(row, cutHeightPx, cellCuts) {
5193
+ function splitRowAtHeight(row, cutHeightPx, cellCuts, paragraphCuts) {
5048
5194
  const splits = row.cells.map((cell, index) => {
5049
5195
  const padding = row.cellPadding?.[index] ?? { top: 0, bottom: 0 };
5050
- const split = splitCellAtHeight(cell, (cellCuts?.[index] ?? cutHeightPx) - padding.bottom);
5196
+ const cuts = paragraphCuts ? new Map(
5197
+ cell.flatMap((box) => {
5198
+ const cut = paragraphCuts.get(box.blockId);
5199
+ return cut === void 0 ? [] : [[box.blockId, cut - padding.bottom]];
5200
+ })
5201
+ ) : void 0;
5202
+ const split = splitCellAtHeight(
5203
+ cell,
5204
+ (cellCuts?.[index] ?? cutHeightPx) - padding.bottom,
5205
+ cuts
5206
+ );
5051
5207
  if (split.before.length) split.beforeHeightPx += padding.bottom;
5052
5208
  if (split.after.length)
5053
5209
  split.after = split.after.map((box) => ({ ...box, yPx: box.yPx + padding.top }));
5054
5210
  return split;
5055
5211
  });
5056
5212
  const anyAfter = splits.some((split) => split.after.length > 0);
5213
+ const beforeHeightPx = Math.max(0, ...splits.map((split) => split.beforeHeightPx));
5057
5214
  const before = {
5058
- heightPx: Math.max(0, ...splits.map((split) => split.beforeHeightPx)),
5215
+ heightPx: beforeHeightPx,
5059
5216
  cells: splits.map((split) => split.before),
5217
+ ...row.cellTables ? {
5218
+ cellTables: row.cellTables.map(
5219
+ (tables, index) => clipTableFrames(
5220
+ tables,
5221
+ 0,
5222
+ splits[index].beforeHeightPx - (row.cellPadding?.[index]?.bottom ?? 0)
5223
+ )
5224
+ )
5225
+ } : {},
5060
5226
  geometry: row.geometry.map((geometry, index) => ({
5061
5227
  ...geometry,
5062
- contentHeightPx: splits[index].beforeHeightPx
5228
+ contentHeightPx: splits[index].beforeHeightPx,
5229
+ ...row.borderBottomPx !== void 0 ? { alignmentHeightPx: beforeHeightPx } : {}
5063
5230
  })),
5064
- ...row.cellPadding ? { cellPadding: row.cellPadding } : {}
5231
+ ...row.cellPadding ? { cellPadding: row.cellPadding } : {},
5232
+ ...row.borderBottomPx !== void 0 ? { borderBottomPx: row.borderBottomPx } : {}
5065
5233
  };
5066
5234
  if (!anyAfter) return { before, after: null };
5235
+ const afterHeightPx = Math.max(
5236
+ 0,
5237
+ ...splits.map(
5238
+ (split, index) => cellContentBottom(split.after, row.cellPadding?.[index]?.bottom ?? 0)
5239
+ )
5240
+ );
5067
5241
  const after = {
5068
- heightPx: Math.max(
5069
- 0,
5070
- ...splits.map(
5071
- (split, index) => split.after.length ? split.after.at(-1).yPx + split.after.at(-1).heightPx + (row.cellPadding?.[index]?.bottom ?? 0) : 0
5072
- )
5073
- ),
5242
+ heightPx: afterHeightPx,
5074
5243
  cells: splits.map((split) => split.after),
5244
+ ...row.cellTables ? {
5245
+ cellTables: row.cellTables.map(
5246
+ (tables, index) => splits[index].after.length ? clipTableFrames(
5247
+ tables,
5248
+ splits[index].afterOrigin ?? 0,
5249
+ row.heightPx,
5250
+ row.cellPadding?.[index]?.top ?? 0
5251
+ ) : []
5252
+ )
5253
+ } : {},
5075
5254
  geometry: row.geometry.map((geometry, index) => ({
5076
5255
  ...geometry,
5077
- contentHeightPx: splits[index].after.length ? splits[index].after.at(-1).yPx + splits[index].after.at(-1).heightPx + (row.cellPadding?.[index]?.bottom ?? 0) : 0
5256
+ contentHeightPx: cellContentBottom(
5257
+ splits[index].after,
5258
+ row.cellPadding?.[index]?.bottom ?? 0
5259
+ ),
5260
+ ...row.borderBottomPx !== void 0 ? { alignmentHeightPx: afterHeightPx } : {}
5078
5261
  })),
5079
- ...row.cellPadding ? { cellPadding: row.cellPadding } : {}
5262
+ ...row.cellPadding ? { cellPadding: row.cellPadding } : {},
5263
+ ...row.borderBottomPx !== void 0 ? { borderBottomPx: row.borderBottomPx } : {}
5080
5264
  };
5081
5265
  return { before, after };
5082
5266
  }
@@ -5137,11 +5321,11 @@ function splitNote(note, budget) {
5137
5321
  // docx/layout/footnote-columns.ts
5138
5322
  function footnoteColumns(page, columns) {
5139
5323
  const width = page.widthPx - page.marginLeftPx - page.marginRightPx;
5140
- const equal = (width - columns.gapPx * (columns.count - 1)) / columns.count;
5324
+ const equal2 = (width - columns.gapPx * (columns.count - 1)) / columns.count;
5141
5325
  let x = 0;
5142
5326
  return Array.from({ length: columns.count }, (_, index) => {
5143
5327
  const own = columns.widths?.[index];
5144
- const column = { xPx: x, widthPx: Math.max(1, own?.widthPx ?? equal) };
5328
+ const column = { xPx: x, widthPx: Math.max(1, own?.widthPx ?? equal2) };
5145
5329
  x += column.widthPx + (own?.gapPx ?? columns.gapPx);
5146
5330
  return column;
5147
5331
  });
@@ -5549,6 +5733,10 @@ var PageCursor = class {
5549
5733
  this.page.heightPx - this.page.marginTopPx - this.geometry.marginBottomPx - this.columnTopPx
5550
5734
  );
5551
5735
  }
5736
+ /** Number of independently balanced note columns, distinct from legacy body-column notes. */
5737
+ get footnoteColumnCount() {
5738
+ return this.legacyFootnoteColumns ? 1 : this.noteColumns?.count ?? 1;
5739
+ }
5552
5740
  remainingHeightPx() {
5553
5741
  return this.columnHeightPx - (this.yPx - this.columnTopPx) - this.footnotes.reserved(this.page, this.noteColumnIndex) - this.pendingHeightPx();
5554
5742
  }
@@ -5793,20 +5981,132 @@ function placeParagraph(cursor, placement, note) {
5793
5981
  }
5794
5982
  }
5795
5983
 
5984
+ // docx/layout/table-note-stories.ts
5985
+ function independentNoteStories(cell) {
5986
+ const first3 = cell.blocks?.[0];
5987
+ if (first3?.kind !== "table" || first3.rows.length !== 1 || cell.verticalAlign && cell.verticalAlign !== "top")
5988
+ return void 0;
5989
+ if (cell.blocks.slice(1).some((block) => block.kind !== "paragraph" || block.footnotes?.length))
5990
+ return void 0;
5991
+ const row = first3.rows[0];
5992
+ if (row.heightPx !== void 0 || row.cantSplit || row.isHeader || !nestedFrameProfile(first3, first3.widthPx))
5993
+ return void 0;
5994
+ if (row.cells.some(
5995
+ (inner) => inner.blocks?.some((block) => block.kind === "table") || cellParagraphs(inner).some(
5996
+ (paragraph) => paragraph.keepLines || paragraph.keepNext || paragraph.pageBreakBefore
5997
+ ) || inner.verticalAlign && inner.verticalAlign !== "top"
5998
+ ))
5999
+ return void 0;
6000
+ if (!measuredNoteReferences(row.cells.flatMap(cellParagraphs))) return void 0;
6001
+ const groups = /* @__PURE__ */ new Map();
6002
+ row.cells.forEach((inner, index) => {
6003
+ for (const paragraph of cellParagraphs(inner)) groups.set(paragraph.id, first3.id + ":" + index);
6004
+ });
6005
+ return groups.size ? groups : void 0;
6006
+ }
6007
+ function measuredNoteReferences(paragraphs) {
6008
+ const markers = paragraphs.flatMap(
6009
+ (paragraph) => paragraph.runs.flatMap(
6010
+ (run) => run.footnoteReferenceId === void 0 ? [] : [run.footnoteReferenceId]
6011
+ )
6012
+ );
6013
+ const ids = new Set(markers);
6014
+ const unanchored = paragraphs.flatMap(
6015
+ (paragraph) => (paragraph.footnotes ?? []).filter(
6016
+ (note) => !paragraph.runs.some((run) => run.footnoteReferenceId === note.id)
6017
+ )
6018
+ ).length;
6019
+ return markers.length + unanchored <= 3 && ids.size === markers.length && !(markers.length + unanchored > 1 && unanchored);
6020
+ }
6021
+
6022
+ // docx/layout/table-border-layout.ts
6023
+ function layoutBorderedRow(row, width, measurer, note, profile, remaining) {
6024
+ const reserved = profile !== void 0 && (profile.leftPx > 0 || profile.rightPx > 0 || profile.bottomPx > 0);
6025
+ const layout = layoutRow(
6026
+ row,
6027
+ width,
6028
+ measurer,
6029
+ note,
6030
+ remaining,
6031
+ void 0,
6032
+ reserved,
6033
+ profile?.leftPx
6034
+ );
6035
+ if (!reserved) return layout;
6036
+ const bottom = Math.max(0, ...layout.geometry.map((cell) => cell.borders?.bottom?.widthPx ?? 0));
6037
+ return {
6038
+ ...layout,
6039
+ heightPx: layout.heightPx + bottom,
6040
+ borderBottomPx: bottom,
6041
+ cellPadding: row.cells.map((cell) => ({
6042
+ top: (cell.padding?.top ?? 0) + (cell.borders?.top?.widthPx ?? 0),
6043
+ bottom: (cell.padding?.bottom ?? 0) + (cell.borders?.bottom?.widthPx ?? 0)
6044
+ }))
6045
+ };
6046
+ }
6047
+ var TableFragmentBorders = class {
6048
+ bottom = 0;
6049
+ included = true;
6050
+ record(bottom) {
6051
+ this.bottom = bottom ?? 0;
6052
+ this.included = true;
6053
+ }
6054
+ remove(row) {
6055
+ if (!row || !this.included) return 0;
6056
+ row.heightPx -= this.bottom;
6057
+ this.included = false;
6058
+ return -this.bottom;
6059
+ }
6060
+ restore(row) {
6061
+ if (!row || this.included) return 0;
6062
+ row.heightPx += this.bottom;
6063
+ this.included = true;
6064
+ return this.bottom;
6065
+ }
6066
+ };
6067
+
6068
+ // docx/layout/table-note-completion.ts
6069
+ function completedNoteStoryMinimum(row, stories, pendingBlocks, available) {
6070
+ let minimum = 0;
6071
+ for (const [index, cell] of row.cells.entries()) {
6072
+ const groups = /* @__PURE__ */ new Map();
6073
+ for (const box of cell) {
6074
+ const group = stories.get(box.blockId);
6075
+ if (group !== void 0) {
6076
+ const boxes = groups.get(group) ?? [];
6077
+ boxes.push(box);
6078
+ groups.set(group, boxes);
6079
+ }
6080
+ }
6081
+ for (const boxes of groups.values()) {
6082
+ if (boxes.reduce((count, box) => count + box.lines.length, 0) !== 1 || boxes.some((box) => pendingBlocks.has(box.blockId)))
6083
+ continue;
6084
+ const bottom = cellContentBottom(boxes, row.cellPadding?.[index]?.bottom ?? 0);
6085
+ if (bottom <= available + 1e-6) minimum = Math.max(minimum, bottom);
6086
+ }
6087
+ }
6088
+ return minimum;
6089
+ }
6090
+
5796
6091
  // docx/layout/table-footnotes.ts
5797
- function prefix(row, cut, cellCuts) {
6092
+ function prefix(row, cut, cellCuts, paragraphCuts) {
5798
6093
  const cells = row.cells.map((cell, index) => {
5799
- const contentCut = (cellCuts?.[index] ?? cut) - (row.cellPadding?.[index]?.bottom ?? 0);
6094
+ const padding = row.cellPadding?.[index]?.bottom ?? 0;
5800
6095
  const boxes = [];
5801
6096
  for (const box of cell) {
5802
- if (box.yPx >= contentCut + 1e-6) break;
5803
- if (box.yPx + box.heightPx <= contentCut + 1e-6) {
6097
+ const contentCut = (paragraphCuts?.get(box.blockId) ?? cellCuts?.[index] ?? cut) - padding;
6098
+ if (box.yPx >= contentCut + 1e-6) {
6099
+ if (!paragraphCuts?.size) break;
6100
+ continue;
6101
+ }
6102
+ if (box.yPx + box.heightPx + (box.tableFrameInsets?.bottom ?? 0) <= contentCut + 1e-6) {
5804
6103
  boxes.push(box);
5805
6104
  continue;
5806
6105
  }
5807
6106
  const lines = [];
5808
6107
  for (const line2 of box.lines) {
5809
- if (box.yPx + line2.yPx + line2.heightPx > contentCut + 1e-6) break;
6108
+ if (box.yPx + line2.yPx + line2.heightPx + (box.tableFrameInsets?.bottom ?? 0) > contentCut + 1e-6)
6109
+ break;
5810
6110
  lines.push(line2);
5811
6111
  }
5812
6112
  if (lines.length)
@@ -5826,9 +6126,7 @@ function prefix(row, cut, cellCuts) {
5826
6126
  cells,
5827
6127
  heightPx: Math.max(
5828
6128
  0,
5829
- ...cells.flatMap(
5830
- (cell, index) => cell.map((box) => box.yPx + box.heightPx + (row.cellPadding?.[index]?.bottom ?? 0))
5831
- )
6129
+ ...cells.map((cell, index) => cellContentBottom(cell, row.cellPadding?.[index]?.bottom ?? 0))
5832
6130
  )
5833
6131
  };
5834
6132
  }
@@ -5838,16 +6136,23 @@ var TableFootnotes = class {
5838
6136
  this.table = table;
5839
6137
  this.height = height;
5840
6138
  this.resolve = resolve;
5841
- for (const row of table.rows)
5842
- for (const cell of row.cells)
6139
+ for (const row of table.rows) {
6140
+ const measured = measuredNoteReferences(row.cells.flatMap(cellParagraphs));
6141
+ for (const cell of row.cells) {
5843
6142
  for (const paragraph of cellParagraphs(cell))
5844
6143
  if (paragraph.footnotes?.length) this.paragraphs.set(paragraph.id, paragraph);
6144
+ for (const [id, group] of (measured ? independentNoteStories(cell) : void 0) ?? []) {
6145
+ this.stories.set(id, group);
6146
+ }
6147
+ }
6148
+ }
5845
6149
  }
5846
6150
  cursor;
5847
6151
  table;
5848
6152
  height;
5849
6153
  resolve;
5850
6154
  paragraphs = /* @__PURE__ */ new Map();
6155
+ stories = /* @__PURE__ */ new Map();
5851
6156
  placed = /* @__PURE__ */ new Set();
5852
6157
  fragment = /* @__PURE__ */ new Map();
5853
6158
  references(row) {
@@ -5869,14 +6174,30 @@ var TableFootnotes = class {
5869
6174
  ) ?? box.lines[0];
5870
6175
  const bottom = Math.min(
5871
6176
  row.heightPx,
5872
- box.yPx + (line2?.yPx ?? 0) + (line2?.heightPx ?? box.heightPx)
6177
+ box.yPx + (line2?.yPx ?? 0) + (line2?.heightPx ?? box.heightPx) + (box.tableFrameInsets?.bottom ?? 0)
5873
6178
  );
5874
6179
  const prior = refs.get(note.id);
5875
- if (!prior || bottom < prior.bottom) refs.set(note.id, { note, bottom });
6180
+ if (!prior || bottom < prior.bottom)
6181
+ refs.set(note.id, {
6182
+ note,
6183
+ bottom,
6184
+ blockId: box.blockId,
6185
+ before: box.yPx + (line2?.yPx ?? 0) + (box.tableFrameInsets?.bottom ?? 0)
6186
+ });
5876
6187
  }
5877
6188
  }
5878
6189
  return [...refs.values()];
5879
6190
  }
6191
+ /** A consumed marker no longer constrains its story. Deferred markers stop just before
6192
+ * their own line, allowing another pending story to use a different complete prefix. */
6193
+ storyCut(pending, boxes, padding, cut, space, included) {
6194
+ const ids = new Set(boxes.map((box) => box.blockId));
6195
+ const refs = pending.filter((ref) => ids.has(ref.blockId));
6196
+ let limit = refs.some((ref) => included.has(ref.note.id)) ? Math.min(cut, space) : space;
6197
+ for (const ref of refs)
6198
+ if (!included.has(ref.note.id)) limit = Math.min(limit, ref.before + padding);
6199
+ return limit;
6200
+ }
5880
6201
  hold(refs, minimumBody = 0) {
5881
6202
  const referenceBottom = Math.max(0, ...[...refs.values()].map((ref) => ref.bottom));
5882
6203
  const bodyHeight = Math.max(referenceBottom, minimumBody);
@@ -5887,17 +6208,20 @@ var TableFootnotes = class {
5887
6208
  this.cursor.usedColumnHeightPx + referenceBottom
5888
6209
  );
5889
6210
  }
5890
- holdCandidate(row) {
6211
+ holdCandidate(row, completionMinimum = 0) {
5891
6212
  const refs = new Map(this.fragment);
5892
6213
  for (const ref of this.references(row))
5893
6214
  if (!refs.has(ref.note.id))
5894
6215
  refs.set(ref.note.id, { ...ref, bottom: this.height() + ref.bottom });
5895
6216
  const referenceBottom = Math.max(0, ...[...refs.values()].map((ref) => ref.bottom));
5896
- const contentBottom = this.height() + Math.max(0, ...row.cells.flatMap((cell) => cell.map((box) => box.yPx + box.heightPx)));
6217
+ const contentBottom = this.height() + Math.max(0, ...row.cells.map((cell) => cellContentBottom(cell)));
5897
6218
  const paddingBottom = Math.max(0, ...(row.cellPadding ?? []).map((padding) => padding.bottom));
5898
6219
  this.hold(
5899
6220
  refs,
5900
- contentBottom <= referenceBottom + 1e-6 ? this.height() + row.heightPx : referenceBottom + paddingBottom
6221
+ Math.max(
6222
+ completionMinimum,
6223
+ contentBottom <= referenceBottom + 1e-6 ? this.height() + row.heightPx : referenceBottom + paddingBottom
6224
+ )
5901
6225
  );
5902
6226
  }
5903
6227
  /** Largest complete prefix that keeps each new marker with available note content. */
@@ -5910,29 +6234,76 @@ var TableFootnotes = class {
5910
6234
  return { layout: row, cut: row.heightPx };
5911
6235
  }
5912
6236
  if (!atomic) {
6237
+ const independent = row.cells.some(
6238
+ (cell) => cell.some((box) => this.stories.has(box.blockId))
6239
+ );
6240
+ const pending = independent ? this.references(row) : [];
6241
+ const completing = (this.cursor.footnoteColumnCount === 2 || this.cursor.footnoteColumnCount === 3) && pending.length === 1 && !this.stories.has(pending[0].blockId) ? completedNoteStoryMinimum(
6242
+ row,
6243
+ this.stories,
6244
+ new Set(pending.map((ref) => ref.blockId)),
6245
+ available
6246
+ ) : 0;
5913
6247
  const cuts = /* @__PURE__ */ new Set();
5914
6248
  for (const [index, cell] of row.cells.entries())
5915
6249
  for (const box of cell) {
5916
6250
  if (box.yPx > available + 1e-6) break;
5917
6251
  for (const line2 of box.lines) {
5918
- const bottom = box.yPx + line2.yPx + line2.heightPx + (row.cellPadding?.[index]?.bottom ?? 0);
6252
+ const bottom = box.yPx + line2.yPx + line2.heightPx + (box.tableFrameInsets?.bottom ?? 0) + (row.cellPadding?.[index]?.bottom ?? 0);
5919
6253
  if (bottom > available + 1e-6) break;
5920
6254
  if (bottom > 0) cuts.add(bottom);
5921
6255
  }
5922
6256
  }
5923
6257
  for (const cut of [...cuts].sort((a, b) => b - a)) {
5924
6258
  const candidate = prefix(row, cut);
5925
- this.holdCandidate(candidate);
6259
+ const included = new Set(this.references(candidate).map((ref) => ref.note.id));
6260
+ const completion = completing > 0 && included.has(pending[0]?.note.id ?? "") ? this.height() + completing : 0;
6261
+ this.holdCandidate(candidate, completion);
5926
6262
  if (candidate.heightPx <= this.cursor.remainingHeightPx() - this.height() + 1e-6) {
5927
6263
  const space = this.cursor.remainingHeightPx() - this.height();
5928
6264
  const cellCuts = row.cells.map(
5929
- (cell) => cell.some((box) => this.paragraphs.has(box.blockId)) ? cut : space
6265
+ (cell, index) => independent ? this.storyCut(
6266
+ pending,
6267
+ cell,
6268
+ row.cellPadding?.[index]?.bottom ?? 0,
6269
+ cut,
6270
+ space,
6271
+ included
6272
+ ) : cell.some((box) => this.paragraphs.has(box.blockId)) ? cut : space
5930
6273
  );
5931
- const expanded = prefix(row, cut, cellCuts);
5932
- this.holdCandidate(expanded);
6274
+ const paragraphCuts = /* @__PURE__ */ new Map();
6275
+ for (const [index, cell] of row.cells.entries()) {
6276
+ const groups = /* @__PURE__ */ new Map();
6277
+ for (const box of cell) {
6278
+ const group = this.stories.get(box.blockId);
6279
+ if (group !== void 0) {
6280
+ const boxes = groups.get(group) ?? [];
6281
+ boxes.push(box);
6282
+ groups.set(group, boxes);
6283
+ }
6284
+ }
6285
+ for (const boxes of groups.values()) {
6286
+ const limit = this.storyCut(
6287
+ pending,
6288
+ boxes,
6289
+ row.cellPadding?.[index]?.bottom ?? 0,
6290
+ cut,
6291
+ space,
6292
+ included
6293
+ );
6294
+ for (const box of boxes) paragraphCuts.set(box.blockId, limit);
6295
+ }
6296
+ }
6297
+ const expanded = prefix(row, cut, cellCuts, paragraphCuts);
6298
+ this.holdCandidate(expanded, completion);
5933
6299
  if (expanded.heightPx <= this.cursor.remainingHeightPx() - this.height() + 1e-6)
5934
- return { layout: expanded, cut, cellCuts };
5935
- this.holdCandidate(candidate);
6300
+ return {
6301
+ layout: expanded,
6302
+ cut,
6303
+ cellCuts,
6304
+ ...paragraphCuts.size ? { paragraphCuts } : {}
6305
+ };
6306
+ this.holdCandidate(candidate, completion);
5936
6307
  return { layout: candidate, cut };
5937
6308
  }
5938
6309
  }
@@ -5960,25 +6331,44 @@ function placeTable(cursor, table, measurer, note, resolveFootnotes) {
5960
6331
  const firstBodyRow = table.rows.findIndex((row) => !row.isHeader);
5961
6332
  const headerRows = table.rows.slice(0, firstBodyRow < 0 ? table.rows.length : firstBodyRow);
5962
6333
  const xPosition = () => {
5963
- const width = table.widthPx ?? cursor.columnWidthPx;
6334
+ const width = (table.widthPx ?? cursor.columnWidthPx) + (activeBorders ? (profile.leftPx + profile.rightPx) / 2 : 0);
5964
6335
  return table.alignment === "center" ? Math.max(0, (cursor.columnWidthPx - width) / 2) : table.alignment === "right" ? Math.max(0, cursor.columnWidthPx - width) : table.indentPx ?? 0;
5965
6336
  };
6337
+ const profile = nestedFrameProfile(table, table.widthPx ?? cursor.columnWidthPx);
6338
+ const activeBorders = profile !== void 0 && (profile.leftPx > 0 || profile.rightPx > 0 || profile.bottomPx > 0);
6339
+ if (!profile && table.rows.some((row) => row.cells.some((cell) => cell.borders)))
6340
+ note(
6341
+ "Ordinary table border occupancy remains approximate for missing edges, non-solid or conflicting shared borders and changing row grids."
6342
+ );
6343
+ const layoutRow2 = (row, width, m, n2, remaining) => layoutBorderedRow(
6344
+ row,
6345
+ activeBorders ? table.widthPx ?? width : width,
6346
+ m,
6347
+ n2,
6348
+ profile,
6349
+ remaining
6350
+ );
6351
+ const fragmentBorders = new TableFragmentBorders();
5966
6352
  let fragmentRows = [];
5967
6353
  let fragmentHeight = 0;
5968
6354
  const footnotes = resolveFootnotes ? new TableFootnotes(cursor, table, () => fragmentHeight, resolveFootnotes) : void 0;
5969
6355
  const toRowBox = (layout, repeated) => {
6356
+ fragmentHeight += fragmentBorders.remove(fragmentRows.at(-1));
5970
6357
  footnotes?.add(layout, repeated);
5971
6358
  const box = {
5972
6359
  yPx: fragmentHeight,
5973
6360
  heightPx: layout.heightPx,
5974
6361
  repeated,
5975
6362
  cells: layout.cells,
5976
- geometry: layout.geometry
6363
+ geometry: layout.geometry,
6364
+ ...layout.cellTables ? { cellTables: layout.cellTables } : {}
5977
6365
  };
5978
6366
  fragmentHeight += layout.heightPx;
6367
+ fragmentBorders.record(layout.borderBottomPx);
5979
6368
  return box;
5980
6369
  };
5981
6370
  function flushFragment() {
6371
+ fragmentHeight += fragmentBorders.restore(fragmentRows.at(-1));
5982
6372
  if (!fragmentRows.length) {
5983
6373
  footnotes?.holdFragment();
5984
6374
  return;
@@ -5986,6 +6376,9 @@ function placeTable(cursor, table, measurer, note, resolveFootnotes) {
5986
6376
  const box = {
5987
6377
  kind: "table",
5988
6378
  blockId: table.id,
6379
+ ...activeBorders ? {
6380
+ widthPx: (table.widthPx ?? cursor.columnWidthPx) + (profile.leftPx + profile.rightPx) / 2
6381
+ } : {},
5989
6382
  ...xPosition() ? { xPx: xPosition() } : {},
5990
6383
  yPx: 0,
5991
6384
  heightPx: fragmentHeight,
@@ -6004,15 +6397,16 @@ function placeTable(cursor, table, measurer, note, resolveFootnotes) {
6004
6397
  }
6005
6398
  function addHeaders() {
6006
6399
  for (const header of headerRows)
6007
- fragmentRows.push(toRowBox(layoutRow(header, cursor.columnWidthPx, measurer, note), true));
6400
+ fragmentRows.push(toRowBox(layoutRow2(header, cursor.columnWidthPx, measurer, note), true));
6008
6401
  }
6009
6402
  function placeRow(row, initial, cantSplit) {
6010
6403
  let remainingRow = initial;
6011
6404
  while (remainingRow) {
6405
+ fragmentHeight += fragmentBorders.remove(fragmentRows.at(-1));
6012
6406
  const minimum = cantSplit ? Math.min(remainingRow.heightPx, cursor.columnHeightPx) : Math.min(
6013
6407
  ...remainingRow.cells.flatMap(
6014
- (cell) => cell.map(
6015
- (paragraph) => paragraph.yPx + (paragraph.lines[0]?.yPx ?? 0) + (paragraph.lines[0]?.heightPx ?? 0)
6408
+ (cell, index) => cell.map(
6409
+ (paragraph) => paragraph.yPx + (paragraph.lines[0]?.yPx ?? 0) + (paragraph.lines[0]?.heightPx ?? 0) + (remainingRow.borderBottomPx !== void 0 ? (remainingRow.cellPadding?.[index]?.bottom ?? 0) + (paragraph.tableFrameInsets?.bottom ?? 0) : 0)
6016
6410
  )
6017
6411
  )
6018
6412
  );
@@ -6020,7 +6414,7 @@ function placeTable(cursor, table, measurer, note, resolveFootnotes) {
6020
6414
  const width = cursor.columnWidthPx;
6021
6415
  cursor.advancePastFootnotes(fragmentHeight + (Number.isFinite(minimum) ? minimum : 0));
6022
6416
  if (width !== cursor.columnWidthPx) {
6023
- remainingRow = layoutRow(
6417
+ remainingRow = layoutRow2(
6024
6418
  remainingRow === initial ? row : { ...row, heightPx: 0, heightRule: "atLeast" },
6025
6419
  cursor.columnWidthPx,
6026
6420
  measurer,
@@ -6046,12 +6440,13 @@ function placeTable(cursor, table, measurer, note, resolveFootnotes) {
6046
6440
  const { before: before2, after: after2 } = splitRowAtHeight(
6047
6441
  remainingRow,
6048
6442
  selected.cut + 1e-6,
6049
- selected.cellCuts?.map((cut) => cut + 1e-6)
6443
+ selected.cellCuts?.map((cut) => cut + 1e-6),
6444
+ selected.paragraphCuts ? new Map([...selected.paragraphCuts].map(([id, cut]) => [id, cut + 1e-6])) : void 0
6050
6445
  );
6051
6446
  fragmentRows.push(toRowBox(before2, false));
6052
6447
  const previousWidth2 = cursor.columnWidthPx;
6053
6448
  startNewFragment();
6054
- remainingRow = after2 && previousWidth2 !== cursor.columnWidthPx ? layoutRow(
6449
+ remainingRow = after2 && (selected.paragraphCuts || previousWidth2 !== cursor.columnWidthPx) ? layoutRow2(
6055
6450
  { ...row, heightPx: 0, heightRule: "atLeast" },
6056
6451
  cursor.columnWidthPx,
6057
6452
  measurer,
@@ -6064,7 +6459,7 @@ function placeTable(cursor, table, measurer, note, resolveFootnotes) {
6064
6459
  const previousWidth2 = cursor.columnWidthPx;
6065
6460
  startNewFragment();
6066
6461
  if (previousWidth2 !== cursor.columnWidthPx)
6067
- remainingRow = layoutRow(
6462
+ remainingRow = layoutRow2(
6068
6463
  remainingRow === initial ? row : { ...row, heightPx: 0, heightRule: "atLeast" },
6069
6464
  cursor.columnWidthPx,
6070
6465
  measurer,
@@ -6083,14 +6478,14 @@ function placeTable(cursor, table, measurer, note, resolveFootnotes) {
6083
6478
  if (cantSplit) {
6084
6479
  if (fragmentRows.length || !cursor.atColumnTop) {
6085
6480
  startNewFragment();
6086
- remainingRow = layoutRow(row, cursor.columnWidthPx, measurer, note);
6481
+ remainingRow = layoutRow2(row, cursor.columnWidthPx, measurer, note);
6087
6482
  }
6088
6483
  const noteWidth = cursor.columnWidthPx;
6089
6484
  cursor.advancePastFootnotes(
6090
6485
  fragmentHeight + Math.min(remainingRow.heightPx, cursor.columnHeightPx)
6091
6486
  );
6092
6487
  if (noteWidth !== cursor.columnWidthPx) {
6093
- remainingRow = layoutRow(row, cursor.columnWidthPx, measurer, note);
6488
+ remainingRow = layoutRow2(row, cursor.columnWidthPx, measurer, note);
6094
6489
  if (fragmentRows.length) {
6095
6490
  fragmentRows = [];
6096
6491
  fragmentHeight = 0;
@@ -6116,7 +6511,7 @@ function placeTable(cursor, table, measurer, note, resolveFootnotes) {
6116
6511
  const previousWidth = cursor.columnWidthPx;
6117
6512
  if (before.cells.some((cell) => cell.length)) fragmentRows.push(toRowBox(before, false));
6118
6513
  startNewFragment();
6119
- remainingRow = after && previousWidth !== cursor.columnWidthPx ? layoutRow(
6514
+ remainingRow = after && previousWidth !== cursor.columnWidthPx ? layoutRow2(
6120
6515
  { ...row, heightPx: 0, heightRule: "atLeast" },
6121
6516
  cursor.columnWidthPx,
6122
6517
  measurer,
@@ -6126,7 +6521,13 @@ function placeTable(cursor, table, measurer, note, resolveFootnotes) {
6126
6521
  }
6127
6522
  }
6128
6523
  for (const row of table.rows) {
6129
- const layout = layoutRow(row, cursor.columnWidthPx, measurer, note);
6524
+ const layout = layoutRow2(row, cursor.columnWidthPx, measurer, note);
6525
+ if (activeBorders && layout.heightPx > cursor.remainingHeightPx() && row.cells.some(
6526
+ (cell) => (!measuredNoteReferences(row.cells.flatMap(cellParagraphs)) || !independentNoteStories(cell)) && cell.blocks?.some((block) => block.kind === "table") && cellParagraphs(cell).some((p) => p.footnotes?.length)
6527
+ ))
6528
+ note(
6529
+ "Independent inner-cell continuation around nested note references remains approximate when an enclosing bordered row splits."
6530
+ );
6130
6531
  placeRow(
6131
6532
  row,
6132
6533
  layout,
@@ -6637,13 +7038,13 @@ function cssHex(value) {
6637
7038
  const hex = value?.replace(/^#/, "");
6638
7039
  return hex && /^[0-9a-f]{6}$/i.test(hex) ? `#${hex}` : void 0;
6639
7040
  }
6640
- function border(side, theme) {
7041
+ function border(side, theme, preserveSingleWidth = false) {
6641
7042
  if (!side?.style || side.style === "none" || side.style === "nil") return void 0;
6642
7043
  const style = side.style.startsWith("double") ? "double" : /dot/i.test(side.style) ? "dotted" : /dash/i.test(side.style) ? "dashed" : "solid";
6643
7044
  const themed = side.themeColor && theme ? resolveThemeColorReference({ token: side.themeColor }, theme) : void 0;
6644
7045
  const hex = cssHex(side.color);
6645
7046
  const widthPx = Math.max(
6646
- style === "double" ? 3 : 1,
7047
+ style === "double" ? 3 : preserveSingleWidth && style === "solid" ? 0 : 1,
6647
7048
  (side.sizeEighthPoints ?? 4) / 8 * 96 / 72
6648
7049
  );
6649
7050
  return { widthPx, style, color: themed ?? hex ?? "#000000" };
@@ -6656,7 +7057,7 @@ function rowLayout(props) {
6656
7057
  ...props.header ? { isHeader: true } : {}
6657
7058
  };
6658
7059
  }
6659
- function adaptTable(table, model, adaptParagraph) {
7060
+ function adaptTable(table, model, adaptParagraph, _nested = false) {
6660
7061
  const grid = table.grid?.map(twipsToPx2);
6661
7062
  const columnCount = grid?.length ?? Math.max(1, ...table.rows.map((row) => row.length));
6662
7063
  const gridWidth = (start, span) => grid ? grid.slice(start, start + span).reduce((sum, width) => sum + width, 0) : void 0;
@@ -6698,7 +7099,7 @@ function adaptTable(table, model, adaptParagraph) {
6698
7099
  );
6699
7100
  const borders = {};
6700
7101
  for (const side of ["top", "right", "bottom", "left"]) {
6701
- const resolved = border(visuals.borders?.[side], model.theme);
7102
+ const resolved = border(visuals.borders?.[side], model.theme, true);
6702
7103
  if (resolved) borders[side] = resolved;
6703
7104
  }
6704
7105
  if (cell.verticalMerge === "continue") delete borders.top;
@@ -6717,7 +7118,7 @@ function adaptTable(table, model, adaptParagraph) {
6717
7118
  paragraphs: cell.paragraphs.map(adaptParagraph),
6718
7119
  ...cell.sourceBlocks ? {
6719
7120
  blocks: cellSourceBlocks(cell).map(
6720
- (block) => block.type === "paragraph" ? adaptParagraph(block) : adaptTable(block, model, adaptParagraph)
7121
+ (block) => block.type === "paragraph" ? adaptParagraph(block) : adaptTable(block, model, adaptParagraph, true)
6721
7122
  )
6722
7123
  } : {},
6723
7124
  ...width !== void 0 ? { widthPx: width } : {},
@@ -7017,7 +7418,7 @@ function adaptDocumentModel(model, note = () => {
7017
7418
  reportOnce(VERTICAL_MERGE_NOTE);
7018
7419
  if (block.rows.some((row) => row.some((cell) => cell.nestedTables?.length)))
7019
7420
  reportOnce(
7020
- "Nested tables are projected as read-only cell paragraphs in Print Layout; their borders, shading, vertical merge and repeated header geometry are not rendered."
7421
+ "Nested tables are projected as read-only cell paragraphs in Print Layout; vertical merge and repeated header geometry are not rendered."
7021
7422
  );
7022
7423
  return adaptTable(block, model, adaptParagraph);
7023
7424
  }
@@ -7284,4 +7685,4 @@ function cssFontStack(family) {
7284
7685
  );
7285
7686
  }
7286
7687
 
7287
- export { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE_PT, DEFAULT_TAB_STOP_PX, FLOAT_WRAP_NOTE, NO_TWIPS, PageCursor, SINGLE_LINE_SPACING_UNITS, adaptDocumentModel, adjustForWidowOrphan, appendBreakMarker, createFakeMeasurer, cssFontStack, fieldDisplayText, floatPosition, fontMetrics, headerFooterForPage, isEastAsianChar, layoutDocument, layoutDocumentModel, layoutParagraph, layoutRow, layoutSections, ligatureCss, pageNumberValues, pageNumbers, paragraphFloats, placeParagraph, placeTab, placeTable, positionFloats, ptToPx, pxToTwips, sectionPageCounts, splitRowAtHeight, suppressesSpacing, tokenizeRun, twipsToPx, widowControlEnabled };
7688
+ export { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE_PT, DEFAULT_TAB_STOP_PX, FLOAT_WRAP_NOTE, NO_TWIPS, PageCursor, SINGLE_LINE_SPACING_UNITS, adaptDocumentModel, adjustForWidowOrphan, appendBreakMarker, cellVerticalOffset, createFakeMeasurer, cssFontStack, fieldDisplayText, floatPosition, fontMetrics, headerFooterForPage, isEastAsianChar, layoutDocument, layoutDocumentModel, layoutParagraph, layoutRow, layoutSections, ligatureCss, pageNumberValues, pageNumbers, paragraphFloats, placeParagraph, placeTab, placeTable, positionFloats, ptToPx, pxToTwips, sectionPageCounts, splitRowAtHeight, suppressesSpacing, tokenizeRun, twipsToPx, widowControlEnabled };