@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
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { hasPageBreakBefore } from "../keep-together.js";
|
|
2
2
|
import { recordMeasureBlock, recordMeasureBlockError } from "../layoutInstrumentation.js";
|
|
3
3
|
import { isParagraphFrameTextBox } from "../paragraphFrame.js";
|
|
4
|
+
import { resolveTableSpacing } from "../paragraphSpacing.js";
|
|
4
5
|
import { sectionStartAdvanceOf } from "../section-breaks.js";
|
|
5
6
|
import { bandFragmentX, bandTopContentY, isPageFrameRelativeAnchor } from "../textBoxFlow.js";
|
|
6
7
|
import { getTextBoxGroupId } from "../textBoxGroup.js";
|
|
7
8
|
import { DEFAULT_TEXTBOX_MARGINS, floatingTextBoxReservesBand, floatingTextBoxWrapsText, resolveTableCellPadding, resolveTableWidthPx, tableColumnsArePinned } from "../types.js";
|
|
8
9
|
import { getCachedParagraphMeasure, setCachedParagraphMeasure } from "./cache.js";
|
|
9
10
|
import { resolveFloatingTablePageX } from "./floatingTablePosition.js";
|
|
11
|
+
import { getHyphenationDictionaryMisses } from "./hyphenationDictionaries.js";
|
|
10
12
|
import { findClearLineY, measureParagraph } from "./measureParagraph.js";
|
|
13
|
+
import { buildTableCellFloatingZones, getTableCellFloatingImages } from "./tableCellFloating.js";
|
|
11
14
|
import { createTableCellFlowState, finishTableCellFlow, placeTableCellBlock } from "./tableCellFlow.js";
|
|
12
|
-
import { buildTableCellGrid, getFirstAvailableColumn, getTableCellVerticalBorderHeight } from "./tableCellGrid.js";
|
|
15
|
+
import { buildTableCellGrid, getFirstAvailableColumn, getTableCellSpacingInsetsX, getTableCellSpacingInsetsY, getTableCellVerticalBorderHeight } from "./tableCellGrid.js";
|
|
13
16
|
import { layoutTextBoxContent } from "./textBoxParagraphLayout.js";
|
|
14
17
|
//#region src/layout-engine/measure/measureBlocks.ts
|
|
15
18
|
/**
|
|
@@ -59,6 +62,27 @@ function emuToPixels(emu) {
|
|
|
59
62
|
if (emu === void 0) return 0;
|
|
60
63
|
return Math.round(emu * 96 / 914400);
|
|
61
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Re-measure a cell's paragraphs against the wrap exclusions of the pictures
|
|
67
|
+
* anchored in it, so lines a picture displaces (beside it, or below it when it
|
|
68
|
+
* leaves no room) count toward the cell height. Mirrors the painter's
|
|
69
|
+
* per-paragraph re-measure so both agree on where each line sits.
|
|
70
|
+
*/
|
|
71
|
+
function wrapTableCellParagraphsAroundFloats(cell, cellMeasure, contentWidth, fieldValues) {
|
|
72
|
+
const zones = buildTableCellFloatingZones(getTableCellFloatingImages(cell, cellMeasure, contentWidth), contentWidth);
|
|
73
|
+
if (zones.length === 0) return;
|
|
74
|
+
const flowState = createTableCellFlowState();
|
|
75
|
+
for (let blockIdx = 0; blockIdx < cell.blocks.length; blockIdx++) {
|
|
76
|
+
const block = cell.blocks[blockIdx];
|
|
77
|
+
let blockMeasure = cellMeasure.blocks[blockIdx];
|
|
78
|
+
if (!block || !blockMeasure) continue;
|
|
79
|
+
if (block.kind === "paragraph") {
|
|
80
|
+
blockMeasure = measureBlock(block, contentWidth, zones, placeTableCellBlock({ ...flowState }, block, blockMeasure).contentTop, fieldValues);
|
|
81
|
+
cellMeasure.blocks[blockIdx] = blockMeasure;
|
|
82
|
+
}
|
|
83
|
+
placeTableCellBlock(flowState, block, blockMeasure);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
62
86
|
function measureTableBlock(tableBlock, contentWidth, fieldValues) {
|
|
63
87
|
let columnWidths = tableBlock.columnWidths ?? [];
|
|
64
88
|
const explicitWidthPx = resolveTableWidthPx(tableBlock.width, tableBlock.widthType, contentWidth);
|
|
@@ -74,24 +98,32 @@ function measureTableBlock(tableBlock, contentWidth, fieldValues) {
|
|
|
74
98
|
}
|
|
75
99
|
}
|
|
76
100
|
const cellGrid = buildTableCellGrid(tableBlock.rows, columnWidths.length);
|
|
101
|
+
const spacingRows = resolveTableSpacing(tableBlock).rows;
|
|
102
|
+
const cellSpacing = tableBlock.cellSpacing;
|
|
103
|
+
const bordersSeparated = (cellSpacing ?? 0) > 0;
|
|
104
|
+
const rowCount = tableBlock.rows.length;
|
|
77
105
|
const columnsPinned = tableColumnsArePinned(tableBlock);
|
|
78
106
|
const rows = tableBlock.rows.map((row, rowIdx) => {
|
|
79
107
|
let columnIndex = getFirstAvailableColumn(cellGrid, rowIdx, row.gridBefore ?? 0);
|
|
80
108
|
return {
|
|
81
|
-
cells: row.cells.map((cell) => {
|
|
109
|
+
cells: row.cells.map((cell, cellIdx) => {
|
|
82
110
|
const colSpan = cell.colSpan ?? 1;
|
|
83
111
|
let cellWidth = 0;
|
|
84
112
|
for (let c = 0; c < colSpan && columnIndex + c < columnWidths.length; c++) cellWidth += columnWidths[columnIndex + c] ?? 0;
|
|
85
113
|
if (cellWidth === 0) cellWidth = cell.width ?? 100;
|
|
114
|
+
const spacingInsets = getTableCellSpacingInsetsX(cellSpacing, columnIndex, colSpan, columnWidths.length);
|
|
115
|
+
cellWidth = Math.max(0, cellWidth - spacingInsets.start - spacingInsets.end);
|
|
86
116
|
columnIndex = getFirstAvailableColumn(cellGrid, rowIdx, columnIndex + colSpan);
|
|
87
117
|
const { left: padLeft, right: padRight } = resolveTableCellPadding(cell);
|
|
88
118
|
const cellContentWidth = Math.max(1, cellWidth - padLeft - padRight);
|
|
89
|
-
const
|
|
119
|
+
const keepSingleLine = cell.noWrap === true && !columnsPinned;
|
|
120
|
+
const measureWidth = keepSingleLine ? NO_WRAP_MEASURE_WIDTH : cellContentWidth;
|
|
90
121
|
const cellMeasure = {
|
|
91
122
|
blocks: cell.blocks.map((b) => measureBlock(b, measureWidth, void 0, void 0, fieldValues)),
|
|
92
123
|
width: cellWidth,
|
|
93
124
|
height: 0
|
|
94
125
|
};
|
|
126
|
+
if (!keepSingleLine) wrapTableCellParagraphsAroundFloats(spacingRows[rowIdx]?.cells[cellIdx] ?? cell, cellMeasure, cellContentWidth, fieldValues);
|
|
95
127
|
if (cell.colSpan !== void 0) cellMeasure.colSpan = cell.colSpan;
|
|
96
128
|
if (cell.rowSpan !== void 0) cellMeasure.rowSpan = cell.rowSpan;
|
|
97
129
|
return cellMeasure;
|
|
@@ -111,7 +143,7 @@ function measureTableBlock(tableBlock, contentWidth, fieldValues) {
|
|
|
111
143
|
}));
|
|
112
144
|
continue;
|
|
113
145
|
}
|
|
114
|
-
const sourceRowCells =
|
|
146
|
+
const sourceRowCells = spacingRows[rowIdx]?.cells;
|
|
115
147
|
let maxCellHeightWithBorders = 0;
|
|
116
148
|
let maxCellInsets = 0;
|
|
117
149
|
for (let cellIdx = 0; cellIdx < row.cells.length; cellIdx++) {
|
|
@@ -129,15 +161,17 @@ function measureTableBlock(tableBlock, contentWidth, fieldValues) {
|
|
|
129
161
|
const { top: padTop, bottom: padBottom } = resolveTableCellPadding(sourceCell);
|
|
130
162
|
cell.height += padTop + padBottom;
|
|
131
163
|
if ((sourceCell?.rowSpan ?? 1) > 1) continue;
|
|
132
|
-
const borderHeight = getTableCellVerticalBorderHeight(cellGrid, sourceCell, rowIdx);
|
|
164
|
+
const borderHeight = getTableCellVerticalBorderHeight(cellGrid, sourceCell, rowIdx, bordersSeparated);
|
|
133
165
|
maxCellHeightWithBorders = Math.max(maxCellHeightWithBorders, cell.height + borderHeight);
|
|
134
166
|
maxCellInsets = Math.max(maxCellInsets, padTop + padBottom + borderHeight);
|
|
135
167
|
}
|
|
136
168
|
const explicitHeight = sourceRow?.height;
|
|
137
169
|
const heightRule = sourceRow?.heightRule;
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
170
|
+
const spacing = getTableCellSpacingInsetsY(cellSpacing, rowIdx, 1, rowCount);
|
|
171
|
+
const spacingHeight = spacing.top + spacing.bottom;
|
|
172
|
+
if (explicitHeight && heightRule === "exact") row.height = (bordersSeparated ? Math.max(0, explicitHeight - (cellSpacing ?? 0)) : explicitHeight) + spacingHeight;
|
|
173
|
+
else if (explicitHeight) row.height = Math.max(maxCellHeightWithBorders, explicitHeight + maxCellInsets) + spacingHeight;
|
|
174
|
+
else row.height = maxCellHeightWithBorders + spacingHeight;
|
|
141
175
|
}
|
|
142
176
|
for (let rowIdx = 0; rowIdx < rows.length; rowIdx++) {
|
|
143
177
|
const sourceRow = tableBlock.rows[rowIdx];
|
|
@@ -151,7 +185,8 @@ function measureTableBlock(tableBlock, contentWidth, fieldValues) {
|
|
|
151
185
|
const spanEnd = Math.min(rows.length, rowIdx + rowSpan);
|
|
152
186
|
let combinedHeight = 0;
|
|
153
187
|
for (let spannedRowIdx = rowIdx; spannedRowIdx < spanEnd; spannedRowIdx++) combinedHeight += rows[spannedRowIdx]?.height ?? 0;
|
|
154
|
-
const
|
|
188
|
+
const spanSpacing = getTableCellSpacingInsetsY(cellSpacing, rowIdx, rowSpan, rowCount);
|
|
189
|
+
const deficit = measuredCell.height + getTableCellVerticalBorderHeight(cellGrid, sourceCell, rowIdx, bordersSeparated) + spanSpacing.top + spanSpacing.bottom - combinedHeight;
|
|
155
190
|
if (deficit <= 0) continue;
|
|
156
191
|
for (let spannedRowIdx = spanEnd - 1; spannedRowIdx >= rowIdx; spannedRowIdx--) {
|
|
157
192
|
if (tableBlock.rows[spannedRowIdx]?.heightRule === "exact") continue;
|
|
@@ -529,8 +564,9 @@ function measureBlock(block, contentWidth, floatingZones, cumulativeY, fieldValu
|
|
|
529
564
|
if (floatingZones) measureOpts.floatingZones = floatingZones;
|
|
530
565
|
if (fieldValues) measureOpts.fieldValues = fieldValues;
|
|
531
566
|
if (options?.allowEndTabOverflow === true) measureOpts.allowEndTabOverflow = true;
|
|
567
|
+
const missesBefore = getHyphenationDictionaryMisses();
|
|
532
568
|
const result = measureParagraph(pBlock, contentWidth, measureOpts);
|
|
533
|
-
if (cacheable) setCachedParagraphMeasure(pBlock, contentWidth, result);
|
|
569
|
+
if (cacheable && getHyphenationDictionaryMisses() === missesBefore) setCachedParagraphMeasure(pBlock, contentWidth, result);
|
|
534
570
|
return result;
|
|
535
571
|
}
|
|
536
572
|
case "table": return measureTableBlock(block, contentWidth, fieldValues);
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { getHorizontalScaleFactor } from "../../utils/horizontalScale.js";
|
|
2
|
-
import { SCRIPT_CLASS,
|
|
2
|
+
import { SCRIPT_CLASS, hasComplexScript, hasEastAsiaSlotText, scriptClassOf, segmentByScript } from "../../utils/scriptSegments.js";
|
|
3
3
|
import { getCachedFontMetrics, getCachedTextWidth, getTextWidthCacheGeneration, setCachedFontMetrics, setCachedTextWidth } from "./cache.js";
|
|
4
4
|
import { applyComplexScriptFormatting, hasComplexScriptFormatting } from "./complexScriptFormatting.js";
|
|
5
5
|
import { buildFontString, getResolvedData, ptToPx } from "./measureHelpers.js";
|
|
6
6
|
import { setMeasureProvider } from "./measureProvider.js";
|
|
7
7
|
import { canPrefetchMeasurement, prefetchMeasurement } from "./measureWorker.js";
|
|
8
8
|
import { WORKER_FONT_FINGERPRINT_TEXT, countCodePoints } from "./measureWorkerProtocol.js";
|
|
9
|
+
import { SMALL_CAPS_SCALE, smallCapsMask, smallCapsSegments } from "./smallCapsCasing.js";
|
|
9
10
|
import { getFontKerningMode } from "./textMeasurementPolicy.js";
|
|
10
11
|
import { panic } from "better-result";
|
|
11
12
|
//#region src/layout-engine/measure/measureContainer.ts
|
|
@@ -70,7 +71,7 @@ function getWorkerFontFingerprintWidth(ctx, font, fontCacheKey, fontKerning) {
|
|
|
70
71
|
*/
|
|
71
72
|
function canvasGetFontMetrics(style) {
|
|
72
73
|
const fontSize = style.fontSize ?? 11;
|
|
73
|
-
const fontFamily = style.fontFamily ?? "
|
|
74
|
+
const fontFamily = style.fontFamily ?? "Times New Roman";
|
|
74
75
|
const bold = style.bold ?? false;
|
|
75
76
|
const italic = style.italic ?? false;
|
|
76
77
|
const fontVariant = style.fontVariant;
|
|
@@ -136,6 +137,7 @@ function canvasMeasureTextWidth(text, sourceStyle) {
|
|
|
136
137
|
if (!text) return 0;
|
|
137
138
|
const style = sourceStyle.forceComplexScript ? applyComplexScriptFormatting(sourceStyle, sourceStyle) : sourceStyle;
|
|
138
139
|
const measuredText = applyTextTransform(text, style);
|
|
140
|
+
if (!sourceStyle.forceComplexScript && style.fontVariant === "small-caps") return measureSmallCapsWidth(measuredText, style);
|
|
139
141
|
if (!sourceStyle.forceComplexScript && !style.letterSpacing && needsPerScriptFonts(style, measuredText)) return measureMixedScriptWidth(measuredText, style);
|
|
140
142
|
const ctx = getCanvasContext();
|
|
141
143
|
const font = buildFontString(style);
|
|
@@ -197,7 +199,7 @@ function glyphAdvanceStyle(style) {
|
|
|
197
199
|
* AND text that would select it. Keeps the all-Latin path on a single font.
|
|
198
200
|
*/
|
|
199
201
|
function needsPerScriptFonts(style, measuredText) {
|
|
200
|
-
if (style.eastAsiaFontFamily &&
|
|
202
|
+
if (style.eastAsiaFontFamily && hasEastAsiaSlotText(measuredText, style.eastAsiaHint)) return true;
|
|
201
203
|
return hasComplexScriptFormatting(style) && hasComplexScript(measuredText);
|
|
202
204
|
}
|
|
203
205
|
/** Resolve the independent font/style slot selected by a script segment. */
|
|
@@ -219,7 +221,39 @@ function measureMixedScriptWidth(measuredText, style) {
|
|
|
219
221
|
const letterSpacing = style.letterSpacing ?? 0;
|
|
220
222
|
const horizontalScale = getHorizontalScaleFactor(style.horizontalScale);
|
|
221
223
|
let glyphWidth = 0;
|
|
222
|
-
for (const segment of segmentByScript(measuredText)) glyphWidth += canvasMeasureTextWidth(segment.text, glyphAdvanceStyle(scriptStyle(style, segment.script)));
|
|
224
|
+
for (const segment of segmentByScript(measuredText, style.eastAsiaHint)) glyphWidth += canvasMeasureTextWidth(segment.text, glyphAdvanceStyle(scriptStyle(style, segment.script)));
|
|
225
|
+
let width = glyphWidth;
|
|
226
|
+
if (letterSpacing) {
|
|
227
|
+
const codePoints = countCodePoints(measuredText);
|
|
228
|
+
if (codePoints > 1) width += letterSpacing * (codePoints - 1);
|
|
229
|
+
}
|
|
230
|
+
return width * horizontalScale;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Width of a `w:smallCaps` run: its lowercase letters (and the whitespace
|
|
234
|
+
* that follows them — see smallCapsCasing.ts) draw at `SMALL_CAPS_SCALE` of
|
|
235
|
+
* the run's size, everything else at full size. A small segment is measured
|
|
236
|
+
* at that literal, smaller font string — not the full-size advance scaled by
|
|
237
|
+
* arithmetic — so hinting and sub-pixel snapping agree with the painted DOM
|
|
238
|
+
* span, which sets this same font-size. Each segment still recurses into
|
|
239
|
+
* {@link canvasMeasureTextWidth} (so one that itself needs the East-Asian or
|
|
240
|
+
* complex-script font still gets it); letter spacing and horizontal scale
|
|
241
|
+
* apply once over the whole string, as {@link measureMixedScriptWidth} does
|
|
242
|
+
* for a script split.
|
|
243
|
+
*/
|
|
244
|
+
function measureSmallCapsWidth(measuredText, style) {
|
|
245
|
+
const letterSpacing = style.letterSpacing ?? 0;
|
|
246
|
+
const horizontalScale = getHorizontalScaleFactor(style.horizontalScale);
|
|
247
|
+
const segmentStyle = { ...style };
|
|
248
|
+
delete segmentStyle.fontVariant;
|
|
249
|
+
delete segmentStyle.letterSpacing;
|
|
250
|
+
delete segmentStyle.horizontalScale;
|
|
251
|
+
const smallSegmentStyle = {
|
|
252
|
+
...segmentStyle,
|
|
253
|
+
fontSize: (segmentStyle.fontSize ?? 11) * SMALL_CAPS_SCALE
|
|
254
|
+
};
|
|
255
|
+
let glyphWidth = 0;
|
|
256
|
+
for (const segment of smallCapsSegments(measuredText)) glyphWidth += canvasMeasureTextWidth(segment.text, segment.small ? smallSegmentStyle : segmentStyle);
|
|
223
257
|
let width = glyphWidth;
|
|
224
258
|
if (letterSpacing) {
|
|
225
259
|
const codePoints = countCodePoints(measuredText);
|
|
@@ -293,18 +327,26 @@ function canvasMeasureRun(text, sourceStyle) {
|
|
|
293
327
|
...style.eastAsiaAlternateFontFamily !== void 0 ? { alternateFontFamily: style.eastAsiaAlternateFontFamily } : {}
|
|
294
328
|
}) : void 0;
|
|
295
329
|
const complexScriptFont = hasComplexScriptFormatting(style) && !style.letterSpacing ? buildFontString(scriptStyle(style, SCRIPT_CLASS.complex)) : void 0;
|
|
330
|
+
const smallCapsFont = style.fontVariant === "small-caps" ? buildFontString({
|
|
331
|
+
...style,
|
|
332
|
+
fontSize: (style.fontSize ?? 11) * SMALL_CAPS_SCALE
|
|
333
|
+
}) : void 0;
|
|
296
334
|
const letterSpacing = style.letterSpacing ?? 0;
|
|
297
335
|
const scale = getHorizontalScaleFactor(style.horizontalScale);
|
|
298
336
|
const charWidths = [];
|
|
299
337
|
let totalWidth = 0;
|
|
338
|
+
const smallCapsAt = smallCapsFont !== void 0 ? smallCapsMask(text) : void 0;
|
|
300
339
|
let offset = 0;
|
|
340
|
+
let codePointIndex = 0;
|
|
301
341
|
for (const char of text) {
|
|
302
342
|
const cp = char.codePointAt(0);
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
|
|
343
|
+
const isSmallCap = smallCapsAt?.[codePointIndex] ?? false;
|
|
344
|
+
const measured = isSmallCap ? char.toLocaleUpperCase() : applyTextTransform(char, style);
|
|
345
|
+
if (isSmallCap && smallCapsFont !== void 0) ctx.font = smallCapsFont;
|
|
346
|
+
else if (eastAsiaFont !== void 0 || complexScriptFont !== void 0) {
|
|
347
|
+
const script = scriptClassOf(cp, style.eastAsiaHint);
|
|
306
348
|
ctx.font = (script === SCRIPT_CLASS.eastAsia ? eastAsiaFont : void 0) ?? (script === SCRIPT_CLASS.complex ? complexScriptFont : void 0) ?? baseFont;
|
|
307
|
-
}
|
|
349
|
+
} else if (smallCapsFont !== void 0) ctx.font = baseFont;
|
|
308
350
|
let charWidth = ctx.measureText(measured).width;
|
|
309
351
|
if (letterSpacing && offset + char.length < text.length) charWidth += letterSpacing;
|
|
310
352
|
charWidth *= scale;
|
|
@@ -312,6 +354,7 @@ function canvasMeasureRun(text, sourceStyle) {
|
|
|
312
354
|
if (char.length === 2) charWidths.push(0);
|
|
313
355
|
totalWidth += charWidth;
|
|
314
356
|
offset += char.length;
|
|
357
|
+
codePointIndex += 1;
|
|
315
358
|
}
|
|
316
359
|
return {
|
|
317
360
|
width: totalWidth,
|
|
@@ -2,8 +2,20 @@ import { RunFormatting } from "../types.js";
|
|
|
2
2
|
import { FontStyle } from "./measureTypes.js";
|
|
3
3
|
//#region src/layout-engine/measure/measureHelpers.d.ts
|
|
4
4
|
declare const DEFAULT_FONT_SIZE = 11;
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* The face for text whose run properties name no font anywhere in the cascade:
|
|
7
|
+
* no `w:rFonts` family or theme reference in the run, its styles or
|
|
8
|
+
* `w:docDefaults`. ECMA-376 §17.3.2.26 leaves this to the application; its
|
|
9
|
+
* default is Times New Roman. A document that names a font at any level never
|
|
10
|
+
* reaches it.
|
|
11
|
+
*/
|
|
12
|
+
declare const DEFAULT_FONT_FAMILY = "Times New Roman";
|
|
13
|
+
/**
|
|
14
|
+
* Point size of `w:vertAlign="superscript"`/`"subscript"` text: two thirds
|
|
15
|
+
* of the run's size, truncated to whole half-points (the `w:sz` unit) and
|
|
16
|
+
* never below one half-point.
|
|
17
|
+
*/
|
|
18
|
+
declare function docxScriptFontSize(fontSize: number): number;
|
|
7
19
|
/**
|
|
8
20
|
* Build a measurement `FontStyle` from a run's formatting. Single source of
|
|
9
21
|
* truth for run → FontStyle so every measurement path (layout line-breaking,
|
|
@@ -88,4 +100,4 @@ declare function halfPtToPx(halfPt: number): number;
|
|
|
88
100
|
*/
|
|
89
101
|
declare function pxToHalfPt(px: number): number;
|
|
90
102
|
//#endregion
|
|
91
|
-
export { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE,
|
|
103
|
+
export { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, buildFontString, buildRunFontStyle, clearFontResolvedCache, docxScriptFontSize, findCharacterAtX, getResolvedData, getXForCharacter, halfPtToPx, ptToPx, pxToHalfPt, pxToPt, pxToTwips, twipsToPx };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { resolveFontFamily } from "../../utils/fontResolver.js";
|
|
2
2
|
import "../../utils/fontWeights.js";
|
|
3
3
|
import { normalizeHorizontalScalePercent } from "../../utils/horizontalScale.js";
|
|
4
|
+
import { eastAsiaHintApplies } from "../../utils/scriptSegments.js";
|
|
4
5
|
import { FONT_KERNING_MODE, getRunFontKerningMode } from "./textMeasurementPolicy.js";
|
|
5
6
|
//#region src/layout-engine/measure/measureHelpers.ts
|
|
6
7
|
/**
|
|
@@ -16,8 +17,34 @@ const TWIPS_PER_INCH = 1440;
|
|
|
16
17
|
const PX_PER_INCH = 96;
|
|
17
18
|
const TWIPS_PER_PX = TWIPS_PER_INCH / PX_PER_INCH;
|
|
18
19
|
const DEFAULT_FONT_SIZE = 11;
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
/**
|
|
21
|
+
* The face for text whose run properties name no font anywhere in the cascade:
|
|
22
|
+
* no `w:rFonts` family or theme reference in the run, its styles or
|
|
23
|
+
* `w:docDefaults`. ECMA-376 §17.3.2.26 leaves this to the application; its
|
|
24
|
+
* default is Times New Roman. A document that names a font at any level never
|
|
25
|
+
* reaches it.
|
|
26
|
+
*/
|
|
27
|
+
const DEFAULT_FONT_FAMILY = "Times New Roman";
|
|
28
|
+
/**
|
|
29
|
+
* Point size of `w:vertAlign="superscript"`/`"subscript"` text: two thirds
|
|
30
|
+
* of the run's size, truncated to whole half-points (the `w:sz` unit) and
|
|
31
|
+
* never below one half-point.
|
|
32
|
+
*/
|
|
33
|
+
function docxScriptFontSize(fontSize) {
|
|
34
|
+
return Math.max(1, Math.floor(fontSize * 2 * 2 / 3)) / 2;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The `textTransform`/`fontVariant` a run's caps toggles contribute.
|
|
38
|
+
*
|
|
39
|
+
* `w:caps` wins when both are set: full-size capitals, never synthesized
|
|
40
|
+
* small ones (matching the else-if in formatToStyle.ts's live editor
|
|
41
|
+
* styling).
|
|
42
|
+
*/
|
|
43
|
+
function capsStyleOf(run) {
|
|
44
|
+
if (run.allCaps) return { textTransform: "uppercase" };
|
|
45
|
+
if (run.smallCaps) return { fontVariant: "small-caps" };
|
|
46
|
+
return {};
|
|
47
|
+
}
|
|
21
48
|
/**
|
|
22
49
|
* Build a measurement `FontStyle` from a run's formatting. Single source of
|
|
23
50
|
* truth for run → FontStyle so every measurement path (layout line-breaking,
|
|
@@ -28,13 +55,14 @@ const DOCX_SCRIPT_FONT_SCALE = .75;
|
|
|
28
55
|
*/
|
|
29
56
|
function buildRunFontStyle(run, fallbackFontFamily, fallbackFontSize) {
|
|
30
57
|
const baseFontSize = run.fontSize ?? fallbackFontSize;
|
|
31
|
-
const fontSize = run.superscript || run.subscript ? baseFontSize
|
|
58
|
+
const fontSize = run.superscript || run.subscript ? docxScriptFontSize(baseFontSize) : baseFontSize;
|
|
32
59
|
const horizontalScale = normalizeHorizontalScalePercent(run.horizontalScale);
|
|
33
60
|
return {
|
|
34
61
|
fontFamily: run.fontFamily ?? fallbackFontFamily,
|
|
35
62
|
...run.alternateFontFamily !== void 0 ? { alternateFontFamily: run.alternateFontFamily } : {},
|
|
36
63
|
...run.eastAsiaFontFamily !== void 0 ? { eastAsiaFontFamily: run.eastAsiaFontFamily } : {},
|
|
37
64
|
...run.eastAsiaAlternateFontFamily !== void 0 ? { eastAsiaAlternateFontFamily: run.eastAsiaAlternateFontFamily } : {},
|
|
65
|
+
...eastAsiaHintApplies(run) ? { eastAsiaHint: true } : {},
|
|
38
66
|
...run.complexScriptFontFamily !== void 0 ? { complexScriptFontFamily: run.complexScriptFontFamily } : {},
|
|
39
67
|
...run.complexScriptAlternateFontFamily !== void 0 ? { complexScriptAlternateFontFamily: run.complexScriptAlternateFontFamily } : {},
|
|
40
68
|
...run.complexScriptFontSize !== void 0 ? { complexScriptFontSize: run.complexScriptFontSize } : {},
|
|
@@ -45,8 +73,7 @@ function buildRunFontStyle(run, fallbackFontFamily, fallbackFontSize) {
|
|
|
45
73
|
...run.bold !== void 0 ? { bold: run.bold } : {},
|
|
46
74
|
...run.italic !== void 0 ? { italic: run.italic } : {},
|
|
47
75
|
...run.letterSpacing !== void 0 ? { letterSpacing: run.letterSpacing } : {},
|
|
48
|
-
...run
|
|
49
|
-
...run.smallCaps ? { fontVariant: "small-caps" } : {},
|
|
76
|
+
...capsStyleOf(run),
|
|
50
77
|
...horizontalScale !== void 0 ? { horizontalScale } : {},
|
|
51
78
|
kerning: getRunFontKerningMode(run, fallbackFontSize) === FONT_KERNING_MODE.enabled
|
|
52
79
|
};
|
|
@@ -103,11 +130,10 @@ function clearFontResolvedCache() {
|
|
|
103
130
|
function buildFontString(style) {
|
|
104
131
|
const parts = [];
|
|
105
132
|
if (style.italic) parts.push("italic");
|
|
106
|
-
if (style.fontVariant) parts.push(style.fontVariant);
|
|
107
133
|
if (style.bold) parts.push("700");
|
|
108
134
|
const fontSizePx = ptToPx(style.fontSize ?? 11);
|
|
109
135
|
parts.push(`${fontSizePx}px`);
|
|
110
|
-
const fontFamily = style.fontFamily ?? "
|
|
136
|
+
const fontFamily = style.fontFamily ?? "Times New Roman";
|
|
111
137
|
parts.push(getResolvedFallback(fontFamily, style.alternateFontFamily));
|
|
112
138
|
return parts.join(" ");
|
|
113
139
|
}
|
|
@@ -181,4 +207,4 @@ function pxToHalfPt(px) {
|
|
|
181
207
|
return pxToPt(px) * 2;
|
|
182
208
|
}
|
|
183
209
|
//#endregion
|
|
184
|
-
export { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE,
|
|
210
|
+
export { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, buildFontString, buildRunFontStyle, clearFontResolvedCache, docxScriptFontSize, findCharacterAtX, getResolvedData, getXForCharacter, halfPtToPx, ptToPx, pxToHalfPt, pxToPt, pxToTwips, twipsToPx };
|