@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.
Files changed (125) 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/styleParser.js +2 -1
  15. package/dist/docx/textBoxParser.js +14 -0
  16. package/dist/docx/vmlImageParser.js +72 -152
  17. package/dist/docx/vmlPreview.d.ts +31 -0
  18. package/dist/docx/vmlPreview.js +535 -0
  19. package/dist/docx/wrapTypes.d.ts +2 -36
  20. package/dist/docx/wrapTypes.js +1 -39
  21. package/dist/fonts/fontAlternates.d.ts +8 -0
  22. package/dist/fonts/fontAlternates.js +16 -0
  23. package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
  24. package/dist/layout-bridge/convert/footnoteLayout.js +34 -3
  25. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
  26. package/dist/layout-bridge/convert/headerFooterLayout.js +5 -0
  27. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +4 -2
  28. package/dist/layout-bridge/convert/toFlowBlocks.js +136 -120
  29. package/dist/layout-bridge/dom/noteStoryDom.d.ts +1 -1
  30. package/dist/layout-bridge/engine/selectionRects.js +3 -1
  31. package/dist/layout-engine/index.js +65 -32
  32. package/dist/layout-engine/justifiedLineFit.d.ts +7 -0
  33. package/dist/layout-engine/justifiedLineFit.js +6 -0
  34. package/dist/layout-engine/keep-together.js +15 -10
  35. package/dist/layout-engine/measure/complexScriptFormatting.d.ts +6 -1
  36. package/dist/layout-engine/measure/complexScriptFormatting.js +11 -2
  37. package/dist/layout-engine/measure/floatingTablePosition.d.ts +16 -1
  38. package/dist/layout-engine/measure/floatingTablePosition.js +29 -6
  39. package/dist/layout-engine/measure/lineBreakProvider.js +67 -8
  40. package/dist/layout-engine/measure/listMarkerWidth.d.ts +1 -0
  41. package/dist/layout-engine/measure/listMarkerWidth.js +23 -10
  42. package/dist/layout-engine/measure/measureBlocks.d.ts +15 -0
  43. package/dist/layout-engine/measure/measureBlocks.js +197 -30
  44. package/dist/layout-engine/measure/measureContainer.js +20 -24
  45. package/dist/layout-engine/measure/measureHelpers.d.ts +1 -1
  46. package/dist/layout-engine/measure/measureHelpers.js +14 -8
  47. package/dist/layout-engine/measure/measureParagraph.js +111 -37
  48. package/dist/layout-engine/measure/measureTypes.d.ts +3 -0
  49. package/dist/layout-engine/measure/tabCalculator.d.ts +123 -0
  50. package/dist/layout-engine/measure/tabCalculator.js +144 -0
  51. package/dist/layout-engine/measure/tableCellFlow.d.ts +21 -1
  52. package/dist/layout-engine/measure/tableCellFlow.js +45 -8
  53. package/dist/layout-engine/measure/tableInlinePlacement.d.ts +10 -2
  54. package/dist/layout-engine/measure/tableInlinePlacement.js +17 -15
  55. package/dist/layout-engine/paginator.d.ts +26 -2
  56. package/dist/layout-engine/paginator.js +76 -14
  57. package/dist/layout-engine/paragraphSpacing.d.ts +3 -1
  58. package/dist/layout-engine/paragraphSpacing.js +4 -3
  59. package/dist/layout-engine/renderedBreakReconciliation.d.ts +14 -3
  60. package/dist/layout-engine/renderedBreakReconciliation.js +45 -14
  61. package/dist/layout-engine/section-breaks.d.ts +3 -1
  62. package/dist/layout-engine/section-breaks.js +6 -2
  63. package/dist/layout-engine/types.d.ts +18 -1
  64. package/dist/layout-engine/types.js +1 -1
  65. package/dist/layout-painter/imageLayout.d.ts +1 -1
  66. package/dist/layout-painter/renderPage.js +6 -1
  67. package/dist/layout-painter/renderParagraph.js +169 -82
  68. package/dist/layout-painter/renderTable.d.ts +1 -1
  69. package/dist/layout-painter/renderTable.js +18 -7
  70. package/dist/layout-painter/renderTextBox.d.ts +1 -1
  71. package/dist/layout-painter/renderTextBox.js +6 -1
  72. package/dist/markdown/renderParagraph.js +7 -2
  73. package/dist/model.d.ts +3 -3
  74. package/dist/model.js +2 -2
  75. package/dist/paged-layout/sectionBlockWidths.d.ts +14 -0
  76. package/dist/paged-layout/sectionBlockWidths.js +103 -14
  77. package/dist/prosemirror/attrs/index.js +14 -2
  78. package/dist/prosemirror/bookmarkBoundaryAttrs.d.ts +8 -0
  79. package/dist/prosemirror/bookmarkBoundaryAttrs.js +66 -0
  80. package/dist/prosemirror/conversion/fromProseDoc.js +176 -54
  81. package/dist/prosemirror/conversion/toProseDoc.js +179 -64
  82. package/dist/prosemirror/extensions/StarterKit.js +11 -3
  83. package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +8 -4
  84. package/dist/prosemirror/extensions/features/PasteCleanupExtension.d.ts +4 -1
  85. package/dist/prosemirror/extensions/features/PasteCleanupExtension.js +9 -5
  86. package/dist/prosemirror/extensions/features/pasteCleanup.d.ts +10 -23
  87. package/dist/prosemirror/extensions/features/pasteCleanup.js +77 -22
  88. package/dist/prosemirror/extensions/marks/CharacterSpacingExtension.js +9 -5
  89. package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.d.ts +9 -0
  90. package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.js +67 -0
  91. package/dist/prosemirror/extensions/nodes/FieldExtension.d.ts +10 -4
  92. package/dist/prosemirror/extensions/nodes/FieldExtension.js +77 -59
  93. package/dist/prosemirror/extensions/nodes/TableExtension.js +2 -0
  94. package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.d.ts +4 -1
  95. package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.js +4 -3
  96. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +6 -2
  97. package/dist/prosemirror/listMarker.d.ts +58 -0
  98. package/dist/prosemirror/listMarker.js +185 -0
  99. package/dist/prosemirror/numberedRefFields.d.ts +24 -0
  100. package/dist/prosemirror/numberedRefFields.js +276 -0
  101. package/dist/prosemirror/paraText.js +56 -17
  102. package/dist/prosemirror/plugins/anonymizationDecorations.js +1 -1
  103. package/dist/prosemirror/plugins/pmTextScan.d.ts +3 -1
  104. package/dist/prosemirror/plugins/pmTextScan.js +28 -7
  105. package/dist/prosemirror/plugins/templateDirectives.js +2 -2
  106. package/dist/prosemirror/schema/index.d.ts +2 -2
  107. package/dist/prosemirror/schema/nodes.d.ts +17 -1
  108. package/dist/prosemirror/utils/tabCalculator.d.ts +2 -123
  109. package/dist/prosemirror/utils/tabCalculator.js +1 -140
  110. package/dist/prosemirror/validation.js +93 -0
  111. package/dist/render-dom/RemoteSelectionOverlay.d.ts +4 -4
  112. package/dist/types/documentEnumValues.d.ts +4 -1
  113. package/dist/types/documentEnumValues.js +12 -1
  114. package/dist/types/editor-story.d.ts +8 -0
  115. package/dist/types/editor-story.js +0 -0
  116. package/dist/types/remote-selection.d.ts +11 -0
  117. package/dist/types/remote-selection.js +0 -0
  118. package/dist/types/wrap.d.ts +36 -0
  119. package/dist/types/wrap.js +40 -0
  120. package/dist/utils/fontResolver.d.ts +1 -1
  121. package/dist/utils/fontResolver.js +42 -2
  122. package/dist/utils/formatToStyle.js +4 -2
  123. package/dist/utils/horizontalScale.d.ts +28 -0
  124. package/dist/utils/horizontalScale.js +42 -0
  125. package/package.json +2 -2
@@ -1,12 +1,13 @@
1
- import { calculateTabWidth, pixelsToTwips } from "../../prosemirror/utils/tabCalculator.js";
2
1
  import { CJK_FALLBACK_FONT_FAMILY, isCjkFont } from "../../utils/fontResolver.js";
2
+ import { getHorizontalScaleFactor } from "../../utils/horizontalScale.js";
3
3
  import { isRtlParagraph } from "../../utils/paragraphBaseDirection.js";
4
4
  import { inlineImageBoundingBox } from "../../utils/rotationBoundingBox.js";
5
5
  import { hasCjk, hasComplexScript } from "../../utils/scriptSegments.js";
6
+ import { JUSTIFIED_LIST_FINAL_LINE_MAX_SHRINK_RATIO, JUSTIFIED_LIST_SPACE_CONTRACTION_RATIO, supportsJustifiedListFinalLineContraction } from "../justifiedLineFit.js";
6
7
  import { measuredLineAdvance } from "../lineFlow.js";
7
8
  import { isFloatingImageRun } from "../types.js";
8
9
  import { clampFloatingWrapMargins } from "./clampFloatingWrapMargins.js";
9
- import { hasComplexScriptFormatting, resolveComplexScriptFormatting } from "./complexScriptFormatting.js";
10
+ import { applyComplexScriptFormatting, hasComplexScriptFormatting } from "./complexScriptFormatting.js";
10
11
  import { resolveEffectiveLineBreakPolicy } from "./effectiveLineBreakPolicy.js";
11
12
  import { getFloatingAvailableWidth, getFloatingMargins } from "./floatingZones.js";
12
13
  import { defaultLineBreakProvider, getLineBreakProvider } from "./lineBreakProvider.js";
@@ -14,6 +15,7 @@ import { findGraphemeBreaks, findHyphenationBreaks, findWordBreaks, isBreakChar,
14
15
  import { getListMarkerInlineWidth } from "./listMarkerWidth.js";
15
16
  import { buildRunFontStyle, ptToPx, twipsToPx } from "./measureHelpers.js";
16
17
  import { getFontMetrics, measureRun, measureTextWidth } from "./measureProvider.js";
18
+ import { calculateTabWidth, pixelsToTwips } from "./tabCalculator.js";
17
19
  import { countCompressibleSpaces } from "./textMeasurementPolicy.js";
18
20
  //#region src/layout-engine/measure/measureParagraph.ts
19
21
  /**
@@ -30,9 +32,6 @@ const JUSTIFY_SHRINK_TOLERANCE_RATIO = .016;
30
32
  const JUSTIFY_SPACE_CONTRACTION_RATIO = .075;
31
33
  const JUSTIFY_LIST_MARKER_SPACE_CONTRACTION_RATIO = .195;
32
34
  const JUSTIFY_DEEP_HANGING_LIST_MARKER_SHRINK_TOLERANCE_RATIO = .022;
33
- const JUSTIFY_LIST_CONTINUATION_SPACE_CONTRACTION_RATIO = .32;
34
- const JUSTIFY_LIST_CONTINUATION_MAX_SHRINK_TOLERANCE_RATIO = .015;
35
- const JUSTIFY_INSET_LIST_SHRINK_TOLERANCE_RATIO = .015;
36
35
  const JUSTIFY_LITERAL_TAB_CONTINUATION_SHRINK_TOLERANCE_RATIO = .017;
37
36
  const JUSTIFY_HANGING_TAB_SHRINK_TOLERANCE_RATIO = .021;
38
37
  const DEFAULT_LIST_HANGING_INDENT_PX = 24;
@@ -89,10 +88,15 @@ function runToFontStyle(run) {
89
88
  function cjkLineHeightStyle(run, baseStyle) {
90
89
  if (!run.text || !hasCjk(run.text)) return baseStyle;
91
90
  const eastAsia = baseStyle.eastAsiaFontFamily;
92
- if (eastAsia !== void 0 && isCjkFont(eastAsia)) return {
93
- ...baseStyle,
94
- fontFamily: eastAsia
95
- };
91
+ if (eastAsia !== void 0 && isCjkFont(eastAsia)) {
92
+ const result = {
93
+ ...baseStyle,
94
+ fontFamily: eastAsia
95
+ };
96
+ delete result.alternateFontFamily;
97
+ if (baseStyle.eastAsiaAlternateFontFamily !== void 0) result.alternateFontFamily = baseStyle.eastAsiaAlternateFontFamily;
98
+ return result;
99
+ }
96
100
  if (isCjkFont(baseStyle.fontFamily ?? DEFAULT_FONT_FAMILY)) return baseStyle;
97
101
  return {
98
102
  ...baseStyle,
@@ -102,10 +106,7 @@ function cjkLineHeightStyle(run, baseStyle) {
102
106
  /** Pick the dominant typography metrics for a run containing complex script. */
103
107
  function complexScriptLineHeightStyle(run, baseStyle) {
104
108
  if (!hasComplexScriptFormatting(run) || !run.forceComplexScript && !hasComplexScript(run.text)) return baseStyle;
105
- const complexStyle = {
106
- ...baseStyle,
107
- ...resolveComplexScriptFormatting(run)
108
- };
109
+ const complexStyle = applyComplexScriptFormatting(baseStyle, run);
109
110
  return (complexStyle.fontSize ?? DEFAULT_FONT_SIZE) >= (baseStyle.fontSize ?? DEFAULT_FONT_SIZE) ? complexStyle : baseStyle;
110
111
  }
111
112
  /**
@@ -153,7 +154,8 @@ const WORD_SINGLE_LINE_FLOOR = 1.15;
153
154
  function calculateEmptyParagraphMetrics(fontSize, spacing, fontFamily, attrs) {
154
155
  const result = calculateTypographyMetrics(fontSize, spacing, getFontMetrics({
155
156
  fontSize,
156
- fontFamily: fontFamily ?? DEFAULT_FONT_FAMILY
157
+ fontFamily: fontFamily ?? DEFAULT_FONT_FAMILY,
158
+ ...attrs?.defaultAlternateFontFamily !== void 0 ? { alternateFontFamily: attrs.defaultAlternateFontFamily } : {}
157
159
  }));
158
160
  const lineRule = spacing?.lineRule ?? "auto";
159
161
  if (lineRule === "auto" || lineRule === "atLeast") {
@@ -378,15 +380,10 @@ function resolveJustifyFitStrategy(block, isFirstLine, profile) {
378
380
  type: "width",
379
381
  ratio: JUSTIFY_DEEP_HANGING_LIST_MARKER_SHRINK_TOLERANCE_RATIO
380
382
  };
381
- const left = block.attrs.indent?.left ?? 0;
382
- if (hanging > 0 && left > hanging) return {
383
- type: "width",
384
- ratio: JUSTIFY_INSET_LIST_SHRINK_TOLERANCE_RATIO
385
- };
386
383
  return {
387
384
  type: "space",
388
- ratio: JUSTIFY_LIST_CONTINUATION_SPACE_CONTRACTION_RATIO,
389
- maxWidthRatio: JUSTIFY_LIST_CONTINUATION_MAX_SHRINK_TOLERANCE_RATIO
385
+ ratio: JUSTIFIED_LIST_SPACE_CONTRACTION_RATIO,
386
+ maxWidthRatio: JUSTIFY_SHRINK_TOLERANCE_RATIO
390
387
  };
391
388
  }
392
389
  const hasTabStops = (block.attrs?.tabs?.length ?? 0) > 0;
@@ -411,6 +408,54 @@ function resolveJustifyFitStrategy(block, isFirstLine, profile) {
411
408
  ratio: JUSTIFY_SPACE_CONTRACTION_RATIO
412
409
  };
413
410
  }
411
+ function resolveFinalLineJustifyFitStrategy(block, profile) {
412
+ if (supportsJustifiedListFinalLineContraction(block)) return {
413
+ type: "space",
414
+ ratio: JUSTIFIED_LIST_SPACE_CONTRACTION_RATIO,
415
+ maxWidthRatio: JUSTIFIED_LIST_FINAL_LINE_MAX_SHRINK_RATIO
416
+ };
417
+ return resolveJustifyFitStrategy(block, false, profile);
418
+ }
419
+ function resolveCandidateJustifyFitStrategy({ isFirstLine, isFinalCandidate, firstLineStrategy, continuationStrategy, finalLineStrategy }) {
420
+ if (isFirstLine) return firstLineStrategy;
421
+ if (isFinalCandidate) return finalLineStrategy;
422
+ return continuationStrategy;
423
+ }
424
+ const isIgnorableFinalTailRun = (run) => run.kind === "renderedPageBreak" || isTextRun(run) && run.text.length === 0;
425
+ function isFinalTextCandidate(block, runIndex, nextBreak) {
426
+ const currentRun = block.runs[runIndex];
427
+ if (!currentRun || !isTextRun(currentRun) || nextBreak !== currentRun.text.length) return false;
428
+ for (let index = runIndex + 1; index < block.runs.length; index += 1) {
429
+ const run = block.runs[index];
430
+ if (run && isIgnorableFinalTailRun(run)) continue;
431
+ return false;
432
+ }
433
+ return true;
434
+ }
435
+ function resolveTextCandidateFit({ block, line, isFirstLine, isFinalCandidate, candidateWidth, candidateSpaceWidth, fallbackTolerancePx, continuationStrategy, finalStrategy }) {
436
+ if (isFirstLine || !isFinalCandidate || !supportsJustifiedListFinalLineContraction(block)) return {
437
+ type: "ordinary",
438
+ tolerancePx: fallbackTolerancePx
439
+ };
440
+ const ordinaryTolerancePx = justifyFitTolerance(line, continuationStrategy, candidateSpaceWidth);
441
+ if (candidateWidth <= line.availableWidth + ordinaryTolerancePx) return {
442
+ type: "ordinary",
443
+ tolerancePx: ordinaryTolerancePx
444
+ };
445
+ const finalTolerancePx = justifyFitTolerance(line, finalStrategy, candidateSpaceWidth);
446
+ if (candidateWidth > line.availableWidth + finalTolerancePx) return {
447
+ type: "final-contraction-rejected",
448
+ tolerancePx: finalTolerancePx
449
+ };
450
+ return {
451
+ type: "final-contraction-admitted",
452
+ tolerancePx: finalTolerancePx,
453
+ paint: {
454
+ type: "space-contraction",
455
+ contractionPx: candidateWidth - line.availableWidth
456
+ }
457
+ };
458
+ }
414
459
  function compressibleSpaceWidth(text, style) {
415
460
  const count = countCompressibleSpaces(text);
416
461
  return count === 0 ? 0 : count * measureTextWidth(" ", style);
@@ -453,7 +498,7 @@ function measureWordWithTrailingWhitespace(word, style) {
453
498
  fullWordWidth: measureTextWidth(word, style)
454
499
  };
455
500
  const trailingWhitespaceWidth = measureTextWidth(trailingWhitespace, style);
456
- const boundarySpacing = measuredWord.length > 0 ? (style.letterSpacing ?? 0) * ((style.horizontalScale ?? 100) / 100) : 0;
501
+ const boundarySpacing = measuredWord.length > 0 ? (style.letterSpacing ?? 0) * getHorizontalScaleFactor(style.horizontalScale) : 0;
457
502
  return {
458
503
  measuredWord,
459
504
  wordWidth,
@@ -678,6 +723,7 @@ function measureParagraph(block, maxWidth, options) {
678
723
  };
679
724
  const firstLineJustifyFitStrategy = resolveJustifyFitStrategy(block, true, justificationProfile);
680
725
  const continuationJustifyFitStrategy = resolveJustifyFitStrategy(block, false, justificationProfile);
726
+ const finalLineJustifyFitStrategy = resolveFinalLineJustifyFitStrategy(block, justificationProfile);
681
727
  const floatingZones = options?.floatingZones;
682
728
  const paragraphYOffset = options?.paragraphYOffset ?? 0;
683
729
  const indent = attrs?.indent;
@@ -792,7 +838,8 @@ function measureParagraph(block, maxWidth, options) {
792
838
  const fontSize = line.maxFontMetrics ? line.maxFontSize : paragraphFontSize;
793
839
  const metrics = line.maxFontMetrics ?? getFontMetrics({
794
840
  fontSize: paragraphFontSize,
795
- fontFamily: paragraphFontFamily
841
+ fontFamily: paragraphFontFamily,
842
+ ...attrs?.defaultAlternateFontFamily !== void 0 ? { alternateFontFamily: attrs.defaultAlternateFontFamily } : {}
796
843
  });
797
844
  const finalTypography = { ...calculateTypographyMetrics(fontSize, spacing, metrics) };
798
845
  const inlineObjectHeight = Math.max(line.maxImageHeightPx, line.maxMathHeightPx);
@@ -840,6 +887,7 @@ function measureParagraph(block, maxWidth, options) {
840
887
  toChar: currentLine.toChar,
841
888
  width: Math.max(0, currentLine.width - currentLine.trailingWhitespaceWidth),
842
889
  ...finalTypography,
890
+ ...currentLine.justificationPaint !== void 0 ? { justificationPaint: currentLine.justificationPaint } : {},
843
891
  ...currentLine.discretionaryHyphen ? { discretionaryHyphen: currentLine.discretionaryHyphen } : {},
844
892
  ...currentLine.renderedPageBreakBefore ? { renderedPageBreakBefore: true } : {}
845
893
  };
@@ -900,7 +948,7 @@ function measureParagraph(block, maxWidth, options) {
900
948
  if (crossRunResume && runIndex < crossRunResume.runIndex) continue;
901
949
  const run = runs[runIndex];
902
950
  if (run.kind === "renderedPageBreak") {
903
- if (!runs.slice(runIndex + 1).some((followingRun) => followingRun.kind !== "renderedPageBreak")) {
951
+ if (!runs.slice(runIndex + 1).some((followingRun) => !isIgnorableFinalTailRun(followingRun))) {
904
952
  currentLine.toRun = runIndex;
905
953
  currentLine.toChar = 0;
906
954
  continue;
@@ -1051,10 +1099,18 @@ function measureParagraph(block, maxWidth, options) {
1051
1099
  const nextBreak = wordBreaks[wordBreakIndex] ?? text.length;
1052
1100
  const word = text.slice(charIndex, nextBreak);
1053
1101
  const { measuredWord, wordWidth, fullWordWidth } = measureWordWithTrailingWhitespace(word, style);
1102
+ let leadingLetterSpacing = (currentLine.fromRun !== currentLine.toRun || currentLine.fromChar !== currentLine.toChar) && currentLine.toRun === runIndex && currentLine.toChar === charIndex ? (style.letterSpacing ?? 0) * ((style.horizontalScale ?? 100) / 100) : 0;
1054
1103
  const hangingPunctuationWidth = trailingHangingPunctuationWidth(measuredWord, style, breakPolicy, effectiveLineBreakPolicy.hangingPunctuation);
1055
1104
  const isFirstLine = lines.length === 0;
1056
1105
  const regularSpaceWidth = compressibleSpaceWidth(measuredWord, style);
1057
- const widthTolerance = isJustifiedParagraph ? justifyFitTolerance(currentLine, isFirstLine ? firstLineJustifyFitStrategy : continuationJustifyFitStrategy, regularSpaceWidth) : WIDTH_TOLERANCE;
1106
+ const justifyFitStrategy = resolveCandidateJustifyFitStrategy({
1107
+ isFirstLine,
1108
+ isFinalCandidate: isFinalTextCandidate(block, runIndex, nextBreak),
1109
+ firstLineStrategy: firstLineJustifyFitStrategy,
1110
+ continuationStrategy: continuationJustifyFitStrategy,
1111
+ finalLineStrategy: finalLineJustifyFitStrategy
1112
+ });
1113
+ const widthTolerance = isJustifiedParagraph ? justifyFitTolerance(currentLine, justifyFitStrategy, regularSpaceWidth) : WIDTH_TOLERANCE;
1058
1114
  const automaticHyphenation = effectiveLineBreakPolicy.automaticHyphenation;
1059
1115
  const mayHyphenateLine = automaticHyphenation.type === "enabled" && (automaticHyphenation.consecutiveLineLimit === 0 || consecutiveHyphenatedLines < automaticHyphenation.consecutiveLineLimit) && exceedsHyphenationZone(currentLine, automaticHyphenation.hyphenationZoneTwips);
1060
1116
  const isRunTail = nextBreak === text.length;
@@ -1073,10 +1129,10 @@ function measureParagraph(block, maxWidth, options) {
1073
1129
  breaks: findHyphenationBreaks(fullWordText, breakPolicy)
1074
1130
  };
1075
1131
  }
1076
- const overflowsCurrentLine = wordWidth > 0 && currentLine.width + wordWidth > currentLine.availableWidth + widthTolerance;
1132
+ const overflowsCurrentLine = wordWidth > 0 && currentLine.width + leadingLetterSpacing + wordWidth > currentLine.availableWidth + widthTolerance;
1077
1133
  if (mayHyphenateLine && overflowsCurrentLine && activeHyphenationWord) {
1078
1134
  const consumed = charIndex - activeHyphenationWord.start;
1079
- const spaceLeft = currentLine.availableWidth - currentLine.width + widthTolerance;
1135
+ const spaceLeft = currentLine.availableWidth - currentLine.width - leadingLetterSpacing + widthTolerance;
1080
1136
  const hyphenWidth = measureTextWidth("-", style);
1081
1137
  let fittingBreak;
1082
1138
  for (const breakOffset of activeHyphenationWord.breaks) {
@@ -1086,7 +1142,7 @@ function measureParagraph(block, maxWidth, options) {
1086
1142
  if (fittingBreak !== void 0) {
1087
1143
  const absoluteBreak = activeHyphenationWord.start + fittingBreak;
1088
1144
  const prefix = text.slice(charIndex, absoluteBreak);
1089
- currentLine.width += measureTextWidth(prefix, style) + hyphenWidth;
1145
+ currentLine.width += leadingLetterSpacing + measureTextWidth(prefix, style) + hyphenWidth;
1090
1146
  currentLine.trailingWhitespaceWidth = 0;
1091
1147
  currentLine.toRun = runIndex;
1092
1148
  currentLine.toChar = absoluteBreak;
@@ -1097,8 +1153,8 @@ function measureParagraph(block, maxWidth, options) {
1097
1153
  continue;
1098
1154
  }
1099
1155
  }
1100
- if (crossRunWord && currentLine.width + crossRunWord.width > currentLine.availableWidth + widthTolerance) {
1101
- const spaceLeft = currentLine.availableWidth - currentLine.width + widthTolerance;
1156
+ if (crossRunWord && currentLine.width + leadingLetterSpacing + crossRunWord.width > currentLine.availableWidth + widthTolerance) {
1157
+ const spaceLeft = currentLine.availableWidth - currentLine.width - leadingLetterSpacing + widthTolerance;
1102
1158
  let fittingPrefix;
1103
1159
  for (let breakIndex = crossRunWord.breaks.length - 1; breakIndex >= 0; breakIndex -= 1) {
1104
1160
  const breakOffset = crossRunWord.breaks[breakIndex];
@@ -1112,7 +1168,7 @@ function measureParagraph(block, maxWidth, options) {
1112
1168
  }
1113
1169
  if (fittingPrefix) {
1114
1170
  for (const segment of fittingPrefix.segments) updateMaxFont(cjkLineHeightStyle(segment.run, complexScriptLineHeightStyle(segment.run, segment.style)));
1115
- currentLine.width += fittingPrefix.width + measureTextWidth("-", fittingPrefix.hyphenStyle);
1171
+ currentLine.width += leadingLetterSpacing + fittingPrefix.width + measureTextWidth("-", fittingPrefix.hyphenStyle);
1116
1172
  currentLine.trailingWhitespaceWidth = 0;
1117
1173
  currentLine.toRun = fittingPrefix.endRunIndex;
1118
1174
  currentLine.toChar = fittingPrefix.endChar;
@@ -1133,18 +1189,21 @@ function measureParagraph(block, maxWidth, options) {
1133
1189
  }
1134
1190
  if (wordWidth - hangingPunctuationWidth > currentLine.availableWidth + widthTolerance) {
1135
1191
  let chunkStart = 0;
1192
+ let chunkLeadingLetterSpacing = leadingLetterSpacing;
1136
1193
  while (chunkStart < measuredWord.length) {
1137
- const spaceLeft = currentLine.availableWidth - currentLine.width + WIDTH_TOLERANCE;
1194
+ const spaceLeft = currentLine.availableWidth - currentLine.width - chunkLeadingLetterSpacing + WIDTH_TOLERANCE;
1138
1195
  let bestEnd = findMaxFittingLength(measuredWord.slice(chunkStart), style, spaceLeft, currentLine.width === 0, breakPolicy);
1139
1196
  if (bestEnd === 0) {
1140
1197
  startNewLine(runIndex, charIndex + chunkStart);
1198
+ chunkLeadingLetterSpacing = 0;
1141
1199
  updateMaxFont(lineHeightStyle);
1142
1200
  continue;
1143
1201
  }
1144
1202
  const chunkEnd = chunkStart + bestEnd;
1145
1203
  const chunk = measuredWord.slice(chunkStart, chunkEnd);
1146
1204
  const chunkWidth = measureTextWidth(chunk, style);
1147
- currentLine.width += chunkWidth;
1205
+ currentLine.width += chunkLeadingLetterSpacing + chunkWidth;
1206
+ chunkLeadingLetterSpacing = 0;
1148
1207
  currentLine.trailingWhitespaceWidth = chunkWidth - measureTextWidth(trimTrailingSpacesAndTabs(chunk), style);
1149
1208
  currentLine.regularSpaceWidth += compressibleSpaceWidth(chunk, style);
1150
1209
  currentLine.toRun = runIndex;
@@ -1168,13 +1227,27 @@ function measureParagraph(block, maxWidth, options) {
1168
1227
  const rawGlueWidth = isRunTail && wordTail !== void 0 && (!isBreakChar(wordTail) || protectedGlueWidth > 0) ? Math.max(trailingGlueWidths[runIndex] ?? 0, protectedGlueWidth) : 0;
1169
1228
  const glueWidth = rawGlueWidth > 0 && wordWidth + rawGlueWidth <= getPostWrapAvailableWidth() + widthTolerance ? rawGlueWidth : 0;
1170
1229
  const hangingAllowance = glueWidth === 0 ? hangingPunctuationWidth : 0;
1171
- if (wordWidth > 0 && currentLine.width > 0 && currentLine.width + wordWidth + glueWidth > currentLine.availableWidth + widthTolerance + hangingAllowance) {
1230
+ const candidateFit = isJustifiedParagraph ? resolveTextCandidateFit({
1231
+ block,
1232
+ line: currentLine,
1233
+ isFirstLine,
1234
+ isFinalCandidate: isFinalTextCandidate(block, runIndex, nextBreak),
1235
+ candidateWidth: currentLine.width + leadingLetterSpacing + wordWidth + glueWidth - hangingAllowance,
1236
+ candidateSpaceWidth: regularSpaceWidth,
1237
+ fallbackTolerancePx: widthTolerance,
1238
+ continuationStrategy: continuationJustifyFitStrategy,
1239
+ finalStrategy: finalLineJustifyFitStrategy
1240
+ }) : void 0;
1241
+ const finalWrapTolerance = candidateFit?.tolerancePx ?? widthTolerance;
1242
+ if (wordWidth > 0 && currentLine.width > 0 && currentLine.width + leadingLetterSpacing + wordWidth + glueWidth > currentLine.availableWidth + finalWrapTolerance + hangingAllowance) {
1172
1243
  startNewLine(runIndex, charIndex);
1244
+ leadingLetterSpacing = 0;
1173
1245
  updateMaxFont(lineHeightStyle);
1174
1246
  }
1175
- currentLine.width += fullWordWidth;
1247
+ if (candidateFit?.type === "final-contraction-admitted") currentLine.justificationPaint = candidateFit.paint;
1248
+ currentLine.width += leadingLetterSpacing + fullWordWidth;
1176
1249
  const wordTrailingWhitespaceWidth = fullWordWidth - wordWidth;
1177
- currentLine.trailingWhitespaceWidth = wordWidth === 0 ? currentLine.trailingWhitespaceWidth + wordTrailingWhitespaceWidth : wordTrailingWhitespaceWidth;
1250
+ currentLine.trailingWhitespaceWidth = wordWidth === 0 ? currentLine.trailingWhitespaceWidth + leadingLetterSpacing + wordTrailingWhitespaceWidth : wordTrailingWhitespaceWidth;
1178
1251
  currentLine.regularSpaceWidth += compressibleSpaceWidth(word, style);
1179
1252
  currentLine.toRun = runIndex;
1180
1253
  currentLine.toChar = nextBreak;
@@ -1187,7 +1260,8 @@ function measureParagraph(block, maxWidth, options) {
1187
1260
  const fontFamily = currentLine.maxFontMetrics?.fontFamily ?? attrs?.defaultFontFamily;
1188
1261
  updateMaxFont({
1189
1262
  fontSize: listParagraphMarkFontSize,
1190
- ...fontFamily === void 0 ? {} : { fontFamily }
1263
+ ...fontFamily === void 0 ? {} : { fontFamily },
1264
+ ...currentLine.maxFontMetrics === null && attrs?.defaultAlternateFontFamily !== void 0 ? { alternateFontFamily: attrs.defaultAlternateFontFamily } : {}
1191
1265
  });
1192
1266
  }
1193
1267
  finalizeLine();
@@ -14,6 +14,7 @@ declare namespace measureTypes_d_exports {
14
14
  */
15
15
  type FontStyle = {
16
16
  fontFamily?: string;
17
+ alternateFontFamily?: string;
17
18
  /**
18
19
  * East-Asian font for CJK code points. When set, `measureTextWidth` /
19
20
  * `measureRun` measure CJK code points with this font and the rest with
@@ -21,11 +22,13 @@ type FontStyle = {
21
22
  * and click positioning stay in sync.
22
23
  */
23
24
  eastAsiaFontFamily?: string;
25
+ eastAsiaAlternateFontFamily?: string;
24
26
  /**
25
27
  * Complex-script font for Arabic, Hebrew, Indic and South-East Asian code
26
28
  * points. Same contract as `eastAsiaFontFamily`, over a different slot.
27
29
  */
28
30
  complexScriptFontFamily?: string;
31
+ complexScriptAlternateFontFamily?: string;
29
32
  complexScriptFontSize?: number;
30
33
  complexScriptBold?: boolean;
31
34
  complexScriptItalic?: boolean;
@@ -0,0 +1,123 @@
1
+ //#region src/layout-engine/measure/tabCalculator.d.ts
2
+ /**
3
+ * Tab Width Calculator
4
+ *
5
+ * Computes tab widths based on position and tab stops.
6
+ * Follows OOXML tab stop semantics:
7
+ * - Default tab interval: 720 twips (0.5 inch, 48px at 96dpi)
8
+ * - Tab stops can be start (left), end (right), center, decimal, or bar
9
+ * - Explicit tab stops override default stops
10
+ *
11
+ * Based on ECMA-376 specification and clean-room understanding of tab layout.
12
+ */
13
+ /**
14
+ * Tab alignment types
15
+ */
16
+ type TabAlignment = "start" | "end" | "center" | "decimal" | "bar" | "clear";
17
+ /**
18
+ * Tab leader (fill character) types
19
+ */
20
+ type TabLeader = "none" | "dot" | "hyphen" | "underscore" | "middleDot" | "heavy";
21
+ /**
22
+ * Tab stop definition
23
+ */
24
+ type TabStop = {
25
+ /** Tab alignment mode */
26
+ val: TabAlignment;
27
+ /** Position in twips from left margin */
28
+ pos: number;
29
+ /** Optional leader character */
30
+ leader?: TabLeader;
31
+ };
32
+ /**
33
+ * Context for tab calculations
34
+ */
35
+ type TabContext = {
36
+ /** Explicit tab stops from paragraph or style */
37
+ explicitStops?: TabStop[];
38
+ /** Default tab interval in twips (default: 720 = 0.5 inch) */
39
+ defaultTabInterval?: number;
40
+ /** Left indent in twips */
41
+ leftIndent?: number;
42
+ };
43
+ /**
44
+ * Result of tab width calculation
45
+ */
46
+ type TabWidthResult = {
47
+ /** Width of the tab in pixels */
48
+ width: number;
49
+ /** Leader character to render (if any) */
50
+ leader?: TabLeader;
51
+ /** Alignment that was applied */
52
+ alignment: TabAlignment | "default";
53
+ };
54
+ /** Default tab interval: 720 twips = 0.5 inch */
55
+ declare const DEFAULT_TAB_INTERVAL_TWIPS = 720;
56
+ /** Twips per inch */
57
+ declare const TWIPS_PER_INCH = 1440;
58
+ /** Pixels per inch (96 dpi standard for CSS) */
59
+ declare const PIXELS_PER_INCH = 96;
60
+ /**
61
+ * Convert twips to pixels
62
+ * @param twips - Value in twips (1/1440 inch)
63
+ * @returns Value in pixels (at 96 dpi)
64
+ */
65
+ declare function twipsToPixels(twips: number): number;
66
+ /**
67
+ * Convert pixels to twips
68
+ * @param pixels - Value in pixels (at 96 dpi)
69
+ * @returns Value in twips (1/1440 inch)
70
+ */
71
+ declare function pixelsToTwips(pixels: number): number;
72
+ /**
73
+ * Compute the list of effective tab stops for a paragraph
74
+ *
75
+ * Merges explicit stops with default stops at regular intervals.
76
+ * Explicit stops before the left indent remain available to a hanging first
77
+ * line. Continuation lines naturally skip them because their cursor already
78
+ * starts at the left indent.
79
+ *
80
+ * @param context - Tab context with explicit stops and settings
81
+ * @returns Sorted array of tab stops in twips
82
+ */
83
+ declare function computeTabStops(context: TabContext): TabStop[];
84
+ /**
85
+ * Pixel widths of the content after a tab, needed to position non-left stops.
86
+ *
87
+ * Caller measures its own runs (the painter and the layout measurer have
88
+ * different run models and measurement paths) and passes the result here, so
89
+ * this module owns the stop grid + alignment math without owning measurement.
90
+ */
91
+ type TabFollowingContent = {
92
+ /** Total width of the runs after the tab. Used for `end` and `center` stops. */
93
+ followingWidth?: number;
94
+ /** Width of the content before the decimal separator. Used for `decimal` stops. */
95
+ decimalPrefixWidth?: number;
96
+ };
97
+ /**
98
+ * Calculate the width of a tab character.
99
+ *
100
+ * Finds the next tab stop after the current position and computes the width
101
+ * needed to reach it. For `end`/`center`/`decimal` stops the content after the
102
+ * tab is anchored against the stop, so the caller supplies its measured width
103
+ * via {@link TabFollowingContent}.
104
+ *
105
+ * @param currentXPx - Current horizontal position in pixels (from the content
106
+ * area's left edge — the same origin tab stop positions are measured from)
107
+ * @param context - Tab context with stops and settings
108
+ * @param following - Measured widths of the content after the tab
109
+ * @returns Tab width result with width in pixels
110
+ */
111
+ declare function calculateTabWidth(currentXPx: number, context: TabContext, following?: TabFollowingContent): TabWidthResult;
112
+ /**
113
+ * Calculate tab width with simple default stops
114
+ *
115
+ * Simplified version for basic tab rendering without explicit stops.
116
+ * Uses the default 0.5 inch (48px) tab interval.
117
+ *
118
+ * @param currentXPx - Current horizontal position in pixels
119
+ * @returns Width of the tab in pixels
120
+ */
121
+ declare function calculateSimpleTabWidth(currentXPx: number): number;
122
+ //#endregion
123
+ export { DEFAULT_TAB_INTERVAL_TWIPS, PIXELS_PER_INCH, TWIPS_PER_INCH, TabAlignment, TabContext, TabFollowingContent, TabLeader, TabStop, TabWidthResult, calculateSimpleTabWidth, calculateTabWidth, computeTabStops, pixelsToTwips, twipsToPixels };
@@ -0,0 +1,144 @@
1
+ //#region src/layout-engine/measure/tabCalculator.ts
2
+ /** Default tab interval: 720 twips = 0.5 inch */
3
+ const DEFAULT_TAB_INTERVAL_TWIPS = 720;
4
+ /** Twips per inch */
5
+ const TWIPS_PER_INCH = 1440;
6
+ /** Pixels per inch (96 dpi standard for CSS) */
7
+ const PIXELS_PER_INCH = 96;
8
+ /** Ignore sub-twip round trips when the cursor already occupies a tab stop. */
9
+ const TAB_POSITION_EPSILON_TWIPS = .01;
10
+ /** Positions closer than this are treated as the same tab stop. */
11
+ const TAB_STOP_MATCH_TOLERANCE_TWIPS = 20;
12
+ /**
13
+ * Convert twips to pixels
14
+ * @param twips - Value in twips (1/1440 inch)
15
+ * @returns Value in pixels (at 96 dpi)
16
+ */
17
+ function twipsToPixels(twips) {
18
+ return twips / TWIPS_PER_INCH * 96;
19
+ }
20
+ /**
21
+ * Convert pixels to twips
22
+ * @param pixels - Value in pixels (at 96 dpi)
23
+ * @returns Value in twips (1/1440 inch)
24
+ */
25
+ function pixelsToTwips(pixels) {
26
+ return pixels / 96 * TWIPS_PER_INCH;
27
+ }
28
+ /**
29
+ * Compute the list of effective tab stops for a paragraph
30
+ *
31
+ * Merges explicit stops with default stops at regular intervals.
32
+ * Explicit stops before the left indent remain available to a hanging first
33
+ * line. Continuation lines naturally skip them because their cursor already
34
+ * starts at the left indent.
35
+ *
36
+ * @param context - Tab context with explicit stops and settings
37
+ * @returns Sorted array of tab stops in twips
38
+ */
39
+ function computeTabStops(context) {
40
+ const { explicitStops = [], defaultTabInterval = 720, leftIndent = 0 } = context;
41
+ const tabInterval = Number.isFinite(defaultTabInterval) && defaultTabInterval >= TAB_STOP_MATCH_TOLERANCE_TWIPS ? defaultTabInterval : 720;
42
+ const validExplicitStops = explicitStops.filter((stop) => stop.val !== "clear" && stop.val !== "bar");
43
+ const clearPositions = explicitStops.filter((stop) => stop.val === "clear").map((stop) => stop.pos);
44
+ let maxExplicit = 0;
45
+ for (const stop of validExplicitStops) maxExplicit = Math.max(maxExplicit, stop.pos);
46
+ const stops = explicitStops.filter((stop) => stop.val !== "clear");
47
+ const hasLeftIndentClear = clearPositions.some((p) => Math.abs(p - leftIndent) < TAB_STOP_MATCH_TOLERANCE_TWIPS);
48
+ const addsImplicitLeftIndent = leftIndent > 0 && !hasLeftIndentClear && !validExplicitStops.some((stop) => stop.pos <= leftIndent);
49
+ if (addsImplicitLeftIndent) stops.push({
50
+ val: "start",
51
+ pos: leftIndent,
52
+ leader: "none"
53
+ });
54
+ let pos = Math.floor(Math.max(maxExplicit, leftIndent) / tabInterval) * tabInterval;
55
+ const limitPos = leftIndent + 14400;
56
+ while (pos < limitPos) {
57
+ pos += tabInterval;
58
+ let hasExplicitStop = false;
59
+ for (const stop of stops) if (Math.abs(stop.pos - pos) < TAB_STOP_MATCH_TOLERANCE_TWIPS) {
60
+ hasExplicitStop = true;
61
+ break;
62
+ }
63
+ let hasClearStop = false;
64
+ for (const clearPos of clearPositions) if (Math.abs(clearPos - pos) < TAB_STOP_MATCH_TOLERANCE_TWIPS) {
65
+ hasClearStop = true;
66
+ break;
67
+ }
68
+ const isAtLeftIndent = addsImplicitLeftIndent && Math.abs(pos - leftIndent) < TAB_STOP_MATCH_TOLERANCE_TWIPS;
69
+ if (!hasExplicitStop && !hasClearStop && !isAtLeftIndent) stops.push({
70
+ val: "start",
71
+ pos,
72
+ leader: "none"
73
+ });
74
+ }
75
+ return stops.toSorted((a, b) => a.pos - b.pos);
76
+ }
77
+ /**
78
+ * Calculate the width of a tab character.
79
+ *
80
+ * Finds the next tab stop after the current position and computes the width
81
+ * needed to reach it. For `end`/`center`/`decimal` stops the content after the
82
+ * tab is anchored against the stop, so the caller supplies its measured width
83
+ * via {@link TabFollowingContent}.
84
+ *
85
+ * @param currentXPx - Current horizontal position in pixels (from the content
86
+ * area's left edge — the same origin tab stop positions are measured from)
87
+ * @param context - Tab context with stops and settings
88
+ * @param following - Measured widths of the content after the tab
89
+ * @returns Tab width result with width in pixels
90
+ */
91
+ function calculateTabWidth(currentXPx, context, following = {}) {
92
+ const { defaultTabInterval = 720 } = context;
93
+ const currentXTwips = pixelsToTwips(currentXPx);
94
+ const nextStop = computeTabStops(context).find((stop) => stop.pos > currentXTwips + TAB_POSITION_EPSILON_TWIPS);
95
+ if (!nextStop) {
96
+ const defaultTabPx = twipsToPixels(defaultTabInterval);
97
+ let tabWidth = defaultTabPx - currentXPx % defaultTabPx;
98
+ if (tabWidth <= 0) tabWidth = defaultTabPx;
99
+ return {
100
+ width: tabWidth,
101
+ alignment: "default"
102
+ };
103
+ }
104
+ let width = twipsToPixels(nextStop.pos) - currentXPx;
105
+ if (nextStop.val === "center") width -= (following.followingWidth ?? 0) / 2;
106
+ else if (nextStop.val === "end") width -= following.followingWidth ?? 0;
107
+ else if (nextStop.val === "decimal") width -= following.decimalPrefixWidth ?? 0;
108
+ else if (nextStop.val === "bar") return {
109
+ width: 0,
110
+ ...nextStop.leader !== void 0 ? { leader: nextStop.leader } : {},
111
+ alignment: "bar"
112
+ };
113
+ if (width <= 0) {
114
+ const defaultTabPx = twipsToPixels(defaultTabInterval);
115
+ let fallbackWidth = defaultTabPx - currentXPx % defaultTabPx;
116
+ if (fallbackWidth <= 0) fallbackWidth = defaultTabPx;
117
+ return {
118
+ width: fallbackWidth,
119
+ alignment: "default"
120
+ };
121
+ }
122
+ return {
123
+ width,
124
+ ...nextStop.leader !== void 0 ? { leader: nextStop.leader } : {},
125
+ alignment: nextStop.val
126
+ };
127
+ }
128
+ /**
129
+ * Calculate tab width with simple default stops
130
+ *
131
+ * Simplified version for basic tab rendering without explicit stops.
132
+ * Uses the default 0.5 inch (48px) tab interval.
133
+ *
134
+ * @param currentXPx - Current horizontal position in pixels
135
+ * @returns Width of the tab in pixels
136
+ */
137
+ function calculateSimpleTabWidth(currentXPx) {
138
+ const defaultTabPx = twipsToPixels(720);
139
+ let tabWidth = defaultTabPx - currentXPx % defaultTabPx;
140
+ if (tabWidth < defaultTabPx / 4) tabWidth += defaultTabPx;
141
+ return tabWidth;
142
+ }
143
+ //#endregion
144
+ export { DEFAULT_TAB_INTERVAL_TWIPS, PIXELS_PER_INCH, TWIPS_PER_INCH, calculateSimpleTabWidth, calculateTabWidth, computeTabStops, pixelsToTwips, twipsToPixels };
@@ -6,10 +6,30 @@ type TableCellBlockPlacement = {
6
6
  contentHeight: number;
7
7
  leadingSpacing: number;
8
8
  };
9
+ declare const TABLE_CELL_FLOW_POSITION: {
10
+ readonly start: "start";
11
+ readonly interior: "interior";
12
+ };
13
+ type TableCellFlowPosition = (typeof TABLE_CELL_FLOW_POSITION)[keyof typeof TABLE_CELL_FLOW_POSITION];
14
+ declare const TABLE_CELL_TRAILING_SPACING_TYPE: {
15
+ readonly none: "none";
16
+ readonly authored: "authored";
17
+ readonly automatic: "automatic";
18
+ };
19
+ type TableCellTrailingSpacing = {
20
+ type: typeof TABLE_CELL_TRAILING_SPACING_TYPE.none;
21
+ } | {
22
+ type: typeof TABLE_CELL_TRAILING_SPACING_TYPE.authored;
23
+ value: number;
24
+ } | {
25
+ type: typeof TABLE_CELL_TRAILING_SPACING_TYPE.automatic;
26
+ value: number;
27
+ };
9
28
  type TableCellFlowState = {
10
29
  height: number;
30
+ position: TableCellFlowPosition;
11
31
  previousParagraphWasEmpty: boolean;
12
- trailingSpacing: number;
32
+ trailingSpacing: TableCellTrailingSpacing;
13
33
  };
14
34
  declare const createTableCellFlowState: () => TableCellFlowState;
15
35
  declare const finishTableCellFlow: (state: TableCellFlowState) => number;