@stll/folio-core 0.7.0 → 0.9.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 (193) hide show
  1. package/README.md +39 -0
  2. package/dist/ai-edits/apply.js +18 -4
  3. package/dist/ai-edits/blockRange.d.ts +13 -2
  4. package/dist/ai-edits/blockRange.js +23 -2
  5. package/dist/ai-edits/headless.d.ts +31 -4
  6. package/dist/ai-edits/headless.js +148 -35
  7. package/dist/ai-edits/index.d.ts +6 -4
  8. package/dist/ai-edits/index.js +4 -1
  9. package/dist/ai-edits/scoped-reading.d.ts +9 -0
  10. package/dist/ai-edits/scoped-reading.js +60 -0
  11. package/dist/ai-edits/snapshot.js +53 -7
  12. package/dist/ai-edits/types.d.ts +46 -3
  13. package/dist/compat/eigenpal.d.ts +8 -3
  14. package/dist/compat/eigenpal.js +7 -2
  15. package/dist/controller/fontReadiness.d.ts +29 -0
  16. package/dist/controller/fontReadiness.js +139 -0
  17. package/dist/controller/latestRequestGate.d.ts +13 -0
  18. package/dist/controller/latestRequestGate.js +16 -0
  19. package/dist/controller/layoutPipeline.js +88 -10
  20. package/dist/document-operations.d.ts +10 -3
  21. package/dist/document-operations.js +29 -14
  22. package/dist/docx/blockContentParser.js +53 -10
  23. package/dist/docx/commentParser.js +1 -1
  24. package/dist/docx/compatibility.d.ts +30 -2
  25. package/dist/docx/compatibility.js +118 -39
  26. package/dist/docx/conformance.d.ts +6 -0
  27. package/dist/docx/conformance.js +18 -0
  28. package/dist/docx/encryption/agileDecryption.js +1 -1
  29. package/dist/docx/encryption/encryptionInfo.js +1 -1
  30. package/dist/docx/encryption/openEncryptedDocx.js +1 -1
  31. package/dist/docx/ensureParaIds.d.ts +29 -0
  32. package/dist/docx/ensureParaIds.js +423 -0
  33. package/dist/docx/fontTableParser.d.ts +6 -0
  34. package/dist/docx/fontTableParser.js +72 -0
  35. package/dist/docx/groupDrawingParser.js +7 -2
  36. package/dist/docx/headerFooterParser.js +1 -1
  37. package/dist/docx/normalizeBaseDirection.js +1 -1
  38. package/dist/docx/numberingParser.js +2 -0
  39. package/dist/docx/paragraphParser.js +21 -1
  40. package/dist/docx/paragraphTextBoxEnrichment.js +141 -2
  41. package/dist/docx/parser.d.ts +1 -1
  42. package/dist/docx/parser.js +14 -9
  43. package/dist/docx/rezip.js +70 -5
  44. package/dist/docx/runConsolidator.js +4 -0
  45. package/dist/docx/runParser.js +19 -1
  46. package/dist/docx/selectiveSave.js +3 -3
  47. package/dist/docx/selectiveXmlPatch.d.ts +10 -1
  48. package/dist/docx/selectiveXmlPatch.js +1 -1
  49. package/dist/docx/serializer/fontTableSerializer.d.ts +6 -0
  50. package/dist/docx/serializer/fontTableSerializer.js +26 -0
  51. package/dist/docx/serializer/headerFooterSerializer.js +1 -1
  52. package/dist/docx/serializer/paragraphSerializer.js +8 -0
  53. package/dist/docx/serializer/runSerializer.js +14 -2
  54. package/dist/docx/serializer/settingsSerializer.d.ts +6 -0
  55. package/dist/docx/serializer/settingsSerializer.js +16 -0
  56. package/dist/docx/serializer/stylesSerializer.d.ts +6 -0
  57. package/dist/docx/serializer/stylesSerializer.js +56 -0
  58. package/dist/docx/serializer/tableSerializer.js +29 -26
  59. package/dist/docx/serializer/themeSerializer.d.ts +6 -0
  60. package/dist/docx/serializer/themeSerializer.js +36 -0
  61. package/dist/docx/settingsParser.js +46 -0
  62. package/dist/docx/styleParser.js +26 -1
  63. package/dist/docx/tableParser.js +19 -1
  64. package/dist/docx/textBoxParser.js +6 -1
  65. package/dist/docx/vmlImageParser.js +146 -1
  66. package/dist/i18n/messages/catalogs.gen.d.ts +5916 -5916
  67. package/dist/i18n/messages/catalogs.gen.js +5910 -5910
  68. package/dist/index.d.ts +8 -3
  69. package/dist/index.js +7 -2
  70. package/dist/layout-bridge/convert/footnoteLayout.d.ts +3 -0
  71. package/dist/layout-bridge/convert/footnoteLayout.js +3 -1
  72. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +3 -0
  73. package/dist/layout-bridge/convert/headerFooterLayout.js +22 -3
  74. package/dist/layout-bridge/convert/paragraphFrames.d.ts +7 -0
  75. package/dist/layout-bridge/convert/paragraphFrames.js +136 -0
  76. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +14 -2
  77. package/dist/layout-bridge/convert/toFlowBlocks.js +93 -17
  78. package/dist/layout-bridge/engine/hitTest.js +2 -1
  79. package/dist/layout-bridge/engine/measuring/index.d.ts +2 -1
  80. package/dist/layout-bridge/engine/measuring/index.js +2 -1
  81. package/dist/layout-bridge/engine/selectionRects.js +2 -1
  82. package/dist/layout-engine/headerFooterRefs.d.ts +7 -0
  83. package/dist/layout-engine/headerFooterRefs.js +40 -0
  84. package/dist/layout-engine/index.d.ts +3 -2
  85. package/dist/layout-engine/index.js +68 -106
  86. package/dist/layout-engine/measure/cache.js +11 -3
  87. package/dist/layout-engine/measure/font-metrics.worker.js +1 -0
  88. package/dist/layout-engine/measure/index.d.ts +2 -1
  89. package/dist/layout-engine/measure/index.js +2 -1
  90. package/dist/layout-engine/measure/lineBreakProvider.d.ts +33 -0
  91. package/dist/layout-engine/measure/lineBreakProvider.js +252 -0
  92. package/dist/layout-engine/measure/lineBreaks.d.ts +7 -2
  93. package/dist/layout-engine/measure/lineBreaks.js +11 -18
  94. package/dist/layout-engine/measure/listMarkerWidth.d.ts +4 -1
  95. package/dist/layout-engine/measure/listMarkerWidth.js +33 -8
  96. package/dist/layout-engine/measure/measureBlocks.d.ts +4 -2
  97. package/dist/layout-engine/measure/measureBlocks.js +138 -86
  98. package/dist/layout-engine/measure/measureContainer.js +32 -9
  99. package/dist/layout-engine/measure/measureHelpers.js +5 -2
  100. package/dist/layout-engine/measure/measureParagraph.js +346 -74
  101. package/dist/layout-engine/measure/measureTypes.d.ts +2 -1
  102. package/dist/layout-engine/measure/measureWorker.d.ts +2 -2
  103. package/dist/layout-engine/measure/measureWorker.js +8 -19
  104. package/dist/layout-engine/measure/measureWorkerProtocol.d.ts +3 -14
  105. package/dist/layout-engine/measure/tableCellFloating.d.ts +10 -3
  106. package/dist/layout-engine/measure/tableCellFloating.js +37 -29
  107. package/dist/layout-engine/measure/tableCellFlow.d.ts +27 -0
  108. package/dist/layout-engine/measure/tableCellFlow.js +73 -0
  109. package/dist/layout-engine/measure/tableCellGrid.d.ts +15 -0
  110. package/dist/layout-engine/measure/tableCellGrid.js +62 -0
  111. package/dist/layout-engine/measure/textBoxParagraphLayout.d.ts +18 -0
  112. package/dist/layout-engine/measure/textBoxParagraphLayout.js +32 -0
  113. package/dist/layout-engine/measure/textMeasurementPolicy.d.ts +18 -0
  114. package/dist/layout-engine/measure/textMeasurementPolicy.js +24 -0
  115. package/dist/layout-engine/paginator.d.ts +2 -1
  116. package/dist/layout-engine/paginator.js +7 -6
  117. package/dist/layout-engine/paragraphFrame.d.ts +22 -0
  118. package/dist/layout-engine/paragraphFrame.js +17 -0
  119. package/dist/layout-engine/paragraphSequence.d.ts +7 -0
  120. package/dist/layout-engine/paragraphSequence.js +25 -0
  121. package/dist/layout-engine/paragraphSpacing.d.ts +17 -0
  122. package/dist/layout-engine/paragraphSpacing.js +30 -0
  123. package/dist/layout-engine/renderedBreakReconciliation.d.ts +63 -0
  124. package/dist/layout-engine/renderedBreakReconciliation.js +86 -0
  125. package/dist/layout-engine/tableRowBreak.d.ts +5 -2
  126. package/dist/layout-engine/tableRowBreak.js +62 -29
  127. package/dist/layout-engine/types.d.ts +54 -12
  128. package/dist/layout-engine/types.js +15 -2
  129. package/dist/layout-painter/anchoredImagePosition.d.ts +23 -0
  130. package/dist/layout-painter/anchoredImagePosition.js +95 -0
  131. package/dist/layout-painter/borderStroke.d.ts +23 -0
  132. package/dist/layout-painter/borderStroke.js +39 -0
  133. package/dist/layout-painter/renderImage.d.ts +5 -4
  134. package/dist/layout-painter/renderImage.js +18 -4
  135. package/dist/layout-painter/renderPage.d.ts +2 -27
  136. package/dist/layout-painter/renderPage.js +15 -101
  137. package/dist/layout-painter/renderParagraph.js +45 -26
  138. package/dist/layout-painter/renderTable.d.ts +2 -0
  139. package/dist/layout-painter/renderTable.js +257 -47
  140. package/dist/layout-painter/renderTextBox.js +17 -7
  141. package/dist/managers/DocumentLoaderManager.d.ts +1 -1
  142. package/dist/managers/DocumentLoaderManager.js +2 -2
  143. package/dist/paged-layout/sectionBlockWidths.d.ts +3 -0
  144. package/dist/paged-layout/sectionBlockWidths.js +9 -0
  145. package/dist/paged-layout/sectionGeometry.js +1 -1
  146. package/dist/prosemirror/attrs/index.d.ts +4 -2
  147. package/dist/prosemirror/attrs/index.js +32 -2
  148. package/dist/prosemirror/commands/propertyChangeScope.js +1 -1
  149. package/dist/prosemirror/conversion/fromProseDoc.js +35 -18
  150. package/dist/prosemirror/conversion/toProseDoc.js +73 -37
  151. package/dist/prosemirror/extensions/StarterKit.js +2 -0
  152. package/dist/prosemirror/extensions/core/ParagraphExtension.js +8 -2
  153. package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +1 -1
  154. package/dist/prosemirror/extensions/features/ListExtension.js +2 -0
  155. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +68 -16
  156. package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +1 -0
  157. package/dist/prosemirror/extensions/marks/HighlightExtension.js +1 -1
  158. package/dist/prosemirror/extensions/marks/LanguageExtension.d.ts +7 -0
  159. package/dist/prosemirror/extensions/marks/LanguageExtension.js +30 -0
  160. package/dist/prosemirror/extensions/marks/RunShadingExtension.js +1 -1
  161. package/dist/prosemirror/extensions/marks/TextColorExtension.js +1 -1
  162. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +1 -1
  163. package/dist/prosemirror/extensions/marks/markUtils.js +12 -0
  164. package/dist/prosemirror/extensions/nodes/ImageExtension.js +2 -1
  165. package/dist/prosemirror/extensions/nodes/TableExtension.js +4 -2
  166. package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +2 -1
  167. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +7 -0
  168. package/dist/prosemirror/schema/index.d.ts +2 -2
  169. package/dist/prosemirror/schema/marks.d.ts +6 -1
  170. package/dist/prosemirror/schema/nodes.d.ts +16 -12
  171. package/dist/prosemirror/styles/resolvedStyleAttrs.js +2 -0
  172. package/dist/prosemirror/utils/tabCalculator.d.ts +3 -1
  173. package/dist/prosemirror/utils/tabCalculator.js +12 -10
  174. package/dist/prosemirror/validation.js +4 -1
  175. package/dist/redline.js +13 -3
  176. package/dist/server.d.ts +9 -4
  177. package/dist/server.js +7 -2
  178. package/dist/style-sets/extract.d.ts +35 -0
  179. package/dist/style-sets/extract.js +123 -0
  180. package/dist/style-sets/stellaStyle.d.ts +13 -0
  181. package/dist/style-sets/stellaStyle.js +516 -0
  182. package/dist/style-sets/types.d.ts +31 -0
  183. package/dist/style-sets/types.js +5 -0
  184. package/dist/types/index.d.ts +2 -1
  185. package/dist/utils/createDocument.d.ts +12 -2
  186. package/dist/utils/createDocument.js +55 -36
  187. package/dist/utils/fontResolver.js +6 -6
  188. package/dist/utils/formatToStyle.js +1 -1
  189. package/dist/utils/hexId.d.ts +8 -1
  190. package/dist/utils/hexId.js +11 -3
  191. package/dist/utils/paragraphFormattingMerge.d.ts +14 -3
  192. package/dist/utils/paragraphFormattingMerge.js +14 -4
  193. package/package.json +5 -1
@@ -1,11 +1,15 @@
1
1
  import { emuToPixels } from "../utils/units.js";
2
2
  import { measuredLineAdvance } from "./lineFlow.js";
3
- import { DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, floatingTextBoxReservesBand, floatingTextBoxWrapsText, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned } from "./types.js";
3
+ import { DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned } from "./types.js";
4
+ import { getParagraphSpacingAfter, getParagraphSpacingBefore } from "./paragraphSpacing.js";
4
5
  import { calculateChainHeight, computeKeepNextChains, getMidChainIndices, hasKeepLines, hasPageBreakBefore } from "./keep-together.js";
6
+ import { continuesNumberedSequence } from "./paragraphSequence.js";
5
7
  import { createPaginator } from "./paginator.js";
6
8
  import { getParagraphFragmentPmRange } from "./paragraphFragmentRange.js";
7
- import { buildTableRowBreakInfo, snapRowBreak } from "./tableRowBreak.js";
9
+ import { INITIAL_RENDERED_BREAK_STATE, reconcileAfterBlock, reconcileBreakBeforeBlock, recordReflowBoundary } from "./renderedBreakReconciliation.js";
10
+ import { buildTableRowBreakInfo, getRowContinuationSkip, snapRowBreak } from "./tableRowBreak.js";
8
11
  import { bandFragmentX, bandTopContentY, isPageFrameRelativeAnchor } from "./textBoxFlow.js";
12
+ import { resolveSectionHeaderFooterRefs } from "./headerFooterRefs.js";
9
13
  import { applyPendingToActive, createInitialSectionState, getEffectiveColumns, getEffectiveMargins, getEffectivePageSize, scheduleSectionBreak } from "./section-breaks.js";
10
14
  import { assertExhaustiveFlowBlock, findPageIndexContainingPmPos } from "./pmPageIndex.js";
11
15
  import { panic } from "better-result";
@@ -19,6 +23,7 @@ const DEFAULT_COLUMNS = {
19
23
  count: 1,
20
24
  gap: 0
21
25
  };
26
+ const DEFAULT_SECTION_BREAK_TYPE = "nextPage";
22
27
  function collectSectionConfigs(blocks, initialConfig, finalConfig) {
23
28
  const configs = [];
24
29
  const breakIndices = [];
@@ -42,63 +47,6 @@ function collectSectionConfigs(blocks, initialConfig, finalConfig) {
42
47
  breakIndices
43
48
  };
44
49
  }
45
- /**
46
- * Whether a paragraph block has no visible content (no runs, or a single
47
- * empty text run).
48
- */
49
- function isEmptyParagraph(block) {
50
- if (block.runs.length === 0) return true;
51
- if (block.runs.length !== 1) return false;
52
- const r = block.runs[0];
53
- return r?.kind === "text" && r.text === "";
54
- }
55
- function isPaginationEmptyParagraph(block) {
56
- return block.runs.every((run) => {
57
- if (run.kind === "text") return run.text.trim().length === 0;
58
- return run.kind === "tab" || run.kind === "lineBreak";
59
- });
60
- }
61
- function pageHasVisibleBodyContent(page, blocksById) {
62
- for (const fragment of page.fragments) {
63
- if (fragment.kind !== "paragraph") return true;
64
- const block = blocksById.get(String(fragment.blockId));
65
- if (block?.kind !== "paragraph" || !isPaginationEmptyParagraph(block)) return true;
66
- }
67
- return false;
68
- }
69
- function continuesNumberedSequence(previous, current) {
70
- if (previous?.kind !== "paragraph" || current.kind !== "paragraph") return false;
71
- const previousNumPr = previous.attrs?.numPr;
72
- const currentNumPr = current.attrs?.numPr;
73
- if (previousNumPr?.numId === void 0 || currentNumPr?.numId === void 0) return false;
74
- return previousNumPr.numId === currentNumPr.numId && previousNumPr.ilvl === currentNumPr.ilvl;
75
- }
76
- function pageStartsWithPreviousParagraphContinuation(page, previousBlock) {
77
- if (previousBlock?.kind !== "paragraph") return false;
78
- return page.fragments.some((fragment) => fragment.kind === "paragraph" && String(fragment.blockId) === String(previousBlock.id) && fragment.continuesFromPrev === true);
79
- }
80
- /**
81
- * Get spacing before a paragraph block. Empty paragraphs whose
82
- * `before` came only from the implicit default paragraph style collapse to
83
- * zero. Reference layout keeps inherited spacing when the empty paragraph itself is
84
- * authored through direct `w:pPr` formatting or an explicit `w:pStyle`.
85
- */
86
- function getSpacingBefore(block) {
87
- const value = block.attrs?.spacing?.before ?? 0;
88
- if (value === 0) return 0;
89
- if (isEmptyParagraph(block) && !block.attrs?.styleId && !block.attrs?.hasDirectParagraphFormatting && !block.attrs?.spacingExplicit?.before) return 0;
90
- return value;
91
- }
92
- /**
93
- * Get spacing after a paragraph block. Same implicit-default-style collapse
94
- * rule as `getSpacingBefore`.
95
- */
96
- function getSpacingAfter(block) {
97
- const value = block.attrs?.spacing?.after ?? 0;
98
- if (value === 0) return 0;
99
- if (isEmptyParagraph(block) && !block.attrs?.styleId && !block.attrs?.hasDirectParagraphFormatting && !block.attrs?.spacingExplicit?.after) return 0;
100
- return value;
101
- }
102
50
  function balancedParagraphSectionHeight({ blocks, measures, startIndex, endIndex, incomingSpacing, columnCount, availableHeight }) {
103
51
  if (columnCount <= 1 || startIndex >= endIndex || availableHeight <= 0) return;
104
52
  const lineUnits = [];
@@ -110,7 +58,7 @@ function balancedParagraphSectionHeight({ blocks, measures, startIndex, endIndex
110
58
  const measure = measures[index];
111
59
  if (block?.kind !== "paragraph" || measure?.kind !== "paragraph") return;
112
60
  if (block.attrs?.keepNext === true || block.attrs?.keepLines === true || block.runs.some((run) => run.kind === "text" && run.footnoteRefId !== void 0)) return;
113
- const leadingSpacing = Math.max(getSpacingBefore(block), trailingSpacing);
61
+ const leadingSpacing = Math.max(getParagraphSpacingBefore(block), trailingSpacing);
114
62
  if (measure.lines.length === 0) {
115
63
  if (leadingSpacing > 0) {
116
64
  lineUnits.push(leadingSpacing);
@@ -127,7 +75,7 @@ function balancedParagraphSectionHeight({ blocks, measures, startIndex, endIndex
127
75
  tallestUnit = Math.max(tallestUnit, unitHeight);
128
76
  }
129
77
  if (tallestUnit > availableHeight || totalHeight > availableHeight * columnCount) return;
130
- trailingSpacing = getSpacingAfter(block);
78
+ trailingSpacing = getParagraphSpacingAfter(block);
131
79
  }
132
80
  if (totalHeight <= 0) return;
133
81
  const columnsNeeded = (targetHeight) => {
@@ -236,13 +184,14 @@ function layoutDocument(blocks, measures, options) {
236
184
  const finalColumns = options.finalColumns ?? options.columns;
237
185
  if (finalColumns !== void 0) finalConfig.columns = finalColumns;
238
186
  const { configs: sectionConfigs, breakIndices } = collectSectionConfigs(blocks, bodyConfig, finalConfig);
239
- const sectionBreakTypes = [...breakIndices.map((index) => blocks[index].type), options.bodyBreakType];
187
+ const sectionBreakTypes = breakIndices.map((index) => blocks[index].type);
240
188
  const initialConfig = sectionConfigs.at(0) ?? bodyConfig;
241
189
  const paginator = createPaginator({
242
190
  pageSize: initialConfig.pageSize,
243
191
  margins: initialConfig.margins,
244
192
  ...options.mirrorMargins === true ? { mirrorMargins: true } : {},
245
193
  ...options.firstPageMargins !== void 0 ? { firstPageMargins: options.firstPageMargins } : {},
194
+ ...options.sectionEvenPageMargins !== void 0 ? { sectionEvenPageMargins: options.sectionEvenPageMargins } : {},
246
195
  columns: initialConfig.columns ?? DEFAULT_COLUMNS,
247
196
  ...options.footnoteReservedHeights !== void 0 ? { footnoteReservedHeights: options.footnoteReservedHeights } : {},
248
197
  ...options.sectionHeaderFooterRefs !== void 0 ? { sectionHeaderFooterRefs: options.sectionHeaderFooterRefs } : {}
@@ -257,28 +206,41 @@ function layoutDocument(blocks, measures, options) {
257
206
  let activeSectionMarginTop = initialConfig.margins.top;
258
207
  let activeSectionPageHeight = initialConfig.pageSize.h;
259
208
  let activeSectionMarginBottom = initialConfig.margins.bottom;
260
- let naturalPageAdvanceSinceRenderedBreak = "none";
209
+ let renderedBreakState = INITIAL_RENDERED_BREAK_STATE;
261
210
  for (let i = 0; i < blocks.length; i++) {
262
211
  const block = blocks[i];
263
212
  const measure = measures[i];
264
- const hasRenderedPageBreak = block.kind === "paragraph" && block.attrs?.renderedPageBreakBefore === true;
265
- if (hasPageBreakBefore(block)) paginator.forcePageBreak();
266
- else if (hasRenderedPageBreak) {
267
- const state = paginator.getCurrentState();
268
- if (!(pageStartsWithPreviousParagraphContinuation(state.page, blocks[i - 1]) || naturalPageAdvanceSinceRenderedBreak === "reflowBoundary" || block.kind === "paragraph" && isPaginationEmptyParagraph(block) && naturalPageAdvanceSinceRenderedBreak !== "none" || naturalPageAdvanceSinceRenderedBreak !== "none" && continuesNumberedSequence(blocks[i - 1], block)) && pageHasVisibleBodyContent(state.page, blocksById)) paginator.forcePageBreak();
269
- }
270
- if (hasRenderedPageBreak || hasPageBreakBefore(block)) naturalPageAdvanceSinceRenderedBreak = "none";
213
+ const renderedBreakNeedsSnap = measure.kind === "paragraph" && !paginator.fits(measure.totalHeight);
214
+ const hasExplicitPageBreak = hasPageBreakBefore(block);
215
+ const breakDecision = reconcileBreakBeforeBlock({
216
+ state: renderedBreakState,
217
+ block,
218
+ previousBlock: blocks[i - 1],
219
+ page: paginator.getCurrentState().page,
220
+ blocksById,
221
+ hasExplicitPageBreak,
222
+ renderedBreakNeedsSnap
223
+ });
224
+ if (breakDecision.forcePageBreak) paginator.forcePageBreak();
225
+ renderedBreakState = breakDecision.state;
271
226
  const chain = keepNextChains.get(i);
272
227
  if (chain && !midChainIndices.has(i)) {
273
228
  const chainHeight = calculateChainHeight(chain, blocks, measures);
274
229
  const pageBeforeChainLayout = paginator.getCurrentState().page.number;
275
230
  paginator.ensureFits(chainHeight);
276
- if (paginator.getCurrentState().page.number > pageBeforeChainLayout) naturalPageAdvanceSinceRenderedBreak = "reflowBoundary";
231
+ if (paginator.getCurrentState().page.number > pageBeforeChainLayout) renderedBreakState = recordReflowBoundary(renderedBreakState, true);
277
232
  }
278
233
  const pageBeforeBlockLayout = paginator.getCurrentState().page.number;
279
234
  switch (block.kind) {
280
235
  case "paragraph":
281
- layoutParagraph(block, measure, paginator, paginator.columnWidth, options.footnoteHeightById);
236
+ layoutParagraph({
237
+ block,
238
+ measure,
239
+ paginator,
240
+ contentWidth: paginator.columnWidth,
241
+ footnoteHeightById: options.footnoteHeightById,
242
+ suppressSpaceBefore: breakDecision.suppressSpaceBefore
243
+ });
282
244
  break;
283
245
  case "table":
284
246
  if (block.floating) layoutFloatingTable(block, measure, paginator, paginator.getContentWidth());
@@ -303,7 +265,9 @@ function layoutDocument(blocks, measures, options) {
303
265
  break;
304
266
  case "sectionBreak": {
305
267
  const nextSectionConfig = sectionConfigs[sectionIdx + 1] ?? initialConfig;
306
- handleSectionBreak(block, paginator, nextSectionConfig, sectionBreakTypes[sectionIdx + 1] ?? sectionBreakTypes[sectionIdx], sectionIdx + 1);
268
+ let nextType = options.bodyBreakType ?? sectionBreakTypes[sectionIdx] ?? DEFAULT_SECTION_BREAK_TYPE;
269
+ if (sectionIdx + 1 < sectionBreakTypes.length) nextType = sectionBreakTypes[sectionIdx + 1] ?? DEFAULT_SECTION_BREAK_TYPE;
270
+ handleSectionBreak(block, paginator, nextSectionConfig, nextType, sectionIdx + 1);
307
271
  const nextColumns = nextSectionConfig.columns;
308
272
  const nextBreakIndex = breakIndices[sectionIdx + 1] ?? blocks.length;
309
273
  const nextBreak = blocks[nextBreakIndex];
@@ -329,9 +293,13 @@ function layoutDocument(blocks, measures, options) {
329
293
  }
330
294
  default: break;
331
295
  }
332
- const isVisibleBodyBlock = block.kind === "paragraph" ? !isEmptyParagraph(block) : block.kind !== "pageBreak" && block.kind !== "columnBreak" && block.kind !== "sectionBreak";
333
- if (block.kind === "pageBreak" || block.kind === "columnBreak" || block.kind === "sectionBreak") naturalPageAdvanceSinceRenderedBreak = "none";
334
- else if (isVisibleBodyBlock && paginator.getCurrentState().page.number > pageBeforeBlockLayout) naturalPageAdvanceSinceRenderedBreak = paginator.pages[pageBeforeBlockLayout - 1]?.fragments.some(({ blockId }) => blockId === block.id) ? "reflowBoundary" : "ordinary";
296
+ renderedBreakState = reconcileAfterBlock({
297
+ state: renderedBreakState,
298
+ block,
299
+ pageNumberBefore: pageBeforeBlockLayout,
300
+ pageNumberAfter: paginator.getCurrentState().page.number,
301
+ previousPage: paginator.pages[pageBeforeBlockLayout - 1]
302
+ });
335
303
  }
336
304
  if (paginator.pages.length === 0) paginator.getCurrentState();
337
305
  return {
@@ -374,21 +342,11 @@ function getLineFootnoteRefs(block, fromRun, toRun, fnHeights) {
374
342
  height
375
343
  };
376
344
  }
377
- /**
378
- * Layout a paragraph block onto pages.
379
- *
380
- * When `footnoteHeightById` is provided, each line carrying a footnote
381
- * ref additionally reserves space on its host page for the fn's
382
- * content. Pagination decisions look at the line's effective height
383
- * (`lineHeight + lineFnHeight`) so a line cannot land on a page that
384
- * lacks room for both — preventing footnote overflow without the
385
- * static-reservation iteration loop.
386
- */
387
- function layoutParagraph(block, measure, paginator, contentWidth, footnoteHeightById) {
345
+ function layoutParagraph({ block, measure, paginator, contentWidth, footnoteHeightById, suppressSpaceBefore }) {
388
346
  const lines = measure.lines;
389
347
  if (lines.length === 0) {
390
- const spaceBefore = getSpacingBefore(block);
391
- const spaceAfter = getSpacingAfter(block);
348
+ const spaceBefore = suppressSpaceBefore ? 0 : getParagraphSpacingBefore(block);
349
+ const spaceAfter = getParagraphSpacingAfter(block);
392
350
  const state = paginator.getCurrentState();
393
351
  const fragment = {
394
352
  kind: "paragraph",
@@ -406,8 +364,8 @@ function layoutParagraph(block, measure, paginator, contentWidth, footnoteHeight
406
364
  paginator.addFragment(fragment, 0, spaceBefore, spaceAfter);
407
365
  return;
408
366
  }
409
- const spaceBefore = getSpacingBefore(block);
410
- const spaceAfter = getSpacingAfter(block);
367
+ const spaceBefore = suppressSpaceBefore ? 0 : getParagraphSpacingBefore(block);
368
+ const spaceAfter = getParagraphSpacingAfter(block);
411
369
  let currentLineIndex = 0;
412
370
  while (currentLineIndex < lines.length) {
413
371
  const state = paginator.getCurrentState();
@@ -522,6 +480,7 @@ function layoutTable(block, measure, paginator, footnoteHeightById) {
522
480
  const headerRowsHeight = getHeaderRowsHeight(measure, headerRowCount);
523
481
  let currentRowIndex = 0;
524
482
  const breakInfo = buildTableRowBreakInfo(block, measure);
483
+ const hasVerticalMerges = block.rows.some((row) => row.cells.some((cell) => (cell.rowSpan ?? 1) > 1));
525
484
  const computeTableX = (columnIndex) => {
526
485
  let x = paginator.getColumnX(columnIndex);
527
486
  if (block.justification === "center") x += (paginator.columnWidth - measure.totalWidth) / 2;
@@ -534,7 +493,6 @@ function layoutTable(block, measure, paginator, footnoteHeightById) {
534
493
  return x;
535
494
  };
536
495
  const getCurrentRowCapacity = (state = paginator.getCurrentState()) => state.rawContentBottom - state.topMargin;
537
- const getCurrentAvailableHeight = (state = paginator.getCurrentState()) => state.contentBottom - state.cursorY;
538
496
  const hasAdjacentPriorTableRows = (rowIndex, state = paginator.getCurrentState()) => {
539
497
  const previous = state.page.fragments.at(-1);
540
498
  return previous?.kind === "table" && previous.blockId === block.id && previous.toRow === rowIndex && previous.y + previous.height === state.cursorY && previous.x === computeTableX(state.columnIndex);
@@ -542,18 +500,21 @@ function layoutTable(block, measure, paginator, footnoteHeightById) {
542
500
  const shouldRepeatHeaderRows = (rowIndex, consumed, state = paginator.getCurrentState()) => headerRowCount > 0 && rowIndex >= headerRowCount && !(consumed === 0 && hasAdjacentPriorTableRows(rowIndex, state));
543
501
  const canSplitRow = (rowIndex, state = paginator.getCurrentState()) => {
544
502
  const row = rows[rowIndex];
545
- if (!row) return false;
546
- const repeatedHeaderOverhead = shouldRepeatHeaderRows(rowIndex, 0, state) ? headerRowsHeight : 0;
503
+ const sourceRow = block.rows[rowIndex];
504
+ if (!row || !sourceRow || sourceRow.cantSplit || sourceRow.isHeader || hasVerticalMerges) return false;
547
505
  if ((breakInfo.breakOffsets[rowIndex]?.length ?? 0) <= 1) return false;
548
- const requiredHeight = row.height + repeatedHeaderOverhead;
549
- if (requiredHeight > getCurrentRowCapacity(state)) return true;
550
- return hasAdjacentPriorTableRows(rowIndex, state) && requiredHeight > getCurrentAvailableHeight(state) - state.trailingSpacing;
506
+ const freshHeaderOverhead = headerRowCount > 0 && rowIndex >= headerRowCount ? headerRowsHeight : 0;
507
+ const oversized = row.height + freshHeaderOverhead > getCurrentRowCapacity(state);
508
+ if (!oversized && (state.footnoteHeight > 0 || (rowFootnoteIds[rowIndex]?.length ?? 0) > 0)) return false;
509
+ const currentHeaderOverhead = shouldRepeatHeaderRows(rowIndex, 0, state) ? headerRowsHeight : 0;
510
+ const currentAvailableHeight = paginator.getAvailableHeight() - currentHeaderOverhead - state.trailingSpacing;
511
+ return oversized || row.height > currentAvailableHeight;
551
512
  };
552
513
  while (currentRowIndex < rows.length) {
553
- const oversizedRow = rows[currentRowIndex];
514
+ const splittableRow = rows[currentRowIndex];
554
515
  if (canSplitRow(currentRowIndex)) {
555
516
  let consumed = 0;
556
- while (consumed < oversizedRow.height) {
517
+ while (consumed < splittableRow.height) {
557
518
  const sliceState = paginator.getCurrentState();
558
519
  const repeatHeaderRows = shouldRepeatHeaderRows(currentRowIndex, consumed, sliceState);
559
520
  const headerOverhead = repeatHeaderRows ? headerRowsHeight : 0;
@@ -565,11 +526,12 @@ function layoutTable(block, measure, paginator, footnoteHeightById) {
565
526
  continue;
566
527
  }
567
528
  const from = consumed;
568
- slice = (breakInfo.breakOffsets[currentRowIndex]?.find((o) => o > from) ?? oversizedRow.height) - consumed;
529
+ slice = (breakInfo.breakOffsets[currentRowIndex]?.find((o) => o > from) ?? splittableRow.height) - consumed;
569
530
  }
570
531
  const sliceBottom = consumed + slice;
571
- const reachesRowEnd = sliceBottom >= oversizedRow.height;
572
- const moreAfter = !reachesRowEnd || currentRowIndex + 1 < rows.length;
532
+ const continuationSkip = sliceBottom < splittableRow.height ? getRowContinuationSkip(breakInfo, currentRowIndex, sliceBottom) : 0;
533
+ const nextConsumed = Math.min(splittableRow.height, sliceBottom + continuationSkip);
534
+ const moreAfter = !(nextConsumed >= splittableRow.height) || currentRowIndex + 1 < rows.length;
573
535
  const fragmentHeight = headerOverhead + slice;
574
536
  const sliceFragment = {
575
537
  kind: "table",
@@ -586,14 +548,14 @@ function layoutTable(block, measure, paginator, footnoteHeightById) {
586
548
  ...moreAfter ? { continuesOnNext: true } : {},
587
549
  ...repeatHeaderRows ? { headerRowCount } : {},
588
550
  ...consumed > 0 ? { topClip: consumed } : {},
589
- ...reachesRowEnd ? {} : { bottomClip: sliceBottom },
551
+ ...sliceBottom >= splittableRow.height ? {} : { bottomClip: sliceBottom },
590
552
  ...block.sdtGroups ? { sdtGroups: block.sdtGroups } : {}
591
553
  };
592
554
  const sliceResult = paginator.addFragment(sliceFragment, fragmentHeight, 0, 0);
593
555
  sliceFragment.y = sliceResult.y;
594
556
  sliceFragment.x = computeTableX(sliceResult.state.columnIndex);
595
- consumed = sliceBottom;
596
- if (consumed < oversizedRow.height) paginator.forceColumnBreak();
557
+ consumed = nextConsumed;
558
+ if (consumed < splittableRow.height) paginator.forceColumnBreak();
597
559
  }
598
560
  currentRowIndex += 1;
599
561
  continue;
@@ -929,4 +891,4 @@ function handleSectionBreak(_block, paginator, nextSectionConfig, nextSectionTyp
929
891
  paginator.updateColumns(nextSectionConfig.columns ?? DEFAULT_COLUMNS);
930
892
  }
931
893
  //#endregion
932
- export { DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, applyContextualSpacing, applyPendingToActive, assertExhaustiveFlowBlock, calculateChainHeight, collectSectionConfigs, computeKeepNextChains, createInitialSectionState, createPaginator, findPageIndexContainingPmPos, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getEffectiveColumns, getEffectiveMargins, getEffectivePageSize, getHeaderRowsHeight, getMidChainIndices, hasKeepLines, hasPageBreakBefore, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, layoutDocument, scheduleSectionBreak, tableColumnsArePinned };
894
+ export { DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, applyContextualSpacing, applyPendingToActive, assertExhaustiveFlowBlock, calculateChainHeight, collectSectionConfigs, computeKeepNextChains, createInitialSectionState, createPaginator, findPageIndexContainingPmPos, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getEffectiveColumns, getEffectiveMargins, getEffectivePageSize, getHeaderRowsHeight, getMidChainIndices, getTableRowLeadingWidth, hasKeepLines, hasPageBreakBefore, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, layoutDocument, resolveSectionHeaderFooterRefs, scheduleSectionBreak, tableColumnsArePinned };
@@ -1,3 +1,4 @@
1
+ import { getLineBreakProviderGeneration } from "./lineBreakProvider.js";
1
2
  //#region src/layout-engine/measure/cache.ts
2
3
  /**
3
4
  * Current max size for text width cache
@@ -152,10 +153,10 @@ const paragraphMeasureCache = /* @__PURE__ */ new Map();
152
153
  * Used as cache key to identify identical content
153
154
  */
154
155
  function hashParagraphBlock(block) {
155
- const parts = [];
156
- for (const run of block.runs) if (run.kind === "text") parts.push(`t:${run.text}|${run.fontFamily}|${run.eastAsiaFontFamily}|${run.fontSize}|${run.bold}|${run.italic}|${run.allCaps}|${run.smallCaps}|${run.horizontalScale}|${run.letterSpacing}`);
156
+ const parts = [`lbp:${getLineBreakProviderGeneration()}`];
157
+ for (const run of block.runs) if (run.kind === "text") parts.push(`t:${run.text}|${run.fontFamily}|${run.eastAsiaFontFamily}|${run.fontSize}|${run.bold}|${run.italic}|${run.allCaps}|${run.smallCaps}|${run.horizontalScale}|${run.letterSpacing}|${run.language?.val}|${run.language?.eastAsia}|${run.language?.bidi}`);
157
158
  else if (run.kind === "tab") parts.push(`tab:${run.width}`);
158
- else if (run.kind === "image") parts.push(`img:${run.width}x${run.height}`);
159
+ else if (run.kind === "image") parts.push(`img:${run.width}x${run.height}:${run.exactLineHeight === true ? "exact" : "text"}`);
159
160
  else if (run.kind === "lineBreak") parts.push("br");
160
161
  const attrs = block.attrs;
161
162
  if (attrs) {
@@ -167,6 +168,13 @@ function hashParagraphBlock(block) {
167
168
  if (attrs.defaultFontFamily != null) parts.push(`dff:${attrs.defaultFontFamily}`);
168
169
  if (attrs.suppressEmptyParagraphHeight) parts.push("sup");
169
170
  if (attrs.reserveEmptyOutlineHeight) parts.push("outline-empty-reserve");
171
+ if (attrs.kinsoku !== void 0) parts.push(`kinsoku:${attrs.kinsoku}`);
172
+ if (attrs.overflowPunctuation !== void 0) parts.push(`overflow-punct:${attrs.overflowPunctuation}`);
173
+ if (attrs.suppressAutoHyphens !== void 0) parts.push(`suppress-auto-hyphens:${attrs.suppressAutoHyphens}`);
174
+ const automaticHyphenation = attrs.automaticHyphenation;
175
+ if (automaticHyphenation) parts.push(`auto-hyphens:${automaticHyphenation.doNotHyphenateCaps}|${automaticHyphenation.consecutiveLineLimit}`);
176
+ const lineBreakRules = attrs.lineBreakRules;
177
+ if (lineBreakRules) parts.push(`line-break-rules:${lineBreakRules.noLineBreaksBefore?.language}|${lineBreakRules.noLineBreaksBefore?.characters}|${lineBreakRules.noLineBreaksAfter?.language}|${lineBreakRules.noLineBreaksAfter?.characters}|${lineBreakRules.useLegacyEthiopicAmharicRules}`);
170
178
  const borders = attrs.borders;
171
179
  if (borders) {
172
180
  const signature = (border) => border ? `${border.width ?? ""},${border.style ?? ""},${border.color ?? ""}` : "";
@@ -44,6 +44,7 @@ function isFontFingerprintMatch(context, expectedWidth) {
44
44
  function measureEntry(entry) {
45
45
  const context = getCtx();
46
46
  context.font = entry.font;
47
+ context.fontKerning = entry.fontKerning;
47
48
  if (!isFontFingerprintMatch(context, entry.fontFingerprintWidth)) return null;
48
49
  let width = context.measureText(entry.text).width;
49
50
  const codePoints = countCodePoints(entry.text);
@@ -1,9 +1,10 @@
1
1
  import { FontMetrics, FontStyle, RunMeasurement, TextMeasurement } from "./measureTypes.js";
2
2
  import { MeasureProvider, getFontMetrics, getMeasureProvider, measureRun, measureText, measureTextWidth, resetMeasureProvider, setMeasureProvider } from "./measureProvider.js";
3
3
  import { buildFontString, findCharacterAtX, getXForCharacter, halfPtToPx, ptToPx, pxToHalfPt, pxToPt, pxToTwips, twipsToPx } from "./measureHelpers.js";
4
+ import { LineBreakPolicy, LineBreakProvider, defaultLineBreakProvider, getLineBreakProvider, getLineBreakProviderGeneration, resetLineBreakProvider, setLineBreakProvider } from "./lineBreakProvider.js";
4
5
  import { FloatingExclusionRect, FloatingImageZone, FloatingLineMargins, FloatingLineSegmentZone, WrapTextDirection, getFloatingAvailableWidth, getFloatingMargins, rectsToFloatingZones } from "./floatingZones.js";
5
6
  import { clampFloatingWrapMargins } from "./clampFloatingWrapMargins.js";
6
7
  import { MeasureParagraphOptions, getRunCharWidths, measureParagraph, measureParagraphs } from "./measureParagraph.js";
7
8
  import { clearAllCaches, clearFontMetricsCache, clearParagraphMeasureCache, clearTextWidthCache, getCachedFontMetrics, getCachedParagraphMeasure, getCachedTextWidth, getFontCacheSize, getParagraphCacheSize, getTextCacheSize, getTotalCacheSize, hashParagraphBlock, setCachedFontMetrics, setCachedParagraphMeasure, setCachedTextWidth, setFontCacheSize, setParagraphCacheSize, setTextCacheSize } from "./cache.js";
8
9
  import { FolioMeasurementFeatureFlags, isWorkerFontMetricsEnabled, setFolioMeasurementFlags } from "./featureFlags.js";
9
- export { type FloatingExclusionRect, type FloatingImageZone, type FloatingLineMargins, type FloatingLineSegmentZone, type FolioMeasurementFeatureFlags, type FontMetrics, type FontStyle, type MeasureParagraphOptions, type MeasureProvider, type RunMeasurement, type TextMeasurement, type WrapTextDirection, buildFontString, clampFloatingWrapMargins, clearAllCaches, clearFontMetricsCache, clearParagraphMeasureCache, clearTextWidthCache, findCharacterAtX, getCachedFontMetrics, getCachedParagraphMeasure, getCachedTextWidth, getFloatingAvailableWidth, getFloatingMargins, getFontCacheSize, getFontMetrics, getMeasureProvider, getParagraphCacheSize, getRunCharWidths, getTextCacheSize, getTotalCacheSize, getXForCharacter, halfPtToPx, hashParagraphBlock, isWorkerFontMetricsEnabled, measureParagraph, measureParagraphs, measureRun, measureText, measureTextWidth, ptToPx, pxToHalfPt, pxToPt, pxToTwips, rectsToFloatingZones, resetMeasureProvider, setCachedFontMetrics, setCachedParagraphMeasure, setCachedTextWidth, setFolioMeasurementFlags, setFontCacheSize, setMeasureProvider, setParagraphCacheSize, setTextCacheSize, twipsToPx };
10
+ export { type FloatingExclusionRect, type FloatingImageZone, type FloatingLineMargins, type FloatingLineSegmentZone, type FolioMeasurementFeatureFlags, type FontMetrics, type FontStyle, type LineBreakPolicy, type LineBreakProvider, type MeasureParagraphOptions, type MeasureProvider, type RunMeasurement, type TextMeasurement, type WrapTextDirection, buildFontString, clampFloatingWrapMargins, clearAllCaches, clearFontMetricsCache, clearParagraphMeasureCache, clearTextWidthCache, defaultLineBreakProvider, findCharacterAtX, getCachedFontMetrics, getCachedParagraphMeasure, getCachedTextWidth, getFloatingAvailableWidth, getFloatingMargins, getFontCacheSize, getFontMetrics, getLineBreakProvider, getLineBreakProviderGeneration, getMeasureProvider, getParagraphCacheSize, getRunCharWidths, getTextCacheSize, getTotalCacheSize, getXForCharacter, halfPtToPx, hashParagraphBlock, isWorkerFontMetricsEnabled, measureParagraph, measureParagraphs, measureRun, measureText, measureTextWidth, ptToPx, pxToHalfPt, pxToPt, pxToTwips, rectsToFloatingZones, resetLineBreakProvider, resetMeasureProvider, setCachedFontMetrics, setCachedParagraphMeasure, setCachedTextWidth, setFolioMeasurementFlags, setFontCacheSize, setLineBreakProvider, setMeasureProvider, setParagraphCacheSize, setTextCacheSize, twipsToPx };
@@ -1,8 +1,9 @@
1
1
  import { getFontMetrics, getMeasureProvider, measureRun, measureText, measureTextWidth, resetMeasureProvider, setMeasureProvider } from "./measureProvider.js";
2
2
  import { buildFontString, findCharacterAtX, getXForCharacter, halfPtToPx, ptToPx, pxToHalfPt, pxToPt, pxToTwips, twipsToPx } from "./measureHelpers.js";
3
+ import { defaultLineBreakProvider, getLineBreakProvider, getLineBreakProviderGeneration, resetLineBreakProvider, setLineBreakProvider } from "./lineBreakProvider.js";
3
4
  import { clampFloatingWrapMargins } from "./clampFloatingWrapMargins.js";
4
5
  import { getFloatingAvailableWidth, getFloatingMargins, rectsToFloatingZones } from "./floatingZones.js";
5
6
  import { getRunCharWidths, measureParagraph, measureParagraphs } from "./measureParagraph.js";
6
7
  import { clearAllCaches, clearFontMetricsCache, clearParagraphMeasureCache, clearTextWidthCache, getCachedFontMetrics, getCachedParagraphMeasure, getCachedTextWidth, getFontCacheSize, getParagraphCacheSize, getTextCacheSize, getTotalCacheSize, hashParagraphBlock, setCachedFontMetrics, setCachedParagraphMeasure, setCachedTextWidth, setFontCacheSize, setParagraphCacheSize, setTextCacheSize } from "./cache.js";
7
8
  import { isWorkerFontMetricsEnabled, setFolioMeasurementFlags } from "./featureFlags.js";
8
- export { buildFontString, clampFloatingWrapMargins, clearAllCaches, clearFontMetricsCache, clearParagraphMeasureCache, clearTextWidthCache, findCharacterAtX, getCachedFontMetrics, getCachedParagraphMeasure, getCachedTextWidth, getFloatingAvailableWidth, getFloatingMargins, getFontCacheSize, getFontMetrics, getMeasureProvider, getParagraphCacheSize, getRunCharWidths, getTextCacheSize, getTotalCacheSize, getXForCharacter, halfPtToPx, hashParagraphBlock, isWorkerFontMetricsEnabled, measureParagraph, measureParagraphs, measureRun, measureText, measureTextWidth, ptToPx, pxToHalfPt, pxToPt, pxToTwips, rectsToFloatingZones, resetMeasureProvider, setCachedFontMetrics, setCachedParagraphMeasure, setCachedTextWidth, setFolioMeasurementFlags, setFontCacheSize, setMeasureProvider, setParagraphCacheSize, setTextCacheSize, twipsToPx };
9
+ export { buildFontString, clampFloatingWrapMargins, clearAllCaches, clearFontMetricsCache, clearParagraphMeasureCache, clearTextWidthCache, defaultLineBreakProvider, findCharacterAtX, getCachedFontMetrics, getCachedParagraphMeasure, getCachedTextWidth, getFloatingAvailableWidth, getFloatingMargins, getFontCacheSize, getFontMetrics, getLineBreakProvider, getLineBreakProviderGeneration, getMeasureProvider, getParagraphCacheSize, getRunCharWidths, getTextCacheSize, getTotalCacheSize, getXForCharacter, halfPtToPx, hashParagraphBlock, isWorkerFontMetricsEnabled, measureParagraph, measureParagraphs, measureRun, measureText, measureTextWidth, ptToPx, pxToHalfPt, pxToPt, pxToTwips, rectsToFloatingZones, resetLineBreakProvider, resetMeasureProvider, setCachedFontMetrics, setCachedParagraphMeasure, setCachedTextWidth, setFolioMeasurementFlags, setFontCacheSize, setLineBreakProvider, setMeasureProvider, setParagraphCacheSize, setTextCacheSize, twipsToPx };
@@ -0,0 +1,33 @@
1
+ //#region src/layout-engine/measure/lineBreakProvider.d.ts
2
+ /**
3
+ * Replaceable line-break policy for the layout engine.
4
+ *
5
+ * Providers return UTF-16 offsets so their output composes directly with the
6
+ * run and ProseMirror positions used by the rest of the layout pipeline. The
7
+ * default provider uses the platform's Unicode segmenter, then applies the
8
+ * OOXML line-edge restrictions that are available in the flow model.
9
+ */
10
+ type LineBreakPolicy = {
11
+ /** BCP-47 language tag resolved from `w:lang` for this run. */locale?: string; /** Apply East Asian first/last-character restrictions (`w:kinsoku`). */
12
+ kinsoku?: boolean; /** Document replacement list: characters that may not begin a line. */
13
+ noLineBreaksBefore?: string; /** Document replacement list: characters that may not end a line. */
14
+ noLineBreaksAfter?: string; /** Use the legacy Ethiopic/Amharic compatibility behavior. */
15
+ useLegacyEthiopicAmharicRules?: boolean; /** Keep words made entirely of capital letters unhyphenated. */
16
+ doNotHyphenateCaps?: boolean; /** The run renders with the DOCX all-caps transform. */
17
+ renderedAllCaps?: boolean;
18
+ };
19
+ type LineBreakProvider = {
20
+ /** Legal soft-wrap offsets, in ascending UTF-16 order. */findBreaks: (text: string, policy?: LineBreakPolicy) => number[]; /** Grapheme-safe emergency-wrap offsets, in ascending UTF-16 order. */
21
+ findGraphemeBreaks: (text: string, policy?: LineBreakPolicy) => number[]; /** Dictionary-based discretionary hyphen offsets, in ascending UTF-16 order. */
22
+ findHyphenationBreaks?: (text: string, policy?: LineBreakPolicy) => number[]; /** Whether a trailing grapheme may overhang the line edge. */
23
+ isHangingPunctuation?: (text: string, policy?: LineBreakPolicy) => boolean;
24
+ };
25
+ declare const MAX_HYPHENATION_WORD_LENGTH = 256;
26
+ declare const defaultLineBreakProvider: LineBreakProvider;
27
+ declare const getLineBreakProvider: () => LineBreakProvider;
28
+ /** Changes whenever the active provider changes, so layout caches cannot go stale. */
29
+ declare const getLineBreakProviderGeneration: () => number;
30
+ declare const setLineBreakProvider: (provider: LineBreakProvider) => void;
31
+ declare const resetLineBreakProvider: () => void;
32
+ //#endregion
33
+ export { LineBreakPolicy, LineBreakProvider, MAX_HYPHENATION_WORD_LENGTH, defaultLineBreakProvider, getLineBreakProvider, getLineBreakProviderGeneration, resetLineBreakProvider, setLineBreakProvider };