New

docx-editor 1.x has shipped. Vue support, i18n, agents. Read the migration guide →

API Referencev1.0.2

@eigenpal/docx-editor-core/layout-painter

Layout Painter

Main entry point for rendering Layout data to DOM. Provides reconciliation for efficient incremental updates.

Stable enough for the first-party React adapter, but the API may change in minor releases until a third-party adapter validates it. Pin a version range if you depend on this directly.

Functions(18)

Capture the rendered position of an inline image as EMUs, normalised to unzoomed coordinates. Returns horizontal offset relative to the page content area (column origin) and vertical offset relative to the containing paragraph — matches the OOXML attrs the resolver writes (`relativeFrom: 'column'` / `relativeFrom: 'paragraph'`).

`zoom` defaults to 1; pass the editor's current zoom factor when the pages container has a CSS scale applied so `getBoundingClientRect` deltas are converted back to authored-pixel space before going to EMU.

Returns undefined for non-inline images or detached DOM.

declare function captureInlinePositionEmu(imageEl: HTMLElement, zoom?: number): {
    horizontalEmu: number;
    verticalEmu: number;
} | undefined;

Create a new LayoutPainter instance

declare function createPainter(options?: PainterOptions): LayoutPainter;

Map an image's current OOXML attrs onto the menu's choice vocabulary so the menu can highlight the active option. Returns null for `topAndBottom` and any unknown wrap type — those don't have a directional menu entry.

`cssFloat` accepts both `null` and `undefined` so framework adapters can use either as their "unset" sentinel without an extra normalisation step.

declare function deriveLayoutChoice(wrapType: WrapType, cssFloat?: ImageAttrs['cssFloat'] | null): ImageLayoutTarget | null;

Walk up from a click target to the nearest rendered image element, returning just the element (no PM position parsing). Used by the left-click selection path in both adapters — `data-pm-start` is read separately by callers that need the position. Returns the element when the click was directly on an inline `<img.layout-run-image>` OR inside one of the container classes registered in `LAYOUT_IMAGE_CLASSES`.

declare function findImageElement(target: EventTarget | null): HTMLElement | null;

Walk up from an event target looking for any rendered image element. Returns the PM position embedded in `data-pm-start`, or null if the target isn't on an image.

declare function hitTestImage(target: EventTarget | null): ImageHitTestResult | null;

Check if an image run is a floating image positioned at page/cell level.

declare function isFloatingImageRun(run: ImageRun): boolean;

Whether a given option is enabled for an image with the given current wrap type. Every option stays clickable — picking the option that matches the current state is a no-op (the PM command early-returns), which matches Word's behavior. We don't grey out the current option, so the menu reads consistently regardless of whether the image is inline or anchored.

The flag is kept around for forward-compatibility (e.g. future read-only mode), but currently always returns true.

declare function isImageLayoutOptionEnabled(_option: ImageLayoutOptionDef, _currentWrapType: WrapType): boolean;

Check if a floating image should create text wrapping exclusion zones. wrapNone images (`behind` / `inFront`) are positioned floats but do not shrink line widths; text paints over or under them.

declare function isTextWrappingFloatingImageRun(run: ImageRun): boolean;

Render a fragment to DOM

declare function renderFragment(fragment: Fragment, context: RenderContext, options?: RenderFragmentOptions): HTMLElement;

Render an image fragment to DOM

declare function renderImageFragment(fragment: ImageFragment, block: ImageBlock, _measure: ImageMeasure, _context: RenderContext, options?: RenderImageFragmentOptions): HTMLElement;

Render a single line

declare function renderLine(block: ParagraphBlock, line: MeasuredLine, alignment: 'left' | 'center' | 'right' | 'justify' | undefined, doc: Document, options?: RenderLineOptions): HTMLElement;

Render a single page to DOM

declare function renderPage(page: Page, context: RenderContext, options?: RenderPageOptions): HTMLElement;

Render multiple pages to a container with virtualization for large documents.

For documents with fewer than VIRTUALIZATION_THRESHOLD pages, all pages are rendered eagerly. For larger documents, only pages near the visible viewport are fully rendered — off-screen pages are lightweight shells with correct dimensions to preserve scroll position.

An IntersectionObserver watches page elements and populates/clears content as pages scroll into and out of view.

declare function renderPages(pages: Page[], container: HTMLElement, options?: RenderPageOptions & {
    pageGap?: number;
    footnotesByPage?: Map<number, FootnoteRenderItem[]>;
}): RenderPagesUpdateKind;

Render a paragraph fragment

declare function renderParagraphFragment(fragment: ParagraphFragment, block: ParagraphBlock, measure: ParagraphMeasure, context: RenderContext, options?: RenderParagraphOptions): HTMLElement;

Render a table fragment to DOM

declare function renderTableFragment(fragment: TableFragment, block: TableBlock, measure: TableMeasure, context: RenderContext, options?: RenderTableFragmentOptions): HTMLElement;

Render a text box fragment to DOM

declare function renderTextBoxFragment(fragment: TextBoxFragment, block: TextBoxBlock, measure: TextBoxMeasure, context: RenderContext, options?: RenderTextBoxFragmentOptions): HTMLElement;

Slice runs for a specific line

declare function sliceRunsForLine(block: ParagraphBlock, line: MeasuredLine): Run[];

Translate the legacy toolbar wrap-type vocabulary (`wrapLeft` / `wrapRight` / `square` / `tight` / `through` / `topAndBottom` / `behind` / `inFront` / `inline`) into a `ImageLayoutTarget` so toolbar dispatch shares the same PM command path as the right-click menu.

Returns `undefined` for unknown values; callers should treat that as "no-op".

declare function toolbarValueToLayoutTarget(value: string): ImageLayoutTarget | undefined;

Classes(1)

Layout Painter class

Renders Layout data to DOM with efficient reconciliation. Only updates changed pages and fragments for better performance.

declare class LayoutPainter
MemberTypeSummary
(constructor)Constructs a new instance of the `LayoutPainter` class
getPageCountGet the current page count
getPageElementGet a page element by index
mountMount the painter to a container element
paintPaint a layout to the container
resolvedCommentIdsSet<number>
scrollToPageScroll to a specific page
setBlockLookupSet the block lookup map for rendering fragments
unmountUnmount the painter

Interfaces(8)

Block lookup entry for painter

interface BlockLookupEntry
MemberTypeSummary
blockFlowBlock
measureMeasure
version?string

A single footnote item ready for rendering at page bottom.

interface FootnoteRenderItem
MemberTypeSummary
content?FootnoteContentMeasured body-pipeline content used for WYSIWYG painting.
displayNumberstringDisplay number (e.g. "1", "2")
textstringPlain text content

Header/footer content for rendering

interface HeaderFooterContent
MemberTypeSummary
blocksFlowBlock[]Flow blocks for the header/footer content.
heightnumberTotal height of the content.
measuresMeasure[]Measurements for the blocks.
visualBottom?numberBottom-most visual extent relative to the nominal flow origin.
visualTop?numberTop-most visual extent relative to the nominal flow origin.
interface ImageHitTestResult
MemberTypeSummary
imageElHTMLElementThe matched element — pass to `captureInlinePositionEmu` if it's inline.
posnumberPM doc position of the image node, read from `data-pm-start`.
interface ImageLayoutOptionDef
MemberTypeSummary
choiceImageLayoutTargetChoice value — what gets dispatched on click.
i18nDescKeystringi18n key under `imageWrap.menuDesc.*`.
i18nLabelKeystringi18n key under `imageWrap.menu.*`.
iconHintImageLayoutIconHintHint for the framework's icon registry.

Painter options

interface PainterOptions
MemberTypeSummary
containerBackground?stringContainer background color
document?DocumentDocument to create elements in
pageBackground?stringBackground color for pages
pageGap?numberGap between pages in pixels
showShadow?booleanShow page shadows

Context passed to fragment renderers

interface RenderContext
MemberTypeSummary
contentWidth?numberContent width in pixels (page width minus margins) - used for justify
insideTableCell?booleanWhen true, floating images render in-flow instead of being skipped (for table cells)
pageNumbernumberCurrent page number (1-indexed)
positioning?'absolute' | 'flow'How the renderer should position its outer element. The body lays fragments at absolute (x, y) on the page (`'absolute'`, the default), while headers/footers and text boxes flow blocks vertically and let normal document flow handle placement (`'flow'`). The caller passes 'flow' instead of overwriting the renderer's inline styles after the fact (#379).
resolvedCommentIds?Set<number>Comment IDs that are resolved — skip highlight for these
section'body' | 'header' | 'footer'Which section is being rendered
totalPagesnumberTotal number of pages

Options for rendering a page

interface RenderPageOptions
MemberTypeSummary
backgroundColor?stringBackground color for pages
blockLookup?BlockLookupBlock lookup for rendering actual content.
document?DocumentDocument to create elements in (default: window.document)
firstPageFooterContent?HeaderFooterContentFooter content for the first page only (when titlePg is set).
firstPageHeaderContent?HeaderFooterContentHeader content for the first page only (when titlePg is set).
footerContent?HeaderFooterContentFooter content to render (used for all pages, or pages 2+ when titlePg is set).
footerDistance?numberDistance from page bottom to footer content.
footnoteArea?FootnoteRenderItem[]Footnotes to render at the bottom of this page.
headerContent?HeaderFooterContentHeader content to render (used for all pages, or pages 2+ when titlePg is set).
headerDistance?numberDistance from page top to header content.
pageBorders?{ top?: BorderSpec; bottom?: BorderSpec; left?: BorderSpec; right?: BorderSpec; display?: 'allPages' | 'firstPage' | 'notFirstPage'; offsetFrom?: 'page' | 'text'; zOrder?: 'front' | 'back'; }OOXML page borders from section properties.
pageClassName?stringCustom page class name
resolvedCommentIds?Set<number>Comment IDs that are resolved — skip highlight for these
showBorders?booleanShow page borders (for debugging)
showShadow?booleanDrop shadow on pages
theme?Theme | nullTheme for resolving border colors.
titlePg?booleanWhether different first page headers/footers are enabled (w:titlePg).

Type aliases(3)

Block lookup map type

type BlockLookup = Map<string, BlockLookupEntry>;

Hint to the framework's icon registry for which Material Symbol — or equivalent — to render alongside each option. Bindings own the icon component itself.

type ImageLayoutIconHint = 'inline' | 'squareLeft' | 'squareRight' | 'behind' | 'inFront';

Multi-page rendering with virtualization.

For documents under VIRTUALIZATION_THRESHOLD pages, all pages render eagerly. Larger documents render only pages near the viewport — off-screen pages are lightweight shells (correct dimensions, no fragment content) so scroll position is preserved. An IntersectionObserver populates and clears page content as the user scrolls. Incremental updates (re-rendering only fingerprint-changed pages) avoid blink when the document model shifts.

type RenderPagesUpdateKind = 'incremental' | 'full';

Variables(6)

CSS class names for fragment elements

FRAGMENT_CLASS_NAMES: {
    fragment: string;
    paragraph: string;
    table: string;
    image: string;
    line: string;
    run: string;
}

CSS class names for image elements

IMAGE_CLASS_NAMES: {
    image: string;
    imageAnchored: string;
}

Mirrors Word's Wrap Text menu — five directional options.

IMAGE_LAYOUT_OPTIONS: readonly ImageLayoutOptionDef[]

Image layout helpers shared between framework adapters (React, Vue, ...).

Everything here is framework-agnostic: pure DOM math + pure functions over OOXML wrap-type vocabulary. The corresponding UI bindings (right-click menu, toolbar dropdown) live in each framework adapter and call into these.

LAYOUT_IMAGE_CLASSES: {
    readonly runImage: "layout-run-image";
    readonly blockImage: "layout-block-image";
    readonly pageFloatingImage: "layout-page-floating-image";
    readonly cellFloatingImage: "layout-cell-floating-image";
    readonly pageContent: "layout-page-content";
    readonly paragraph: "layout-paragraph";
}

CSS class names for table elements

TABLE_CLASS_NAMES: {
    table: string;
    row: string;
    cell: string;
    cellContent: string;
    resizeHandle: string;
    rowResizeHandle: string;
    tableEdgeHandleBottom: string;
    tableEdgeHandleRight: string;
}

CSS class names for text box elements

TEXTBOX_CLASS_NAMES: {
    textBox: string;
}