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
@@ -4427,6 +4427,12 @@ function appendBreakMarker(tokens, runIndex, sourceStart, kind) {
4427
4427
  tokens.push({ kind: kind === "page" ? "pageBreak" : "columnBreak", runIndex, sourceStart });
4428
4428
  }
4429
4429
 
4430
+ // docx/layout/result.ts
4431
+ function cellVerticalOffset(geometry, rowHeightPx) {
4432
+ const free = Math.max(0, (geometry.alignmentHeightPx ?? rowHeightPx) - geometry.contentHeightPx);
4433
+ return geometry.verticalAlign === "center" ? free / 2 : geometry.verticalAlign === "bottom" ? free : 0;
4434
+ }
4435
+
4430
4436
  // docx/layout/page-fields.ts
4431
4437
  function fieldDisplayText(run, values) {
4432
4438
  if (!run.field || isFieldLocked(run)) return run.text;
@@ -4891,6 +4897,79 @@ function widowControlEnabled(value) {
4891
4897
  return value !== false;
4892
4898
  }
4893
4899
 
4900
+ // docx/layout/table-content.ts
4901
+ function cellContentBottom(boxes, paddingBottom = 0) {
4902
+ return Math.max(
4903
+ 0,
4904
+ ...boxes.map(
4905
+ (box) => box.yPx + box.heightPx + (box.tableFrameInsets?.bottom ?? 0) + paddingBottom
4906
+ )
4907
+ );
4908
+ }
4909
+
4910
+ // docx/layout/table-frames.ts
4911
+ function clipTableFrames(tables, start, end, inset = 0) {
4912
+ return tables.flatMap((table) => {
4913
+ const top = Math.max(start, table.yPx);
4914
+ const bottom = Math.min(end, table.yPx + table.heightPx);
4915
+ if (bottom <= top) return [];
4916
+ const rows = table.rows.flatMap((row) => {
4917
+ const rowTop = table.yPx + row.yPx;
4918
+ const a = Math.max(top, rowTop), b = Math.min(bottom, rowTop + row.heightPx);
4919
+ if (b <= a) return [];
4920
+ return [
4921
+ {
4922
+ ...row,
4923
+ yPx: a - top,
4924
+ heightPx: b - a,
4925
+ ...row.cellTables ? {
4926
+ cellTables: row.cellTables.map(
4927
+ (nested) => clipTableFrames(nested, a - rowTop, b - rowTop)
4928
+ )
4929
+ } : {}
4930
+ }
4931
+ ];
4932
+ });
4933
+ return [{ ...table, yPx: top - start + inset, heightPx: bottom - top, rows }];
4934
+ });
4935
+ }
4936
+
4937
+ // docx/layout/nested-frame-profile.ts
4938
+ 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;
4939
+ function nestedFrameProfile(table, tableWidthPx = table.widthPx) {
4940
+ const sides = table.rows.flatMap(
4941
+ (row) => row.cells.flatMap(
4942
+ (cell) => ["top", "right", "bottom", "left"].map((side) => cell.borders?.[side])
4943
+ )
4944
+ );
4945
+ if (sides.every((side) => !side)) return { leftPx: 0, rightPx: 0, bottomPx: 0 };
4946
+ if (!sides.every((side) => side?.style === "solid" && side.widthPx > 0)) return void 0;
4947
+ const first3 = table.rows[0], left = first3.cells[0].borders.left, right = first3.cells.at(-1).borders.right;
4948
+ for (const [index, row] of table.rows.entries()) {
4949
+ if (row.cells.length !== first3.cells.length || !equal(row.cells[0]?.borders?.left, left) || !equal(row.cells.at(-1)?.borders?.right, right))
4950
+ return void 0;
4951
+ for (const [column, cell] of row.cells.entries()) {
4952
+ const sourceWidth = cell.widthPx ?? (tableWidthPx === void 0 ? void 0 : tableWidthPx / row.cells.length);
4953
+ 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);
4954
+ if (paintWidth !== void 0 && paintWidth <= 0) return void 0;
4955
+ if (!equal(cell.borders?.top, row.cells[0]?.borders?.top) || !equal(cell.borders?.bottom, row.cells[0]?.borders?.bottom))
4956
+ return void 0;
4957
+ if (column && !equal(row.cells[column - 1]?.borders?.right, cell.borders?.left))
4958
+ return void 0;
4959
+ if (index) {
4960
+ const previous = table.rows[index - 1].cells[column];
4961
+ if (previous.xPx !== cell.xPx || previous.widthPx !== cell.widthPx || !equal(previous.borders?.bottom, cell.borders?.top))
4962
+ return void 0;
4963
+ }
4964
+ }
4965
+ }
4966
+ return {
4967
+ leftPx: left.widthPx,
4968
+ rightPx: right.widthPx,
4969
+ bottomPx: table.rows.at(-1).cells[0].borders.bottom.widthPx
4970
+ };
4971
+ }
4972
+
4894
4973
  // docx/layout/table-cell-layout.ts
4895
4974
  function cellParagraphs(cell) {
4896
4975
  const walk = (blocks) => blocks.flatMap(
@@ -4910,6 +4989,7 @@ function stackCellBlocks(cell, width, measurer, note, top, starts) {
4910
4989
  );
4911
4990
  let y = top;
4912
4991
  const boxes = [];
4992
+ const tables = [];
4913
4993
  for (const block of cell.blocks) {
4914
4994
  if (block.kind === "paragraph") {
4915
4995
  if (starts && !starts.has(block.id)) continue;
@@ -4919,27 +4999,72 @@ function stackCellBlocks(cell, width, measurer, note, top, starts) {
4919
4999
  continue;
4920
5000
  }
4921
5001
  const tableWidth = block.widthPx ?? width;
4922
- const x = block.alignment === "center" ? Math.max(0, (width - tableWidth) / 2) : block.alignment === "right" ? Math.max(0, width - tableWidth) : block.indentPx ?? 0;
5002
+ const profile = nestedFrameProfile(block, tableWidth);
5003
+ const reserveBorders = profile !== void 0;
5004
+ if (!reserveBorders)
5005
+ note(
5006
+ "Nested table border occupancy is approximate for missing edges, non-solid or conflicting shared borders and changing row grids."
5007
+ );
5008
+ const externalRight = profile ? (profile.leftPx + profile.rightPx) / 2 : 0;
5009
+ const paintedWidth = tableWidth + externalRight;
5010
+ const x = block.alignment === "center" ? Math.max(0, (width - paintedWidth) / 2) : block.alignment === "right" ? Math.max(0, width - paintedWidth) : block.indentPx ?? 0;
5011
+ const frame = {
5012
+ kind: "table",
5013
+ blockId: block.id,
5014
+ xPx: x,
5015
+ widthPx: paintedWidth,
5016
+ yPx: y,
5017
+ heightPx: 0,
5018
+ rows: []
5019
+ };
4923
5020
  for (const row of block.rows) {
4924
5021
  if (starts && !row.cells.some((nested) => cellParagraphs(nested).some((p) => starts.has(p.id))))
4925
5022
  continue;
4926
- const layout = layoutRow(row, tableWidth, measurer, note, void 0, starts);
5023
+ const layout = layoutRow(
5024
+ row,
5025
+ tableWidth,
5026
+ measurer,
5027
+ note,
5028
+ void 0,
5029
+ starts,
5030
+ reserveBorders,
5031
+ profile?.leftPx
5032
+ );
5033
+ frame.rows.push({
5034
+ yPx: y - frame.yPx,
5035
+ heightPx: layout.heightPx,
5036
+ repeated: false,
5037
+ cells: layout.cells,
5038
+ geometry: layout.geometry,
5039
+ ...layout.cellTables ? { cellTables: layout.cellTables } : {}
5040
+ });
4927
5041
  layout.cells.forEach((paragraphs, index) => {
4928
5042
  const geometry = layout.geometry[index];
4929
5043
  for (const paragraph of paragraphs)
4930
5044
  boxes.push({
4931
5045
  ...paragraph,
4932
- yPx: y + paragraph.yPx,
5046
+ yPx: y + paragraph.yPx + cellVerticalOffset(geometry, layout.heightPx),
4933
5047
  xPx: x + geometry.xPx + geometry.paddingLeftPx + (paragraph.xPx ?? 0),
4934
5048
  widthPx: paragraph.widthPx ?? Math.max(1, geometry.widthPx - geometry.paddingLeftPx - geometry.paddingRightPx),
4935
- readOnly: true
5049
+ readOnly: true,
5050
+ tableFrameInsets: {
5051
+ top: (paragraph.tableFrameInsets?.top ?? 0) + (reserveBorders ? geometry.borders?.top?.widthPx ?? 0 : 0),
5052
+ bottom: (paragraph.tableFrameInsets?.bottom ?? 0) + (reserveBorders ? geometry.borders?.bottom?.widthPx ?? 0 : 0)
5053
+ }
4936
5054
  });
4937
5055
  });
4938
5056
  y += layout.heightPx;
4939
5057
  }
5058
+ const bottomBorder = profile?.bottomPx ?? 0;
5059
+ if (frame.rows.length) {
5060
+ frame.rows.at(-1).heightPx += bottomBorder;
5061
+ y += bottomBorder;
5062
+ frame.heightPx = y - frame.yPx;
5063
+ tables.push(frame);
5064
+ }
4940
5065
  }
4941
5066
  boxes.sort((a, b) => a.yPx - b.yPx);
4942
- return { heightPx: y, boxes };
5067
+ return { heightPx: y, boxes, tables };
4943
5068
  }
4944
5069
 
4945
5070
  // docx/layout/table-layout.ts
@@ -4970,7 +5095,7 @@ function stackParagraphs(paragraphs, widthPx, measurer, note, topPx = 0, startTo
4970
5095
  }
4971
5096
  return { heightPx: y, boxes };
4972
5097
  }
4973
- function layoutRow(row, tableWidthPx, measurer, note, remaining, startTokens) {
5098
+ function layoutRow(row, tableWidthPx, measurer, note, remaining, startTokens, frameBorders = false, frameLeftBorderPx) {
4974
5099
  const count = row.cells.length || 1;
4975
5100
  let x = 0;
4976
5101
  const geometry = [];
@@ -4978,10 +5103,21 @@ function layoutRow(row, tableWidthPx, measurer, note, remaining, startTokens) {
4978
5103
  const continuation = remaining?.[geometry.length];
4979
5104
  const starts = continuation ? new Map(continuation.map((box) => [box.blockId, box.lines[0]?.firstToken ?? 0])) : startTokens;
4980
5105
  const widthPx = cell.widthPx ?? tableWidthPx / count;
4981
- const padding = cell.padding ?? { top: 0, right: 0, bottom: 0, left: 0 };
5106
+ const margins = cell.padding ?? { top: 0, right: 0, bottom: 0, left: 0 };
5107
+ const leftBorder = frameBorders ? cell.borders?.left?.widthPx ?? 0 : 0;
5108
+ const rightBorder = frameBorders ? cell.borders?.right?.widthPx ?? 0 : 0;
5109
+ const padding = frameBorders ? {
5110
+ top: margins.top + (cell.borders?.top?.widthPx ?? 0),
5111
+ bottom: margins.bottom,
5112
+ left: leftBorder / 2 + Math.max(margins.left, leftBorder / 2),
5113
+ right: Math.max(margins.right, rightBorder / 2) - rightBorder / 2
5114
+ } : margins;
5115
+ const externalRight = frameBorders && geometry.length === row.cells.length - 1 ? rightBorder : 0;
5116
+ const paintWidth = widthPx + (frameBorders ? (leftBorder - rightBorder) / 2 + externalRight : 0);
5117
+ const frameX = frameBorders ? ((frameLeftBorderPx ?? leftBorder) - leftBorder) / 2 : 0;
4982
5118
  const content2 = stackCellBlocks(
4983
5119
  cell,
4984
- Math.max(1, widthPx - padding.left - padding.right),
5120
+ Math.max(1, paintWidth - padding.left - padding.right - externalRight),
4985
5121
  measurer,
4986
5122
  note,
4987
5123
  padding.top,
@@ -4989,48 +5125,58 @@ function layoutRow(row, tableWidthPx, measurer, note, remaining, startTokens) {
4989
5125
  );
4990
5126
  const heightPx2 = content2.heightPx + padding.bottom;
4991
5127
  geometry.push({
4992
- xPx: cell.xPx ?? x,
4993
- widthPx,
5128
+ xPx: (cell.xPx ?? x) + frameX,
5129
+ widthPx: paintWidth,
5130
+ ...frameBorders ? { borderInsetsReserved: true } : {},
4994
5131
  paddingLeftPx: padding.left,
4995
- paddingRightPx: padding.right,
5132
+ paddingRightPx: padding.right + externalRight,
4996
5133
  contentHeightPx: heightPx2,
4997
5134
  ...cell.borders ? { borders: cell.borders } : {},
4998
5135
  ...cell.shading ? { shading: cell.shading } : {},
4999
5136
  ...cell.verticalAlign ? { verticalAlign: cell.verticalAlign } : {}
5000
5137
  });
5001
5138
  x = (cell.xPx ?? x) + widthPx;
5002
- return { heightPx: heightPx2, boxes: content2.boxes };
5139
+ return { heightPx: heightPx2, boxes: content2.boxes, tables: content2.tables ?? [] };
5003
5140
  });
5004
5141
  const content = Math.max(0, ...laidOut.map((cell) => cell.heightPx));
5005
5142
  const heightPx = row.heightPx === void 0 ? content : row.heightRule === "exact" ? row.heightPx : Math.max(content, row.heightPx);
5143
+ if (frameBorders) for (const cell of geometry) cell.alignmentHeightPx = heightPx;
5006
5144
  return {
5007
5145
  heightPx,
5008
5146
  cells: laidOut.map((cell) => cell.boxes),
5009
5147
  geometry,
5148
+ ...laidOut.some((cell) => cell.tables.length) ? { cellTables: laidOut.map((cell) => cell.tables) } : {},
5010
5149
  cellPadding: row.cells.map((cell) => ({
5011
5150
  top: cell.padding?.top ?? 0,
5012
5151
  bottom: cell.padding?.bottom ?? 0
5013
5152
  }))
5014
5153
  };
5015
5154
  }
5016
- function splitCellAtHeight(boxes, cutHeightPx) {
5155
+ function splitCellAtHeight(boxes, cutHeightPx, paragraphCuts) {
5017
5156
  const before = [];
5018
5157
  const after = [];
5019
5158
  let beforeHeightPx = 0;
5020
5159
  for (const box of boxes) {
5021
- if (box.yPx + box.heightPx <= cutHeightPx) {
5160
+ const cut = paragraphCuts?.get(box.blockId) ?? cutHeightPx;
5161
+ if (box.yPx + box.heightPx + (box.tableFrameInsets?.bottom ?? 0) <= cut) {
5022
5162
  before.push(box);
5023
- beforeHeightPx = Math.max(beforeHeightPx, box.yPx + box.heightPx);
5163
+ beforeHeightPx = Math.max(
5164
+ beforeHeightPx,
5165
+ box.yPx + box.heightPx + (box.tableFrameInsets?.bottom ?? 0)
5166
+ );
5024
5167
  continue;
5025
5168
  }
5026
5169
  const beforeLines = box.lines.filter(
5027
- (line2) => box.yPx + line2.yPx + line2.heightPx <= cutHeightPx
5170
+ (line2) => box.yPx + line2.yPx + line2.heightPx + (box.tableFrameInsets?.bottom ?? 0) <= cut
5028
5171
  );
5029
5172
  const afterLines = box.lines.slice(beforeLines.length);
5030
5173
  if (beforeLines.length) {
5031
5174
  const bottom = beforeLines.at(-1).yPx + beforeLines.at(-1).heightPx;
5032
5175
  before.push({ ...box, heightPx: bottom, lines: beforeLines });
5033
- beforeHeightPx = Math.max(beforeHeightPx, box.yPx + bottom);
5176
+ beforeHeightPx = Math.max(
5177
+ beforeHeightPx,
5178
+ box.yPx + bottom + (box.tableFrameInsets?.bottom ?? 0)
5179
+ );
5034
5180
  }
5035
5181
  if (afterLines.length) {
5036
5182
  const shift = expectDefined(afterLines[0], "first line after split").yPx;
@@ -5042,43 +5188,81 @@ function splitCellAtHeight(boxes, cutHeightPx) {
5042
5188
  });
5043
5189
  }
5044
5190
  }
5045
- const afterOrigin = Math.min(...after.map((box) => box.yPx));
5191
+ const afterOrigin = Math.min(...after.map((box) => box.yPx - (box.tableFrameInsets?.top ?? 0)));
5046
5192
  for (const box of after) box.yPx -= afterOrigin;
5047
- return { before, after, beforeHeightPx };
5193
+ return { before, after, beforeHeightPx, ...after.length ? { afterOrigin } : {} };
5048
5194
  }
5049
- function splitRowAtHeight(row, cutHeightPx, cellCuts) {
5195
+ function splitRowAtHeight(row, cutHeightPx, cellCuts, paragraphCuts) {
5050
5196
  const splits = row.cells.map((cell, index) => {
5051
5197
  const padding = row.cellPadding?.[index] ?? { top: 0, bottom: 0 };
5052
- const split = splitCellAtHeight(cell, (cellCuts?.[index] ?? cutHeightPx) - padding.bottom);
5198
+ const cuts = paragraphCuts ? new Map(
5199
+ cell.flatMap((box) => {
5200
+ const cut = paragraphCuts.get(box.blockId);
5201
+ return cut === void 0 ? [] : [[box.blockId, cut - padding.bottom]];
5202
+ })
5203
+ ) : void 0;
5204
+ const split = splitCellAtHeight(
5205
+ cell,
5206
+ (cellCuts?.[index] ?? cutHeightPx) - padding.bottom,
5207
+ cuts
5208
+ );
5053
5209
  if (split.before.length) split.beforeHeightPx += padding.bottom;
5054
5210
  if (split.after.length)
5055
5211
  split.after = split.after.map((box) => ({ ...box, yPx: box.yPx + padding.top }));
5056
5212
  return split;
5057
5213
  });
5058
5214
  const anyAfter = splits.some((split) => split.after.length > 0);
5215
+ const beforeHeightPx = Math.max(0, ...splits.map((split) => split.beforeHeightPx));
5059
5216
  const before = {
5060
- heightPx: Math.max(0, ...splits.map((split) => split.beforeHeightPx)),
5217
+ heightPx: beforeHeightPx,
5061
5218
  cells: splits.map((split) => split.before),
5219
+ ...row.cellTables ? {
5220
+ cellTables: row.cellTables.map(
5221
+ (tables, index) => clipTableFrames(
5222
+ tables,
5223
+ 0,
5224
+ splits[index].beforeHeightPx - (row.cellPadding?.[index]?.bottom ?? 0)
5225
+ )
5226
+ )
5227
+ } : {},
5062
5228
  geometry: row.geometry.map((geometry, index) => ({
5063
5229
  ...geometry,
5064
- contentHeightPx: splits[index].beforeHeightPx
5230
+ contentHeightPx: splits[index].beforeHeightPx,
5231
+ ...row.borderBottomPx !== void 0 ? { alignmentHeightPx: beforeHeightPx } : {}
5065
5232
  })),
5066
- ...row.cellPadding ? { cellPadding: row.cellPadding } : {}
5233
+ ...row.cellPadding ? { cellPadding: row.cellPadding } : {},
5234
+ ...row.borderBottomPx !== void 0 ? { borderBottomPx: row.borderBottomPx } : {}
5067
5235
  };
5068
5236
  if (!anyAfter) return { before, after: null };
5237
+ const afterHeightPx = Math.max(
5238
+ 0,
5239
+ ...splits.map(
5240
+ (split, index) => cellContentBottom(split.after, row.cellPadding?.[index]?.bottom ?? 0)
5241
+ )
5242
+ );
5069
5243
  const after = {
5070
- heightPx: Math.max(
5071
- 0,
5072
- ...splits.map(
5073
- (split, index) => split.after.length ? split.after.at(-1).yPx + split.after.at(-1).heightPx + (row.cellPadding?.[index]?.bottom ?? 0) : 0
5074
- )
5075
- ),
5244
+ heightPx: afterHeightPx,
5076
5245
  cells: splits.map((split) => split.after),
5246
+ ...row.cellTables ? {
5247
+ cellTables: row.cellTables.map(
5248
+ (tables, index) => splits[index].after.length ? clipTableFrames(
5249
+ tables,
5250
+ splits[index].afterOrigin ?? 0,
5251
+ row.heightPx,
5252
+ row.cellPadding?.[index]?.top ?? 0
5253
+ ) : []
5254
+ )
5255
+ } : {},
5077
5256
  geometry: row.geometry.map((geometry, index) => ({
5078
5257
  ...geometry,
5079
- contentHeightPx: splits[index].after.length ? splits[index].after.at(-1).yPx + splits[index].after.at(-1).heightPx + (row.cellPadding?.[index]?.bottom ?? 0) : 0
5258
+ contentHeightPx: cellContentBottom(
5259
+ splits[index].after,
5260
+ row.cellPadding?.[index]?.bottom ?? 0
5261
+ ),
5262
+ ...row.borderBottomPx !== void 0 ? { alignmentHeightPx: afterHeightPx } : {}
5080
5263
  })),
5081
- ...row.cellPadding ? { cellPadding: row.cellPadding } : {}
5264
+ ...row.cellPadding ? { cellPadding: row.cellPadding } : {},
5265
+ ...row.borderBottomPx !== void 0 ? { borderBottomPx: row.borderBottomPx } : {}
5082
5266
  };
5083
5267
  return { before, after };
5084
5268
  }
@@ -5139,11 +5323,11 @@ function splitNote(note, budget) {
5139
5323
  // docx/layout/footnote-columns.ts
5140
5324
  function footnoteColumns(page, columns) {
5141
5325
  const width = page.widthPx - page.marginLeftPx - page.marginRightPx;
5142
- const equal = (width - columns.gapPx * (columns.count - 1)) / columns.count;
5326
+ const equal2 = (width - columns.gapPx * (columns.count - 1)) / columns.count;
5143
5327
  let x = 0;
5144
5328
  return Array.from({ length: columns.count }, (_, index) => {
5145
5329
  const own = columns.widths?.[index];
5146
- const column = { xPx: x, widthPx: Math.max(1, own?.widthPx ?? equal) };
5330
+ const column = { xPx: x, widthPx: Math.max(1, own?.widthPx ?? equal2) };
5147
5331
  x += column.widthPx + (own?.gapPx ?? columns.gapPx);
5148
5332
  return column;
5149
5333
  });
@@ -5551,6 +5735,10 @@ var PageCursor = class {
5551
5735
  this.page.heightPx - this.page.marginTopPx - this.geometry.marginBottomPx - this.columnTopPx
5552
5736
  );
5553
5737
  }
5738
+ /** Number of independently balanced note columns, distinct from legacy body-column notes. */
5739
+ get footnoteColumnCount() {
5740
+ return this.legacyFootnoteColumns ? 1 : this.noteColumns?.count ?? 1;
5741
+ }
5554
5742
  remainingHeightPx() {
5555
5743
  return this.columnHeightPx - (this.yPx - this.columnTopPx) - this.footnotes.reserved(this.page, this.noteColumnIndex) - this.pendingHeightPx();
5556
5744
  }
@@ -5795,20 +5983,132 @@ function placeParagraph(cursor, placement, note) {
5795
5983
  }
5796
5984
  }
5797
5985
 
5986
+ // docx/layout/table-note-stories.ts
5987
+ function independentNoteStories(cell) {
5988
+ const first3 = cell.blocks?.[0];
5989
+ if (first3?.kind !== "table" || first3.rows.length !== 1 || cell.verticalAlign && cell.verticalAlign !== "top")
5990
+ return void 0;
5991
+ if (cell.blocks.slice(1).some((block) => block.kind !== "paragraph" || block.footnotes?.length))
5992
+ return void 0;
5993
+ const row = first3.rows[0];
5994
+ if (row.heightPx !== void 0 || row.cantSplit || row.isHeader || !nestedFrameProfile(first3, first3.widthPx))
5995
+ return void 0;
5996
+ if (row.cells.some(
5997
+ (inner) => inner.blocks?.some((block) => block.kind === "table") || cellParagraphs(inner).some(
5998
+ (paragraph) => paragraph.keepLines || paragraph.keepNext || paragraph.pageBreakBefore
5999
+ ) || inner.verticalAlign && inner.verticalAlign !== "top"
6000
+ ))
6001
+ return void 0;
6002
+ if (!measuredNoteReferences(row.cells.flatMap(cellParagraphs))) return void 0;
6003
+ const groups = /* @__PURE__ */ new Map();
6004
+ row.cells.forEach((inner, index) => {
6005
+ for (const paragraph of cellParagraphs(inner)) groups.set(paragraph.id, first3.id + ":" + index);
6006
+ });
6007
+ return groups.size ? groups : void 0;
6008
+ }
6009
+ function measuredNoteReferences(paragraphs) {
6010
+ const markers = paragraphs.flatMap(
6011
+ (paragraph) => paragraph.runs.flatMap(
6012
+ (run) => run.footnoteReferenceId === void 0 ? [] : [run.footnoteReferenceId]
6013
+ )
6014
+ );
6015
+ const ids = new Set(markers);
6016
+ const unanchored = paragraphs.flatMap(
6017
+ (paragraph) => (paragraph.footnotes ?? []).filter(
6018
+ (note) => !paragraph.runs.some((run) => run.footnoteReferenceId === note.id)
6019
+ )
6020
+ ).length;
6021
+ return markers.length + unanchored <= 3 && ids.size === markers.length && !(markers.length + unanchored > 1 && unanchored);
6022
+ }
6023
+
6024
+ // docx/layout/table-border-layout.ts
6025
+ function layoutBorderedRow(row, width, measurer, note, profile, remaining) {
6026
+ const reserved = profile !== void 0 && (profile.leftPx > 0 || profile.rightPx > 0 || profile.bottomPx > 0);
6027
+ const layout = layoutRow(
6028
+ row,
6029
+ width,
6030
+ measurer,
6031
+ note,
6032
+ remaining,
6033
+ void 0,
6034
+ reserved,
6035
+ profile?.leftPx
6036
+ );
6037
+ if (!reserved) return layout;
6038
+ const bottom = Math.max(0, ...layout.geometry.map((cell) => cell.borders?.bottom?.widthPx ?? 0));
6039
+ return {
6040
+ ...layout,
6041
+ heightPx: layout.heightPx + bottom,
6042
+ borderBottomPx: bottom,
6043
+ cellPadding: row.cells.map((cell) => ({
6044
+ top: (cell.padding?.top ?? 0) + (cell.borders?.top?.widthPx ?? 0),
6045
+ bottom: (cell.padding?.bottom ?? 0) + (cell.borders?.bottom?.widthPx ?? 0)
6046
+ }))
6047
+ };
6048
+ }
6049
+ var TableFragmentBorders = class {
6050
+ bottom = 0;
6051
+ included = true;
6052
+ record(bottom) {
6053
+ this.bottom = bottom ?? 0;
6054
+ this.included = true;
6055
+ }
6056
+ remove(row) {
6057
+ if (!row || !this.included) return 0;
6058
+ row.heightPx -= this.bottom;
6059
+ this.included = false;
6060
+ return -this.bottom;
6061
+ }
6062
+ restore(row) {
6063
+ if (!row || this.included) return 0;
6064
+ row.heightPx += this.bottom;
6065
+ this.included = true;
6066
+ return this.bottom;
6067
+ }
6068
+ };
6069
+
6070
+ // docx/layout/table-note-completion.ts
6071
+ function completedNoteStoryMinimum(row, stories, pendingBlocks, available) {
6072
+ let minimum = 0;
6073
+ for (const [index, cell] of row.cells.entries()) {
6074
+ const groups = /* @__PURE__ */ new Map();
6075
+ for (const box of cell) {
6076
+ const group = stories.get(box.blockId);
6077
+ if (group !== void 0) {
6078
+ const boxes = groups.get(group) ?? [];
6079
+ boxes.push(box);
6080
+ groups.set(group, boxes);
6081
+ }
6082
+ }
6083
+ for (const boxes of groups.values()) {
6084
+ if (boxes.reduce((count, box) => count + box.lines.length, 0) !== 1 || boxes.some((box) => pendingBlocks.has(box.blockId)))
6085
+ continue;
6086
+ const bottom = cellContentBottom(boxes, row.cellPadding?.[index]?.bottom ?? 0);
6087
+ if (bottom <= available + 1e-6) minimum = Math.max(minimum, bottom);
6088
+ }
6089
+ }
6090
+ return minimum;
6091
+ }
6092
+
5798
6093
  // docx/layout/table-footnotes.ts
5799
- function prefix(row, cut, cellCuts) {
6094
+ function prefix(row, cut, cellCuts, paragraphCuts) {
5800
6095
  const cells = row.cells.map((cell, index) => {
5801
- const contentCut = (cellCuts?.[index] ?? cut) - (row.cellPadding?.[index]?.bottom ?? 0);
6096
+ const padding = row.cellPadding?.[index]?.bottom ?? 0;
5802
6097
  const boxes = [];
5803
6098
  for (const box of cell) {
5804
- if (box.yPx >= contentCut + 1e-6) break;
5805
- if (box.yPx + box.heightPx <= contentCut + 1e-6) {
6099
+ const contentCut = (paragraphCuts?.get(box.blockId) ?? cellCuts?.[index] ?? cut) - padding;
6100
+ if (box.yPx >= contentCut + 1e-6) {
6101
+ if (!paragraphCuts?.size) break;
6102
+ continue;
6103
+ }
6104
+ if (box.yPx + box.heightPx + (box.tableFrameInsets?.bottom ?? 0) <= contentCut + 1e-6) {
5806
6105
  boxes.push(box);
5807
6106
  continue;
5808
6107
  }
5809
6108
  const lines = [];
5810
6109
  for (const line2 of box.lines) {
5811
- if (box.yPx + line2.yPx + line2.heightPx > contentCut + 1e-6) break;
6110
+ if (box.yPx + line2.yPx + line2.heightPx + (box.tableFrameInsets?.bottom ?? 0) > contentCut + 1e-6)
6111
+ break;
5812
6112
  lines.push(line2);
5813
6113
  }
5814
6114
  if (lines.length)
@@ -5828,9 +6128,7 @@ function prefix(row, cut, cellCuts) {
5828
6128
  cells,
5829
6129
  heightPx: Math.max(
5830
6130
  0,
5831
- ...cells.flatMap(
5832
- (cell, index) => cell.map((box) => box.yPx + box.heightPx + (row.cellPadding?.[index]?.bottom ?? 0))
5833
- )
6131
+ ...cells.map((cell, index) => cellContentBottom(cell, row.cellPadding?.[index]?.bottom ?? 0))
5834
6132
  )
5835
6133
  };
5836
6134
  }
@@ -5840,16 +6138,23 @@ var TableFootnotes = class {
5840
6138
  this.table = table;
5841
6139
  this.height = height;
5842
6140
  this.resolve = resolve;
5843
- for (const row of table.rows)
5844
- for (const cell of row.cells)
6141
+ for (const row of table.rows) {
6142
+ const measured = measuredNoteReferences(row.cells.flatMap(cellParagraphs));
6143
+ for (const cell of row.cells) {
5845
6144
  for (const paragraph of cellParagraphs(cell))
5846
6145
  if (paragraph.footnotes?.length) this.paragraphs.set(paragraph.id, paragraph);
6146
+ for (const [id, group] of (measured ? independentNoteStories(cell) : void 0) ?? []) {
6147
+ this.stories.set(id, group);
6148
+ }
6149
+ }
6150
+ }
5847
6151
  }
5848
6152
  cursor;
5849
6153
  table;
5850
6154
  height;
5851
6155
  resolve;
5852
6156
  paragraphs = /* @__PURE__ */ new Map();
6157
+ stories = /* @__PURE__ */ new Map();
5853
6158
  placed = /* @__PURE__ */ new Set();
5854
6159
  fragment = /* @__PURE__ */ new Map();
5855
6160
  references(row) {
@@ -5871,14 +6176,30 @@ var TableFootnotes = class {
5871
6176
  ) ?? box.lines[0];
5872
6177
  const bottom = Math.min(
5873
6178
  row.heightPx,
5874
- box.yPx + (line2?.yPx ?? 0) + (line2?.heightPx ?? box.heightPx)
6179
+ box.yPx + (line2?.yPx ?? 0) + (line2?.heightPx ?? box.heightPx) + (box.tableFrameInsets?.bottom ?? 0)
5875
6180
  );
5876
6181
  const prior = refs.get(note.id);
5877
- if (!prior || bottom < prior.bottom) refs.set(note.id, { note, bottom });
6182
+ if (!prior || bottom < prior.bottom)
6183
+ refs.set(note.id, {
6184
+ note,
6185
+ bottom,
6186
+ blockId: box.blockId,
6187
+ before: box.yPx + (line2?.yPx ?? 0) + (box.tableFrameInsets?.bottom ?? 0)
6188
+ });
5878
6189
  }
5879
6190
  }
5880
6191
  return [...refs.values()];
5881
6192
  }
6193
+ /** A consumed marker no longer constrains its story. Deferred markers stop just before
6194
+ * their own line, allowing another pending story to use a different complete prefix. */
6195
+ storyCut(pending, boxes, padding, cut, space, included) {
6196
+ const ids = new Set(boxes.map((box) => box.blockId));
6197
+ const refs = pending.filter((ref) => ids.has(ref.blockId));
6198
+ let limit = refs.some((ref) => included.has(ref.note.id)) ? Math.min(cut, space) : space;
6199
+ for (const ref of refs)
6200
+ if (!included.has(ref.note.id)) limit = Math.min(limit, ref.before + padding);
6201
+ return limit;
6202
+ }
5882
6203
  hold(refs, minimumBody = 0) {
5883
6204
  const referenceBottom = Math.max(0, ...[...refs.values()].map((ref) => ref.bottom));
5884
6205
  const bodyHeight = Math.max(referenceBottom, minimumBody);
@@ -5889,17 +6210,20 @@ var TableFootnotes = class {
5889
6210
  this.cursor.usedColumnHeightPx + referenceBottom
5890
6211
  );
5891
6212
  }
5892
- holdCandidate(row) {
6213
+ holdCandidate(row, completionMinimum = 0) {
5893
6214
  const refs = new Map(this.fragment);
5894
6215
  for (const ref of this.references(row))
5895
6216
  if (!refs.has(ref.note.id))
5896
6217
  refs.set(ref.note.id, { ...ref, bottom: this.height() + ref.bottom });
5897
6218
  const referenceBottom = Math.max(0, ...[...refs.values()].map((ref) => ref.bottom));
5898
- const contentBottom = this.height() + Math.max(0, ...row.cells.flatMap((cell) => cell.map((box) => box.yPx + box.heightPx)));
6219
+ const contentBottom = this.height() + Math.max(0, ...row.cells.map((cell) => cellContentBottom(cell)));
5899
6220
  const paddingBottom = Math.max(0, ...(row.cellPadding ?? []).map((padding) => padding.bottom));
5900
6221
  this.hold(
5901
6222
  refs,
5902
- contentBottom <= referenceBottom + 1e-6 ? this.height() + row.heightPx : referenceBottom + paddingBottom
6223
+ Math.max(
6224
+ completionMinimum,
6225
+ contentBottom <= referenceBottom + 1e-6 ? this.height() + row.heightPx : referenceBottom + paddingBottom
6226
+ )
5903
6227
  );
5904
6228
  }
5905
6229
  /** Largest complete prefix that keeps each new marker with available note content. */
@@ -5912,29 +6236,76 @@ var TableFootnotes = class {
5912
6236
  return { layout: row, cut: row.heightPx };
5913
6237
  }
5914
6238
  if (!atomic) {
6239
+ const independent = row.cells.some(
6240
+ (cell) => cell.some((box) => this.stories.has(box.blockId))
6241
+ );
6242
+ const pending = independent ? this.references(row) : [];
6243
+ const completing = (this.cursor.footnoteColumnCount === 2 || this.cursor.footnoteColumnCount === 3) && pending.length === 1 && !this.stories.has(pending[0].blockId) ? completedNoteStoryMinimum(
6244
+ row,
6245
+ this.stories,
6246
+ new Set(pending.map((ref) => ref.blockId)),
6247
+ available
6248
+ ) : 0;
5915
6249
  const cuts = /* @__PURE__ */ new Set();
5916
6250
  for (const [index, cell] of row.cells.entries())
5917
6251
  for (const box of cell) {
5918
6252
  if (box.yPx > available + 1e-6) break;
5919
6253
  for (const line2 of box.lines) {
5920
- const bottom = box.yPx + line2.yPx + line2.heightPx + (row.cellPadding?.[index]?.bottom ?? 0);
6254
+ const bottom = box.yPx + line2.yPx + line2.heightPx + (box.tableFrameInsets?.bottom ?? 0) + (row.cellPadding?.[index]?.bottom ?? 0);
5921
6255
  if (bottom > available + 1e-6) break;
5922
6256
  if (bottom > 0) cuts.add(bottom);
5923
6257
  }
5924
6258
  }
5925
6259
  for (const cut of [...cuts].sort((a, b) => b - a)) {
5926
6260
  const candidate = prefix(row, cut);
5927
- this.holdCandidate(candidate);
6261
+ const included = new Set(this.references(candidate).map((ref) => ref.note.id));
6262
+ const completion = completing > 0 && included.has(pending[0]?.note.id ?? "") ? this.height() + completing : 0;
6263
+ this.holdCandidate(candidate, completion);
5928
6264
  if (candidate.heightPx <= this.cursor.remainingHeightPx() - this.height() + 1e-6) {
5929
6265
  const space = this.cursor.remainingHeightPx() - this.height();
5930
6266
  const cellCuts = row.cells.map(
5931
- (cell) => cell.some((box) => this.paragraphs.has(box.blockId)) ? cut : space
6267
+ (cell, index) => independent ? this.storyCut(
6268
+ pending,
6269
+ cell,
6270
+ row.cellPadding?.[index]?.bottom ?? 0,
6271
+ cut,
6272
+ space,
6273
+ included
6274
+ ) : cell.some((box) => this.paragraphs.has(box.blockId)) ? cut : space
5932
6275
  );
5933
- const expanded = prefix(row, cut, cellCuts);
5934
- this.holdCandidate(expanded);
6276
+ const paragraphCuts = /* @__PURE__ */ new Map();
6277
+ for (const [index, cell] of row.cells.entries()) {
6278
+ const groups = /* @__PURE__ */ new Map();
6279
+ for (const box of cell) {
6280
+ const group = this.stories.get(box.blockId);
6281
+ if (group !== void 0) {
6282
+ const boxes = groups.get(group) ?? [];
6283
+ boxes.push(box);
6284
+ groups.set(group, boxes);
6285
+ }
6286
+ }
6287
+ for (const boxes of groups.values()) {
6288
+ const limit = this.storyCut(
6289
+ pending,
6290
+ boxes,
6291
+ row.cellPadding?.[index]?.bottom ?? 0,
6292
+ cut,
6293
+ space,
6294
+ included
6295
+ );
6296
+ for (const box of boxes) paragraphCuts.set(box.blockId, limit);
6297
+ }
6298
+ }
6299
+ const expanded = prefix(row, cut, cellCuts, paragraphCuts);
6300
+ this.holdCandidate(expanded, completion);
5935
6301
  if (expanded.heightPx <= this.cursor.remainingHeightPx() - this.height() + 1e-6)
5936
- return { layout: expanded, cut, cellCuts };
5937
- this.holdCandidate(candidate);
6302
+ return {
6303
+ layout: expanded,
6304
+ cut,
6305
+ cellCuts,
6306
+ ...paragraphCuts.size ? { paragraphCuts } : {}
6307
+ };
6308
+ this.holdCandidate(candidate, completion);
5938
6309
  return { layout: candidate, cut };
5939
6310
  }
5940
6311
  }
@@ -5962,25 +6333,44 @@ function placeTable(cursor, table, measurer, note, resolveFootnotes) {
5962
6333
  const firstBodyRow = table.rows.findIndex((row) => !row.isHeader);
5963
6334
  const headerRows = table.rows.slice(0, firstBodyRow < 0 ? table.rows.length : firstBodyRow);
5964
6335
  const xPosition = () => {
5965
- const width = table.widthPx ?? cursor.columnWidthPx;
6336
+ const width = (table.widthPx ?? cursor.columnWidthPx) + (activeBorders ? (profile.leftPx + profile.rightPx) / 2 : 0);
5966
6337
  return table.alignment === "center" ? Math.max(0, (cursor.columnWidthPx - width) / 2) : table.alignment === "right" ? Math.max(0, cursor.columnWidthPx - width) : table.indentPx ?? 0;
5967
6338
  };
6339
+ const profile = nestedFrameProfile(table, table.widthPx ?? cursor.columnWidthPx);
6340
+ const activeBorders = profile !== void 0 && (profile.leftPx > 0 || profile.rightPx > 0 || profile.bottomPx > 0);
6341
+ if (!profile && table.rows.some((row) => row.cells.some((cell) => cell.borders)))
6342
+ note(
6343
+ "Ordinary table border occupancy remains approximate for missing edges, non-solid or conflicting shared borders and changing row grids."
6344
+ );
6345
+ const layoutRow2 = (row, width, m, n2, remaining) => layoutBorderedRow(
6346
+ row,
6347
+ activeBorders ? table.widthPx ?? width : width,
6348
+ m,
6349
+ n2,
6350
+ profile,
6351
+ remaining
6352
+ );
6353
+ const fragmentBorders = new TableFragmentBorders();
5968
6354
  let fragmentRows = [];
5969
6355
  let fragmentHeight = 0;
5970
6356
  const footnotes = resolveFootnotes ? new TableFootnotes(cursor, table, () => fragmentHeight, resolveFootnotes) : void 0;
5971
6357
  const toRowBox = (layout, repeated) => {
6358
+ fragmentHeight += fragmentBorders.remove(fragmentRows.at(-1));
5972
6359
  footnotes?.add(layout, repeated);
5973
6360
  const box = {
5974
6361
  yPx: fragmentHeight,
5975
6362
  heightPx: layout.heightPx,
5976
6363
  repeated,
5977
6364
  cells: layout.cells,
5978
- geometry: layout.geometry
6365
+ geometry: layout.geometry,
6366
+ ...layout.cellTables ? { cellTables: layout.cellTables } : {}
5979
6367
  };
5980
6368
  fragmentHeight += layout.heightPx;
6369
+ fragmentBorders.record(layout.borderBottomPx);
5981
6370
  return box;
5982
6371
  };
5983
6372
  function flushFragment() {
6373
+ fragmentHeight += fragmentBorders.restore(fragmentRows.at(-1));
5984
6374
  if (!fragmentRows.length) {
5985
6375
  footnotes?.holdFragment();
5986
6376
  return;
@@ -5988,6 +6378,9 @@ function placeTable(cursor, table, measurer, note, resolveFootnotes) {
5988
6378
  const box = {
5989
6379
  kind: "table",
5990
6380
  blockId: table.id,
6381
+ ...activeBorders ? {
6382
+ widthPx: (table.widthPx ?? cursor.columnWidthPx) + (profile.leftPx + profile.rightPx) / 2
6383
+ } : {},
5991
6384
  ...xPosition() ? { xPx: xPosition() } : {},
5992
6385
  yPx: 0,
5993
6386
  heightPx: fragmentHeight,
@@ -6006,15 +6399,16 @@ function placeTable(cursor, table, measurer, note, resolveFootnotes) {
6006
6399
  }
6007
6400
  function addHeaders() {
6008
6401
  for (const header of headerRows)
6009
- fragmentRows.push(toRowBox(layoutRow(header, cursor.columnWidthPx, measurer, note), true));
6402
+ fragmentRows.push(toRowBox(layoutRow2(header, cursor.columnWidthPx, measurer, note), true));
6010
6403
  }
6011
6404
  function placeRow(row, initial, cantSplit) {
6012
6405
  let remainingRow = initial;
6013
6406
  while (remainingRow) {
6407
+ fragmentHeight += fragmentBorders.remove(fragmentRows.at(-1));
6014
6408
  const minimum = cantSplit ? Math.min(remainingRow.heightPx, cursor.columnHeightPx) : Math.min(
6015
6409
  ...remainingRow.cells.flatMap(
6016
- (cell) => cell.map(
6017
- (paragraph) => paragraph.yPx + (paragraph.lines[0]?.yPx ?? 0) + (paragraph.lines[0]?.heightPx ?? 0)
6410
+ (cell, index) => cell.map(
6411
+ (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)
6018
6412
  )
6019
6413
  )
6020
6414
  );
@@ -6022,7 +6416,7 @@ function placeTable(cursor, table, measurer, note, resolveFootnotes) {
6022
6416
  const width = cursor.columnWidthPx;
6023
6417
  cursor.advancePastFootnotes(fragmentHeight + (Number.isFinite(minimum) ? minimum : 0));
6024
6418
  if (width !== cursor.columnWidthPx) {
6025
- remainingRow = layoutRow(
6419
+ remainingRow = layoutRow2(
6026
6420
  remainingRow === initial ? row : { ...row, heightPx: 0, heightRule: "atLeast" },
6027
6421
  cursor.columnWidthPx,
6028
6422
  measurer,
@@ -6048,12 +6442,13 @@ function placeTable(cursor, table, measurer, note, resolveFootnotes) {
6048
6442
  const { before: before2, after: after2 } = splitRowAtHeight(
6049
6443
  remainingRow,
6050
6444
  selected.cut + 1e-6,
6051
- selected.cellCuts?.map((cut) => cut + 1e-6)
6445
+ selected.cellCuts?.map((cut) => cut + 1e-6),
6446
+ selected.paragraphCuts ? new Map([...selected.paragraphCuts].map(([id, cut]) => [id, cut + 1e-6])) : void 0
6052
6447
  );
6053
6448
  fragmentRows.push(toRowBox(before2, false));
6054
6449
  const previousWidth2 = cursor.columnWidthPx;
6055
6450
  startNewFragment();
6056
- remainingRow = after2 && previousWidth2 !== cursor.columnWidthPx ? layoutRow(
6451
+ remainingRow = after2 && (selected.paragraphCuts || previousWidth2 !== cursor.columnWidthPx) ? layoutRow2(
6057
6452
  { ...row, heightPx: 0, heightRule: "atLeast" },
6058
6453
  cursor.columnWidthPx,
6059
6454
  measurer,
@@ -6066,7 +6461,7 @@ function placeTable(cursor, table, measurer, note, resolveFootnotes) {
6066
6461
  const previousWidth2 = cursor.columnWidthPx;
6067
6462
  startNewFragment();
6068
6463
  if (previousWidth2 !== cursor.columnWidthPx)
6069
- remainingRow = layoutRow(
6464
+ remainingRow = layoutRow2(
6070
6465
  remainingRow === initial ? row : { ...row, heightPx: 0, heightRule: "atLeast" },
6071
6466
  cursor.columnWidthPx,
6072
6467
  measurer,
@@ -6085,14 +6480,14 @@ function placeTable(cursor, table, measurer, note, resolveFootnotes) {
6085
6480
  if (cantSplit) {
6086
6481
  if (fragmentRows.length || !cursor.atColumnTop) {
6087
6482
  startNewFragment();
6088
- remainingRow = layoutRow(row, cursor.columnWidthPx, measurer, note);
6483
+ remainingRow = layoutRow2(row, cursor.columnWidthPx, measurer, note);
6089
6484
  }
6090
6485
  const noteWidth = cursor.columnWidthPx;
6091
6486
  cursor.advancePastFootnotes(
6092
6487
  fragmentHeight + Math.min(remainingRow.heightPx, cursor.columnHeightPx)
6093
6488
  );
6094
6489
  if (noteWidth !== cursor.columnWidthPx) {
6095
- remainingRow = layoutRow(row, cursor.columnWidthPx, measurer, note);
6490
+ remainingRow = layoutRow2(row, cursor.columnWidthPx, measurer, note);
6096
6491
  if (fragmentRows.length) {
6097
6492
  fragmentRows = [];
6098
6493
  fragmentHeight = 0;
@@ -6118,7 +6513,7 @@ function placeTable(cursor, table, measurer, note, resolveFootnotes) {
6118
6513
  const previousWidth = cursor.columnWidthPx;
6119
6514
  if (before.cells.some((cell) => cell.length)) fragmentRows.push(toRowBox(before, false));
6120
6515
  startNewFragment();
6121
- remainingRow = after && previousWidth !== cursor.columnWidthPx ? layoutRow(
6516
+ remainingRow = after && previousWidth !== cursor.columnWidthPx ? layoutRow2(
6122
6517
  { ...row, heightPx: 0, heightRule: "atLeast" },
6123
6518
  cursor.columnWidthPx,
6124
6519
  measurer,
@@ -6128,7 +6523,13 @@ function placeTable(cursor, table, measurer, note, resolveFootnotes) {
6128
6523
  }
6129
6524
  }
6130
6525
  for (const row of table.rows) {
6131
- const layout = layoutRow(row, cursor.columnWidthPx, measurer, note);
6526
+ const layout = layoutRow2(row, cursor.columnWidthPx, measurer, note);
6527
+ if (activeBorders && layout.heightPx > cursor.remainingHeightPx() && row.cells.some(
6528
+ (cell) => (!measuredNoteReferences(row.cells.flatMap(cellParagraphs)) || !independentNoteStories(cell)) && cell.blocks?.some((block) => block.kind === "table") && cellParagraphs(cell).some((p) => p.footnotes?.length)
6529
+ ))
6530
+ note(
6531
+ "Independent inner-cell continuation around nested note references remains approximate when an enclosing bordered row splits."
6532
+ );
6132
6533
  placeRow(
6133
6534
  row,
6134
6535
  layout,
@@ -6639,13 +7040,13 @@ function cssHex(value) {
6639
7040
  const hex = value?.replace(/^#/, "");
6640
7041
  return hex && /^[0-9a-f]{6}$/i.test(hex) ? `#${hex}` : void 0;
6641
7042
  }
6642
- function border(side, theme) {
7043
+ function border(side, theme, preserveSingleWidth = false) {
6643
7044
  if (!side?.style || side.style === "none" || side.style === "nil") return void 0;
6644
7045
  const style = side.style.startsWith("double") ? "double" : /dot/i.test(side.style) ? "dotted" : /dash/i.test(side.style) ? "dashed" : "solid";
6645
7046
  const themed = side.themeColor && theme ? resolveThemeColorReference({ token: side.themeColor }, theme) : void 0;
6646
7047
  const hex = cssHex(side.color);
6647
7048
  const widthPx = Math.max(
6648
- style === "double" ? 3 : 1,
7049
+ style === "double" ? 3 : preserveSingleWidth && style === "solid" ? 0 : 1,
6649
7050
  (side.sizeEighthPoints ?? 4) / 8 * 96 / 72
6650
7051
  );
6651
7052
  return { widthPx, style, color: themed ?? hex ?? "#000000" };
@@ -6658,7 +7059,7 @@ function rowLayout(props) {
6658
7059
  ...props.header ? { isHeader: true } : {}
6659
7060
  };
6660
7061
  }
6661
- function adaptTable(table, model, adaptParagraph) {
7062
+ function adaptTable(table, model, adaptParagraph, _nested = false) {
6662
7063
  const grid = table.grid?.map(twipsToPx2);
6663
7064
  const columnCount = grid?.length ?? Math.max(1, ...table.rows.map((row) => row.length));
6664
7065
  const gridWidth = (start, span) => grid ? grid.slice(start, start + span).reduce((sum, width) => sum + width, 0) : void 0;
@@ -6700,7 +7101,7 @@ function adaptTable(table, model, adaptParagraph) {
6700
7101
  );
6701
7102
  const borders = {};
6702
7103
  for (const side of ["top", "right", "bottom", "left"]) {
6703
- const resolved = border(visuals.borders?.[side], model.theme);
7104
+ const resolved = border(visuals.borders?.[side], model.theme, true);
6704
7105
  if (resolved) borders[side] = resolved;
6705
7106
  }
6706
7107
  if (cell.verticalMerge === "continue") delete borders.top;
@@ -6719,7 +7120,7 @@ function adaptTable(table, model, adaptParagraph) {
6719
7120
  paragraphs: cell.paragraphs.map(adaptParagraph),
6720
7121
  ...cell.sourceBlocks ? {
6721
7122
  blocks: cellSourceBlocks(cell).map(
6722
- (block) => block.type === "paragraph" ? adaptParagraph(block) : adaptTable(block, model, adaptParagraph)
7123
+ (block) => block.type === "paragraph" ? adaptParagraph(block) : adaptTable(block, model, adaptParagraph, true)
6723
7124
  )
6724
7125
  } : {},
6725
7126
  ...width !== void 0 ? { widthPx: width } : {},
@@ -7019,7 +7420,7 @@ function adaptDocumentModel(model, note = () => {
7019
7420
  reportOnce(VERTICAL_MERGE_NOTE);
7020
7421
  if (block.rows.some((row) => row.some((cell) => cell.nestedTables?.length)))
7021
7422
  reportOnce(
7022
- "Nested tables are projected as read-only cell paragraphs in Print Layout; their borders, shading, vertical merge and repeated header geometry are not rendered."
7423
+ "Nested tables are projected as read-only cell paragraphs in Print Layout; vertical merge and repeated header geometry are not rendered."
7023
7424
  );
7024
7425
  return adaptTable(block, model, adaptParagraph);
7025
7426
  }
@@ -7296,6 +7697,7 @@ exports.SINGLE_LINE_SPACING_UNITS = SINGLE_LINE_SPACING_UNITS;
7296
7697
  exports.adaptDocumentModel = adaptDocumentModel;
7297
7698
  exports.adjustForWidowOrphan = adjustForWidowOrphan;
7298
7699
  exports.appendBreakMarker = appendBreakMarker;
7700
+ exports.cellVerticalOffset = cellVerticalOffset;
7299
7701
  exports.createFakeMeasurer = createFakeMeasurer;
7300
7702
  exports.cssFontStack = cssFontStack;
7301
7703
  exports.fieldDisplayText = fieldDisplayText;