@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.
- package/dist/ai-edits/apply.js +565 -121
- package/dist/ai-edits/headless.d.ts +37 -1
- package/dist/ai-edits/headless.js +47 -16
- package/dist/ai-edits/minimal-replacement.d.ts +64 -0
- package/dist/ai-edits/minimal-replacement.js +282 -0
- package/dist/controller/layoutPipeline.js +11 -1
- package/dist/display-list/build/paragraphPrimitives.js +63 -7
- package/dist/display-list/build/textBoxPrimitives.js +1 -1
- package/dist/display-list/build/unsupported.d.ts +2 -2
- package/dist/display-list/build/unsupported.js +0 -0
- package/dist/display-list/dom/renderDisplayListToDom.d.ts +10 -1
- package/dist/display-list/dom/renderDisplayListToDom.js +8 -8
- 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/docx/blockContentParser.js +2 -2
- package/dist/docx/bulletMarkers.d.ts +18 -2
- package/dist/docx/bulletMarkers.js +27 -2
- package/dist/docx/drawingGroupChildren.d.ts +60 -0
- package/dist/docx/drawingGroupChildren.js +176 -0
- package/dist/docx/drawingUtils.js +3 -0
- package/dist/docx/groupDrawingParser.d.ts +7 -1
- package/dist/docx/groupDrawingParser.js +123 -32
- package/dist/docx/packageParts.d.ts +45 -1
- package/dist/docx/packageParts.js +93 -2
- package/dist/docx/paraIdAttribute.d.ts +3 -2
- package/dist/docx/paragraphParser.js +91 -7
- package/dist/docx/paragraphTextBoxEnrichment.js +126 -4
- package/dist/docx/runParser.js +10 -0
- package/dist/docx/selectiveSaveFlags.d.ts +10 -3
- package/dist/docx/selectiveSaveFlags.js +1 -1
- package/dist/docx/selectiveXmlPatch.d.ts +56 -34
- package/dist/docx/selectiveXmlPatch.js +222 -115
- package/dist/docx/serializer/groupTextBoxWriteBack.d.ts +12 -0
- package/dist/docx/serializer/groupTextBoxWriteBack.js +101 -0
- package/dist/docx/serializer/paragraphSerializer.js +5 -4
- package/dist/docx/serializer/runSerializer.d.ts +5 -1
- package/dist/docx/serializer/runSerializer.js +4 -3
- package/dist/docx/shapeAlternateContent.d.ts +25 -0
- package/dist/docx/shapeAlternateContent.js +52 -0
- package/dist/docx/textBoxParser.d.ts +10 -2
- package/dist/docx/textBoxParser.js +9 -3
- 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/internal/headlessRevisionResolution.js +50 -13
- 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 -2060
- 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 +4 -19
- package/dist/layout-engine/index.js +14 -910
- package/dist/layout-engine/keep-together.d.ts +5 -2
- package/dist/layout-engine/keep-together.js +5 -1
- package/dist/layout-engine/layoutFlowShared.d.ts +6 -0
- package/dist/layout-engine/layoutFlowShared.js +8 -0
- package/dist/layout-engine/measure/advanceComposition.js +49 -3
- 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 +7 -4
- package/dist/layout-engine/measure/measureContainer.js +46 -3
- package/dist/layout-engine/measure/measureHelpers.js +13 -3
- package/dist/layout-engine/measure/measureParagraph.d.ts +1 -20
- package/dist/layout-engine/measure/measureParagraph.js +41 -780
- 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/measure/smallCapsCasing.d.ts +56 -0
- package/dist/layout-engine/measure/smallCapsCasing.js +88 -0
- package/dist/layout-engine/paginator.d.ts +2 -0
- package/dist/layout-engine/paginator.js +5 -1
- package/dist/layout-engine/paragraphLayout.d.ts +23 -0
- package/dist/layout-engine/paragraphLayout.js +204 -0
- package/dist/layout-engine/paragraphSpacing.d.ts +4 -2
- package/dist/layout-engine/paragraphSpacing.js +2 -1
- 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/tableRowBreak.d.ts +8 -1
- package/dist/layout-engine/tableRowBreak.js +22 -1
- package/dist/layout-engine/textBoxLayout.d.ts +15 -0
- package/dist/layout-engine/textBoxLayout.js +137 -0
- package/dist/layout-engine/types.d.ts +21 -2
- package/dist/layout-painter/renderPage.js +1 -0
- package/dist/layout-painter/renderParagraph.js +41 -4
- package/dist/layout-painter/renderTextBox.d.ts +12 -2
- package/dist/layout-painter/renderTextBox.js +26 -2
- package/dist/paged-layout/sectionBlockWidths.d.ts +2 -1
- package/dist/paged-layout/sectionBlockWidths.js +1 -1
- package/dist/prosemirror/alternateContentAttrs.d.ts +9 -0
- package/dist/prosemirror/alternateContentAttrs.js +35 -0
- package/dist/prosemirror/attrs/index.js +90 -0
- package/dist/prosemirror/commands/comments.js +51 -41
- package/dist/prosemirror/contentControlRevisions.d.ts +43 -0
- package/dist/prosemirror/contentControlRevisions.js +84 -0
- package/dist/prosemirror/conversion/fromProseDoc.js +46 -22
- package/dist/prosemirror/conversion/toProseDoc.js +42 -19
- package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.d.ts +7 -7
- package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +7 -7
- package/dist/prosemirror/extensions/nodes/FieldExtension.js +6 -3
- package/dist/prosemirror/extensions/nodes/SdtExtension.js +9 -2
- package/dist/prosemirror/extensions/nodes/ShapeExtension.js +1 -0
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +3 -0
- package/dist/prosemirror/listMarker.js +2 -2
- package/dist/prosemirror/paragraphDirection.d.ts +29 -2
- package/dist/prosemirror/paragraphDirection.js +21 -2
- package/dist/prosemirror/plugins/suggestionMode.js +37 -8
- package/dist/prosemirror/rejoinRunCarriers.d.ts +27 -0
- package/dist/prosemirror/rejoinRunCarriers.js +66 -0
- package/dist/prosemirror/runIdentityAcrossRevisions.d.ts +10 -0
- package/dist/prosemirror/runIdentityAcrossRevisions.js +90 -0
- package/dist/prosemirror/schema/nodes.d.ts +37 -5
- package/dist/server.d.ts +3 -2
- package/dist/types/content.d.ts +2 -2
- package/package.json +2 -2
- package/dist/layout-engine/justifiedLineFit.d.ts +0 -7
- package/dist/layout-engine/justifiedLineFit.js +0 -6
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
import { emuToPixels } from "../utils/units.js";
|
|
2
1
|
import { reflowFootnoteColumns } from "./footnoteColumnReflow.js";
|
|
3
2
|
import { resolveSectionHeaderFooterRefs } from "./headerFooterRefs.js";
|
|
3
|
+
import { layoutImage } from "./imageLayout.js";
|
|
4
4
|
import { calculateChainHeight, computeKeepNextChains, getMidChainIndices, hasKeepLines, hasPageBreakBefore } from "./keep-together.js";
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import { resolveTableInlineOffset } from "./measure/tableInlinePlacement.js";
|
|
5
|
+
import "./layoutFlowShared.js";
|
|
6
|
+
import { measuredLineAdvance } from "./lineFlow.js";
|
|
8
7
|
import { annotateNoteSeparators, continuesNoteArea, createNoteAreaContinuation } from "./noteAreaFlow.js";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
8
|
+
import { createPaginator } from "./paginator.js";
|
|
9
|
+
import { layoutParagraph } from "./paragraphLayout.js";
|
|
10
|
+
import { paragraphsShareStyle, resolveEffectiveParagraphSpacingTree } from "./paragraphSpacing.js";
|
|
12
11
|
import { assertExhaustiveFlowBlock, findPageIndexContainingPmPos } from "./pmPageIndex.js";
|
|
13
12
|
import { INITIAL_RENDERED_BREAK_STATE, PAGE_ADVANCE, reconcileAfterBlock, reconcileBreakBeforeBlock, recordReflowBoundary } from "./renderedBreakReconciliation.js";
|
|
14
|
-
import { applyPendingToActive, createInitialSectionState, getEffectiveColumns, getEffectiveMargins, getEffectivePageSize, normalizeSectionBreakType,
|
|
13
|
+
import { applyPendingToActive, createInitialSectionState, getEffectiveColumns, getEffectiveMargins, getEffectivePageSize, normalizeSectionBreakType, scheduleSectionBreak } from "./section-breaks.js";
|
|
14
|
+
import { CONTINUE_PAGE_NUMBERING, DEFAULT_COLUMNS, balancedParagraphSectionHeight, collectSectionConfigs, handleSectionBreak } from "./sectionLayout.js";
|
|
15
15
|
import { applySectionVerticalAlignment } from "./sectionVerticalAlignment.js";
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
16
|
+
import { getHeaderRowsHeight, layoutFloatingTable, layoutTable } from "./tableLayout.js";
|
|
17
|
+
import { tableKeepNextOpeningHeight } from "./tableRowBreak.js";
|
|
18
|
+
import { layoutTextBox } from "./textBoxLayout.js";
|
|
18
19
|
import { DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, NOTE_SEPARATOR_RULE_THICKNESS, NOTE_SEPARATOR_WIDTH_FRACTION, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isListNumPr, isTextWrappingFloatingImageRun, resolveTableCellPadding, resolveTableWidthPx, stripFlowBlockPmAnchors, tableColumnsArePinned } from "./types.js";
|
|
19
20
|
import { panic } from "better-result";
|
|
20
21
|
//#region src/layout-engine/index.ts
|
|
@@ -23,92 +24,6 @@ import { panic } from "better-result";
|
|
|
23
24
|
*
|
|
24
25
|
* Converts blocks + measures into positioned fragments on pages.
|
|
25
26
|
*/
|
|
26
|
-
const RENDERED_BREAK_REFLOW_TOLERANCE_LINES = 3;
|
|
27
|
-
const DEFAULT_COLUMNS = {
|
|
28
|
-
count: 1,
|
|
29
|
-
gap: 0
|
|
30
|
-
};
|
|
31
|
-
const CONTINUE_PAGE_NUMBERING = { type: "continue" };
|
|
32
|
-
function collectSectionConfigs(blocks, initialConfig, finalConfig) {
|
|
33
|
-
const configs = [];
|
|
34
|
-
const breakIndices = [];
|
|
35
|
-
let previousConfig = initialConfig;
|
|
36
|
-
for (let index = 0; index < blocks.length; index += 1) {
|
|
37
|
-
const block = blocks[index];
|
|
38
|
-
if (block?.kind !== "sectionBreak") continue;
|
|
39
|
-
const sectionBreak = block;
|
|
40
|
-
const config = {
|
|
41
|
-
pageSize: sectionBreak.pageSize ?? previousConfig.pageSize,
|
|
42
|
-
margins: sectionBreak.margins ?? previousConfig.margins,
|
|
43
|
-
pageNumbering: sectionBreak.pageNumbering ?? CONTINUE_PAGE_NUMBERING
|
|
44
|
-
};
|
|
45
|
-
if (sectionBreak.columns !== void 0) config.columns = sectionBreak.columns;
|
|
46
|
-
configs.push(config);
|
|
47
|
-
breakIndices.push(index);
|
|
48
|
-
previousConfig = configs.at(-1) ?? previousConfig;
|
|
49
|
-
}
|
|
50
|
-
configs.push(finalConfig);
|
|
51
|
-
return {
|
|
52
|
-
configs,
|
|
53
|
-
breakIndices
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
function balancedParagraphSectionHeight({ blocks, measures, startIndex, endIndex, incomingSpacing, columnCount, availableHeight }) {
|
|
57
|
-
if (columnCount <= 1 || startIndex >= endIndex || availableHeight <= 0) return;
|
|
58
|
-
const lineUnits = [];
|
|
59
|
-
let totalHeight = 0;
|
|
60
|
-
let tallestUnit = 0;
|
|
61
|
-
let trailingSpacing = incomingSpacing;
|
|
62
|
-
for (let index = startIndex; index < endIndex; index++) {
|
|
63
|
-
const block = blocks[index];
|
|
64
|
-
const measure = measures[index];
|
|
65
|
-
if (block?.kind !== "paragraph" || measure?.kind !== "paragraph") return;
|
|
66
|
-
if (block.attrs?.keepNext === true || block.attrs?.keepLines === true || block.runs.some((run) => run.kind === "text" && run.footnoteRefId !== void 0)) return;
|
|
67
|
-
const leadingSpacing = collapseParagraphSpacing({
|
|
68
|
-
before: getParagraphSpacingBefore(block),
|
|
69
|
-
after: trailingSpacing
|
|
70
|
-
});
|
|
71
|
-
if (measure.lines.length === 0) {
|
|
72
|
-
if (leadingSpacing > 0) {
|
|
73
|
-
lineUnits.push(leadingSpacing);
|
|
74
|
-
totalHeight += leadingSpacing;
|
|
75
|
-
tallestUnit = Math.max(tallestUnit, leadingSpacing);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
for (let lineIndex = 0; lineIndex < measure.lines.length; lineIndex++) {
|
|
79
|
-
const line = measure.lines[lineIndex];
|
|
80
|
-
if (!line) continue;
|
|
81
|
-
const unitHeight = measuredLineAdvance(line) + (lineIndex === 0 ? leadingSpacing : 0);
|
|
82
|
-
lineUnits.push(unitHeight);
|
|
83
|
-
totalHeight += unitHeight;
|
|
84
|
-
tallestUnit = Math.max(tallestUnit, unitHeight);
|
|
85
|
-
}
|
|
86
|
-
if (tallestUnit > availableHeight || totalHeight > availableHeight * columnCount) return;
|
|
87
|
-
trailingSpacing = getParagraphSpacingAfter(block);
|
|
88
|
-
}
|
|
89
|
-
if (totalHeight <= 0) return;
|
|
90
|
-
const columnsNeeded = (targetHeight) => {
|
|
91
|
-
let usedHeight = 0;
|
|
92
|
-
let usedColumns = 1;
|
|
93
|
-
for (const unitHeight of lineUnits) if (usedHeight > 0 && usedHeight + unitHeight > targetHeight) {
|
|
94
|
-
usedColumns += 1;
|
|
95
|
-
usedHeight = unitHeight;
|
|
96
|
-
} else usedHeight += unitHeight;
|
|
97
|
-
return usedColumns;
|
|
98
|
-
};
|
|
99
|
-
let lower = Math.max(tallestUnit, totalHeight / columnCount);
|
|
100
|
-
let upper = Math.min(totalHeight, availableHeight);
|
|
101
|
-
if (columnsNeeded(upper) > columnCount) return;
|
|
102
|
-
for (let iteration = 0; iteration < 32; iteration++) {
|
|
103
|
-
const middle = (lower + upper) / 2;
|
|
104
|
-
if (columnsNeeded(middle) <= columnCount) upper = middle;
|
|
105
|
-
else lower = middle;
|
|
106
|
-
}
|
|
107
|
-
return Math.ceil(upper * 1e3) / 1e3;
|
|
108
|
-
}
|
|
109
|
-
function hasWidowControl(block) {
|
|
110
|
-
return block.attrs?.widowControl !== false;
|
|
111
|
-
}
|
|
112
27
|
/**
|
|
113
28
|
* Apply contextual spacing suppression (OOXML §17.3.1.9).
|
|
114
29
|
*
|
|
@@ -194,6 +109,7 @@ function layoutDocumentPass(blocks, measures, options) {
|
|
|
194
109
|
...options.mirrorMargins === true ? { mirrorMargins: true } : {},
|
|
195
110
|
...options.firstPageMargins !== void 0 ? { firstPageMargins: options.firstPageMargins } : {},
|
|
196
111
|
...options.sectionEvenPageMargins !== void 0 ? { sectionEvenPageMargins: options.sectionEvenPageMargins } : {},
|
|
112
|
+
...options.sectionFirstPageMargins !== void 0 ? { sectionFirstPageMargins: options.sectionFirstPageMargins } : {},
|
|
197
113
|
columns: initialConfig.columns ?? DEFAULT_COLUMNS,
|
|
198
114
|
pageNumbering: initialConfig.pageNumbering,
|
|
199
115
|
...options.footnoteReservedHeights !== void 0 ? { footnoteReservedHeights: options.footnoteReservedHeights } : {},
|
|
@@ -216,7 +132,7 @@ function layoutDocumentPass(blocks, measures, options) {
|
|
|
216
132
|
if (noteAreaContinuation !== void 0) paginator.setPageTopContinuation(continuesNoteArea(options.noteAreas, block) ? noteAreaContinuation : void 0);
|
|
217
133
|
const firstLine = measure.kind === "paragraph" ? measure.lines.at(0) : void 0;
|
|
218
134
|
const firstLineAdvance = firstLine ? measuredLineAdvance(firstLine) : 0;
|
|
219
|
-
const renderedBreakNeedsSnap = measure.kind === "paragraph" && (!paginator.fits(measure.totalHeight) || firstLineAdvance > 0 && paginator.getAvailableHeight() <= firstLineAdvance *
|
|
135
|
+
const renderedBreakNeedsSnap = measure.kind === "paragraph" && (!paginator.fits(measure.totalHeight) || firstLineAdvance > 0 && paginator.getAvailableHeight() <= firstLineAdvance * 3);
|
|
220
136
|
const hasExplicitPageBreak = block.kind === "pageBreak" || hasPageBreakBefore(block);
|
|
221
137
|
const breakDecision = reconcileBreakBeforeBlock({
|
|
222
138
|
state: renderedBreakState,
|
|
@@ -234,7 +150,7 @@ function layoutDocumentPass(blocks, measures, options) {
|
|
|
234
150
|
renderedBreakState = breakDecision.state;
|
|
235
151
|
const chain = keepNextChains.get(i);
|
|
236
152
|
if (chain && !midChainIndices.has(i)) {
|
|
237
|
-
const chainHeight = calculateChainHeight(chain, blocks, measures, paginator.getCurrentState().trailingSpacing);
|
|
153
|
+
const chainHeight = calculateChainHeight(chain, blocks, measures, paginator.getCurrentState().trailingSpacing, tableKeepNextOpeningHeight);
|
|
238
154
|
const pageBeforeChainLayout = paginator.getCurrentState().page.number;
|
|
239
155
|
paginator.ensureFits(chainHeight);
|
|
240
156
|
if (paginator.getCurrentState().page.number > pageBeforeChainLayout) renderedBreakState = recordReflowBoundary(renderedBreakState, true);
|
|
@@ -318,817 +234,5 @@ function layoutDocumentPass(blocks, measures, options) {
|
|
|
318
234
|
...options.pageGap !== void 0 ? { pageGap: options.pageGap } : {}
|
|
319
235
|
};
|
|
320
236
|
}
|
|
321
|
-
/**
|
|
322
|
-
* Footnote refs whose run sits in line `[fromRun..toRun]`, with their
|
|
323
|
-
* pre-measured content heights. Empty when the engine isn't tracking
|
|
324
|
-
* dynamic fn demand. Used by `layoutParagraph` to (a) reserve fn
|
|
325
|
-
* height per line and (b) record the IDs on the host page so the
|
|
326
|
-
* painter renders the fn on the page where the ref-bearing line
|
|
327
|
-
* actually landed — even when the paragraph splits across pages
|
|
328
|
-
* (fragment pmStart/pmEnd is paragraph-wide and cannot disambiguate
|
|
329
|
-
* between split halves).
|
|
330
|
-
*/
|
|
331
|
-
function getLineFootnoteRefs(block, fromRun, toRun, fnHeights) {
|
|
332
|
-
if (!fnHeights) return {
|
|
333
|
-
ids: [],
|
|
334
|
-
height: 0
|
|
335
|
-
};
|
|
336
|
-
const ids = [];
|
|
337
|
-
let height = 0;
|
|
338
|
-
for (let r = fromRun; r <= toRun; r++) {
|
|
339
|
-
const run = block.runs[r];
|
|
340
|
-
if (!run || run.kind !== "text") continue;
|
|
341
|
-
const id = run.footnoteRefId;
|
|
342
|
-
if (id === void 0) continue;
|
|
343
|
-
const h = fnHeights.get(id);
|
|
344
|
-
if (h !== void 0) {
|
|
345
|
-
ids.push(id);
|
|
346
|
-
height += h;
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
return {
|
|
350
|
-
ids,
|
|
351
|
-
height
|
|
352
|
-
};
|
|
353
|
-
}
|
|
354
|
-
function projectedFootnoteReserveGrowth(state, additionalDemandHeight) {
|
|
355
|
-
const projectedDemand = state.footnoteDemandHeight + additionalDemandHeight;
|
|
356
|
-
return Math.max(state.footnoteHeightFloor, projectedDemand) - state.footnoteHeight;
|
|
357
|
-
}
|
|
358
|
-
function layoutParagraph({ block, measure, paginator, contentWidth, footnoteHeightById, suppressSpaceBefore }) {
|
|
359
|
-
const lines = measure.lines;
|
|
360
|
-
if (lines.length === 0) {
|
|
361
|
-
const spaceBefore = suppressSpaceBefore ? 0 : getParagraphSpacingBefore(block);
|
|
362
|
-
const spaceAfter = getParagraphSpacingAfter(block);
|
|
363
|
-
const state = paginator.getCurrentState();
|
|
364
|
-
const fragment = {
|
|
365
|
-
kind: "paragraph",
|
|
366
|
-
...block.attrs?.suppressEmptyParagraphHeight === true ? { paginationRole: "empty-carrier" } : {},
|
|
367
|
-
blockId: block.id,
|
|
368
|
-
x: paginator.getColumnX(state.columnIndex),
|
|
369
|
-
y: state.cursorY + spaceBefore,
|
|
370
|
-
width: contentWidth,
|
|
371
|
-
height: 0,
|
|
372
|
-
fromLine: 0,
|
|
373
|
-
toLine: 0,
|
|
374
|
-
...block.pmStart !== void 0 ? { pmStart: block.pmStart } : {},
|
|
375
|
-
...block.pmEnd !== void 0 ? { pmEnd: block.pmEnd } : {},
|
|
376
|
-
...block.sdtGroups ? { sdtGroups: block.sdtGroups } : {}
|
|
377
|
-
};
|
|
378
|
-
paginator.addFragment(fragment, 0, spaceBefore, spaceAfter);
|
|
379
|
-
return;
|
|
380
|
-
}
|
|
381
|
-
const spaceBefore = suppressSpaceBefore ? 0 : getParagraphSpacingBefore(block);
|
|
382
|
-
const spaceAfter = getParagraphSpacingAfter(block);
|
|
383
|
-
let keepLinesDemand;
|
|
384
|
-
if (lines.length > 1 && hasKeepLines(block)) {
|
|
385
|
-
let footnoteHeight = 0;
|
|
386
|
-
for (const line of lines) footnoteHeight += getLineFootnoteRefs(block, line.fromRun, line.toRun, footnoteHeightById).height;
|
|
387
|
-
keepLinesDemand = {
|
|
388
|
-
linesHeight: measuredLineRangeHeight(lines, 0, lines.length),
|
|
389
|
-
footnoteHeight
|
|
390
|
-
};
|
|
391
|
-
}
|
|
392
|
-
let keepLinesMoveAttempted = false;
|
|
393
|
-
let currentLineIndex = 0;
|
|
394
|
-
while (currentLineIndex < lines.length) {
|
|
395
|
-
const state = paginator.getCurrentState();
|
|
396
|
-
const availableHeight = paginator.getAvailableHeight();
|
|
397
|
-
const currentLine = lines.at(currentLineIndex);
|
|
398
|
-
const markerAlreadySatisfied = state.columnIndex === 0 && state.cursorY === state.topMargin && state.page.fragments.length === 0;
|
|
399
|
-
if (currentLine?.renderedPageBreakBefore === true && !markerAlreadySatisfied && availableHeight <= measuredLineAdvance(currentLine) * RENDERED_BREAK_REFLOW_TOLERANCE_LINES) {
|
|
400
|
-
paginator.forcePageBreak();
|
|
401
|
-
continue;
|
|
402
|
-
}
|
|
403
|
-
if (currentLineIndex === 0 && state.trailingSpacing > 0 && state.cursorY !== state.topMargin) {
|
|
404
|
-
const firstLine = lines[0];
|
|
405
|
-
const firstLineRefs = getLineFootnoteRefs(block, firstLine.fromRun, firstLine.toRun, footnoteHeightById);
|
|
406
|
-
const firstLineHeight = measuredLineAdvance(firstLine) + projectedFootnoteReserveGrowth(state, firstLineRefs.height);
|
|
407
|
-
const collapsedLead = collapseParagraphSpacing({
|
|
408
|
-
before: spaceBefore,
|
|
409
|
-
after: state.trailingSpacing
|
|
410
|
-
});
|
|
411
|
-
const columnCapacity = state.contentBottom - state.topMargin;
|
|
412
|
-
if (collapsedLead + firstLineHeight > availableHeight && spaceBefore + firstLineHeight <= columnCapacity) {
|
|
413
|
-
paginator.ensureFits(collapsedLead + firstLineHeight);
|
|
414
|
-
continue;
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
if (keepLinesDemand !== void 0 && !keepLinesMoveAttempted && currentLineIndex === 0 && state.cursorY !== state.topMargin) {
|
|
418
|
-
const wholeHeight = keepLinesDemand.linesHeight + projectedFootnoteReserveGrowth(state, keepLinesDemand.footnoteHeight);
|
|
419
|
-
const collapsedLead = collapseParagraphSpacing({
|
|
420
|
-
before: spaceBefore,
|
|
421
|
-
after: state.trailingSpacing
|
|
422
|
-
});
|
|
423
|
-
const columnCapacity = state.contentBottom - state.topMargin;
|
|
424
|
-
if (collapsedLead + wholeHeight > availableHeight && spaceBefore + wholeHeight <= columnCapacity) {
|
|
425
|
-
keepLinesMoveAttempted = true;
|
|
426
|
-
paginator.ensureFits(collapsedLead + wholeHeight);
|
|
427
|
-
continue;
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
let linesHeight = 0;
|
|
431
|
-
let linesFnHeight = 0;
|
|
432
|
-
const linesFnIds = [];
|
|
433
|
-
let fittingLines = 0;
|
|
434
|
-
let forcePageBreakAfterFragment = false;
|
|
435
|
-
const firstFragmentSpaceBefore = currentLineIndex === 0 ? collapseParagraphSpacing({
|
|
436
|
-
before: spaceBefore,
|
|
437
|
-
after: state.trailingSpacing
|
|
438
|
-
}) : 0;
|
|
439
|
-
for (let j = currentLineIndex; j < lines.length; j++) {
|
|
440
|
-
const line = lines[j];
|
|
441
|
-
const lineAdvance = measuredLineAdvance(line);
|
|
442
|
-
if (j > currentLineIndex && line.renderedPageBreakBefore === true && availableHeight - (linesHeight + firstFragmentSpaceBefore + linesFnHeight) <= lineAdvance * RENDERED_BREAK_REFLOW_TOLERANCE_LINES) {
|
|
443
|
-
forcePageBreakAfterFragment = true;
|
|
444
|
-
break;
|
|
445
|
-
}
|
|
446
|
-
const lineRefs = getLineFootnoteRefs(block, line.fromRun, line.toRun, footnoteHeightById);
|
|
447
|
-
const totalWithLine = linesHeight + lineAdvance;
|
|
448
|
-
const footnoteGrowth = projectedFootnoteReserveGrowth(state, linesFnHeight + lineRefs.height);
|
|
449
|
-
if (totalWithLine + firstFragmentSpaceBefore + footnoteGrowth <= availableHeight || fittingLines === 0) {
|
|
450
|
-
linesHeight = totalWithLine;
|
|
451
|
-
linesFnHeight += lineRefs.height;
|
|
452
|
-
for (const id of lineRefs.ids) linesFnIds.push(id);
|
|
453
|
-
fittingLines++;
|
|
454
|
-
} else break;
|
|
455
|
-
}
|
|
456
|
-
let forceBreakAfterFragment = false;
|
|
457
|
-
if (hasWidowControl(block)) {
|
|
458
|
-
const remainingAfter = lines.length - (currentLineIndex + fittingLines);
|
|
459
|
-
if (fittingLines > 1 && remainingAfter === 1) {
|
|
460
|
-
if (currentLineIndex === 0 && fittingLines === 2 && state.cursorY !== state.topMargin) {
|
|
461
|
-
paginator.forceColumnBreak();
|
|
462
|
-
continue;
|
|
463
|
-
}
|
|
464
|
-
fittingLines -= 1;
|
|
465
|
-
forceBreakAfterFragment = true;
|
|
466
|
-
linesHeight = 0;
|
|
467
|
-
linesFnHeight = 0;
|
|
468
|
-
linesFnIds.length = 0;
|
|
469
|
-
for (let j = currentLineIndex; j < currentLineIndex + fittingLines; j++) {
|
|
470
|
-
const line = lines[j];
|
|
471
|
-
linesHeight += measuredLineAdvance(line);
|
|
472
|
-
const lineRefs = getLineFootnoteRefs(block, line.fromRun, line.toRun, footnoteHeightById);
|
|
473
|
-
linesFnHeight += lineRefs.height;
|
|
474
|
-
for (const id of lineRefs.ids) linesFnIds.push(id);
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
const isFirstFragment = currentLineIndex === 0;
|
|
479
|
-
const isLastFragment = currentLineIndex + fittingLines >= lines.length;
|
|
480
|
-
const effectiveSpaceBefore = isFirstFragment ? spaceBefore : 0;
|
|
481
|
-
const effectiveSpaceAfter = isLastFragment ? spaceAfter : 0;
|
|
482
|
-
const pmRange = getParagraphFragmentPmRange(block, measure, currentLineIndex, currentLineIndex + fittingLines);
|
|
483
|
-
const fragment = {
|
|
484
|
-
kind: "paragraph",
|
|
485
|
-
...block.attrs?.suppressEmptyParagraphHeight === true ? { paginationRole: "empty-carrier" } : {},
|
|
486
|
-
blockId: block.id,
|
|
487
|
-
x: paginator.getColumnX(state.columnIndex),
|
|
488
|
-
y: 0,
|
|
489
|
-
width: contentWidth,
|
|
490
|
-
height: linesHeight,
|
|
491
|
-
fromLine: currentLineIndex,
|
|
492
|
-
toLine: currentLineIndex + fittingLines,
|
|
493
|
-
...pmRange.pmStart !== void 0 ? { pmStart: pmRange.pmStart } : {},
|
|
494
|
-
...pmRange.pmEnd !== void 0 ? { pmEnd: pmRange.pmEnd } : {},
|
|
495
|
-
...!isFirstFragment ? { continuesFromPrev: true } : {},
|
|
496
|
-
...!isLastFragment ? { continuesOnNext: true } : {},
|
|
497
|
-
...block.sdtGroups ? { sdtGroups: block.sdtGroups } : {}
|
|
498
|
-
};
|
|
499
|
-
if (linesFnHeight > 0) {
|
|
500
|
-
const stateBefore = paginator.getCurrentState();
|
|
501
|
-
paginator.ensureFits(effectiveSpaceBefore + linesHeight + projectedFootnoteReserveGrowth(stateBefore, linesFnHeight));
|
|
502
|
-
const stateAfter = paginator.getCurrentState();
|
|
503
|
-
if (stateAfter.footnoteDemandHeight === 0) paginator.ensureFits(effectiveSpaceBefore + linesHeight + projectedFootnoteReserveGrowth(stateAfter, linesFnHeight + 12));
|
|
504
|
-
}
|
|
505
|
-
fragment.y = paginator.addFragment(fragment, linesHeight, effectiveSpaceBefore, effectiveSpaceAfter).y;
|
|
506
|
-
if (linesFnHeight > 0) paginator.addFootnoteHeight(linesFnHeight, linesFnIds);
|
|
507
|
-
currentLineIndex += fittingLines;
|
|
508
|
-
if (currentLineIndex < lines.length) if (forcePageBreakAfterFragment) paginator.forcePageBreak();
|
|
509
|
-
else if (forceBreakAfterFragment) paginator.forceColumnBreak();
|
|
510
|
-
else paginator.ensureFits(measuredLineAdvance(lines[currentLineIndex]));
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
/**
|
|
514
|
-
* Count consecutive header rows at the start of a table.
|
|
515
|
-
* Header rows are marked with isHeader: true in the block data.
|
|
516
|
-
*/
|
|
517
|
-
function countHeaderRows(block) {
|
|
518
|
-
let count = 0;
|
|
519
|
-
for (const row of block.rows) if (row.isHeader) count++;
|
|
520
|
-
else break;
|
|
521
|
-
return count;
|
|
522
|
-
}
|
|
523
|
-
/**
|
|
524
|
-
* Calculate total height of header rows from their measures.
|
|
525
|
-
*/
|
|
526
|
-
function getHeaderRowsHeight(measure, headerRowCount) {
|
|
527
|
-
let height = 0;
|
|
528
|
-
for (let i = 0; i < headerRowCount && i < measure.rows.length; i++) height += measure.rows[i].height;
|
|
529
|
-
return height;
|
|
530
|
-
}
|
|
531
|
-
const tableRowStartsWithRenderedPageBreak = (block, rowIndex) => {
|
|
532
|
-
const row = block.rows[rowIndex];
|
|
533
|
-
const visibleCells = row?.cells.filter((cell) => cell.blocks.some((cellBlock) => cellBlock.kind !== "paragraph" || !isEmptyParagraph(cellBlock)));
|
|
534
|
-
if (!visibleCells || visibleCells.length === 0) return false;
|
|
535
|
-
const startsWithRenderedPageBreak = (cell) => {
|
|
536
|
-
const firstVisibleBlock = cell.blocks.find((cellBlock) => cellBlock.kind !== "paragraph" || !isEmptyParagraph(cellBlock));
|
|
537
|
-
return firstVisibleBlock?.kind === "paragraph" && firstVisibleBlock.attrs?.renderedPageBreakBefore === true;
|
|
538
|
-
};
|
|
539
|
-
if (row?.heightRule === "exact") return startsWithRenderedPageBreak(visibleCells[0]);
|
|
540
|
-
return visibleCells.every(startsWithRenderedPageBreak);
|
|
541
|
-
};
|
|
542
|
-
const getVerticallyMergedRows = (block) => {
|
|
543
|
-
const mergedRows = /* @__PURE__ */ new Set();
|
|
544
|
-
for (let rowIndex = 0; rowIndex < block.rows.length; rowIndex += 1) {
|
|
545
|
-
const row = block.rows[rowIndex];
|
|
546
|
-
if (!row) continue;
|
|
547
|
-
for (const cell of row.cells) {
|
|
548
|
-
const rowSpan = cell.rowSpan ?? 1;
|
|
549
|
-
if (rowSpan <= 1) continue;
|
|
550
|
-
for (let mergedRowIndex = rowIndex; mergedRowIndex < Math.min(block.rows.length, rowIndex + rowSpan); mergedRowIndex += 1) mergedRows.add(mergedRowIndex);
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
return mergedRows;
|
|
554
|
-
};
|
|
555
|
-
const flowBlockHasTrackedChanges = (block) => {
|
|
556
|
-
if (block.kind === "paragraph") return block.runs.some((run) => {
|
|
557
|
-
if (run.kind === "lineBreak") return false;
|
|
558
|
-
return run.isInsertion || run.isDeletion;
|
|
559
|
-
});
|
|
560
|
-
if (block.kind === "table") return block.rows.some((row) => row.cells.some((cell) => cell.blocks.some(flowBlockHasTrackedChanges)));
|
|
561
|
-
if (block.kind === "textBox") return block.content.some(flowBlockHasTrackedChanges);
|
|
562
|
-
return false;
|
|
563
|
-
};
|
|
564
|
-
const tableRowHasTrackedChanges = (block, rowIndex) => block.rows[rowIndex]?.cells.some((cell) => cell.blocks.some(flowBlockHasTrackedChanges)) ?? false;
|
|
565
|
-
/**
|
|
566
|
-
* Layout a table block onto pages.
|
|
567
|
-
*/
|
|
568
|
-
function layoutTable(block, measure, paginator, footnoteHeightById) {
|
|
569
|
-
const rows = measure.rows;
|
|
570
|
-
if (rows.length === 0) return;
|
|
571
|
-
const rowFootnoteIds = block.rows.map((row) => collectTableRowFootnoteIds(row, footnoteHeightById));
|
|
572
|
-
const headerRowCount = countHeaderRows(block);
|
|
573
|
-
const headerRowsHeight = getHeaderRowsHeight(measure, headerRowCount);
|
|
574
|
-
let currentRowIndex = 0;
|
|
575
|
-
const breakInfo = buildTableRowBreakInfo(block, measure);
|
|
576
|
-
const verticallyMergedRows = getVerticallyMergedRows(block);
|
|
577
|
-
const computeTableX = ({ columnIndex, rowIndex }) => {
|
|
578
|
-
return paginator.getColumnX(columnIndex) + resolveTableInlineOffset({
|
|
579
|
-
table: block,
|
|
580
|
-
rowJustification: block.rows[rowIndex]?.justification,
|
|
581
|
-
frameWidth: paginator.columnWidth,
|
|
582
|
-
tableWidth: measure.totalWidth
|
|
583
|
-
});
|
|
584
|
-
};
|
|
585
|
-
const getCurrentRowCapacity = (state = paginator.getCurrentState()) => state.rawContentBottom - state.topMargin;
|
|
586
|
-
const hasAdjacentPriorTableRows = (rowIndex, state = paginator.getCurrentState()) => {
|
|
587
|
-
const previous = state.page.fragments.at(-1);
|
|
588
|
-
return previous?.kind === "table" && previous.blockId === block.id && previous.toRow === rowIndex && previous.y + previous.height === state.cursorY;
|
|
589
|
-
};
|
|
590
|
-
const shouldRepeatHeaderRows = (rowIndex, consumed, state = paginator.getCurrentState()) => headerRowCount > 0 && rowIndex >= headerRowCount && !(consumed === 0 && hasAdjacentPriorTableRows(rowIndex, state));
|
|
591
|
-
const rowOpensFlowRegion = (rowIndex, state) => {
|
|
592
|
-
if (state.cursorY === state.topMargin) return true;
|
|
593
|
-
const previous = state.page.fragments.at(-1);
|
|
594
|
-
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;
|
|
595
|
-
};
|
|
596
|
-
const canSplitRow = (rowIndex, state = paginator.getCurrentState()) => {
|
|
597
|
-
const row = rows[rowIndex];
|
|
598
|
-
const sourceRow = block.rows[rowIndex];
|
|
599
|
-
if (!row || !sourceRow || sourceRow.isHeader || verticallyMergedRows.has(rowIndex)) return false;
|
|
600
|
-
if ((breakInfo.breakOffsets[rowIndex]?.length ?? 0) <= 1) return false;
|
|
601
|
-
const freshHeaderOverhead = headerRowCount > 0 && rowIndex >= headerRowCount ? headerRowsHeight : 0;
|
|
602
|
-
const oversized = row.height + freshHeaderOverhead > getCurrentRowCapacity(state);
|
|
603
|
-
if (sourceRow.cantSplit) return oversized && sourceRow.heightRule !== "exact" && rowOpensFlowRegion(rowIndex, state);
|
|
604
|
-
if (!oversized && (state.footnoteHeight > 0 || (rowFootnoteIds[rowIndex]?.length ?? 0) > 0)) return false;
|
|
605
|
-
const currentHeaderOverhead = shouldRepeatHeaderRows(rowIndex, 0, state) ? headerRowsHeight : 0;
|
|
606
|
-
const currentAvailableHeight = paginator.getAvailableHeight() - currentHeaderOverhead - state.trailingSpacing;
|
|
607
|
-
return oversized || row.height > currentAvailableHeight;
|
|
608
|
-
};
|
|
609
|
-
while (currentRowIndex < rows.length) {
|
|
610
|
-
const rowState = paginator.getCurrentState();
|
|
611
|
-
const rowHeaderOverhead = shouldRepeatHeaderRows(currentRowIndex, 0, rowState) ? headerRowsHeight : 0;
|
|
612
|
-
const rowAvailableHeight = paginator.getAvailableHeight() - rowHeaderOverhead - rowState.trailingSpacing;
|
|
613
|
-
const rowStartsFreshPage = rowState.cursorY === rowState.topMargin && rowState.page.fragments.length === 0;
|
|
614
|
-
if (block.rows[currentRowIndex]?.breakBefore === "page" && !rowStartsFreshPage) {
|
|
615
|
-
paginator.forcePageBreak();
|
|
616
|
-
continue;
|
|
617
|
-
}
|
|
618
|
-
if (!rowStartsFreshPage && tableRowStartsWithRenderedPageBreak(block, currentRowIndex) && !tableRowHasTrackedChanges(block, currentRowIndex) && rows[currentRowIndex].height > rowAvailableHeight) {
|
|
619
|
-
paginator.forcePageBreak();
|
|
620
|
-
continue;
|
|
621
|
-
}
|
|
622
|
-
const splittableRow = rows[currentRowIndex];
|
|
623
|
-
if (canSplitRow(currentRowIndex)) {
|
|
624
|
-
let consumed = 0;
|
|
625
|
-
const renderedBreakHints = tableRowHasTrackedChanges(block, currentRowIndex) ? [] : breakInfo.renderedBreakHints[currentRowIndex] ?? [];
|
|
626
|
-
let renderedBreakHintIndex = 0;
|
|
627
|
-
const discardPassedRenderedBreakHints = () => {
|
|
628
|
-
while ((renderedBreakHints[renderedBreakHintIndex]?.offset ?? Infinity) <= consumed) renderedBreakHintIndex += 1;
|
|
629
|
-
};
|
|
630
|
-
const advanceAfterNaturalFlowBreak = (previousPageNumber) => {
|
|
631
|
-
discardPassedRenderedBreakHints();
|
|
632
|
-
if (paginator.getCurrentState().page.number > previousPageNumber && renderedBreakHintIndex < renderedBreakHints.length) renderedBreakHintIndex += 1;
|
|
633
|
-
};
|
|
634
|
-
while (consumed < splittableRow.height) {
|
|
635
|
-
const sliceState = paginator.getCurrentState();
|
|
636
|
-
const repeatHeaderRows = shouldRepeatHeaderRows(currentRowIndex, consumed, sliceState);
|
|
637
|
-
const headerOverhead = repeatHeaderRows ? headerRowsHeight : 0;
|
|
638
|
-
const sliceAvail = paginator.getAvailableHeight() - headerOverhead - (consumed === 0 ? sliceState.trailingSpacing : 0);
|
|
639
|
-
let slice = snapRowBreak(breakInfo, currentRowIndex, consumed, sliceAvail);
|
|
640
|
-
let forceRenderedPageBreak = false;
|
|
641
|
-
if (slice <= 0) {
|
|
642
|
-
if (!(sliceState.cursorY === sliceState.topMargin && sliceState.page.fragments.length === 0)) {
|
|
643
|
-
const previousPageNumber = sliceState.page.number;
|
|
644
|
-
paginator.forceColumnBreak();
|
|
645
|
-
advanceAfterNaturalFlowBreak(previousPageNumber);
|
|
646
|
-
continue;
|
|
647
|
-
}
|
|
648
|
-
const from = consumed;
|
|
649
|
-
slice = (breakInfo.breakOffsets[currentRowIndex]?.find((o) => o > from) ?? splittableRow.height) - consumed;
|
|
650
|
-
}
|
|
651
|
-
discardPassedRenderedBreakHints();
|
|
652
|
-
const renderedBreakHint = renderedBreakHints[renderedBreakHintIndex];
|
|
653
|
-
if (renderedBreakHint && renderedBreakHint.offset <= consumed + slice && sliceAvail - (renderedBreakHint.offset - consumed) <= renderedBreakHint.lineAdvance * RENDERED_BREAK_REFLOW_TOLERANCE_LINES) {
|
|
654
|
-
slice = renderedBreakHint.offset - consumed;
|
|
655
|
-
renderedBreakHintIndex += 1;
|
|
656
|
-
forceRenderedPageBreak = true;
|
|
657
|
-
}
|
|
658
|
-
const sliceBottom = consumed + slice;
|
|
659
|
-
const continuationSkip = sliceBottom < splittableRow.height ? getRowContinuationSkip(breakInfo, currentRowIndex, sliceBottom) : 0;
|
|
660
|
-
const nextConsumed = Math.min(splittableRow.height, sliceBottom + continuationSkip);
|
|
661
|
-
const moreAfter = !(nextConsumed >= splittableRow.height) || currentRowIndex + 1 < rows.length;
|
|
662
|
-
const fragmentHeight = headerOverhead + slice;
|
|
663
|
-
const sliceFragment = {
|
|
664
|
-
kind: "table",
|
|
665
|
-
blockId: block.id,
|
|
666
|
-
x: computeTableX({
|
|
667
|
-
columnIndex: sliceState.columnIndex,
|
|
668
|
-
rowIndex: currentRowIndex
|
|
669
|
-
}),
|
|
670
|
-
y: 0,
|
|
671
|
-
width: measure.totalWidth,
|
|
672
|
-
height: fragmentHeight,
|
|
673
|
-
fromRow: currentRowIndex,
|
|
674
|
-
toRow: currentRowIndex + 1,
|
|
675
|
-
...block.pmStart !== void 0 ? { pmStart: block.pmStart } : {},
|
|
676
|
-
...block.pmEnd !== void 0 ? { pmEnd: block.pmEnd } : {},
|
|
677
|
-
...consumed > 0 || currentRowIndex > 0 ? { continuesFromPrev: true } : {},
|
|
678
|
-
...moreAfter ? { continuesOnNext: true } : {},
|
|
679
|
-
...repeatHeaderRows ? { headerRowCount } : {},
|
|
680
|
-
...consumed > 0 ? { topClip: consumed } : {},
|
|
681
|
-
...sliceBottom >= splittableRow.height ? {} : { bottomClip: sliceBottom },
|
|
682
|
-
...block.sdtGroups ? { sdtGroups: block.sdtGroups } : {}
|
|
683
|
-
};
|
|
684
|
-
const sliceResult = paginator.addFragment(sliceFragment, fragmentHeight, 0, 0);
|
|
685
|
-
sliceFragment.y = sliceResult.y;
|
|
686
|
-
sliceFragment.x = computeTableX({
|
|
687
|
-
columnIndex: sliceResult.state.columnIndex,
|
|
688
|
-
rowIndex: currentRowIndex
|
|
689
|
-
});
|
|
690
|
-
consumed = nextConsumed;
|
|
691
|
-
if (consumed < splittableRow.height) if (forceRenderedPageBreak) paginator.forcePageBreak();
|
|
692
|
-
else {
|
|
693
|
-
const previousPageNumber = paginator.getCurrentState().page.number;
|
|
694
|
-
paginator.forceColumnBreak();
|
|
695
|
-
advanceAfterNaturalFlowBreak(previousPageNumber);
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
currentRowIndex += 1;
|
|
699
|
-
continue;
|
|
700
|
-
}
|
|
701
|
-
const state = paginator.getCurrentState();
|
|
702
|
-
const rawAvailableHeight = paginator.getAvailableHeight();
|
|
703
|
-
const isFirstFragment = currentRowIndex === 0;
|
|
704
|
-
const bandSkip = isFirstFragment ? measure.bandSkipBefore ?? 0 : 0;
|
|
705
|
-
const availableHeight = rawAvailableHeight - (isFirstFragment ? Math.max(bandSkip, state.trailingSpacing) : 0);
|
|
706
|
-
const repeatHeaderRowsForNormalFragment = shouldRepeatHeaderRows(currentRowIndex, 0, state);
|
|
707
|
-
const normalHeaderOverhead = repeatHeaderRowsForNormalFragment ? headerRowsHeight : 0;
|
|
708
|
-
let rowsHeight = 0;
|
|
709
|
-
let fittingRows = 0;
|
|
710
|
-
const pageFootnoteIds = new Set(state.page.footnoteIds ?? []);
|
|
711
|
-
const fragmentFootnoteIds = [];
|
|
712
|
-
let fragmentFootnoteHeight = 0;
|
|
713
|
-
let retryOnNextFlowRegion = false;
|
|
714
|
-
const fragmentX = computeTableX({
|
|
715
|
-
columnIndex: state.columnIndex,
|
|
716
|
-
rowIndex: currentRowIndex
|
|
717
|
-
});
|
|
718
|
-
for (let j = currentRowIndex; j < rows.length; j++) {
|
|
719
|
-
if (j > currentRowIndex && block.rows[j]?.breakBefore === "page") break;
|
|
720
|
-
if (j > currentRowIndex && computeTableX({
|
|
721
|
-
columnIndex: state.columnIndex,
|
|
722
|
-
rowIndex: j
|
|
723
|
-
}) !== fragmentX) break;
|
|
724
|
-
const rowHeight = rows[j].height;
|
|
725
|
-
const currentRowFootnoteIds = rowFootnoteIds[j] ?? [];
|
|
726
|
-
const fragmentFootnoteCountBeforeRow = fragmentFootnoteIds.length;
|
|
727
|
-
let rowFootnoteHeight = 0;
|
|
728
|
-
for (const id of currentRowFootnoteIds) {
|
|
729
|
-
if (pageFootnoteIds.has(id) || fragmentFootnoteIds.includes(id)) continue;
|
|
730
|
-
fragmentFootnoteIds.push(id);
|
|
731
|
-
rowFootnoteHeight += footnoteHeightById?.get(id) ?? 0;
|
|
732
|
-
}
|
|
733
|
-
const candidateFootnoteHeight = fragmentFootnoteHeight + rowFootnoteHeight;
|
|
734
|
-
const footnoteGrowth = projectedFootnoteReserveGrowth(state, candidateFootnoteHeight + (state.footnoteDemandHeight === 0 && candidateFootnoteHeight > 0 ? 12 : 0));
|
|
735
|
-
if (rowsHeight + rowHeight + normalHeaderOverhead + footnoteGrowth <= availableHeight) {
|
|
736
|
-
rowsHeight += rowHeight;
|
|
737
|
-
fragmentFootnoteHeight += rowFootnoteHeight;
|
|
738
|
-
fittingRows++;
|
|
739
|
-
} else if (fittingRows === 0) {
|
|
740
|
-
if (state.cursorY === state.topMargin && state.page.fragments.length === 0) {
|
|
741
|
-
rowsHeight += rowHeight;
|
|
742
|
-
fragmentFootnoteHeight += rowFootnoteHeight;
|
|
743
|
-
fittingRows++;
|
|
744
|
-
break;
|
|
745
|
-
}
|
|
746
|
-
fragmentFootnoteIds.splice(fragmentFootnoteCountBeforeRow);
|
|
747
|
-
paginator.forceColumnBreak();
|
|
748
|
-
retryOnNextFlowRegion = true;
|
|
749
|
-
break;
|
|
750
|
-
} else {
|
|
751
|
-
fragmentFootnoteIds.splice(fragmentFootnoteCountBeforeRow);
|
|
752
|
-
break;
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
if (retryOnNextFlowRegion) continue;
|
|
756
|
-
const fragmentHeight = rowsHeight + normalHeaderOverhead;
|
|
757
|
-
const isLastFragment = currentRowIndex + fittingRows >= rows.length;
|
|
758
|
-
const desiredX = fragmentX;
|
|
759
|
-
const fragment = {
|
|
760
|
-
kind: "table",
|
|
761
|
-
blockId: block.id,
|
|
762
|
-
x: desiredX,
|
|
763
|
-
y: 0,
|
|
764
|
-
width: measure.totalWidth,
|
|
765
|
-
height: fragmentHeight,
|
|
766
|
-
fromRow: currentRowIndex,
|
|
767
|
-
toRow: currentRowIndex + fittingRows,
|
|
768
|
-
...block.pmStart !== void 0 ? { pmStart: block.pmStart } : {},
|
|
769
|
-
...block.pmEnd !== void 0 ? { pmEnd: block.pmEnd } : {},
|
|
770
|
-
...!isFirstFragment ? { continuesFromPrev: true } : {},
|
|
771
|
-
...!isLastFragment ? { continuesOnNext: true } : {},
|
|
772
|
-
...repeatHeaderRowsForNormalFragment ? { headerRowCount } : {},
|
|
773
|
-
...block.sdtGroups ? { sdtGroups: block.sdtGroups } : {}
|
|
774
|
-
};
|
|
775
|
-
if (fragmentFootnoteHeight > 0) paginator.addFootnoteHeight(fragmentFootnoteHeight, fragmentFootnoteIds);
|
|
776
|
-
fragment.y = paginator.addFragment(fragment, fragmentHeight, bandSkip, 0).y;
|
|
777
|
-
fragment.x = desiredX;
|
|
778
|
-
currentRowIndex += fittingRows;
|
|
779
|
-
if (currentRowIndex < rows.length) {
|
|
780
|
-
if (canSplitRow(currentRowIndex)) {
|
|
781
|
-
const nextState = paginator.getCurrentState();
|
|
782
|
-
const nextHeaderOverhead = shouldRepeatHeaderRows(currentRowIndex, 0, nextState) ? headerRowsHeight : 0;
|
|
783
|
-
const nextSliceAvail = paginator.getAvailableHeight() - nextHeaderOverhead - nextState.trailingSpacing;
|
|
784
|
-
if (snapRowBreak(breakInfo, currentRowIndex, 0, nextSliceAvail) > 0) continue;
|
|
785
|
-
}
|
|
786
|
-
const nextState = paginator.getCurrentState();
|
|
787
|
-
const nextRowHeight = rows[currentRowIndex].height + (shouldRepeatHeaderRows(currentRowIndex, 0, nextState) ? headerRowsHeight : 0);
|
|
788
|
-
paginator.ensureFits(nextRowHeight);
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
function collectTableRowFootnoteIds(row, footnoteHeightById) {
|
|
793
|
-
if (!row || !footnoteHeightById || row.hidden) return [];
|
|
794
|
-
const ids = [];
|
|
795
|
-
const walk = (blocks) => {
|
|
796
|
-
for (const block of blocks) {
|
|
797
|
-
if (block.kind === "paragraph") {
|
|
798
|
-
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);
|
|
799
|
-
continue;
|
|
800
|
-
}
|
|
801
|
-
if (block.kind === "table") {
|
|
802
|
-
for (const nestedRow of block.rows) {
|
|
803
|
-
if (nestedRow.hidden) continue;
|
|
804
|
-
for (const cell of nestedRow.cells) walk(cell.blocks);
|
|
805
|
-
}
|
|
806
|
-
continue;
|
|
807
|
-
}
|
|
808
|
-
if (block.kind === "textBox") walk(block.content);
|
|
809
|
-
}
|
|
810
|
-
};
|
|
811
|
-
for (const cell of row.cells) walk(cell.blocks);
|
|
812
|
-
return ids;
|
|
813
|
-
}
|
|
814
|
-
/**
|
|
815
|
-
* Layout a floating table (anchored) without advancing the cursor.
|
|
816
|
-
*/
|
|
817
|
-
function layoutFloatingTable(block, measure, paginator, contentWidth) {
|
|
818
|
-
const state = paginator.getCurrentState();
|
|
819
|
-
const floating = block.floating;
|
|
820
|
-
const page = state.page;
|
|
821
|
-
const margins = page.margins;
|
|
822
|
-
const tableWidth = measure.totalWidth;
|
|
823
|
-
const tableHeight = measure.totalHeight;
|
|
824
|
-
const contentHeight = page.size.h - margins.top - margins.bottom;
|
|
825
|
-
let baseY = margins.top;
|
|
826
|
-
if (floating?.vertAnchor === "page") baseY = 0;
|
|
827
|
-
else if (floating?.vertAnchor === "text") baseY = state.cursorY;
|
|
828
|
-
let x = paginator.getColumnX(state.columnIndex);
|
|
829
|
-
if (floating) x = resolveFloatingTablePageX({
|
|
830
|
-
anchor: floating,
|
|
831
|
-
justification: block.justification,
|
|
832
|
-
tableWidth,
|
|
833
|
-
marginWidth: contentWidth,
|
|
834
|
-
pageWidth: page.size.w,
|
|
835
|
-
marginLeft: margins.left,
|
|
836
|
-
textFrameWidth: paginator.columnWidth,
|
|
837
|
-
textFrameLeft: paginator.getColumnX(state.columnIndex)
|
|
838
|
-
});
|
|
839
|
-
let y = state.cursorY;
|
|
840
|
-
let usedExplicitY = false;
|
|
841
|
-
if (floating?.tblpY !== void 0) {
|
|
842
|
-
y = baseY + floating.tblpY;
|
|
843
|
-
usedExplicitY = true;
|
|
844
|
-
} else if (floating?.tblpYSpec) {
|
|
845
|
-
usedExplicitY = true;
|
|
846
|
-
const spec = floating.tblpYSpec;
|
|
847
|
-
if (spec === "top") y = baseY;
|
|
848
|
-
else if (spec === "bottom") y = baseY + contentHeight - tableHeight;
|
|
849
|
-
else if (spec === "center") y = baseY + (contentHeight - tableHeight) / 2;
|
|
850
|
-
}
|
|
851
|
-
if (!usedExplicitY) y = paginator.ensureFits(tableHeight).cursorY;
|
|
852
|
-
const fragment = {
|
|
853
|
-
kind: "table",
|
|
854
|
-
blockId: block.id,
|
|
855
|
-
x,
|
|
856
|
-
y,
|
|
857
|
-
width: tableWidth,
|
|
858
|
-
height: tableHeight,
|
|
859
|
-
fromRow: 0,
|
|
860
|
-
toRow: block.rows.length,
|
|
861
|
-
...block.pmStart !== void 0 ? { pmStart: block.pmStart } : {},
|
|
862
|
-
...block.pmEnd !== void 0 ? { pmEnd: block.pmEnd } : {},
|
|
863
|
-
isFloating: true,
|
|
864
|
-
...block.sdtGroups ? { sdtGroups: block.sdtGroups } : {}
|
|
865
|
-
};
|
|
866
|
-
paginator.addUnflowedFragment(fragment);
|
|
867
|
-
}
|
|
868
|
-
/**
|
|
869
|
-
* Layout an image block onto pages.
|
|
870
|
-
*/
|
|
871
|
-
function layoutImage(block, measure, paginator) {
|
|
872
|
-
if (block.anchor?.isAnchored) {
|
|
873
|
-
layoutAnchoredImage(block, measure, paginator);
|
|
874
|
-
return;
|
|
875
|
-
}
|
|
876
|
-
const bandSkip = measure.bandSkipBefore ?? 0;
|
|
877
|
-
const state = paginator.ensureFits(bandSkip + measure.height);
|
|
878
|
-
const fragment = {
|
|
879
|
-
kind: "image",
|
|
880
|
-
blockId: block.id,
|
|
881
|
-
x: paginator.getColumnX(state.columnIndex),
|
|
882
|
-
y: 0,
|
|
883
|
-
width: measure.width,
|
|
884
|
-
height: measure.height,
|
|
885
|
-
...block.pmStart !== void 0 ? { pmStart: block.pmStart } : {},
|
|
886
|
-
...block.pmEnd !== void 0 ? { pmEnd: block.pmEnd } : {}
|
|
887
|
-
};
|
|
888
|
-
fragment.y = paginator.addFragment(fragment, measure.height, bandSkip, 0).y;
|
|
889
|
-
}
|
|
890
|
-
/**
|
|
891
|
-
* Layout an anchored (floating) image.
|
|
892
|
-
*/
|
|
893
|
-
function layoutAnchoredImage(block, measure, paginator) {
|
|
894
|
-
const state = paginator.getCurrentState();
|
|
895
|
-
const anchor = block.anchor;
|
|
896
|
-
if (!anchor) return;
|
|
897
|
-
const x = anchor.offsetH ?? paginator.getColumnX(state.columnIndex);
|
|
898
|
-
const y = anchor.offsetV ?? state.cursorY;
|
|
899
|
-
const fragment = {
|
|
900
|
-
kind: "image",
|
|
901
|
-
blockId: block.id,
|
|
902
|
-
x,
|
|
903
|
-
y,
|
|
904
|
-
width: measure.width,
|
|
905
|
-
height: measure.height,
|
|
906
|
-
...block.pmStart !== void 0 ? { pmStart: block.pmStart } : {},
|
|
907
|
-
...block.pmEnd !== void 0 ? { pmEnd: block.pmEnd } : {},
|
|
908
|
-
isAnchored: true,
|
|
909
|
-
zIndex: anchor.behindDoc ? -1 : 1
|
|
910
|
-
};
|
|
911
|
-
paginator.addUnflowedFragment(fragment);
|
|
912
|
-
}
|
|
913
|
-
const TEXT_BOX_ANCHOR_BLOCK_ID = Symbol.for("stll.textBoxAnchorBlockId");
|
|
914
|
-
const readTextBoxAnchorBlockId = (block) => Reflect.get(block, TEXT_BOX_ANCHOR_BLOCK_ID);
|
|
915
|
-
/**
|
|
916
|
-
* Layout a text box block onto pages.
|
|
917
|
-
*/
|
|
918
|
-
function layoutTextBox(block, measure, { paginator, sectionMarginTop, sectionPageHeight, sectionMarginBottom }) {
|
|
919
|
-
if (isPagePinnedBandTextBox(block)) {
|
|
920
|
-
const state = paginator.getCurrentState();
|
|
921
|
-
const bandTop = bandTopContentY(block.position?.vertical, {
|
|
922
|
-
pageHeight: sectionPageHeight,
|
|
923
|
-
marginTop: sectionMarginTop,
|
|
924
|
-
marginBottom: sectionMarginBottom,
|
|
925
|
-
boxHeight: measure.height
|
|
926
|
-
});
|
|
927
|
-
const x = bandFragmentX(block.position?.horizontal, {
|
|
928
|
-
pageWidth: state.page.size.w,
|
|
929
|
-
marginLeft: state.page.margins.left,
|
|
930
|
-
marginRight: state.page.margins.right,
|
|
931
|
-
activeColumnLeft: paginator.getColumnX(state.columnIndex),
|
|
932
|
-
activeColumnWidth: paginator.columnWidth,
|
|
933
|
-
boxWidth: measure.width
|
|
934
|
-
});
|
|
935
|
-
const fragment = {
|
|
936
|
-
kind: "textBox",
|
|
937
|
-
blockId: block.id,
|
|
938
|
-
x,
|
|
939
|
-
y: block.position?.vertical?.relativeTo === "page" ? sectionMarginTop + bandTop : state.topMargin + bandTop,
|
|
940
|
-
width: measure.width,
|
|
941
|
-
height: measure.height,
|
|
942
|
-
isPositioned: true,
|
|
943
|
-
...block.pmStart !== void 0 ? { pmStart: block.pmStart } : {},
|
|
944
|
-
...block.pmEnd !== void 0 ? { pmEnd: block.pmEnd } : {}
|
|
945
|
-
};
|
|
946
|
-
paginator.addUnflowedFragment(fragment);
|
|
947
|
-
return;
|
|
948
|
-
}
|
|
949
|
-
if (block.position !== void 0) {
|
|
950
|
-
const state = paginator.getCurrentState();
|
|
951
|
-
const x = bandFragmentX(block.position.horizontal, {
|
|
952
|
-
pageWidth: state.page.size.w,
|
|
953
|
-
marginLeft: state.page.margins.left,
|
|
954
|
-
marginRight: state.page.margins.right,
|
|
955
|
-
activeColumnLeft: paginator.getColumnX(state.columnIndex),
|
|
956
|
-
activeColumnWidth: paginator.columnWidth,
|
|
957
|
-
boxWidth: measure.width
|
|
958
|
-
});
|
|
959
|
-
const vertical = block.position.vertical;
|
|
960
|
-
const anchorBlockId = readTextBoxAnchorBlockId(block);
|
|
961
|
-
const anchorParagraph = vertical?.relativeTo === "paragraph" && typeof anchorBlockId === "string" ? state.page.fragments.find((fragment) => fragment.kind === "paragraph" && fragment.blockId === anchorBlockId) : void 0;
|
|
962
|
-
const bandGeometry = {
|
|
963
|
-
pageHeight: sectionPageHeight,
|
|
964
|
-
marginTop: sectionMarginTop,
|
|
965
|
-
marginBottom: sectionMarginBottom,
|
|
966
|
-
boxHeight: measure.height
|
|
967
|
-
};
|
|
968
|
-
let y;
|
|
969
|
-
if (vertical?.relativeTo === "page") y = sectionMarginTop + bandTopContentY(vertical, bandGeometry);
|
|
970
|
-
else if (isPageFrameRelativeAnchor(vertical?.relativeTo)) y = state.topMargin + bandTopContentY(vertical, {
|
|
971
|
-
pageHeight: sectionPageHeight,
|
|
972
|
-
marginTop: sectionMarginTop,
|
|
973
|
-
marginBottom: sectionMarginBottom,
|
|
974
|
-
boxHeight: measure.height
|
|
975
|
-
});
|
|
976
|
-
else y = (anchorParagraph?.y ?? state.cursorY) + emuToPixels(vertical?.posOffset ?? 0);
|
|
977
|
-
const fragment = {
|
|
978
|
-
kind: "textBox",
|
|
979
|
-
blockId: block.id,
|
|
980
|
-
x,
|
|
981
|
-
y,
|
|
982
|
-
width: measure.width,
|
|
983
|
-
height: measure.height,
|
|
984
|
-
isPositioned: true,
|
|
985
|
-
...block.pmStart !== void 0 ? { pmStart: block.pmStart } : {},
|
|
986
|
-
...block.pmEnd !== void 0 ? { pmEnd: block.pmEnd } : {}
|
|
987
|
-
};
|
|
988
|
-
paginator.addUnflowedFragment(fragment);
|
|
989
|
-
return;
|
|
990
|
-
}
|
|
991
|
-
const effect = block.effectExtent;
|
|
992
|
-
const occupiedHeight = measure.height + (effect?.top ?? 0) + (effect?.bottom ?? 0);
|
|
993
|
-
const occupiedWidth = measure.width + (effect?.left ?? 0) + (effect?.right ?? 0);
|
|
994
|
-
const host = block.hostParagraph;
|
|
995
|
-
const fragment = {
|
|
996
|
-
kind: "textBox",
|
|
997
|
-
blockId: block.id,
|
|
998
|
-
x: 0,
|
|
999
|
-
y: 0,
|
|
1000
|
-
width: measure.width,
|
|
1001
|
-
height: measure.height,
|
|
1002
|
-
...block.pmStart !== void 0 ? { pmStart: block.pmStart } : {},
|
|
1003
|
-
...block.pmEnd !== void 0 ? { pmEnd: block.pmEnd } : {}
|
|
1004
|
-
};
|
|
1005
|
-
const result = paginator.addFragment(fragment, occupiedHeight, host?.spacing?.before ?? 0, host?.spacing?.after ?? 0);
|
|
1006
|
-
fragment.x = result.x + hostParagraphLineOffset(host, paginator.columnWidth, occupiedWidth) + (effect?.left ?? 0);
|
|
1007
|
-
fragment.y = result.y + (effect?.top ?? 0);
|
|
1008
|
-
}
|
|
1009
|
-
/**
|
|
1010
|
-
* Where a line holding only an object of `width` starts within a column:
|
|
1011
|
-
* after the host paragraph's start and first-line indents, then aligned in
|
|
1012
|
-
* what remains before its end indent.
|
|
1013
|
-
*/
|
|
1014
|
-
function hostParagraphLineOffset(host, columnWidth, width) {
|
|
1015
|
-
if (!host) return 0;
|
|
1016
|
-
const indent = host.indent;
|
|
1017
|
-
const start = (indent?.left ?? 0) + (indent?.firstLine ?? 0) - (indent?.hanging ?? 0);
|
|
1018
|
-
const slack = Math.max(0, columnWidth - start - (indent?.right ?? 0) - width);
|
|
1019
|
-
switch (host.alignment) {
|
|
1020
|
-
case "center": return start + slack / 2;
|
|
1021
|
-
case "right": return start + slack;
|
|
1022
|
-
case "left":
|
|
1023
|
-
case "justify":
|
|
1024
|
-
case void 0: return start;
|
|
1025
|
-
default:
|
|
1026
|
-
host.alignment;
|
|
1027
|
-
return start;
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
/**
|
|
1031
|
-
* A topAndBottom text box whose vertical anchor pins it to the page frame
|
|
1032
|
-
* (page/margin/margin-strip) — it floats to a fixed page position rather than
|
|
1033
|
-
* flowing in document order. Must agree with the measure pass's band extraction
|
|
1034
|
-
* (extractFloatingZones), which uses the same predicate. eigenpal #694.
|
|
1035
|
-
*/
|
|
1036
|
-
function isPagePinnedBandTextBox(block) {
|
|
1037
|
-
if (!floatingTextBoxReservesBand(block)) return false;
|
|
1038
|
-
return isPageFrameRelativeAnchor(block.position?.vertical?.relativeTo);
|
|
1039
|
-
}
|
|
1040
|
-
/**
|
|
1041
|
-
* Start the next section in the current page region, below the outgoing
|
|
1042
|
-
* content.
|
|
1043
|
-
*
|
|
1044
|
-
* ECMA-376 §17.6.22: a `continuous` break normally keeps the current page
|
|
1045
|
-
* geometry and defers the new size/margins to the next natural page break. But
|
|
1046
|
-
* a break that changes page size or orientation cannot share a physical sheet
|
|
1047
|
-
* with the preceding section, so Word and LibreOffice promote it to a page
|
|
1048
|
-
* break (eigenpal/docx-editor#841).
|
|
1049
|
-
*
|
|
1050
|
-
* Compare against the last laid-out page without materializing one: a break
|
|
1051
|
-
* before any content has no sheet to share, so it defers (the first content
|
|
1052
|
-
* then opens a page with the new geometry) rather than stranding a blank
|
|
1053
|
-
* leading page.
|
|
1054
|
-
*/
|
|
1055
|
-
function startSectionInPlace(paginator, nextSectionConfig, nextSectionIndex) {
|
|
1056
|
-
const currentPage = paginator.states.at(-1)?.page;
|
|
1057
|
-
const nextSize = nextSectionConfig.pageSize;
|
|
1058
|
-
const pageSizeChanges = currentPage != null && (Math.round(nextSize.w) !== Math.round(currentPage.size.w) || Math.round(nextSize.h) !== Math.round(currentPage.size.h));
|
|
1059
|
-
if (nextSectionIndex !== void 0) paginator.startSection({
|
|
1060
|
-
sectionIndex: nextSectionIndex,
|
|
1061
|
-
pageNumbering: nextSectionConfig.pageNumbering,
|
|
1062
|
-
placement: pageSizeChanges ? SECTION_START_PLACEMENT.NEXT_PAGE : SECTION_START_PLACEMENT.CONTINUOUS
|
|
1063
|
-
});
|
|
1064
|
-
if (pageSizeChanges) {
|
|
1065
|
-
paginator.updatePageLayout(nextSize, nextSectionConfig.margins);
|
|
1066
|
-
if (!paginator.retargetCurrentBlankPage()) paginator.forcePageBreak({ coalesceBlankPage: true });
|
|
1067
|
-
return;
|
|
1068
|
-
}
|
|
1069
|
-
paginator.updatePageLayout(nextSize, nextSectionConfig.margins, false);
|
|
1070
|
-
paginator.retargetCurrentBlankPage();
|
|
1071
|
-
}
|
|
1072
|
-
/**
|
|
1073
|
-
* Handle a section break block.
|
|
1074
|
-
* @param block - The section break block (current section's properties)
|
|
1075
|
-
* @param paginator - The paginator instance
|
|
1076
|
-
* @param nextSectionConfig - Page layout for the NEXT section
|
|
1077
|
-
* @param nextSectionType - Break type of the NEXT section (how it starts relative to current)
|
|
1078
|
-
*/
|
|
1079
|
-
function handleSectionBreak(_block, paginator, nextSectionConfig, nextSectionType, nextSectionIndex) {
|
|
1080
|
-
switch (nextSectionType) {
|
|
1081
|
-
case "nextPage":
|
|
1082
|
-
paginator.updatePageLayout(nextSectionConfig.pageSize, nextSectionConfig.margins);
|
|
1083
|
-
if (nextSectionIndex !== void 0) paginator.startSection({
|
|
1084
|
-
sectionIndex: nextSectionIndex,
|
|
1085
|
-
pageNumbering: nextSectionConfig.pageNumbering
|
|
1086
|
-
});
|
|
1087
|
-
paginator.forcePageBreak({ coalesceBlankPage: true });
|
|
1088
|
-
break;
|
|
1089
|
-
case "evenPage":
|
|
1090
|
-
if (paginator.forcePageBreak({ coalesceBlankPage: true }).page.number % 2 !== 0) paginator.forcePageBreak();
|
|
1091
|
-
paginator.updatePageLayout(nextSectionConfig.pageSize, nextSectionConfig.margins);
|
|
1092
|
-
if (nextSectionIndex !== void 0) paginator.startSection({
|
|
1093
|
-
sectionIndex: nextSectionIndex,
|
|
1094
|
-
pageNumbering: nextSectionConfig.pageNumbering
|
|
1095
|
-
});
|
|
1096
|
-
if (!paginator.retargetCurrentBlankPage()) panic("Even-page section target must be blank");
|
|
1097
|
-
break;
|
|
1098
|
-
case "oddPage":
|
|
1099
|
-
if (paginator.forcePageBreak({ coalesceBlankPage: true }).page.number % 2 === 0) paginator.forcePageBreak();
|
|
1100
|
-
paginator.updatePageLayout(nextSectionConfig.pageSize, nextSectionConfig.margins);
|
|
1101
|
-
if (nextSectionIndex !== void 0) paginator.startSection({
|
|
1102
|
-
sectionIndex: nextSectionIndex,
|
|
1103
|
-
pageNumbering: nextSectionConfig.pageNumbering
|
|
1104
|
-
});
|
|
1105
|
-
if (!paginator.retargetCurrentBlankPage()) panic("Odd-page section target must be blank");
|
|
1106
|
-
break;
|
|
1107
|
-
case "nextColumn": {
|
|
1108
|
-
const currentPage = paginator.states.at(-1)?.page;
|
|
1109
|
-
if (currentPage === void 0 || !physicalColumnRegionIsShared({
|
|
1110
|
-
pageSize: currentPage.size,
|
|
1111
|
-
margins: currentPage.margins,
|
|
1112
|
-
columns: paginator.columns
|
|
1113
|
-
}, nextSectionConfig)) {
|
|
1114
|
-
startSectionInPlace(paginator, nextSectionConfig, nextSectionIndex);
|
|
1115
|
-
break;
|
|
1116
|
-
}
|
|
1117
|
-
if (nextSectionIndex !== void 0) paginator.startSection({
|
|
1118
|
-
sectionIndex: nextSectionIndex,
|
|
1119
|
-
pageNumbering: nextSectionConfig.pageNumbering,
|
|
1120
|
-
placement: SECTION_START_PLACEMENT.CONTINUOUS
|
|
1121
|
-
});
|
|
1122
|
-
paginator.updatePageLayout(nextSectionConfig.pageSize, nextSectionConfig.margins, false);
|
|
1123
|
-
paginator.forceColumnBreak();
|
|
1124
|
-
return;
|
|
1125
|
-
}
|
|
1126
|
-
case "continuous":
|
|
1127
|
-
startSectionInPlace(paginator, nextSectionConfig, nextSectionIndex);
|
|
1128
|
-
break;
|
|
1129
|
-
default:
|
|
1130
|
-
}
|
|
1131
|
-
paginator.updateColumns(nextSectionConfig.columns ?? DEFAULT_COLUMNS);
|
|
1132
|
-
}
|
|
1133
237
|
//#endregion
|
|
1134
238
|
export { DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, NOTE_SEPARATOR_RULE_THICKNESS, NOTE_SEPARATOR_WIDTH_FRACTION, applyContextualSpacing, applyPendingToActive, assertExhaustiveFlowBlock, calculateChainHeight, collectSectionConfigs, computeKeepNextChains, createInitialSectionState, createPaginator, findPageIndexContainingPmPos, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getEffectiveColumns, getEffectiveMargins, getEffectivePageSize, getHeaderRowsHeight, getMidChainIndices, getTableRowLeadingWidth, hasKeepLines, hasPageBreakBefore, isFloatingImageRun, isFloatingTextBoxBlock, isListNumPr, isTextWrappingFloatingImageRun, layoutDocument, resolveSectionHeaderFooterRefs, resolveTableCellPadding, resolveTableWidthPx, scheduleSectionBreak, stripFlowBlockPmAnchors, tableColumnsArePinned };
|