@stll/folio-core 0.23.1 → 0.25.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/controller/fontReadiness.js +12 -7
- package/dist/controller/layoutPipeline.js +3 -4
- package/dist/docx/fieldParser.d.ts +1 -1
- package/dist/docx/fieldParser.js +2 -9
- package/dist/docx/numberingParser.d.ts +2 -1
- package/dist/docx/numberingParser.js +24 -80
- package/dist/docx/paragraphParser.js +3 -4
- package/dist/docx/paragraphTextBoxEnrichment.js +1 -0
- package/dist/docx/sectionParser.js +2 -2
- package/dist/docx/serializer/runSerializer.js +1 -0
- package/dist/docx/textBoxParser.js +4 -0
- package/dist/fields/fieldContext.d.ts +2 -1
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +9 -1
- package/dist/layout-bridge/convert/headerFooterLayout.js +27 -2
- package/dist/layout-bridge/convert/toFlowBlocks.js +12 -16
- package/dist/layout-bridge/engine/selectionRects.js +3 -5
- package/dist/layout-engine/index.d.ts +2 -2
- package/dist/layout-engine/index.js +3 -5
- package/dist/layout-engine/measure/cache.js +4 -0
- package/dist/layout-engine/measure/listMarkerWidth.d.ts +3 -1
- package/dist/layout-engine/measure/listMarkerWidth.js +25 -21
- package/dist/layout-engine/measure/measureBlocks.js +27 -11
- package/dist/layout-engine/measure/measureParagraph.js +7 -1
- package/dist/layout-engine/measure/tableCellFloating.js +2 -3
- package/dist/layout-engine/renderedBreakReconciliation.js +1 -1
- package/dist/layout-engine/tableRowBreak.js +2 -2
- package/dist/layout-engine/types.d.ts +16 -11
- package/dist/layout-engine/types.js +15 -1
- package/dist/layout-painter/renderParagraph.d.ts +9 -1
- package/dist/layout-painter/renderParagraph.js +65 -52
- package/dist/layout-painter/renderTable.js +2 -5
- package/dist/layout-painter/renderTextBox.js +1 -1
- package/dist/layout-painter/renderUtils.d.ts +2 -1
- package/dist/prosemirror/attrs/index.js +4 -6
- package/dist/prosemirror/conversion/fromProseDoc.js +2 -3
- package/dist/prosemirror/conversion/toProseDoc.js +3 -3
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +1 -3
- package/dist/prosemirror/extensions/features/ListExtension.js +1 -3
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +2 -0
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +7 -0
- package/dist/prosemirror/schema/nodes.d.ts +5 -7
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -3
- package/dist/utils/paragraphBaseDirection.d.ts +6 -0
- package/dist/utils/paragraphBaseDirection.js +12 -0
- package/package.json +2 -2
|
@@ -186,6 +186,10 @@ function hashParagraphBlock(block) {
|
|
|
186
186
|
if (attrs.reserveEmptyOutlineHeight) parts.push("outline-empty-reserve");
|
|
187
187
|
if (attrs.documentGridLinePitch !== void 0) parts.push(`documentGrid:${attrs.documentGridLinePitch}|${attrs.snapToGrid !== false}`);
|
|
188
188
|
if (attrs.justificationCompatibility) parts.push(`justify-compat:${attrs.justificationCompatibility.type}`);
|
|
189
|
+
if (attrs.listMarker !== void 0) {
|
|
190
|
+
const marker = attrs.listMarkerFormatting;
|
|
191
|
+
parts.push(`marker:${attrs.listMarker}|${attrs.listMarkerHidden}|${attrs.listMarkerAlignment}|${attrs.listMarkerSuffix}|${marker?.fontFamily}|${marker?.eastAsiaFontFamily}|${marker?.complexScriptFontFamily}|${marker?.fontSize}|${marker?.complexScriptFontSize}|${marker?.bold}|${marker?.complexScriptBold}|${marker?.italic}|${marker?.complexScriptItalic}|${marker?.rtl}|${marker?.forceComplexScript}`);
|
|
192
|
+
}
|
|
189
193
|
parts.push(...lineBreakPolicyCacheParts(attrs));
|
|
190
194
|
const borders = attrs.borders;
|
|
191
195
|
if (borders) {
|
|
@@ -8,7 +8,7 @@ import { ParagraphBlock } from "../types.js";
|
|
|
8
8
|
declare const DEFAULT_TAB_STOP_TWIPS = 720;
|
|
9
9
|
/**
|
|
10
10
|
* Marker font resolution per ECMA-376 §17.9.6:
|
|
11
|
-
* 1. explicit numbering-level rPr (`attrs.
|
|
11
|
+
* 1. explicit numbering-level rPr (`attrs.listMarkerFormatting`),
|
|
12
12
|
* 2. first body text run's font,
|
|
13
13
|
* 3. paragraph defaults, then document defaults.
|
|
14
14
|
*/
|
|
@@ -16,6 +16,8 @@ declare function resolveListMarkerFont(block: ParagraphBlock): {
|
|
|
16
16
|
fontFamily: string;
|
|
17
17
|
fontSize: number;
|
|
18
18
|
bold?: boolean;
|
|
19
|
+
italic?: boolean;
|
|
20
|
+
rtl?: boolean;
|
|
19
21
|
};
|
|
20
22
|
/**
|
|
21
23
|
* Compute the marker's inline-block width in pixels, or 0 if the paragraph
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { hasCjk, hasComplexScript } from "../../utils/scriptSegments.js";
|
|
2
|
+
import { hasComplexScriptFormatting, resolveComplexScriptFormatting } from "./complexScriptFormatting.js";
|
|
1
3
|
import { ptToPx } from "./measureHelpers.js";
|
|
2
4
|
import { measureTextWidth } from "./measureProvider.js";
|
|
3
5
|
//#region src/layout-engine/measure/listMarkerWidth.ts
|
|
@@ -12,21 +14,33 @@ const DEFAULT_TAB_STOP_TWIPS = 720;
|
|
|
12
14
|
const TWIPS_TO_PX = 96 / 1440;
|
|
13
15
|
/**
|
|
14
16
|
* Marker font resolution per ECMA-376 §17.9.6:
|
|
15
|
-
* 1. explicit numbering-level rPr (`attrs.
|
|
17
|
+
* 1. explicit numbering-level rPr (`attrs.listMarkerFormatting`),
|
|
16
18
|
* 2. first body text run's font,
|
|
17
19
|
* 3. paragraph defaults, then document defaults.
|
|
18
20
|
*/
|
|
19
21
|
function resolveListMarkerFont(block) {
|
|
20
22
|
const attrs = block.attrs;
|
|
21
23
|
const firstTextRun = block.runs.find((r) => r.kind === "text");
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
fontFamily,
|
|
27
|
-
fontSize,
|
|
28
|
-
...bold !== void 0 ? { bold } : {}
|
|
24
|
+
const markerFormatting = attrs?.listMarkerFormatting;
|
|
25
|
+
const bold = markerFormatting?.bold ?? firstTextRun?.bold;
|
|
26
|
+
const italic = markerFormatting?.italic ?? firstTextRun?.italic;
|
|
27
|
+
const base = {
|
|
28
|
+
fontFamily: markerFormatting?.fontFamily ?? firstTextRun?.fontFamily ?? attrs?.defaultFontFamily ?? DEFAULT_FONT_FAMILY,
|
|
29
|
+
fontSize: markerFormatting?.fontSize ?? firstTextRun?.fontSize ?? attrs?.defaultFontSize ?? DEFAULT_FONT_SIZE,
|
|
30
|
+
...bold !== void 0 ? { bold } : {},
|
|
31
|
+
...italic !== void 0 ? { italic } : {},
|
|
32
|
+
...markerFormatting?.rtl !== void 0 ? { rtl: markerFormatting.rtl } : {}
|
|
29
33
|
};
|
|
34
|
+
const marker = attrs?.listMarker ?? "";
|
|
35
|
+
if (markerFormatting && hasComplexScriptFormatting(markerFormatting) && (markerFormatting.forceComplexScript || hasComplexScript(marker))) return {
|
|
36
|
+
...base,
|
|
37
|
+
...resolveComplexScriptFormatting(markerFormatting)
|
|
38
|
+
};
|
|
39
|
+
if (markerFormatting?.eastAsiaFontFamily && hasCjk(marker)) return {
|
|
40
|
+
...base,
|
|
41
|
+
fontFamily: markerFormatting.eastAsiaFontFamily
|
|
42
|
+
};
|
|
43
|
+
return base;
|
|
30
44
|
}
|
|
31
45
|
/**
|
|
32
46
|
* Compute the marker's inline-block width in pixels, or 0 if the paragraph
|
|
@@ -49,12 +63,7 @@ function resolveListMarkerFont(block) {
|
|
|
49
63
|
function getListMarkerInlineWidth(block) {
|
|
50
64
|
const attrs = block.attrs;
|
|
51
65
|
if (!attrs?.listMarker || attrs.listMarkerHidden) return 0;
|
|
52
|
-
const
|
|
53
|
-
const style = {
|
|
54
|
-
fontFamily,
|
|
55
|
-
fontSize,
|
|
56
|
-
...bold !== void 0 ? { bold } : {}
|
|
57
|
-
};
|
|
66
|
+
const style = resolveListMarkerFont(block);
|
|
58
67
|
const naturalWidth = measureTextWidth(attrs.listMarker, style);
|
|
59
68
|
const markerEndOffset = getMarkerEndOffset(naturalWidth, attrs.listMarkerAlignment);
|
|
60
69
|
const suffix = attrs.listMarkerSuffix ?? "tab";
|
|
@@ -75,19 +84,14 @@ function getListMarkerInlineWidth(block) {
|
|
|
75
84
|
let bodyStart;
|
|
76
85
|
if (firstCustomPast !== void 0 && firstGridPast !== void 0) bodyStart = Math.min(firstCustomPast, firstGridPast);
|
|
77
86
|
else bodyStart = firstCustomPast ?? firstGridPast;
|
|
78
|
-
if (bodyStart === void 0) return naturalWidth + ptToPx(fontSize) * .5;
|
|
87
|
+
if (bodyStart === void 0) return naturalWidth + ptToPx(style.fontSize ?? DEFAULT_FONT_SIZE) * .5;
|
|
79
88
|
return bodyStart - markerStartPx;
|
|
80
89
|
}
|
|
81
90
|
/** Paint-only offset that places the marker around its authored list anchor. */
|
|
82
91
|
function getListMarkerVisualOffset(block) {
|
|
83
92
|
const attrs = block.attrs;
|
|
84
93
|
if (!attrs?.listMarker || attrs.listMarkerHidden) return 0;
|
|
85
|
-
const
|
|
86
|
-
const naturalWidth = measureTextWidth(attrs.listMarker, {
|
|
87
|
-
fontFamily,
|
|
88
|
-
fontSize,
|
|
89
|
-
...bold !== void 0 ? { bold } : {}
|
|
90
|
-
});
|
|
94
|
+
const naturalWidth = measureTextWidth(attrs.listMarker, resolveListMarkerFont(block));
|
|
91
95
|
if (attrs.listMarkerAlignment === "right") return -naturalWidth;
|
|
92
96
|
if (attrs.listMarkerAlignment === "center") return -naturalWidth / 2;
|
|
93
97
|
return 0;
|
|
@@ -2,7 +2,7 @@ import { recordMeasureBlock, recordMeasureBlockError } from "../layoutInstrument
|
|
|
2
2
|
import { isParagraphFrameTextBox } from "../paragraphFrame.js";
|
|
3
3
|
import { bandFragmentX, bandTopContentY, isPageFrameRelativeAnchor } from "../textBoxFlow.js";
|
|
4
4
|
import { getTextBoxGroupId } from "../textBoxGroup.js";
|
|
5
|
-
import { DEFAULT_TEXTBOX_MARGINS, floatingTextBoxReservesBand, floatingTextBoxWrapsText, tableColumnsArePinned } from "../types.js";
|
|
5
|
+
import { DEFAULT_TEXTBOX_MARGINS, floatingTextBoxReservesBand, floatingTextBoxWrapsText, resolveTableCellPadding, tableColumnsArePinned } from "../types.js";
|
|
6
6
|
import { getCachedParagraphMeasure, setCachedParagraphMeasure } from "./cache.js";
|
|
7
7
|
import { resolveFloatingTableX } from "./floatingTablePosition.js";
|
|
8
8
|
import { findClearLineY, measureParagraph } from "./measureParagraph.js";
|
|
@@ -63,8 +63,6 @@ function resolveTableWidthPx(width, widthType, contentWidth) {
|
|
|
63
63
|
if (widthType === "dxa" || !widthType || widthType === "auto") return Math.round(width / 20 * 1.333);
|
|
64
64
|
}
|
|
65
65
|
function measureTableBlock(tableBlock, contentWidth, fieldValues) {
|
|
66
|
-
const DEFAULT_CELL_PADDING_X = 7;
|
|
67
|
-
const DEFAULT_CELL_PADDING_Y = 0;
|
|
68
66
|
let columnWidths = tableBlock.columnWidths ?? [];
|
|
69
67
|
const explicitWidthPx = resolveTableWidthPx(tableBlock.width, tableBlock.widthType, contentWidth);
|
|
70
68
|
if (columnWidths.length === 0 && tableBlock.rows.length > 0) {
|
|
@@ -89,8 +87,7 @@ function measureTableBlock(tableBlock, contentWidth, fieldValues) {
|
|
|
89
87
|
for (let c = 0; c < colSpan && columnIndex + c < columnWidths.length; c++) cellWidth += columnWidths[columnIndex + c] ?? 0;
|
|
90
88
|
if (cellWidth === 0) cellWidth = cell.width ?? 100;
|
|
91
89
|
columnIndex = getFirstAvailableColumn(cellGrid, rowIdx, columnIndex + colSpan);
|
|
92
|
-
const padLeft = cell
|
|
93
|
-
const padRight = cell.padding?.right ?? DEFAULT_CELL_PADDING_X;
|
|
90
|
+
const { left: padLeft, right: padRight } = resolveTableCellPadding(cell);
|
|
94
91
|
const cellContentWidth = Math.max(1, cellWidth - padLeft - padRight);
|
|
95
92
|
const measureWidth = cell.noWrap === true && !columnsPinned ? NO_WRAP_MEASURE_WIDTH : cellContentWidth;
|
|
96
93
|
const cellMeasure = {
|
|
@@ -132,8 +129,7 @@ function measureTableBlock(tableBlock, contentWidth, fieldValues) {
|
|
|
132
129
|
placeTableCellBlock(flowState, sourceBlock, blockMeasure);
|
|
133
130
|
}
|
|
134
131
|
cell.height = finishTableCellFlow(flowState);
|
|
135
|
-
const padTop = sourceCell
|
|
136
|
-
const padBottom = sourceCell?.padding?.bottom ?? DEFAULT_CELL_PADDING_Y;
|
|
132
|
+
const { top: padTop, bottom: padBottom } = resolveTableCellPadding(sourceCell);
|
|
137
133
|
cell.height += padTop + padBottom;
|
|
138
134
|
if ((sourceCell?.rowSpan ?? 1) > 1) continue;
|
|
139
135
|
const borderHeight = getTableCellVerticalBorderHeight(cellGrid, sourceCell, rowIdx);
|
|
@@ -444,14 +440,34 @@ function measureTextBoxBlock(tb, fieldValues) {
|
|
|
444
440
|
const innerWidth = tb.width - margins.left - margins.right;
|
|
445
441
|
const innerMeasures = tb.content.map((block) => {
|
|
446
442
|
if (block.kind === "table") return measureTableBlock(block, innerWidth, fieldValues);
|
|
447
|
-
return measureParagraph(block, innerWidth, fieldValues ? { fieldValues } : void 0);
|
|
443
|
+
return measureParagraph(block, tb.textWrap === "none" ? NO_WRAP_MEASURE_WIDTH : innerWidth, fieldValues ? { fieldValues } : void 0);
|
|
448
444
|
});
|
|
445
|
+
let naturalContentWidth = 0;
|
|
446
|
+
for (let index = 0; index < innerMeasures.length; index++) {
|
|
447
|
+
const measure = innerMeasures[index];
|
|
448
|
+
if (measure.kind === "table") {
|
|
449
|
+
naturalContentWidth = Math.max(naturalContentWidth, measure.totalWidth);
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
const block = tb.content[index];
|
|
453
|
+
if (!block || block.kind !== "paragraph") continue;
|
|
454
|
+
const indent = block.attrs?.indent;
|
|
455
|
+
const left = indent?.left ?? 0;
|
|
456
|
+
const right = indent?.right ?? 0;
|
|
457
|
+
for (let lineIndex = 0; lineIndex < measure.lines.length; lineIndex++) {
|
|
458
|
+
const line = measure.lines[lineIndex];
|
|
459
|
+
const start = left + (lineIndex === 0 ? (indent?.firstLine ?? 0) - (indent?.hanging ?? 0) : 0);
|
|
460
|
+
const end = start + line.width + right;
|
|
461
|
+
naturalContentWidth = Math.max(naturalContentWidth, Math.max(0, end) - Math.min(0, start));
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
const fittedWidth = naturalContentWidth + margins.left + margins.right;
|
|
465
|
+
const totalWidth = tb.autoFit === "shape" && tb.textWrap === "none" ? Math.max(tb.width, fittedWidth) : tb.width;
|
|
449
466
|
const contentBoxHeight = layoutTextBoxContent(tb.content, innerMeasures).totalHeight + margins.top + margins.bottom;
|
|
450
|
-
const totalHeight = tb.autoFit === "shape" ? Math.max(tb.height ?? 0, contentBoxHeight) : tb.height ?? contentBoxHeight;
|
|
451
467
|
return {
|
|
452
468
|
kind: "textBox",
|
|
453
|
-
width:
|
|
454
|
-
height:
|
|
469
|
+
width: totalWidth,
|
|
470
|
+
height: tb.autoFit === "shape" ? Math.max(tb.height ?? 0, contentBoxHeight) : tb.height ?? contentBoxHeight,
|
|
455
471
|
innerMeasures
|
|
456
472
|
};
|
|
457
473
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { calculateTabWidth, pixelsToTwips } from "../../prosemirror/utils/tabCalculator.js";
|
|
2
2
|
import { CJK_FALLBACK_FONT_FAMILY, isCjkFont } from "../../utils/fontResolver.js";
|
|
3
|
+
import { isRtlParagraph } from "../../utils/paragraphBaseDirection.js";
|
|
3
4
|
import { inlineImageBoundingBox } from "../../utils/rotationBoundingBox.js";
|
|
4
5
|
import { hasCjk, hasComplexScript } from "../../utils/scriptSegments.js";
|
|
5
6
|
import { measuredLineAdvance } from "../lineFlow.js";
|
|
@@ -668,6 +669,7 @@ function findClearLineY(startY, lineHeight, zones, contentWidth, minWidth) {
|
|
|
668
669
|
function measureParagraph(block, maxWidth, options) {
|
|
669
670
|
const runs = block.runs;
|
|
670
671
|
const attrs = block.attrs;
|
|
672
|
+
const isRtl = isRtlParagraph(block);
|
|
671
673
|
const spacing = attrs?.spacing;
|
|
672
674
|
const isJustifiedParagraph = attrs?.alignment === "justify";
|
|
673
675
|
const justificationProfile = {
|
|
@@ -936,9 +938,13 @@ function measureParagraph(block, maxWidth, options) {
|
|
|
936
938
|
decimalPrefixWidth
|
|
937
939
|
});
|
|
938
940
|
let tabWidth = tabResult.width;
|
|
941
|
+
const authoredEndpoint = contentX + tabWidth + followingWidth;
|
|
942
|
+
const activeContentRightEdge = maxWidth - currentLine.rightOffset;
|
|
943
|
+
const preservesLogicalRtlEndStop = isRtl && tabResult.alignment === "end" && authoredEndpoint <= activeContentRightEdge + WIDTH_TOLERANCE;
|
|
939
944
|
const landsOnLeftIndent = tabResult.alignment === "start" && indentLeft > 0 && Math.abs(contentX + tabWidth - indentLeft) <= WIDTH_TOLERANCE;
|
|
940
945
|
const lineRightEdgeX = indentLeft + (isFirstLine ? firstLineOffset + markerInlineWidth : 0) + currentLine.availableWidth + currentLine.leftOffset;
|
|
941
|
-
if (!landsOnLeftIndent && !hasFollowingTabOnLine(runs, runIndex) && canClampTabToRightEdge(tabResult.alignment, currentLine.width, hasPriorTabOnLine(runs, runIndex), followingWidth, currentLine.availableWidth) && (tabWidth > 0 || followingWidth > 0) && contentX + tabWidth + followingWidth > lineRightEdgeX + WIDTH_TOLERANCE) tabWidth = Math.max(1, lineRightEdgeX - contentX - followingWidth);
|
|
946
|
+
if (!preservesLogicalRtlEndStop && !landsOnLeftIndent && !hasFollowingTabOnLine(runs, runIndex) && canClampTabToRightEdge(tabResult.alignment, currentLine.width, hasPriorTabOnLine(runs, runIndex), followingWidth, currentLine.availableWidth) && (tabWidth > 0 || followingWidth > 0) && contentX + tabWidth + followingWidth > lineRightEdgeX + WIDTH_TOLERANCE) tabWidth = Math.max(1, lineRightEdgeX - contentX - followingWidth);
|
|
947
|
+
if (preservesLogicalRtlEndStop) currentLine.availableWidth = Math.max(currentLine.availableWidth, currentLine.width + tabWidth + followingWidth);
|
|
942
948
|
if (currentLine.width + tabWidth > currentLine.availableWidth + WIDTH_TOLERANCE) {
|
|
943
949
|
startNewLine(runIndex, 0);
|
|
944
950
|
updateMaxFont(style);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { emuToPixels } from "../../utils/units.js";
|
|
2
|
-
import { isFloatingImageRun } from "../types.js";
|
|
2
|
+
import { isFloatingImageRun, resolveTableCellPadding } from "../types.js";
|
|
3
3
|
import { clampFloatingWrapMargins } from "./clampFloatingWrapMargins.js";
|
|
4
4
|
import { createTableCellFlowState, placeTableCellBlock } from "./tableCellFlow.js";
|
|
5
5
|
//#region src/layout-engine/measure/tableCellFloating.ts
|
|
@@ -34,8 +34,7 @@ const resolveCellScopedPosition = ({ run, paragraphY, contentWidth }) => {
|
|
|
34
34
|
};
|
|
35
35
|
};
|
|
36
36
|
function getTableCellContentWidth(cell, cellMeasure) {
|
|
37
|
-
const padLeft = cell
|
|
38
|
-
const padRight = cell?.padding?.right ?? 7;
|
|
37
|
+
const { left: padLeft, right: padRight } = resolveTableCellPadding(cell);
|
|
39
38
|
return Math.max(0, cellMeasure.width - padLeft - padRight);
|
|
40
39
|
}
|
|
41
40
|
function getTableCellFloatingImages(cell, cellMeasure, contentWidth, resolvePosition) {
|
|
@@ -11,7 +11,7 @@ const INITIAL_RENDERED_BREAK_STATE = { type: "noPageAdvance" };
|
|
|
11
11
|
*/
|
|
12
12
|
const reconcileBreakBeforeBlock = ({ state, block, previousBlock, page, blocksById, hasExplicitPageBreak, renderedBreakNeedsSnap }) => {
|
|
13
13
|
if (hasExplicitPageBreak) return {
|
|
14
|
-
forcePageBreak:
|
|
14
|
+
forcePageBreak: !(state.type === "pageAdvance" && state.reason === "authoredBoundary" && state.boundary === "sectionBreak" && previousBlock?.kind === "sectionBreak" && previousBlock.type !== "continuous"),
|
|
15
15
|
suppressSpaceBefore: false,
|
|
16
16
|
state: INITIAL_RENDERED_BREAK_STATE
|
|
17
17
|
};
|
|
@@ -2,6 +2,7 @@ import { measureParagraph } from "./measure/measureParagraph.js";
|
|
|
2
2
|
import { buildTableCellFloatingZones, getTableCellContentWidth, getTableCellFloatingImages } from "./measure/tableCellFloating.js";
|
|
3
3
|
import { createTableCellFlowState, placeTableCellBlock } from "./measure/tableCellFlow.js";
|
|
4
4
|
import { isEmptyParagraph } from "./paragraphSpacing.js";
|
|
5
|
+
import { resolveTableCellPadding } from "./types.js";
|
|
5
6
|
//#region src/layout-engine/tableRowBreak.ts
|
|
6
7
|
/**
|
|
7
8
|
* Table row-break geometry. Ported from eigenpal/docx-editor#698 (folio subset).
|
|
@@ -18,7 +19,6 @@ import { isEmptyParagraph } from "./paragraphSpacing.js";
|
|
|
18
19
|
* here yet (a separate follow-up); each row uses its own cells' content.
|
|
19
20
|
*/
|
|
20
21
|
const BREAK_OFFSET_EPSILON = .01;
|
|
21
|
-
const DEFAULT_TABLE_CELL_PADDING_TOP = 1;
|
|
22
22
|
function isInsideRange(offset, range) {
|
|
23
23
|
return offset > range.top && offset < range.bottom;
|
|
24
24
|
}
|
|
@@ -49,7 +49,7 @@ function cellBreakGeometry(cell, measure) {
|
|
|
49
49
|
const suppressibleLeadingRanges = [];
|
|
50
50
|
const cellBlocks = cell?.blocks;
|
|
51
51
|
const blockMeasures = measure.blocks;
|
|
52
|
-
const padTop = cell
|
|
52
|
+
const { top: padTop } = resolveTableCellPadding(cell);
|
|
53
53
|
const contentWidth = getTableCellContentWidth(cell, measure);
|
|
54
54
|
const floatingZones = buildTableCellFloatingZones(cell !== void 0 ? getTableCellFloatingImages(cell, measure, contentWidth) : [], contentWidth);
|
|
55
55
|
const flowState = createTableCellFlowState();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { OutlineStyleAttr } from "../types/documentEnumValues.js";
|
|
2
|
-
import { ImagePosition, ImageWrap, SdtProperties, SdtType, ShapeTextBody, TableCellFormatting, TableWidthType } from "@stll/docx-core/model";
|
|
2
|
+
import { ImagePosition, ImageWrap, NumberFormat, SdtProperties, SdtType, ShapeTextBody, TableCellFormatting, TableWidthType } from "@stll/docx-core/model";
|
|
3
3
|
declare namespace types_d_exports {
|
|
4
|
-
export { BlockId, BorderStyle, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, Layout, LayoutOptions, LineBreakRun, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, RenderedPageBreakRun, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, SectionPageNumbering, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned };
|
|
4
|
+
export { BlockId, BorderStyle, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, Layout, LayoutOptions, LineBreakRun, ListMarkerFormatting, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, RenderedPageBreakRun, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, SectionPageNumbering, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, resolveTableCellPadding, tableColumnsArePinned };
|
|
5
5
|
}
|
|
6
6
|
/**
|
|
7
7
|
* Unique identifier for a block in the document.
|
|
@@ -127,6 +127,7 @@ type RunFormatting = {
|
|
|
127
127
|
/** Groups a suggestion's runs for accept/reject and scroll-to. */
|
|
128
128
|
suggestionId?: string;
|
|
129
129
|
};
|
|
130
|
+
type ListMarkerFormatting = Pick<RunFormatting, "fontFamily" | "eastAsiaFontFamily" | "complexScriptFontFamily" | "fontSize" | "complexScriptFontSize" | "bold" | "complexScriptBold" | "italic" | "complexScriptItalic" | "rtl" | "forceComplexScript">;
|
|
130
131
|
/**
|
|
131
132
|
* Hyperlink information for a run.
|
|
132
133
|
*/
|
|
@@ -461,15 +462,14 @@ type ParagraphAttrs = {
|
|
|
461
462
|
listMarker?: string;
|
|
462
463
|
listIsBullet?: boolean;
|
|
463
464
|
listMarkerHidden?: boolean;
|
|
464
|
-
|
|
465
|
-
|
|
465
|
+
/** Canonical numbering-level marker typography, resolved for layout. */
|
|
466
|
+
listMarkerFormatting?: ListMarkerFormatting;
|
|
466
467
|
/**
|
|
467
468
|
* Effective paragraph-mark size used only to measure a visible list
|
|
468
469
|
* paragraph's final line. Word lets the inherited complex-script size raise
|
|
469
470
|
* that line box without enlarging the marker or Western text glyphs.
|
|
470
471
|
*/
|
|
471
472
|
listParagraphMarkFontSize?: number;
|
|
472
|
-
listMarkerBold?: boolean;
|
|
473
473
|
/** Horizontal alignment of the marker around the paragraph's list anchor. */
|
|
474
474
|
listMarkerAlignment?: "left" | "center" | "right";
|
|
475
475
|
/**
|
|
@@ -578,6 +578,9 @@ type TableCell = {
|
|
|
578
578
|
*/
|
|
579
579
|
noWrap?: boolean;
|
|
580
580
|
};
|
|
581
|
+
type TableCellPadding = NonNullable<TableCell["padding"]>;
|
|
582
|
+
/** Resolve authored cell margins against the TableNormal defaults. */
|
|
583
|
+
declare const resolveTableCellPadding: (cell: Pick<TableCell, "padding"> | undefined) => TableCellPadding;
|
|
581
584
|
/**
|
|
582
585
|
* A table row containing cells.
|
|
583
586
|
*/
|
|
@@ -705,11 +708,11 @@ type SectionBreakBlock = {
|
|
|
705
708
|
/** Normalized section page-number policy. An omitted OOXML start continues numbering. */
|
|
706
709
|
type SectionPageNumbering = {
|
|
707
710
|
type: "continue";
|
|
708
|
-
format?:
|
|
711
|
+
format?: NumberFormat;
|
|
709
712
|
} | {
|
|
710
713
|
type: "restart";
|
|
711
714
|
start: number;
|
|
712
|
-
format?:
|
|
715
|
+
format?: NumberFormat;
|
|
713
716
|
};
|
|
714
717
|
type PageHeaderFooterRefs = {
|
|
715
718
|
titlePg?: boolean;
|
|
@@ -760,6 +763,8 @@ type TextBoxBlock = {
|
|
|
760
763
|
height?: number;
|
|
761
764
|
/** Text fitting behavior */
|
|
762
765
|
autoFit?: ShapeTextBody["autoFit"];
|
|
766
|
+
/** Horizontal text wrapping inside the box */
|
|
767
|
+
textWrap?: ShapeTextBody["textWrap"];
|
|
763
768
|
/** Fill/background color */
|
|
764
769
|
fillColor?: string;
|
|
765
770
|
/** Border width in pixels */
|
|
@@ -1081,7 +1086,7 @@ type Page = {
|
|
|
1081
1086
|
/** Authored page number shown by PAGE fields. */
|
|
1082
1087
|
logicalNumber: number;
|
|
1083
1088
|
/** OOXML number format for this page's section. */
|
|
1084
|
-
logicalNumberFormat?:
|
|
1089
|
+
logicalNumberFormat?: NumberFormat;
|
|
1085
1090
|
/** Fragments positioned on this page. */
|
|
1086
1091
|
fragments: Fragment[];
|
|
1087
1092
|
/** Page margins. */
|
|
@@ -1235,8 +1240,6 @@ type LayoutOptions = {
|
|
|
1235
1240
|
* page.
|
|
1236
1241
|
*/
|
|
1237
1242
|
footnoteHeightById?: Map<number, number>;
|
|
1238
|
-
/** Section break type for the body-level (final) section (for section transition logic). */
|
|
1239
|
-
bodyBreakType?: "continuous" | "nextPage" | "evenPage" | "oddPage";
|
|
1240
1243
|
/** Header/footer references for each document section, by section index. */
|
|
1241
1244
|
sectionHeaderFooterRefs?: PageHeaderFooterRefs[];
|
|
1242
1245
|
};
|
|
@@ -1308,6 +1311,8 @@ type HeaderFooterContent = {
|
|
|
1308
1311
|
*/
|
|
1309
1312
|
marginPushTop?: number;
|
|
1310
1313
|
marginPushBottom?: number;
|
|
1314
|
+
/** Page-coordinate lower edge of a header wrap band overlapping the body top. */
|
|
1315
|
+
bodyTopClearance?: number;
|
|
1311
1316
|
/**
|
|
1312
1317
|
* Relationship id (`rId`) of the source HF part. Emitted as `data-rid`
|
|
1313
1318
|
* on the painted `.layout-page-header` / `.layout-page-footer` so the
|
|
@@ -1380,4 +1385,4 @@ declare function tableColumnsArePinned(table: TableBlock): boolean;
|
|
|
1380
1385
|
/** Return the leading visual offset for a row with omitted grid columns. */
|
|
1381
1386
|
declare const getTableRowLeadingWidth: (row: TableRow, columnWidths: readonly number[]) => number;
|
|
1382
1387
|
//#endregion
|
|
1383
|
-
export { BlockId, BorderStyle, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, Layout, LayoutOptions, LineBreakRun, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, RenderedPageBreakRun, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, SectionPageNumbering, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned, types_d_exports };
|
|
1388
|
+
export { BlockId, BorderStyle, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, Layout, LayoutOptions, LineBreakRun, ListMarkerFormatting, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, RenderedPageBreakRun, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, SectionPageNumbering, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, resolveTableCellPadding, tableColumnsArePinned, types_d_exports };
|
|
@@ -13,8 +13,22 @@ var types_exports = /* @__PURE__ */ __exportAll({
|
|
|
13
13
|
isFloatingImageRun: () => isFloatingImageRun,
|
|
14
14
|
isFloatingTextBoxBlock: () => isFloatingTextBoxBlock,
|
|
15
15
|
isTextWrappingFloatingImageRun: () => isTextWrappingFloatingImageRun,
|
|
16
|
+
resolveTableCellPadding: () => resolveTableCellPadding,
|
|
16
17
|
tableColumnsArePinned: () => tableColumnsArePinned
|
|
17
18
|
});
|
|
19
|
+
const DEFAULT_TABLE_CELL_PADDING = {
|
|
20
|
+
top: 0,
|
|
21
|
+
right: 7,
|
|
22
|
+
bottom: 0,
|
|
23
|
+
left: 7
|
|
24
|
+
};
|
|
25
|
+
/** Resolve authored cell margins against the TableNormal defaults. */
|
|
26
|
+
const resolveTableCellPadding = (cell) => ({
|
|
27
|
+
top: cell?.padding?.top ?? DEFAULT_TABLE_CELL_PADDING.top,
|
|
28
|
+
right: cell?.padding?.right ?? DEFAULT_TABLE_CELL_PADDING.right,
|
|
29
|
+
bottom: cell?.padding?.bottom ?? DEFAULT_TABLE_CELL_PADDING.bottom,
|
|
30
|
+
left: cell?.padding?.left ?? DEFAULT_TABLE_CELL_PADDING.left
|
|
31
|
+
});
|
|
18
32
|
/** Default internal margins for text boxes (OOXML defaults in pixels) */
|
|
19
33
|
const DEFAULT_TEXTBOX_MARGINS = {
|
|
20
34
|
top: 4,
|
|
@@ -128,4 +142,4 @@ const getTableRowLeadingWidth = (row, columnWidths) => {
|
|
|
128
142
|
return columnWidths.slice(0, row.gridBefore ?? 0).reduce((sum, columnWidth) => sum + columnWidth, 0);
|
|
129
143
|
};
|
|
130
144
|
//#endregion
|
|
131
|
-
export { DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned, types_exports };
|
|
145
|
+
export { DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, resolveTableCellPadding, tableColumnsArePinned, types_exports };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MeasuredLine, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphMeasure, Run, TabStop } from "../layout-engine/types.js";
|
|
1
|
+
import { HyperlinkInfo, MeasuredLine, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphMeasure, Run, TabStop } from "../layout-engine/types.js";
|
|
2
2
|
import { RenderContext } from "./renderUtils.js";
|
|
3
3
|
//#region src/layout-painter/renderParagraph.d.ts
|
|
4
4
|
/**
|
|
@@ -64,8 +64,14 @@ type RenderLineOptions = {
|
|
|
64
64
|
context?: RenderContext;
|
|
65
65
|
/** Left indent in pixels */
|
|
66
66
|
leftIndentPx?: number;
|
|
67
|
+
/** Authored OOXML left indent used by logical tab-stop calculations. */
|
|
68
|
+
tabLeftIndentPx?: number;
|
|
67
69
|
/** First line indent in pixels (positive) or hanging indent (negative) */
|
|
68
70
|
firstLineIndentPx?: number;
|
|
71
|
+
/** Paragraph base direction for logical first-line indentation. */
|
|
72
|
+
isRtl?: boolean;
|
|
73
|
+
/** Full paragraph content-box width before physical indents. */
|
|
74
|
+
contentWidthPx?: number;
|
|
69
75
|
/** Line-specific floating image margins (calculated per-line based on Y overlap) */
|
|
70
76
|
floatingMargins?: {
|
|
71
77
|
leftMargin: number;
|
|
@@ -73,6 +79,8 @@ type RenderLineOptions = {
|
|
|
73
79
|
};
|
|
74
80
|
/** Track inline image runs already rendered in this paragraph fragment to prevent duplicates */
|
|
75
81
|
renderedInlineImageKeys?: Set<string>;
|
|
82
|
+
/** Hyperlink fragments whose complete displayed text is a URL. */
|
|
83
|
+
leftToRightDisplayedUrlHyperlinks?: ReadonlySet<HyperlinkInfo>;
|
|
76
84
|
/**
|
|
77
85
|
* Rightmost x where inline content may render, in content-area coords. Used
|
|
78
86
|
* by the right-tab anchor (TOC pattern); passed in directly rather than
|