@arbidocs/blocks 0.4.101 → 0.4.102

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 (180) hide show
  1. package/dist/chunk-6MG62I5B.cjs +272 -0
  2. package/dist/chunk-6MG62I5B.cjs.map +1 -0
  3. package/dist/chunk-EQB3WELX.js +269 -0
  4. package/dist/chunk-EQB3WELX.js.map +1 -0
  5. package/dist/document-tools/AdoptSignatureDialog.d.ts +25 -0
  6. package/dist/document-tools/DocumentToolButtons.d.ts +2 -0
  7. package/dist/document-tools/SignatureRequestBanner.d.ts +18 -0
  8. package/dist/document-tools/index.cjs +175 -0
  9. package/dist/document-tools/index.cjs.map +1 -1
  10. package/dist/document-tools/index.d.ts +3 -1
  11. package/dist/document-tools/index.js +168 -3
  12. package/dist/document-tools/index.js.map +1 -1
  13. package/dist/document-tools/viewer/GuidedSigningViewer.d.ts +26 -0
  14. package/dist/document-tools/viewer/SignatureFieldsOverlay.d.ts +41 -0
  15. package/dist/document-tools/viewer/SignatureRequestEditor.d.ts +26 -0
  16. package/dist/document-tools/viewer/index.cjs +983 -0
  17. package/dist/document-tools/viewer/index.cjs.map +1 -1
  18. package/dist/document-tools/viewer/index.d.ts +3 -0
  19. package/dist/document-tools/viewer/index.js +983 -5
  20. package/dist/document-tools/viewer/index.js.map +1 -1
  21. package/node_modules/@arbidocs/coedit/LICENSE +21 -0
  22. package/node_modules/@arbidocs/coedit/dist/encoding.cjs +751 -0
  23. package/node_modules/@arbidocs/coedit/dist/encoding.cjs.map +1 -0
  24. package/node_modules/@arbidocs/coedit/dist/encoding.d.cts +192 -0
  25. package/node_modules/@arbidocs/coedit/dist/encoding.d.ts +192 -0
  26. package/node_modules/@arbidocs/coedit/dist/encoding.js +716 -0
  27. package/node_modules/@arbidocs/coedit/dist/encoding.js.map +1 -0
  28. package/node_modules/@arbidocs/coedit/dist/hocuspocus.cjs +1759 -0
  29. package/node_modules/@arbidocs/coedit/dist/hocuspocus.cjs.map +1 -0
  30. package/node_modules/@arbidocs/coedit/dist/hocuspocus.d.cts +39 -0
  31. package/node_modules/@arbidocs/coedit/dist/hocuspocus.d.ts +39 -0
  32. package/node_modules/@arbidocs/coedit/dist/hocuspocus.js +1736 -0
  33. package/node_modules/@arbidocs/coedit/dist/hocuspocus.js.map +1 -0
  34. package/node_modules/@arbidocs/coedit/dist/index.cjs +1692 -0
  35. package/node_modules/@arbidocs/coedit/dist/index.cjs.map +1 -0
  36. package/node_modules/@arbidocs/coedit/dist/index.d.cts +16 -0
  37. package/node_modules/@arbidocs/coedit/dist/index.d.ts +16 -0
  38. package/node_modules/@arbidocs/coedit/dist/index.js +1664 -0
  39. package/node_modules/@arbidocs/coedit/dist/index.js.map +1 -0
  40. package/node_modules/@arbidocs/coedit/dist/server.cjs +1852 -0
  41. package/node_modules/@arbidocs/coedit/dist/server.cjs.map +1 -0
  42. package/node_modules/@arbidocs/coedit/dist/server.d.cts +43 -0
  43. package/node_modules/@arbidocs/coedit/dist/server.d.ts +43 -0
  44. package/node_modules/@arbidocs/coedit/dist/server.js +1822 -0
  45. package/node_modules/@arbidocs/coedit/dist/server.js.map +1 -0
  46. package/node_modules/@arbidocs/coedit/dist/session-Cf9iu1Vt.d.cts +82 -0
  47. package/node_modules/@arbidocs/coedit/dist/session-Cf9iu1Vt.d.ts +82 -0
  48. package/node_modules/@arbidocs/coedit/dist/template.cjs +1627 -0
  49. package/node_modules/@arbidocs/coedit/dist/template.cjs.map +1 -0
  50. package/node_modules/@arbidocs/coedit/dist/template.d.cts +125 -0
  51. package/node_modules/@arbidocs/coedit/dist/template.d.ts +125 -0
  52. package/node_modules/@arbidocs/coedit/dist/template.js +1612 -0
  53. package/node_modules/@arbidocs/coedit/dist/template.js.map +1 -0
  54. package/node_modules/@arbidocs/coedit/dist/versions-DXiFQMA6.d.cts +8 -0
  55. package/node_modules/@arbidocs/coedit/dist/versions-DXiFQMA6.d.ts +8 -0
  56. package/node_modules/@arbidocs/coedit/package.json +68 -0
  57. package/node_modules/@docx-editor.dev/core/LICENSE +201 -0
  58. package/node_modules/@docx-editor.dev/core/README.md +125 -0
  59. package/node_modules/@docx-editor.dev/core/THIRD_PARTY_NOTICES.md +56 -0
  60. package/node_modules/@docx-editor.dev/core/dist/automation.cjs +1 -0
  61. package/node_modules/@docx-editor.dev/core/dist/automation.d.ts +162 -0
  62. package/node_modules/@docx-editor.dev/core/dist/automation.js +1 -0
  63. package/node_modules/@docx-editor.dev/core/dist/binding.cjs +1 -0
  64. package/node_modules/@docx-editor.dev/core/dist/binding.d.ts +164 -0
  65. package/node_modules/@docx-editor.dev/core/dist/binding.js +1 -0
  66. package/node_modules/@docx-editor.dev/core/dist/chunk-2GO5T2JU.cjs +1 -0
  67. package/node_modules/@docx-editor.dev/core/dist/chunk-2RC2EPND.js +3 -0
  68. package/node_modules/@docx-editor.dev/core/dist/chunk-4GBTELJV.cjs +28 -0
  69. package/node_modules/@docx-editor.dev/core/dist/chunk-4R3TEXMN.js +18 -0
  70. package/node_modules/@docx-editor.dev/core/dist/chunk-5CSUYANV.js +1 -0
  71. package/node_modules/@docx-editor.dev/core/dist/chunk-6APGXLBB.cjs +3 -0
  72. package/node_modules/@docx-editor.dev/core/dist/chunk-6AWEKBLB.cjs +1 -0
  73. package/node_modules/@docx-editor.dev/core/dist/chunk-6EYYL7FL.js +1 -0
  74. package/node_modules/@docx-editor.dev/core/dist/chunk-6JIJOZDW.js +1 -0
  75. package/node_modules/@docx-editor.dev/core/dist/chunk-BTQKU64Q.cjs +1 -0
  76. package/node_modules/@docx-editor.dev/core/dist/chunk-E7BQA6UP.js +2 -0
  77. package/node_modules/@docx-editor.dev/core/dist/chunk-EO4C5QJW.cjs +2 -0
  78. package/node_modules/@docx-editor.dev/core/dist/chunk-ETTO6GK3.js +1 -0
  79. package/node_modules/@docx-editor.dev/core/dist/chunk-FIJZIQXA.cjs +18 -0
  80. package/node_modules/@docx-editor.dev/core/dist/chunk-FRXAOVTB.js +28 -0
  81. package/node_modules/@docx-editor.dev/core/dist/chunk-J6BVWIDB.js +2 -0
  82. package/node_modules/@docx-editor.dev/core/dist/chunk-JGNVNT2A.cjs +2 -0
  83. package/node_modules/@docx-editor.dev/core/dist/chunk-K5U2PLDP.cjs +2 -0
  84. package/node_modules/@docx-editor.dev/core/dist/chunk-KKIJG3C6.js +2 -0
  85. package/node_modules/@docx-editor.dev/core/dist/chunk-LWDSDCMO.js +27 -0
  86. package/node_modules/@docx-editor.dev/core/dist/chunk-NBCVVZOM.cjs +1 -0
  87. package/node_modules/@docx-editor.dev/core/dist/chunk-O2AL2HSJ.cjs +1 -0
  88. package/node_modules/@docx-editor.dev/core/dist/chunk-O5INAWZQ.js +1 -0
  89. package/node_modules/@docx-editor.dev/core/dist/chunk-QMLCVPCD.cjs +1 -0
  90. package/node_modules/@docx-editor.dev/core/dist/chunk-RCHS4ZEH.cjs +27 -0
  91. package/node_modules/@docx-editor.dev/core/dist/chunk-RIYSCEFQ.js +2 -0
  92. package/node_modules/@docx-editor.dev/core/dist/chunk-S6J4O5XP.js +1 -0
  93. package/node_modules/@docx-editor.dev/core/dist/chunk-SRPILEWV.cjs +5 -0
  94. package/node_modules/@docx-editor.dev/core/dist/chunk-TSLBZ26X.js +1 -0
  95. package/node_modules/@docx-editor.dev/core/dist/chunk-UKVVUHZL.cjs +1 -0
  96. package/node_modules/@docx-editor.dev/core/dist/chunk-VDNNLG6X.js +2 -0
  97. package/node_modules/@docx-editor.dev/core/dist/chunk-VHAVHCQY.js +7 -0
  98. package/node_modules/@docx-editor.dev/core/dist/chunk-VX43VBAX.cjs +7 -0
  99. package/node_modules/@docx-editor.dev/core/dist/chunk-Z6WBFXJU.js +5 -0
  100. package/node_modules/@docx-editor.dev/core/dist/chunk-ZUHOMK52.cjs +2 -0
  101. package/node_modules/@docx-editor.dev/core/dist/collaboration/replication.cjs +1 -0
  102. package/node_modules/@docx-editor.dev/core/dist/collaboration/replication.d.ts +270 -0
  103. package/node_modules/@docx-editor.dev/core/dist/collaboration/replication.js +1 -0
  104. package/node_modules/@docx-editor.dev/core/dist/collaboration.cjs +1 -0
  105. package/node_modules/@docx-editor.dev/core/dist/collaboration.d.ts +269 -0
  106. package/node_modules/@docx-editor.dev/core/dist/collaboration.js +1 -0
  107. package/node_modules/@docx-editor.dev/core/dist/contracts/document.cjs +1 -0
  108. package/node_modules/@docx-editor.dev/core/dist/contracts/document.d.ts +290 -0
  109. package/node_modules/@docx-editor.dev/core/dist/contracts/document.js +0 -0
  110. package/node_modules/@docx-editor.dev/core/dist/contracts/editor.cjs +1 -0
  111. package/node_modules/@docx-editor.dev/core/dist/contracts/editor.d.ts +22 -0
  112. package/node_modules/@docx-editor.dev/core/dist/contracts/editor.js +1 -0
  113. package/node_modules/@docx-editor.dev/core/dist/contracts/interaction.cjs +1 -0
  114. package/node_modules/@docx-editor.dev/core/dist/contracts/interaction.d.ts +103 -0
  115. package/node_modules/@docx-editor.dev/core/dist/contracts/interaction.js +0 -0
  116. package/node_modules/@docx-editor.dev/core/dist/contracts/modules.cjs +1 -0
  117. package/node_modules/@docx-editor.dev/core/dist/contracts/modules.d.ts +143 -0
  118. package/node_modules/@docx-editor.dev/core/dist/contracts/modules.js +1 -0
  119. package/node_modules/@docx-editor.dev/core/dist/contracts/types.cjs +1 -0
  120. package/node_modules/@docx-editor.dev/core/dist/contracts/types.d.ts +11 -0
  121. package/node_modules/@docx-editor.dev/core/dist/contracts/types.js +0 -0
  122. package/node_modules/@docx-editor.dev/core/dist/custom-node-writes-cvL5rhcD.d.ts +323 -0
  123. package/node_modules/@docx-editor.dev/core/dist/dist-PKZR6RSH.js +2 -0
  124. package/node_modules/@docx-editor.dev/core/dist/document-protection-BAGVGqaP.d.ts +20 -0
  125. package/node_modules/@docx-editor.dev/core/dist/editor-Ci7IZGmG.d.ts +2448 -0
  126. package/node_modules/@docx-editor.dev/core/dist/editor-scope-OHPwVQ5l.d.ts +84 -0
  127. package/node_modules/@docx-editor.dev/core/dist/editor.cjs +1 -0
  128. package/node_modules/@docx-editor.dev/core/dist/editor.css +15 -0
  129. package/node_modules/@docx-editor.dev/core/dist/editor.d.ts +1394 -0
  130. package/node_modules/@docx-editor.dev/core/dist/editor.js +1 -0
  131. package/node_modules/@docx-editor.dev/core/dist/export.cjs +2 -0
  132. package/node_modules/@docx-editor.dev/core/dist/export.d.ts +391 -0
  133. package/node_modules/@docx-editor.dev/core/dist/export.js +2 -0
  134. package/node_modules/@docx-editor.dev/core/dist/failure-NS2lsya4.d.ts +115 -0
  135. package/node_modules/@docx-editor.dev/core/dist/font-resolver-BVQB14qT.d.ts +256 -0
  136. package/node_modules/@docx-editor.dev/core/dist/harfbuzz.wasm +0 -0
  137. package/node_modules/@docx-editor.dev/core/dist/headless-document-view-qH331moE.d.ts +143 -0
  138. package/node_modules/@docx-editor.dev/core/dist/image-resources-BN-hUgCn.d.ts +211 -0
  139. package/node_modules/@docx-editor.dev/core/dist/index-Bsg7oIKP.d.ts +3869 -0
  140. package/node_modules/@docx-editor.dev/core/dist/index.cjs +1 -0
  141. package/node_modules/@docx-editor.dev/core/dist/index.d.ts +53 -0
  142. package/node_modules/@docx-editor.dev/core/dist/index.js +1 -0
  143. package/node_modules/@docx-editor.dev/core/dist/layout-shaping-BEyuiQOk.d.ts +246 -0
  144. package/node_modules/@docx-editor.dev/core/dist/layout.cjs +1 -0
  145. package/node_modules/@docx-editor.dev/core/dist/layout.d.ts +3151 -0
  146. package/node_modules/@docx-editor.dev/core/dist/layout.js +1 -0
  147. package/node_modules/@docx-editor.dev/core/dist/node-module-shim-TTKTT66I.js +1 -0
  148. package/node_modules/@docx-editor.dev/core/dist/note-properties-4Y9o6Gyk.d.ts +68 -0
  149. package/node_modules/@docx-editor.dev/core/dist/omml-equation-CuzBL5DD.d.ts +85 -0
  150. package/node_modules/@docx-editor.dev/core/dist/output.cjs +1 -0
  151. package/node_modules/@docx-editor.dev/core/dist/output.d.ts +97 -0
  152. package/node_modules/@docx-editor.dev/core/dist/output.js +1 -0
  153. package/node_modules/@docx-editor.dev/core/dist/protocol-Bqk4Izjc.d.ts +1651 -0
  154. package/node_modules/@docx-editor.dev/core/dist/review-pane-D9jm8DcY.d.ts +128 -0
  155. package/node_modules/@docx-editor.dev/core/dist/review-paragraph-order-DT-n6ouC.d.ts +85 -0
  156. package/node_modules/@docx-editor.dev/core/dist/revision-projection-DZ4kKVev.d.ts +242 -0
  157. package/node_modules/@docx-editor.dev/core/dist/semantic-cell-selection-D9D1KSXq.d.ts +464 -0
  158. package/node_modules/@docx-editor.dev/core/dist/semantic-interaction-CIC-PooQ.d.ts +135 -0
  159. package/node_modules/@docx-editor.dev/core/dist/semantic-paint-BME37OMv.d.ts +326 -0
  160. package/node_modules/@docx-editor.dev/core/dist/semantic-record-queries-CBlZqZNa.d.ts +177 -0
  161. package/node_modules/@docx-editor.dev/core/dist/semantic-records-4B3WwFAi.d.ts +3532 -0
  162. package/node_modules/@docx-editor.dev/core/dist/store.cjs +1 -0
  163. package/node_modules/@docx-editor.dev/core/dist/store.d.ts +2706 -0
  164. package/node_modules/@docx-editor.dev/core/dist/store.js +1 -0
  165. package/node_modules/@docx-editor.dev/core/dist/tree-package-fragment-BeeKxDw8.d.ts +1201 -0
  166. package/node_modules/@docx-editor.dev/core/dist/tree-session-contract-CVbmlMlq.d.ts +664 -0
  167. package/node_modules/@docx-editor.dev/core/dist/types-barrel-D3xCgQX6.d.ts +4269 -0
  168. package/node_modules/@docx-editor.dev/core/licenses/HarfBuzz-COPYING.txt +42 -0
  169. package/node_modules/@docx-editor.dev/core/package.json +140 -0
  170. package/node_modules/@docx-editor.dev/react/LICENSE +201 -0
  171. package/node_modules/@docx-editor.dev/react/README.md +141 -0
  172. package/node_modules/@docx-editor.dev/react/THIRD_PARTY_NOTICES.md +18 -0
  173. package/node_modules/@docx-editor.dev/react/dist/index.d.mts +4928 -0
  174. package/node_modules/@docx-editor.dev/react/dist/index.d.ts +4928 -0
  175. package/node_modules/@docx-editor.dev/react/dist/index.js +4 -0
  176. package/node_modules/@docx-editor.dev/react/dist/index.mjs +4 -0
  177. package/node_modules/@docx-editor.dev/react/package.json +74 -0
  178. package/node_modules/@docx-editor.dev/react/src/styles/editor.css +6 -0
  179. package/node_modules/@docx-editor.dev/react/src/styles/zIndex.ts +28 -0
  180. package/package.json +26 -15
@@ -0,0 +1,3151 @@
1
+ /**
2
+ * `@docx-editor.dev/core/layout` — DOM-free pagination, shaping, and hit testing.
3
+ *
4
+ * Text is measured through an injected `TextMeasurer` and shaped through an injected
5
+ * `TextShaper`, so the same code paginates in a browser and on a server. Points everywhere;
6
+ * twips convert at property-read boundaries.
7
+ *
8
+ * Incremental by construction: per-block cache keys and flow checkpoints mean a pass that
9
+ * changes nothing returns the previous pages by identity.
10
+ *
11
+ * @packageDocumentation
12
+ * @public
13
+ */
14
+ import { s as NumberingLevel, t as NumberingIndex, S as SemanticLayout, b as LayoutBox, D as DrawingPoint, u as DrawingInsets, e as StyleSpanRecord, I as InlineDrawingRecord, M as ModelRange, v as TableBorderSide, w as TableFloatXSpec, x as TableFloatYSpec, P as ParagraphFragmentRecord, B as BlockFragmentRecord, A as AnchoredDrawingRecord, a as PageRecord, y as ResolvedListItem, z as RefFieldContext, T as TextMeasurer, E as InlineDrawingLayoutContext, G as CellBorderBox, J as TableBorderBox, K as TableCellStyleFormatting, O as TableFloatPosition, Q as StyleCascadeTable, C as ContentControlBoundaryRecord$1, m as FixedPointRoundingMode, U as FixedPoint, V as VersionedShapingLibrary, j as TextShaper, W as FontByteValidator, r as ShapedRun, L as LineRecord, X as SourceRange, Y as HyperlinkProjector, Z as FieldLinkProjector, _ as StoryPageFieldNeeds, $ as HyperlinkFieldSpec, a0 as SpanLinkRecord, f as SectionPageBorders, a1 as PageBorderFrameRecord, a2 as ParagraphBorderStrokeRecord, a3 as StrikeoutStrokePt } from './semantic-records-4B3WwFAi.js';
15
+ export { a4 as AUTO_PARAGRAPH_SPACING_PT, a5 as AbstractNumDefinition, a6 as AnchoredDrawingLayoutFallback, a7 as BidiEmbeddingLevels, a8 as BorderGridGeometry, a9 as CascadedParagraphFormatting, aa as CjkTypographySettings, ab as ContentControlGeometryFragment, ac as ContentControlLevel, d as ContentControlLock, ad as ContentControlMappedType, ae as DEFAULT_RUN_STYLE, af as DEFAULT_TAB_INTERVAL_PT, ag as DEFAULT_TAB_INTERVAL_TWIPS, ah as DeclaredFontSubstitution, ai as DrawingClipFallback, aj as DrawingGeometry, ak as DrawingGroupPictureRecord, al as EMPTY_NUMBERING_INDEX, am as EMPTY_TAB_STOPS, an as EquationFractionGeometry, ao as EquationGeometry, ap as EquationNaryGeometry, aq as EquationRadicalGeometry, ar as EquationRowGeometry, as as EquationScriptGeometry, at as EquationSpanRecord, au as EquationTextGeometry, av as FieldAtomMarker, aw as FontFingerprintInputs, n as FontRequest, ax as FontResolutionError, ay as FontResolutionErrorCode, az as FontResourceDefinition, aA as FontResourceInstrumentation, F as FontResourceSnapshot, aB as FontResourceSnapshotOptions, aC as FontSlot, q as FontSubstitution, aD as FontValidationResult, aE as GlyphOutline, aF as GroupTextboxStoryRecord, aG as HARD_MAX_AGGREGATE_FONT_BYTES, aH as HARD_MAX_FONT_BYTES, aI as HARD_MAX_FONT_SOURCES, H as HeaderFooterStoryRecord, aJ as LevelOverride, aK as LineSpacingRule, aL as ListMarkerAlign, p as ListMarkerPictureRecord, aM as ListMarkerRecord, aN as ListSuffix, aO as MAX_BORDER_SPACE_PT, aP as MAX_BORDER_WIDTH_PT, aQ as MAX_LVL_OVERRIDES, aR as MAX_NUMBERING_DEFINITIONS, aS as MAX_PARAGRAPH_SPACING_PT, aT as MAX_SHAPING_CONTEXT, aU as MAX_STYLE_DEFINITIONS, aV as MAX_TABLE_BORDER_STROKES, aW as MAX_TAB_POSITION_TWIPS, aX as MAX_TAB_STOPS, l as NormalizationPolicy, N as NoteAreaRecord, c as NoteStoryRecord, aY as NumDefinition, aZ as NumberingLevelIndent, a_ as NumberingPictureBullet, a$ as PAGE_BORDER_SIDES, b0 as PARAGRAPH_BORDER_SIDES, b1 as PageBorderDisplay, b2 as PageBorderOffsetFrom, b3 as PageBorderSide, b4 as PageBorderStrokeRecord, b5 as PageBorderZOrder, b6 as PageRefCalibrationCell, b7 as PageRefFieldProjection, b8 as ParagraphAutoSpacingContext, b9 as ParagraphBorderEdge, ba as ParagraphBorderSide, bb as ParagraphBorders, bc as ParagraphBottomBorderRecord, bd as ParagraphIndent, be as ParagraphLayoutInputs, bf as ParagraphLineSpacing, bg as ParagraphSpacing, bh as RefFieldSpec, bi as RefNoteParts, bj as ResolvedCellBorders, k as ResolvedFont, bk as ResolvedPictureBullet, R as ResolvedRunStyle, bl as ResolvedTabStops, bm as ResolvedTableBorderEdge, bn as ResolvedTableBorderEdgeSegment, bo as ResolvedUnderline, bp as SINGLE_LINE_SPACING, bq as ScriptItem, br as SemanticArtifactRootStoryKind, bs as SemanticArtifactStoryKind, bt as SemanticCommentArtifactRecord, bu as SemanticReviewArtifactOccurrence, bv as SemanticReviewArtifactOccurrenceGeometry, bw as SemanticReviewArtifactPosition, o as SemanticReviewArtifactRecord, bx as SemanticReviewArtifactSource, by as SemanticTrackedChangeArtifactRecord, bz as ShapeInput, bA as ShapedCluster, bB as ShapedFontSpan, bC as ShapedGlyph, bD as ShapedRunComparatorInputs, bE as ShapedVerticalMetrics, bF as ShapingContext, bG as ShapingEnvironment, bH as ShapingEnvironmentFingerprintInputs, i as ShapingEnvironmentInput, bI as StyleDefinition, bJ as TAB_LEADER_GLYPH, bK as TabAlignment, bL as TabDestination, bM as TabLeader, bN as TabStop, bO as TableBorderSideName, bP as TableBorderStrokeRecord, h as TableCellFragmentRecord, bQ as TableFragmentRecord, g as TableRowFragmentRecord, bR as TextDirection, bS as TextboxStoryFallbackReason, bT as TextboxStoryLayout, bU as UnsupportedScriptError, bV as VerticalAlign, bW as appliedSpaceBefore, bX as applyLineSpacing, bY as baselineShiftPtOf, bZ as borderExtentPt, b_ as borderWeight, b$ as bottomBorderExtentPt, c0 as boundedStructuralFontValidator, c1 as buildNumberingIndex, c2 as buildStyleCascadeTable, c3 as cascadeParagraphFormatting, c4 as cascadeRunProperties, c5 as cascadedBottomBorder, c6 as cascadedParagraphBorders, c7 as cascadedTabStops, c8 as collapsedSpaceBefore, c9 as createFontResourceSnapshot, ca as createShapedRun, cb as createShapingEnvironment, cc as defaultTabIntervalFromSettings, cd as displayText, ce as fixedPoint, cf as fontRequestKey, cg as glyphSizeFactorOf, ch as isValidStyleId, ci as itemizeScriptFontSlots, cj as layoutEquation, ck as measureDisplayText, cl as mergeListIndent, cm as nextTabDestination, cn as pageBordersFingerprint, co as paragraphBorderExtentPt, cp as paragraphBorderStrokeWidthPt, cq as paragraphBorders, cr as paragraphBordersFingerprint, cs as paragraphBreaksBefore, ct as paragraphContextualSpacing, cu as paragraphLineSpacing, cv as paragraphSpacing, cw as paragraphTabStops, cx as parsePageBorders, cy as parseRefInstruction, cz as readBorderSide, cA as readCellBorders, cB as readNumPr, cC as readTableBorders, cD as resolveBorderConflict, cE as resolveNumberingLevel, cF as resolveParagraphLayoutInputs, cG as resolveRunStyle, cH as resolveStoryListItems, cI as resolveStoryRefFields, cJ as resolveTableCellBorderGrid, cK as runStylesEqual, cL as shapedRunComparatorInputs, cM as shapingEnvironmentFingerprint, cN as shapingEnvironmentFingerprintInputs, cO as styleForFontSlot, cP as tabAdvanceWidth, cQ as tabStopsFingerprint, cR as withDefaultTabInterval, cS as withNumberingStyleLinks, cT as withResolvedListItems } from './semantic-records-4B3WwFAi.js';
16
+ export { C as CacheLookup, b as CacheMiss, c as CacheProvenance, e as LayoutEnvironmentShapedMeasurerOptions, f as LayoutFontConfiguration, g as LayoutFontSource, h as LayoutFontSubstitution, i as LayoutShapingConfigurationError, j as LayoutShapingEnvironment, L as LayoutShapingInstrumentation, a as LayoutShapingOptions, O as OperationSnapshot, k as OperationSnapshotField, l as OperationSnapshotGuard, P as PreparedLayoutFontConfiguration, R as ResolvedCache, m as ResourceDependencyProvenance, S as ShapedMeasurerOptions, n as captureOperationSnapshot, o as createLayoutShapedMeasurer, p as createLayoutShaping, q as createShapedMeasurer, d as disposeLayoutShaping, r as guardOperationSnapshot, s as layoutFontConfigurationFingerprint, t as prepareLayoutFontConfiguration } from './layout-shaping-BEyuiQOk.js';
17
+ import { a as SemanticStoryKind, S as SemanticRootStoryKind, b as SemanticStoryVisit, T as TextboxOwnerRecord } from './semantic-record-queries-CBlZqZNa.js';
18
+ export { c as SemanticDrawingLayer, d as SemanticDrawingVisit, e as StoryDrawingContext, f as StoryDrawingHost, g as StoryParagraphFragmentContext, h as contentControlsOfLayout, i as effectiveContentControlLock, j as forEachSemanticDrawing, k as forEachSemanticStory, l as forEachStoryParagraphFragment, m as fragmentsOfParagraph, n as lineAtPosition, o as linesOf, p as paragraphFragmentsOf, q as paragraphFragmentsOfBlocks, u as unionLayoutBoxes } from './semantic-record-queries-CBlZqZNa.js';
19
+ import { ak as ImpactClass, N as OoxmlNode, X as OoxmlProperty, Z as OoxmlElement, O as OoxmlPart, W as TreeDocOp, Q as OoxmlPackage, b9 as ContentControlKind, ba as ContentControlLevel, bc as ContentControlLock, b6 as ContentControlDataBinding, a as ReviewItem } from './types-barrel-D3xCgQX6.js';
20
+ export { b1 as CommentAnchor, b2 as CommentPosition, C as CommentRecord, aY as CommentThreadState, aK as DrawingAccessibility, aw as DrawingHorizontalReferenceFrame, aL as DrawingImageEffects, aM as DrawingTransform, ax as DrawingVerticalReferenceFrame, an as ImageWrapTarget, c as ReviewCommentItem, d as ReviewCustomItem, R as ReviewModelInput, e as ReviewPosition, f as ReviewRange, b as ReviewRevisionItem, g as ReviewRevisionKind, al as SourceCrop, aJ as TableBorderStyle, dB as VectorShapeComponent, aN as VectorShapeProjection, cs as W15_NAMESPACE_URI, dC as activeReviewItem, cW as firstReviewRange, ds as reviewItemKey, dD as reviewItemPositionRank, dt as reviewItemRanges, dE as reviewItemsAt, dF as reviewThreadRootOf } from './types-barrel-D3xCgQX6.js';
21
+ import { D as DocumentProperties, H as HeadlessDocumentView, r as relationshipTargetIn } from './headless-document-view-qH331moE.js';
22
+ import { o as RevisionAttribution, b as RevisionDisplayMode, c as RevisionAuthorFilter } from './revision-projection-DZ4kKVev.js';
23
+ export { p as DEFAULT_REVISION_DISPLAY_MODE, q as RevisionFilter, s as RevisionKind, t as formatRevisionOf, u as markRevisionRemovesMark, v as paragraphMarkFormatRevisionOf, w as paragraphMarkRevisionOf, x as paragraphMarkRevisionsOf, y as revisionAuthorFilter, z as revisionsAreDeletion, A as revisionsVisible, B as shownMarkRevision } from './revision-projection-DZ4kKVev.js';
24
+ import { b as ResolvedFootnoteProperties, R as ResolvedEndnoteProperties } from './note-properties-4Y9o6Gyk.js';
25
+ import { e as TreeModelChange, N as NoteKind, aA as noteTypeOf, k as FieldResultsMode } from './tree-package-fragment-BeeKxDw8.js';
26
+ import { P as PageGeometry, a as SectionColumns } from './semantic-cell-selection-D9D1KSXq.js';
27
+ export { C as CellSelection, D as DEFAULT_PAGE_GEOMETRY, b as DEFAULT_SECTION_PROPERTIES, c as DEFAULT_VERTICAL_WEIGHT, d as DocumentSection, e as DocumentSectionsEnumeration, f as DrawingOverlayFrame, H as HitPoint, g as HitTestOptions, M as MAX_DOCUMENT_SECTIONS, h as PlacedCell, i as SectionBreakType, j as SectionColumnDefinition, k as SectionMargins, l as SectionPageNumbering, S as SectionProperties, m as SemanticHit, n as SemanticHitDrawing, T as TableCellAddress, o as TableCellContext, p as caretBoxOnLine, q as cellSelectionBetween, r as cellSelectionRects, s as cellSelectionText, t as contentControlAtPoint, u as enumerateDocumentSections, v as enumerateDocumentSectionsBounded, w as findDrawingOverlayFrameInLayout, x as geometryOfSection, y as hitTestPage, z as hitTestSheet, A as isFurniturePoint, B as lineEndOffset, E as pageAtY, F as paragraphSectionNode, G as paragraphsInCells, I as parsePageNumbering, J as parseSectionProperties, K as projectedSectionSourceIndexes, L as readSectionProperties, N as spanOffsetX, O as spansInCells, Q as tableContextAt } from './semantic-cell-selection-D9D1KSXq.js';
28
+ export { M as MAX_SDT_NESTING, c as collectFlowBlocks, a as commentBodyText, b as commentInitials, j as contentControlContentChildren, i as isContentControl, f as isContentControlContent, p as paragraphOrderOfPart } from './review-paragraph-order-DT-n6ouC.js';
29
+ import { S as SemanticPosition, b as SelectionRect, a as SemanticSelection, C as CaretGeometry } from './semantic-interaction-CIC-PooQ.js';
30
+ export { c as CaretAtOptions, M as MoveCaretOptions, N as NavigationCommand, d as caretAt, e as caretStops, f as caretStopsForBlocks, g as compositionAnchor, h as contentControlsInLayout, i as deletedTextBoundaries, m as moveCaret, p as paragraphTextFromLayout, s as spansInSelection } from './semantic-interaction-CIC-PooQ.js';
31
+ import './image-resources-BN-hUgCn.js';
32
+ import './omml-equation-CuzBL5DD.js';
33
+ import './editor-scope-OHPwVQ5l.js';
34
+
35
+ /**
36
+ * The result of counting ONE list paragraph: the level that applied, the counter vector after it,
37
+ * and the marker text a reader sees.
38
+ *
39
+ * Counters are a vector across all nine levels, not a single number, because a deeper level
40
+ * restarting resets the ones below it while leaving those above intact.
41
+ */
42
+ interface ListCounterAdvance {
43
+ /** Effective abstract numbering template for this num instance. */
44
+ readonly abstractNumId: string;
45
+ readonly numId: string;
46
+ readonly ilvl: number;
47
+ readonly level: NumberingLevel;
48
+ /** Counter vector after this item was counted (indices 0..8). */
49
+ readonly counters: readonly number[];
50
+ /** Expanded marker text (empty when vanished / empty lvlText). */
51
+ readonly markerText: string;
52
+ }
53
+ /**
54
+ * The running counters for one layout pass over one story.
55
+ *
56
+ * Stateful and order-dependent by nature: a list number is a function of every numbered paragraph
57
+ * before it, which is why markers are computed during layout and cannot be read off a paragraph
58
+ * in isolation.
59
+ */
60
+ interface ListCounterState {
61
+ /**
62
+ * Advance counters for one list paragraph.
63
+ *
64
+ * Returns null when the numbering definition cannot be resolved — callers treat the
65
+ * paragraph as non-list (inert fallback).
66
+ */
67
+ advance(numId: string, ilvl: number): ListCounterAdvance | null;
68
+ }
69
+ /**
70
+ * Create a fresh counter bag for one story (body, or one header/footer part).
71
+ */
72
+ declare function createListCounterState(index: NumberingIndex): ListCounterState;
73
+
74
+ /** Synchronous platform-neutral SHA-256 used before font bytes cross into a shaping implementation. */
75
+ declare const sha256FontBytes: (bytes: Uint8Array) => string;
76
+
77
+ /**
78
+ * What a batch of commits can affect.
79
+ *
80
+ * `paragraphIds` is the set the commits touched directly. What that set MEANS depends on
81
+ * `impact`: for a local impact it bounds the work, for a structural one it only says where
82
+ * the reflow starts.
83
+ */
84
+ interface LayoutScope {
85
+ readonly impact: ImpactClass;
86
+ /** Node ids touched directly by the coalesced commits. */
87
+ readonly paragraphIds: ReadonlySet<string>;
88
+ /** Ids the commits created — no previous layout exists for these. */
89
+ readonly created: ReadonlySet<string>;
90
+ /** Ids the commits removed — any retained layout for these must be released. */
91
+ readonly deleted: ReadonlySet<string>;
92
+ /** Cache keys the commits invalidated (styles, numbering, fonts). */
93
+ readonly dependencyKeys: ReadonlySet<string>;
94
+ /** True when the block SEQUENCE changed, so ids alone cannot bound the reflow. */
95
+ readonly structural: boolean;
96
+ /** The revision this scope describes — the layout result must carry the same one. */
97
+ readonly revision: number;
98
+ }
99
+ /** How the scheduler produces layouts and when it publishes them. */
100
+ interface LayoutSchedulerOptions {
101
+ /**
102
+ * Produce a complete layout for the CURRENT model state.
103
+ *
104
+ * Given the accumulated scope so an incremental implementation can use it. It must tag
105
+ * the result with the revision it actually read, which is what makes staleness detectable
106
+ * rather than assumed.
107
+ */
108
+ readonly run: (scope: LayoutScope) => SemanticLayout;
109
+ /** The model's revision right now. Read at publish time, never cached. */
110
+ readonly currentRevision: () => number;
111
+ /** Called with a layout that is known current. Never called with a stale one. */
112
+ readonly publish: (layout: SemanticLayout, scope: LayoutScope) => void;
113
+ /**
114
+ * Defer work to a later turn, returning a canceller.
115
+ *
116
+ * Injected rather than assumed: the browser wants an animation frame, tests want to step
117
+ * the queue by hand, and a server has neither.
118
+ */
119
+ readonly schedule?: (run: () => void) => () => void;
120
+ /**
121
+ * Run a layout in slices instead of in one call (task 9.5).
122
+ *
123
+ * Optional: without it `run` is used and completes in one turn, which is right for a
124
+ * document small enough that slicing costs more than it saves.
125
+ */
126
+ readonly runCooperatively?: (scope: LayoutScope) => CooperativeRun;
127
+ }
128
+ interface CooperativeRun {
129
+ /**
130
+ * Advance the work. Returns the finished layout, or null to be called again.
131
+ *
132
+ * Split so a global relayout does not hold the main thread for the whole document: the
133
+ * scheduler yields between slices and a newer revision can cancel the run mid-flight
134
+ * rather than the user waiting for work whose result is already stale.
135
+ */
136
+ step(): SemanticLayout | null;
137
+ /** Abandon the run. Nothing partial is ever published. */
138
+ cancel(): void;
139
+ }
140
+ /**
141
+ * Coalesces commits into layout passes.
142
+ *
143
+ * A keystroke is one commit but must not be one full layout pass, so changes accumulate into a
144
+ * scope and are laid out together. Every published layout is tagged with the revision it actually
145
+ * read, which is what makes a stale result detectable rather than merely late.
146
+ */
147
+ interface LayoutScheduler {
148
+ /** Record a commit. Coalesces with anything already pending. */
149
+ notify(change: TreeModelChange): void;
150
+ /** Request a relayout for a reason the store did not report (page size, zoom, fonts). */
151
+ invalidateAll(revision: number, reason?: string): void;
152
+ /** Run any pending work now, synchronously. Returns whether a layout was published. */
153
+ flush(): boolean;
154
+ /** The scope that would be used if `flush` ran now, or null when nothing is pending. */
155
+ pending(): LayoutScope | null;
156
+ /** Drop pending work without publishing — for teardown. */
157
+ cancel(): void;
158
+ /** How many layouts were discarded for being stale. Diagnostics, and a test hook. */
159
+ readonly staleDiscards: number;
160
+ /** How many cooperative runs were abandoned because a newer revision arrived. */
161
+ readonly cancelledRuns: number;
162
+ }
163
+ /**
164
+ * Build the scheduler that turns a stream of commits into coalesced layout passes.
165
+ *
166
+ * Keystrokes arrive faster than a document can be laid out, so changes accumulate into one scope
167
+ * and are laid out together rather than once per commit.
168
+ */
169
+ declare function createLayoutScheduler(options: LayoutSchedulerOptions): LayoutScheduler;
170
+
171
+ interface PositionedTableAnchorSignal {
172
+ readonly anchorId: string;
173
+ readonly column: number;
174
+ readonly fragmentIndex: number;
175
+ readonly anchorY: number;
176
+ }
177
+ interface PendingPositionedTableTokens {
178
+ readonly token: string;
179
+ readonly previous: PendingPositionedTableTokens | undefined;
180
+ readonly length: number;
181
+ readonly signature: string;
182
+ }
183
+
184
+ /**
185
+ * What the BODY flow knows about page numbering while it MEASURES a page-field placeholder.
186
+ *
187
+ * The section's `w:pgNumType/@w:fmt` binds a PAGE field with no `\#` picture and no `\*`
188
+ * number format. A field that states either renders through it on every section.
189
+ */
190
+ interface BodyPageFieldContext {
191
+ /** The section's `w:pgNumType/@w:fmt`; absent when the section authors none. */
192
+ readonly format?: string;
193
+ }
194
+ /**
195
+ * Page-field evaluation context for furniture projection.
196
+ *
197
+ * `pageNumber` is the displayed PAGE value after section `w:pgNumType/@w:start` (1-based).
198
+ * `pageCount` is document NUMPAGES. `sectionPageCount` is SECTIONPAGES for the attached
199
+ * section. `format` is the authored `w:pgNumType/@w:fmt` applied only to PAGE. `sheetNumber` is
200
+ * the 1-based physical sheet, which a restart does not renumber: it decides which margin an
201
+ * `inside` or `outside` header frame takes.
202
+ *
203
+ * A new field here must also join `sameFieldPageContext` below, or finalize compares two
204
+ * contexts that differ in it as equal and keeps a reused story's stale text.
205
+ */
206
+ interface FieldPageContext {
207
+ /** Local character-style results for one physical page. @internal */
208
+ readonly characterStyleValues?: ReadonlyMap<string, string>;
209
+ readonly pageNumber: number;
210
+ readonly pageCount: number;
211
+ /** SECTIONPAGES; defaults to `pageCount` when omitted (single-section callers). */
212
+ readonly sectionPageCount?: number;
213
+ /** Authored ST_NumberFormat for PAGE; absent → decimal. */
214
+ readonly format?: string;
215
+ /** 1-based physical sheet; absent → `pageNumber` (single-section callers). */
216
+ readonly sheetNumber?: number;
217
+ }
218
+ /**
219
+ * Format a displayed PAGE value through the shared ST_NumberFormat resolver.
220
+ *
221
+ * Unknown / script-specific formats fall back to decimal (same convention as list markers).
222
+ * `none` / `bullet` are meaningless for page numbers and also fall back to decimal so a
223
+ * hostile fmt cannot blank the furniture.
224
+ */
225
+ declare function formatPageNumber(value: number, format: string | undefined): string;
226
+
227
+ type WrapTextSide = 'bothSides' | 'left' | 'right' | 'largest';
228
+ interface WrapExclusionInput {
229
+ readonly mode: 'square' | 'tight' | 'through' | 'topAndBottom';
230
+ readonly contentBounds: LayoutBox;
231
+ readonly polygon: readonly DrawingPoint[] | null;
232
+ readonly clipPolygon: readonly DrawingPoint[] | null;
233
+ readonly wrapDistances: DrawingInsets;
234
+ readonly effectInsets: DrawingInsets;
235
+ readonly textSide: WrapTextSide;
236
+ readonly contentLeft: number;
237
+ readonly contentRight: number;
238
+ }
239
+
240
+ /** Paint layer relative to body text — not the OOXML wrap element. */
241
+ type DrawingPaintLayer = 'behind' | 'inFront';
242
+ /** The header or footer object a furniture exclusion zone comes from. */
243
+ interface FurnitureZoneSource {
244
+ readonly partName: string;
245
+ readonly kind: 'drawing' | 'table';
246
+ readonly nodeId: string;
247
+ }
248
+ interface ExclusionZone {
249
+ /** Objects outside body drawing flow publish their exclusion directly instead of synthesizing it. */
250
+ readonly sourceKind?: 'table' | 'frame' | 'furniture';
251
+ /**
252
+ * Placed by an earlier section on the sheet a `continuous` section continues on. Its anchor
253
+ * is not in the continued section, so the zone reaches every line and column there.
254
+ */
255
+ readonly earlierSection?: boolean;
256
+ /**
257
+ * The header or footer object a `furniture` zone comes from. Node ids are unique only within
258
+ * one part, so a furniture zone is identified by its part, its kind, and its node together
259
+ * (see {@link exclusionZoneIdentity}), never by a composed id string.
260
+ */
261
+ readonly furnitureSource?: FurnitureZoneSource;
262
+ readonly drawingNodeId: string;
263
+ readonly anchorParagraphId: string;
264
+ /** UTF-16 model offset of the anchor atom — exclusions apply at/after this point in the paragraph. */
265
+ readonly anchorModelStart: number;
266
+ /**
267
+ * The band's vertical position is resolved against the page or a margin, not against the
268
+ * anchor's own flow position, so it cannot move when the text beside it reflows.
269
+ *
270
+ * Such a band excludes every line it crosses on its page, including lines that PRECEDE the
271
+ * anchor — a picture pinned to the top of the margin wraps the paragraphs above its anchor
272
+ * exactly as it wraps the ones below. Flow-relative bands (`paragraph`, `line`) stay
273
+ * forward-only: reaching back would move the anchor that positions them.
274
+ */
275
+ readonly pageFramedBand?: boolean;
276
+ readonly sourceOrder: number;
277
+ readonly paintLayer: DrawingPaintLayer;
278
+ readonly relativeHeight: number;
279
+ readonly allowOverlap: boolean;
280
+ /** Owning column index in a multi-column section — 0 for single-column and HF stories. */
281
+ readonly columnIndex: number;
282
+ /** Resolved top edge in page-content coordinates after overlap displacement. */
283
+ readonly y: number;
284
+ readonly verticalBand: LayoutBox;
285
+ readonly input: WrapExclusionInput;
286
+ }
287
+
288
+ type AutonumFieldKind = 'AUTONUM' | 'AUTONUMLGL' | 'AUTONUMOUT';
289
+ /** One recognized AUTONUM-family instruction: the kind and its supported switches. */
290
+ interface AutonumFieldSpec {
291
+ readonly kind: AutonumFieldKind;
292
+ /** ST_NumberFormat resolved from the `\*` switch; null paints decimal. */
293
+ readonly numFmt: string | null;
294
+ /** `\e`: display the number without its trailing period. */
295
+ readonly suppressPeriod: boolean;
296
+ }
297
+ /**
298
+ * Recognize `AUTONUM | AUTONUMLGL | AUTONUMOUT [\* <format>] [\e] [\* MERGEFORMAT]`, or null
299
+ * for anything else. Any unrecognized switch fails the parse so the field stays inert (paints
300
+ * nothing — its historical rendering), never the raw instruction, never a guessed number.
301
+ */
302
+ declare function parseAutonumInstruction(raw: string): AutonumFieldSpec | null;
303
+
304
+ /**
305
+ * Lookup of derived display marks keyed by {@link formatNoteScopeId}.
306
+ *
307
+ * `null` mark = customMarkFollows (no automatic digits). Absent key = dangling / unknown
308
+ * — fail-open with an empty display (model atom preserved).
309
+ */
310
+ interface NoteMarkContext {
311
+ /**
312
+ * scopeId → formatted mark (or null when suppressed).
313
+ *
314
+ * IMMUTABLE once the context is built. `noteMarksCacheToken` memoizes on the context object
315
+ * and that token reaches every paragraph's cache key, so a later `marks.set` would pin the
316
+ * whole document to a token for marks it no longer has.
317
+ */
318
+ readonly marks: ReadonlyMap<string, string | null>;
319
+ /**
320
+ * When set, every automatic mark measures at least this string's width (eachPage
321
+ * reservation). The string is the widest-measuring candidate under the effective mark
322
+ * style (actual marks plus a bounded per-section value window) — not merely the longest
323
+ * by codepoint count. Display text may still be the real mark; measurement uses the wider
324
+ * of the two so digit-width / proportional-glyph feedback cannot oscillate.
325
+ */
326
+ readonly reservedMarkText?: string;
327
+ /**
328
+ * Scope id of the note story currently being laid out (`footnote:N` / `endnote:N`).
329
+ *
330
+ * Body `noteReference` atoms carry `@w:id`; note-body `noteRef` atoms do not — they
331
+ * inherit identity from the enclosing note via this key.
332
+ */
333
+ readonly activeNoteKey?: string;
334
+ /** Project separator markers as inline rules inside their authored stories. */
335
+ readonly measureSeparatorMarkers?: true;
336
+ }
337
+ /**
338
+ * The key one note's mark is stored under — `footnote:N` / `endnote:N`.
339
+ *
340
+ * The same encoding `EditorScope` uses for note ids, so a mark context and a scope address name
341
+ * the same note without a translation step.
342
+ */
343
+ declare function noteMarkKey(noteKind: NoteKind, noteId: number): string;
344
+ /**
345
+ * Navigation role for a projected note atom:
346
+ * - `to-note` — body `noteReference` jumps into the note story
347
+ * - `to-body` — note-body `noteRef` jumps back to the body citation
348
+ */
349
+ type NoteNavDirection = 'to-note' | 'to-body';
350
+ interface ProjectedNoteMark {
351
+ readonly noteSeparator?: 'separator' | 'continuationSeparator';
352
+ readonly text: string;
353
+ readonly measureText?: string;
354
+ readonly projected: boolean;
355
+ readonly kind: NoteKind | null;
356
+ readonly noteId: number | null;
357
+ readonly nav?: NoteNavDirection;
358
+ readonly scopeId?: string;
359
+ }
360
+ /** Resolve display text for a noteReference / noteRef node under a mark context. */
361
+ declare function projectedNoteMarkText(node: OoxmlNode, context: NoteMarkContext | undefined): ProjectedNoteMark | null;
362
+
363
+ /** A fingerprint over one paragraph's layout inputs. */
364
+ type ParagraphLayoutKey = string;
365
+ /** Cache counters, for asserting that incremental layout is actually reusing work. */
366
+ interface LayoutCacheStats {
367
+ readonly hits: number;
368
+ readonly misses: number;
369
+ readonly evictions: number;
370
+ readonly size: number;
371
+ }
372
+ /**
373
+ * The per-paragraph measurement cache.
374
+ *
375
+ * Caches the BREAK only — where a paragraph's lines fall at a given width — never its placement.
376
+ * An edit high in a document still repaginates everything below it, while paragraphs nobody
377
+ * touched are never measured again.
378
+ */
379
+ interface ParagraphLayoutCache<T> {
380
+ /**
381
+ * Whether values released after placement remain available to later layout passes.
382
+ *
383
+ * One-shot exporters set this false so producers may avoid defensive snapshots whose
384
+ * only purpose is protecting a value reused by a future pass.
385
+ */
386
+ readonly retainAcrossPasses?: boolean;
387
+ /**
388
+ * Derive a break key inside this cache's ownership scope.
389
+ *
390
+ * Export caches use this hook to release construction-only subtree fingerprints together
391
+ * with measured breaks. Optional so structural/custom cache implementations remain valid.
392
+ */
393
+ keyFor?(inputs: ParagraphKeyInputs): ParagraphLayoutKey;
394
+ get(key: ParagraphLayoutKey): T | undefined;
395
+ set(key: ParagraphLayoutKey, value: T): void;
396
+ /** Release a break after final placement when this cache was created for a one-shot pass. */
397
+ release?(key: ParagraphLayoutKey): void;
398
+ /** Drop entries for paragraphs a commit removed, so the cache cannot grow without bound. */
399
+ retain(keys: ReadonlySet<ParagraphLayoutKey>): void;
400
+ /**
401
+ * Whether THIS published pass should pay for a document-wide {@link retain} sweep.
402
+ *
403
+ * Retention only trims memory — the aging window tolerates deferral — while building the
404
+ * union of live keys costs real time on a large document, so callers ask per pass and
405
+ * sweep on a stride. Per cache instance, so one editor's cadence cannot starve another's.
406
+ * Optional for compatibility: a cache without it is retained on every pass.
407
+ */
408
+ retentionPassDue?(): boolean;
409
+ clear(): void;
410
+ readonly stats: LayoutCacheStats;
411
+ }
412
+ /**
413
+ * Everything that decides whether a cached paragraph break is still valid.
414
+ *
415
+ * `producer` is in the key because a font arriving after first paint changes every advance in the
416
+ * document while no content changes — without it the cache would serve the pre-font layout
417
+ * forever.
418
+ */
419
+ interface ParagraphKeyInputs {
420
+ readonly paragraph: OoxmlNode;
421
+ readonly properties: readonly OoxmlProperty[];
422
+ /** Available width, which decides where the lines break. */
423
+ readonly width: number;
424
+ /**
425
+ * Who produced the measurements.
426
+ *
427
+ * Fonts loading after first paint change every advance while no content changes, so a
428
+ * cache keyed on content alone would serve the pre-font layout forever.
429
+ */
430
+ readonly producer: string;
431
+ /**
432
+ * Inline drawing projection/resource epoch for this paragraph.
433
+ *
434
+ * Pending→ready/refused transitions and extent/hidden changes must invalidate breaks even
435
+ * when paragraph text is unchanged.
436
+ */
437
+ readonly drawingToken?: string;
438
+ /** Paragraph-local semantic projection identity (links and live metadata fields). */
439
+ readonly projectionToken?: string;
440
+ /** Active page exclusion zones affecting this paragraph's break. */
441
+ readonly exclusionToken?: string;
442
+ }
443
+ /**
444
+ * The cache key for one paragraph's measured break.
445
+ *
446
+ * Folds in the content, the available width, and the measurement producer. Anything that changes
447
+ * where lines fall must be in here, or the cache serves a break taken under different conditions.
448
+ */
449
+ declare function paragraphLayoutKey(inputs: ParagraphKeyInputs): ParagraphLayoutKey;
450
+ /** How large the paragraph cache grows before least-recently-used eviction. */
451
+ interface ParagraphLayoutCacheOptions {
452
+ /**
453
+ * Entries retained before the least recently used are dropped.
454
+ *
455
+ * The default has to exceed a realistic document, or a full pass evicts exactly what the
456
+ * next one needs and the cache costs more than it saves. Unset, an estimated byte budget
457
+ * bounds the cache instead, because one cached break can be a hundred times another.
458
+ */
459
+ readonly maxEntries?: number;
460
+ /** Keep placed breaks for later revisions. Default true; false bounds one-shot exporters. */
461
+ readonly retainAcrossPasses?: boolean;
462
+ }
463
+
464
+ /**
465
+ * A bounded least-recently-used cache with generation-scoped retention.
466
+ *
467
+ * Bounded because a long editing session touches far more paragraph states than a document
468
+ * contains — every keystroke mints a new key for the paragraph being typed in — and an
469
+ * unbounded cache would hold every intermediate state of the session.
470
+ *
471
+ * The bound never evicts the CURRENT working set: entries named by this generation's
472
+ * retain or touched since it began are skipped, and the cache grows past its soft budget
473
+ * when a document is larger than it — evicting live entries made every full pass on a
474
+ * 500-page document re-measure the whole document.
475
+ */
476
+ declare function createParagraphLayoutCache<T>(options?: ParagraphLayoutCacheOptions): ParagraphLayoutCache<T>;
477
+
478
+ interface TocLinkRange {
479
+ readonly start: number;
480
+ readonly end: number;
481
+ }
482
+ type TocLinkRanges = ReadonlyMap<string, readonly TocLinkRange[]>;
483
+
484
+ interface FieldCodeRange {
485
+ readonly start: number;
486
+ readonly end: number;
487
+ readonly code?: string;
488
+ readonly sourceNodeId?: string;
489
+ readonly suppressParagraph: boolean;
490
+ }
491
+ type FieldCodeRanges = ReadonlyMap<string, readonly FieldCodeRange[]>;
492
+
493
+ interface PendingLine {
494
+ readonly spans: StyleSpanRecord[];
495
+ readonly drawings: InlineDrawingRecord[];
496
+ readonly start: number;
497
+ end: number;
498
+ width: number;
499
+ height: number;
500
+ baseline: number;
501
+ /**
502
+ * Space ABOVE the glyph band inside {@link height}.
503
+ *
504
+ * Exact spacing can center the glyphs and move the baseline. Auto/atLeast spacing leaves
505
+ * this at zero and puts its extra depth below instead.
506
+ */
507
+ leading: number;
508
+ /**
509
+ * Auto/atLeast line-spacing depth below the painted glyph band.
510
+ *
511
+ * Word lets this external depth cross the bottom text margin when the glyphs themselves
512
+ * still fit. Pagination therefore budgets {@link height} minus this amount at a page
513
+ * bottom, while paint keeps the full box and padding.
514
+ */
515
+ trailingSpacing: number;
516
+ /** When true, layout must start a new page after this line is placed. */
517
+ pageBreakAfter?: boolean;
518
+ /** When true, layout must advance to the next authored section column. */
519
+ columnBreakAfter?: boolean;
520
+ /** An authored line break (`w:br` textWrapping or `w:cr`), never an automatic wrap. */
521
+ manualBreakAfter?: true;
522
+ /**
523
+ * The flow kept the paragraph's last word on this line by borrowing inter-word space.
524
+ * Alignment compresses a paragraph's last line only when this is set, never for another
525
+ * overflow such as hanging punctuation.
526
+ */
527
+ spaceShrink?: true;
528
+ /** Model ranges on this line covering deleted content; see {@link LineRecord.deletedRanges}. */
529
+ deletedRanges?: readonly ModelRange[];
530
+ /** Vertical gap inserted before this line to clear a drawing exclusion band. */
531
+ exclusionSkipBefore?: number;
532
+ /**
533
+ * The line follows a text wrapping break that clears floating objects (`w:br w:clear`).
534
+ * Placement keeps its skip even on a line before the paragraph's first anchor.
535
+ */
536
+ breakClearance?: true;
537
+ /** Clearance inherited by an empty anchor paragraph from other drawing bands. */
538
+ anchorClearanceBefore?: number;
539
+ /**
540
+ * The first-line offset this line was broken with: the paragraph's first line, and the
541
+ * first line after page breaks that open it ({@link holdsOnlyPageBreak}). Absent when zero.
542
+ */
543
+ firstLineOffset?: number;
544
+ /**
545
+ * The horizontal passage a float left this line, when that passage is narrower than the
546
+ * paragraph's measure and holds the whole line.
547
+ *
548
+ * `start` is where the line's content begins (after any first-line indent) and `end` is the
549
+ * passage's right edge, both in paragraph-local coordinates. Centring, right alignment and
550
+ * justification align INSIDE this passage: Word centres a heading between the two pictures
551
+ * beside it, not between the page margins, and stretches a justified line to the float's
552
+ * near edge rather than through it. Absent when no float shortens the line, when the line
553
+ * steps over a float into a later passage, or when the passage is the whole measure.
554
+ */
555
+ wrapSegment?: {
556
+ readonly start: number;
557
+ readonly end: number;
558
+ };
559
+ /** Tracked anchored-drawing attributions on this line; see {@link LineRecord.anchorRevisions}. */
560
+ anchorRevisions?: readonly RevisionAttribution[];
561
+ /** Revisions a resolved view answered on this line; see {@link LineRecord.changeSites}. */
562
+ changeSites?: readonly RevisionAttribution[];
563
+ }
564
+
565
+ /**
566
+ * What one cell side is before any ADJACENT cell has its say.
567
+ *
568
+ * - omitted → the table's own rule for that position (`tblBorders`, `insideH`/`insideV`)
569
+ * - edge → the cell wins outright; no weight fight with the table
570
+ * - none → an explicit `w:val="nil"`. Suppresses a matching table border on interior
571
+ * and perimeter sides alike, so a table whose cells all declare none paints borderless
572
+ * like Word even when `tblBorders` still carry `single` rules.
573
+ */
574
+ declare function effectiveBorderSide(authored: TableBorderSide, tableSide: TableBorderSide, _options?: {
575
+ readonly interior?: boolean;
576
+ }): TableBorderSide;
577
+
578
+ /** Minimum stroke width for a compound (double/triple) band, in points. */
579
+ declare const COMPOUND_BORDER_MIN_STROKE_PT = 0.25;
580
+ /** Minimum gap between compound strokes, in points. */
581
+ declare const COMPOUND_BORDER_MIN_GAP_PT = 0.25;
582
+ /**
583
+ * How a multi-line border style (double, triple) is drawn: stroke width, gap, and total extent.
584
+ *
585
+ * Table stroke coordinates centre the painted band on the authored edge box.
586
+ * Content insets must also account for the portion that extends inward.
587
+ */
588
+ interface CompoundBorderMetrics {
589
+ readonly strokePt: number;
590
+ readonly gapPt: number;
591
+ readonly extentPt: number;
592
+ /** Centers the compound band on the authored width; negative extends outward. */
593
+ readonly insetPt: number;
594
+ }
595
+ /**
596
+ * Double strokes use the authored width, with a quarter-point minimum.
597
+ * The complete band is centred on the authored edge box. Device pixel rounding
598
+ * belongs to rendering; these layout metrics remain in continuous points.
599
+ */
600
+ declare function computeDoubleBorderMetricsPt(widthPt: number): CompoundBorderMetrics;
601
+
602
+ /** Soft ceiling on fragments emitted for one authored row (hostile / runaway splits). */
603
+ declare const MAX_TABLE_ROW_FRAGMENTS = 4096;
604
+ /**
605
+ * Why a table could not be paginated as authored.
606
+ *
607
+ * Each is a bound: a row taller than a page, a row that cannot be split, or a row producing more
608
+ * fragments than the limit allows.
609
+ */
610
+ type TablePaginationErrorCode = 'table-row-overheight' | 'table-row-split-unsupported' | 'table-row-fragment-limit';
611
+ /**
612
+ * Bounded table pagination failure. Prefer this over emitting a fragment that overflows
613
+ * the page content box.
614
+ */
615
+ declare class TablePaginationError extends Error {
616
+ readonly code: TablePaginationErrorCode;
617
+ constructor(code: TablePaginationErrorCode, message: string);
618
+ }
619
+
620
+ type ParagraphFrameHeightRule = 'auto' | 'atLeast' | 'exact';
621
+ type ParagraphFrameWrap = 'auto' | 'around' | 'tight' | 'through' | 'none' | 'notBeside';
622
+ /** Bounded text-frame properties resolved for semantic layout. */
623
+ interface ParagraphFrame {
624
+ /** Explicit-size drop cap aligned against this many lines of its anchor paragraph. */
625
+ readonly dropCapLines?: number;
626
+ readonly dropCap?: 'drop' | 'margin';
627
+ readonly dropCapRtl?: boolean;
628
+ readonly x: number;
629
+ readonly y: number;
630
+ /** Zero until the containing text width resolves an omitted `w:w`. */
631
+ readonly width: number;
632
+ readonly autoWidth: boolean;
633
+ /** Authored exact or minimum height. Absent for `hRule="auto"`. */
634
+ readonly height?: number;
635
+ readonly heightRule: ParagraphFrameHeightRule;
636
+ readonly horizontalAnchor: 'page' | 'margin' | 'text';
637
+ readonly verticalAnchor: 'page' | 'margin' | 'text';
638
+ readonly xAlign?: TableFloatXSpec;
639
+ readonly yAlign?: TableFloatYSpec;
640
+ readonly anchorLocked: boolean;
641
+ /** Equal authored frame properties group adjacent paragraphs into one frame. */
642
+ readonly token: string;
643
+ readonly wrap: ParagraphFrameWrap;
644
+ readonly hSpace: number;
645
+ readonly vSpace: number;
646
+ }
647
+
648
+ interface PendingParagraphFrame {
649
+ readonly frame: ParagraphFrame;
650
+ readonly fragment: ParagraphFragmentRecord;
651
+ readonly groupId: string;
652
+ readonly sourceOrder: number;
653
+ }
654
+ /** Immutable prefix shared by every checkpoint; one node per pending paragraph. */
655
+ interface PendingParagraphFrames {
656
+ readonly item: PendingParagraphFrame;
657
+ readonly previous: PendingParagraphFrames | undefined;
658
+ readonly length: number;
659
+ readonly signature: string;
660
+ }
661
+
662
+ /** The flow state as it stood immediately before one block was placed. */
663
+ interface FlowCheckpoint {
664
+ /** Completed pages at this point. The prefix of the previous layout that still stands. */
665
+ readonly pageCount: number;
666
+ /** Fragments already on the page being built. */
667
+ readonly pageFragments: readonly BlockFragmentRecord[];
668
+ /** Anchored drawings already collected for the open page. */
669
+ readonly pendingAnchoredDrawings: readonly AnchoredDrawingRecord[];
670
+ /** Local frame paragraphs waiting for their next regular paragraph anchor. */
671
+ readonly pendingParagraphFrames?: PendingParagraphFrames;
672
+ /**
673
+ * Anchored drawings overlap resolution pushed onto the NEXT page, and how many times each
674
+ * drawing has been pushed.
675
+ *
676
+ * Flow state like the rest of this record, and carried for the same reason: a resume that
677
+ * started with an empty list dropped a drawing that the previous pass had deferred but not
678
+ * yet placed, and a convergence that did not compare them accepted a flow that still owed
679
+ * the next page a drawing as equal to one that owed it nothing.
680
+ */
681
+ readonly deferredAnchoredDrawings: readonly AnchoredDrawingRecord[];
682
+ readonly anchorPageDeferCounts: ReadonlyMap<string, number>;
683
+ /** Sheet-positioned tables and zero-line anchors still owed by the open page. */
684
+ readonly pendingPositionedTableTokens?: PendingPositionedTableTokens;
685
+ readonly positionedTableAnchorSignals?: readonly PositionedTableAnchorSignal[];
686
+ readonly cursorY: number;
687
+ readonly lineCounter: number;
688
+ /** Trailing paragraph spacing participating in adjacent-spacing collapse. */
689
+ readonly previousSpaceAfter: number;
690
+ /** Active column index when the section uses multiple columns. */
691
+ readonly flowColumnIndex: number;
692
+ }
693
+ /**
694
+ * What the last layout pass actually did — the observable evidence that incremental layout is
695
+ * working.
696
+ *
697
+ * `reusedPages` and `fullPasses` are the ones that matter: a typing keystroke that rebuilds every
698
+ * page produces identical output and unusable performance, so the tests assert on these rather
699
+ * than on the rendered result.
700
+ */
701
+ interface LayoutSessionStats {
702
+ /** Paragraphs placed by the last pass, against the number in the document. */
703
+ readonly placed: number;
704
+ readonly total: number;
705
+ /** Pages carried over from the previous layout without being rebuilt. */
706
+ readonly reusedPages: number;
707
+ /** Passes that could not resume and laid the document out from the top. */
708
+ readonly fullPasses: number;
709
+ }
710
+ /** One section's place on the previous document sheet stack. */
711
+ interface SectionStackSpan {
712
+ readonly startIndex: number;
713
+ readonly pageCount: number;
714
+ readonly sheetY: number;
715
+ /** Remapped pages (projectors intact) from the last pass for this section. */
716
+ readonly remappedPages: readonly PageRecord[];
717
+ /**
718
+ * The section-local pages this span's pass returned — identity evidence for reuse. (On a
719
+ * continuous section the host tail merges instead of remapping, so `remappedPages` covers
720
+ * only the overflow sheets while this list still holds every returned page.)
721
+ *
722
+ * A section can lay out more than once inside one document pass (a reserve re-run, a
723
+ * balancing probe), and the last run then reports "nothing placed" against its OWN session
724
+ * even though an earlier run this pass rebuilt the pages. Counts and stats cannot tell
725
+ * those apart; only these object identities can say the span still describes the pages the
726
+ * section just returned.
727
+ */
728
+ readonly sourcePages: readonly PageRecord[];
729
+ /** The blank parity sheet published in front of the section, kept for identity reuse. */
730
+ readonly paritySheet?: PageRecord;
731
+ }
732
+ /** Orchestrator state for multi-section incremental layout. */
733
+ interface MultiSectionLayoutState {
734
+ structureKey: string;
735
+ /** Each section's part of {@link structureKey}, so an added section keeps the others. */
736
+ sectionKeys: readonly string[];
737
+ sections: LayoutSession[];
738
+ spans: SectionStackSpan[];
739
+ previousRemapped: readonly PageRecord[];
740
+ previousFinalized: SemanticLayout | null;
741
+ previousPageCount: number;
742
+ /**
743
+ * The PAGEREF assignment token of the pages behind `previousFinalized`. A target can move
744
+ * sheets while the TOC page's own remapped record stays identity-unchanged, so the finalized
745
+ * restore must compare this before handing back a previously substituted sheet.
746
+ */
747
+ previousPageRefToken: string;
748
+ }
749
+ /**
750
+ * Carried-over state that makes layout incremental.
751
+ *
752
+ * A caller creates one and hands the SAME object back each pass. It holds the previous pages, the
753
+ * per-block cache keys and the flow checkpoints a pass resumes from, so an edit low in a document
754
+ * re-lays only what follows it. A no-change pass returns the previous pages by identity.
755
+ */
756
+ interface LayoutSession {
757
+ /** @internal Mutable across passes; a caller only creates one and passes it back. */
758
+ previous: SemanticLayout | null;
759
+ checkpoints: FlowCheckpoint[];
760
+ keys: string[];
761
+ /** Geometry and flow context of the previous pass; a change forces a full pass. */
762
+ context: string;
763
+ /**
764
+ * Producer of the previous pass, compared beside {@link context} rather than embedded in
765
+ * it: the producer carries the content-control token, which runs to kilobytes on a
766
+ * control-heavy document, and embedding it copied that token into every section's context
767
+ * string on every pass. Identity-stable when unchanged, so the comparison is a pointer
768
+ * check.
769
+ *
770
+ * Optional so a session literal built before this field existed still type-checks; an
771
+ * absent producer never matches and forces a conservative full pass.
772
+ */
773
+ producer?: string;
774
+ /**
775
+ * Whether the previous pass read page PARITY: even/odd header variants, or an anchored
776
+ * drawing positioned against an inside/outside frame or alignment.
777
+ *
778
+ * The document page index is not part of {@link context} — page numbers re-project at
779
+ * finalize and page shells renumber at remap — so parity is the only page-position input
780
+ * that can invalidate a section's layout. Tracked separately so the common document pays
781
+ * nothing for a start-index shift.
782
+ */
783
+ parityDependent: boolean;
784
+ /** Parity (0/1) of the document page index this session's layout started on. */
785
+ startPageParity: number;
786
+ /**
787
+ * The section's memoized prepass (prepared blocks, cache keys, flow keys, document
788
+ * order), reused verbatim while its inputs are unchanged. Opaque here: the shape is
789
+ * owned by `semantic-layout.ts`, which is the only reader and writer.
790
+ */
791
+ prepass: unknown;
792
+ /**
793
+ * The notes pass's memoized state (reference hits, mark contexts, per-page attach
794
+ * results), reused while its inputs are unchanged. Opaque here: the shape is owned by
795
+ * `note-pagination.ts`, which is the only reader and writer.
796
+ */
797
+ notes: unknown;
798
+ /** Line counter at the start of the previous pass, for translating reused section counts. */
799
+ startLineCounter: number;
800
+ /**
801
+ * Line counter after the last block of the previous pass.
802
+ *
803
+ * Multi-section orchestration threads a global line counter across sections; early-exit
804
+ * paths (unchanged / converged) must report this rather than the resume cursor.
805
+ */
806
+ endLineCounter: number;
807
+ /**
808
+ * Flow state after the last block of the previous pass, for a section that CONTINUES
809
+ * onto this one's last sheet (`w:type="continuous"`).
810
+ *
811
+ * `endCursorY` is the used height of that sheet's content column; `endSpaceAfter` is the
812
+ * trailing paragraph spacing still eligible for adjacent-spacing collapse. Reported by
813
+ * the early-exit paths (unchanged / converged) for the same reason as
814
+ * {@link endLineCounter}: the resume cursor is not the end of the flow.
815
+ */
816
+ endCursorY: number;
817
+ endSpaceAfter: number;
818
+ /** Whether the last page of that pass was still open (no trailing page break). */
819
+ endsOpenPage: boolean;
820
+ stats: LayoutSessionStats;
821
+ /**
822
+ * Column-height limit chosen by the last balanced multi-column pass, or null when the
823
+ * last pass did not balance.
824
+ *
825
+ * Lets the next pass try the remembered limit FIRST: an unchanged balanced section
826
+ * early-exits on that single attempt instead of re-running the natural pass and the
827
+ * whole balance search every time.
828
+ */
829
+ balanceLimit: number | null;
830
+ /** Present when the last pass was multi-section; child sessions live here. */
831
+ multi: MultiSectionLayoutState | null;
832
+ /**
833
+ * Page-bottom footnote reserves from the last published notes layout.
834
+ *
835
+ * Seeded into the next {@link layoutSemanticDocumentWithNotes} call so the first body
836
+ * pass already matches the reserved context key — without this, every notes document
837
+ * starts empty, mismatches, and throws away the session on reflow.
838
+ */
839
+ notePageBottomReserves: ReadonlyMap<number, number> | null;
840
+ }
841
+ /**
842
+ * A layout session, retained across revisions by the caller.
843
+ */
844
+ declare function createLayoutSession(): LayoutSession;
845
+
846
+ /** Collect story paragraphs, preserving table depth limits across immutable row reuse. */
847
+ declare function walkStoryParagraphs(blocks: readonly OoxmlElement[], maxTableDepth?: number): OoxmlElement[];
848
+
849
+ /**
850
+ * Horizontal marker box inside the first-line indent slot.
851
+ *
852
+ * Coordinates are relative to the same origin as paragraph content (`indent.left` is the
853
+ * text start). Returns null when there is nothing to paint.
854
+ *
855
+ * `w:hanging` and a positive `w:firstLine` are one mutually exclusive slot
856
+ * (§17.3.1.10, §17.3.1.12), so the marker has two placements, not an interaction:
857
+ * a hanging level puts the marker BEFORE the text start (`left - hanging`); a
858
+ * positive-firstLine level puts it AFTER (`left + firstLine`) — the standard legal
859
+ * shape `w:ind w:left="0" w:firstLine="720"` numbers at 0.5" while continuation
860
+ * lines return to the margin. Reading only the hanging model painted every such
861
+ * marker at the left margin.
862
+ */
863
+ declare function listMarkerBox(item: ResolvedListItem, markerWidth: number, lineY: number, lineHeight: number): {
864
+ x: number;
865
+ y: number;
866
+ width: number;
867
+ height: number;
868
+ } | null;
869
+
870
+ /** Soft ceiling on `basedOn` chain length — enough for real templates, refuses hostile graphs. */
871
+ declare const MAX_STYLE_BASED_ON_DEPTH = 32;
872
+
873
+ /** Far above anything Word authors (its UI caps at 63) while keeping allocation bounded. */
874
+ declare const MAX_TABLE_COLUMNS = 1024;
875
+
876
+ /**
877
+ * `w:tblW` / `w:tcW` / `w:wBefore` (CT_TblWidth, 17.4.63 / 17.4.71 / 17.4.86): a PREFERRED
878
+ * width plus the unit it is stated in. Preferred is the operative word — it is what the
879
+ * producer asked for, not what the table resolved to.
880
+ *
881
+ * `pct` is stated in fiftieths of a percent (5000 = 100%) by Word, and in the `"50%"`
882
+ * string form of `ST_Percentage` by others; both are read. `auto` and `nil` carry no width.
883
+ */
884
+ type PreferredWidthType = 'dxa' | 'pct' | 'auto' | 'nil';
885
+ /**
886
+ * `w:tblW` / `w:tcW` — a requested width, whose UNIT depends on its type.
887
+ *
888
+ * Points for `dxa`, percent for `pct`, and zero for `auto`/`nil`. Reading `value` without `type`
889
+ * is always wrong.
890
+ */
891
+ interface PreferredWidth {
892
+ readonly type: PreferredWidthType;
893
+ /** POINTS for `dxa`, PERCENT for `pct` (0–100, or up to 655.34 for a table), 0 otherwise. */
894
+ readonly value: number;
895
+ }
896
+ /** The frozen "no preferred width" value — what a table or cell that declares none resolves to. */
897
+ declare const AUTO_PREFERRED_WIDTH: PreferredWidth;
898
+
899
+ /**
900
+ * The uniform 3 pt (60 twip) inset this lane used to apply on every side.
901
+ *
902
+ * Word's defaults are not uniform — see {@link DEFAULT_CELL_MARGINS} — so nothing resolves
903
+ * against this any more. Kept because it is published.
904
+ */
905
+ declare const CELL_PAD = 3;
906
+ /** Resolved cell padding in points, after the table default and any per-cell override. */
907
+ interface CellMarginsPt {
908
+ readonly top: number;
909
+ readonly right: number;
910
+ readonly bottom: number;
911
+ readonly left: number;
912
+ }
913
+ /**
914
+ * Traditional default table-style padding: 0 vertical, 108 twips horizontal.
915
+ *
916
+ * Kept as a published constant. Authored styles can supply these values; omitted
917
+ * margins instead resolve to a 10-twip horizontal application fallback.
918
+ */
919
+ declare const DEFAULT_CELL_MARGINS: CellMarginsPt;
920
+
921
+ /** `w:vAlign` — where a cell's content sits when the row is taller than the content. */
922
+ type CellVerticalAlign = 'top' | 'center' | 'bottom';
923
+
924
+ /**
925
+ * What autofit needs from layout to measure each cell the way layout paints it.
926
+ * @public
927
+ */
928
+ interface TableAutofitContext {
929
+ /** Measures text exactly as line breaking does. */
930
+ readonly measurer: TextMeasurer;
931
+ /** List items by paragraph id: a list level indents its paragraph. */
932
+ readonly listItems?: ReadonlyMap<string, ResolvedListItem>;
933
+ /** Resolves inline pictures, so a picture column keeps the width the picture paints at. */
934
+ readonly inlineDrawingLayout?: InlineDrawingLayoutContext;
935
+ /** Field and note context, so the minimum measures the text layout paints. */
936
+ readonly fields?: AutofitFieldContext;
937
+ /** The document's default tab interval, where an unwrapped line's tabs advance to. */
938
+ readonly defaultTabStopPt?: number;
939
+ /**
940
+ * A token that changes whenever a paragraph's projected text can change: the same tokens
941
+ * the paragraph break cache keys on. Minimums are cached only when this and
942
+ * {@link TableAutofitContext.passToken} are both given.
943
+ */
944
+ readonly paragraphToken?: (paragraph: OoxmlElement) => string;
945
+ /**
946
+ * A token over every other input the minimum reads: the drawing context and every member of
947
+ * {@link TableAutofitContext.fields}. It must change when any of them changes. List state is
948
+ * not part of it: {@link TableAutofitContext.paragraphToken} must carry each paragraph's
949
+ * list tokens.
950
+ */
951
+ readonly passToken?: string;
952
+ }
953
+ /**
954
+ * The field and note context cell layout projects paragraphs with.
955
+ * @public
956
+ */
957
+ interface AutofitFieldContext {
958
+ /** The page a header or footer paints on, for its PAGE and NUMPAGES results. */
959
+ readonly pageContext?: FieldPageContext;
960
+ /** Footnote and endnote reference marks, as numbered for this pass. Treated as immutable. */
961
+ readonly noteMarks?: NoteMarkContext;
962
+ /** Package properties that AUTHOR, TITLE, and similar fields paint. */
963
+ readonly documentProperties?: DocumentProperties;
964
+ /** Body page-field placeholders; `false` paints cached results instead. */
965
+ readonly bodyPageFields?: BodyPageFieldContext | false;
966
+ /** Refreshed cross-reference results. */
967
+ readonly refFields?: RefFieldContext;
968
+ /** Paint field instructions instead of results. */
969
+ readonly showFieldCodes?: boolean;
970
+ /** Field-code ranges by paragraph id, when instructions are shown. */
971
+ readonly fieldCodeRanges?: FieldCodeRanges;
972
+ /** Table-of-contents link styling by paragraph id. */
973
+ readonly tocLinkStyleRanges?: TocLinkRanges;
974
+ }
975
+
976
+ /**
977
+ * Where a table's left edge sits inside the box that contains it.
978
+ *
979
+ * Ordinary left-aligned tables start at their indent, which may be negative and is not
980
+ * limited to the remaining width. Centered and right-aligned left-to-right tables use the
981
+ * remaining width and ignore the indent. `outerRuleOffsetPt` then moves the grid inward by
982
+ * half the outer side rule where the table puts that rule's outer edge on the aligned edge.
983
+ * For a supported mode-14 fixed table, that offset instead aligns the first cell's content.
984
+ * A legacy content-aligned table instead puts the content edge of its leading or trailing
985
+ * cell on the aligned edge of the text column; its indent still applies from the leading edge.
986
+ */
987
+ declare function tableOriginX(structure: SemanticTableStructure, containerWidthPt: number): number;
988
+
989
+ /**
990
+ * Layout-time nesting ceiling. Parse-time depth (MAX_DEPTH = 256 XML levels) alone still
991
+ * admits ~80 levels of `w:tbl` recursion into the layout walk; deeper tables render as an
992
+ * empty cell box rather than recursing.
993
+ */
994
+ declare const MAX_TABLE_NESTING = 16;
995
+ /**
996
+ * Soft ceiling on an authored `w:trHeight` (~22"). Hostile `w:val` otherwise becomes a
997
+ * multi-page row that every pagination preflight and cell box inherits.
998
+ */
999
+ declare const MAX_TABLE_ROW_HEIGHT_PT: number;
1000
+ /**
1001
+ * `w:trPr/w:trHeight` (17.4.81) resolved for layout. Points leave the reader already —
1002
+ * twips convert once here, matching every other table geometry boundary.
1003
+ *
1004
+ * Word quirk (matches Form025U and Word's UI export): a present `@w:val` with an omitted
1005
+ * `@w:hRule` is treated as `atLeast`, not ECMA's `auto`. Explicit `auto` still ignores val.
1006
+ */
1007
+ type TableRowHeightRule = 'auto' | 'atLeast' | 'exact';
1008
+ /**
1009
+ * `w:trHeight` — a row's height rule and its value.
1010
+ *
1011
+ * `auto` carries no value at all, which is why this is a union rather than a rule plus an
1012
+ * optional number.
1013
+ */
1014
+ type TableRowHeight = {
1015
+ readonly rule: 'auto';
1016
+ } | {
1017
+ readonly rule: 'atLeast' | 'exact';
1018
+ readonly valuePt: number;
1019
+ };
1020
+ /** `w:tblPr/w:jc` (17.4.29, ST_JcTable): where the table sits within the text column. */
1021
+ type TableAlignment = 'left' | 'center' | 'right';
1022
+ /**
1023
+ * One cell in the resolved table structure.
1024
+ *
1025
+ * `gridSpan` is clamped at READ time and layout never re-derives it — the value comes from a file
1026
+ * and would otherwise be a loop bound an attacker controls.
1027
+ */
1028
+ interface SemanticTableCell {
1029
+ readonly revisionShading?: 'inserted' | 'deleted' | 'merged' | 'split';
1030
+ readonly revisionShadingAuthor?: string;
1031
+ readonly id: string;
1032
+ /** Derived content-edge geometry for a verified legacy percentage-width parent table. */
1033
+ readonly legacyContentAlignment?: true;
1034
+ /** Resolved simple side rules centered on a legacy absolute-width table grid. */
1035
+ readonly centeredSideRules?: true;
1036
+ /** Simple side rules painted centered on the grid line, without moving the content edge. */
1037
+ readonly centeredSidePaint?: true;
1038
+ /** Clamped to [1, MAX_TABLE_COLUMNS] at read time; layout never re-derives it. */
1039
+ readonly gridSpan: number;
1040
+ /** Physical grid column after width/style resolution and the bidiVisual projection. */
1041
+ readonly gridColumn: number;
1042
+ /** Stored grid index when bidiVisual maps this cell into a physical RTL grid. */
1043
+ readonly logicalGridColumn?: number;
1044
+ /** Canonical `w:gridCol` node id for this cell's start column, when the grid is authored. */
1045
+ readonly gridColumnId?: string;
1046
+ /** A vMerge cell that is not the restart continues the cell above: box, no content. */
1047
+ readonly vMergeContinue: boolean;
1048
+ /** `w:hideMark` excludes the end-of-cell glyph from row sizing. */
1049
+ readonly hideEndMark?: boolean;
1050
+ /** `w:vAlign` — defaults to top when omitted/unrecognised. */
1051
+ readonly vAlign: CellVerticalAlign;
1052
+ /** `w:textDirection`; unsupported values keep horizontal layout. */
1053
+ readonly textDirection: 'horizontal' | 'btLr';
1054
+ /** Resolved per-side margins (tcMar over tblCellMar over the table style over Word's default). */
1055
+ readonly margins: CellMarginsPt;
1056
+ /** Three-state authored `tcBorders` (omitted / none / edge). */
1057
+ readonly borders: CellBorderBox;
1058
+ /** The row's `w:tblPrEx` table borders, which replace the table's own for this cell. */
1059
+ readonly rowTableBorders?: TableBorderBox;
1060
+ /** Resolved incident edges used for content clearance, preserving authored border provenance. */
1061
+ readonly contentBorders?: CellBorderBox;
1062
+ /** The resolved cell (including a vertical merge) ends at the authored table bottom. */
1063
+ readonly contentBottomIsOuter?: boolean;
1064
+ /** Clearance this AUTHORED ROW reserves above its content for its own top rule, in points. */
1065
+ readonly topBandClearancePt?: number;
1066
+ /** The CELL declares `w:tcBorders/w:top` as `nil`, rather than a table style doing so. */
1067
+ readonly suppressesTopBand?: true;
1068
+ /** Row-local clearance in points for authored minima, before vMerge combines content boxes. */
1069
+ readonly minimumContentInsets?: {
1070
+ readonly top: number;
1071
+ readonly bottom: number;
1072
+ };
1073
+ /** Validated 6-hex shading fill, absent for none/auto. */
1074
+ readonly shading?: string;
1075
+ /**
1076
+ * `w:tcW` — the width this cell asked for, as authored.
1077
+ *
1078
+ * Published for consumers that need the cell's own statement (a column-resize handle has
1079
+ * to write back to it). Column geometry is NOT derived from this field: the resolver works
1080
+ * from a flat claim list built in the same pass, because resolving a column means looking
1081
+ * at every cell that covers it across every row, not at one cell at a time. Read
1082
+ * `columnWidthsPt` for what the table actually laid out.
1083
+ */
1084
+ readonly preferredWidth: PreferredWidth;
1085
+ /**
1086
+ * What the table style says about this cell's paragraphs and runs (17.7.6.6) — a header
1087
+ * row's bold and centring live here, not in the cell's own properties.
1088
+ */
1089
+ readonly styleFormatting: TableCellStyleFormatting;
1090
+ /** Block children in reading order, with content-control wrappers flattened. */
1091
+ readonly blocks: readonly OoxmlElement[];
1092
+ }
1093
+ /** One row in the resolved structure: its cells, its height rule, and any row-level revision. */
1094
+ interface SemanticTableRow {
1095
+ readonly id: string;
1096
+ /** Pending Word row insertion/deletion authored in `w:trPr`. */
1097
+ readonly revisionKind?: 'insert' | 'delete';
1098
+ /**
1099
+ * The `w:trPr/w:ins|w:del` attribution, carried with the kind so a painted row can say
1100
+ * WHOSE pending decision it is — the review model addresses the decision by exactly this
1101
+ * `(id, author, date)` triple, and a surface with only the kind could highlight the row
1102
+ * but never open its card.
1103
+ */
1104
+ readonly revisionId?: string;
1105
+ readonly revisionAuthor?: string;
1106
+ readonly revisionDate?: string;
1107
+ /** The row insertion a resolved view kept the row through; see the fragment record. */
1108
+ readonly changeSites?: readonly RevisionAttribution[];
1109
+ /** `w:trPr/w:tblHeader` — the row repeats atop each page the table continues onto. */
1110
+ readonly isHeader: boolean;
1111
+ /**
1112
+ * `w:trPr/w:cantSplit` — the row must stay on one page. When no page can hold it, the
1113
+ * row starts on a fresh page and splits there, so its content is kept.
1114
+ */
1115
+ readonly cantSplit: boolean;
1116
+ /** `w:trPr/w:trHeight` — auto / atLeast floor / exact (clipped) row height. */
1117
+ readonly height: TableRowHeight;
1118
+ readonly cells: readonly SemanticTableCell[];
1119
+ }
1120
+ /**
1121
+ * A table resolved into a rectangular grid: column widths, rows, and the widths it asked for.
1122
+ *
1123
+ * The grid is normalized here so layout never has to reconcile `w:gridCol` against actual cell
1124
+ * spans — vertical merges and column spans are already accounted for.
1125
+ */
1126
+ interface SemanticTableStructure {
1127
+ /** Whether stored columns and horizontal table properties display right to left. */
1128
+ readonly bidiVisual?: true;
1129
+ readonly columnWidthsPt: readonly number[];
1130
+ readonly rows: readonly SemanticTableRow[];
1131
+ /** Verified pre-2013 percentage-width inline table; derived, never serialized. */
1132
+ readonly legacyContentAlignment?: true;
1133
+ /** A legacy content-aligned table whose outer trailing edge, not its content, meets the
1134
+ * trailing text edge: a top-level table in a text box. Derived, never serialized. */
1135
+ readonly legacyTrailingOuterEdge?: true;
1136
+ /**
1137
+ * Derived, never serialized: how far the grid moves from the aligned table edge, in points.
1138
+ * A supported mode-14 fixed table shifts by its leading cell margin in the other direction.
1139
+ * A compatibility-mode-15 left- or right-aligned table puts the outer edge of its side rule
1140
+ * on that edge, so its first grid line sits half a rule inward. Positive moves right.
1141
+ */
1142
+ readonly outerRuleOffsetPt?: number;
1143
+ /** `w:tblPr/w:tblW` — the width the table asked for. */
1144
+ readonly tableWidth: PreferredWidth;
1145
+ /**
1146
+ * `w:tblInd` (17.4.50) in points — "this indentation should shift the table into the text
1147
+ * margin by the specified amount". Applies to a left-aligned table; `w:jc` decides the
1148
+ * placement outright for the other two. Negative on a top-level table, which the indent
1149
+ * pulls into the leading margin; never negative on a nested table.
1150
+ */
1151
+ readonly indentPt: number;
1152
+ /** `w:tblPr/w:jc` (17.4.29) — where the table sits in the text column. */
1153
+ readonly alignment: TableAlignment;
1154
+ /**
1155
+ * `w:tblPr/w:tblpPr` (17.4.57) — present when the table is positioned against an anchor
1156
+ * box. Placement then comes from {@link tableFloatOriginX} rather than `w:jc`/`w:tblInd`.
1157
+ */
1158
+ readonly float?: TableFloatPosition;
1159
+ /**
1160
+ * `w:tblCellSpacing` (17.4.45) in points: the space on each side of every cell. Cells sit
1161
+ * twice this apart and from the table edge; the gaps come out of the table width, and each
1162
+ * column gives up its share in proportion (`table-cell-spacing.ts`). Rows do not yet get
1163
+ * the matching gaps above and below them, so a spaced table's rows stay as tall as they
1164
+ * would be without it.
1165
+ */
1166
+ readonly cellSpacingPt: number;
1167
+ /**
1168
+ * `w:tblPr/w:tblLayout/@w:type="fixed"` (17.4.52 — 17.4.53 is the `w:tblPrEx` exception
1169
+ * variant, not this element). Fixed layout takes the grid as final;
1170
+ * anything else is autofit, which in Word never renders wider than the text column.
1171
+ */
1172
+ readonly layoutFixed: boolean;
1173
+ /** Table-level `tblBorders` (three-state, including insideH/insideV). */
1174
+ readonly tableBorders: TableBorderBox;
1175
+ /** Table-level `tblCellMar` defaults (per-side, Word's own default when a side is omitted). */
1176
+ readonly defaultMargins: CellMarginsPt;
1177
+ }
1178
+ /**
1179
+ * Read one typed table node into a bounded structure, or null when the node is not a
1180
+ * typed table or sits beyond the nesting ceiling.
1181
+ */
1182
+ declare function readTableStructure(table: OoxmlNode, contentWidthPt: number, depth: number, styleCascade?: StyleCascadeTable,
1183
+ /** Which revisions the view resolves away; only the proposed result performs the join. */
1184
+ displayMode?: RevisionDisplayMode, authorFilter?: RevisionAuthorFilter, compatibilityMode?: number,
1185
+ /** Widens autofit columns to their content minimums; layout passes its measurer. */
1186
+ autofit?: TableAutofitContext,
1187
+ /** The table is a top-level table of a text box story. */
1188
+ textBox?: boolean): SemanticTableStructure | null;
1189
+
1190
+ /** Paragraph ids in document order, deduplicated across fragments. */
1191
+ declare function documentOrder(layout: SemanticLayout): string[];
1192
+ /**
1193
+ * Paragraph ids of EVERY story the layout paints, in the order they sit on the page.
1194
+ *
1195
+ * Body, then each page's header and footer, then its note areas — per page, so a story's own
1196
+ * paragraphs stay adjacent and in reading order. That is all any caller needs: a selection
1197
+ * cannot span two stories (the engine refuses one), so only the order WITHIN a story is ever
1198
+ * compared, and this gets that right for all of them at once.
1199
+ *
1200
+ * What a caller passes when it has no story in hand — `selectionRects` and
1201
+ * `spansInSelection` both REQUIRE an order rather than defaulting to one, deliberately.
1202
+ * {@link documentOrder} is the body alone, and
1203
+ * using it as a default is what let selection reads silently answer about the wrong story —
1204
+ * two paragraphs selected in a header both ranked -1, the walk gave up, and the run properties
1205
+ * came back short. A caller that DOES know its story should still pass that story's order:
1206
+ * it is smaller, and it cannot match a paragraph the caret is not among.
1207
+ */
1208
+ declare function everyStoryOrder(layout: SemanticLayout): string[];
1209
+
1210
+ /**
1211
+ * The physical rectangles covering a selection, including disjoint bands within bidi lines.
1212
+ *
1213
+ * BODY fragments only, and a selection outside the body paints nothing. That is a real gap —
1214
+ * a retained pin in a header shows no highlight, and a comment anchored in one draws no band
1215
+ * even though the review queue lists it — but it is the honest shape of what this can answer
1216
+ * today. Widening the walk to the other stories is not enough on its own: header and footer
1217
+ * fragments carry positions relative to their own story box, note fragments relative to their
1218
+ * note area, and one header story object is attached to EVERY page it applies to. Fed straight
1219
+ * into a page-content-relative rect, those produce a band per page, at coordinates belonging
1220
+ * to a different box. Measured, that put a header comment's band on all six pages of a
1221
+ * document at the top-left of the body text. Painting nothing is wrong; painting over the
1222
+ * wrong words is worse, so the walk stays here until the geometry is carried with it.
1223
+ */
1224
+ declare function selectionRects(layout: SemanticLayout, selection: SemanticSelection,
1225
+ /**
1226
+ * Reading order of the ACTIVE story.
1227
+ *
1228
+ * REQUIRED, for the reason `spansInSelection` gives: any default is one story's order, and
1229
+ * it is wrong for every caret outside that story. A caller with no story in hand passes
1230
+ * {@link everyStoryOrder}.
1231
+ */
1232
+ order: readonly string[],
1233
+ /**
1234
+ * For EXACT edge x positions inside a span. Layout no longer publishes eager per-character
1235
+ * caret edges, so without a measurer an intra-span boundary interpolates across the span's
1236
+ * advance — visibly wrong in a proportional face. Pass the layout's measurer.
1237
+ */
1238
+ measurer?: TextMeasurer): SelectionRect[];
1239
+ /** A model range to highlight, and the key the caller knows it by. */
1240
+ interface KeyedRange {
1241
+ readonly key: string;
1242
+ readonly from: SemanticPosition;
1243
+ readonly to: SemanticPosition;
1244
+ }
1245
+ /**
1246
+ * Rectangles for MANY ranges in ONE pass over the lines.
1247
+ *
1248
+ * Not `selectionRects` in a loop. That walks every page, fragment and line per range, and a
1249
+ * contract with two hundred comments would re-walk the whole document two hundred times on
1250
+ * every layout — the highlight would cost more than the layout it decorates. One pass tests
1251
+ * each line against every range instead, which is the same work a single selection does.
1252
+ */
1253
+ declare function keyedRangeRects(layout: SemanticLayout, ranges: readonly KeyedRange[],
1254
+ /**
1255
+ * Pages to measure, or every page when absent.
1256
+ *
1257
+ * A band that is not on screen is not painted, so measuring it is pure cost — and it is
1258
+ * cost paid per keystroke, because an edit republishes the layout. Bounding this to the
1259
+ * materialized pages is what keeps typing in a heavily reviewed document as fast as
1260
+ * typing in a clean one.
1261
+ */
1262
+ pages?: ReadonlySet<number>,
1263
+ /** For exact intra-span edge positions (see {@link selectionRects}). */
1264
+ measurer?: TextMeasurer): Map<string, SelectionRect[]>;
1265
+
1266
+ /**
1267
+ * The next word boundary from `offset`, in `direction`.
1268
+ *
1269
+ * Word-LEFT skips any whitespace immediately behind the caret and then the word behind that,
1270
+ * which makes repeated presses walk words rather than alternate with the preceding space.
1271
+ *
1272
+ * `stops` are offsets a word may not run THROUGH even when the characters either side are
1273
+ * word characters. They exist because the text this walks is what the VIEW is showing, and a
1274
+ * view can show two versions of the document side by side: struck text and the text proposed
1275
+ * to replace it abut with no separator, so `ALL CAPS` deleted and `fsdfsd` inserted read as
1276
+ * the single word `CAPSfsdfsd`. Those characters are never adjacent in any one version, and a
1277
+ * double-click that took both selected across a decision the reader had not made. See
1278
+ * `deletedTextBoundaries`.
1279
+ */
1280
+ declare function wordBoundary(text: string, offset: number, direction: -1 | 1, stops?: ReadonlySet<number>): number;
1281
+
1282
+ /**
1283
+ * Where an INSERT aimed at this position actually lands: past any deletion it sits inside.
1284
+ *
1285
+ * The caret may rest anywhere in struck text — Word's rule, and the tracked lane's
1286
+ * (`tree-op-tracked.ts`): all-markup shows the words, so the reader can put the caret
1287
+ * between two of them. What may NOT happen is new content landing inside the `w:del`,
1288
+ * where it would serialize as `w:t` under a wrapper that requires `w:delText` and be taken
1289
+ * down by an accept of someone else's deletion. A deletion stays contiguous, so the words
1290
+ * go after it — the order a replacement reads in.
1291
+ *
1292
+ * RANGE endpoints are not this function's business: a drag may legitimately cover deleted
1293
+ * text, so callers adjust only collapsed insertion points.
1294
+ */
1295
+ declare function positionPastDeletion(layout: SemanticLayout, position: SemanticPosition): SemanticPosition;
1296
+
1297
+ /**
1298
+ * The caret position nearest a point, in PAGE-CONTENT coordinates.
1299
+ *
1300
+ * Never returns null for a point inside the document: a click in the margin, past the end of
1301
+ * a line, or below the last line still has an obvious intended caret, and refusing to answer
1302
+ * would make those clicks do nothing.
1303
+ *
1304
+ * The rules live in `semantic-hit-test.ts`, which answers with the cell address and the
1305
+ * on-glyphs flag a pointer controller needs too; this keeps the geometry-only shape for
1306
+ * callers that want nothing else.
1307
+ */
1308
+ declare function hitTestSemantic(layout: SemanticLayout, point: {
1309
+ readonly x: number;
1310
+ readonly y: number;
1311
+ readonly pageIndex?: number;
1312
+ }): CaretGeometry | null;
1313
+ /**
1314
+ * Innermost content-control boundary at a page-content point, or null outside every control.
1315
+ *
1316
+ * Prefers the deepest nesting depth when nested boundaries share geometry.
1317
+ */
1318
+ declare function contentControlAtSemantic(layout: SemanticLayout, point: {
1319
+ readonly x: number;
1320
+ readonly y: number;
1321
+ readonly pageIndex?: number;
1322
+ }): ContentControlBoundaryRecord$1 | null;
1323
+
1324
+ /**
1325
+ * Rewrite private-use symbol-font codepoints to their Unicode equivalents.
1326
+ *
1327
+ * Applies only to a known symbol-encoded family, only to the U+F020–U+F0FF range, and only
1328
+ * to codepoints with an exact Unicode counterpart — anything else is returned untouched, so
1329
+ * an unrecognised glyph is never replaced by a wrong one. Returns the input string by
1330
+ * identity when nothing changes.
1331
+ *
1332
+ * `isFamilyAvailable` is the escape hatch: a host that KNOWS the authored font is present
1333
+ * should return true, and the file's own codepoint is kept so the real typeface draws it.
1334
+ * Absent, the fallback applies, because an unmapped PUA codepoint renders as a tofu box in
1335
+ * every font that is not the symbol-encoded original.
1336
+ *
1337
+ * Called without the oracle it is also the EXTRACTION answer: the text a symbol glyph should
1338
+ * read as when the drawn glyph stays the authored one. A PDF exporter maps the drawn private
1339
+ * codepoint back through this for its `ToUnicode` table, so copying a Word bullet out of the
1340
+ * page yields a bullet rather than a private-use character.
1341
+ *
1342
+ * @public
1343
+ */
1344
+ declare function mapSymbolPuaText(text: string, fontFamily: string | null | undefined, isFamilyAvailable?: (family: string) => boolean): string;
1345
+
1346
+ /** Convert signed font units by an exact rational multiplier using the declared tie rule. */
1347
+ declare const roundFontUnitToFixedPoint: (fontUnits: number, denominator: number, numerator: number, mode: FixedPointRoundingMode) => FixedPoint;
1348
+
1349
+ /**
1350
+ * The exact HarfBuzz build this engine shapes against.
1351
+ *
1352
+ * Pinned and verified at load: a runtime reporting a different version is REFUSED rather than
1353
+ * used, because glyph positioning can change between releases and a cached measurement taken
1354
+ * under one build must not be trusted under another.
1355
+ */
1356
+ declare const HARFBUZZ_SHAPING_LIBRARY: VersionedShapingLibrary;
1357
+ /** Load and verify the HarfBuzz WASM runtime without adding top-level await to import graphs. */
1358
+ declare function initializeHarfBuzz(): Promise<void>;
1359
+ /** Whether the WASM runtime is loaded and shaping can proceed synchronously. */
1360
+ declare function isHarfBuzzInitialized(): boolean;
1361
+ /**
1362
+ * Why shaping refused.
1363
+ *
1364
+ * Mostly RESOURCE limits, because every input here derives from a file: text length, codepoint
1365
+ * count, glyph count and outline size are all attacker-influenced, and an unbounded shape call is
1366
+ * a denial-of-service vector rather than a rendering bug.
1367
+ */
1368
+ type HarfBuzzShapingErrorCode = 'notInitialized' | 'wasmUnavailable' | 'unsupportedRuntime' | 'fontOverLimit' | 'malformedFont' | 'textOverLimit' | 'codepointsOverLimit' | 'glyphOverLimit' | 'outlineOverLimit' | 'shapedRunOverLimit' | 'unsupportedVariationAxes' | 'unsupportedFallback' | 'unsupportedColorFont' | 'unsupportedNormalization' | 'invalidBidiLevel' | 'shapingLibraryMismatch' | 'disposed';
1369
+ /**
1370
+ * A shaping call that was refused, carrying the limit it exceeded where there was one.
1371
+ *
1372
+ * Thrown rather than returned: unlike a missing font, a run that cannot be shaped has no sensible
1373
+ * fallback measurement, and continuing would lay text out at made-up widths.
1374
+ */
1375
+ declare class HarfBuzzShapingError extends Error {
1376
+ readonly name = "HarfBuzzShapingError";
1377
+ readonly code: HarfBuzzShapingErrorCode;
1378
+ readonly limit?: number;
1379
+ readonly actual?: number;
1380
+ readonly diagnostic?: string;
1381
+ constructor(code: HarfBuzzShapingErrorCode, details?: {
1382
+ readonly limit?: number;
1383
+ readonly actual?: number;
1384
+ readonly diagnostic?: string;
1385
+ readonly cause?: unknown;
1386
+ });
1387
+ }
1388
+ /**
1389
+ * Resource budgets and cache sizes for one shaper. Every field optional.
1390
+ *
1391
+ * The caches exist because shaping is the expensive step: a face is opened once and reused, and
1392
+ * identical runs return their previous result. The caps exist because file-derived input decides
1393
+ * how much work is asked for.
1394
+ */
1395
+ interface HarfBuzzTextShaperOptions {
1396
+ readonly maxFontBytes?: number;
1397
+ readonly maxInputUtf16?: number;
1398
+ readonly maxCodepoints?: number;
1399
+ readonly maxGlyphs?: number;
1400
+ readonly maxCachedFaces?: number;
1401
+ /** Aggregate retained native font bytes; oversized faces are shaped without retention. */
1402
+ readonly maxCachedFontBytes?: number;
1403
+ readonly maxCachedShapes?: number;
1404
+ readonly maxOutlineBytes?: number;
1405
+ readonly maxCachedOutlineBytes?: number;
1406
+ readonly maxShapedRunBytes?: number;
1407
+ readonly maxCachedShapeBytes?: number;
1408
+ readonly instrumentation?: HarfBuzzTextShaperInstrumentation;
1409
+ }
1410
+ /** One face-cache transition, for instrumentation. */
1411
+ interface HarfBuzzFaceCacheEvent {
1412
+ readonly kind: 'created' | 'hit' | 'evicted';
1413
+ readonly identity: string;
1414
+ }
1415
+ /**
1416
+ * Optional counters for the work a shaper is expected to do rarely.
1417
+ *
1418
+ * Exists so tests can assert the ABSENCE of work — re-opening a face or re-copying its bytes per
1419
+ * shape call would not change any output, only make typing slow, which no rendering assertion
1420
+ * would catch.
1421
+ */
1422
+ interface HarfBuzzTextShaperInstrumentation {
1423
+ readonly onFaceCacheEvent?: (event: HarfBuzzFaceCacheEvent) => void;
1424
+ readonly onShapeCacheEvent?: (event: HarfBuzzShapeCacheEvent) => void;
1425
+ readonly onByteCopy?: () => void;
1426
+ readonly onTableScan?: () => void;
1427
+ readonly onShapeCall?: () => void;
1428
+ readonly onOutlinePathCall?: () => void;
1429
+ readonly onOutlineCacheEvent?: (event: HarfBuzzOutlineCacheEvent) => void;
1430
+ }
1431
+ /** One shape-cache transition, for instrumentation. */
1432
+ interface HarfBuzzShapeCacheEvent {
1433
+ readonly kind: 'hit' | 'miss' | 'stored' | 'evicted' | 'skipped' | 'cleared';
1434
+ readonly retainedBytes: number;
1435
+ }
1436
+ /** One outline-cache transition, for instrumentation. */
1437
+ interface HarfBuzzOutlineCacheEvent {
1438
+ readonly kind: 'created' | 'hit' | 'evicted' | 'skipped' | 'cleared';
1439
+ readonly retainedBytes: number;
1440
+ }
1441
+ /**
1442
+ * A {@link TextShaper} backed by HarfBuzz, holding WASM resources.
1443
+ *
1444
+ * {@link HarfBuzzTextShaper.dispose} is not optional housekeeping: the cached faces are WASM
1445
+ * allocations that garbage collection cannot reclaim, so a shaper outliving its editor leaks
1446
+ * until the page goes away.
1447
+ */
1448
+ interface HarfBuzzTextShaper extends TextShaper {
1449
+ dispose(): void;
1450
+ }
1451
+ /** Structural sfnt validation that does not construct native HarfBuzz objects. */
1452
+ declare const harfBuzzFontValidator: FontByteValidator;
1453
+ /**
1454
+ * Build a HarfBuzz-backed shaper.
1455
+ *
1456
+ * Requires `initializeHarfBuzz()` to have resolved — shaping is synchronous, so the WASM runtime
1457
+ * must already be loaded. Dispose the result when the editor goes away, or its cached faces leak.
1458
+ */
1459
+ declare const createHarfBuzzTextShaper: (options?: HarfBuzzTextShaperOptions) => HarfBuzzTextShaper;
1460
+
1461
+ /**
1462
+ * Point the text shaper at an externally hosted copy of `harfbuzz.wasm`.
1463
+ *
1464
+ * Needed only under bundlers that do not emit `new URL(..., import.meta.url)`
1465
+ * assets. esbuild and Bun are the common ones, and so is any build that inlines
1466
+ * dynamic imports, such as a library bundle. There, the build succeeds and the
1467
+ * shaper fails at runtime with an `EditorFontError` whose code is
1468
+ * `wasmUnavailable`; this function is the fix. Webpack, Turbopack and Vite emit
1469
+ * the binary on their own, and passing a URL there simply overrides theirs.
1470
+ *
1471
+ * ```ts
1472
+ * import { setHarfBuzzWasmUrl } from '@docx-editor.dev/core/layout';
1473
+ * setHarfBuzzWasmUrl('/static/harfbuzz.wasm');
1474
+ * ```
1475
+ *
1476
+ * Pass a URL your application controls. It is fetched and instantiated as
1477
+ * WebAssembly, so never derive it from user input, a query parameter, or remote
1478
+ * configuration. Serving it cross-origin needs that origin in your `connect-src`
1479
+ * CSP directive, and WebAssembly needs `wasm-unsafe-eval` in `script-src` either
1480
+ * way.
1481
+ *
1482
+ * Call it before the first editor is created. The runtime reads the location once
1483
+ * and caches the result, so a call afterwards warns and does nothing: fix the
1484
+ * call site and reload. The file to serve is exported as
1485
+ * `@docx-editor.dev/core/harfbuzz.wasm`, and it must be the copy from the
1486
+ * installed package version, because the runtime refuses a version mismatch at
1487
+ * load rather than shaping with unverified metrics.
1488
+ *
1489
+ * @public
1490
+ */
1491
+ declare function setHarfBuzzWasmUrl(url: string | URL): void;
1492
+
1493
+ /** UTF-16 boundaries that are both whole-grapheme and HarfBuzz cluster edges. */
1494
+ declare function shapedHorizontalBoundaries(run: ShapedRun): readonly number[];
1495
+ /**
1496
+ * Whether an offset is a real caret position: both a grapheme boundary and a shaped cluster edge.
1497
+ *
1498
+ * Both conditions, because they disagree. A ligature is one cluster spanning two graphemes, and
1499
+ * an offset inside it has no geometry the shaper can answer for — placing a caret there would
1500
+ * mean inventing an x coordinate.
1501
+ */
1502
+ declare function isWholeGraphemeHorizontalBoundary(run: ShapedRun, utf16Offset: number): boolean;
1503
+ /** Every published shaped boundary has exact geometry from the shaping result. */
1504
+ declare function isGeometryTrustedCaretOffset(run: ShapedRun, utf16Offset: number): boolean;
1505
+ /**
1506
+ * Whether the distance from a line's start to an offset can be trusted as exact.
1507
+ *
1508
+ * Requires BOTH ends to be shaped boundaries: accumulating advances across an offset the shaper
1509
+ * cannot place would produce a caret x that drifts further along the line.
1510
+ */
1511
+ declare function isCumulativeGeometryTrustedFromLineOrigin(run: ShapedRun, lineStartUtf16Offset: number, utf16Offset: number): boolean;
1512
+ /** Grapheme offsets corresponding to exact shaped boundaries. */
1513
+ declare function semanticHorizontalBoundaries(run: ShapedRun): readonly number[];
1514
+
1515
+ /**
1516
+ * One user-perceived character, and the UTF-16 range that encodes it.
1517
+ *
1518
+ * The unit a caret moves by. An emoji with a skin-tone modifier is ONE segment spanning several
1519
+ * UTF-16 code units, so stepping by code unit would put the caret inside it.
1520
+ */
1521
+ interface GraphemeSegment {
1522
+ readonly index: number;
1523
+ readonly text: string;
1524
+ readonly utf16From: number;
1525
+ readonly utf16To: number;
1526
+ }
1527
+ /**
1528
+ * The replaceable segmentation strategy.
1529
+ *
1530
+ * An explicit seam rather than a direct `Intl.Segmenter` call, so tests can install a
1531
+ * deterministic boundary and a runtime without `Intl.Segmenter` can be given one.
1532
+ */
1533
+ interface GraphemeBoundary {
1534
+ segment(text: string): readonly GraphemeSegment[];
1535
+ }
1536
+ /** Invariant locale for deterministic cross-runtime grapheme boundaries. */
1537
+ declare const GRAPHEME_SEGMENTER_LOCALE: 'und';
1538
+ /** Whether this runtime provides `Intl.Segmenter`, which the default boundary requires. */
1539
+ declare function isIntlSegmenterAvailable(): boolean;
1540
+ /**
1541
+ * The default boundary, over `Intl.Segmenter` at the invariant `und` locale.
1542
+ *
1543
+ * Locale-invariant on purpose: grapheme boundaries must not vary with the user's locale, or the
1544
+ * same document would paginate differently for different readers.
1545
+ */
1546
+ declare const intlGraphemeBoundary: GraphemeBoundary;
1547
+ /** Current boundary generation, for callers that cache segmentation-derived answers. */
1548
+ declare function graphemeBoundaryEpoch(): number;
1549
+ /**
1550
+ * Split text into graphemes through the active boundary.
1551
+ *
1552
+ * Memoized on the last texts seen, because paragraph layout asks about the same string
1553
+ * repeatedly and a full segmentation pass per call made layout quadratic in paragraph length.
1554
+ */
1555
+ declare function segmentGraphemes(text: string): readonly GraphemeSegment[];
1556
+ /**
1557
+ * Install a different segmentation strategy, invalidating the memo.
1558
+ *
1559
+ * For tests and for runtimes lacking `Intl.Segmenter`. Call {@link resetGraphemeBoundary} to
1560
+ * restore the default.
1561
+ */
1562
+ declare function setGraphemeBoundary(boundary: GraphemeBoundary): void;
1563
+ /** Restore the default `Intl.Segmenter` boundary and clear the memo. */
1564
+ declare function resetGraphemeBoundary(): void;
1565
+ /** How many user-perceived characters the text holds. */
1566
+ declare function graphemeCount(text: string): number;
1567
+ /**
1568
+ * The grapheme index containing a UTF-16 offset. Clamps rather than throwing.
1569
+ *
1570
+ * Backed by a single-entry index cache: this runs once per character during paragraph layout, and
1571
+ * re-segmenting per call is what made a 20,000-character paragraph take minutes to open. One
1572
+ * entry rather than a map, because the keys are file-derived strings of unbounded size.
1573
+ */
1574
+ declare function utf16OffsetToGrapheme(text: string, utf16Offset: number): number;
1575
+ /** The UTF-16 offset a grapheme index starts at. Clamps rather than throwing. */
1576
+ declare function graphemeOffsetToUtf16(text: string, graphemeOffset: number): number;
1577
+
1578
+ /**
1579
+ * One word-segmentation span. `wordLike` separates words from the whitespace and punctuation
1580
+ * between them, which is what double-click selection needs to skip.
1581
+ */
1582
+ interface WordSegment {
1583
+ readonly utf16From: number;
1584
+ readonly utf16To: number;
1585
+ readonly wordLike: boolean;
1586
+ }
1587
+ /**
1588
+ * The replaceable word-segmentation strategy.
1589
+ *
1590
+ * Falls back to a BOUNDED grapheme-safe splitter where `Intl.Segmenter` is absent — bounded
1591
+ * because the input is file-derived, and grapheme-safe so a fallback never splits inside an
1592
+ * emoji.
1593
+ */
1594
+ interface WordBoundary {
1595
+ segment(text: string): readonly WordSegment[];
1596
+ }
1597
+ /** Invariant locale for deterministic cross-runtime word boundaries. */
1598
+ declare const WORD_SEGMENTER_LOCALE: 'und';
1599
+ /** Whether this runtime provides word-granularity `Intl.Segmenter`. */
1600
+ declare function isIntlWordSegmenterAvailable(): boolean;
1601
+ /** Intl.Segmenter word boundary for the invariant locale. */
1602
+ declare function createIntlWordBoundary(): WordBoundary;
1603
+ /** Bounded deterministic fallback — not full UAX #29 word conformance. */
1604
+ declare function boundedFallbackWordSegments(text: string): readonly WordSegment[];
1605
+ /** Explicit bounded fallback boundary (grapheme-safe, narrower than Intl). */
1606
+ declare function createBoundedFallbackWordBoundary(): WordBoundary;
1607
+ /** Injection points for {@link createDefaultWordBoundary}, so tests can force either path. */
1608
+ interface WordBoundaryResolverDeps {
1609
+ readonly isIntlAvailable?: () => boolean;
1610
+ readonly createIntlBoundary?: () => WordBoundary;
1611
+ readonly createFallbackBoundary?: () => WordBoundary;
1612
+ }
1613
+ /** Resolve production word boundary: Intl when available/construction succeeds, else bounded fallback. */
1614
+ declare function createDefaultWordBoundary(deps?: WordBoundaryResolverDeps): WordBoundary;
1615
+ /** Cached immutable production boundary (first resolved instance only). */
1616
+ declare function resolveDefaultWordBoundary(): WordBoundary;
1617
+ /** Split text into word segments through the given boundary, or the resolved default. */
1618
+ declare function segmentWords(text: string, boundary?: WordBoundary): readonly WordSegment[];
1619
+ /**
1620
+ * A word span expressed in GRAPHEME offsets rather than UTF-16 ones.
1621
+ *
1622
+ * What selection actually uses: a range whose ends are UTF-16 offsets could land inside a
1623
+ * grapheme, and selecting half an emoji is not a word.
1624
+ */
1625
+ interface GraphemeWordSegmentRecord {
1626
+ readonly graphemeFrom: number;
1627
+ readonly graphemeTo: number;
1628
+ readonly wordLike: boolean;
1629
+ }
1630
+ /** Map UTF-16 word segments to grapheme-safe half-open ranges within one paragraph. */
1631
+ declare function wordSegmentsToGraphemeRecords(text: string, segments: readonly WordSegment[]): readonly GraphemeWordSegmentRecord[];
1632
+
1633
+ /** One span in the engine's published story order. @public */
1634
+ interface SemanticSpanVisit {
1635
+ readonly page: PageRecord;
1636
+ readonly story: SemanticStoryKind;
1637
+ /** Root story from which textbox descent began; equal to `story` outside textboxes. */
1638
+ readonly rootStory: SemanticRootStoryKind;
1639
+ /** Precise root host and absolute origin for story-relative geometry. */
1640
+ readonly root: SemanticStoryVisit;
1641
+ /** Absolute origin of the immediate root or textbox story containing this span. */
1642
+ readonly storyOrigin: Readonly<{
1643
+ x: number;
1644
+ y: number;
1645
+ }>;
1646
+ /** Absolute laid-out span bounds in page-stack coordinates. */
1647
+ readonly absoluteBox: LayoutBox;
1648
+ /** Owning note scope/area where applicable; null for body and page furniture. */
1649
+ readonly noteScopeId: string | null;
1650
+ readonly noteAreaKind: SemanticStoryVisit['noteAreaKind'];
1651
+ /** Zero outside a textbox, otherwise its bounded nesting depth. */
1652
+ readonly textboxDepth: number;
1653
+ /** Immediate textbox-owning drawing, anchored or inline, or null in the root story. */
1654
+ readonly textboxOwner: TextboxOwnerRecord | null;
1655
+ /** Root-to-leaf textbox owners, preserving drawing identity for future exporters. */
1656
+ readonly textboxPath: readonly TextboxOwnerRecord[];
1657
+ /** Enclosing published fragment; use paragraphId for the authored span owner. */
1658
+ readonly paragraph: ParagraphFragmentRecord;
1659
+ /** Authored paragraph owning this span, including spans merged into another fragment. */
1660
+ readonly paragraphId: string;
1661
+ readonly line: LineRecord;
1662
+ readonly span: StyleSpanRecord;
1663
+ /**
1664
+ * Model address for authored text. Projected atoms intentionally return null even though
1665
+ * their geometry record carries a range used internally by layout.
1666
+ */
1667
+ readonly sourceRange: SourceRange | null;
1668
+ }
1669
+ /** Return the model address exporters may use, excluding layout-projected atoms. @public */
1670
+ declare function exportSourceRangeOf(span: StyleSpanRecord): SourceRange | null;
1671
+ /**
1672
+ * Visit every published span in page/story order without consulting the source package.
1673
+ * @public
1674
+ */
1675
+ declare function forEachSemanticSpan(layout: SemanticLayout, visitor: (visit: SemanticSpanVisit) => void): void;
1676
+
1677
+ /**
1678
+ * The part of a line that belongs to ONE paragraph.
1679
+ *
1680
+ * A line normally belongs to one paragraph outright, and then this is the whole of it — the
1681
+ * same object every caller read before, so nothing about an ordinary document takes a new
1682
+ * path. A resolved display mode merges paragraphs that a tracked decision merges, and the line
1683
+ * carrying the join holds spans from two of them. Offsets there are ambiguous by themselves:
1684
+ * both paragraphs start at zero, so an offset means nothing without the paragraph it counts in.
1685
+ */
1686
+ interface LineSegment {
1687
+ readonly paragraphId: string;
1688
+ readonly start: number;
1689
+ readonly end: number;
1690
+ readonly spans: readonly StyleSpanRecord[];
1691
+ readonly drawings: readonly InlineDrawingRecord[];
1692
+ }
1693
+ /** Every paragraph a line carries, in visual order. One entry for an ordinary line. */
1694
+ declare function lineSegments(line: LineRecord): readonly LineSegment[];
1695
+
1696
+ /**
1697
+ * The box a painter clips a vector shape to: `bounds` grown on each side where a stroked
1698
+ * outline reaches past the drawing extent.
1699
+ *
1700
+ * An outline is centred on its geometry and is not cut at the extent, a text column, or a cell
1701
+ * edge that meets the extent: a rule at x=0 of a group with `wp:effectExtent l="0"` keeps
1702
+ * its full width. The reach comes from butt-capped segments and miter joins, measured
1703
+ * only on the part of each segment inside the extent, so geometry outside the extent stays
1704
+ * clipped and a side grows by at most the miter reach. A line-end triangle on a line that
1705
+ * ends inside the extent grows it too. A side stays as it is when a layout region or a
1706
+ * negative effect extent cut into the shape itself, and a drawing clipped to nothing stays
1707
+ * empty.
1708
+ *
1709
+ * Only the paint clip grows. Collisions, hit testing, and pointer targets keep `paintBounds`,
1710
+ * so a painter clips with this box without making it clickable.
1711
+ *
1712
+ * `bounds` is the drawing's `paintBounds` in any translation: the record's own, or the
1713
+ * absolute paint bounds of an export visit.
1714
+ *
1715
+ * @public
1716
+ */
1717
+ declare function vectorShapeInkClip(drawing: InlineDrawingRecord | AnchoredDrawingRecord, bounds: LayoutBox): LayoutBox;
1718
+
1719
+ /** Memoized style inputs shared by every story in one document view. @public */
1720
+ interface DocumentStyleDependencies {
1721
+ readonly styleCascade: () => StyleCascadeTable | undefined;
1722
+ readonly defaultTabStopPt: () => number;
1723
+ readonly compatibilityMode?: () => number | undefined;
1724
+ readonly numberingIndex: () => NumberingIndex;
1725
+ }
1726
+ /** Build the cascade and numbering projections layout consumes. @public */
1727
+ declare function createDocumentStyleDependencies(view: HeadlessDocumentView): DocumentStyleDependencies;
1728
+
1729
+ /** Paired projector/cache identities shared by browser, exporters, and future story hosts. */
1730
+ interface StoryProjectionDependencies {
1731
+ readonly tokenForParagraphForPart: (partName: string, paragraph: OoxmlNode) => string;
1732
+ /** Memoized aggregate for every paragraph in an immutable table subtree. */
1733
+ readonly tokenForTableForPart: (partName: string, table: OoxmlNode) => string;
1734
+ readonly epochForPart: (partName: string) => string;
1735
+ }
1736
+
1737
+ /**
1738
+ * Sanitized projectors paired with the cache identities required to use them safely.
1739
+ *
1740
+ * Keep this object intact when passing it to a document composition helper. Projected text and
1741
+ * its dependency tokens are one contract: accepting either half independently permits stale
1742
+ * paragraph caches after relationship- or property-only revisions.
1743
+ * @public
1744
+ */
1745
+ interface DocumentLinkProjectors extends StoryProjectionDependencies {
1746
+ readonly projectLink: HyperlinkProjector;
1747
+ readonly projectLinkForPart: (partName: string) => HyperlinkProjector;
1748
+ }
1749
+ /** Sanitized body and per-part link projectors over one neutral document view. @public */
1750
+ declare function createDocumentLinkProjectors(view: HeadlessDocumentView): DocumentLinkProjectors;
1751
+
1752
+ /** Page geometry for header/footer anchored frame resolution (story-relative layout space). */
1753
+ interface HeaderFooterPageContext {
1754
+ readonly pageNumber: number;
1755
+ readonly pageWidth: number;
1756
+ readonly pageHeight: number;
1757
+ readonly marginLeft: number;
1758
+ readonly marginRight: number;
1759
+ readonly marginTop: number;
1760
+ readonly marginBottom: number;
1761
+ /** `w:pgMar/@w:header` for a header, `@w:footer` for a footer: the story edge's distance. */
1762
+ readonly storyDistance?: number;
1763
+ }
1764
+ /** Per-sheet geometry needed to resolve header/footer anchors before wrap and clipping. */
1765
+ interface HeaderFooterLayoutPageContext extends FieldPageContext {
1766
+ /** Effective content-box inset after this page's header/footer reserves are applied. */
1767
+ readonly contentInsetTop?: number;
1768
+ /** Effective bottom content-box inset after footer reserve. */
1769
+ readonly contentInsetBottom?: number;
1770
+ /** Story-box top measured from the physical sheet top. */
1771
+ readonly storyTop?: number;
1772
+ }
1773
+ interface HeaderFooterStoryLayout {
1774
+ readonly partName: string;
1775
+ /**
1776
+ * The part this story was laid out from.
1777
+ *
1778
+ * Carried because a derivation that runs over the finished layout cannot reach it otherwise:
1779
+ * the layout receives furniture as fragments plus a NAME, and a name is not a tree. The
1780
+ * content-control boundary pass needs the tree to know which controls exist at all.
1781
+ */
1782
+ readonly part?: OoxmlPart;
1783
+ /** Main-document relationship id when the furniture source knows it. */
1784
+ readonly rId?: string;
1785
+ /**
1786
+ * Bounded identity of the story's canonical OOXML content.
1787
+ *
1788
+ * Furniture cache keys must not rely on {@link flowHeight} alone: equal-height A→B edits
1789
+ * would otherwise reuse stale page furniture. Derived as a 16-hex FNV-1a over the part's
1790
+ * canonical fingerprint — never DOM identity or unbounded raw serialization in the key.
1791
+ * PAGE/NUMPAGES/SECTIONPAGES projection shares this key; page context is cached separately.
1792
+ */
1793
+ readonly contentKey: string;
1794
+ /** Snapshot identity for projected links and live document-property fields in this story. */
1795
+ readonly projectionEpoch?: string;
1796
+ /** Story-relative fragments; origin at the story box's top-left. */
1797
+ readonly fragments: readonly BlockFragmentRecord[];
1798
+ /** The height the blocks actually flow to — what sizes the box on every page. */
1799
+ readonly flowHeight: number;
1800
+ /**
1801
+ * Allowlisted complex PAGE / NUMPAGES / SECTIONPAGES presence detected for this story.
1802
+ *
1803
+ * Callers use this to skip attaching a page-field projector when the baseline is enough.
1804
+ */
1805
+ readonly pageFieldNeeds: StoryPageFieldNeeds;
1806
+ /** Anchored drawings owned by this story, in story-relative coordinates. */
1807
+ readonly anchoredDrawings?: readonly AnchoredDrawingRecord[];
1808
+ /**
1809
+ * Re-layout this story under page-field and optional attached-page geometry context.
1810
+ *
1811
+ * Field-free stories return `this`. Count-only stories cache by the counts they read.
1812
+ * PAGE stories cache by the distinct evaluated values (including format) with a bounded LRU.
1813
+ */
1814
+ readonly withPageContext: (ctx: HeaderFooterLayoutPageContext) => HeaderFooterStoryLayout;
1815
+ }
1816
+ /**
1817
+ * Story-level inputs the body path has always had and furniture never did.
1818
+ *
1819
+ * A BAG, not more positional parameters. The signature below already carries fifteen, and its
1820
+ * own doc comment used to tell new callers to "keep passing `undefined` for what they do not
1821
+ * set" — which is why `numberingIndex` was never added and a numbered paragraph in a header
1822
+ * painted no marker for as long as headers have been editable. Everything new goes here.
1823
+ */
1824
+ interface HeaderFooterStoryInputs {
1825
+ readonly compatibilityMode?: number;
1826
+ /**
1827
+ * `numbering.xml`, so a `w:numPr` paragraph in this story resolves a marker — directly or
1828
+ * inside an anchored text box, which lays out with its own per-box counters.
1829
+ *
1830
+ * Absent, the story lays out exactly as before: no marker record, and no numbering indent
1831
+ * merged into the paragraph's own.
1832
+ */
1833
+ readonly numberingIndex?: NumberingIndex;
1834
+ /** Sanitized hyperlink seams scoped to this header/footer part. */
1835
+ readonly projectLink?: HyperlinkProjector;
1836
+ readonly projectFieldLink?: FieldLinkProjector;
1837
+ /** @internal */
1838
+ readonly showFieldCodes?: boolean;
1839
+ /** Per-paragraph identity for links and live document-property projection. */
1840
+ readonly projectionTokenForParagraph?: (paragraph: OoxmlNode) => string;
1841
+ /** Memoized aggregate projection identity for table subtrees. */
1842
+ readonly projectionTokenForTable?: (table: OoxmlNode) => string;
1843
+ /** Aggregate identity of link and live-field projections for furniture reuse keys. */
1844
+ readonly projectionEpoch?: string;
1845
+ /** Reviewers whose changes project as accepted in this story. */
1846
+ readonly revisionAuthorFilter?: RevisionAuthorFilter;
1847
+ }
1848
+ /**
1849
+ * Lay one header/footer part out at `contentWidth`.
1850
+ *
1851
+ * Line ids are namespaced by part so the body's `line-N` counter — which incremental
1852
+ * convergence compares — never moves because a header changed.
1853
+ *
1854
+ * When `pageContext` is set, allowlisted PAGE/NUMPAGES/SECTIONPAGES instructions project
1855
+ * live values; otherwise those fields contribute only cached result text (often empty).
1856
+ * Field-free stories ignore `pageContext` and share one baseline layout.
1857
+ *
1858
+ * `defaultTabStopPt` is the document's `w:settings/w:defaultTabStop` (ECMA-376 §17.15.1.25)
1859
+ * in points; absent keeps the 0.5" schema default. Furniture tabs on the SAME grid as the
1860
+ * body — a page-number tab in a metric-locale footer belongs on the document's interval, not
1861
+ * on a constant. It sits at the tail because the parameters ahead of it are already
1862
+ * positional; new callers should keep passing `undefined` for what they do not set.
1863
+ *
1864
+ * NEW inputs belong in {@link HeaderFooterStoryInputs}, the trailing bag, rather than as a
1865
+ * sixteenth position. Fifteen is what stopped `numberingIndex` being threaded here at all.
1866
+ */
1867
+ declare function layoutHeaderFooterStory(part: OoxmlPart, contentWidth: number, measurer: TextMeasurer, producer: string, cache?: ParagraphLayoutCache<readonly PendingLine[]>, styleCascade?: StyleCascadeTable, pageContext?: FieldPageContext, maxPageContextEntries?: number, defaultTabStopPt?: number, displayMode?: RevisionDisplayMode, inlineDrawingLayout?: InlineDrawingLayoutContext, drawingTokenForParagraph?: (paragraph: OoxmlNode) => string, drawingLayoutToken?: string, hfPageContext?: HeaderFooterPageContext, documentProperties?: DocumentProperties, inputs?: HeaderFooterStoryInputs): HeaderFooterStoryLayout;
1868
+
1869
+ /** Which header/footer variant a page shows (ECMA-376 §17.10.5). */
1870
+ type HeaderFooterVariantName = 'default' | 'first' | 'even';
1871
+ /**
1872
+ * Pre-laid page furniture, supplied by the host (phase 2).
1873
+ *
1874
+ * Baseline stories are laid out once per variant (`layoutHeaderFooterStory`) for furniture
1875
+ * height. Stories that actually contain allowlisted PAGE/NUMPAGES fields attach a projector
1876
+ * so document-level finalize can re-layout under the known page count; field-free furniture
1877
+ * reuses the baseline on every sheet.
1878
+ */
1879
+ interface PageFurniture {
1880
+ readonly titlePage: boolean;
1881
+ readonly evenAndOddHeaders: boolean;
1882
+ readonly headers: ReadonlyMap<HeaderFooterVariantName, HeaderFooterStoryLayout>;
1883
+ readonly footers: ReadonlyMap<HeaderFooterVariantName, HeaderFooterStoryLayout>;
1884
+ }
1885
+
1886
+ /** Page furniture supplied to semantic layout. @public */
1887
+ interface DocumentFurnitureSource {
1888
+ furniture(): PageFurniture | undefined;
1889
+ sectionFurniture(): readonly (PageFurniture | undefined)[];
1890
+ }
1891
+ /** Inputs that remain valid for the lifetime of one furniture source. @public */
1892
+ interface CreateDocumentFurnitureSourceOptions {
1893
+ readonly view: HeadlessDocumentView;
1894
+ readonly measurer: TextMeasurer;
1895
+ readonly producer: string;
1896
+ readonly cache: ParagraphLayoutCache<readonly PendingLine[]>;
1897
+ readonly styleCascade?: () => StyleCascadeTable | undefined;
1898
+ readonly numberingIndex?: () => NumberingIndex;
1899
+ readonly defaultTabStopPt?: () => number;
1900
+ readonly compatibilityMode?: () => number | undefined;
1901
+ readonly displayMode?: RevisionDisplayMode;
1902
+ readonly revisionAuthorFilter?: RevisionAuthorFilter;
1903
+ readonly inlineDrawingLayoutForPart?: (partName: string) => InlineDrawingLayoutContext | undefined;
1904
+ readonly drawingLayoutTokenForPart?: (partName: string) => string;
1905
+ readonly drawingTokenForParagraphForPart?: (partName: string, paragraph: OoxmlNode) => string;
1906
+ /** Link/property projection and its inseparable cache identities. */
1907
+ readonly linkProjectors: DocumentLinkProjectors;
1908
+ readonly projectFieldLink?: FieldLinkProjector;
1909
+ /** Field-code inspection projection. @internal */
1910
+ readonly showFieldCodes?: boolean;
1911
+ }
1912
+ /** Build section-aware header/footer layout from a neutral document view. @public */
1913
+ declare function createDocumentFurnitureSource(options: CreateDocumentFurnitureSourceOptions): DocumentFurnitureSource;
1914
+
1915
+ /**
1916
+ * Where one note is referenced from — what per-page and per-section restart rules are computed
1917
+ * against.
1918
+ */
1919
+ interface NoteReferenceSite {
1920
+ /** Stable note id (`w:id`). */
1921
+ readonly noteId: number;
1922
+ /** Section index of the reference (0-based). */
1923
+ readonly sectionIndex: number;
1924
+ /**
1925
+ * Page index of the reference when known (0-based). Required for `eachPage` restart;
1926
+ * when omitted, `eachPage` behaves like `continuous` for that site.
1927
+ */
1928
+ readonly pageIndex?: number;
1929
+ /** When true, consumes no automatic number (`customMarkFollows`). */
1930
+ readonly customMarkFollows?: boolean;
1931
+ }
1932
+ /**
1933
+ * The mark a note reference paints.
1934
+ *
1935
+ * `null` where `w:customMarkFollows` suppresses it: the document supplies its own glyph, and
1936
+ * painting an automatic number too would show the note twice.
1937
+ */
1938
+ interface NoteDisplayMark {
1939
+ readonly noteId: number;
1940
+ /** Formatted mark, or `null` when suppressed by customMarkFollows. */
1941
+ readonly mark: string | null;
1942
+ /** 1-based automatic sequence number when assigned; absent when suppressed. */
1943
+ readonly displayNumber?: number;
1944
+ }
1945
+ type ResolvedNoteProperties = ResolvedFootnoteProperties | ResolvedEndnoteProperties;
1946
+ /**
1947
+ * Derive display marks for references of one note kind in document order.
1948
+ *
1949
+ * Restart rules:
1950
+ * - `continuous` — single sequence across the document from `numStart`
1951
+ * - `eachSect` — restart at `numStart` when `sectionIndex` changes
1952
+ * - `eachPage` — restart when `pageIndex` changes (falls back to continuous if unknown)
1953
+ *
1954
+ * IDs are stable; only display numbers change. Non-mutating.
1955
+ */
1956
+ declare function deriveNoteDisplayMarks(noteKind: NoteKind, references: readonly NoteReferenceSite[], properties: ResolvedNoteProperties): readonly NoteDisplayMark[];
1957
+ /**
1958
+ * Derive marks using per-reference-section resolved properties (`numFmt` / `numStart` /
1959
+ * `numRestart`). Restart rules consult each site's own section props.
1960
+ */
1961
+ declare function deriveNoteDisplayMarksResolved(_noteKind: NoteKind, references: readonly NoteReferenceSite[], resolveProps: (sectionIndex: number) => ResolvedNoteProperties): readonly NoteDisplayMark[];
1962
+ /** Map noteId → formatted mark for quick lookup (last site wins if duplicated). */
1963
+ declare function noteDisplayMarkMap(marks: readonly NoteDisplayMark[]): ReadonlyMap<number, string | null>;
1964
+
1965
+ /** Hard ceiling on notes laid out in one pass (fail closed beyond). */
1966
+ declare const MAX_NOTES_LAID_OUT = 10000;
1967
+ /** Hard ceiling on fragments emitted for one note (split / continuation). */
1968
+ declare const MAX_NOTE_FRAGMENTS = 512;
1969
+ /**
1970
+ * Why note layout stopped short and fell back.
1971
+ *
1972
+ * Every one is a BOUND rather than a bug: note counts, fragment counts and heights all come from
1973
+ * a file, and a document can ask for more note area than a page has. Falling back with a reason
1974
+ * keeps the document open instead of failing to lay out.
1975
+ */
1976
+ type NoteLayoutFallbackReason = 'note-count-limit' | 'note-fragment-limit' | 'note-reflow-exhausted' | 'note-height-cap'
1977
+ /** Authored separator/continuationSeparator taller than the content column. */
1978
+ | 'note-separator-height-cap' | 'note-continuation-notice-height-cap' | 'missing-note-body' | 'dangling-note-reference';
1979
+ /**
1980
+ * One note's body laid out as its own story, in story-relative coordinates.
1981
+ *
1982
+ * Relative rather than page-absolute because a note moves between pages during pagination — the
1983
+ * page it lands on is decided after its content is measured.
1984
+ */
1985
+ interface NoteStoryLayout {
1986
+ readonly noteKind: NoteKind;
1987
+ readonly noteId: number;
1988
+ /** `footnote:N` / `endnote:N` — matches EditorScope note id encoding. */
1989
+ readonly scopeId: string;
1990
+ readonly noteType: ReturnType<typeof noteTypeOf>;
1991
+ /** Story-relative fragments; origin at the story box's top-left. */
1992
+ readonly fragments: readonly BlockFragmentRecord[];
1993
+ /** Height the blocks flow to (points). */
1994
+ readonly flowHeight: number;
1995
+ /** True when layout hit a named bound and returned a truncated / empty story. */
1996
+ readonly fallbackReason?: NoteLayoutFallbackReason;
1997
+ }
1998
+ /** Paint style for Word-default / marker-only separator rules (not CSS inventing content). */
1999
+ type NoteSeparatorRuleStyle = 'single' | 'double';
2000
+ /**
2001
+ * The rule between body text and the note area.
2002
+ *
2003
+ * Synthesized when the document declares none, because Word draws one regardless — a document
2004
+ * without an authored separator still shows the line a reader expects.
2005
+ */
2006
+ interface NoteSeparatorLayout {
2007
+ readonly kind: 'separator' | 'continuationSeparator';
2008
+ readonly fragments: readonly BlockFragmentRecord[];
2009
+ readonly flowHeight: number;
2010
+ /** True when the engine synthesized a default rule (document had none). */
2011
+ readonly synthetic: boolean;
2012
+ /**
2013
+ * Layout-owned rule when the separator is marker-only (`w:separator` /
2014
+ * `w:continuationSeparator`) or fully synthetic. Absent when an authored separator
2015
+ * story has real paragraph/run/border content that paint should render as fragments.
2016
+ */
2017
+ readonly ruleStyle?: NoteSeparatorRuleStyle;
2018
+ /** Marker rule geometry inside the measured separator story. */
2019
+ readonly ruleBox?: LayoutBox;
2020
+ /** Resolved marker-run color; null/absent uses automatic black. */
2021
+ readonly ruleColor?: string | null;
2022
+ /** Set when an oversize authored separator was replaced with a synthetic rule. */
2023
+ readonly fallbackReason?: NoteLayoutFallbackReason;
2024
+ }
2025
+ /**
2026
+ * Inline drawing support for ONE notes part.
2027
+ *
2028
+ * A note lives in `/word/footnotes.xml` or `/word/endnotes.xml`, not in the body part, so its
2029
+ * pictures resolve against that part's relationships — the same per-part shape header/footer
2030
+ * furniture uses. Without it a note paragraph flows with no drawing context at all and a
2031
+ * picture inside it contributes no record: no image, and no placeholder either.
2032
+ */
2033
+ interface NoteStoryDrawings {
2034
+ readonly inlineDrawingLayout: InlineDrawingLayoutContext;
2035
+ /**
2036
+ * Per-paragraph projection + RESOURCE token for the break cache key.
2037
+ *
2038
+ * Image resources settle asynchronously, and the authored extent does not move when one
2039
+ * does — so without the resource in the key the cached `pending` lines are served forever
2040
+ * and a decoded picture never reaches the page.
2041
+ */
2042
+ readonly drawingTokenForParagraph?: (paragraph: OoxmlNode) => string;
2043
+ }
2044
+ interface LayoutNoteStoryOptions {
2045
+ readonly measurer: TextMeasurer;
2046
+ readonly producer: string;
2047
+ readonly cache?: ParagraphLayoutCache<readonly PendingLine[]>;
2048
+ readonly styleCascade?: StyleCascadeTable;
2049
+ /**
2050
+ * `numbering.xml`, so a `w:numPr` paragraph inside a note resolves a marker.
2051
+ *
2052
+ * Per story, like every other: `createListCounterState` is fresh per walk, so a numbered
2053
+ * list in a footnote starts at `w:start` rather than continuing the body's sequence.
2054
+ */
2055
+ readonly numberingIndex?: NumberingIndex;
2056
+ readonly defaultTabStopPt?: number;
2057
+ readonly compatibilityMode?: number;
2058
+ /**
2059
+ * The active line-grid pitch of the section that owns the note, in points. Note lines snap
2060
+ * to it like body lines do; absent means no grid.
2061
+ */
2062
+ readonly lineGridPitchPt?: number;
2063
+ readonly displayMode?: RevisionDisplayMode;
2064
+ readonly revisionAuthorFilter?: RevisionAuthorFilter;
2065
+ /**
2066
+ * Same projector seams the BODY walk uses. Without them a `w:hyperlink` or a HYPERLINK
2067
+ * field inside a note painted as plain text — measured, but carrying no link record for
2068
+ * paint to anchor and navigation to activate.
2069
+ */
2070
+ readonly projectLink?: HyperlinkProjector;
2071
+ /**
2072
+ * Per-part projector, preferred over `projectLink` when `ownerPartName` names the part.
2073
+ *
2074
+ * A `w:hyperlink` in `/word/footnotes.xml` declares its `r:id` in `footnotes.xml.rels`,
2075
+ * so it must resolve there — the body projector answers from the body part's
2076
+ * relationships, and when the two parts assign one id to different targets the footnote
2077
+ * link navigated to the body's target. Same per-part rule pictures follow through
2078
+ * {@link drawingsForPart}.
2079
+ */
2080
+ readonly projectLinkForPart?: (ownerPartName: string) => HyperlinkProjector | undefined;
2081
+ readonly projectFieldLink?: FieldLinkProjector;
2082
+ /** Field-code inspection projection. @internal */
2083
+ readonly showFieldCodes?: boolean;
2084
+ readonly projectionTokenForParagraphForPart?: (ownerPartName: string, paragraph: OoxmlNode) => string;
2085
+ readonly projectionTokenForTableForPart?: (ownerPartName: string, table: OoxmlNode) => string;
2086
+ /** Document properties for a document-property field inside a note story. */
2087
+ readonly documentProperties?: DocumentProperties;
2088
+ /**
2089
+ * The document's resolved REF inputs, so a footnote's cross-reference paints the live
2090
+ * value the body paints. The flow folds each paragraph's resolved values into its break
2091
+ * key, which is what repaints a note after a renumbering edit it cannot otherwise see.
2092
+ */
2093
+ readonly refFields?: RefFieldContext;
2094
+ /** Derived display marks for noteRef projection inside the note body. */
2095
+ readonly noteMarks?: NoteMarkContext;
2096
+ /**
2097
+ * Cap on flow height for a single note story (points). Hostile notes must not allocate
2098
+ * unbounded page fragments; overflow is split by the pagination layer, not here.
2099
+ */
2100
+ readonly maxFlowHeightPt?: number;
2101
+ /** Resolves inline drawing support for the notes part a story lives in. */
2102
+ readonly drawingsForPart?: (ownerPartName: string) => NoteStoryDrawings | undefined;
2103
+ /**
2104
+ * Notes part the story being laid out came from. Set by {@link layoutNoteById} /
2105
+ * {@link layoutNoteSeparator}, which are the callers that hold the part.
2106
+ */
2107
+ readonly ownerPartName?: string;
2108
+ }
2109
+ /**
2110
+ * Stable line-id namespace for one note. Body line counters compare these as opaque strings
2111
+ * and must not collide with `line-N` / `hf-…` ids.
2112
+ */
2113
+ declare function noteLineIdPrefix(noteKind: NoteKind, noteId: number): string;
2114
+ /** Collect normal (body) notes from a notes part, bounded. */
2115
+ declare function normalNotesOf(part: OoxmlPart | null | undefined): readonly OoxmlElement[];
2116
+ /** Find separator / continuationSeparator note body in a notes part. */
2117
+ declare function findSeparatorNote(part: OoxmlPart | null | undefined, kind: 'separator' | 'continuationSeparator'): OoxmlElement | undefined;
2118
+ /**
2119
+ * Lay one note node out at `contentWidth`.
2120
+ *
2121
+ * Does not paginate. Callers that need splits ask for fragments and cut at paragraph/line
2122
+ * boundaries in the note-pagination layer.
2123
+ */
2124
+ declare function layoutNoteStory(note: OoxmlNode, contentWidth: number, options: LayoutNoteStoryOptions): NoteStoryLayout | null;
2125
+ /** Layout a note by id from a notes part; null when missing. */
2126
+ declare function layoutNoteById(part: OoxmlPart | null | undefined, noteId: number, contentWidth: number, options: LayoutNoteStoryOptions): NoteStoryLayout | null;
2127
+ /**
2128
+ * Word-default paint style for a separator marker.
2129
+ *
2130
+ * Footnote and endnote separators both use a short single rule. A full-width double
2131
+ * border on a body heading (e.g. the comprehensive fixture’s end banner) is ordinary
2132
+ * paragraph `w:pBdr` ownership and must not transfer onto the note separator record.
2133
+ * Authored separator stories with real paragraph/run/border content bypass this via
2134
+ * fragment paint.
2135
+ */
2136
+ declare function defaultNoteSeparatorRuleStyle(_noteKind: NoteKind, _kind: 'separator' | 'continuationSeparator'): NoteSeparatorRuleStyle;
2137
+ /**
2138
+ * True when a separator note contains only OOXML separator markers (and empty noteRef
2139
+ * atoms Word often authors beside them) — no measurable text or paragraph borders.
2140
+ */
2141
+ declare function isMarkerOnlySeparatorNote(note: OoxmlNode, displayMode?: RevisionDisplayMode, revisionAuthorFilter?: RevisionAuthorFilter): boolean;
2142
+ /**
2143
+ * Layout the document's separator note, or synthesize a short horizontal rule.
2144
+ *
2145
+ * Single-marker / missing separators emit no paragraph fragments — paint draws the rule
2146
+ * from {@link NoteSeparatorLayout.ruleStyle} + box geometry. Authored separators with
2147
+ * real paragraph/run/border content keep their fragment story (including `w:pBdr`).
2148
+ * Explicitly empty stories and hidden markers do not acquire a default rule.
2149
+ *
2150
+ * When `maxFlowHeightPt` is set and an authored separator exceeds it, the engine fails
2151
+ * closed to a short synthetic rule ({@link note-separator-height-cap}) so note pagination
2152
+ * cannot burn the overflow budget on zero-progress separator-only pages.
2153
+ */
2154
+ declare function layoutNoteSeparator(part: OoxmlPart | null | undefined, kind: 'separator' | 'continuationSeparator', contentWidth: number, options: LayoutNoteStoryOptions, noteKind: NoteKind, maxFlowHeightPt?: number): NoteSeparatorLayout;
2155
+ /** Default rule geometry for a synthetic / marker-only separator, story-relative. */
2156
+ declare function syntheticSeparatorBox(contentWidth: number, flowHeight: number, kind?: 'separator' | 'continuationSeparator'): LayoutBox;
2157
+ /** Absolute separator box: short rule for marker/synthetic, full width for authored stories. */
2158
+ declare function noteSeparatorAreaBox(separator: NoteSeparatorLayout, contentX: number, contentWidth: number, areaTop: number): LayoutBox;
2159
+
2160
+ interface PageRefHit {
2161
+ readonly noteKind: NoteKind;
2162
+ readonly noteId: number;
2163
+ readonly paragraphId: string;
2164
+ /** Canonical UTF-16 atom offset within the paragraph. */
2165
+ readonly atomOffset: number;
2166
+ readonly customMarkFollows: boolean;
2167
+ readonly sectionIndex: number;
2168
+ }
2169
+
2170
+ /**
2171
+ * Bound on reflow attempts for an UNSEEDED document layout pass (cold open).
2172
+ *
2173
+ * Sized for a cold open of a reference-dense document: adoption is a forward fixed-point
2174
+ * iteration whose settled prefix extends a few pages per round, so a legal document with a
2175
+ * hundred footnotes converges in tens of rounds (a 53-page/108-note fixture took 24).
2176
+ * Orbits exit earlier through the fingerprint/envelope checks below, so the cap is a
2177
+ * safety bound, not the expected cost.
2178
+ */
2179
+ declare const MAX_NOTE_REFLOW_ATTEMPTS = 64;
2180
+ /** Cap on empty pages created solely to drain footnote/endnote overflow. */
2181
+ declare const MAX_NOTE_OVERFLOW_PAGES = 256;
2182
+ /**
2183
+ * Cap on synthetic eachPage mark candidates measured per section (plus actual marks).
2184
+ *
2185
+ * eachPage sequences restart every page, so a page almost never carries more than a
2186
+ * handful of auto-numbered notes. Measuring `numStart .. numStart + N - 1` covers
2187
+ * single→double digit decimal growth and typical roman width peaks (e.g. `viii` vs `ix`)
2188
+ * without scanning hostile `numStart` ranges unboundedly. Derived marks already assigned
2189
+ * for the pass are always included in addition to this window.
2190
+ */
2191
+ declare const MAX_EACH_PAGE_MARK_CANDIDATES = 12;
2192
+ /**
2193
+ * Why note PAGINATION fell back, widening {@link NoteLayoutFallbackReason} with the reasons that
2194
+ * only arise while distributing notes across pages.
2195
+ */
2196
+ type NotePaginationFallbackReason = NoteLayoutFallbackReason | 'note-reflow-exhausted' | 'note-area-fragment-limit' | 'note-overflow-page-limit'
2197
+ /**
2198
+ * Overflow/drain iteration placed zero note stories while carry/pending remained —
2199
+ * abort rather than minting blank separator-only sheets up to the page budget.
2200
+ */
2201
+ | 'note-overflow-stalled'
2202
+ /** A single note line exceeds the full content column; content is not placed overflowing. */
2203
+ | 'note-line-exceeds-page';
2204
+ /**
2205
+ * Everything note pagination needs: the note parts, and the per-section properties governing
2206
+ * them.
2207
+ *
2208
+ * Per-SECTION because numbering, restart rules and placement are all section properties — one
2209
+ * document can restart footnote numbering at every section and end notes at the document end.
2210
+ */
2211
+ interface NotesLayoutInput {
2212
+ readonly footnotesPart: OoxmlPart | null;
2213
+ readonly endnotesPart: OoxmlPart | null;
2214
+ /** Per-section resolved footnote properties (index-aligned with document sections). */
2215
+ readonly footnotePropsBySection: readonly ResolvedFootnoteProperties[];
2216
+ /** Per-section resolved endnote properties. */
2217
+ readonly endnotePropsBySection: readonly ResolvedEndnoteProperties[];
2218
+ /** Document-level defaults (section 0 fallback). */
2219
+ readonly documentFootnoteProps: ResolvedFootnoteProperties;
2220
+ readonly documentEndnoteProps: ResolvedEndnoteProperties;
2221
+ readonly measurer: TextMeasurer;
2222
+ readonly producer: string;
2223
+ readonly cache?: ParagraphLayoutCache<readonly PendingLine[]>;
2224
+ readonly styleCascade?: StyleCascadeTable;
2225
+ /** `numbering.xml`, so a `w:numPr` paragraph inside a note resolves a marker. */
2226
+ readonly numberingIndex?: NumberingIndex;
2227
+ /** Per-section active line-grid pitch in points; note lines snap to their section's grid. */
2228
+ readonly lineGridPitchBySection?: readonly (number | undefined)[];
2229
+ readonly defaultTabStopPt?: number;
2230
+ readonly compatibilityMode?: number;
2231
+ readonly displayMode?: RevisionDisplayMode;
2232
+ readonly revisionAuthorFilter?: RevisionAuthorFilter;
2233
+ /**
2234
+ * Link projector seams, same as the body walk's. Normally injected by `semantic-layout`
2235
+ * from its own options, so a note's `w:hyperlink` / HYPERLINK field carries the same
2236
+ * sanitized record a body one does instead of painting dead text.
2237
+ */
2238
+ readonly projectLink?: HyperlinkProjector;
2239
+ /**
2240
+ * Per notes-part link projector, preferred over `projectLink`: a `w:hyperlink` inside
2241
+ * `/word/footnotes.xml` or `/word/endnotes.xml` declares its `r:id` in that part's own
2242
+ * `.rels`, not the body part's. The surface supplies this; the inherited body projector
2243
+ * remains only a fallback for callers without per-part resolution.
2244
+ */
2245
+ readonly projectLinkForPart?: (ownerPartName: string) => HyperlinkProjector | undefined;
2246
+ /**
2247
+ * Content token over the notes parts' relationship records, standing in for
2248
+ * {@link projectLinkForPart}'s closure in the notes-pass memo. The projector reads
2249
+ * relationship state the pinned part identity cannot see move: a replicated rels-only
2250
+ * change lands without splicing the notes part, so only a content token catches it.
2251
+ * Required whenever `projectLinkForPart` is supplied — without it the memo is disabled,
2252
+ * the same fail-closed rule `drawingsForPart` follows through `drawingLayoutEpoch`.
2253
+ */
2254
+ readonly linkRelsEpoch?: string;
2255
+ /** Per-notes-part paragraph identity for projected links and metadata fields. */
2256
+ readonly projectionTokenForParagraphForPart?: (ownerPartName: string, paragraph: OoxmlNode) => string;
2257
+ /** Memoized table aggregate counterpart to `projectionTokenForParagraphForPart`. */
2258
+ readonly projectionTokenForTableForPart?: (ownerPartName: string, table: OoxmlNode) => string;
2259
+ /** Combined notes-part freshness signal; outer memo only, never a paragraph producer. */
2260
+ readonly projectionEpoch?: string;
2261
+ readonly projectFieldLink?: FieldLinkProjector;
2262
+ /** Field-code inspection projection. @internal */
2263
+ readonly showFieldCodes?: boolean;
2264
+ /** Document properties for a document-property field inside a note story. */
2265
+ readonly documentProperties?: DocumentProperties;
2266
+ /**
2267
+ * The document's resolved REF inputs, so a footnote's cross-reference paints the live
2268
+ * value the body paints. Normally injected by `semantic-layout` from the context it built
2269
+ * over the body and note stories; its values token joins the notes-pass fingerprint so a
2270
+ * renumbering edit repaints the notes that cite the renumbered target.
2271
+ */
2272
+ readonly refFields?: RefFieldContext;
2273
+ /**
2274
+ * Inline drawing support per notes part. Absent means note paragraphs flow without
2275
+ * drawing records, which is what a headless caller with no image port wants.
2276
+ */
2277
+ readonly drawingsForPart?: (ownerPartName: string) => NoteStoryDrawings | undefined;
2278
+ /**
2279
+ * Part-level drawing epoch covering the notes parts, standing in for
2280
+ * {@link drawingsForPart}'s closure in the notes-pass memo (the closure is rebuilt per
2281
+ * pass, so only an epoch can say "the drawing state did not move"). A caller that
2282
+ * supplies `drawingsForPart` without this keeps the rebuild path.
2283
+ */
2284
+ readonly drawingLayoutEpoch?: string;
2285
+ }
2286
+ /**
2287
+ * The layout with notes attached, plus any fallbacks taken and the mark context used.
2288
+ *
2289
+ * The marks come back because they feed the body's incremental cache tokens: a note number that
2290
+ * changed must invalidate the paragraph that references it.
2291
+ */
2292
+ interface NotesAttachResult {
2293
+ readonly layout: SemanticLayout;
2294
+ readonly fallbackReasons: readonly NotePaginationFallbackReason[];
2295
+ /** Mark context used for the final body projection (for incremental cache tokens). */
2296
+ readonly noteMarks: NoteMarkContext;
2297
+ }
2298
+ /**
2299
+ * Whether a paragraph fragment owns a note atom at `atomOffset`.
2300
+ *
2301
+ * Fragment ranges are half-open for content ownership: `[start, end)`. The shared
2302
+ * boundary offset belongs to the later fragment (downstream affinity), matching line
2303
+ * splits where `fragmentStart = previous.range.end`.
2304
+ */
2305
+ declare function fragmentOwnsAtomOffset(fragment: ParagraphFragmentRecord, atomOffset: number): boolean;
2306
+ /** Paragraph-id → refs index for linear {@link filterRefsOnPage} over a layout pass. */
2307
+ type PageRefIndex = ReadonlyMap<string, readonly PageRefHit[]>;
2308
+ /** Build a reusable paragraph-id index (document order preserved per paragraph). */
2309
+ declare function buildPageRefIndex(allRefs: readonly PageRefHit[]): PageRefIndex;
2310
+ /**
2311
+ * Collect note references that appear in laid-out body fragments on a page.
2312
+ * Matches {@link ParagraphFragmentRecord.range} ownership (half-open + boundary affinity).
2313
+ *
2314
+ * Pass {@link buildPageRefIndex} result as `refIndex` for O(fragments + matching refs)
2315
+ * instead of scanning every document ref against every page fragment.
2316
+ */
2317
+ declare function filterRefsOnPage(page: PageRecord, allRefs: readonly PageRefHit[], refIndex?: PageRefIndex): readonly PageRefHit[];
2318
+ /**
2319
+ * Compute per-page bottom reserves (points) needed for footnotes given a provisional layout.
2320
+ * Used by the bounded reflow loop before final attach.
2321
+ *
2322
+ * Height is measured against a column-derived note budget (not leftover body slack). Measuring
2323
+ * from slack makes `stable` true on the first pass and never shrinks the body — references and
2324
+ * notes then compete for the same band. Oversized notes still split/continue within the budget;
2325
+ * {@link MIN_FOOTNOTE_BODY_BAND_PT} keeps a body band so reflow cannot chase blank sheets.
2326
+ *
2327
+ * Answers under the STRICT co-location policy: every footnote starts on the page that
2328
+ * references it. The bounded orphan-pair refinement is a second phase of the internal
2329
+ * reflow loop, which owns the strict fixed point the refinement needs as its starting
2330
+ * point and as its fallback, so a single call from outside that loop cannot reach it.
2331
+ */
2332
+ declare function computeFootnoteReserves(layout: SemanticLayout, allRefs: readonly PageRefHit[], input: NotesLayoutInput, noteMarks: NoteMarkContext,
2333
+ /** Session-carried notes memo (opaque; owned by this module). */
2334
+ passMemo?: unknown,
2335
+ /**
2336
+ * The reserve map `layout` was laid under. Feeds the hold-out's observed-refusal test
2337
+ * (a block the body pass declined to split under a known budget only moves whole);
2338
+ * absent, the hold-out releases the ambiguous cases — a caller outside the reflow loop
2339
+ * must not manufacture holds the loop never observed.
2340
+ */
2341
+ previousReserves?: ReadonlyMap<number, number>): {
2342
+ readonly reserves: ReadonlyMap<number, number>;
2343
+ readonly stable: boolean;
2344
+ readonly reasons: readonly NotePaginationFallbackReason[];
2345
+ };
2346
+ /**
2347
+ * Attach footnote/endnote areas onto a body layout. Does not re-paginate — callers that
2348
+ * need reservation must re-run body layout with {@link pageBottomReserves} first.
2349
+ */
2350
+ declare function attachNotesToLayout(layout: SemanticLayout, allRefs: readonly PageRefHit[], input: NotesLayoutInput, options?: {
2351
+ readonly fallbackReasons?: readonly NotePaginationFallbackReason[];
2352
+ readonly paragraphSectionIndex?: ReadonlyMap<string, number>;
2353
+ /**
2354
+ * Session-carried notes memo, already validated against `allRefs` and `input`.
2355
+ * Opaque at this boundary; the shape is owned by this module.
2356
+ */
2357
+ readonly memo?: unknown;
2358
+ }): NotesAttachResult;
2359
+ /**
2360
+ * Build a continuous (pre-page) mark context for the first body layout pass.
2361
+ * eachPage reserves digit width; {@link reprojectBodyNoteMarks} publishes final marks
2362
+ * onto body citations after page assignment in {@link attachNotesToLayout}.
2363
+ */
2364
+ declare function provisionalNoteMarks(refs: readonly PageRefHit[], input: NotesLayoutInput): NoteMarkContext;
2365
+
2366
+ /** Inputs for projecting notes through the same semantic layout pass. @public */
2367
+ interface CreateDocumentNotesInputOptions {
2368
+ readonly view: HeadlessDocumentView;
2369
+ readonly measurer: TextMeasurer;
2370
+ readonly producer: string;
2371
+ readonly cache: Parameters<typeof layoutHeaderFooterStory>[4];
2372
+ readonly styleCascade?: () => StyleCascadeTable | undefined;
2373
+ readonly numberingIndex?: () => NumberingIndex;
2374
+ readonly defaultTabStopPt?: number;
2375
+ readonly compatibilityMode?: number;
2376
+ readonly inlineDrawingLayoutForPart?: (partName: string) => InlineDrawingLayoutContext | undefined;
2377
+ readonly drawingTokenForParagraphForPart?: (partName: string, paragraph: OoxmlNode) => string;
2378
+ readonly drawingLayoutEpochForPart?: (partName: string) => string;
2379
+ /** Link/property projection and its inseparable cache identities. */
2380
+ readonly linkProjectors: DocumentLinkProjectors;
2381
+ readonly projectFieldLink?: NotesLayoutInput['projectFieldLink'];
2382
+ /** Field-code inspection projection. @internal */
2383
+ readonly showFieldCodes?: boolean;
2384
+ readonly displayMode?: RevisionDisplayMode;
2385
+ readonly revisionAuthorFilter?: RevisionAuthorFilter;
2386
+ }
2387
+ /** Build note layout input from the neutral package view, or undefined when unused. @public */
2388
+ declare function createDocumentNotesInput(options: CreateDocumentNotesInputOptions): NotesLayoutInput | undefined;
2389
+ /** Resolve a relationship in a non-body story for shared link projection. @public */
2390
+ declare function documentRelationshipTargetIn(view: HeadlessDocumentView, partName: string, relationshipId: string): ReturnType<typeof relationshipTargetIn>;
2391
+
2392
+ /** Sanitized field link retained for interactive consumers. @public */
2393
+ interface RegisteredFieldLink {
2394
+ readonly id: string;
2395
+ readonly paragraphId: '';
2396
+ readonly start: 0;
2397
+ readonly end: 0;
2398
+ readonly text: '';
2399
+ readonly kind: 'external' | 'internal' | 'unresolved';
2400
+ readonly href: string | null;
2401
+ readonly authored: string;
2402
+ readonly anchor?: string;
2403
+ readonly tooltip?: string;
2404
+ }
2405
+ /** Per-layout registry for projected HYPERLINK fields. @public */
2406
+ interface FieldLinkRegistry {
2407
+ project(spec: HyperlinkFieldSpec): SpanLinkRecord | null;
2408
+ linkById(linkId: string): RegisteredFieldLink | null;
2409
+ clear(): void;
2410
+ }
2411
+ /** Create a bounded, content-keyed field-link registry. @public */
2412
+ declare function createFieldLinkRegistry(): FieldLinkRegistry;
2413
+
2414
+ /**
2415
+ * Strict hex fill: exactly six hex digits. Rejects `auto`, `nil`, and any non-hex payload
2416
+ * (CSS functions, URLs, short hex, theme tokens).
2417
+ */
2418
+ declare function resolveStrictHexFill(raw: string | undefined): string | undefined;
2419
+ /**
2420
+ * Resolve a `w:shd` attribute bag to a validated RRGGBB fill, or undefined.
2421
+ *
2422
+ * `w:themeFill` is a REFERENCE, and Word always writes the value it resolved to alongside
2423
+ * it: `<w:shd w:val="clear" w:fill="D9E2F3" w:themeFill="accent1" w:themeFillTint="33"/>`.
2424
+ * Reading `w:fill` in that case is not inventing a colour from the theme — it is reading
2425
+ * the colour the producer computed. Dropping the fill because a theme reference sat next
2426
+ * to it left every accent-shaded cell and paragraph unpainted.
2427
+ *
2428
+ * `val="nil"` clears shading. Pattern vals are not rendered; a valid solid `fill` still
2429
+ * paints as a clear fill until pattern support lands. A theme reference with no usable
2430
+ * `w:fill` still resolves to nothing rather than a guess.
2431
+ */
2432
+ declare function resolveOoxmlShadingFill(attributes: Readonly<Record<string, string>> | undefined): string | undefined;
2433
+ /** Read `w:shd` from a typed/generic element (table `tcPr`, nested `pPr`, …). */
2434
+ declare function shadingFillFromElement(shd: OoxmlElement | undefined): string | undefined;
2435
+ /**
2436
+ * Resolve paragraph shading from cascaded flat `w:pPr` properties.
2437
+ *
2438
+ * Later `w:shd` entries win (defaults → style → direct), matching spacing/border cascade.
2439
+ */
2440
+ declare function paragraphShading(props: readonly OoxmlProperty[]): string | undefined;
2441
+ /**
2442
+ * Page-content box for paragraph shading: union of this fragment's line boxes.
2443
+ *
2444
+ * Excludes collapsed before/after spacing and bottom-border extent so the painted band
2445
+ * matches Word's content-area fill (character shading height on a single line).
2446
+ */
2447
+ declare function paragraphShadingBox(lines: readonly {
2448
+ readonly box: LayoutBox;
2449
+ }[], x: number, width: number): LayoutBox | undefined;
2450
+
2451
+ /**
2452
+ * Everything a layout pass needs beyond the document itself.
2453
+ *
2454
+ * `measurer` is the only required field — layout is DOM-free and measures through whatever is
2455
+ * injected here, which is what lets the same code paginate on a server and in a browser.
2456
+ */
2457
+ interface SemanticLayoutOptions {
2458
+ readonly geometry?: PageGeometry;
2459
+ readonly measurer: TextMeasurer;
2460
+ /**
2461
+ * Reuse of measured-and-broken paragraphs across revisions (task 9.2).
2462
+ *
2463
+ * Only the BREAK is cached. Placement — y, fragments, page cuts — is always redone, so
2464
+ * an edit high in the document still repaginates everything below it while paragraphs
2465
+ * nobody touched are never measured again.
2466
+ */
2467
+ readonly cache?: ParagraphLayoutCache<readonly PendingLine[]>;
2468
+ /**
2469
+ * Collector for the cache keys a pass wants retained, instead of retaining directly.
2470
+ * Supplied by the multi-section orchestrator, which retains once over the union —
2471
+ * retaining per section evicted every other section's entries. `false` skips retention
2472
+ * for this pass entirely (the orchestrator strides sweeps). See
2473
+ * `retainLiveBreakKeys`.
2474
+ */
2475
+ readonly retainKeys?: Set<string> | false;
2476
+ /**
2477
+ * Part-level drawing projection/resource epoch for the section prepass memo.
2478
+ *
2479
+ * Moves whenever any drawing projection or resource state in the part does, standing in
2480
+ * for the per-paragraph drawing tokens the memo would otherwise have to recompute to
2481
+ * validate. A caller that supplies `drawingTokenForParagraph` without this epoch keeps
2482
+ * the recompute path — the memo must never miss a token move it cannot see.
2483
+ */
2484
+ readonly drawingLayoutEpoch?: string;
2485
+ /** Part-level freshness signal for paragraph-local semantic projection tokens. */
2486
+ readonly projectionEpoch?: string;
2487
+ /**
2488
+ * Who produced the measurements, folded into every cache key.
2489
+ *
2490
+ * A font arriving after first paint changes every advance in the document while no
2491
+ * content changes; without this the cache would serve the pre-font layout forever.
2492
+ */
2493
+ readonly producer?: string;
2494
+ /**
2495
+ * Incremental placement across revisions (task 9.3).
2496
+ *
2497
+ * Holds the previous complete layout and a flow checkpoint per paragraph, so a pass can
2498
+ * resume just before the first affected paragraph instead of re-placing the document from
2499
+ * the top, and can stop early when the flow reconverges with the previous run.
2500
+ *
2501
+ * Multi-section documents keep per-section child sessions on {@link LayoutSession.multi}.
2502
+ */
2503
+ readonly session?: LayoutSession;
2504
+ /** Header/footer stories to attach per page; absent means no furniture. */
2505
+ readonly furniture?: PageFurniture;
2506
+ /**
2507
+ * Which tracked revisions this pass resolves away (ECMA-376 §17.13).
2508
+ *
2509
+ * `all-markup` (the default) lays out both halves of every change. `proposed` lays out what
2510
+ * the document becomes if every change is accepted; `original` what it was before any of
2511
+ * them. Both are LAYOUT INPUTS: neither applies a `TreeDocOp` nor publishes a `ModelChange`,
2512
+ * so a user who switches to the proposed result, saves, and sends the file has not silently
2513
+ * accepted every proposal in it.
2514
+ */
2515
+ readonly displayMode?: RevisionDisplayMode;
2516
+ /** Reviewers whose revisions render as their accepted projection. */
2517
+ readonly revisionAuthorFilter?: RevisionAuthorFilter;
2518
+ /**
2519
+ * Per-section furniture, index-aligned with `enumerateDocumentSections`.
2520
+ *
2521
+ * When present, multi-section layout attaches each section's own headers/footers (after
2522
+ * OOXML inheritance). `furniture` remains the single-section / last-section fallback.
2523
+ */
2524
+ readonly sectionFurniture?: readonly (PageFurniture | undefined)[];
2525
+ /**
2526
+ * `w:evenAndOddHeaders` from settings.xml. A section that restarts its page numbering then
2527
+ * starts on a sheet whose position has the parity of the restarted number. When absent,
2528
+ * layout reads the flag from the section furniture.
2529
+ */
2530
+ readonly evenAndOddHeaders?: boolean;
2531
+ /** Authored column count/gap for anchored `relativeFrom="column"` frame resolution. */
2532
+ readonly sectionColumns?: SectionColumns;
2533
+ /** Active section line-grid pitch: line-unit margins and line snapping. Absent: no grid. */
2534
+ readonly paragraphLineUnitPt?: number;
2535
+ /**
2536
+ * This section's resolved `w:pgBorders`, for the frame each of its sheets publishes.
2537
+ *
2538
+ * Section-scoped, not document-scoped: `w:display="firstPage"` means the first page of THIS
2539
+ * section, and multi-section layout hands each pass its own.
2540
+ */
2541
+ readonly sectionPageBorders?: SectionPageBorders;
2542
+ /**
2543
+ * Styles-part cascade table (docDefaults + `w:style` last-wins). Absent keeps direct
2544
+ * formatting only — the pre-cascade behaviour, used by unit tests that never open a
2545
+ * package.
2546
+ */
2547
+ readonly styleCascade?: StyleCascadeTable;
2548
+ /**
2549
+ * Projection of `/word/numbering.xml`. Absent keeps pre-list behaviour (no markers /
2550
+ * level indents). The index is immutable for a session; list counter state is derived
2551
+ * per layout pass from document order.
2552
+ */
2553
+ readonly numberingIndex?: NumberingIndex;
2554
+ /**
2555
+ * Optional precomputed list items for the body story. When absent and
2556
+ * {@link numberingIndex} is set, layout walks the full body (including table cells)
2557
+ * once so counters continue across section boundaries and table document order.
2558
+ */
2559
+ readonly listItems?: ReadonlyMap<string, ResolvedListItem>;
2560
+ /**
2561
+ * `w:settings/w:defaultTabStop` in points (ECMA-376 §17.15.1.25); absent keeps the 0.5"
2562
+ * schema default.
2563
+ *
2564
+ * It arrives as an option because the paragraph cascade cannot see `settings.xml`. A
2565
+ * metric-locale template declares 1134 twips (2cm) and every default-interval tab in the
2566
+ * document belongs on that grid. Constant for a session — the settings part is immutable
2567
+ * here — which is why the prepared-block memo does not key on it.
2568
+ */
2569
+ readonly defaultTabStopPt?: number;
2570
+ /** Explicit Word compatibility mode; absent retains existing geometry. */
2571
+ readonly compatibilityMode?: number;
2572
+ /**
2573
+ * Turns a typed `w:hyperlink` into the SANITIZED record its spans carry.
2574
+ *
2575
+ * An option because resolving `r:id` needs the package's relationships, which layout — a
2576
+ * per-part walk — cannot see. Absent means link runs still measure, break and paint;
2577
+ * they simply carry no link, so nothing is clickable and no text is lost. That is the
2578
+ * degradation a headless test or a furniture-only pass gets, and it is the safe one.
2579
+ */
2580
+ readonly projectLink?: HyperlinkProjector;
2581
+ /**
2582
+ * Turns a parsed HYPERLINK field instruction into the SANITIZED record its result carries.
2583
+ *
2584
+ * An option for the same reason as {@link projectLink}: the raw target must cross the
2585
+ * surface's href trust boundary, which layout cannot see. Absent means the field's cached
2586
+ * result still measures, breaks and paints — it simply is not clickable.
2587
+ */
2588
+ readonly projectFieldLink?: FieldLinkProjector;
2589
+ /** Field-code inspection projection. @internal */
2590
+ readonly showFieldCodes?: boolean;
2591
+ /**
2592
+ * How the pass addresses saved field results (DATE, MERGEFIELD, HYPERLINK display text, see
2593
+ * `FieldResultsMode`). `atomic` (the default) lays every field out as one model unit.
2594
+ * `editable` lays a saved result out as ordinary text at the offsets the store's editable
2595
+ * mode addresses, so a caret, a hit, and a selection inside it land on its characters. The
2596
+ * mode is folded into every cache key.
2597
+ */
2598
+ readonly fieldResults?: FieldResultsMode;
2599
+ /** @internal */
2600
+ readonly fieldCodeRanges?: FieldCodeRanges;
2601
+ /** @internal Word TOC character-style suppression. */
2602
+ readonly tocLinkStyleRanges?: TocLinkRanges;
2603
+ /**
2604
+ * The document's parsed metadata, for document-property fields (TITLE, AUTHOR, …). Read once
2605
+ * by the surface and shared across body, table, note and header/footer flows.
2606
+ */
2607
+ readonly documentProperties?: DocumentProperties;
2608
+ /**
2609
+ * Footnote/endnote layout input. When present, body layout projects note marks and a
2610
+ * post-pass attaches note areas (with bounded reflow for pageBottom reservation).
2611
+ */
2612
+ readonly notes?: NotesLayoutInput;
2613
+ /**
2614
+ * Per-page bottom reserves (points) subtracted from content height before line placement,
2615
+ * keyed by DOCUMENT page index. A section's pass reads its own slice through
2616
+ * `pageIndexStart`. Produced by the note reflow loop; absent means full content column.
2617
+ */
2618
+ readonly pageBottomReserves?: ReadonlyMap<number, number>;
2619
+ /** Derived note marks for body/note projection (provisional or final). */
2620
+ readonly noteMarks?: NoteMarkContext;
2621
+ /** Inline drawing projection for typed `w:drawing` / `wp:inline` nodes. */
2622
+ readonly inlineDrawingLayout?: InlineDrawingLayoutContext;
2623
+ /** Per-paragraph drawing projection/resource token for break cache keys. */
2624
+ readonly drawingTokenForParagraph?: (paragraph: OoxmlNode) => string;
2625
+ /** Per-paragraph identity for projected links and live document-property text. */
2626
+ readonly projectionTokenForParagraph?: (paragraph: OoxmlNode) => string;
2627
+ /** Memoized aggregate projection identity for an immutable table subtree. */
2628
+ readonly projectionTokenForTable?: (table: OoxmlNode) => string;
2629
+ /** @deprecated Prefer {@link drawingTokenForParagraph}. */
2630
+ readonly drawingLayoutToken?: string;
2631
+ /** Internal: reflow pass index; negative values establish anchor pages before backward wrap. */
2632
+ readonly drawingExclusionPass?: number;
2633
+ /** Internal: converged exclusion zones — skips the reflow loop when set with zones. */
2634
+ readonly drawingExclusionConverged?: boolean;
2635
+ /** Internal: exclusion zones from the prior reflow pass, keyed by page index. */
2636
+ readonly drawingExclusionZonesByPage?: ReadonlyMap<number, readonly ExclusionZone[]>;
2637
+ /** Canonical drawing traversal order within the owner story part. */
2638
+ readonly drawingSourceOrder?: ReadonlyMap<string, number>;
2639
+ /**
2640
+ * TOC instruction-only opening and ending chrome paragraph ids. Empty chrome suppresses
2641
+ * its placeholder line; opening paragraph marks after a separator remain visible result lines.
2642
+ * Tree nodes stay intact for refresh/save.
2643
+ */
2644
+ readonly tocFieldChromeParagraphIds?: ReadonlySet<string>;
2645
+ /**
2646
+ * Begin-paragraph ids of empty TOCs. These keep one layout line so paint can host an
2647
+ * identifiable empty-TOC furniture placeholder (overrides chrome suppression).
2648
+ */
2649
+ readonly emptyTocPlaceholderParagraphIds?: ReadonlySet<string>;
2650
+ /**
2651
+ * Empty result-paragraph ids inside empty TOCs. Suppressed like field chrome so blank
2652
+ * cached rows do not stack under the empty placeholder.
2653
+ */
2654
+ readonly emptyTocSuppressedResultParagraphIds?: ReadonlySet<string>;
2655
+ /**
2656
+ * Lay out only the first `bodyBlockLimit` body blocks. A large open lays out a growing
2657
+ * prefix in separate tasks, each pass resuming from the last, so the page stays
2658
+ * responsive; the final pass has no limit. @internal
2659
+ */
2660
+ readonly bodyBlockLimit?: number;
2661
+ }
2662
+
2663
+ /**
2664
+ * A deterministic measurer for tests and headless use.
2665
+ *
2666
+ * Monospace by construction: every character is the same width and every line the same
2667
+ * height, scaled by `w:sz` when present. Real shaping is the HarfBuzz path; this exists so
2668
+ * layout behaviour can be asserted without a font stack deciding the answer.
2669
+ */
2670
+ declare function createFixedMeasurer(charWidth?: number, lineHeight?: number): TextMeasurer;
2671
+
2672
+ /**
2673
+ * Every content control a part declares, in document order, WITHOUT geometry.
2674
+ *
2675
+ * For callers that want the ROSTER rather than the rectangles — the Tab walk through form
2676
+ * fields is the one, and it needs story order, not boxes. `fragments` comes back empty here
2677
+ * by design; a caller that needs geometry reads `layout.contentControls`, which covers every
2678
+ * story the layout draws.
2679
+ */
2680
+ declare function contentControlRecordsInPart(part: OoxmlPart,
2681
+ /**
2682
+ * Keep only controls holding one of these paragraphs.
2683
+ *
2684
+ * For a notes PART, which holds every note in the document rather than one story. Rostered
2685
+ * whole, Tab walked out of the open footnote and into the next one, and the keystrokes after
2686
+ * it landed in a note the reader was not in.
2687
+ */
2688
+ withinParagraphs?: ReadonlySet<string>): readonly ContentControlBoundaryRecord$1[];
2689
+ /**
2690
+ * The innermost content control holding `paragraphId`, in `part`.
2691
+ *
2692
+ * Deepest wins, matching the geometry path's innermost-by-nesting rule: a control inside a
2693
+ * control is the one the caret is actually in.
2694
+ */
2695
+ declare function contentControlHoldingParagraph(part: OoxmlPart, paragraphId: string): ContentControlBoundaryRecord$1 | null;
2696
+
2697
+ /**
2698
+ * Lay one story part out into pages.
2699
+ *
2700
+ * The engine's layout entry point. Walks body, header, footer and note roots, flattens block
2701
+ * SDTs, paginates tables with header-row repeats and vertical merges, and resolves every
2702
+ * paragraph through the style cascade.
2703
+ *
2704
+ * Incremental when given a {@link LayoutSession}: per-block cache keys plus flow checkpoints mean
2705
+ * a pass that changes nothing returns the previous pages by identity.
2706
+ */
2707
+ declare function layoutSemanticDocument(part: OoxmlPart, revision: number, options: SemanticLayoutOptions): SemanticLayout;
2708
+
2709
+ /** Soft ceiling on an expanded marker string (codepoints). */
2710
+ declare const MAX_MARKER_TEXT_LENGTH = 64;
2711
+ /** Soft ceiling on authored `w:lvlText` before expansion. */
2712
+ declare const MAX_LVL_TEXT_LENGTH = 64;
2713
+ /** Clamp a list counter into a safe non-negative integer Word can format. */
2714
+ declare function clampListValue(value: number): number;
2715
+ /** `decimal` (§17.18.59). Clamped, because the counter derives from file-declared restarts. */
2716
+ declare function formatDecimal(value: number): string;
2717
+ /** `decimalZero` (§17.18.59): single digits zero-padded to two. */
2718
+ declare function formatDecimalZero(value: number): string;
2719
+ /**
2720
+ * `upperRoman` (§17.18.59). Saturates at 3999, the largest value classical Roman numerals
2721
+ * express — beyond it there is nothing correct to emit, so it stops rather than inventing
2722
+ * notation.
2723
+ */
2724
+ declare function formatUpperRoman(value: number): string;
2725
+ /** `lowerRoman` (§17.18.59). */
2726
+ declare function formatLowerRoman(value: number): string;
2727
+ /**
2728
+ * Excel-style letter sequence: 1→A … 26→Z, 27→AA.
2729
+ * Caps length so a hostile counter cannot grow without bound.
2730
+ */
2731
+ declare function formatUpperLetter(value: number): string;
2732
+ /** `lowerLetter` (§17.18.59): the {@link formatUpperLetter} sequence, lower-cased. */
2733
+ declare function formatLowerLetter(value: number): string;
2734
+ /**
2735
+ * Format one counter for a `w:numFmt` value (ST_NumberFormat, §17.18.59).
2736
+ *
2737
+ * `none` prints NOTHING — it is the format Word uses for a level that contributes only
2738
+ * literal text, and formatting it as decimal invents a number the document never had.
2739
+ * `bullet` is not formatted here — callers use the literal `lvlText`.
2740
+ *
2741
+ * The remaining enumerants (`japaneseCounting`, `thaiNumbers`, `ganada`, …) are
2742
+ * per-script numeral sequences we do not carry glyph tables for. They fall back to decimal
2743
+ * deliberately: the ORDINAL is still the authored one, only the script differs, which reads
2744
+ * as a number in the wrong alphabet rather than as a missing or wrong marker.
2745
+ */
2746
+ declare function formatNumFmt(numFmt: string, value: number): string;
2747
+ /**
2748
+ * Expand `w:lvlText` placeholders `%1`…`%9` using per-level counters and formats.
2749
+ *
2750
+ * `formats[i]` / `counters[i]` correspond to ilvl `i`. Missing slots use decimal / 1.
2751
+ * Literal percent signs that are not `%1`…`%9` are kept. Output is hard-capped.
2752
+ */
2753
+ declare function expandLvlText(lvlText: string, counters: readonly number[], formats: readonly string[]): string;
2754
+
2755
+ /**
2756
+ * The stack used when a run names no font (or names one the sink refuses).
2757
+ *
2758
+ * Paint only sets `font-family` when `w:rFonts` supplies a validated name, so an unstyled
2759
+ * run inherits the surrounding face. Measuring one stack and painting another drifts every
2760
+ * advance; this is the Word-like Latin fallback the canvas path measures against.
2761
+ */
2762
+ declare const DEFAULT_CANVAS_FONT_STACK = "Calibri, Carlito, Helvetica, Arial, sans-serif";
2763
+ /**
2764
+ * The canvas text-metrics surface the editor injects.
2765
+ *
2766
+ * Structural subset of `CanvasRenderingContext2D` — declared here so the layout lane stays
2767
+ * off the DOM lib. Hosts pass a real 2d context; tests pass a controllable mock.
2768
+ */
2769
+ interface CanvasTextMetrics {
2770
+ readonly width: number;
2771
+ readonly actualBoundingBoxLeft?: number;
2772
+ readonly actualBoundingBoxRight?: number;
2773
+ readonly fontBoundingBoxAscent?: number;
2774
+ readonly fontBoundingBoxDescent?: number;
2775
+ }
2776
+ /**
2777
+ * The slice of a 2D canvas context measurement needs.
2778
+ *
2779
+ * A structural subset rather than `CanvasRenderingContext2D`, so layout stays DOM-free and a test
2780
+ * can supply a deterministic stub.
2781
+ */
2782
+ interface CanvasTextContext {
2783
+ font: string;
2784
+ /** Optional browser kerning control; older contexts retain their native behavior. */
2785
+ fontKerning?: 'auto' | 'normal' | 'none';
2786
+ textAlign?: 'left' | 'right' | 'center' | 'start' | 'end';
2787
+ direction?: 'ltr' | 'rtl' | 'inherit';
2788
+ measureText(text: string): CanvasTextMetrics;
2789
+ }
2790
+ /**
2791
+ * How the canvas measurer resolves fonts, scales, and bounds its caches. Every field optional.
2792
+ *
2793
+ * Layout never creates a canvas itself: without a `context` this measurer does not exist and the
2794
+ * surface falls back to fixed metrics.
2795
+ */
2796
+ interface CanvasMeasurerOptions {
2797
+ /**
2798
+ * Layout units to CSS pixels — the same value the painter uses.
2799
+ *
2800
+ * Measuring at the layout size and multiplying afterwards is not the same as measuring at
2801
+ * the painted size: font metrics are hinted per pixel size. Everything here measures at
2802
+ * the painted size and converts back.
2803
+ */
2804
+ readonly scale?: number;
2805
+ readonly fallbackFamily?: string;
2806
+ /**
2807
+ * The engine-minted family a document-embedded face was registered under, if any.
2808
+ *
2809
+ * Paint prefers this alias over the declared family, so measurement has to as well: a
2810
+ * document whose face is embedded rather than installed would otherwise be measured
2811
+ * against the fallback and painted with the embedded glyphs, and every advance, wrap
2812
+ * point and page break would be taken from a font the reader never sees.
2813
+ */
2814
+ readonly fontAlias?: (family: string) => string | undefined;
2815
+ /**
2816
+ * Injected 2d text context from the editor/browser seam.
2817
+ *
2818
+ * `undefined` or `null` makes {@link tryCreateCanvasMeasurer} return null so the surface
2819
+ * falls back to the fixed measurer. Layout never creates a canvas element itself.
2820
+ */
2821
+ readonly context?: CanvasTextContext | null;
2822
+ /**
2823
+ * Unique `(font, text)` width entries retained before LRU eviction.
2824
+ *
2825
+ * Long editing sessions measure many transient paragraph states; bounding this cache keeps
2826
+ * memory predictable without changing layout output.
2827
+ */
2828
+ readonly maxWidthEntries?: number;
2829
+ /** Distinct font-shorthand line metrics retained before LRU eviction. */
2830
+ readonly maxMetricsEntries?: number;
2831
+ }
2832
+ /**
2833
+ * The measurer a surface ended up with, plus the identity its cache keys must include.
2834
+ *
2835
+ * The `producer` string is load-bearing: the same canvas measuring against document-embedded
2836
+ * faces produces DIFFERENT advances, so the two must not share a cache key space or a document
2837
+ * would keep its pre-font pagination after the font arrived.
2838
+ */
2839
+ interface ResolvedSurfaceMeasurer {
2840
+ readonly measurer: TextMeasurer;
2841
+ /**
2842
+ * Cache-invalidation identity when the caller did not supply `producer`.
2843
+ *
2844
+ * `canvas-measurer+embedded` is the canvas measurer resolving document-embedded faces:
2845
+ * the same canvas, different advances, so it must not share a cache key space.
2846
+ */
2847
+ readonly producer: 'canvas-measurer' | 'canvas-measurer+embedded' | 'fixed-measurer';
2848
+ }
2849
+ /**
2850
+ * Whether an injected canvas text context is usable for measurement.
2851
+ *
2852
+ * Availability is decided by the editor seam (which alone may create a canvas). Layout
2853
+ * never probes `document` — a missing context is simply "unavailable".
2854
+ */
2855
+ declare function isCanvasMeasurementAvailable(context?: CanvasTextContext | null | undefined): boolean;
2856
+ /**
2857
+ * Build a canvas-backed measurer, or `null` when no 2d context was injected.
2858
+ *
2859
+ * Prefer {@link resolveDefaultSurfaceMeasurer} at the editor surface: that keeps the
2860
+ * fixed fallback for SSR/tests in one place.
2861
+ */
2862
+ declare function tryCreateCanvasMeasurer(options?: CanvasMeasurerOptions): TextMeasurer | null;
2863
+ /**
2864
+ * The surface's default measurer: canvas when a 2d context was injected, otherwise fixed.
2865
+ *
2866
+ * Host-supplied and shaping measurers override this entirely — call only when the options
2867
+ * did not already name one. The editor seam is responsible for creating the canvas context.
2868
+ */
2869
+ declare function resolveDefaultSurfaceMeasurer(scale?: number, options?: CanvasMeasurerOptions): ResolvedSurfaceMeasurer;
2870
+
2871
+ type RefreshFieldResultsOp = Extract<TreeDocOp, {
2872
+ op: 'refreshFieldResults';
2873
+ }>;
2874
+
2875
+ interface RefFieldRefreshOptions {
2876
+ /**
2877
+ * The package the part belongs to, for the note-part half of the resolution context —
2878
+ * the same context paint uses, so a body REF that targets a footnote bookmark computes
2879
+ * the same value both places. Absent narrows resolution to body-declared bookmarks.
2880
+ */
2881
+ readonly package?: OoxmlPackage;
2882
+ readonly styleCascade?: StyleCascadeTable;
2883
+ readonly numberingIndex?: NumberingIndex;
2884
+ /** The mode the surface painted under, so the saved values match the painted ones. */
2885
+ readonly displayMode?: RevisionDisplayMode;
2886
+ /**
2887
+ * The DISPLAYED page number of one paragraph in the current finalized layout, or null when
2888
+ * it is not placed. PAGEREF results ride the plan through this: the value is pagination's,
2889
+ * so only a caller holding the laid-out pages can answer, and a plan without it keeps every
2890
+ * PAGEREF result as loaded. The same calibration verdict paint took gates each rewrite.
2891
+ */
2892
+ readonly pageRefPageNumberOf?: (targetParagraphId: string) => string | null;
2893
+ }
2894
+ /**
2895
+ * A {@link RefFieldRefreshOptions.pageRefPageNumberOf} source over one finalized layout.
2896
+ *
2897
+ * Builds the target → host-page index once, on first demand, and answers every field from
2898
+ * it — the same walk finalize substitution takes, so the saved number is the painted one.
2899
+ */
2900
+ declare function pageRefPageNumbersFromLayout(layout: SemanticLayout): (targetParagraphId: string) => string | null;
2901
+ /**
2902
+ * Plan the refresh for one body part, or null when every supported REF result is already
2903
+ * fresh (the no-op save path — no transaction, no revision bump, no undo entry).
2904
+ */
2905
+ declare function planRefFieldResultRefresh(part: OoxmlPart, options: RefFieldRefreshOptions): RefreshFieldResultsOp | null;
2906
+
2907
+ /**
2908
+ * Return story blocks in document order, flattening block-level content controls.
2909
+ * Compatible hidden paragraph marks join in body stories using the supplied styles and numbering.
2910
+ * Repeated calls with the same part and projection inputs share one array. Treat it as read-only.
2911
+ *
2912
+ * @public
2913
+ */
2914
+ declare function storyBlocks(part: OoxmlPart, displayMode?: RevisionDisplayMode, authorFilter?: RevisionAuthorFilter, styles?: StyleCascadeTable, numberingIndex?: NumberingIndex): OoxmlElement[];
2915
+ /**
2916
+ * Blocks of one typed footnote/endnote node — a separate semantic story root.
2917
+ *
2918
+ * The footnotes/endnotes part root is never a story; each note is laid out independently
2919
+ * so line ids and incremental convergence stay namespaced by note identity.
2920
+ */
2921
+ declare function noteStoryBlocks(note: OoxmlNode, displayMode?: RevisionDisplayMode, authorFilter?: RevisionAuthorFilter): OoxmlElement[];
2922
+
2923
+ /** TOC boundary paragraphs whose empty instruction/chrome lines are suppressed. */
2924
+ declare function tocFieldChromeParagraphIds(part: OoxmlPart): ReadonlySet<string>;
2925
+ /**
2926
+ * Begin-paragraph ids of TOCs that have no visible cached result rows.
2927
+ *
2928
+ * Layout keeps a single caret-height line on these ids so paint can host an identifiable
2929
+ * empty-TOC furniture placeholder; ordinary field chrome on the same ids stays suppressed.
2930
+ */
2931
+ declare function emptyTocPlaceholderParagraphIds(part: OoxmlPart): ReadonlySet<string>;
2932
+ /**
2933
+ * Empty result-paragraph ids inside an empty TOC.
2934
+ *
2935
+ * Suppressed like field chrome so blank cached rows do not stack under the empty placeholder.
2936
+ */
2937
+ declare function emptyTocSuppressedResultParagraphIds(part: OoxmlPart): ReadonlySet<string>;
2938
+
2939
+ /** One page-local rectangle covering the part of a control that sits on that page. */
2940
+ interface ContentControlFragmentRecord extends LayoutBox {
2941
+ readonly pageIndex: number;
2942
+ }
2943
+ /**
2944
+ * Everything a consumer needs to point at one control without walking the tree.
2945
+ *
2946
+ * `controlId` is the canonical NODE id — the identity `w:id` cannot provide, because the file
2947
+ * may omit it and may repeat it. `id` is the file's own `w:id` where it wrote one, carried as
2948
+ * metadata for a caller that has to speak to something outside this engine.
2949
+ */
2950
+ interface ContentControlBoundaryRecord {
2951
+ readonly controlId: string;
2952
+ readonly type: ContentControlKind;
2953
+ readonly level: ContentControlLevel;
2954
+ /** 0 for a top-level control; the nesting depth otherwise. */
2955
+ readonly depth: number;
2956
+ readonly lock: ContentControlLock;
2957
+ /** The lock in force here, including every lock an enclosing control imposes. */
2958
+ readonly effectiveLock: ContentControlLock;
2959
+ readonly showingPlaceholder: boolean;
2960
+ readonly temporary: boolean;
2961
+ readonly tag?: string;
2962
+ readonly alias?: string;
2963
+ readonly id?: number;
2964
+ /** Preserved `w:dataBinding` metadata. Present means value writes are refused as bound. */
2965
+ readonly dataBinding?: ContentControlDataBinding;
2966
+ /** Paragraphs the control's content holds, in reading order. */
2967
+ readonly paragraphIds: readonly string[];
2968
+ /** One rectangle per page the content reaches, in the page's own coordinate space. */
2969
+ readonly fragments: readonly ContentControlFragmentRecord[];
2970
+ }
2971
+ /**
2972
+ * Derive one boundary record per control the part declares, in document order.
2973
+ *
2974
+ * A control whose content the layout never placed — one inside a story this layout is not of,
2975
+ * or one holding nothing — answers no fragments rather than a zero-sized box at the origin: a
2976
+ * rectangle nothing painted is a rectangle a hit test would match.
2977
+ */
2978
+ declare function contentControlBoundaries(part: OoxmlPart, layout: SemanticLayout): readonly ContentControlBoundaryRecord[];
2979
+
2980
+ /** Where one paragraph sits, resolved once so a card is an O(1) lookup. */
2981
+ interface ReviewParagraphAnchor {
2982
+ readonly pageIndex: number;
2983
+ /** Sheet-absolute y of the page's content box. */
2984
+ readonly contentY: number;
2985
+ /** The fragment's own y, measured from that content box. */
2986
+ readonly fragmentY: number;
2987
+ readonly lines?: readonly {
2988
+ readonly range: {
2989
+ readonly end: number;
2990
+ };
2991
+ readonly box: {
2992
+ readonly y: number;
2993
+ };
2994
+ /**
2995
+ * Present on a real line. A merged fragment's join line carries spans from two
2996
+ * paragraphs, and its `range` can only name one of them, so the OTHER paragraph's extent
2997
+ * on that line is readable here and nowhere else.
2998
+ */
2999
+ readonly spans?: readonly {
3000
+ readonly range: {
3001
+ readonly paragraphId: string;
3002
+ readonly end: number;
3003
+ };
3004
+ }[];
3005
+ }[];
3006
+ }
3007
+ /**
3008
+ * The y of the line a position sits on, measured from the anchor's own origin.
3009
+ *
3010
+ * An ordinary line answers from its own range, exactly as it always did. On a merged
3011
+ * fragment's join line the range names one of the two paragraphs, so an offset compared
3012
+ * against it either overshot — putting a card in the absorbed half beside the wrong line —
3013
+ * or matched the first line every time.
3014
+ */
3015
+ declare function anchorLineY(anchor: ReviewParagraphAnchor, paragraphId: string, offset: number): number;
3016
+ /**
3017
+ * Paragraph id to its place on the page, in ONE pass over the layout.
3018
+ *
3019
+ * Built once per layout and reused by every card. The straightforward version — scan the
3020
+ * pages until the paragraph turns up, per card — is a full-document walk per card, and a
3021
+ * contract with two hundred comments walked the document two hundred times every time the
3022
+ * caret moved. Toggling the pane was visibly slow for exactly that reason.
3023
+ */
3024
+ declare function reviewAnchorIndex<TPage extends {
3025
+ readonly index: number;
3026
+ readonly contentBox: {
3027
+ readonly y: number;
3028
+ };
3029
+ }>(layout: {
3030
+ readonly pages: readonly TPage[];
3031
+ }, paragraphFragments: (page: TPage) => readonly {
3032
+ readonly paragraphId: string;
3033
+ readonly box: {
3034
+ readonly y: number;
3035
+ };
3036
+ readonly lines?: readonly {
3037
+ readonly range: {
3038
+ readonly end: number;
3039
+ };
3040
+ readonly box: {
3041
+ readonly y: number;
3042
+ };
3043
+ /** Present on a real line; a merged one carries spans from more than one paragraph. */
3044
+ readonly spans?: readonly {
3045
+ readonly range: {
3046
+ readonly paragraphId: string;
3047
+ readonly end: number;
3048
+ };
3049
+ }[];
3050
+ }[];
3051
+ }[]): Map<string, ReviewParagraphAnchor>;
3052
+ /**
3053
+ * Where a card belongs beside the page, from LAYOUT RECORDS.
3054
+ *
3055
+ * The one question the tree cannot answer, and the one a surface must not answer for itself:
3056
+ * measuring painted DOM puts the sidebar a repaint behind the document and breaks outright
3057
+ * while pagination is in flight.
3058
+ *
3059
+ * Returns null when the item has no resolvable range, or when its paragraph is not in this
3060
+ * layout — a comment anchored in a header belongs to a story the body layout never saw.
3061
+ */
3062
+ declare function reviewItemGeometry(item: ReviewItem, index: ReadonlyMap<string, ReviewParagraphAnchor>): {
3063
+ readonly pageIndex: number;
3064
+ readonly y: number;
3065
+ } | null;
3066
+
3067
+ /**
3068
+ * The frame for one sheet, or undefined when this page does not carry one.
3069
+ *
3070
+ * `isFirstPageOfSection` is the SECTION's first page, not the document's: `w:display` is a
3071
+ * section property, so in a three-section document a `firstPage` frame draws three times.
3072
+ *
3073
+ * The four rules close a rectangle rather than each spanning its own side. Word draws one box,
3074
+ * and horizontal rules that stopped at the text column while the verticals sat outside it read
3075
+ * as two rules with two detached bars beside them — the same thing `w:pBdr` gets right by
3076
+ * spanning from the left rule's outer edge to the right rule's.
3077
+ */
3078
+ declare function pageBorderFrame(borders: SectionPageBorders | undefined, geometry: PageGeometry, isFirstPageOfSection: boolean): PageBorderFrameRecord | undefined;
3079
+
3080
+ /** The visible band of the document, in layout units. */
3081
+ interface ViewportWindow {
3082
+ /** Distance from the top of the document to the top of the visible area, in layout units. */
3083
+ readonly top: number;
3084
+ readonly height: number;
3085
+ }
3086
+ /**
3087
+ * Which pages to build in detail.
3088
+ *
3089
+ * A page left out keeps its size and position but no content, so the document's height and page
3090
+ * count are unchanged and scrolling to it reveals it rather than reflowing everything below.
3091
+ */
3092
+ interface MaterializationInput {
3093
+ readonly layout: SemanticLayout;
3094
+ /** Omitted means "no viewport": everything is materialized, which is the honest default. */
3095
+ readonly viewport?: ViewportWindow;
3096
+ /** Extra pages kept ready either side of the visible band. */
3097
+ readonly overscanPages?: number;
3098
+ /** Pages that must be built wherever they are — caret, selection, a search hit. */
3099
+ readonly pinnedPages?: Iterable<number>;
3100
+ }
3101
+ /**
3102
+ * The page indices to build in detail.
3103
+ *
3104
+ * Returns indices rather than records so a caller can compare cheaply against what it
3105
+ * already has mounted, and so the decision can be made without touching the layout.
3106
+ */
3107
+ declare function pagesToMaterialize(input: MaterializationInput): Set<number>;
3108
+
3109
+ /** Character border geometry from placed spans, shared by screen and export painters. */
3110
+ declare function runBorderStrokesForLine(line: LineRecord): readonly ParagraphBorderStrokeRecord[];
3111
+
3112
+ /**
3113
+ * `w:pPr/w:rPr/w:del` or `w:moveFrom` — a tracked revision REMOVES the paragraph mark.
3114
+ *
3115
+ * Both say the break goes away once the decision is taken: a deletion outright, a `moveFrom`
3116
+ * because the paragraph left this place for another. Read from the paragraph-mark run
3117
+ * properties only. A `w:del` anywhere else in the paragraph deletes run content, which is a
3118
+ * different statement entirely.
3119
+ */
3120
+ declare function paragraphMarkDeleted(paragraph: OoxmlNode): boolean;
3121
+ /**
3122
+ * True when a tracked revision has removed this paragraph from the rendered document, so
3123
+ * layout should emit no box for it at all.
3124
+ */
3125
+ declare function revisionRemovesParagraph(paragraph: OoxmlNode, displayMode?: RevisionDisplayMode, authorFilter?: RevisionAuthorFilter): boolean;
3126
+
3127
+ /**
3128
+ * Fit complete leader advances to one shared horizontal grid.
3129
+ *
3130
+ * `startPt` is measured from the paper's left edge, or the local textbox frame.
3131
+ * Offsets remain relative to the supplied gap. No glyph is clipped at either end,
3132
+ * and lines in the same face share their dot columns regardless of preceding text.
3133
+ * @public
3134
+ */
3135
+ declare function tabLeaderPattern(startPt: number, widthPt: number, advancePt: number): {
3136
+ readonly offsetPt: number;
3137
+ readonly count: number;
3138
+ };
3139
+
3140
+ /**
3141
+ * Story-relative rule box for a measured separator span.
3142
+ *
3143
+ * Word paints `w:separator` as a strikeout stroke, so `stroke` carries the face's `OS/2`
3144
+ * `yStrikeoutPosition` and `yStrikeoutSize` at the run's size. Without a resolved face the
3145
+ * box keeps the legacy quarter-em offset and hairline thickness.
3146
+ * @public
3147
+ */
3148
+ declare function noteSeparatorRuleBox(span: StyleSpanRecord, line: LineRecord, stroke?: StrikeoutStrokePt): LayoutBox;
3149
+
3150
+ export { AUTO_PREFERRED_WIDTH, AnchoredDrawingRecord, BlockFragmentRecord, CELL_PAD, COMPOUND_BORDER_MIN_GAP_PT, COMPOUND_BORDER_MIN_STROKE_PT, CaretGeometry, CellBorderBox, ContentControlBoundaryRecord$1 as ContentControlBoundaryRecord, DEFAULT_CANVAS_FONT_STACK, DEFAULT_CELL_MARGINS, DrawingInsets, DrawingPoint, FieldLinkProjector, FixedPoint, FixedPointRoundingMode, FontByteValidator, GRAPHEME_SEGMENTER_LOCALE, HARFBUZZ_SHAPING_LIBRARY, HarfBuzzShapingError, HyperlinkFieldSpec, HyperlinkProjector, InlineDrawingRecord, LayoutBox, LineRecord, MAX_EACH_PAGE_MARK_CANDIDATES, MAX_LVL_TEXT_LENGTH, MAX_MARKER_TEXT_LENGTH, MAX_NOTES_LAID_OUT, MAX_NOTE_FRAGMENTS, MAX_NOTE_OVERFLOW_PAGES, MAX_NOTE_REFLOW_ATTEMPTS, MAX_STYLE_BASED_ON_DEPTH, MAX_TABLE_COLUMNS, MAX_TABLE_NESTING, MAX_TABLE_ROW_FRAGMENTS, MAX_TABLE_ROW_HEIGHT_PT, NumberingIndex, NumberingLevel, PageBorderFrameRecord, PageGeometry, PageRecord, ParagraphBorderStrokeRecord, ParagraphFragmentRecord, RefFieldContext, ResolvedListItem, ReviewItem, RevisionAttribution, RevisionAuthorFilter, RevisionDisplayMode, SectionColumns, SectionPageBorders, SelectionRect, SemanticLayout, SemanticPosition, SemanticRootStoryKind, SemanticSelection, SemanticStoryKind, SemanticStoryVisit, ShapedRun, SourceRange, SpanLinkRecord, StrikeoutStrokePt, StyleCascadeTable, StyleSpanRecord, TableBorderBox, TableBorderSide, TableCellStyleFormatting, TablePaginationError, TextMeasurer, TextShaper, TextboxOwnerRecord, VersionedShapingLibrary, WORD_SEGMENTER_LOCALE, anchorLineY, attachNotesToLayout, boundedFallbackWordSegments, buildPageRefIndex, clampListValue, computeDoubleBorderMetricsPt, computeFootnoteReserves, contentControlAtSemantic, contentControlBoundaries, contentControlHoldingParagraph, contentControlRecordsInPart, createBoundedFallbackWordBoundary, createDefaultWordBoundary, createDocumentFurnitureSource, createDocumentLinkProjectors, createDocumentNotesInput, createDocumentStyleDependencies, createFieldLinkRegistry, createFixedMeasurer, createHarfBuzzTextShaper, createIntlWordBoundary, createLayoutScheduler, createLayoutSession, createListCounterState, createParagraphLayoutCache, defaultNoteSeparatorRuleStyle, deriveNoteDisplayMarks, deriveNoteDisplayMarksResolved, documentOrder, documentRelationshipTargetIn, effectiveBorderSide, emptyTocPlaceholderParagraphIds, emptyTocSuppressedResultParagraphIds, everyStoryOrder, expandLvlText, exportSourceRangeOf, filterRefsOnPage, findSeparatorNote, forEachSemanticSpan, formatDecimal, formatDecimalZero, formatLowerLetter, formatLowerRoman, formatNumFmt, formatPageNumber, formatUpperLetter, formatUpperRoman, fragmentOwnsAtomOffset, graphemeBoundaryEpoch, graphemeCount, graphemeOffsetToUtf16, harfBuzzFontValidator, hitTestSemantic, initializeHarfBuzz, intlGraphemeBoundary, isCanvasMeasurementAvailable, isCumulativeGeometryTrustedFromLineOrigin, isGeometryTrustedCaretOffset, isHarfBuzzInitialized, isIntlSegmenterAvailable, isIntlWordSegmenterAvailable, isMarkerOnlySeparatorNote, isWholeGraphemeHorizontalBoundary, keyedRangeRects, layoutHeaderFooterStory, layoutNoteById, layoutNoteSeparator, layoutNoteStory, layoutSemanticDocument, lineSegments, listMarkerBox, mapSymbolPuaText, normalNotesOf, noteDisplayMarkMap, noteLineIdPrefix, noteMarkKey, noteSeparatorAreaBox, noteSeparatorRuleBox, noteStoryBlocks, pageBorderFrame, pageRefPageNumbersFromLayout, pagesToMaterialize, paragraphLayoutKey, paragraphMarkDeleted, paragraphShading, paragraphShadingBox, parseAutonumInstruction, planRefFieldResultRefresh, positionPastDeletion, projectedNoteMarkText, provisionalNoteMarks, readTableStructure, resetGraphemeBoundary, resolveDefaultSurfaceMeasurer, resolveDefaultWordBoundary, resolveOoxmlShadingFill, resolveStrictHexFill, reviewAnchorIndex, reviewItemGeometry, revisionRemovesParagraph, roundFontUnitToFixedPoint, runBorderStrokesForLine, segmentGraphemes, segmentWords, selectionRects, semanticHorizontalBoundaries, setGraphemeBoundary, setHarfBuzzWasmUrl, sha256FontBytes, shadingFillFromElement, shapedHorizontalBoundaries, storyBlocks, syntheticSeparatorBox, tabLeaderPattern, tableOriginX, tocFieldChromeParagraphIds, tryCreateCanvasMeasurer, utf16OffsetToGrapheme, vectorShapeInkClip, walkStoryParagraphs, wordBoundary, wordSegmentsToGraphemeRecords };
3151
+ export type { AutofitFieldContext, AutonumFieldKind, AutonumFieldSpec, CanvasMeasurerOptions, CanvasTextContext, CanvasTextMetrics, CellMarginsPt, CellVerticalAlign, CompoundBorderMetrics, ContentControlFragmentRecord, CreateDocumentFurnitureSourceOptions, CreateDocumentNotesInputOptions, DocumentFurnitureSource, DocumentLinkProjectors, DocumentStyleDependencies, FieldLinkRegistry, GraphemeBoundary, GraphemeSegment, GraphemeWordSegmentRecord, HarfBuzzFaceCacheEvent, HarfBuzzOutlineCacheEvent, HarfBuzzShapeCacheEvent, HarfBuzzShapingErrorCode, HarfBuzzTextShaper, HarfBuzzTextShaperInstrumentation, HarfBuzzTextShaperOptions, HeaderFooterVariantName, KeyedRange, LayoutCacheStats, LayoutScheduler, LayoutSchedulerOptions, LayoutScope, LayoutSession, LayoutSessionStats, LineSegment, ListCounterAdvance, ListCounterState, MaterializationInput, NoteDisplayMark, NoteLayoutFallbackReason, NoteMarkContext, NotePaginationFallbackReason, NoteReferenceSite, NoteSeparatorLayout, NoteSeparatorRuleStyle, NoteStoryDrawings, NoteStoryLayout, NotesAttachResult, NotesLayoutInput, PageFurniture, PageRefIndex, ParagraphKeyInputs, ParagraphLayoutCache, ParagraphLayoutCacheOptions, PreferredWidth, PreferredWidthType, RefFieldRefreshOptions, RegisteredFieldLink, ResolvedSurfaceMeasurer, ReviewParagraphAnchor, SemanticLayoutOptions, SemanticSpanVisit, SemanticTableCell, SemanticTableRow, SemanticTableStructure, StoryProjectionDependencies, TableAlignment, TableAutofitContext, TablePaginationErrorCode, TableRowHeight, TableRowHeightRule, ViewportWindow, WordBoundary, WordBoundaryResolverDeps, WordSegment };