@stll/folio-core 0.48.0 → 0.50.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 (170) hide show
  1. package/dist/ai-edits/apply.js +565 -121
  2. package/dist/ai-edits/headless.d.ts +37 -1
  3. package/dist/ai-edits/headless.js +47 -16
  4. package/dist/ai-edits/minimal-replacement.d.ts +64 -0
  5. package/dist/ai-edits/minimal-replacement.js +282 -0
  6. package/dist/controller/layoutPipeline.js +11 -1
  7. package/dist/display-list/build/paragraphPrimitives.js +63 -7
  8. package/dist/display-list/build/textBoxPrimitives.js +1 -1
  9. package/dist/display-list/build/unsupported.d.ts +2 -2
  10. package/dist/display-list/build/unsupported.js +0 -0
  11. package/dist/display-list/dom/renderDisplayListToDom.d.ts +10 -1
  12. package/dist/display-list/dom/renderDisplayListToDom.js +8 -8
  13. package/dist/display-list/html/renderDisplayListToHtml.d.ts +21 -0
  14. package/dist/display-list/html/renderDisplayListToHtml.js +126 -0
  15. package/dist/display-list/selectDisplayPages.d.ts +6 -0
  16. package/dist/display-list/selectDisplayPages.js +44 -0
  17. package/dist/docx/blockContentParser.js +2 -2
  18. package/dist/docx/bulletMarkers.d.ts +18 -2
  19. package/dist/docx/bulletMarkers.js +27 -2
  20. package/dist/docx/drawingGroupChildren.d.ts +60 -0
  21. package/dist/docx/drawingGroupChildren.js +176 -0
  22. package/dist/docx/drawingUtils.js +3 -0
  23. package/dist/docx/groupDrawingParser.d.ts +7 -1
  24. package/dist/docx/groupDrawingParser.js +123 -32
  25. package/dist/docx/packageParts.d.ts +45 -1
  26. package/dist/docx/packageParts.js +93 -2
  27. package/dist/docx/paraIdAttribute.d.ts +3 -2
  28. package/dist/docx/paragraphParser.js +91 -7
  29. package/dist/docx/paragraphTextBoxEnrichment.js +126 -4
  30. package/dist/docx/runParser.js +10 -0
  31. package/dist/docx/selectiveSaveFlags.d.ts +10 -3
  32. package/dist/docx/selectiveSaveFlags.js +1 -1
  33. package/dist/docx/selectiveXmlPatch.d.ts +56 -34
  34. package/dist/docx/selectiveXmlPatch.js +222 -115
  35. package/dist/docx/serializer/groupTextBoxWriteBack.d.ts +12 -0
  36. package/dist/docx/serializer/groupTextBoxWriteBack.js +101 -0
  37. package/dist/docx/serializer/paragraphSerializer.js +5 -4
  38. package/dist/docx/serializer/runSerializer.d.ts +5 -1
  39. package/dist/docx/serializer/runSerializer.js +4 -3
  40. package/dist/docx/shapeAlternateContent.d.ts +25 -0
  41. package/dist/docx/shapeAlternateContent.js +52 -0
  42. package/dist/docx/textBoxParser.d.ts +10 -2
  43. package/dist/docx/textBoxParser.js +9 -3
  44. package/dist/export-pdf.d.ts +35 -2
  45. package/dist/export-pdf.js +51 -20
  46. package/dist/fonts/fontsourceFaces.d.ts +47 -0
  47. package/dist/fonts/fontsourceFaces.js +230 -0
  48. package/dist/internal/headlessRevisionResolution.js +50 -13
  49. package/dist/layout-bridge/convert/flowBlockPostprocessing.d.ts +8 -0
  50. package/dist/layout-bridge/convert/flowBlockPostprocessing.js +106 -0
  51. package/dist/layout-bridge/convert/flowBorders.d.ts +17 -0
  52. package/dist/layout-bridge/convert/flowBorders.js +31 -0
  53. package/dist/layout-bridge/convert/flowConversionShared.d.ts +111 -0
  54. package/dist/layout-bridge/convert/flowConversionShared.js +23 -0
  55. package/dist/layout-bridge/convert/footnoteLayout.d.ts +2 -1
  56. package/dist/layout-bridge/convert/footnoteLayout.js +2 -1
  57. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +2 -1
  58. package/dist/layout-bridge/convert/imageConversion.d.ts +33 -0
  59. package/dist/layout-bridge/convert/imageConversion.js +118 -0
  60. package/dist/layout-bridge/convert/listMarkers.d.ts +28 -0
  61. package/dist/layout-bridge/convert/listMarkers.js +98 -0
  62. package/dist/layout-bridge/convert/noteReferences.d.ts +14 -0
  63. package/dist/layout-bridge/convert/noteReferences.js +29 -0
  64. package/dist/layout-bridge/convert/pageBreakSplitting.d.ts +16 -0
  65. package/dist/layout-bridge/convert/pageBreakSplitting.js +140 -0
  66. package/dist/layout-bridge/convert/paragraphAttrs.d.ts +20 -0
  67. package/dist/layout-bridge/convert/paragraphAttrs.js +234 -0
  68. package/dist/layout-bridge/convert/paragraphConversion.d.ts +12 -0
  69. package/dist/layout-bridge/convert/paragraphConversion.js +112 -0
  70. package/dist/layout-bridge/convert/paragraphRuns.d.ts +15 -0
  71. package/dist/layout-bridge/convert/paragraphRuns.js +281 -0
  72. package/dist/layout-bridge/convert/runFormattingMerge.d.ts +19 -0
  73. package/dist/layout-bridge/convert/runFormattingMerge.js +167 -0
  74. package/dist/layout-bridge/convert/runMarkFormatting.d.ts +11 -0
  75. package/dist/layout-bridge/convert/runMarkFormatting.js +291 -0
  76. package/dist/layout-bridge/convert/sectionBoundaries.d.ts +35 -0
  77. package/dist/layout-bridge/convert/sectionBoundaries.js +114 -0
  78. package/dist/layout-bridge/convert/tableConversion.d.ts +12 -0
  79. package/dist/layout-bridge/convert/tableConversion.js +303 -0
  80. package/dist/layout-bridge/convert/textBoxFill.d.ts +20 -0
  81. package/dist/layout-bridge/convert/textBoxFill.js +42 -0
  82. package/dist/layout-bridge/convert/textFormattingConversion.d.ts +22 -0
  83. package/dist/layout-bridge/convert/textFormattingConversion.js +120 -0
  84. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +5 -113
  85. package/dist/layout-bridge/convert/toFlowBlocks.js +11 -2060
  86. package/dist/layout-bridge/engine/hitTest.js +1 -1
  87. package/dist/layout-bridge/engine/measuring/index.d.ts +2 -1
  88. package/dist/layout-bridge/engine/measuring/index.js +2 -1
  89. package/dist/layout-bridge/engine/selectionRects.js +1 -1
  90. package/dist/layout-engine/imageLayout.d.ts +9 -0
  91. package/dist/layout-engine/imageLayout.js +48 -0
  92. package/dist/layout-engine/index.d.ts +4 -19
  93. package/dist/layout-engine/index.js +14 -910
  94. package/dist/layout-engine/keep-together.d.ts +5 -2
  95. package/dist/layout-engine/keep-together.js +5 -1
  96. package/dist/layout-engine/layoutFlowShared.d.ts +6 -0
  97. package/dist/layout-engine/layoutFlowShared.js +8 -0
  98. package/dist/layout-engine/measure/advanceComposition.js +49 -3
  99. package/dist/layout-engine/measure/crossRunWords.d.ts +45 -0
  100. package/dist/layout-engine/measure/crossRunWords.js +172 -0
  101. package/dist/layout-engine/measure/floatingLineClearance.d.ts +24 -0
  102. package/dist/layout-engine/measure/floatingLineClearance.js +44 -0
  103. package/dist/layout-engine/measure/inlineObjectMetrics.d.ts +6 -0
  104. package/dist/layout-engine/measure/inlineObjectMetrics.js +52 -0
  105. package/dist/layout-engine/measure/justification.d.ts +32 -0
  106. package/dist/layout-engine/measure/justification.js +83 -0
  107. package/dist/layout-engine/measure/lineFitting.d.ts +48 -0
  108. package/dist/layout-engine/measure/lineFitting.js +145 -0
  109. package/dist/layout-engine/measure/lineTypography.d.ts +44 -0
  110. package/dist/layout-engine/measure/lineTypography.js +124 -0
  111. package/dist/layout-engine/measure/measureBlocks.js +7 -4
  112. package/dist/layout-engine/measure/measureContainer.js +46 -3
  113. package/dist/layout-engine/measure/measureHelpers.js +13 -3
  114. package/dist/layout-engine/measure/measureParagraph.d.ts +1 -20
  115. package/dist/layout-engine/measure/measureParagraph.js +41 -780
  116. package/dist/layout-engine/measure/paragraphMeasureShared.d.ts +109 -0
  117. package/dist/layout-engine/measure/paragraphMeasureShared.js +82 -0
  118. package/dist/layout-engine/measure/paragraphTabs.d.ts +18 -0
  119. package/dist/layout-engine/measure/paragraphTabs.js +78 -0
  120. package/dist/layout-engine/measure/smallCapsCasing.d.ts +56 -0
  121. package/dist/layout-engine/measure/smallCapsCasing.js +88 -0
  122. package/dist/layout-engine/paginator.d.ts +2 -0
  123. package/dist/layout-engine/paginator.js +5 -1
  124. package/dist/layout-engine/paragraphLayout.d.ts +23 -0
  125. package/dist/layout-engine/paragraphLayout.js +204 -0
  126. package/dist/layout-engine/paragraphSpacing.d.ts +4 -2
  127. package/dist/layout-engine/paragraphSpacing.js +2 -1
  128. package/dist/layout-engine/sectionLayout.d.ts +45 -0
  129. package/dist/layout-engine/sectionLayout.js +216 -0
  130. package/dist/layout-engine/tableLayout.d.ts +17 -0
  131. package/dist/layout-engine/tableLayout.js +369 -0
  132. package/dist/layout-engine/tableRowBreak.d.ts +8 -1
  133. package/dist/layout-engine/tableRowBreak.js +22 -1
  134. package/dist/layout-engine/textBoxLayout.d.ts +15 -0
  135. package/dist/layout-engine/textBoxLayout.js +137 -0
  136. package/dist/layout-engine/types.d.ts +21 -2
  137. package/dist/layout-painter/renderPage.js +1 -0
  138. package/dist/layout-painter/renderParagraph.js +41 -4
  139. package/dist/layout-painter/renderTextBox.d.ts +12 -2
  140. package/dist/layout-painter/renderTextBox.js +26 -2
  141. package/dist/paged-layout/sectionBlockWidths.d.ts +2 -1
  142. package/dist/paged-layout/sectionBlockWidths.js +1 -1
  143. package/dist/prosemirror/alternateContentAttrs.d.ts +9 -0
  144. package/dist/prosemirror/alternateContentAttrs.js +35 -0
  145. package/dist/prosemirror/attrs/index.js +90 -0
  146. package/dist/prosemirror/commands/comments.js +51 -41
  147. package/dist/prosemirror/contentControlRevisions.d.ts +43 -0
  148. package/dist/prosemirror/contentControlRevisions.js +84 -0
  149. package/dist/prosemirror/conversion/fromProseDoc.js +46 -22
  150. package/dist/prosemirror/conversion/toProseDoc.js +42 -19
  151. package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.d.ts +7 -7
  152. package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +7 -7
  153. package/dist/prosemirror/extensions/nodes/FieldExtension.js +6 -3
  154. package/dist/prosemirror/extensions/nodes/SdtExtension.js +9 -2
  155. package/dist/prosemirror/extensions/nodes/ShapeExtension.js +1 -0
  156. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +3 -0
  157. package/dist/prosemirror/listMarker.js +2 -2
  158. package/dist/prosemirror/paragraphDirection.d.ts +29 -2
  159. package/dist/prosemirror/paragraphDirection.js +21 -2
  160. package/dist/prosemirror/plugins/suggestionMode.js +37 -8
  161. package/dist/prosemirror/rejoinRunCarriers.d.ts +27 -0
  162. package/dist/prosemirror/rejoinRunCarriers.js +66 -0
  163. package/dist/prosemirror/runIdentityAcrossRevisions.d.ts +10 -0
  164. package/dist/prosemirror/runIdentityAcrossRevisions.js +90 -0
  165. package/dist/prosemirror/schema/nodes.d.ts +37 -5
  166. package/dist/server.d.ts +3 -2
  167. package/dist/types/content.d.ts +2 -2
  168. package/package.json +2 -2
  169. package/dist/layout-engine/justifiedLineFit.d.ts +0 -7
  170. package/dist/layout-engine/justifiedLineFit.js +0 -6
@@ -0,0 +1,369 @@
1
+ import { projectedFootnoteReserveGrowth } from "./layoutFlowShared.js";
2
+ import { resolveFloatingTablePageX } from "./measure/floatingTablePosition.js";
3
+ import { resolveTableInlineOffset } from "./measure/tableInlinePlacement.js";
4
+ import { isEmptyParagraph } from "./paragraphSpacing.js";
5
+ import { buildTableRowBreakInfo, getRowContinuationSkip, snapRowBreak } from "./tableRowBreak.js";
6
+ import "./types.js";
7
+ //#region src/layout-engine/tableLayout.ts
8
+ /**
9
+ * Places measured tables onto pages: row splitting across pages, repeated
10
+ * header rows, rendered-break and tracked-change row handling, row footnote
11
+ * collection, and floating table placement.
12
+ */
13
+ /**
14
+ * Count consecutive header rows at the start of a table.
15
+ * Header rows are marked with isHeader: true in the block data.
16
+ */
17
+ function countHeaderRows(block) {
18
+ let count = 0;
19
+ for (const row of block.rows) if (row.isHeader) count++;
20
+ else break;
21
+ return count;
22
+ }
23
+ /**
24
+ * Calculate total height of header rows from their measures.
25
+ */
26
+ function getHeaderRowsHeight(measure, headerRowCount) {
27
+ let height = 0;
28
+ for (let i = 0; i < headerRowCount && i < measure.rows.length; i++) height += measure.rows[i].height;
29
+ return height;
30
+ }
31
+ const tableRowStartsWithRenderedPageBreak = (block, rowIndex) => {
32
+ const row = block.rows[rowIndex];
33
+ const visibleCells = row?.cells.filter((cell) => cell.blocks.some((cellBlock) => cellBlock.kind !== "paragraph" || !isEmptyParagraph(cellBlock)));
34
+ if (!visibleCells || visibleCells.length === 0) return false;
35
+ const startsWithRenderedPageBreak = (cell) => {
36
+ const firstVisibleBlock = cell.blocks.find((cellBlock) => cellBlock.kind !== "paragraph" || !isEmptyParagraph(cellBlock));
37
+ return firstVisibleBlock?.kind === "paragraph" && firstVisibleBlock.attrs?.renderedPageBreakBefore === true;
38
+ };
39
+ if (row?.heightRule === "exact") return startsWithRenderedPageBreak(visibleCells[0]);
40
+ return visibleCells.every(startsWithRenderedPageBreak);
41
+ };
42
+ const getVerticallyMergedRows = (block) => {
43
+ const mergedRows = /* @__PURE__ */ new Set();
44
+ for (let rowIndex = 0; rowIndex < block.rows.length; rowIndex += 1) {
45
+ const row = block.rows[rowIndex];
46
+ if (!row) continue;
47
+ for (const cell of row.cells) {
48
+ const rowSpan = cell.rowSpan ?? 1;
49
+ if (rowSpan <= 1) continue;
50
+ for (let mergedRowIndex = rowIndex; mergedRowIndex < Math.min(block.rows.length, rowIndex + rowSpan); mergedRowIndex += 1) mergedRows.add(mergedRowIndex);
51
+ }
52
+ }
53
+ return mergedRows;
54
+ };
55
+ const flowBlockHasTrackedChanges = (block) => {
56
+ if (block.kind === "paragraph") return block.runs.some((run) => {
57
+ if (run.kind === "lineBreak") return false;
58
+ return run.isInsertion || run.isDeletion;
59
+ });
60
+ if (block.kind === "table") return block.rows.some((row) => row.cells.some((cell) => cell.blocks.some(flowBlockHasTrackedChanges)));
61
+ if (block.kind === "textBox") return block.content.some(flowBlockHasTrackedChanges);
62
+ return false;
63
+ };
64
+ const tableRowHasTrackedChanges = (block, rowIndex) => block.rows[rowIndex]?.cells.some((cell) => cell.blocks.some(flowBlockHasTrackedChanges)) ?? false;
65
+ /**
66
+ * Layout a table block onto pages.
67
+ */
68
+ function layoutTable(block, measure, paginator, footnoteHeightById) {
69
+ const rows = measure.rows;
70
+ if (rows.length === 0) return;
71
+ const rowFootnoteIds = block.rows.map((row) => collectTableRowFootnoteIds(row, footnoteHeightById));
72
+ const headerRowCount = countHeaderRows(block);
73
+ const headerRowsHeight = getHeaderRowsHeight(measure, headerRowCount);
74
+ let currentRowIndex = 0;
75
+ const breakInfo = buildTableRowBreakInfo(block, measure);
76
+ const verticallyMergedRows = getVerticallyMergedRows(block);
77
+ const computeTableX = ({ columnIndex, rowIndex }) => {
78
+ return paginator.getColumnX(columnIndex) + resolveTableInlineOffset({
79
+ table: block,
80
+ rowJustification: block.rows[rowIndex]?.justification,
81
+ frameWidth: paginator.columnWidth,
82
+ tableWidth: measure.totalWidth
83
+ });
84
+ };
85
+ const getCurrentRowCapacity = (state = paginator.getCurrentState()) => state.rawContentBottom - state.topMargin;
86
+ const hasAdjacentPriorTableRows = (rowIndex, state = paginator.getCurrentState()) => {
87
+ const previous = state.page.fragments.at(-1);
88
+ return previous?.kind === "table" && previous.blockId === block.id && previous.toRow === rowIndex && previous.y + previous.height === state.cursorY;
89
+ };
90
+ const shouldRepeatHeaderRows = (rowIndex, consumed, state = paginator.getCurrentState()) => headerRowCount > 0 && rowIndex >= headerRowCount && !(consumed === 0 && hasAdjacentPriorTableRows(rowIndex, state));
91
+ const rowOpensFlowRegion = (rowIndex, state) => {
92
+ if (state.cursorY === state.topMargin) return true;
93
+ const previous = state.page.fragments.at(-1);
94
+ return rowIndex > 0 && rowIndex <= headerRowCount && previous?.kind === "table" && previous.blockId === block.id && previous.fromRow === 0 && previous.toRow === rowIndex && previous.y === state.topMargin && previous.y + previous.height === state.cursorY;
95
+ };
96
+ const canSplitRow = (rowIndex, state = paginator.getCurrentState()) => {
97
+ const row = rows[rowIndex];
98
+ const sourceRow = block.rows[rowIndex];
99
+ if (!row || !sourceRow || sourceRow.isHeader || verticallyMergedRows.has(rowIndex)) return false;
100
+ if ((breakInfo.breakOffsets[rowIndex]?.length ?? 0) <= 1) return false;
101
+ const freshHeaderOverhead = headerRowCount > 0 && rowIndex >= headerRowCount ? headerRowsHeight : 0;
102
+ const oversized = row.height + freshHeaderOverhead > getCurrentRowCapacity(state);
103
+ if (sourceRow.cantSplit) return oversized && sourceRow.heightRule !== "exact" && rowOpensFlowRegion(rowIndex, state);
104
+ if (!oversized && (state.footnoteHeight > 0 || (rowFootnoteIds[rowIndex]?.length ?? 0) > 0)) return false;
105
+ const currentHeaderOverhead = shouldRepeatHeaderRows(rowIndex, 0, state) ? headerRowsHeight : 0;
106
+ const currentAvailableHeight = paginator.getAvailableHeight() - currentHeaderOverhead - state.trailingSpacing;
107
+ return oversized || row.height > currentAvailableHeight;
108
+ };
109
+ while (currentRowIndex < rows.length) {
110
+ const rowState = paginator.getCurrentState();
111
+ const rowHeaderOverhead = shouldRepeatHeaderRows(currentRowIndex, 0, rowState) ? headerRowsHeight : 0;
112
+ const rowAvailableHeight = paginator.getAvailableHeight() - rowHeaderOverhead - rowState.trailingSpacing;
113
+ const rowStartsFreshPage = rowState.cursorY === rowState.topMargin && rowState.page.fragments.length === 0;
114
+ if (block.rows[currentRowIndex]?.breakBefore === "page" && !rowStartsFreshPage) {
115
+ paginator.forcePageBreak();
116
+ continue;
117
+ }
118
+ if (!rowStartsFreshPage && tableRowStartsWithRenderedPageBreak(block, currentRowIndex) && !tableRowHasTrackedChanges(block, currentRowIndex) && rows[currentRowIndex].height > rowAvailableHeight) {
119
+ paginator.forcePageBreak();
120
+ continue;
121
+ }
122
+ const splittableRow = rows[currentRowIndex];
123
+ if (canSplitRow(currentRowIndex)) {
124
+ let consumed = 0;
125
+ const renderedBreakHints = tableRowHasTrackedChanges(block, currentRowIndex) ? [] : breakInfo.renderedBreakHints[currentRowIndex] ?? [];
126
+ let renderedBreakHintIndex = 0;
127
+ const discardPassedRenderedBreakHints = () => {
128
+ while ((renderedBreakHints[renderedBreakHintIndex]?.offset ?? Infinity) <= consumed) renderedBreakHintIndex += 1;
129
+ };
130
+ const advanceAfterNaturalFlowBreak = (previousPageNumber) => {
131
+ discardPassedRenderedBreakHints();
132
+ if (paginator.getCurrentState().page.number > previousPageNumber && renderedBreakHintIndex < renderedBreakHints.length) renderedBreakHintIndex += 1;
133
+ };
134
+ while (consumed < splittableRow.height) {
135
+ const sliceState = paginator.getCurrentState();
136
+ const repeatHeaderRows = shouldRepeatHeaderRows(currentRowIndex, consumed, sliceState);
137
+ const headerOverhead = repeatHeaderRows ? headerRowsHeight : 0;
138
+ const sliceAvail = paginator.getAvailableHeight() - headerOverhead - (consumed === 0 ? sliceState.trailingSpacing : 0);
139
+ let slice = snapRowBreak(breakInfo, currentRowIndex, consumed, sliceAvail);
140
+ let forceRenderedPageBreak = false;
141
+ if (slice <= 0) {
142
+ if (!(sliceState.cursorY === sliceState.topMargin && sliceState.page.fragments.length === 0)) {
143
+ const previousPageNumber = sliceState.page.number;
144
+ paginator.forceColumnBreak();
145
+ advanceAfterNaturalFlowBreak(previousPageNumber);
146
+ continue;
147
+ }
148
+ const from = consumed;
149
+ slice = (breakInfo.breakOffsets[currentRowIndex]?.find((o) => o > from) ?? splittableRow.height) - consumed;
150
+ }
151
+ discardPassedRenderedBreakHints();
152
+ const renderedBreakHint = renderedBreakHints[renderedBreakHintIndex];
153
+ if (renderedBreakHint && renderedBreakHint.offset <= consumed + slice && sliceAvail - (renderedBreakHint.offset - consumed) <= renderedBreakHint.lineAdvance * 3) {
154
+ slice = renderedBreakHint.offset - consumed;
155
+ renderedBreakHintIndex += 1;
156
+ forceRenderedPageBreak = true;
157
+ }
158
+ const sliceBottom = consumed + slice;
159
+ const continuationSkip = sliceBottom < splittableRow.height ? getRowContinuationSkip(breakInfo, currentRowIndex, sliceBottom) : 0;
160
+ const nextConsumed = Math.min(splittableRow.height, sliceBottom + continuationSkip);
161
+ const moreAfter = !(nextConsumed >= splittableRow.height) || currentRowIndex + 1 < rows.length;
162
+ const fragmentHeight = headerOverhead + slice;
163
+ const sliceFragment = {
164
+ kind: "table",
165
+ blockId: block.id,
166
+ x: computeTableX({
167
+ columnIndex: sliceState.columnIndex,
168
+ rowIndex: currentRowIndex
169
+ }),
170
+ y: 0,
171
+ width: measure.totalWidth,
172
+ height: fragmentHeight,
173
+ fromRow: currentRowIndex,
174
+ toRow: currentRowIndex + 1,
175
+ ...block.pmStart !== void 0 ? { pmStart: block.pmStart } : {},
176
+ ...block.pmEnd !== void 0 ? { pmEnd: block.pmEnd } : {},
177
+ ...consumed > 0 || currentRowIndex > 0 ? { continuesFromPrev: true } : {},
178
+ ...moreAfter ? { continuesOnNext: true } : {},
179
+ ...repeatHeaderRows ? { headerRowCount } : {},
180
+ ...consumed > 0 ? { topClip: consumed } : {},
181
+ ...sliceBottom >= splittableRow.height ? {} : { bottomClip: sliceBottom },
182
+ ...block.sdtGroups ? { sdtGroups: block.sdtGroups } : {}
183
+ };
184
+ const sliceResult = paginator.addFragment(sliceFragment, fragmentHeight, 0, 0);
185
+ sliceFragment.y = sliceResult.y;
186
+ sliceFragment.x = computeTableX({
187
+ columnIndex: sliceResult.state.columnIndex,
188
+ rowIndex: currentRowIndex
189
+ });
190
+ consumed = nextConsumed;
191
+ if (consumed < splittableRow.height) if (forceRenderedPageBreak) paginator.forcePageBreak();
192
+ else {
193
+ const previousPageNumber = paginator.getCurrentState().page.number;
194
+ paginator.forceColumnBreak();
195
+ advanceAfterNaturalFlowBreak(previousPageNumber);
196
+ }
197
+ }
198
+ currentRowIndex += 1;
199
+ continue;
200
+ }
201
+ const state = paginator.getCurrentState();
202
+ const rawAvailableHeight = paginator.getAvailableHeight();
203
+ const isFirstFragment = currentRowIndex === 0;
204
+ const bandSkip = isFirstFragment ? measure.bandSkipBefore ?? 0 : 0;
205
+ const availableHeight = rawAvailableHeight - (isFirstFragment ? Math.max(bandSkip, state.trailingSpacing) : 0);
206
+ const repeatHeaderRowsForNormalFragment = shouldRepeatHeaderRows(currentRowIndex, 0, state);
207
+ const normalHeaderOverhead = repeatHeaderRowsForNormalFragment ? headerRowsHeight : 0;
208
+ let rowsHeight = 0;
209
+ let fittingRows = 0;
210
+ const pageFootnoteIds = new Set(state.page.footnoteIds ?? []);
211
+ const fragmentFootnoteIds = [];
212
+ let fragmentFootnoteHeight = 0;
213
+ let retryOnNextFlowRegion = false;
214
+ const fragmentX = computeTableX({
215
+ columnIndex: state.columnIndex,
216
+ rowIndex: currentRowIndex
217
+ });
218
+ for (let j = currentRowIndex; j < rows.length; j++) {
219
+ if (j > currentRowIndex && block.rows[j]?.breakBefore === "page") break;
220
+ if (j > currentRowIndex && computeTableX({
221
+ columnIndex: state.columnIndex,
222
+ rowIndex: j
223
+ }) !== fragmentX) break;
224
+ const rowHeight = rows[j].height;
225
+ const currentRowFootnoteIds = rowFootnoteIds[j] ?? [];
226
+ const fragmentFootnoteCountBeforeRow = fragmentFootnoteIds.length;
227
+ let rowFootnoteHeight = 0;
228
+ for (const id of currentRowFootnoteIds) {
229
+ if (pageFootnoteIds.has(id) || fragmentFootnoteIds.includes(id)) continue;
230
+ fragmentFootnoteIds.push(id);
231
+ rowFootnoteHeight += footnoteHeightById?.get(id) ?? 0;
232
+ }
233
+ const candidateFootnoteHeight = fragmentFootnoteHeight + rowFootnoteHeight;
234
+ const footnoteGrowth = projectedFootnoteReserveGrowth(state, candidateFootnoteHeight + (state.footnoteDemandHeight === 0 && candidateFootnoteHeight > 0 ? 12 : 0));
235
+ if (rowsHeight + rowHeight + normalHeaderOverhead + footnoteGrowth <= availableHeight) {
236
+ rowsHeight += rowHeight;
237
+ fragmentFootnoteHeight += rowFootnoteHeight;
238
+ fittingRows++;
239
+ } else if (fittingRows === 0) {
240
+ if (state.cursorY === state.topMargin && state.page.fragments.length === 0) {
241
+ rowsHeight += rowHeight;
242
+ fragmentFootnoteHeight += rowFootnoteHeight;
243
+ fittingRows++;
244
+ break;
245
+ }
246
+ fragmentFootnoteIds.splice(fragmentFootnoteCountBeforeRow);
247
+ paginator.forceColumnBreak();
248
+ retryOnNextFlowRegion = true;
249
+ break;
250
+ } else {
251
+ fragmentFootnoteIds.splice(fragmentFootnoteCountBeforeRow);
252
+ break;
253
+ }
254
+ }
255
+ if (retryOnNextFlowRegion) continue;
256
+ const fragmentHeight = rowsHeight + normalHeaderOverhead;
257
+ const isLastFragment = currentRowIndex + fittingRows >= rows.length;
258
+ const desiredX = fragmentX;
259
+ const fragment = {
260
+ kind: "table",
261
+ blockId: block.id,
262
+ x: desiredX,
263
+ y: 0,
264
+ width: measure.totalWidth,
265
+ height: fragmentHeight,
266
+ fromRow: currentRowIndex,
267
+ toRow: currentRowIndex + fittingRows,
268
+ ...block.pmStart !== void 0 ? { pmStart: block.pmStart } : {},
269
+ ...block.pmEnd !== void 0 ? { pmEnd: block.pmEnd } : {},
270
+ ...!isFirstFragment ? { continuesFromPrev: true } : {},
271
+ ...!isLastFragment ? { continuesOnNext: true } : {},
272
+ ...repeatHeaderRowsForNormalFragment ? { headerRowCount } : {},
273
+ ...block.sdtGroups ? { sdtGroups: block.sdtGroups } : {}
274
+ };
275
+ if (fragmentFootnoteHeight > 0) paginator.addFootnoteHeight(fragmentFootnoteHeight, fragmentFootnoteIds);
276
+ fragment.y = paginator.addFragment(fragment, fragmentHeight, bandSkip, 0).y;
277
+ fragment.x = desiredX;
278
+ currentRowIndex += fittingRows;
279
+ if (currentRowIndex < rows.length) {
280
+ if (canSplitRow(currentRowIndex)) {
281
+ const nextState = paginator.getCurrentState();
282
+ const nextHeaderOverhead = shouldRepeatHeaderRows(currentRowIndex, 0, nextState) ? headerRowsHeight : 0;
283
+ const nextSliceAvail = paginator.getAvailableHeight() - nextHeaderOverhead - nextState.trailingSpacing;
284
+ if (snapRowBreak(breakInfo, currentRowIndex, 0, nextSliceAvail) > 0) continue;
285
+ }
286
+ const nextState = paginator.getCurrentState();
287
+ const nextRowHeight = rows[currentRowIndex].height + (shouldRepeatHeaderRows(currentRowIndex, 0, nextState) ? headerRowsHeight : 0);
288
+ paginator.ensureFits(nextRowHeight);
289
+ }
290
+ }
291
+ }
292
+ function collectTableRowFootnoteIds(row, footnoteHeightById) {
293
+ if (!row || !footnoteHeightById || row.hidden) return [];
294
+ const ids = [];
295
+ const walk = (blocks) => {
296
+ for (const block of blocks) {
297
+ if (block.kind === "paragraph") {
298
+ for (const run of block.runs) if (run.kind === "text" && run.footnoteRefId !== void 0 && footnoteHeightById.has(run.footnoteRefId) && !ids.includes(run.footnoteRefId)) ids.push(run.footnoteRefId);
299
+ continue;
300
+ }
301
+ if (block.kind === "table") {
302
+ for (const nestedRow of block.rows) {
303
+ if (nestedRow.hidden) continue;
304
+ for (const cell of nestedRow.cells) walk(cell.blocks);
305
+ }
306
+ continue;
307
+ }
308
+ if (block.kind === "textBox") walk(block.content);
309
+ }
310
+ };
311
+ for (const cell of row.cells) walk(cell.blocks);
312
+ return ids;
313
+ }
314
+ /**
315
+ * Layout a floating table (anchored) without advancing the cursor.
316
+ */
317
+ function layoutFloatingTable(block, measure, paginator, contentWidth) {
318
+ const state = paginator.getCurrentState();
319
+ const floating = block.floating;
320
+ const page = state.page;
321
+ const margins = page.margins;
322
+ const tableWidth = measure.totalWidth;
323
+ const tableHeight = measure.totalHeight;
324
+ const contentHeight = page.size.h - margins.top - margins.bottom;
325
+ let baseY = margins.top;
326
+ if (floating?.vertAnchor === "page") baseY = 0;
327
+ else if (floating?.vertAnchor === "text") baseY = state.cursorY;
328
+ let x = paginator.getColumnX(state.columnIndex);
329
+ if (floating) x = resolveFloatingTablePageX({
330
+ anchor: floating,
331
+ justification: block.justification,
332
+ tableWidth,
333
+ marginWidth: contentWidth,
334
+ pageWidth: page.size.w,
335
+ marginLeft: margins.left,
336
+ textFrameWidth: paginator.columnWidth,
337
+ textFrameLeft: paginator.getColumnX(state.columnIndex)
338
+ });
339
+ let y = state.cursorY;
340
+ let usedExplicitY = false;
341
+ if (floating?.tblpY !== void 0) {
342
+ y = baseY + floating.tblpY;
343
+ usedExplicitY = true;
344
+ } else if (floating?.tblpYSpec) {
345
+ usedExplicitY = true;
346
+ const spec = floating.tblpYSpec;
347
+ if (spec === "top") y = baseY;
348
+ else if (spec === "bottom") y = baseY + contentHeight - tableHeight;
349
+ else if (spec === "center") y = baseY + (contentHeight - tableHeight) / 2;
350
+ }
351
+ if (!usedExplicitY) y = paginator.ensureFits(tableHeight).cursorY;
352
+ const fragment = {
353
+ kind: "table",
354
+ blockId: block.id,
355
+ x,
356
+ y,
357
+ width: tableWidth,
358
+ height: tableHeight,
359
+ fromRow: 0,
360
+ toRow: block.rows.length,
361
+ ...block.pmStart !== void 0 ? { pmStart: block.pmStart } : {},
362
+ ...block.pmEnd !== void 0 ? { pmEnd: block.pmEnd } : {},
363
+ isFloating: true,
364
+ ...block.sdtGroups ? { sdtGroups: block.sdtGroups } : {}
365
+ };
366
+ paginator.addUnflowedFragment(fragment);
367
+ }
368
+ //#endregion
369
+ export { getHeaderRowsHeight, layoutFloatingTable, layoutTable };
@@ -19,6 +19,13 @@ type TableRowBreakInfo = {
19
19
  /** Suppressible leading paragraph whitespace after each matching break offset. */
20
20
  continuationSkips: number[][];
21
21
  };
22
+ /**
23
+ * Height of the start of a table that a preceding `w:keepNext` paragraph
24
+ * (§17.3.1.15) must share a page with: the leading header rows (§17.4.49) and
25
+ * the first body row. A row that may break across pages (no `w:cantSplit`,
26
+ * §17.4.6) needs only its first line there; an unbreakable row needs all of it.
27
+ */
28
+ declare function tableKeepNextOpeningHeight(block: TableBlock, measure: TableMeasure): number;
22
29
  /** Build break geometry for a table from its block + measure. */
23
30
  declare function buildTableRowBreakInfo(block: TableBlock, measure: TableMeasure): TableRowBreakInfo;
24
31
  /** Leading empty-paragraph whitespace to consume when a row resumes after `offset`. */
@@ -30,4 +37,4 @@ declare function getRowContinuationSkip(info: TableRowBreakInfo, rowIndex: numbe
30
37
  */
31
38
  declare function snapRowBreak(info: TableRowBreakInfo, rowIndex: number, fromOffset: number, maxSlice: number): number;
32
39
  //#endregion
33
- export { TableRowBreakInfo, buildTableRowBreakInfo, getRowContinuationSkip, snapRowBreak };
40
+ export { TableRowBreakInfo, buildTableRowBreakInfo, getRowContinuationSkip, snapRowBreak, tableKeepNextOpeningHeight };
@@ -141,6 +141,27 @@ const continuationSkipAfter = (geometries, offset) => {
141
141
  if (skips.length === 0 || skips.some((skip) => skip <= BREAK_OFFSET_EPSILON)) return 0;
142
142
  return Math.min(...skips);
143
143
  };
144
+ /**
145
+ * Height of the start of a table that a preceding `w:keepNext` paragraph
146
+ * (§17.3.1.15) must share a page with: the leading header rows (§17.4.49) and
147
+ * the first body row. A row that may break across pages (no `w:cantSplit`,
148
+ * §17.4.6) needs only its first line there; an unbreakable row needs all of it.
149
+ */
150
+ function tableKeepNextOpeningHeight(block, measure) {
151
+ let height = 0;
152
+ let rowIndex = 0;
153
+ while (rowIndex < measure.rows.length && block.rows[rowIndex]?.isHeader === true) {
154
+ height += measure.rows[rowIndex]?.height ?? 0;
155
+ rowIndex += 1;
156
+ }
157
+ const row = measure.rows[rowIndex];
158
+ const sourceRow = block.rows[rowIndex];
159
+ if (!row || !sourceRow) return height;
160
+ const breakOffsets = buildTableRowBreakInfo(block, measure).breakOffsets[rowIndex] ?? [];
161
+ const firstBreak = breakOffsets.at(0);
162
+ const rowMayBreak = sourceRow.cantSplit !== true && breakOffsets.length > 1;
163
+ return height + (rowMayBreak && firstBreak !== void 0 ? firstBreak : row.height);
164
+ }
144
165
  /** Build break geometry for a table from its block + measure. */
145
166
  function buildTableRowBreakInfo(block, measure) {
146
167
  const rowCount = measure.rows.length;
@@ -208,4 +229,4 @@ function snapRowBreak(info, rowIndex, fromOffset, maxSlice) {
208
229
  return best;
209
230
  }
210
231
  //#endregion
211
- export { buildTableRowBreakInfo, getRowContinuationSkip, snapRowBreak };
232
+ export { buildTableRowBreakInfo, getRowContinuationSkip, snapRowBreak, tableKeepNextOpeningHeight };
@@ -0,0 +1,15 @@
1
+ import { TextBoxBlock, TextBoxMeasure } from "./types.js";
2
+ import { createPaginator } from "./paginator.js";
3
+ //#region src/layout-engine/textBoxLayout.d.ts
4
+ type LayoutTextBoxOptions = {
5
+ paginator: ReturnType<typeof createPaginator>;
6
+ sectionMarginTop: number;
7
+ sectionPageHeight: number;
8
+ sectionMarginBottom: number;
9
+ };
10
+ /**
11
+ * Layout a text box block onto pages.
12
+ */
13
+ declare function layoutTextBox(block: TextBoxBlock, measure: TextBoxMeasure, { paginator, sectionMarginTop, sectionPageHeight, sectionMarginBottom }: LayoutTextBoxOptions): void;
14
+ //#endregion
15
+ export { layoutTextBox };
@@ -0,0 +1,137 @@
1
+ import { emuToPixels } from "../utils/units.js";
2
+ import { bandFragmentX, bandTopContentY, isPageFrameRelativeAnchor } from "./textBoxFlow.js";
3
+ import { floatingTextBoxReservesBand } from "./types.js";
4
+ //#region src/layout-engine/textBoxLayout.ts
5
+ /**
6
+ * Places text boxes onto pages: inline and anchored placement, page-pinned
7
+ * wrap bands, and host-paragraph line offsets.
8
+ */
9
+ const TEXT_BOX_ANCHOR_BLOCK_ID = Symbol.for("stll.textBoxAnchorBlockId");
10
+ const readTextBoxAnchorBlockId = (block) => Reflect.get(block, TEXT_BOX_ANCHOR_BLOCK_ID);
11
+ /**
12
+ * Layout a text box block onto pages.
13
+ */
14
+ function layoutTextBox(block, measure, { paginator, sectionMarginTop, sectionPageHeight, sectionMarginBottom }) {
15
+ if (isPagePinnedBandTextBox(block)) {
16
+ const state = paginator.getCurrentState();
17
+ const bandTop = bandTopContentY(block.position?.vertical, {
18
+ pageHeight: sectionPageHeight,
19
+ marginTop: sectionMarginTop,
20
+ marginBottom: sectionMarginBottom,
21
+ boxHeight: measure.height
22
+ });
23
+ const x = bandFragmentX(block.position?.horizontal, {
24
+ pageWidth: state.page.size.w,
25
+ marginLeft: state.page.margins.left,
26
+ marginRight: state.page.margins.right,
27
+ activeColumnLeft: paginator.getColumnX(state.columnIndex),
28
+ activeColumnWidth: paginator.columnWidth,
29
+ boxWidth: measure.width
30
+ });
31
+ const fragment = {
32
+ kind: "textBox",
33
+ blockId: block.id,
34
+ x,
35
+ y: block.position?.vertical?.relativeTo === "page" ? sectionMarginTop + bandTop : state.topMargin + bandTop,
36
+ width: measure.width,
37
+ height: measure.height,
38
+ isPositioned: true,
39
+ ...block.pmStart !== void 0 ? { pmStart: block.pmStart } : {},
40
+ ...block.pmEnd !== void 0 ? { pmEnd: block.pmEnd } : {}
41
+ };
42
+ paginator.addUnflowedFragment(fragment);
43
+ return;
44
+ }
45
+ if (block.position !== void 0) {
46
+ const state = paginator.getCurrentState();
47
+ const x = bandFragmentX(block.position.horizontal, {
48
+ pageWidth: state.page.size.w,
49
+ marginLeft: state.page.margins.left,
50
+ marginRight: state.page.margins.right,
51
+ activeColumnLeft: paginator.getColumnX(state.columnIndex),
52
+ activeColumnWidth: paginator.columnWidth,
53
+ boxWidth: measure.width
54
+ });
55
+ const vertical = block.position.vertical;
56
+ const anchorBlockId = readTextBoxAnchorBlockId(block);
57
+ const anchorParagraph = vertical?.relativeTo === "paragraph" && typeof anchorBlockId === "string" ? state.page.fragments.find((fragment) => fragment.kind === "paragraph" && fragment.blockId === anchorBlockId) : void 0;
58
+ const bandGeometry = {
59
+ pageHeight: sectionPageHeight,
60
+ marginTop: sectionMarginTop,
61
+ marginBottom: sectionMarginBottom,
62
+ boxHeight: measure.height
63
+ };
64
+ let y;
65
+ if (vertical?.relativeTo === "page") y = sectionMarginTop + bandTopContentY(vertical, bandGeometry);
66
+ else if (isPageFrameRelativeAnchor(vertical?.relativeTo)) y = state.topMargin + bandTopContentY(vertical, {
67
+ pageHeight: sectionPageHeight,
68
+ marginTop: sectionMarginTop,
69
+ marginBottom: sectionMarginBottom,
70
+ boxHeight: measure.height
71
+ });
72
+ else y = (anchorParagraph?.y ?? state.cursorY) + emuToPixels(vertical?.posOffset ?? 0);
73
+ const fragment = {
74
+ kind: "textBox",
75
+ blockId: block.id,
76
+ x,
77
+ y,
78
+ width: measure.width,
79
+ height: measure.height,
80
+ isPositioned: true,
81
+ ...block.pmStart !== void 0 ? { pmStart: block.pmStart } : {},
82
+ ...block.pmEnd !== void 0 ? { pmEnd: block.pmEnd } : {}
83
+ };
84
+ paginator.addUnflowedFragment(fragment);
85
+ return;
86
+ }
87
+ const effect = block.effectExtent;
88
+ const occupiedHeight = measure.height + (effect?.top ?? 0) + (effect?.bottom ?? 0);
89
+ const occupiedWidth = measure.width + (effect?.left ?? 0) + (effect?.right ?? 0);
90
+ const host = block.hostParagraph;
91
+ const fragment = {
92
+ kind: "textBox",
93
+ blockId: block.id,
94
+ x: 0,
95
+ y: 0,
96
+ width: measure.width,
97
+ height: measure.height,
98
+ ...block.pmStart !== void 0 ? { pmStart: block.pmStart } : {},
99
+ ...block.pmEnd !== void 0 ? { pmEnd: block.pmEnd } : {}
100
+ };
101
+ const result = paginator.addFragment(fragment, occupiedHeight, host?.spacing?.before ?? 0, host?.spacing?.after ?? 0);
102
+ fragment.x = result.x + hostParagraphLineOffset(host, paginator.columnWidth, occupiedWidth) + (effect?.left ?? 0);
103
+ fragment.y = result.y + (effect?.top ?? 0);
104
+ }
105
+ /**
106
+ * Where a line holding only an object of `width` starts within a column:
107
+ * after the host paragraph's start and first-line indents, then aligned in
108
+ * what remains before its end indent.
109
+ */
110
+ function hostParagraphLineOffset(host, columnWidth, width) {
111
+ if (!host) return 0;
112
+ const indent = host.indent;
113
+ const start = (indent?.left ?? 0) + (indent?.firstLine ?? 0) - (indent?.hanging ?? 0);
114
+ const slack = Math.max(0, columnWidth - start - (indent?.right ?? 0) - width);
115
+ switch (host.alignment) {
116
+ case "center": return start + slack / 2;
117
+ case "right": return start + slack;
118
+ case "left":
119
+ case "justify":
120
+ case void 0: return start;
121
+ default:
122
+ host.alignment;
123
+ return start;
124
+ }
125
+ }
126
+ /**
127
+ * A topAndBottom text box whose vertical anchor pins it to the page frame
128
+ * (page/margin/margin-strip) — it floats to a fixed page position rather than
129
+ * flowing in document order. Must agree with the measure pass's band extraction
130
+ * (extractFloatingZones), which uses the same predicate. eigenpal #694.
131
+ */
132
+ function isPagePinnedBandTextBox(block) {
133
+ if (!floatingTextBoxReservesBand(block)) return false;
134
+ return isPageFrameRelativeAnchor(block.position?.vertical?.relativeTo);
135
+ }
136
+ //#endregion
137
+ export { layoutTextBox };
@@ -2,7 +2,7 @@ import { OutlineStyleAttr } from "../types/documentEnumValues.js";
2
2
  import { CssBorderStyle } from "../utils/borderCss.js";
3
3
  import { ImagePosition, ImageWrap, InlineWrapper, NumberFormat, OutlineLevel, ParagraphNumberingOverride, PreviewDescriptor, SdtProperties, SdtType, SectionStart, ShapeTextBody, TableCellFormatting, TableWidthType, UnderlineStyle, VerticalAlign } from "@stll/docx-core/model";
4
4
  declare namespace types_d_exports {
5
- export { BlockId, BorderStyle, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, Layout, LayoutOptions, LineBreakRun, ListMarkerFormatting, MathRun, Measure, MeasuredLine, NOTE_SEPARATOR_RULE_THICKNESS, NOTE_SEPARATOR_WIDTH_FRACTION, NoteAreaLayout, NoteSeparatorKind, NoteSeparatorRule, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, RenderedPageBreakRun, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, SectionPageNumbering, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isListNumPr, isTextWrappingFloatingImageRun, resolveTableCellPadding, resolveTableWidthPx, stripFlowBlockPmAnchors, tableColumnsArePinned };
5
+ export { BlockId, BorderStyle, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, Layout, LayoutOptions, LineBreakRun, ListMarkerFormatting, MathRun, Measure, MeasuredLine, NOTE_SEPARATOR_RULE_THICKNESS, NOTE_SEPARATOR_WIDTH_FRACTION, NoteAreaLayout, NoteSeparatorKind, NoteSeparatorRule, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, RenderedPageBreakRun, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, SectionPageNumbering, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxGradientFill, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isListNumPr, isTextWrappingFloatingImageRun, resolveTableCellPadding, resolveTableWidthPx, stripFlowBlockPmAnchors, tableColumnsArePinned };
6
6
  }
7
7
  /**
8
8
  * Unique identifier for a block in the document.
@@ -849,6 +849,21 @@ declare const DEFAULT_TEXTBOX_MARGINS: {
849
849
  };
850
850
  /** Default text box width in pixels when no width is specified */
851
851
  declare const DEFAULT_TEXTBOX_WIDTH = 200;
852
+ /**
853
+ * A linear DrawingML gradient fill with its colors resolved (ECMA-376
854
+ * §20.1.8.33, §20.1.8.41). It runs along `angle`, in degrees clockwise from
855
+ * the positive x axis; when `scaled`, that angle is stated in the unit square
856
+ * and stretched with the box.
857
+ */
858
+ type TextBoxGradientFill = {
859
+ angle: number;
860
+ scaled: boolean;
861
+ /** Stops in ascending order; `offset` runs 0 to 1. */
862
+ stops: {
863
+ offset: number;
864
+ color: string;
865
+ }[];
866
+ };
852
867
  /**
853
868
  * Text box block — positioned container with block content.
854
869
  */
@@ -869,6 +884,8 @@ type TextBoxBlock = {
869
884
  verticalAlign?: ShapeTextBody["anchor"];
870
885
  /** Fill/background color */
871
886
  fillColor?: string;
887
+ /** Gradient fill, painted when no `fillColor` states a solid one. */
888
+ fillGradient?: TextBoxGradientFill;
872
889
  /** Border width in pixels */
873
890
  outlineWidth?: number;
874
891
  /** Border color */
@@ -1341,6 +1358,8 @@ type LayoutOptions = {
1341
1358
  firstPageMargins?: PageMargins;
1342
1359
  /** Per-section body margins used on even authored page numbers. */
1343
1360
  sectionEvenPageMargins?: (PageMargins | undefined)[];
1361
+ /** Per-section body margins used on the first page of a `w:titlePg` section. */
1362
+ sectionFirstPageMargins?: (PageMargins | undefined)[];
1344
1363
  /** Page-number policy for the initial section. */
1345
1364
  pageNumbering?: SectionPageNumbering;
1346
1365
  /** Body-level final section page size. */
@@ -1576,4 +1595,4 @@ declare const getTableRowLeadingWidth: (row: TableRow, columnWidths: readonly nu
1576
1595
  */
1577
1596
  declare function stripFlowBlockPmAnchors(block: FlowBlock): FlowBlock;
1578
1597
  //#endregion
1579
- export { BlockId, BorderStyle, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, Layout, LayoutOptions, LineBreakRun, ListMarkerFormatting, MathRun, Measure, MeasuredLine, NOTE_SEPARATOR_RULE_THICKNESS, NOTE_SEPARATOR_WIDTH_FRACTION, NoteAreaLayout, NoteSeparatorKind, NoteSeparatorRule, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, RenderedPageBreakRun, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, SectionPageNumbering, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isListNumPr, isTextWrappingFloatingImageRun, resolveTableCellPadding, resolveTableWidthPx, stripFlowBlockPmAnchors, tableColumnsArePinned, types_d_exports };
1598
+ export { BlockId, BorderStyle, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, Layout, LayoutOptions, LineBreakRun, ListMarkerFormatting, MathRun, Measure, MeasuredLine, NOTE_SEPARATOR_RULE_THICKNESS, NOTE_SEPARATOR_WIDTH_FRACTION, NoteAreaLayout, NoteSeparatorKind, NoteSeparatorRule, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, RenderedPageBreakRun, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, SectionPageNumbering, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxGradientFill, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isListNumPr, isTextWrappingFloatingImageRun, resolveTableCellPadding, resolveTableWidthPx, stripFlowBlockPmAnchors, tableColumnsArePinned, types_d_exports };
@@ -1,3 +1,4 @@
1
+ import "../layout-engine/measure/floatingLineClearance.js";
1
2
  import { resolveFloatingTableX } from "../layout-engine/measure/floatingTablePosition.js";
2
3
  import { rectsToFloatingZones } from "../layout-engine/measure/floatingZones.js";
3
4
  import { DEFAULT_FONT_FAMILY } from "../layout-engine/measure/measureHelpers.js";