Functions
absoluteTabWidth
function absoluteTabWidth(kind: string, currentX: number, following: number, room: TabRoom): number
`w:ptab`: a place in the column rather than a stop on the ruler.
It is how a running head puts a title on the left and a page number on the
right with no stop declared anywhere — and treated as an ordinary tab it
advances by the default interval instead, which leaves the page number a
third of the way across.
The width it returns may be negative, and deliberately: the place it names is
a place, not an advance, and a paragraph whose indent has already spent the
line is exactly where that matters. An academic report's footer indents past
the right margin and then asks for a right-hand `w:ptab`, and Word answers by
setting the page number ending at the margin — one line, where clamping the
tab to a pixel gives two and takes six pixels off the text area of every
page. A width cannot be negative, so the caller makes the distance up with a
negative margin, which moves what follows without giving the line a box it
has to find room for.
anchorLeftOf
function anchorLeftOf(base: HorizontalAnchorBase | undefined, placement: { readonly offset?: number; readonly align?: HorizontalAlign; }, width: number, origins: HorizontalOrigins): number
Where the left edge of an anchored box lands, in points across the sheet.
The same shape as the vertical rule with one thing more: the anchor may state
an *alignment* rather than an offset, and then the box is put against an edge
of its base or centred in it.
`float-across-*` puts a square inch of picture two inches along — five
documents, one page each — and the text says where it landed:
stated box left which is
from page, 192 192 the sheet's left edge, plus 192
from margin, 192 288 the text area's left edge (96), plus 192
from column, 192 288 the same, in a section of one column
align right 601.7 the box's right edge on the text area's right
align centre 348.9 the box centred in the text area
`column` and `margin` came out identical and had to: the probe's section has
one column, so the two are the same span. What a column anchor does in a
two-column section is not measured.
`inside` and `outside` — of both the bases and the alignments — are about
which side of a spread the page is on, and are answered here as `left` and
`right`, which is right for a document printed one side and a guess for one
printed two.
anchorTopOf
function anchorTopOf(base: VerticalAnchorBase | undefined, offset: number, origins: AnchorOrigins): number
Where the top of an anchored box lands, in points down the sheet.
The base and the offset, and nothing else — which sounds too simple to need
measuring and was not: nothing in the corpus had ever put `relativeFrom` to
Word, every probe until now anchoring from the paragraph where one base looks
like another.
`float-anchored-from-page`, `-margin`, `-paragraph` and `-line` put the same
square inch of picture at the same half inch of offset and differ in nothing
else. Which lines Word shortens says where the box landed:
base box which is
page 48 … 144 the top of the sheet, plus 48
margin 144 … 240 the top of the text area (96), plus 48
paragraph 197.7 … 293.7 the top of the anchoring paragraph (149.7), plus 48
`line` came out identical to `paragraph`, and had to: the anchor sits in the
first line of its paragraph, so the two origins are the same point. What a
`line` anchor does in the *second* line of a paragraph is not measured.
The margin bases other than `margin` itself — `topMargin`, `bottomMargin`,
`insideMargin`, `outsideMargin` — are not measured either, and are answered
here from the text area, which is right for `topMargin` and a guess for the
rest.
appendTo
function appendTo(blocks: readonly BlockNode[], containerId: number | undefined, appended: readonly BlockNode[]): readonly BlockNode[]
Appends blocks to the end of a container: the body, a cell or a content block.
applyCharacterSprms
function applyCharacterSprms(grpprl: Uint8Array, base: RunProperties, context: ConvertContext): RunProperties
Applies a character `grpprl`.
`base` is what is already in force, and it matters for more than
convenience: a toggle's operand can say "invert whatever you inherited",
which cannot be answered without it.
applyParagraphSprms
function applyParagraphSprms(grpprl: Uint8Array, base: ParagraphProperties, context: ConvertContext): ParagraphProperties
Applies a paragraph `grpprl`.
applySectionSprms
function applySectionSprms(grpprl: Uint8Array, base?: SectionProperties): SectionProperties
applyStep
function applyStep(blocks: readonly BlockNode[], step: Step): StepResult
asInt16
function asInt16(sprm: Sprm): number
The operand as a signed sixteen-bit value: every measurement in twips is one.
asInt32
function asInt32(sprm: Sprm): number
The operand as a signed thirty-two-bit value.
attributeKey
function attributeKey(namespace: string, localName: string, attribute: string): string
baselineStepOf
function baselineStepOf(previous: LineBox, next: LineBox): number
From one baseline to the next, in points.
The quantity a PDF actually shows, and the one a line height is mistaken for.
What separates two baselines is the room left under the first and the room
taken above the second, so two lines of different heights are not the height
of either.
bookmarkMarks
function bookmarkMarks(bookmarks: readonly DocBookmark[]): BookmarkMarks
borderGapOf
function borderGapOf(previous: ParagraphSpacing, next: ParagraphSpacing): number
What the borders add at a seam, in points — and nothing where they meet.
Two paragraphs carrying the *same* border are one bordered block to Word: it
draws no rule between them and adds no space. `paragraph-borders-that-meet`
steps a bare 17.92px between three paragraphs that all state `w:sz="4"` with
a point of space, and 4.64px more than a line between a border of four and
one of eight — which is the first's bottom and the second's top, both.
Compared on the width and the space alone, which is what this carries. Two
borders alike in those and different in colour would be merged here and drawn
apart by Word; no probe asks, and the corpus has not been read for it.
borderSpaceOf
function borderSpaceOf(border: ParagraphBorder | undefined): number
What a paragraph's border adds above or below its text, in points.
The rule itself and the space it asks for, both outside the text. A border
`w:val="none"` is not one and states no size; a size of zero is a hairline
Word still draws, and the corpus has both.
breakPoints
function breakPoints(text: string): number[]
Every offset in a string at which a line may start.
breaksAfter
function breaksAfter(character: string | undefined): boolean
Whether a line may begin after this character, whatever follows it.
Asked of the character before an object, where what follows is not text at
all. `U+FFFC` is the object replacement character, whose line-break class is
`CB` — the standard's own way of saying "an object stands here" — so the
question is put to the algorithm exactly as the algorithm expects it.
breaksBetween
function breaksBetween(left: string, right: string): boolean
Whether a line may end after `left` and begin with `right`.
Kept for the callers that hold two characters and no more — a seam between
two spans in the browser path. It is a **weaker** question than the one the
algorithm answers, because the rules that need context cannot see any: a pair
is asked as if it were the whole of the text, so `sot` and `eot` stand either
side of it. Prefer {@link breakPoints} over the whole string wherever the
whole string is to hand.
buildBlocks
function buildBlocks(context: BodyContext, from: number, to: number): BlockNode[]
Builds the blocks of one story.
cellContentWidthOf
function cellContentWidthOf(columnWidth: number, margins: CellSideMargins | undefined): number
How wide a cell's content may be, in twips.
The column it occupies less the margins beside it, which is the width the
lines of the cell are broken to. `table-cell-text-width` states 108 twips on
both sides of a 2500-twip column: Word begins the first cell's text at 103.2
pixels against a table edge of 96.0, and the second cell's at 270.0 — the
edge, the column, and the margin again.
childArtfrom @genomdev/office-core
function childArt(record: ArtRecord, type: number): ArtRecord | undefined
The first record of a type among a record's immediate children.
chooseLines
function chooseLines(pieces: readonly Piece[], roomOf: (index: number) => LineRoom, justified: boolean, hyphenation?: { readonly zone: number; readonly consecutiveLimit: number; }): Line[]
columnOffsetsOf
function columnOffsetsOf(grid: readonly number[]): number[]
Where the columns of a row begin, in twips from the table's left edge.
The running sum of the grid, which is what a cell's `w:gridSpan` is counted
against and what says which column a cell of a short row belongs to.
columnWidthsOf
function columnWidthsOf(grid: readonly number[], layout: TableLayout | undefined, textAreaTwips: number, statedTwips?: number, minimums?: readonly number[]): number[]
The width of each column, in twips.
columnWidthsPx
function columnWidthsPx(grid: readonly number[], layout: TableLayout | undefined, textAreaTwips: number, statedTwips?: number): number[]
The same widths in CSS pixels, which is what a comparison is read in.
columnWidthTwips
function columnWidthTwips(section: SectionProperties, columnIndex: number): number
Width available to one column, accounting for the gaps between columns.
Unequal columns declare their own widths; equal ones split the remaining space.
`max(0, …)` rather than the raw arithmetic: `columns-space-12000` asks for a
gap wider than the text area, and Word answers with columns of no width at
all — one character to the line — rather than with columns that overlap.
composeDocument
function composeDocument(document: DocxDocument, options: ComposeOptions): Promise<ComposedDocument>
A document turned into what the engine takes, and the engine's answer.
Exported because more than one tool needs it and a second copy would drift:
`geometry-view.mjs` draws what this returns, and drawing something other than
what was measured would be worse than drawing nothing.
contentHeightTwips
function contentHeightTwips(section: SectionProperties): number
Height of the text area, i.e. the page minus its vertical margins.
contentWidthTwips
function contentWidthTwips(section: SectionProperties): number
Width of the text area, i.e. the page minus its horizontal margins.
coveringFace
function coveringFace(faces: Faces, family: string | undefined, text: string, bold: boolean, italic: boolean): string | undefined
The face nearest a character's own where its own has no glyph for it.
Word draws a character its face lacks in something that has it, and the
difference is not decorative: `.notdef` is half an em in Calibri, and a line
of them holds twice the text it should.
createDocument
function createDocument(options?: CreateDocumentOptions): DocumentBuilder
cutOverlong
function cutOverlong(pieces: readonly Piece[], room: number): Piece[]
Cuts a piece too wide for the room into pieces that fit.
Word's one break inside a word. It offers no opportunity there and would not
take one if it did — but a token wider than the column has to end somewhere,
and Word ends it at the column's edge: `break-inside-a-token` sets ten
twenty-six character tokens in a 2000-twip column and Word answers
`aaaaaaaa/bbbbbbbb/cc` and `cccccc` — a cut in the middle of a run of the
same letter, at no punctuation and no boundary of any kind. The slash, the
stop, the comma, the colon, the bracket and the underscore all read alike,
which is what says the cut is the column and not the character.
Never less than a character, or a column narrower than one letter would take
for ever.
**Cutting the word rather than the piece was built and refused.** A token is
not always one piece — `nl-09a49d274e30` writes 252 ellipses as three runs —
and cut piece by piece their three tails stand on one line together: 1344px
of text in a 672px column, which `tools/compare/overrun.mjs` sees and the
page count cannot. Pouring the whole run of pieces through the column instead
mends that document's lines exactly (39 and 33, Word's own) and costs
`legal-91fd8fe7b274` its page count: 1606 exact against 1607. Grouping by
`breakBefore` alone also cost `it-dc1d8d29bc77`; grouping by "no break and no
space" fixed that and not the other.
What decided it is that the repair is worth almost nothing on the axis it was
built for: three lines of the corpus's 2841 that stand past the paper. The
rest come from elsewhere — pictures and tables wider than their column — so
this is not where that problem lives.
**Measured again at 1744 exact page counts**, carrying the width from one
piece to the next and resetting it wherever a line may begin: the same two
documents change hands — `nl-09a49d274e30` won, `legal-91fd8fe7b274` lost —
and `pl-063a569635f2` goes from nine pages to ten against Word's eight. 1744
exact either way, 44 pages of error against 43.
And that document says where the real fault is. Its dotted rule comes out
1375.5px on a 632px line even after the cut, because the cut sizes a piece to
the *whole* room while the line it lands on already holds 481px of text. What
is missing is not a wider notion of the token but the emergency cut being
made where the remaining room is known, which is `chooseLines` and not here.
defaultCodePageOf
function defaultCodePageOf(lid: number): number
The document language's code page, for a document with no font information.
defaultListNumbering
function defaultListNumbering(options: { readonly bulleted: number; readonly numbered: number; }): NumberingDefinitions
A bulleted or numbered list a builder can actually make.
Word's own defaults, because they are what a reader expects a list to look
like and because a builder that asked for them would be asking a caller to
know the format. Three levels, which is as deep as a generated document goes
in practice; a caller wanting more supplies its own definitions.
The indents are Word's: 720 twips a level, with the marker hanging 360 back
from the text — which is what makes the text of every item line up whatever
the marker's width.
defaultStyleSheet
function defaultStyleSheet(): StyleSheet
The styles a generated document actually uses, and nothing beyond them.
distributeColumns
function distributeColumns(given: readonly ColumnDemand[], assignable: number, fixed?: boolean): number[]
The used width of every column, in the same unit the demands were given in.
documentFaces
function documentFaces(options: DocumentFacesOptions): Faces
The faces of one document, resolved by name against the written tables.
eastAsianSlotIsUnset
function eastAsianSlotIsUnset(context: FontSlotContext): boolean
Whether the run's East Asian slot is Times New Roman over an even pair.
§2.1.88 b's second bullet and §2.1.88 d, which are the same rule written
twice. Compared case-insensitively because a document may write
`Times New Roman` in any casing and Word matches families that way.
elementKey
function elementKey(namespace: string, localName: string): string
How markup is named in the register, in reports and in error messages.
`w:spacing in pPr` for an element, `w:spacing@w:beforeAutospacing` for an
attribute. The prefix is the canonical one of the specification rather than
the one the file happened to use, so that the same gap in two documents is
one line and not two.
everyBlock
function everyBlock(blocks: readonly BlockNode[]): Generator<BlockNode>
Every block of a tree, in document order, descending into tables.
extensionFor
function extensionFor(mimeType: string): string
The file extension a media part should carry, from its content type.
The extension matters more than it looks: `[Content_Types].xml` declares
types **by extension**, so a part named `.bin` and declared `image/png` is
a part Word will not render. The list is what the corpus actually holds.
familiesNamedBy
function familiesNamedBy(named: Iterable<string>, fonts?: DocumentFonts): readonly string[]
The families a document might ask for, so a caller can load their tables.
Every name the walk could reach, for the names the document actually uses:
the name itself, its measured stand-ins, its codepage-stripped form, its
family, the script's stand-ins, the alternative the file names, the
document's default, the shape's stand-ins and the last resorts. A superset,
deliberately — loading a table nobody asks for costs a fetch, and missing one
costs the deterministic path.
fcLcb
function fcLcb(fib: Fib, index: number): FcLcb
Reads a pair by name, or a zero-length one when the document has no such part.
fieldTypeOf
function fieldTypeOf(instruction: string): string
Extracts the field type — the first token of the instruction — in upper case.
findArtfrom @genomdev/office-core
function findArt(records: readonly ArtRecord[], type: number): Generator<ArtRecord>
Every record of a type, at any depth.
flattenContentControls
function flattenContentControls(blocks: readonly BlockNode[]): readonly BlockNode[]
Removes structured document tags, lifting their content to where they stood.
A content control is a wrapper, not content: `w:sdt` marks a region as a
date picker or a drop-down or a bound field, and what a reader sees is the
`w:sdtContent` inside it. Nothing that consumes the document — the renderer,
the extractor, the search index — has any use for the wrapper.
It lives here rather than in either consumer because both of them number the
blocks they walk, and those numbers are addresses. A renderer that flattens
and an extractor that nests produce two different numbering schemes for the
same document, and every address minted by one lands somewhere else in the
other — silently, and only in the documents that happen to use a content
control. One function with two callers cannot drift.
foldFontName
function foldFontName(name: string): string
The key a family name is looked up by: quotes off, spaces out, lower case.
The same fold `wordLineRatio` uses, and deliberately no more than that. NFKC
would fold `MS 明朝` onto `MS 明朝`, which is tidier and wrong: the
full-width spelling is a *different* name to Windows, and the whole point of
the alias is that the two names have to be told apart to be joined.
fontSlotFor
function fontSlotFor(code: number, context: FontSlotContext): FontSlot
The slot a code point takes, by [MS-OI29500] §2.1.88 b.
Astral characters are answered as the table's fallthrough answers everything
it does not list — `hAnsi` — rather than by their surrogate halves: a caller
that hands over a whole code point is asking about the character, and the
surrogate rows are about UTF-16 units.
formatNumber
function formatNumber(value: number, format: NumberFormat, language?: string): string
The text a counter shows in a given format.
gridFromCells
function gridFromCells(rows: readonly (readonly { readonly width?: TableWidth; readonly span?: number; }[])[], tableTwips: number): number[]
The column widths of a table that states no `w:tblGrid`.
The grid is optional, and a table without one is not a table without columns:
the cells say how wide they are, in `w:tcW`, and Word reads the columns off
them. `administrative-a6c03313c613` is one — no grid at all, `w:tblW` of 5000
`pct` and one cell a row saying the same — and read as a grid of nothing its
every column came out nought wide, so every word in it wrapped and its 107
pages became 215.
The widest row decides how many columns there are, which is what a grid would
have said. A cell that states no width of its own takes an equal share of
whatever the ones that do have left.
hangingSetFor
function hangingSetFor(language: KinsokuLanguage): ReadonlySet<string>
What `w:overflowPunct` lets hang, for a run written in this language.
Nothing hangs outside the four: Word's own dialog offers the setting beside
the other East Asian typography options, and a Latin paragraph has no
hanging punctuation whatever `w:overflowPunct` says. Which of the four
actually hangs is a separate question, measured rather than documented — see
`hangsPunctuation` in `line-break.ts`.
ignoredAttributeReason
function ignoredAttributeReason(key: string, parent: string): string | undefined
Four spellings, from the most specific to the least.
`w:spacing@w:line in pPr` names one attribute of one element in one place;
`@w:rsidR` names one that hangs off half the elements in the format; and
`w:lsdException@*` names an element whose every attribute is bookkeeping —
without which the register would need a line for each of the five hundred
that a `latentStyles` block writes.
ignoredElementReason
function ignoredElementReason(key: string, parent: string): string | undefined
The reason this markup is passed over, or `undefined` when there is none.
inlineText
function inlineText(nodes: readonly InlineNode[]): string
Concatenates the text of inline nodes, expanding links, fields and revisions.
inSchemaOrder
function inSchemaOrder(names: readonly string[], order: readonly string[]): { readonly ok: boolean; readonly offender?: string; }
Whether a run of element names is in the order the schema states.
The check the writers are held to. Names the schema does not list are
refused rather than ignored: an element nobody put in the order is an element
whose position nobody has checked.
insertBlocks
function insertBlocks(blocks: readonly BlockNode[], id: number, inserted: readonly BlockNode[], where: "before" | "after"): readonly BlockNode[]
Inserts blocks before or after a node.
Before and after rather than "at an index", because an index is a position in
an array the caller cannot see and a neighbour is a thing it can name.
intervalAt
function intervalAt<T>(plex: Plex<T>, position: number): number
The index of the interval containing `position`.
Binary search rather than a scan: a document of any size has thousands of
character-formatting runs, and every character of text asks this question.
isGroupFrame
function isGroupFrame(drawing: DrawingNode): boolean
Whether a node is the frame of a group and says how many members follow it.
isIdentifiedBlock
function isIdentifiedBlock(block: BlockNode): block is IdentifiedBlock
isParagraph
function isParagraph(node: BlockNode): node is ParagraphNode
isSelfContained
function isSelfContained(paragraph: ParagraphNode): boolean
Whether a paragraph's composition depends only on the paragraph.
The three kinds of sequential state named in this file's note, detected by
looking: a list membership, a note reference, or a field. Anything else is a
function of its own contents and can be reused.
isTable
function isTable(node: BlockNode): node is TableNode
isWritableChart
function isWritableChart(drawing: DrawingNode): boolean
Whether this drawing is a chart frame the writer can point back at its part.
isWritableDiagram
function isWritableDiagram(drawing: DrawingNode): boolean
Whether this drawing is a SmartArt frame with all four of its parts named.
All four, because `dgm:relIds` requires all four. A diagram read before the
model kept the other three — or one written by a producer that left an
attribute out — cannot be written back as a diagram, and saying so is better
than writing a frame Word offers to repair.
isWritableDrawing
function isWritableDrawing(drawing: DrawingNode): boolean
Whether the model holds enough of a drawing to write it back at all.
isWritablePicture
function isWritablePicture(drawing: DrawingNode): boolean
Whether the model holds enough of this drawing to write it back.
A relationship to a media part and an extent, and nothing standing in the way
— a text box, a diagram, a chart or a shape is more than a picture and the
model does not hold the rest of it.
isWritableShape
function isWritableShape(drawing: DrawingNode): boolean
Whether the model holds enough of this drawing to write it as a shape.
An extent and either something to draw or something to say. A drawing with
neither a look nor a text box is a frame around nothing, and writing an
invisible empty rectangle is worse than reporting that it was dropped: it
takes room on the page and shows nothing.
isWritableVml
function isWritableVml(shape: VmlShapeNode): boolean
Whether the model holds enough of this shape to write it back.
Almost always. A VML shape carries its own style string, and a shape with a
style has a place and a size; one without has neither and there is nothing
to write. A group is judged by its own box for the same reason — its
children's coordinates mean nothing without it.
kinsokuLanguageOf
function kinsokuLanguageOf(tag: string | undefined): KinsokuLanguage | undefined
Which of the four languages a `w:lang/@w:eastAsia` tag names, if any.
Chinese is split by script and not by country: `zh-TW`, `zh-HK` and `zh-MO`
are written in traditional characters and take the traditional list; `zh-CN`
and `zh-SG` take the simplified one. A tag naming the script outright —
`zh-Hant`, which Word writes for some templates — says so directly.
kinsokuSetFor
function kinsokuSetFor(language: KinsokuLanguage, overrides?: KinsokuOverrides): KinsokuSet
The set actually in force for a language, given what the document says.
See the note at the head of the file for the order of precedence, which is
[MS-OI29500]'s and not the standard's.
languageTag
function languageTag(lid: number): string | undefined
layoutDocument
function layoutDocument(blocks: readonly BlockInput[], page: PageSetup, faces: Faces, running?: RunningInput, options?: LayoutOptions): LayoutResult
Lays the blocks out on pages.
Greedy, one block at a time, which is what Word does: a block is broken into
lines at the column's width, the lines are dropped onto the page until it is
full, and what is left starts the next page. Nothing an earlier page holds is
revisited when a later one fills up.
layoutMath
function layoutMath(nodes: readonly MathElement[], face: MathFace, sizePx: number, display?: boolean): MathBox
Lays an equation out.
`display` is `m:oMathPara` — an equation on a line of its own, which takes
the roomier of every pair of constants the table states. An inline equation
takes the tighter, so that a fraction in a sentence does not open the line.
lineBoxOf
function lineBoxOf(runs: readonly LineRun[], statedSpacing: LineSpacing, grid?: DocumentGrid, compat?: LineCompat, paragraphType?: LineRun): LineBox
lineHeightOf
function lineHeightOf(runs: readonly LineRun[], spacing: LineSpacing, grid?: DocumentGrid): number
The height of one line, in points.
locate
function locate(blocks: readonly BlockNode[], id: number): { readonly node: IdentifiedNode; readonly path: NodePath; } | undefined
Finds a block by id, anywhere in the tree, with the path that reaches it.
mathToText
function mathToText(node: MathNode | MathElement): string
Extracts the plain text of a math expression.
Used for text extraction and search, where an equation should contribute its
symbols rather than being silently dropped.
mediaPartName
function mediaPartName(taken: (name: string) => boolean, extension: string): string
A part name for a picture, and it must not collide with one already there.
`word/media/image1.png` is the convention, and a package being written may
already hold `image1.png` — copied through from the source — so the number
walks until it finds a name nobody has taken.
meetsFloat
function meetsFloat(band: Band, column: Span, floats: readonly (FloatBox & { readonly wrap?: WrapSide; })[]): boolean
Whether a box would take room from a line, without working out how much.
The question a paragraph asks before it decides to break its lines itself:
one that meets no float can be broken to a single width, and one that does
cannot.
mergeBorders
function mergeBorders(base: Borders, override: Borders): Borders
Merges border sets edge by edge.
mergeTextBoxRanges
function mergeTextBoxRanges(first: TextBoxRanges, second: TextBoxRanges): TextBoxRanges
Merges the header story's text boxes into the body's, keeping both indexes.
naturalLineOf
function naturalLineOf(runs: readonly LineRun[], grid?: DocumentGrid): number
The line the type needs before `w:spacing` has its say, in points.
observeMarkup
function observeMarkup(observe: (sighting: MarkupSighting) => void): () => void
Watches everything the readers pass over until the returned function is called.
The hooks are static on the parser — the readers are hot loops and threading
an option through every one of them would cost more than the feature is
worth — so this is process-wide and must be undone.
openDoc
function openDoc(source: ByteSource, options?: OpenDocOptions): Promise<DocxDocument>
openDocx
function openDocx(source: ByteSource, options?: OpenDocxOptions): Promise<DocxDocument>
Opens a Word document.
The main part is parsed in a single streaming pass, while the supporting parts
that a given rendering may not need — headers, footers, footnotes, comments —
are left to be loaded on demand. That matters more than it sounds: a document
with fifteen section-specific headers pays for the two that are actually
displayed.
paragraphGapOf
function paragraphGapOf(previous: ParagraphSpacing, next: ParagraphSpacing, lineTwips?: number): number
The space between two paragraphs, in points.
**The larger of the two, not their sum.** Three probes settle it, each ruling
out one reading the others cannot:
- `contextual-spacing` states `before` and `after` both at 240 twips and Word
puts 16.00px between the lines — 240 twips exactly, where the sum would be
32px. That disposes of adding them.
- `empty-paragraph-height` has a separator with `before` 400 and `after` 0,
and Word gives it its 400 twips. That disposes of reading only `after`.
- `line-spacing-default-two-paragraphs` has `after` 160 and `before` 0, and
Word gives it its 160. That disposes of reading only `before`.
A table cell is no exception, and neither is a blank paragraph in one. This
once summed the seam where a written paragraph met a blank one in a cell, on
a reading of two drifting rows of `forms-99782b65f187`, and
`educational-57592f55761a` contradicted it: its title cell puts 105 twips
after the title and 240 before a blank line, Word opens 240, and the sum
made the row 7px tall and floated the page's table off by as much.
`cell-blank-paragraph-seam` asks directly — written to blank and blank to
written, 40 against 40, 105 against 240 and the reverse, at single and at
one-and-a-half lines — and every seam is the larger of the two.
`w:contextualSpacing` then suppresses a paragraph's own space where its
neighbour names the same style — and only the same style: the probe puts two
different contextual styles next to each other and Word keeps the space
between them.
paragraphStepOf
function paragraphStepOf(previous: LineBox, next: LineBox, gap: number): number
From the last baseline of one paragraph to the first of the next, in points.
The same arithmetic as {@link baselineStepOf} with the space between them
added, which is what a reference shows at a paragraph boundary and what makes
such a step useless as a reading of a line's height.
parseArtPropertiesfrom @genomdev/office-core
function parseArtProperties(record: ArtRecord | undefined): Map<number, ArtProperty>
parseArtRecordsfrom @genomdev/office-core
function parseArtRecords(bytes: Uint8Array, from?: number, to?: number): ArtRecord[]
Reads a sequence of records.
Stops at the first header it cannot believe rather than continuing, because a
wrong length here does not lose one record — it puts the reader in the middle
of somebody's pixel data, where every eight bytes look like a header.
parseBorders
function parseBorders(parser: XmlPullParser): Borders
Parses a border container: `w:pBdr`, `w:tblBorders`, `w:tcBorders`, `w:pgBorders`.
`w:start` and `w:left` name the same edge, and a file may carry both: the
first is what the strict standard writes and the second what the transitional
one kept from Word 2007. Where both are present the logical spelling wins,
whichever came first in the file — [MS-OI29500] §2.1.1758 through §2.1.1765
say it eight times over, once for each of the four containers' two edges.
Reading them into one case, as this did, made the answer depend on the order
a writer happened to emit them in.
parseCellProperties
function parseCellProperties(parser: XmlPullParser): CellProperties
Parses `w:tcPr`.
parseChartfrom @genomdev/office-core
function parseChart(parser: XmlPullParser): ChartDefinition
Parses a whole chart part.
parseDiagramDrawingfrom @genomdev/office-core
function parseDiagramDrawing(parser: XmlPullParser): DiagramDrawing
Parses the drawing Word cached for a SmartArt diagram.
The part is a flat shape tree with absolute geometry, so it needs no
knowledge of the diagram algorithm that produced it.
parseFib
function parseFib(bytes: Uint8Array): Fib
parseFontTable
function parseFontTable(parser: XmlPullParser): FontDefinition[]
Parses `fontTable.xml`.
parseNumbering
function parseNumbering(parser: XmlPullParser): NumberingDefinitions
Parses `numbering.xml`.
The part has three kinds of top-level content: abstract definitions holding
the actual level formatting, concrete instances that paragraphs reference and
which may override individual levels, and picture bullets. Keeping the
indirection intact matters — two lists sharing an abstract definition must
still number independently.
parseOfficeArtContentfrom @genomdev/office-core
function parseOfficeArtContent(bytes: Uint8Array | undefined): OfficeArtContent
parseParagraphProperties
function parseParagraphProperties(parser: XmlPullParser, onSectionProperties?: (parser: XmlPullParser) => void): ParagraphProperties
Parses `w:pPr`.
parsePieceTable
function parsePieceTable(clx: Uint8Array | undefined, fib: Fib): Piece[]
Reads the piece table out of the `Clx`.
The `Clx` is a sequence of blocks: any number of `Prc` blocks, which carry
formatting for the pieces and are of no use to a reader, and then exactly one
`Pcdt` holding the table itself. Walking rather than seeking is what makes
the `Prc` blocks skippable, and they do occur.
parsePlex
function parsePlex<T>(bytes: Uint8Array | undefined, structSize: number, readStruct: (reader: ByteReader, index: number) => T): Plex<T>
Reads a plex.
parsePositionPlex
function parsePositionPlex(bytes: Uint8Array | undefined): Plex<never>
A plex with no elements, only boundaries.
parseRowProperties
function parseRowProperties(parser: XmlPullParser): RowProperties
Parses `w:trPr`.
parseRunProperties
function parseRunProperties(parser: XmlPullParser): RunProperties
Parses `w:rPr`.
Must be called positioned on the `w:rPr` start tag; returns with the parser on
the matching end tag.
parseSectionProperties
function parseSectionProperties(parser: XmlPullParser, over?: SectionProperties): SectionProperties
parseSettings
function parseSettings(parser: XmlPullParser): DocumentSettings
Parses `settings.xml`.
parseSttb
function parseSttb(bytes: Uint8Array | undefined, codePage?: number): SttbEntry[]
parseStyles
function parseStyles(parser: XmlPullParser): StyleSheet
Parses `styles.xml`.
Styles are stored exactly as written, with no inheritance resolved. Flattening
`w:basedOn` chains at parse time is tempting but wrong: the resolution order
differs between paragraph properties, run properties and the thirteen
conditional table blocks, and a flattened style can no longer answer "was this
value set here or inherited", which the table style cascade needs.
Resolution lives in {@link StyleResolver }, where it is memoised per style id.
parseStylesheet
function parseStylesheet(bytes: Uint8Array | undefined, fonts: readonly DocFont[]): { styles: StyleSheet; styleIdOf(istd: number): string | undefined; }
Reads the stylesheet and resolves it into the shared model.
Resolution happens here rather than being left to the style resolver because
the two formats disagree about what a style stores. WordprocessingML stores
the *difference* from the parent, and so does this — but a `grpprl` is
applied to a starting point, and for a style that starting point is its
parent's resolved properties. Applying each style's modifiers to nothing
gives a document whose headings lose everything they inherited.
parseTableProperties
function parseTableProperties(parser: XmlPullParser): TableProperties
Parses `w:tblPr` or `w:tblPrEx`.
parseTheme
function parseTheme(parser: XmlPullParser): Theme
Parses `theme1.xml`.
piecesOf
function piecesOf(spans: readonly LineSpan[], perCharacter?: boolean, hyphenation?: Hyphenating): Piece[]
propertyValuefrom @genomdev/office-core
function propertyValue(properties: ReadonlyMap<number, ArtProperty>, id: number): number | undefined
readAnchors
function readAnchors(bytes: Uint8Array | undefined, inHeader: boolean): Plex<ShapeAnchor>
Reads the anchors of one story.
The plex is keyed by character position, so the result is looked up with the
position of the `0x08` that stands for the shape.
readBookmarks
function readBookmarks(names: Uint8Array | undefined, starts: Uint8Array | undefined, ends: Uint8Array | undefined, codePage: number): DocBookmark[]
readChpxSpans
function readChpxSpans(binTable: Uint8Array | undefined, stream: Uint8Array, entrySize?: number): ChpxSpan[]
Reads every character-formatting page the bin table points at.
Eagerly rather than on demand, and deliberately: the pages are a few hundred
for a large document, they are scattered so reading them one at a time is
the slowest possible access pattern, and every one of them will be needed by
the time the document has been laid out once.
readInlinePicture
function readInlinePicture(data: Uint8Array, location: number): Promise<InlinePicture | undefined>
readPapxSpans
function readPapxSpans(binTable: Uint8Array | undefined, stream: Uint8Array, entrySize?: number, dataStream?: Uint8Array): PapxSpan[]
Reads every paragraph-formatting page.
Each entry is one paragraph, which makes this plex the document's paragraph
list as much as its formatting: the boundaries it gives are exactly the ones
the paragraph marks in the text imply, and having both is how a reader can
tell a paragraph mark from a byte that merely looks like one.
readPictures
function readPictures(drawingGroup: ArtRecord | undefined, delayStream?: Uint8Array): Promise<DocPicture[]>
Reads every picture the document's store lists.
The store holds one entry per picture and, in Word, almost never the picture
itself: the entry carries an offset into a *delay stream*, and the delay
stream is `WordDocument`. So the pictures sit among the text, in the one
place a reader of the drawing layer would not think to look, and a store that
appears to hold nothing is the normal case rather than an empty document.
readSepx
function readSepx(stream: Uint8Array, fc: number): Uint8Array | undefined
Reads a section's `grpprl` out of `WordDocument`.
A `SED` points at it rather than holding it, and the pointer is `FFFFFFFF`
when the section carries no properties of its own at all.
readShapes
function readShapes(content: OfficeArtContent, textBoxes: TextBoxRanges): Map<number, DocShape>
Indexes every shape in the document by its identifier.
Both drawings are walked — the body's and the headers' — into one index,
because an anchor names a shape and says nothing about which drawing it is
in, and the two identifier spaces do not overlap.
readTextBoxRanges
function readTextBoxRanges(bytes: Uint8Array | undefined, from: number): TextBoxRanges
Reads which stretch of the text box story belongs to which shape.
The entries carry an identifier, and it took a corpus to establish what that
identifier is: not the shape's `lTxid` property, which is what the name
suggests and which turns out to be an index shifted sixteen bits, but the
shape's own **identifier**. A document with one text box has a shape numbered
1026 and an entry saying 1026; the shape's `lTxid` says 65536.
removeNode
function removeNode(blocks: readonly BlockNode[], id: number): readonly BlockNode[]
Removes a node by id.
replaceNode
function replaceNode(blocks: readonly BlockNode[], id: number, replacement: IdentifiedNode): readonly BlockNode[]
Replaces a node by id, rebuilding only the path down to it.
Answers the same array when the id is not there, so a caller can tell a
change that did nothing from one that did.
resolveLevel
function resolveLevel(numbering: NumberingDefinitions, numberingId: number | undefined, level: number | undefined, styleLinkResolver?: (styleId: string) => number | undefined): NumberingLevel | undefined
Resolves the effective definition of a list level.
Walks instance → override → abstract definition, following `numStyleLink`
indirection when an instance points at a numbering style rather than at a
concrete definition.
roomBeside
function roomBeside(band: Band, column: Span, floats: readonly (FloatBox & { readonly wrap?: WrapSide; })[]): Span[]
The room left for one line beside the floats it meets, as the runs it may use.
**Several runs, not one.** A box in the middle of a column leaves room on
both sides of it and Word uses both: `float-wrap-side-bothsides` puts a
square inch in the middle of a 601.7-pixel column and Word sets one line as
two fragments, 96.0 to 223.4 and 404.1 to 661.7. `bothSides` is also the
default, so this is the ordinary case and not the exotic one.
The sweep says what each value does, on the same picture in the same place:
wrapText where the text goes
bothSides 96…223 *and* 404…694 — both, on one line
left 96…270 only
right 404…694 only
largest 404…694 — the same as right, the right being the wider
An earlier reading of this took the wider side always, which is right for a
box against a margin — where the narrow side is nothing — and wrong for one
in the middle, which is what the default does most.
rowHeightOf
function rowHeightOf(content: number, height: RowHeight | undefined, margins: RowMargins | undefined): number
The height of one row, in points.
saveDocx
function saveDocx(document: SavableDocument, options?: SaveOptions): Promise<SaveResult>
scaffoldParts
function scaffoldParts(options: { readonly styles?: StyleSheet; readonly numbering?: NumberingDefinitions; readonly metadata?: DocumentMetadata; }): Scaffold
shapePropertiesfrom @genomdev/office-core
function shapeProperties(container: ArtRecord): Map<number, ArtProperty>
All three property tables of a shape, merged in the order they override.
spanAt
function spanAt<T extends { fcStart: number; fcEnd: number; }>(spans: readonly T[], fc: number): T | undefined
Binary search for the span covering a file offset.
spanWidthOf
function spanWidthOf(grid: readonly number[], from: number, span?: number): number
The width of a cell that spans several columns of the grid, in twips.
The sum of the columns it covers, and nothing taken off for the rules between
them. `table-grid-span` puts a cell over the first two of three 2000-twip
columns and an ordinary row of three beneath it as a ruler: the ruler's cells
begin at 96.7, 230.1 and 363.4 pixels — 133.3 apart, which is 2000 twips —
and the cell after the span begins at 363.4 exactly, where the third column
does. The vertical rule between the merged columns costs the span nothing.
spreadMergedHeight
function spreadMergedHeight(rows: readonly number[], merged: number): number[]
The heights of a run of rows a cell is merged down, in points.
A vertically merged cell is not in any one of its rows: its text runs through
all of them, and the rows together have to be tall enough to hold it. Where
they already are, nothing moves; where they are not, the shortfall lands on
the **last** row of the run.
`table-vertical-merge` shows both halves. A cell of five words is merged down
three rows beside three cells of one line each, and Word sets its three lines
17.92px apart — one line, continuously, paying no attention to the rows —
while the neighbours sit at 129.15, 147.07 and 167.71. The first two rows are
exactly a line tall, and the third is not: it carries what is left.
sprms
function sprms(grpprl: Uint8Array, from?: number): Generator<Sprm>
Walks a `grpprl`.
Stops at the first opcode it cannot size rather than guessing, because a
misread length does not produce one wrong property — it desynchronises the
stream and every property after it is read out of the middle of an operand.
stopAfter
function stopAfter(currentX: number, stops: readonly PlacedStop[]): PlacedStop | undefined
The declared stop a tab at `currentX` reaches, or nothing where it falls
through to the default interval.
The first stop strictly to the right of the current position wins. The margin
is a shade wider than the guard a right-aligned stop leaves behind it, so
that the stop a tab has just reached is never chosen again — which would ask
the same text to end twice at the same place and collapse the tab to nothing.
storyRanges
function storyRanges(fib: Fib): StoryRanges
strictMarkup
function strictMarkup(options?: StrictMarkupOptions): () => void
Makes unaccounted markup an error for the duration of the returned handle.
For development runs and tests, never for a viewer: against real files this
throws constantly, and that is the point — it is a search for the gaps, not a
way to read documents.
substitutesFor
function substitutesFor(name: string): string[]
Faces to fall back on when the one the document names is not installed.
Word does this itself and calls it font substitution: it keeps a table of
metric-compatible pairs and, finding neither the named face nor a mapping,
picks something of the right shape. A viewer that passes the name straight to
CSS and stops gets the browser's last-resort face instead, which has neither
the metrics nor the look — every line comes out a different width, and the
comparison reports it in the same shape as a real defect.
Two rules, both of which the corpus asks for by name:
- The metric-compatible clones. Liberation Serif is Times New Roman's metrics
to the glyph and is what LibreOffice writes when it saves a document that
used one; five documents in the corpus name it and none of them means a
different typeface.
- A weight or a slope carried in the family name. `Arial Bold` is not a
family and no system has it — it is Arial, and the boldness is already on
the run. Dropping the trailing style word finds the family that exists.
tabAdvance
function tabAdvance(currentX: number, following: () => number, stops: readonly PlacedStop[], room: TabRoom, decimalHead?: () => number): TabAdvance
What a tab at `currentX` advances by.
`following` is the width of the text between this tab and the next. It is a
function and not a number because only a stop that *ends* its text at the
stop needs it, and measuring it is the expensive half of laying out a tab:
the browser walks the nodes after the tab with a `Range`. Most tabs are
left-aligned and never ask.
tableTopOf
function tableTopOf(anchor: TableVerticalAnchor | undefined, yTwips: number, origins: { readonly page: number; readonly margin: number; readonly text: number; }): number
Where the top of a floating table lands, in points down the sheet.
`w:tblpPr` takes a table out of the flow and puts it where its anchors say,
with the text running around it — the same idea as an anchored drawing, a
separate implementation in Word, and its own vocabulary: `text` where a
drawing says `paragraph`, and `w:tblpY` in twips where a drawing has EMU.
`float-table-from-page`, `-margin` and `-text` put the same two-inch table
two inches down and differ in nothing else:
vertAnchor table top which is
page 192 the top of the sheet, plus 192
margin 287.8 the top of the text area (96), plus 192
text 341.8 where the table stood in the flow (149.7), plus 192
The text wraps around it as it does around a drawing: the table occupies 96
to 288 and the lines beside it begin at 289.
tallestOf
function tallestOf(runs: readonly LineRun[]): LineRun | undefined
The run that decides the line, or nothing where the line has none.
Tallest is not largest: a face's line is a multiple of its size and the
multiples differ by six per cent between the faces of an ordinary document,
so eleven-point Calibri needs 13.42 points where twelve-point Times needs
13.8 — the *smaller* run is the taller line. `line-height-owner-plus-smaller-
calibri` puts that pair to Word and Word answers with the Times line.
toggle
function toggle(sprm: Sprm, inherited: boolean | undefined): boolean | undefined
A toggle's operand.
Bold, italic and their nineteen relatives are stored as a byte with four
meanings, not two: off, on, and the two that make sense only against
something inherited — leave as inherited, and invert it. A style that turns
bold on and a paragraph inside it that says "invert" is how Word writes a
heading whose emphasised words are set roman.
walkBlocks
function walkBlocks(blocks: readonly BlockNode[]): Generator<BlockNode>
Walks every block in a subtree, descending into tables and content blocks.
A generator rather than an array: callers usually stop early (finding the
first section break, locating a bookmark) and materialising the whole document
as a flat list would defeat the point of a streaming parser.
walkDocx
function walkDocx(document: DocxDocument, hash: string, options: ResolvedOptions): Promise<{ blocks: Block[]; annotations: Annotation[]; }>
Reading a Word document as content.
The hard parts, in the order they bite:
A Word document is not one stream. The body, each header, each footer, each
footnote and each comment are separate flows, and numbering them as if they
were one would make every address after the first footnote wrong. So each
gets its own locator flow and its own walk.
A list is not a list. Word stores a paragraph with a numbering reference, not
a nested structure, and the number itself is nowhere in the file — it is the
paragraph's position among its neighbours, resolved through a definition that
may restart partway down. So the numbers are computed here, in document
order, and the flat sequence of numbered paragraphs is folded back into the
tree it was drawn as.
A heading is a claim, not a fact. `w:outlineLevel` and the built-in heading
styles both say so and they disagree; a document whose author never used a
heading style has no headings, and inventing them from type size gives a
table of contents made of pull quotes.
walkInline
function walkInline(nodes: readonly InlineNode[]): Generator<InlineNode>
Walks every inline node, descending into hyperlinks, fields and revisions.
widenForSpans
function widenForSpans(columns: ColumnDemand[], spans: readonly { readonly from: number; readonly span: number; readonly minContent: number; readonly maxContent: number; }[]): void
A cell spanning several columns, widening them until they hold it.
CSS 2.1: "For each cell that spans more than one column, increase the minimum
widths of the columns it spans so that together, they are at least as wide as
the cell. Do the same for the maximum widths." The same ladder as above
decides which of the spanned columns grows, which is why it is one function.
Applied after every single-column cell has had its say, and in order of
increasing span, so that a cell over two columns settles before one over
three has to reason about them.
widestRoom
function widestRoom(band: Band, column: Span, floats: readonly (FloatBox & { readonly wrap?: WrapSide; })[]): Span | undefined
The widest run of room a line has, or nothing where it has none.
For a caller that sets one run of words to a line and does not divide it —
which is every caller until the line breaker learns to.
widthOf
function widthOf(width: TableWidth | undefined, ofTwips: number): number | undefined
The width a `w:tblW` or `w:tcW` means, in twips.
`pct` is in fiftieths of a per cent — 5000 is the whole of it — and what it is
a per cent *of* is what the caller passes: the text area for a table, the
table for a cell.
withHangingStop
function withHangingStop(stops: readonly PlacedStop[], hanging: number, indentX: number, listItem?: boolean): readonly PlacedStop[]
The paragraph's own left indent, as a stop, where the paragraph hangs.
That is what a hanging indent is *for*: the label sits out in the margin, a
tab carries the reader to the indent, and every line after the first starts
there. Legal drafting and anything converted from it writes list items
exactly so — a tab, `(a)`, a tab, the text — and without the implicit stop
the second tab falls through to the default interval or, when the declared
stop lies just behind it, to nothing at all. The text then abuts the label:
`(a)be made in writing`.
Left-aligned, and only when the paragraph hangs: an indent with no hanging
part is where the text already begins, so a stop there would advance by
nothing.
## `w:doNotUseIndentAsNumberingTabStop` is not honoured, and neither is it by Word
ECMA-376 Part 4 §14.8.3 declares a flag for exactly this stop — "specifies
that applications shall not use the custom tab stop generated by the hanging
indent when advancing the text after the numbering" — and 53 documents of
the corpus set it, more than any other compatibility flag we do not read.
Implementing it makes no difference because Word does not implement it.
`compat-indent-as-numbering-gap-{on,off}` are the same document but for the
flag, numbered `w:ind w:left="1000" w:hanging="360"`: the number is drawn at
640 twips, the implied stop is at 1000, and the first default stop past the
number is at 720 — the two readings are 280 twips apart and cannot be
confused. Word draws the text at 1002 twips in both. `-mode12-` asks the
question again of a document that declares itself Word 2007, in case the
transitional flag were honoured only in a transitional mode: 1002 twips
again. `-stop-` is the calibration pair at `left="360"`, where both readings
would land in the same place, and it agrees with itself.
So Word does not place the text after a number by finding a tab stop at all
where the indent is one — it places it at the indent, and the flag has
nothing to switch off. The stop below stays unconditional.
withInheritedRunningParts
function withInheritedRunningParts(section: SectionProperties, previous: SectionProperties | undefined): SectionProperties
Carries the running parts of one section forward into the next.
Headers and footers are the one part of a section that is *not* self
contained. Word writes a complete `w:sectPr` for everything else — page size,
margins, columns — but a header reference is written only where the header
changes. Everywhere else the section is linked to the one before it, which is
exactly what the "Link to Previous" button in Word's header editor controls,
and what its absence from the file means.
The link is per type. A section may replace the default header and keep
inheriting the first-page and even-page ones, and a report that changes its
running title at each chapter while keeping one title page does precisely
that. Inheriting all-or-nothing puts the wrong title on every chapter after
the first.
There is no way to declare "no header here": to break the link and show
nothing, Word references a header part that is empty. Absence therefore
always means inherit, never means none — a distinction that matters, because
a section that reserved no room for an inherited header would run its text
under it.
wordLineRatio
function wordLineRatio(fontFamily: string | undefined): number | undefined
Word's ratio for the first family of a stack that has one.
The stack is read left to right, as CSS resolves it: the first family the
table knows is the one the text will actually be set in, and a fallback named
after it never gets the chance to decide the line.
writeBlock
function writeBlock(context: WriteContext, block: BlockNode): void
writeBlocks
function writeBlocks(context: WriteContext, blocks: readonly BlockNode[]): void
Writes a run of blocks: the contents of a `w:body`, a cell or a header.
writeCellProperties
function writeCellProperties(writer: XmlWriter, properties: CellProperties | undefined): void
`w:tcPr`, in `CT_TcPrBase` order.
writeChartGraphicData
function writeChartGraphicData(writer: XmlWriter, drawing: DrawingNode): void
`a:graphicData` holding the one element that names a chart part.
writeChartPart
function writeChartPart(chart: ChartDefinition): string
writeDiagramGraphicData
function writeDiagramGraphicData(writer: XmlWriter, drawing: DrawingNode): void
`a:graphicData` holding the four relationships a diagram is reached by.
writeDocumentPart
function writeDocumentPart(input: DocumentPartInput, lost?: Map<string, number>): { readonly xml: string; readonly lost: Map<string, number>; }
writeDrawing
function writeDrawing(writer: XmlWriter, drawing: DrawingNode, naming: PictureNaming, content?: ShapeContent): void
writeGroupDrawing
function writeGroupDrawing(writer: XmlWriter, frame: DrawingNode, members: readonly DrawingNode[], naming: PictureNaming, content: ShapeContent): readonly DrawingNode[]
Writes a group and returns the members it could not write.
The return is not an error: a group holding a chart is a group whose other
shapes are still worth drawing, and the caller reports what was dropped by
the same route it reports everything else. An empty array is a whole group.
writeInlines
function writeInlines(context: WriteContext, nodes: readonly InlineNode[]): void
Inline content, grouped back into the runs it came from.
The model flattens a `w:r` into the nodes it produced — a run holding text, a
tab and a break becomes three nodes — so writing one node per element would
turn one run into three, each with its own copy of the formatting. That is
valid and it is not what the file said, and on a document of a million runs
it is three times the size.
So nodes are regrouped: consecutive nodes sharing one formatting object go
back into one `w:r`.
writeMath
function writeMath(writer: XmlWriter, node: MathNode, content: MathContent): void
Writes an equation as `m:oMath`, or as `m:oMathPara` where it stood alone.
Never inside a `w:r`. `m:oMath` is paragraph content in the schema — a
sibling of the runs, not a child of one — and an equation written inside a
run is a document Word offers to repair. See `writeRun`, which closes the run
around it.
writeNumberingPart
function writeNumberingPart(numbering: NumberingDefinitions): string
writeParagraph
function writeParagraph(context: WriteContext, paragraph: ParagraphNode): void
A paragraph, built from the model.
The section properties of a paragraph that ends a section are written inside
`w:pPr`, which is where the format puts them and which is the one part of a
paragraph's properties that is not formatting.
writeParagraphProperties
function writeParagraphProperties(writer: XmlWriter, properties: ParagraphProperties | undefined, writeSection?: () => void): void
`w:pPr`, in `CT_PPrBase` order.
writeRowProperties
function writeRowProperties(writer: XmlWriter, properties: RowProperties | undefined): void
`w:trPr`, in `CT_TrPrBase` order.
writeRunProperties
function writeRunProperties(writer: XmlWriter, properties: RunProperties | undefined, element?: string, markRevision?: RevisionInfo): void
`w:rPr`, in `EG_RPrBase` order.
Written even when it holds nothing only if the caller asks: an empty `w:rPr`
is legal, Word writes them, and a run whose properties are empty is better
off with no element at all.
writeSectionProperties
function writeSectionProperties(writer: XmlWriter, section: SectionProperties): void
`w:sectPr`, in `EG_SectPrContents` order.
The one property element whose *absence* is not the same as its default. A
section states its page, its margins and its columns whether or not they
differ from anything, because a `w:sectPr` is what makes a section — and the
references to its running heads and its page numbering are the parts a
reader notices missing first: a report whose letterhead is gone, a chapter
that starts numbering at one.
Everything here was written because a comparison of two *models* said it was
being lost. Nothing about the text of those documents changed, which is why
`tools/write/model.mjs` exists.
writeShapeElement
function writeShapeElement(writer: XmlWriter, drawing: DrawingNode, content: ShapeContent, place?: { readonly x: number; readonly y: number; readonly id: number; readonly name: string; }): void
The `wps:wsp` itself, without the graphic data around it.
Written under `a:graphicData` for a shape standing on its own, and directly
inside `wpg:wgp` for one that is a member of a group. `place` gives the
member its rectangle in the group's coordinate space; a shape on its own sits
at the origin of a space its frame has already positioned.
writeShapeGraphicData
function writeShapeGraphicData(writer: XmlWriter, drawing: DrawingNode, content: ShapeContent): void
The graphic data of a shape, for the frame `drawing.ts` puts around it.
Called with the writer positioned inside `a:graphic`, which is where every
kind of drawing becomes its own kind.
writeStylesPart
function writeStylesPart(sheet: StyleSheet): string
writeTable
function writeTable(context: WriteContext, table: TableNode): void
A table, and the two things about writing one that are easy to get wrong.
`w:tblGrid` is not optional in practice: Word lays a table out from the grid,
and a table written without one is drawn with every column the same width
whatever the cells say. And `w:tblPr` comes before it, before the rows, and
nothing may come between them.
writeTableProperties
function writeTableProperties(writer: XmlWriter, properties: TableProperties | undefined, element?: string): void
`w:tblPr`, in `CT_TblPrBase` order.
writeVmlPicture
function writeVmlPicture(writer: XmlWriter, shape: VmlShapeNode, content: VmlContent): void
Writes a `w:pict` holding one VML shape or group.
Interfaces
AbstractNumbering
interface AbstractNumbering
An abstract list definition, `w:abstractNum`.
nsid
string | undefined
`w:nsid`: the identity of the list, shared by every copy of it.
Two documents pasted together keep their lists apart by this, and Word
matches a list against the one in its own gallery by it. A hex word.
template
string | undefined
`w:tmpl`: the gallery entry the definition was made from.
restartAfterBreak
boolean | undefined
The list starts again at the first item after a section break,
`w15:restartNumberingAfterBreak`.
Word 2013 added it and writes it into every list it saves. A numbered list
that runs on across a chapter boundary and one that restarts at each
chapter are the same markup apart from this.
Three-valued on purpose: a list that says nothing is not a list that says
no. Word's default is to restart, so writing `0` where the file wrote
nothing would change the document.
multiLevelType
string | undefined
`singleLevel`, `multilevel` or `hybridMultilevel`.
styleLink
string | undefined
Identifier shared by lists created from the same gallery entry.
numStyleLink
string | undefined
Numbering style that this definition implements.
levels
ReadonlyMap<number, NumberingLevel>
AltChunkNode
interface AltChunkNode
External content embedded by reference, `w:altChunk`.
id
number
Identity, for as long as the document is open.
What an edit names and what an incremental relayout compares. Not an index:
inserting a paragraph renumbers every index after it and leaves every id
alone, which is the entire reason this is here. See `model/ids.ts`.
AnchorOrigins
interface AnchorOrigins
The places an anchor may be counted from, in points down the sheet.
page
number
The top of the sheet, which is nought by definition and named for clarity.
margin
number
The top of the text area — the page less its top margin.
paragraph
number
The top of the paragraph the anchor sits in.
line
number
The top of the line the anchor sits on.
Annotation
interface Annotation
What one placeholder character in the text refers to.
kind
"footnote" | "endnote" | "comment"
ArtPropertyfrom @genomdev/office-core
interface ArtProperty
One entry of a shape's property table.
The table is two lists in one record, and that is the whole subtlety of it.
First a fixed-size array of (identifier, value) pairs; then, appended after
the array in the same order, the payloads of whichever of those pairs said
their value was a length rather than a value. Reading only the array gives a
shape whose picture is "eighteen bytes" instead of a picture.
value
number
The value, or the length of {@link complex} when the property is one.
isBlipId
boolean
True when the value names a blip rather than being one.
complex
Uint8Array<ArrayBufferLike> | undefined
ArtRecordfrom @genomdev/office-core
interface ArtRecord
One record: its header, and either its bytes or its children.
data
Uint8Array<ArrayBufferLike>
The record's payload, a container's included.
A view rather than a copy, so keeping it for a record that also has
children costs nothing — and there are records where it is the only way
through. PowerPoint marks `msofbtClientData` and `msofbtClientTextbox` as
containers and then fills them with records of *its own* language, which
happen to share this eight-byte header exactly. Walked as Office Art they
produce a plausible tree of the wrong thing; the reader that knows better
needs the bytes.
Band
interface Band
A line's own vertical extent, which is what decides whether a box touches it.
BlockGeometry
interface BlockGeometry
One block, placed, with its lines.
index
number
Which block of the flow this is, or `-1` for something that is not one.
A running head, a running foot and the footnote area belong to the *page*
rather than to the flow, and they were all reported as `-1` — which was
enough for a comparison, which only wants to know where a line is, and not
nearly enough for a painter, which has to draw a head differently from a
foot. {@link BlockGeometry.furniture} says which.
furniture?
"footer" | "header" | "notes" | "lineNumbers" | undefined
What this is, where it is not a block of the flow.
Absent for the flow itself, which is the overwhelming majority.
frame?
{ readonly x: number; readonly width: number; } | undefined
The column the block was set in: its left edge and its width, in pixels.
Not derivable from the lines. A paragraph's shading and its rules fill the
*measure*, not the ink — an empty paragraph with a grey background is a
grey band the width of the column, and a short line in a shaded paragraph
is still shaded to the right margin. Absent where the block is not of the
flow.
cells?
readonly CellGeometry[] | undefined
The cells of a table, where this block is one.
In the page's frame, like everything else here. A table divided over two
pages reports on each page the cells that page draws.
bands?
readonly BandGeometry[] | undefined
The paragraph bands of a box, where this block is one.
A block of the flow has none: its band is its {@link BlockGeometry.frame}
and the ref the painter already holds. See {@link BandGeometry}.
BodyContext
interface BodyContext extends ConvertContext
Everything the builder needs that is not the text itself.
ids
NodeIds
The identities the nodes of this document are given.
Identity is all a node carries beside what it means, whichever generation
it came from — which is why `.doc` opens into the same model as `.docx`
and can be saved as one.
fonts
readonly DocFont[]
The font table: character properties name a font by its index in it.
defaultCodePage
number
The document's language, which decides how eight-bit text with no font decodes.
legacyProperties?
boolean | undefined
The property modifiers are Word 6/95's, whose opcodes are a byte wide.
Set for a document old enough that its formatting cannot be read with the
decoder here. The text, the paragraphs and the tables still can, so such a
document is read without formatting rather than not read — and reading the
old opcodes with the new decoder would not degrade, it would invent: the
same bytes mean different properties, so the paragraphs would come out
confidently wrong rather than plain.
keepDeletedContent?
boolean | undefined
Show text that a tracked change deleted.
Off by default, which is what a reader means by "open this document": the
deleted words are not part of it. Leaving them in does not merely add
text — it makes the document state the opposite of what its author left
behind, and nothing on the page says which sentence is which.
sectionEnds?
ReadonlyMap<number, SectionProperties> | undefined
Section properties keyed by the character position the section ends at.
annotations?
ReadonlyMap<number, Annotation> | undefined
What the placeholder characters stand for, keyed by character position.
A footnote reference, an endnote reference and a comment reference are all
one character in the text with nothing in it to say which — the tables that
list them are the only thing that does, and without this map every one of
them is dropped as an unreadable control character.
fcOf
(cp: number) => number
Character position to file offset, which the formatting tables are keyed by.
media?
MediaStore | undefined
The pictures the document holds, already read and named.
shapesAt?
((cp: number) => readonly AnchoredShape[]) | undefined
The shapes anchored at a character position; a group contributes several.
bookmarks?
BookmarkMarks | undefined
Where each bookmark opens and closes, by character position.
BodyParserOptions
interface BodyParserOptions
Options controlling which optional content the body parser keeps.
keepDeletedContent?
boolean | undefined
Keep content marked as deleted by revision tracking.
Off by default, which renders the document as if all changes were accepted —
what a reader expects to see. The viewer can turn it on to show markup.
parseMath?
boolean | undefined
Parse OMML equations. On by default.
onProgress?
((fraction: number) => void) | undefined
Report progress as a fraction of the part consumed.
ids?
NodeIds | undefined
The identity minter of the document being read.
Shared across every part, because a header's paragraphs and the body's are
paragraphs of one document and an edit names one of them without caring
which part it came from. A parser given none mints its own, which is what a
test that parses a fragment wants.
preserve?
boolean | undefined
Whether to record where each node stood, and to keep what is not modelled.
On by default. Off, the parser behaves exactly as it did before spans
existed: no `source`, no `Raw` nodes, nothing kept that nothing draws. The
cost of leaving it on is twelve bytes a block and one node per unmodelled
element; the cost of turning it off is that saving the document loses them.
BookmarkEndNode
interface BookmarkEndNode
BookmarkMarks
interface BookmarkMarks
What starts and what ends at each character position.
starts
ReadonlyMap<number, readonly string[]>
ends
ReadonlyMap<number, readonly string[]>
BookmarkStartNode
interface BookmarkStartNode
Border
interface Border
sizeEighths
number | undefined
Width in eighths of a point, as stored in `w:sz`.
spacePoints
number | undefined
Distance from the text in points, `w:space`.
color
string | undefined
Colour as `RRGGBB`, or `auto`.
themeColor
string | undefined
Theme colour reference, `w:themeColor`.
themeTint
string | undefined
Lightening applied to `themeColor`, `w:themeTint`.
themeShade
string | undefined
Darkening applied to `themeColor`, `w:themeShade`.
Borders
interface Borders
tl2br?
Border | undefined
Diagonal borders, table cells only.
between?
Border | undefined
Borders between paragraphs sharing a border set.
BoxSpan
interface BoxSpan
Anything on a line that is not text the breaker may cut: a picture, a tab
already sized against its stop, a numbering label, or the padding and border
of a boxed run.
Its width is given, not measured. What differs between them is only what a
break may do around them, which is what the two flags say.
label?
boolean | undefined
Part of a list's label rather than of its text; see {@link Piece.label}.
mayBegin
boolean
Whether a line may begin at the box, if what precedes it allows one.
False for a tab — a tab that began a line would be advancing to a stop
behind it — and for the insets of an inline box, which are not breakable
material at all but a widening of the text around them.
opensLine
boolean
Whether a line may begin *after* the box, whatever preceded it.
True for an object, which is a thing on the line and ends a word the way a
space does. False for an inset, which must leave the text on both sides of
it reading as one word.
widthAt?
((x: number) => number) | undefined
The width, where it depends on where the box lands.
A tab is exactly this and nothing else is: its advance is not a width but
the distance to the next stop, so it cannot be known until the line has
been filled as far as the tab. Given, it replaces {@link BoxSpan.width}.
The `x` it is handed is measured from where a *continuation* line of the
paragraph begins — its left indent — so a caller with the column's ruler in
hand adds that indent and no more. The first line's own indent is already
in it: `chooseLines` knows how much narrower the first line is, and both
lines end at the same right edge.
hangAt?
((x: number) => number) | undefined
How far the text after the box hangs past the edge the line was broken to.
A tab stop is a position on the *column's* ruler and may sit past the
paragraph's own right indent; Word honours it there, and the page number of
a table-of-contents entry hangs into the indent while the title above it
still wraps at it. See {@link TabAdvance.beyond }.
A shift and not a width, deliberately: the line is broken and aligned at
the indent — which is what keeps the *wrap* Word's — and what follows the
tab is then painted out over it. Given, everything after the box on the
line moves right by this much, and the leader fills the room as well.
leaderAt?
((x: number) => { readonly glyph: string; readonly width: number; readonly run?: unknown; } | undefined) | undefined
The character a tab draws across the room it takes, and its advance.
`w:tabs/w:tab/@w:leader` — the dots of a table of contents. They are text
on the line as far as anything reading the page is concerned, and a line
whose leader is dropped does not match the line Word drew.
endsLine?
boolean | undefined
Whether the line must end here — an explicit `<w:br/>`.
It ends the word as well, so that no pair straddles it, but it opens no
line: what follows begins one because the break did, not because a break
opportunity was found.
fillsLineAt?
((x: number) => boolean) | undefined
Whether the box ends its line, given the abscissa it lands at.
endsLineAt?
((x: number) => boolean) | undefined
wrapsAt?
((x: number) => boolean) | undefined
BreakNode
interface BreakNode
clear
"all" | "none" | "left" | "right" | undefined
Text wrapping around a floating object, `w:br/@w:clear`.
BreakPiece
interface BreakPiece
An explicit line break, `<w:br/>`: the line ends here and the next begins.
Not a paragraph boundary — the spacing around a paragraph is not paid — and
not a space either. A paragraph that writes one and has it dropped comes out
as one long line, which fits several of Word's on each of ours and gives a
document a third of the pages it should have.
ref?
unknown
See {@link PieceRef}.
family?
string | undefined
The type the break is written in, which rules a line holding nothing else.
See the note below this interface for what was refused; what is carried
now is narrower: only a line with no other run on it is ruled by its break.
CellInput
interface CellInput
One cell of a row, with whatever it holds.
ref?
unknown
What whoever built this wants back when it is drawn; see {@link PieceRef}.
Borders, shading, the address a citation uses — none of them changes where
anything goes, so none of them is the engine's, and this is how they reach
the painter without the engine learning what a border is.
align?
"center" | "top" | "bottom" | undefined
`w:vAlign`: where the content sits in a cell taller than it is.
vMerge?
"restart" | "continue" | undefined
`w:vMerge`: whether the cell begins a vertical merge or continues one.
A continuation draws nothing of its own — its text belongs to the cell that
began the run — and it makes its row no taller; the run's height is spread
over the rows it covers by {@link spreadMergedHeight}.
vertical?
boolean | undefined
`w:textDirection`: the cell's text runs up or down rather than across.
A rotated cell is as tall as its text is long, and as wide as its text is
tall — the two measurements change places. Its lines are not placed: where
they would be drawn is a matter for the painter, and a rotated line's words
are nowhere a comparison by baseline would look for them. What matters to
the page is the height, and that is a length of text.
direction?
"tbRl" | "btLr" | undefined
Which way a vertical cell's text runs: `tbRl` top to bottom with its lines
stacked from the right, `btLr` bottom to top with its lines from the left.
upright?
boolean | undefined
`tbRlV`: East Asian vertical; see {@link LineGeometry.upright}.
span?
number | undefined
`w:gridSpan`: how many columns of the grid the cell covers.
width?
TableWidth | undefined
`w:tcW`: the width this cell would prefer, where it states one.
Read only by the content-driven autofit, which needs to know whether a
column is a length, a percentage or nothing at all; the width a cell is
*given* comes from the grid. See `table-width.ts`.
noWrap?
boolean | undefined
`w:noWrap`: the cell's content is measured as if it could not be broken.
ECMA-376 §17.4.29 in as many words — "the contents of that this table cell
shall be treated as though they have no breaking characters" — which makes
the cell's minimum content width equal to its maximum. It bears only on the
autofit; a cell under a stated grid wraps as it must.
margins?
CellSideMargins | undefined
The cell's own margins where it states them, the table's otherwise.
hideMark?
boolean | undefined
`w:hideMark`: the end-of-cell glyph does not rule the row.
An empty cell is still a line tall, because the mark Word draws in it is a
line — unless the cell says this, and then it is nothing at all.
`table-hide-mark-plain` puts four rows of empty cells between two markers
and Word spends 67.5px on them, four lines; `-hidden` writes `w:hideMark`
on every cell of the same four rows and Word spends **nought** — the marker
under the table sits one ordinary line below the one over it. `-sized`
states eighteen points on the hidden mark and Word still spends nothing, so
it is not a smaller line but no line; `-mixed` puts a line of text beside a
hidden empty cell and the row is that line, so the cell that holds
something still rules.
86 documents of the corpus write it, and 11.6 % of them disagree with Word
about their page count where the corpus as a whole disagrees about 5.0 %.
CellMargins
interface CellMargins
CellProperties
interface CellProperties
Cell-level formatting, `w:tcPr`.
gridSpan?
number | undefined
Number of grid columns the cell spans, `w:gridSpan`.
verticalMerge?
"restart" | "continue" | undefined
Vertical merge state, `w:vMerge`.
horizontalMerge?
"restart" | "continue" | undefined
Horizontal merge state, `w:hMerge`; the legacy form of `gridSpan`.
verticalAlignment?
"both" | "center" | "top" | "bottom" | undefined
conditionalFormatting?
ConditionalFormatting | undefined
CellSideMargins
interface CellSideMargins
The room a cell keeps beside its content, in twips.
top?
number | undefined
`w:tcMar/@w:top` and `@w:bottom`, which Word defaults to nought.
They take no width off the line, which is why the pair beside them is
enough for {@link cellContentWidthOf} — but a cell that states them is
taller by them, and its content begins that far down.
ChartAxisfrom @genomdev/office-core
interface ChartAxis
position
"b" | "t" | "r" | "l"
`c:axPos`: `l`, `r`, `b` or `t`.
kind
"value" | "category" | "date" | "series"
Whether the axis is a category axis, a value axis or a date axis.
deleted
boolean
`c:delete`, an axis that is present in the file and not on the page.
reversed
boolean
`c:scaling/c:orientation`; `maxMin` reverses the axis.
numberFormat
string | undefined
`c:numFmt/@formatCode`, the format the tick labels are printed in.
labelled
boolean
Whether tick labels are drawn at all, from `c:tickLblPos`.
titleSize
number | undefined
The size the title states, `c:title//a:defRPr/@sz`, in points.
line
DiagramOutline | undefined
The axis line itself, `c:spPr/a:ln`.
text
ChartTextStyle | undefined
`c:txPr`: the type its tick labels are set in.
majorTickMark
string | undefined
`c:majorTickMark`: `none`, `in`, `out` or `cross`.
crossBetween
string | undefined
`c:crossBetween`: whether the marks sit between the ticks or on them.
`between` puts a bar in the middle of its band, which is what a bar chart
wants; `midCat` puts a line's first point on the axis itself, which is what
a line chart wants and what makes its curve start at the left edge instead
of half a band in.
ChartDefinitionfrom @genomdev/office-core
interface ChartDefinition
titleAutomatic?
boolean | undefined
Whether the title is one the file wrote out or one the application makes.
`<c:title/>` with no `c:tx` is not a chart without a title: it is a chart
whose title the application supplies. Both applications name it after the
one series where there is one — see `automaticTitle` — and where there is
more than one PowerPoint leaves it blank while Word writes the words
`Chart Title`. This is that case, and the host fills it in through
`ChartRenderContext.blankTitle`.
legend
ChartLegendPosition | undefined
legendText
ChartTextStyle | undefined
`c:legend/c:txPr`: the type the legend's own entries are set in.
legendLayout?
ChartManualLayout | undefined
`c:legend/c:layout/c:manualLayout`: where the author dragged the legend.
legendOverlay?
boolean | undefined
`c:legend/c:overlay`: whether the legend sits over the plot rather than beside it.
fill
DiagramColor | undefined
Fill of the whole chart area, `c:chartSpace/c:spPr`.
fillGradient?
ChartGradient | undefined
...where that fill is a gradient rather than one colour.
A chart flooded with a gradient and lettered in white is unreadable drawn
on white paper: `tdf128207` has a black-to-grey bar chart whose every
label, tick and title disappeared. Kept as the stops and the angle, which
is what a gradient is; a reader that can only manage one colour takes the
first stop.
fillPattern?
ChartPattern | undefined
...or a hatching, `a:pattFill`.
`tdf128207` stands its pie on a dark downward diagonal; drawn as plain
white the chart loses the one thing that told it apart from its neighbour.
outline
DiagramOutline | undefined
plotFill
DiagramColor | undefined
Fill of the plot area, which is usually absent and then transparent.
plotOutline
DiagramOutline | undefined
`c:plotArea/c:spPr/a:ln`: the frame drawn around the plot itself.
Not the axes — those are two rules and this is four — and a chart that
states one is framed by Word whatever its axes do.
blanks
"span" | "gap" | "zero"
`c:dispBlanksAs`: what a plot does where its data has a hole.
`gap` leaves the mark out, `zero` draws it on the floor, `span` joins across
it. Word's own default is `gap`, and a series of measurements taken weekly
with a fortnight missing looks entirely different under each of the three.
text
ChartTextStyle | undefined
`c:chartSpace/c:txPr`: the type the whole chart is set in.
The one every real chart states, and the one nothing read. A chart written
by PowerPoint puts its size here once and leaves each axis silent, so a
renderer looking only at the axes falls back to its own default: eighteen
points of stated type drawn at ten, and a title at fourteen. It is not a
subtle error — it is every label of every chart at three quarters of its
size, therefore at three quarters of its width, therefore centred over a
different part of the bar it labels.
plotArea
ChartManualLayout | undefined
Where the author put the plot area, if the file says; see {@link ChartManualLayout}.
language
string | undefined
`c:lang`: the language the chart states its numbers were written in.
Read and kept, and *not* what decides the decimal separator: a chart
stating `en-US` has its labels drawn `3,3` on a German machine, so the
application writes the separator of whoever opens the file. See
`chart-render`'s `separators`.
titleSize
number | undefined
`c:title`'s own size in points, where it states one.
titleStyle?
ChartTextStyle | undefined
The weight, the slant, the colour and the face the title states.
titleLines?
readonly ChartTitleLine[] | undefined
The title's paragraphs, each with the size it states.
A chart title is a text body: `aascu.org…` heads three of its charts with
a line of 18.72 px over a parenthesis of 16, and one string at one size
folds into different words on different lines. Empty for a title the
application generated (see `automaticTitle`), which is one line by
construction.
titleLayout?
ChartManualLayout | undefined
`c:title/c:layout/c:manualLayout`: where the author dragged the title.
Three charts side by side on slide 16 of `aascu.org…` and two of them
state one; ignored, their headings sit eight pixels below PowerPoint's
while the third — which states nothing — lands to a quarter of a pixel.
fonts?
{ readonly major: string | undefined; readonly minor: string | undefined; } | undefined
The typefaces of the theme the chart part carries of its own.
A chart is related to a `themeOverride` part as often as not — Excel
writes one whenever the workbook's theme differs from the document's — and
it is that theme, not the presentation's, that the chart's unstated fonts
resolve against. Slide 8 of `aascu.org…` is the case: the deck is set in
Lucida Sans Unicode, the chart's override says Calibri, and a title drawn
in the deck's face is wide enough to fold into three lines where
PowerPoint folds it into two.
Filled in by whoever read the part, since the chart XML cannot see its own
relationships.
ChartOptions
interface ChartOptions
A chart to put in a document, said in the least a chart can be said in.
The full `ChartDefinition` is what a chart *read from a file* comes back as —
axes with ids, plots with gap widths, every colour of every series — and
almost none of it is what somebody inserting a chart wants to type. This is
the short form: what kind, what the categories are called, and one or more
named series of numbers. {@link DocumentBuilder.chart} fills in the rest with
what Word own Insert Chart produces.
kind?
"column" | "area" | "bar" | "line" | "pie" | "doughnut" | undefined
categories
readonly string[]
One label per point, shared by every series.
series
readonly { readonly name?: string; readonly values: readonly (number | undefined)[]; readonly color?: string; }[]
legend?
"b" | "t" | "r" | "tr" | "l" | undefined
Where the key goes; absent means no key at all.
showValues?
boolean | undefined
Print the value beside every mark.
ChartPlotfrom @genomdev/office-core
interface ChartPlot
One `c:*Chart` inside the plot area.
direction
"col" | "bar"
`c:barDir`: `col` for vertical bars, `bar` for horizontal ones.
gapWidth
number
Space between category groups as a percentage of bar width, `c:gapWidth`.
overlap
number
How far bars of one category overlap, as a percentage, `c:overlap`.
holeSize
number
Hole of a doughnut as a percentage of its diameter, `c:holeSize`.
markers
boolean
Whether a line plot draws markers at its points.
varyColors
boolean
`c:varyColors`: paint each point of the plot differently.
What makes a one-series bar chart a row of coloured bars rather than a row
of identical ones, and it is on by default for a pie — where it is the only
thing that tells one slice from the next.
scatterStyle
string | undefined
`c:scatterStyle`: whether a scatter plot joins its points, and with what.
`marker` is a cloud of points with no line; `line` and `lineMarker` join
them with segments; `smooth` and `smoothMarker` with a curve.
radarStyle?
string | undefined
`c:radarStyle`: `standard` joins the points, `marker` marks them too and
`filled` floods the shape they enclose.
showValues
boolean
Whether the plot asks for its values to be printed beside every mark.
axisIds
readonly string[]
Axis ids this plot is drawn against, in the order they were declared.
ChartPointfrom @genomdev/office-core
interface ChartPoint
One value of a series, with the point it belongs to.
ChartSeriesfrom @genomdev/office-core
interface ChartSeries
name
string | undefined
`c:tx`, the cached series name.
categories
readonly (string | undefined)[]
Category labels, from `c:cat`; sparse points are `undefined`.
values
readonly (number | undefined)[]
Values, from `c:val` or `c:yVal`.
xValues
readonly (number | undefined)[] | undefined
Horizontal values of a scatter or bubble plot, from `c:xVal`.
sizes
readonly (number | undefined)[] | undefined
Bubble sizes, from `c:bubbleSize`.
outline
DiagramOutline | undefined
invertIfNegative
boolean
`c:invertIfNegative`, a bar below zero drawn in the inverse of its fill.
pointFills
ReadonlyMap<number, DiagramColor>
`c:dPt`, one point painted differently from the rest of its series.
pointMarkers
ReadonlyMap<number, ChartPointMarker>
`c:dPt/c:marker`: the symbol one point carries where its series carries
none.
A survival curve marks its censored observations this way — the line says
`c:symbol val="none"` and every censored point states a `plus` of its own —
and drawn without them the chart loses the very thing it is about.
smooth
boolean
`c:smooth`, a line drawn as a spline rather than as segments.
marker
string | undefined
`c:marker/c:symbol`; `none` when the series asks for no marker.
categoryFormat?
string | undefined
`c:cat//c:formatCode`: how the cached category numbers are to be printed.
Absent on the ordinary chart, whose categories are words. A date axis
caches serial numbers and names the format beside them.
markerStyle
ChartPointMarker | undefined
`c:marker` whole: the symbol with the size and the paint stated beside it.
A series states its markers once and its points may each restate them, so
the two carry the same shape.
showValues
boolean
Whether the series asked for its values to be printed beside the marks.
labelledPoints?
ReadonlySet<number> | undefined
The points that carry a label of their own, where the series carries none.
`c:dLbls` may delete the series' labels outright and then give three of
its points a `c:dLbl` apiece, which is what PowerPoint writes when a
label is dragged or typed over: the pies of `7317951-ICON-BITS` state
`<c:delete val="1"/>` and label every slice. Undefined means the series'
own answer covers every point.
customLabels?
ReadonlyMap<number, readonly string[]> | undefined
`c:dLbl/c:tx/c:rich`: what a point's label says, where somebody typed over
it. One string per paragraph, with `{{VALUE}}`, `{{CATEGORYNAME}}` and the
other fields left for the renderer to fill in. The text stands in for the
value, and a point that carries one is labelled whether or not the series
shows its values.
labelText?
ChartTextStyle | undefined
`c:dLbls/c:txPr`: the type the series' own labels are set in.
A chart states its type on `c:chartSpace`, on an axis, or here — and here
is the only place `aascu.org…` states it, so its values were drawn at the
renderer's default of ten points against PowerPoint's fourteen.
showCategories?
boolean | undefined
`c:dLbls/c:showCatName`: the label says the point's category, not its value.
Word's label is the concatenation of whatever `c:dLbls` turns on — series
name, category, value, percentage — and a pie is the chart that usually
turns the category on and the value off. `tdf137154` is eight slices of
`long data label N` labelled that way, and reading only `c:showVal` drew
the number behind each of them instead.
labelsStated?
boolean | undefined
Whether the series wrote a `c:dLbls` of its own.
The plot's group is a default the series inherit, and a series that states
its own is not one of them. `tdf137154` states both — the series asks for
the category and not the value, the plot for the value and not the category
— and merging the two labels every slice `long data label 8, 2`.
labelPosition?
string | undefined
`c:dLbls/c:dLblPos`: where the label sits against its point.
order
number
Order the series is drawn and listed in, `c:order`.
ChpxSpan
interface ChpxSpan
Character formatting for one stretch of the file.
grpprl
Uint8Array<ArrayBufferLike>
ColorReference
interface ColorReference
A colour as DrawingML writes it: a source plus modifiers on it.
srgb
string | undefined
Literal `RRGGBB`, from `a:srgbClr`.
scheme
string | undefined
Theme slot, from `a:schemeClr`.
luminanceModulation?
number | undefined
`a:lumMod`, in thousandths of a percent.
luminanceOffset?
number | undefined
`a:lumOff`, in thousandths of a percent.
alpha?
number | undefined
`a:alpha`, in thousandths of a percent; absent means opaque.
shade?
number | undefined
`a:shade`, in thousandths of a percent.
tint?
number | undefined
`a:tint`, in thousandths of a percent.
saturationModulation?
number | undefined
`a:satMod`, in thousandths of a percent.
saturationOffset?
number | undefined
`a:satOff`, in thousandths of a percent.
hueModulation?
number | undefined
`a:hueMod`, in thousandths of a percent.
hueOffset?
number | undefined
`a:hueOff`, in sixtieths of a thousandth of a degree.
grayscale?
boolean | undefined
`a:gray`: painted in shades of grey.
inverted?
boolean | undefined
`a:inv`: every channel inverted.
ColumnDemand
interface ColumnDemand
One column, as the distribution needs to see it.
minContent
number
The narrowest the column may be without its content overflowing.
CSS calls it the minimum content width; Word arrives at the same thing
through `w:noWrap`, which asks for a cell "treated as though [its contents]
have no breaking characters" and so raises this to the maximum.
maxContent
number
The width the content takes when only its explicit breaks are honoured.
originates
boolean
Whether any cell begins in this column; a column of spans alone has none.
ComposeCacheStats
interface ComposeCacheStats
What a second composition of the same document need not do again.
`composeDocument` turns a parsed document into what the layout engine takes,
and for a document of a thousand pages that is the most expensive thing the
viewer does after parsing: every run resolved through the style cascade,
every family resolved through the substitution chain, every piece of text cut
into the spans the line breaker works on. Doing it again after a keystroke
changed one paragraph is doing it again for the other two hundred thousand.
The cache is keyed on the **node object**, which is what makes it work at
all: the edit layer replaces a node rather than mutating one — see
`edit/tree.ts` — so after an edit every paragraph but the one that changed is
the very same object it was, and identity answers "did this change?" exactly
and in one comparison.
## What is not cached, and why that is not a compromise
A composition is not a pure function of a paragraph. Three things carry state
along the walk:
- **a list's number**, which depends on every numbered paragraph before it;
- **a footnote's mark**, which is counted from one over the document;
- **a `LISTNUM` field**, which advances a counter of its own.
A paragraph that touches any of them is composed afresh. That is not a
limitation worked around but the honest reading of what those paragraphs are:
their composition depends on their neighbours, so their composition cannot be
reused when their neighbours may have moved. Everything else — which is the
great majority of every document measured — is a function of the paragraph,
the width it is set in and the margin it is set against, and those three are
the key.
hits
number
Compositions answered from the cache.
misses
number
Compositions the cache had no answer for.
ineligible
number
Paragraphs the cache declined to hold; see the note above.
ComposedDocument
interface ComposedDocument
The blocks, the first section's page, and what is drawn on every page.
nodeOfBlock
readonly (BlockNode | undefined)[]
The body node each composed block came from, where it came from one.
The way back from the engine's numbering to the document's, and the only
one there is. The two do not correspond: a section is announced as a block
of its own, a paragraph broken over a page arrives as two, a content-block
wrapper is flattened away and a marker is dropped — so the *n*th composed
block is not the *n*th anything the reader could name.
Nothing else can be reconstructed from the outside, and every caller that
tried arithmetic instead got a wrong page or none: an outline is a list of
paragraphs, and the question "which page is this paragraph on" has to be
asked with the paragraph.
Nothing for a block the engine was given rather than the body — a section,
an endnote appended after the last of it.
faces
Faces
The faces the caller's resolver returned, so it need not build them twice.
missing
ReadonlyMap<string, number>
What the walk left out, and how often, by its own name for it.
A running head it cannot read is one entry here; there is nothing else it
declines to compose. Empty over the whole corpus, and kept because a walk
that silently drops a head moves every page of the document that has one.
ComposeOptions
interface ComposeOptions
What the caller has to supply because a pure function cannot know it.
faces
(context: { defaultFamily: () => string | undefined; }) => Faces
How a family name becomes something measurable.
Called once, after the document's theme and default face are known — which
is why it is a function and not a value: the resolution chain wants the
document's own default as its next-to-last resort, and that default is not
known until the theme has been read.
cache?
ComposeCache | undefined
Work kept from an earlier composition of the same document.
What makes composing a document a second time cost a walk rather than a
composition. Nothing is required and nothing changes without it; with it, a
paragraph the edit layer did not replace is the same object it was and its
composition is reused. See `layout/compose-cache.ts` for what is kept and
what deliberately is not.
chartText?
((chart: ChartDefinition, widthPx: number, heightPx: number, themeColor: (slot: string) => string | undefined) => readonly ObjectGlyph[]) | undefined
Where a chart's words go, for a caller that can work it out.
A chart is stored only as its definition, and turning one into places is
what `@genomdev/office-core`'s chart drawing does — the scale, the room the
ticks need, where that leaves the plot. That drawing is the browser half,
and this engine is below it: dependencies point downwards. So the answer is
*supplied*, exactly as {@link faces} supplies measurement, and
`@genomdev/docx/view`'s `chartGlyphs` is the implementation a host passes.
Without it a chart is a box with nothing in it, which is what this engine
drew for the thirteen documents of the corpus that are a page and a chart.
A host that *draws* the chart itself — the viewer paints the real thing,
marks and all — leaves this unset: the words would then be drawn twice. It
is for whoever reads the computed page as text: a comparison, an extractor,
a search index.
hyphenates?
((language: string) => boolean) | undefined
Which of the document's languages may be hyphenated, where the caller knows.
**Word hyphenates a language only where its proofing tools are installed**,
and that is a property of the machine rather than of the file: the same
document opened on two computers paginates differently.
`auto-hyphenation-by-language` is the probe — the same four long words in
six languages, in a column narrow enough that every line has to choose —
and the Word that draws this corpus's references breaks the German, the
English and the French and leaves the Portuguese, the Swedish and the
Russian whole. The six `MSHY7*.LEX` modules beside it say which: EN, ES, FR,
GE, IT, NL.
Left unset, every language the patterns cover is hyphenated, which is what a
viewer should do: a reader has no proofing tools to install, and the Word
the author wrote in almost certainly had the module for the language he was
writing. It is set by the *comparison*, which must ask this engine for the
page the reference Word drew and not for the page Word ought to draw.
ComposeState
interface ComposeState
What one document's walk carries with it.
Every field is settled once, from the document, before the first block is
built; none of them changes because of anything the walk finds, except
{@link ComposeState.sheet}, which follows the section in force.
It is a parameter and not a module variable, and that is the difference
between a tool and a package. The tool it came from wrote in as many words
that it "reads one document at a time, and never two at once" — true of a
corpus run, false of a viewer with two tabs open. Held in the module, the
second document to start would take the first's page size halfway through.
sheet
{ heightPx: number; marginTopPx: number; marginBottomPx: number; widthPx: number; marginLeftPx: number; marginRightPx: number; }
The sheet the blocks being walked belong to, in pixels.
part?
string | undefined
The part the walk is inside, where it is not the document's own.
A relationship id is resolved against the part that wrote it: `rId5` in
`word/header1.xml` is a different picture from `rId5` in
`word/document.xml`, and often no picture at all. The reader takes the part
as an argument and defaults to the main one, so a header's letterhead
looked up in `document.xml.rels` came back as nothing and drew a broken
image at the corner of the sheet.
Carried on the piece — see {@link PieceRef} — because whoever draws it is
a long way from this walk by then.
numberingByName
Map<string, AbstractNumbering>
The document's abstract numbering by name, for a `LISTNUM` field.
noteCount
number
How many footnote references the walk has passed, and what number each got.
Word numbers the marks from one over the document, in the order the
references are met, and the note's own `w:footnoteRef` shows the same
number. The tool this came from kept the count *on the map of notes* — a
counter stashed on somebody else's object, which works exactly as long as
one document is being read at a time.
facesOfDocument
Faces | undefined
The faces of this document, for the script fallbacks that need to ask.
kinsokuOverrides
KinsokuOverrides
`w:kinsoku` and the document's own lists, memoised by language.
kinsokuSets
Map<string, KinsokuSet | undefined>
compressesJustified
boolean
`w:compat` and `w:settings`, read once; see where each is used.
wordWrapHonoured
boolean
Whether `w:wordWrap="0"` is honoured here; see where it is set.
gridCharTwips
number
What the section's character grid adds to every character, in twips.
ideographWords
boolean
Whether a run of ideographs that follows a space is one word to the line
breaker: Word 2007 and earlier, `compatibilityMode` 12 or none. See
`LineSpan.ideographWords`.
compressesPunctuation
boolean
`w:characterSpacingControl` says `compressPunctuation` or
`compressPunctuationAndJapaneseKana`: a mark may give back its blank half.
Absent is `doNotCompress`, ECMA-376's default.
mathJustification?
string | undefined
`m:mathPr/m:defJc`, where the document states one; see `mathAlignmentOf`.
hyphenates
boolean
`w:autoHyphenation` and what goes with it.
Word breaks a word at the foot of a line only where the document asks for
it, in the language the *run* declares, and never in a paragraph that says
`w:suppressAutoHyphens`. The zone is how much white it will leave at the
right rather than break a word; the limit is how many lines in a row may
end in a hyphen. See `Hyphenation` and where the pieces are built.
kernsPunctuation
boolean
`w:noPunctuationKerning` the other way round; see `kernsPunctuation`.
languages
Set<string>
Every `w:lang` the walk saw, so the patterns can be fetched for them.
hyphenation
Hyphenation
The patterns themselves, which arrive after the walk and before the layout.
A module per language and a dynamic import each, so they cannot be had
while a piece is being built; the walk collects the languages, this object
is handed to every `PageSetup` as it is made, and `composeDocument` fills
it in before it returns. The engine is synchronous and asks it then.
fillHyphenation
(loaded: Hyphenation) => void
hyphenatesLanguage
(language: string) => boolean
{@link ComposeOptions.hyphenates}, or everything the patterns cover.
underlinesTrailingSpace
boolean
`w:ulTrailSpace`: the spaces that end a line are underlined too.
bookmarks?
ReadonlySet<string> | undefined
Every bookmark the body writes, gathered before a piece is built.
`PAGEREF` needs it at composition and not at the end: a reference to a
bookmark that is gone draws `Error! Bookmark not defined.`, and that is
twenty-eight characters the line has to be measured with. Marked and
resolved last like a page number, the tab before it would reach for a
right stop with one character's width in hand, and the sentence would run
past the stop.
styleIds?
ReadonlyMap<string, string> | undefined
Every style by the name a `STYLEREF` instruction may call it, lowercased.
The instruction names the style as a reader does — `STYLEREF "Heading 1"`
against a style whose id is `Heading1` — so both the name and the id are
keys, and the comparison is case-blind because writers are not consistent
about it.
resolveHyperlink?
((relationshipId: string) => string | undefined) | undefined
A hyperlink relationship's address; see where an empty link is shown.
chartOf?
((relationshipId: string, ownerPart?: string) => ChartDefinition | undefined) | undefined
A chart part, by the relationship id the drawing names it with.
Loaded before anything is composed, so that a chart's text can be placed
while the piece that holds it is built; see `chart-text.ts`.
themeColorOf?
((slot: string) => string | undefined) | undefined
The theme's answer for a colour slot, which the chart drawing asks for.
chartText?
((chart: ChartDefinition, widthPx: number, heightPx: number, themeColor: (slot: string) => string | undefined) => readonly ObjectGlyph[]) | undefined
{@link ComposeOptions.chartText}, carried to where the piece is built.
ConditionalFormatting
interface ConditionalFormatting
Which conditional table-style formats apply to a row, cell or paragraph.
Word encodes this as a bit string in `w:cnfStyle`; the decoded flags are what
the style resolver needs in order to layer `firstRow`, `band1Horz` and the
rest on top of the base table style.
ContentBlockNode
interface ContentBlockNode
A grouping node produced by flattening a structured document tag.
Content controls carry semantics (a date picker, a repeating section) that the
viewer does not act on, but discarding the wrapper entirely would lose the tag
name that documents and tooling rely on for navigation.
id
number
Identity, for as long as the document is open.
What an edit names and what an incremental relayout compares. Not an index:
inserting a paragraph renumbers every index after it and leaves every id
alone, which is the entire reason this is here. See `model/ids.ts`.
controlId?
number | undefined
`w:sdtPr/w:id`: the control's own identity, a signed 32-bit number.
Not {@link id} — that one is minted per read. This is written into the file
and is what a data binding, a repeating section and Word's own document
part gallery name the control by, so a control that comes back without it
is a control nothing points at any more.
ConvertContext
interface ConvertContext
What the converters need to know about the document around them.
fonts
readonly DocFont[]
The font table: character properties name a font by its index in it.
styleIdOf
(istd: number) => string | undefined
Style index to style identifier, which for these documents is its name.
CreateDocumentOptions
interface CreateDocumentOptions
A Word document, built from nothing.
The shortest path from data to a `.docx`, and the reason the whole write
side exists in the shape it does:
```ts
const document = createDocument({ title: 'Quarterly report' });
document.heading('Quarterly report');
document.paragraph('Revenue rose by eleven per cent.');
document.table([
['Region', 'Revenue'],
['North', '1 204'],
], { header: true });
await writeFile('report.docx', (await document.save()).bytes);
```
It is a thin arrangement over {@link EditSession}: every method builds nodes
and puts them through a step, so a document being generated and a document
being edited are the same object with the same undo, and code written against
one works against the other. That is not symmetry for its own sake — the
common real task is *neither* of them alone but both: open a template, fill
in its content controls, append a table, save.
metadata?
DocumentMetadata | undefined
section?
SectionProperties | undefined
The section every page is set on; A4 with Word's own margins by default.
styles?
StyleSheet | undefined
The style table to write; the built-in set by default.
DiagramColorfrom @genomdev/office-core
interface DiagramColor
kind
"srgb" | "scheme" | "system"
`srgb` is literal, `scheme` refers to the theme, `system` to the host.
value
string
Hex digits, a theme slot name, or the last colour Word saw for a system slot.
transforms
readonly DiagramColorTransform[]
DiagramColorTransformfrom @genomdev/office-core
interface DiagramColorTransform
One transform applied to a colour, `a:lumMod` and its siblings.
Kept as a list rather than resolved fields because the transforms compose in
document order, and SmartArt relies on that: a colour list shifts hue,
saturation and luminance together to spread one accent colour across the
nodes of a diagram.
name
string
Local name, such as `lumMod`, `satOff`, `hueOff`, `alpha`, `tint`.
value
number
Percentages as a fraction; `hueMod` and `hueOff` in degrees.
DiagramDrawingfrom @genomdev/office-core
interface DiagramDrawing
DiagramFramefrom @genomdev/office-core
interface DiagramFrame
A position and size in EMU, with the rotation applied about its centre.
rotation
number
Clockwise rotation in degrees.
DiagramOutlinefrom @genomdev/office-core
interface DiagramOutline
dash
string | undefined
`a:prstDash/@val`, such as `dash` or `sysDot`.
cap?
"round" | "square" | "flat" | undefined
`a:ln/@cap`: how the line ends, `rnd`, `sq` or flat.
join?
"round" | "bevel" | "miter" | undefined
`a:round`/`a:bevel`/`a:miter`: how two segments meet.
headEnd?
DiagramLineEnd | undefined
`a:headEnd` and `a:tailEnd`: what the line carries at each end.
An arrow is the difference between a diagram that says "A causes B" and one
that says the two are related: 185 lines of the corpus carry one, and drawn
without it a flow chart loses its direction.
tailEnd?
DiagramLineEnd | undefined
DiagramParagraphfrom @genomdev/office-core
interface DiagramParagraph
align
"left" | "center" | "right" | "justify" | undefined
level
number
Outline level, `a:pPr/@lvl`; zero for a top-level paragraph.
bullet
string | undefined
`a:buChar/@char`, or undefined when the paragraph is not bulleted.
indentEmu
number | undefined
First-line indent, negative for a hanging indent.
lineSpacing
number | undefined
`a:lnSpc/a:spcPct` as a fraction of a single line.
spaceBefore
number | undefined
`a:spcBef`/`a:spcAft` as a fraction of a line.
DiagramRunfrom @genomdev/office-core
interface DiagramRun
A run of text inside a diagram shape. Properties come from `a:rPr`.
sizePoints
number | undefined
Size in points; `a:rPr/@sz` is in hundredths of a point.
DiagramShapefrom @genomdev/office-core
interface DiagramShape
preset
string
`a:prstGeom/@prst`, defaulting to `rect`.
adjust
ReadonlyMap<string, number>
Adjust values, `a:avLst/a:gd`, keyed by name and in their raw units.
fill
DiagramColor | undefined
Solid fill colour; undefined for `a:noFill` and for fills not yet read.
outline
DiagramOutline | undefined
text
DiagramTextBody | undefined
fontSlot?
"major" | "minor" | undefined
`dsp:style/a:fontRef/@idx`: the theme typeface of runs that name none.
fontColor?
DiagramColor | undefined
...and the colour `a:fontRef` gives them.
DiagramTextBodyfrom @genomdev/office-core
interface DiagramTextBody
frame
DiagramFrame
Where the text goes, `dsp:txXfrm`.
Separate from the shape's own frame because a preset shape rarely gives its
text the whole box: a chevron reserves the point, a callout the tail. Word
writes the solved rectangle so the reader does not have to know the
geometry of every preset.
anchor
"center" | "top" | "bottom"
paragraphs
readonly DiagramParagraph[]
DocBookmark
interface DocBookmark
A bookmark, in character positions.
DocFont
interface DocFont
A font as the document declares it.
alternateName
string | undefined
The font Word substitutes when this one is missing.
charset
number
Character set identifier, which is what says how eight-bit text decodes.
codePage
number | undefined
The code page implied by that character set.
family
string | undefined
Font family: roman, swiss, modern, script, decorative.
pitch
string | undefined
Fixed or variable pitch.
DocShape
interface DocShape
What a shape turns out to be.
storeIndex
number | undefined
The picture it shows, as a place in the document's store.
textRange
readonly [number, number] | undefined
Or the text box story it shows, as a character range within that story.
look
ShapeLook | undefined
Or, failing both, how it is drawn.
rotation
number | undefined
Clockwise rotation in degrees.
name
string | undefined
The name the author gave it, which is what alt text usually is here.
hidden
boolean
The shape is not drawn at all.
group
{ readonly space: Rect; readonly members: readonly DocShape[]; } | undefined
The members of a group, and the coordinate space they are stated in.
A group is anchored to the text as one shape and holds its members in a
space of its own — often a hundred thousand units across, so that a member
can be placed finely inside a box of any size on the page. Putting them
down means mapping that space onto the rectangle the anchor gives, which is
the whole of what a group is: 1,424 shapes of the corpus are inside one,
and without the mapping every one of them is in the top-left corner.
childRect
Rect | undefined
Where a member sits inside its group's space.
DocumentFacesOptions
interface DocumentFacesOptions extends DocumentFonts
tables
ReadonlyMap<string, AdvanceCuts>
The advances the caller has loaded, by folded family name.
Loaded rather than looked up, because loading is asynchronous and measuring
is not: a document names two to five families, and {@link familiesNamedBy}
says which before the walk starts.
embedded?
((family: string, bold: boolean, italic: boolean) => FaceMetrics | undefined) | undefined
A face read out of the document's own bytes, where it carries one.
`w:embedRegular` and its three siblings. The best source there is — it is
the very file the document was written against — and the only one that
answers for kerning and for joined scripts, which no table of widths can.
shapedWidth?
((family: string, bold: boolean, italic: boolean, text: string, sizePx: number) => number | undefined) | undefined
The width of text whose letters join, from whoever can shape it.
A table of advances holds the width of each character standing alone, and
in Arabic that is not the width of the word: `GSUB` gives the letters
initial, medial and final forms and the forms are narrower. Summed as
isolates, `tdf104649` comes out at 66 pages against Word's 56 and
`reports-cb3783573860` at four against three — the only two documents of
2062 that the tables move at all.
So the one question the written advances cannot answer is asked of whoever
can: the font program in Node, the browser's own shaper in a browser. It is
the single place the two sides do not share a number, and it is a
parameter so that the fact is visible rather than buried.
kernedWidth?
((family: string, bold: boolean, italic: boolean, text: string, sizePx: number) => number | undefined) | undefined
The width of text with the face's own kerning pairs applied.
The second and last question the written advances cannot answer, and for
the same kind of reason as the first: `kern` is thousands of pairs — 26 706
in Calibri, 29 715 in Cambria — and writing them down would be several
megabytes of table to buy 99 lines of 171 739 and not one page count.
Word kerns only at or above the size `w:kern` names, and 1303 of the
corpus's 1672 documents name none. So it is asked of whoever has the font:
the font program in Node, the browser's own `font-kerning` in a browser —
which is the face it will actually draw with, and therefore the right
answer for what the reader sees.
used?
Map<string, number> | undefined
How often each family was asked for, so a caller can report it.
missing?
Map<string, number> | undefined
What could not be resolved, and how often.
DocumentFonts
interface DocumentFonts
What the document itself knows about the faces it names.
shapeOf?
((family: string) => string | undefined) | undefined
`w:family`: `roman`, `swiss`, `modern`, `script` or `decorative`.
alternativeOf?
((family: string) => string | undefined) | undefined
`w:altName`: the name the file says this face may be drawn as.
ownDefault?
(() => string | undefined) | undefined
The document's own default face, as a thunk: the theme decides it.
DocumentGrid
interface DocumentGrid
A ruled page: `w:docGrid`, which lines snap to.
linePitchTwips
number
`w:linePitch`, in twips: the distance between two rules.
type?
string | undefined
`w:type`, which decides whether the page is ruled at all.
DocumentMedia
interface DocumentMedia
What a document exposes about the pictures it holds outside a package.
pictures
() => Iterable<DocumentPicture>
Every picture, for a save that has to carry them all.
DocumentPartInput
interface DocumentPartInput
finalSection
SectionProperties | undefined
The section that closes the body, `w:body/w:sectPr`.
background?
DocumentBackground | undefined
`w:background`: the colour behind every page, which stands before the body.
DocumentPicture
interface DocumentPicture
The pictures a document holds, and how they reach a package being written.
A `.docx` saved back to a `.docx` needs none of this: the media parts are in
the package already and are copied through as the compressed bytes they were.
Two cases are not that:
- **`.doc` converted to `.docx`.** The pictures live in the binary file's own
store, keyed by names like `doc-picture-3`, and the package being written
has no such parts and no relationships pointing at them. Without this, the
drawings are written referring to relationships that do not exist — which
Word draws as an empty frame with a red cross, and which no comparison of
the extracted *text* would ever notice.
- **a picture a caller added**, which has bytes and nothing else.
The contract is deliberately small: name the pictures, and hand over the
bytes of one. Everything else — the part name, the extension, the content
type, the relationship — is the writer's arithmetic.
id
string
The id a drawing's `r:embed` names it by.
bytes
Uint8Array<ArrayBufferLike>
DocumentSettings
interface DocumentSettings
Selected values from `settings.xml` that affect rendering.
defaultTabStopTwips
number
Default tab stop interval in twips, `w:defaultTabStop`.
evenAndOddHeaders
boolean
Even and odd pages use different headers, `w:evenAndOddHeaders`.
autoHyphenation
boolean
Automatic hyphenation is enabled for the document.
characterSpacing?
string | undefined
`w:characterSpacingControl`: how far East Asian punctuation may be squeezed.
`compressPunctuation` — the default, and what Word writes unless told
otherwise — lets a line take back the blank half of a full-width mark when
it needs the room; `doNotCompress` keeps it. See `Piece.squeezableEm`.
doNotHyphenateCaps
boolean
`w:doNotHyphenateCaps`: leave words written in capitals whole.
Word's own default is to hyphenate them, and the checkbox in the
hyphenation dialog is the other way round from this element.
noPunctuationKerning
boolean
`w:noPunctuationKerning`: two adjacent full-width marks keep their own ems.
Word kerns them into one half em between the inks unless this says not to,
and it only ever kerns a run that asks to be kerned at all — see
`kernsPunctuation` in `layout/line-break.ts`.
mirrorMargins
boolean
Mirror margins for double-sided printing, `w:mirrorMargins`.
trackRevisions
boolean
Whether tracked changes are being recorded.
footnoteNumberStart
number | undefined
Starting number of footnotes and endnotes.
footnotePosition
string | undefined
Where footnotes are placed, `w:footnotePr/w:pos`.
footnoteNumberFormat
string | undefined
`w:numFmt`: what the note marks are numbered with, document-wide.
footnoteRestart
string | undefined
`w:numRestart`: continuous, each page, or each section.
compatibility
ReadonlyMap<string, string>
Compatibility flags that change layout, `w:compat`.
The lookup the layout engine asks, and derived rather than read: the flags
of `w:compat` and the `w:compatSetting`s beside them, merged by name,
because `compatibilityMode` is a setting and `noExtraLineSpacing` is a flag
and a caller asking "what does this document say about spacing" should not
have to know which is which. What is written back comes from
{@link compatibilityFlags} and {@link compatibilitySettings}.
strictFirstAndLastChars
boolean
`w:strictFirstAndLastChars`: the strict kinsoku pair for Japanese.
The default Japanese list does not hold the small kana back from beginning
a line and the strict one does — which is why a probe asked of a document
without this setting saw Word let `っ` open a line. See `layout/kinsoku.ts`.
noLineBreaksBefore
ReadonlyMap<string, string>
`w:noLineBreaksBefore`: the characters this document will not begin a line
with, by the `w:lang` each element names. Supersedes the standard's list.
noLineBreaksAfter
ReadonlyMap<string, string>
`w:noLineBreaksAfter`, likewise, for the characters that may not end one.
decimalSymbol
string | undefined
Character used as the decimal separator in fields.
documentProtection
string | undefined
The document is protected; the viewer surfaces this as read-only.
colorSchemeMapping
ReadonlyMap<string, string>
Which theme slot each document colour slot actually names,
`w:clrSchemeMapping`.
A document says `w:themeColor="text1"` and the theme has no `text1`: it has
`dk1`, `lt1`, `dk2` and `lt2`, and this element is the map between them. The
identity mapping is the common case, which is why an implementation without
it looks right — until a document swaps them, as every template with a dark
background does, and then every heading is painted in the colour of the
paper it stands on.
themeFontLanguages
{ latin: string | undefined; eastAsia: string | undefined; complex: string | undefined; } | undefined
The languages the theme's font slots are chosen for, `w:themeFontLang`.
Word resolves `minorHAnsi` differently depending on which of the three
scripts the run is in, and this is what says which language each script is.
bordersSurroundHeader
boolean
Page borders are drawn around the header and the footer as well as the body.
`w:bordersDoNotSurroundHeader` and `w:bordersDoNotSurroundFooter`, inverted
so that the field reads as what happens rather than as what does not. Word's
default is that the border does surround them.
displayBackgroundShape
boolean
`w:displayBackgroundShape`: whether `w:background` is painted at all.
simple
ReadonlyMap<string, string>
Every child of `w:settings` whose whole content is a `w:val`, by name.
Two thirds of `CT_Settings` is switches of exactly this shape — an element
that is on by being present — and a field apiece would be sixty fields no
reader of this library would ever name. The map keys are element names and
the values are what `w:val` said, or the empty string for an element
written bare. `w:compat` has been read this way since it was first read at
all; this is the same decision one level up.
A setting that gets a field of its own is not in here: the parser routes
each name once. Extension elements are keyed by their prefix, `w15:docId`.
zoom
{ readonly value?: string; readonly percent?: string; } | undefined
`w:zoom`: what the document opens at.
proofState
{ readonly spelling?: string; readonly grammar?: string; } | undefined
`w:proofState`: how far the spell- and grammar-checker had got.
attachedTemplate
string | undefined
`w:attachedTemplate/@r:id`: the relationship naming the template.
documentProtectionSettings
ReadonlyMap<string, string> | undefined
`w:documentProtection`, whole.
The `edit` attribute alone is in {@link documentProtection}, which is what
the viewer asks. This is the element as written, hash and salt included —
a save that dropped the hash would unprotect the document.
writeProtection
ReadonlyMap<string, string> | undefined
`w:writeProtection`: the password to modify, and whether to recommend it.
stylePaneFilter
ReadonlyMap<string, string> | undefined
`w:stylePaneFormatFilter`: which styles the pane offers.
activeWritingStyles
readonly ReadonlyMap<string, string>[]
`w:activeWritingStyle`: a grammar checker registered per language.
documentVariables
ReadonlyMap<string, string>
`w:docVars`: named variables a field or a macro reads.
mathProperties
ReadonlyMap<string, string> | undefined
`m:mathPr`: how equations are set.
The same shape as {@link simple} and for the same reason: sixteen children,
every one of them a `m:val`, none of which this engine lays out.
attachedSchemas
readonly string[]
`w:attachedSchema`: the custom XML schemas the document declares.
footnoteSeparators
readonly number[]
`w:footnotePr/w:footnote/@w:id`: the notes that are separators, not notes.
shapeDefaults
ShapeDefaults | undefined
`w:shapeDefaults` and `w:hdrShapeDefaults`: the VML drawing defaults.
headerShapeDefaults
ShapeDefaults | undefined
smartTagTypes
readonly ReadonlyMap<string, string>[]
`w:smartTagType`: the smart-tag vocabularies the document declares.
mailMerge
MailMerge | undefined
`w:mailMerge`: the data source a merge document is bound to.
compatibilityFlags
ReadonlyMap<string, string>
`w:compat`'s own children, as against the `w:compatSetting`s beside them.
compatibilitySettings
readonly CompatibilitySetting[]
`w:compatSetting`: a named setting with the namespace that defines it.
DocxDocument
interface DocxDocument extends GenomDocument
A parsed Word document.
Everything needed to render, search or convert the document, and nothing that
depends on how it will be displayed. Headers, footers, footnotes and comments
are loaded lazily: a document may carry dozens of header parts of which a
given rendering uses two.
format
"docx" | "doc"
`docx`, or `doc` for the binary generation.
Word 97-2003 is read into this same model rather than converted into an
OOXML package first, so a document of either generation is one of these and
the viewer, the extractor and the addressing scheme need no branch for it.
The format is still reported truthfully, because a caller choosing a
renderer, naming a download or reporting a failure does need to know.
body
readonly BlockNode[]
Body content in document order.
finalSection
SectionProperties
Section properties of the final section, `w:body/w:sectPr`.
background
DocumentBackground | undefined
`w:background`: the colour behind every page.
A sibling of `w:body` rather than a property of a section, because it is the
document's and not the page's. Whether it is painted at all is
`w:displayBackgroundShape` in the settings — Word writes the colour into
every document saved from a template with one and paints it only for the
screen unless told otherwise.
fonts
readonly FontDefinition[]
styleResolver
StyleResolver
The style resolver bound to this document.
On the interface and not only on the class that implements it: composing a
document for the layout engine needs it, and that composition is part of
the package rather than of one implementation. Declaring it here is what
says the document *has* a resolver, rather than that one particular reader
happens to expose one.
loadHeader
(relationshipId: string) => Promise<HeaderFooter | undefined>
Loads a header part by relationship id.
loadFooter
(relationshipId: string) => Promise<HeaderFooter | undefined>
loadFootnotes
() => Promise<ReadonlyMap<string, Note>>
Footnotes keyed by id; parsed on first access.
loadEndnotes
() => Promise<ReadonlyMap<string, Note>>
loadComments
() => Promise<ReadonlyMap<string, Comment>>
detach
() => Promise<void>
Stops the document depending on the file it was opened from.
Everything this library models is already a value — no node refers to any
byte — but the parts it does not model are read from the source on demand:
the theme, the pictures, the macro project, whatever an application put in
`customXml`. Calling this reads the source once and holds it, after which
the document can be saved, drawn and searched with the original gone.
Not done at open time on purpose: most callers open a document, draw it and
drop it, and reading the whole file for that is a cost with nothing on the
other side of it.
detached
boolean
Whether {@link detach} has been called and the source is no longer needed.
loadDiagram
(dataRelationshipId: string, ownerPart?: string) => Promise<DiagramDrawing | undefined>
Loads the shapes Word laid out for a SmartArt diagram, by its data part id.
diagram
(dataRelationshipId: string, ownerPart?: string) => DiagramDrawing | undefined
Returns a diagram already loaded, for use during synchronous rendering.
preloadDiagrams
(blocks: readonly BlockNode[], ownerPart?: string) => Promise<void>
Loads every diagram reachable from a set of blocks.
loadChart
(relationshipId: string, ownerPart?: string) => Promise<ChartDefinition | undefined>
Loads a chart part by the relationship id the drawing names it with.
chart
(relationshipId: string, ownerPart?: string) => ChartDefinition | undefined
Returns a chart already loaded, for use during synchronous rendering.
preloadCharts
(blocks: readonly BlockNode[], ownerPart?: string) => Promise<void>
Loads every chart reachable from a set of blocks.
resolveImage
(relationshipId: string, ownerPart?: string) => Promise<string | undefined>
Resolves a relationship id into an image URL.
resolveImageSvg?
((relationshipId: string, ownerPart?: string) => Promise<string | undefined>) | undefined
The same picture as an SVG the document can hold, where that is better.
Answers `undefined` for a picture that is a picture; see the implementation.
resolveHyperlink
(relationshipId: string, ownerPart?: string) => string | undefined
Resolves a hyperlink relationship id into a URL.
outline
() => readonly OutlineEntry[]
Headings of the document, for a navigation pane.
bookmarks
() => ReadonlyMap<string, number>
Bookmark name → index of the block that contains it.
DrawingNode
interface DrawingNode
An image or shape produced by DrawingML, `w:drawing`.
relationshipId
string | undefined
Relationship id of the embedded image, `r:embed`.
linkRelationshipId
string | undefined
Relationship id of a linked (external) image, `r:link`.
description
string | undefined
Alt text for accessibility.
placement
"inline" | "anchor"
`inline` flows with text; `anchor` is positioned and wrapped.
wrap
DrawingWrap | undefined
Text wrapping mode of an anchored drawing.
rotation
number | undefined
Clockwise rotation in degrees.
crop
{ left: number; top: number; right: number; bottom: number; } | undefined
Source rectangle crop, as fractions of the image size.
pictureEffects?
PictureEffects | undefined
What `a:blip` does to the picture's pixels before it is drawn; see {@link PictureEffects}.
textBox
readonly BlockNode[] | undefined
Content of a drawing that holds a text box rather than a picture.
diagramDataId
string | undefined
Relationship id of a SmartArt data part, `dgm:relIds/@r:dm`.
The shapes are not in this part; it is only the handle from which they can
be reached, so it is kept rather than the drawing itself, which lives in a
separate part and is loaded on demand.
diagramLayoutId?
string | undefined
The other three parts `dgm:relIds` names: layout, style and colours.
Nothing reads them — the shapes a viewer draws come through the data part —
and they are kept because `dgm:relIds` requires all four attributes. A
SmartArt frame written with the data part alone is a frame Word reports as
corrupt, so a reader that keeps one id can preserve a diagram it opens and
not one it writes.
chartId
string | undefined
Relationship id of the chart part, `c:chart/@r:id`.
A chart is stored only as its definition, so this is the whole of what the
drawing says about it: the frame's size, and the part that fills it.
effectExtent
{ left: number; top: number; right: number; bottom: number; } | undefined
Space reserved around the drawing for its effects, `wp:effectExtent`, in EMU.
Word adds it to the extent when it reserves room in the flow, so a drawing
with an effect extent starts that far inside the box the paragraph gave it.
shape
ShapeStyle | undefined
Fill, outline and geometry of a shape, `wps:spPr` composed with `wps:style`.
isGroupFrame?
boolean | undefined
The box a group keeps for itself, holding no content of its own.
A group's members are placed absolutely inside it, so none of them reserves
the space the drawing occupies or gives the text something to flow around.
This node does both, and draws nothing: it is the group's frame, not a
picture that failed to load.
groupMembers?
number | undefined
How many of the nodes after this frame are its members.
A group is read into a flat list — the frame and then its shapes — and the
view needs to know where the group ends to put the shapes back inside it.
An inline group has to: its members are placed against the group's own box,
and the group only knows where that is once the text has flowed.
sizeRelH?
RelativeSize | undefined
`wp14:sizeRelH` and `wp14:sizeRelV`: the extent stated as a share of
something else.
A banner across the top of a page is written this way — "one hundred per
cent of the margin" — and the `wp:extent` beside it is the size Word
happened to draw last, not the size it means. Dropping them turns a shape
that follows the page into one that is frozen at whatever width the page
was when it was saved.
Named after the elements and not expanded, because the obvious expansion is
taken: `wp:anchor/@relativeHeight` is the drawing's **z-order**, on the
same element, and a field called `relativeHeight` meaning a share of the
page is a trap laid for the next reader.
anchorId?
string | undefined
`wp14:anchorId` and `wp14:editId`: Word's own identity for the anchor.
Eight hex digits apiece, the same kind of thing as a paragraph's
`w14:paraId` — written into the file, meaningful across it, and not to be
confused with anything this library mints.
layoutInCell?
boolean | undefined
`wp:anchor/@layoutInCell`: the object is placed inside the cell it is in.
Off, it floats against the page and the table is laid out as if it were not
there. Word writes it on every anchor; it is read and held rather than
drawn, because this engine places an anchored object against the column it
is in either way.
pictureName?
string | undefined
`pic:cNvPr/@name`: what the author called the picture inside the frame.
Not {@link name}, which is `wp:docPr/@name` — the *drawing's* name, one
level up. The format gives the same attribute name to two elements of one
anchor, and a single field called `name` would mean whichever the last
reader thought. See `tools/spec/names.mjs`.
DrawingWrap
interface DrawingWrap
type
"none" | "square" | "tight" | "through" | "topAndBottom"
outline?
readonly WrapPoint[] | undefined
`wp:wrapPolygon`: the outline the text follows instead of the box.
Present only under `wp:wrapTight` and `wp:wrapThrough`, and written in a
square of 21 600 units mapped onto the drawing's extent — the same space
VML's `wrapcoords` uses, which [MS-OI29500] §2.1.1790 ww states outright
for that attribute. Values reach outside the square: a picture whose
polygon runs from −284 to 21 600 wraps a little wider than it is drawn.
Word writes one for every tight-wrapped drawing, and for most of them it
traces the bounding box and says nothing new. It is the other kind that
matters: of the corpus's 49 documents with a polygon, 16 carry one that is
not a rectangle, and there the text goes into the shape's concavities.
side
"both" | "left" | "right" | "largest" | undefined
distanceTopEmu
number
Distance Word keeps between the drawing and the text beside it, in EMU.
`distL` and `distR` default to a tenth of an inch for a wrapped drawing —
twelve pixels, not the nine a reader would get by rounding an eighth. Three
pixels sounds like nothing until every line beside every figure starts three
pixels off.
EditOptions
interface EditOptions
A document open for editing.
The public shape is the ordinary one — `edit.paragraph(id).setText(...)`,
`edit.undo()`, `edit.save()` — and underneath every one of those is a step
over an immutable tree. The two halves are worth stating separately because
choosing them together is the whole design:
**Why the surface is mutable-looking.** Anyone who has written this kind of
code before has written it against Aspose, DocIO or python-docx, and in all
three a document is an object you change. A functional surface —
`document = edit(document, step)` — is honest and nobody wants it for
generating a report: it turns four lines into fourteen and every one of them
threads a variable.
**Why the inside is not.** Undo, redo, knowing what changed, replaying an
edit that arrived from somewhere else, and re-laying out only the paragraphs
that moved are five different things and they are all the same thing: they
need the version before and the version after to both exist. A tree that was
mutated has only one.
The reference a caller holds is a {@link ParagraphRef}, which is an id and
not a node. It never goes stale: the node it names is looked up in the
current version each time, so an edit somewhere else does not invalidate it —
and neither does an edit to the paragraph itself.
ids?
NodeIds | undefined
Where new identities come from; defaults to the document's own minter.
EmbeddedFontReference
interface EmbeddedFontReference
type
"regular" | "bold" | "italic" | "boldItalic"
fontKey
string | undefined
Obfuscation key; embedded fonts are XOR-scrambled with it.
FaceMetrics
interface FaceMetrics
A face, as far as placing text on a page is concerned.
family?
string | undefined
The family these numbers were actually taken from.
Not always the one the run names. A document may name a face nobody has —
`tdf157096` is set in `Noto Sans`, which this machine has never had — and
the widths then come from whatever the substitution chain settled on. A
painter that names the *document's* family in its CSS hands the browser a
font it cannot find, and the browser answers with its own default: the
page is drawn in a serif at widths measured on a sans, and every run of
every line stands at an abscissa its own glyphs do not reach.
So the face says what it is, and the painter names *that*. It is the same
rule the whole of this engine rests on — one measurement, one source —
applied to the last step, where the browser finally draws something.
Absent where the caller cannot say.
ascent
number
Ascent above the baseline, as a fraction of the em.
descent
number
Descent below it, positive, as a fraction of the em.
lineGap
number
The gap the font asks for above the ascent, as a fraction of the em.
widthOf
(text: string, sizePx: number) => number
The width of a string at a given size, in the same units as the size.
covers?
((code: number) => boolean) | undefined
Whether the face has a glyph for a code point at all.
Not the same question as its width. A face asked for a character it does
not have answers with `.notdef` — half an em in Calibri — and a line
measured on that is a line of a face nobody will draw in: Word substitutes
another face for the character, and the substitute is a different width.
`zh-98a5dfd0b39a` came to hold twice the text a line of it should for
exactly this reason.
Absent where the caller cannot say, in which case whoever asks must treat
every character as covered.
kernedWidthOf?
((text: string, sizePx: number) => number) | undefined
The same with the face's own kerning pairs applied.
Word kerns above the size `w:kern` names, and 369 of the corpus's 1672
documents name one. Absent where the caller cannot kern, in which case
{@link TextPiece.kerns} is ignored.
math?
MathConstants | undefined
What the face states about setting mathematics, divided by its em.
Absent for the great majority of faces, which carry no `MATH` table;
`layout/math-box.ts` falls back to the classical numbers for those.
Faces
interface Faces
of
(family: string | undefined, bold: boolean, italic: boolean) => FaceMetrics | undefined
The face a run is set in, or nothing where the machine has none.
genericOf?
((family: string | undefined) => string | undefined) | undefined
The CSS generic the document's `w:fontTable` puts this family on, if any.
Nothing to do with measuring — the engine never reaches a generic, because
a generic has no advances. It is for the painter, whose browser may not
have the face the engine measured with; see `cssGenericFor`.
Fib
interface Fib
nFib
number
Format version. 193 and up is Word 97; below 105 is Word 6/95.
isOldFormat
boolean
Word 6.0 or Word 95 rather than Word 97.
Three things change with it: two extra reserved longs before the story
sizes, bin table entries of two bytes rather than four, and — the one that
cannot be worked around — property modifiers with single-byte opcodes. The
first two are read; the third is why formatting is not.
lid
number
Language the document was written in, used when nothing names a code page.
isTemplate
boolean
The document is a template (`.dot`).
isComplex
boolean
The text is stored in pieces rather than as one run.
isEncrypted
boolean
The document is encrypted or obfuscated; nothing below it can be read.
tableStreamName
"0Table" | "1Table"
Which table stream this save wrote: `1Table` when set, `0Table` when not.
fcMin
number
Where the text starts in `WordDocument`, for a document with no piece table.
ccpText
number
Character counts, story by story. They partition the text end to end.
pairs
readonly FcLcb[]
The (offset, length) pairs, indexed by {@link Fc}.
FieldNode
interface FieldNode
A field: `PAGE`, `NUMPAGES`, `TOC`, `REF`, `HYPERLINK`, and so on.
Both the simple form (`w:fldSimple`) and the complex form (a `w:fldChar`
begin/separate/end sequence) are normalised into this single node, because the
distinction is a serialisation detail that no consumer should have to know
about. `result` holds the text Word last computed, which is what gets
displayed for any field the viewer does not evaluate itself.
instruction
string
The full instruction text, e.g. `PAGE \\* MERGEFORMAT`.
fieldType
string
Field type in upper case, e.g. `PAGE`; the first token of the instruction.
result
readonly InlineNode[]
Cached result as computed by Word.
instructionFields?
readonly FieldNode[] | undefined
Fields written inside this field's *instruction*, in the order they appear.
`IF <STYLEREF CharPartNo> = 0 "..." "..."` is an argument, not something to
draw, so the instruction keeps its place with a marker — `U+0001`, the
index, `U+0001` — and the field itself is kept here. A consumer that
evaluates the outer field can then put the right one back; one that does
not can ignore both, which is what the instruction text alone would have
given it anyway.
form?
{ readonly checkBox?: { readonly checked: boolean; readonly sizeHalfPoints?: number; }; } | undefined
`w:fldChar/w:ffData`: what a legacy form field is, where the field is one.
Only the check box is read. Its result is empty in the file — Word draws the
box itself — so without this a form of `FORMCHECKBOX` fields is a form of
gaps.
FloatBox
interface FloatBox
A rectangle on the page, in points from the top-left of the text area.
outline?
readonly { readonly x: number; readonly y: number; }[] | undefined
`wp:wrapPolygon`: the outline the text follows instead of the rectangle.
Points in a square of 21 600 units mapped onto the box, which is how Word
writes them and how VML's `wrapcoords` is read ([MS-OI29500] §2.1.1790 ww).
They are not clamped to that square — a polygon may reach outside the
drawing it belongs to, and Word lets it.
A box with an outline still obstructs the same *rows*; what changes is how
far into each row it reaches. A triangle takes the whole width of the line
that crosses its base and almost none of the line that crosses its apex,
and text set against its rectangle loses that room on every line but one.
outlineLeft?
number | undefined
The drawing's own left edge and width, which the outline is measured
against.
`left` and `right` above are the *gapped* box — the drawing plus the room
`distL` and `distR` keep beside it — and the polygon is written against the
drawing alone. Keeping the drawing's own span here is what lets the gap be
put back on afterwards: the outline is narrowed to the line and then
widened by whatever the two edges differ by, which is the gap.
FontDefinition
interface FontDefinition
A font declared in `fontTable.xml`.
pitch
string | undefined
Pitch: `fixed`, `variable` or `default`.
altName
string | undefined
Fonts Word will substitute if this one is unavailable.
charset
string | undefined
`w:charset` value, needed to interpret symbol fonts.
characterSet
string | undefined
`w:charset/@w:characterSet`: the IANA name of the same character set.
Written beside the numeric one by Word itself — `w:val="80"` and
`w:characterSet="SHIFTJIS"` are the same statement twice — and kept
because a producer that writes only the name has said the same thing.
panose
string | undefined
`w:panose1`: the face's own description, as twenty hex digits.
Ten numbers — family kind, serif style, weight, proportion, contrast,
stroke variation, arm style, letterform, midline, x-height — and what Word
matches against the machine's fonts when the named one is missing.
notTrueType
boolean
`w:notTrueType`: the face is not a TrueType font.
Which is to say it is a bitmap or a printer font, and Word will not embed
it, scale it smoothly or hint it. Almost nothing is any more; the flag is
still written by converters of documents that once were.
signature
ReadonlyMap<string, string> | undefined
`w:sig`: which Unicode ranges and code pages the face covers.
Four `usb` words for the Unicode subsets and two `csb` for the code pages,
as hex. It is what Word matches when it has to find a face that can draw a
character the named one cannot, so a document that loses it loses the
fallback the author's machine would have chosen.
embedded
readonly EmbeddedFontReference[]
Embedded font references keyed by style.
FontReference
interface FontReference
The four script slots Word resolves a font family from.
ascii
string | undefined
Font for Latin text.
hAnsi
string | undefined
Font for high-ANSI text; usually equal to `ascii`.
eastAsia
string | undefined
Font for East Asian text.
cs
string | undefined
Font for complex-script text, which includes Arabic and Hebrew.
asciiTheme
string | undefined
Theme slots: `minorHAnsi`, `majorHAnsi`, and so on.
hint
"default" | "eastAsia" | "cs" | undefined
How the font of a symbol run is chosen, `w:hint`.
FontSlotContext
interface FontSlotContext
What the run says about itself, as far as the slot is concerned.
hint?
string | undefined
`w:rFonts/@w:hint`, which decides the ambiguous blocks.
complexScript?
boolean | undefined
`w:cs` — the run is set in a complex script.
rtl?
boolean | undefined
`w:rtl` — the run runs right to left.
eastAsianLanguage?
string | undefined
`w:lang/@w:eastAsia`, for the clauses that name Chinese.
eastAsia?
string | undefined
`w:rFonts/@w:eastAsia`, for the Times New Roman rule.
ascii?
string | undefined
`w:rFonts/@w:ascii`.
hAnsi?
string | undefined
`w:rFonts/@w:hAnsi`.
FrameProperties
interface FrameProperties
heightTwips
number | undefined
`w:h`, where the frame states one; a height of nought is no height stated.
heightRule?
"auto" | "exact" | "atLeast" | undefined
`w:hRule`, defaulted the way Word defaults it.
[MS-OI29500] §2.1.45(f): `atLeast` where a height is stated and `auto`
where none is, rather than the `auto` the standard names in both cases.
lines?
number | undefined
`w:lines`: how many lines of the paragraph the frame is drawn around.
horizontalAnchor
string | undefined
`w:hAnchor` and `w:vAnchor`, both defaulting to `text`.
The standard says `page` for either; [MS-OI29500] §2.1.43 e and g say Word
uses `text`, which is the difference between a frame beside its paragraph
and one in the corner of the sheet. (§2.1.45 is `w:jc`; the citation here
named it for a while and was wrong.)
dropCap
string | undefined
`w:dropCap`: `drop` or `margin` for an initial the text runs beside.
horizontalSpaceTwips
number | undefined
`w:hSpace`: what the frame keeps between itself and the text beside it.
HorizontalOrigins
interface HorizontalOrigins
The places a horizontal anchor may be counted from, in points across the sheet.
page
number
The left edge of the sheet, nought by definition and named for clarity.
margin
Span
The text area: its left edge and its right.
column
Span
The column the anchor is in, which is the text area outside a multi-column section.
sheetRight?
number | undefined
The right edge of the sheet, where the caller knows it.
`relativeFrom="page"` with `wp:align="right"` puts a drawing against the
paper's edge, not the text's: `es-1fb59f6f5ac1` hangs a full-page letterhead
that way and it stood the right margin's width to the left, the crest cut
off and a white strip down the sheet. Absent, the page's right is taken as
the text area's, which is what every caller said before.
HyperlinkNode
interface HyperlinkNode
relationshipId
string | undefined
Relationship id pointing at an external URL.
anchor
string | undefined
Bookmark name for an internal link.
InlinePicture
interface InlinePicture
A picture placed in the text, as the character that stands for it says.
Word puts one character in the flow — `0x01` — and everything about the
picture is somewhere else: a modifier on the character gives an offset into
the `Data` stream, and at that offset is a header saying how big the picture
is, followed by a drawing container holding the picture itself.
The size is two numbers, not one. `dxaGoal` is the picture's natural size and
`mx` is what the author scaled it to, so a photograph dragged to half its
size has an unchanged `dxaGoal` and an `mx` of five hundred. Using the first
alone puts every resized picture on the page at the size it was before
somebody resized it.
location
number
Where in the `Data` stream this was found, which is how a run names it.
widthTwips
number
Displayed width in twips: the natural size with the author's scaling applied.
crop
{ left: number; top: number; right: number; bottom: number; } | undefined
Cropping as fractions of the picture, from the shape's own properties.
picture
StoredPicture | undefined
The picture, when it is stored inside the container.
storeIndex
number | undefined
Or its place in the document's store, when the shape names one instead.
look
ShapeLook | undefined
Or how it is drawn, when it is not a picture at all.
The character that stands for a picture also stands for a *shape* placed in
the line — a grey rectangle, a rule, a coloured panel — with the same header
in front of it and a shape container instead of a picture behind. A reader
that insists on finding bytes there finds none and drops the shape.
KinsokuOverrides
interface KinsokuOverrides
What a document says in `settings.xml` about the sets.
strict?
boolean | undefined
`w:strictFirstAndLastChars`, which changes the Japanese pair.
noStart?
ReadonlyMap<string, string> | undefined
`w:noLineBreaksBefore`, by the `w:lang` it names.
noEnd?
ReadonlyMap<string, string> | undefined
`w:noLineBreaksAfter`, likewise.
KinsokuSet
interface KinsokuSet
One language's two lists.
noStart
ReadonlySet<string>
Characters that may not begin a line — Word's "first characters".
noEnd
ReadonlySet<string>
Characters that may not end one — Word's "last characters".
LayoutOptions
interface LayoutOptions
What a caller may ask of a layout beyond its blocks.
All three are about *not holding the whole document at once*. A page of dense
text is some hundreds of lines and some thousands of spans; a thousand pages
of it is millions of objects, and a viewer that draws three of them at a time
has no use for the other nine hundred and ninety-seven.
The engine still lays the document out from the beginning — where a page ends
depends on every page before it, and no arrangement of options changes that —
but what it *keeps* is the caller's to decide.
onPage?
((page: PageGeometry, index: number) => void) | undefined
Called as each page closes, with the page and its index.
The page is handed over before anything else sees it and is not held
afterwards unless {@link retain} says so, which is what makes a streamed
layout constant in memory.
retain?
{ readonly from: number; readonly count: number; } | undefined
Which pages to keep in the result.
Absent keeps all of them, which is what every existing caller expects. A
range keeps that window; an empty window keeps none, and the result is then
the page count and the sizes — enough for a scrollbar and a page number,
and nothing that has to be drawn.
stopAfter?
number | undefined
Stop once this many pages have been produced.
For a caller that wants page forty and does not care what follows it.
A **lower bound, not an exact count**: the check is made between blocks, so
a table or a long paragraph that was being placed is finished rather than
abandoned, and the result may hold a page or two more.
What is guaranteed is the part that matters: every page but the last is
exactly the page the full layout produces, in order, from the first. A page
is what it is because of everything above it, and stopping early cannot
change what is above. The last page is the one the stop cut short — it
holds what had been placed on it and is closed as `end` — so a caller that
needs page forty asks for forty-one and takes the fortieth.
{@link LayoutResult.pageCount} is then the count of what was laid out
rather than of the document.
LayoutResult
interface LayoutResult
pages
readonly PageGeometry[]
The pages, as far as the caller asked to keep them.
Every page by default. A caller that gave {@link LayoutOptions.retain} gets
the window it asked for and no more; see {@link sizes}, which is what a
scrollbar needs and costs two numbers a page rather than a page's worth of
geometry.
sizes
readonly { readonly width: number; readonly height: number; }[]
Every page's extent, whether or not its geometry was kept.
The whole of what a virtualiser needs to be correct before a single page
has been drawn — and for a document of a thousand pages it is sixteen
kilobytes against the hundreds of megabytes the geometry would be.
from
number
Where the retained window begins, as a page index.
pageCount
number
How many pages the document came to, retained or not.
unsupported
Readonly<Record<string, number>>
How many blocks were refused, by reason.
Line
interface Line
One chosen line, as a half-open range of pieces.
offset
number
Where the line begins, from {@link LineRoom.offset}.
width
number
Advance of everything on it, the spaces that end it included.
ink
number
The same without those spaces: where the ink actually ends.
spaces
number
Spaces on it, and how many of them lie between two pieces of ink.
hyphen?
boolean | undefined
The line ends in the middle of a word and draws a hyphen for it.
Its width is already in {@link Line.width} and {@link Line.ink} — the mark
is part of the line, and both the alignment and the justification are
measured with it — so a caller that only places lines need not read this.
A caller that *draws* one must: the letters are the word's and the mark is
not among them.
squeeze
number
What the spaces on it promised the fit test, in pixels.
A justified line keeps a word that overruns by less than its spaces can
give back between them, and the giving back is the caller's — see
`page.ts`. Stated so the caller can spend *this* and no more: a line wider
than its room for some other reason — a token cut at the column's edge, a
picture nothing can shrink — was never promised anything, and squeezing its
spaces to pull it back would move words Word leaves where they are.
LineBox
interface LineBox
One line: how tall it is and how far down it the baseline sits, in points.
ink?
number | undefined
How much of the box has to fit on the page, where that is less than all of
it.
A ruled page centres the type in its step, and the empty half-step under
the last line of a page may hang past the bottom margin: the
twenty-fourth line of `doc-grid-auto-multiple` has a box ending 4.9px below
the margin and Word draws it on that page all the same, while the type's
own line ends 3.5px above it. Absent everywhere else, where the box is the
measure.
LineEnd
interface LineEnd
An arrowhead at one end of a line, `a:headEnd` and `a:tailEnd`.
type
string
`triangle`, `stealth`, `diamond`, `oval`, `arrow`, or `none`.
width
string | undefined
`sm`, `med` or `lg`; absent means medium.
LineGeometry
interface LineGeometry
One line, placed.
rotation?
90 | -90 | undefined
A line of a cell whose text runs down (`90`) or up (`-90`) the page.
`x` and `y` are then the corner the line box turns about: the painter
places the box there and rotates it, so the spans inside keep their
positions along the line. See where a vertical cell is set.
upright?
boolean | undefined
...set in East Asian vertical, `tbRlV`: ideographs stand upright down the
column and only Latin lies on its side, where plain `tbRl` turns the whole
line. The painter draws such a line in a vertical writing mode rather than
turning it.
wordSpacing?
number | undefined
`w:jc="both"`: the extra room every space on this line was given.
The line breaker stretches a justified line by widening the *pieces* it
holds — see `chooseLines` — which puts every word where Word puts it and
leaves each word's own glyphs drawn at their natural width inside a wider
slot. That is enough while a piece is one word, and it is not enough where
a piece holds spaces of its own: the browser draws those at their natural
width and the words after them stand short.
So the stretch is stated as well, in the unit CSS has for it. Absent on a
line the breaker had nothing left to give, which is most of them.
Negative on a line that overruns: justification takes back from the spaces
what the breaker promised when it kept the word that overran.
letterSpacing?
number | undefined
`w:jc="distribute"`: the room a line leaves, dealt out between its
characters rather than its spaces, and on the last line as on the rest.
merged?
boolean | undefined
A line of a cell that is merged down past this row.
Such a line is placed against the **run** — `w:vAlign` centres a merged
cell's words between the top of the first row and the bottom of the last —
so it may sit far below the row that carries it, and it is not the row's
own depth. Where a page ends inside the run, the divider would otherwise
cut the row at that line: `educational-5fdff3d8253d` labels four rows
`Knowledge and Skills` down the side, and the second word of the label,
348px into a row 150px tall, sent everything after it to the next page.
alignment?
"center" | "right" | undefined
`w:jc`, where it put the line anywhere but at its start.
What moves the line again when a field's answer changes its width after
the line was placed. A page number is laid out as a one-character mark —
measured as that face's `.notdef`, twelve and a half pixels of Times New
Roman at twelve point against eight for a digit — and a footer centred on
the mark drew `1` two pixels right of the middle and `10` three pixels
left of it: 282 of the 344 page-number lines in the ink sample were off,
and drawn from where the mark stood. See `realignedTabs`.
x
number
Left edge, in pixels from the page's left edge.
y
number
Top of the line box, in pixels from the page's top edge.
baseline
number
The baseline, in the same frame — what Word's own PDF states.
size
number
The type the line is set in, in pixels: the size of its tallest run.
Not the height, which is the whole line box and a sixth larger. Anything
that draws the line needs it, and nothing else on the geometry states it.
spaceAfter?
number | undefined
The space the paragraph asks after itself, on the paragraph's last line
inside a box; see where a row is divided.
paragraph?
{ readonly id: number; readonly widows: boolean; } | undefined
Which paragraph of a box the line belongs to, and whether that paragraph
keeps widows and orphans; see where a row is divided.
aligned?
{ readonly align: "center" | "bottom"; readonly by: number; } | undefined
The cell this line was moved down inside by `w:vAlign`, and which way.
Only where the cell is shorter than its row and so had room to be moved —
a cell that fills its row is aligned by having nowhere to go. Carried
because a row divided between two pages is aligned again on each of them:
see where a row is divided, and `row-split-valign-center`.
**One object per cell, shared by its lines, and that is how they are
grouped again**: a column number is not an identity — a table nested in a
cell numbers its own columns, and a vertically merged run folded into one
row holds the cells of every row it covers. Grouped by column,
`reports-8a5682f91fe7` gathered thirteen hundred lines of a 13767px row
into one "cell" and drew them on a single sheet: ten pages against Word's
twenty-five.
spans?
readonly SpanGeometry[] | undefined
What is actually on the line, run by run, for whoever draws it.
{@link LineGeometry.text} is the line as one string, which is what a
comparison against Word's own PDF pairs on. It is not enough to draw with:
a line is a sequence of runs, each with its own face, colour and
decorations, and each landing at an abscissa this engine worked out. The
two are the same characters said twice, once for measuring and once for
painting.
row?
number | undefined
Which row of its table the line stands in, where it stands in one.
The painter needs it — a cell's rules and shading are the row's — and so
does any measurement that wants to say *which* row came out wrong rather
than which document did. A line of the flow has none.
LineNumbering
interface LineNumbering
Line numbering in the margin, `w:lnNumType`.
restart
"continuous" | "newPage" | "newSection" | undefined
LineRoom
interface LineRoom
The room one line of a paragraph has: where it begins and how wide it is.
Two numbers and not one, because the things that narrow a line do not all
narrow it from the right. A first-line indent moves its left edge in; a
hanging indent moves it out; a floating object on the left of the column
moves every line beside it in and the ones above and below it back. The
offset is measured from where a *continuation* line of the paragraph begins,
so an ordinary paragraph's later lines have an offset of nought.
LineRun
interface LineRun
A run, as far as the line it sits on is concerned.
sizeHalfPoints
number
Resolved size in half-points, as `w:sz` states it.
ascent
number
Ascent above the baseline, as a fraction of the em.
descent
number
Descent below it, positive, as a fraction of the em.
lineGap
number
The gap the font asks for above the ascent, as a fraction of the em.
emRatio?
number | undefined
The line this face rules at, in ems, where Word's own answer is known.
Overrides everything else: the metrics, which are the browser's reading of
the font, and the East Asian constant, which is what most such faces
measure and not what all of them do. `wordLineRatio` in `view/css/theme.ts`
is where the answers are kept — most East Asian faces are the 1.3 below,
and DFKai-SB is 1.71, a fifth taller than its neighbours.
The model holds no table of its own on purpose. A constant it believes is a
constant nobody can correct from outside; a number it is handed is one the
caller can measure.
object?
boolean | undefined
Whether the run is an inline object rather than type.
A picture rules the line by standing on the baseline, and a line multiple
does not multiply it: see the `auto` branch of {@link lineBoxOf}.
label?
boolean | undefined
Whether the run is a list's label rather than its text.
A label is set at the head of the first line and is not content of its
own; whether it rules that line the way text does is measured under
{@link lineBoxOf}.
blank?
boolean | undefined
Whether the run holds nothing but whitespace; see {@link tallestOf}.
raisePoints?
number | undefined
`w:position`: how far the run is moved off the baseline, in points, up
being positive.
The line grows by the movement — upward into its ascent, downward into its
descent. `run-position-up-6`, `-up-20` and `-down-6` measure it: a run
raised three points makes its line 4.0px taller and its baseline 4.0px
lower, one raised ten points 13.5px, and one lowered three points 4.2px
taller with the baseline where it was.
mark?
boolean | undefined
Whether the run is the paragraph mark rather than anything typed.
The mark rules a line only where nothing else can — see {@link tallestOf}.
A run of seventy-two point spaces is *not* nothing else in the sense the
blank rule means, so the two flags are separate: `blank` says the run has
no ink, `mark` says there was nothing to have ink.
eastAsian?
boolean | undefined
Whether the run is East Asian text, whose line the face does not decide.
Word rules a line of ideographs at {@link EAST_ASIAN_LINE} times the size
and pays no attention to the font: `east-asian-natural-line-height` sets
twelve-point SimSun, NSimSun, FangSong, MingLiU, DFKai-SB and Microsoft
JhengHei one after another, and Word rules every one of them 20.80px apart
— 1.30 em exactly — where the faces' own lines run from 1.2 to 1.5.
LineSpacing
interface LineSpacing
`w:spacing`, as far as the line is concerned.
rule
"auto" | "exact" | "atLeast"
line?
number | undefined
`w:line`: 240ths of the natural line under `auto`, twips under the others.
Absent is single spacing, which is `auto` at 240 and is what a paragraph
that says nothing at all gets.
MarkupSighting
interface MarkupSighting
One piece of markup nothing read, as the observer receives it.
key
string
`w:zoom`, or `w:spacing@w:beforeAutospacing`.
parent
string
Local name of the element it was found in.
reason
string | undefined
Why it is passed over, when the register says so.
MathBox
interface MathBox
An equation's box, and what stands inside it.
ascent
number
Above the baseline.
MathContent
interface MathContent
writeRunProperties
(properties: RunProperties | undefined) => void
Writes a `w:rPr` for the face the equation is set in, where it has one.
MathElement
interface MathElement
A node of the OMML tree, kept close to the source markup.
name
string
Local name of the OMML element, e.g. `f` for a fraction.
text?
string | undefined
Text content for `m:t` nodes.
children?
readonly MathElement[] | undefined
attributes?
Readonly<Record<string, string>> | undefined
Selected attributes needed for rendering, e.g. delimiter characters.
properties?
RunProperties | undefined
MathFace
interface MathFace
What the face can say about itself, in em fractions and pixels.
widthOf
(text: string, sizePx: number) => number
The advance of a string at a type size, in the same units as the size.
ascent
number
How far the face reaches above the baseline, as a fraction of the em.
constants?
MathConstants | undefined
The face's own constants, **divided by its em**, or none.
Optional so that the unit cannot be got wrong at the seam. `MATH` is a
table nearly no face carries, so nearly every equation is set by the
classical set — and that set is published *per thousand*, which is the form
`classicalMathConstants` answers in by default. Handed over so, and
multiplied here by the type size, every length in an equation came out a
thousand times too big: one sum in the project's own demo document was
given a box 933 pixels tall and the page after it was blank paper.
A caller that has the face's table hands it over; one that has not hands
over nothing, and the fallback below is in the units this file works in
because this file chose them.
MathGlyphRun
interface MathGlyphRun
One run of text inside an equation, placed against the box's baseline.
dx
number
From the box's left edge.
dy
number
From the box's baseline, positive downwards.
rule?
{ readonly width: number; readonly height: number; } | undefined
A rule rather than text: a fraction's bar, a radical's overline.
Drawn as a filled rectangle of `width` by `height` with its top at `dy`;
`text` is empty for these.
scaleY?
number | undefined
Drawn this many times its height, about its own baseline: a delimiter
grown to the height of what it encloses. See `case 'd'`.
MathNode
interface MathNode
An Office Math (OMML) expression, converted to MathML by the renderer.
display
"inline" | "block"
`inline` for `m:oMath`, `block` for `m:oMathPara`.
properties?
RunProperties | undefined
The face and size the equation is set in, read from the first `w:rPr`
inside it — the only place a file states them. Weight and slope are not
kept: [MS-OI29500] §22.1.2.87 a says Word ignores them there.
Measurement
interface Measurement
A measurement together with the unit it was declared in, `ST_TblWidth`.
type
"auto" | "nil" | "dxa" | "pct"
`dxa` twips, `pct` fiftieths of a percent, `auto`, `nil`.
Note
interface Note
A footnote or endnote.
type
"normal" | "separator" | "continuationSeparator" | "continuationNotice"
`normal` is a real note; `separator` and `continuationSeparator` are chrome.
NoteInput
interface NoteInput
A footnote, as far as the page is concerned: the blocks it is made of.
Where it goes is not stated because it is not the note's to state — Word sets
every note referenced on a page at the foot of that page, in order, upwards
from the bottom margin.
at?
number | undefined
Where the note's mark stands, as an index into the paragraph's pieces.
A note belongs to the page its *mark* lands on, not to the page its
paragraph began on. `policies-b2c6929e77b7` is the document that says so:
a paragraph straddles pages one and two, its mark falls on the line that
goes over, and Word sets the note at the foot of page two — where this
engine, binding the note to the paragraph, set it at the foot of page one
and gave page two an extra line of body for the room it had not spent.
Three pages short over fifty-four, and 2361 lines on the wrong one.
Absent, the note is bound to the paragraph as before.
NoteReferenceNode
interface NoteReferenceNode
kind
NodeKind.FootnoteReference | NodeKind.EndnoteReference
customMark
boolean
A custom mark suppresses automatic numbering.
mark?
boolean | undefined
The number printed at the head of the note itself, `w:footnoteRef`.
The same number as the reference in the body, in the place and the type the
note's own text puts it — which is not always at the very start, and is
never in the type the body used. It carries no id: the note it belongs to
is the one it is written inside.
NumberingCounterState
interface NumberingCounterState
counters
ReadonlyMap<string, number>
NumberingDefinitions
interface NumberingDefinitions
The parsed contents of `numbering.xml`.
abstract
ReadonlyMap<number, AbstractNumbering>
instances
ReadonlyMap<number, NumberingInstance>
pictureBullets
ReadonlyMap<number, PictureBullet>
partName?
string | undefined
The part these definitions were read from, usually `word/numbering.xml`.
A picture bullet names its image by a relationship id, and a relationship
id means nothing without the part it was written in: `rId1` in
`numbering.xml` is a different picture from `rId1` in `document.xml`, and
usually no picture at all.
numIdMacAtCleanup
number | undefined
`w:numIdMacAtCleanup`: the highest `w:numId` Word for Mac had reached.
A counter one implementation keeps so that it does not reuse an id another
has handed out. Nothing reads it here; it is written back because a
document that loses it can be given a duplicate list id by the next editor.
NumberingInstance
interface NumberingInstance
A concrete list instance, `w:num`.
Paragraphs reference instances, not abstract definitions. The indirection
exists so two lists can share formatting while numbering independently, and it
is also where per-instance level overrides live.
durableId
string | undefined
`@w16cid:durableId`: an identity for the instance that survives renumbering.
`w:numId` is a position in this document's table and changes when lists are
added or merged; the durable id does not, which is how a comment or a
cross-reference made in one editing session still points at the same list in
the next.
overrides
ReadonlyMap<number, NumberingLevelOverride>
Level overrides applied on top of the abstract definition.
NumberingLevel
interface NumberingLevel
One level of a list definition, `w:lvl`.
level
number
Zero-based level index, 0..8.
text
string
Number text pattern, `w:lvlText`.
Placeholders `%1`..`%9` are substituted with the counter of the
corresponding level, which is what produces "1.2.3" style numbering.
templateCode
string | undefined
`@w:tplc`: which entry of Word's list gallery this level came from.
Opaque to everyone but Word, which uses it to recognise a list the user
picked from the gallery and to offer the same one again.
tentative
boolean
`@w:tentative`: a level Word invented rather than the author defining it.
A list defined to three levels is written with nine, and the six nobody
asked for are marked. Word discards a tentative level the moment the author
defines one, and keeps it invisible until then.
alignment
"left" | "center" | "right" | undefined
style
string | undefined
`w:pStyle`: the paragraph style this level is attached to.
paragraph
ParagraphProperties | undefined
Indentation and tab settings for the level.
run
RunProperties | undefined
Formatting of the number itself.
restart
number | undefined
Level after which the counter restarts, `w:lvlRestart`.
pictureBulletId
number | undefined
Picture bullet id, `w:lvlPicBulletId`.
isLegal
boolean
The level is a legal-numbering variant, `w:isLgl`.
legacy
LegacyNumbering | undefined
Word 6 numbering compatibility, `w:legacy`.
A list converted from a document old enough to predate `w:numPr` keeps its
original geometry: the number sits in a box of a stated width and the text
is indented by a stated amount, rather than the number hanging in the
paragraph's own indent. Ignoring it moves every line of such a list.
NumberingLevelOverride
interface NumberingLevelOverride
startOverride
number | undefined
Restart value, `w:startOverride`.
definition
NumberingLevel | undefined
A fully redefined level.
NumberingReference
interface NumberingReference
`w:numPr`: which list a paragraph belongs to, and how deep in it.
Both halves are optional and separately so, because a style states them
separately. `Heading2` says `<w:ilvl w:val="1"/>` and no `w:numId` at all —
"whatever list applies to me, I am its second level" — and reading that as
"no numbering" drops the level of every outline-numbered heading in the
document. The pair is absent only where the paragraph stated neither.
id
number | undefined
`w:numId`, referencing an instance in `numbering.xml`.
level
number | undefined
Zero-based list level, `w:ilvl`.
NumberLabel
interface NumberLabel
The computed label of one numbered paragraph.
text
string
The rendered text, e.g. `2.3.` or `•`.
suffix
"tab" | "space" | "nothing"
What separates the label from the paragraph text.
level
NumberingLevel
The level definition the label came from.
value
number
The raw counter value, useful for cross references.
pictureBullet?
{ readonly relationshipId: string; readonly widthEmu: number | undefined; readonly heightEmu: number | undefined; readonly part: string | undefined; } | undefined
`w:lvlPicBulletId`: the picture Word draws in place of the bullet.
The level still states a character — `` in Symbol — and Word draws the
picture over it, so a reader that takes the text draws a dot where the
document has a tick, a flower or a logo.
OfficeArtContentfrom @genomdev/office-core
interface OfficeArtContent
What Word puts in `fcDggInfo`.
The drawing group first — which holds the store every picture in the document
lives in — and then up to two drawings, one for the body and one for the
headers. Each is introduced by a single byte saying which it is, and that byte
is the thing to trust: documents exist whose two drawings are in the other
order, and reading them by position puts the header's shapes in the body.
OpaqueInput
interface OpaqueInput
A block this engine will not place, and will not pretend to.
reason
string
Why, so a comparison can say what it skipped.
OpenDocOptions
interface OpenDocOptions extends OpenOptions
Opening a Word 97-2003 document.
The result is a {@link DocxDocument} — the same object `.docx` produces, and
deliberately so. The alternative, and the one most projects take, is to
convert the binary file into an OOXML package and hand that to the existing
reader; it is easier to start and worse to finish, because everything the
document says then has to survive being written out in a second format and
read back. Producing the model directly means the viewer, the extractor and
the addressing scheme work on a 1997 document with no code that knows it is
one.
keepDeletedContent?
boolean | undefined
Keep content marked deleted by revision tracking. Off by default.
OpenDocxOptions
interface OpenDocxOptions extends OpenOptions
Options accepted when opening a Word document.
keepDeletedContent?
boolean | undefined
Keep content marked deleted by revision tracking. Off by default.
parseMath?
boolean | undefined
Parse OMML equations. On by default.
preserve?
boolean | undefined
Record where every node was read from, and keep what is not modelled.
On by default, and what makes saving lossless: see `model/source.ts`. Off,
the document is read exactly as it was before spans existed — nothing is
recorded, nothing unmodelled is kept, and saving can only write what the
model understands.
maxCacheBytes?
number | undefined
Maximum bytes of inflated package parts held in memory.
Defaults to 64 MB. Raising it speeds up documents whose images are viewed
repeatedly; lowering it bounds memory on very large files.
Outline
interface Outline
How the edge of a shape is drawn, `a:ln`.
widthEmu
number | undefined
Line width in EMU, `a:ln/@w`; absent means the theme's.
dash?
string | undefined
`a:prstDash/@val`, the pattern by name: `dash`, `sysDot`, `lgDashDot`…
cap?
"round" | "square" | "flat" | undefined
`a:ln/@cap`: how the line ends.
join?
"round" | "bevel" | "miter" | undefined
The corner treatment: `a:miter`, `a:round` or `a:bevel`.
miterLimit?
number | undefined
`a:miter/@lim`: how far a sharp corner may extend before it is cut off.
In thousandths of a percent of the line width. Nothing draws it — a border
has no corner sharp enough to matter and CSS has no way to state one — and
it is the whole of what the element says, so a miter written back without
it is a different miter.
OutlineEntry
interface OutlineEntry
An entry of the document outline, derived from heading paragraphs.
level
number
Heading level, 1..9.
node?
BlockNode | undefined
The heading paragraph itself, which is what a viewer navigates by.
Identity rather than position, and that is the whole point of it. There is
no one numbering of a document's blocks: this walk counts paragraphs inside
table cells and the layout does not, the layout inserts a block where a
section begins and this does not, and a paragraph broken across a page
becomes two blocks there and stays one here. On a fourteen-page report the
two numberings ran to 466 and 278, so more than half the headings named a
block the layout had never heard of and the rest named the wrong one — an
outline whose later entries did nothing at all and whose earlier ones went
quietly to the wrong page.
A node is the same object on both sides, and cannot drift.
blockIndex
number
Index of the paragraph in the flattened body.
A position in *this* walk — paragraphs inside table cells included — which
is not the layout's numbering and cannot be used to find a page. Kept
because it identifies the entry within an outline; navigate by
{@link node}.
bookmark
string | undefined
Bookmark name when the heading carries one.
PageBorders
interface PageBorders
The frame Word draws round a page, `w:pgBorders`.
Where it sits is as much a part of it as which edges are drawn: the same
border set measured from the paper and measured from the text is two
different frames, one round the sheet and one round the type area.
offsetFrom
"text" | "page"
`page` measures each edge's `w:space` from the paper, `text` from the type area.
display
"allPages" | "firstPage" | "notFirstPage"
Which pages get the frame, `w:display`.
The names mean what they say, and the standard's own gloss of the second
one does not: [MS-OI29500] §2.1.549 records that ISO/IEC 29500 defines
`notFirstPage` as "displayed on only the first page in the parent section"
while Word displays it "on all pages except the first page in the parent
section". Read as Word reads it, and against the first page **of the
section** rather than of the document — see `#mountPageBorder`.
PageGeometry
interface PageGeometry
One page, placed.
floats?
readonly FloatGeometry[] | undefined
The floating objects this page carries; see {@link FloatGeometry}.
noteRule?
{ readonly x: number; readonly y: number; readonly width: number; } | undefined
The rule above the footnotes, where the page has any.
`<w:separator/>` is a run that draws no glyph, so the note area's own
geometry cannot carry it: the engine already pays for its line — see
`separatorLine` — and until this was here the line was paid for and left
blank, which is a footnote hanging in the middle of the page under
nothing. Stated as a rectangle rather than as a flag because only the
engine knows where the note area begins.
ended?
string | undefined
Why the page ended, for the instruments rather than for the painter.
A page that ends because a line would not fit is a different defect from
one that ends because a heading asked for a new page, and a comparison that
sees only *where* the two engines part cannot tell them apart. Named at the
moment the page is closed: `full`, `break`, `section`, `row`, `column`,
`keep`, `end`.
section?
number | undefined
Which section of the document the page belongs to, counted from nought.
Recorded so that `SECTIONPAGES` has something to count, and because a
comparison that knows where the sections fall can say whether a page went
astray at a break or in the middle of a stretch.
border?
{ readonly ref: PieceRef; readonly firstOfSection: boolean; readonly margins: { readonly top: number; readonly right: number; readonly bottom: number; readonly left: number; }; } | undefined
The frame round the sheet, where the section in force draws one.
Drawn by the viewer before the engine switch and by nobody after it: the
model kept `w:pgBorders`, the writer kept writing it, and every page of the
twelve corpus documents that ask for a frame came out without one. The
engine says only what it alone knows — whether this sheet opens its
section, which `w:display` turns on, and where the section's margins are,
which a frame measured from the text is drawn against.
PageMargins
interface PageMargins
headerTwips
number
Distance from the page edge to the header, `w:header`.
gutterTwips
number
Extra binding margin, `w:gutter`.
PageNumbering
interface PageNumbering
Page number format for the section, `w:pgNumType`.
PageSetup
interface PageSetup
The page, as the section states it.
pageBorder?
unknown
`w:pgBorders`: the frame the section draws round its sheets.
Carried and not read — which sides, how far in and on which pages is the
painter's business, like every other {@link PieceRef} — and put on the setup
because the setup is the one thing that already changes exactly where a
section does. See {@link PageGeometry.border}.
kernsPunctuation?
boolean | undefined
`w:noPunctuationKerning` the other way round: whether two adjacent
full-width marks share one half em.
On unless a document turns it off, and it does nothing unless the run also
asks to be kerned — see {@link TextPiece.kerns} and `kernsPunctuation`.
hyphenation?
{ readonly points: Hyphenation; readonly zoneTwips: number; readonly consecutiveLimit: number; readonly caps: boolean; } | undefined
`w:autoHyphenation`: the document breaks words at the end of a line.
Absent where it does not, which is the common case and the whole of why
this is one optional object rather than four fields: the engine asks once.
- `points` answers where a word of a given language may break. It is
fetched asynchronously by the composer and filled in before the layout
runs, so it answers nothing until then and the engine never waits.
- `zoneTwips` is `w:hyphenationZone`: the white Word tolerates at the end
of a line before it reaches for a hyphen. A word is broken only where
leaving it whole would leave more than this empty.
- `consecutiveLimit` is `w:consecutiveHyphenLimit`: how many lines in a row
may end in a hyphen, nought meaning no limit.
- `caps` is the other way round from `w:doNotHyphenateCaps`: false leaves a
word written wholly in capitals whole.
rulesTableLines?
boolean | undefined
`w:adjustLineHeightInTable`: the document grid rules lines inside a table.
[MS-OI29500] states the rule outright — the grid's line pitch **shall not
be added to any line inside a table cell unless this flag is present in the
document's compatibility settings**. Without it a cell's lines are set at
their natural height however fine or coarse the section's `w:docGrid`; with
it they are stepped like any other line on the page.
67 of the corpus's 1151 documents with a settings part declare it.
noExtraLineSpacing?
boolean | undefined
`w:noExtraLineSpacing`: an `exact` line's surplus stands below the text.
ECMA-376 Part 4 §14.8.3.28, and Word honours it — see the `exact` branch of
{@link lineBoxOf}, which is where it is read.
suppressTopSpacing?
boolean | undefined
`w:suppressTopSpacing`: the first line of a page loses the room its rule
would keep over it.
ECMA-376 Part 4 §14.8.3.43, and Word honours it — by a narrower rule than
the sentence describes, measured at {@link topSpacingShiftOf}, which is
where the amount comes from.
keepsBreakLinesRagged?
boolean | undefined
`w:doNotExpandShiftReturn`: a justified line a `<w:br/>` ended is left
ragged.
Off by default, and then Word stretches that line across the column like
any other it justifies: probes `compat-shift-return-*` draw `A short first
line` at 72–526pt without the flag and at 72–146pt with it.
indentsToText?
boolean | undefined
Whether `w:tblInd` is measured to the first cell's text rather than to the
table's edge.
The older of two layouts, and `w:compatSetting compatibilityMode` names it
again: `table-indent-stated-zero` and `table-indent-in-compatibility-mode-15`
are the same document but for that setting, and Word begins the first
cell's text at 96.0 — the margin, the table's edge a cell margin outside it
— in the first and at 103.2 — the edge at the margin — in the second.
inCell?
boolean | undefined
The text is laid out inside a table cell, where Word hangs no mark past
the edge: `east-asian-squeeze-for-kinsoku-cell` is the paragraphs that hang
one on the page, in a cell as wide as the column, and every one of them
takes the ideograph before the mark down instead.
defaultTabTwips?
number | undefined
`w:defaultTabStop`: the interval between the stops nobody declared.
notesBeneathText?
boolean | undefined
`w:footnotePr/w:pos`: the notes follow the text instead of hanging from the
foot of the page.
`pageBottom` is the default and is what almost every document takes. The
other three values all come to the same thing:
`footnote-position-{beneathtext,sectend,docend}` put six lines, a note
reference and four more on a page a third full, and Word draws the note at
243.6 points down in all three — one line under the last line of text,
with the separator between — where `footnote-position-pagebottom` draws it
at 767. So `sectEnd` is `beneathText` ([MS-OI29500] §2.1.541 b), and so in
practice is the `docEnd` that same note says Word does not support.
Five documents of the corpus ask for one of the three.
decimalSymbol?
string | undefined
`w:decimalSymbol`: the character a `decimal` tab stop aligns on.
ECMA-376 §17.15.1.29 defaults it to the full stop. Word reads its own
locale here and not the document — see the `decimal` branch of
{@link tabAdvance} — and a viewer has no locale to read, so the document's
statement is what is honoured.
headerTwips?
number | undefined
`w:pgMar/@w:header`: from the top edge of the page to the running head.
footerTwips?
number | undefined
`w:pgMar/@w:footer`: from the bottom edge to the running foot.
pageNumberStart?
number | undefined
`w:pgNumType/@w:start`: the number the section's first page carries.
Not decoration. The number a `PAGE` field prints is this one, and so is the
parity that decides which of an odd-and-even pair a sheet carries: a
document whose body restarts at one after four pages of front matter has
its left-hand pages on the other side from where the sheet count would put
them.
lineNumbering?
{ readonly countBy: number; readonly start: number; readonly distanceTwips?: number | undefined; readonly restart: "newPage" | "newSection" | "continuous"; } | undefined
`w:lnNumType`: the numbers Word rules down the left of the text.
Absent where the section asks for none — see `setupOf`, which reads
`w:countBy` as the standard writes it: nought multiples is no numbering.
verticalAlign?
"both" | "center" | "top" | "bottom" | undefined
`w:vAlign`: where the body sits between the margins of a page it does not
fill.
A title page is the case, and it is not decoration: a page centred puts its
first line halfway down the sheet, so every line of it is compared against
a line of Word's that is nowhere near. `both` is Word's justified setting,
which spreads the paragraphs rather than moving them, and is left alone.
columns?
{ readonly count: number; readonly spaceTwips?: number; readonly widths?: readonly { readonly widthTwips: number; readonly spaceTwips?: number; }[]; } | undefined
`w:cols`: how many columns the text area is divided into, and the gutter
between them.
The text fills the first column to its foot, then the second, and the page
ends when the last is full. A section that states none is one column, which
is the same thing said differently.
PageSize
interface PageSize
code?
number | undefined
`w:code`: the paper Word asks the printer for.
A number out of the Windows `DEVMODE` table — 9 is A4, 1 is Letter — and
nothing on the page depends on it. It is what a printer driver is handed,
so a document that loses it is one that comes out of the tray it was not
meant to. Fifty-one documents in three hundred state it.
PapxSpan
interface PapxSpan
Paragraph formatting for one paragraph, and the style it is based on.
grpprl
Uint8Array<ArrayBufferLike>
ParagraphBorder
interface ParagraphBorder
A rule drawn above or below a paragraph.
sizeEighths?
number | undefined
`w:sz`, in eighths of a point.
spacePoints?
number | undefined
`w:space`, in points, between the rule and the text.
ParagraphInput
interface ParagraphInput
A paragraph, resolved: everything the geometry needs and nothing else.
suppressLineNumbers?
boolean | undefined
`w:suppressLineNumbers`: the paragraph takes no part in line numbering.
bookmarks?
readonly string[] | undefined
The bookmarks that begin in this paragraph, or just above it.
What a `PAGEREF` resolves against: the page this paragraph landed on is
the page those bookmarks are on. Named rather than positioned, because a
bookmark takes no room and the page is all a reference wants.
styleId?
string | undefined
The paragraph's style id, which `STYLEREF` looks its text up by.
Carried for that one field and nothing else: the cascade is resolved by the
time a paragraph reaches the engine, so the id is not otherwise wanted.
ref?
unknown
What whoever built this wants back when it is drawn; see {@link PieceRef}.
Borders, shading, the address a citation uses — none of them changes where
anything goes, so none of them is the engine's, and this is how they reach
the painter without the engine learning what a border is.
pieces
readonly (TextPiece | TabPiece | ObjectPiece | BreakPiece)[]
tabs?
readonly { positionTwips: number; alignment: string; leader?: string; }[] | undefined
The paragraph's own tab stops, `w:tabs`, in twips.
indentLeftTwips?
number | undefined
Left, right and first-line indents, in twips.
labelAlignment?
"left" | "center" | "right" | undefined
`w:lvlJc`: where the numbering label sits against its position.
keepNext?
boolean | undefined
`w:keepNext`: this paragraph stays with the one after it.
pageBreakBefore?
boolean | undefined
`w:pageBreakBefore`.
ownPageBreak?
boolean | undefined
`w:pageBreakBefore` written on this paragraph, and nothing else.
The two differ where a break in a run put the paragraph at the top of a
page: that paragraph opens a page and did not ask to. Only the one that
asked keeps its space above — see where `fill` acts on this.
columnBreakBefore?
boolean | undefined
A `<w:br w:type="column"/>` ended the paragraph above this one.
The column's break, not the page's: the text goes on at the top of the next
column and only reaches the next page when there is no next column.
`section-start-nextcolumn` and `column-break-in-the-last-column` are the
two that ask, and the second is what says the difference matters — three
groups and two column breaks on a two-column sheet come back from Word two
pages long.
keepLines?
boolean | undefined
`w:keepLines`: every line of the paragraph on one page.
A heading of two lines split across a page boundary is what it forbids.
Where the whole of it will not fit in the room left, the whole of it goes
down — and where it will not fit on a page of its own either, it is set
where it falls, because there is nowhere better.
snapToGrid?
boolean | undefined
`w:snapToGrid`: whether the document's grid rules this paragraph's lines.
widowControl?
boolean | undefined
`w:widowControl`, which Word turns on by default.
lineless?
boolean | undefined
A paragraph whose floats are placed and which takes no line itself: the
empty carrier of a continuous section break. See where it is built.
notes?
readonly NoteInput[] | undefined
The footnotes whose references this paragraph carries.
Their text is set at the foot of the page the reference lands on, and the
room it takes comes off the body — which is why they are the engine's
business and not the painter's.
floats?
readonly FloatInput[] | undefined
The floating objects anchored in this paragraph.
They obstruct this paragraph and the ones after it, for as far down the
page as they reach. Anchored *in* a paragraph is where the format puts
them; where they land on the page is the caller's answer.
frame?
FrameInput | undefined
`w:framePr`: the paragraph is drawn where it says, not where the flow ends.
A text frame is the older half of what `w:drawing` does for a picture — a
paragraph given a width, an anchor and an offset — and Word takes it out of
the flow entirely: it costs the page no height, and the text around it runs
*beside* it rather than under.
The browser path can only do the first half of that, because CSS has no way
to say "keep out of this box" for a box that is not a float, and doing the
first half alone lets a page accept content it has no room for — measured
there at 875 matched lines gained against 4241 words lost. This engine has
real obstacles, so it can do both, and both is what Word does.
ParagraphNode
interface ParagraphNode
id
number
Identity, for as long as the document is open.
What an edit names and what an incremental relayout compares. Not an index:
inserting a paragraph renumbers every index after it and leaves every id
alone, which is the entire reason this is here. See `model/ids.ts`.
sectionProperties
SectionProperties | undefined
Section properties attached to this paragraph mark.
Present only on the last paragraph of a section; it defines the page setup
of the section that *ends* here, which is the part of the format that most
often trips up implementations.
paraId?
string | undefined
`w14:paraId`: the identity Word gives this paragraph, eight hex digits.
Not {@link id}, and the difference matters. `id` is minted by whoever read
the document and means nothing outside this process; `paraId` is written
into the file, survives it, and is what the rest of the package points at —
`w15:commentEx` names the paragraph a comment is anchored to by its
`paraId`, and co-authoring tells two edits of one paragraph apart by it. A
document that loses them is one whose comments no longer resolve.
textId?
string | undefined
`w14:textId`: Word's hash of the paragraph's text, used to spot changes.
openFields?
readonly string[] | undefined
The complex fields an earlier paragraph began and had not ended when this
one begins, by type, outermost first.
A field is a flat run of `w:fldChar` marks, and nothing keeps it inside one
paragraph: a table of contents opens in its first entry and closes after
its last, a hundred paragraphs on. Each paragraph reads its own fields and
closes whatever is still open at its end, so without this every entry after
the first stood outside the field that made it.
And that is a difference Word draws. Inside a `TOC` field an entry's
hyperlink does not take the `Hyperlink` style: `toc-hyperlink-*`, four
probes of one contents page, print the entries black and plain with the
field and blue and underlined without it — whether or not a content
control is wrapped round them.
ParagraphProperties
interface ParagraphProperties
Paragraph-level formatting, `w:pPr`.
style?
string | undefined
`w:pStyle`: the paragraph style this paragraph is written against.
indentLeftTwips?
number | undefined
Left indent in twips; `w:start` in newer files.
indentFirstLineTwips?
number | undefined
First-line indent in twips.
Mutually exclusive with {@link indentHangingTwips}: Word writes one or the
other, and a hanging indent is a negative first-line indent applied together
with a matching left indent.
indentLeftChars?
number | undefined
The same four indents in hundredths of a character, `w:leftChars` and kin.
A character is as wide as the paragraph's font makes it, which is why East
Asian documents measure indents this way: two characters must stay two
characters at any size. Word prefers this form where both are written, and
only the renderer knows the font size that turns it into a length.
spaceBeforeLines?
number | undefined
The same space stated in hundredths of a line, `w:beforeLines`.
A "line" is Word's nominal one — 240 twentieths of a point — not the height
the paragraph's own type happens to need, and the line units win over the
points where a paragraph states both.
spaceBeforeAuto?
boolean | undefined
Automatic spacing overrides the explicit value when set.
lineSpacing?
number | undefined
Line spacing; interpretation depends on {@link lineSpacingRule}.
lineSpacingRule?
LineSpacingRule | undefined
contextualSpacing?
boolean | undefined
Suppress spacing between paragraphs of the same style, `w:contextualSpacing`.
wordWrap?
boolean | undefined
Whether a line may break only between words, `w:wordWrap`.
On by default and interesting only when a paragraph turns it off: Word then
breaks a line inside a word rather than let it hang past the margin. A
narrow column of a URL, a table cell holding a long identifier, and East
Asian text set in a measure narrower than one of its words all rely on it —
without it the word overhangs the column and the page reads as one whose
text does not fit its own frame.
autoSpaceLatin?
boolean | undefined
Space automatically inserted between East Asian and Latin text,
`w:autoSpaceDE`; and between East Asian text and digits, `w:autoSpaceDN`.
Both are on unless the paragraph turns them off, which is why they are
modelled as "off when false" rather than "on when true".
snapToGrid?
boolean | undefined
Whether the paragraph's lines sit on the document grid, `w:snapToGrid`.
On unless the paragraph says otherwise, and only meaningful where the
section declares a grid.
kinsoku?
boolean | undefined
East Asian line-breaking rules, `w:kinsoku`.
On by default, and what stops a line from ending in an opening bracket or
beginning with a full stop. CSS says the same thing as `line-break`, so the
rule reaches the page rather than only the model.
overflowPunctuation?
boolean | undefined
`w:overflowPunct`: punctuation may hang past the margin rather than wrap.
topLinePunctuation?
boolean | undefined
`w:topLinePunct`: punctuation is compressed at the start of a line.
adjustRightIndent?
boolean | undefined
`w:adjustRightInd`: the right indent is adjusted to the document grid.
Only meaningful with `w:docGrid`, which is what makes it an East Asian
setting written into western documents by every converter that has ever
touched one.
suppressOverlap?
boolean | undefined
`w:suppressOverlap`: a framed paragraph may not overlap another.
tabs?
readonly TabStop[] | undefined
numbering?
NumberingReference | undefined
List membership, `w:numPr`.
outlineLevel?
number | undefined
Outline level 0..8 as stored; 9 means body text.
markRunProperties?
RunProperties | undefined
Formatting of the paragraph mark itself, `w:pPr/w:rPr`.
conditionalFormatting?
ConditionalFormatting | undefined
Conditional formatting flags inherited from the table style.
textAlignment?
"auto" | "center" | "top" | "bottom" | "baseline" | undefined
revision?
RevisionInfo | undefined
Set when the paragraph mark is part of a tracked change.
frame?
FrameProperties | undefined
Frame properties for a text frame, `w:framePr`.
ParagraphSpacing
interface ParagraphSpacing
A paragraph, as far as the space around it is concerned.
before?
number | undefined
`w:spacing/@w:before`, in twips.
after?
number | undefined
`w:spacing/@w:after`, in twips.
beforeLines?
number | undefined
The same space in hundredths of a line, `w:beforeLines`, which wins.
The line is Word's nominal one — {@link NOMINAL_LINE_TWIPS} — and not the
one the paragraph's type needs. `spacing-in-line-units` says both halves of
that outright: eleven-point Calibri, whose own line is 17.92px, is given
16.03px for `w:beforeLines="100"` — 240 twips — and the same 16.00px again
where the paragraph is set in twenty-two point. A paragraph stating a
hundred lines *and* 1200 twips is given the lines.
beforeAuto?
boolean | undefined
`w:beforeAutospacing`, which overrides whatever the paragraph stated.
Word's HTML spacing: {@link AUTO_SPACING_TWIPS} whatever the type and
whatever `w:before` says. `autospacing-between-paragraphs` states 100 twips
beside the flag and Word gives 18.7px — fourteen points.
autoIsFixed?
boolean | undefined
`w:doNotUseHTMLParagraphAutoSpacing`: the paragraph's own twips instead,
and both spaces of a seam paid rather than the larger; see
{@link paragraphGapOf}.
"Use fixed paragraph spacing for the HTML auto setting" — the document says
that {@link ParagraphSpacing.beforeAuto} is to be read as whatever
`w:before` states rather than as Word's HTML fourteen points. 24 documents
of the corpus declare it, and none of them turns on it: 1608 exact page
counts and 45 pages of error either way. Carried because it is a rule, not
because the corpus asked.
numberingId?
number | undefined
The list this paragraph is an item of, where it is one.
Automatic spacing is dropped *between* items of one list and kept at its
edges: `autospacing-in-a-list` steps 36.5px into the first item, 17.92px —
a bare line — between the items, and 36.6px out of the last.
borderTop?
ParagraphBorder | undefined
The paragraph's own top and bottom borders, where it draws them.
A border is drawn outside the text and takes its own width plus the space
it asks for. `paragraph-border-space` puts three of them at 0, 11 and 31
points of space under one `w:sz="12"` rule — an eighth-point unit, so a
line 1.5pt wide — and Word steps 1.95px, 16.64px and 43.36px more than a
bare line, which is that width plus that space each time.
borderBottom?
ParagraphBorder | undefined
contextual?
boolean | undefined
`w:contextualSpacing`: suppress this paragraph's own space beside its like.
style?
string | undefined
The style it names, which is what "the same style" is decided on.
PictureBullet
interface PictureBullet
A picture used as a bullet, `w:numPicBullet`.
relationshipId
string | undefined
Relationship id of the image.
title
string | undefined
`v:imagedata/@o:title`: the picture's own name, which Word shows as a tip.
PictureFill
interface PictureFill
A picture used as a fill rather than drawn as itself, `a:blipFill`.
crop
{ left: number; top: number; right: number; bottom: number; } | undefined
`a:srcRect`, as fractions of the source size.
mode
"stretch" | "tile"
`a:stretch` fills the box with one copy; `a:tile` repeats the picture.
The distinction is not decoration: a tiled logo at its natural size and the
same logo stretched over a full-width banner are different pictures on the
page, and the tiled one is what a watermark and every textured panel use.
tileScaleX
number | undefined
Scale of one tile, from `a:tile/@sx` and `@sy`, as a fraction.
PictureNaming
interface PictureNaming
next
() => number
A number unique within the part, for `wp:docPr/@id`.
Word will open a document whose drawings share an id, and its own repair
dialogue will offer to fix it; a counter costs nothing and avoids the
question. The caller holds it because it has to be unique across the part
and this function sees one drawing.
PictureOptions
interface PictureOptions
width?
number | undefined
How wide and tall to draw it, in **points**.
Points because that is the unit the rest of this model states type and
spacing in, and because the format's own unit — the English Metric Unit,
914 400 to the inch — is not one anybody wants to type. Give
{@link widthEmu} instead where an exact value matters.
description?
string | undefined
The alternative text, which is the one part of a picture a person writes.
Strongly worth giving: it is what a reader with a screen reader gets
instead of the picture, and what a search finds.
mimeType?
string | undefined
The content type; sniffed from the bytes when it is not given.
PicturePlacement
interface PicturePlacement
What a run's picture is and how big it should be drawn.
id
string
The name {@link MediaStore.url} resolves.
crop
{ left: number; top: number; right: number; bottom: number; } | undefined
look
ShapeLook | undefined
How to draw it, when what is in the line is a shape rather than a picture.
Piece
interface Piece
span
number
Which span of the paragraph the piece was cut from, and where in it.
The rules here need neither, and carry them so that the caller can put a
chosen line back where it came from: the browser path turns them into a
DOM position, a layout computed in Node into an offset in the model. A box
spans no text and reports `from === to === 0`.
label?
boolean | undefined
The piece is part of a list's label, not of its text.
A label is set at the head of the paragraph's first line and the text
follows it there; a line holding the label and nothing else is not a line
Word draws. `chooseLines` will not close one.
eastAsianSeam?
boolean | undefined
The break before the piece is one between two characters, either of them
East Asian, and not one a space or a Latin rule opened.
Word does not take such a break on a line that could end at a space
instead; see where `chunk` is built in {@link chooseLines}.
width
number
Advance in pixels, including the spaces that end it.
widthAt?
((x: number) => number) | undefined
The advance, where the box's is decided by where it lands. See {@link BoxSpan.widthAt}.
hangAt?
((x: number) => number) | undefined
How far what follows the box is painted past the line's edge. See {@link BoxSpan.hangAt}.
leaderAt?
((x: number) => { readonly glyph: string; readonly width: number; readonly run?: unknown; } | undefined) | undefined
What the box draws across itself, where it draws anything.
inkWidth
number
Advance without those spaces: what a line's fit is judged on.
hangWidth?
number | undefined
How much of the ink at the piece's end could hang outside the column.
Kept and not used, because the corpus refused it. `w:overflowPunct` —
"allow punctuation to extend past the right margin" — is on unless a
document turns it off, and it looked like the explanation for Word's own
lines being wider than the column they are set in, which the justification
allowance is otherwise standing in for. It is not. Let the stops and commas
hang *and* keep the allowance and this engine fits more than ever — 715
lines broken late against 650. Let them hang *instead* of the allowance and
it is worse still: 1228 exact page counts against 1244, 107 420 lines on
their page against 110 302, and the early breaks more than double.
So whatever Word is doing when it sets a line past its column, it is
neither hanging its punctuation nor doing it to every line that ends in a
comma. The measured allowance stays, and this stays measured and unused.
sliceWidth?
((from: number, to: number) => number) | undefined
How wide a part of this piece's own text is, for the one break Word makes
inside a word: see the emergency cut in {@link chooseLines}.
endsLineAt?
((x: number) => boolean) | undefined
Whether this piece ends the line it lands on, given where it lands.
A tab sent to a stop outside the column does: Word cannot honour the stop
on this line and does not try again on the next — it ends the line there.
See {@link TabAdvance.unreachable }.
wrapsAt?
((x: number) => boolean) | undefined
Whether the piece begins the next line instead, given where it would land.
fillsLineAt?
((x: number) => boolean) | undefined
Whether the piece fills its line to the margin, leaving the mark nowhere.
kernBefore?
number | undefined
The kerning between the last character before this piece and its first.
A piece is measured on its own, and a canvas kerns only inside the string
it is given, so the pair that straddles the seam is lost. Over a line of
thirty words that is most of a pixel — small, but it lands exactly where it
hurts: a line Word fills to the margin comes out wider than the column and
gives up its last word. Charged to the piece on the right of the seam, and
dropped when that piece begins a line, where there is no pair to kern.
Absent where there is no seam to speak of: an object, a break, an inset.
gapBefore?
number | undefined
The East Asian gap before this piece; see {@link LineSpan.gapAt }.
wideCount?
number | undefined
How many full-width characters the piece holds; see `SQUEEZE_OF_EAST_ASIAN`.
spaces
number
Spaces inside it that justification may stretch or squeeze.
trailingSpaces
number
How many of those spaces end it, and so hang past the margin.
breakBefore
boolean
Whether a line may begin here.
breakAfter
boolean
Whether the line must end here — an explicit `<w:br/>`.
fontSize
number
Type size of the text, which bounds how far a line may be compressed.
squeezableEm?
number | undefined
How much of the piece is the blank half of an East Asian punctuation mark.
**Nothing sets it, because Word does not take that half back to fit another
character on the line.** A full-width comma, stop or bracket is a
half-width glyph drawn inside a full em, the other half is the space the
composition rules put beside it, and `w:characterSpacingControl` — whose
default is `compressPunctuation` — reads as if Word reclaimed it when a
line needed the room. Twelve probes say it does not.
`punctuation-compress-*` set MS 明朝 at twelve point — a fixed-width face,
240 twips to the character, marks included — in a column exactly twenty
characters wide, and hand it twenty-one characters of which two are marks.
Half an em apiece would bring the line back to exactly twenty ems and the
twenty-first character would fit. Word puts **twenty** characters on the
line and sends the twenty-first down, in every configuration asked:
two commas, two stops, two brackets, a comma and a bracket, two middle
dots; `doNotCompress` and `compressPunctuationAndJapaneseKana` beside the
default; with the run declared Japanese and without; flush left,
justified and distributed.
`-one-comma` asks it once more in a column half a character wider, where a
single mark's half is the whole of the difference, and the answer is the
same. `-plain` is the calibration and reads twenty.
So the setting is about how the marks are *drawn* — Word's own description
is "compress punctuation", not "compress the line" — and not about where a
line ends. The corpus had suggested otherwise, and every reading built on
it split three documents won against three lost; see the git history for
the five that were tried.
Kept as a field because the fit still honours it: anything that finds a
case where Word does reclaim the half has somewhere to put it.
punctuationCap?
number | undefined
The most a line holding this piece may take back from its marks, in all.
hangOrSqueeze?
boolean | undefined
A line holding this piece may hang its last mark or squeeze its marks, not both.
hyphenAfter?
number | undefined
What a line ending here owes for the hyphen it would draw, in pixels.
Set on the piece *before* a hyphenation point and nowhere else, so its
presence is the whole statement: the next piece is the rest of a word this
one began, a line that stops here draws a hyphen, and a line that runs
through draws nothing. See where the points are found in {@link piecesOf}.
The width is the face's own hyphen at the run's size, which is the face the
mark is drawn in — the fragment before the break is what carries it, and a
word that changes face mid-way changes it after the mark.
PlacedStop
interface PlacedStop
A tab stop placed on the column's ruler.
x
number
Position on the ruler, in pixels from the column's left edge.
Plex
interface Plex<T>
A plex: the one container the binary format uses for everything positional.
Sections, fields, bookmarks, footnotes, the bin tables — all of them are the
same shape. A run of `n + 1` positions in ascending order, and then `n`
fixed-size structures, one for each of the intervals the positions cut the
document into. Nothing in the file says how many elements there are: the
count follows from the total length and the size of one structure, which is
knowledge the reader has to bring.
positions: 0 120 400 400 980
data: [0] [1] [2] [3]
Two of these intervals in the illustration are worth noticing. `[2]` is
empty, which happens and is not corruption; and the last position is the end
of the last interval, not the start of a further one.
positions
readonly number[]
`n + 1` boundaries, in the units the plex is addressed by.
data
readonly T[]
`n` structures, one per interval.
Rect
interface Rect
A rectangle, in whatever coordinate space its owner counts in.
ResolvedStyle
interface ResolvedStyle
The fully resolved property sets of a style chain.
chain
readonly Style[]
The chain itself, from the requested style up to the root.
RevisionInfo
interface RevisionInfo
Tracked-change metadata attached to a run, paragraph mark, row or cell.
type
"inserted" | "deleted" | "formatChange" | "moveFrom" | "moveTo"
RevisionNode
interface RevisionNode
A tracked insertion or deletion wrapping inline content.
RowHeight
interface RowHeight
`w:trHeight`: how tall a row is, and how strictly.
One object, because the element is replaced whole: a row that states a height
cancels the style's rule along with the style's height, and two flat fields
would let the rule survive the value it belongs to.
twips?
number | undefined
`w:val`, in twips.
rule?
"auto" | "exact" | "atLeast" | undefined
`w:hRule`, where the row states one.
Absent is `atLeast` and not the `auto` the specification names — see
`layout/table-row.ts`, where that is measured rather than assumed.
RowInput
interface RowInput
ref?
unknown
What whoever built this wants back when it is drawn; see {@link PieceRef}.
align?
"left" | "center" | "right" | undefined
`w:trPr/w:jc`: where this row stands, where it differs from the table.
cantSplit?
boolean | undefined
`w:cantSplit`: the row may not be divided between two pages.
height?
RowHeight | undefined
`w:trHeight`.
margins?
RowMargins | undefined
The room the cells keep above and below their content, in twips.
Word's own default is nothing at all — `table-row-height-bare` rules a row
of one line at exactly the height of that line — so a table that states no
vertical cell margin gets none.
header?
boolean | undefined
`w:tblHeader`: the row repeats at the top of every page the table reaches.
gridBefore?
number | undefined
`w:gridBefore`: grid columns the row leaves empty before its first cell.
A row that begins part-way across the table. Read as nought its first cell
took the first column's width, which in `table_gridbefore` is 73 twips
against the 2272 the cell states — five pixels of room for a paragraph, and
with a word cut to fit it the document came out five pages against one.
widthBeforeTwips?
number | undefined
`w:wBefore`: how wide those skipped columns are, in twips.
The standard attaches the element to `w:gridAfter`; [MS-OI29500] §2.1.184 a
says Word reads it against `w:gridBefore`, which is the only reading under
which a row that starts short starts where the file says. Absent, the grid
decides.
ruleAbovePoints?
number | undefined
The horizontal rule drawn above this row, in points.
A row is taller than its content by the width of the rule above it, and the
content sits below that rule. `table-row-height-rule-4/8/12/24/48` measures
it and the answer is exactly linear: a table of eight one-line rows in
eleven-point Times steps 253 twips a row bare, and 253 plus the stated
width with a rule — half a point for `w:sz="4"`, six points for `w:sz="48"`,
and the three between them on the same line. The first baseline moves down
by the table's own top rule by the same amount.
`w:sz` is in eighths of a point; this is points, because that is what the
geometry is in and a conversion belongs to whoever reads the document.
RowMargins
interface RowMargins
The room a cell keeps above and below its content, in twips.
RowProperties
interface RowProperties
Row-level formatting, `w:trPr`.
header?
boolean | undefined
`w:tblHeader`: repeat this row as a header on every page.
cantSplit?
boolean | undefined
Forbid splitting the row across pages, `w:cantSplit`.
gridBefore?
number | undefined
Grid cells skipped before the first cell, `w:gridBefore`.
conditionalFormatting?
ConditionalFormatting | undefined
tableExceptions?
TableProperties | undefined
Table properties this row overrides, `w:tblPrEx`.
Word writes them when rows of one table came from two tables that were
joined: the second keeps its own borders, cell margins and indent, and the
table's own `w:tblPr` no longer describes it. Read as the table's, such a
row draws the wrong rules and sits at the wrong indent — and since the
overrides are almost always borders, the visible result is a table whose
lower half is gridded and whose upper half is not.
RunningInput
interface RunningInput
What is drawn on every page: the running head and foot.
They are the page's, not the flow's, and they decide how much of the page the
flow gets. Word's body begins at the *later* of the top margin and the foot of
the head, and ends at the earlier of the bottom margin and the head of the
foot — so a head taller than its margin pushes the text down and takes a line
off every page of the document.
header?
readonly BlockInput[] | undefined
footer?
readonly BlockInput[] | undefined
titlePage?
boolean | undefined
`w:titlePg`: the first page of the section carries its own pair.
Where the section declares a `first` header this is that header; where it
declares none — which is the commoner half of the corpus — the first page
carries **nothing**, and its body begins at the top margin rather than
below a head that is not there. A title page given the ordinary head loses
a line at the top of every page that follows it, because the section's
first page break then falls in the wrong place.
firstHeader?
readonly BlockInput[] | undefined
firstFooter?
readonly BlockInput[] | undefined
evenAndOdd?
boolean | undefined
`w:evenAndOddHeaders`: the left-hand pages carry their own pair.
A document setting rather than a section one, but it is answered here
because it is the same question — which of the section's heads this sheet
carries. Page one is odd, and the parity is counted over the document and
not over the section: Word numbers the sheets, not the parts.
evenHeader?
readonly BlockInput[] | undefined
evenFooter?
readonly BlockInput[] | undefined
noteSeparator?
readonly BlockInput[] | undefined
An empty paragraph in the document's own default type.
The footnote separator is a paragraph holding a rule and it costs the page
one line — but a line of *which* type is not obvious, and the probes
`footnote-separator-taller` and `-shorter` answer: the document's default,
neither the notes' type nor the separator paragraph's own. This engine is
given resolved paragraphs and cannot ask what the default is, so whoever
builds the blocks says it here.
noteSeparatorDeclared?
boolean | undefined
{@link RunningInput.noteSeparator} is the document's own separator, named
in its settings, and costs its whole height — spacing and every line — not
one line of it.
noteNotice?
boolean | undefined
`w:footnotePr/w:footnote`: the document declares a continuation notice.
Word keeps a line for it at the foot of every page that carries notes,
whether or not a note runs on — the notes are drawn a line higher and the
body gets a line less. `footnote-notice-declared` puts its note at 1004.77
where `-undeclared` and `-absent` put theirs at 1022.69, and a notice
written into `footnotes.xml` that the settings do not name costs nothing.
RunProperties
interface RunProperties
Character-level formatting, `w:rPr`.
style?
string | undefined
`w:rStyle`: the character style this run is written against.
fonts?
FontReference | undefined
hidden?
boolean | undefined
Hidden text, `w:vanish`.
sizeHalfPoints?
number | undefined
Font size in half-points, `w:sz`.
highlight?
string | undefined
Named highlight colour, `w:highlight`.
spacingTwips?
number | undefined
Character spacing in twips, `w:spacing`; may be negative.
scalePercent?
number | undefined
Horizontal scaling as a percentage, `w:w`.
positionHalfPoints?
number | undefined
Baseline offset in half-points, `w:position`; positive raises the text.
kerningHalfPoints?
number | undefined
Minimum size in half-points at which kerning applies, `w:kern`.
verticalAlign?
"baseline" | "superscript" | "subscript" | undefined
rtl?
boolean | undefined
Right-to-left run, `w:rtl`.
emphasisMark?
"none" | "dot" | "comma" | "circle" | "underDot" | undefined
Emphasis mark placed above or below the text, `w:em`.
language?
string | undefined
Language tags, used for spell-check and for correct hyphenation.
fitTextTwips?
number | undefined
Text is stretched to fit the given width in twips, `w:fitText`.
snapToGrid?
boolean | undefined
`w:snapToGrid`: the run's characters sit on the document grid.
textOutline?
TextOutline | undefined
`w14:textOutline`: the glyphs are drawn as outlines.
A heading set in outline and one set solid are the same markup apart from
this element, and dropping it paints a hollow title in solid black.
textFill?
Fill | undefined
`w14:textFill`: what the inside of the glyphs is painted with.
textShadow?
ShapeShadow | undefined
`w14:shadow`: the one text effect a page draws without a filter.
glow?
TextGlow | undefined
`w14:glow`: a coloured halo around the glyphs.
reflection?
TextReflection | undefined
`w14:reflection`: a mirrored copy of the text below it.
properties3d?
TextProperties3D | undefined
`w14:props3d`: the bevels and the material the glyphs are extruded in.
scene3d?
TextScene3D | undefined
`w14:scene3d`: the camera and the lighting the extrusion is seen under.
ligatures?
string | undefined
`w14:ligatures`: which OpenType ligature sets the run asks for.
`none`, `standard`, `contextual`, `historical`, `discretional` and the
combinations of them. It only shows in a face that ships the feature, which
is why nothing draws it — and why a document that asks for it must not
come back having stopped asking.
numberForm?
string | undefined
`w14:numForm`: `lining` or `oldStyle` figures.
numberSpacing?
string | undefined
`w14:numSpacing`: `tabular` or `proportional` figures.
stylisticSets?
readonly number[] | undefined
`w14:stylisticSets`: the `ss01`..`ss20` sets asked for, by number.
contextualAlternates?
boolean | undefined
`w14:cntxtAlts`: contextual alternates, on unless the run says otherwise.
effect?
string | undefined
`w:effect`: the animated text effects of Word 6.
`blinkBackground`, `lights`, `antsBlack`, `antsRed`, `shimmer`, `sparkle`.
Word stopped drawing them in 2007 and still reads and writes them.
noProof?
boolean | undefined
`w:noProof`: the run is not to be spell-checked.
complexScript?
boolean | undefined
`w:cs`: the run is set in a complex script.
Which of two sets of properties applies to it — `w:bCs` and `w:iCs` rather
than `w:b` and `w:i`, and the complex-script size rather than `w:sz`.
revision?
RevisionInfo | undefined
Set when the run is inside a tracked insertion or deletion.
propertiesChange?
PropertiesChange | undefined
`w:rPrChange`: what the run looked like before somebody reformatted it.
A tracked *formatting* change, which is neither an insertion nor a
deletion: the words are the same and the properties are not. The element
carries who changed them and when, and the whole of the previous `w:rPr`
inside it — which is what "reject this change" puts back.
Nothing draws it: the page shows the formatting that applies now. It is held
because a document saved without it has silently accepted every formatting
change in it.
SavableDocument
interface SavableDocument extends DocxDocument
What a save needs of a document, beyond the model.
Stated as a structural type rather than taken from the class, so that a
document produced by an edit — which is not that class — can be saved by the
same code.
media?
DocumentMedia | undefined
The pictures the document holds outside a package, where it holds any.
A `.docx` has none — its media are parts, and parts are copied through. A
`.doc` keeps its pictures in the binary file's own store, and a document
built by a caller keeps them wherever the caller put them. See
`write/media.ts`.
charts?
ReadonlyMap<string, ChartDefinition> | undefined
Charts the package does not hold, by the relationship id their frames name.
A chart read from a `.docx` is a part that a save copies through, and none
of it reaches here. This is for the other case: a chart a caller *made*,
which has a definition and no part, and where the whole of `chart1.xml` has
to be produced. See `write/chart.ts`.
SaveOptions
interface SaveOptions
bodyChanged?
boolean | undefined
Whether the body was changed since it was read.
The default is to work it out: a document nobody has edited still holds the
span of its own body, and copying it is both exact and instant. An edit
layer that knows better says so.
compress?
boolean | undefined
Compress the parts this save produces. On by default.
stories?
readonly StoryEdit[] | undefined
Stories other than the body that were changed; see {@link StoryEdit}.
Absent, a save writes the main part and copies every other. Given, the
parts those stories live in are rewritten whole from the model — a notes
part holds every note of the document, so changing one writes all of them.
SaveResult
interface SaveResult
bytes
Uint8Array<ArrayBufferLike>
lost
ReadonlyMap<string, number>
What had to be written from the model although it came from the file.
Empty on every save of an unedited document. An entry means a node carried
a span that could not be read back — a package that has been disposed, or a
document opened with preservation off — and what it held may not have
survived.
SectionProperties
interface SectionProperties
pageNumbering
PageNumbering | undefined
lineNumbering
LineNumbering | undefined
headers
readonly HeaderFooterReference[]
footers
readonly HeaderFooterReference[]
titlePage
boolean
The first page uses a distinct header and footer, `w:titlePg`.
verticalAlignment
"both" | "center" | "top" | "bottom" | undefined
Vertical alignment of text on the page, `w:vAlign`.
rtl
boolean
Right-to-left section, `w:bidi`.
documentGrid
DocumentGrid | undefined
`w:docGrid`: the grid East Asian setting places its characters on.
textDirection?
string | undefined
The direction the section's text runs in, `w:textDirection`.
`tbRl` and `tbRlV` are vertical setting: the lines run top to bottom and
the columns right to left, so the page's width and height swap roles. It is
a property of the section rather than of any paragraph in it.
footnoteProperties?
SectionNoteProperties | undefined
Footnote and endnote numbering stated for this section alone.
endnoteProperties?
SectionNoteProperties | undefined
suppressEndnotes?
boolean | undefined
`w:noEndnote`: endnotes are suppressed in this section.
formProtection?
boolean | undefined
`w:formProt`: everything in the section but its form fields is locked.
Nothing on the page shows it and everything about editing the document
does: a section that loses it is a form whose text a reader can now type
over. Fifty-seven documents in three hundred state it.
rtlGutter?
boolean | undefined
`w:rtlGutter`: the binding gutter is on the right edge, not the left.
Shading
interface Shading
pattern
ShadingPattern | undefined
fill
string | undefined
Background colour as `RRGGBB`, or `auto`.
color
string | undefined
Pattern foreground colour.
themeFillTint
string | undefined
Lightening applied to `themeFill`, `w:themeFillTint`; `FF` means none.
themeFillShade
string | undefined
Darkening applied to `themeFill`, `w:themeFillShade`; `FF` means none.
themeTint
string | undefined
Lightening applied to `themeColor`, `w:themeTint`.
themeShade
string | undefined
Darkening applied to `themeColor`, `w:themeShade`.
ShapeAnchor
interface ShapeAnchor
An anchor: where a shape sits, and how the text behaves around it.
spid
number
Which shape, by the identifier the drawing gives it.
left
number
The rectangle it occupies, in twips.
wrap
"none" | "square" | "tight" | "through" | "topAndBottom"
How text flows around it.
relativeFromH
"page" | "column" | "margin"
What the horizontal position is measured from.
relativeFromV
"page" | "paragraph" | "margin"
And the vertical, which has a different set of answers.
behindText
boolean
The shape is painted under the text rather than over it.
inHeader
boolean
The anchor is in a header story rather than the body.
ShapeContent
interface ShapeContent
writeBlocks
(blocks: DrawingNode["textBox"]) => void
Writes the blocks of a text box into the writer, where the shape has one.
ShapeLook
interface ShapeLook
How a shape is painted, when it is a shape rather than a picture.
geometry
string
The geometry, named the way DrawingML names it.
fill
string | undefined
Fill colour as `RRGGBB`, or nothing when the shape is unfilled.
lineColor
string | undefined
Outline colour, or nothing when there is no outline.
lineWidthEmu
number | undefined
Outline width in EMU.
ShapeShadow
interface ShapeShadow
An outer shadow cast by a shape, `a:effectLst/a:outerShdw`.
directionDegrees
number
Degrees clockwise from east, the direction the shadow falls in.
color
ColorReference | undefined
scaleX?
number | undefined
`@sx`, `@sy`: the shadow scaled, in thousandths of a percent.
A perspective shadow is one squashed to a fraction of its height and
skewed; the four together are a matrix, and a `box-shadow` has none. Held
because the file states it, drawn by nothing yet.
skewX?
number | undefined
`@kx`, `@ky`: skewed, in sixtieths of a thousandth of a degree.
alignment?
string | undefined
`@algn`: which corner of the box the shadow is anchored to.
rotateWithShape?
boolean | undefined
`@rotWithShape`: the shadow turns when the shape does.
ShapeStyle
interface ShapeStyle
The visual style of a DrawingML shape.
geometry
string | undefined
`a:prstGeom/@prst`, or `custom` for a `a:custGeom`.
geometryDetail?
DiagramGeometry | undefined
The geometry as DrawingML states it: adjust values, or the outline itself.
The name alone says a shape is not a rectangle; this says what it is, and
it is the same record a slide and a worksheet carry, drawn by the same
code. Before it, a chevron in a document was a box.
shadow?
ShapeShadow | undefined
`a:effectLst/a:outerShdw`, the only effect a page shows without a filter.
blackAndWhiteMode?
string | undefined
`@bwMode`: what the shape becomes when the document is printed in black.
`auto`, `gray`, `ltGray`, `clr`, `black`, `white`, `hidden` and the rest of
the twelve. Nothing here prints, so nothing draws it; Word writes it on
almost every shape it saves and a shape that loses it has lost the author's
answer to a question nobody asked yet.
extent?
{ readonly widthEmu: number; readonly heightEmu: number; } | undefined
`a:xfrm/a:ext` of a *turned* shape, which is not the drawing's extent.
For an upright shape the two agree and the field is absent. For a turned one
they cannot agree: `wp:extent` is the bounding box the drawing occupies
*after* the rotation and `a:xfrm/a:ext` is the box the shape is drawn in
before it — a tall label rotated ninety degrees states one as the other's
transpose. Written back from the drawing's extent, a rotated shape comes
back the wrong shape.
Held only where the drawing is turned, and that is not tidiness: a member of
a group holds its size in the *frame's* space — the reader maps it there and
the writer declares the child space identical — while the extent it arrived
with is in the group's own. Keeping both would be keeping one number in two
coordinate systems and calling them the same thing.
locks?
ReadonlyMap<string, string> | undefined
`a:spLocks` or `a:picLocks`: what an editor may not do to this shape.
Held as the attributes the element carried, because that is what it is: a
row of switches whose names are the operations. None of them changes a
pixel, and an editor that drops them lets the user resize a logo that was
locked against it.
insets
{ left: number; top: number; right: number; bottom: number; } | undefined
Text insets of `wps:bodyPr`, in EMU.
wraps?
boolean | undefined
`wps:bodyPr@wrap`: false where the text of the box is not broken at all.
autoFits?
boolean | undefined
`a:spAutoFit`: the box fits itself to its text, and its stated extent is a
leftover rather than a size.
verticalAlignment
"center" | "top" | "bottom" | undefined
Vertical anchor of the text inside the shape, `wps:bodyPr/@anchor`.
textDirection?
string | undefined
`wps:bodyPr/@vert`: the direction the text in the box is set in.
A writing mode rather than a rotation — the lines still stack, they stack
across the box instead of down it — which is what a title down the edge of
a cover page is, and what a rotated `div` would get wrong.
fontColor
ColorReference | undefined
Colour the shape's theme reference gives its text, `a:fontRef`.
textBody?
TextBodyProperties | undefined
The rest of `wps:bodyPr`: the columns, the overflow, the odd compatibility
switch. Read and thrown away until a census of the corpus counted the
documents that state them.
themeStyle?
ShapeThemeStyle | undefined
`wps:style`: which slots of the theme's format scheme the shape takes.
Span
interface Span
The horizontal room a line has, in points.
Sprm
interface Sprm
One decoded property modifier.
opcode
number
The full sixteen-bit opcode, which is what a switch matches on.
value
number
The operand as an unsigned integer, for the fixed-width forms.
operand
Uint8Array<ArrayBufferLike>
The operand's bytes, which is what the variable-width forms need.
StepResult
interface StepResult
What a step did, and how to undo it.
inverse
Step
The step that puts things back exactly as they were.
touched
readonly number[]
The ids the step touched, for whoever memoises per node.
changed
boolean
Whether the tree actually changed.
StoryEdit
interface StoryEdit
One story of a document, changed, and which flow of text it is.
A Word document is several flows and only one of them is the body: a running
head is a part of its own, a footnote is one of many inside a part they
share. Both are named rather than located — a relationship id for a running
part, the note's own id for a note — because there is no longer any offset to
locate them by. See `edit/session.ts`, which is where these come from.
kind
"footnote" | "endnote" | "footer" | "header"
id
string
The relationship id of a running part, or the id of a note.
StoryRanges
interface StoryRanges
Where each story begins, in characters.
The text of a document is one sequence of characters, and the stories are
consecutive slices of it in a fixed order. Nothing marks the boundaries in
the text itself: the counts in the header are the only thing that says where
the body ends and the footnotes start.
main
readonly [number, number]
footnotes
readonly [number, number]
headers
readonly [number, number]
comments
readonly [number, number]
endnotes
readonly [number, number]
textboxes
readonly [number, number]
headerTextboxes
readonly [number, number]
end
number
One past the last character of the last story.
StrictMarkupOptions
interface StrictMarkupOptions
What strict mode refuses to walk past.
attributes?
boolean | undefined
Stop on an unaccounted attribute as well as an unaccounted element.
SttbEntry
interface SttbEntry
One entry: its text, and whatever fixed-size record followed it.
extra
Uint8Array<ArrayBufferLike>
Style
interface Style
A named style from `styles.xml`.
name
string
Display name, `w:name`; what the user sees in the Word style gallery.
basedOn
string | undefined
Parent style id, `w:basedOn`.
next
string | undefined
Style applied to the following paragraph, `w:next`.
link
string | undefined
Paired character style of a paragraph style, `w:link`.
customStyle
boolean
`@w:customStyle`: defined by the author rather than built into Word.
semiHidden
boolean
Hidden from the UI but still applicable.
visibility
StyleVisibility
What the styles pane does with it; see {@link StyleVisibility}.
paragraph
ParagraphProperties | undefined
run
RunProperties | undefined
table
TableProperties | undefined
row
RowProperties | undefined
cell
CellProperties | undefined
tableOverrides
readonly TableStyleOverride[]
Conditional blocks; present on table styles only.
StyleSheet
interface StyleSheet
The complete style table of a document.
Holds both the named styles and the document defaults, which sit at the bottom
of the cascade and are the reason an empty paragraph still has a font.
styles
ReadonlyMap<string, Style>
defaultParagraph
ParagraphProperties | undefined
`w:docDefaults/w:pPrDefault`.
defaultParagraphDeclared
boolean
Whether the package wrote a `w:pPrDefault` at all, empty or not.
Not the same question as whether it holds anything. An empty element is a
statement — the defaults are the format's own, single spaced with no gap —
where its absence leaves the question to Word, which answers with the
defaults of its blank template. See `BUILT_IN_PARAGRAPH_DEFAULTS`.
defaultRun
RunProperties | undefined
`w:docDefaults/w:rPrDefault`.
defaultStyleIds
Readonly<Record<StyleType, string | undefined>>
Id of the style marked default for each type.
byName
ReadonlyMap<string, Style>
Look-up by display name, needed because numbering references styles by name.
latentStyles
LatentStyles | undefined
`w:latentStyles`: what the styles nobody defined look like in the pane.
SymbolNode
interface SymbolNode
A character from a symbol font, `w:sym`.
char
number
Character code, usually in the private use area (0xF000 and up).
TabAdvance
interface TabAdvance
What a tab advances by, and how far the text after it may hang.
width
number
The advance itself, never nought: a tab that collapsed would run words together.
beyond
number
How far the text after the tab is painted out over the right indent.
Clamping the stop to the paragraph's box is what keeps the *wrap* Word's,
and that is not negotiable. But the clamp was paying for the wrap with the
position: the page number of every entry of an Australian statute's
fifteen-page table of contents sat thirty-eight pixels left of Word's, on
147 pages of it. A *relative* shift is not a wider box — the line is laid
out at the indent and the trailing text is then painted out over it, which
is what hanging into the right indent looks like.
stop
PlacedStop | undefined
The stop it reached, where it reached a declared one.
wraps?
boolean | undefined
An implicit stop past the text area: the tab begins the next line.
unreachable
boolean
Whether the stop it was sent to lies outside the column altogether.
A stop that *starts* its text — a left one — and stands past the right
edge cannot be honoured on this line or on any other, and Word ends the
line at it: `hu-4fd3c254b2ea`, a Hungarian domain application form, rules
its fields with `<w:tab w:val="left" w:pos="9923"/>` in a column of 9922
twips — one twip short — and sets each of them on two lines. 43 documents
of the corpus declare such a stop.
Worth a page of error and 514 lines on Word's page: 1607 exact page counts
either way, 47 pages of error against 48, and no document won or lost. The
Hungarian form itself is not among them — it needs the *tab* moved down and
not the line ended, and that reading is worse everywhere else (1606/48).
Only where the stop starts the text. A right or decimal stop past the edge
is Word setting the text *at* the edge, which is measured and is why the
clamp exists at all.
fillsLine?
boolean | undefined
Whether the tab fills the line to the margin; see `tabAdvance`.
TableCellNode
interface TableCellNode
id
number
Identity, for as long as the document is open.
What an edit names and what an incremental relayout compares. Not an index:
inserting a paragraph renumbers every index after it and leaves every id
alone, which is the entire reason this is here. See `model/ids.ts`.
TableCellPosition
interface TableCellPosition
Where a cell sits in its table, used to select conditional formats.
headerRowCount
number
Number of header rows, which shifts band numbering.
explicit?
ConditionalFormatting | undefined
Explicit flags from `w:cnfStyle`, which override positional inference.
TableFloatPosition
interface TableFloatPosition
TableInput
interface TableInput
A table, as far as the geometry is concerned.
Rows are placed whole. Word will divide a row between two pages where the
document allows it, and this does not: a row that will not fit starts the
next page. It is the conservative half of the rule — the page it puts the row
on is right whenever the row was not going to be divided, which is most rows
of most tables — and it is honest about what it does rather than guessing at
where inside a row the division would fall.
ref?
unknown
What whoever built this wants back when it is drawn; see {@link PieceRef}.
gridStated?
boolean | undefined
Whether `w:tblGrid` said anything the caller could use.
`false` where {@link TableInput.grid} had to be invented — no grid, or one
of nought-width columns — and the content-driven autofit takes over. See
`table-width.ts` and `measureTable`.
anchoredToSheet?
boolean | undefined
`w:tblpPr` anchored to the sheet or the margin, inside a running part.
The body ignores this — a floating table there has been measured four times
and costs the corpus every time (see {@link TableInput.rows}) — but a
running part is not the body. It is not paginated, it exists to be drawn at
a fixed place, and Word's own running feet are built this way: the foot of
`policies-30ce1bb7535e` is one table with
`<w:tblpPr w:vertAnchor="page" w:horzAnchor="page" w:tblpX="1418"
w:tblpY="15761"/>` and two rows of an exact 340 twips. Charged to the foot
those rows made it 45.3px tall and put the body's floor at 1030.0 where
Word's is the page margin, 1047.0.
floating?
boolean | undefined
`w:tblpPr`: the table is out of the flow and anchored somewhere of its own.
The engine reads {@link TableInput.floats} for the box; this says *why*
there is one, which is what a comparison needs to separate a document whose
tables float from one whose tables do not. Stated by whoever composes the
blocks, because only that half has seen `w:tblpPr`.
pageBreakBefore?
boolean | undefined
`w:pageBreakBefore`, or a page break that ended the paragraph above it.
align?
"left" | "center" | "right" | undefined
`w:tblPr/w:jc`: where a table narrower than the column stands in it.
Not the alignment of the text inside it — that is each paragraph's own —
but of the table as a block. Read as `left` whatever it said, every centred
table stood at the margin: `technical-dbc7409f423c` begins its rows 96
pixels left of Word's, `reference-2f22e0d5b12d` 113, and a row that begins
in the wrong place has every line of it in the wrong place.
grid
readonly number[]
`w:tblGrid`, in twips, as the document states it.
layout?
"fixed" | "autofit" | undefined
`w:tblLayout`; absent is `autofit`, which is Word's default.
indentTwips?
number | undefined
`w:tblInd`, in twips.
indentStated?
boolean | undefined
Whether `w:tblInd` is written on the table rather than inherited.
An indent a table *style* states is not inherited: `table-indent-from-the-style-720`
draws exactly where `table-indent-nowhere` draws. See {@link tableEdgeIndentTwips}.
widthTwips?
number | undefined
`w:tblW` where the table states its width in twips.
ruleBelowPoints?
number | undefined
The rule closing the table, in points, which the last row is taller by.
The counterpart of {@link RowInput.ruleAbovePoints}: every row carries the
rule above it, and the bottom of the table has no row below to carry it.
`table-row-height-rule-8` and `-48` put a line under the table and it sits
6.7px further down in the second — the six points against one that their
bottom rules differ by. Charged to the last row, so that a table divided
between two pages takes it to the page the last row lands on.
frameAbovePoints?
number | undefined
The table's own frame, top and bottom, in points: `w:tblBorders/w:top` and
`w:bottom` as the style and the table resolve them.
Asked for where a page divides the table. `table-rule-at-page-break-*`:
Word draws the frame's bottom under the last part of the table on a sheet
and the frame's top over the first part on the next, and it keeps room for
both — the continuation's first line stands as far under the top as the
table's own first line does, and a row divided at the foot leaves the
bottom rule's width free under its last line.
ruleBesideTwips?
number | undefined
The vertical rule beside a cell, in twips — half of it on each side.
See where `measureTable` spends it. Stated in twips rather than points
because it is taken off a width, and widths in a table are twips.
cellSpacingTwips?
number | undefined
`w:tblCellSpacing`, in twips: the gap Word leaves around every cell.
Stated as a half-gap. `table-cell-spacing-60` and `-240` measure it: a row
of one line steps 253 twips with no spacing, 373 with sixty and 733 with
two hundred and forty — 253 plus twice the value, which is the gap under
one cell and over the next. The table's own edge adds the same again, so
the first row's text sits twice the value below the top.
floats?
FloatInput | undefined
`w:tblpPr`: the table is drawn where its anchors say and the text runs past.
Its box, in page pixels, exactly as {@link FloatInput} states one — the
caller works out the anchors because only it knows the sheet. Absent, the
table is in the flow, which is what all but a few are.
TableLook
interface TableLook
noHorizontalBanding
boolean
When true, row banding is suppressed.
TableNode
interface TableNode
id
number
Identity, for as long as the document is open.
What an edit names and what an incremental relayout compares. Not an index:
inserting a paragraph renumbers every index after it and leaves every id
alone, which is the entire reason this is here. See `model/ids.ts`.
grid
readonly number[]
Column widths in twips from `w:tblGrid`.
TableOptions
interface TableOptions
header?
boolean | undefined
The first row is a header: repeated on every page and set in bold.
style?
string | undefined
The table style, `Table Grid` by default so the cells have rules.
widths?
readonly number[] | undefined
Column widths in twips; equal columns across the text area by default.
TableProperties
interface TableProperties
Table-level formatting, `w:tblPr`.
style?
string | undefined
`w:tblStyle`: the table style this table is written against.
cellMargins?
CellMargins | undefined
Default cell margins, `w:tblCellMar`.
cellSpacing?
Measurement | undefined
Spacing between cells, `w:tblCellSpacing`.
layout?
"fixed" | "autofit" | undefined
`fixed` or `autofit`, `w:tblLayout`.
look?
TableLook | undefined
Which conditional formats the table style should apply, `w:tblLook`.
bidiVisual?
boolean | undefined
Right-to-left column order, `w:bidiVisual`.
floatPosition?
TableFloatPosition | undefined
Floating table position, `w:tblpPr`.
allowOverlap?
boolean | undefined
`w:tblOverlap`: whether a floating table may be overlapped by another.
TableRowNode
interface TableRowNode
id
number
Identity, for as long as the document is open.
What an edit names and what an incremental relayout compares. Not an index:
inserting a paragraph renumbers every index after it and leaves every id
alone, which is the entire reason this is here. See `model/ids.ts`.
paraId?
string | undefined
`w14:paraId`: the identity Word gives this row, eight hex digits.
Not {@link id}, and the difference matters. `id` is minted by whoever read
the document and means nothing outside this process; `paraId` is written
into the file, survives it, and is what the rest of the package points at —
`w15:commentEx` names the row a comment is anchored to by its
`paraId`, and co-authoring tells two edits of one row apart by it. A
document that loses them is one whose comments no longer resolve.
textId?
string | undefined
`w14:textId`: Word's hash of the row's text, used to spot changes.
TableStyleContext
interface TableStyleContext
Formatting contributed by a table style to the content of one cell.
cacheKey
string
Identifies this context for caching.
TableStyleOverride
interface TableStyleOverride
One conditional block of a table style.
paragraph
ParagraphProperties | undefined
run
RunProperties | undefined
table
TableProperties | undefined
row
RowProperties | undefined
cell
CellProperties | undefined
TableWidth
interface TableWidth
A width as `w:tblW` and `w:tcW` state one.
type
string
`dxa` twips, `pct` fiftieths of a per cent, `auto`, `nil`.
TabNode
interface TabNode
position?
{ readonly alignment: "left" | "center" | "right"; readonly relativeTo: string; readonly leader: string | undefined; } | undefined
An absolute position tab, `w:ptab`, which names a place rather than a stop.
Ordinary tabs advance to the next stop on the ruler; this one goes to the
left, the middle or the right of the text column whatever the ruler says,
which is how a running head puts a title on the left and a page number on
the right without either being declared anywhere.
TabRoom
interface TabRoom
Where a tab may take the text, and by what ruler.
contentWidth
number
The column's own width: the ruler the stops are measured on.
rightEdgeX
number
The paragraph's right edge on that ruler, which text may not pass.
`w:ind w:right` says where the text wraps, and a tab stop is a position on
the *column's* ruler that may sit past it. Word honours it there — the page
number of a table-of-contents entry hangs into the indent while the title
above it still wraps at the indent. CSS has no way to say that, so the two
are separated: the text is placed inside this edge, and {@link * TabAdvance.beyond} says how far it may then be painted out over the indent.
marginRight
number
How far a stop may hang past that edge: the right indent and no further.
`w:ind w:right` is the only thing a tab is allowed to reach over — the text
hangs into the paragraph's own indent, never out of whatever holds the
paragraph. Unbounded it reaches out of a table cell as readily as out of an
indent: a French household roster sets a right stop at 8278 twips inside
cells a tenth that wide, and its first two columns walked off the row.
defaultWidth
number
Word's interval between the stops nobody declared.
TabStop
interface TabStop
Tab stop, `w:tab`.
alignment
"start" | "end" | "left" | "center" | "right" | "bar" | "clear" | "decimal"
positionTwips
number
Position in twips from the left text margin.
leader
"none" | "dot" | "hyphen" | "underscore" | "heavy" | "middleDot" | undefined
Leader character drawn in the space before the stop.
TextBoxRanges
interface TextBoxRanges
Where each text box's words are, looked up two ways.
Two ways because neither alone finds them all. A shape's `lTxid` property is
an index into the plex — one-based, in its *high* sixteen bits, which is the
kind of detail no specification sentence survives — and every shape in the
corpus that carries one can be resolved that way: 509 of 509. The plex's own
entries also carry the shape's identifier, which resolves 496 of the same
509, so it is kept as the second answer rather than the first.
byIndex
readonly (readonly [number, number])[]
By position in the plex, which is what a shape's text id names.
bySpid
ReadonlyMap<number, readonly [number, number]>
By shape identifier, which the plex entries state.
TextColumn
interface TextColumn
TextNode
interface TextNode
properties
RunProperties
Formatting shared with other runs; interned by the parser.
TextPiece
interface TextPiece
A run of text, resolved: what it says and what it is set in.
ref?
unknown
See {@link PieceRef}: the painter's business, carried and not read.
label?
boolean | undefined
The piece belongs to a list's label rather than to its text.
A label is not content of its own: Word sets it at the head of the
paragraph's first line and the text follows it there. A line holding
nothing else is not a line Word draws, so the line breaker is not allowed
to close one — see `chooseLines`.
eastAsianLanguage?
string | undefined
`w:lang/@w:eastAsia`: the East Asian language the run is written in.
Word applies the East Asian line-breaking rules to a run that names one and
to no other — see {@link LineSpan.eastAsianTypography }, where the probe
that settles it is described. Carried as the tag rather than as a flag so
that the rule about which tags count lives in one place.
language?
string | undefined
`w:lang/@w:val`: the language the run is written in, for hyphenation.
Set by the composer exactly where the document hyphenates this run — the
setting is on, the paragraph does not suppress it and the run names a
language — so its presence is the whole of the decision; see
{@link PageSetup.hyphenation} and {@link LineSpan.language }.
kinsoku?
KinsokuSet | undefined
The characters this run may not begin or end a line with.
ECMA-376 §17.3.1.16's lists, already chosen — `kinsokuSetFor` in
`layout/kinsoku.ts` does the choosing, and three things decide it: the
paragraph's `w:kinsoku`, which is on unless it says otherwise; the run's
`w:lang/@w:eastAsia`, without which Word does not take the text for East
Asian at all; and what `settings.xml` put in place of the defaults.
Resolved by the caller rather than here for the same reason
{@link TextPiece.overflowPunctuation} is: the caller holds `settings.xml`
and the style hierarchy, and the layout holds neither.
characterLevelBreaks?
boolean | undefined
Whether a line of this run may end **inside** a word.
`w:wordWrap w:val="off"` asks for it (ECMA-376 §17.3.1.45), and
[MS-OI29500] §2.1.67 narrows it twice: Word honours it only where it finds
Korean in the paragraph, and only for space-delimited languages. Both gates
are the caller's, as above.
ideographWords?
boolean | undefined
See `LineSpan.ideographWords`.
eastAsianDocument?
boolean | undefined
The document names an East Asian face for its default, and Word lays it
out as an East Asian document: `w:autoSpaceDE`'s quarter em is opened —
`autospace-default-latin` against `-simsun` — and a line's last mark hangs
— `east-asian-squeeze-for-kinsoku` against `-latin-default`. See `gapAt`
and `hanging` where the line spans are built.
compressesPunctuation?
boolean | undefined
The document is East Asian and says `compressPunctuation`, so on a
justified line a full-width mark may give back its blank half — see
`LineSpan.compressiblePunctuation`.
keepAll?
boolean | undefined
Whether a line of this run may end only between words.
Korean, which Word sets `keep-all`: see the pass of that name in
`line-break.ts`. Resolved by the caller, which holds the run's language.
overflowPunctuation?
boolean | undefined
`w:overflowPunct`, where the paragraph turns the hanging off.
letterSpacingTwips?
number | undefined
`w:spacing` in a run's properties: extra room after every character.
Word's "expanded" and "condensed" character spacing, in twips, positive or
negative. It is not a style but a width — a heading set at twenty twips a
letter is a fifth of an inch wider over thirty characters, and a line
measured without it holds a word Word had to send down. 129 of the corpus's
734 real documents use it, across 79 413 runs.
kerns?
boolean | undefined
`w:kern`: whether this run's type is large enough to be kerned.
The attribute states a size in half-points and means "kern at this size and
above", so whether it applies is the caller's arithmetic and not the
layout's. Unkerned text is wider than Word's, which is what the
justification allowance in `line-break.ts` has been standing in for.
scalePercent?
number | undefined
`w:w`: the glyphs drawn at a percentage of their own width.
Word's character scaling — a hundred is the face as drawn, fifty is half as
wide, two hundred twice. It stretches the glyphs and not the room between
them, which is why it multiplies the face's advance and leaves
{@link TextPiece.letterSpacingTwips} alone.
sizeHalfPoints
number
Size in half-points, as `w:sz` states it.
eastAsian?
boolean | undefined
East Asian text, whose line the face does not decide.
emRatio?
number | undefined
Word's own line for the face, in ems, where the project has measured one.
lineSizeHalfPoints?
number | undefined
The size the line is ruled by, where it differs from the size drawn.
A raised run — `w:vertAlign` — is drawn at about two thirds of what it
states and rules the line by the whole of it; see where the caller sets
this. Absent, the drawn size rules.
pageRef?
string | undefined
`PAGEREF`: the bookmark whose page this run's {@link PAGEREF_FIELD} shows.
Carried on the piece rather than in the text because the name is not drawn
— the number is — and because the page it resolves to is not known until
every page has been placed.
styleRef?
string | undefined
`STYLEREF`: the style whose paragraph this run's {@link STYLEREF_FIELD} shows.
A style *id*, resolved from the name the instruction gives while the style
table is still to hand.
charStyle?
string | undefined
`w:rStyle`: the character style this run is set in, for `STYLEREF`.
A `STYLEREF` may name a *character* style rather than a paragraph one, and
then what it shows is the nearest run in it — which is how a running head
assembles "Part 1A Control of liquor" out of two runs of a heading. Carried
on the piece because that is where the run survives.
styleRefLast?
boolean | undefined
`STYLEREF l`: the last such paragraph on the page rather than the first.
styleRefCached?
string | undefined
What the file was saved with, for a style no paragraph on any page carries.
pageRefCached?
string | undefined
The digits the file was saved with, for a bookmark this document has not.
Word prints `Error! Bookmark not defined.` there and the cache usually
already says so; where it says a number instead, the number the author last
saw is a better answer than nothing. The engine does not write that
sentence itself — Word localises it, and guessing the language to guess the
wording would be two guesses.
raisePoints?
number | undefined
`w:position`, in points: how far off the baseline the run is moved.
shiftPoints?
number | undefined
`w:vertAlign`, in points: how far the *drawn* glyph stands off the baseline.
Apart from {@link raisePoints} because the two differ in what they cost:
`w:position` grows the line by what it moves, and a superscript does not —
it already rules its line by its full size, so the lift fits in room the
line has. Whoever draws the run adds both; whoever measures the line takes
only the first.
mark?
boolean | undefined
The paragraph mark, which stands in where the paragraph has no ink.
It rules the line only where nothing typed can — see `tallestOf`.
TextSpan
interface TextSpan
A stretch of a paragraph set in one face, before it is cut into pieces.
Measurement is a function rather than a number because the cutting decides
what to measure: a piece is a slice of the span, and where the slices fall is
not known until the break opportunities have been found. The browser path
hands over a canvas closure, a layout computed in Node hands over the font
file's own advances, and the rules below cannot tell which.
eastAsianTypography?
boolean | undefined
Whether Word applies the East Asian line-breaking rules to this text.
**Kinsoku is not a property of the script but of the run's declared
language.** `punctuation-kinsoku-by-lang` is twenty ideographs and a full
stop in a column exactly twenty ideographs wide, and Word's answer depends
on one attribute: with `w:lang w:eastAsia="ja-JP"` on the run it moves the
ideograph before the stop down as well, so that no line begins with a mark
— nineteen characters on the first line; with no East Asian language it
fills the line and **begins the next one with the stop**. Its two
neighbours say the same of the other two declarations that looked like
candidates: neither `w:useFELayout` nor `w:themeFontLang` turns the rules
on, and only the run's own `w:lang` does.
That is what the 97 lines of the corpus beginning with a closing mark are:
not Word breaking its own rule, but text Word never took for East Asian.
Undeclared here means the rules apply — the browser path knows no
languages, and every real East Asian document declares one.
kinsoku?
KinsokuSet | undefined
The characters this run may not begin or end a line with.
ECMA-376 §17.3.1.16's own lists, chosen by the run's `w:lang/@w:eastAsia`
and by what the document put in place of them; `undefined` where the rules
do not apply. See `layout/kinsoku.ts`.
UAX #14 already forbids most of what they name — `。` is `CL` and `「` is
`OP` — and what it does not is the point of carrying them: `℃`, `°`, `‰`
and the currency signs are `PO` and `PR`, which the standard breaks either
side of and Word does not.
characterLevelBreaks?
boolean | undefined
Whether a line may end **inside** a word of this run.
**Nothing sets this from `w:wordWrap` any more: Word ignores the element.**
ECMA-376 §17.3.1.45 spells the example out — `world` broken between the `o`
and the `r` — and [MS-OI29500] §2.1.67 narrows it twice, to paragraphs
holding Korean and to "space-delimited languages". Six probes write it, in
the place `CT_PPr` puts it, and the Word that draws the references honours
none of them:
- `korean-break-wordwrap-off` and `-wordwrap-on` over Korean, which is the
language §2.1.67 a names. Eight syllables either way, the word level.
- `korean-break-latin` writes `w:wordWrap w:val="0"` over
`aaaaaaaaaaaaaaaaaaaa Supercalifragilistic` in a column that holds the
first word and not the second beside it — the one geometry that tells the
levels apart, a word longer than the whole column being cut whatever the
setting says. Word sends the second word down whole.
- `korean-break-latin-plain` is that document without the element and comes
out identical, and `-latin-korean` adds `가나` to the paragraph so
§2.1.67 a's condition is met. Identical again.
Kept as a property because `latinBreaksAnywhere` still arrives through the
same mechanism, and because a document that finds the case Word honours has
one place to put it.
ideographWords?
boolean | undefined
A run of ideographs that follows a space is one word: the line may end
between two of them only where the run begins the line.
Word 2007's rule and not Word 2010's — see where `chunk` is built in
`chooseLines`, and `han-after-latin-word-break-compat*`.
compressiblePunctuation?
"modern" | "legacy" | undefined
Each full-width mark of the run may give back its blank half to keep a
character on a justified line: `east-asian-compress-punctuation`, where
a comma is drawn 7.67px wide at 14px to pull the fortieth character in,
and two commas 10.71 each — only as much as the line needs. See
`Piece.squeezableEm`.
compressibleNakaten?
boolean | undefined
Whether this run's middle dot gives its blanks back with the other marks.
Japanese only: `east-asian-compress-by-mark-ja-mode-14` compresses `・` as
it compresses a comma, and `-zh-mode-14` — the same probe in SimSun under
`zh-CN` — leaves it at its full em while its commas and colons give as the
Japanese ones do. Resolved by the caller, which knows the run's
`w:lang/@w:eastAsia`.
keepAll?
boolean | undefined
Whether a line of this run may end only between words.
Korean, and nothing else: see the `keep-all` pass in
{@link wordBreakActions} for the five probes. Resolved by the caller, which
knows the run's `w:lang/@w:eastAsia`.
latinBreaksAnywhere?
boolean | undefined
Whether a Latin word standing among ideographs may be broken anywhere.
**What Word does where the run declares no East Asian language at all.**
Counted over the corpus's Chinese: of the twenty-two documents that declare
`zh-CN` or `zh-TW`, not one has Word end a full line inside a Latin word;
of the three that declare none — `educational-80b7c45d441f`,
`zh-98a5dfd0b39a` and `zh-a691a0344d9a` — every one does, twelve lines
between them. `educational-80b7c45d441f` splits `2005` across two lines
twice over and `70%` once, and none of the three writes `w:wordWrap`.
The reading is that Word's word breaker is chosen by the declaration, the
same one kinsoku is behind, and an undeclared run holding ideographs gets
no word breaker at all — so the Latin inside it is unprotected. Resolved by
the caller, which knows the run's language and its text.
hanging?
ReadonlySet<string> | undefined
Whether the punctuation that ends a line may stand outside the column.
`w:overflowPunct` is on unless a document turns it off, and no layout Word
draws honours it: see {@link hangsPunctuation}, where seven probes and the
corpus agree that nothing hangs. Kept because the mechanism is right and
only its trigger is empty.
label?
boolean | undefined
Part of a list's label rather than of its text; see {@link Piece.label}.
widthOf
(from: number, to: number) => number
Advance of `text.slice(from, to)`, in pixels.
kernAt?
((at: number) => number) | undefined
What the seam before an offset costs in kerning.
Omitted where the text is not kerned, which is most of a corpus: Word kerns
only above the size `w:kern` names.
gapAt?
((at: number) => number) | undefined
What the seam before an offset costs in East Asian gap.
Kept apart from the kerning because it behaves differently: kerning is a
width, and the gap is fixed spacing — see the `fits` budget in
{@link chooseLines}.
spaceWidth?
number | undefined
The advance of a space in this span's face, at its size.
language?
string | undefined
`w:lang/@w:val`: the language the run is written in, for hyphenation.
Word hyphenates each run by its own language and by no other — a Ukrainian
paper whose body is English is hyphenated as English — and this is that
declaration, resolved by the caller. Absent where the document does not
hyphenate at all, or where the run suppresses it.
hyphenWidth?
number | undefined
The advance of a hyphen in this span's face, at its size.
What a line that breaks a word of this run owes for the mark it draws.
Present exactly where {@link language} is: no width, no hyphenation.
fontSize
number
Type size in pixels, which bounds how far a justified line may be squeezed.
Theme
interface Theme
colorScheme
ThemeColorScheme | undefined
fontScheme
ThemeFontScheme | undefined
ThemeColorScheme
interface ThemeColorScheme
A theme colour scheme, from `theme1.xml`.
colors
ReadonlyMap<string, string>
Slot name (`accent1`, `dk1`, `lt2`, ...) → `RRGGBB`.
ThemeFontScheme
interface ThemeFontScheme
majorLatin
string | undefined
Font of the heading slot, used by `majorHAnsi` references.
majorScripts
ReadonlyMap<string, string>
The faces the scheme names for particular scripts, `a:font/@script`.
A theme states its East Asian and complex-script slots twice: once as
`a:ea` and `a:cs`, which are usually empty, and once as a list of faces by
script — `Jpan`, `Hans`, `Arab` and two dozen more. Which of the list
applies is not a property of the theme at all: `w:themeFontLang` in the
settings says which language each of the three slots is chosen for, and
Word looks the script up from that.
Keyed by the script tag as written, so the lookup is the specification's.
minorScripts
ReadonlyMap<string, string>
Underline
interface Underline
style
UnderlineStyle | undefined
Absent where `w:val` is: the element then states only its colour, and the kind is inherited.
VmlContent
interface VmlContent
writeBlocks
(blocks: readonly BlockNode[]) => void
Writes the blocks of a text box, which are ordinary document blocks.
next
() => number
A number unique within the part, for the shape's `id`.
VmlShapeNode
interface VmlShapeNode
A legacy VML shape, `w:pict`. Word still writes these for text boxes.
style
string | undefined
The CSS-like style string from the VML `style` attribute, completed.
VML puts a shape's place and size in this string — and not for all of them:
a `v:line` and a `v:polyline` state their rectangle in `from`, `to` and
`points` instead, and their style says only that they float. The reader
writes that rectangle into the string, in the spelling the shape's context
uses, so that one renderer and one writer can read the geometry of every
VML shape out of one place. Nothing the file stated is replaced.
textBox
readonly BlockNode[] | undefined
textBoxInset
{ left: number; top: number; right: number; bottom: number; } | undefined
Padding inside the text box, `v:textbox/@inset`, in points.
Word's default is a tenth of an inch at the sides and half that above and
below, and a converter that wants none says so. Assuming a constant instead
is not a rounding error: a badge 49 points wide whose text is indented 31
points has eighteen points to set two digits in, and ten points of invented
padding leaves too few — the number then wraps one digit per line.
horizontalRule?
{ readonly percent: number | undefined; readonly align: string | undefined; } | undefined
`o:hr`: the shape is Word's *horizontal line*, not a rectangle.
The classic Insert → Horizontal Line, written as `<v:rect o:hr="t"
style="width:0;height:1.5pt"/>`. Its width of nought is not a shape with no
size — it is the rule saying "as wide as the text", and `o:hrpct` narrows
it to a share of that in thousandths. A reader of the style alone throws it
away: one report rules four such lines down its last page and drew none
of them.
textBoxDirection?
string | undefined
Which way the text in the box runs, from `v:textbox/@style`.
VML says it with `layout-flow` and `mso-layout-flow-alt` where DrawingML
says it with `wps:bodyPr/@vert`; the values are named as `w:textDirection`
names them, so the view has one vocabulary to answer.
path
string | undefined
The shape's own outline, `v:shape/@path`, in its own coordinate space.
A converter does not use the preset shapes: it writes every circle, rounded
panel and arrow as an explicit polygon. Drawn as the rectangle it occupies,
a circle is a square — which is what a page number in a round badge looks
like on every page of a converted report.
pathBox
{ originX: number; originY: number; width: number; height: number; } | undefined
The coordinate space `path` is drawn in, `@coordsize` and `@coordorigin`.
textRectangle
{ left: number; top: number; right: number; bottom: number; } | undefined
`v:path/@textboxrect`: the part of the outline the words go in.
In the same coordinate space as {@link VmlShapeNode.path}. A shape that
states none sets its text in its whole box, which is what a rectangle does.
strokeStyle?
VmlStroke | undefined
How the outline is drawn, `v:stroke`.
crop?
{ left: number; top: number; right: number; bottom: number; } | undefined
`v:imagedata` crop fractions, the VML spelling of `a:srcRect`.
A logo cropped to its wordmark and the same logo drawn whole are the same
relationship id and two different pictures on the page.
shadow?
{ color: string | undefined; offsetXPoints: number; offsetYPoints: number; } | undefined
`v:shadow`, offsets in points.
wordArtText?
string | undefined
The text of a WordArt shape, `v:textpath/@string`.
Not a text box: WordArt carries its text as an attribute of the shape, so a
reader that walks past `v:textpath` loses the words entirely — and WordArt
is what a converted document uses for its title.
wordArtStyle?
string | undefined
The font `v:textpath/@style` sets that text in, as a CSS style string.
wordArtFits?
boolean | undefined
`v:textpath/@fitshape` or `@fitpath`: the words are stretched to the shape.
Which is the whole point of WordArt — the built-in `_x0000_t136` declares
`fitshape="t"` and the shape that uses it adds `fitpath="t"`, and neither
writes a font size, because the size *is* the box. Drawn at the size the
run inherits, a masthead comes out a third of the height Word draws it.
rotationDegrees?
number | undefined
`rotation` in the style string, in degrees clockwise about the centre.
Word's watermark is a WordArt shape at `rotation:315`; drawn flat it runs
across the text instead of up the diagonal.
children
readonly VmlShapeNode[] | undefined
Shapes of a `v:group`, positioned inside its box.
A group is a coordinate system: it declares a box on the page in real units
and a grid inside it in arbitrary ones, and its children give their places
in that grid. Keeping the group as a node rather than flattening it into
page positions is what makes an *inline* group work — a badge sitting in a
line of text is a group too, and a flattened one would be torn out of the
flow and pinned to the corner of the paragraph.
A child's `floating` offsets are measured from the group's own box, not
from the page.
floating
VmlFloat | undefined
How the shape sits in the text, the VML counterpart of `wp:inline` and
`wp:anchor`.
Undefined for a shape that flows with the text. A floating shape is the
common case in documents produced by converters, which use a page-sized
rectangle behind the text for the background of a cover page.
WriteContext
interface WriteContext
The body of a document, written back out.
## One path, not two
Everything here is written **from the model**. There is no second path that
copies characters out of the file the document came from: the model is the
document, and a save is a serialisation of it.
That was not always so. The writer used to carry a span on every node saying
where it stood in the original part, and to copy those characters back for
any node an edit had not touched — which bought a byte-for-byte round trip
and cost the thing that mattered more: the document could not be saved
without the file it came from. Two paths also meant every function here began
by asking which one it was on.
What that costs is stated plainly rather than hidden: markup the reader does
not model is **not** carried through. It is read, it draws nothing, and it is
gone at the next save. `lost` names what the *writer* could not express;
`parser/coverage.ts` names what the *reader* walked past, and between them
there is no third category.
lost
Map<string, number>
What the model held and the writer could not express, by name and by count.
Empty on an ordinary save. An entry means a node the model has a shape for
but the writer has no markup for — a drawing that is neither a picture, a
shape, a group nor a frame — and the caller is told rather than left to
discover it.
pictures?
{ next(): number; } | undefined
Numbers for the drawings this part writes, unique within it.
`wp:docPr/@id` has to be unique across the part and a drawing does not know
what else the part holds, so the counter belongs to the walk. Absent for a
caller that writes fragments rather than a part; drawings then take their
numbers from a counter of their own, which is enough for a fragment that
holds one.