@stll/folio-core 0.47.7 → 0.49.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/minimal-replacement.d.ts +64 -0
- package/dist/ai-edits/minimal-replacement.js +282 -0
- package/dist/controller/fontReadiness.js +3 -3
- package/dist/controller/hyphenationReadiness.d.ts +19 -0
- package/dist/controller/hyphenationReadiness.js +47 -0
- package/dist/controller/layoutPipeline.d.ts +3 -0
- package/dist/controller/layoutPipeline.js +70 -18
- package/dist/display-list/build/buildDisplayList.js +39 -12
- package/dist/display-list/build/pageFurniture.d.ts +7 -1
- package/dist/display-list/build/pageFurniture.js +21 -8
- package/dist/display-list/build/paragraphPrimitives.js +68 -11
- package/dist/display-list/build/tablePrimitives.js +10 -6
- 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.js +0 -1
- package/dist/docx/attributeRemainder.js +10 -2
- package/dist/docx/blockContentParser.d.ts +2 -0
- package/dist/docx/blockContentParser.js +79 -64
- package/dist/docx/bulletMarkers.d.ts +18 -2
- package/dist/docx/bulletMarkers.js +27 -2
- package/dist/docx/commentParser.d.ts +2 -1
- package/dist/docx/commentParser.js +57 -45
- package/dist/docx/containerChildren.d.ts +21 -8
- package/dist/docx/containerChildren.js +39 -9
- package/dist/docx/documentParser.d.ts +5 -1
- package/dist/docx/documentParser.js +8 -5
- package/dist/docx/documentSectionFacts.d.ts +22 -0
- package/dist/docx/documentSectionFacts.js +43 -0
- 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/fieldParser.d.ts +8 -1
- package/dist/docx/fieldParser.js +47 -3
- package/dist/docx/fontTableParser.js +34 -27
- package/dist/docx/footnoteParser.d.ts +3 -2
- package/dist/docx/footnoteParser.js +15 -12
- package/dist/docx/groupDrawingParser.d.ts +9 -2
- package/dist/docx/groupDrawingParser.js +125 -37
- package/dist/docx/headerFooterParser.d.ts +7 -2
- package/dist/docx/headerFooterParser.js +15 -5
- package/dist/docx/hyperlinkParser.d.ts +48 -36
- package/dist/docx/hyperlinkParser.js +97 -82
- package/dist/docx/numberingParser.js +108 -84
- 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.d.ts +5 -1
- package/dist/docx/paragraphParser.js +468 -365
- package/dist/docx/paragraphProperties.js +123 -109
- package/dist/docx/paragraphPropertySource.d.ts +6 -1
- package/dist/docx/paragraphPropertySource.js +9 -1
- package/dist/docx/paragraphTextBoxEnrichment.d.ts +2 -1
- package/dist/docx/paragraphTextBoxEnrichment.js +143 -10
- package/dist/docx/parser.d.ts +7 -1
- package/dist/docx/parser.js +33 -16
- package/dist/docx/previewBudget.d.ts +55 -33
- package/dist/docx/previewBudget.js +59 -65
- package/dist/docx/rezip.js +41 -58
- package/dist/docx/runParser.d.ts +4 -1
- package/dist/docx/runParser.js +182 -184
- package/dist/docx/sdtProperties.js +87 -84
- package/dist/docx/sectionParser.js +169 -160
- 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 +47 -42
- package/dist/docx/serializer/runSerializer.d.ts +5 -1
- package/dist/docx/serializer/runSerializer.js +4 -3
- package/dist/docx/serializer/settingsSerializer.js +19 -1
- package/dist/docx/settingsParser.js +8 -0
- package/dist/docx/shapeAlternateContent.d.ts +25 -0
- package/dist/docx/shapeAlternateContent.js +52 -0
- package/dist/docx/streamingXmlParser.d.ts +6 -2
- package/dist/docx/streamingXmlParser.js +12 -7
- package/dist/docx/styleParser.js +34 -8
- package/dist/docx/tableParser.d.ts +21 -12
- package/dist/docx/tableParser.js +518 -441
- package/dist/docx/textBoxParser.d.ts +18 -5
- package/dist/docx/textBoxParser.js +12 -6
- package/dist/docx/verbatimCapture.d.ts +10 -8
- package/dist/docx/verbatimCapture.js +45 -2
- package/dist/docx/vmlImageParser.d.ts +15 -13
- package/dist/docx/vmlImageParser.js +75 -60
- package/dist/docx/vmlPreview.d.ts +3 -3
- package/dist/docx/vmlPreview.js +3 -4
- package/dist/docx/xmlNamespaceContext.d.ts +9 -0
- package/dist/docx/xmlNamespaceContext.js +54 -0
- package/dist/docx/xmlParser.d.ts +6 -3
- package/dist/docx/xmlParser.js +11 -41
- package/dist/docx/xmlResourceLimits.d.ts +2 -1
- package/dist/docx/xmlResourceLimits.js +4 -1
- package/dist/fonts/headlessMeasure.js +0 -0
- package/dist/headless-layout.d.ts +11 -2
- package/dist/headless-layout.js +153 -107
- package/dist/internal/headlessRevisionResolution.js +50 -13
- package/dist/internal/paragraphFormattingSerialization.js +3 -3
- package/dist/layout-bridge/convert/endnoteLayout.d.ts +65 -0
- package/dist/layout-bridge/convert/endnoteLayout.js +227 -0
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +19 -3
- package/dist/layout-bridge/convert/footnoteLayout.js +40 -31
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -7
- package/dist/layout-bridge/convert/headerFooterLayout.js +5 -20
- package/dist/layout-bridge/convert/paragraphMarkFormatting.d.ts +10 -0
- package/dist/layout-bridge/convert/paragraphMarkFormatting.js +59 -0
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +29 -1
- package/dist/layout-bridge/convert/toFlowBlocks.js +284 -39
- package/dist/layout-bridge/engine/hitTest.js +5 -3
- package/dist/layout-bridge/engine/selectionRects.js +5 -3
- package/dist/layout-engine/anchorLayoutInCellCompatibility.d.ts +10 -0
- package/dist/layout-engine/anchorLayoutInCellCompatibility.js +22 -0
- package/dist/layout-engine/index.d.ts +2 -2
- package/dist/layout-engine/index.js +102 -14
- package/dist/layout-engine/justificationCompatibility.d.ts +1 -1
- package/dist/layout-engine/justificationCompatibility.js +9 -5
- package/dist/layout-engine/keep-together.d.ts +15 -6
- package/dist/layout-engine/keep-together.js +43 -16
- package/dist/layout-engine/layoutInstrumentation.d.ts +8 -2
- package/dist/layout-engine/layoutInstrumentation.js +8 -1
- package/dist/layout-engine/measure/advanceComposition.js +50 -7
- package/dist/layout-engine/measure/hyphenationDictionaries.d.ts +74 -0
- package/dist/layout-engine/measure/hyphenationDictionaries.js +138 -0
- package/dist/layout-engine/measure/hyphenationPreload.d.ts +11 -0
- package/dist/layout-engine/measure/hyphenationPreload.js +24 -0
- package/dist/layout-engine/measure/lineBreakProvider.d.ts +12 -2
- package/dist/layout-engine/measure/lineBreakProvider.js +30 -30
- package/dist/layout-engine/measure/lineBreaks.d.ts +6 -1
- package/dist/layout-engine/measure/lineBreaks.js +10 -3
- package/dist/layout-engine/measure/listMarkerWidth.d.ts +9 -6
- package/dist/layout-engine/measure/listMarkerWidth.js +20 -22
- package/dist/layout-engine/measure/measureBlocks.js +46 -10
- package/dist/layout-engine/measure/measureContainer.js +51 -8
- package/dist/layout-engine/measure/measureHelpers.d.ts +15 -3
- package/dist/layout-engine/measure/measureHelpers.js +34 -8
- package/dist/layout-engine/measure/measureParagraph.js +143 -101
- package/dist/layout-engine/measure/measureTypes.d.ts +5 -0
- package/dist/layout-engine/measure/smallCapsCasing.d.ts +56 -0
- package/dist/layout-engine/measure/smallCapsCasing.js +88 -0
- package/dist/layout-engine/measure/tabCalculator.d.ts +2 -0
- package/dist/layout-engine/measure/tabCalculator.js +2 -1
- package/dist/layout-engine/measure/tableCellFloating.d.ts +2 -0
- package/dist/layout-engine/measure/tableCellFloating.js +51 -19
- package/dist/layout-engine/measure/tableCellGrid.d.ts +28 -3
- package/dist/layout-engine/measure/tableCellGrid.js +45 -5
- package/dist/layout-engine/measure/tableFragmentBorderGeometry.js +2 -1
- package/dist/layout-engine/noteAreaFlow.d.ts +19 -0
- package/dist/layout-engine/noteAreaFlow.js +83 -0
- package/dist/layout-engine/paginator.d.ts +12 -1
- package/dist/layout-engine/paginator.js +18 -2
- package/dist/layout-engine/paragraphSpacing.d.ts +4 -2
- package/dist/layout-engine/paragraphSpacing.js +2 -1
- package/dist/layout-engine/tableRowBreak.d.ts +8 -1
- package/dist/layout-engine/tableRowBreak.js +25 -2
- package/dist/layout-engine/types.d.ts +108 -3
- package/dist/layout-engine/types.js +96 -1
- package/dist/layout-painter/index.d.ts +3 -0
- package/dist/layout-painter/renderPage.js +32 -83
- package/dist/layout-painter/renderParagraph.d.ts +6 -1
- package/dist/layout-painter/renderParagraph.js +75 -26
- package/dist/layout-painter/renderTable.js +26 -13
- package/dist/layout-painter/renderTextBox.d.ts +12 -2
- package/dist/layout-painter/renderTextBox.js +26 -2
- package/dist/prosemirror/alternateContentAttrs.d.ts +9 -0
- package/dist/prosemirror/alternateContentAttrs.js +35 -0
- package/dist/prosemirror/attrs/index.d.ts +9 -9
- package/dist/prosemirror/attrs/index.js +133 -11
- 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 +54 -23
- package/dist/prosemirror/conversion/markInterner.d.ts +21 -0
- package/dist/prosemirror/conversion/markInterner.js +90 -0
- package/dist/prosemirror/conversion/toProseDoc.js +117 -58
- package/dist/prosemirror/extensions/core/DocExtension.js +2 -1
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +1 -0
- package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.d.ts +7 -7
- package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +7 -7
- package/dist/prosemirror/extensions/marks/markUtils.d.ts +6 -2
- package/dist/prosemirror/extensions/marks/markUtils.js +39 -51
- 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 +5 -1
- package/dist/prosemirror/listMarker.d.ts +1 -0
- package/dist/prosemirror/listMarker.js +4 -2
- package/dist/prosemirror/listRenderingAttrs.d.ts +1 -0
- package/dist/prosemirror/listRenderingAttrs.js +3 -0
- 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 +50 -6
- package/dist/prosemirror/textBoxHostParagraph.d.ts +13 -0
- package/dist/prosemirror/textBoxHostParagraph.js +21 -0
- package/dist/prosemirror/validation.js +91 -80
- package/dist/types/content.d.ts +2 -2
- package/dist/utils/createDocument.js +10 -1
- package/dist/utils/scriptSegments.d.ts +33 -4
- package/dist/utils/scriptSegments.js +39 -6
- package/dist/utils/textFormattingMerge.d.ts +17 -3
- package/dist/utils/textFormattingMerge.js +18 -12
- package/dist/utils/trailingText.d.ts +22 -0
- package/dist/utils/trailingText.js +32 -0
- package/package.json +2 -2
- package/dist/layout-engine/justifiedLineFit.d.ts +0 -7
- package/dist/layout-engine/justifiedLineFit.js +0 -6
|
@@ -53,6 +53,7 @@ function getTableCellFloatingImages(cell, cellMeasure, contentWidth, resolvePosi
|
|
|
53
53
|
const distLeft = run.distLeft ?? 12;
|
|
54
54
|
const distRight = run.distRight ?? 12;
|
|
55
55
|
const verticalOriginY = run.position?.vertical?.relativeTo === "paragraph" ? placement.top : placement.contentTop;
|
|
56
|
+
if (run.layoutInCell === false && !resolvePosition) continue;
|
|
56
57
|
const resolved = run.layoutInCell === false && resolvePosition ? resolvePosition(run, verticalOriginY) : resolveCellScopedPosition({
|
|
57
58
|
run,
|
|
58
59
|
paragraphY: verticalOriginY,
|
|
@@ -82,6 +83,7 @@ function getTableCellFloatingImages(cell, cellMeasure, contentWidth, resolvePosi
|
|
|
82
83
|
distLeft,
|
|
83
84
|
distRight,
|
|
84
85
|
wrapText,
|
|
86
|
+
...run.wrapType !== void 0 ? { wrapType: run.wrapType } : {},
|
|
85
87
|
...run.pmStart !== void 0 ? { pmStart: run.pmStart } : {},
|
|
86
88
|
...run.pmEnd !== void 0 ? { pmEnd: run.pmEnd } : {}
|
|
87
89
|
});
|
|
@@ -89,26 +91,56 @@ function getTableCellFloatingImages(cell, cellMeasure, contentWidth, resolvePosi
|
|
|
89
91
|
}
|
|
90
92
|
return result;
|
|
91
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* The text exclusion one cell-scoped picture casts, in cell content
|
|
96
|
+
* coordinates, or `undefined` when text ignores it.
|
|
97
|
+
*
|
|
98
|
+
* - `wp:wrapNone` (`behind` / `inFront`): text paints over or under the
|
|
99
|
+
* picture, so nothing is excluded.
|
|
100
|
+
* - `wp:wrapTopAndBottom`: no text beside the picture; lines it overlaps move
|
|
101
|
+
* below it.
|
|
102
|
+
* - `wp:wrapSquare` and the contour modes: text runs on the picture's open
|
|
103
|
+
* side. When the picture and its wrap distances cover the cell's content
|
|
104
|
+
* width there is no open side, and the text moves below the picture as for
|
|
105
|
+
* `wrapTopAndBottom`.
|
|
106
|
+
*/
|
|
107
|
+
function tableCellFloatingZone(img, contentWidth) {
|
|
108
|
+
if (img.wrapType === "behind" || img.wrapType === "inFront") return;
|
|
109
|
+
const rectLeft = img.x - img.distLeft;
|
|
110
|
+
const rectRight = img.x + img.width + img.distRight;
|
|
111
|
+
const topY = img.y - img.distTop;
|
|
112
|
+
const bottomY = img.y + img.height + img.distBottom;
|
|
113
|
+
const belowPicture = {
|
|
114
|
+
leftMargin: 0,
|
|
115
|
+
rightMargin: 0,
|
|
116
|
+
topY,
|
|
117
|
+
bottomY,
|
|
118
|
+
fullWidthBlock: true
|
|
119
|
+
};
|
|
120
|
+
if (img.wrapType === "topAndBottom") return belowPicture;
|
|
121
|
+
let leftMargin = 0;
|
|
122
|
+
let rightMargin = 0;
|
|
123
|
+
const wrapText = img.wrapText ?? "bothSides";
|
|
124
|
+
if (wrapText === "right") leftMargin = rectRight;
|
|
125
|
+
else if (wrapText === "left") rightMargin = contentWidth - rectLeft;
|
|
126
|
+
else if (img.side === "left") leftMargin = rectRight;
|
|
127
|
+
else rightMargin = contentWidth - rectLeft;
|
|
128
|
+
if (rectRight > 0 && rectLeft < contentWidth && Math.max(leftMargin, rightMargin) >= contentWidth) return belowPicture;
|
|
129
|
+
const clamped = clampFloatingWrapMargins(leftMargin, rightMargin, contentWidth);
|
|
130
|
+
return {
|
|
131
|
+
leftMargin: clamped.leftMargin,
|
|
132
|
+
rightMargin: clamped.rightMargin,
|
|
133
|
+
topY,
|
|
134
|
+
bottomY
|
|
135
|
+
};
|
|
136
|
+
}
|
|
92
137
|
function buildTableCellFloatingZones(floatingImages, contentWidth) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const wrapText = img.wrapText ?? "bothSides";
|
|
100
|
-
if (wrapText === "right") leftMargin = rectRight;
|
|
101
|
-
else if (wrapText === "left") rightMargin = contentWidth - (img.x - img.distLeft);
|
|
102
|
-
else if (img.side === "left") leftMargin = rectRight;
|
|
103
|
-
else rightMargin = contentWidth - (img.x - img.distLeft);
|
|
104
|
-
const clamped = clampFloatingWrapMargins(leftMargin, rightMargin, contentWidth);
|
|
105
|
-
return {
|
|
106
|
-
leftMargin: clamped.leftMargin,
|
|
107
|
-
rightMargin: clamped.rightMargin,
|
|
108
|
-
topY: rectTop,
|
|
109
|
-
bottomY: rectBottom
|
|
110
|
-
};
|
|
111
|
-
});
|
|
138
|
+
const zones = [];
|
|
139
|
+
for (const img of floatingImages) {
|
|
140
|
+
const zone = tableCellFloatingZone(img, contentWidth);
|
|
141
|
+
if (zone !== void 0) zones.push(zone);
|
|
142
|
+
}
|
|
143
|
+
return zones;
|
|
112
144
|
}
|
|
113
145
|
//#endregion
|
|
114
146
|
export { buildTableCellFloatingZones, getTableCellContentWidth, getTableCellFloatingImages };
|
|
@@ -16,13 +16,38 @@ type BuildTableCellPlacementsOptions = {
|
|
|
16
16
|
grid: TableCellGrid;
|
|
17
17
|
columnWidths: readonly number[];
|
|
18
18
|
bidi: boolean;
|
|
19
|
+
/** `w:tblCellSpacing` in pixels; see {@link getTableCellSpacingInsetsX}. */
|
|
20
|
+
cellSpacing?: number | undefined;
|
|
19
21
|
};
|
|
22
|
+
type TableCellSpacingInsets = {
|
|
23
|
+
readonly start: number;
|
|
24
|
+
readonly end: number;
|
|
25
|
+
};
|
|
26
|
+
type TableCellSpacingInsetsY = {
|
|
27
|
+
readonly top: number;
|
|
28
|
+
readonly bottom: number;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Logical inline insets of a cell box inside its grid slot under
|
|
32
|
+
* `w:tblCellSpacing` (§17.4.43). The spacing separates every cell from its
|
|
33
|
+
* neighbours and from the table edge: each cell keeps one unit on each side of
|
|
34
|
+
* its slot, and a slot at the table's leading or trailing grid edge keeps one
|
|
35
|
+
* more for the edge itself.
|
|
36
|
+
*/
|
|
37
|
+
declare const getTableCellSpacingInsetsX: (cellSpacing: number | undefined, sourceColumn: number, columnSpan: number, columnCount: number) => TableCellSpacingInsets;
|
|
38
|
+
/**
|
|
39
|
+
* Block-direction insets of a cell box inside the rows it spans, the
|
|
40
|
+
* `w:tblCellSpacing` counterpart of {@link getTableCellSpacingInsetsX}: one
|
|
41
|
+
* unit above and below every cell, plus one more at the table's first and
|
|
42
|
+
* last rows.
|
|
43
|
+
*/
|
|
44
|
+
declare const getTableCellSpacingInsetsY: (cellSpacing: number | undefined, rowIndex: number, rowSpan: number, rowCount: number) => TableCellSpacingInsetsY;
|
|
20
45
|
declare const buildTableCellGrid: (rows: readonly TableRow[], columnCount: number) => TableCellGrid;
|
|
21
46
|
declare const getFirstAvailableColumn: (grid: TableCellGrid, rowIndex: number, startingColumn: number) => number;
|
|
22
47
|
declare const getSourceCellAt: (grid: TableCellGrid, rowIndex: number, columnIndex: number) => TableCell | undefined;
|
|
23
48
|
declare const getSourceCellColumn: (grid: TableCellGrid, cell: TableCell) => number | undefined;
|
|
24
49
|
/** Resolve all source cells to canonical logical columns and physical boxes. */
|
|
25
|
-
declare const buildTableCellPlacements: ({ grid, columnWidths, bidi }: BuildTableCellPlacementsOptions) => TableCellPlacements;
|
|
26
|
-
declare const getTableCellVerticalBorderHeight: (grid: TableCellGrid, cell: TableCell | undefined, rowIndex: number) => number;
|
|
50
|
+
declare const buildTableCellPlacements: ({ grid, columnWidths, bidi, cellSpacing }: BuildTableCellPlacementsOptions) => TableCellPlacements;
|
|
51
|
+
declare const getTableCellVerticalBorderHeight: (grid: TableCellGrid, cell: TableCell | undefined, rowIndex: number, separated?: boolean) => number;
|
|
27
52
|
//#endregion
|
|
28
|
-
export { TableCellGrid, TableCellPlacement, TableCellPlacements, buildTableCellGrid, buildTableCellPlacements, getFirstAvailableColumn, getSourceCellAt, getSourceCellColumn, getTableCellVerticalBorderHeight };
|
|
53
|
+
export { TableCellGrid, TableCellPlacement, TableCellPlacements, TableCellSpacingInsets, TableCellSpacingInsetsY, buildTableCellGrid, buildTableCellPlacements, getFirstAvailableColumn, getSourceCellAt, getSourceCellColumn, getTableCellSpacingInsetsX, getTableCellSpacingInsetsY, getTableCellVerticalBorderHeight };
|
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
import { paintsCssBorder } from "../../utils/borderCss.js";
|
|
2
2
|
//#region src/layout-engine/measure/tableCellGrid.ts
|
|
3
|
+
const spacingUnit = (cellSpacing) => cellSpacing !== void 0 && Number.isFinite(cellSpacing) && cellSpacing > 0 ? cellSpacing : 0;
|
|
4
|
+
/**
|
|
5
|
+
* Logical inline insets of a cell box inside its grid slot under
|
|
6
|
+
* `w:tblCellSpacing` (§17.4.43). The spacing separates every cell from its
|
|
7
|
+
* neighbours and from the table edge: each cell keeps one unit on each side of
|
|
8
|
+
* its slot, and a slot at the table's leading or trailing grid edge keeps one
|
|
9
|
+
* more for the edge itself.
|
|
10
|
+
*/
|
|
11
|
+
const getTableCellSpacingInsetsX = (cellSpacing, sourceColumn, columnSpan, columnCount) => {
|
|
12
|
+
const unit = spacingUnit(cellSpacing);
|
|
13
|
+
if (unit === 0) return {
|
|
14
|
+
start: 0,
|
|
15
|
+
end: 0
|
|
16
|
+
};
|
|
17
|
+
return {
|
|
18
|
+
start: sourceColumn <= 0 ? unit * 2 : unit,
|
|
19
|
+
end: sourceColumn + columnSpan >= columnCount ? unit * 2 : unit
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Block-direction insets of a cell box inside the rows it spans, the
|
|
24
|
+
* `w:tblCellSpacing` counterpart of {@link getTableCellSpacingInsetsX}: one
|
|
25
|
+
* unit above and below every cell, plus one more at the table's first and
|
|
26
|
+
* last rows.
|
|
27
|
+
*/
|
|
28
|
+
const getTableCellSpacingInsetsY = (cellSpacing, rowIndex, rowSpan, rowCount) => {
|
|
29
|
+
const unit = spacingUnit(cellSpacing);
|
|
30
|
+
if (unit === 0) return {
|
|
31
|
+
top: 0,
|
|
32
|
+
bottom: 0
|
|
33
|
+
};
|
|
34
|
+
return {
|
|
35
|
+
top: rowIndex <= 0 ? unit * 2 : unit,
|
|
36
|
+
bottom: rowIndex + Math.max(1, rowSpan) >= rowCount ? unit * 2 : unit
|
|
37
|
+
};
|
|
38
|
+
};
|
|
3
39
|
const buildTableCellGrid = (rows, columnCount) => {
|
|
4
40
|
const occupiedColumnsByRow = /* @__PURE__ */ new Map();
|
|
5
41
|
const sourceCellsByRow = /* @__PURE__ */ new Map();
|
|
@@ -36,7 +72,7 @@ const getFirstAvailableColumn = (grid, rowIndex, startingColumn) => firstAvailab
|
|
|
36
72
|
const getSourceCellAt = (grid, rowIndex, columnIndex) => grid.sourceCellsByRow.get(rowIndex)?.get(columnIndex);
|
|
37
73
|
const getSourceCellColumn = (grid, cell) => grid.sourceColumnsByCell.get(cell);
|
|
38
74
|
/** Resolve all source cells to canonical logical columns and physical boxes. */
|
|
39
|
-
const buildTableCellPlacements = ({ grid, columnWidths, bidi }) => {
|
|
75
|
+
const buildTableCellPlacements = ({ grid, columnWidths, bidi, cellSpacing }) => {
|
|
40
76
|
const columnOffsets = [0];
|
|
41
77
|
for (const columnWidth of columnWidths) columnOffsets.push((columnOffsets.at(-1) ?? 0) + columnWidth);
|
|
42
78
|
const tableWidth = columnOffsets.at(-1) ?? 0;
|
|
@@ -45,8 +81,11 @@ const buildTableCellPlacements = ({ grid, columnWidths, bidi }) => {
|
|
|
45
81
|
if (sourceColumn < 0 || sourceColumn >= columnWidths.length) continue;
|
|
46
82
|
const declaredColumnSpan = Math.max(1, Math.trunc(cell.colSpan ?? 1));
|
|
47
83
|
const columnSpan = Math.min(declaredColumnSpan, columnWidths.length - sourceColumn);
|
|
48
|
-
const
|
|
49
|
-
const
|
|
84
|
+
const slotLeft = columnOffsets.at(sourceColumn) ?? 0;
|
|
85
|
+
const slotRight = columnOffsets.at(sourceColumn + columnSpan) ?? slotLeft;
|
|
86
|
+
const insets = getTableCellSpacingInsetsX(cellSpacing, sourceColumn, columnSpan, columnWidths.length);
|
|
87
|
+
const logicalLeft = Math.min(slotRight, slotLeft + insets.start);
|
|
88
|
+
const logicalRight = Math.max(logicalLeft, slotRight - insets.end);
|
|
50
89
|
const width = logicalRight - logicalLeft;
|
|
51
90
|
placements.set(cell, {
|
|
52
91
|
sourceColumn,
|
|
@@ -57,7 +96,8 @@ const buildTableCellPlacements = ({ grid, columnWidths, bidi }) => {
|
|
|
57
96
|
}
|
|
58
97
|
return placements;
|
|
59
98
|
};
|
|
60
|
-
const getTableCellVerticalBorderHeight = (grid, cell, rowIndex) => {
|
|
99
|
+
const getTableCellVerticalBorderHeight = (grid, cell, rowIndex, separated = false) => {
|
|
100
|
+
if (separated) return (cell?.borders?.top?.width ?? 0) + (cell?.borders?.bottom?.width ?? 0);
|
|
61
101
|
const sourceColumn = cell ? getSourceCellColumn(grid, cell) : void 0;
|
|
62
102
|
const aboveBottom = (sourceColumn === void 0 ? void 0 : getSourceCellAt(grid, rowIndex - 1, sourceColumn))?.borders?.bottom;
|
|
63
103
|
const aboveOwnsEdge = aboveBottom !== void 0 && paintsCssBorder(aboveBottom.style);
|
|
@@ -83,4 +123,4 @@ const getOrCreateOccupiedRow = (occupiedColumnsByRow, rowIndex) => {
|
|
|
83
123
|
return occupiedColumns;
|
|
84
124
|
};
|
|
85
125
|
//#endregion
|
|
86
|
-
export { buildTableCellGrid, buildTableCellPlacements, getFirstAvailableColumn, getSourceCellAt, getSourceCellColumn, getTableCellVerticalBorderHeight };
|
|
126
|
+
export { buildTableCellGrid, buildTableCellPlacements, getFirstAvailableColumn, getSourceCellAt, getSourceCellColumn, getTableCellSpacingInsetsX, getTableCellSpacingInsetsY, getTableCellVerticalBorderHeight };
|
|
@@ -9,7 +9,8 @@ const tableFragmentBottomBorders = ({ fragment, block, measure, placements: supp
|
|
|
9
9
|
const placements = suppliedPlacements ?? buildTableCellPlacements({
|
|
10
10
|
grid: buildTableCellGrid(block.rows, measure.columnWidths.length),
|
|
11
11
|
columnWidths: measure.columnWidths,
|
|
12
|
-
bidi: block.bidi === true
|
|
12
|
+
bidi: block.bidi === true,
|
|
13
|
+
cellSpacing: block.cellSpacing
|
|
13
14
|
});
|
|
14
15
|
return splitRow.cells.flatMap((cell) => {
|
|
15
16
|
const placement = placements.get(cell);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { FlowBlock, Layout, Measure, NoteAreaLayout } from "./types.js";
|
|
2
|
+
import { PageTopContinuation, Paginator } from "./paginator.js";
|
|
3
|
+
//#region src/layout-engine/noteAreaFlow.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* The continuation separator a page opens with while a note area continues
|
|
6
|
+
* onto it. Its space after collapses with the next note paragraph's space
|
|
7
|
+
* before, as between any two paragraphs.
|
|
8
|
+
*/
|
|
9
|
+
declare function createNoteAreaContinuation(noteAreas: NoteAreaLayout | undefined, paginator: Paginator): PageTopContinuation | undefined;
|
|
10
|
+
/** Whether laying out `block` continues a note area onto any page it opens. */
|
|
11
|
+
declare function continuesNoteArea(noteAreas: NoteAreaLayout | undefined, block: FlowBlock): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Record where each separator mark landed. A rule is centred on the first line
|
|
14
|
+
* of the separator paragraph: a `w:separator` spans part of the column and a
|
|
15
|
+
* `w:continuationSeparator` all of it.
|
|
16
|
+
*/
|
|
17
|
+
declare function annotateNoteSeparators(layout: Layout, blocks: readonly FlowBlock[], measures: readonly Measure[], noteAreas: NoteAreaLayout | undefined): Layout;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { annotateNoteSeparators, continuesNoteArea, createNoteAreaContinuation };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { measuredLineRangeHeight } from "./lineFlow.js";
|
|
2
|
+
import { getParagraphSpacingAfter } from "./paragraphSpacing.js";
|
|
3
|
+
import { NOTE_SEPARATOR_RULE_THICKNESS, NOTE_SEPARATOR_WIDTH_FRACTION } from "./types.js";
|
|
4
|
+
//#region src/layout-engine/noteAreaFlow.ts
|
|
5
|
+
/**
|
|
6
|
+
* Note areas that flow with the body (ECMA-376 §17.11).
|
|
7
|
+
*
|
|
8
|
+
* Endnotes are collected after the last body block of the document, or of a
|
|
9
|
+
* section, and paginate exactly as body content does. Two things set them
|
|
10
|
+
* apart from the body: the area opens with its `w:separator` story, and every
|
|
11
|
+
* page the area continues onto opens with its `w:continuationSeparator` story.
|
|
12
|
+
* Both stories draw their separator mark as a horizontal rule.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* The continuation separator a page opens with while a note area continues
|
|
16
|
+
* onto it. Its space after collapses with the next note paragraph's space
|
|
17
|
+
* before, as between any two paragraphs.
|
|
18
|
+
*/
|
|
19
|
+
function createNoteAreaContinuation(noteAreas, paginator) {
|
|
20
|
+
const continuation = noteAreas?.continuationSeparator;
|
|
21
|
+
if (continuation === void 0) return;
|
|
22
|
+
const { block, measure } = continuation;
|
|
23
|
+
const height = measuredLineRangeHeight(measure.lines, 0, measure.lines.length);
|
|
24
|
+
return (state, x, width) => {
|
|
25
|
+
const fragment = {
|
|
26
|
+
kind: "paragraph",
|
|
27
|
+
blockId: block.id,
|
|
28
|
+
x,
|
|
29
|
+
y: state.cursorY,
|
|
30
|
+
width,
|
|
31
|
+
height,
|
|
32
|
+
fromLine: 0,
|
|
33
|
+
toLine: measure.lines.length
|
|
34
|
+
};
|
|
35
|
+
paginator.addUnflowedFragment(fragment);
|
|
36
|
+
state.trailingSpacing = getParagraphSpacingAfter(block);
|
|
37
|
+
return height;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** Whether laying out `block` continues a note area onto any page it opens. */
|
|
41
|
+
function continuesNoteArea(noteAreas, block) {
|
|
42
|
+
return noteAreas?.contentBlockIds.has(String(block.id)) === true;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Record where each separator mark landed. A rule is centred on the first line
|
|
46
|
+
* of the separator paragraph: a `w:separator` spans part of the column and a
|
|
47
|
+
* `w:continuationSeparator` all of it.
|
|
48
|
+
*/
|
|
49
|
+
function annotateNoteSeparators(layout, blocks, measures, noteAreas) {
|
|
50
|
+
if (noteAreas === void 0 || noteAreas.separatorBlockIds.size === 0) return layout;
|
|
51
|
+
const measureById = /* @__PURE__ */ new Map();
|
|
52
|
+
for (const [index, block] of blocks.entries()) {
|
|
53
|
+
const measure = measures[index];
|
|
54
|
+
if (noteAreas.separatorBlockIds.has(String(block.id)) && measure?.kind === "paragraph") measureById.set(String(block.id), measure);
|
|
55
|
+
}
|
|
56
|
+
const continuation = noteAreas.continuationSeparator;
|
|
57
|
+
if (continuation !== void 0) measureById.set(String(continuation.block.id), continuation.measure);
|
|
58
|
+
for (const page of layout.pages) {
|
|
59
|
+
const rules = [];
|
|
60
|
+
for (const fragment of page.fragments) {
|
|
61
|
+
if (fragment.kind !== "paragraph" || fragment.fromLine !== 0) continue;
|
|
62
|
+
const kind = noteAreas.separatorBlockIds.get(String(fragment.blockId));
|
|
63
|
+
const firstLine = measureById.get(String(fragment.blockId))?.lines.at(0);
|
|
64
|
+
if (kind === void 0 || firstLine === void 0) continue;
|
|
65
|
+
rules.push({
|
|
66
|
+
x: fragment.x,
|
|
67
|
+
y: fragment.y + (firstLine.lineHeight - NOTE_SEPARATOR_RULE_THICKNESS) / 2,
|
|
68
|
+
width: fragment.width * separatorWidthFraction(kind)
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
if (rules.length > 0) page.noteSeparators = rules;
|
|
72
|
+
}
|
|
73
|
+
return layout;
|
|
74
|
+
}
|
|
75
|
+
function separatorWidthFraction(kind) {
|
|
76
|
+
switch (kind) {
|
|
77
|
+
case "separator": return NOTE_SEPARATOR_WIDTH_FRACTION;
|
|
78
|
+
case "continuationSeparator": return 1;
|
|
79
|
+
default: return kind;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//#endregion
|
|
83
|
+
export { annotateNoteSeparators, continuesNoteArea, createNoteAreaContinuation };
|
|
@@ -51,6 +51,8 @@ type PaginatorOptions = {
|
|
|
51
51
|
firstPageMargins?: PageMargins;
|
|
52
52
|
/** Per-section body margins used on even authored page numbers. */
|
|
53
53
|
sectionEvenPageMargins?: (PageMargins | undefined)[];
|
|
54
|
+
/** Per-section body margins used on the first page of a `w:titlePg` section. */
|
|
55
|
+
sectionFirstPageMargins?: (PageMargins | undefined)[];
|
|
54
56
|
/** Page-number policy for the initial section. */
|
|
55
57
|
pageNumbering?: SectionPageNumbering;
|
|
56
58
|
/** Column configuration (optional). */
|
|
@@ -65,6 +67,13 @@ type PaginatorOptions = {
|
|
|
65
67
|
type ForcePageBreakOptions = {
|
|
66
68
|
coalesceBlankPage?: boolean;
|
|
67
69
|
};
|
|
70
|
+
/**
|
|
71
|
+
* Content that opens every page created while a flow continues from an
|
|
72
|
+
* earlier page (a note area's continuation separator). Called with the new
|
|
73
|
+
* page's state and the first column's box; returns the height it placed,
|
|
74
|
+
* which the page's flow then starts below.
|
|
75
|
+
*/
|
|
76
|
+
type PageTopContinuation = (state: PageState, x: number, width: number) => number;
|
|
68
77
|
declare const SECTION_START_PLACEMENT: {
|
|
69
78
|
readonly CONTINUOUS: "continuous";
|
|
70
79
|
readonly NEXT_PAGE: "nextPage";
|
|
@@ -147,7 +156,9 @@ declare function createPaginator(options: PaginatorOptions): {
|
|
|
147
156
|
}, newMargins?: PageMargins, applyImmediately?: boolean) => void;
|
|
148
157
|
/** Mark the next created page as the first page of a new section. */
|
|
149
158
|
startSection: ({ sectionIndex, pageNumbering, placement }: StartSectionOptions) => void;
|
|
159
|
+
/** Set (or clear) the content that opens each page a flow continues onto. */
|
|
160
|
+
setPageTopContinuation: (continuation: PageTopContinuation | undefined) => void;
|
|
150
161
|
};
|
|
151
162
|
type Paginator = ReturnType<typeof createPaginator>;
|
|
152
163
|
//#endregion
|
|
153
|
-
export { FOOTNOTE_SEPARATOR_HEIGHT, PageState, Paginator, PaginatorOptions, SECTION_START_PLACEMENT, calculateColumnLefts, calculateColumnWidths, createPaginator, resolveColumnLeft };
|
|
164
|
+
export { FOOTNOTE_SEPARATOR_HEIGHT, PageState, PageTopContinuation, Paginator, PaginatorOptions, SECTION_START_PLACEMENT, calculateColumnLefts, calculateColumnWidths, createPaginator, resolveColumnLeft };
|
|
@@ -62,6 +62,7 @@ function createPaginator(options) {
|
|
|
62
62
|
type: "pending",
|
|
63
63
|
placement: "nextPage"
|
|
64
64
|
};
|
|
65
|
+
let pageTopContinuation;
|
|
65
66
|
const pages = [];
|
|
66
67
|
const states = [];
|
|
67
68
|
const paginationStateByPage = /* @__PURE__ */ new WeakMap();
|
|
@@ -93,7 +94,11 @@ function createPaginator(options) {
|
|
|
93
94
|
}
|
|
94
95
|
function getPageMargins(pageNumber, logicalPageNumber) {
|
|
95
96
|
const evenMargins = logicalPageNumber % 2 === 0 ? options.sectionEvenPageMargins?.[currentSectionIndex] : void 0;
|
|
96
|
-
const
|
|
97
|
+
const titlePageMargins = currentSectionPageNumber === 1 ? options.sectionFirstPageMargins?.[currentSectionIndex] : void 0;
|
|
98
|
+
let pageMargins;
|
|
99
|
+
if (titlePageMargins) pageMargins = { ...titlePageMargins };
|
|
100
|
+
else if (pageNumber === 1 && options.firstPageMargins) pageMargins = { ...options.firstPageMargins };
|
|
101
|
+
else pageMargins = { ...evenMargins ?? margins };
|
|
97
102
|
if (options.mirrorMargins === true && pageNumber % 2 === 0) return {
|
|
98
103
|
...pageMargins,
|
|
99
104
|
left: pageMargins.right,
|
|
@@ -180,6 +185,13 @@ function createPaginator(options) {
|
|
|
180
185
|
states.push(state);
|
|
181
186
|
columnRegionTop = topMargin;
|
|
182
187
|
columnRegionMaxBottom = topMargin;
|
|
188
|
+
const continuationHeight = pageTopContinuation?.(state, getColumnX(0), columnWidths[0] ?? getContentWidth()) ?? 0;
|
|
189
|
+
if (continuationHeight > 0) {
|
|
190
|
+
state.cursorY += continuationHeight;
|
|
191
|
+
state.topMargin += continuationHeight;
|
|
192
|
+
columnRegionTop = state.topMargin;
|
|
193
|
+
columnRegionMaxBottom = state.topMargin;
|
|
194
|
+
}
|
|
183
195
|
if (options.onNewPage) options.onNewPage(state);
|
|
184
196
|
return state;
|
|
185
197
|
}
|
|
@@ -503,7 +515,11 @@ function createPaginator(options) {
|
|
|
503
515
|
/** Update page size/margins for subsequent pages. */
|
|
504
516
|
updatePageLayout,
|
|
505
517
|
/** Mark the next created page as the first page of a new section. */
|
|
506
|
-
startSection
|
|
518
|
+
startSection,
|
|
519
|
+
/** Set (or clear) the content that opens each page a flow continues onto. */
|
|
520
|
+
setPageTopContinuation: (continuation) => {
|
|
521
|
+
pageTopContinuation = continuation;
|
|
522
|
+
}
|
|
507
523
|
};
|
|
508
524
|
}
|
|
509
525
|
//#endregion
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FlowBlock, ParagraphBlock } from "./types.js";
|
|
1
|
+
import { FlowBlock, ParagraphBlock, TableBlock } from "./types.js";
|
|
2
2
|
//#region src/layout-engine/paragraphSpacing.d.ts
|
|
3
3
|
type EffectiveParagraphSpacing = Readonly<{
|
|
4
4
|
before: number;
|
|
@@ -35,5 +35,7 @@ declare const resolveEffectiveParagraphSpacing: (block: ParagraphBlock) => Effec
|
|
|
35
35
|
*/
|
|
36
36
|
declare const resolveEffectiveParagraphSpacingTree: (blocks: FlowBlock[]) => FlowBlock[];
|
|
37
37
|
declare const paragraphsShareStyle: (previous: ParagraphBlock, current: ParagraphBlock) => boolean;
|
|
38
|
+
/** Derive the effective paragraph spacing of every cell story in a table. */
|
|
39
|
+
declare const resolveTableSpacing: (block: TableBlock) => TableBlock;
|
|
38
40
|
//#endregion
|
|
39
|
-
export { collapseParagraphSpacing, getParagraphSpacingAfter, getParagraphSpacingBefore, isAuthoredEmptyParagraph, isEmptyParagraph, paragraphsShareStyle, resolveEffectiveParagraphSpacing, resolveEffectiveParagraphSpacingTree };
|
|
41
|
+
export { collapseParagraphSpacing, getParagraphSpacingAfter, getParagraphSpacingBefore, isAuthoredEmptyParagraph, isEmptyParagraph, paragraphsShareStyle, resolveEffectiveParagraphSpacing, resolveEffectiveParagraphSpacingTree, resolveTableSpacing };
|
|
@@ -88,6 +88,7 @@ const resolveTextBoxContentSpacing = (blocks) => {
|
|
|
88
88
|
});
|
|
89
89
|
return changed ? resolved : blocks;
|
|
90
90
|
};
|
|
91
|
+
/** Derive the effective paragraph spacing of every cell story in a table. */
|
|
91
92
|
const resolveTableSpacing = (block) => {
|
|
92
93
|
let changed = false;
|
|
93
94
|
const rows = block.rows.map((row) => {
|
|
@@ -122,4 +123,4 @@ const resolveTextBoxSpacing = (block) => {
|
|
|
122
123
|
};
|
|
123
124
|
};
|
|
124
125
|
//#endregion
|
|
125
|
-
export { collapseParagraphSpacing, getParagraphSpacingAfter, getParagraphSpacingBefore, isAuthoredEmptyParagraph, isEmptyParagraph, paragraphsShareStyle, resolveEffectiveParagraphSpacing, resolveEffectiveParagraphSpacingTree };
|
|
126
|
+
export { collapseParagraphSpacing, getParagraphSpacingAfter, getParagraphSpacingBefore, isAuthoredEmptyParagraph, isEmptyParagraph, paragraphsShareStyle, resolveEffectiveParagraphSpacing, resolveEffectiveParagraphSpacingTree, resolveTableSpacing };
|
|
@@ -19,6 +19,13 @@ type TableRowBreakInfo = {
|
|
|
19
19
|
/** Suppressible leading paragraph whitespace after each matching break offset. */
|
|
20
20
|
continuationSkips: number[][];
|
|
21
21
|
};
|
|
22
|
+
/**
|
|
23
|
+
* Height of the start of a table that a preceding `w:keepNext` paragraph
|
|
24
|
+
* (§17.3.1.15) must share a page with: the leading header rows (§17.4.49) and
|
|
25
|
+
* the first body row. A row that may break across pages (no `w:cantSplit`,
|
|
26
|
+
* §17.4.6) needs only its first line there; an unbreakable row needs all of it.
|
|
27
|
+
*/
|
|
28
|
+
declare function tableKeepNextOpeningHeight(block: TableBlock, measure: TableMeasure): number;
|
|
22
29
|
/** Build break geometry for a table from its block + measure. */
|
|
23
30
|
declare function buildTableRowBreakInfo(block: TableBlock, measure: TableMeasure): TableRowBreakInfo;
|
|
24
31
|
/** Leading empty-paragraph whitespace to consume when a row resumes after `offset`. */
|
|
@@ -30,4 +37,4 @@ declare function getRowContinuationSkip(info: TableRowBreakInfo, rowIndex: numbe
|
|
|
30
37
|
*/
|
|
31
38
|
declare function snapRowBreak(info: TableRowBreakInfo, rowIndex: number, fromOffset: number, maxSlice: number): number;
|
|
32
39
|
//#endregion
|
|
33
|
-
export { TableRowBreakInfo, buildTableRowBreakInfo, getRowContinuationSkip, snapRowBreak };
|
|
40
|
+
export { TableRowBreakInfo, buildTableRowBreakInfo, getRowContinuationSkip, snapRowBreak, tableKeepNextOpeningHeight };
|
|
@@ -2,6 +2,7 @@ import { measuredLineAdvance } from "./lineFlow.js";
|
|
|
2
2
|
import { measureParagraph } from "./measure/measureParagraph.js";
|
|
3
3
|
import { buildTableCellFloatingZones, getTableCellContentWidth, getTableCellFloatingImages } from "./measure/tableCellFloating.js";
|
|
4
4
|
import { createTableCellFlowState, placeTableCellBlock } from "./measure/tableCellFlow.js";
|
|
5
|
+
import { getTableCellSpacingInsetsY } from "./measure/tableCellGrid.js";
|
|
5
6
|
import { isEmptyParagraph } from "./paragraphSpacing.js";
|
|
6
7
|
import { resolveTableCellPadding } from "./types.js";
|
|
7
8
|
//#region src/layout-engine/tableRowBreak.ts
|
|
@@ -140,6 +141,27 @@ const continuationSkipAfter = (geometries, offset) => {
|
|
|
140
141
|
if (skips.length === 0 || skips.some((skip) => skip <= BREAK_OFFSET_EPSILON)) return 0;
|
|
141
142
|
return Math.min(...skips);
|
|
142
143
|
};
|
|
144
|
+
/**
|
|
145
|
+
* Height of the start of a table that a preceding `w:keepNext` paragraph
|
|
146
|
+
* (§17.3.1.15) must share a page with: the leading header rows (§17.4.49) and
|
|
147
|
+
* the first body row. A row that may break across pages (no `w:cantSplit`,
|
|
148
|
+
* §17.4.6) needs only its first line there; an unbreakable row needs all of it.
|
|
149
|
+
*/
|
|
150
|
+
function tableKeepNextOpeningHeight(block, measure) {
|
|
151
|
+
let height = 0;
|
|
152
|
+
let rowIndex = 0;
|
|
153
|
+
while (rowIndex < measure.rows.length && block.rows[rowIndex]?.isHeader === true) {
|
|
154
|
+
height += measure.rows[rowIndex]?.height ?? 0;
|
|
155
|
+
rowIndex += 1;
|
|
156
|
+
}
|
|
157
|
+
const row = measure.rows[rowIndex];
|
|
158
|
+
const sourceRow = block.rows[rowIndex];
|
|
159
|
+
if (!row || !sourceRow) return height;
|
|
160
|
+
const breakOffsets = buildTableRowBreakInfo(block, measure).breakOffsets[rowIndex] ?? [];
|
|
161
|
+
const firstBreak = breakOffsets.at(0);
|
|
162
|
+
const rowMayBreak = sourceRow.cantSplit !== true && breakOffsets.length > 1;
|
|
163
|
+
return height + (rowMayBreak && firstBreak !== void 0 ? firstBreak : row.height);
|
|
164
|
+
}
|
|
143
165
|
/** Build break geometry for a table from its block + measure. */
|
|
144
166
|
function buildTableRowBreakInfo(block, measure) {
|
|
145
167
|
const rowCount = measure.rows.length;
|
|
@@ -164,7 +186,8 @@ function buildTableRowBreakInfo(block, measure) {
|
|
|
164
186
|
const measuredCell = measuredCells[c];
|
|
165
187
|
if (!measuredCell) continue;
|
|
166
188
|
const sourceCell = sourceCells[c];
|
|
167
|
-
const
|
|
189
|
+
const spacing = getTableCellSpacingInsetsY(block.cellSpacing, r, sourceCell?.rowSpan ?? 1, block.rows.length);
|
|
190
|
+
const geometry = shiftCellGeometry(cellBreakGeometry(sourceCell, measuredCell), spacing.top + getVerticalAlignmentOffset(sourceCell, measuredCell, Math.max(0, rowHeight - spacing.top - spacing.bottom)));
|
|
168
191
|
cellGeometries.push(geometry);
|
|
169
192
|
for (const b of geometry.bottoms) if (b > 0 && b < rowHeight) offsets.add(b);
|
|
170
193
|
}
|
|
@@ -206,4 +229,4 @@ function snapRowBreak(info, rowIndex, fromOffset, maxSlice) {
|
|
|
206
229
|
return best;
|
|
207
230
|
}
|
|
208
231
|
//#endregion
|
|
209
|
-
export { buildTableRowBreakInfo, getRowContinuationSkip, snapRowBreak };
|
|
232
|
+
export { buildTableRowBreakInfo, getRowContinuationSkip, snapRowBreak, tableKeepNextOpeningHeight };
|