VMPrint AST Reference

This document is the reference for the public VMPrint document input format: the JSON/object tree you author and hand to the VMPrint pipeline.

The AST is the canonical public source format. The engine internally normalizes it into a spatial runtime form, but callers work with the AST.

If you want the guided teaching path instead of the full contract, start with the authoring guide:


1. Pipeline Overview

Markdown string
  -> [remark / semantic.ts]
SemanticDocument                (draft2final intermediate layer)
  -> [FormatHandler / FormatContext]
DocumentInput / Element tree    <- public authored source
  -> [normalize()]
SpatialDocument / runtime config
  -> [LayoutEngine]
Page[] of Box[]                 (flat, positioned output)

Direct callers usually construct DocumentInput. The SemanticDocument layer is only relevant when using draft2final.


2. DocumentInput

interface DocumentInput {
    documentVersion: '1.0' | '1.1';
    layout: LayoutConfig;
    fonts?: FontSources;
    styles: Partial<Record<string, ElementStyle>>;
    elements: Element[];
    header?: PageRegionDefinition;
    footer?: PageRegionDefinition;
    debug?: boolean;
}
Field Required Description
documentVersion yes "1.1" is the current and only supported authored surface.
layout yes Page geometry and default typography.
fonts no Font file sources keyed by weight/style.
styles yes Named style table; keys are element type strings.
elements yes Top-level content elements.
header no Running page header; see §15.
footer no Running page footer; see §15.
debug no Enable engine debug output.

3. LayoutConfig

All numeric values are in points unless noted.

interface LayoutConfig {
    pageSize: 'A4' | 'LETTER' | { width: number; height: number };
    orientation?: 'portrait' | 'landscape';
    margins: { top: number; right: number; bottom: number; left: number };
    fontFamily: string;
    fontSize: number;
    lineHeight: number;

    pageBackground?: string;

    headerInsetTop?: number;
    headerInsetBottom?: number;
    footerInsetTop?: number;
    footerInsetBottom?: number;

    pageNumberStart?: number;

    lang?: string;
    direction?: 'ltr' | 'rtl' | 'auto';
    hyphenation?: 'off' | 'auto' | 'soft';
    hyphenateCaps?: boolean;
    hyphenMinWordLength?: number;
    hyphenMinPrefix?: number;
    hyphenMinSuffix?: number;
    justifyEngine?: 'legacy' | 'advanced';
    justifyStrategy?: 'auto' | 'space' | 'inter-character';

    opticalScaling?: {
        enabled?: boolean;
        cjk?: number;
        korean?: number;
        thai?: number;
        devanagari?: number;
        arabic?: number;
        cyrillic?: number;
        latin?: number;
        default?: number;
    };

    storyWrapOpticalUnderhang?: boolean;
}

4. Font Sources

interface FontSources {
    regular?: string;
    bold?: string;
    italic?: string;
    bolditalic?: string;
    [key: string]: string | undefined;
}

Values are file paths or embedded data URLs.


5. Element

interface Element {
    type: string;
    content: string;
    children?: Element[];

    image?: EmbeddedImagePayload;
    table?: TableLayoutOptions;

    slots?: StripSlot[];
    columns?: number;
    gutter?: number;
    balance?: boolean;
    zones?: ZoneDefinition[];

    zoneLayout?: ZoneLayoutOptions;
    stripLayout?: StripLayoutOptions;
    dropCap?: DropCapSpec;
    columnSpan?: 'all' | number;
    placement?: StoryLayoutDirective;

    properties?: ElementProperties;
}
Field Description
type Identifies the element. Used to look up the base style from styles.
content Flat text for leaf nodes. Use "" for containers.
children Structural children or inline runs.
image Preferred on AST 1.1+ for image-bearing nodes.
table Preferred on AST 1.1+ on type: "table" elements.
slots strip only. One-row horizontal composition slots.
columns story only. Number of columns.
gutter story only. Inter-column gap in points.
balance story only. Balance column heights.
zones zone-map only. Independent authored regions.
zoneLayout Preferred on AST 1.1+ on type: "zone-map" elements.
stripLayout Preferred on AST 1.1+ on type: "strip" elements.
dropCap Preferred on AST 1.1+ for paragraph-like elements.
columnSpan Preferred on AST 1.1+ for children of multi-column story elements.
properties Per-element overrides; see §6.

Style resolution order: styles[element.type] -> properties.style.


6. ElementProperties

interface ElementProperties {
    style?: Partial<ElementStyle>;

    colSpan?: number;
    rowSpan?: number;

    sourceId?: string;
    linkTarget?: string;
    semanticRole?: string;
    reflowKey?: string;

    keepWithNext?: boolean;
    marginTop?: number;
    marginBottom?: number;

    simulationContinuation?: SimulationContinuationSpec;
    pageOverrides?: {
        header?: PageRegionContent | null;
        footer?: PageRegionContent | null;
    };

    sourceRange?: { lineStart: number; colStart: number; lineEnd: number; colEnd: number };
    sourceSyntax?: string;
    language?: string;
}

AST 1.1 keeps properties for overrides, metadata, and cross-cutting controls. Structural payloads live on the element itself.

Property Applies to Description
style any Inline style overrides.
colSpan table-cell Number of columns this cell spans.
rowSpan table-cell Number of rows this cell spans.
sourceId any Caller-assigned stable ID surfaced in BoxMeta.
linkTarget inline text, inline Hyperlink URL.
semanticRole table-row "header" marks the row as a header row.
reflowKey any Explicit cache key for the reflow cache.
keepWithNext any Keep this element on the same page as the one after it.
marginTop any Top margin shorthand override.
marginBottom any Bottom margin shorthand override.
simulationContinuation any Cross-page split markers; see §14.
pageOverrides any Override or suppress the header/footer for this element’s pages.
language code blocks Language hint such as "typescript".

7. ElementStyle

All fields are optional. Common fields include:

See engine/src/engine/types.ts for the complete shape.


8. Reserved Structural type Values

type Purpose
story Multi-column flowing content area. Uses columns, gutter, balance. Direct children may carry placement.
table Table container. Children must be table-row. Uses element.table.
table-row Table row. Children must be table-cell.
table-cell Table cell. Supports properties.colSpan and properties.rowSpan.
strip One-row horizontal composition band. Uses slots[] plus element.stripLayout.
zone-map Independent-region layout. Uses zones[] plus element.zoneLayout.

All other type strings are user-defined and are used for style lookup.


9. Inline Element Types

type Description
text Plain text run.
inline Styled inline wrapper.
image Inline image. Uses element.image.
inline-box Inline bordered widget.

10. Image Payload (element.image)

interface EmbeddedImagePayload {
    data: string;
    mimeType?: string;
    fit?: 'contain' | 'fill';
}

Used for both block and inline images.


11. Table Configuration (element.table)

interface TableLayoutOptions {
    headerRows?: number;
    repeatHeader?: boolean;
    columnGap?: number;
    rowGap?: number;
    columns?: TableColumnSizing[];
    cellStyle?: Partial<ElementStyle>;
    headerCellStyle?: Partial<ElementStyle>;
}

TableColumnSizing

interface TableColumnSizing {
    mode?: 'fixed' | 'auto' | 'flex';
    value?: number;
    fr?: number;
    min?: number;
    max?: number;
    basis?: number;
    minContent?: number;
    maxContent?: number;
    grow?: number;
    shrink?: number;
}

11a. Strip (type: "strip")

A strip is a compact one-row horizontal composition band for bylines, folio lines, masthead sub-rows, and similar left/center/right compositions.

{
  "type": "strip",
  "stripLayout": {
    "tracks": [
      { "mode": "flex", "fr": 1 },
      { "mode": "fixed", "value": 32 },
      { "mode": "flex", "fr": 1 }
    ],
    "gap": 8
  },
  "slots": [
    { "id": "left", "elements": [{ "type": "folio-left", "content": "Work Title" }] },
    { "id": "center", "elements": [{ "type": "folio-center", "content": "{pageNumber}" }] },
    { "id": "right", "elements": [{ "type": "folio-right", "content": "Chapter Title" }] }
  ]
}
interface StripLayoutOptions {
    tracks?: TableColumnSizing[];
    gap?: number;
}

interface StripSlot {
    id?: string;
    elements: Element[];
    style?: Record<string, any>;
}

Use strip for lightweight composition, not tabular data.


11b. Zone Map (type: "zone-map")

A zone-map defines independent layout regions inside the current field. The classic strip form is still supported, but zones may also use explicit rectangular region bounds with x, y, width, and optional height.

{
  "type": "zone-map",
  "zoneLayout": {
    "columns": [
      { "mode": "flex", "fr": 2 },
      { "mode": "flex", "fr": 1 }
    ],
    "gap": 16,
    "frameOverflow": "move-whole",
    "worldBehavior": "fixed"
  },
  "properties": {
    "style": {
      "marginTop": 12,
      "marginBottom": 12
    }
  },
  "zones": [
    {
      "id": "main",
      "elements": [
        { "type": "h2", "content": "Main Area" },
        { "type": "p", "content": "Body text in the left zone." }
      ]
    },
    {
      "id": "sidebar",
      "elements": [
        { "type": "sidebar-label", "content": "SIDEBAR" },
        { "type": "sidebar-body", "content": "Sidebar content." }
      ]
    }
  ]
}
interface ZoneLayoutOptions {
    columns?: TableColumnSizing[];
    gap?: number;
    frameOverflow?: 'move-whole' | 'continue';
    worldBehavior?: 'fixed' | 'spanning' | 'expandable';
}

interface ZoneDefinition {
    id?: string;
    elements: Element[];
    style?: Record<string, any>;
}

zones[] entries are region descriptors, not DOM children.

frameOverflow makes the zone field lifecycle explicit:

worldBehavior makes the authored world rule explicit:

frameOverflow and worldBehavior are related but not the same thing.

For now:


12. Drop Cap (element.dropCap)

interface DropCapSpec {
    enabled?: boolean;
    lines?: number;
    characters?: number;
    gap?: number;
    characterStyle?: Partial<ElementStyle>;
}

13. Story Placement (element.placement)

Declared on direct children of a story element to float or absolutely position them relative to the story’s content area.

interface StoryLayoutDirective {
    mode: 'float' | 'story-absolute';
    x?: number;
    y?: number;
    align?: 'left' | 'right' | 'center';
    wrap?: 'around' | 'top-bottom' | 'none';
    gap?: number;
}

Any block element can float or use story-absolute if it carries explicit obstacle size through style width and height. Images may omit explicit size and derive it from intrinsic image dimensions.


13a. Column Span (element.columnSpan)

Declared on children of a multi-column story. A spanned element breaks the column flow, is laid out at full story width, then flow resumes below.

columnSpan?: 'all' | number

14. Simulation Continuation (properties.simulationContinuation)

Controls marker elements inserted automatically around page splits.


15. Page Regions

interface PageRegionDefinition {
    default?: PageRegionContent | null;
    firstPage?: PageRegionContent | null;
    odd?: PageRegionContent | null;
    even?: PageRegionContent | null;
}

interface PageRegionContent {
    elements: Element[];
    style?: Partial<ElementStyle>;
}

"{pageNumber}", "{physicalPageNumber}", and "{totalPages}" tokens inside content are substituted during finalization.


16. Nesting Rules

Parent type Valid children
story Any block Element. Direct children may carry placement.
table table-row only.
table-row table-cell only.
table-cell Either content or inline children.
paragraph-like Inline children: text, inline, image, inline-box.
page region Any Element.

17. Minimal Example

{
  "documentVersion": "1.1",
  "layout": {
    "pageSize": "LETTER",
    "margins": { "top": 72, "right": 72, "bottom": 72, "left": 72 },
    "fontFamily": "Helvetica",
    "fontSize": 12,
    "lineHeight": 1.4
  },
  "styles": {
    "h1": { "fontSize": 24, "fontWeight": "bold", "marginBottom": 12, "keepWithNext": true },
    "paragraph": { "marginBottom": 10, "allowLineSplit": true, "orphans": 2, "widows": 2 }
  },
  "elements": [
    { "type": "h1", "content": "Title" },
    {
      "type": "paragraph",
      "content": "",
      "children": [
        { "type": "text", "content": "Plain text, then " },
        { "type": "text", "content": "bold", "properties": { "style": { "fontWeight": "bold" } } },
        { "type": "text", "content": " and " },
        {
          "type": "inline",
          "content": "",
          "properties": { "style": { "fontStyle": "italic", "color": "#333" } },
          "children": [{ "type": "text", "content": "italic" }]
        },
        { "type": "text", "content": "." }
      ]
    }
  ]
}

Table Example

{
  "type": "table",
  "content": "",
  "table": {
    "headerRows": 1,
    "repeatHeader": true,
    "columns": [
      { "mode": "flex", "fr": 2 },
      { "mode": "flex", "fr": 1 },
      { "mode": "fixed", "value": 60 }
    ]
  },
  "children": [
    {
      "type": "table-row",
      "content": "",
      "properties": { "semanticRole": "header" },
      "children": [
        { "type": "table-cell", "content": "Name" },
        { "type": "table-cell", "content": "Status" },
        { "type": "table-cell", "content": "Score" }
      ]
    }
  ]
}

Story / Float Example

{
  "type": "story",
  "columns": 2,
  "gutter": 18,
  "children": [
    {
      "type": "image",
      "content": "",
      "image": { "data": "<base64>", "mimeType": "image/png", "fit": "contain" },
      "properties": {
        "layout": { "mode": "float", "align": "right", "wrap": "around", "gap": 8 },
        "style": { "width": 120, "height": 90 }
      }
    },
    { "type": "paragraph", "content": "Text flows around the floated image." }
  ]
}

18. Key Source Files

What Where
Type definitions engine/src/engine/types.ts
AST normalization engine/src/engine/document.ts
Spatial fixture normalization helper engine/tests/harness/spatialize.ts
Architecture and Runtime Internals ENGINE-INTERNALS.md
Scripting API scripting.html
Overlay system overlay.html
Standard fonts standard-fonts.html
Testing guide TESTING.md
Markdown compilation core transmuters/markdown-core/src/index.ts
Transmuters transmuters/
Regression fixtures engine/tests/fixtures