@stll/folio-core 0.29.0 → 0.31.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.
Files changed (103) hide show
  1. package/dist/controller/folioEditor.d.ts +28 -1
  2. package/dist/controller/folioEditor.js +37 -26
  3. package/dist/controller/fontReadiness.js +28 -24
  4. package/dist/controller/hiddenEditorManager.d.ts +2 -7
  5. package/dist/controller/layoutPipeline.js +33 -3
  6. package/dist/controller/noteEditorManager.d.ts +3 -6
  7. package/dist/docx/metafileSvg.d.ts +12 -0
  8. package/dist/docx/metafileSvg.js +462 -0
  9. package/dist/docx/paragraphTextBoxEnrichment.js +55 -4
  10. package/dist/docx/parser.d.ts +2 -2
  11. package/dist/docx/parser.js +26 -0
  12. package/dist/docx/runParser.js +2 -1
  13. package/dist/docx/serializer/runSerializer.js +14 -2
  14. package/dist/docx/server/createBilingualDocument.d.ts +20 -3
  15. package/dist/docx/server/createBilingualDocument.js +153 -22
  16. package/dist/docx/styleParser.js +2 -1
  17. package/dist/docx/textBoxParser.js +14 -0
  18. package/dist/docx/vmlImageParser.js +72 -152
  19. package/dist/docx/vmlPreview.d.ts +31 -0
  20. package/dist/docx/vmlPreview.js +535 -0
  21. package/dist/docx/wrapTypes.d.ts +2 -36
  22. package/dist/docx/wrapTypes.js +1 -39
  23. package/dist/fonts/fontAlternates.d.ts +8 -0
  24. package/dist/fonts/fontAlternates.js +16 -0
  25. package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
  26. package/dist/layout-bridge/convert/footnoteLayout.js +34 -3
  27. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
  28. package/dist/layout-bridge/convert/headerFooterLayout.js +5 -0
  29. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +3 -0
  30. package/dist/layout-bridge/convert/toFlowBlocks.js +78 -28
  31. package/dist/layout-bridge/dom/noteStoryDom.d.ts +1 -1
  32. package/dist/layout-bridge/engine/selectionRects.js +3 -1
  33. package/dist/layout-engine/index.js +65 -32
  34. package/dist/layout-engine/justifiedLineFit.d.ts +7 -0
  35. package/dist/layout-engine/justifiedLineFit.js +6 -0
  36. package/dist/layout-engine/keep-together.js +15 -10
  37. package/dist/layout-engine/measure/complexScriptFormatting.d.ts +6 -1
  38. package/dist/layout-engine/measure/complexScriptFormatting.js +11 -2
  39. package/dist/layout-engine/measure/floatingTablePosition.d.ts +16 -1
  40. package/dist/layout-engine/measure/floatingTablePosition.js +29 -6
  41. package/dist/layout-engine/measure/lineBreakProvider.js +67 -8
  42. package/dist/layout-engine/measure/listMarkerWidth.d.ts +1 -0
  43. package/dist/layout-engine/measure/listMarkerWidth.js +23 -10
  44. package/dist/layout-engine/measure/measureBlocks.d.ts +15 -0
  45. package/dist/layout-engine/measure/measureBlocks.js +197 -30
  46. package/dist/layout-engine/measure/measureContainer.js +20 -24
  47. package/dist/layout-engine/measure/measureHelpers.d.ts +1 -1
  48. package/dist/layout-engine/measure/measureHelpers.js +14 -8
  49. package/dist/layout-engine/measure/measureParagraph.js +111 -37
  50. package/dist/layout-engine/measure/measureTypes.d.ts +3 -0
  51. package/dist/layout-engine/measure/tabCalculator.d.ts +123 -0
  52. package/dist/layout-engine/measure/tabCalculator.js +144 -0
  53. package/dist/layout-engine/measure/tableCellFlow.d.ts +21 -1
  54. package/dist/layout-engine/measure/tableCellFlow.js +45 -8
  55. package/dist/layout-engine/measure/tableInlinePlacement.d.ts +10 -2
  56. package/dist/layout-engine/measure/tableInlinePlacement.js +17 -15
  57. package/dist/layout-engine/paginator.d.ts +26 -2
  58. package/dist/layout-engine/paginator.js +76 -14
  59. package/dist/layout-engine/paragraphSpacing.d.ts +3 -1
  60. package/dist/layout-engine/paragraphSpacing.js +4 -3
  61. package/dist/layout-engine/renderedBreakReconciliation.d.ts +14 -3
  62. package/dist/layout-engine/renderedBreakReconciliation.js +45 -14
  63. package/dist/layout-engine/section-breaks.d.ts +3 -1
  64. package/dist/layout-engine/section-breaks.js +6 -2
  65. package/dist/layout-engine/types.d.ts +18 -1
  66. package/dist/layout-engine/types.js +1 -1
  67. package/dist/layout-painter/imageLayout.d.ts +1 -1
  68. package/dist/layout-painter/renderPage.js +6 -1
  69. package/dist/layout-painter/renderParagraph.js +169 -82
  70. package/dist/layout-painter/renderTable.d.ts +1 -1
  71. package/dist/layout-painter/renderTable.js +18 -7
  72. package/dist/layout-painter/renderTextBox.d.ts +1 -1
  73. package/dist/layout-painter/renderTextBox.js +6 -1
  74. package/dist/model.d.ts +3 -3
  75. package/dist/model.js +2 -2
  76. package/dist/paged-layout/sectionBlockWidths.d.ts +14 -0
  77. package/dist/paged-layout/sectionBlockWidths.js +103 -14
  78. package/dist/prosemirror/attrs/index.js +12 -1
  79. package/dist/prosemirror/conversion/fromProseDoc.js +6 -2
  80. package/dist/prosemirror/conversion/toProseDoc.js +10 -3
  81. package/dist/prosemirror/extensions/marks/CharacterSpacingExtension.js +9 -5
  82. package/dist/prosemirror/extensions/nodes/TableExtension.js +2 -0
  83. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +6 -2
  84. package/dist/prosemirror/schema/nodes.d.ts +4 -0
  85. package/dist/prosemirror/utils/tabCalculator.d.ts +2 -123
  86. package/dist/prosemirror/utils/tabCalculator.js +1 -140
  87. package/dist/render-dom/RemoteSelectionOverlay.d.ts +4 -4
  88. package/dist/server.d.ts +2 -2
  89. package/dist/server.js +2 -2
  90. package/dist/types/documentEnumValues.d.ts +4 -1
  91. package/dist/types/documentEnumValues.js +12 -1
  92. package/dist/types/editor-story.d.ts +8 -0
  93. package/dist/types/editor-story.js +0 -0
  94. package/dist/types/remote-selection.d.ts +11 -0
  95. package/dist/types/remote-selection.js +0 -0
  96. package/dist/types/wrap.d.ts +36 -0
  97. package/dist/types/wrap.js +40 -0
  98. package/dist/utils/fontResolver.d.ts +1 -1
  99. package/dist/utils/fontResolver.js +42 -2
  100. package/dist/utils/formatToStyle.js +4 -2
  101. package/dist/utils/horizontalScale.d.ts +28 -0
  102. package/dist/utils/horizontalScale.js +42 -0
  103. package/package.json +2 -2
@@ -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 { resolveFloatingTableX } from "./measure/floatingTablePosition.js";
7
- import { resolveTableInlinePlacement } from "./measure/tableInlinePlacement.js";
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 (breakDecision.forcePageBreak) paginator.forcePageBreak();
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] ?? DEFAULT_SECTION_BREAK_TYPE, sectionIdx + 1);
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
- const x = paginator.getColumnX(columnIndex);
544
- const placement = resolveTableInlinePlacement(block);
545
- if (placement.alignment === "center") return x + (paginator.columnWidth - measure.totalWidth) / 2;
546
- if (placement.alignment === "right") return x + paginator.columnWidth - measure.totalWidth - placement.offset;
547
- return x + placement.offset;
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 && previous.x === computeTableX(state.columnIndex);
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(sliceState.columnIndex),
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(sliceResult.state.columnIndex);
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 = computeTableX(state.columnIndex);
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 = baseX + resolveFloatingTableX(floating, block.justification, tableWidth, contentWidth);
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(nextSectionIndex, nextSectionConfig.pageNumbering);
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(nextSectionIndex, nextSectionConfig.pageNumbering);
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(nextSectionIndex, nextSectionConfig.pageNumbering);
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(nextSectionIndex, nextSectionConfig.pageNumbering);
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
- function isVisuallyEmptyParagraph(block) {
10
- if (block.runs.length === 0) return true;
11
- if (block.runs.length !== 1) return false;
12
- const run = block.runs.at(0);
13
- return run?.kind === "text" && run.text === "";
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" || !isVisuallyEmptyParagraph(block) || blocks[index - 1]?.kind !== "table") return false;
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 (!isVisuallyEmptyParagraph(nextBlock)) return true;
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 || isVisuallyEmptyParagraph(nextPara)) {
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" && isVisuallyEmptyParagraph(firstBlock) && !firstBlock.attrs?.keepNext;
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 hasComplexScriptFormatting = (source) => source.complexScriptFontFamily !== void 0 || source.complexScriptFontSize !== void 0 || source.complexScriptBold !== void 0 || source.complexScriptItalic !== void 0;
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 clamp(contentWidth - tableWidth);
10
- if (spec === "center") return clamp((contentWidth - tableWidth) / 2);
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 clamp((contentWidth - tableWidth) / 2);
13
- if (justification === "right") return clamp(contentWidth - tableWidth);
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 allowsBreak = (text, index, policy) => {
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) || BREAK_AFTER_CHARACTER.has(character) || isLegacyEthiopicBreakCharacter(character, policy)) pushBreak(breaks, text, index + 1, policy);
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 && (/\s/u.test(previous) && !NONBREAKING_SPACES.has(previous) || BREAK_AFTER_CHARACTER.has(previous) || isLegacyEthiopicBreakCharacter(previous, policy))) pushBreak(breaks, text, index, policy);
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
  };
@@ -14,6 +14,7 @@ declare const DEFAULT_TAB_STOP_TWIPS = 720;
14
14
  */
15
15
  declare function resolveListMarkerFont(block: ParagraphBlock): {
16
16
  fontFamily: string;
17
+ alternateFontFamily?: string;
17
18
  fontSize: number;
18
19
  bold?: boolean;
19
20
  italic?: boolean;
@@ -1,5 +1,5 @@
1
1
  import { hasCjk, hasComplexScript } from "../../utils/scriptSegments.js";
2
- import { hasComplexScriptFormatting, resolveComplexScriptFormatting } from "./complexScriptFormatting.js";
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: markerFormatting?.fontFamily ?? firstTextRun?.fontFamily ?? attrs?.defaultFontFamily ?? DEFAULT_FONT_FAMILY,
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
- ...base,
37
- ...resolveComplexScriptFormatting(markerFormatting)
38
- };
39
- if (markerFormatting?.eastAsiaFontFamily && hasCjk(marker)) return {
40
- ...base,
41
- fontFamily: markerFormatting.eastAsiaFontFamily
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
  /**
@@ -3,12 +3,27 @@ import { FloatingImageZone } from "./floatingZones.js";
3
3
  import "./measureParagraph.js";
4
4
  //#region src/layout-engine/measure/measureBlocks.d.ts
5
5
  declare function measureTableBlock(tableBlock: TableBlock, contentWidth: number, fieldValues?: ReadonlyMap<number, string>): TableMeasure;
6
+ type PhysicalBandPageGeometry = {
7
+ pageWidth: number | number[];
8
+ pageHeight: number | number[];
9
+ marginTop: number | number[];
10
+ marginLeft: number | number[];
11
+ marginRight: number | number[];
12
+ marginBottom: number | number[];
13
+ };
6
14
  type BandPageGeometry = {
7
15
  pageWidth?: number | number[];
8
16
  pageHeight: number | number[];
9
17
  marginLeft?: number | number[];
10
18
  marginRight?: number | number[];
11
19
  marginBottom: number | number[];
20
+ /** Absolute page X of each block's active column origin. */
21
+ contentLeft?: number | number[];
22
+ physicalPage?: PhysicalBandPageGeometry;
23
+ /** Authored flow-column index occupied by each block. */
24
+ columnIndex?: number | number[];
25
+ /** Authored flow-column count for each block's section. */
26
+ columnCount?: number | number[];
12
27
  };
13
28
  /**
14
29
  * Measure a block based on its type.