@docx-editor.dev/core/layout

@docx-editor.dev/core/layout — DOM-free pagination, shaping, and hit testing.

Text is measured through an injected TextMeasurer and shaped through an injected TextShaper, so the same code paginates in a browser and on a server. Points everywhere; twips convert at property-read boundaries.

Incremental by construction: per-block cache keys and flow checkpoints mean a pass that changes nothing returns the previous pages by identity.

Functions (209)

activeReviewItemfunctionSource ↗

The item the caret is in, or null.

A resolved comment never activates: a settled thread must not reopen itself as the reviewer types near it.

A REPLY resolves to the thread it belongs to. A reply is anchored over its parent's range, so both cover the caret — and the reply, being newer, wins the innermost test. It is not a card of its own (it renders inside its parent's), so the thread would have gone active with nothing on screen showing it: the reply box vanished from a comment the moment somebody replied to it.

declare function activeReviewItem(items: readonly ReviewItem[], position: ReviewPosition, order: ReadonlyMap<string, number>): ReviewItem | null;

appliedSpaceBeforefunctionSource ↗

Applied before-spacing for placement (Word 2013+ / compat mode 15).

Adjacent before/after still collapse to the larger gap, but before is dropped entirely when the paragraph begins at the top of a page mid-section. The first paragraph of a document or section retains before. Callers publish this applied value on the fragment so shading, borders, selection, and paint share one geometry.

declare function appliedSpaceBefore(before: number, previousAfter: number, atTopOfPage: boolean, firstParagraphOfSection: boolean): number;

applyLineSpacingfunctionSource ↗

Apply resolved line spacing to a line's natural (glyph-derived) box.

Word places auto / atLeast extras BELOW the line (the last line's multiple spacing still separates it from the next paragraph). Putting that delta above inverted cover-page rhythm: w:line="460" on "between" opened a large gap above the word and almost none before "MERIDIAN". exact taller than the glyphs centers the text (ECMA-376 17.3.1.33). An exact box smaller than the glyphs keeps the baseline inside so clipped text still sits on it.

declare function applyLineSpacing(spacing: ParagraphLineSpacing, naturalHeight: number, naturalBaseline: number): {
    height: number;
    baseline: number;
};

attachNotesToLayoutfunctionSource ↗

Attach footnote/endnote areas onto a body layout. Does not re-paginate — callers that need reservation must re-run body layout with [pageBottomReserves](pageBottomReserves) first.

declare function attachNotesToLayout(layout: SemanticLayout, allRefs: readonly PageRefHit[], input: NotesLayoutInput, options?: {
    readonly fallbackReasons?: readonly NotePaginationFallbackReason[];
    readonly paragraphSectionIndex?: ReadonlyMap<string, number>;
}): NotesAttachResult;

baselineShiftPtOffunctionSource ↗

How far a run's glyphs are lifted off the line's baseline, in points. Positive is up.

Super and subscript move the GLYPHS without moving the run's box, so the box keeps tiling the line and the selection band stays continuous. Anything drawing at the glyphs — the painter, and the caret — has to apply this itself, and from one place, or the two drift.

declare function baselineShiftPtOf(style: ResolvedRunStyle): number;

borderExtentPtfunctionSource ↗

Width contribution of a resolved edge for content inset / row sizing.

declare function borderExtentPt(edge: ResolvedTableBorderEdge | TableBorderSide | undefined): number;

borderWeightfunctionSource ↗

Conflict weight: the authored width in eighths of a point, and nothing else.

Word-matching, not conformance — §17.4.39 (w:tblBorders) and §17.4.66 (w:tcBorders) describe the elements and specify no conflict algorithm. Word picks the heavier RULE, so a 6pt dashed rule beats a hairline single. Folding the style rank into the weight (an sz × border-number product) made a 0.5pt double outrank a 1pt single and made every dashed or dotted rule weigh 1 regardless of w:sz, which erased its width entirely.

declare function borderWeight(side: TableBorderSide): number;

bottomBorderExtentPtfunctionSource ↗

Vertical extent a bottom border adds below the last line (gap + rule).

declare function bottomBorderExtentPt(edge: ParagraphBorderEdge | undefined): number;

boundedFallbackWordSegmentsfunctionSource ↗

Bounded deterministic fallback — not full UAX #29 word conformance.

declare function boundedFallbackWordSegments(text: string): readonly WordSegment[];

buildNumberingIndexfunctionSource ↗

Project numbering.xml into the bounded index.

Every ceiling here exists because the input is file-derived: definition counts, override counts, indent magnitudes and style-link hop depth are all capped, and nothing from the file becomes a loop bound or an allocation size.

declare function buildNumberingIndex(root: OoxmlElement | null | undefined): NumberingIndex;

buildPageRefIndexfunctionSource ↗

Build a reusable paragraph-id index (document order preserved per paragraph).

declare function buildPageRefIndex(allRefs: readonly PageRefHit[]): PageRefIndex;

buildStyleCascadeTablefunctionSource ↗

Build a cascade table from a styles part root.

Only direct w:style children of the root participate (bounded count). Duplicate styleId values keep the last definition, matching Word's reader for this fixture class. Default paragraph/character style ids track w:default="1" with the same last-wins rule.

declare function buildStyleCascadeTable(stylesRoot: OoxmlElement | null, themeFonts?: ThemeFonts): StyleCascadeTable;

captureOperationSnapshotfunctionSource ↗

Capture, validate, and freeze the environment used throughout one derived operation.

captureOperationSnapshot: (source: OperationSnapshot) => OperationSnapshot

caretAtfunctionSource ↗

Geometry for one model position, or null when it is not laid out.

declare function caretAt(layout: SemanticLayout, position: SemanticPosition, measurerOrOptions?: TextMeasurer | CaretAtOptions): CaretGeometry | null;

caretBoxOnLinefunctionSource ↗

Where a caret sits on a line: its x, and the box it should be drawn at.

The height comes from the RUN at the insertion point, not from the line. A line is as tall as its largest run, so a caret in 11pt text on a line that also carries 36pt text was drawn three times the height of the text it sits in. Word sizes the insertion point to the run it would type into, which is also how the painter already draws the selection band: every run is its own inline box, and the band steps with the text.

Affinity at a shared model boundary: - after a layout-sized atom (tab / projected field), prefer the DOWNSTREAM span so the caret sits at the aligned destination (e.g. before CONFIDENTIAL after a right tab), matching hit-testing and caretStops; - otherwise the run BEFORE the offset wins — the run a keystroke would continue — except at the start of the line, where there is nothing before it.

declare function caretBoxOnLine(line: LineRecord, offset: number, measurer: TextMeasurer | undefined): {
    x: number;
    y: number;
    height: number;
};

caretStopsfunctionSource ↗

Every caret stop in the document body, in reading order.

One per character boundary on every line, plus the line end. Derived rather than stored, so a stop can never survive the content it described. Ownership of a position SHARED by two lines is decided here exactly as caretAt decides it. Furniture stories use [caretStopsForBlocks](caretStopsForBlocks) so open header/footer navigation never walks body stops.

declare function caretStops(layout: SemanticLayout, measurer?: TextMeasurer): CaretGeometry[];

caretStopsForBlocksfunctionSource ↗

Caret stops for one story's block fragments (header/footer), in reading order.

Coordinates stay story-relative — the same space hitTestFragments and furniture paint use — so arrow motion follows tab-stop geometry and projected field atoms without mixing body sheet offsets.

declare function caretStopsForBlocks(layout: SemanticLayout, pageIndex: number, fragments: readonly BlockFragmentRecord[], measurer?: TextMeasurer): CaretGeometry[];

cascadedBottomBorderfunctionSource ↗

Bottom border after cascade: a later w:pBdr replaces an earlier one; absence inherits. nil/none clear the edge via paragraphBorders.

declare function cascadedBottomBorder(paragraphPropertyNodes: readonly OoxmlNode[]): ParagraphBorderEdge | undefined;

cascadedParagraphBordersfunctionSource ↗

w:pBdr after the style cascade: a later w:pBdr replaces an earlier one WHOLESALE.

Word does not merge edges across the cascade. A style that states only w:bottom discards the box its basedOn ancestor declared, so folding edge by edge would leave a lone underline surrounded by a box no one authored. Absence inherits; nil/none clear.

declare function cascadedParagraphBorders(paragraphPropertyNodes: readonly OoxmlNode[]): ParagraphBorders;

cascadedTabStopsfunctionSource ↗

Resolve tab stops from cascaded w:pPr nodes (docDefaults → style chain → direct).

Each w:tabs merges with clear support; absence inherits. The leader travels with the stop that declared it — a clear at the same position discards both together.

declare function cascadedTabStops(paragraphPropertyNodes: readonly OoxmlNode[]): ResolvedTabStops;

cascadeParagraphFormattingfunctionSource ↗

Cascade paragraph + inherited run properties for one paragraph's direct w:pPr.

Order: docDefaults → table style → basedOn ancestors → paragraph style → direct formatting, which is the style hierarchy of 17.7.2: a table style sits above the document defaults and below the paragraph style a cell paragraph names for itself. When w:pStyle is absent, the document's default paragraph style (w:default="1") is used. Direct formatting is last so it overrides inherited values inside the existing resolvers.

declare function cascadeParagraphFormatting(table: StyleCascadeTable, directPPr: OoxmlNode | undefined, tableCellStyle?: TableCellStyleFormatting): CascadedParagraphFormatting;

cascadeRunPropertiesfunctionSource ↗

Merge inherited paragraph-style run props with a run's direct rPr (direct last).

When a cascade table is supplied, also resolves w:rStyle character styles (basedOn chain, cycle/depth capped). Runs without an explicit rStyle pick up the default character style (w:default="1"). Precedence: inherited → character style chain → direct formatting.

declare function cascadeRunProperties(inheritedRunProperties: readonly OoxmlProperty[], directRunProperties: readonly OoxmlProperty[], table?: StyleCascadeTable): readonly OoxmlProperty[];

cellSelectionBetweenfunctionSource ↗

The rectangle two cells define.

Grown to a fixpoint rather than taken literally: a cell that spans two columns cannot be half selected, and a vertically merged run cannot be selected in the middle. Word grows the rectangle until every cell it touches is wholly inside it, so dragging into a merged cell pulls the selection out to that cell's full extent.

declare function cellSelectionBetween(layout: SemanticLayout, anchor: TableCellAddress, head: TableCellAddress): CellSelection | null;

cellSelectionRectsfunctionSource ↗

One rectangle per painted occurrence of a selected cell, in page-content coordinates.

declare function cellSelectionRects(layout: SemanticLayout, cellIds: readonly string[]): readonly {
    pageIndex: number;
    x: number;
    y: number;
    width: number;
    height: number;
}[];

cellSelectionTextfunctionSource ↗

A cell selection as plain text: tabs between cells, newlines between rows.

What a spreadsheet and every other word processor put on the clipboard for a rectangle, and the only shape that survives the trip: the text range a rectangle stands in for would paste back as one run of characters with the grid gone.

declare function cellSelectionText(layout: SemanticLayout, selection: CellSelection): string;

clampListValuefunctionSource ↗

Clamp a list counter into a safe non-negative integer Word can format.

declare function clampListValue(value: number): number;

collapsedSpaceBeforefunctionSource ↗

Gap to insert before a paragraph once the previous paragraph's after is already in the flow cursor — Word takes the larger of the two rather than summing them.

declare function collapsedSpaceBefore(before: number, previousAfter: number): number;

collectFlowBlocksfunctionSource ↗

Collect paragraph and table blocks from a sibling list, flattening through content-control wrappers up to [MAX_CONTENT_CONTROL_NESTING](MAX_CONTENT_CONTROL_NESTING).

accept filters which typed blocks are kept (e.g. skip revision-removed paragraphs). When nesting exceeds the bound the wrapper is skipped entirely — same fail-closed rule as the historical storyBlocks walk.

declare function collectFlowBlocks(children: readonly OoxmlNode[], depth?: number, accept?: (block: OoxmlElement) => boolean): OoxmlElement[];

commentBodyTextfunctionSource ↗

Plain text of a comment's body, so a card never re-implements the run walk.

declare function commentBodyText(comment: CommentRecord): string;

commentInitialsfunctionSource ↗

Author initials for an avatar, from @w:initials or the name.

declare function commentInitials(comment: CommentRecord): string;

compositionAnchorfunctionSource ↗

The anchor an IME composition is attached to.

Composition needs a position that survives the intermediate transactions it produces, so it is expressed in model coordinates and re-resolved against each new layout rather than cached as geometry.

declare function compositionAnchor(layout: SemanticLayout, position: SemanticPosition): CaretGeometry | null;

computeDoubleBorderMetricsPtfunctionSource ↗

Deterministic double stroke / gap / extent in layout points (scale-independent).

Thin authored widths inflate to a 1+1+1 point compound so a w:sz="3" double remains visible at paint scale 1 — matching Word's hairline-double floor.

declare function computeDoubleBorderMetricsPt(widthPt: number): CompoundBorderMetrics;

computeFootnoteReservesfunctionSource ↗

Compute per-page bottom reserves (points) needed for footnotes given a provisional layout. Used by the bounded reflow loop before final attach.

Height is measured against a column-derived note budget (not leftover body slack). Measuring from slack makes stable true on the first pass and never shrinks the body — references and notes then compete for the same band. Oversized notes still split/continue within the budget; [MIN_FOOTNOTE_BODY_BAND_PT](MIN_FOOTNOTE_BODY_BAND_PT) keeps a body band so reflow cannot chase blank sheets.

declare function computeFootnoteReserves(layout: SemanticLayout, allRefs: readonly PageRefHit[], input: NotesLayoutInput, noteMarks: NoteMarkContext): {
    readonly reserves: ReadonlyMap<number, number>;
    readonly stable: boolean;
    readonly reasons: readonly NotePaginationFallbackReason[];
};

contentControlAtPointfunctionSource ↗

Innermost content control whose published boundary geometry contains point on pageIndex.

Nested controls that share the same content box resolve to the deepest nesting depth.

declare function contentControlAtPoint(layout: SemanticLayout, pageIndex: number, point: HitPoint): ContentControlBoundaryRecord | null;

contentControlAtSemanticfunctionSource ↗

Innermost content-control boundary at a page-content point, or null outside every control.

Prefers the deepest nesting depth when nested boundaries share geometry.

declare function contentControlAtSemantic(layout: SemanticLayout, point: {
    readonly x: number;
    readonly y: number;
    readonly pageIndex?: number;
}): ContentControlBoundaryRecord | null;

contentControlBoundariesfunctionSource ↗

Derive one boundary record per control the part declares, in document order.

A control whose content the layout never placed — one inside a story this layout is not of, or one holding nothing — answers no fragments rather than a zero-sized box at the origin: a rectangle nothing painted is a rectangle a hit test would match.

declare function contentControlBoundaries(part: OoxmlPart, layout: SemanticLayout): readonly ContentControlBoundaryRecord[];

contentControlContentChildrenfunctionSource ↗

Children of every w:sdtContent under a control, in document order.

Does not recurse into nested controls — callers that flatten blocks or inline runs do that with their own depth counter against [MAX_CONTENT_CONTROL_NESTING](MAX_CONTENT_CONTROL_NESTING).

declare function contentControlContentChildren(control: OoxmlNode): readonly OoxmlNode[];

contentControlsInLayoutfunctionSource ↗

Layout-published content-control boundaries in document order.

declare function contentControlsInLayout(layout: SemanticLayout): readonly ContentControlBoundaryRecord[];

contentControlsOfLayoutfunctionSource ↗

Every content-control boundary on a layout, preferring the layout-level list.

declare function contentControlsOfLayout(layout: SemanticLayout): readonly ContentControlBoundaryRecord[];

createBoundedFallbackWordBoundaryfunctionSource ↗

Explicit bounded fallback boundary (grapheme-safe, narrower than Intl).

declare function createBoundedFallbackWordBoundary(): WordBoundary;

createDefaultWordBoundaryfunctionSource ↗

Resolve production word boundary: Intl when available/construction succeeds, else bounded fallback.

declare function createDefaultWordBoundary(deps?: WordBoundaryResolverDeps): WordBoundary;

createFixedMeasurerfunctionSource ↗

A deterministic measurer for tests and headless use.

Monospace by construction: every character is the same width and every line the same height, scaled by w:sz when present. Real shaping is the HarfBuzz path; this exists so layout behaviour can be asserted without a font stack deciding the answer.

declare function createFixedMeasurer(charWidth?: number, lineHeight?: number): TextMeasurer;

createFontResourceSnapshotfunctionSource ↗

Admit a set of faces and return the immutable snapshot layout resolves against.

Admission is the trust boundary for font bytes: counts and sizes are checked against the hard ceilings, each face's hash is re-derived and compared, and the bytes are handed to the injected validator. A face failing any of these is recorded as a typed refusal rather than dropped, so resolve can explain itself instead of answering "missing".

createFontResourceSnapshot: (options: FontResourceSnapshotOptions) => FontResourceSnapshot

createHarfBuzzTextShaperfunctionSource ↗

Build a HarfBuzz-backed shaper.

Requires initializeHarfBuzz() to have resolved — shaping is synchronous, so the WASM runtime must already be loaded. Dispose the result when the editor goes away, or its cached faces leak.

createHarfBuzzTextShaper: (options?: HarfBuzzTextShaperOptions) => HarfBuzzTextShaper

createIntlWordBoundaryfunctionSource ↗

Intl.Segmenter word boundary for the invariant locale.

declare function createIntlWordBoundary(): WordBoundary;

createLayoutSchedulerfunctionSource ↗

Build the scheduler that turns a stream of commits into coalesced layout passes.

Keystrokes arrive faster than a document can be laid out, so changes accumulate into one scope and are laid out together rather than once per commit.

declare function createLayoutScheduler(options: LayoutSchedulerOptions): LayoutScheduler;

createLayoutSessionfunctionSource ↗

A layout session, retained across revisions by the caller.

declare function createLayoutSession(): LayoutSession;

createListCounterStatefunctionSource ↗

Create a fresh counter bag for one story (body, or one header/footer part).

declare function createListCounterState(index: NumberingIndex): ListCounterState;

createParagraphLayoutCachefunctionSource ↗

A bounded least-recently-used cache.

Bounded because a long editing session touches far more paragraph states than a document contains — every keystroke mints a new key for the paragraph being typed in — and an unbounded cache would hold every intermediate state of the session.

declare function createParagraphLayoutCache<T>(options?: ParagraphLayoutCacheOptions): ParagraphLayoutCache<T>;

createShapedMeasurerfunctionSource ↗

A [TextMeasurer](TextMeasurer) that measures through the shaper rather than through a canvas.

The accurate path: advances come from the same shaping run that will position the glyphs, so measurement and paint cannot disagree. Falls back per-run when a font is unavailable rather than throwing, because a document naming a font nobody has must still lay out.

declare function createShapedMeasurer(options: ShapedMeasurerOptions): TextMeasurer;

createShapedRunfunctionSource ↗

Validate and freeze a shaped run against the environment that produced it.

Checks the internal consistency a shaper must satisfy: direction matches the environment, cluster ranges tile the text without gaps or overlap, glyph ranges stay in bounds, font spans cover every glyph, and caret edges are monotonic. A shaper that violates any of these produces a caret that lands in the wrong place, which is far harder to diagnose downstream than here.

createShapedRun: (input: ShapedRun, environmentInput: ShapingEnvironmentInput) => ShapedRun

createShapingEnvironmentfunctionSource ↗

Validate and freeze a shaping environment.

Checks every field that could silently corrupt a measurement: OpenType tags must be four ASCII bytes, script and language must be non-blank and control-character free, fonts must already be validated. Throws rather than coercing — a bad tag that shapes anyway produces a document that measures wrong everywhere and looks fine.

createShapingEnvironment: (input: ShapingEnvironmentInput) => ShapingEnvironment

defaultNoteSeparatorRuleStylefunctionSource ↗

Word-default paint style for a separator marker.

Footnote and endnote separators both use a short single rule. A full-width double border on a body heading (e.g. the comprehensive fixture’s end banner) is ordinary paragraph w:pBdr ownership and must not transfer onto the note separator record. Authored separator stories with real paragraph/run/border content bypass this via fragment paint.

declare function defaultNoteSeparatorRuleStyle(_noteKind: NoteKind, _kind: 'separator' | 'continuationSeparator'): NoteSeparatorRuleStyle;

defaultTabIntervalFromSettingsfunctionSource ↗

Read w:settings/w:defaultTabStop (ECMA-376 §17.15.1.25), in points.

Word's own interval, not a constant: a metric-locale template writes w:val="1134" (2cm) and every default-interval tab in the document lands on that grid instead of the 0.5" one. The value is FILE-DERIVED, so a non-integer, non-positive or out-of-range val falls back to the schema default rather than being trusted into layout arithmetic.

ST_TwipsMeasure also admits a universal measure ("2cm"); Word writes plain twips, and the spelled form falls back to the default rather than being parsed here.

declare function defaultTabIntervalFromSettings(settings: OoxmlNode | null | undefined): number;

deriveNoteDisplayMarksfunctionSource ↗

Derive display marks for references of one note kind in document order.

Restart rules: - continuous — single sequence across the document from numStart - eachSect — restart at numStart when sectionIndex changes - eachPage — restart when pageIndex changes (falls back to continuous if unknown)

IDs are stable; only display numbers change. Non-mutating.

declare function deriveNoteDisplayMarks(noteKind: NoteKind, references: readonly NoteReferenceSite[], properties: ResolvedNoteProperties): readonly NoteDisplayMark[];

deriveNoteDisplayMarksResolvedfunctionSource ↗

Derive marks using per-reference-section resolved properties (numFmt / numStart / numRestart). Restart rules consult each site's own section props.

declare function deriveNoteDisplayMarksResolved(_noteKind: NoteKind, references: readonly NoteReferenceSite[], resolveProps: (sectionIndex: number) => ResolvedNoteProperties): readonly NoteDisplayMark[];

displayTextfunctionSource ↗

The text as it is DRAWN, after case transforms. Measurement must use this, not the source.

declare function displayText(text: string, style: ResolvedRunStyle): string;

documentOrderfunctionSource ↗

Paragraph ids in document order, deduplicated across fragments.

declare function documentOrder(layout: SemanticLayout): string[];

effectiveBorderSidefunctionSource ↗

What one cell side is before any ADJACENT cell has its say.

- omitted → the table's own rule for that position (tblBorders, insideH/insideV) - edge → the cell wins outright; no weight fight with the table - none → an explicit w:val="nil". Suppresses a matching table border on interior and perimeter sides alike, so a table whose cells all declare none paints borderless like Word even when tblBorders still carry single rules.

declare function effectiveBorderSide(authored: TableBorderSide, tableSide: TableBorderSide, _options?: {
    readonly interior?: boolean;
}): TableBorderSide;

effectiveContentControlLockfunctionSource ↗

Collapse raw + ancestor locks into one ST_Lock vocabulary value.

declare function effectiveContentControlLock(locks: readonly ContentControlLock[]): ContentControlLock;

emptyTocPlaceholderParagraphIdsfunctionSource ↗

Begin-paragraph ids of TOCs that have no visible cached result rows.

Layout keeps a single caret-height line on these ids so paint can host an identifiable empty-TOC furniture placeholder; ordinary field chrome on the same ids stays suppressed.

declare function emptyTocPlaceholderParagraphIds(part: OoxmlPart): ReadonlySet<string>;

emptyTocSuppressedResultParagraphIdsfunctionSource ↗

Empty result-paragraph ids inside an empty TOC.

Suppressed like field chrome so blank cached rows do not stack under the empty placeholder.

declare function emptyTocSuppressedResultParagraphIds(part: OoxmlPart): ReadonlySet<string>;

enumerateDocumentSectionsfunctionSource ↗

Split the body story into sections.

A paragraph carrying w:pPr/w:sectPr ends the current section (that paragraph is IN the section being ended). The body-level w:sectPr ends the final section. A document with neither yields one section of Word defaults covering every block.

Enumeration is capped at [MAX_DOCUMENT_SECTIONS](MAX_DOCUMENT_SECTIONS). Further paragraph-level section breaks are ignored and remaining blocks fold into the last accepted section (fail closed).

displayMode MUST match the one the caller passes to storyBlocks. blockStart / blockEndExclusive are indices into that list, and the list changes shape with the mode: the proposed view drops a paragraph whose mark and content a revision both removed. Slicing a filtered list with indices counted over an unfiltered one puts body text under another section's page geometry — the wrong paper size, the wrong margins, the wrong header.

declare function enumerateDocumentSections(part: OoxmlPart, displayMode?: RevisionDisplayMode): DocumentSection[];

enumerateDocumentSectionsBoundedfunctionSource ↗

Like [enumerateDocumentSections](enumerateDocumentSections), but reports whether the section bound clipped hostile input. Prefer the plain enumerator for normal layout; use this when a caller needs a named fail-closed diagnostic.

declare function enumerateDocumentSectionsBounded(part: OoxmlPart, displayMode?: RevisionDisplayMode): DocumentSectionsEnumeration;

expandLvlTextfunctionSource ↗

Expand w:lvlText placeholders %1%9 using per-level counters and formats.

formats[i] / counters[i] correspond to ilvl i. Missing slots use decimal / 1. Literal percent signs that are not %1%9 are kept. Output is hard-capped.

declare function expandLvlText(lvlText: string, counters: readonly number[], formats: readonly string[]): string;

filterRefsOnPagefunctionSource ↗

Collect note references that appear in laid-out body fragments on a page. Matches [ParagraphFragmentRecord.range](ParagraphFragmentRecord.range) ownership (half-open + boundary affinity).

Pass [buildPageRefIndex](buildPageRefIndex) result as refIndex for O(fragments + matching refs) instead of scanning every document ref against every page fragment.

declare function filterRefsOnPage(page: PageRecord, allRefs: readonly PageRefHit[], refIndex?: PageRefIndex): readonly PageRefHit[];

findDrawingOverlayFrameInLayoutfunctionSource ↗

Locate a drawing's painted extent on the published layout.

Coordinates are page-content relative — the same space [hitTestPage](hitTestPage) uses — so an overlay can position from records without reading painted DOM geometry.

declare function findDrawingOverlayFrameInLayout(layout: SemanticLayout, drawingNodeId: string): DrawingOverlayFrame | null;

findSeparatorNotefunctionSource ↗

Find separator / continuationSeparator note body in a notes part.

declare function findSeparatorNote(part: OoxmlPart | null | undefined, kind: 'separator' | 'continuationSeparator'): OoxmlElement | undefined;

firstReviewRangefunctionSource ↗

The first range of an item, in authored order, or null when it has none.

declare function firstReviewRange(item: ReviewItem): ReviewRange | null;

fixedPointfunctionSource ↗

Brand a safe integer as a [FixedPoint](FixedPoint) coordinate.

Fixed point rather than float throughout shaping so that two runs shaped identically compare EQUAL — float accumulation would make the same text measure differently depending on how it was split, and pagination is decided on those measurements.

fixedPoint: (value: number) => FixedPoint

fontRequestKeyfunctionSource ↗

The canonical string identifying one face request.

Case- and whitespace-normalized, so "Times New Roman" and "times new roman" are one key — a document may name a family either way and both must reach the same face.

fontRequestKey: (request: FontRequest) => string

formatDecimalfunctionSource ↗

decimal (§17.18.59). Clamped, because the counter derives from file-declared restarts.

declare function formatDecimal(value: number): string;

formatDecimalZerofunctionSource ↗

decimalZero (§17.18.59): single digits zero-padded to two.

declare function formatDecimalZero(value: number): string;

formatLowerLetterfunctionSource ↗

lowerLetter (§17.18.59): the [formatUpperLetter](formatUpperLetter) sequence, lower-cased.

declare function formatLowerLetter(value: number): string;

formatLowerRomanfunctionSource ↗

lowerRoman (§17.18.59).

declare function formatLowerRoman(value: number): string;

formatNumFmtfunctionSource ↗

Format one counter for a w:numFmt value (ST_NumberFormat, §17.18.59).

none prints NOTHING — it is the format Word uses for a level that contributes only literal text, and formatting it as decimal invents a number the document never had. bullet is not formatted here — callers use the literal lvlText.

The remaining enumerants (japaneseCounting, hebrew1, thaiNumbers, ganada, …) are per-script numeral sequences we do not carry glyph tables for. They fall back to decimal deliberately: the ORDINAL is still the authored one, only the script differs, which reads as a number in the wrong alphabet rather than as a missing or wrong marker.

declare function formatNumFmt(numFmt: string, value: number): string;

formatPageNumberfunctionSource ↗

Format a displayed PAGE value through the shared ST_NumberFormat resolver.

Unknown / script-specific formats fall back to decimal (same convention as list markers). none / bullet are meaningless for page numbers and also fall back to decimal so a hostile fmt cannot blank the furniture.

declare function formatPageNumber(value: number, format: string | undefined): string;

formatRevisionOffunctionSource ↗

The tracked FORMAT change on a property list, from w:rPrChange or w:pPrChange.

A property change alters no characters, so it has no span of its own to strike or underline. Word marks the affected text and says what changed; the minimum a reader needs is to see that this text's formatting is itself a pending decision.

Read from the flattened property list because that is what layout already carries — the change wrapper is a w:rPr/w:pPr child like any other.

declare function formatRevisionOf(properties: readonly {
    readonly localName: string;
    readonly attributes?: Readonly<Record<string, string>>;
}[]): RevisionAttribution | null;

formatUpperLetterfunctionSource ↗

Excel-style letter sequence: 1→A … 26→Z, 27→AA. Caps length so a hostile counter cannot grow without bound.

declare function formatUpperLetter(value: number): string;

formatUpperRomanfunctionSource ↗

upperRoman (§17.18.59). Saturates at 3999, the largest value classical Roman numerals express — beyond it there is nothing correct to emit, so it stops rather than inventing notation.

declare function formatUpperRoman(value: number): string;

fragmentOwnsAtomOffsetfunctionSource ↗

Whether a paragraph fragment owns a note atom at atomOffset.

Fragment ranges are half-open for content ownership: [start, end). The shared boundary offset belongs to the later fragment (downstream affinity), matching line splits where fragmentStart = previous.range.end.

declare function fragmentOwnsAtomOffset(fragment: ParagraphFragmentRecord, atomOffset: number): boolean;

fragmentsOfParagraphfunctionSource ↗

Every fragment belonging to one paragraph, in order, across page boundaries.

declare function fragmentsOfParagraph(layout: SemanticLayout, paragraphId: string): ParagraphFragmentRecord[];

geometryOfSectionfunctionSource ↗

Section properties as the geometry layout paginates against.

The gutter is added to the LEFT margin: it is binding allowance, extra space on the inner edge, and folding it into the content width instead would silently narrow every line.

declare function geometryOfSection(section: SectionProperties): PageGeometry;

graphemeBoundaryEpochfunctionSource ↗

Current boundary generation, for callers that cache segmentation-derived answers.

declare function graphemeBoundaryEpoch(): number;

graphemeCountfunctionSource ↗

How many user-perceived characters the text holds.

declare function graphemeCount(text: string): number;

graphemeOffsetToUtf16functionSource ↗

The UTF-16 offset a grapheme index starts at. Clamps rather than throwing.

declare function graphemeOffsetToUtf16(text: string, graphemeOffset: number): number;

guardOperationSnapshotfunctionSource ↗

Compare the current environment against the one an operation captured, naming what changed.

guardOperationSnapshot: (captured: OperationSnapshot, current: OperationSnapshot) => OperationSnapshotGuard

hitTestPagefunctionSource ↗

Hit test a point given in PAGE-CONTENT coordinates, the space the fragment boxes use.

declare function hitTestPage(layout: SemanticLayout, pageIndex: number, point: HitPoint, options?: HitTestOptions): SemanticHit | null;

hitTestSemanticfunctionSource ↗

The caret position nearest a point, in PAGE-CONTENT coordinates.

Never returns null for a point inside the document: a click in the margin, past the end of a line, or below the last line still has an obvious intended caret, and refusing to answer would make those clicks do nothing.

The rules live in semantic-hit-test.ts, which answers with the cell address and the on-glyphs flag a pointer controller needs too; this keeps the geometry-only shape for callers that want nothing else.

declare function hitTestSemantic(layout: SemanticLayout, point: {
    readonly x: number;
    readonly y: number;
    readonly pageIndex?: number;
}): CaretGeometry | null;

hitTestSheetfunctionSource ↗

Hit test a point given in SHEET coordinates — the space page.box lives in, and the space a surface's own pixel offsets convert into.

declare function hitTestSheet(layout: SemanticLayout, point: HitPoint, options?: HitTestOptions): SemanticHit | null;

initializeHarfBuzzfunctionSource ↗

Load and verify the HarfBuzz WASM runtime without adding top-level await to import graphs.

declare function initializeHarfBuzz(): Promise<void>;

isCanvasMeasurementAvailablefunctionSource ↗

Whether an injected canvas text context is usable for measurement.

Availability is decided by the editor seam (which alone may create a canvas). Layout never probes document — a missing context is simply "unavailable".

declare function isCanvasMeasurementAvailable(context?: CanvasTextContext | null | undefined): boolean;

isContentControlfunctionSource ↗

Block or inline structured-document-tag wrapper — typed or generic during migration.

Generic fallback requires the WordprocessingML namespace so foreign-namespace <x:sdt> elements stay opaque wrappers and are never treated as Word controls.

declare function isContentControl(node: OoxmlNode): node is ContentControlLike;

isContentControlContentfunctionSource ↗

True when node is the control's content container (w:sdtContent).

Generic sdtContent requires the WML namespace — same foreign-namespace rule as [isContentControl](isContentControl).

declare function isContentControlContent(node: OoxmlNode): node is ContentControlContentLike;

isCumulativeGeometryTrustedFromLineOriginfunctionSource ↗

Whether the distance from a line's start to an offset can be trusted as exact.

Requires BOTH ends to be shaped boundaries: accumulating advances across an offset the shaper cannot place would produce a caret x that drifts further along the line.

declare function isCumulativeGeometryTrustedFromLineOrigin(run: ShapedRun, lineStartUtf16Offset: number, utf16Offset: number): boolean;

isFurniturePointfunctionSource ↗

True when a sheet-space point falls inside a page's header or footer box.

declare function isFurniturePoint(layout: SemanticLayout, point: HitPoint): boolean;

isGeometryTrustedCaretOffsetfunctionSource ↗

Every published shaped boundary has exact geometry from the shaping result.

declare function isGeometryTrustedCaretOffset(run: ShapedRun, utf16Offset: number): boolean;

isHarfBuzzInitializedfunctionSource ↗

Whether the WASM runtime is loaded and shaping can proceed synchronously.

declare function isHarfBuzzInitialized(): boolean;

isIntlSegmenterAvailablefunctionSource ↗

Whether this runtime provides Intl.Segmenter, which the default boundary requires.

declare function isIntlSegmenterAvailable(): boolean;

isIntlWordSegmenterAvailablefunctionSource ↗

Whether this runtime provides word-granularity Intl.Segmenter.

declare function isIntlWordSegmenterAvailable(): boolean;

isMarkerOnlySeparatorNotefunctionSource ↗

True when a separator note contains only OOXML separator markers (and empty noteRef atoms Word often authors beside them) — no measurable text or paragraph borders.

declare function isMarkerOnlySeparatorNote(note: OoxmlNode): boolean;

isValidStyleIdfunctionSource ↗

Accepted style ids only — over-long, control-bearing, or dangerous keys are dropped.

declare function isValidStyleId(raw: string | undefined): raw is string;

isWholeGraphemeHorizontalBoundaryfunctionSource ↗

Whether an offset is a real caret position: both a grapheme boundary and a shaped cluster edge.

Both conditions, because they disagree. A ligature is one cluster spanning two graphemes, and an offset inside it has no geometry the shaper can answer for — placing a caret there would mean inventing an x coordinate.

declare function isWholeGraphemeHorizontalBoundary(run: ShapedRun, utf16Offset: number): boolean;

itemizeScriptFontSlotsfunctionSource ↗

Split text into shapeable runs by script, bidi level and font slot.

Consumes the bidi levels rather than re-deriving direction, so itemization and reordering agree by construction instead of by two implementations happening to match.

declare function itemizeScriptFontSlots(text: string, paragraphOffset: number, embedding: BidiEmbeddingLevels): readonly ScriptItem[];

keyedRangeRectsfunctionSource ↗

Rectangles for MANY ranges in ONE pass over the lines.

Not selectionRects in a loop. That walks every page, fragment and line per range, and a contract with two hundred comments would re-walk the whole document two hundred times on every layout — the highlight would cost more than the layout it decorates. One pass tests each line against every range instead, which is the same work a single selection does.

declare function keyedRangeRects(layout: SemanticLayout, ranges: readonly KeyedRange[], 
pages?: ReadonlySet<number>): Map<string, SelectionRect[]>;

layoutHeaderFooterStoryfunctionSource ↗

Lay one header/footer part out at contentWidth.

Line ids are namespaced by part so the body's line-N counter — which incremental convergence compares — never moves because a header changed.

When pageContext is set, allowlisted PAGE/NUMPAGES/SECTIONPAGES instructions project live values; otherwise those fields contribute only cached result text (often empty). Field-free stories ignore pageContext and share one baseline layout.

defaultTabStopPt is the document's w:settings/w:defaultTabStop (ECMA-376 §17.15.1.25) in points; absent keeps the 0.5" schema default. Furniture tabs on the SAME grid as the body — a page-number tab in a metric-locale footer belongs on the document's interval, not on a constant. It sits at the tail because the parameters ahead of it are already positional; new callers should keep passing undefined for what they do not set.

declare function layoutHeaderFooterStory(part: OoxmlPart, contentWidth: number, measurer: TextMeasurer, producer: string, cache?: ParagraphLayoutCache<readonly PendingLine[]>, styleCascade?: StyleCascadeTable, pageContext?: FieldPageContext, maxPageContextEntries?: number, defaultTabStopPt?: number, displayMode?: RevisionDisplayMode, inlineDrawingLayout?: InlineDrawingLayoutContext, drawingTokenForParagraph?: (paragraph: OoxmlNode) => string, drawingLayoutToken?: string, hfPageContext?: HeaderFooterPageContext): HeaderFooterStoryLayout;

layoutNoteByIdfunctionSource ↗

Layout a note by id from a notes part; null when missing.

declare function layoutNoteById(part: OoxmlPart | null | undefined, noteId: number, contentWidth: number, options: LayoutNoteStoryOptions): NoteStoryLayout | null;

layoutNoteSeparatorfunctionSource ↗

Layout the document's separator note, or synthesize a short horizontal rule.

Marker-only / missing separators emit no paragraph fragments — paint draws the rule from [NoteSeparatorLayout.ruleStyle](NoteSeparatorLayout.ruleStyle) + box geometry. Authored separators with real paragraph/run/border content keep their fragment story (including w:pBdr).

When maxFlowHeightPt is set and an authored separator exceeds it, the engine fails closed to a short synthetic rule ({@link note-separator-height-cap}) so note pagination cannot burn the overflow budget on zero-progress separator-only pages.

declare function layoutNoteSeparator(part: OoxmlPart | null | undefined, kind: 'separator' | 'continuationSeparator', contentWidth: number, options: LayoutNoteStoryOptions, noteKind: NoteKind, maxFlowHeightPt?: number): NoteSeparatorLayout;

layoutNoteStoryfunctionSource ↗

Lay one note node out at contentWidth.

Does not paginate. Callers that need splits ask for fragments and cut at paragraph/line boundaries in the note-pagination layer.

declare function layoutNoteStory(note: OoxmlNode, contentWidth: number, options: LayoutNoteStoryOptions): NoteStoryLayout | null;

layoutSemanticDocumentfunctionSource ↗

Lay one story part out into pages.

The engine's layout entry point. Walks body, header, footer and note roots, flattens block SDTs, paginates tables with header-row repeats and vertical merges, and resolves every paragraph through the style cascade.

Incremental when given a [LayoutSession](LayoutSession): per-block cache keys plus flow checkpoints mean a pass that changes nothing returns the previous pages by identity.

declare function layoutSemanticDocument(part: OoxmlPart, revision: number, options: SemanticLayoutOptions): SemanticLayout;

lineAtPositionfunctionSource ↗

The line containing a model position, or null when the position is not laid out.

declare function lineAtPosition(layout: SemanticLayout, paragraphId: string, offset: number): LineRecord | null;

lineEndOffsetfunctionSource ↗

The end position of a line, as Word places it.

On a SOFT-WRAPPED line the space that caused the break is painted at the end of the line but the caret belongs before it — otherwise clicking in the right margin puts the caret visually at the start of the NEXT line, which reads as the click having missed. The last line of a paragraph has no such space to discount.

A HARD BREAK is the same story with a character that is always there: the position after it belongs to the line the break opened (caretAt places it there), so a click in the right margin of the line the break ENDED has to stop in front of it or the caret appears a row below the click.

A PAGE break is discounted even on the paragraph's LAST line, which is the one case the last-line shortcut got wrong. The position after such a break is on the next page — and when the remainder is empty it has no line anywhere, because Word Online starts the following block flush at the top of that page. So the caret for it stays behind on the line the break ended, and a click in the wide blank space beside the mark resolved to a position a page away from where it landed: the caret appeared under the pointer and the typing came out on the next page. <w:p><w:r><w:br w:type="page"/></w:r></w:p> is the commonest way to end a page, so that blank space is most of a page wide.

declare function lineEndOffset(layout: SemanticLayout, line: LineRecord): number;

linesOffunctionSource ↗

Every line in a layout, in reading order — the order caret navigation walks.

declare function linesOf(layout: SemanticLayout): LineRecord[];

listMarkerBoxfunctionSource ↗

Horizontal marker box inside the hanging indent slot.

Coordinates are relative to the same origin as paragraph content (indent.left is the text start). Returns null when there is nothing to paint.

declare function listMarkerBox(item: ResolvedListItem, markerWidth: number, lineY: number, lineHeight: number): {
    x: number;
    y: number;
    width: number;
    height: number;
} | null;

measureDisplayTextfunctionSource ↗

Measure run text the way layout breaks lines and paints glyphs (caps/small-caps aware).

declare function measureDisplayText(text: string, style: ResolvedRunStyle, measurer: TextMeasurer): number;

mergeListIndentfunctionSource ↗

The effective indent of a list paragraph: STYLE, then the numbering LEVEL, then DIRECT.

Word applies a level's w:pPr/w:ind between the paragraph style and the paragraph's own formatting, per attribute — and the ordering matters on real documents. A converted agreement numbers its (a) items with a level stating left=1512 hanging=738 under a ListParagraph style stating left=775 hanging=624, and states only hanging="737" on the paragraph itself. Reading the flattened cascade as "the paragraph's indent" gave the STYLE's 775 to a level that had overridden it, so every lettered sub-item hung a full indent step to the left of where Word puts it.

inherited is the cascade WITHOUT the paragraph's own w:pPr (defaults, table cell style, style chain); direct is that w:pPr alone.

declare function mergeListIndent(levelIndent: NumberingLevelIndent, inherited: readonly OoxmlProperty[], direct?: readonly OoxmlProperty[]): NumberingLevelIndent;

moveCaretfunctionSource ↗

Move a caret.

Vertical movement keeps a DESIRED X so a caret travelling through short lines returns to its original column rather than collapsing to the end of the shortest one. The caller threads that value; passing null starts a fresh vertical run from the current position.

declare function moveCaret(layout: SemanticLayout, position: SemanticPosition, command: NavigationCommand, desiredX?: number | null, options?: MoveCaretOptions): {
    position: SemanticPosition;
    desiredX: number | null;
} | null;

nextTabDestinationfunctionSource ↗

Next tab destination strictly past currentX, preferring custom stops then the default interval. Destination is clamped to rightEdge so stops cannot escape the content box.

declare function nextTabDestination(tabs: ResolvedTabStops, currentX: number, rightEdge: number): TabDestination;

normalNotesOffunctionSource ↗

Collect normal (body) notes from a notes part, bounded.

declare function normalNotesOf(part: OoxmlPart | null | undefined): readonly OoxmlElement[];

noteDisplayMarkMapfunctionSource ↗

Map noteId → formatted mark for quick lookup (last site wins if duplicated).

declare function noteDisplayMarkMap(marks: readonly NoteDisplayMark[]): ReadonlyMap<number, string | null>;

noteLineIdPrefixfunctionSource ↗

Stable line-id namespace for one note. Body line counters compare these as opaque strings and must not collide with line-N / hf-… ids.

declare function noteLineIdPrefix(noteKind: NoteKind, noteId: number): string;

noteMarkKeyfunctionSource ↗

The key one note's mark is stored under — footnote:N / endnote:N.

The same encoding EditorScope uses for note ids, so a mark context and a scope address name the same note without a translation step.

declare function noteMarkKey(noteKind: NoteKind, noteId: number): string;

noteSeparatorAreaBoxfunctionSource ↗

Absolute separator box: short rule for marker/synthetic, full width for authored stories.

declare function noteSeparatorAreaBox(separator: NoteSeparatorLayout, contentX: number, contentWidth: number, areaTop: number): LayoutBox;

noteStoryBlocksfunctionSource ↗

Blocks of one typed footnote/endnote node — a separate semantic story root.

The footnotes/endnotes part root is never a story; each note is laid out independently so line ids and incremental convergence stay namespaced by note identity.

declare function noteStoryBlocks(note: OoxmlNode, displayMode?: RevisionDisplayMode): OoxmlElement[];

pageAtYfunctionSource ↗

The page a sheet-space y belongs to.

The gutter between page *i* and page *i+1* resolves to page *i*, because the nearest text to a point in that gap is the last line of the page above it. That falls out of searching for the last page whose top is at or above the point, with no special case: the gutter is inside [top(i), top(i+1)) by construction. A point above the first page clamps to it, and a point past the last page clamps to that.

declare function pageAtY(layout: SemanticLayout, sheetY: number): number;

pagesToMaterializefunctionSource ↗

The page indices to build in detail.

Returns indices rather than records so a caller can compare cheaply against what it already has mounted, and so the decision can be made without touching the layout.

declare function pagesToMaterialize(input: MaterializationInput): Set<number>;

paragraphBorderExtentPtfunctionSource ↗

Extent one border edge occupies away from the text it decorates: gap plus rule, in points.

Vertically that is flow height — a top rule pushes the first line down, a bottom rule holds the page open below the last one — so pagination has to see it. Horizontally it is publish-only: Word draws left/right paragraph rules OUTSIDE the text column and never re-breaks the lines, which is why adding a box to a paragraph in Word does not reflow it.

declare function paragraphBorderExtentPt(edge: ParagraphBorderEdge | undefined): number;

paragraphBordersfunctionSource ↗

Resolve w:pBdr from the paragraph-properties node.

Nested — every edge is a child of pBdr, not an attribute — so this reads the typed tree rather than the flattened OoxmlProperty[] bag propertiesOf builds for leaf props.

declare function paragraphBorders(pPr: OoxmlNode | undefined): ParagraphBorders;

paragraphBordersFingerprintfunctionSource ↗

Identity of a paragraph's border set, for the w:between group rule.

Word treats consecutive paragraphs whose border settings are IDENTICAL as ONE bordered block: the top rule draws above the first, the bottom rule below the last, and each interior boundary gets w:between or nothing (§17.3.1.24). That is why applying a box to three selected paragraphs in Word draws one box and not three.

Empty string means "no borders", which never groups with anything.

declare function paragraphBordersFingerprint(borders: ParagraphBorders): string;

paragraphBorderStrokeWidthPtfunctionSource ↗

Visual stroke thickness layout publishes for one edge (points).

Compound ST_Border values (double, …) use the shared inflated band so a thin w:sz="3" double still occupies a visible double-line box — matching table borders.

declare function paragraphBorderStrokeWidthPt(edge: ParagraphBorderEdge): number;

paragraphBreaksBeforefunctionSource ↗

Whether a paragraph must start a new page (w:pageBreakBefore).

declare function paragraphBreaksBefore(props: readonly OoxmlProperty[]): boolean;

paragraphContextualSpacingfunctionSource ↗

w:contextualSpacing (17.3.1.9): drop before/after between paragraphs of the SAME style. Word's built-in ListParagraph sets it, so every list authored in Word gets a paragraph gap between items without this.

declare function paragraphContextualSpacing(props: readonly OoxmlProperty[]): boolean;

paragraphFragmentsOffunctionSource ↗

Depth-first paragraph fragments of one page, in reading order.

Table interiors flatten through rows and cells; header-repeat rows are skipped unless asked for, so interaction sees each caret stop exactly once while paint sees everything.

declare function paragraphFragmentsOf(page: PageRecord, includeHeaderRepeats?: boolean): ParagraphFragmentRecord[];

paragraphFragmentsOfBlocksfunctionSource ↗

Depth-first paragraph fragments of one block list, in reading order.

The same walk as [paragraphFragmentsOf](paragraphFragmentsOf) for fragment lists that do not sit on the page directly — a header/footer story's fragments, a note story's.

declare function paragraphFragmentsOfBlocks(blocks: readonly BlockFragmentRecord[], includeHeaderRepeats?: boolean): ParagraphFragmentRecord[];

paragraphLayoutKeyfunctionSource ↗

The cache key for one paragraph's measured break.

Folds in the content, the available width, and the measurement producer. Anything that changes where lines fall must be in here, or the cache serves a break taken under different conditions.

declare function paragraphLayoutKey(inputs: ParagraphKeyInputs): ParagraphLayoutKey;

paragraphLineSpacingfunctionSource ↗

Resolve w:line / w:lineRule from flat paragraph properties.

Merged per attribute for the same reason as before/after: w:spacing is one element carrying independent attributes, and a style that states only @line must not reset the rule an earlier entry in the cascade set.

declare function paragraphLineSpacing(props: readonly OoxmlProperty[]): ParagraphLineSpacing;

paragraphMarkDeletedfunctionSource ↗

w:pPr/w:rPr/w:del — the paragraph mark was deleted by a tracked revision.

Read from the paragraph-mark run properties only. A w:del anywhere else in the paragraph deletes run content, which is a different statement entirely.

declare function paragraphMarkDeleted(paragraph: OoxmlNode): boolean;

paragraphMarkRevisionOffunctionSource ↗

The revision on a paragraph's own MARK, from w:pPr/w:rPr/w:ins|w:del.

EG_ParaRPrTrackChanges records that the pilcrow itself was inserted or deleted, which is how Word writes a paragraph split or merge. It is not content — there is no text to decorate — so a surface shows it as a mark of its own beside the paragraph, the way Word draws a struck- through ¶.

Property-position w:ins/w:del stay generic in the tree deliberately, so this reads them by name rather than by kind.

declare function paragraphMarkRevisionOf(paragraph: OoxmlNode): RevisionAttribution | null;

paragraphOrderOfPartfunctionSource ↗

Paragraph node id → document position, from the TREE rather than from a layout.

Memoized on the immutable root: one full derivation pass asks this question three times (replacement pairing, the queue's merged order, the session's cached order), and each answer was a fresh full-tree walk. The instance is SHARED, so the return type is ReadonlyMap: a caller mutating it would poison every later reader of this root.

declare function paragraphOrderOfPart(part: OoxmlPart): ReadonlyMap<string, number>;

paragraphSectionNodefunctionSource ↗

w:sectPr nested under a paragraph's w:pPr, if present.

declare function paragraphSectionNode(paragraph: OoxmlElement): OoxmlElement | undefined;

paragraphShadingfunctionSource ↗

Resolve paragraph shading from cascaded flat w:pPr properties.

Later w:shd entries win (defaults → style → direct), matching spacing/border cascade.

declare function paragraphShading(props: readonly OoxmlProperty[]): string | undefined;

paragraphShadingBoxfunctionSource ↗

Page-content box for paragraph shading: union of this fragment's line boxes.

Excludes collapsed before/after spacing and bottom-border extent so the painted band matches Word's content-area fill (character shading height on a single line).

declare function paragraphShadingBox(lines: readonly {
    readonly box: LayoutBox;
}[], x: number, width: number): LayoutBox | undefined;

paragraphsInCellsfunctionSource ↗

Paragraph ids inside a set of cells, in document order, each once.

declare function paragraphsInCells(layout: SemanticLayout, cellIds: readonly string[]): readonly string[];

paragraphSpacingfunctionSource ↗

Resolve w:spacing before/after from flat paragraph properties.

Line spacing (w:line / w:lineRule) is a separate concern — it changes measured line height, not the gap between paragraphs — and is not resolved here.

w:beforeAutospacing / w:afterAutospacing REPLACE the authored measurement on their own side rather than adding to it; see [AUTO_PARAGRAPH_SPACING_PT](AUTO_PARAGRAPH_SPACING_PT).

declare function paragraphSpacing(props: readonly OoxmlProperty[], context?: ParagraphAutoSpacingContext): ParagraphSpacing;

paragraphTabStopsfunctionSource ↗

Direct w:pPr only — used when no style cascade table is present.

declare function paragraphTabStops(pPr: OoxmlNode | undefined): ResolvedTabStops;

paragraphTextFromLayoutfunctionSource ↗

The text of one paragraph, read back from the layout records.

Word boundaries need characters, and the records carry them: every span holds the text it was laid out from, keyed by the source range it covers. Reading them back keeps word motion in the interaction lane instead of making it a second consumer of the model.

declare function paragraphTextFromLayout(layout: SemanticLayout, paragraphId: string): string;

parsePageNumberingfunctionSource ↗

Parse authored w:pgNumType without inventing schema defaults.

Returns undefined when the element is absent. An empty element yields {} so callers can tell "present but unauthored" from "missing" and serialization can re-emit empty. Hostile / out-of-range attribute values are dropped rather than clamped into meaning.

declare function parsePageNumbering(sectPr: OoxmlNode): SectionPageNumbering | undefined;

parseSectionPropertiesfunctionSource ↗

Parse one w:sectPr into geometry/break properties (null reads as Word's defaults).

declare function parseSectionProperties(sectPr: OoxmlNode | null | undefined): SectionProperties;

projectedNoteMarkTextfunctionSource ↗

Resolve display text for a noteReference / noteRef node under a mark context.

declare function projectedNoteMarkText(node: OoxmlNode, context: NoteMarkContext | undefined): ProjectedNoteMark | null;

provisionalNoteMarksfunctionSource ↗

Build a continuous (pre-page) mark context for the first body layout pass. eachPage reserves digit width; [reprojectBodyNoteMarks](reprojectBodyNoteMarks) publishes final marks onto body citations after page assignment in [attachNotesToLayout](attachNotesToLayout).

declare function provisionalNoteMarks(refs: readonly PageRefHit[], input: NotesLayoutInput): NoteMarkContext;

readBorderSidefunctionSource ↗

Read one OOXML border child into the three-state model.

declare function readBorderSide(node: OoxmlElement | undefined): TableBorderSide;

readCellBordersfunctionSource ↗

Read one cell's w:tcBorders, under the same bounds [readTableBorders](readTableBorders) applies.

declare function readCellBorders(tcPr: OoxmlElement | undefined): CellBorderBox;

readNumPrfunctionSource ↗

Read w:numPr from cascaded paragraph-property nodes (last wins).

Flat OoxmlProperty[] bags drop nested ilvl/numId, so this walks the tree nodes the same way borders and tabs do.

declare function readNumPr(paragraphPropertyNodes: readonly OoxmlNode[]): {
    numId: string;
    ilvl: number;
} | null;

readSectionPropertiesfunctionSource ↗

The section properties a part declares, or Word's defaults where it says nothing.

Returns the FINAL section (body-level w:sectPr, else the last paragraph-level one). Multi-section geometry belongs to enumerateDocumentSections; chrome that needs "the document's page" still reads the last section, which is what Word's body-level sectPr is.

declare function readSectionProperties(part: OoxmlPart): SectionProperties;

readTableBordersfunctionSource ↗

Read a table's w:tblBorders, dropping or clamping hostile values.

Widths and colours come from a file: an out-of-range w:sz becomes a layout dimension, so it is bounded here rather than downstream.

declare function readTableBorders(tblPr: OoxmlElement | undefined): TableBorderBox;

readTableStructurefunctionSource ↗

Read one typed table node into a bounded structure, or null when the node is not a typed table or sits beyond the nesting ceiling.

declare function readTableStructure(table: OoxmlNode, contentWidthPt: number, depth: number, styleCascade?: StyleCascadeTable, 
displayMode?: RevisionDisplayMode): SemanticTableStructure | null;

resetGraphemeBoundaryfunctionSource ↗

Restore the default Intl.Segmenter boundary and clear the memo.

declare function resetGraphemeBoundary(): void;

resolveBorderConflictfunctionSource ↗

Pick the winner between two candidates on a shared grid line (zero cell spacing).

none loses to any edge; two none/omitted yield omitted (no paint). Width decides; equal widths rank by style, then prefer the darker color, then preferFirst (reading-order / first candidate).

declare function resolveBorderConflict(first: TableBorderSide, second: TableBorderSide, preferFirst?: boolean): TableBorderSide;

resolveDefaultSurfaceMeasurerfunctionSource ↗

The surface's default measurer: canvas when a 2d context was injected, otherwise fixed.

Host-supplied and shaping measurers override this entirely — call only when the options did not already name one. The editor seam is responsible for creating the canvas context.

declare function resolveDefaultSurfaceMeasurer(scale?: number, options?: CanvasMeasurerOptions): ResolvedSurfaceMeasurer;

resolveDefaultWordBoundaryfunctionSource ↗

Cached immutable production boundary (first resolved instance only).

declare function resolveDefaultWordBoundary(): WordBoundary;

resolveNumberingLevelfunctionSource ↗

The effective level for one paragraph's numbering reference, after overrides and style links.

Answers null for a reference the index cannot resolve — a paragraph naming a definition the file never declared is an unnumbered paragraph, not an error.

declare function resolveNumberingLevel(index: NumberingIndex, numId: string, ilvl: number): {
    readonly abstractNumId: string;
    readonly level: NumberingLevel;
    readonly startOverride?: number;
} | null;

resolveOoxmlShadingFillfunctionSource ↗

Resolve a w:shd attribute bag to a validated RRGGBB fill, or undefined.

w:themeFill is a REFERENCE, and Word always writes the value it resolved to alongside it: <w:shd w:val="clear" w:fill="D9E2F3" w:themeFill="accent1" w:themeFillTint="33"/>. Reading w:fill in that case is not inventing a colour from the theme — it is reading the colour the producer computed. Dropping the fill because a theme reference sat next to it left every accent-shaded cell and paragraph unpainted.

val="nil" clears shading. Pattern vals are not rendered; a valid solid fill still paints as a clear fill until pattern support lands. A theme reference with no usable w:fill still resolves to nothing rather than a guess.

declare function resolveOoxmlShadingFill(attributes: Readonly<Record<string, string>> | undefined): string | undefined;

resolveParagraphLayoutInputsfunctionSource ↗

Resolve every paragraph input semantic layout / table cells share: cascaded props when a style table is present, otherwise direct formatting only.

When listItem is provided, its merged level indent becomes the paragraph indent (list hanging / left from numbering.xml), which is what Word uses for fixture list paragraphs that author no direct w:ind.

tableCellStyle carries what the enclosing table's style says about this cell's paragraphs; body paragraphs pass nothing.

inTableCell is asked for separately because a cell paragraph may have no table style to inherit at all, and w:beforeAutospacing still needs to know it is in a cell.

declare function resolveParagraphLayoutInputs(paragraph: OoxmlElement, contentWidth: number, styleCascade: StyleCascadeTable | undefined, listItem?: ResolvedListItem, tableCellStyle?: TableCellStyleFormatting, inTableCell?: boolean): ParagraphLayoutInputs;

resolveRunStylefunctionSource ↗

Resolve one run's direct formatting.

Unrecognised values are DROPPED rather than guessed: a w:sz of "large" leaves the default size rather than inventing one, because a wrong measurement moves every glyph after it and a missing one is visible immediately.

themeFonts resolves w:rFonts theme references. Absent, a theme-only rFonts leaves the family inherited — which is what every run of a theme-fonted document does, so the whole document falls back to the surface default face.

declare function resolveRunStyle(props: readonly OoxmlProperty[], themeFonts?: ThemeFonts): ResolvedRunStyle;

resolveStoryListItemsfunctionSource ↗

Resolve every list paragraph in a story to a [ResolvedListItem](ResolvedListItem), keyed by node id.

Non-list paragraphs are absent from the map. Hostile / missing numbering resolves inertly (paragraph omitted — laid out as ordinary text).

declare function resolveStoryListItems(blocks: readonly OoxmlElement[], index: NumberingIndex, styleCascade: StyleCascadeTable | undefined, isFontAvailable?: (family: string) => boolean): ReadonlyMap<string, ResolvedListItem>;

resolveStrictHexFillfunctionSource ↗

Strict hex fill: exactly six hex digits. Rejects auto, nil, and any non-hex payload (CSS functions, URLs, short hex, theme tokens).

declare function resolveStrictHexFill(raw: string | undefined): string | undefined;

resolveTableCellBorderGridfunctionSource ↗

Resolve borders for every cell in a laid-out table fragment.

Shared vertical edges: conflict(left.right, right.left) → assigned to the left cell, segmented per row when a vMerge restart faces differing neighbors. Shared horizontal edges: conflict(above.bottom, below.top) → assigned to the above cell, segmented per grid column when a gridSpan cell faces differing below neighbors; vMerge interior seams are suppressed per interval.

When geometry is provided, compound edges expand into explicit stroke records with corner-adjusted endpoints in cell-local points.

declare function resolveTableCellBorderGrid(rows: readonly (readonly BorderGridCell[])[], table: TableBorderBox, columnCount: number, geometry?: BorderGridGeometry, work?: TableBorderGridResolveWork, ownershipBudget?: TableBorderOwnershipBudget): ResolvedCellBorders[][];

reviewAnchorIndexfunctionSource ↗

Paragraph id to its place on the page, in ONE pass over the layout.

Built once per layout and reused by every card. The straightforward version — scan the pages until the paragraph turns up, per card — is a full-document walk per card, and a contract with two hundred comments walked the document two hundred times every time the caret moved. Toggling the pane was visibly slow for exactly that reason.

declare function reviewAnchorIndex<TPage extends {
    readonly index: number;
    readonly contentBox: {
        readonly y: number;
    };
}>(layout: {
    readonly pages: readonly TPage[];
}, paragraphFragments: (page: TPage) => readonly {
    readonly paragraphId: string;
    readonly box: {
        readonly y: number;
    };
    readonly lines?: readonly {
        readonly range: {
            readonly end: number;
        };
        readonly box: {
            readonly y: number;
        };
    }[];
}[]): Map<string, ReviewParagraphAnchor>;

reviewItemGeometryfunctionSource ↗

Where a card belongs beside the page, from LAYOUT RECORDS.

The one question the tree cannot answer, and the one a surface must not answer for itself: measuring painted DOM puts the sidebar a repaint behind the document and breaks outright while pagination is in flight.

Returns null when the item has no resolvable range, or when its paragraph is not in this layout — a comment anchored in a header belongs to a story the body layout never saw.

declare function reviewItemGeometry(item: ReviewItem, index: ReadonlyMap<string, ReviewParagraphAnchor>): {
    readonly pageIndex: number;
    readonly y: number;
} | null;

reviewItemKeyfunctionSource ↗

The stable key a surface uses for the active item and for a React list.

declare function reviewItemKey(item: ReviewItem): string;

reviewItemPositionRankfunctionSource ↗

A single comparable number for document order.

Paragraph index dominates offset, so a revision spanning paragraphs still sorts by where it STARTS. An item with no resolvable range sorts last rather than to position zero, which is where an orphan used to land — tearing an orphaned reply out of its own thread.

declare function reviewItemPositionRank(item: ReviewItem, order: ReadonlyMap<string, number>): number;

reviewItemRangesfunctionSource ↗

Every range a decision touches. One card can cover several, in different paragraphs.

declare function reviewItemRanges(item: ReviewItem): readonly ReviewRange[];

reviewItemsAtfunctionSource ↗

Every item covering a position, innermost first.

Returning the whole stack rather than one winner is what lets a surface offer cycling, a stacked card, or a "1 of 3" affordance. A comment wrapping a revision used to be unreachable because only the tightest range was ever returned.

declare function reviewItemsAt(items: readonly ReviewItem[], position: ReviewPosition, order: ReadonlyMap<string, number>): ReviewItem[];

reviewThreadRootOffunctionSource ↗

Walk a reply up to the card that heads its thread. Guarded against a cyclic file.

The head is not always a comment. A reply to a tracked change renders inside the REVISION's card, so resolving it to itself opened an item nothing on screen was drawing — the reply box vanished the moment somebody answered a change.

EXPORTED because the paginated surface answers "which card is open" itself, against its own dismissed-key state, rather than through [activeReviewItem](activeReviewItem). Two copies of the innermost-wins rule was survivable while a reply could only be a comment — the parent came first in comments.xml order and won the tie by accident. It stopped being survivable the moment a reply could answer a revision, which outranks it outright.

declare function reviewThreadRootOf(items: readonly ReviewItem[], comment: ReviewCommentItem): ReviewItem;

revisionRemovesParagraphfunctionSource ↗

True when a tracked revision has removed this paragraph from the rendered document, so layout should emit no box for it at all.

declare function revisionRemovesParagraph(paragraph: OoxmlNode, displayMode?: 'all-markup' | 'proposed' | 'original'): boolean;

revisionsAreDeletionfunctionSource ↗

True when this stack of revisions marks its content as deleted from the live document.

declare function revisionsAreDeletion(revisions: readonly RevisionAttribution[]): boolean;

revisionsVisiblefunctionSource ↗

Whether content under this stack of revisions is laid out in the given mode.

Containment governs, so a single enclosing wrapper the mode resolves away suppresses everything inside it regardless of what the inner wrappers say. An insertion inside a deletion does not survive the proposed result: the deletion it sits in was accepted.

declare function revisionsVisible(revisions: readonly RevisionAttribution[], mode: RevisionDisplayMode): boolean;

roundFontUnitToFixedPointfunctionSource ↗

Convert signed font units by an exact rational multiplier using the declared tie rule.

roundFontUnitToFixedPoint: (fontUnits: number, denominator: number, numerator: number, mode: FixedPointRoundingMode) => FixedPoint

runStylesEqualfunctionSource ↗

Whether two resolved styles are identical, for span merging and cache keys.

declare function runStylesEqual(a: ResolvedRunStyle, b: ResolvedRunStyle): boolean;

segmentGraphemesfunctionSource ↗

Split text into graphemes through the active boundary.

Memoized on the last texts seen, because paragraph layout asks about the same string repeatedly and a full segmentation pass per call made layout quadratic in paragraph length.

declare function segmentGraphemes(text: string): readonly GraphemeSegment[];

segmentWordsfunctionSource ↗

Split text into word segments through the given boundary, or the resolved default.

declare function segmentWords(text: string, boundary?: WordBoundary): readonly WordSegment[];

selectionRectsfunctionSource ↗

The rectangles covering a selection, one per line it spans.

declare function selectionRects(layout: SemanticLayout, selection: SemanticSelection): SelectionRect[];

semanticHorizontalBoundariesfunctionSource ↗

Grapheme offsets corresponding to exact shaped boundaries.

declare function semanticHorizontalBoundaries(run: ShapedRun): readonly number[];

setGraphemeBoundaryfunctionSource ↗

Install a different segmentation strategy, invalidating the memo.

For tests and for runtimes lacking Intl.Segmenter. Call [resetGraphemeBoundary](resetGraphemeBoundary) to restore the default.

declare function setGraphemeBoundary(boundary: GraphemeBoundary): void;

sha256FontBytesfunctionSource ↗

Synchronous platform-neutral SHA-256 used before font bytes cross into a shaping implementation.

sha256FontBytes: (bytes: Uint8Array) => string

shadingFillFromElementfunctionSource ↗

Read w:shd from a typed/generic element (table tcPr, nested pPr, …).

declare function shadingFillFromElement(shd: OoxmlElement | undefined): string | undefined;

shapedHorizontalBoundariesfunctionSource ↗

UTF-16 boundaries that are both whole-grapheme and HarfBuzz cluster edges.

declare function shapedHorizontalBoundaries(run: ShapedRun): readonly number[];

shapedRunComparatorInputsfunctionSource ↗

Reduce a shaped run to its comparable form, validating it on the way through.

What the D9 determinism oracles compare: two shaping runs of the same text in the same environment must produce byte-identical structures here.

shapedRunComparatorInputs: (input: ShapedRun, environment: ShapingEnvironmentInput) => ShapedRunComparatorInputs

shapingEnvironmentFingerprintfunctionSource ↗

Canonical serialization of every shaping variable, including byte hash and provenance.

shapingEnvironmentFingerprint: (environment: ShapingEnvironmentInput) => string

shapingEnvironmentFingerprintInputsfunctionSource ↗

Reduce an environment to its comparable form, with records sorted so key order cannot affect the result.

shapingEnvironmentFingerprintInputs: (input: ShapingEnvironmentInput) => ShapingEnvironmentFingerprintInputs

spanOffsetXfunctionSource ↗

The x of a model offset inside a span — the inverse of [offsetWithinSpan](offsetWithinSpan).

Shares the measurement, and the cache, with the hit test. Interpolating across the span's advance instead is exact only for a uniform one: in a proportional face the caret for offset 4 of an 8-character span is drawn at half its width, which lands in the middle of a glyph rather than between two.

declare function spanOffsetX(span: StyleSpanRecord, offset: number, measurer: TextMeasurer | undefined): number;

spansInCellsfunctionSource ↗

The style spans a cell selection covers, for reporting active formatting.

declare function spansInCells(layout: SemanticLayout, cellIds: readonly string[]): readonly StyleSpanRecord[];

spansInSelectionfunctionSource ↗

The style spans a selection touches, for reporting active formatting.

declare function spansInSelection(layout: SemanticLayout, selection: SemanticSelection): StyleSpanRecord[];

storyBlocksfunctionSource ↗

The story's blocks — paragraphs and tables — in document order, flattening through block-level content-control wrappers under the shared nesting budget.

declare function storyBlocks(part: OoxmlPart, displayMode?: RevisionDisplayMode): OoxmlElement[];

syntheticSeparatorBoxfunctionSource ↗

Default rule geometry for a synthetic / marker-only separator, story-relative.

declare function syntheticSeparatorBox(contentWidth: number, flowHeight: number): LayoutBox;

tabAdvanceWidthfunctionSource ↗

Width of a tab glyph so the following segment lands on the destination.

segmentWidth / decimalOffset are already measured in points. Decimal offset is the advance from the segment start to the decimal point (0 when none — treated like right).

declare function tabAdvanceWidth(alignment: TabAlignment, currentX: number, destinationX: number, segmentWidth: number, decimalOffset: number): number;

tableContextAtfunctionSource ↗

The table context of one paragraph — what a toolbar reflects when the caret is in a cell.

Answers for a plain caret, not only for a cell selection, because "am I in a table" is a question about where the caret is and a toolbar that only knew during a rectangle drag would show its table controls disabled while the user was typing in a cell.

declare function tableContextAt(layout: SemanticLayout, paragraphId: string): TableCellContext | null;

tableOriginXfunctionSource ↗

Where a table's left edge sits inside the box that contains it.

17.4.50 puts a left-aligned table at w:tblInd from the leading margin. 17.4.29's other two placements are stated relative to the containing box instead, so the indent does not also apply to them — Word centres a centred table in the text column whatever indent the file carries. A table wider than its container starts flush so its leading edge stays on the page rather than being centred off it.

declare function tableOriginX(structure: SemanticTableStructure, containerWidthPt: number): number;

tabStopsFingerprintfunctionSource ↗

Stable fingerprint for layout cache keys — nested w:tabs are not in flat OoxmlProperty bags, so style-inherited stops must be named explicitly or breaks would collide.

declare function tabStopsFingerprint(tabs: ResolvedTabStops): string;

tocFieldChromeParagraphIdsfunctionSource ↗

Paragraph ids for TOC field begin/end chrome that must not reserve vertical flow when empty.

declare function tocFieldChromeParagraphIds(part: OoxmlPart): ReadonlySet<string>;

tryCreateCanvasMeasurerfunctionSource ↗

Build a canvas-backed measurer, or null when no 2d context was injected.

Prefer [resolveDefaultSurfaceMeasurer](resolveDefaultSurfaceMeasurer) at the editor surface: that keeps the fixed fallback for SSR/tests in one place.

declare function tryCreateCanvasMeasurer(options?: CanvasMeasurerOptions): TextMeasurer | null;

unionLayoutBoxesfunctionSource ↗

Axis-aligned union of boxes, or null when the list is empty.

Used when a control's content spans several fragments or spans on one page.

declare function unionLayoutBoxes(boxes: readonly LayoutBox[]): LayoutBox | null;

utf16OffsetToGraphemefunctionSource ↗

The grapheme index containing a UTF-16 offset. Clamps rather than throwing.

Backed by a single-entry index cache: this runs once per character during paragraph layout, and re-segmenting per call is what made a 20,000-character paragraph take minutes to open. One entry rather than a map, because the keys are file-derived strings of unbounded size.

declare function utf16OffsetToGrapheme(text: string, utf16Offset: number): number;

walkStoryParagraphsfunctionSource ↗

Collect paragraphs of a block list in document order, descending into tables.

Caps nesting so a hostile nested-table document cannot recurse without bound.

declare function walkStoryParagraphs(blocks: readonly OoxmlElement[], maxTableDepth?: number): OoxmlElement[];

withDefaultTabIntervalfunctionSource ↗

Republish stops under a document-wide default-tab interval (w:defaultTabStop).

The cascade resolves stops from the paragraph's own property chain, which cannot see settings.xml; the interval is a document constant that arrives from the session. Returns the input unchanged when nothing moves, so a cache-key fingerprint stays stable.

declare function withDefaultTabInterval(tabs: ResolvedTabStops, defaultIntervalPt: number | undefined): ResolvedTabStops;

Resolve w:numStyleLink delegation using the document's styles (§17.9.21).

Without a style table there is nothing to follow, so the index is returned unchanged — and so it is when nothing delegates, which keeps layout cache identity.

declare function withNumberingStyleLinks(index: NumberingIndex, styleCascade: StyleCascadeTable | undefined): NumberingIndex;

withResolvedListItemsfunctionSource ↗

Attach a full-story list-item map to layout options.

Resolves once over blocks (body story including table cells) so counters continue across section boundaries. No-ops when numbering is absent.

declare function withResolvedListItems<T extends {
    readonly numberingIndex?: NumberingIndex;
    readonly listItems?: ReadonlyMap<string, ResolvedListItem>;
    readonly styleCascade?: StyleCascadeTable;
    readonly isFontAvailable?: (family: string) => boolean;
}>(options: T, blocks: readonly OoxmlElement[]): T & {
    readonly numberingIndex: NumberingIndex;
    readonly listItems?: ReadonlyMap<string, ResolvedListItem>;
};

wordBoundaryfunctionSource ↗

The next word boundary from offset, in direction.

Word-LEFT skips any whitespace immediately behind the caret and then the word behind that, which is what every editor does and what makes repeated presses walk words rather than alternate between a word and the space before it. Word-RIGHT stops at the END of the current word, then skips the following whitespace on the next press.

declare function wordBoundary(text: string, offset: number, direction: -1 | 1): number;

wordSegmentsToGraphemeRecordsfunctionSource ↗

Map UTF-16 word segments to grapheme-safe half-open ranges within one paragraph.

declare function wordSegmentsToGraphemeRecords(text: string, segments: readonly WordSegment[]): readonly GraphemeWordSegmentRecord[];

Classes (5)

FontResolutionErrorclassSource ↗

A face that could not be admitted, carrying whatever evidence the refusal produced.

RETURNED rather than thrown by FontResourceSnapshot.resolve, because a missing face is an ordinary condition — layout falls back and carries on. It extends Error so a caller that would rather throw can.

declare class FontResolutionError extends Error
MemberTypeSummary
(constructor)Constructs a new instance of the `FontResolutionError` class
actual?number
actualHash?string
codeFontResolutionErrorCode
diagnostic?string
expectedHash?string
limit?number
name
requestFontRequest

HarfBuzzShapingErrorclassSource ↗

A shaping call that was refused, carrying the limit it exceeded where there was one.

Thrown rather than returned: unlike a missing font, a run that cannot be shaped has no sensible fallback measurement, and continuing would lay text out at made-up widths.

declare class HarfBuzzShapingError extends Error
MemberTypeSummary
(constructor)Constructs a new instance of the `HarfBuzzShapingError` class
actual?number
codeHarfBuzzShapingErrorCode
diagnostic?string
limit?number
name

ResolvedCacheclassSource ↗

The layout measurement cache, keyed by fingerprint rather than by revision.

An entry may be reused ACROSS revisions: the model revision is recorded as PROVENANCE, not as an equality condition. Reuse is proven only when the transitive dependency fingerprint, the unit's own input fingerprint, and every non-model environment input all match — which is what lets an edit in one paragraph leave the rest of a long document measured.

declare class ResolvedCache<V>
MemberTypeSummary
evictEpochDrop entries produced against a stale operation epoch — restart affected work on an epoch change (8.3). Returns the number evicted.
evictResourcesEvict only entries whose consumed resource fingerprint changed at the new resource epoch.
getLook up `key` against the CURRENT fingerprints + snapshot (revision excluded from the match). A hit proves the entry is unaffected; a miss names the reason (absent/dependency/input/epoch).
setStore a freshly computed entry with full provenance.
sizenumber

TablePaginationErrorclassSource ↗

Bounded table pagination failure. Prefer this over emitting a fragment that overflows the page content box.

declare class TablePaginationError extends Error
MemberTypeSummary
(constructor)Constructs a new instance of the `TablePaginationError` class
codeTablePaginationErrorCode

UnsupportedScriptErrorclassSource ↗

A code point whose script this engine does not itemize.

Carries the offending codePoint so the caller can report which character stopped it, rather than failing anonymously somewhere in the middle of a paragraph.

declare class UnsupportedScriptError extends Error
MemberTypeSummary
(constructor)Constructs a new instance of the `UnsupportedScriptError` class
code
codePointnumber
name

Interfaces (159)

AbstractNumDefinitioninterfaceSource ↗

One w:abstractNum — the shape of a list, without being a list.

Never referenced by a paragraph directly. Paragraphs name a [NumDefinition](NumDefinition), which names one of these, so several lists can share a definition and still count separately.

interface AbstractNumDefinition
MemberTypeSummary
abstractNumIdstring
levelsReadonlyMap<number, NumberingLevel>

BidiEmbeddingLevelsinterfaceSource ↗

UAX #9 embedding levels, one per UTF-16 code unit, plus the paragraph ranges they were resolved within.

The level is EXACT, not a direction: its parity gives direction, but the numeric value is what reordering needs, and collapsing it early loses nested isolates.

interface BidiEmbeddingLevels
MemberTypeSummary
levelsUint8Array
paragraphsreadonly { readonly start: number; readonly end: number; readonly level: number; }[]

BorderGridGeometryinterfaceSource ↗

The absolute grid a table's borders are drawn on: column widths, and per-row tops and heights.

Shared by every edge so adjacent cells resolve to the SAME line, rather than each computing its own and leaving a hairline gap between them.

interface BorderGridGeometry
MemberTypeSummary
cellBoxesreadonly (readonly { readonly width: number; readonly height: number; }[])[]Per laid-out cell: width/height after vMerge expansion (cell-local stroke space).
columnWidthsPtreadonly number[]Absolute column widths for the whole table (points).
rowBandsreadonly { readonly y: number; readonly height: number; }[]Per laid-out row: absolute top and height in the same coordinate space as cell boxes (only relative differences matter for vertical edge segmentation).

CacheProvenanceinterfaceSource ↗

Everything recorded with a cache entry: the model revision (provenance only) + the fingerprints and snapshot that DO gate reuse.

interface CacheProvenance extends OperationSnapshot
MemberTypeSummary
dependencyFingerprintstringFingerprint of the transitive dependency closure (see DependencyGraph.fingerprint).
inputFingerprintstringFingerprint of the unit's own direct inputs (its content).
resourceDependenciesreadonly ResourceDependencyProvenance[]Exact operation resources this entry consumed, sorted by key.
revisionnumberModel revision the entry was computed at — provenance, NOT compared for reuse.

CanvasMeasurerOptionsinterfaceSource ↗

How the canvas measurer resolves fonts, scales, and bounds its caches. Every field optional.

Layout never creates a canvas itself: without a context this measurer does not exist and the surface falls back to fixed metrics.

interface CanvasMeasurerOptions
MemberTypeSummary
context?CanvasTextContext | nullInjected 2d text context from the editor/browser seam.
fallbackFamily?string
fontAlias?(family: string) => string | undefinedThe engine-minted family a document-embedded face was registered under, if any.
maxMetricsEntries?numberDistinct font-shorthand line metrics retained before LRU eviction.
maxWidthEntries?numberUnique `(font, text)` width entries retained before LRU eviction.
scale?numberLayout units to CSS pixels — the same value the painter uses.

CanvasTextContextinterfaceSource ↗

The slice of a 2D canvas context measurement needs.

A structural subset rather than CanvasRenderingContext2D, so layout stays DOM-free and a test can supply a deterministic stub.

interface CanvasTextContext
MemberTypeSummary
fontstring
measureText

CanvasTextMetricsinterfaceSource ↗

The canvas text-metrics surface the editor injects.

Structural subset of CanvasRenderingContext2D — declared here so the layout lane stays off the DOM lib. Hosts pass a real 2d context; tests pass a controllable mock.

interface CanvasTextMetrics
MemberTypeSummary
fontBoundingBoxAscent?number
fontBoundingBoxDescent?number
widthnumber

CaretAtOptionsinterfaceSource ↗

How caret geometry is resolved.

preferPage disambiguates a paragraph that paints on SEVERAL pages — a shared header appears once per page, and without a preference the caret could be placed on any of its copies.

interface CaretAtOptions
MemberTypeSummary
measurer?TextMeasurer
preferredPageIndex?numberPrefer geometry from this sheet when the same paragraph paints on multiple pages (shared header/footer copies).

CaretGeometryinterfaceSource ↗

A caret position with the geometry that renders it.

interface CaretGeometry
MemberTypeSummary
heightnumber
lineIdstring
pageIndexnumber
positionSemanticPosition
xnumberPage-relative, in the same coordinate space as the line boxes.
ynumber

CascadedParagraphFormattinginterfaceSource ↗

A paragraph's properties after the cascade, plus the same list WITHOUT its own w:pPr.

Both, because a writer needs to know what a paragraph INHERITS to decide whether setting a value is a change or a no-op — and writing back an inherited value freezes it into the paragraph as though the author had chosen it.

interface CascadedParagraphFormatting
MemberTypeSummary
inheritedParagraphPropertiesreadonly OoxmlProperty[]The same list WITHOUT the paragraph's own `w:pPr` — everything it inherits.
markRunPropertiesreadonly OoxmlProperty[]Content cascade plus direct `w:pPr/w:rPr` — empty-line metrics and last-line mark height.
paragraphPropertiesreadonly OoxmlProperty[]Flat paragraph properties in cascade order (defaults → bases → style → direct).
paragraphPropertyNodesreadonly OoxmlNode[]Matching `w:pPr` nodes for nested border resolution.
runPropertiesreadonly OoxmlProperty[]Inherited run properties for CONTENT runs (before direct run `rPr`).
styleIdstring | nullThe style this paragraph resolved to, or null when it names none and there is no document default. `w:contextualSpacing` compares neighbours by exactly this.

CellBorderBoxinterfaceSource ↗

The four resolved edges of one cell, after conflict resolution against its neighbours.

interface CellBorderBox
MemberTypeSummary
bottomTableBorderSide
leftTableBorderSide
rightTableBorderSide
topTableBorderSide

CellMarginsPtinterfaceSource ↗

Resolved cell padding in points, after the table default and any per-cell override.

interface CellMarginsPt
MemberTypeSummary
bottomnumber
leftnumber
rightnumber
topnumber

CellSelectioninterfaceSource ↗

A rectangle of table cells.

interface CellSelection
MemberTypeSummary
cellIdsreadonly string[]Every selected `w:tc`, in document order, with merges resolved.
columns{ readonly from: number; readonly to: number; }Inclusive grid columns.
kind'cells'
rows{ readonly from: number; readonly to: number; }Inclusive row ordinals within the table.
tableIdstringCanonical node id of the `w:tbl`.
textSemanticSelectionThe equivalent text range.

CommentAnchorinterfaceSource ↗

Where a comment is anchored, as a range.

orphaned records that the file did not give this comment a usable range — a reference with no range markers, or a start with no end. The comment is still listed, marked orphaned, rather than dropped: a reviewer's remark disappearing silently is worse than one that says it lost its text.

interface CommentAnchor
MemberTypeSummary
commentIdstring
endCommentPosition
orphanedboolean
partNamestringCanonical name of the part the range lives in, so a header comment is attributable.
startCommentPosition

CommentPositioninterfaceSource ↗

A position in one story: a paragraph node id plus a UTF-16 offset inside it.

interface CommentPosition
MemberTypeSummary
offsetnumber
paragraphIdstring

CommentRecordinterfaceSource ↗

One comment as authored in word/comments.xml.

interface CommentRecord
MemberTypeSummary
authorstring
blocksreadonly OoxmlElement[]Body paragraphs, as tree nodes, so the surface renders measured text rather than a string.
date?string
idstring
initials?string
paraId?string`w14:paraId` of the first body paragraph — the key thread state is stored under.
parentCommentId?string`@w16cid:parentId` — the `w:id` of the comment this replies to, when the file names it.

CommentThreadStateinterfaceSource ↗

Thread state for one comment, read from commentsExtended.xml.

interface CommentThreadState
MemberTypeSummary
doneboolean
parentParaId?string`@w15:paraIdParent` — the comment this one replies to, absent for a top-level comment.

CompoundBorderMetricsinterfaceSource ↗

How a multi-line border style (double, triple) is drawn: stroke width, gap, and total extent.

The band is CENTRED on the authored width, so a double border occupies the space Word gives it rather than growing the cell it surrounds.

interface CompoundBorderMetrics
MemberTypeSummary
extentPtnumber
gapPtnumber
insetPtnumberCenters the compound band on the authored width; negative extends outward.
strokePtnumber

ContentControlBoundaryRecordinterfaceSource ↗

Layout-published boundary for one content control (w:sdt / typed contentControl).

Chrome, lock feedback, and hit resolution read this record — never painted DOM. The wrapper itself is not a layout box; fragments cover the content that already flowed in place.

interface ContentControlBoundaryRecord
MemberTypeSummary
alias?string
boundboolean`w:dataBinding` is present — content edits are refused as bound.
controlTypeContentControlMappedType
effectiveLockContentControlLockNested lock union with every ancestor control, collapsed back to a single `ST_Lock` vocabulary value (both axes locked → `sdtContentLocked`).
fragmentsreadonly ContentControlGeometryFragment[]
idstringCanonical node id of the control wrapper — not `w:id`.
levelContentControlLevel
lockContentControlLockThis control's own `w:lock`, before ancestor union.
nestingDepthnumber0 for a top-level control; increments through nested wrappers under the shared nesting bound.
placeholderboolean`w:showingPlcHdr` is present on the control's properties.
tag?string

ContentControlFragmentRecordinterfaceSource ↗

One page-local rectangle covering the part of a control that sits on that page.

interface ContentControlFragmentRecord extends LayoutBox
MemberTypeSummary
pageIndexnumber

ContentControlGeometryFragmentinterfaceSource ↗

One piece of a control's content geometry.

A block control that crosses a page break publishes one fragment per page rather than a single rectangle covering the inter-page gap. An inline control publishes one fragment per LINE it touches, covering the text's vertical extent (line-spacing leading excluded), so a wrapped control never claims the words beside it. Coordinates match fragment boxes (page-content space).

interface ContentControlGeometryFragment
MemberTypeSummary
boxLayoutBox
pageIndexnumber

DeclaredFontSubstitutioninterfaceSource ↗

A host-declared redirect: requests for from resolve to to.

How a metric-compatible substitute is wired in — a document naming Calibri resolves to Carlito without the document being rewritten.

interface DeclaredFontSubstitution
MemberTypeSummary
fromFontRequest
toFontRequest

DocumentSectioninterfaceSource ↗

One section of the body story: contiguous top-level blocks plus the properties that end it.

blockStart / blockEndExclusive index into storyBlocks(part).

interface DocumentSection
MemberTypeSummary
blockEndExclusivenumber
blockStartnumber
indexnumber
propertiesSectionProperties

DocumentSectionsEnumerationinterfaceSource ↗

Every section in a document, with a flag saying whether the list was cut short.

truncated is reported rather than silent: section count comes from a file, so a crafted document declaring thousands of paragraph-level w:sectPr marks is bounded, and a reader should be able to tell that happened.

interface DocumentSectionsEnumeration
MemberTypeSummary
sectionsDocumentSection[]
truncatedbooleanTrue when paragraph-level sectPr marks beyond [MAX_DOCUMENT_SECTIONS](MAX_DOCUMENT_SECTIONS) were dropped.

DrawingOverlayFrameinterfaceSource ↗

Page-content overlay rectangle for a drawing's painted extent.

interface DrawingOverlayFrame
MemberTypeSummary
heightnumber
pageIndexnumber
recordInlineDrawingRecord | AnchoredDrawingRecord
widthnumber
xnumberRelative to [PageRecord.contentBox](PageRecord.contentBox).
ynumber

FontFingerprintInputsinterfaceSource ↗

A font reduced to the values that identify it for fingerprinting.

Includes the content hash and faceIndex, so two faces with the same family name but different bytes fingerprint differently — which is what stops a cached measurement being reused against a substituted face.

interface FontFingerprintInputs
MemberTypeSummary
byteLengthnumber
faceIndexnumber
familystring
hashstring
idstring
identitystring
requestFontRequest
substitutionFontSubstitution | null

FontRequestinterfaceSource ↗

A face, as something asks for it: family plus the two axes this engine admits.

Only static weights and slants. Variable-font axes are deliberately outside this vocabulary — the shaper refuses variation axes, and a variable file admitted here would render bold at regular weight.

interface FontRequest
MemberTypeSummary
familystring
style'normal' | 'italic'
weightnumber

FontResourceDefinitioninterfaceSource ↗

One face offered to a snapshot, before admission.

hash is checked against the bytes, so a source cannot claim a face it did not supply. Setting availability to forbidden declares a face that exists but must not be used, which resolves as a typed refusal rather than as "missing".

interface FontResourceDefinition
MemberTypeSummary
availability?'available' | 'forbidden'
bytesUint8Array
faceIndexnumber
hashstring
idstring
requestFontRequest

FontResourceInstrumentationinterfaceSource ↗

Optional counters for the operations font admission is expected to do RARELY.

Exists so tests can assert the absence of work: hashing and byte-copying a 64 MB face on every resolve would not be visible in output, only in a stalled document, so the checks assert these fire once rather than per call.

interface FontResourceInstrumentation
MemberTypeSummary
onAdmission?() => void
onHash?() => void
onOwnedByteCopy?() => void
onTableScan?() => void

FontResourceSnapshotinterfaceSource ↗

An immutable set of admitted faces, and the only way to reach one.

epoch identifies the snapshot: fonts change by REPLACING it, never by mutation, so a layout pass holds one snapshot for its whole run and cannot observe a face appearing or vanishing midway.

interface FontResourceSnapshot
MemberTypeSummary
epochnumber
resolveThe admitted face, or a typed refusal. Never throws.

FontResourceSnapshotOptionsinterfaceSource ↗

How a snapshot is built: which faces, under what limits, validated how.

maxFontBytes is clamped to [HARD_MAX_FONT_BYTES](HARD_MAX_FONT_BYTES) — a caller may tighten the budget but never widen it past the engine's own ceiling.

interface FontResourceSnapshotOptions
MemberTypeSummary
epochnumber
instrumentation?FontResourceInstrumentation
maxFontBytesnumber
resourcesreadonly FontResourceDefinition[]
substitutions?readonly DeclaredFontSubstitution[]
validateFontFontByteValidator

FontSubstitutioninterfaceSource ↗

A request that was answered by a DIFFERENT face than the one asked for.

Recorded rather than silently applied, because a substitution changes measurement: it is part of the shaping fingerprint, so a cached run shaped against a substitute is never reused for the real face.

interface FontSubstitution
MemberTypeSummary
requestedFontRequest
resolvedFontRequest

GlyphOutlineinterfaceSource ↗

A glyph's outline as SVG path data, in font design units.

interface GlyphOutline
MemberTypeSummary
pathstring
unitsPerEmnumberDesign units per em — divide by this to scale the path to a point size.

GraphemeBoundaryinterfaceSource ↗

The replaceable segmentation strategy.

An explicit seam rather than a direct Intl.Segmenter call, so tests can install a deterministic boundary and a runtime without Intl.Segmenter can be given one.

interface GraphemeBoundary
MemberTypeSummary
segment

GraphemeSegmentinterfaceSource ↗

One user-perceived character, and the UTF-16 range that encodes it.

The unit a caret moves by. An emoji with a skin-tone modifier is ONE segment spanning several UTF-16 code units, so stepping by code unit would put the caret inside it.

interface GraphemeSegment
MemberTypeSummary
indexnumber
textstring
utf16Fromnumber
utf16Tonumber

GraphemeWordSegmentRecordinterfaceSource ↗

A word span expressed in GRAPHEME offsets rather than UTF-16 ones.

What selection actually uses: a range whose ends are UTF-16 offsets could land inside a grapheme, and selecting half an emoji is not a word.

interface GraphemeWordSegmentRecord
MemberTypeSummary
graphemeFromnumber
graphemeTonumber
wordLikeboolean

HarfBuzzFaceCacheEventinterfaceSource ↗

One face-cache transition, for instrumentation.

interface HarfBuzzFaceCacheEvent
MemberTypeSummary
identitystring
kind'created' | 'hit' | 'evicted'

HarfBuzzOutlineCacheEventinterfaceSource ↗

One outline-cache transition, for instrumentation.

interface HarfBuzzOutlineCacheEvent
MemberTypeSummary
kind'created' | 'hit' | 'evicted' | 'skipped' | 'cleared'
retainedBytesnumber

HarfBuzzShapeCacheEventinterfaceSource ↗

One shape-cache transition, for instrumentation.

interface HarfBuzzShapeCacheEvent
MemberTypeSummary
kind'hit' | 'miss' | 'stored' | 'evicted' | 'skipped' | 'cleared'
retainedBytesnumber

HarfBuzzTextShaperinterfaceSource ↗

A [TextShaper](TextShaper) backed by HarfBuzz, holding WASM resources.

[HarfBuzzTextShaper.dispose](HarfBuzzTextShaper.dispose) is not optional housekeeping: the cached faces are WASM allocations that garbage collection cannot reclaim, so a shaper outliving its editor leaks until the page goes away.

interface HarfBuzzTextShaper extends TextShaper
MemberTypeSummary
dispose

HarfBuzzTextShaperInstrumentationinterfaceSource ↗

Optional counters for the work a shaper is expected to do rarely.

Exists so tests can assert the ABSENCE of work — re-opening a face or re-copying its bytes per shape call would not change any output, only make typing slow, which no rendering assertion would catch.

interface HarfBuzzTextShaperInstrumentation
MemberTypeSummary
onByteCopy?() => void
onFaceCacheEvent?(event: HarfBuzzFaceCacheEvent) => void
onOutlineCacheEvent?(event: HarfBuzzOutlineCacheEvent) => void
onOutlinePathCall?() => void
onShapeCacheEvent?(event: HarfBuzzShapeCacheEvent) => void
onShapeCall?() => void
onTableScan?() => void

HarfBuzzTextShaperOptionsinterfaceSource ↗

Resource budgets and cache sizes for one shaper. Every field optional.

The caches exist because shaping is the expensive step: a face is opened once and reused, and identical runs return their previous result. The caps exist because file-derived input decides how much work is asked for.

interface HarfBuzzTextShaperOptions
MemberTypeSummary
instrumentation?HarfBuzzTextShaperInstrumentation
maxCachedFaces?number
maxCachedOutlineBytes?number
maxCachedShapeBytes?number
maxCachedShapes?number
maxCodepoints?number
maxFontBytes?number
maxGlyphs?number
maxInputUtf16?number
maxOutlineBytes?number
maxShapedRunBytes?number

HeaderFooterStoryRecordinterfaceSource ↗

One header or footer story as it sits on one page.

box is absolute (sheet coordinates) and sized to the story's FLOW height — never to any anchored-object extent, which is the rule that keeps a decorated header's hit area from covering the body. fragments are story-relative (origin at the box's top-left). Baseline furniture may be shared across pages of the same variant when the story has no allowlisted PAGE/NUMPAGES/SECTIONPAGES fields; after page-field finalize, projections are per page (or per distinct field values for count-only stories).

interface HeaderFooterStoryRecord
MemberTypeSummary
anchoredDrawings?readonly AnchoredDrawingRecord[]Anchored drawings owned by this story, in story-relative coordinates.
boxLayoutBox
fragmentsreadonly BlockFragmentRecord[]
kind'header' | 'footer'
pageFieldProjector?(context: { readonly pageNumber: number; readonly pageCount: number; readonly sectionPageCount?: number; readonly format?: string; }) => HeaderFooterStoryRecordTransient projector used between furniture attach and document-level page-field finalize. Absent on published layout records after finalize.
partNamestring
rId?stringMain-document relationship id that resolves to this part (`EditorScope.rId`).
variant'default' | 'first' | 'even'

HitPointinterfaceSource ↗

A point in the coordinate space named by the function taking it.

interface HitPoint
MemberTypeSummary
xnumber
ynumber

HitTestOptionsinterfaceSource ↗

How precisely a hit resolves within a run.

Without a measurer the offset is INTERPOLATED across the span's advance, which is exact only for monospaced text — supply one for proportional fonts, or a click lands a character or two away from the glyph under the pointer.

interface HitTestOptions
MemberTypeSummary
measurer?TextMeasurerExact resolution of the character within a run.
verticalWeight?numberVertical weight for the nearest-block rule.

KeyedRangeinterfaceSource ↗

A model range to highlight, and the key the caller knows it by.

interface KeyedRange
MemberTypeSummary
fromSemanticPosition
keystring
toSemanticPosition

LayoutBoxinterfaceSource ↗

A rectangle in layout POINTS.

Points everywhere in this layer — twips convert at property-read boundaries and CSS pixels at paint. A box carrying either of those would eventually be added to one carrying the other.

interface LayoutBox
MemberTypeSummary
heightnumber
widthnumber
xnumber
ynumber

LayoutCacheStatsinterfaceSource ↗

Cache counters, for asserting that incremental layout is actually reusing work.

interface LayoutCacheStats
MemberTypeSummary
evictionsnumber
hitsnumber
missesnumber
sizenumber

LayoutSchedulerinterfaceSource ↗

Coalesces commits into layout passes.

A keystroke is one commit but must not be one full layout pass, so changes accumulate into a scope and are laid out together. Every published layout is tagged with the revision it actually read, which is what makes a stale result detectable rather than merely late.

interface LayoutScheduler
MemberTypeSummary
cancelDrop pending work without publishing — for teardown.
cancelledRunsnumberHow many cooperative runs were abandoned because a newer revision arrived.
flushRun any pending work now, synchronously. Returns whether a layout was published.
invalidateAllRequest a relayout for a reason the store did not report (page size, zoom, fonts).
notifyRecord a commit. Coalesces with anything already pending.
pendingThe scope that would be used if `flush` ran now, or null when nothing is pending.
staleDiscardsnumberHow many layouts were discarded for being stale. Diagnostics, and a test hook.

LayoutSchedulerOptionsinterfaceSource ↗

How the scheduler produces layouts and when it publishes them.

interface LayoutSchedulerOptions
MemberTypeSummary
currentRevision() => numberThe model's revision right now. Read at publish time, never cached.
publish(layout: SemanticLayout, scope: LayoutScope) => voidCalled with a layout that is known current. Never called with a stale one.
run(scope: LayoutScope) => SemanticLayoutProduce a complete layout for the CURRENT model state.
runCooperatively?(scope: LayoutScope) => CooperativeRunRun a layout in slices instead of in one call (task 9.5).
schedule?(run: () => void) => () => voidDefer work to a later turn, returning a canceller.

LayoutScopeinterfaceSource ↗

What a batch of commits can affect.

paragraphIds is the set the commits touched directly. What that set MEANS depends on impact: for a local impact it bounds the work, for a structural one it only says where the reflow starts.

interface LayoutScope
MemberTypeSummary
createdReadonlySet<string>Ids the commits created — no previous layout exists for these.
deletedReadonlySet<string>Ids the commits removed — any retained layout for these must be released.
dependencyKeysReadonlySet<string>Cache keys the commits invalidated (styles, numbering, fonts).
impactImpactClass
paragraphIdsReadonlySet<string>Node ids touched directly by the coalesced commits.
revisionnumberThe revision this scope describes — the layout result must carry the same one.
structuralbooleanTrue when the block SEQUENCE changed, so ids alone cannot bound the reflow.

LayoutSessioninterfaceSource ↗

Carried-over state that makes layout incremental.

A caller creates one and hands the SAME object back each pass. It holds the previous pages, the per-block cache keys and the flow checkpoints a pass resumes from, so an edit low in a document re-lays only what follows it. A no-change pass returns the previous pages by identity.

interface LayoutSession
MemberTypeSummary
balanceLimitnumber | nullColumn-height limit chosen by the last balanced multi-column pass, or null when the last pass did not balance.
checkpointsFlowCheckpoint[]
contextstringGeometry and producer of the previous pass; a change to either forces a full pass.
endCursorYnumberFlow state after the last block of the previous pass, for a section that CONTINUES onto this one's last sheet (`w:type="continuous"`).
endLineCounternumberLine counter after the last block of the previous pass.
endsOpenPagebooleanWhether the last page of that pass was still open (no trailing page break).
endSpaceAfternumber
keysstring[]
multiMultiSectionLayoutState | nullPresent when the last pass was multi-section; child sessions live here.
notePageBottomReservesReadonlyMap<number, number> | nullPage-bottom footnote reserves from the last published notes layout.
statsLayoutSessionStats

LayoutSessionStatsinterfaceSource ↗

What the last layout pass actually did — the observable evidence that incremental layout is working.

reusedPages and fullPasses are the ones that matter: a typing keystroke that rebuilds every page produces identical output and unusable performance, so the tests assert on these rather than on the rendered result.

interface LayoutSessionStats
MemberTypeSummary
fullPassesnumberPasses that could not resume and laid the document out from the top.
placednumberParagraphs placed by the last pass, against the number in the document.
reusedPagesnumberPages carried over from the previous layout without being rebuilt.
totalnumber

LayoutShapingOptionsinterfaceSource ↗

A fully resolved shaping bundle: fonts, shaper, and the environment they were admitted under. Produced by the editor lane's font configuration (createLayoutShaping) and consumed to build shaped measurers. Lived in the legacy metrics.ts until the legacy layout lane was deleted; the type is the surviving contract between the two lanes.

interface LayoutShapingOptions
MemberTypeSummary
defaultFont{ readonly family: string; readonly sizeHalfPoints: number; }
environment{ readonly variationAxes: Readonly<Record<string, number>>; readonly shapingLibrary: VersionedShapingLibrary; readonly unicodeDataVersion: string; readonly normalization: NormalizationPolicy; readonly language: string; readonly features: Readonly<Record<string, number>>; readonly fixedPointScale: number; readonly roundingMode: FixedPointRoundingMode; }
fontsFontResourceSnapshot
ligatureCaretPolicy'cluster-edges-only'
operationOperationSnapshot
shaperTextShaper

LevelOverrideinterfaceSource ↗

One w:lvlOverride on a w:num: a restart value, a replacement level, or both.

How two lists share an abstract definition while numbering independently.

interface LevelOverride
MemberTypeSummary
level?NumberingLevelFull level replacement when `w:lvl` is present under the override.
startOverride?number

LineRecordinterfaceSource ↗

One laid-out line: its geometry, its baseline, and the styled spans it renders.

The unit hit-testing and caret placement resolve against. Geometry comes from HERE, never from the DOM, which is what lets an empty paragraph still get a caret.

interface LineRecord
MemberTypeSummary
baselinenumberDistance from the line box top to the text baseline.
boxLayoutBox
contentXnumberWhere the line's content actually starts, after alignment and the first-line indent.
deletedRanges?readonly ModelRange[]Model ranges on this line covering DELETED content, absent when there is none.
drawings?readonly InlineDrawingRecord[]Inline drawings on this line, absent when there are none.
idstring
leadingnumberSpace ABOVE the glyph band inside [box](box) (exact centering, not auto/atLeast).
rangeSourceRange
spansreadonly StyleSpanRecord[]
trailingSpacing?numberAuto/atLeast line-spacing depth BELOW the glyph band, inside [box](box).

ListCounterAdvanceinterfaceSource ↗

The result of counting ONE list paragraph: the level that applied, the counter vector after it, and the marker text a reader sees.

Counters are a vector across all nine levels, not a single number, because a deeper level restarting resets the ones below it while leaving those above intact.

interface ListCounterAdvance
MemberTypeSummary
abstractNumIdstringEffective abstract numbering template for this num instance.
countersreadonly number[]Counter vector after this item was counted (indices 0..8).
ilvlnumber
levelNumberingLevel
markerTextstringExpanded marker text (empty when vanished / empty lvlText).
numIdstring

ListCounterStateinterfaceSource ↗

The running counters for one layout pass over one story.

Stateful and order-dependent by nature: a list number is a function of every numbered paragraph before it, which is why markers are computed during layout and cannot be read off a paragraph in isolation.

interface ListCounterState
MemberTypeSummary
advanceAdvance counters for one list paragraph.

ListMarkerRecordinterfaceSource ↗

A numbering marker as layout published it.

Geometry is in the same coordinate space as the fragment (page-content or cell-content relative). Paint positions from this box and MUST NOT remeasure the marker.

interface ListMarkerRecord
MemberTypeSummary
boxLayoutBox
levelnumberThe `w:ilvl` this marker was resolved at, 0..8.
numFmtstring`w:numFmt` of the resolved level — `bullet` or a numbering format.
numIdstringThe `w:numId` this marker resolved through.
styleResolvedRunStyle
textstring

MaterializationInputinterfaceSource ↗

Which pages to build in detail.

A page left out keeps its size and position but no content, so the document's height and page count are unchanged and scrolling to it reveals it rather than reflowing everything below.

interface MaterializationInput
MemberTypeSummary
layoutSemanticLayout
overscanPages?numberExtra pages kept ready either side of the visible band.
pinnedPages?Iterable<number>Pages that must be built wherever they are — caret, selection, a search hit.
viewport?ViewportWindowOmitted means "no viewport": everything is materialized, which is the honest default.

MoveCaretOptionsinterfaceSource ↗

How a caret move resolves.

Story-scoped stops are REQUIRED when navigating inside an open header or footer: the body's stops describe a different story, and moving through them would walk the caret out of the furniture the user is editing.

interface MoveCaretOptions
MemberTypeSummary
measurer?TextMeasurer
stops?readonly CaretGeometry[]Precomputed stops for the active story. Open header/footer navigation MUST pass story-scoped stops from [caretStopsForBlocks](caretStopsForBlocks); body keeps the default.

NoteDisplayMarkinterfaceSource ↗

The mark a note reference paints.

null where w:customMarkFollows suppresses it: the document supplies its own glyph, and painting an automatic number too would show the note twice.

interface NoteDisplayMark
MemberTypeSummary
displayNumber?number1-based automatic sequence number when assigned; absent when suppressed.
markstring | nullFormatted mark, or `null` when suppressed by customMarkFollows.
noteIdnumber

NoteMarkContextinterfaceSource ↗

Lookup of derived display marks keyed by [formatNoteScopeId](formatNoteScopeId).

null mark = customMarkFollows (no automatic digits). Absent key = dangling / unknown — fail-open with an empty display (model atom preserved).

interface NoteMarkContext
MemberTypeSummary
activeNoteKey?stringScope id of the note story currently being laid out (`footnote:N` / `endnote:N`).
marksReadonlyMap<string, string | null>scopeId → formatted mark (or null when suppressed).
reservedMarkText?stringWhen set, every automatic mark measures at least this string's width (eachPage reservation). The string is the widest-measuring candidate under the effective mark style (actual marks plus a bounded per-section value window) — not merely the longest by codepoint count. Display text may still be the real mark; measurement uses the wider of the two so digit-width / proportional-glyph feedback cannot oscillate.

NoteReferenceSiteinterfaceSource ↗

Where one note is referenced from — what per-page and per-section restart rules are computed against.

interface NoteReferenceSite
MemberTypeSummary
customMarkFollows?booleanWhen true, consumes no automatic number (`customMarkFollows`).
noteIdnumberStable note id (`w:id`).
pageIndex?numberPage index of the reference when known (0-based). Required for `eachPage` restart; when omitted, `eachPage` behaves like `continuous` for that site.
sectionIndexnumberSection index of the reference (0-based).

NotesAttachResultinterfaceSource ↗

The layout with notes attached, plus any fallbacks taken and the mark context used.

The marks come back because they feed the body's incremental cache tokens: a note number that changed must invalidate the paragraph that references it.

interface NotesAttachResult
MemberTypeSummary
fallbackReasonsreadonly NotePaginationFallbackReason[]
layoutSemanticLayout
noteMarksNoteMarkContextMark context used for the final body projection (for incremental cache tokens).

NoteSeparatorLayoutinterfaceSource ↗

The rule between body text and the note area.

Synthesized when the document declares none, because Word draws one regardless — a document without an authored separator still shows the line a reader expects.

interface NoteSeparatorLayout
MemberTypeSummary
fallbackReason?NoteLayoutFallbackReasonSet when an oversize authored separator was replaced with a synthetic rule.
flowHeightnumber
fragmentsreadonly BlockFragmentRecord[]
kind'separator' | 'continuationSeparator'
ruleStyle?NoteSeparatorRuleStyleLayout-owned rule when the separator is marker-only (`w:separator` / `w:continuationSeparator`) or fully synthetic. Absent when an authored separator story has real paragraph/run/border content that paint should render as fragments.
syntheticbooleanTrue when the engine synthesized a default rule (document had none).

NotesLayoutInputinterfaceSource ↗

Everything note pagination needs: the note parts, and the per-section properties governing them.

Per-SECTION because numbering, restart rules and placement are all section properties — one document can restart footnote numbering at every section and end notes at the document end.

interface NotesLayoutInput
MemberTypeSummary
cache?ParagraphLayoutCache<readonly PendingLine[]>
defaultTabStopPt?number
documentEndnotePropsResolvedEndnoteProperties
documentFootnotePropsResolvedFootnotePropertiesDocument-level defaults (section 0 fallback).
drawingsForPart?(ownerPartName: string) => NoteStoryDrawings | undefinedInline drawing support per notes part. Absent means note paragraphs flow without drawing records, which is what a headless caller with no image port wants.
endnotePropsBySectionreadonly ResolvedEndnoteProperties[]Per-section resolved endnote properties.
endnotesPartOoxmlPart | null
footnotePropsBySectionreadonly ResolvedFootnoteProperties[]Per-section resolved footnote properties (index-aligned with document sections).
footnotesPartOoxmlPart | null
measurerTextMeasurer
producerstring
styleCascade?StyleCascadeTable

NoteStoryDrawingsinterfaceSource ↗

Inline drawing support for ONE notes part.

A note lives in /word/footnotes.xml or /word/endnotes.xml, not in the body part, so its pictures resolve against that part's relationships — the same per-part shape header/footer furniture uses. Without it a note paragraph flows with no drawing context at all and a picture inside it contributes no record: no image, and no placeholder either.

interface NoteStoryDrawings
MemberTypeSummary
drawingTokenForParagraph?(paragraph: OoxmlNode) => stringPer-paragraph projection + RESOURCE token for the break cache key.
inlineDrawingLayoutInlineDrawingLayoutContext

NoteStoryLayoutinterfaceSource ↗

One note's body laid out as its own story, in story-relative coordinates.

Relative rather than page-absolute because a note moves between pages during pagination — the page it lands on is decided after its content is measured.

interface NoteStoryLayout
MemberTypeSummary
fallbackReason?NoteLayoutFallbackReasonTrue when layout hit a named bound and returned a truncated / empty story.
flowHeightnumberHeight the blocks flow to (points).
fragmentsreadonly BlockFragmentRecord[]Story-relative fragments; origin at the story box's top-left.
noteIdnumber
noteKindNoteKind
noteTypeReturnType<typeof noteTypeOf>
scopeIdstring`footnote:N` / `endnote:N` — matches EditorScope note id encoding.

NumberingIndexinterfaceSource ↗

The bounded projection of numbering.xml that list layout resolves against.

Projection ONLY — never a mutation or serialization authority. Hostile values are dropped or clamped, and a missing definition resolves to "no list" rather than a guess.

interface NumberingIndex
MemberTypeSummary
abstractNumsReadonlyMap<string, AbstractNumDefinition>
numsReadonlyMap<string, NumDefinition>

NumberingLevelinterfaceSource ↗

One w:lvl: how this depth numbers, what its marker looks like, and how it indents.

interface NumberingLevel
MemberTypeSummary
ilvlnumber
indentNumberingLevelIndent
isLglboolean`w:isLgl` (§17.9.9): render EVERY level referenced by this level's `w:lvlText` in decimal, whatever number format those levels declare for themselves.
lvlJcListMarkerAlign
lvlRestart?number`w:lvlRestart` one-based trigger level, or `0` when the level never restarts. Omitted in XML → restart when the previous level (or any earlier level) is used.
lvlTextstring
numFmtstring
runPropertiesreadonly OoxmlProperty[]Level `w:rPr` as flat properties (for marker face / vanish).
startnumber
suffListSuffix
vanishbooleanTrue when level run props request vanish — marker must not paint.

NumberingLevelIndentinterfaceSource ↗

One level's indent, plus which parts the level actually AUTHORED.

The provenance matters: w:ind cascades per-attribute, so a level that authored only left must not overwrite an inherited hanging with a synthesized zero.

interface NumberingLevelIndent
MemberTypeSummary
firstLinenumber
hangingnumber
leftnumber
rightnumber
stated?{ readonly left: boolean; readonly right: boolean; readonly firstLineOffset: boolean; }Which of these the LEVEL actually authored.

NumDefinitioninterfaceSource ↗

One w:num — the thing a paragraph's w:numId actually names.

Points at an [AbstractNumDefinition](AbstractNumDefinition) and may override any of its levels.

interface NumDefinition
MemberTypeSummary
abstractNumIdstring
numIdstring
overridesReadonlyMap<number, LevelOverride>

OperationSnapshotinterfaceSource ↗

Immutable per-operation environment. Configuration, extension, shaping, and producer changes are coarse gates; resource changes are compared through CacheProvenance.resourceDependencies.

interface OperationSnapshot
MemberTypeSummary
configEpochnumber
extensionFingerprintstring
producerVersionnumber
resourceEpochnumber
shapingHashstring

PageFurnitureinterfaceSource ↗

Pre-laid page furniture, supplied by the host (phase 2).

Baseline stories are laid out once per variant (layoutHeaderFooterStory) for furniture height. Stories that actually contain allowlisted PAGE/NUMPAGES fields attach a projector so document-level finalize can re-layout under the known page count; field-free furniture reuses the baseline on every sheet.

interface PageFurniture
MemberTypeSummary
evenAndOddHeadersboolean
footersReadonlyMap<HeaderFooterVariantName, HeaderFooterStoryLayout>
headersReadonlyMap<HeaderFooterVariantName, HeaderFooterStoryLayout>
titlePageboolean

PageGeometryinterfaceSource ↗

Page geometry, in points.

interface PageGeometry
MemberTypeSummary
footerDistance?number`w:pgMar/@footer` — sheet edge to footer bottom, in points. Defaults to 36.
headerDistance?number`w:pgMar/@header` — sheet edge to header top, in points. Defaults to 36 (720 twips).
heightnumber
margin{ readonly top: number; readonly right: number; readonly bottom: number; readonly left: number; }
widthnumber

PageRecordinterfaceSource ↗

One laid-out page: the sheet, the content area, and everything that landed on it.

Page identity is REUSED across incremental passes — a pass that changes nothing returns the previous records by reference, which is what lets paint skip untouched pages entirely.

interface PageRecord
MemberTypeSummary
anchoredDrawings?readonly AnchoredDrawingRecord[]Page-content anchored drawings on this sheet, absent when there are none.
boxLayoutBoxThe whole sheet.
columnSeparators?readonly LayoutBox[]Layout-owned vertical rules requested by `w:cols/@w:sep`, content-box relative.
contentBoxLayoutBoxThe area inside the margins that content flows into.
contentControls?readonly ContentControlBoundaryRecord[]Content-control boundaries whose geometry intersects this page.
endnotes?NoteAreaRecordEndnotes collected on this page (sectEnd / docEnd).
footnotes?NoteAreaRecordFootnotes reserved on this page (pageBottom / beneathText / continuations).
fragmentsreadonly BlockFragmentRecord[]
header?HeaderFooterStoryRecordPage furniture for this page's variant, absent when the document declares none.
idstring
indexnumber
noteStream?PageNoteStreamOwnership of note-only overflow sheets. Absent on ordinary body pages. Layout pagination sets this so endnote hosting does not treat footnote drain pages as free body space.
pageFieldSource?{ readonly pageNumber: number; readonly sectionPageCount: number; readonly format?: string; }Section-local PAGE/SECTIONPAGES inputs for finalize. Absent → physical page index and document-wide section count (empty `w:pgNumType` behaviour).

ParagraphAutoSpacingContextinterfaceSource ↗

Where a paragraph sits, for the two contexts in which Word's auto spacing resolves to 0 instead of [AUTO_PARAGRAPH_SPACING_PT](AUTO_PARAGRAPH_SPACING_PT).

Both come from the HTML model the attribute emulates: a <li> and a <td> collapse the paragraph margin, a bare <p> does not. A caller that says nothing gets the body answer.

interface ParagraphAutoSpacingContext
MemberTypeSummary
inList?booleanThe paragraph participates in numbering (`w:numPr`), i.e. it is a list item.
inTableCell?booleanThe paragraph lives in a table cell.

ParagraphBorderEdgeinterfaceSource ↗

One resolved w:pBdr edge: its style, colour, thickness and gap.

widthPt and spacePt are already converted and CLAMPED — both come from a file, and an unbounded border width becomes a layout dimension.

interface ParagraphBorderEdge
MemberTypeSummary
colorstring | nullRRGGBB, or null when auto/missing (paint defaults to black).
shadow?true`w:shadow` — Word offsets a drop shadow behind the rule.
spacePtnumberGap from text to the rule, in points (`w:space`).
valstringAuthored `ST_Border` value (`single`, `dashed`, …).
widthPtnumberBorder thickness in points (`w:sz` is eighths of a point).

ParagraphBordersinterfaceSource ↗

A paragraph's resolved w:pBdr (ECMA-376 §17.3.1.24).

top/left/bottom/right are the four physical edges of the box. The other two are group-relative: between draws at a boundary INSIDE a run of consecutive paragraphs whose border settings are identical, and bar is the vertical change-bar rule beside the paragraph, drawn whether or not the paragraph groups with its neighbours.

interface ParagraphBorders
MemberTypeSummary
bar?ParagraphBorderEdge
between?ParagraphBorderEdge
bottom?ParagraphBorderEdge
left?ParagraphBorderEdge
right?ParagraphBorderEdge
top?ParagraphBorderEdge

ParagraphBorderStrokeRecordinterfaceSource ↗

One w:pBdr rule as layout published it.

box is the STROKE rectangle in the same coordinate space as the fragment, so paint sets a position and a colour and nothing else. It matters that paint cannot derive these itself: Word draws the side rules OUTSIDE the text column, so box.x on a left/bar stroke is left of the fragment box and a painter reasoning from the fragment alone would put it inside the text.

interface ParagraphBorderStrokeRecord
MemberTypeSummary
boxLayoutBox
edgeParagraphBorderEdge
sideParagraphBorderSide

ParagraphBottomBorderRecordinterfaceSource ↗

A bottom paragraph border as layout published it.

box is the rule's geometry in the same coordinate space as the fragment (page-content relative). Paint positions from this box and MUST NOT remeasure the border.

interface ParagraphBottomBorderRecord
MemberTypeSummary
boxLayoutBox
edgeParagraphBorderEdge

ParagraphFragmentRecordinterfaceSource ↗

The part of one paragraph that sits on one page.

A paragraph that crosses a page boundary produces several fragments that all name the SAME paragraphId, which is what lets selection and hit-testing treat it as one paragraph while pagination treats it as two boxes.

interface ParagraphFragmentRecord
MemberTypeSummary
borders?readonly ParagraphBorderStrokeRecord[]Every `w:pBdr` rule this fragment draws, in paint order.
bottomBorder?ParagraphBottomBorderRecordBottom rule on the final fragment when `w:pBdr/w:bottom` resolves; absent otherwise.
boxLayoutBox
fragmentIndexnumber0 for the first fragment of the paragraph, 1 for its continuation, and so on.
idstring
indentParagraphIndentThe paragraph's EFFECTIVE indent in points, cascade and numbering merge included.
kind'paragraph'
linesreadonly LineRecord[]
marker?ListMarkerRecordList marker painted in the hanging-indent slot of the FIRST fragment only.
markRevision?RevisionAttributionThe revision on this paragraph's own MARK (`w:pPr/w:rPr/w:ins|w:del`), absent when there is none.
paragraphIdstring
propsreadonly OoxmlProperty[]
rangeSourceRange
shading?stringValidated 6-hex paragraph shading fill (`w:pPr/w:shd`), absent for none/auto.
shadingBox?LayoutBoxGeometry of the paragraph shading band when [shading](shading) is set. Absent when there is no fill.
spacingParagraphSpacingBefore/after spacing applied to THIS fragment, in points.

ParagraphIndentinterfaceSource ↗

A paragraph's resolved indent in points, in the vocabulary w:ind uses.

left/right are signed; hanging is not (ST_TwipsMeasure). firstLine is signed even though the schema declares it unsigned, because Word's model keeps one signed first-line indent and this engine follows it.

interface ParagraphIndent
MemberTypeSummary
firstLinenumber
hangingnumber
leftnumber
rightnumber

ParagraphKeyInputsinterfaceSource ↗

Everything that decides whether a cached paragraph break is still valid.

producer is in the key because a font arriving after first paint changes every advance in the document while no content changes — without it the cache would serve the pre-font layout forever.

interface ParagraphKeyInputs
MemberTypeSummary
drawingToken?stringInline drawing projection/resource epoch for this paragraph.
exclusionToken?stringActive page exclusion zones affecting this paragraph's break.
paragraphOoxmlNode
producerstringWho produced the measurements.
propertiesreadonly OoxmlProperty[]
widthnumberAvailable width, which decides where the lines break.

ParagraphLayoutCacheinterfaceSource ↗

The per-paragraph measurement cache.

Caches the BREAK only — where a paragraph's lines fall at a given width — never its placement. An edit high in a document still repaginates everything below it, while paragraphs nobody touched are never measured again.

interface ParagraphLayoutCache<T>
MemberTypeSummary
clear
get
retainDrop entries for paragraphs a commit removed, so the cache cannot grow without bound.
set
statsLayoutCacheStats

ParagraphLayoutCacheOptionsinterfaceSource ↗

How large the paragraph cache grows before least-recently-used eviction.

interface ParagraphLayoutCacheOptions
MemberTypeSummary
maxEntries?numberEntries retained before the least recently used are dropped.

ParagraphLayoutInputsinterfaceSource ↗

Everything the line breaker needs about one paragraph, already cascaded and converted.

interface ParagraphLayoutInputs
MemberTypeSummary
alignmentAlignment
availablenumber
bordersParagraphBordersEvery `CT_PBdr` edge after cascade, not just the bottom one.
bottomBorderParagraphBorderEdge | undefined
contextualSpacingboolean`w:contextualSpacing`: drop before/after between same-style neighbours.
indent{ left: number; right: number; hanging: number; firstLine: number; }
inheritedRunPropertiesreadonly OoxmlProperty[]
lineSpacingParagraphLineSpacingResolved `w:line` / `w:lineRule`; single spacing where the cascade says nothing.
listItem?ResolvedListItemResolved list marker inputs when the paragraph participates in numbering.
markRunPropertiesreadonly OoxmlProperty[]Paragraph-mark cascade (`inheritedRunProperties` + direct `w:pPr/w:rPr`). Empty-line sizing and last-line mark height — never content-run face.
propsOoxmlProperty[]
shadingstring | undefinedValidated 6-hex paragraph shading fill from cascaded `w:pPr/w:shd`, absent for none.
spacingParagraphSpacing
styleIdstring | nullResolved paragraph style id, for the `w:contextualSpacing` neighbour comparison.
tabStopsResolvedTabStopsCascaded custom tab stops + default interval for paragraph-flow breaking.
tabStopsCacheTokenstringFingerprint folded into the paragraph layout cache key — nested `w:tabs` are absent from flat property bags, so style-inherited stops must be named explicitly.

ParagraphLineSpacinginterfaceSource ↗

Resolved line spacing: the rule, and the value it applies.

value means different things per rule — 240ths of a line under auto, points under exact and atLeast — which is why the two travel together and neither is useful alone.

interface ParagraphLineSpacing
MemberTypeSummary
ruleLineSpacingRule
valuenumber`auto`: the 240ths-of-a-line multiplier numerator. Otherwise points.

ParagraphSpacinginterfaceSource ↗

A paragraph's resolved space before and after, in points.

Already collapsed against w:contextualSpacing, so adjacent same-style paragraphs that suppress their gap arrive here with it removed rather than leaving that to whoever stacks them.

interface ParagraphSpacing
MemberTypeSummary
afternumber`w:spacing/@after`, in points.
beforenumber`w:spacing/@before`, in points.

PlacedCellinterfaceSource ↗

One painted occurrence of a cell, and where it sits.

interface PlacedCell
MemberTypeSummary
cellTableCellFragmentRecord
isHeaderRepeatboolean
pageIndexnumber
rowTableRowFragmentRecord
rowIndexnumberOrdinal within the whole table, shared by a header row and every repeat of it.
tableIdstring

PreferredWidthinterfaceSource ↗

w:tblW / w:tcW — a requested width, whose UNIT depends on its type.

Points for dxa, percent for pct, and zero for auto/nil. Reading value without type is always wrong.

interface PreferredWidth
MemberTypeSummary
typePreferredWidthType
valuenumberPOINTS for `dxa`, PERCENT (0–100) for `pct`, 0 for `auto`/`nil`.

ResolvedCellBordersinterfaceSource ↗

Layout-owned cell borders after conflict resolution and compound expansion.

Convenience top/left/bottom/right are set when that side has a single uniform full-span winner (existing consumers / CSS simple edges). Multi-interval winners live only on edgeSegments. Compound geometry is always on strokes.

interface ResolvedCellBorders
MemberTypeSummary
bottom?ResolvedTableBorderEdge
edgeSegments?readonly ResolvedTableBorderEdgeSegment[]
left?ResolvedTableBorderEdge
right?ResolvedTableBorderEdge
strokes?readonly TableBorderStrokeRecord[]
top?ResolvedTableBorderEdge

ResolvedFontinterfaceSource ↗

A face that passed admission: the bytes are present, within limits, hash-verified, and parse as a font.

Branded, so a ResolvedFont cannot be constructed by an object literal. Holding one is proof the checks ran — which is what lets shaping skip re-validating on every call.

interface ResolvedFont
MemberTypeSummary
[RESOLVED_FONT_BRAND]true
byteLengthnumberOwned byte length, available without creating a defensive byte copy.
bytesUint8Array
faceIndexnumber
familystring
hashstring
idstring
identitystring
requestFontRequest
substitutionFontSubstitution | null

ResolvedListIteminterfaceSource ↗

A paragraph's list membership fully resolved: definition, level, marker text and geometry.

markerText is already expanded through the counter state, so it is the string a reader sees rather than the w:lvlText template.

interface ResolvedListItem
MemberTypeSummary
abstractNumIdstring
cacheTokenstringFingerprint for layout cache keys (indent + level identity, not ordinal).
ilvlnumber
indentNumberingLevelIndentEffective indent after merging level + paragraph indents, in points.
markerAlignListMarkerAlign
markerStyleResolvedRunStyle
markerTextstring
numFmtstring`w:numFmt` of the resolved level — `bullet` or a numbering format.
numIdstring
suffixListSuffix

ResolvedRunStyleinterfaceSource ↗

A run's character properties after the full cascade, in the units layout works in.

Points rather than half-points, RRGGBB rather than theme references — everything already resolved, so measurement and paint never re-run the cascade per glyph.

interface ResolvedRunStyle
MemberTypeSummary
baselineShiftPtnumber`w:position`, in points. Positive raises the baseline.
boldboolean
capsboolean
characterSpacingPtnumber`w:spacing`, in points. Added to every advance.
colorstring | nullRRGGBB, or null for the inherited/automatic colour.
doubleStrikeboolean
fontFamilystring | null
fontSizePtnumberPoints. `w:sz` is half-points, so 22 becomes 11.
hiddenboolean`w:vanish` (ECMA-376 §17.3.2.45): the run is hidden text.
highlightstring | nullAn `ST_HighlightColor` name, or null.
horizontalScalePercentnumber`w:w`, as a percentage. 100 is unscaled.
italicboolean
kerningMinPtnumber`w:kern`, in points: the size at or above which kerning applies. 0 disables it.
shadingstring | nullCharacter shading fill (`w:rPr/w:shd`), validated RRGGBB, or null.
smallCapsboolean
strikeboolean
underlineResolvedUnderline | null
verticalAlignVerticalAlign

ResolvedSurfaceMeasurerinterfaceSource ↗

The measurer a surface ended up with, plus the identity its cache keys must include.

The producer string is load-bearing: the same canvas measuring against document-embedded faces produces DIFFERENT advances, so the two must not share a cache key space or a document would keep its pre-font pagination after the font arrived.

interface ResolvedSurfaceMeasurer
MemberTypeSummary
measurerTextMeasurer
producer'canvas-measurer' | 'canvas-measurer+embedded' | 'fixed-measurer'Cache-invalidation identity when the caller did not supply `producer`.

ResolvedTableBorderEdgeinterfaceSource ↗

Final edge winner; absent means the side/interval is not drawn.

interface ResolvedTableBorderEdge
MemberTypeSummary
colorstring | null
styleTableBorderStyle
widthPtnumber

ResolvedTableBorderEdgeSegmentinterfaceSource ↗

Conflict winner over one grid interval of a cell side.

gridStart/gridEnd are absolute grid column indices for horizontal sides and absolute row indices for vertical sides (half-open). startPt/endPt are cell-local along-axis positions in layout points.

interface ResolvedTableBorderEdgeSegment
MemberTypeSummary
edgeResolvedTableBorderEdge
endPtnumber
gridEndnumber
gridStartnumber
sideTableBorderSideName
startPtnumber

ResolvedTabStopsinterfaceSource ↗

A paragraph's tab stops after the style cascade, with the default interval that applies past the last explicit one.

interface ResolvedTabStops
MemberTypeSummary
defaultIntervalPtnumberDefault-tab interval in points (always positive and bounded).
stopsreadonly TabStop[]Custom stops sorted by ascending position.

ResolvedUnderlineinterfaceSource ↗

A resolved underline: its variant, and its colour when it does not follow the text.

interface ResolvedUnderline
MemberTypeSummary
colorstring | nullRRGGBB, or null when the underline follows the text colour.
variantstringThe authored `ST_Underline` variant.

ResourceDependencyProvenanceinterfaceSource ↗

One resource a cached entry consumed, and the fingerprint it had at the time.

Per-dependency rather than one global epoch, so updating one font does not evict entries that consumed a different, unchanged font.

interface ResourceDependencyProvenance
MemberTypeSummary
fingerprintstring
keystring

ReviewCommentIteminterfaceSource ↗

One comment as a review card. A reply carries parentId; OOXML gives replies no separate element, so threads are reconstructed from that link.

interface ReviewCommentItem
MemberTypeSummary
commentCommentRecord
idstring
kind'comment'
orphanedbooleanTrue when the file gave this comment no usable range.
parentId?stringThe comment this replies to, absent for a top-level comment.
parentRevisionId?stringThe REVISION this comment answers, when it covers exactly that change's characters.
rangeReviewRange | null
replyIdsreadonly string[]Replies to this comment, in document order. Empty for a reply or a childless comment.
resolvedboolean

ReviewCustomIteminterfaceSource ↗

A card contributed by a recognized custom node (defineCustomNode with a reviewCard hook), anchored at the node's range.

Informational, never resolvable: there is nothing to accept or reject, so the engine refuses those verbs on it. title and detail are HOST-authored (the definition's hook produced them), but attrs and text originate in a file an attacker controls — a surface renders every one of these as text, never markup.

interface ReviewCustomItem
MemberTypeSummary
attrsReadonly<Record<string, string>>Attrs decoded from the tag, after the definition's recognition hook. Untrusted input.
cardedbooleanWhether this node asked for a sidebar card.
data?unknownThe payload the node's control binds to, after the definition validated it.
detail?stringCard body, from the definition's `reviewCard` hook.
icon?stringGlyph for this node in the collapsed rail, as an SVG path in a `0 -960 960 960` viewBox.
idstringThe SDT node's stable id in the canonical tree.
kind'custom'
namestringThe definition's `name`.
rangeReviewRange | null
tagstringThe raw `w:tag` the node was recognized from. Untrusted input.
textstringThe SDT's literal content text. Untrusted input.
titlestringCard title, from the definition's `reviewCard` hook. Empty when `carded` is false.

ReviewModelInputinterfaceSource ↗

What the review queue derivation reads: one story part plus its comment parts.

interface ReviewModelInput
MemberTypeSummary
commentsExtendedPart?OoxmlPart | undefined`word/commentsExtended.xml`, absent when the package has none.
commentsPart?OoxmlPart | undefined`word/comments.xml`, absent when the package has none.
customNodePayloads?ReadonlyMap<string, { readonly nodeId: string; readonly label: string; readonly data: string; }> | undefined
customNodes?readonly unknown[] | undefinedCustom node definitions from the module registry, forwarded OPAQUELY.
furnitureParts?readonly OoxmlPart[] | undefinedHeader/footer story parts, in section order. Their revisions and comment anchors join the queue: a tracked change in a header is a pending decision like any other, and a queue that only walked the body silently hid it from the rail AND from Accept All.
reportCustomNodeDiagnostic?((diagnostic: unknown) => void) | undefinedWhere a capability package reports a node it could not read. Supplied per editor, so a page with two of them keeps their diagnostics apart.
storyPartOoxmlPartThe story the ranges live in — the main document, a header, a note.

ReviewParagraphAnchorinterfaceSource ↗

Where one paragraph sits, resolved once so a card is an O(1) lookup.

interface ReviewParagraphAnchor
MemberTypeSummary
contentYnumberSheet-absolute y of the page's content box.
fragmentYnumberThe fragment's own y, measured from that content box.
lines?readonly { readonly range: { readonly end: number; }; readonly box: { readonly y: number; }; }[]
pageIndexnumber

ReviewPositioninterfaceSource ↗

A position in the model offset space of one story.

interface ReviewPosition
MemberTypeSummary
offsetnumber
paragraphIdstring

ReviewRangeinterfaceSource ↗

Where an item is anchored: a range in one story.

interface ReviewRange
MemberTypeSummary
endReviewPosition
partNamestring
startReviewPosition

ReviewRevisionIteminterfaceSource ↗

One tracked change as a review card.

Keyed per DECISION rather than per site: a revision spanning three ranges is one card, because accepting it accepts all three.

interface ReviewRevisionItem
MemberTypeSummary
addressRevisionAddressThe payload `acceptRevision` / `rejectRevision` take.
addressesreadonly RevisionAddress[]EVERY address this decision covers, `address` first.
authorstring
date?string
idstringStable across renders and unique per DECISION, not per site.
kind'revision'
pairedWith?stringThe other half of a move, or the other side of a delete/insert replacement.
rangesreadonly ReviewRange[]Every site this decision touches, in document order.
readOnlybooleanTrue when the engine cannot resolve this kind, so accept and reject must not be offered.
replacedRangeCount?numberHow many leading `ranges` are the STRUCK half of a replacement.
replacedTextstringThe words a replacement removes. Empty for every other kind.
replyIdsreadonly string[]Comments answering this change, in document order.
revisionKindReviewRevisionKind
textstringText the revision covers, for the card summary. Empty for changes with no characters.

RevisionAttributioninterfaceSource ↗

One revision wrapper's provenance, as authored.

id is the verbatim @w:id string rather than a number: ST_DecimalNumber restricts xsd:integer with no bounds, so a file may carry a value outside the safe integer range, and parsing it to a number would silently merge two distinct revisions.

date is absent when the file omits it. @w:date is optional on CT_TrackChange, and inventing one is a silent content change.

interface RevisionAttribution
MemberTypeSummary
authorstring
date?string
idstring
kindRevisionKind
nodeIdstringThe wrapper's node id, so a surface can address this exact site.

ScriptIteminterfaceSource ↗

A run of text sharing one script, direction and font slot — the unit handed to the shaper.

Shaping cannot span a script change: Arabic and Latin in one call would produce wrong joining behaviour, so a run is itemized into these first.

interface ScriptItem
MemberTypeSummary
bidiLevelnumber
directionTextDirection
fromnumber
script'Zyyy' | 'Latn' | 'Grek' | 'Cyrl' | 'Hani' | 'Hebr' | 'Arab' | 'Deva' | 'Beng' | 'Thai' | 'Khmr'
slotFontSlot
tonumber

SectionColumnDefinitioninterfaceSource ↗

One explicit w:col: its width and the gap after it.

interface SectionColumnDefinition
MemberTypeSummary
gapTwipsnumberSpace after this column; zero on the final column.
widthTwipsnumber

SectionColumnsinterfaceSource ↗

A section's column layout.

Equal-width and explicit-width columns are one type because a file may declare w:num with no w:col children at all, and layout must handle both without branching at every use site.

interface SectionColumns
MemberTypeSummary
countnumber
definitions?readonly SectionColumnDefinition[]Authored `w:col` geometry when `equalWidth` is false.
equalWidth?boolean
gapTwipsnumberShared gap for equal-width columns and fallback gap for incomplete explicit definitions.
separator?boolean

SectionMarginsinterfaceSource ↗

A section's margins in twips, including the header and footer reserve bands.

interface SectionMargins
MemberTypeSummary
bottomTwipsnumber
footerTwipsnumber
gutterTwipsnumber
headerTwipsnumber
leftTwipsnumber
rightTwipsnumber
topTwipsnumber

SectionPageNumberinginterfaceSource ↗

Authored w:pgNumType (ECMA-376 CT_PageNumber).

Distinguishes three states via [SectionProperties.pageNumbering](SectionProperties.pageNumbering): - element absent → undefined (Word defaults; no empty element to re-emit) - empty <w:pgNumType/>{} (present, no authored attrs; must round-trip empty) - attributes set → only those keys appear (never invent schema defaults like fmt=decimal)

chapStyle / chapSep are preserved for consumers; PAGE projection does not yet compose chapter numbers (heading outline resolution is out of this slice).

interface SectionPageNumbering
MemberTypeSummary
chapSep?stringAuthored `@w:chapSep` when present (hyphen / period / colon / emDash / enDash).
chapStyle?numberAuthored `@w:chapStyle` outline level when present and in range.
fmt?stringAuthored `@w:fmt` (ST_NumberFormat) when present; otherwise omitted.
start?numberAuthored `@w:start` when present and in range; otherwise omitted.

SectionPropertiesinterfaceSource ↗

One section's resolved w:sectPr, as layout needs it.

Resolved, not raw: defaults the file omitted are filled in here (an absent w:type is nextPage), so layout never has to know which attributes were authored and which were inherited.

interface SectionProperties
MemberTypeSummary
breakTypeSectionBreakTypeAbsent `w:type` defaults to `nextPage`.
columnsSectionColumns
landscapeboolean
marginsSectionMargins
pageNumbering?SectionPageNumberingAuthored page-number type. Absent when `w:pgNumType` is missing; empty object when the element is present with no attributes (comprehensive-fixture shape).
pageSize{ readonly widthTwips: number; readonly heightTwips: number; }
titlePageboolean

SelectionRectinterfaceSource ↗

One painted selection rectangle, in page-relative layout points.

interface SelectionRect
MemberTypeSummary
heightnumber
pageIndexnumber
widthnumber
xnumber
ynumber

SemanticHitinterfaceSource ↗

What a point landed on: a semantic position, its caret geometry, and where it sits.

interface SemanticHit
MemberTypeSummary
caretCaretGeometry
cellTableCellAddress | nullNull outside a table; the innermost cell when tables nest.
contentControlIdstring | nullInnermost content-control boundary covering the hit point, or null outside every control.
drawingSemanticHitDrawing | nullNon-null when the hit resolved to an inline drawing atom.
lineIdstring
onGlyphsbooleanTrue when the point was inside the resolved line's box AND inside one of its spans.
pageIndexnumber
positionSemanticPosition

SemanticHitDrawinginterfaceSource ↗

Stable inline drawing identity when a hit resolves to a drawing atom.

interface SemanticHitDrawing
MemberTypeSummary
drawingNodeIdstring
paragraphIdstring
startnumber

SemanticLayoutinterfaceSource ↗

A complete layout pass: every page, plus the document-wide indexes derived alongside them.

Stamped with the store revision it was laid out from, so anything holding geometry can tell whether the document has moved underneath it — which is how stale pointer gestures and overlays are refused rather than applied to coordinates that no longer describe anything.

interface SemanticLayout
MemberTypeSummary
contentControls?readonly ContentControlBoundaryRecord[]Every content-control boundary in document order, including multi-page fragment lists.
controlContextToken?stringFingerprint of wrapper-only control metadata (alias, tag, lock, type, placeholder, binding).
pagesreadonly PageRecord[]
revisionnumberThe store revision these records were laid out from.

SemanticLayoutOptionsinterfaceSource ↗

Everything a layout pass needs beyond the document itself.

measurer is the only required field — layout is DOM-free and measures through whatever is injected here, which is what lets the same code paginate on a server and in a browser.

interface SemanticLayoutOptions
MemberTypeSummary
cache?ParagraphLayoutCache<readonly PendingLine[]>Reuse of measured-and-broken paragraphs across revisions (task 9.2).
defaultTabStopPt?number`w:settings/w:defaultTabStop` in points (ECMA-376 §17.15.1.25); absent keeps the 0.5" schema default.
displayMode?RevisionDisplayModeWhich tracked revisions this pass resolves away (ECMA-376 §17.13).
drawingExclusionConverged?booleanInternal: converged exclusion zones — skips the reflow loop when set with zones.
drawingExclusionPass?numberInternal: reflow pass index while wrap exclusions converge.
drawingExclusionZonesByPage?ReadonlyMap<number, readonly ExclusionZone[]>Internal: exclusion zones from the prior reflow pass, keyed by page index.
drawingLayoutToken?string
drawingSourceOrder?ReadonlyMap<string, number>Canonical drawing traversal order within the owner story part.
drawingTokenForParagraph?(paragraph: OoxmlNode) => stringPer-paragraph drawing projection/resource token for break cache keys.
emptyTocPlaceholderParagraphIds?ReadonlySet<string>Begin-paragraph ids of empty TOCs. These keep one layout line so paint can host an identifiable empty-TOC furniture placeholder (overrides chrome suppression).
emptyTocSuppressedResultParagraphIds?ReadonlySet<string>Empty result-paragraph ids inside empty TOCs. Suppressed like field chrome so blank cached rows do not stack under the empty placeholder.
furniture?PageFurnitureHeader/footer stories to attach per page; absent means no furniture.
geometry?PageGeometry
inlineDrawingLayout?InlineDrawingLayoutContextInline drawing projection for typed `w:drawing` / `wp:inline` nodes.
listItems?ReadonlyMap<string, ResolvedListItem>Optional precomputed list items for the body story. When absent and [numberingIndex](numberingIndex) is set, layout walks the full body (including table cells) once so counters continue across section boundaries and table document order.
measurerTextMeasurer
noteMarks?NoteMarkContextDerived note marks for body/note projection (provisional or final).
notes?NotesLayoutInputFootnote/endnote layout input. When present, body layout projects note marks and a post-pass attaches note areas (with bounded reflow for pageBottom reservation).
numberingIndex?NumberingIndexProjection of `/word/numbering.xml`. Absent keeps pre-list behaviour (no markers / level indents). The index is immutable for a session; list counter state is derived per layout pass from document order.
pageBottomReserves?ReadonlyMap<number, number>Per-page bottom reserves (points) subtracted from content height before line placement. Produced by the note reflow loop; absent means full content column.
producer?stringWho produced the measurements, folded into every cache key.
sectionColumns?SectionColumnsAuthored column count/gap for anchored `relativeFrom="column"` frame resolution.
sectionFurniture?readonly (PageFurniture | undefined)[]Per-section furniture, index-aligned with `enumerateDocumentSections`.
session?LayoutSessionIncremental placement across revisions (task 9.3).
styleCascade?StyleCascadeTableStyles-part cascade table (docDefaults + `w:style` last-wins). Absent keeps direct formatting only — the pre-cascade behaviour, used by unit tests that never open a package.
tocFieldChromeParagraphIds?ReadonlySet<string>Cross-paragraph TOC field begin/end paragraph ids. Empty chrome on these ids suppresses the caret placeholder line in layout while the tree nodes stay intact for refresh/save.

SemanticPositioninterfaceSource ↗

A caret position in the model.

interface SemanticPosition
MemberTypeSummary
offsetnumber
paragraphIdstring

SemanticSelectioninterfaceSource ↗

A selection as two semantic positions — never as DOM nodes.

anchor is where the selection started and head is where it currently ends, so head before anchor is an ordinary backwards selection rather than an error. Collapsed when the two are equal, which is what a caret is.

interface SemanticSelection
MemberTypeSummary
anchorSemanticPosition
headSemanticPosition

SemanticTableCellinterfaceSource ↗

One cell in the resolved table structure.

gridSpan is clamped at READ time and layout never re-derives it — the value comes from a file and would otherwise be a loop bound an attacker controls.

interface SemanticTableCell
MemberTypeSummary
blocksreadonly OoxmlElement[]Block children in reading order, with content-control wrappers flattened.
bordersCellBorderBoxThree-state authored `tcBorders` (omitted / none / edge).
gridColumnnumberAbsolute grid column this cell starts on, after `w:gridBefore` and every preceding span. Structural conditional formats and cell geometry both key on this, never on the cell's position in the row: one `gridSpan` cell otherwise shifts firstCol/lastCol and the vertical bands for every cell after it.
gridColumnId?stringCanonical `w:gridCol` node id for this cell's start column, when the grid is authored.
gridSpannumberClamped to [1, MAX_TABLE_COLUMNS] at read time; layout never re-derives it.
idstring
marginsCellMarginsPtResolved per-side margins (tcMar over tblCellMar over CELL_PAD).
preferredWidthPreferredWidth`w:tcW` — the width this cell asked for, as authored.
shading?stringValidated 6-hex shading fill, absent for none/auto.
styleFormattingTableCellStyleFormattingWhat the table style says about this cell's paragraphs and runs (17.7.6.6) — a header row's bold and centring live here, not in the cell's own properties.
vAlignCellVerticalAlign`w:vAlign` — defaults to top when omitted/unrecognised.
vMergeContinuebooleanA vMerge cell that is not the restart continues the cell above: box, no content.

SemanticTableRowinterfaceSource ↗

One row in the resolved structure: its cells, its height rule, and any row-level revision.

interface SemanticTableRow
MemberTypeSummary
cantSplitboolean`w:trPr/w:cantSplit` — the row must stay on one page. When it cannot fit a fresh page, layout fails closed rather than fragmenting or overflowing the content box.
cellsreadonly SemanticTableCell[]
heightTableRowHeight`w:trPr/w:trHeight` — auto / atLeast floor / exact (clipped) row height.
idstring
isHeaderboolean`w:trPr/w:tblHeader` — the row repeats atop each page the table continues onto.
revisionAuthor?string
revisionDate?string
revisionId?stringThe `w:trPr/w:ins|w:del` attribution, carried with the kind so a painted row can say WHOSE pending decision it is — the review model addresses the decision by exactly this `(id, author, date)` triple, and a surface with only the kind could highlight the row but never open its card.
revisionKind?'insert' | 'delete'Pending Word row insertion/deletion authored in `w:trPr`.

SemanticTableStructureinterfaceSource ↗

A table resolved into a rectangular grid: column widths, rows, and the widths it asked for.

The grid is normalized here so layout never has to reconcile w:gridCol against actual cell spans — vertical merges and column spans are already accounted for.

interface SemanticTableStructure
MemberTypeSummary
alignmentTableAlignment`w:tblPr/w:jc` (17.4.29) — where the table sits in the text column.
cellSpacingPtnumber`w:tblCellSpacing` (17.4.45) in points: the gap between adjacent cell edges. Applied as a half-gap inset on each side of every cell, so cells separate visually without the grid itself moving. Word ALSO grows the table's overall width by the spacing it adds around the outside; that part is not modelled, so a spaced table is laid out on the same grid its file states rather than a wider one.
columnWidthsPtreadonly number[]
defaultMarginsCellMarginsPtTable-level `tblCellMar` defaults (per-side, CELL_PAD when a side is omitted).
float?TableFloatPosition`w:tblPr/w:tblpPr` (17.4.57) — present when the table is positioned against an anchor box. Placement then comes from [tableFloatOriginX](tableFloatOriginX) rather than `w:jc`/`w:tblInd`.
indentPtnumber`w:tblInd` (17.4.50) in points — "this indentation should shift the table into the text margin by the specified amount". Applies to a left-aligned table; `w:jc` decides the placement outright for the other two.
layoutFixedboolean`w:tblPr/w:tblLayout/@w:type="fixed"` (17.4.52 — 17.4.53 is the `w:tblPrEx` exception variant, not this element). Fixed layout takes the grid as final; anything else is autofit, which in Word never renders wider than the text column.
rowsreadonly SemanticTableRow[]
tableBordersTableBorderBoxTable-level `tblBorders` (three-state, including insideH/insideV).
tableWidthPreferredWidth`w:tblPr/w:tblW` — the width the table asked for.

ShapedClusterinterfaceSource ↗

One cluster: the smallest indivisible text-to-glyph correspondence.

The unit the CARET moves by. A cluster may be several characters (a ligature) or several glyphs (a decomposed mark), so neither a character index nor a glyph index is a valid caret position — caretEdges is, and it includes both endpoints.

interface ShapedCluster
MemberTypeSummary
advanceFixedPoint
caretEdgesreadonly FixedPoint[]Fixed-point edges from this cluster's visual origin, including both endpoints.
fontSpannumberIndex into ShapedRun.fontSpans, preserving the exact fallback choice.
glyphEndnumber
glyphStartnumberHalf-open visual glyph range in ShapedRun.glyphs.
textEndnumber
textStartnumberHalf-open logical UTF-16 range in ShapedRun.text.

ShapedFontSpaninterfaceSource ↗

A stretch of glyphs that came from ONE face.

A run whose text needed fallback has several of these. Recording the exact face per span is what lets a re-shape reproduce the same result rather than re-running fallback selection and possibly choosing differently.

interface ShapedFontSpan
MemberTypeSummary
fallbackIndexnumber | nullnull denotes the primary font; otherwise this is the environment fallback-order index.
fontResolvedFont
glyphEndnumber
glyphStartnumber

ShapedGlyphinterfaceSource ↗

One positioned glyph.

id is a glyph index in its FACE, not a character — a ligature is one glyph spanning several characters, and a single character may produce several glyphs. Use cluster to get back to text.

interface ShapedGlyph
MemberTypeSummary
advanceXFixedPoint
advanceYFixedPoint
clusternumberUTF-16 text offset identifying the cluster that produced this glyph.
idnumber
offsetXFixedPoint
offsetYFixedPoint
originXFixedPointPen origin before this glyph's shaping offsets, in fixed-point run coordinates.
originYFixedPoint
outlineGlyphOutlineExact monochrome outline returned by the admitted HarfBuzz face.

ShapedMeasurerOptionsinterfaceSource ↗

How the shaped measurer resolves fonts and bounds its work.

Font resolution is the HOST's: returning null means "not available" and measurement falls back rather than throwing, because a document naming a font nobody has must still lay out.

interface ShapedMeasurerOptions
MemberTypeSummary
fallbackTextMeasurerUsed when no font resolves.
fixedPointScale?numberFixed-point units per point in the shaper's output.
language?string
resolveFont(style: ResolvedRunStyle) => ResolvedFont | nullThe font a run should be measured with.
script?stringISO 15924 script and BCP 47 language for shaping. Latin/English by default.
shaperTextShaper
shapingLibraryVersionedShapingLibrary
unicodeDataVersionstring

ShapedRuninterfaceSource ↗

One shaped run: text turned into positioned glyphs, with everything needed to measure it, paint it, and put a caret in it.

The engine's measurement unit. Layout never measures characters — it measures these.

interface ShapedRun
MemberTypeSummary
bidiLevelnumber
clustersreadonly ShapedCluster[]
directionTextDirection
fontSpansreadonly ShapedFontSpan[]
glyphsreadonly ShapedGlyph[]
metricsShapedVerticalMetrics
textstring

ShapedRunComparatorInputsinterfaceSource ↗

A [ShapedRun](ShapedRun) reduced to the fields two shaping results must agree on to be considered identical.

Fonts appear as [FontFingerprintInputs](FontFingerprintInputs) rather than whole ResolvedFont objects, so the comparison is over VALUES and does not depend on object identity — which is what makes it work across a reload or a worker boundary.

interface ShapedRunComparatorInputs
MemberTypeSummary
bidiLevelnumber
clustersreadonly ShapedCluster[]
directionTextDirection
fontSpansreadonly { readonly glyphStart: number; readonly glyphEnd: number; readonly fallbackIndex: number | null; readonly font: FontFingerprintInputs; }[]
glyphsreadonly ShapedGlyph[]
languagestring
metricsShapedVerticalMetrics
scriptstring
textstring

ShapedVerticalMetricsinterfaceSource ↗

A run's vertical metrics, from the face that shaped it. Drives line height and baseline.

interface ShapedVerticalMetrics
MemberTypeSummary
ascentFixedPoint
descentFixedPoint
lineGapFixedPoint

ShapeInputinterfaceSource ↗

One shaping call: the text, its size, its bidi level, and the environment to shape it in.

interface ShapeInput
MemberTypeSummary
bidiLevelnumberExact UAX #9 embedding/isolate level; direction is its parity projection.
environmentShapingEnvironment
fontSizeHalfPointsnumber
textstring

ShapingEnvironmentinterfaceSource ↗

A validated [ShapingEnvironmentInput](ShapingEnvironmentInput) — build one with createShapingEnvironment.

Structurally identical to its input, but the nominal distinction is the point: holding one means the tags, axes and fonts inside it have already been checked.

interface ShapingEnvironment extends ShapingEnvironmentInput

ShapingEnvironmentFingerprintInputsinterfaceSource ↗

A [ShapingEnvironment](ShapingEnvironment) reduced to comparable values.

Records and axis maps become SORTED entry arrays, because two environments differing only in key insertion order must fingerprint the same — otherwise a cache would miss on a difference that changes no glyph.

interface ShapingEnvironmentFingerprintInputs
MemberTypeSummary
directionTextDirection
fallbackOrderreadonly FontFingerprintInputs[]
featuresreadonly (readonly [string, number])[]
fixedPointScalenumber
fontFontFingerprintInputs
languagestring
normalizationNormalizationPolicy
roundingModeFixedPointRoundingMode
scriptstring
shapingLibraryVersionedShapingLibrary
unicodeDataVersionstring
variationAxesreadonly (readonly [string, number])[]

ShapingEnvironmentInputinterfaceSource ↗

Everything that determines how text shapes — the complete input to a [ShapingEnvironment](ShapingEnvironment).

Exhaustive on purpose. Any field that could change a glyph's position belongs here, because the environment's fingerprint is what decides whether a cached shaped run may be reused.

interface ShapingEnvironmentInput
MemberTypeSummary
directionTextDirection
fallbackOrderreadonly ResolvedFont[]
featuresReadonly<Record<string, number>>
fixedPointScalenumber
fontResolvedFont
languagestring
normalizationNormalizationPolicy
roundingModeFixedPointRoundingMode
scriptstring
shapingLibraryVersionedShapingLibrary
unicodeDataVersionstring
variationAxesReadonly<Record<string, number>>

SourceRangeinterfaceSource ↗

A half-open UTF-16 range inside one paragraph, addressed by its canonical node id.

interface SourceRange
MemberTypeSummary
endnumber
paragraphIdstring
startnumber

SpanLinkRecordinterfaceSource ↗

The hyperlink a span sits inside, as layout resolved it.

Already SANITIZED: href is the runtime projection produced once at the trust boundary, and null means the link is inert — a refused scheme, or a relationship the package does not declare. Paint, hit-testing and the popover consume this and never the authored target, so there is exactly one place a file-derived URL becomes something a browser can follow.

id is the w:hyperlink node's canonical id, which is what makes the spans of one link recognisable as one link across the several lines it wraps onto — and what an unlink or a retarget addresses.

interface SpanLinkRecord
MemberTypeSummary
anchor?stringBookmark name for an internal link, so navigation need not re-parse the fragment.
hrefstring | nullSanitized runtime projection: an absolute URL, `#anchor`, or null when inert.
idstring
kind'external' | 'internal' | 'unresolved'
tooltip?string`w:tooltip` — paint puts it on the anchor's `title`.

StyleCascadeTableinterfaceSource ↗

The whole styles part, indexed and ready to resolve against.

cacheToken is load-bearing: it folds into layout cache producers so breaks measured under one styles part are never reused under another.

interface StyleCascadeTable
MemberTypeSummary
cacheTokenstringBounded fingerprint folded into layout cache producers so a different styles part cannot reuse breaks measured under another cascade. Computed once per table (FNV-1a hex).
defaultCharacterStyleIdstring | null`w:style[@w:default='1'][@w:type='character']` — last wins among defaults of that type.
defaultParagraphStyleIdstring | null`w:style[@w:default='1'][@w:type='paragraph']` — last wins among defaults of that type.
docDefaultsParagraphreadonly OoxmlProperty[]
docDefaultsParagraphNodeOoxmlElement | undefined
docDefaultsRunreadonly OoxmlProperty[]
stylesReadonlyMap<string, StyleDefinition>
themeFontsThemeFontsThe theme part's Latin typefaces, for `w:rFonts` theme references.

StyleDefinitioninterfaceSource ↗

One w:style as the cascade reads it: its properties, and the style it is based on.

interface StyleDefinition
MemberTypeSummary
basedOnstring | null
conditionalTableFormatsReadonlyMap<string, OoxmlElement>`w:tblStylePr` conditional formats by `w:type` (`firstRow`, `band1Horz`, …).
paragraphPropertiesreadonly OoxmlProperty[]
paragraphPropertiesNodeOoxmlElement | undefinedThe style's `w:pPr` node, when present — needed for nested `w:pBdr`.
runPropertiesreadonly OoxmlProperty[]
styleIdstring
tablePropertiesNodeOoxmlElement | undefinedThe style's `w:tblPr`, for a `w:type="table"` style.
typestring

StyleSpanRecordinterfaceSource ↗

A run of text on one line sharing identical resolved formatting.

interface StyleSpanRecord
MemberTypeSummary
boxLayoutBox
caretEdges?readonly number[]Cumulative advances from [box](box).x to each UTF-16 caret boundary in [text](text).
noteNav?{ readonly scopeId: string; readonly direction: 'to-note' | 'to-body'; }Footnote/endnote navigation metadata for projected note atoms.
projected?booleanLive PAGE/NUMPAGES/SECTIONPAGES projection (layout-time evaluated text).
propsreadonly OoxmlProperty[]The run's authored properties, retained as evidence.
rangeSourceRange
revisions?readonly RevisionAttribution[]The revision wrappers this text sits inside, outermost first, absent when untracked.
styleResolvedRunStyleThe same properties RESOLVED — one unit system, defaults applied.
tabLeader?TabLeader`w:tab/@w:leader` of the stop a `\t` span advanced to (ECMA-376 §17.3.1.38).
tabLeaderAdvancePt?numberAdvance of ONE leader glyph in this run's face, in points, measured by layout.
textstring
wrapAdvanceBefore?numberHorizontal jump, in points, that a floating object's wrap zone forced before this span.

TabDestinationinterfaceSource ↗

Where one tab character lands: the resolved x, and the stop that decided it.

interface TabDestination
MemberTypeSummary
alignmentTabAlignment
leader?TabLeaderLeader of the stop that was reached; absent for `none` and for default-interval tabs.
positionPtnumber

TableBorderBoxinterfaceSource ↗

A table's six authored border edges: four outer, plus the two interior intervals.

interface TableBorderBox
MemberTypeSummary
bottomTableBorderSide
insideHTableBorderSide
insideVTableBorderSide
leftTableBorderSide
rightTableBorderSide
topTableBorderSide

TableBorderStrokeRecordinterfaceSource ↗

One published stroke rectangle in cell-local layout points.

Paint multiplies x/y/width/height by scale and draws — no metrics, gaps, or corner math.

interface TableBorderStrokeRecord
MemberTypeSummary
colorstring | null
cssStyle'solid' | 'dashed' | 'dotted'CSS keyword for this stroke (compound strokes are always solid).
heightnumber
role'outer' | 'inner' | 'middle' | 'edge'
sideTableBorderSideName
widthnumber
xnumber
ynumber

TableCellAddressinterfaceSource ↗

The innermost table cell a point resolved through.

interface TableCellAddress
MemberTypeSummary
cellIdstringCanonical node id of the `w:tc`.
gridColumnnumber
gridSpannumber
rowIdstringCanonical node id of the `w:tr`.
rowIndexnumberOrdinal in the WHOLE table, stable across fragments and header repeats.
tableIdstringCanonical node id of the `w:tbl`.

TableCellContextinterfaceSource ↗

Where a paragraph sits in a table, if it sits in one.

interface TableCellContext
MemberTypeSummary
columnIndexnumber
columnsnumber
rowIndexnumber
rowsnumber
tableIdstring

TableCellFragmentRecordinterfaceSource ↗

One table cell on one page, already resolved against the grid.

gridSpan is clamped at read time, because it comes from a file and an unclamped span is a loop bound an attacker controls. A vertical-merge continuation paints its box but holds no blocks — its content belongs to the cell that started the merge.

interface TableCellFragmentRecord
MemberTypeSummary
blocksreadonly BlockFragmentRecord[]Nested blocks in reading order; recursion carries nested tables.
borders?ResolvedCellBordersLayout-owned resolved borders after collapsed conflict resolution.
boxLayoutBox
gridColumnnumberFirst grid column this cell occupies.
gridColumnId?stringCanonical `w:gridCol` node id for this cell's start column, when authored.
gridSpannumberGrid columns spanned, already clamped at read time.
idstringCanonical node id of the `w:tc`.
paintInert?booleanWhen true, paint skips borders/fill/content for this cell (vMerge continue). Grid bookkeeping and the box remain so selection/geometry walks stay consistent.
rowSpan?numberNumber of rows this restart cell visually spans (1 when not a vertical merge).
shading?stringValidated 6-hex cell shading fill, absent for none/auto.
vMergeContinuebooleanA vertical-merge continuation paints its box but holds no blocks.

TableCellStyleFormattinginterfaceSource ↗

What a table style contributes to the paragraphs of ONE cell: the style's whole-table w:pPr/w:rPr followed by every w:tblStylePr the cell is under (17.7.6.6), weakest first in the caller's condition order (banding, column, row, corner).

This is how Word makes a header row bold and centred while the document states nothing but <w:tblStyle w:val="…"/> on the table and plain runs in the cells.

interface TableCellStyleFormatting
MemberTypeSummary
paragraphPropertiesreadonly OoxmlProperty[]
paragraphPropertyNodesreadonly OoxmlElement[]Matching `w:pPr` nodes, for nested `w:pBdr` / `w:tabs` resolution.
runPropertiesreadonly OoxmlProperty[]Inherited run properties for every run in the cell, before the paragraph style.

TableFragmentRecordinterfaceSource ↗

The part of one table that sits on one page.

A table that crosses a page boundary produces one fragment per page it spans, which is what lets it checkpoint like a paragraph: the flow loop places whole fragments, and resuming after a table needs no knowledge of its interior.

interface TableFragmentRecord
MemberTypeSummary
boxLayoutBox
columnEdgesreadonly number[]Resolved column boundary x positions in table-local points, left edge through right edge. Length is column count + 1.
fragmentIndexnumber0 for the first page the table touches, 1 for its continuation, and so on.
idstring
kind'table'
nestingDepthnumberNesting depth: 0 for body-level tables, increasing for nested tables.
rowsreadonly TableRowFragmentRecord[]
tableIdstringCanonical node id of the `w:tbl`.

TableRowFragmentRecordinterfaceSource ↗

One table row on one page.

A FRAGMENT, not the row: a row split across a page break appears once per page it touches, and a repeated header row appears on every page of its table.

interface TableRowFragmentRecord
MemberTypeSummary
boxLayoutBox
cellsreadonly TableCellFragmentRecord[]
idstringCanonical node id of the `w:tr`.
isContinuation?booleanTrue when this record continues a row that already emitted content on a prior page (cell content fragmented at a paragraph/line boundary). Same `id` as the lead fragment.
isHeaderRepeatbooleanTrue for a `w:tblHeader` row RE-EMITTED at the top of a continuation page. Painted, but excluded from interaction walks so each caret stop exists exactly once.
revisionAuthor?string
revisionDate?string
revisionId?stringThe `w:trPr/w:ins|w:del` attribution — the `(id, author, date)` triple the review model addresses this decision by, so painted rows can carry it the way revision spans do.
revisionKind?'insert' | 'delete'Pending tracked row insertion/deletion, when authored in `w:trPr`.
rowIndexnumberAuthored row ordinal within the table; repeats share the original row's index.

TabStopinterfaceSource ↗

One authored w:tab: where it sits, how it aligns, and what fills the gap.

interface TabStop
MemberTypeSummary
alignmentTabAlignment
leader?TabLeaderAbsent for `none` — the schema default and the overwhelming majority of stops.
positionPtnumberPosition from the paragraph content origin, in points.

TextMeasurerinterfaceSource ↗

Text measurement, injected.

A real implementation shapes with the resolved font; the tests supply a deterministic one. Layout never reads the DOM, so this is the only way width and height enter it.

interface TextMeasurer
MemberTypeSummary
lineMetricsLine height and baseline for the resolved style.
measureAdvance width of `text` in the resolved style.

TextShaperinterfaceSource ↗

The one thing layout needs from a shaping backend.

Injected rather than imported, which is what keeps layout DOM-free and testable: a fixed-metric shaper measures deterministically in a test, HarfBuzz measures for real in a browser, and layout cannot tell the difference.

interface TextShaper
MemberTypeSummary
shape

VersionedShapingLibraryinterfaceSource ↗

The shaping library and its exact version.

Versioned because a library upgrade can change glyph positioning, and a cached measurement taken under the old one must not be reused under the new one.

interface VersionedShapingLibrary
MemberTypeSummary
namestring
versionstring

ViewportWindowinterfaceSource ↗

The visible band of the document, in layout units.

interface ViewportWindow
MemberTypeSummary
heightnumber
topnumberDistance from the top of the document to the top of the visible area, in layout units.

WordBoundaryinterfaceSource ↗

The replaceable word-segmentation strategy.

Falls back to a BOUNDED grapheme-safe splitter where Intl.Segmenter is absent — bounded because the input is file-derived, and grapheme-safe so a fallback never splits inside an emoji.

interface WordBoundary
MemberTypeSummary
segment

WordBoundaryResolverDepsinterfaceSource ↗

Injection points for [createDefaultWordBoundary](createDefaultWordBoundary), so tests can force either path.

interface WordBoundaryResolverDeps
MemberTypeSummary
createFallbackBoundary?() => WordBoundary
createIntlBoundary?() => WordBoundary
isIntlAvailable?() => boolean

WordSegmentinterfaceSource ↗

One word-segmentation span. wordLike separates words from the whitespace and punctuation between them, which is what double-click selection needs to skip.

interface WordSegment
MemberTypeSummary
utf16Fromnumber
utf16Tonumber
wordLikeboolean

Type aliases (45)

BlockFragmentRecordtypeSource ↗

A top-level (or cell-level) block fragment, discriminated by kind.

type BlockFragmentRecord = ParagraphFragmentRecord | TableFragmentRecord;

CacheLookuptypeSource ↗

A cache probe: the value and its provenance on a hit, or the reason it missed.

Misses are typed rather than merely absent, so a caller can tell a cold entry from one invalidated by a dependency change.

type CacheLookup<V> = {
    readonly hit: true;
    readonly value: V;
    readonly provenance: CacheProvenance;
} | CacheMiss;

CacheMisstypeSource ↗

Why a lookup missed — for cache-instrumentation assertions.

type CacheMiss = {
    readonly hit: false;
    readonly reason: 'absent';
} | {
    readonly hit: false;
    readonly reason: 'dependency-changed';
} | {
    readonly hit: false;
    readonly reason: 'input-changed';
} | {
    readonly hit: false;
    readonly reason: 'resource-changed';
    readonly resourceKey: string;
} | {
    readonly hit: false;
    readonly reason: 'epoch-changed';
    readonly epoch: keyof OperationSnapshot;
};

CellVerticalAligntypeSource ↗

w:vAlign — where a cell's content sits when the row is taller than the content.

type CellVerticalAlign = 'top' | 'center' | 'bottom';

ContentControlLeveltypeSource ↗

Where the control sits in the tree relative to its content.

type ContentControlLevel = 'block' | 'inline' | 'row' | 'cell';

ContentControlLocktypeSource ↗

Raw w:lock/@w:val on one control, or unlocked when absent / unrecognised.

Effective permissions across a nesting chain are the union of every ancestor's lock on two axes (content edit / removal); see [ContentControlBoundaryRecord.effectiveLock](ContentControlBoundaryRecord.effectiveLock).

type ContentControlLock = 'unlocked' | 'sdtLocked' | 'contentLocked' | 'sdtContentLocked';

ContentControlMappedTypetypeSource ↗

Mapped control type for layout / chrome — same members as the shipped public ContentControlType. Untyped and preserved-only kinds report as richText.

type ContentControlMappedType = 'richText' | 'plainText' | 'checkbox' | 'dropdown' | 'comboBox' | 'date' | 'picture' | 'repeatingSection';

FixedPointtypeSource ↗

Fixed-point coordinates are safe integers in units declared by ShapingEnvironment.fixedPointScale.

type FixedPoint = number & {
    readonly [FIXED_POINT]: true;
};

FixedPointRoundingModetypeSource ↗

How fixed-point conversion breaks ties. Part of the shaping fingerprint.

type FixedPointRoundingMode = 'halfAwayFromZero' | 'halfToEven' | 'towardZero';

FontByteValidatortypeSource ↗

The injected check that bytes really are a font.

Injected because it is the shaper that knows — HarfBuzz can open a face and read its tables, and this layer must not duplicate that judgement. Every byte reaching it is file input.

type FontByteValidator = (bytes: Uint8Array, faceIndex: number) => FontValidationResult;

FontResolutionErrorCodetypeSource ↗

Why a face was not admitted.

forbidden and hashMismatch are adversarial signals rather than ordinary failures: the first is a face the host declared off-limits, the second is bytes that are not what their source claimed.

type FontResolutionErrorCode = 'missing' | 'forbidden' | 'overLimit' | 'malformed' | 'hashMismatch';

FontSlottypeSource ↗

Which w:rFonts slot a character resolves its face through.

OOXML gives a run up to four faces and picks between them by SCRIPT, so one run of mixed Latin and CJK text uses two different fonts without saying so anywhere in its properties.

type FontSlot = 'ascii' | 'hAnsi' | 'eastAsia' | 'cs';

FontValidationResulttypeSource ↗

Whether bytes parse as a usable font, with a diagnostic when they do not.

type FontValidationResult = {
    readonly valid: true;
} | {
    readonly valid: false;
    readonly diagnostic: string;
};

HarfBuzzShapingErrorCodetypeSource ↗

Why shaping refused.

Mostly RESOURCE limits, because every input here derives from a file: text length, codepoint count, glyph count and outline size are all attacker-influenced, and an unbounded shape call is a denial-of-service vector rather than a rendering bug.

type HarfBuzzShapingErrorCode = 'notInitialized' | 'fontOverLimit' | 'malformedFont' | 'textOverLimit' | 'codepointsOverLimit' | 'glyphOverLimit' | 'outlineOverLimit' | 'shapedRunOverLimit' | 'unsupportedVariationAxes' | 'unsupportedFallback' | 'unsupportedColorFont' | 'unsupportedNormalization' | 'invalidBidiLevel' | 'shapingLibraryMismatch' | 'disposed';

HeaderFooterVariantNametypeSource ↗

Which header/footer variant a page shows (ECMA-376 §17.10.5).

type HeaderFooterVariantName = 'default' | 'first' | 'even';

HyperlinkProjectortypeSource ↗

How layout turns a typed w:hyperlink node into the sanitized record spans carry.

Injected rather than computed here because resolving r:id needs the PACKAGE's relationships and this module only ever sees one part's tree. null means the caller declined to project — the runs still measure and paint, they simply carry no link, which is the right degradation: text is never lost for want of a target.

type HyperlinkProjector = (link: OoxmlNode) => SpanLinkRecord | null;

LineSpacingRuletypeSource ↗

Resolved line spacing (w:spacing/@line + @lineRule, ECMA-376 17.3.1.33).

auto is the interesting one: @line is 240ths of a line, so 240 is single, 360 is one-and-a-half, 480 is double — and Word's own Normal style since 2013 is 259, i.e. 1.08. A document laid out at a flat single spacing is ~8% tight on EVERY line, which moves every page break, so this is not a cosmetic detail.

exact fixes the line box at @line twips and lets tall glyphs clip, the way Word does. atLeast uses it as a floor.

type LineSpacingRule = 'auto' | 'exact' | 'atLeast';

ListMarkerAligntypeSource ↗

w:lvlJc — how a list marker aligns within its own indent.

type ListMarkerAlign = 'left' | 'center' | 'right';

ListSuffixtypeSource ↗

w:suff — what separates a list marker from the text after it.

type ListSuffix = 'tab' | 'space' | 'nothing';

One caret movement, in Word's own vocabulary.

Visual rather than logical where the two differ: left means left on screen, which in right-to-left text is forward through the string.

type NavigationCommand = 'left' | 'right' | 'up' | 'down' | 'wordLeft' | 'wordRight' | 'lineStart' | 'lineEnd' | 'documentStart' | 'documentEnd' | 'pageUp' | 'pageDown';

NormalizationPolicytypeSource ↗

Which Unicode normalization is applied before shaping, if any.

type NormalizationPolicy = 'none' | 'NFC' | 'NFD' | 'NFKC' | 'NFKD';

NoteLayoutFallbackReasontypeSource ↗

Why note layout stopped short and fell back.

Every one is a BOUND rather than a bug: note counts, fragment counts and heights all come from a file, and a document can ask for more note area than a page has. Falling back with a reason keeps the document open instead of failing to lay out.

type NoteLayoutFallbackReason = 'note-count-limit' | 'note-fragment-limit' | 'note-reflow-exhausted' | 'note-height-cap'
/** Authored separator/continuationSeparator taller than the content column. */
 | 'note-separator-height-cap' | 'missing-note-body' | 'dangling-note-reference';

NotePaginationFallbackReasontypeSource ↗

Why note PAGINATION fell back, widening [NoteLayoutFallbackReason](NoteLayoutFallbackReason) with the reasons that only arise while distributing notes across pages.

type NotePaginationFallbackReason = NoteLayoutFallbackReason | 'note-reflow-exhausted' | 'note-area-fragment-limit' | 'note-overflow-page-limit'
/**
 * Overflow/drain iteration placed zero note stories while carry/pending remained —
 * abort rather than minting blank separator-only sheets up to the page budget.
 */
 | 'note-overflow-stalled'
/** A single note line exceeds the full content column; content is not placed overflowing. */
 | 'note-line-exceeds-page';

NoteSeparatorRuleStyletypeSource ↗

Paint style for Word-default / marker-only separator rules (not CSS inventing content).

type NoteSeparatorRuleStyle = 'single' | 'double';

OperationSnapshotFieldtypeSource ↗

Which field of an [OperationSnapshot](OperationSnapshot) changed.

type OperationSnapshotField = keyof OperationSnapshot;

OperationSnapshotGuardtypeSource ↗

Whether the environment is still the one an in-flight operation started under.

restart names the fields that moved. A long layout pass whose fonts or configuration change midway must restart rather than finish against a mixture of both.

type OperationSnapshotGuard = {
    readonly status: 'current';
} | {
    readonly status: 'restart';
    readonly changed: readonly OperationSnapshotField[];
};

PageRefIndextypeSource ↗

Paragraph-id → refs index for linear [filterRefsOnPage](filterRefsOnPage) over a layout pass.

type PageRefIndex = ReadonlyMap<string, readonly PageRefHit[]>;

ParagraphBorderSidetypeSource ↗

Which of the six CT_PBdr edges.

Four are physical box edges; between and bar are group-relative, drawn only where consecutive paragraphs share a border definition.

type ParagraphBorderSide = (typeof PARAGRAPH_BORDER_SIDES)[number];

PreferredWidthTypetypeSource ↗

w:tblW / w:tcW / w:wBefore (CT_TblWidth, 17.4.63 / 17.4.71 / 17.4.86): a PREFERRED width plus the unit it is stated in. Preferred is the operative word — it is what the producer asked for, not what the table resolved to.

pct is stated in fiftieths of a percent (5000 = 100%) by Word, and in the "50%" string form of ST_Percentage by others; both are read. auto and nil carry no width.

type PreferredWidthType = 'dxa' | 'pct' | 'auto' | 'nil';

ReviewItemtypeSource ↗

One pending decision in the review queue: a tracked change, a comment thread, or a pro custom-node card. Discriminate on kind.

type ReviewItem = ReviewRevisionItem | ReviewCommentItem | ReviewCustomItem;

ReviewRevisionKindtypeSource ↗

What kind of decision a revision card represents.

Wider than the four content wrappers, because a reviewer has to be shown every pending decision, including the ones that decorate no characters. A card the surface cannot show is a change the reviewer never learns about — and acceptAllRevisions refuses if ANY revision in the document is one the engine cannot resolve, so an invisible one makes Accept All fail for a reason nothing on screen explains.

type ReviewRevisionKind = 'insert' | 'delete'
/**
 * A deletion and an insertion that are one edit: text typed over a selection.
 *
 * Word shows these as a single `Replaced "x" with "y"` card, and resolving one half
 * without the other is never what the reviewer meant — accepting the deletion alone
 * leaves the replacement text unproposed, rejecting it alone leaves both.
 */
 | 'replace' | 'moveFrom' | 'moveTo'
/** `w:rPrChange` / `w:pPrChange` — the words are unchanged, their formatting is not. */
 | 'format'
/** `w:pPr/w:rPr/w:ins|w:del` — a paragraph split or merge. */
 | 'paragraphMark'
/** A row, cell, section or grid revision. Supported row revisions are resolvable. */
 | 'structural';

RevisionDisplayModetypeSource ↗

Which revisions layout resolves before producing pages.

- all-markup shows both halves of every change. - proposed shows what the document becomes if every change is accepted. - original shows what it was before any of them.

The last two are specified as equal to accept-all and reject-all OUTPUT, which is what makes them testable, without either applying an op.

type RevisionDisplayMode = 'all-markup' | 'proposed' | 'original';

RevisionKindtypeSource ↗

What a revision wrapper asserts about the content inside it.

moveFrom / moveTo are deliberately distinct from delete / insert: a move is one decision with two halves, and presenting it as an unrelated deletion and insertion invites resolving one without the other, which duplicates or loses the content.

type RevisionKind = 'insert' | 'delete' | 'moveFrom' | 'moveTo' | 'format';

SectionBreakTypetypeSource ↗

How this section is placed relative to the previous one (ECMA-376 17.6.22, ST_SectionMark).

All five schema values are read. nextColumn paginates like nextPage for now: in a single-column section that IS Word's behaviour, and multi-column flow is not modelled, so collapsing it at the parse boundary would only hide the authored value from a consumer that asks.

type SectionBreakType = 'nextPage' | 'continuous' | 'evenPage' | 'oddPage' | 'nextColumn';

TabAlignmenttypeSource ↗

How a tab stop positions the text that follows it.

Only left is a plain cursor jump. The other three size the tab glyph from the MEASURED following segment, so its end, centre or decimal point lands on the stop.

type TabAlignment = 'left' | 'center' | 'right' | 'decimal';

TabLeadertypeSource ↗

w:tab/@w:leader (ECMA-376 §17.3.1.38, ST_TabTlc): the character repeated across the space a tab reserves. none is the default and is represented by an absent leader.

This is the difference between a Word table of contents and a column of headings floating next to a column of page numbers, so it is carried through layout to paint rather than dropped as a geometry-irrelevant attribute.

type TabLeader = 'dot' | 'hyphen' | 'underscore' | 'heavy' | 'middleDot';

TableAlignmenttypeSource ↗

w:tblPr/w:jc (17.4.29, ST_JcTable): where the table sits within the text column.

type TableAlignment = 'left' | 'center' | 'right';

TableBorderSidetypeSource ↗

One border edge in one of three states.

omitted and none are NOT the same: an omitted edge inherits from the table style or the neighbouring cell, while an explicit none wins the conflict and draws nothing. Collapsing them would let a style's border reappear where the author removed it.

type TableBorderSide = {
    readonly state: 'omitted';
} | {
    readonly state: 'none';
} | {
    readonly state: 'edge';
    readonly style: TableBorderStyle;
    readonly color: string | null;
    readonly widthPt: number;
};

TableBorderSideNametypeSource ↗

Which physical edge of a box a border sits on.

type TableBorderSideName = 'top' | 'right' | 'bottom' | 'left';

TableBorderStyletypeSource ↗

One of the border line styles this engine draws.

An ALLOWLIST, not the full ST_Border enumeration: a file may name any of the seventy-odd OOXML border styles, and anything outside this set falls back rather than being passed through to paint.

type TableBorderStyle = (typeof TABLE_BORDER_STYLES)[number];

TablePaginationErrorCodetypeSource ↗

Why a table could not be paginated as authored.

Each is a bound: a row taller than a page, a row that cannot be split, or a row producing more fragments than the limit allows.

type TablePaginationErrorCode = 'table-row-overheight' | 'table-row-split-unsupported' | 'table-row-fragment-limit';

TableRowHeighttypeSource ↗

w:trHeight — a row's height rule and its value.

auto carries no value at all, which is why this is a union rather than a rule plus an optional number.

type TableRowHeight = {
    readonly rule: 'auto';
} | {
    readonly rule: 'atLeast' | 'exact';
    readonly valuePt: number;
};

TableRowHeightRuletypeSource ↗

w:trPr/w:trHeight (17.4.81) resolved for layout. Points leave the reader already — twips convert once here, matching every other table geometry boundary.

Word quirk (matches Form025U and Word's UI export): a present @w:val with an omitted @w:hRule is treated as atLeast, not ECMA's auto. Explicit auto still ignores val.

type TableRowHeightRule = 'auto' | 'atLeast' | 'exact';

TextDirectiontypeSource ↗

Which way a run reads. The parity projection of its bidi embedding level.

type TextDirection = 'ltr' | 'rtl';

VerticalAligntypeSource ↗

w:vertAlign — script position, which also scales the run's effective size.

type VerticalAlign = 'baseline' | 'superscript' | 'subscript';

Variables (52)

AUTO_PARAGRAPH_SPACING_PTconstSource ↗

The gap Word substitutes when w:beforeAutospacing / w:afterAutospacing is on (ECMA-376 §17.3.1.2, §17.3.1.13).

The attribute means "the consumer decides", and the authored @before / @after beside it is IGNORED rather than used as the value. Word's answer is HTML's default <p> margin, 14pt, which is what a document round-tripped through Word's HTML filter carries — and this one is everywhere, because Word writes w:before="100" w:beforeAutospacing="1" for it. Reading only the literal 100 twips lays every such paragraph out 9pt tight, which moves page breaks.

AUTO_PARAGRAPH_SPACING_PT = 14

AUTO_PREFERRED_WIDTHconstSource ↗

The frozen "no preferred width" value — what a table or cell that declares none resolves to.

AUTO_PREFERRED_WIDTH: PreferredWidth

boundedStructuralFontValidatorconstSource ↗

Bounded minimum sfnt/TTC check; Task 4 supplies full parser-backed validation.

boundedStructuralFontValidator: FontByteValidator

CELL_PADconstSource ↗

Fallback cell padding in points (60 twips) when neither tblCellMar nor tcMar authors a side. Matches the historical uniform CELL_PAD inset.

CELL_PAD = 3

COMPOUND_BORDER_MIN_GAP_PTconstSource ↗

Minimum gap between compound strokes, in points.

COMPOUND_BORDER_MIN_GAP_PT = 1

COMPOUND_BORDER_MIN_STROKE_PTconstSource ↗

Minimum stroke width for a compound (double/triple) band, in points.

COMPOUND_BORDER_MIN_STROKE_PT = 1

DEFAULT_CANVAS_FONT_STACKconstSource ↗

The stack used when a run names no font (or names one the sink refuses).

Paint only sets font-family when w:rFonts supplies a validated name, so an unstyled run inherits the surrounding face. Measuring one stack and painting another drifts every advance; this is the Word-like Latin fallback the canvas path measures against.

DEFAULT_CANVAS_FONT_STACK = "Calibri, Carlito, Helvetica, Arial, sans-serif"

DEFAULT_CELL_MARGINSconstSource ↗

Word's own default cell padding, applied where a table declares no w:tblCellMar.

DEFAULT_CELL_MARGINS: CellMarginsPt

DEFAULT_PAGE_GEOMETRYconstSource ↗

US Letter with one-inch margins, in points.

DEFAULT_PAGE_GEOMETRY: PageGeometry

DEFAULT_REVISION_DISPLAY_MODEconstSource ↗

How a document renders tracked changes when nothing says otherwise.

all-markup matches Word's own default: a reader who opens a document with pending changes sees them, rather than a clean-looking document hiding edits nobody has accepted.

DEFAULT_REVISION_DISPLAY_MODE: RevisionDisplayMode

DEFAULT_RUN_STYLEconstSource ↗

The style a run inherits when it authors nothing.

DEFAULT_RUN_STYLE: ResolvedRunStyle

DEFAULT_SECTION_PROPERTIESconstSource ↗

US Letter, portrait, one-inch margins: Word's own default when a section says nothing.

DEFAULT_SECTION_PROPERTIES: SectionProperties

DEFAULT_TAB_INTERVAL_PTconstSource ↗

The default tab interval in points — [DEFAULT_TAB_INTERVAL_TWIPS](DEFAULT_TAB_INTERVAL_TWIPS) converted.

DEFAULT_TAB_INTERVAL_PT: number

DEFAULT_TAB_INTERVAL_TWIPSconstSource ↗

OOXML / Word default when w:settings/w:defaultTabStop is absent: 720 twips = 0.5".

DEFAULT_TAB_INTERVAL_TWIPS = 720

DEFAULT_VERTICAL_WEIGHTconstSource ↗

How much more a point of vertical distance counts than a point of horizontal distance.

Without it, clicking far out in the right margin beside a two-word line picks whichever block happens to be directly below, because that block is horizontally nearer. Weighting the vertical axis makes "the line I am level with" win, which is what the pointer meant.

DEFAULT_VERTICAL_WEIGHT = 8

EMPTY_NUMBERING_INDEXconstSource ↗

Empty index for tests / documents without numbering.

EMPTY_NUMBERING_INDEX: NumberingIndex

EMPTY_TAB_STOPSconstSource ↗

The frozen "no custom stops" value, so a paragraph without tabs mints no object.

EMPTY_TAB_STOPS: ResolvedTabStops

GRAPHEME_SEGMENTER_LOCALEconstSource ↗

Invariant locale for deterministic cross-runtime grapheme boundaries.

GRAPHEME_SEGMENTER_LOCALE: "und"

HARD_MAX_AGGREGATE_FONT_BYTESconstSource ↗

Most bytes all admitted faces may total, whatever a caller configures.

HARD_MAX_AGGREGATE_FONT_BYTES: number

HARD_MAX_FONT_BYTESconstSource ↗

Largest single face this engine will ever admit, whatever a caller configures.

A CEILING, not a default: a host may set a smaller maxFontBytes, but nothing can raise it past this. Font bytes come from files, and an unbounded face is a memory-exhaustion vector.

HARD_MAX_FONT_BYTES: number

HARD_MAX_FONT_SOURCESconstSource ↗

Most faces one snapshot may hold, whatever a caller configures.

HARD_MAX_FONT_SOURCES = 256

HARFBUZZ_SHAPING_LIBRARYconstSource ↗

The exact HarfBuzz build this engine shapes against.

Pinned and verified at load: a runtime reporting a different version is REFUSED rather than used, because glyph positioning can change between releases and a cached measurement taken under one build must not be trusted under another.

HARFBUZZ_SHAPING_LIBRARY: VersionedShapingLibrary

harfBuzzFontValidatorconstSource ↗

Structural sfnt validation that does not construct native HarfBuzz objects.

harfBuzzFontValidator: FontByteValidator

intlGraphemeBoundaryconstSource ↗

The default boundary, over Intl.Segmenter at the invariant und locale.

Locale-invariant on purpose: grapheme boundaries must not vary with the user's locale, or the same document would paginate differently for different readers.

intlGraphemeBoundary: GraphemeBoundary

MAX_BORDER_SPACE_PTconstSource ↗

Soft ceiling on border-to-text gap (w:space, already in points).

MAX_BORDER_SPACE_PT = 3168

MAX_BORDER_WIDTH_PTconstSource ↗

Soft ceiling on border width (96 eighths = 12pt). Word's UI tops out well below this.

MAX_BORDER_WIDTH_PT = 12

MAX_DOCUMENT_SECTIONSconstSource ↗

Hard ceiling on sections enumerated from a document (matches write-path MAX_SECTIONS in note/hf lifecycle). Hostile packages with unbounded w:sectPr marks fail closed here rather than amplifying layout props arrays.

MAX_DOCUMENT_SECTIONS = 4096

MAX_EACH_PAGE_MARK_CANDIDATESconstSource ↗

Cap on synthetic eachPage mark candidates measured per section (plus actual marks).

eachPage sequences restart every page, so a page almost never carries more than a handful of auto-numbered notes. Measuring numStart .. numStart + N - 1 covers single→double digit decimal growth and typical roman width peaks (e.g. viii vs ix) without scanning hostile numStart ranges unboundedly. Derived marks already assigned for the pass are always included in addition to this window.

MAX_EACH_PAGE_MARK_CANDIDATES = 12

MAX_LVL_OVERRIDESconstSource ↗

Soft ceiling on override entries per w:num.

MAX_LVL_OVERRIDES = 9

MAX_LVL_TEXT_LENGTHconstSource ↗

Soft ceiling on authored w:lvlText before expansion.

MAX_LVL_TEXT_LENGTH = 64

MAX_MARKER_TEXT_LENGTHconstSource ↗

Soft ceiling on an expanded marker string (codepoints).

MAX_MARKER_TEXT_LENGTH = 64

MAX_NOTE_FRAGMENTSconstSource ↗

Hard ceiling on fragments emitted for one note (split / continuation).

MAX_NOTE_FRAGMENTS = 512

MAX_NOTE_OVERFLOW_PAGESconstSource ↗

Cap on empty pages created solely to drain footnote/endnote overflow.

MAX_NOTE_OVERFLOW_PAGES = 256

MAX_NOTE_REFLOW_ATTEMPTSconstSource ↗

Bound on reflow attempts per document layout pass.

MAX_NOTE_REFLOW_ATTEMPTS = 8

MAX_NOTES_LAID_OUTconstSource ↗

Hard ceiling on notes laid out in one pass (fail closed beyond).

MAX_NOTES_LAID_OUT = 10000

MAX_NUMBERING_DEFINITIONSconstSource ↗

Soft ceiling on abstractNum / num entries read from one part.

MAX_NUMBERING_DEFINITIONS = 512

MAX_PARAGRAPH_SPACING_PTconstSource ↗

Soft ceiling matching the spike's resolved-style limit (31_680 twips ≈ 22"). Beyond that an attacker-authored spacing would push pagination into pathological page counts.

MAX_PARAGRAPH_SPACING_PT: number

MAX_SDT_NESTINGconstSource ↗

Nested content-control wrappers deeper than this stop flattening; content stays preserved.

MAX_CONTENT_CONTROL_NESTING = 32

MAX_STYLE_BASED_ON_DEPTHconstSource ↗

Soft ceiling on basedOn chain length — enough for real templates, refuses hostile graphs.

MAX_STYLE_BASED_ON_DEPTH = 32

MAX_STYLE_DEFINITIONSconstSource ↗

Soft ceiling on style definitions read from one styles part.

MAX_STYLE_DEFINITIONS = 4096

MAX_TAB_POSITION_TWIPSconstSource ↗

Soft ceiling on a tab position (31_680 twips ≈ 22"), matching paragraph-spacing bounds so a hostile stop cannot shove layout into pathological widths.

MAX_TAB_POSITION_TWIPS = 31680

MAX_TAB_STOPSconstSource ↗

Soft ceiling matching Word's practical custom-tab UI limit.

MAX_TAB_STOPS = 64

MAX_TABLE_BORDER_STROKESconstSource ↗

Soft cap on published stroke rectangles per cell (security / pathological spans).

MAX_TABLE_BORDER_STROKES = 256

MAX_TABLE_COLUMNSconstSource ↗

Far above anything Word authors (its UI caps at 63) while keeping allocation bounded.

MAX_TABLE_COLUMNS = 1024

MAX_TABLE_NESTINGconstSource ↗

Layout-time nesting ceiling. Parse-time depth (MAX_DEPTH = 256 XML levels) alone still admits ~80 levels of w:tbl recursion into the layout walk; deeper tables render as an empty cell box rather than recursing.

MAX_TABLE_NESTING = 16

MAX_TABLE_ROW_FRAGMENTSconstSource ↗

Soft ceiling on fragments emitted for one authored row (hostile / runaway splits).

MAX_TABLE_ROW_FRAGMENTS = 4096

MAX_TABLE_ROW_HEIGHT_PTconstSource ↗

Soft ceiling on an authored w:trHeight (~22"). Hostile w:val otherwise becomes a multi-page row that every pagination preflight and cell box inherits.

MAX_TABLE_ROW_HEIGHT_PT: number

PARAGRAPH_BORDER_SIDESconstSource ↗

The six CT_PBdr children, in schema order (ECMA-376 §17.3.1.24).

PARAGRAPH_BORDER_SIDES: readonly ["top", "left", "bottom", "right", "between", "bar"]

SINGLE_LINE_SPACINGconstSource ↗

Single spacing: what a paragraph that says nothing gets.

SINGLE_LINE_SPACING: ParagraphLineSpacing

TAB_LEADER_GLYPHconstSource ↗

The character each leader repeats (§17.3.1.38, ST_TabTlc).

Lives with the type rather than with the painter because LAYOUT has to measure it: a leader is the same character typed over and over, and the only way to space it the way typing it would is to ask the measurer how wide it actually is.

TAB_LEADER_GLYPH: ReadonlyMap<TabLeader, string>

W15_NAMESPACE_URIconstSource ↗

The w15 namespace: commentsExtended.xml — thread parent and resolved state.

W15_NAMESPACE_URI = "http://schemas.microsoft.com/office/word/2012/wordml"

WORD_SEGMENTER_LOCALEconstSource ↗

Invariant locale for deterministic cross-runtime word boundaries.

WORD_SEGMENTER_LOCALE: "und"

On this page

FunctionsactiveReviewItemappliedSpaceBeforeapplyLineSpacingattachNotesToLayoutbaselineShiftPtOfborderExtentPtborderWeightbottomBorderExtentPtboundedFallbackWordSegmentsbuildNumberingIndexbuildPageRefIndexbuildStyleCascadeTablecaptureOperationSnapshotcaretAtcaretBoxOnLinecaretStopscaretStopsForBlockscascadedBottomBordercascadedParagraphBorderscascadedTabStopscascadeParagraphFormattingcascadeRunPropertiescellSelectionBetweencellSelectionRectscellSelectionTextclampListValuecollapsedSpaceBeforecollectFlowBlockscommentBodyTextcommentInitialscompositionAnchorcomputeDoubleBorderMetricsPtcomputeFootnoteReservescontentControlAtPointcontentControlAtSemanticcontentControlBoundariescontentControlContentChildrencontentControlsInLayoutcontentControlsOfLayoutcreateBoundedFallbackWordBoundarycreateDefaultWordBoundarycreateFixedMeasurercreateFontResourceSnapshotcreateHarfBuzzTextShapercreateIntlWordBoundarycreateLayoutSchedulercreateLayoutSessioncreateListCounterStatecreateParagraphLayoutCachecreateShapedMeasurercreateShapedRuncreateShapingEnvironmentdefaultNoteSeparatorRuleStyledefaultTabIntervalFromSettingsderiveNoteDisplayMarksderiveNoteDisplayMarksResolveddisplayTextdocumentOrdereffectiveBorderSideeffectiveContentControlLockemptyTocPlaceholderParagraphIdsemptyTocSuppressedResultParagraphIdsenumerateDocumentSectionsenumerateDocumentSectionsBoundedexpandLvlTextfilterRefsOnPagefindDrawingOverlayFrameInLayoutfindSeparatorNotefirstReviewRangefixedPointfontRequestKeyformatDecimalformatDecimalZeroformatLowerLetterformatLowerRomanformatNumFmtformatPageNumberformatRevisionOfformatUpperLetterformatUpperRomanfragmentOwnsAtomOffsetfragmentsOfParagraphgeometryOfSectiongraphemeBoundaryEpochgraphemeCountgraphemeOffsetToUtf16guardOperationSnapshothitTestPagehitTestSemantichitTestSheetinitializeHarfBuzzisCanvasMeasurementAvailableisContentControlisContentControlContentisCumulativeGeometryTrustedFromLineOriginisFurniturePointisGeometryTrustedCaretOffsetisHarfBuzzInitializedisIntlSegmenterAvailableisIntlWordSegmenterAvailableisMarkerOnlySeparatorNoteisValidStyleIdisWholeGraphemeHorizontalBoundaryitemizeScriptFontSlotskeyedRangeRectslayoutHeaderFooterStorylayoutNoteByIdlayoutNoteSeparatorlayoutNoteStorylayoutSemanticDocumentlineAtPositionlineEndOffsetlinesOflistMarkerBoxmeasureDisplayTextmergeListIndentmoveCaretnextTabDestinationnormalNotesOfnoteDisplayMarkMapnoteLineIdPrefixnoteMarkKeynoteSeparatorAreaBoxnoteStoryBlockspageAtYpagesToMaterializeparagraphBorderExtentPtparagraphBordersparagraphBordersFingerprintparagraphBorderStrokeWidthPtparagraphBreaksBeforeparagraphContextualSpacingparagraphFragmentsOfparagraphFragmentsOfBlocksparagraphLayoutKeyparagraphLineSpacingparagraphMarkDeletedparagraphMarkRevisionOfparagraphOrderOfPartparagraphSectionNodeparagraphShadingparagraphShadingBoxparagraphsInCellsparagraphSpacingparagraphTabStopsparagraphTextFromLayoutparsePageNumberingparseSectionPropertiesprojectedNoteMarkTextprovisionalNoteMarksreadBorderSidereadCellBordersreadNumPrreadSectionPropertiesreadTableBordersreadTableStructureresetGraphemeBoundaryresolveBorderConflictresolveDefaultSurfaceMeasurerresolveDefaultWordBoundaryresolveNumberingLevelresolveOoxmlShadingFillresolveParagraphLayoutInputsresolveRunStyleresolveStoryListItemsresolveStrictHexFillresolveTableCellBorderGridreviewAnchorIndexreviewItemGeometryreviewItemKeyreviewItemPositionRankreviewItemRangesreviewItemsAtreviewThreadRootOfrevisionRemovesParagraphrevisionsAreDeletionrevisionsVisibleroundFontUnitToFixedPointrunStylesEqualsegmentGraphemessegmentWordsselectionRectssemanticHorizontalBoundariessetGraphemeBoundarysha256FontBytesshadingFillFromElementshapedHorizontalBoundariesshapedRunComparatorInputsshapingEnvironmentFingerprintshapingEnvironmentFingerprintInputsspanOffsetXspansInCellsspansInSelectionstoryBlockssyntheticSeparatorBoxtabAdvanceWidthtableContextAttableOriginXtabStopsFingerprinttocFieldChromeParagraphIdstryCreateCanvasMeasurerunionLayoutBoxesutf16OffsetToGraphemewalkStoryParagraphswithDefaultTabIntervalwithNumberingStyleLinkswithResolvedListItemswordBoundarywordSegmentsToGraphemeRecordsClassesFontResolutionErrorHarfBuzzShapingErrorResolvedCacheTablePaginationErrorUnsupportedScriptErrorInterfacesAbstractNumDefinitionBidiEmbeddingLevelsBorderGridGeometryCacheProvenanceCanvasMeasurerOptionsCanvasTextContextCanvasTextMetricsCaretAtOptionsCaretGeometryCascadedParagraphFormattingCellBorderBoxCellMarginsPtCellSelectionCommentAnchorCommentPositionCommentRecordCommentThreadStateCompoundBorderMetricsContentControlBoundaryRecordContentControlFragmentRecordContentControlGeometryFragmentDeclaredFontSubstitutionDocumentSectionDocumentSectionsEnumerationDrawingOverlayFrameFontFingerprintInputsFontRequestFontResourceDefinitionFontResourceInstrumentationFontResourceSnapshotFontResourceSnapshotOptionsFontSubstitutionGlyphOutlineGraphemeBoundaryGraphemeSegmentGraphemeWordSegmentRecordHarfBuzzFaceCacheEventHarfBuzzOutlineCacheEventHarfBuzzShapeCacheEventHarfBuzzTextShaperHarfBuzzTextShaperInstrumentationHarfBuzzTextShaperOptionsHeaderFooterStoryRecordHitPointHitTestOptionsKeyedRangeLayoutBoxLayoutCacheStatsLayoutSchedulerLayoutSchedulerOptionsLayoutScopeLayoutSessionLayoutSessionStatsLayoutShapingOptionsLevelOverrideLineRecordListCounterAdvanceListCounterStateListMarkerRecordMaterializationInputMoveCaretOptionsNoteDisplayMarkNoteMarkContextNoteReferenceSiteNotesAttachResultNoteSeparatorLayoutNotesLayoutInputNoteStoryDrawingsNoteStoryLayoutNumberingIndexNumberingLevelNumberingLevelIndentNumDefinitionOperationSnapshotPageFurniturePageGeometryPageRecordParagraphAutoSpacingContextParagraphBorderEdgeParagraphBordersParagraphBorderStrokeRecordParagraphBottomBorderRecordParagraphFragmentRecordParagraphIndentParagraphKeyInputsParagraphLayoutCacheParagraphLayoutCacheOptionsParagraphLayoutInputsParagraphLineSpacingParagraphSpacingPlacedCellPreferredWidthResolvedCellBordersResolvedFontResolvedListItemResolvedRunStyleResolvedSurfaceMeasurerResolvedTableBorderEdgeResolvedTableBorderEdgeSegmentResolvedTabStopsResolvedUnderlineResourceDependencyProvenanceReviewCommentItemReviewCustomItemReviewModelInputReviewParagraphAnchorReviewPositionReviewRangeReviewRevisionItemRevisionAttributionScriptItemSectionColumnDefinitionSectionColumnsSectionMarginsSectionPageNumberingSectionPropertiesSelectionRectSemanticHitSemanticHitDrawingSemanticLayoutSemanticLayoutOptionsSemanticPositionSemanticSelectionSemanticTableCellSemanticTableRowSemanticTableStructureShapedClusterShapedFontSpanShapedGlyphShapedMeasurerOptionsShapedRunShapedRunComparatorInputsShapedVerticalMetricsShapeInputShapingEnvironmentShapingEnvironmentFingerprintInputsShapingEnvironmentInputSourceRangeSpanLinkRecordStyleCascadeTableStyleDefinitionStyleSpanRecordTabDestinationTableBorderBoxTableBorderStrokeRecordTableCellAddressTableCellContextTableCellFragmentRecordTableCellStyleFormattingTableFragmentRecordTableRowFragmentRecordTabStopTextMeasurerTextShaperVersionedShapingLibraryViewportWindowWordBoundaryWordBoundaryResolverDepsWordSegmentType aliasesBlockFragmentRecordCacheLookupCacheMissCellVerticalAlignContentControlLevelContentControlLockContentControlMappedTypeFixedPointFixedPointRoundingModeFontByteValidatorFontResolutionErrorCodeFontSlotFontValidationResultHarfBuzzShapingErrorCodeHeaderFooterVariantNameHyperlinkProjectorLineSpacingRuleListMarkerAlignListSuffixNavigationCommandNormalizationPolicyNoteLayoutFallbackReasonNotePaginationFallbackReasonNoteSeparatorRuleStyleOperationSnapshotFieldOperationSnapshotGuardPageRefIndexParagraphBorderSidePreferredWidthTypeReviewItemReviewRevisionKindRevisionDisplayModeRevisionKindSectionBreakTypeTabAlignmentTabLeaderTableAlignmentTableBorderSideTableBorderSideNameTableBorderStyleTablePaginationErrorCodeTableRowHeightTableRowHeightRuleTextDirectionVerticalAlignVariablesAUTO_PARAGRAPH_SPACING_PTAUTO_PREFERRED_WIDTHboundedStructuralFontValidatorCELL_PADCOMPOUND_BORDER_MIN_GAP_PTCOMPOUND_BORDER_MIN_STROKE_PTDEFAULT_CANVAS_FONT_STACKDEFAULT_CELL_MARGINSDEFAULT_PAGE_GEOMETRYDEFAULT_REVISION_DISPLAY_MODEDEFAULT_RUN_STYLEDEFAULT_SECTION_PROPERTIESDEFAULT_TAB_INTERVAL_PTDEFAULT_TAB_INTERVAL_TWIPSDEFAULT_VERTICAL_WEIGHTEMPTY_NUMBERING_INDEXEMPTY_TAB_STOPSGRAPHEME_SEGMENTER_LOCALEHARD_MAX_AGGREGATE_FONT_BYTESHARD_MAX_FONT_BYTESHARD_MAX_FONT_SOURCESHARFBUZZ_SHAPING_LIBRARYharfBuzzFontValidatorintlGraphemeBoundaryMAX_BORDER_SPACE_PTMAX_BORDER_WIDTH_PTMAX_DOCUMENT_SECTIONSMAX_EACH_PAGE_MARK_CANDIDATESMAX_LVL_OVERRIDESMAX_LVL_TEXT_LENGTHMAX_MARKER_TEXT_LENGTHMAX_NOTE_FRAGMENTSMAX_NOTE_OVERFLOW_PAGESMAX_NOTE_REFLOW_ATTEMPTSMAX_NOTES_LAID_OUTMAX_NUMBERING_DEFINITIONSMAX_PARAGRAPH_SPACING_PTMAX_SDT_NESTINGMAX_STYLE_BASED_ON_DEPTHMAX_STYLE_DEFINITIONSMAX_TAB_POSITION_TWIPSMAX_TAB_STOPSMAX_TABLE_BORDER_STROKESMAX_TABLE_COLUMNSMAX_TABLE_NESTINGMAX_TABLE_ROW_FRAGMENTSMAX_TABLE_ROW_HEIGHT_PTPARAGRAPH_BORDER_SIDESSINGLE_LINE_SPACINGTAB_LEADER_GLYPHW15_NAMESPACE_URIWORD_SEGMENTER_LOCALE