@docx-editor.dev/core 2.0.1 → 2.1.1

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 (107) hide show
  1. package/README.md +32 -33
  2. package/dist/automation.cjs +1 -1
  3. package/dist/automation.d.cts +2 -2
  4. package/dist/automation.d.ts +2 -2
  5. package/dist/automation.js +1 -1
  6. package/dist/binding.cjs +1 -1
  7. package/dist/binding.d.cts +14 -8
  8. package/dist/binding.d.ts +14 -8
  9. package/dist/binding.js +1 -1
  10. package/dist/chunk-2DTQDAN7.js +2 -0
  11. package/dist/chunk-3JKQE4QJ.cjs +2 -0
  12. package/dist/chunk-4WWEDTTE.js +2 -0
  13. package/dist/chunk-6WIIHJFY.js +1 -0
  14. package/dist/chunk-7VQ7YLW3.cjs +1 -0
  15. package/dist/chunk-BS554B6D.cjs +2 -0
  16. package/dist/chunk-BZK64YU3.cjs +10 -0
  17. package/dist/chunk-F2VQQW5R.cjs +12 -0
  18. package/dist/chunk-GQQOL4PV.js +1 -0
  19. package/dist/chunk-JQFDLZXQ.cjs +2 -0
  20. package/dist/chunk-O2WQY7IT.cjs +5 -0
  21. package/dist/chunk-PC76F2HR.cjs +1 -0
  22. package/dist/chunk-QRAUDDVY.js +2 -0
  23. package/dist/chunk-RKNH4ZFT.js +12 -0
  24. package/dist/chunk-STCITBJK.js +12 -0
  25. package/dist/chunk-TBBXCC7D.js +5 -0
  26. package/dist/chunk-TSXT537P.cjs +12 -0
  27. package/dist/chunk-Y7TA7TPF.js +10 -0
  28. package/dist/contracts/editor.d.cts +6 -6
  29. package/dist/contracts/editor.d.ts +6 -6
  30. package/dist/contracts/interaction.d.cts +6 -6
  31. package/dist/contracts/interaction.d.ts +6 -6
  32. package/dist/contracts/modules.cjs +1 -1
  33. package/dist/contracts/modules.d.cts +30 -3
  34. package/dist/contracts/modules.d.ts +30 -3
  35. package/dist/contracts/modules.js +1 -1
  36. package/dist/{tree-package-store-DOKMtCnh.d.cts → custom-node-writes-YqISlSRl.d.cts} +181 -4
  37. package/dist/{tree-package-store-D5S7VKs5.d.ts → custom-node-writes-ttKuYd0M.d.ts} +181 -4
  38. package/dist/{editor-D0UDock4.d.cts → editor-B93PggnR.d.cts} +24 -5
  39. package/dist/{editor-C1QRi1v4.d.ts → editor-CFsSrxGR.d.ts} +24 -5
  40. package/dist/editor.cjs +1 -1
  41. package/dist/editor.css +1368 -279
  42. package/dist/editor.d.cts +19 -19
  43. package/dist/editor.d.ts +19 -19
  44. package/dist/editor.js +1 -1
  45. package/dist/{image-resources-BJnTpG66.d.ts → image-resources-C_YnqPhn.d.ts} +19 -11
  46. package/dist/{image-resources-BpIydXtU.d.cts → image-resources-DwpNnTg3.d.cts} +19 -11
  47. package/dist/{index-CrW-IxXt.d.cts → index-DQhDm4o0.d.cts} +34 -13
  48. package/dist/{index-ZqBEOrTV.d.ts → index-DTbKoFJL.d.ts} +34 -13
  49. package/dist/index.cjs +1 -1
  50. package/dist/index.d.cts +13 -13
  51. package/dist/index.d.ts +13 -13
  52. package/dist/index.js +1 -1
  53. package/dist/layout.cjs +1 -1
  54. package/dist/layout.d.cts +869 -819
  55. package/dist/layout.d.ts +869 -819
  56. package/dist/layout.js +1 -1
  57. package/dist/output.cjs +1 -1
  58. package/dist/output.d.cts +4 -4
  59. package/dist/output.d.ts +4 -4
  60. package/dist/output.js +1 -1
  61. package/dist/{protocol-oxHaWmt4.d.ts → protocol-BWVA3Q1p.d.ts} +56 -2
  62. package/dist/{protocol-CAkOBBGE.d.cts → protocol-FAz_nPm_.d.cts} +56 -2
  63. package/dist/review-reads-CsoXSvUg.d.ts +401 -0
  64. package/dist/review-reads-qW0jvdF1.d.cts +401 -0
  65. package/dist/{review-support-DS8DYX2a.d.cts → review-support-D64Wu236.d.cts} +37 -5
  66. package/dist/{review-support-Fy3vdIea.d.ts → review-support-nsLXC9jf.d.ts} +37 -5
  67. package/dist/{semantic-cell-selection-DsmHmLJ5.d.ts → semantic-cell-selection-D_8RbPII.d.ts} +2 -2
  68. package/dist/{semantic-cell-selection-CZjBSmMv.d.cts → semantic-cell-selection-DwLB71fF.d.cts} +2 -2
  69. package/dist/{semantic-interaction-Dy-dCUFF.d.ts → semantic-interaction-CXH2wFqi.d.ts} +1 -1
  70. package/dist/{semantic-interaction-D-Dszxrn.d.cts → semantic-interaction-DqcEonqD.d.cts} +1 -1
  71. package/dist/{semantic-paint-drawings-BA3z9jl8.d.ts → semantic-paint-drawings-B5Wk2so5.d.ts} +1 -1
  72. package/dist/{semantic-paint-drawings-D7qbB0E2.d.cts → semantic-paint-drawings-x8g44lD_.d.cts} +1 -1
  73. package/dist/{semantic-records-CJR3BvHs.d.cts → semantic-records-COVFW5aS.d.cts} +77 -4
  74. package/dist/{semantic-records-CWodBJsz.d.ts → semantic-records-zTC0_1zi.d.ts} +77 -4
  75. package/dist/{shaped-measurer-CNIT_svZ.d.cts → shaped-measurer-AFnFxoot.d.cts} +1 -1
  76. package/dist/{shaped-measurer-B-TAtcQU.d.ts → shaped-measurer-CZXL-a6v.d.ts} +1 -1
  77. package/dist/store.cjs +1 -1
  78. package/dist/store.d.cts +223 -317
  79. package/dist/store.d.ts +223 -317
  80. package/dist/store.js +1 -1
  81. package/dist/{tree-op-types-CwOeiaQ8.d.ts → tree-op-types-DWLCllG7.d.ts} +80 -25
  82. package/dist/{tree-op-types-DMiW6nIH.d.cts → tree-op-types-UOXlRUqQ.d.cts} +80 -25
  83. package/dist/{tree-session-CiUel5Kz.d.ts → tree-session-BtVFXsF6.d.ts} +45 -7
  84. package/dist/{tree-session-D47_imvR.d.cts → tree-session-DxIOHUOD.d.cts} +45 -7
  85. package/dist/{tree-store-CxifqwIv.d.cts → tree-store-B1lEkdXc.d.cts} +1 -1
  86. package/dist/{tree-store-hLtjeyWE.d.ts → tree-store-BscFPB7x.d.ts} +1 -1
  87. package/package.json +10 -5
  88. package/dist/chunk-5KSCB2OZ.js +0 -2
  89. package/dist/chunk-BSC44TIT.js +0 -5
  90. package/dist/chunk-DLPL5DAG.js +0 -12
  91. package/dist/chunk-DOEIARE2.js +0 -2
  92. package/dist/chunk-G7YCQBZE.cjs +0 -5
  93. package/dist/chunk-HR7K3W6A.js +0 -1
  94. package/dist/chunk-HVAV2JSN.js +0 -11
  95. package/dist/chunk-MYH2HCUJ.cjs +0 -2
  96. package/dist/chunk-NSM75TN6.cjs +0 -1
  97. package/dist/chunk-PHDVKUWZ.js +0 -1
  98. package/dist/chunk-QADYGK5L.cjs +0 -12
  99. package/dist/chunk-RNBHTPW7.js +0 -10
  100. package/dist/chunk-UD2ZXLFI.cjs +0 -2
  101. package/dist/chunk-VYAVK2AH.cjs +0 -1
  102. package/dist/chunk-YUK46A2N.cjs +0 -11
  103. package/dist/chunk-ZMIX42J7.cjs +0 -10
  104. package/dist/chunk-ZT55Q52J.cjs +0 -2
  105. package/dist/chunk-ZUALAR4M.js +0 -2
  106. package/dist/note-properties-CnmCoory.d.cts +0 -68
  107. package/dist/note-properties-m75hmP70.d.ts +0 -68
package/dist/layout.d.ts CHANGED
@@ -1,21 +1,22 @@
1
- import { V as VersionedShapingLibrary, T as TextShaper, F as FontByteValidator, a as FixedPointRoundingMode, b as FixedPoint, c as TextDirection, S as ShapedRun } from './shaped-measurer-B-TAtcQU.js';
2
- export { C as CacheLookup, d as CacheMiss, e as CacheProvenance, D as DeclaredFontSubstitution, f as FontFingerprintInputs, g as FontRequest, h as FontResolutionError, i as FontResolutionErrorCode, j as FontResourceDefinition, k as FontResourceInstrumentation, l as FontResourceSnapshot, m as FontResourceSnapshotOptions, n as FontSubstitution, o as FontValidationResult, G as GlyphOutline, H as HARD_MAX_AGGREGATE_FONT_BYTES, p as HARD_MAX_FONT_BYTES, q as HARD_MAX_FONT_SOURCES, L as LayoutShapingOptions, N as NormalizationPolicy, O as OperationSnapshot, r as OperationSnapshotField, s as OperationSnapshotGuard, R as ResolvedCache, t as ResolvedFont, u as ResourceDependencyProvenance, v as ShapeInput, w as ShapedCluster, x as ShapedFontSpan, y as ShapedGlyph, z as ShapedMeasurerOptions, A as ShapedRunComparatorInputs, B as ShapedVerticalMetrics, E as ShapingEnvironment, I as ShapingEnvironmentFingerprintInputs, J as ShapingEnvironmentInput, K as boundedStructuralFontValidator, M as captureOperationSnapshot, P as createFontResourceSnapshot, Q as createShapedMeasurer, U as createShapedRun, W as createShapingEnvironment, X as fixedPoint, Y as fontRequestKey, Z as guardOperationSnapshot, _ as sha256FontBytes, $ as shapedRunComparatorInputs, a0 as shapingEnvironmentFingerprint, a1 as shapingEnvironmentFingerprintInputs } from './shaped-measurer-B-TAtcQU.js';
3
- import { d as LayoutBox, D as DrawingPoint, e as DrawingInsets, f as TableBorderSide, a as StyleSpanRecord, I as InlineDrawingRecord, M as ModelRange, R as ResolvedRunStyle, g as ThemeFonts, h as ParagraphSpacing, i as ParagraphLineSpacing, j as ParagraphBorderEdge, k as ParagraphBorders, l as ResolvedTabStops, B as BlockFragmentRecord, T as TextMeasurer, S as SemanticLayout, m as PageRecord, n as ParagraphFragmentRecord, A as AnchoredDrawingRecord, F as FieldPageContext, o as InlineDrawingLayoutContext, P as PageGeometry, H as HyperlinkProjector, p as CellBorderBox, q as TableBorderBox } from './semantic-records-CWodBJsz.js';
4
- export { r as BorderGridGeometry, C as ContentControlBoundaryRecord, s as ContentControlGeometryFragment, t as ContentControlLevel, u as ContentControlLock, v as ContentControlMappedType, w as DEFAULT_PAGE_GEOMETRY, x as DEFAULT_RUN_STYLE, y as DEFAULT_TAB_INTERVAL_PT, z as DEFAULT_TAB_INTERVAL_TWIPS, E as EMPTY_TAB_STOPS, G as HeaderFooterStoryRecord, L as LineRecord, J as LineSpacingRule, K as ListMarkerRecord, N as MAX_BORDER_SPACE_PT, O as MAX_BORDER_WIDTH_PT, Q as MAX_PARAGRAPH_SPACING_PT, U as MAX_TABLE_BORDER_STROKES, V as MAX_TAB_POSITION_TWIPS, W as MAX_TAB_STOPS, X as PARAGRAPH_BORDER_SIDES, Y as ParagraphBorderSide, Z as ParagraphBorderStrokeRecord, _ as ParagraphBottomBorderRecord, $ as ParagraphIndent, a0 as ResolvedCellBorders, a1 as ResolvedTableBorderEdge, a2 as ResolvedTableBorderEdgeSegment, a3 as ResolvedUnderline, a4 as SINGLE_LINE_SPACING, a5 as SourceRange, a6 as SpanLinkRecord, a7 as TAB_LEADER_GLYPH, a8 as TabAlignment, a9 as TabDestination, aa as TabLeader, ab as TabStop, ac as TableBorderSideName, ad as TableBorderStrokeRecord, c as TableCellFragmentRecord, ae as TableFragmentRecord, b as TableRowFragmentRecord, af as VerticalAlign, ag as appliedSpaceBefore, ah as applyLineSpacing, ai as baselineShiftPtOf, aj as borderExtentPt, ak as borderWeight, al as bottomBorderExtentPt, am as cascadedParagraphBorders, an as cascadedTabStops, ao as collapsedSpaceBefore, ap as contentControlsOfLayout, aq as defaultTabIntervalFromSettings, ar as displayText, as as effectiveContentControlLock, at as formatPageNumber, au as fragmentsOfParagraph, av as lineAtPosition, aw as linesOf, ax as measureDisplayText, ay as nextTabDestination, az as paragraphBorderExtentPt, aA as paragraphBorderStrokeWidthPt, aB as paragraphBorders, aC as paragraphBordersFingerprint, aD as paragraphBreaksBefore, aE as paragraphContextualSpacing, aF as paragraphFragmentsOf, aG as paragraphFragmentsOfBlocks, aH as paragraphLineSpacing, aI as paragraphSpacing, aJ as paragraphTabStops, aK as readBorderSide, aL as readCellBorders, aM as readTableBorders, aN as resolveBorderConflict, aO as resolveRunStyle, aP as resolveTableCellBorderGrid, aQ as runStylesEqual, aR as tabAdvanceWidth, aS as tabStopsFingerprint, aT as unionLayoutBoxes, aU as withDefaultTabInterval } from './semantic-records-CWodBJsz.js';
1
+ import { V as VersionedShapingLibrary, T as TextShaper, F as FontByteValidator, a as FixedPointRoundingMode, b as FixedPoint, c as TextDirection, S as ShapedRun } from './shaped-measurer-CZXL-a6v.js';
2
+ export { C as CacheLookup, d as CacheMiss, e as CacheProvenance, D as DeclaredFontSubstitution, f as FontFingerprintInputs, g as FontRequest, h as FontResolutionError, i as FontResolutionErrorCode, j as FontResourceDefinition, k as FontResourceInstrumentation, l as FontResourceSnapshot, m as FontResourceSnapshotOptions, n as FontSubstitution, o as FontValidationResult, G as GlyphOutline, H as HARD_MAX_AGGREGATE_FONT_BYTES, p as HARD_MAX_FONT_BYTES, q as HARD_MAX_FONT_SOURCES, L as LayoutShapingOptions, N as NormalizationPolicy, O as OperationSnapshot, r as OperationSnapshotField, s as OperationSnapshotGuard, R as ResolvedCache, t as ResolvedFont, u as ResourceDependencyProvenance, v as ShapeInput, w as ShapedCluster, x as ShapedFontSpan, y as ShapedGlyph, z as ShapedMeasurerOptions, A as ShapedRunComparatorInputs, B as ShapedVerticalMetrics, E as ShapingEnvironment, I as ShapingEnvironmentFingerprintInputs, J as ShapingEnvironmentInput, K as boundedStructuralFontValidator, M as captureOperationSnapshot, P as createFontResourceSnapshot, Q as createShapedMeasurer, U as createShapedRun, W as createShapingEnvironment, X as fixedPoint, Y as fontRequestKey, Z as guardOperationSnapshot, _ as sha256FontBytes, $ as shapedRunComparatorInputs, a0 as shapingEnvironmentFingerprint, a1 as shapingEnvironmentFingerprintInputs } from './shaped-measurer-CZXL-a6v.js';
3
+ import { d as LayoutBox, D as DrawingPoint, e as DrawingInsets, a as StyleSpanRecord, I as InlineDrawingRecord, M as ModelRange, R as ResolvedRunStyle, f as ThemeFonts, g as ParagraphSpacing, h as ParagraphLineSpacing, i as ParagraphBorderEdge, j as ParagraphBorders, k as ResolvedTabStops, l as TableBorderSide, B as BlockFragmentRecord, m as InlineDrawingLayoutContext, T as TextMeasurer, S as SemanticLayout, n as PageRecord, o as ParagraphFragmentRecord, A as AnchoredDrawingRecord, F as FieldPageContext, P as PageGeometry, H as HyperlinkProjector, p as CellBorderBox, q as TableBorderBox } from './semantic-records-zTC0_1zi.js';
4
+ export { r as AUTO_PARAGRAPH_SPACING_PT, s as BorderGridGeometry, C as ContentControlBoundaryRecord, t as ContentControlGeometryFragment, u as ContentControlLevel, v as ContentControlLock, w as ContentControlMappedType, x as DEFAULT_PAGE_GEOMETRY, y as DEFAULT_RUN_STYLE, z as DEFAULT_TAB_INTERVAL_PT, E as DEFAULT_TAB_INTERVAL_TWIPS, G as EMPTY_TAB_STOPS, J as HeaderFooterStoryRecord, L as LineRecord, K as LineSpacingRule, N as ListMarkerRecord, O as MAX_BORDER_SPACE_PT, Q as MAX_BORDER_WIDTH_PT, U as MAX_PARAGRAPH_SPACING_PT, V as MAX_TABLE_BORDER_STROKES, W as MAX_TAB_POSITION_TWIPS, X as MAX_TAB_STOPS, Y as PARAGRAPH_BORDER_SIDES, Z as ParagraphAutoSpacingContext, _ as ParagraphBorderSide, $ as ParagraphBorderStrokeRecord, a0 as ParagraphBottomBorderRecord, a1 as ParagraphIndent, a2 as ResolvedCellBorders, a3 as ResolvedTableBorderEdge, a4 as ResolvedTableBorderEdgeSegment, a5 as ResolvedUnderline, a6 as SINGLE_LINE_SPACING, a7 as SourceRange, a8 as SpanLinkRecord, a9 as TAB_LEADER_GLYPH, aa as TabAlignment, ab as TabDestination, ac as TabLeader, ad as TabStop, ae as TableBorderSideName, af as TableBorderStrokeRecord, c as TableCellFragmentRecord, ag as TableFragmentRecord, b as TableRowFragmentRecord, ah as VerticalAlign, ai as appliedSpaceBefore, aj as applyLineSpacing, ak as baselineShiftPtOf, al as borderExtentPt, am as borderWeight, an as bottomBorderExtentPt, ao as cascadedParagraphBorders, ap as cascadedTabStops, aq as collapsedSpaceBefore, ar as contentControlsOfLayout, as as defaultTabIntervalFromSettings, at as displayText, au as effectiveContentControlLock, av as formatPageNumber, aw as fragmentsOfParagraph, ax as lineAtPosition, ay as linesOf, az as measureDisplayText, aA as nextTabDestination, aB as paragraphBorderExtentPt, aC as paragraphBorderStrokeWidthPt, aD as paragraphBorders, aE as paragraphBordersFingerprint, aF as paragraphBreaksBefore, aG as paragraphContextualSpacing, aH as paragraphFragmentsOf, aI as paragraphFragmentsOfBlocks, aJ as paragraphLineSpacing, aK as paragraphSpacing, aL as paragraphTabStops, aM as readBorderSide, aN as readCellBorders, aO as readTableBorders, aP as resolveBorderConflict, aQ as resolveRunStyle, aR as resolveTableCellBorderGrid, aS as runStylesEqual, aT as tabAdvanceWidth, aU as tabStopsFingerprint, aV as unionLayoutBoxes, aW as withDefaultTabInterval } from './semantic-records-zTC0_1zi.js';
5
5
  import { O as OoxmlNode, a as OoxmlElement, e as OoxmlPart } from './ooxml-tree-BU0e3DVi.js';
6
- import { O as OoxmlProperty, C as ContentControlKind, n as ContentControlLevel, o as ContentControlLock, p as ContentControlDataBinding, I as ImpactClass } from './tree-op-types-CwOeiaQ8.js';
7
- export { l as TableBorderStyle } from './tree-op-types-CwOeiaQ8.js';
6
+ import { O as OoxmlProperty, C as ContentControlKind, n as ContentControlLevel, o as ContentControlLock, p as ContentControlDataBinding, I as ImpactClass } from './tree-op-types-DWLCllG7.js';
7
+ export { l as TableBorderStyle } from './tree-op-types-DWLCllG7.js';
8
8
  import { N as NoteKind, n as noteTypeOf } from './note-nodes-BPSTVDMr.js';
9
- import { R as ResolvedFootnoteProperties, a as ResolvedEndnoteProperties } from './note-properties-m75hmP70.js';
9
+ import { R as ResolvedFootnoteProperties, a as ResolvedEndnoteProperties } from './review-reads-CsoXSvUg.js';
10
+ export { p as paragraphOrderOfPart } from './review-reads-CsoXSvUg.js';
10
11
  import { R as RevisionDisplayMode } from './revision-projection-DHAkcD_L.js';
11
12
  export { D as DEFAULT_REVISION_DISPLAY_MODE, a as RevisionAttribution, b as RevisionKind, f as formatRevisionOf, p as paragraphMarkRevisionOf, r as revisionsAreDeletion, c as revisionsVisible } from './revision-projection-DHAkcD_L.js';
12
- import { a as SectionColumns } from './semantic-cell-selection-DsmHmLJ5.js';
13
- export { C as CellSelection, D as DEFAULT_SECTION_PROPERTIES, b as DEFAULT_VERTICAL_WEIGHT, c as DocumentSection, d as DocumentSectionsEnumeration, e as DrawingOverlayFrame, H as HitPoint, f as HitTestOptions, M as MAX_DOCUMENT_SECTIONS, P as PlacedCell, g as SectionBreakType, h as SectionColumnDefinition, i as SectionMargins, j as SectionPageNumbering, S as SectionProperties, k as SemanticHit, l as SemanticHitDrawing, T as TableCellAddress, m as TableCellContext, n as caretBoxOnLine, o as cellSelectionBetween, p as cellSelectionRects, q as cellSelectionText, r as contentControlAtPoint, s as enumerateDocumentSections, t as enumerateDocumentSectionsBounded, u as findDrawingOverlayFrameInLayout, v as geometryOfSection, w as hitTestPage, x as hitTestSheet, y as isFurniturePoint, z as lineEndOffset, A as pageAtY, B as paragraphSectionNode, E as paragraphsInCells, F as parsePageNumbering, G as parseSectionProperties, I as readSectionProperties, J as spanOffsetX, K as spansInCells, L as tableContextAt } from './semantic-cell-selection-DsmHmLJ5.js';
13
+ import { a as SectionColumns } from './semantic-cell-selection-D_8RbPII.js';
14
+ export { C as CellSelection, D as DEFAULT_SECTION_PROPERTIES, b as DEFAULT_VERTICAL_WEIGHT, c as DocumentSection, d as DocumentSectionsEnumeration, e as DrawingOverlayFrame, H as HitPoint, f as HitTestOptions, M as MAX_DOCUMENT_SECTIONS, P as PlacedCell, g as SectionBreakType, h as SectionColumnDefinition, i as SectionMargins, j as SectionPageNumbering, S as SectionProperties, k as SemanticHit, l as SemanticHitDrawing, T as TableCellAddress, m as TableCellContext, n as caretBoxOnLine, o as cellSelectionBetween, p as cellSelectionRects, q as cellSelectionText, r as contentControlAtPoint, s as enumerateDocumentSections, t as enumerateDocumentSectionsBounded, u as findDrawingOverlayFrameInLayout, v as geometryOfSection, w as hitTestPage, x as hitTestSheet, y as isFurniturePoint, z as lineEndOffset, A as pageAtY, B as paragraphSectionNode, E as paragraphsInCells, F as parsePageNumbering, G as parseSectionProperties, I as readSectionProperties, J as spanOffsetX, K as spansInCells, L as tableContextAt } from './semantic-cell-selection-D_8RbPII.js';
14
15
  export { M as MAX_SDT_NESTING, c as collectFlowBlocks, a as contentControlContentChildren, i as isContentControl, b as isContentControlContent } from './content-control-walk-D06GijR0.js';
15
- export { h as CommentAnchor, i as CommentPosition, C as CommentRecord, j as CommentThreadState, c as ReviewCommentItem, d as ReviewCustomItem, a as ReviewItem, R as ReviewModelInput, k as ReviewParagraphAnchor, e as ReviewPosition, f as ReviewRange, b as ReviewRevisionItem, g as ReviewRevisionKind, W as W15_NAMESPACE_URI, l as activeReviewItem, m as commentBodyText, n as commentInitials, o as firstReviewRange, p as paragraphOrderOfPart, r as reviewAnchorIndex, q as reviewItemGeometry, s as reviewItemKey, t as reviewItemPositionRank, u as reviewItemRanges, v as reviewItemsAt, w as reviewThreadRootOf } from './review-support-Fy3vdIea.js';
16
- import { T as TreeModelChange } from './tree-store-hLtjeyWE.js';
17
- export { b as CaretAtOptions, C as CaretGeometry, K as KeyedRange, M as MoveCaretOptions, N as NavigationCommand, c as SelectionRect, S as SemanticPosition, a as SemanticSelection, d as caretAt, e as caretStops, f as caretStopsForBlocks, g as compositionAnchor, h as contentControlAtSemantic, i as contentControlsInLayout, j as documentOrder, k as hitTestSemantic, l as keyedRangeRects, m as moveCaret, p as paragraphTextFromLayout, s as selectionRects, n as spansInSelection, w as wordBoundary } from './semantic-interaction-Dy-dCUFF.js';
18
- import './image-resources-BJnTpG66.js';
16
+ export { h as CommentAnchor, i as CommentPosition, C as CommentRecord, j as CommentThreadState, c as ReviewCommentItem, d as ReviewCustomItem, a as ReviewItem, R as ReviewModelInput, k as ReviewParagraphAnchor, e as ReviewPosition, f as ReviewRange, b as ReviewRevisionItem, g as ReviewRevisionKind, W as W15_NAMESPACE_URI, l as activeReviewItem, m as commentBodyText, n as commentInitials, o as firstReviewRange, r as reviewAnchorIndex, p as reviewItemGeometry, q as reviewItemKey, s as reviewItemPositionRank, t as reviewItemRanges, u as reviewItemsAt, v as reviewThreadRootOf } from './review-support-nsLXC9jf.js';
17
+ import { T as TreeModelChange } from './tree-store-BscFPB7x.js';
18
+ export { b as CaretAtOptions, C as CaretGeometry, K as KeyedRange, M as MoveCaretOptions, N as NavigationCommand, c as SelectionRect, S as SemanticPosition, a as SemanticSelection, d as caretAt, e as caretStops, f as caretStopsForBlocks, g as compositionAnchor, h as contentControlAtSemantic, i as contentControlsInLayout, j as documentOrder, k as hitTestSemantic, l as keyedRangeRects, m as moveCaret, p as paragraphTextFromLayout, s as selectionRects, n as spansInSelection, w as wordBoundary } from './semantic-interaction-CXH2wFqi.js';
19
+ import './image-resources-C_YnqPhn.js';
19
20
  import './ooxml-package-D_CtywTW.js';
20
21
 
21
22
  /**
@@ -32,18 +33,84 @@ interface StoryPageFieldNeeds {
32
33
  readonly hasSectionPages: boolean;
33
34
  }
34
35
 
35
- type WrapTextSide = 'bothSides' | 'left' | 'right' | 'largest';
36
- interface WrapExclusionInput {
37
- readonly mode: 'square' | 'tight' | 'through' | 'topAndBottom';
38
- readonly contentBounds: LayoutBox;
39
- readonly polygon: readonly DrawingPoint[] | null;
40
- readonly clipPolygon: readonly DrawingPoint[] | null;
41
- readonly wrapDistances: DrawingInsets;
42
- readonly effectInsets: DrawingInsets;
43
- readonly textSide: WrapTextSide;
44
- readonly contentLeft: number;
45
- readonly contentRight: number;
36
+ /** A fingerprint over one paragraph's layout inputs. */
37
+ type ParagraphLayoutKey = string;
38
+ /** Cache counters, for asserting that incremental layout is actually reusing work. */
39
+ interface LayoutCacheStats {
40
+ readonly hits: number;
41
+ readonly misses: number;
42
+ readonly evictions: number;
43
+ readonly size: number;
44
+ }
45
+ /**
46
+ * The per-paragraph measurement cache.
47
+ *
48
+ * Caches the BREAK only — where a paragraph's lines fall at a given width — never its placement.
49
+ * An edit high in a document still repaginates everything below it, while paragraphs nobody
50
+ * touched are never measured again.
51
+ */
52
+ interface ParagraphLayoutCache<T> {
53
+ get(key: ParagraphLayoutKey): T | undefined;
54
+ set(key: ParagraphLayoutKey, value: T): void;
55
+ /** Drop entries for paragraphs a commit removed, so the cache cannot grow without bound. */
56
+ retain(keys: ReadonlySet<ParagraphLayoutKey>): void;
57
+ clear(): void;
58
+ readonly stats: LayoutCacheStats;
59
+ }
60
+ /**
61
+ * Everything that decides whether a cached paragraph break is still valid.
62
+ *
63
+ * `producer` is in the key because a font arriving after first paint changes every advance in the
64
+ * document while no content changes — without it the cache would serve the pre-font layout
65
+ * forever.
66
+ */
67
+ interface ParagraphKeyInputs {
68
+ readonly paragraph: OoxmlNode;
69
+ readonly properties: readonly OoxmlProperty[];
70
+ /** Available width, which decides where the lines break. */
71
+ readonly width: number;
72
+ /**
73
+ * Who produced the measurements.
74
+ *
75
+ * Fonts loading after first paint change every advance while no content changes, so a
76
+ * cache keyed on content alone would serve the pre-font layout forever.
77
+ */
78
+ readonly producer: string;
79
+ /**
80
+ * Inline drawing projection/resource epoch for this paragraph.
81
+ *
82
+ * Pending→ready/refused transitions and extent/hidden changes must invalidate breaks even
83
+ * when paragraph text is unchanged.
84
+ */
85
+ readonly drawingToken?: string;
86
+ /** Active page exclusion zones affecting this paragraph's break. */
87
+ readonly exclusionToken?: string;
88
+ }
89
+ /**
90
+ * The cache key for one paragraph's measured break.
91
+ *
92
+ * Folds in the content, the available width, and the measurement producer. Anything that changes
93
+ * where lines fall must be in here, or the cache serves a break taken under different conditions.
94
+ */
95
+ declare function paragraphLayoutKey(inputs: ParagraphKeyInputs): ParagraphLayoutKey;
96
+ /** How large the paragraph cache grows before least-recently-used eviction. */
97
+ interface ParagraphLayoutCacheOptions {
98
+ /**
99
+ * Entries retained before the least recently used are dropped.
100
+ *
101
+ * The default has to exceed a realistic document, or a full pass evicts exactly what the
102
+ * next one needs and the cache costs more than it saves.
103
+ */
104
+ readonly maxEntries?: number;
46
105
  }
106
+ /**
107
+ * A bounded least-recently-used cache.
108
+ *
109
+ * Bounded because a long editing session touches far more paragraph states than a document
110
+ * contains — every keystroke mints a new key for the paragraph being typed in — and an
111
+ * unbounded cache would hold every intermediate state of the session.
112
+ */
113
+ declare function createParagraphLayoutCache<T>(options?: ParagraphLayoutCacheOptions): ParagraphLayoutCache<T>;
47
114
 
48
115
  /**
49
116
  * Lookup of derived display marks keyed by {@link formatNoteScopeId}.
@@ -95,988 +162,940 @@ interface ProjectedNoteMark {
95
162
  /** Resolve display text for a noteReference / noteRef node under a mark context. */
96
163
  declare function projectedNoteMarkText(node: OoxmlNode, context: NoteMarkContext | undefined): ProjectedNoteMark | null;
97
164
 
98
- /**
99
- * What one cell side is before any ADJACENT cell has its say.
100
- *
101
- * - omitted → the table's own rule for that position (`tblBorders`, `insideH`/`insideV`)
102
- * - edge → the cell wins outright; no weight fight with the table
103
- * - none → an explicit `w:val="nil"`. Suppresses a matching table border on interior
104
- * and perimeter sides alike, so a table whose cells all declare none paints borderless
105
- * like Word even when `tblBorders` still carry `single` rules.
106
- */
107
- declare function effectiveBorderSide(authored: TableBorderSide, tableSide: TableBorderSide, _options?: {
108
- readonly interior?: boolean;
109
- }): TableBorderSide;
165
+ type WrapTextSide = 'bothSides' | 'left' | 'right' | 'largest';
166
+ interface WrapExclusionInput {
167
+ readonly mode: 'square' | 'tight' | 'through' | 'topAndBottom';
168
+ readonly contentBounds: LayoutBox;
169
+ readonly polygon: readonly DrawingPoint[] | null;
170
+ readonly clipPolygon: readonly DrawingPoint[] | null;
171
+ readonly wrapDistances: DrawingInsets;
172
+ readonly effectInsets: DrawingInsets;
173
+ readonly textSide: WrapTextSide;
174
+ readonly contentLeft: number;
175
+ readonly contentRight: number;
176
+ }
110
177
 
111
- /** Minimum stroke width for a compound (double/triple) band, in points. */
112
- declare const COMPOUND_BORDER_MIN_STROKE_PT = 1;
113
- /** Minimum gap between compound strokes, in points. */
114
- declare const COMPOUND_BORDER_MIN_GAP_PT = 1;
178
+ /** Paint layer relative to body text — not the OOXML wrap element. */
179
+ type DrawingPaintLayer = 'behind' | 'inFront';
180
+ interface ExclusionZone {
181
+ readonly drawingNodeId: string;
182
+ readonly anchorParagraphId: string;
183
+ /** UTF-16 model offset of the anchor atom — exclusions apply at/after this point in the paragraph. */
184
+ readonly anchorModelStart: number;
185
+ readonly sourceOrder: number;
186
+ readonly paintLayer: DrawingPaintLayer;
187
+ readonly relativeHeight: number;
188
+ readonly allowOverlap: boolean;
189
+ /** Owning column index in a multi-column section — 0 for single-column and HF stories. */
190
+ readonly columnIndex: number;
191
+ /** Resolved top edge in page-content coordinates after overlap displacement. */
192
+ readonly y: number;
193
+ readonly verticalBand: LayoutBox;
194
+ readonly input: WrapExclusionInput;
195
+ }
196
+
197
+ interface PendingLine {
198
+ readonly spans: StyleSpanRecord[];
199
+ readonly drawings: InlineDrawingRecord[];
200
+ readonly start: number;
201
+ end: number;
202
+ width: number;
203
+ height: number;
204
+ baseline: number;
205
+ /**
206
+ * Space ABOVE the glyph band inside {@link height}.
207
+ *
208
+ * Exact spacing can center the glyphs and move the baseline. Auto/atLeast spacing leaves
209
+ * this at zero and puts its extra depth below instead.
210
+ */
211
+ leading: number;
212
+ /**
213
+ * Auto/atLeast line-spacing depth below the painted glyph band.
214
+ *
215
+ * Word lets this external depth cross the bottom text margin when the glyphs themselves
216
+ * still fit. Pagination therefore budgets {@link height} minus this amount at a page
217
+ * bottom, while paint keeps the full box and padding.
218
+ */
219
+ trailingSpacing: number;
220
+ /** When true, layout must start a new page after this line is placed. */
221
+ pageBreakAfter?: boolean;
222
+ /** When true, layout must advance to the next authored section column. */
223
+ columnBreakAfter?: boolean;
224
+ /** Model ranges on this line covering deleted content; see {@link LineRecord.deletedRanges}. */
225
+ deletedRanges?: readonly ModelRange[];
226
+ /** Vertical gap inserted before this line to clear a topAndBottom exclusion band. */
227
+ exclusionSkipBefore?: number;
228
+ }
229
+ /** Horizontal alignment of a paragraph (`w:jc`, ECMA-376 §17.3.1.13). */
230
+ type Alignment = 'left' | 'center' | 'right' | 'both';
231
+
232
+ /** Soft ceiling on abstractNum / num entries read from one part. */
233
+ declare const MAX_NUMBERING_DEFINITIONS = 512;
234
+ /** Soft ceiling on override entries per `w:num`. */
235
+ declare const MAX_LVL_OVERRIDES = 9;
236
+ /** `w:suff` — what separates a list marker from the text after it. */
237
+ type ListSuffix = 'tab' | 'space' | 'nothing';
238
+ /** `w:lvlJc` — how a list marker aligns within its own indent. */
239
+ type ListMarkerAlign = 'left' | 'center' | 'right';
115
240
  /**
116
- * How a multi-line border style (double, triple) is drawn: stroke width, gap, and total extent.
241
+ * One level's indent, plus which parts the level actually AUTHORED.
117
242
  *
118
- * The band is CENTRED on the authored width, so a double border occupies the space Word gives it
119
- * rather than growing the cell it surrounds.
243
+ * The provenance matters: `w:ind` cascades per-attribute, so a level that authored only `left`
244
+ * must not overwrite an inherited `hanging` with a synthesized zero.
120
245
  */
121
- interface CompoundBorderMetrics {
122
- readonly strokePt: number;
123
- readonly gapPt: number;
124
- readonly extentPt: number;
125
- /** Centers the compound band on the authored width; negative extends outward. */
126
- readonly insetPt: number;
246
+ interface NumberingLevelIndent {
247
+ readonly left: number;
248
+ readonly right: number;
249
+ readonly hanging: number;
250
+ readonly firstLine: number;
251
+ /**
252
+ * Which of these the LEVEL actually authored.
253
+ *
254
+ * A level's `w:pPr/w:ind` sits between the paragraph style and direct formatting, so the
255
+ * merge has to tell "the level says left = 0" from "the level says nothing about left and
256
+ * the style's value stands". Absent (the default) reads as "says nothing", which is what a
257
+ * hand-built level in a test means.
258
+ */
259
+ readonly stated?: {
260
+ readonly left: boolean;
261
+ readonly right: boolean;
262
+ /** `w:hanging`/`w:firstLine` are one mutually exclusive slot (§17.3.1.10, §17.3.1.12). */
263
+ readonly firstLineOffset: boolean;
264
+ };
265
+ }
266
+ /** One `w:lvl`: how this depth numbers, what its marker looks like, and how it indents. */
267
+ interface NumberingLevel {
268
+ readonly ilvl: number;
269
+ readonly start: number;
270
+ readonly numFmt: string;
271
+ readonly lvlText: string;
272
+ readonly lvlJc: ListMarkerAlign;
273
+ readonly suff: ListSuffix;
274
+ readonly indent: NumberingLevelIndent;
275
+ /**
276
+ * `w:lvlRestart` one-based trigger level, or `0` when the level never restarts.
277
+ * Omitted in XML → restart when the previous level (or any earlier level) is used.
278
+ */
279
+ readonly lvlRestart?: number;
280
+ /**
281
+ * `w:isLgl` (§17.9.9): render EVERY level referenced by this level's `w:lvlText` in
282
+ * decimal, whatever number format those levels declare for themselves.
283
+ */
284
+ readonly isLgl: boolean;
285
+ /** Level `w:rPr` as flat properties (for marker face / vanish). */
286
+ readonly runProperties: readonly OoxmlProperty[];
287
+ /** True when level run props request vanish — marker must not paint. */
288
+ readonly vanish: boolean;
127
289
  }
128
290
  /**
129
- * Deterministic double stroke / gap / extent in layout points (scale-independent).
291
+ * One `w:lvlOverride` on a `w:num`: a restart value, a replacement level, or both.
130
292
  *
131
- * Thin authored widths inflate to a 1+1+1 point compound so a `w:sz="3"` double remains
132
- * visible at paint scale 1 — matching Word's hairline-double floor.
293
+ * How two lists share an abstract definition while numbering independently.
133
294
  */
134
- declare function computeDoubleBorderMetricsPt(widthPt: number): CompoundBorderMetrics;
135
-
295
+ interface LevelOverride {
296
+ readonly startOverride?: number;
297
+ /** Full level replacement when `w:lvl` is present under the override. */
298
+ readonly level?: NumberingLevel;
299
+ }
136
300
  /**
137
- * The exact HarfBuzz build this engine shapes against.
301
+ * One `w:abstractNum` — the shape of a list, without being a list.
138
302
  *
139
- * Pinned and verified at load: a runtime reporting a different version is REFUSED rather than
140
- * used, because glyph positioning can change between releases and a cached measurement taken
141
- * under one build must not be trusted under another.
303
+ * Never referenced by a paragraph directly. Paragraphs name a {@link NumDefinition}, which names
304
+ * one of these, so several lists can share a definition and still count separately.
142
305
  */
143
- declare const HARFBUZZ_SHAPING_LIBRARY: VersionedShapingLibrary;
144
- /** Load and verify the HarfBuzz WASM runtime without adding top-level await to import graphs. */
145
- declare function initializeHarfBuzz(): Promise<void>;
146
- /** Whether the WASM runtime is loaded and shaping can proceed synchronously. */
147
- declare function isHarfBuzzInitialized(): boolean;
148
- /**
149
- * Why shaping refused.
150
- *
151
- * Mostly RESOURCE limits, because every input here derives from a file: text length, codepoint
152
- * count, glyph count and outline size are all attacker-influenced, and an unbounded shape call is
153
- * a denial-of-service vector rather than a rendering bug.
154
- */
155
- type HarfBuzzShapingErrorCode = 'notInitialized' | 'fontOverLimit' | 'malformedFont' | 'textOverLimit' | 'codepointsOverLimit' | 'glyphOverLimit' | 'outlineOverLimit' | 'shapedRunOverLimit' | 'unsupportedVariationAxes' | 'unsupportedFallback' | 'unsupportedColorFont' | 'unsupportedNormalization' | 'invalidBidiLevel' | 'shapingLibraryMismatch' | 'disposed';
306
+ interface AbstractNumDefinition {
307
+ readonly abstractNumId: string;
308
+ readonly levels: ReadonlyMap<number, NumberingLevel>;
309
+ /**
310
+ * `w:numStyleLink` (§17.9.21): this definition carries no levels of its own — the numbering
311
+ * lives on the named style. Word follows the link; resolve it with
312
+ * {@link resolveNumberingStyleLinks} before reading levels, or every paragraph on this
313
+ * abstract renders with NO marker at all.
314
+ */
315
+ readonly numStyleLink?: string;
316
+ /**
317
+ * `w:styleLink` (§17.9.23): the definition side of the same pair — this abstract IS the
318
+ * numbering of the named style. Kept so a link can be verified rather than assumed.
319
+ */
320
+ readonly styleLink?: string;
321
+ }
156
322
  /**
157
- * A shaping call that was refused, carrying the limit it exceeded where there was one.
323
+ * One `w:num` — the thing a paragraph's `w:numId` actually names.
158
324
  *
159
- * Thrown rather than returned: unlike a missing font, a run that cannot be shaped has no sensible
160
- * fallback measurement, and continuing would lay text out at made-up widths.
325
+ * Points at an {@link AbstractNumDefinition} and may override any of its levels.
161
326
  */
162
- declare class HarfBuzzShapingError extends Error {
163
- readonly name = "HarfBuzzShapingError";
164
- readonly code: HarfBuzzShapingErrorCode;
165
- readonly limit?: number;
166
- readonly actual?: number;
167
- readonly diagnostic?: string;
168
- constructor(code: HarfBuzzShapingErrorCode, details?: {
169
- readonly limit?: number;
170
- readonly actual?: number;
171
- readonly diagnostic?: string;
172
- });
327
+ interface NumDefinition {
328
+ readonly numId: string;
329
+ readonly abstractNumId: string;
330
+ readonly overrides: ReadonlyMap<number, LevelOverride>;
173
331
  }
174
332
  /**
175
- * Resource budgets and cache sizes for one shaper. Every field optional.
333
+ * The bounded projection of `numbering.xml` that list layout resolves against.
176
334
  *
177
- * The caches exist because shaping is the expensive step: a face is opened once and reused, and
178
- * identical runs return their previous result. The caps exist because file-derived input decides
179
- * how much work is asked for.
335
+ * Projection ONLY — never a mutation or serialization authority. Hostile values are dropped or
336
+ * clamped, and a missing definition resolves to "no list" rather than a guess.
180
337
  */
181
- interface HarfBuzzTextShaperOptions {
182
- readonly maxFontBytes?: number;
183
- readonly maxInputUtf16?: number;
184
- readonly maxCodepoints?: number;
185
- readonly maxGlyphs?: number;
186
- readonly maxCachedFaces?: number;
187
- readonly maxCachedShapes?: number;
188
- readonly maxOutlineBytes?: number;
189
- readonly maxCachedOutlineBytes?: number;
190
- readonly maxShapedRunBytes?: number;
191
- readonly maxCachedShapeBytes?: number;
192
- readonly instrumentation?: HarfBuzzTextShaperInstrumentation;
193
- }
194
- /** One face-cache transition, for instrumentation. */
195
- interface HarfBuzzFaceCacheEvent {
196
- readonly kind: 'created' | 'hit' | 'evicted';
197
- readonly identity: string;
338
+ interface NumberingIndex {
339
+ readonly abstractNums: ReadonlyMap<string, AbstractNumDefinition>;
340
+ readonly nums: ReadonlyMap<string, NumDefinition>;
198
341
  }
199
342
  /**
200
- * Optional counters for the work a shaper is expected to do rarely.
343
+ * Build a numbering index from the root of a numbering part (`w:numbering`).
201
344
  *
202
- * Exists so tests can assert the ABSENCE of work — re-opening a face or re-copying its bytes per
203
- * shape call would not change any output, only make typing slow, which no rendering assertion
204
- * would catch.
345
+ * Empty / missing roots yield an empty index. Duplicate ids keep the first definition.
205
346
  */
206
- interface HarfBuzzTextShaperInstrumentation {
207
- readonly onFaceCacheEvent?: (event: HarfBuzzFaceCacheEvent) => void;
208
- readonly onShapeCacheEvent?: (event: HarfBuzzShapeCacheEvent) => void;
209
- readonly onByteCopy?: () => void;
210
- readonly onTableScan?: () => void;
211
- readonly onShapeCall?: () => void;
212
- readonly onOutlinePathCall?: () => void;
213
- readonly onOutlineCacheEvent?: (event: HarfBuzzOutlineCacheEvent) => void;
214
- }
215
- /** One shape-cache transition, for instrumentation. */
216
- interface HarfBuzzShapeCacheEvent {
217
- readonly kind: 'hit' | 'miss' | 'stored' | 'evicted' | 'skipped' | 'cleared';
218
- readonly retainedBytes: number;
219
- }
220
- /** One outline-cache transition, for instrumentation. */
221
- interface HarfBuzzOutlineCacheEvent {
222
- readonly kind: 'created' | 'hit' | 'evicted' | 'skipped' | 'cleared';
223
- readonly retainedBytes: number;
224
- }
225
347
  /**
226
- * A {@link TextShaper} backed by HarfBuzz, holding WASM resources.
348
+ * Project `numbering.xml` into the bounded index.
227
349
  *
228
- * {@link HarfBuzzTextShaper.dispose} is not optional housekeeping: the cached faces are WASM
229
- * allocations that garbage collection cannot reclaim, so a shaper outliving its editor leaks
230
- * until the page goes away.
350
+ * Every ceiling here exists because the input is file-derived: definition counts, override
351
+ * counts, indent magnitudes and style-link hop depth are all capped, and nothing from the file
352
+ * becomes a loop bound or an allocation size.
231
353
  */
232
- interface HarfBuzzTextShaper extends TextShaper {
233
- dispose(): void;
234
- }
235
- /** Structural sfnt validation that does not construct native HarfBuzz objects. */
236
- declare const harfBuzzFontValidator: FontByteValidator;
237
- /** Convert signed font units by an exact rational multiplier using the declared tie rule. */
238
- declare const roundFontUnitToFixedPoint: (fontUnits: number, denominator: number, numerator: number, mode: FixedPointRoundingMode) => FixedPoint;
354
+ declare function buildNumberingIndex(root: OoxmlElement | null | undefined): NumberingIndex;
355
+ /** Resolve the effective level for a `numId` + `ilvl`, applying overrides. */
239
356
  /**
240
- * Build a HarfBuzz-backed shaper.
357
+ * The effective level for one paragraph's numbering reference, after overrides and style links.
241
358
  *
242
- * Requires `initializeHarfBuzz()` to have resolved — shaping is synchronous, so the WASM runtime
243
- * must already be loaded. Dispose the result when the editor goes away, or its cached faces leak.
359
+ * Answers null for a reference the index cannot resolve — a paragraph naming a definition the
360
+ * file never declared is an unnumbered paragraph, not an error.
244
361
  */
245
- declare const createHarfBuzzTextShaper: (options?: HarfBuzzTextShaperOptions) => HarfBuzzTextShaper;
362
+ declare function resolveNumberingLevel(index: NumberingIndex, numId: string, ilvl: number): {
363
+ readonly abstractNumId: string;
364
+ readonly level: NumberingLevel;
365
+ readonly startOverride?: number;
366
+ } | null;
367
+ /** Empty index for tests / documents without numbering. */
368
+ declare const EMPTY_NUMBERING_INDEX: NumberingIndex;
246
369
 
247
370
  /**
248
- * UAX #9 embedding levels, one per UTF-16 code unit, plus the paragraph ranges they were resolved
249
- * within.
371
+ * A paragraph's list membership fully resolved: definition, level, marker text and geometry.
250
372
  *
251
- * The level is EXACT, not a direction: its parity gives direction, but the numeric value is what
252
- * reordering needs, and collapsing it early loses nested isolates.
373
+ * `markerText` is already expanded through the counter state, so it is the string a reader sees
374
+ * rather than the `w:lvlText` template.
253
375
  */
254
- interface BidiEmbeddingLevels {
255
- readonly levels: Uint8Array;
256
- readonly paragraphs: readonly {
257
- readonly start: number;
258
- readonly end: number;
259
- readonly level: number;
260
- }[];
376
+ interface ResolvedListItem {
377
+ readonly numId: string;
378
+ readonly ilvl: number;
379
+ readonly abstractNumId: string;
380
+ /** `w:numFmt` of the resolved level — `bullet` or a numbering format. */
381
+ readonly numFmt: string;
382
+ readonly markerText: string;
383
+ readonly markerAlign: ListMarkerAlign;
384
+ readonly suffix: ListSuffix;
385
+ /** Effective indent after merging level + paragraph indents, in points. */
386
+ readonly indent: NumberingLevelIndent;
387
+ readonly markerStyle: ResolvedRunStyle;
388
+ /** Fingerprint for layout cache keys (indent + level identity, not ordinal). */
389
+ readonly cacheToken: string;
261
390
  }
262
-
263
391
  /**
264
- * Which `w:rFonts` slot a character resolves its face through.
392
+ * Read `w:numPr` from cascaded paragraph-property nodes (last wins).
265
393
  *
266
- * OOXML gives a run up to four faces and picks between them by SCRIPT, so one run of mixed Latin
267
- * and CJK text uses two different fonts without saying so anywhere in its properties.
394
+ * Flat `OoxmlProperty[]` bags drop nested `ilvl`/`numId`, so this walks the tree nodes
395
+ * the same way borders and tabs do.
268
396
  */
269
- type FontSlot = 'ascii' | 'hAnsi' | 'eastAsia' | 'cs';
397
+ declare function readNumPr(paragraphPropertyNodes: readonly OoxmlNode[]): {
398
+ numId: string;
399
+ ilvl: number;
400
+ } | null;
270
401
  /**
271
- * A run of text sharing one script, direction and font slot — the unit handed to the shaper.
402
+ * Resolve `w:numStyleLink` delegation using the document's styles (§17.9.21).
272
403
  *
273
- * Shaping cannot span a script change: Arabic and Latin in one call would produce wrong joining
274
- * behaviour, so a run is itemized into these first.
404
+ * Without a style table there is nothing to follow, so the index is returned unchanged —
405
+ * and so it is when nothing delegates, which keeps layout cache identity.
275
406
  */
276
- interface ScriptItem {
277
- readonly from: number;
278
- readonly to: number;
279
- readonly direction: TextDirection;
280
- readonly bidiLevel: number;
281
- readonly script: 'Zyyy' | 'Latn' | 'Grek' | 'Cyrl' | 'Hani' | 'Hebr' | 'Arab' | 'Deva' | 'Beng' | 'Thai' | 'Khmr';
282
- readonly slot: FontSlot;
283
- }
407
+ declare function withNumberingStyleLinks(index: NumberingIndex, styleCascade: StyleCascadeTable | undefined): NumberingIndex;
284
408
  /**
285
- * A code point whose script this engine does not itemize.
409
+ * The effective indent of a list paragraph: STYLE, then the numbering LEVEL, then DIRECT.
286
410
  *
287
- * Carries the offending `codePoint` so the caller can report which character stopped it, rather
288
- * than failing anonymously somewhere in the middle of a paragraph.
411
+ * Word applies a level's `w:pPr/w:ind` between the paragraph style and the paragraph's own
412
+ * formatting, per attribute — and the ordering matters on real documents. A converted
413
+ * agreement numbers its `(a)` items with a level stating `left=1512 hanging=738` under a
414
+ * `ListParagraph` style stating `left=775 hanging=624`, and states only `hanging="737"` on
415
+ * the paragraph itself. Reading the flattened cascade as "the paragraph's indent" gave the
416
+ * STYLE's 775 to a level that had overridden it, so every lettered sub-item hung a full
417
+ * indent step to the left of where Word puts it.
418
+ *
419
+ * `inherited` is the cascade WITHOUT the paragraph's own `w:pPr` (defaults, table cell style,
420
+ * style chain); `direct` is that `w:pPr` alone.
289
421
  */
290
- declare class UnsupportedScriptError extends Error {
291
- readonly name = "UnsupportedScriptError";
292
- readonly code = "unsupportedScript";
293
- readonly codePoint: number;
294
- constructor(codePoint: number);
295
- }
422
+ declare function mergeListIndent(levelIndent: NumberingLevelIndent, inherited: readonly OoxmlProperty[], direct?: readonly OoxmlProperty[]): NumberingLevelIndent;
296
423
  /**
297
- * Split text into shapeable runs by script, bidi level and font slot.
424
+ * Collect paragraphs of a block list in document order, descending into tables.
298
425
  *
299
- * Consumes the bidi levels rather than re-deriving direction, so itemization and reordering agree
300
- * by construction instead of by two implementations happening to match.
426
+ * Caps nesting so a hostile nested-table document cannot recurse without bound.
301
427
  */
302
- declare function itemizeScriptFontSlots(text: string, paragraphOffset: number, embedding: BidiEmbeddingLevels): readonly ScriptItem[];
303
-
304
- /** UTF-16 boundaries that are both whole-grapheme and HarfBuzz cluster edges. */
305
- declare function shapedHorizontalBoundaries(run: ShapedRun): readonly number[];
428
+ declare function walkStoryParagraphs(blocks: readonly OoxmlElement[], maxTableDepth?: number): OoxmlElement[];
306
429
  /**
307
- * Whether an offset is a real caret position: both a grapheme boundary and a shaped cluster edge.
430
+ * Resolve every list paragraph in a story to a {@link ResolvedListItem}, keyed by node id.
308
431
  *
309
- * Both conditions, because they disagree. A ligature is one cluster spanning two graphemes, and
310
- * an offset inside it has no geometry the shaper can answer for — placing a caret there would
311
- * mean inventing an x coordinate.
432
+ * Non-list paragraphs are absent from the map. Hostile / missing numbering resolves inertly
433
+ * (paragraph omitted — laid out as ordinary text).
312
434
  */
313
- declare function isWholeGraphemeHorizontalBoundary(run: ShapedRun, utf16Offset: number): boolean;
314
- /** Every published shaped boundary has exact geometry from the shaping result. */
315
- declare function isGeometryTrustedCaretOffset(run: ShapedRun, utf16Offset: number): boolean;
435
+ declare function resolveStoryListItems(blocks: readonly OoxmlElement[], index: NumberingIndex, styleCascade: StyleCascadeTable | undefined, isFontAvailable?: (family: string) => boolean): ReadonlyMap<string, ResolvedListItem>;
316
436
  /**
317
- * Whether the distance from a line's start to an offset can be trusted as exact.
437
+ * Attach a full-story list-item map to layout options.
318
438
  *
319
- * Requires BOTH ends to be shaped boundaries: accumulating advances across an offset the shaper
320
- * cannot place would produce a caret x that drifts further along the line.
439
+ * Resolves once over `blocks` (body story including table cells) so counters continue across
440
+ * section boundaries. No-ops when numbering is absent.
321
441
  */
322
- declare function isCumulativeGeometryTrustedFromLineOrigin(run: ShapedRun, lineStartUtf16Offset: number, utf16Offset: number): boolean;
323
- /** Grapheme offsets corresponding to exact shaped boundaries. */
324
- declare function semanticHorizontalBoundaries(run: ShapedRun): readonly number[];
325
-
442
+ declare function withResolvedListItems<T extends {
443
+ readonly numberingIndex?: NumberingIndex;
444
+ readonly listItems?: ReadonlyMap<string, ResolvedListItem>;
445
+ readonly styleCascade?: StyleCascadeTable;
446
+ /**
447
+ * Host oracle for "is this font family really loaded". Supplied, a Symbol/Wingdings
448
+ * bullet keeps the file's own private-use codepoint so the authored typeface draws it;
449
+ * absent, it falls back to the Unicode equivalent rather than a tofu box.
450
+ */
451
+ readonly isFontAvailable?: (family: string) => boolean;
452
+ }>(options: T, blocks: readonly OoxmlElement[]): T & {
453
+ readonly numberingIndex: NumberingIndex;
454
+ readonly listItems?: ReadonlyMap<string, ResolvedListItem>;
455
+ };
326
456
  /**
327
- * One user-perceived character, and the UTF-16 range that encodes it.
457
+ * Horizontal marker box inside the hanging indent slot.
328
458
  *
329
- * The unit a caret moves by. An emoji with a skin-tone modifier is ONE segment spanning several
330
- * UTF-16 code units, so stepping by code unit would put the caret inside it.
459
+ * Coordinates are relative to the same origin as paragraph content (`indent.left` is the
460
+ * text start). Returns null when there is nothing to paint.
331
461
  */
332
- interface GraphemeSegment {
333
- readonly index: number;
334
- readonly text: string;
335
- readonly utf16From: number;
336
- readonly utf16To: number;
462
+ declare function listMarkerBox(item: ResolvedListItem, markerWidth: number, lineY: number, lineHeight: number): {
463
+ x: number;
464
+ y: number;
465
+ width: number;
466
+ height: number;
467
+ } | null;
468
+
469
+ /** Soft ceiling on `basedOn` chain length — enough for real templates, refuses hostile graphs. */
470
+ declare const MAX_STYLE_BASED_ON_DEPTH = 32;
471
+ /** Soft ceiling on style definitions read from one styles part. */
472
+ declare const MAX_STYLE_DEFINITIONS = 4096;
473
+ /** One `w:style` as the cascade reads it: its properties, and the style it is based on. */
474
+ interface StyleDefinition {
475
+ readonly styleId: string;
476
+ readonly type: string;
477
+ readonly basedOn: string | null;
478
+ readonly paragraphProperties: readonly OoxmlProperty[];
479
+ readonly runProperties: readonly OoxmlProperty[];
480
+ /** The style's `w:pPr` node, when present — needed for nested `w:pBdr`. */
481
+ readonly paragraphPropertiesNode: OoxmlElement | undefined;
482
+ /** The style's `w:tblPr`, for a `w:type="table"` style. */
483
+ readonly tablePropertiesNode: OoxmlElement | undefined;
484
+ /**
485
+ * `w:tblStylePr` conditional formats by `w:type` (`firstRow`, `band1Horz`, …).
486
+ *
487
+ * Word puts a table's real appearance here: `Table Grid` carries its grid in the style's
488
+ * `w:tblBorders`, and the banded/​header looks live in these. A document states only
489
+ * `<w:tblStyle w:val="TableGrid"/>`.
490
+ */
491
+ readonly conditionalTableFormats: ReadonlyMap<string, OoxmlElement>;
337
492
  }
338
493
  /**
339
- * The replaceable segmentation strategy.
494
+ * The whole styles part, indexed and ready to resolve against.
340
495
  *
341
- * An explicit seam rather than a direct `Intl.Segmenter` call, so tests can install a
342
- * deterministic boundary and a runtime without `Intl.Segmenter` can be given one.
496
+ * `cacheToken` is load-bearing: it folds into layout cache producers so breaks measured under one
497
+ * styles part are never reused under another.
343
498
  */
344
- interface GraphemeBoundary {
345
- segment(text: string): readonly GraphemeSegment[];
499
+ interface StyleCascadeTable {
500
+ /**
501
+ * Bounded fingerprint folded into layout cache producers so a different styles part cannot
502
+ * reuse breaks measured under another cascade. Computed once per table (FNV-1a hex).
503
+ */
504
+ readonly cacheToken: string;
505
+ readonly docDefaultsRun: readonly OoxmlProperty[];
506
+ readonly docDefaultsParagraph: readonly OoxmlProperty[];
507
+ readonly docDefaultsParagraphNode: OoxmlElement | undefined;
508
+ /** `w:style[@w:default='1'][@w:type='paragraph']` — last wins among defaults of that type. */
509
+ readonly defaultParagraphStyleId: string | null;
510
+ /** `w:style[@w:default='1'][@w:type='character']` — last wins among defaults of that type. */
511
+ readonly defaultCharacterStyleId: string | null;
512
+ /**
513
+ * The theme part's Latin typefaces, for `w:rFonts` theme references.
514
+ *
515
+ * Lives on the cascade because it is document-level style material with the same
516
+ * lifetime, and because every site that resolves run properties already holds this table.
517
+ */
518
+ readonly themeFonts: ThemeFonts;
519
+ readonly styles: ReadonlyMap<string, StyleDefinition>;
346
520
  }
347
- /** Invariant locale for deterministic cross-runtime grapheme boundaries. */
348
- declare const GRAPHEME_SEGMENTER_LOCALE: "und";
349
- /** Whether this runtime provides `Intl.Segmenter`, which the default boundary requires. */
350
- declare function isIntlSegmenterAvailable(): boolean;
351
521
  /**
352
- * The default boundary, over `Intl.Segmenter` at the invariant `und` locale.
522
+ * A paragraph's properties after the cascade, plus the same list WITHOUT its own `w:pPr`.
353
523
  *
354
- * Locale-invariant on purpose: grapheme boundaries must not vary with the user's locale, or the
355
- * same document would paginate differently for different readers.
524
+ * Both, because a writer needs to know what a paragraph INHERITS to decide whether setting a
525
+ * value is a change or a no-op — and writing back an inherited value freezes it into the
526
+ * paragraph as though the author had chosen it.
356
527
  */
357
- declare const intlGraphemeBoundary: GraphemeBoundary;
358
- /** Current boundary generation, for callers that cache segmentation-derived answers. */
359
- declare function graphemeBoundaryEpoch(): number;
528
+ interface CascadedParagraphFormatting {
529
+ /** Flat paragraph properties in cascade order (defaults → bases → style → direct). */
530
+ readonly paragraphProperties: readonly OoxmlProperty[];
531
+ /**
532
+ * The same list WITHOUT the paragraph's own `w:pPr` — everything it inherits.
533
+ *
534
+ * Numbering needs the two tiers apart: a level's `w:pPr/w:ind` outranks the style's and is
535
+ * outranked by the paragraph's own, and a flattened list cannot say which is which.
536
+ */
537
+ readonly inheritedParagraphProperties: readonly OoxmlProperty[];
538
+ /** Matching `w:pPr` nodes for nested border resolution. */
539
+ readonly paragraphPropertyNodes: readonly OoxmlNode[];
540
+ /**
541
+ * Inherited run properties for CONTENT runs (before direct run `rPr`).
542
+ *
543
+ * Does NOT include direct `w:pPr/w:rPr` — that formats the paragraph MARK only
544
+ * (ECMA-376 §17.3.1.36). Folding mark `w:sz` into content made BodyText runs with no
545
+ * direct size paint at the mark's size (Selection Notice "or" alternative → 6.5pt).
546
+ */
547
+ readonly runProperties: readonly OoxmlProperty[];
548
+ /**
549
+ * Content cascade plus direct `w:pPr/w:rPr` — empty-line metrics and last-line mark height.
550
+ */
551
+ readonly markRunProperties: readonly OoxmlProperty[];
552
+ /**
553
+ * The style this paragraph resolved to, or null when it names none and there is no
554
+ * document default. `w:contextualSpacing` compares neighbours by exactly this.
555
+ */
556
+ readonly styleId: string | null;
557
+ }
558
+ /** Accepted style ids only — over-long, control-bearing, or dangerous keys are dropped. */
559
+ declare function isValidStyleId(raw: string | undefined): raw is string;
360
560
  /**
361
- * Split text into graphemes through the active boundary.
561
+ * What a table style contributes to the paragraphs of ONE cell: the style's whole-table
562
+ * `w:pPr`/`w:rPr` followed by every `w:tblStylePr` the cell is under (17.7.6.6), weakest
563
+ * first in the caller's condition order (banding, column, row, corner).
362
564
  *
363
- * Memoized on the last texts seen, because paragraph layout asks about the same string
364
- * repeatedly and a full segmentation pass per call made layout quadratic in paragraph length.
565
+ * This is how Word makes a header row bold and centred while the document states nothing
566
+ * but `<w:tblStyle w:val="…"/>` on the table and plain runs in the cells.
365
567
  */
366
- declare function segmentGraphemes(text: string): readonly GraphemeSegment[];
568
+ interface TableCellStyleFormatting {
569
+ readonly paragraphProperties: readonly OoxmlProperty[];
570
+ /** Matching `w:pPr` nodes, for nested `w:pBdr` / `w:tabs` resolution. */
571
+ readonly paragraphPropertyNodes: readonly OoxmlElement[];
572
+ /** Inherited run properties for every run in the cell, before the paragraph style. */
573
+ readonly runProperties: readonly OoxmlProperty[];
574
+ }
367
575
  /**
368
- * Install a different segmentation strategy, invalidating the memo.
576
+ * Build a cascade table from a styles part root.
369
577
  *
370
- * For tests and for runtimes lacking `Intl.Segmenter`. Call {@link resetGraphemeBoundary} to
371
- * restore the default.
578
+ * Only direct `w:style` children of the root participate (bounded count). Duplicate
579
+ * `styleId` values keep the last definition, matching Word's reader for this fixture class.
580
+ * Default paragraph/character style ids track `w:default="1"` with the same last-wins rule.
372
581
  */
373
- declare function setGraphemeBoundary(boundary: GraphemeBoundary): void;
374
- /** Restore the default `Intl.Segmenter` boundary and clear the memo. */
375
- declare function resetGraphemeBoundary(): void;
376
- /** How many user-perceived characters the text holds. */
377
- declare function graphemeCount(text: string): number;
582
+ declare function buildStyleCascadeTable(stylesRoot: OoxmlElement | null, themeFonts?: ThemeFonts): StyleCascadeTable;
378
583
  /**
379
- * The grapheme index containing a UTF-16 offset. Clamps rather than throwing.
584
+ * Cascade paragraph + inherited run properties for one paragraph's direct `w:pPr`.
380
585
  *
381
- * Backed by a single-entry index cache: this runs once per character during paragraph layout, and
382
- * re-segmenting per call is what made a 20,000-character paragraph take minutes to open. One
383
- * entry rather than a map, because the keys are file-derived strings of unbounded size.
586
+ * Order: `docDefaults` → table style → `basedOn` ancestors → paragraph style → direct
587
+ * formatting, which is the style hierarchy of 17.7.2: a table style sits above the document
588
+ * defaults and below the paragraph style a cell paragraph names for itself.
589
+ * When `w:pStyle` is absent, the document's default paragraph style (`w:default="1"`) is used.
590
+ * Direct formatting is last so it overrides inherited values inside the existing resolvers.
384
591
  */
385
- declare function utf16OffsetToGrapheme(text: string, utf16Offset: number): number;
386
- /** The UTF-16 offset a grapheme index starts at. Clamps rather than throwing. */
387
- declare function graphemeOffsetToUtf16(text: string, graphemeOffset: number): number;
388
-
592
+ declare function cascadeParagraphFormatting(table: StyleCascadeTable, directPPr: OoxmlNode | undefined, tableCellStyle?: TableCellStyleFormatting): CascadedParagraphFormatting;
389
593
  /**
390
- * One word-segmentation span. `wordLike` separates words from the whitespace and punctuation
391
- * between them, which is what double-click selection needs to skip.
594
+ * Bottom border after cascade: a later `w:pBdr` replaces an earlier one; absence inherits.
595
+ * `nil`/`none` clear the edge via `paragraphBorders`.
392
596
  */
393
- interface WordSegment {
394
- readonly utf16From: number;
395
- readonly utf16To: number;
396
- readonly wordLike: boolean;
397
- }
597
+ declare function cascadedBottomBorder(paragraphPropertyNodes: readonly OoxmlNode[]): ParagraphBorderEdge | undefined;
398
598
  /**
399
- * The replaceable word-segmentation strategy.
599
+ * Merge inherited paragraph-style run props with a run's direct `rPr` (direct last).
400
600
  *
401
- * Falls back to a BOUNDED grapheme-safe splitter where `Intl.Segmenter` is absent — bounded
402
- * because the input is file-derived, and grapheme-safe so a fallback never splits inside an
403
- * emoji.
601
+ * When a cascade table is supplied, also resolves `w:rStyle` character styles (basedOn chain,
602
+ * cycle/depth capped). Runs without an explicit `rStyle` pick up the default character style
603
+ * (`w:default="1"`). Precedence: inherited → character style chain → direct formatting.
404
604
  */
405
- interface WordBoundary {
406
- segment(text: string): readonly WordSegment[];
407
- }
408
- /** Invariant locale for deterministic cross-runtime word boundaries. */
409
- declare const WORD_SEGMENTER_LOCALE: "und";
410
- /** Whether this runtime provides word-granularity `Intl.Segmenter`. */
411
- declare function isIntlWordSegmenterAvailable(): boolean;
412
- /** Intl.Segmenter word boundary for the invariant locale. */
413
- declare function createIntlWordBoundary(): WordBoundary;
414
- /** Bounded deterministic fallback — not full UAX #29 word conformance. */
415
- declare function boundedFallbackWordSegments(text: string): readonly WordSegment[];
416
- /** Explicit bounded fallback boundary (grapheme-safe, narrower than Intl). */
417
- declare function createBoundedFallbackWordBoundary(): WordBoundary;
418
- /** Injection points for {@link createDefaultWordBoundary}, so tests can force either path. */
419
- interface WordBoundaryResolverDeps {
420
- readonly isIntlAvailable?: () => boolean;
421
- readonly createIntlBoundary?: () => WordBoundary;
422
- readonly createFallbackBoundary?: () => WordBoundary;
605
+ declare function cascadeRunProperties(inheritedRunProperties: readonly OoxmlProperty[], directRunProperties: readonly OoxmlProperty[], table?: StyleCascadeTable): readonly OoxmlProperty[];
606
+ /** Everything the line breaker needs about one paragraph, already cascaded and converted. */
607
+ interface ParagraphLayoutInputs {
608
+ readonly props: OoxmlProperty[];
609
+ readonly indent: {
610
+ left: number;
611
+ right: number;
612
+ hanging: number;
613
+ firstLine: number;
614
+ };
615
+ readonly available: number;
616
+ readonly alignment: Alignment;
617
+ readonly spacing: ParagraphSpacing;
618
+ /** Resolved `w:line` / `w:lineRule`; single spacing where the cascade says nothing. */
619
+ readonly lineSpacing: ParagraphLineSpacing;
620
+ /** `w:contextualSpacing`: drop before/after between same-style neighbours. */
621
+ readonly contextualSpacing: boolean;
622
+ /** Resolved paragraph style id, for the `w:contextualSpacing` neighbour comparison. */
623
+ readonly styleId: string | null;
624
+ readonly bottomBorder: ParagraphBorderEdge | undefined;
625
+ /**
626
+ * Every `CT_PBdr` edge after cascade, not just the bottom one.
627
+ *
628
+ * `bottomBorder` stays alongside it because the fragment signature and the table flow
629
+ * read that field by name; this is the whole box, so a cell paragraph gets the same
630
+ * frame a body paragraph does.
631
+ */
632
+ readonly borders: ParagraphBorders;
633
+ /** Validated 6-hex paragraph shading fill from cascaded `w:pPr/w:shd`, absent for none. */
634
+ readonly shading: string | undefined;
635
+ readonly inheritedRunProperties: readonly OoxmlProperty[];
636
+ /**
637
+ * Paragraph-mark cascade (`inheritedRunProperties` + direct `w:pPr/w:rPr`).
638
+ * Empty-line sizing and last-line mark height — never content-run face.
639
+ */
640
+ readonly markRunProperties: readonly OoxmlProperty[];
641
+ /** Cascaded custom tab stops + default interval for paragraph-flow breaking. */
642
+ readonly tabStops: ResolvedTabStops;
643
+ /**
644
+ * Fingerprint folded into the paragraph layout cache key — nested `w:tabs` are absent
645
+ * from flat property bags, so style-inherited stops must be named explicitly.
646
+ */
647
+ readonly tabStopsCacheToken: string;
648
+ /** Resolved list marker inputs when the paragraph participates in numbering. */
649
+ readonly listItem?: ResolvedListItem;
423
650
  }
424
- /** Resolve production word boundary: Intl when available/construction succeeds, else bounded fallback. */
425
- declare function createDefaultWordBoundary(deps?: WordBoundaryResolverDeps): WordBoundary;
426
- /** Cached immutable production boundary (first resolved instance only). */
427
- declare function resolveDefaultWordBoundary(): WordBoundary;
428
- /** Split text into word segments through the given boundary, or the resolved default. */
429
- declare function segmentWords(text: string, boundary?: WordBoundary): readonly WordSegment[];
430
651
  /**
431
- * A word span expressed in GRAPHEME offsets rather than UTF-16 ones.
652
+ * Resolve every paragraph input semantic layout / table cells share: cascaded props when a
653
+ * style table is present, otherwise direct formatting only.
432
654
  *
433
- * What selection actually uses: a range whose ends are UTF-16 offsets could land inside a
434
- * grapheme, and selecting half an emoji is not a word.
655
+ * When `listItem` is provided, its merged level indent becomes the paragraph indent (list
656
+ * hanging / left from `numbering.xml`), which is what Word uses for fixture list paragraphs
657
+ * that author no direct `w:ind`.
658
+ *
659
+ * `tableCellStyle` carries what the enclosing table's style says about this cell's
660
+ * paragraphs; body paragraphs pass nothing.
661
+ *
662
+ * `inTableCell` is asked for separately because a cell paragraph may have no table style to
663
+ * inherit at all, and `w:beforeAutospacing` still needs to know it is in a cell.
435
664
  */
436
- interface GraphemeWordSegmentRecord {
437
- readonly graphemeFrom: number;
438
- readonly graphemeTo: number;
439
- readonly wordLike: boolean;
440
- }
441
- /** Map UTF-16 word segments to grapheme-safe half-open ranges within one paragraph. */
442
- declare function wordSegmentsToGraphemeRecords(text: string, segments: readonly WordSegment[]): readonly GraphemeWordSegmentRecord[];
665
+ declare function resolveParagraphLayoutInputs(paragraph: OoxmlElement, contentWidth: number, styleCascade: StyleCascadeTable | undefined, listItem?: ResolvedListItem, tableCellStyle?: TableCellStyleFormatting, inTableCell?: boolean): ParagraphLayoutInputs;
443
666
 
444
667
  /**
445
- * Strict hex fill: exactly six hex digits. Rejects `auto`, `nil`, and any non-hex payload
446
- * (CSS functions, URLs, short hex, theme tokens).
447
- */
448
- declare function resolveStrictHexFill(raw: string | undefined): string | undefined;
449
- /**
450
- * Resolve a `w:shd` attribute bag to a validated RRGGBB fill, or undefined.
451
- *
452
- * `w:themeFill` is a REFERENCE, and Word always writes the value it resolved to alongside
453
- * it: `<w:shd w:val="clear" w:fill="D9E2F3" w:themeFill="accent1" w:themeFillTint="33"/>`.
454
- * Reading `w:fill` in that case is not inventing a colour from the theme — it is reading
455
- * the colour the producer computed. Dropping the fill because a theme reference sat next
456
- * to it left every accent-shaded cell and paragraph unpainted.
457
- *
458
- * `val="nil"` clears shading. Pattern vals are not rendered; a valid solid `fill` still
459
- * paints as a clear fill until pattern support lands. A theme reference with no usable
460
- * `w:fill` still resolves to nothing rather than a guess.
461
- */
462
- declare function resolveOoxmlShadingFill(attributes: Readonly<Record<string, string>> | undefined): string | undefined;
463
- /** Read `w:shd` from a typed/generic element (table `tcPr`, nested `pPr`, …). */
464
- declare function shadingFillFromElement(shd: OoxmlElement | undefined): string | undefined;
465
- /**
466
- * Resolve paragraph shading from cascaded flat `w:pPr` properties.
467
- *
468
- * Later `w:shd` entries win (defaults → style → direct), matching spacing/border cascade.
469
- */
470
- declare function paragraphShading(props: readonly OoxmlProperty[]): string | undefined;
471
- /**
472
- * Page-content box for paragraph shading: union of this fragment's line boxes.
668
+ * What one cell side is before any ADJACENT cell has its say.
473
669
  *
474
- * Excludes collapsed before/after spacing and bottom-border extent so the painted band
475
- * matches Word's content-area fill (character shading height on a single line).
670
+ * - omitted → the table's own rule for that position (`tblBorders`, `insideH`/`insideV`)
671
+ * - edge → the cell wins outright; no weight fight with the table
672
+ * - none → an explicit `w:val="nil"`. Suppresses a matching table border on interior
673
+ * and perimeter sides alike, so a table whose cells all declare none paints borderless
674
+ * like Word even when `tblBorders` still carry `single` rules.
476
675
  */
477
- declare function paragraphShadingBox(lines: readonly {
478
- readonly box: LayoutBox;
479
- }[], x: number, width: number): LayoutBox | undefined;
676
+ declare function effectiveBorderSide(authored: TableBorderSide, tableSide: TableBorderSide, _options?: {
677
+ readonly interior?: boolean;
678
+ }): TableBorderSide;
480
679
 
680
+ /** Minimum stroke width for a compound (double/triple) band, in points. */
681
+ declare const COMPOUND_BORDER_MIN_STROKE_PT = 1;
682
+ /** Minimum gap between compound strokes, in points. */
683
+ declare const COMPOUND_BORDER_MIN_GAP_PT = 1;
481
684
  /**
482
- * Where one note is referenced from — what per-page and per-section restart rules are computed
483
- * against.
484
- */
485
- interface NoteReferenceSite {
486
- /** Stable note id (`w:id`). */
487
- readonly noteId: number;
488
- /** Section index of the reference (0-based). */
489
- readonly sectionIndex: number;
490
- /**
491
- * Page index of the reference when known (0-based). Required for `eachPage` restart;
492
- * when omitted, `eachPage` behaves like `continuous` for that site.
493
- */
494
- readonly pageIndex?: number;
495
- /** When true, consumes no automatic number (`customMarkFollows`). */
496
- readonly customMarkFollows?: boolean;
497
- }
498
- /**
499
- * The mark a note reference paints.
685
+ * How a multi-line border style (double, triple) is drawn: stroke width, gap, and total extent.
500
686
  *
501
- * `null` where `w:customMarkFollows` suppresses it: the document supplies its own glyph, and
502
- * painting an automatic number too would show the note twice.
687
+ * The band is CENTRED on the authored width, so a double border occupies the space Word gives it
688
+ * rather than growing the cell it surrounds.
503
689
  */
504
- interface NoteDisplayMark {
505
- readonly noteId: number;
506
- /** Formatted mark, or `null` when suppressed by customMarkFollows. */
507
- readonly mark: string | null;
508
- /** 1-based automatic sequence number when assigned; absent when suppressed. */
509
- readonly displayNumber?: number;
690
+ interface CompoundBorderMetrics {
691
+ readonly strokePt: number;
692
+ readonly gapPt: number;
693
+ readonly extentPt: number;
694
+ /** Centers the compound band on the authored width; negative extends outward. */
695
+ readonly insetPt: number;
510
696
  }
511
- type ResolvedNoteProperties = ResolvedFootnoteProperties | ResolvedEndnoteProperties;
512
697
  /**
513
- * Derive display marks for references of one note kind in document order.
514
- *
515
- * Restart rules:
516
- * - `continuous` — single sequence across the document from `numStart`
517
- * - `eachSect` — restart at `numStart` when `sectionIndex` changes
518
- * - `eachPage` — restart when `pageIndex` changes (falls back to continuous if unknown)
698
+ * Deterministic double stroke / gap / extent in layout points (scale-independent).
519
699
  *
520
- * IDs are stable; only display numbers change. Non-mutating.
521
- */
522
- declare function deriveNoteDisplayMarks(noteKind: NoteKind, references: readonly NoteReferenceSite[], properties: ResolvedNoteProperties): readonly NoteDisplayMark[];
523
- /**
524
- * Derive marks using per-reference-section resolved properties (`numFmt` / `numStart` /
525
- * `numRestart`). Restart rules consult each site's own section props.
700
+ * Thin authored widths inflate to a 1+1+1 point compound so a `w:sz="3"` double remains
701
+ * visible at paint scale 1 — matching Word's hairline-double floor.
526
702
  */
527
- declare function deriveNoteDisplayMarksResolved(_noteKind: NoteKind, references: readonly NoteReferenceSite[], resolveProps: (sectionIndex: number) => ResolvedNoteProperties): readonly NoteDisplayMark[];
528
- /** Map noteId → formatted mark for quick lookup (last site wins if duplicated). */
529
- declare function noteDisplayMarkMap(marks: readonly NoteDisplayMark[]): ReadonlyMap<number, string | null>;
703
+ declare function computeDoubleBorderMetricsPt(widthPt: number): CompoundBorderMetrics;
530
704
 
531
- /** A fingerprint over one paragraph's layout inputs. */
532
- type ParagraphLayoutKey = string;
533
- /** Cache counters, for asserting that incremental layout is actually reusing work. */
534
- interface LayoutCacheStats {
535
- readonly hits: number;
536
- readonly misses: number;
537
- readonly evictions: number;
538
- readonly size: number;
539
- }
540
705
  /**
541
- * The per-paragraph measurement cache.
706
+ * The exact HarfBuzz build this engine shapes against.
542
707
  *
543
- * Caches the BREAK only — where a paragraph's lines fall at a given width — never its placement.
544
- * An edit high in a document still repaginates everything below it, while paragraphs nobody
545
- * touched are never measured again.
708
+ * Pinned and verified at load: a runtime reporting a different version is REFUSED rather than
709
+ * used, because glyph positioning can change between releases and a cached measurement taken
710
+ * under one build must not be trusted under another.
546
711
  */
547
- interface ParagraphLayoutCache<T> {
548
- get(key: ParagraphLayoutKey): T | undefined;
549
- set(key: ParagraphLayoutKey, value: T): void;
550
- /** Drop entries for paragraphs a commit removed, so the cache cannot grow without bound. */
551
- retain(keys: ReadonlySet<ParagraphLayoutKey>): void;
552
- clear(): void;
553
- readonly stats: LayoutCacheStats;
554
- }
712
+ declare const HARFBUZZ_SHAPING_LIBRARY: VersionedShapingLibrary;
713
+ /** Load and verify the HarfBuzz WASM runtime without adding top-level await to import graphs. */
714
+ declare function initializeHarfBuzz(): Promise<void>;
715
+ /** Whether the WASM runtime is loaded and shaping can proceed synchronously. */
716
+ declare function isHarfBuzzInitialized(): boolean;
555
717
  /**
556
- * Everything that decides whether a cached paragraph break is still valid.
718
+ * Why shaping refused.
557
719
  *
558
- * `producer` is in the key because a font arriving after first paint changes every advance in the
559
- * document while no content changes — without it the cache would serve the pre-font layout
560
- * forever.
720
+ * Mostly RESOURCE limits, because every input here derives from a file: text length, codepoint
721
+ * count, glyph count and outline size are all attacker-influenced, and an unbounded shape call is
722
+ * a denial-of-service vector rather than a rendering bug.
561
723
  */
562
- interface ParagraphKeyInputs {
563
- readonly paragraph: OoxmlNode;
564
- readonly properties: readonly OoxmlProperty[];
565
- /** Available width, which decides where the lines break. */
566
- readonly width: number;
567
- /**
568
- * Who produced the measurements.
569
- *
570
- * Fonts loading after first paint change every advance while no content changes, so a
571
- * cache keyed on content alone would serve the pre-font layout forever.
572
- */
573
- readonly producer: string;
574
- /**
575
- * Inline drawing projection/resource epoch for this paragraph.
576
- *
577
- * Pending→ready/refused transitions and extent/hidden changes must invalidate breaks even
578
- * when paragraph text is unchanged.
579
- */
580
- readonly drawingToken?: string;
581
- /** Active page exclusion zones affecting this paragraph's break. */
582
- readonly exclusionToken?: string;
583
- }
724
+ type HarfBuzzShapingErrorCode = 'notInitialized' | 'fontOverLimit' | 'malformedFont' | 'textOverLimit' | 'codepointsOverLimit' | 'glyphOverLimit' | 'outlineOverLimit' | 'shapedRunOverLimit' | 'unsupportedVariationAxes' | 'unsupportedFallback' | 'unsupportedColorFont' | 'unsupportedNormalization' | 'invalidBidiLevel' | 'shapingLibraryMismatch' | 'disposed';
584
725
  /**
585
- * The cache key for one paragraph's measured break.
726
+ * A shaping call that was refused, carrying the limit it exceeded where there was one.
586
727
  *
587
- * Folds in the content, the available width, and the measurement producer. Anything that changes
588
- * where lines fall must be in here, or the cache serves a break taken under different conditions.
728
+ * Thrown rather than returned: unlike a missing font, a run that cannot be shaped has no sensible
729
+ * fallback measurement, and continuing would lay text out at made-up widths.
589
730
  */
590
- declare function paragraphLayoutKey(inputs: ParagraphKeyInputs): ParagraphLayoutKey;
591
- /** How large the paragraph cache grows before least-recently-used eviction. */
592
- interface ParagraphLayoutCacheOptions {
593
- /**
594
- * Entries retained before the least recently used are dropped.
595
- *
596
- * The default has to exceed a realistic document, or a full pass evicts exactly what the
597
- * next one needs and the cache costs more than it saves.
598
- */
599
- readonly maxEntries?: number;
731
+ declare class HarfBuzzShapingError extends Error {
732
+ readonly name = "HarfBuzzShapingError";
733
+ readonly code: HarfBuzzShapingErrorCode;
734
+ readonly limit?: number;
735
+ readonly actual?: number;
736
+ readonly diagnostic?: string;
737
+ constructor(code: HarfBuzzShapingErrorCode, details?: {
738
+ readonly limit?: number;
739
+ readonly actual?: number;
740
+ readonly diagnostic?: string;
741
+ });
600
742
  }
601
743
  /**
602
- * A bounded least-recently-used cache.
744
+ * Resource budgets and cache sizes for one shaper. Every field optional.
603
745
  *
604
- * Bounded because a long editing session touches far more paragraph states than a document
605
- * contains — every keystroke mints a new key for the paragraph being typed in — and an
606
- * unbounded cache would hold every intermediate state of the session.
746
+ * The caches exist because shaping is the expensive step: a face is opened once and reused, and
747
+ * identical runs return their previous result. The caps exist because file-derived input decides
748
+ * how much work is asked for.
607
749
  */
608
- declare function createParagraphLayoutCache<T>(options?: ParagraphLayoutCacheOptions): ParagraphLayoutCache<T>;
609
-
610
- /** Paint layer relative to body text — not the OOXML wrap element. */
611
- type DrawingPaintLayer = 'behind' | 'inFront';
612
- interface ExclusionZone {
613
- readonly drawingNodeId: string;
614
- readonly anchorParagraphId: string;
615
- /** UTF-16 model offset of the anchor atom — exclusions apply at/after this point in the paragraph. */
616
- readonly anchorModelStart: number;
617
- readonly sourceOrder: number;
618
- readonly paintLayer: DrawingPaintLayer;
619
- readonly relativeHeight: number;
620
- readonly allowOverlap: boolean;
621
- /** Owning column index in a multi-column section — 0 for single-column and HF stories. */
622
- readonly columnIndex: number;
623
- /** Resolved top edge in page-content coordinates after overlap displacement. */
624
- readonly y: number;
625
- readonly verticalBand: LayoutBox;
626
- readonly input: WrapExclusionInput;
750
+ interface HarfBuzzTextShaperOptions {
751
+ readonly maxFontBytes?: number;
752
+ readonly maxInputUtf16?: number;
753
+ readonly maxCodepoints?: number;
754
+ readonly maxGlyphs?: number;
755
+ readonly maxCachedFaces?: number;
756
+ readonly maxCachedShapes?: number;
757
+ readonly maxOutlineBytes?: number;
758
+ readonly maxCachedOutlineBytes?: number;
759
+ readonly maxShapedRunBytes?: number;
760
+ readonly maxCachedShapeBytes?: number;
761
+ readonly instrumentation?: HarfBuzzTextShaperInstrumentation;
627
762
  }
628
-
629
- interface PendingLine {
630
- readonly spans: StyleSpanRecord[];
631
- readonly drawings: InlineDrawingRecord[];
632
- readonly start: number;
633
- end: number;
634
- width: number;
635
- height: number;
636
- baseline: number;
637
- /**
638
- * Space ABOVE the glyph band inside {@link height}.
639
- *
640
- * Exact spacing can center the glyphs and move the baseline. Auto/atLeast spacing leaves
641
- * this at zero and puts its extra depth below instead.
642
- */
643
- leading: number;
644
- /**
645
- * Auto/atLeast line-spacing depth below the painted glyph band.
646
- *
647
- * Word lets this external depth cross the bottom text margin when the glyphs themselves
648
- * still fit. Pagination therefore budgets {@link height} minus this amount at a page
649
- * bottom, while paint keeps the full box and padding.
650
- */
651
- trailingSpacing: number;
652
- /** When true, layout must start a new page after this line is placed. */
653
- pageBreakAfter?: boolean;
654
- /** When true, layout must advance to the next authored section column. */
655
- columnBreakAfter?: boolean;
656
- /** Model ranges on this line covering deleted content; see {@link LineRecord.deletedRanges}. */
657
- deletedRanges?: readonly ModelRange[];
658
- /** Vertical gap inserted before this line to clear a topAndBottom exclusion band. */
659
- exclusionSkipBefore?: number;
763
+ /** One face-cache transition, for instrumentation. */
764
+ interface HarfBuzzFaceCacheEvent {
765
+ readonly kind: 'created' | 'hit' | 'evicted';
766
+ readonly identity: string;
660
767
  }
661
- /** Horizontal alignment of a paragraph (`w:jc`, ECMA-376 §17.3.1.13). */
662
- type Alignment = 'left' | 'center' | 'right' | 'both';
663
-
664
- /** Soft ceiling on abstractNum / num entries read from one part. */
665
- declare const MAX_NUMBERING_DEFINITIONS = 512;
666
- /** Soft ceiling on override entries per `w:num`. */
667
- declare const MAX_LVL_OVERRIDES = 9;
668
- /** `w:suff` — what separates a list marker from the text after it. */
669
- type ListSuffix = 'tab' | 'space' | 'nothing';
670
- /** `w:lvlJc` — how a list marker aligns within its own indent. */
671
- type ListMarkerAlign = 'left' | 'center' | 'right';
672
768
  /**
673
- * One level's indent, plus which parts the level actually AUTHORED.
769
+ * Optional counters for the work a shaper is expected to do rarely.
674
770
  *
675
- * The provenance matters: `w:ind` cascades per-attribute, so a level that authored only `left`
676
- * must not overwrite an inherited `hanging` with a synthesized zero.
771
+ * Exists so tests can assert the ABSENCE of work — re-opening a face or re-copying its bytes per
772
+ * shape call would not change any output, only make typing slow, which no rendering assertion
773
+ * would catch.
677
774
  */
678
- interface NumberingLevelIndent {
679
- readonly left: number;
680
- readonly right: number;
681
- readonly hanging: number;
682
- readonly firstLine: number;
683
- /**
684
- * Which of these the LEVEL actually authored.
685
- *
686
- * A level's `w:pPr/w:ind` sits between the paragraph style and direct formatting, so the
687
- * merge has to tell "the level says left = 0" from "the level says nothing about left and
688
- * the style's value stands". Absent (the default) reads as "says nothing", which is what a
689
- * hand-built level in a test means.
690
- */
691
- readonly stated?: {
692
- readonly left: boolean;
693
- readonly right: boolean;
694
- /** `w:hanging`/`w:firstLine` are one mutually exclusive slot (§17.3.1.10, §17.3.1.12). */
695
- readonly firstLineOffset: boolean;
696
- };
775
+ interface HarfBuzzTextShaperInstrumentation {
776
+ readonly onFaceCacheEvent?: (event: HarfBuzzFaceCacheEvent) => void;
777
+ readonly onShapeCacheEvent?: (event: HarfBuzzShapeCacheEvent) => void;
778
+ readonly onByteCopy?: () => void;
779
+ readonly onTableScan?: () => void;
780
+ readonly onShapeCall?: () => void;
781
+ readonly onOutlinePathCall?: () => void;
782
+ readonly onOutlineCacheEvent?: (event: HarfBuzzOutlineCacheEvent) => void;
697
783
  }
698
- /** One `w:lvl`: how this depth numbers, what its marker looks like, and how it indents. */
699
- interface NumberingLevel {
700
- readonly ilvl: number;
701
- readonly start: number;
702
- readonly numFmt: string;
703
- readonly lvlText: string;
704
- readonly lvlJc: ListMarkerAlign;
705
- readonly suff: ListSuffix;
706
- readonly indent: NumberingLevelIndent;
707
- /**
708
- * `w:lvlRestart` one-based trigger level, or `0` when the level never restarts.
709
- * Omitted in XML → restart when the previous level (or any earlier level) is used.
710
- */
711
- readonly lvlRestart?: number;
712
- /**
713
- * `w:isLgl` (§17.9.9): render EVERY level referenced by this level's `w:lvlText` in
714
- * decimal, whatever number format those levels declare for themselves.
715
- */
716
- readonly isLgl: boolean;
717
- /** Level `w:rPr` as flat properties (for marker face / vanish). */
718
- readonly runProperties: readonly OoxmlProperty[];
719
- /** True when level run props request vanish — marker must not paint. */
720
- readonly vanish: boolean;
784
+ /** One shape-cache transition, for instrumentation. */
785
+ interface HarfBuzzShapeCacheEvent {
786
+ readonly kind: 'hit' | 'miss' | 'stored' | 'evicted' | 'skipped' | 'cleared';
787
+ readonly retainedBytes: number;
788
+ }
789
+ /** One outline-cache transition, for instrumentation. */
790
+ interface HarfBuzzOutlineCacheEvent {
791
+ readonly kind: 'created' | 'hit' | 'evicted' | 'skipped' | 'cleared';
792
+ readonly retainedBytes: number;
721
793
  }
722
794
  /**
723
- * One `w:lvlOverride` on a `w:num`: a restart value, a replacement level, or both.
795
+ * A {@link TextShaper} backed by HarfBuzz, holding WASM resources.
724
796
  *
725
- * How two lists share an abstract definition while numbering independently.
797
+ * {@link HarfBuzzTextShaper.dispose} is not optional housekeeping: the cached faces are WASM
798
+ * allocations that garbage collection cannot reclaim, so a shaper outliving its editor leaks
799
+ * until the page goes away.
726
800
  */
727
- interface LevelOverride {
728
- readonly startOverride?: number;
729
- /** Full level replacement when `w:lvl` is present under the override. */
730
- readonly level?: NumberingLevel;
801
+ interface HarfBuzzTextShaper extends TextShaper {
802
+ dispose(): void;
731
803
  }
804
+ /** Structural sfnt validation that does not construct native HarfBuzz objects. */
805
+ declare const harfBuzzFontValidator: FontByteValidator;
806
+ /** Convert signed font units by an exact rational multiplier using the declared tie rule. */
807
+ declare const roundFontUnitToFixedPoint: (fontUnits: number, denominator: number, numerator: number, mode: FixedPointRoundingMode) => FixedPoint;
732
808
  /**
733
- * One `w:abstractNum` — the shape of a list, without being a list.
809
+ * Build a HarfBuzz-backed shaper.
734
810
  *
735
- * Never referenced by a paragraph directly. Paragraphs name a {@link NumDefinition}, which names
736
- * one of these, so several lists can share a definition and still count separately.
811
+ * Requires `initializeHarfBuzz()` to have resolved — shaping is synchronous, so the WASM runtime
812
+ * must already be loaded. Dispose the result when the editor goes away, or its cached faces leak.
737
813
  */
738
- interface AbstractNumDefinition {
739
- readonly abstractNumId: string;
740
- readonly levels: ReadonlyMap<number, NumberingLevel>;
741
- /**
742
- * `w:numStyleLink` (§17.9.21): this definition carries no levels of its own — the numbering
743
- * lives on the named style. Word follows the link; resolve it with
744
- * {@link resolveNumberingStyleLinks} before reading levels, or every paragraph on this
745
- * abstract renders with NO marker at all.
746
- */
747
- readonly numStyleLink?: string;
748
- /**
749
- * `w:styleLink` (§17.9.23): the definition side of the same pair — this abstract IS the
750
- * numbering of the named style. Kept so a link can be verified rather than assumed.
751
- */
752
- readonly styleLink?: string;
753
- }
814
+ declare const createHarfBuzzTextShaper: (options?: HarfBuzzTextShaperOptions) => HarfBuzzTextShaper;
815
+
754
816
  /**
755
- * One `w:num` — the thing a paragraph's `w:numId` actually names.
817
+ * UAX #9 embedding levels, one per UTF-16 code unit, plus the paragraph ranges they were resolved
818
+ * within.
756
819
  *
757
- * Points at an {@link AbstractNumDefinition} and may override any of its levels.
820
+ * The level is EXACT, not a direction: its parity gives direction, but the numeric value is what
821
+ * reordering needs, and collapsing it early loses nested isolates.
758
822
  */
759
- interface NumDefinition {
760
- readonly numId: string;
761
- readonly abstractNumId: string;
762
- readonly overrides: ReadonlyMap<number, LevelOverride>;
823
+ interface BidiEmbeddingLevels {
824
+ readonly levels: Uint8Array;
825
+ readonly paragraphs: readonly {
826
+ readonly start: number;
827
+ readonly end: number;
828
+ readonly level: number;
829
+ }[];
763
830
  }
831
+
764
832
  /**
765
- * The bounded projection of `numbering.xml` that list layout resolves against.
833
+ * Which `w:rFonts` slot a character resolves its face through.
766
834
  *
767
- * Projection ONLY — never a mutation or serialization authority. Hostile values are dropped or
768
- * clamped, and a missing definition resolves to "no list" rather than a guess.
835
+ * OOXML gives a run up to four faces and picks between them by SCRIPT, so one run of mixed Latin
836
+ * and CJK text uses two different fonts without saying so anywhere in its properties.
769
837
  */
770
- interface NumberingIndex {
771
- readonly abstractNums: ReadonlyMap<string, AbstractNumDefinition>;
772
- readonly nums: ReadonlyMap<string, NumDefinition>;
773
- }
838
+ type FontSlot = 'ascii' | 'hAnsi' | 'eastAsia' | 'cs';
774
839
  /**
775
- * Build a numbering index from the root of a numbering part (`w:numbering`).
840
+ * A run of text sharing one script, direction and font slot — the unit handed to the shaper.
776
841
  *
777
- * Empty / missing roots yield an empty index. Duplicate ids keep the first definition.
842
+ * Shaping cannot span a script change: Arabic and Latin in one call would produce wrong joining
843
+ * behaviour, so a run is itemized into these first.
778
844
  */
845
+ interface ScriptItem {
846
+ readonly from: number;
847
+ readonly to: number;
848
+ readonly direction: TextDirection;
849
+ readonly bidiLevel: number;
850
+ readonly script: 'Zyyy' | 'Latn' | 'Grek' | 'Cyrl' | 'Hani' | 'Hebr' | 'Arab' | 'Deva' | 'Beng' | 'Thai' | 'Khmr';
851
+ readonly slot: FontSlot;
852
+ }
779
853
  /**
780
- * Project `numbering.xml` into the bounded index.
854
+ * A code point whose script this engine does not itemize.
781
855
  *
782
- * Every ceiling here exists because the input is file-derived: definition counts, override
783
- * counts, indent magnitudes and style-link hop depth are all capped, and nothing from the file
784
- * becomes a loop bound or an allocation size.
856
+ * Carries the offending `codePoint` so the caller can report which character stopped it, rather
857
+ * than failing anonymously somewhere in the middle of a paragraph.
785
858
  */
786
- declare function buildNumberingIndex(root: OoxmlElement | null | undefined): NumberingIndex;
787
- /** Resolve the effective level for a `numId` + `ilvl`, applying overrides. */
859
+ declare class UnsupportedScriptError extends Error {
860
+ readonly name = "UnsupportedScriptError";
861
+ readonly code = "unsupportedScript";
862
+ readonly codePoint: number;
863
+ constructor(codePoint: number);
864
+ }
788
865
  /**
789
- * The effective level for one paragraph's numbering reference, after overrides and style links.
866
+ * Split text into shapeable runs by script, bidi level and font slot.
790
867
  *
791
- * Answers null for a reference the index cannot resolve — a paragraph naming a definition the
792
- * file never declared is an unnumbered paragraph, not an error.
868
+ * Consumes the bidi levels rather than re-deriving direction, so itemization and reordering agree
869
+ * by construction instead of by two implementations happening to match.
793
870
  */
794
- declare function resolveNumberingLevel(index: NumberingIndex, numId: string, ilvl: number): {
795
- readonly abstractNumId: string;
796
- readonly level: NumberingLevel;
797
- readonly startOverride?: number;
798
- } | null;
799
- /** Empty index for tests / documents without numbering. */
800
- declare const EMPTY_NUMBERING_INDEX: NumberingIndex;
871
+ declare function itemizeScriptFontSlots(text: string, paragraphOffset: number, embedding: BidiEmbeddingLevels): readonly ScriptItem[];
801
872
 
873
+ /** UTF-16 boundaries that are both whole-grapheme and HarfBuzz cluster edges. */
874
+ declare function shapedHorizontalBoundaries(run: ShapedRun): readonly number[];
802
875
  /**
803
- * A paragraph's list membership fully resolved: definition, level, marker text and geometry.
876
+ * Whether an offset is a real caret position: both a grapheme boundary and a shaped cluster edge.
804
877
  *
805
- * `markerText` is already expanded through the counter state, so it is the string a reader sees
806
- * rather than the `w:lvlText` template.
878
+ * Both conditions, because they disagree. A ligature is one cluster spanning two graphemes, and
879
+ * an offset inside it has no geometry the shaper can answer for — placing a caret there would
880
+ * mean inventing an x coordinate.
807
881
  */
808
- interface ResolvedListItem {
809
- readonly numId: string;
810
- readonly ilvl: number;
811
- readonly abstractNumId: string;
812
- /** `w:numFmt` of the resolved level — `bullet` or a numbering format. */
813
- readonly numFmt: string;
814
- readonly markerText: string;
815
- readonly markerAlign: ListMarkerAlign;
816
- readonly suffix: ListSuffix;
817
- /** Effective indent after merging level + paragraph indents, in points. */
818
- readonly indent: NumberingLevelIndent;
819
- readonly markerStyle: ResolvedRunStyle;
820
- /** Fingerprint for layout cache keys (indent + level identity, not ordinal). */
821
- readonly cacheToken: string;
822
- }
882
+ declare function isWholeGraphemeHorizontalBoundary(run: ShapedRun, utf16Offset: number): boolean;
883
+ /** Every published shaped boundary has exact geometry from the shaping result. */
884
+ declare function isGeometryTrustedCaretOffset(run: ShapedRun, utf16Offset: number): boolean;
823
885
  /**
824
- * Read `w:numPr` from cascaded paragraph-property nodes (last wins).
886
+ * Whether the distance from a line's start to an offset can be trusted as exact.
825
887
  *
826
- * Flat `OoxmlProperty[]` bags drop nested `ilvl`/`numId`, so this walks the tree nodes
827
- * the same way borders and tabs do.
888
+ * Requires BOTH ends to be shaped boundaries: accumulating advances across an offset the shaper
889
+ * cannot place would produce a caret x that drifts further along the line.
828
890
  */
829
- declare function readNumPr(paragraphPropertyNodes: readonly OoxmlNode[]): {
830
- numId: string;
831
- ilvl: number;
832
- } | null;
891
+ declare function isCumulativeGeometryTrustedFromLineOrigin(run: ShapedRun, lineStartUtf16Offset: number, utf16Offset: number): boolean;
892
+ /** Grapheme offsets corresponding to exact shaped boundaries. */
893
+ declare function semanticHorizontalBoundaries(run: ShapedRun): readonly number[];
894
+
833
895
  /**
834
- * Resolve `w:numStyleLink` delegation using the document's styles (§17.9.21).
896
+ * One user-perceived character, and the UTF-16 range that encodes it.
835
897
  *
836
- * Without a style table there is nothing to follow, so the index is returned unchanged —
837
- * and so it is when nothing delegates, which keeps layout cache identity.
898
+ * The unit a caret moves by. An emoji with a skin-tone modifier is ONE segment spanning several
899
+ * UTF-16 code units, so stepping by code unit would put the caret inside it.
838
900
  */
839
- declare function withNumberingStyleLinks(index: NumberingIndex, styleCascade: StyleCascadeTable | undefined): NumberingIndex;
901
+ interface GraphemeSegment {
902
+ readonly index: number;
903
+ readonly text: string;
904
+ readonly utf16From: number;
905
+ readonly utf16To: number;
906
+ }
840
907
  /**
841
- * The effective indent of a list paragraph: STYLE, then the numbering LEVEL, then DIRECT.
842
- *
843
- * Word applies a level's `w:pPr/w:ind` between the paragraph style and the paragraph's own
844
- * formatting, per attribute — and the ordering matters on real documents. A converted
845
- * agreement numbers its `(a)` items with a level stating `left=1512 hanging=738` under a
846
- * `ListParagraph` style stating `left=775 hanging=624`, and states only `hanging="737"` on
847
- * the paragraph itself. Reading the flattened cascade as "the paragraph's indent" gave the
848
- * STYLE's 775 to a level that had overridden it, so every lettered sub-item hung a full
849
- * indent step to the left of where Word puts it.
908
+ * The replaceable segmentation strategy.
850
909
  *
851
- * `inherited` is the cascade WITHOUT the paragraph's own `w:pPr` (defaults, table cell style,
852
- * style chain); `direct` is that `w:pPr` alone.
910
+ * An explicit seam rather than a direct `Intl.Segmenter` call, so tests can install a
911
+ * deterministic boundary and a runtime without `Intl.Segmenter` can be given one.
853
912
  */
854
- declare function mergeListIndent(levelIndent: NumberingLevelIndent, inherited: readonly OoxmlProperty[], direct?: readonly OoxmlProperty[]): NumberingLevelIndent;
913
+ interface GraphemeBoundary {
914
+ segment(text: string): readonly GraphemeSegment[];
915
+ }
916
+ /** Invariant locale for deterministic cross-runtime grapheme boundaries. */
917
+ declare const GRAPHEME_SEGMENTER_LOCALE: "und";
918
+ /** Whether this runtime provides `Intl.Segmenter`, which the default boundary requires. */
919
+ declare function isIntlSegmenterAvailable(): boolean;
855
920
  /**
856
- * Collect paragraphs of a block list in document order, descending into tables.
921
+ * The default boundary, over `Intl.Segmenter` at the invariant `und` locale.
857
922
  *
858
- * Caps nesting so a hostile nested-table document cannot recurse without bound.
923
+ * Locale-invariant on purpose: grapheme boundaries must not vary with the user's locale, or the
924
+ * same document would paginate differently for different readers.
859
925
  */
860
- declare function walkStoryParagraphs(blocks: readonly OoxmlElement[], maxTableDepth?: number): OoxmlElement[];
926
+ declare const intlGraphemeBoundary: GraphemeBoundary;
927
+ /** Current boundary generation, for callers that cache segmentation-derived answers. */
928
+ declare function graphemeBoundaryEpoch(): number;
861
929
  /**
862
- * Resolve every list paragraph in a story to a {@link ResolvedListItem}, keyed by node id.
930
+ * Split text into graphemes through the active boundary.
863
931
  *
864
- * Non-list paragraphs are absent from the map. Hostile / missing numbering resolves inertly
865
- * (paragraph omitted — laid out as ordinary text).
932
+ * Memoized on the last texts seen, because paragraph layout asks about the same string
933
+ * repeatedly and a full segmentation pass per call made layout quadratic in paragraph length.
866
934
  */
867
- declare function resolveStoryListItems(blocks: readonly OoxmlElement[], index: NumberingIndex, styleCascade: StyleCascadeTable | undefined, isFontAvailable?: (family: string) => boolean): ReadonlyMap<string, ResolvedListItem>;
935
+ declare function segmentGraphemes(text: string): readonly GraphemeSegment[];
868
936
  /**
869
- * Attach a full-story list-item map to layout options.
937
+ * Install a different segmentation strategy, invalidating the memo.
870
938
  *
871
- * Resolves once over `blocks` (body story including table cells) so counters continue across
872
- * section boundaries. No-ops when numbering is absent.
939
+ * For tests and for runtimes lacking `Intl.Segmenter`. Call {@link resetGraphemeBoundary} to
940
+ * restore the default.
873
941
  */
874
- declare function withResolvedListItems<T extends {
875
- readonly numberingIndex?: NumberingIndex;
876
- readonly listItems?: ReadonlyMap<string, ResolvedListItem>;
877
- readonly styleCascade?: StyleCascadeTable;
878
- /**
879
- * Host oracle for "is this font family really loaded". Supplied, a Symbol/Wingdings
880
- * bullet keeps the file's own private-use codepoint so the authored typeface draws it;
881
- * absent, it falls back to the Unicode equivalent rather than a tofu box.
882
- */
883
- readonly isFontAvailable?: (family: string) => boolean;
884
- }>(options: T, blocks: readonly OoxmlElement[]): T & {
885
- readonly numberingIndex: NumberingIndex;
886
- readonly listItems?: ReadonlyMap<string, ResolvedListItem>;
887
- };
942
+ declare function setGraphemeBoundary(boundary: GraphemeBoundary): void;
943
+ /** Restore the default `Intl.Segmenter` boundary and clear the memo. */
944
+ declare function resetGraphemeBoundary(): void;
945
+ /** How many user-perceived characters the text holds. */
946
+ declare function graphemeCount(text: string): number;
888
947
  /**
889
- * Horizontal marker box inside the hanging indent slot.
948
+ * The grapheme index containing a UTF-16 offset. Clamps rather than throwing.
890
949
  *
891
- * Coordinates are relative to the same origin as paragraph content (`indent.left` is the
892
- * text start). Returns null when there is nothing to paint.
893
- */
894
- declare function listMarkerBox(item: ResolvedListItem, markerWidth: number, lineY: number, lineHeight: number): {
895
- x: number;
896
- y: number;
897
- width: number;
898
- height: number;
899
- } | null;
900
-
901
- /** Soft ceiling on `basedOn` chain length — enough for real templates, refuses hostile graphs. */
902
- declare const MAX_STYLE_BASED_ON_DEPTH = 32;
903
- /** Soft ceiling on style definitions read from one styles part. */
904
- declare const MAX_STYLE_DEFINITIONS = 4096;
905
- /** One `w:style` as the cascade reads it: its properties, and the style it is based on. */
906
- interface StyleDefinition {
907
- readonly styleId: string;
908
- readonly type: string;
909
- readonly basedOn: string | null;
910
- readonly paragraphProperties: readonly OoxmlProperty[];
911
- readonly runProperties: readonly OoxmlProperty[];
912
- /** The style's `w:pPr` node, when present — needed for nested `w:pBdr`. */
913
- readonly paragraphPropertiesNode: OoxmlElement | undefined;
914
- /** The style's `w:tblPr`, for a `w:type="table"` style. */
915
- readonly tablePropertiesNode: OoxmlElement | undefined;
916
- /**
917
- * `w:tblStylePr` conditional formats by `w:type` (`firstRow`, `band1Horz`, …).
918
- *
919
- * Word puts a table's real appearance here: `Table Grid` carries its grid in the style's
920
- * `w:tblBorders`, and the banded/​header looks live in these. A document states only
921
- * `<w:tblStyle w:val="TableGrid"/>`.
922
- */
923
- readonly conditionalTableFormats: ReadonlyMap<string, OoxmlElement>;
924
- }
950
+ * Backed by a single-entry index cache: this runs once per character during paragraph layout, and
951
+ * re-segmenting per call is what made a 20,000-character paragraph take minutes to open. One
952
+ * entry rather than a map, because the keys are file-derived strings of unbounded size.
953
+ */
954
+ declare function utf16OffsetToGrapheme(text: string, utf16Offset: number): number;
955
+ /** The UTF-16 offset a grapheme index starts at. Clamps rather than throwing. */
956
+ declare function graphemeOffsetToUtf16(text: string, graphemeOffset: number): number;
957
+
925
958
  /**
926
- * The whole styles part, indexed and ready to resolve against.
927
- *
928
- * `cacheToken` is load-bearing: it folds into layout cache producers so breaks measured under one
929
- * styles part are never reused under another.
959
+ * One word-segmentation span. `wordLike` separates words from the whitespace and punctuation
960
+ * between them, which is what double-click selection needs to skip.
930
961
  */
931
- interface StyleCascadeTable {
932
- /**
933
- * Bounded fingerprint folded into layout cache producers so a different styles part cannot
934
- * reuse breaks measured under another cascade. Computed once per table (FNV-1a hex).
935
- */
936
- readonly cacheToken: string;
937
- readonly docDefaultsRun: readonly OoxmlProperty[];
938
- readonly docDefaultsParagraph: readonly OoxmlProperty[];
939
- readonly docDefaultsParagraphNode: OoxmlElement | undefined;
940
- /** `w:style[@w:default='1'][@w:type='paragraph']` — last wins among defaults of that type. */
941
- readonly defaultParagraphStyleId: string | null;
942
- /** `w:style[@w:default='1'][@w:type='character']` — last wins among defaults of that type. */
943
- readonly defaultCharacterStyleId: string | null;
944
- /**
945
- * The theme part's Latin typefaces, for `w:rFonts` theme references.
946
- *
947
- * Lives on the cascade because it is document-level style material with the same
948
- * lifetime, and because every site that resolves run properties already holds this table.
949
- */
950
- readonly themeFonts: ThemeFonts;
951
- readonly styles: ReadonlyMap<string, StyleDefinition>;
962
+ interface WordSegment {
963
+ readonly utf16From: number;
964
+ readonly utf16To: number;
965
+ readonly wordLike: boolean;
952
966
  }
953
967
  /**
954
- * A paragraph's properties after the cascade, plus the same list WITHOUT its own `w:pPr`.
968
+ * The replaceable word-segmentation strategy.
955
969
  *
956
- * Both, because a writer needs to know what a paragraph INHERITS to decide whether setting a
957
- * value is a change or a no-op — and writing back an inherited value freezes it into the
958
- * paragraph as though the author had chosen it.
970
+ * Falls back to a BOUNDED grapheme-safe splitter where `Intl.Segmenter` is absent — bounded
971
+ * because the input is file-derived, and grapheme-safe so a fallback never splits inside an
972
+ * emoji.
959
973
  */
960
- interface CascadedParagraphFormatting {
961
- /** Flat paragraph properties in cascade order (defaults → bases → style → direct). */
962
- readonly paragraphProperties: readonly OoxmlProperty[];
963
- /**
964
- * The same list WITHOUT the paragraph's own `w:pPr` — everything it inherits.
965
- *
966
- * Numbering needs the two tiers apart: a level's `w:pPr/w:ind` outranks the style's and is
967
- * outranked by the paragraph's own, and a flattened list cannot say which is which.
968
- */
969
- readonly inheritedParagraphProperties: readonly OoxmlProperty[];
970
- /** Matching `w:pPr` nodes for nested border resolution. */
971
- readonly paragraphPropertyNodes: readonly OoxmlNode[];
972
- /** Inherited run properties for every run in the paragraph (before direct run `rPr`). */
973
- readonly runProperties: readonly OoxmlProperty[];
974
- /**
975
- * The style this paragraph resolved to, or null when it names none and there is no
976
- * document default. `w:contextualSpacing` compares neighbours by exactly this.
977
- */
978
- readonly styleId: string | null;
974
+ interface WordBoundary {
975
+ segment(text: string): readonly WordSegment[];
979
976
  }
980
- /** Accepted style ids only — over-long, control-bearing, or dangerous keys are dropped. */
981
- declare function isValidStyleId(raw: string | undefined): raw is string;
977
+ /** Invariant locale for deterministic cross-runtime word boundaries. */
978
+ declare const WORD_SEGMENTER_LOCALE: "und";
979
+ /** Whether this runtime provides word-granularity `Intl.Segmenter`. */
980
+ declare function isIntlWordSegmenterAvailable(): boolean;
981
+ /** Intl.Segmenter word boundary for the invariant locale. */
982
+ declare function createIntlWordBoundary(): WordBoundary;
983
+ /** Bounded deterministic fallback — not full UAX #29 word conformance. */
984
+ declare function boundedFallbackWordSegments(text: string): readonly WordSegment[];
985
+ /** Explicit bounded fallback boundary (grapheme-safe, narrower than Intl). */
986
+ declare function createBoundedFallbackWordBoundary(): WordBoundary;
987
+ /** Injection points for {@link createDefaultWordBoundary}, so tests can force either path. */
988
+ interface WordBoundaryResolverDeps {
989
+ readonly isIntlAvailable?: () => boolean;
990
+ readonly createIntlBoundary?: () => WordBoundary;
991
+ readonly createFallbackBoundary?: () => WordBoundary;
992
+ }
993
+ /** Resolve production word boundary: Intl when available/construction succeeds, else bounded fallback. */
994
+ declare function createDefaultWordBoundary(deps?: WordBoundaryResolverDeps): WordBoundary;
995
+ /** Cached immutable production boundary (first resolved instance only). */
996
+ declare function resolveDefaultWordBoundary(): WordBoundary;
997
+ /** Split text into word segments through the given boundary, or the resolved default. */
998
+ declare function segmentWords(text: string, boundary?: WordBoundary): readonly WordSegment[];
982
999
  /**
983
- * What a table style contributes to the paragraphs of ONE cell: the style's whole-table
984
- * `w:pPr`/`w:rPr` followed by every `w:tblStylePr` the cell is under (17.7.6.6), weakest
985
- * first in the caller's condition order (banding, column, row, corner).
1000
+ * A word span expressed in GRAPHEME offsets rather than UTF-16 ones.
986
1001
  *
987
- * This is how Word makes a header row bold and centred while the document states nothing
988
- * but `<w:tblStyle w:val="…"/>` on the table and plain runs in the cells.
1002
+ * What selection actually uses: a range whose ends are UTF-16 offsets could land inside a
1003
+ * grapheme, and selecting half an emoji is not a word.
989
1004
  */
990
- interface TableCellStyleFormatting {
991
- readonly paragraphProperties: readonly OoxmlProperty[];
992
- /** Matching `w:pPr` nodes, for nested `w:pBdr` / `w:tabs` resolution. */
993
- readonly paragraphPropertyNodes: readonly OoxmlElement[];
994
- /** Inherited run properties for every run in the cell, before the paragraph style. */
995
- readonly runProperties: readonly OoxmlProperty[];
1005
+ interface GraphemeWordSegmentRecord {
1006
+ readonly graphemeFrom: number;
1007
+ readonly graphemeTo: number;
1008
+ readonly wordLike: boolean;
996
1009
  }
1010
+ /** Map UTF-16 word segments to grapheme-safe half-open ranges within one paragraph. */
1011
+ declare function wordSegmentsToGraphemeRecords(text: string, segments: readonly WordSegment[]): readonly GraphemeWordSegmentRecord[];
1012
+
997
1013
  /**
998
- * Build a cascade table from a styles part root.
999
- *
1000
- * Only direct `w:style` children of the root participate (bounded count). Duplicate
1001
- * `styleId` values keep the last definition, matching Word's reader for this fixture class.
1002
- * Default paragraph/character style ids track `w:default="1"` with the same last-wins rule.
1014
+ * Strict hex fill: exactly six hex digits. Rejects `auto`, `nil`, and any non-hex payload
1015
+ * (CSS functions, URLs, short hex, theme tokens).
1003
1016
  */
1004
- declare function buildStyleCascadeTable(stylesRoot: OoxmlElement | null, themeFonts?: ThemeFonts): StyleCascadeTable;
1017
+ declare function resolveStrictHexFill(raw: string | undefined): string | undefined;
1005
1018
  /**
1006
- * Cascade paragraph + inherited run properties for one paragraph's direct `w:pPr`.
1019
+ * Resolve a `w:shd` attribute bag to a validated RRGGBB fill, or undefined.
1007
1020
  *
1008
- * Order: `docDefaults` → table style → `basedOn` ancestors → paragraph style → direct
1009
- * formatting, which is the style hierarchy of 17.7.2: a table style sits above the document
1010
- * defaults and below the paragraph style a cell paragraph names for itself.
1011
- * When `w:pStyle` is absent, the document's default paragraph style (`w:default="1"`) is used.
1012
- * Direct formatting is last so it overrides inherited values inside the existing resolvers.
1021
+ * `w:themeFill` is a REFERENCE, and Word always writes the value it resolved to alongside
1022
+ * it: `<w:shd w:val="clear" w:fill="D9E2F3" w:themeFill="accent1" w:themeFillTint="33"/>`.
1023
+ * Reading `w:fill` in that case is not inventing a colour from the theme — it is reading
1024
+ * the colour the producer computed. Dropping the fill because a theme reference sat next
1025
+ * to it left every accent-shaded cell and paragraph unpainted.
1026
+ *
1027
+ * `val="nil"` clears shading. Pattern vals are not rendered; a valid solid `fill` still
1028
+ * paints as a clear fill until pattern support lands. A theme reference with no usable
1029
+ * `w:fill` still resolves to nothing rather than a guess.
1013
1030
  */
1014
- declare function cascadeParagraphFormatting(table: StyleCascadeTable, directPPr: OoxmlNode | undefined, tableCellStyle?: TableCellStyleFormatting): CascadedParagraphFormatting;
1031
+ declare function resolveOoxmlShadingFill(attributes: Readonly<Record<string, string>> | undefined): string | undefined;
1032
+ /** Read `w:shd` from a typed/generic element (table `tcPr`, nested `pPr`, …). */
1033
+ declare function shadingFillFromElement(shd: OoxmlElement | undefined): string | undefined;
1015
1034
  /**
1016
- * Bottom border after cascade: a later `w:pBdr` replaces an earlier one; absence inherits.
1017
- * `nil`/`none` clear the edge via `paragraphBorders`.
1035
+ * Resolve paragraph shading from cascaded flat `w:pPr` properties.
1036
+ *
1037
+ * Later `w:shd` entries win (defaults → style → direct), matching spacing/border cascade.
1018
1038
  */
1019
- declare function cascadedBottomBorder(paragraphPropertyNodes: readonly OoxmlNode[]): ParagraphBorderEdge | undefined;
1039
+ declare function paragraphShading(props: readonly OoxmlProperty[]): string | undefined;
1020
1040
  /**
1021
- * Merge inherited paragraph-style run props with a run's direct `rPr` (direct last).
1041
+ * Page-content box for paragraph shading: union of this fragment's line boxes.
1022
1042
  *
1023
- * When a cascade table is supplied, also resolves `w:rStyle` character styles (basedOn chain,
1024
- * cycle/depth capped). Runs without an explicit `rStyle` pick up the default character style
1025
- * (`w:default="1"`). Precedence: inherited → character style chain → direct formatting.
1043
+ * Excludes collapsed before/after spacing and bottom-border extent so the painted band
1044
+ * matches Word's content-area fill (character shading height on a single line).
1026
1045
  */
1027
- declare function cascadeRunProperties(inheritedRunProperties: readonly OoxmlProperty[], directRunProperties: readonly OoxmlProperty[], table?: StyleCascadeTable): readonly OoxmlProperty[];
1028
- /** Everything the line breaker needs about one paragraph, already cascaded and converted. */
1029
- interface ParagraphLayoutInputs {
1030
- readonly props: OoxmlProperty[];
1031
- readonly indent: {
1032
- left: number;
1033
- right: number;
1034
- hanging: number;
1035
- firstLine: number;
1036
- };
1037
- readonly available: number;
1038
- readonly alignment: Alignment;
1039
- readonly spacing: ParagraphSpacing;
1040
- /** Resolved `w:line` / `w:lineRule`; single spacing where the cascade says nothing. */
1041
- readonly lineSpacing: ParagraphLineSpacing;
1042
- /** `w:contextualSpacing`: drop before/after between same-style neighbours. */
1043
- readonly contextualSpacing: boolean;
1044
- /** Resolved paragraph style id, for the `w:contextualSpacing` neighbour comparison. */
1045
- readonly styleId: string | null;
1046
- readonly bottomBorder: ParagraphBorderEdge | undefined;
1047
- /**
1048
- * Every `CT_PBdr` edge after cascade, not just the bottom one.
1049
- *
1050
- * `bottomBorder` stays alongside it because the fragment signature and the table flow
1051
- * read that field by name; this is the whole box, so a cell paragraph gets the same
1052
- * frame a body paragraph does.
1053
- */
1054
- readonly borders: ParagraphBorders;
1055
- /** Validated 6-hex paragraph shading fill from cascaded `w:pPr/w:shd`, absent for none. */
1056
- readonly shading: string | undefined;
1057
- readonly inheritedRunProperties: readonly OoxmlProperty[];
1058
- /** Cascaded custom tab stops + default interval for paragraph-flow breaking. */
1059
- readonly tabStops: ResolvedTabStops;
1046
+ declare function paragraphShadingBox(lines: readonly {
1047
+ readonly box: LayoutBox;
1048
+ }[], x: number, width: number): LayoutBox | undefined;
1049
+
1050
+ /**
1051
+ * Where one note is referenced from — what per-page and per-section restart rules are computed
1052
+ * against.
1053
+ */
1054
+ interface NoteReferenceSite {
1055
+ /** Stable note id (`w:id`). */
1056
+ readonly noteId: number;
1057
+ /** Section index of the reference (0-based). */
1058
+ readonly sectionIndex: number;
1060
1059
  /**
1061
- * Fingerprint folded into the paragraph layout cache key — nested `w:tabs` are absent
1062
- * from flat property bags, so style-inherited stops must be named explicitly.
1060
+ * Page index of the reference when known (0-based). Required for `eachPage` restart;
1061
+ * when omitted, `eachPage` behaves like `continuous` for that site.
1063
1062
  */
1064
- readonly tabStopsCacheToken: string;
1065
- /** Resolved list marker inputs when the paragraph participates in numbering. */
1066
- readonly listItem?: ResolvedListItem;
1063
+ readonly pageIndex?: number;
1064
+ /** When true, consumes no automatic number (`customMarkFollows`). */
1065
+ readonly customMarkFollows?: boolean;
1067
1066
  }
1068
1067
  /**
1069
- * Resolve every paragraph input semantic layout / table cells share: cascaded props when a
1070
- * style table is present, otherwise direct formatting only.
1068
+ * The mark a note reference paints.
1071
1069
  *
1072
- * When `listItem` is provided, its merged level indent becomes the paragraph indent (list
1073
- * hanging / left from `numbering.xml`), which is what Word uses for fixture list paragraphs
1074
- * that author no direct `w:ind`.
1070
+ * `null` where `w:customMarkFollows` suppresses it: the document supplies its own glyph, and
1071
+ * painting an automatic number too would show the note twice.
1072
+ */
1073
+ interface NoteDisplayMark {
1074
+ readonly noteId: number;
1075
+ /** Formatted mark, or `null` when suppressed by customMarkFollows. */
1076
+ readonly mark: string | null;
1077
+ /** 1-based automatic sequence number when assigned; absent when suppressed. */
1078
+ readonly displayNumber?: number;
1079
+ }
1080
+ type ResolvedNoteProperties = ResolvedFootnoteProperties | ResolvedEndnoteProperties;
1081
+ /**
1082
+ * Derive display marks for references of one note kind in document order.
1075
1083
  *
1076
- * `tableCellStyle` carries what the enclosing table's style says about this cell's
1077
- * paragraphs; body paragraphs pass nothing.
1084
+ * Restart rules:
1085
+ * - `continuous` — single sequence across the document from `numStart`
1086
+ * - `eachSect` — restart at `numStart` when `sectionIndex` changes
1087
+ * - `eachPage` — restart when `pageIndex` changes (falls back to continuous if unknown)
1088
+ *
1089
+ * IDs are stable; only display numbers change. Non-mutating.
1090
+ */
1091
+ declare function deriveNoteDisplayMarks(noteKind: NoteKind, references: readonly NoteReferenceSite[], properties: ResolvedNoteProperties): readonly NoteDisplayMark[];
1092
+ /**
1093
+ * Derive marks using per-reference-section resolved properties (`numFmt` / `numStart` /
1094
+ * `numRestart`). Restart rules consult each site's own section props.
1078
1095
  */
1079
- declare function resolveParagraphLayoutInputs(paragraph: OoxmlElement, contentWidth: number, styleCascade: StyleCascadeTable | undefined, listItem?: ResolvedListItem, tableCellStyle?: TableCellStyleFormatting): ParagraphLayoutInputs;
1096
+ declare function deriveNoteDisplayMarksResolved(_noteKind: NoteKind, references: readonly NoteReferenceSite[], resolveProps: (sectionIndex: number) => ResolvedNoteProperties): readonly NoteDisplayMark[];
1097
+ /** Map noteId → formatted mark for quick lookup (last site wins if duplicated). */
1098
+ declare function noteDisplayMarkMap(marks: readonly NoteDisplayMark[]): ReadonlyMap<number, string | null>;
1080
1099
 
1081
1100
  /** Hard ceiling on notes laid out in one pass (fail closed beyond). */
1082
1101
  declare const MAX_NOTES_LAID_OUT = 10000;
@@ -1134,6 +1153,25 @@ interface NoteSeparatorLayout {
1134
1153
  /** Set when an oversize authored separator was replaced with a synthetic rule. */
1135
1154
  readonly fallbackReason?: NoteLayoutFallbackReason;
1136
1155
  }
1156
+ /**
1157
+ * Inline drawing support for ONE notes part.
1158
+ *
1159
+ * A note lives in `/word/footnotes.xml` or `/word/endnotes.xml`, not in the body part, so its
1160
+ * pictures resolve against that part's relationships — the same per-part shape header/footer
1161
+ * furniture uses. Without it a note paragraph flows with no drawing context at all and a
1162
+ * picture inside it contributes no record: no image, and no placeholder either.
1163
+ */
1164
+ interface NoteStoryDrawings {
1165
+ readonly inlineDrawingLayout: InlineDrawingLayoutContext;
1166
+ /**
1167
+ * Per-paragraph projection + RESOURCE token for the break cache key.
1168
+ *
1169
+ * Image resources settle asynchronously, and the authored extent does not move when one
1170
+ * does — so without the resource in the key the cached `pending` lines are served forever
1171
+ * and a decoded picture never reaches the page.
1172
+ */
1173
+ readonly drawingTokenForParagraph?: (paragraph: OoxmlNode) => string;
1174
+ }
1137
1175
  interface LayoutNoteStoryOptions {
1138
1176
  readonly measurer: TextMeasurer;
1139
1177
  readonly producer: string;
@@ -1147,6 +1185,13 @@ interface LayoutNoteStoryOptions {
1147
1185
  * unbounded page fragments; overflow is split by the pagination layer, not here.
1148
1186
  */
1149
1187
  readonly maxFlowHeightPt?: number;
1188
+ /** Resolves inline drawing support for the notes part a story lives in. */
1189
+ readonly drawingsForPart?: (ownerPartName: string) => NoteStoryDrawings | undefined;
1190
+ /**
1191
+ * Notes part the story being laid out came from. Set by {@link layoutNoteById} /
1192
+ * {@link layoutNoteSeparator}, which are the callers that hold the part.
1193
+ */
1194
+ readonly ownerPartName?: string;
1150
1195
  }
1151
1196
  /**
1152
1197
  * Stable line-id namespace for one note. Body line counters compare these as opaque strings
@@ -1246,6 +1291,11 @@ interface NotesLayoutInput {
1246
1291
  readonly cache?: ParagraphLayoutCache<readonly PendingLine[]>;
1247
1292
  readonly styleCascade?: StyleCascadeTable;
1248
1293
  readonly defaultTabStopPt?: number;
1294
+ /**
1295
+ * Inline drawing support per notes part. Absent means note paragraphs flow without
1296
+ * drawing records, which is what a headless caller with no image port wants.
1297
+ */
1298
+ readonly drawingsForPart?: (ownerPartName: string) => NoteStoryDrawings | undefined;
1249
1299
  }
1250
1300
  /**
1251
1301
  * The layout with notes attached, plus any fallbacks taken and the mark context used.
@@ -2313,4 +2363,4 @@ declare class TablePaginationError extends Error {
2313
2363
  constructor(code: TablePaginationErrorCode, message: string);
2314
2364
  }
2315
2365
 
2316
- export { AUTO_PREFERRED_WIDTH, type AbstractNumDefinition, type BidiEmbeddingLevels, BlockFragmentRecord, CELL_PAD, COMPOUND_BORDER_MIN_GAP_PT, COMPOUND_BORDER_MIN_STROKE_PT, type CanvasMeasurerOptions, type CanvasTextContext, type CanvasTextMetrics, type CascadedParagraphFormatting, CellBorderBox, type CellMarginsPt, type CellVerticalAlign, type CompoundBorderMetrics, type ContentControlFragmentRecord, DEFAULT_CANVAS_FONT_STACK, DEFAULT_CELL_MARGINS, EMPTY_NUMBERING_INDEX, FixedPoint, FixedPointRoundingMode, FontByteValidator, type FontSlot, GRAPHEME_SEGMENTER_LOCALE, type GraphemeBoundary, type GraphemeSegment, type GraphemeWordSegmentRecord, HARFBUZZ_SHAPING_LIBRARY, type HarfBuzzFaceCacheEvent, type HarfBuzzOutlineCacheEvent, type HarfBuzzShapeCacheEvent, HarfBuzzShapingError, type HarfBuzzShapingErrorCode, type HarfBuzzTextShaper, type HarfBuzzTextShaperInstrumentation, type HarfBuzzTextShaperOptions, type HeaderFooterVariantName, HyperlinkProjector, LayoutBox, type LayoutCacheStats, type LayoutScheduler, type LayoutSchedulerOptions, type LayoutScope, type LayoutSession, type LayoutSessionStats, type LevelOverride, type ListCounterAdvance, type ListCounterState, type ListMarkerAlign, type ListSuffix, MAX_EACH_PAGE_MARK_CANDIDATES, MAX_LVL_OVERRIDES, MAX_LVL_TEXT_LENGTH, MAX_MARKER_TEXT_LENGTH, MAX_NOTES_LAID_OUT, MAX_NOTE_FRAGMENTS, MAX_NOTE_OVERFLOW_PAGES, MAX_NOTE_REFLOW_ATTEMPTS, MAX_NUMBERING_DEFINITIONS, MAX_STYLE_BASED_ON_DEPTH, MAX_STYLE_DEFINITIONS, MAX_TABLE_COLUMNS, MAX_TABLE_NESTING, MAX_TABLE_ROW_FRAGMENTS, MAX_TABLE_ROW_HEIGHT_PT, type MaterializationInput, type NoteDisplayMark, type NoteLayoutFallbackReason, type NoteMarkContext, type NotePaginationFallbackReason, type NoteReferenceSite, type NoteSeparatorLayout, type NoteSeparatorRuleStyle, type NoteStoryLayout, type NotesAttachResult, type NotesLayoutInput, type NumDefinition, type NumberingIndex, type NumberingLevel, type NumberingLevelIndent, type PageFurniture, PageGeometry, PageRecord, type PageRefIndex, ParagraphBorderEdge, ParagraphBorders, ParagraphFragmentRecord, type ParagraphKeyInputs, type ParagraphLayoutCache, type ParagraphLayoutCacheOptions, type ParagraphLayoutInputs, ParagraphLineSpacing, ParagraphSpacing, type PreferredWidth, type PreferredWidthType, type ResolvedListItem, ResolvedRunStyle, type ResolvedSurfaceMeasurer, ResolvedTabStops, RevisionDisplayMode, type ScriptItem, SectionColumns, SemanticLayout, type SemanticLayoutOptions, type SemanticTableCell, type SemanticTableRow, type SemanticTableStructure, ShapedRun, type StyleCascadeTable, type StyleDefinition, StyleSpanRecord, type TableAlignment, TableBorderBox, TableBorderSide, type TableCellStyleFormatting, TablePaginationError, type TablePaginationErrorCode, type TableRowHeight, type TableRowHeightRule, TextDirection, TextMeasurer, TextShaper, UnsupportedScriptError, VersionedShapingLibrary, type ViewportWindow, WORD_SEGMENTER_LOCALE, type WordBoundary, type WordBoundaryResolverDeps, type WordSegment, attachNotesToLayout, boundedFallbackWordSegments, buildNumberingIndex, buildPageRefIndex, buildStyleCascadeTable, cascadeParagraphFormatting, cascadeRunProperties, cascadedBottomBorder, clampListValue, computeDoubleBorderMetricsPt, computeFootnoteReserves, contentControlBoundaries, createBoundedFallbackWordBoundary, createDefaultWordBoundary, createFixedMeasurer, createHarfBuzzTextShaper, createIntlWordBoundary, createLayoutScheduler, createLayoutSession, createListCounterState, createParagraphLayoutCache, defaultNoteSeparatorRuleStyle, deriveNoteDisplayMarks, deriveNoteDisplayMarksResolved, effectiveBorderSide, emptyTocPlaceholderParagraphIds, emptyTocSuppressedResultParagraphIds, expandLvlText, filterRefsOnPage, findSeparatorNote, formatDecimal, formatDecimalZero, formatLowerLetter, formatLowerRoman, formatNumFmt, formatUpperLetter, formatUpperRoman, fragmentOwnsAtomOffset, graphemeBoundaryEpoch, graphemeCount, graphemeOffsetToUtf16, harfBuzzFontValidator, initializeHarfBuzz, intlGraphemeBoundary, isCanvasMeasurementAvailable, isCumulativeGeometryTrustedFromLineOrigin, isGeometryTrustedCaretOffset, isHarfBuzzInitialized, isIntlSegmenterAvailable, isIntlWordSegmenterAvailable, isMarkerOnlySeparatorNote, isValidStyleId, isWholeGraphemeHorizontalBoundary, itemizeScriptFontSlots, layoutHeaderFooterStory, layoutNoteById, layoutNoteSeparator, layoutNoteStory, layoutSemanticDocument, listMarkerBox, mergeListIndent, normalNotesOf, noteDisplayMarkMap, noteLineIdPrefix, noteMarkKey, noteSeparatorAreaBox, noteStoryBlocks, pagesToMaterialize, paragraphLayoutKey, paragraphMarkDeleted, paragraphShading, paragraphShadingBox, projectedNoteMarkText, provisionalNoteMarks, readNumPr, readTableStructure, resetGraphemeBoundary, resolveDefaultSurfaceMeasurer, resolveDefaultWordBoundary, resolveNumberingLevel, resolveOoxmlShadingFill, resolveParagraphLayoutInputs, resolveStoryListItems, resolveStrictHexFill, revisionRemovesParagraph, roundFontUnitToFixedPoint, segmentGraphemes, segmentWords, semanticHorizontalBoundaries, setGraphemeBoundary, shadingFillFromElement, shapedHorizontalBoundaries, storyBlocks, syntheticSeparatorBox, tableOriginX, tocFieldChromeParagraphIds, tryCreateCanvasMeasurer, utf16OffsetToGrapheme, walkStoryParagraphs, withNumberingStyleLinks, withResolvedListItems, wordSegmentsToGraphemeRecords };
2366
+ export { AUTO_PREFERRED_WIDTH, type AbstractNumDefinition, type BidiEmbeddingLevels, BlockFragmentRecord, CELL_PAD, COMPOUND_BORDER_MIN_GAP_PT, COMPOUND_BORDER_MIN_STROKE_PT, type CanvasMeasurerOptions, type CanvasTextContext, type CanvasTextMetrics, type CascadedParagraphFormatting, CellBorderBox, type CellMarginsPt, type CellVerticalAlign, type CompoundBorderMetrics, type ContentControlFragmentRecord, DEFAULT_CANVAS_FONT_STACK, DEFAULT_CELL_MARGINS, EMPTY_NUMBERING_INDEX, FixedPoint, FixedPointRoundingMode, FontByteValidator, type FontSlot, GRAPHEME_SEGMENTER_LOCALE, type GraphemeBoundary, type GraphemeSegment, type GraphemeWordSegmentRecord, HARFBUZZ_SHAPING_LIBRARY, type HarfBuzzFaceCacheEvent, type HarfBuzzOutlineCacheEvent, type HarfBuzzShapeCacheEvent, HarfBuzzShapingError, type HarfBuzzShapingErrorCode, type HarfBuzzTextShaper, type HarfBuzzTextShaperInstrumentation, type HarfBuzzTextShaperOptions, type HeaderFooterVariantName, HyperlinkProjector, LayoutBox, type LayoutCacheStats, type LayoutScheduler, type LayoutSchedulerOptions, type LayoutScope, type LayoutSession, type LayoutSessionStats, type LevelOverride, type ListCounterAdvance, type ListCounterState, type ListMarkerAlign, type ListSuffix, MAX_EACH_PAGE_MARK_CANDIDATES, MAX_LVL_OVERRIDES, MAX_LVL_TEXT_LENGTH, MAX_MARKER_TEXT_LENGTH, MAX_NOTES_LAID_OUT, MAX_NOTE_FRAGMENTS, MAX_NOTE_OVERFLOW_PAGES, MAX_NOTE_REFLOW_ATTEMPTS, MAX_NUMBERING_DEFINITIONS, MAX_STYLE_BASED_ON_DEPTH, MAX_STYLE_DEFINITIONS, MAX_TABLE_COLUMNS, MAX_TABLE_NESTING, MAX_TABLE_ROW_FRAGMENTS, MAX_TABLE_ROW_HEIGHT_PT, type MaterializationInput, type NoteDisplayMark, type NoteLayoutFallbackReason, type NoteMarkContext, type NotePaginationFallbackReason, type NoteReferenceSite, type NoteSeparatorLayout, type NoteSeparatorRuleStyle, type NoteStoryDrawings, type NoteStoryLayout, type NotesAttachResult, type NotesLayoutInput, type NumDefinition, type NumberingIndex, type NumberingLevel, type NumberingLevelIndent, type PageFurniture, PageGeometry, PageRecord, type PageRefIndex, ParagraphBorderEdge, ParagraphBorders, ParagraphFragmentRecord, type ParagraphKeyInputs, type ParagraphLayoutCache, type ParagraphLayoutCacheOptions, type ParagraphLayoutInputs, ParagraphLineSpacing, ParagraphSpacing, type PreferredWidth, type PreferredWidthType, type ResolvedListItem, ResolvedRunStyle, type ResolvedSurfaceMeasurer, ResolvedTabStops, RevisionDisplayMode, type ScriptItem, SectionColumns, SemanticLayout, type SemanticLayoutOptions, type SemanticTableCell, type SemanticTableRow, type SemanticTableStructure, ShapedRun, type StyleCascadeTable, type StyleDefinition, StyleSpanRecord, type TableAlignment, TableBorderBox, TableBorderSide, type TableCellStyleFormatting, TablePaginationError, type TablePaginationErrorCode, type TableRowHeight, type TableRowHeightRule, TextDirection, TextMeasurer, TextShaper, UnsupportedScriptError, VersionedShapingLibrary, type ViewportWindow, WORD_SEGMENTER_LOCALE, type WordBoundary, type WordBoundaryResolverDeps, type WordSegment, attachNotesToLayout, boundedFallbackWordSegments, buildNumberingIndex, buildPageRefIndex, buildStyleCascadeTable, cascadeParagraphFormatting, cascadeRunProperties, cascadedBottomBorder, clampListValue, computeDoubleBorderMetricsPt, computeFootnoteReserves, contentControlBoundaries, createBoundedFallbackWordBoundary, createDefaultWordBoundary, createFixedMeasurer, createHarfBuzzTextShaper, createIntlWordBoundary, createLayoutScheduler, createLayoutSession, createListCounterState, createParagraphLayoutCache, defaultNoteSeparatorRuleStyle, deriveNoteDisplayMarks, deriveNoteDisplayMarksResolved, effectiveBorderSide, emptyTocPlaceholderParagraphIds, emptyTocSuppressedResultParagraphIds, expandLvlText, filterRefsOnPage, findSeparatorNote, formatDecimal, formatDecimalZero, formatLowerLetter, formatLowerRoman, formatNumFmt, formatUpperLetter, formatUpperRoman, fragmentOwnsAtomOffset, graphemeBoundaryEpoch, graphemeCount, graphemeOffsetToUtf16, harfBuzzFontValidator, initializeHarfBuzz, intlGraphemeBoundary, isCanvasMeasurementAvailable, isCumulativeGeometryTrustedFromLineOrigin, isGeometryTrustedCaretOffset, isHarfBuzzInitialized, isIntlSegmenterAvailable, isIntlWordSegmenterAvailable, isMarkerOnlySeparatorNote, isValidStyleId, isWholeGraphemeHorizontalBoundary, itemizeScriptFontSlots, layoutHeaderFooterStory, layoutNoteById, layoutNoteSeparator, layoutNoteStory, layoutSemanticDocument, listMarkerBox, mergeListIndent, normalNotesOf, noteDisplayMarkMap, noteLineIdPrefix, noteMarkKey, noteSeparatorAreaBox, noteStoryBlocks, pagesToMaterialize, paragraphLayoutKey, paragraphMarkDeleted, paragraphShading, paragraphShadingBox, projectedNoteMarkText, provisionalNoteMarks, readNumPr, readTableStructure, resetGraphemeBoundary, resolveDefaultSurfaceMeasurer, resolveDefaultWordBoundary, resolveNumberingLevel, resolveOoxmlShadingFill, resolveParagraphLayoutInputs, resolveStoryListItems, resolveStrictHexFill, revisionRemovesParagraph, roundFontUnitToFixedPoint, segmentGraphemes, segmentWords, semanticHorizontalBoundaries, setGraphemeBoundary, shadingFillFromElement, shapedHorizontalBoundaries, storyBlocks, syntheticSeparatorBox, tableOriginX, tocFieldChromeParagraphIds, tryCreateCanvasMeasurer, utf16OffsetToGrapheme, walkStoryParagraphs, withNumberingStyleLinks, withResolvedListItems, wordSegmentsToGraphemeRecords };