@stll/folio-core 0.28.1 → 0.30.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/folioEditor.d.ts +28 -1
- package/dist/controller/folioEditor.js +37 -26
- package/dist/controller/fontReadiness.js +28 -24
- package/dist/controller/hiddenEditorManager.d.ts +2 -7
- package/dist/controller/layoutPipeline.js +33 -3
- package/dist/controller/noteEditorManager.d.ts +3 -6
- package/dist/docx/metafileSvg.d.ts +12 -0
- package/dist/docx/metafileSvg.js +462 -0
- package/dist/docx/paragraphTextBoxEnrichment.js +55 -4
- package/dist/docx/parser.d.ts +2 -2
- package/dist/docx/parser.js +26 -0
- package/dist/docx/runParser.js +2 -1
- package/dist/docx/serializer/runSerializer.js +14 -2
- package/dist/docx/styleParser.js +2 -1
- package/dist/docx/textBoxParser.js +14 -0
- package/dist/docx/vmlImageParser.js +72 -152
- package/dist/docx/vmlPreview.d.ts +31 -0
- package/dist/docx/vmlPreview.js +535 -0
- package/dist/docx/wrapTypes.d.ts +2 -36
- package/dist/docx/wrapTypes.js +1 -39
- package/dist/fonts/fontAlternates.d.ts +8 -0
- package/dist/fonts/fontAlternates.js +16 -0
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/footnoteLayout.js +34 -3
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/headerFooterLayout.js +5 -0
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +4 -2
- package/dist/layout-bridge/convert/toFlowBlocks.js +136 -120
- package/dist/layout-bridge/dom/noteStoryDom.d.ts +1 -1
- package/dist/layout-bridge/engine/selectionRects.js +3 -1
- package/dist/layout-engine/index.js +65 -32
- package/dist/layout-engine/justifiedLineFit.d.ts +7 -0
- package/dist/layout-engine/justifiedLineFit.js +6 -0
- package/dist/layout-engine/keep-together.js +15 -10
- package/dist/layout-engine/measure/complexScriptFormatting.d.ts +6 -1
- package/dist/layout-engine/measure/complexScriptFormatting.js +11 -2
- package/dist/layout-engine/measure/floatingTablePosition.d.ts +16 -1
- package/dist/layout-engine/measure/floatingTablePosition.js +29 -6
- package/dist/layout-engine/measure/lineBreakProvider.js +67 -8
- package/dist/layout-engine/measure/listMarkerWidth.d.ts +1 -0
- package/dist/layout-engine/measure/listMarkerWidth.js +23 -10
- package/dist/layout-engine/measure/measureBlocks.d.ts +15 -0
- package/dist/layout-engine/measure/measureBlocks.js +197 -30
- package/dist/layout-engine/measure/measureContainer.js +20 -24
- package/dist/layout-engine/measure/measureHelpers.d.ts +1 -1
- package/dist/layout-engine/measure/measureHelpers.js +14 -8
- package/dist/layout-engine/measure/measureParagraph.js +111 -37
- package/dist/layout-engine/measure/measureTypes.d.ts +3 -0
- package/dist/layout-engine/measure/tabCalculator.d.ts +123 -0
- package/dist/layout-engine/measure/tabCalculator.js +144 -0
- package/dist/layout-engine/measure/tableCellFlow.d.ts +21 -1
- package/dist/layout-engine/measure/tableCellFlow.js +45 -8
- package/dist/layout-engine/measure/tableInlinePlacement.d.ts +10 -2
- package/dist/layout-engine/measure/tableInlinePlacement.js +17 -15
- package/dist/layout-engine/paginator.d.ts +26 -2
- package/dist/layout-engine/paginator.js +76 -14
- package/dist/layout-engine/paragraphSpacing.d.ts +3 -1
- package/dist/layout-engine/paragraphSpacing.js +4 -3
- package/dist/layout-engine/renderedBreakReconciliation.d.ts +14 -3
- package/dist/layout-engine/renderedBreakReconciliation.js +45 -14
- package/dist/layout-engine/section-breaks.d.ts +3 -1
- package/dist/layout-engine/section-breaks.js +6 -2
- package/dist/layout-engine/types.d.ts +18 -1
- package/dist/layout-engine/types.js +1 -1
- package/dist/layout-painter/imageLayout.d.ts +1 -1
- package/dist/layout-painter/renderPage.js +6 -1
- package/dist/layout-painter/renderParagraph.js +169 -82
- package/dist/layout-painter/renderTable.d.ts +1 -1
- package/dist/layout-painter/renderTable.js +18 -7
- package/dist/layout-painter/renderTextBox.d.ts +1 -1
- package/dist/layout-painter/renderTextBox.js +6 -1
- package/dist/markdown/renderParagraph.js +7 -2
- package/dist/model.d.ts +3 -3
- package/dist/model.js +2 -2
- package/dist/paged-layout/sectionBlockWidths.d.ts +14 -0
- package/dist/paged-layout/sectionBlockWidths.js +103 -14
- package/dist/prosemirror/attrs/index.js +14 -2
- package/dist/prosemirror/bookmarkBoundaryAttrs.d.ts +8 -0
- package/dist/prosemirror/bookmarkBoundaryAttrs.js +66 -0
- package/dist/prosemirror/conversion/fromProseDoc.js +176 -54
- package/dist/prosemirror/conversion/toProseDoc.js +179 -64
- package/dist/prosemirror/extensions/StarterKit.js +11 -3
- package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +8 -4
- package/dist/prosemirror/extensions/features/PasteCleanupExtension.d.ts +4 -1
- package/dist/prosemirror/extensions/features/PasteCleanupExtension.js +9 -5
- package/dist/prosemirror/extensions/features/pasteCleanup.d.ts +10 -23
- package/dist/prosemirror/extensions/features/pasteCleanup.js +77 -22
- package/dist/prosemirror/extensions/marks/CharacterSpacingExtension.js +9 -5
- package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.d.ts +9 -0
- package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.js +67 -0
- package/dist/prosemirror/extensions/nodes/FieldExtension.d.ts +10 -4
- package/dist/prosemirror/extensions/nodes/FieldExtension.js +77 -59
- package/dist/prosemirror/extensions/nodes/TableExtension.js +2 -0
- package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.d.ts +4 -1
- package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.js +4 -3
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +6 -2
- package/dist/prosemirror/listMarker.d.ts +58 -0
- package/dist/prosemirror/listMarker.js +185 -0
- package/dist/prosemirror/numberedRefFields.d.ts +24 -0
- package/dist/prosemirror/numberedRefFields.js +276 -0
- package/dist/prosemirror/paraText.js +56 -17
- package/dist/prosemirror/plugins/anonymizationDecorations.js +1 -1
- package/dist/prosemirror/plugins/pmTextScan.d.ts +3 -1
- package/dist/prosemirror/plugins/pmTextScan.js +28 -7
- package/dist/prosemirror/plugins/templateDirectives.js +2 -2
- package/dist/prosemirror/schema/index.d.ts +2 -2
- package/dist/prosemirror/schema/nodes.d.ts +17 -1
- package/dist/prosemirror/utils/tabCalculator.d.ts +2 -123
- package/dist/prosemirror/utils/tabCalculator.js +1 -140
- package/dist/prosemirror/validation.js +93 -0
- package/dist/render-dom/RemoteSelectionOverlay.d.ts +4 -4
- package/dist/types/documentEnumValues.d.ts +4 -1
- package/dist/types/documentEnumValues.js +12 -1
- package/dist/types/editor-story.d.ts +8 -0
- package/dist/types/editor-story.js +0 -0
- package/dist/types/remote-selection.d.ts +11 -0
- package/dist/types/remote-selection.js +0 -0
- package/dist/types/wrap.d.ts +36 -0
- package/dist/types/wrap.js +40 -0
- package/dist/utils/fontResolver.d.ts +1 -1
- package/dist/utils/fontResolver.js +42 -2
- package/dist/utils/formatToStyle.js +4 -2
- package/dist/utils/horizontalScale.d.ts +28 -0
- package/dist/utils/horizontalScale.js +42 -0
- package/package.json +2 -2
|
@@ -6,6 +6,7 @@ import { measureRun } from "../../layout-engine/measure/measureProvider.js";
|
|
|
6
6
|
import { buildTableCellFloatingZones, getTableCellContentWidth, getTableCellFloatingImages } from "../../layout-engine/measure/tableCellFloating.js";
|
|
7
7
|
import { buildTableCellGrid, buildTableCellPlacements } from "../../layout-engine/measure/tableCellGrid.js";
|
|
8
8
|
import { resolveTableCellPadding } from "../../layout-engine/types.js";
|
|
9
|
+
import { normalizeHorizontalScalePercent } from "../../utils/horizontalScale.js";
|
|
9
10
|
import { resolvePhysicalParagraphInlineLayout } from "../../utils/paragraphInlineLayout.js";
|
|
10
11
|
import { inlineImageBoundingBox } from "../../utils/rotationBoundingBox.js";
|
|
11
12
|
import { getPageTop } from "./hitTest.js";
|
|
@@ -33,13 +34,14 @@ function runToFontStyle(run) {
|
|
|
33
34
|
return buildRunFontStyle(run, "Arial", 12);
|
|
34
35
|
}
|
|
35
36
|
function mathRunToFontStyle(run) {
|
|
37
|
+
const horizontalScale = normalizeHorizontalScalePercent(run.horizontalScale);
|
|
36
38
|
return {
|
|
37
39
|
fontFamily: run.fontFamily ?? "Cambria Math",
|
|
38
40
|
fontSize: run.fontSize ?? 11,
|
|
39
41
|
...run.bold !== void 0 ? { bold: run.bold } : {},
|
|
40
42
|
...run.italic !== void 0 ? { italic: run.italic } : {},
|
|
41
43
|
...run.letterSpacing !== void 0 ? { letterSpacing: run.letterSpacing } : {},
|
|
42
|
-
...
|
|
44
|
+
...horizontalScale !== void 0 ? { horizontalScale } : {}
|
|
43
45
|
};
|
|
44
46
|
}
|
|
45
47
|
/**
|
|
@@ -3,14 +3,14 @@ import { reflowFootnoteColumns } from "./footnoteColumnReflow.js";
|
|
|
3
3
|
import { resolveSectionHeaderFooterRefs } from "./headerFooterRefs.js";
|
|
4
4
|
import { calculateChainHeight, computeKeepNextChains, getMidChainIndices, hasKeepLines, hasPageBreakBefore } from "./keep-together.js";
|
|
5
5
|
import { measuredLineAdvance } from "./lineFlow.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { createPaginator } from "./paginator.js";
|
|
6
|
+
import { resolveFloatingTablePageX } from "./measure/floatingTablePosition.js";
|
|
7
|
+
import { resolveTableInlineOffset } from "./measure/tableInlinePlacement.js";
|
|
8
|
+
import { SECTION_START_PLACEMENT, createPaginator } from "./paginator.js";
|
|
9
9
|
import { getParagraphFragmentPmRange } from "./paragraphFragmentRange.js";
|
|
10
10
|
import { collapseParagraphSpacing, getParagraphSpacingAfter, getParagraphSpacingBefore, isEmptyParagraph, paragraphsShareStyle, resolveEffectiveParagraphSpacingTree } from "./paragraphSpacing.js";
|
|
11
11
|
import { assertExhaustiveFlowBlock, findPageIndexContainingPmPos } from "./pmPageIndex.js";
|
|
12
|
-
import { INITIAL_RENDERED_BREAK_STATE, reconcileAfterBlock, reconcileBreakBeforeBlock, recordReflowBoundary } from "./renderedBreakReconciliation.js";
|
|
13
|
-
import { applyPendingToActive, createInitialSectionState, getEffectiveColumns, getEffectiveMargins, getEffectivePageSize, scheduleSectionBreak } from "./section-breaks.js";
|
|
12
|
+
import { INITIAL_RENDERED_BREAK_STATE, PAGE_ADVANCE, reconcileAfterBlock, reconcileBreakBeforeBlock, recordReflowBoundary } from "./renderedBreakReconciliation.js";
|
|
13
|
+
import { applyPendingToActive, createInitialSectionState, getEffectiveColumns, getEffectiveMargins, getEffectivePageSize, normalizeSectionBreakType, scheduleSectionBreak } from "./section-breaks.js";
|
|
14
14
|
import { buildTableRowBreakInfo, getRowContinuationSkip, snapRowBreak } from "./tableRowBreak.js";
|
|
15
15
|
import { bandFragmentX, bandTopContentY, isPageFrameRelativeAnchor } from "./textBoxFlow.js";
|
|
16
16
|
import { 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 } from "./types.js";
|
|
@@ -26,7 +26,6 @@ const DEFAULT_COLUMNS = {
|
|
|
26
26
|
count: 1,
|
|
27
27
|
gap: 0
|
|
28
28
|
};
|
|
29
|
-
const DEFAULT_SECTION_BREAK_TYPE = "nextPage";
|
|
30
29
|
const CONTINUE_PAGE_NUMBERING = { type: "continue" };
|
|
31
30
|
function collectSectionConfigs(blocks, initialConfig, finalConfig) {
|
|
32
31
|
const configs = [];
|
|
@@ -215,7 +214,7 @@ function layoutDocumentPass(blocks, measures, options) {
|
|
|
215
214
|
const firstLine = measure.kind === "paragraph" ? measure.lines.at(0) : void 0;
|
|
216
215
|
const firstLineAdvance = firstLine ? measuredLineAdvance(firstLine) : 0;
|
|
217
216
|
const renderedBreakNeedsSnap = measure.kind === "paragraph" && (!paginator.fits(measure.totalHeight) || firstLineAdvance > 0 && paginator.getAvailableHeight() <= firstLineAdvance * RENDERED_BREAK_REFLOW_TOLERANCE_LINES);
|
|
218
|
-
const hasExplicitPageBreak = hasPageBreakBefore(block);
|
|
217
|
+
const hasExplicitPageBreak = block.kind === "pageBreak" || hasPageBreakBefore(block);
|
|
219
218
|
const breakDecision = reconcileBreakBeforeBlock({
|
|
220
219
|
state: renderedBreakState,
|
|
221
220
|
block,
|
|
@@ -225,7 +224,10 @@ function layoutDocumentPass(blocks, measures, options) {
|
|
|
225
224
|
hasExplicitPageBreak,
|
|
226
225
|
renderedBreakNeedsSnap
|
|
227
226
|
});
|
|
228
|
-
if (
|
|
227
|
+
if (block.kind !== "pageBreak") {
|
|
228
|
+
if (breakDecision.pageAdvance === PAGE_ADVANCE.PHYSICAL) paginator.forcePageBreak();
|
|
229
|
+
else if (breakDecision.pageAdvance === PAGE_ADVANCE.COALESCED) paginator.coalescePageBreak();
|
|
230
|
+
}
|
|
229
231
|
renderedBreakState = breakDecision.state;
|
|
230
232
|
const chain = keepNextChains.get(i);
|
|
231
233
|
if (chain && !midChainIndices.has(i)) {
|
|
@@ -262,14 +264,15 @@ function layoutDocumentPass(blocks, measures, options) {
|
|
|
262
264
|
});
|
|
263
265
|
break;
|
|
264
266
|
case "pageBreak":
|
|
265
|
-
paginator.forcePageBreak();
|
|
267
|
+
if (breakDecision.pageAdvance === PAGE_ADVANCE.PHYSICAL) paginator.forcePageBreak();
|
|
268
|
+
else if (breakDecision.pageAdvance === PAGE_ADVANCE.COALESCED) paginator.coalescePageBreak();
|
|
266
269
|
break;
|
|
267
270
|
case "columnBreak":
|
|
268
271
|
paginator.forceColumnBreak();
|
|
269
272
|
break;
|
|
270
273
|
case "sectionBreak": {
|
|
271
274
|
const nextSectionConfig = sectionConfigs[sectionIdx + 1] ?? initialConfig;
|
|
272
|
-
handleSectionBreak(block, paginator, nextSectionConfig, sectionBreakTypes[sectionIdx]
|
|
275
|
+
handleSectionBreak(block, paginator, nextSectionConfig, normalizeSectionBreakType(sectionBreakTypes[sectionIdx]), sectionIdx + 1);
|
|
273
276
|
const nextColumns = nextSectionConfig.columns;
|
|
274
277
|
const nextBreakIndex = breakIndices[sectionIdx + 1] ?? blocks.length;
|
|
275
278
|
const nextBreak = blocks[nextBreakIndex];
|
|
@@ -539,17 +542,18 @@ function layoutTable(block, measure, paginator, footnoteHeightById) {
|
|
|
539
542
|
let currentRowIndex = 0;
|
|
540
543
|
const breakInfo = buildTableRowBreakInfo(block, measure);
|
|
541
544
|
const verticallyMergedRows = getVerticallyMergedRows(block);
|
|
542
|
-
const computeTableX = (columnIndex) => {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
545
|
+
const computeTableX = ({ columnIndex, rowIndex }) => {
|
|
546
|
+
return paginator.getColumnX(columnIndex) + resolveTableInlineOffset({
|
|
547
|
+
table: block,
|
|
548
|
+
rowJustification: block.rows[rowIndex]?.justification,
|
|
549
|
+
frameWidth: paginator.columnWidth,
|
|
550
|
+
tableWidth: measure.totalWidth
|
|
551
|
+
});
|
|
548
552
|
};
|
|
549
553
|
const getCurrentRowCapacity = (state = paginator.getCurrentState()) => state.rawContentBottom - state.topMargin;
|
|
550
554
|
const hasAdjacentPriorTableRows = (rowIndex, state = paginator.getCurrentState()) => {
|
|
551
555
|
const previous = state.page.fragments.at(-1);
|
|
552
|
-
return previous?.kind === "table" && previous.blockId === block.id && previous.toRow === rowIndex && previous.y + previous.height === state.cursorY
|
|
556
|
+
return previous?.kind === "table" && previous.blockId === block.id && previous.toRow === rowIndex && previous.y + previous.height === state.cursorY;
|
|
553
557
|
};
|
|
554
558
|
const shouldRepeatHeaderRows = (rowIndex, consumed, state = paginator.getCurrentState()) => headerRowCount > 0 && rowIndex >= headerRowCount && !(consumed === 0 && hasAdjacentPriorTableRows(rowIndex, state));
|
|
555
559
|
const canSplitRow = (rowIndex, state = paginator.getCurrentState()) => {
|
|
@@ -616,7 +620,10 @@ function layoutTable(block, measure, paginator, footnoteHeightById) {
|
|
|
616
620
|
const sliceFragment = {
|
|
617
621
|
kind: "table",
|
|
618
622
|
blockId: block.id,
|
|
619
|
-
x: computeTableX(
|
|
623
|
+
x: computeTableX({
|
|
624
|
+
columnIndex: sliceState.columnIndex,
|
|
625
|
+
rowIndex: currentRowIndex
|
|
626
|
+
}),
|
|
620
627
|
y: 0,
|
|
621
628
|
width: measure.totalWidth,
|
|
622
629
|
height: fragmentHeight,
|
|
@@ -633,7 +640,10 @@ function layoutTable(block, measure, paginator, footnoteHeightById) {
|
|
|
633
640
|
};
|
|
634
641
|
const sliceResult = paginator.addFragment(sliceFragment, fragmentHeight, 0, 0);
|
|
635
642
|
sliceFragment.y = sliceResult.y;
|
|
636
|
-
sliceFragment.x = computeTableX(
|
|
643
|
+
sliceFragment.x = computeTableX({
|
|
644
|
+
columnIndex: sliceResult.state.columnIndex,
|
|
645
|
+
rowIndex: currentRowIndex
|
|
646
|
+
});
|
|
637
647
|
consumed = nextConsumed;
|
|
638
648
|
if (consumed < splittableRow.height) if (forceRenderedPageBreak) paginator.forcePageBreak();
|
|
639
649
|
else {
|
|
@@ -658,7 +668,15 @@ function layoutTable(block, measure, paginator, footnoteHeightById) {
|
|
|
658
668
|
const fragmentFootnoteIds = [];
|
|
659
669
|
let fragmentFootnoteHeight = 0;
|
|
660
670
|
let retryOnNextFlowRegion = false;
|
|
671
|
+
const fragmentX = computeTableX({
|
|
672
|
+
columnIndex: state.columnIndex,
|
|
673
|
+
rowIndex: currentRowIndex
|
|
674
|
+
});
|
|
661
675
|
for (let j = currentRowIndex; j < rows.length; j++) {
|
|
676
|
+
if (j > currentRowIndex && computeTableX({
|
|
677
|
+
columnIndex: state.columnIndex,
|
|
678
|
+
rowIndex: j
|
|
679
|
+
}) !== fragmentX) break;
|
|
662
680
|
const rowHeight = rows[j].height;
|
|
663
681
|
const currentRowFootnoteIds = rowFootnoteIds[j] ?? [];
|
|
664
682
|
const fragmentFootnoteCountBeforeRow = fragmentFootnoteIds.length;
|
|
@@ -693,7 +711,7 @@ function layoutTable(block, measure, paginator, footnoteHeightById) {
|
|
|
693
711
|
if (retryOnNextFlowRegion) continue;
|
|
694
712
|
const fragmentHeight = rowsHeight + normalHeaderOverhead;
|
|
695
713
|
const isLastFragment = currentRowIndex + fittingRows >= rows.length;
|
|
696
|
-
const desiredX =
|
|
714
|
+
const desiredX = fragmentX;
|
|
697
715
|
const fragment = {
|
|
698
716
|
kind: "table",
|
|
699
717
|
blockId: block.id,
|
|
@@ -760,13 +778,20 @@ function layoutFloatingTable(block, measure, paginator, contentWidth) {
|
|
|
760
778
|
const tableWidth = measure.totalWidth;
|
|
761
779
|
const tableHeight = measure.totalHeight;
|
|
762
780
|
const contentHeight = page.size.h - margins.top - margins.bottom;
|
|
763
|
-
let baseX = margins.left;
|
|
764
781
|
let baseY = margins.top;
|
|
765
|
-
if (floating?.horzAnchor === "page") baseX = 0;
|
|
766
782
|
if (floating?.vertAnchor === "page") baseY = 0;
|
|
767
783
|
else if (floating?.vertAnchor === "text") baseY = state.cursorY;
|
|
768
784
|
let x = paginator.getColumnX(state.columnIndex);
|
|
769
|
-
if (floating) x =
|
|
785
|
+
if (floating) x = resolveFloatingTablePageX({
|
|
786
|
+
anchor: floating,
|
|
787
|
+
justification: block.justification,
|
|
788
|
+
tableWidth,
|
|
789
|
+
marginWidth: contentWidth,
|
|
790
|
+
pageWidth: page.size.w,
|
|
791
|
+
marginLeft: margins.left,
|
|
792
|
+
textFrameWidth: paginator.columnWidth,
|
|
793
|
+
textFrameLeft: paginator.getColumnX(state.columnIndex)
|
|
794
|
+
});
|
|
770
795
|
let y = state.cursorY;
|
|
771
796
|
let usedExplicitY = false;
|
|
772
797
|
if (floating?.tblpY !== void 0) {
|
|
@@ -780,11 +805,6 @@ function layoutFloatingTable(block, measure, paginator, contentWidth) {
|
|
|
780
805
|
else if (spec === "center") y = baseY + (contentHeight - tableHeight) / 2;
|
|
781
806
|
}
|
|
782
807
|
if (!usedExplicitY) y = paginator.ensureFits(tableHeight).cursorY;
|
|
783
|
-
const usesNumericOffset = floating?.tblpX !== void 0 && floating.tblpXSpec === void 0;
|
|
784
|
-
const clampToPage = floating?.horzAnchor === "page" || usesNumericOffset;
|
|
785
|
-
const minX = clampToPage ? 0 : margins.left;
|
|
786
|
-
const maxX = clampToPage ? page.size.w - tableWidth : margins.left + contentWidth - tableWidth;
|
|
787
|
-
if (Number.isFinite(maxX)) x = Math.max(minX, Math.min(x, maxX));
|
|
788
808
|
const fragment = {
|
|
789
809
|
kind: "table",
|
|
790
810
|
blockId: block.id,
|
|
@@ -943,26 +963,39 @@ function handleSectionBreak(_block, paginator, nextSectionConfig, nextSectionTyp
|
|
|
943
963
|
switch (nextSectionType) {
|
|
944
964
|
case "nextPage":
|
|
945
965
|
paginator.updatePageLayout(nextSectionConfig.pageSize, nextSectionConfig.margins);
|
|
946
|
-
if (nextSectionIndex !== void 0) paginator.startSection(
|
|
966
|
+
if (nextSectionIndex !== void 0) paginator.startSection({
|
|
967
|
+
sectionIndex: nextSectionIndex,
|
|
968
|
+
pageNumbering: nextSectionConfig.pageNumbering
|
|
969
|
+
});
|
|
947
970
|
paginator.forcePageBreak({ coalesceBlankPage: true });
|
|
948
971
|
break;
|
|
949
972
|
case "evenPage":
|
|
950
973
|
if (paginator.forcePageBreak({ coalesceBlankPage: true }).page.number % 2 !== 0) paginator.forcePageBreak();
|
|
951
974
|
paginator.updatePageLayout(nextSectionConfig.pageSize, nextSectionConfig.margins);
|
|
952
|
-
if (nextSectionIndex !== void 0) paginator.startSection(
|
|
975
|
+
if (nextSectionIndex !== void 0) paginator.startSection({
|
|
976
|
+
sectionIndex: nextSectionIndex,
|
|
977
|
+
pageNumbering: nextSectionConfig.pageNumbering
|
|
978
|
+
});
|
|
953
979
|
if (!paginator.retargetCurrentBlankPage()) panic("Even-page section target must be blank");
|
|
954
980
|
break;
|
|
955
981
|
case "oddPage":
|
|
956
982
|
if (paginator.forcePageBreak({ coalesceBlankPage: true }).page.number % 2 === 0) paginator.forcePageBreak();
|
|
957
983
|
paginator.updatePageLayout(nextSectionConfig.pageSize, nextSectionConfig.margins);
|
|
958
|
-
if (nextSectionIndex !== void 0) paginator.startSection(
|
|
984
|
+
if (nextSectionIndex !== void 0) paginator.startSection({
|
|
985
|
+
sectionIndex: nextSectionIndex,
|
|
986
|
+
pageNumbering: nextSectionConfig.pageNumbering
|
|
987
|
+
});
|
|
959
988
|
if (!paginator.retargetCurrentBlankPage()) panic("Odd-page section target must be blank");
|
|
960
989
|
break;
|
|
961
990
|
case "continuous": {
|
|
962
991
|
const currentPage = paginator.pages.at(-1);
|
|
963
992
|
const nextSize = nextSectionConfig.pageSize;
|
|
964
993
|
const pageSizeChanges = currentPage != null && (Math.round(nextSize.w) !== Math.round(currentPage.size.w) || Math.round(nextSize.h) !== Math.round(currentPage.size.h));
|
|
965
|
-
if (nextSectionIndex !== void 0) paginator.startSection(
|
|
994
|
+
if (nextSectionIndex !== void 0) paginator.startSection({
|
|
995
|
+
sectionIndex: nextSectionIndex,
|
|
996
|
+
pageNumbering: nextSectionConfig.pageNumbering,
|
|
997
|
+
placement: pageSizeChanges ? SECTION_START_PLACEMENT.NEXT_PAGE : SECTION_START_PLACEMENT.CONTINUOUS
|
|
998
|
+
});
|
|
966
999
|
if (pageSizeChanges) {
|
|
967
1000
|
paginator.updatePageLayout(nextSize, nextSectionConfig.margins);
|
|
968
1001
|
if (!paginator.retargetCurrentBlankPage()) paginator.forcePageBreak({ coalesceBlankPage: true });
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ParagraphBlock } from "./types.js";
|
|
2
|
+
//#region src/layout-engine/justifiedLineFit.d.ts
|
|
3
|
+
declare const JUSTIFIED_LIST_FINAL_LINE_MAX_SHRINK_RATIO = 0.025;
|
|
4
|
+
declare const JUSTIFIED_LIST_SPACE_CONTRACTION_RATIO = 0.32;
|
|
5
|
+
declare const supportsJustifiedListFinalLineContraction: (block: ParagraphBlock) => boolean;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { JUSTIFIED_LIST_FINAL_LINE_MAX_SHRINK_RATIO, JUSTIFIED_LIST_SPACE_CONTRACTION_RATIO, supportsJustifiedListFinalLineContraction };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
//#region src/layout-engine/justifiedLineFit.ts
|
|
2
|
+
const JUSTIFIED_LIST_FINAL_LINE_MAX_SHRINK_RATIO = .025;
|
|
3
|
+
const JUSTIFIED_LIST_SPACE_CONTRACTION_RATIO = .32;
|
|
4
|
+
const supportsJustifiedListFinalLineContraction = (block) => block.attrs?.justificationCompatibility?.type !== "legacy" && block.attrs?.listMarker !== void 0;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { JUSTIFIED_LIST_FINAL_LINE_MAX_SHRINK_RATIO, JUSTIFIED_LIST_SPACE_CONTRACTION_RATIO, supportsJustifiedListFinalLineContraction };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { collapseParagraphSpacing } from "./paragraphSpacing.js";
|
|
1
|
+
import { collapseParagraphSpacing, isAuthoredEmptyParagraph, isEmptyParagraph } from "./paragraphSpacing.js";
|
|
2
2
|
//#region src/layout-engine/keep-together.ts
|
|
3
3
|
/**
|
|
4
4
|
* Keep Together Logic - Handle keepNext and keepLines paragraph properties
|
|
@@ -6,19 +6,24 @@ import { collapseParagraphSpacing } from "./paragraphSpacing.js";
|
|
|
6
6
|
* DOCX paragraphs can have keepNext (keep with next paragraph) and keepLines
|
|
7
7
|
* (keep all lines together) properties that affect pagination.
|
|
8
8
|
*/
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Whether an empty paragraph is only a structural separator through which a
|
|
11
|
+
* keep-with-next chain may pass. An explicitly styled or directly formatted
|
|
12
|
+
* blank is authored layout content: it anchors the preceding keepNext rather
|
|
13
|
+
* than implicitly linking that heading to later content.
|
|
14
|
+
*/
|
|
15
|
+
function isKeepNextPassThroughParagraph(block) {
|
|
16
|
+
if (!isEmptyParagraph(block)) return false;
|
|
17
|
+
if (block.attrs?.suppressEmptyParagraphHeight === true) return true;
|
|
18
|
+
return !isAuthoredEmptyParagraph(block);
|
|
14
19
|
}
|
|
15
20
|
function startsTrailingTableSeparatorChain(blocks, index) {
|
|
16
21
|
const block = blocks[index];
|
|
17
|
-
if (block?.kind !== "paragraph" || !
|
|
22
|
+
if (block?.kind !== "paragraph" || !isKeepNextPassThroughParagraph(block) || blocks[index - 1]?.kind !== "table") return false;
|
|
18
23
|
for (let nextIndex = index + 1; nextIndex < blocks.length; nextIndex++) {
|
|
19
24
|
const nextBlock = blocks[nextIndex];
|
|
20
25
|
if (nextBlock?.kind !== "paragraph") return false;
|
|
21
|
-
if (!
|
|
26
|
+
if (!isKeepNextPassThroughParagraph(nextBlock)) return true;
|
|
22
27
|
}
|
|
23
28
|
return false;
|
|
24
29
|
}
|
|
@@ -47,7 +52,7 @@ function computeKeepNextChains(blocks) {
|
|
|
47
52
|
if (nextBlock.kind === "sectionBreak" || nextBlock.kind === "pageBreak" || nextBlock.kind === "columnBreak") break;
|
|
48
53
|
if (nextBlock.kind !== "paragraph") break;
|
|
49
54
|
const nextPara = nextBlock;
|
|
50
|
-
if (nextPara.attrs?.keepNext ||
|
|
55
|
+
if (nextPara.attrs?.keepNext || isKeepNextPassThroughParagraph(nextPara)) {
|
|
51
56
|
memberIndices.push(j);
|
|
52
57
|
endIndex = j;
|
|
53
58
|
processed.add(j);
|
|
@@ -85,7 +90,7 @@ function calculateChainHeight(chain, blocks, measures) {
|
|
|
85
90
|
if (firstBlock?.kind !== "paragraph" || firstMeasure?.kind !== "paragraph") return 0;
|
|
86
91
|
let totalHeight = (firstBlock.attrs?.spacing?.before ?? 0) + firstMeasure.totalHeight;
|
|
87
92
|
let trailingSpacing = firstBlock.attrs?.spacing?.after ?? 0;
|
|
88
|
-
const startsWithTrailingTableSeparator = blocks[firstMemberIndex - 1]?.kind === "table" &&
|
|
93
|
+
const startsWithTrailingTableSeparator = blocks[firstMemberIndex - 1]?.kind === "table" && isEmptyParagraph(firstBlock) && !firstBlock.attrs?.keepNext;
|
|
89
94
|
const successorIndices = [...chain.memberIndices.slice(1)];
|
|
90
95
|
if (chain.anchorIndex !== -1) successorIndices.push(chain.anchorIndex);
|
|
91
96
|
for (let index = 0; index < successorIndices.length; index++) {
|
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
//#region src/layout-engine/measure/complexScriptFormatting.d.ts
|
|
2
2
|
type ComplexScriptFormattingSource = {
|
|
3
3
|
complexScriptFontFamily?: string;
|
|
4
|
+
complexScriptAlternateFontFamily?: string;
|
|
4
5
|
complexScriptFontSize?: number;
|
|
5
6
|
complexScriptBold?: boolean;
|
|
6
7
|
complexScriptItalic?: boolean;
|
|
7
8
|
};
|
|
8
9
|
type ResolvedComplexScriptFormatting = {
|
|
9
10
|
fontFamily?: string;
|
|
11
|
+
alternateFontFamily?: string;
|
|
10
12
|
fontSize?: number;
|
|
11
13
|
bold?: boolean;
|
|
12
14
|
italic?: boolean;
|
|
13
15
|
};
|
|
14
16
|
/** Resolve Word's independent complex-script slot into ordinary paint fields. */
|
|
15
17
|
declare const resolveComplexScriptFormatting: (source: ComplexScriptFormattingSource) => ResolvedComplexScriptFormatting;
|
|
18
|
+
declare const applyComplexScriptFormatting: <T extends {
|
|
19
|
+
alternateFontFamily?: string;
|
|
20
|
+
}>(base: T, source: ComplexScriptFormattingSource) => T & ResolvedComplexScriptFormatting;
|
|
16
21
|
declare const hasComplexScriptFormatting: (source: ComplexScriptFormattingSource) => boolean;
|
|
17
22
|
//#endregion
|
|
18
|
-
export { ResolvedComplexScriptFormatting, hasComplexScriptFormatting, resolveComplexScriptFormatting };
|
|
23
|
+
export { ResolvedComplexScriptFormatting, applyComplexScriptFormatting, hasComplexScriptFormatting, resolveComplexScriptFormatting };
|
|
@@ -2,10 +2,19 @@
|
|
|
2
2
|
/** Resolve Word's independent complex-script slot into ordinary paint fields. */
|
|
3
3
|
const resolveComplexScriptFormatting = (source) => ({
|
|
4
4
|
...source.complexScriptFontFamily !== void 0 ? { fontFamily: source.complexScriptFontFamily } : {},
|
|
5
|
+
...source.complexScriptAlternateFontFamily !== void 0 ? { alternateFontFamily: source.complexScriptAlternateFontFamily } : {},
|
|
5
6
|
...source.complexScriptFontSize !== void 0 ? { fontSize: source.complexScriptFontSize } : {},
|
|
6
7
|
...source.complexScriptBold !== void 0 ? { bold: source.complexScriptBold } : {},
|
|
7
8
|
...source.complexScriptItalic !== void 0 ? { italic: source.complexScriptItalic } : {}
|
|
8
9
|
});
|
|
9
|
-
const
|
|
10
|
+
const applyComplexScriptFormatting = (base, source) => {
|
|
11
|
+
const result = {
|
|
12
|
+
...base,
|
|
13
|
+
...resolveComplexScriptFormatting(source)
|
|
14
|
+
};
|
|
15
|
+
if (source.complexScriptFontFamily !== void 0 && source.complexScriptAlternateFontFamily === void 0) delete result.alternateFontFamily;
|
|
16
|
+
return result;
|
|
17
|
+
};
|
|
18
|
+
const hasComplexScriptFormatting = (source) => source.complexScriptFontFamily !== void 0 || source.complexScriptAlternateFontFamily !== void 0 || source.complexScriptFontSize !== void 0 || source.complexScriptBold !== void 0 || source.complexScriptItalic !== void 0;
|
|
10
19
|
//#endregion
|
|
11
|
-
export { hasComplexScriptFormatting, resolveComplexScriptFormatting };
|
|
20
|
+
export { applyComplexScriptFormatting, hasComplexScriptFormatting, resolveComplexScriptFormatting };
|
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
import { FloatingTablePosition, TableBlock } from "../types.js";
|
|
2
2
|
//#region src/layout-engine/measure/floatingTablePosition.d.ts
|
|
3
|
+
type FloatingTablePageXOptions = {
|
|
4
|
+
anchor: FloatingTablePosition;
|
|
5
|
+
justification: TableBlock["justification"];
|
|
6
|
+
tableWidth: number;
|
|
7
|
+
marginWidth: number;
|
|
8
|
+
pageWidth: number;
|
|
9
|
+
marginLeft: number;
|
|
10
|
+
textFrameWidth: number;
|
|
11
|
+
textFrameLeft: number;
|
|
12
|
+
};
|
|
3
13
|
/**
|
|
4
14
|
* X of the table's left edge relative to the content-box left edge, px.
|
|
5
15
|
*/
|
|
6
16
|
declare function resolveFloatingTableX(anchor: FloatingTablePosition, justification: TableBlock["justification"], tableWidth: number, contentWidth: number): number;
|
|
17
|
+
/**
|
|
18
|
+
* Absolute page X after resolving the selected anchor frame and constraining
|
|
19
|
+
* the table to the physical page.
|
|
20
|
+
*/
|
|
21
|
+
declare function resolveFloatingTablePageX({ anchor, justification, tableWidth, marginWidth, pageWidth, marginLeft, textFrameWidth, textFrameLeft }: FloatingTablePageXOptions): number;
|
|
7
22
|
//#endregion
|
|
8
|
-
export { resolveFloatingTableX };
|
|
23
|
+
export { resolveFloatingTablePageX, resolveFloatingTableX };
|
|
@@ -3,15 +3,38 @@
|
|
|
3
3
|
* X of the table's left edge relative to the content-box left edge, px.
|
|
4
4
|
*/
|
|
5
5
|
function resolveFloatingTableX(anchor, justification, tableWidth, contentWidth) {
|
|
6
|
-
const clamp = (x) => Math.max(0, x);
|
|
7
6
|
const spec = anchor.tblpXSpec;
|
|
8
7
|
if (spec === "left" || spec === "inside") return 0;
|
|
9
|
-
if (spec === "right" || spec === "outside") return
|
|
10
|
-
if (spec === "center") return
|
|
8
|
+
if (spec === "right" || spec === "outside") return contentWidth - tableWidth;
|
|
9
|
+
if (spec === "center") return (contentWidth - tableWidth) / 2;
|
|
11
10
|
if (anchor.tblpX !== void 0) return anchor.tblpX;
|
|
12
|
-
if (justification === "center") return
|
|
13
|
-
if (justification === "right") return
|
|
11
|
+
if (justification === "center") return (contentWidth - tableWidth) / 2;
|
|
12
|
+
if (justification === "right") return contentWidth - tableWidth;
|
|
14
13
|
return 0;
|
|
15
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Absolute page X after resolving the selected anchor frame and constraining
|
|
17
|
+
* the table to the physical page.
|
|
18
|
+
*/
|
|
19
|
+
function resolveFloatingTablePageX({ anchor, justification, tableWidth, marginWidth, pageWidth, marginLeft, textFrameWidth, textFrameLeft }) {
|
|
20
|
+
let frameWidth = marginWidth;
|
|
21
|
+
let frameLeft = marginLeft;
|
|
22
|
+
switch (anchor.horzAnchor) {
|
|
23
|
+
case "page":
|
|
24
|
+
frameWidth = pageWidth;
|
|
25
|
+
frameLeft = 0;
|
|
26
|
+
break;
|
|
27
|
+
case "text":
|
|
28
|
+
frameWidth = textFrameWidth;
|
|
29
|
+
frameLeft = textFrameLeft;
|
|
30
|
+
break;
|
|
31
|
+
case "margin":
|
|
32
|
+
case void 0: break;
|
|
33
|
+
default: anchor.horzAnchor;
|
|
34
|
+
}
|
|
35
|
+
const resolvedX = frameLeft + resolveFloatingTableX(anchor, justification, tableWidth, frameWidth);
|
|
36
|
+
const maxX = Math.max(0, pageWidth - tableWidth);
|
|
37
|
+
return Math.max(0, Math.min(resolvedX, maxX));
|
|
38
|
+
}
|
|
16
39
|
//#endregion
|
|
17
|
-
export { resolveFloatingTableX };
|
|
40
|
+
export { resolveFloatingTablePageX, resolveFloatingTableX };
|
|
@@ -16,11 +16,13 @@ const { hyphenateSync: hyphenateBritishEnglish } = britishEnglishHyphenation;
|
|
|
16
16
|
const { hyphenateSync: hyphenateAmericanEnglish } = americanEnglishHyphenation;
|
|
17
17
|
const { hyphenateSync: hyphenateSlovak } = slovakHyphenation;
|
|
18
18
|
const SEGMENTER_CACHE_LIMIT = 16;
|
|
19
|
+
const LINE_BREAK_LOCALE_CACHE_LIMIT = 16;
|
|
19
20
|
const DEFAULT_SEGMENTER_KEY = "";
|
|
20
21
|
const SOFT_HYPHEN = "";
|
|
21
22
|
const MAX_HYPHENATION_WORD_LENGTH = 256;
|
|
22
23
|
const wordSegmenters = /* @__PURE__ */ new Map();
|
|
23
24
|
const graphemeSegmenters = /* @__PURE__ */ new Map();
|
|
25
|
+
const eastAsianLineBreakLocales = /* @__PURE__ */ new Map();
|
|
24
26
|
const segmenterLocale = (locale) => {
|
|
25
27
|
const language = locale?.trim().replaceAll("_", "-").split("-").at(0);
|
|
26
28
|
return language && /^[a-z]{2,3}$/iu.test(language) ? language.toLowerCase() : void 0;
|
|
@@ -28,11 +30,19 @@ const segmenterLocale = (locale) => {
|
|
|
28
30
|
const DICTIONARY_BREAK_SCRIPT = /[\p{Script=Thai}\p{Script=Lao}\p{Script=Khmer}\p{Script=Myanmar}]/u;
|
|
29
31
|
const CJK_SCRIPT = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/u;
|
|
30
32
|
const WORD_HANGING_PUNCTUATION_LANGUAGES = /* @__PURE__ */ new Set([
|
|
33
|
+
"cmn",
|
|
31
34
|
"ii",
|
|
32
35
|
"ja",
|
|
33
36
|
"ko",
|
|
34
37
|
"zh"
|
|
35
38
|
]);
|
|
39
|
+
const EAST_ASIAN_LINE_BREAK_SCRIPTS = /* @__PURE__ */ new Set([
|
|
40
|
+
"Hans",
|
|
41
|
+
"Hant",
|
|
42
|
+
"Jpan",
|
|
43
|
+
"Kore",
|
|
44
|
+
"Yiii"
|
|
45
|
+
]);
|
|
36
46
|
const SIMPLE_BREAK_TEXT = /^[\p{Script=Latin}\p{Script=Cyrillic}\p{Script=Greek}\p{Number}\p{Punctuation}\p{White_Space}]*$/u;
|
|
37
47
|
const BREAK_AFTER_CHARACTER = /* @__PURE__ */ new Set([
|
|
38
48
|
"-",
|
|
@@ -81,6 +91,7 @@ const DEFAULT_PROHIBITED_LINE_START = /* @__PURE__ */ new Set([
|
|
|
81
91
|
":",
|
|
82
92
|
";",
|
|
83
93
|
"!",
|
|
94
|
+
"%",
|
|
84
95
|
"?",
|
|
85
96
|
"…",
|
|
86
97
|
"’",
|
|
@@ -136,14 +147,34 @@ const isLegacyEthiopicBreakCharacter = (character, policy) => {
|
|
|
136
147
|
const codePoint = character.codePointAt(0);
|
|
137
148
|
return codePoint !== void 0 && codePoint >= 4961 && codePoint <= 4968;
|
|
138
149
|
};
|
|
139
|
-
const
|
|
150
|
+
const localeUsesEastAsianLineBreaking = (locale) => {
|
|
151
|
+
const language = segmenterLocale(locale);
|
|
152
|
+
if (language === void 0) return;
|
|
153
|
+
const normalized = locale?.trim().replaceAll("_", "-").toLowerCase() ?? language;
|
|
154
|
+
const cached = eastAsianLineBreakLocales.get(normalized);
|
|
155
|
+
if (cached !== void 0) return cached;
|
|
156
|
+
const subtags = normalized.split("-");
|
|
157
|
+
const extensionIndex = subtags.findIndex((part, index) => index > 0 && part.length === 1);
|
|
158
|
+
const explicitScript = subtags.slice(1, extensionIndex === -1 ? void 0 : extensionIndex).find((part) => /^[a-z]{4}$/u.test(part));
|
|
159
|
+
const script = explicitScript ? `${explicitScript[0]?.toUpperCase()}${explicitScript.slice(1)}` : new Intl.Locale(language).maximize().script;
|
|
160
|
+
const result = WORD_HANGING_PUNCTUATION_LANGUAGES.has(language) || script !== void 0 && EAST_ASIAN_LINE_BREAK_SCRIPTS.has(script);
|
|
161
|
+
if (eastAsianLineBreakLocales.size >= LINE_BREAK_LOCALE_CACHE_LIMIT) {
|
|
162
|
+
const oldestKey = eastAsianLineBreakLocales.keys().next().value;
|
|
163
|
+
if (typeof oldestKey === "string") eastAsianLineBreakLocales.delete(oldestKey);
|
|
164
|
+
}
|
|
165
|
+
eastAsianLineBreakLocales.set(normalized, result);
|
|
166
|
+
return result;
|
|
167
|
+
};
|
|
168
|
+
const usesEastAsianLineBreaking = (text, locale) => localeUsesEastAsianLineBreaking(locale) ?? CJK_SCRIPT.test(text);
|
|
169
|
+
const allowsBreak = (text, index, policy, usesEastAsianRules, nextLineStart) => {
|
|
140
170
|
const previous = previousCodePoint(text, index);
|
|
141
|
-
const next = firstCodePoint(text, index);
|
|
171
|
+
const next = nextLineStart ?? firstCodePoint(text, index);
|
|
172
|
+
if (previous === " " && next === "%" && policy?.kinsoku !== true && policy?.noLineBreaksBefore === void 0 && !usesEastAsianRules) return !isProhibitedLineEnd(previous, policy);
|
|
142
173
|
return !isProhibitedLineEnd(previous, policy) && !isProhibitedLineStart(next, policy);
|
|
143
174
|
};
|
|
144
|
-
const pushBreak = (breaks, text, index, policy) => {
|
|
175
|
+
const pushBreak = (breaks, text, index, policy, usesEastAsianRules, nextLineStart) => {
|
|
145
176
|
if (index <= 0 || index > text.length || breaks.at(-1) === index) return;
|
|
146
|
-
if (allowsBreak(text, index, policy)) breaks.push(index);
|
|
177
|
+
if (allowsBreak(text, index, policy, usesEastAsianRules, nextLineStart)) breaks.push(index);
|
|
147
178
|
};
|
|
148
179
|
const findSimpleGraphemeBreaks = (text) => {
|
|
149
180
|
if (!SIMPLE_BREAK_TEXT.test(text)) return;
|
|
@@ -180,12 +211,26 @@ const isSimpleWhitespace = (character, codePoint) => codePoint === 32 || codePoi
|
|
|
180
211
|
const findSimpleBreaks = (text, policy) => {
|
|
181
212
|
if (!SIMPLE_BREAK_TEXT.test(text)) return;
|
|
182
213
|
const breaks = [];
|
|
214
|
+
const usesEastAsianRules = usesEastAsianLineBreaking(text, policy?.locale);
|
|
215
|
+
let whitespaceRunEnd = 0;
|
|
216
|
+
let nextVisibleAfterWhitespace;
|
|
183
217
|
for (let index = 0; index < text.length; index += 1) {
|
|
184
218
|
const codePoint = text.charCodeAt(index);
|
|
185
219
|
if (codePoint >= 55296 && codePoint <= 57343) return;
|
|
186
220
|
if (codePoint === 13 && text.charCodeAt(index + 1) === 10) continue;
|
|
187
221
|
const character = text[index];
|
|
188
|
-
if (isSimpleWhitespace(character, codePoint) && !NONBREAKING_SPACES.has(character)
|
|
222
|
+
if (isSimpleWhitespace(character, codePoint) && !NONBREAKING_SPACES.has(character)) {
|
|
223
|
+
if (index >= whitespaceRunEnd) {
|
|
224
|
+
whitespaceRunEnd = index + 1;
|
|
225
|
+
let next = firstCodePoint(text, whitespaceRunEnd);
|
|
226
|
+
while (next !== void 0 && /\s/u.test(next) && !NONBREAKING_SPACES.has(next)) {
|
|
227
|
+
whitespaceRunEnd += next.length;
|
|
228
|
+
next = firstCodePoint(text, whitespaceRunEnd);
|
|
229
|
+
}
|
|
230
|
+
nextVisibleAfterWhitespace = next;
|
|
231
|
+
}
|
|
232
|
+
pushBreak(breaks, text, index + 1, policy, usesEastAsianRules, nextVisibleAfterWhitespace);
|
|
233
|
+
} else if (BREAK_AFTER_CHARACTER.has(character) || isLegacyEthiopicBreakCharacter(character, policy)) pushBreak(breaks, text, index + 1, policy, usesEastAsianRules);
|
|
189
234
|
}
|
|
190
235
|
return breaks;
|
|
191
236
|
};
|
|
@@ -194,20 +239,34 @@ const findUnicodeBreaks = (text, policy) => {
|
|
|
194
239
|
const simpleBreaks = findSimpleBreaks(text, policy);
|
|
195
240
|
if (simpleBreaks !== void 0) return simpleBreaks;
|
|
196
241
|
const breaks = [];
|
|
242
|
+
const usesEastAsianRules = usesEastAsianLineBreaking(text, policy?.locale);
|
|
197
243
|
const graphemeBreaks = findUnicodeGraphemeBreaks(text, policy);
|
|
244
|
+
let whitespaceRunEnd = 0;
|
|
245
|
+
let nextVisibleAfterWhitespace;
|
|
198
246
|
for (const index of graphemeBreaks) {
|
|
199
247
|
const previous = previousCodePoint(text, index);
|
|
200
|
-
if (previous !== void 0 &&
|
|
248
|
+
if (previous !== void 0 && /\s/u.test(previous) && !NONBREAKING_SPACES.has(previous)) {
|
|
249
|
+
if (index > whitespaceRunEnd) {
|
|
250
|
+
whitespaceRunEnd = index;
|
|
251
|
+
let next = firstCodePoint(text, whitespaceRunEnd);
|
|
252
|
+
while (next !== void 0 && /\s/u.test(next) && !NONBREAKING_SPACES.has(next)) {
|
|
253
|
+
whitespaceRunEnd += next.length;
|
|
254
|
+
next = firstCodePoint(text, whitespaceRunEnd);
|
|
255
|
+
}
|
|
256
|
+
nextVisibleAfterWhitespace = next;
|
|
257
|
+
}
|
|
258
|
+
pushBreak(breaks, text, index, policy, usesEastAsianRules, nextVisibleAfterWhitespace);
|
|
259
|
+
} else if (previous !== void 0 && (BREAK_AFTER_CHARACTER.has(previous) || isLegacyEthiopicBreakCharacter(previous, policy))) pushBreak(breaks, text, index, policy, usesEastAsianRules);
|
|
201
260
|
}
|
|
202
261
|
const wordSegments = [...getSegmenter(wordSegmenters, "word", policy?.locale).segment(text)];
|
|
203
262
|
for (const [segmentIndex, segment] of wordSegments.entries()) {
|
|
204
263
|
const end = segment.index + segment.segment.length;
|
|
205
264
|
const nextSegment = wordSegments[segmentIndex + 1];
|
|
206
|
-
if (segment.isWordLike && nextSegment?.isWordLike === true && DICTIONARY_BREAK_SCRIPT.test(segment.segment + nextSegment.segment)) pushBreak(breaks, text, end, policy);
|
|
265
|
+
if (segment.isWordLike && nextSegment?.isWordLike === true && DICTIONARY_BREAK_SCRIPT.test(segment.segment + nextSegment.segment)) pushBreak(breaks, text, end, policy, usesEastAsianRules);
|
|
207
266
|
}
|
|
208
267
|
if (CJK_SCRIPT.test(text)) for (const index of graphemeBreaks) {
|
|
209
268
|
const previous = previousCodePoint(text, index);
|
|
210
|
-
if (isCjkLineBreakParticipant(previous, policy)) pushBreak(breaks, text, index, policy);
|
|
269
|
+
if (isCjkLineBreakParticipant(previous, policy)) pushBreak(breaks, text, index, policy, usesEastAsianRules);
|
|
211
270
|
}
|
|
212
271
|
return [...new Set(breaks)].sort((left, right) => left - right);
|
|
213
272
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { hasCjk, hasComplexScript } from "../../utils/scriptSegments.js";
|
|
2
|
-
import {
|
|
2
|
+
import { applyComplexScriptFormatting, hasComplexScriptFormatting } from "./complexScriptFormatting.js";
|
|
3
3
|
import { ptToPx } from "./measureHelpers.js";
|
|
4
4
|
import { measureTextWidth } from "./measureProvider.js";
|
|
5
5
|
//#region src/layout-engine/measure/listMarkerWidth.ts
|
|
@@ -24,22 +24,35 @@ function resolveListMarkerFont(block) {
|
|
|
24
24
|
const markerFormatting = attrs?.listMarkerFormatting;
|
|
25
25
|
const bold = markerFormatting?.bold ?? firstTextRun?.bold;
|
|
26
26
|
const italic = markerFormatting?.italic ?? firstTextRun?.italic;
|
|
27
|
+
let fontFamily = attrs?.defaultFontFamily ?? DEFAULT_FONT_FAMILY;
|
|
28
|
+
let alternateFontFamily = attrs?.defaultAlternateFontFamily;
|
|
29
|
+
if (firstTextRun?.fontFamily !== void 0) {
|
|
30
|
+
fontFamily = firstTextRun.fontFamily;
|
|
31
|
+
alternateFontFamily = firstTextRun.alternateFontFamily;
|
|
32
|
+
}
|
|
33
|
+
if (markerFormatting?.fontFamily !== void 0) {
|
|
34
|
+
fontFamily = markerFormatting.fontFamily;
|
|
35
|
+
alternateFontFamily = markerFormatting.alternateFontFamily;
|
|
36
|
+
}
|
|
27
37
|
const base = {
|
|
28
|
-
fontFamily
|
|
38
|
+
fontFamily,
|
|
39
|
+
...alternateFontFamily !== void 0 ? { alternateFontFamily } : {},
|
|
29
40
|
fontSize: markerFormatting?.fontSize ?? firstTextRun?.fontSize ?? attrs?.defaultFontSize ?? DEFAULT_FONT_SIZE,
|
|
30
41
|
...bold !== void 0 ? { bold } : {},
|
|
31
42
|
...italic !== void 0 ? { italic } : {},
|
|
32
43
|
...markerFormatting?.rtl !== void 0 ? { rtl: markerFormatting.rtl } : {}
|
|
33
44
|
};
|
|
34
45
|
const marker = attrs?.listMarker ?? "";
|
|
35
|
-
if (markerFormatting && hasComplexScriptFormatting(markerFormatting) && (markerFormatting.forceComplexScript || hasComplexScript(marker))) return
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
46
|
+
if (markerFormatting && hasComplexScriptFormatting(markerFormatting) && (markerFormatting.forceComplexScript || hasComplexScript(marker))) return applyComplexScriptFormatting(base, markerFormatting);
|
|
47
|
+
if (markerFormatting?.eastAsiaFontFamily && hasCjk(marker)) {
|
|
48
|
+
const result = {
|
|
49
|
+
...base,
|
|
50
|
+
fontFamily: markerFormatting.eastAsiaFontFamily
|
|
51
|
+
};
|
|
52
|
+
delete result.alternateFontFamily;
|
|
53
|
+
if (markerFormatting.eastAsiaAlternateFontFamily !== void 0) result.alternateFontFamily = markerFormatting.eastAsiaAlternateFontFamily;
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
43
56
|
return base;
|
|
44
57
|
}
|
|
45
58
|
/**
|