@stll/folio-core 0.49.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.
- package/dist/ai-edits/headless.d.ts +37 -1
- package/dist/ai-edits/headless.js +47 -16
- package/dist/display-list/dom/renderDisplayListToDom.d.ts +10 -1
- package/dist/display-list/dom/renderDisplayListToDom.js +8 -7
- package/dist/display-list/html/renderDisplayListToHtml.d.ts +21 -0
- package/dist/display-list/html/renderDisplayListToHtml.js +126 -0
- package/dist/display-list/selectDisplayPages.d.ts +6 -0
- package/dist/display-list/selectDisplayPages.js +44 -0
- package/dist/export-pdf.d.ts +35 -2
- package/dist/export-pdf.js +51 -20
- package/dist/fonts/fontsourceFaces.d.ts +47 -0
- package/dist/fonts/fontsourceFaces.js +230 -0
- package/dist/layout-bridge/convert/flowBlockPostprocessing.d.ts +8 -0
- package/dist/layout-bridge/convert/flowBlockPostprocessing.js +106 -0
- package/dist/layout-bridge/convert/flowBorders.d.ts +17 -0
- package/dist/layout-bridge/convert/flowBorders.js +31 -0
- package/dist/layout-bridge/convert/flowConversionShared.d.ts +111 -0
- package/dist/layout-bridge/convert/flowConversionShared.js +23 -0
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +2 -1
- package/dist/layout-bridge/convert/footnoteLayout.js +2 -1
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +2 -1
- package/dist/layout-bridge/convert/imageConversion.d.ts +33 -0
- package/dist/layout-bridge/convert/imageConversion.js +118 -0
- package/dist/layout-bridge/convert/listMarkers.d.ts +28 -0
- package/dist/layout-bridge/convert/listMarkers.js +98 -0
- package/dist/layout-bridge/convert/noteReferences.d.ts +14 -0
- package/dist/layout-bridge/convert/noteReferences.js +29 -0
- package/dist/layout-bridge/convert/pageBreakSplitting.d.ts +16 -0
- package/dist/layout-bridge/convert/pageBreakSplitting.js +140 -0
- package/dist/layout-bridge/convert/paragraphAttrs.d.ts +20 -0
- package/dist/layout-bridge/convert/paragraphAttrs.js +234 -0
- package/dist/layout-bridge/convert/paragraphConversion.d.ts +12 -0
- package/dist/layout-bridge/convert/paragraphConversion.js +112 -0
- package/dist/layout-bridge/convert/paragraphRuns.d.ts +15 -0
- package/dist/layout-bridge/convert/paragraphRuns.js +281 -0
- package/dist/layout-bridge/convert/runFormattingMerge.d.ts +19 -0
- package/dist/layout-bridge/convert/runFormattingMerge.js +167 -0
- package/dist/layout-bridge/convert/runMarkFormatting.d.ts +11 -0
- package/dist/layout-bridge/convert/runMarkFormatting.js +291 -0
- package/dist/layout-bridge/convert/sectionBoundaries.d.ts +35 -0
- package/dist/layout-bridge/convert/sectionBoundaries.js +114 -0
- package/dist/layout-bridge/convert/tableConversion.d.ts +12 -0
- package/dist/layout-bridge/convert/tableConversion.js +303 -0
- package/dist/layout-bridge/convert/textBoxFill.d.ts +20 -0
- package/dist/layout-bridge/convert/textBoxFill.js +42 -0
- package/dist/layout-bridge/convert/textFormattingConversion.d.ts +22 -0
- package/dist/layout-bridge/convert/textFormattingConversion.js +120 -0
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +5 -113
- package/dist/layout-bridge/convert/toFlowBlocks.js +11 -2082
- package/dist/layout-bridge/engine/hitTest.js +1 -1
- package/dist/layout-bridge/engine/measuring/index.d.ts +2 -1
- package/dist/layout-bridge/engine/measuring/index.js +2 -1
- package/dist/layout-bridge/engine/selectionRects.js +1 -1
- package/dist/layout-engine/imageLayout.d.ts +9 -0
- package/dist/layout-engine/imageLayout.js +48 -0
- package/dist/layout-engine/index.d.ts +3 -18
- package/dist/layout-engine/index.js +12 -937
- package/dist/layout-engine/layoutFlowShared.d.ts +6 -0
- package/dist/layout-engine/layoutFlowShared.js +8 -0
- package/dist/layout-engine/measure/crossRunWords.d.ts +45 -0
- package/dist/layout-engine/measure/crossRunWords.js +172 -0
- package/dist/layout-engine/measure/floatingLineClearance.d.ts +24 -0
- package/dist/layout-engine/measure/floatingLineClearance.js +44 -0
- package/dist/layout-engine/measure/inlineObjectMetrics.d.ts +6 -0
- package/dist/layout-engine/measure/inlineObjectMetrics.js +52 -0
- package/dist/layout-engine/measure/justification.d.ts +32 -0
- package/dist/layout-engine/measure/justification.js +83 -0
- package/dist/layout-engine/measure/lineFitting.d.ts +48 -0
- package/dist/layout-engine/measure/lineFitting.js +145 -0
- package/dist/layout-engine/measure/lineTypography.d.ts +44 -0
- package/dist/layout-engine/measure/lineTypography.js +124 -0
- package/dist/layout-engine/measure/measureBlocks.js +2 -1
- package/dist/layout-engine/measure/measureParagraph.d.ts +1 -20
- package/dist/layout-engine/measure/measureParagraph.js +32 -734
- package/dist/layout-engine/measure/paragraphMeasureShared.d.ts +109 -0
- package/dist/layout-engine/measure/paragraphMeasureShared.js +82 -0
- package/dist/layout-engine/measure/paragraphTabs.d.ts +18 -0
- package/dist/layout-engine/measure/paragraphTabs.js +78 -0
- package/dist/layout-engine/paragraphLayout.d.ts +23 -0
- package/dist/layout-engine/paragraphLayout.js +204 -0
- package/dist/layout-engine/sectionLayout.d.ts +45 -0
- package/dist/layout-engine/sectionLayout.js +216 -0
- package/dist/layout-engine/tableLayout.d.ts +17 -0
- package/dist/layout-engine/tableLayout.js +369 -0
- package/dist/layout-engine/textBoxLayout.d.ts +15 -0
- package/dist/layout-engine/textBoxLayout.js +137 -0
- package/dist/layout-painter/renderPage.js +1 -0
- package/dist/paged-layout/sectionBlockWidths.d.ts +2 -1
- package/dist/paged-layout/sectionBlockWidths.js +1 -1
- package/dist/server.d.ts +3 -2
- package/package.json +2 -2
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { measuredLineAdvance } from "./lineFlow.js";
|
|
2
|
+
import { SECTION_START_PLACEMENT } from "./paginator.js";
|
|
3
|
+
import { collapseParagraphSpacing, getParagraphSpacingAfter, getParagraphSpacingBefore } from "./paragraphSpacing.js";
|
|
4
|
+
import { physicalColumnRegionIsShared } from "./section-breaks.js";
|
|
5
|
+
import { panic } from "better-result";
|
|
6
|
+
//#region src/layout-engine/sectionLayout.ts
|
|
7
|
+
/**
|
|
8
|
+
* Section handling during pagination: per-section layout configuration,
|
|
9
|
+
* section-break scheduling (in place, next page, odd/even filler pages), and
|
|
10
|
+
* balanced column height for continuous sections.
|
|
11
|
+
*/
|
|
12
|
+
const DEFAULT_COLUMNS = {
|
|
13
|
+
count: 1,
|
|
14
|
+
gap: 0
|
|
15
|
+
};
|
|
16
|
+
const CONTINUE_PAGE_NUMBERING = { type: "continue" };
|
|
17
|
+
function collectSectionConfigs(blocks, initialConfig, finalConfig) {
|
|
18
|
+
const configs = [];
|
|
19
|
+
const breakIndices = [];
|
|
20
|
+
let previousConfig = initialConfig;
|
|
21
|
+
for (let index = 0; index < blocks.length; index += 1) {
|
|
22
|
+
const block = blocks[index];
|
|
23
|
+
if (block?.kind !== "sectionBreak") continue;
|
|
24
|
+
const sectionBreak = block;
|
|
25
|
+
const config = {
|
|
26
|
+
pageSize: sectionBreak.pageSize ?? previousConfig.pageSize,
|
|
27
|
+
margins: sectionBreak.margins ?? previousConfig.margins,
|
|
28
|
+
pageNumbering: sectionBreak.pageNumbering ?? CONTINUE_PAGE_NUMBERING
|
|
29
|
+
};
|
|
30
|
+
if (sectionBreak.columns !== void 0) config.columns = sectionBreak.columns;
|
|
31
|
+
configs.push(config);
|
|
32
|
+
breakIndices.push(index);
|
|
33
|
+
previousConfig = configs.at(-1) ?? previousConfig;
|
|
34
|
+
}
|
|
35
|
+
configs.push(finalConfig);
|
|
36
|
+
return {
|
|
37
|
+
configs,
|
|
38
|
+
breakIndices
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function balancedParagraphSectionHeight({ blocks, measures, startIndex, endIndex, incomingSpacing, columnCount, availableHeight }) {
|
|
42
|
+
if (columnCount <= 1 || startIndex >= endIndex || availableHeight <= 0) return;
|
|
43
|
+
const lineUnits = [];
|
|
44
|
+
let totalHeight = 0;
|
|
45
|
+
let tallestUnit = 0;
|
|
46
|
+
let trailingSpacing = incomingSpacing;
|
|
47
|
+
for (let index = startIndex; index < endIndex; index++) {
|
|
48
|
+
const block = blocks[index];
|
|
49
|
+
const measure = measures[index];
|
|
50
|
+
if (block?.kind !== "paragraph" || measure?.kind !== "paragraph") return;
|
|
51
|
+
if (block.attrs?.keepNext === true || block.attrs?.keepLines === true || block.runs.some((run) => run.kind === "text" && run.footnoteRefId !== void 0)) return;
|
|
52
|
+
const leadingSpacing = collapseParagraphSpacing({
|
|
53
|
+
before: getParagraphSpacingBefore(block),
|
|
54
|
+
after: trailingSpacing
|
|
55
|
+
});
|
|
56
|
+
if (measure.lines.length === 0) {
|
|
57
|
+
if (leadingSpacing > 0) {
|
|
58
|
+
lineUnits.push(leadingSpacing);
|
|
59
|
+
totalHeight += leadingSpacing;
|
|
60
|
+
tallestUnit = Math.max(tallestUnit, leadingSpacing);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
for (let lineIndex = 0; lineIndex < measure.lines.length; lineIndex++) {
|
|
64
|
+
const line = measure.lines[lineIndex];
|
|
65
|
+
if (!line) continue;
|
|
66
|
+
const unitHeight = measuredLineAdvance(line) + (lineIndex === 0 ? leadingSpacing : 0);
|
|
67
|
+
lineUnits.push(unitHeight);
|
|
68
|
+
totalHeight += unitHeight;
|
|
69
|
+
tallestUnit = Math.max(tallestUnit, unitHeight);
|
|
70
|
+
}
|
|
71
|
+
if (tallestUnit > availableHeight || totalHeight > availableHeight * columnCount) return;
|
|
72
|
+
trailingSpacing = getParagraphSpacingAfter(block);
|
|
73
|
+
}
|
|
74
|
+
if (totalHeight <= 0) return;
|
|
75
|
+
const columnsNeeded = (targetHeight) => {
|
|
76
|
+
let usedHeight = 0;
|
|
77
|
+
let usedColumns = 1;
|
|
78
|
+
for (const unitHeight of lineUnits) if (usedHeight > 0 && usedHeight + unitHeight > targetHeight) {
|
|
79
|
+
usedColumns += 1;
|
|
80
|
+
usedHeight = unitHeight;
|
|
81
|
+
} else usedHeight += unitHeight;
|
|
82
|
+
return usedColumns;
|
|
83
|
+
};
|
|
84
|
+
let lower = Math.max(tallestUnit, totalHeight / columnCount);
|
|
85
|
+
let upper = Math.min(totalHeight, availableHeight);
|
|
86
|
+
if (columnsNeeded(upper) > columnCount) return;
|
|
87
|
+
for (let iteration = 0; iteration < 32; iteration++) {
|
|
88
|
+
const middle = (lower + upper) / 2;
|
|
89
|
+
if (columnsNeeded(middle) <= columnCount) upper = middle;
|
|
90
|
+
else lower = middle;
|
|
91
|
+
}
|
|
92
|
+
return Math.ceil(upper * 1e3) / 1e3;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Start the next section in the current page region, below the outgoing
|
|
96
|
+
* content.
|
|
97
|
+
*
|
|
98
|
+
* ECMA-376 §17.6.22: a `continuous` break normally keeps the current page
|
|
99
|
+
* geometry and defers the new size/margins to the next natural page break. But
|
|
100
|
+
* a break that changes page size or orientation cannot share a physical sheet
|
|
101
|
+
* with the preceding section, so Word and LibreOffice promote it to a page
|
|
102
|
+
* break (eigenpal/docx-editor#841).
|
|
103
|
+
*
|
|
104
|
+
* Compare against the last laid-out page without materializing one: a break
|
|
105
|
+
* before any content has no sheet to share, so it defers (the first content
|
|
106
|
+
* then opens a page with the new geometry) rather than stranding a blank
|
|
107
|
+
* leading page.
|
|
108
|
+
*/
|
|
109
|
+
function startSectionInPlace(paginator, nextSectionConfig, nextSectionIndex) {
|
|
110
|
+
const currentPage = paginator.states.at(-1)?.page;
|
|
111
|
+
const nextSize = nextSectionConfig.pageSize;
|
|
112
|
+
const pageSizeChanges = currentPage != null && (Math.round(nextSize.w) !== Math.round(currentPage.size.w) || Math.round(nextSize.h) !== Math.round(currentPage.size.h));
|
|
113
|
+
if (nextSectionIndex !== void 0) paginator.startSection({
|
|
114
|
+
sectionIndex: nextSectionIndex,
|
|
115
|
+
pageNumbering: nextSectionConfig.pageNumbering,
|
|
116
|
+
placement: pageSizeChanges ? SECTION_START_PLACEMENT.NEXT_PAGE : SECTION_START_PLACEMENT.CONTINUOUS
|
|
117
|
+
});
|
|
118
|
+
if (pageSizeChanges) {
|
|
119
|
+
paginator.updatePageLayout(nextSize, nextSectionConfig.margins);
|
|
120
|
+
if (!paginator.retargetCurrentBlankPage()) paginator.forcePageBreak({ coalesceBlankPage: true });
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
paginator.updatePageLayout(nextSize, nextSectionConfig.margins, false);
|
|
124
|
+
paginator.retargetCurrentBlankPage();
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Whether a section opening on `target` would give two consecutive sheets page
|
|
128
|
+
* numbers that are both odd or both even under `w:evenAndOddHeaders` (§17.10.1). Odd and
|
|
129
|
+
* even page numbers alternate between right- and left-hand sheets, so a
|
|
130
|
+
* `w:pgNumType w:start` restart that repeats whether the previous sheet's number is odd or even
|
|
131
|
+
* needs a blank sheet between them.
|
|
132
|
+
*/
|
|
133
|
+
function sectionStartRepeatsOddEven(paginator, target) {
|
|
134
|
+
if (target.page.headerFooterRefs?.evenAndOddHeaders !== true) return false;
|
|
135
|
+
const previous = paginator.states.at(-2)?.page;
|
|
136
|
+
if (previous === void 0 || previous.number !== target.page.number - 1) return false;
|
|
137
|
+
return previous.logicalNumber % 2 === target.page.logicalNumber % 2;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Keep the blank sheet the section break opened as a filler with no page
|
|
141
|
+
* furniture, and open the section again on the following sheet so its first
|
|
142
|
+
* page keeps the restarted page number.
|
|
143
|
+
*/
|
|
144
|
+
function insertOddEvenFillerPage(paginator, filler, sectionIndex, sectionConfig) {
|
|
145
|
+
filler.page.headerFooterRefs = {};
|
|
146
|
+
paginator.startSection({
|
|
147
|
+
sectionIndex,
|
|
148
|
+
pageNumbering: sectionConfig.pageNumbering
|
|
149
|
+
});
|
|
150
|
+
paginator.forcePageBreak();
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Handle a section break block.
|
|
154
|
+
* @param block - The section break block (current section's properties)
|
|
155
|
+
* @param paginator - The paginator instance
|
|
156
|
+
* @param nextSectionConfig - Page layout for the NEXT section
|
|
157
|
+
* @param nextSectionType - Break type of the NEXT section (how it starts relative to current)
|
|
158
|
+
*/
|
|
159
|
+
function handleSectionBreak(_block, paginator, nextSectionConfig, nextSectionType, nextSectionIndex) {
|
|
160
|
+
switch (nextSectionType) {
|
|
161
|
+
case "nextPage": {
|
|
162
|
+
paginator.updatePageLayout(nextSectionConfig.pageSize, nextSectionConfig.margins);
|
|
163
|
+
if (nextSectionIndex !== void 0) paginator.startSection({
|
|
164
|
+
sectionIndex: nextSectionIndex,
|
|
165
|
+
pageNumbering: nextSectionConfig.pageNumbering
|
|
166
|
+
});
|
|
167
|
+
const target = paginator.forcePageBreak({ coalesceBlankPage: true });
|
|
168
|
+
if (nextSectionIndex !== void 0 && sectionStartRepeatsOddEven(paginator, target)) insertOddEvenFillerPage(paginator, target, nextSectionIndex, nextSectionConfig);
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
case "evenPage":
|
|
172
|
+
if (paginator.forcePageBreak({ coalesceBlankPage: true }).page.number % 2 !== 0) paginator.forcePageBreak();
|
|
173
|
+
paginator.updatePageLayout(nextSectionConfig.pageSize, nextSectionConfig.margins);
|
|
174
|
+
if (nextSectionIndex !== void 0) paginator.startSection({
|
|
175
|
+
sectionIndex: nextSectionIndex,
|
|
176
|
+
pageNumbering: nextSectionConfig.pageNumbering
|
|
177
|
+
});
|
|
178
|
+
if (!paginator.retargetCurrentBlankPage()) panic("Even-page section target must be blank");
|
|
179
|
+
break;
|
|
180
|
+
case "oddPage":
|
|
181
|
+
if (paginator.forcePageBreak({ coalesceBlankPage: true }).page.number % 2 === 0) paginator.forcePageBreak();
|
|
182
|
+
paginator.updatePageLayout(nextSectionConfig.pageSize, nextSectionConfig.margins);
|
|
183
|
+
if (nextSectionIndex !== void 0) paginator.startSection({
|
|
184
|
+
sectionIndex: nextSectionIndex,
|
|
185
|
+
pageNumbering: nextSectionConfig.pageNumbering
|
|
186
|
+
});
|
|
187
|
+
if (!paginator.retargetCurrentBlankPage()) panic("Odd-page section target must be blank");
|
|
188
|
+
break;
|
|
189
|
+
case "nextColumn": {
|
|
190
|
+
const currentPage = paginator.states.at(-1)?.page;
|
|
191
|
+
if (currentPage === void 0 || !physicalColumnRegionIsShared({
|
|
192
|
+
pageSize: currentPage.size,
|
|
193
|
+
margins: currentPage.margins,
|
|
194
|
+
columns: paginator.columns
|
|
195
|
+
}, nextSectionConfig)) {
|
|
196
|
+
startSectionInPlace(paginator, nextSectionConfig, nextSectionIndex);
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
if (nextSectionIndex !== void 0) paginator.startSection({
|
|
200
|
+
sectionIndex: nextSectionIndex,
|
|
201
|
+
pageNumbering: nextSectionConfig.pageNumbering,
|
|
202
|
+
placement: SECTION_START_PLACEMENT.CONTINUOUS
|
|
203
|
+
});
|
|
204
|
+
paginator.updatePageLayout(nextSectionConfig.pageSize, nextSectionConfig.margins, false);
|
|
205
|
+
paginator.forceColumnBreak();
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
case "continuous":
|
|
209
|
+
startSectionInPlace(paginator, nextSectionConfig, nextSectionIndex);
|
|
210
|
+
break;
|
|
211
|
+
default:
|
|
212
|
+
}
|
|
213
|
+
paginator.updateColumns(nextSectionConfig.columns ?? DEFAULT_COLUMNS);
|
|
214
|
+
}
|
|
215
|
+
//#endregion
|
|
216
|
+
export { CONTINUE_PAGE_NUMBERING, DEFAULT_COLUMNS, balancedParagraphSectionHeight, collectSectionConfigs, handleSectionBreak };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { TableBlock, TableMeasure } from "./types.js";
|
|
2
|
+
import { createPaginator } from "./paginator.js";
|
|
3
|
+
//#region src/layout-engine/tableLayout.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Calculate total height of header rows from their measures.
|
|
6
|
+
*/
|
|
7
|
+
declare function getHeaderRowsHeight(measure: TableMeasure, headerRowCount: number): number;
|
|
8
|
+
/**
|
|
9
|
+
* Layout a table block onto pages.
|
|
10
|
+
*/
|
|
11
|
+
declare function layoutTable(block: TableBlock, measure: TableMeasure, paginator: ReturnType<typeof createPaginator>, footnoteHeightById?: Map<number, number>): void;
|
|
12
|
+
/**
|
|
13
|
+
* Layout a floating table (anchored) without advancing the cursor.
|
|
14
|
+
*/
|
|
15
|
+
declare function layoutFloatingTable(block: TableBlock, measure: TableMeasure, paginator: ReturnType<typeof createPaginator>, contentWidth: number): void;
|
|
16
|
+
//#endregion
|
|
17
|
+
export { getHeaderRowsHeight, layoutFloatingTable, layoutTable };
|
|
@@ -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 };
|
|
@@ -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 };
|