New

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

API Referencev1.0.2

@eigenpal/docx-editor-core/docx

DOCX I/O

Parsing DOCX archives into the `Document` model and re-zipping a model back into a DOCX file. Use `./docx/serializer` for the lower-level Document → XML transforms.

The named exports below are the public API contract. Adding a parser helper to a source module does not automatically make it public — it must be added to this barrel to be reachable from `@eigenpal/docx-editor-core/docx`.

Functions(113)

fn

attemptSelectiveSave

packages/core/src/core.ts:38

Attempt a selective save — patch only changed paragraphs in document.xml. Also updates comments, headers/footers, and core properties so that all document parts stay in sync even when only paragraphs are patched.

Returns the saved ArrayBuffer, or null if selective save is not possible (caller should fall back to full repack).

declare function attemptSelectiveSave(doc: Document, originalBuffer: ArrayBuffer, options: SelectiveSaveOptions): Promise<ArrayBuffer | null>;
fn

buildPatchedDocumentXml

packages/core/src/core.ts:39

Build a patched document.xml by splicing new paragraph XML into the original at the correct offsets. Only changed paragraphs are replaced; everything else is preserved byte-for-byte.

Returns null if any step fails.

declare function buildPatchedDocumentXml(originalXml: string, serializedXml: string, changedIds: Set<string>): string | null;

Calculate the width needed for a tab at a given position

declare function calculateTabWidth(currentPosition: number, tabStops: TabStop[], pageWidth: number): number;
fn

calculateTabWidthWithAlignment

packages/core/src/docx/tabParser.ts:261

Calculate tab width considering alignment

For non-left alignments (center, right, decimal), the width depends on the content that follows the tab.

declare function calculateTabWidthWithAlignment(currentPosition: number, tabStops: TabStop[], pageWidth: number, followingContentWidth?: number): {
    width: number;
    alignment: TabStopAlignment;
};

Create a new complex field context

declare function createComplexFieldContext(): ComplexFieldContext;

Create a new DOCX from a Document (without requiring original buffer)

declare function createDocx(doc: Document): Promise<ArrayBuffer>;

Convert EMUs to pixels (at 96 DPI)

1 inch = 914400 EMUs = 96 pixels Returns 0 for null/undefined/NaN inputs.

declare function emuToPixels(emu: number | undefined | null): number;
fn

extractTextBoxContentElements

packages/core/src/docx/textBoxParser.ts:111

Extract raw paragraph elements from w:txbxContent Actual parsing happens via document parser to avoid circular dependencies

declare function extractTextBoxContentElements(txbxContent: Element | null): {
    paragraphElements: Element[];
    tableElements: Element[];
};

Format a number according to the specified format

declare function formatNumber(num: number, format: NumberFormat): string;

Get the bullet character for a bullet list level

declare function getBulletCharacter(level: ListLevel): string;

Get plain text content of an endnote

declare function getEndnoteText(endnote: Endnote): string;

Get the current display value of a field

declare function getFieldDisplayValue(field: Field): string;

Get plain text content of a footnote

declare function getFootnoteText(footnote: Footnote): string;

Get the format switch value (* or @)

declare function getFormatSwitch(instruction: ParsedFieldInstruction): string | undefined;

Get all runs from a hyperlink

declare function getHyperlinkRuns(hyperlink: Hyperlink): Run[];

Get the display text of a hyperlink

Concatenates text from all child runs.

declare function getHyperlinkText(hyperlink: Hyperlink): string;

Get the resolved URL of a hyperlink

For external links, returns the full URL. For internal links, returns the anchor prefixed with #. Returns undefined if the link couldn't be resolved.

declare function getHyperlinkUrl(hyperlink: Hyperlink): string | undefined;

Get image height in pixels

declare function getImageHeightPx(image: Image): number;

Get image width in pixels

declare function getImageWidthPx(image: Image): number;

Get the character used for a tab leader

declare function getLeaderCharacter(leader: TabLeader | undefined): string;

Get the next tab stop position for a given current position

declare function getNextTabStop(currentPosition: number, tabStops: TabStop[], pageWidth: number): TabStop;

Get outline width in pixels

declare function getOutlineWidthPx(shape: Shape): number;

Get shape dimensions in pixels

declare function getShapeDimensionsPx(shape: Shape): {
    width: number;
    height: number;
};

Get shape height in pixels

declare function getShapeHeightPx(shape: Shape): number;

Get shape width in pixels

declare function getShapeWidthPx(shape: Shape): number;

Get the number of columns in a table

Uses the table grid if available, otherwise counts cells in first row.

declare function getTableColumnCount(table: Table): number;

Get the number of rows in a table

declare function getTableRowCount(table: Table): number;

Parse text box content XML element

declare function getTextBoxContentElement(wsp: Element): Element | null;

Get text box dimensions in pixels

declare function getTextBoxDimensionsPx(textBox: TextBox): {
    width: number;
    height: number;
};

Get text box height in pixels

declare function getTextBoxHeightPx(textBox: TextBox): number;

Get text box margins in pixels

declare function getTextBoxMarginsPx(textBox: TextBox): {
    top: number;
    bottom: number;
    left: number;
    right: number;
};

Get outline width in pixels

declare function getTextBoxOutlineWidthPx(textBox: TextBox): number;

Get plain text from text box (helper for search/indexing)

declare function getTextBoxText(textBox: TextBox): string;

Get text box width in pixels

declare function getTextBoxWidthPx(textBox: TextBox): number;

Get wrap distances in pixels

declare function getWrapDistancesPx(image: Image): {
    top: number;
    bottom: number;
    left: number;
    right: number;
};

Check if a hyperlink has any content (runs)

declare function hasContent(hyperlink: Hyperlink): boolean;

Check if shape has fill

declare function hasFill(shape: Shape): boolean;

Check if table has header row

declare function hasHeaderRow(table: Table): boolean;

Check if field has MERGEFORMAT switch (preserve formatting)

declare function hasMergeFormat(instruction: ParsedFieldInstruction): boolean;

Check if shape has outline

declare function hasOutline(shape: Shape): boolean;

Check if text box has content

declare function hasTextBoxContent(textBox: TextBox): boolean;

Check if text box has fill

declare function hasTextBoxFill(textBox: TextBox): boolean;

Check if text box has outline

declare function hasTextBoxOutline(textBox: TextBox): boolean;

Check if a shape has text content

declare function hasTextContent(shape: Shape): boolean;

Check if a leader type requires visible filling

declare function hasVisibleLeader(leader: TabLeader | undefined): boolean;

Inject `commentRangeStart`/`commentRangeEnd` for reply comments that share their parent comment's text range.

declare function injectReplyRangeMarkers(content: BlockContent[], comments: Comment[]): void;
fn

injectTCReplyRangeMarkers

packages/core/src/docx/index.ts:178

Inject `commentRangeStart`/`commentRangeEnd` for comments whose parent is a tracked-change revision (insertion/deletion). The TC content nodes don't carry the comment's range, so we wrap them.

declare function injectTCReplyRangeMarkers(content: BlockContent[], comments: Comment[]): void;

Check if an image is behind text

declare function isBehindText(image: Image): boolean;

Check if a list level is a bullet (not numbered)

declare function isBulletLevel(level: ListLevel): boolean;

Check if a cell is part of a vertical merge

declare function isCellMergeContinuation(cell: TableCell): boolean;

Check if a cell starts a vertical merge

declare function isCellMergeStart(cell: TableCell): boolean;

Check if field is a date/time field

declare function isDateTimeField(field: Field): boolean;

Check if image is decorative (should be ignored by screen readers)

declare function isDecorativeImage(image: Image): boolean;

Check if field is a document property field

declare function isDocPropertyField(field: Field): boolean;

Check if an image is floating (anchored)

declare function isFloatingImage(image: Image): boolean;

Check if shape is floating (anchored)

declare function isFloatingShape(shape: Shape): boolean;

Check if text box is floating (anchored)

declare function isFloatingTextBox(textBox: TextBox): boolean;

Check if an image is in front of text

declare function isInFrontOfText(image: Image): boolean;

Check if an image is inline (not floating)

declare function isInlineImage(image: Image): boolean;

Check if a field type is a known type

declare function isKnownFieldType(type: string): type is FieldType;

Check if a shape is a line (connector)

declare function isLineShape(shape: Shape): boolean;

Check if field is a mail merge field

declare function isMergeField(field: Field): boolean;

Check if field represents a page number

declare function isPageNumberField(field: Field): boolean;

Check if field is a cross-reference field

declare function isReferenceField(field: Field): boolean;

Check if an endnote is a separator (not regular content)

declare function isSeparatorEndnote(endnote: Endnote): boolean;

Check if a footnote is a separator (not regular content)

declare function isSeparatorFootnote(footnote: Footnote): boolean;

Check if a drawing element contains a shape (not an image)

declare function isShapeDrawing(drawingEl: Element): boolean;

Check if a wps:wsp element is a text box

declare function isShapeTextBox(wsp: Element): boolean;

Check if a drawing element contains a text box Text boxes are shapes with wps:txbx content

declare function isTextBoxDrawing(drawingEl: Element): boolean;

Check if a shape is a text box

declare function isTextBoxShape(shape: Shape): boolean;

Check if field is a TOC/Index field

declare function isTocField(field: Field): boolean;

Check if field represents total page count

declare function isTotalPagesField(field: Field): boolean;

Merge tab stops from different sources (style, direct formatting)

Direct formatting tab stops override style tab stops at the same position. "clear" alignment removes a tab stop from the style.

declare function mergeTabStops(styleTabs: TabStop[] | undefined, directTabs: TabStop[] | undefined): TabStop[];

Parse a single border specification

declare function parseBorderSpec(element: Element | null): BorderSpec | undefined;

Parse cell margins (w:tblCellMar or w:tcMar)

declare function parseCellMargins(marginsElement: Element | null): CellMargins | undefined;

Parse a DOCX file into a complete Document model

declare function parseDocx(input: DocxInput, options?: ParseOptions): Promise<Document>;

Parse a w:drawing element

The drawing element contains either wp:inline or wp:anchor.

declare function parseDrawing(drawingEl: Element, rels: RelationshipMap | undefined, media: Map<string, MediaFile> | undefined): Image | null;

Parse endnote properties from w:endnotePr element (Can appear in w:sectPr or w:settings)

declare function parseEndnoteProperties(element: Element | null): EndnoteProperties;

Parse endnotes.xml

declare function parseEndnotes(endnotesXml: string | null, styles?: StyleMap | null, theme?: Theme | null, numbering?: NumberingMap | null, rels?: RelationshipMap | null, media?: Map<string, MediaFile> | null): EndnoteMap;

Parse a complete field instruction into structured data

declare function parseFieldInstruction(instruction: string): ParsedFieldInstruction;

Parse field type from instruction string

Field instructions follow the format: FIELDNAME [arguments] [switches] Examples: - "PAGE \* MERGEFORMAT" - "DATE \ "MMMM d, yyyy"" - "MERGEFIELD client_name \* Upper" - "REF _Ref123456 \h"

declare function parseFieldType(instruction: string): FieldType;

Parse floating table properties (w:tblpPr)

declare function parseFloatingTableProperties(tblpPrElement: Element | null): FloatingTableProperties | undefined;

Parse footnote properties from w:footnotePr element (Can appear in w:sectPr or w:settings)

declare function parseFootnoteProperties(element: Element | null): FootnoteProperties;

Parse footnotes.xml

declare function parseFootnotes(footnotesXml: string | null, styles?: StyleMap | null, theme?: Theme | null, numbering?: NumberingMap | null, rels?: RelationshipMap | null, media?: Map<string, MediaFile> | null): FootnoteMap;

Parse an image from a w:drawing element

This is the main entry point for image parsing.

declare function parseImage(node: Element, rels: RelationshipMap | undefined, media: Map<string, MediaFile> | undefined): Image | null;

Parse numbering.xml into NumberingDefinitions

declare function parseNumbering(numberingXml: string | null): NumberingMap;

Parse shading properties (w:shd)

declare function parseShading(shdElement: Element | null): ShadingProperties | undefined;

Parse a wps:wsp (Word Processing Shape) element

declare function parseShape(node: Element): Shape;

Parse shape from a w:drawing element that contains a shape (not an image)

declare function parseShapeFromDrawing(drawingEl: Element): Shape | null;

Parse a simple field element (w:fldSimple)

declare function parseSimpleField(node: Element, styles: StyleMap | null, theme: Theme | null): SimpleField;

Parse table borders (w:tblBorders or w:tcBorders)

declare function parseTableBorders(bordersElement: Element | null): TableBorders | undefined;

Parse table look flags (w:tblLook)

declare function parseTableLook(lookElement: Element | null): TableLook | undefined;

Parse a table measurement (width, height, etc.)

declare function parseTableMeasurement(element: Element | null): TableMeasurement | undefined;

Parse table properties (w:tblPr)

declare function parseTableProperties(tblPrElement: Element | null): TableFormatting | undefined;

Parse a single tab stop element (w:tab within w:tabs)

declare function parseTabStop(tab: Element): TabStop | null;

Parse tab stops container (w:tabs)

declare function parseTabStops(tabs: Element | null): TabStop[];
fn

parseTabStopsFromParagraphProperties

packages/core/src/docx/tabParser.ts:114

Parse tab stops from paragraph properties element

declare function parseTabStopsFromParagraphProperties(pPr: Element | null): TabStop[] | undefined;

Parse a text box from a w:drawing element

This creates a TextBox object with placeholder content. The actual content parsing requires paragraph/table parsers which creates a circular dependency. The document parser should call parseTextBoxContent() separately with the required parsers.

declare function parseTextBox(drawingEl: Element): TextBox | null;

Parse text box content with provided parser functions This avoids circular dependencies by accepting parser functions as parameters

declare function parseTextBoxContent(txbxContent: Element | null, parseParagraph: ParagraphParserFn, parseTable: TableParserFn | null, styles: StyleMap | null, theme: Theme | null, numbering: NumberingMap | null, rels?: RelationshipMap | null, _media?: Map<string, MediaFile>): Paragraph[];

Parse text box from a wps:wsp element directly Useful when you already have the shape element

declare function parseTextBoxFromShape(wsp: Element, size: ImageSize, position?: ImagePosition, wrap?: ImageWrap): TextBox | null;

Convert pixels to EMUs. EMU coordinates in OOXML are integer-typed (xs:long); rounding here keeps floating-point drift (e.g. 52 px → 495299.99999999994) out of the document.

declare function pixelsToEmu(px: number): number;

Render list marker text by replacing placeholders with formatted numbers

declare function renderListMarker(lvlText: string, counters: number[], formats: NumberFormat[]): string;

Repack a Document into a valid DOCX file

declare function repackDocx(doc: Document, options?: RepackOptions): Promise<ArrayBuffer>;

Resolve fill color to CSS color string

declare function resolveFillColor(shape: Shape): string | undefined;

Resolve a hyperlink's rId to a URL using a relationship map

This is useful when you have a hyperlink that was parsed without relationship context and need to resolve it later.

declare function resolveHyperlinkUrl(hyperlink: Hyperlink, rels: RelationshipMap): string | undefined;

Resolve outline color to CSS color string

declare function resolveOutlineColor(shape: Shape): string | undefined;

Resolve fill color to CSS color string

declare function resolveTextBoxFillColor(textBox: TextBox): string | undefined;

Resolve outline color to CSS color string

declare function resolveTextBoxOutlineColor(textBox: TextBox): string | undefined;
fn

updateMultipleFiles

packages/core/src/core.ts:37

Update multiple files in a DOCX buffer

declare function updateMultipleFiles(originalBuffer: ArrayBuffer, updates: Map<string, string | ArrayBuffer>, options?: RepackOptions): Promise<ArrayBuffer>;
fn

validatePatchSafety

packages/core/src/core.ts:39

Validate that a selective patch can be safely applied.

Checks: - All changed paraIds exist in original XML (exactly once) - All changed paraIds exist in serialized XML (exactly once) - Paragraph count matches between original and serialized

declare function validatePatchSafety(originalXml: string, serializedXml: string, changedIds: Set<string>): PatchValidationResult;

Interfaces(5)

Complex field parsing context

interface ComplexFieldContext
MemberTypeSummary
codeRunsRun[]Runs in the field code section
dirtybooleanWhether field needs update
fldLockbooleanWhether field is locked
instructionstringAccumulated instruction text
nestingLevelnumberNesting level (for nested fields)
resultRunsRun[]Runs in the result section
stateComplexFieldStateCurrent state

Endnote map returned by parseEndnotes

interface EndnoteMap
MemberTypeSummary
byIdMap<number, Endnote>All endnotes indexed by ID
endnotesEndnote[]Array of all endnotes in document order
getContinuationSeparatorGet continuation separator if exists
getEndnoteGet endnote by ID
getNormalEndnotesGet all normal (non-separator) endnotes
getSeparatorGet separator endnote if exists
hasEndnoteCheck if endnote exists

Field switch parsed from instruction

interface FieldSwitch
MemberTypeSummary
switchstringSwitch character (e.g., '*', '', '#', 'h', 'p')
value?stringSwitch value if any

Footnote map returned by parseFootnotes

interface FootnoteMap
MemberTypeSummary
byIdMap<number, Footnote>All footnotes indexed by ID
footnotesFootnote[]Array of all footnotes in document order
getContinuationSeparatorGet continuation separator if exists
getFootnoteGet footnote by ID
getNormalFootnotesGet all normal (non-separator) footnotes
getSeparatorGet separator footnote if exists
hasFootnoteCheck if footnote exists
interface

ParsedFieldInstruction

packages/core/src/docx/fieldParser.ts:157

Parsed field instruction with arguments and switches

interface ParsedFieldInstruction
MemberTypeSummary
argument?stringField argument (e.g., property name for DOCPROPERTY, bookmark name for REF)
rawstringRaw instruction string
switchesFieldSwitch[]Field switches (e.g., * MERGEFORMAT, @ "date format")
typeFieldTypeField type

Type aliases(4)

State machine for tracking complex field parsing

type ComplexFieldState = 'outside' | 'code' | 'result';

Map of rId to numbering definitions

type NumberingMap = {
    definitions: NumberingDefinitions;
    getLevel: (numId: number, ilvl: number) => ListLevel | null;
    getAbstract: (abstractNumId: number) => AbstractNumbering | null;
    getInstance: (numId: number) => NumberingInstance | null;
    hasNumbering: (numId: number) => boolean;
};

Type for the paragraph parser function to avoid circular imports

type ParagraphParserFn = (node: Element, styles: StyleMap | null, theme: Theme | null, numbering: NumberingMap | null, rels?: RelationshipMap | null) => Paragraph;

Type for the table parser function to avoid circular imports

type TableParserFn = (node: Element, styles: StyleMap | null, theme: Theme | null, numbering: NumberingMap | null, rels?: RelationshipMap | null, media?: Map<string, MediaFile>) => Table;

Variables(4)

Default tab alignment

DEFAULT_TAB_ALIGNMENT: TabStopAlignment
const

DEFAULT_TAB_INTERVAL_TWIPS

packages/core/src/docx/tabParser.ts:36

Default tab stop interval in twips (0.5 inches = 720 twips at 1440 twips/inch) Word uses this when no explicit tab stops are defined

DEFAULT_TAB_INTERVAL_TWIPS = 720

Default tab leader

DEFAULT_TAB_LEADER: TabLeader

All known field types from OOXML specification

KNOWN_FIELD_TYPES: FieldType[]