# Generating DOCX

Build LitAST with hand-written nodes or the docx authoring helpers, then produce complete DOCX bytes with generateDocx; includes unit conventions.

```ts
generateDocx(ast: LitAstNode): Promise<Uint8Array>
```

`generateDocx` takes no other arguments. It builds a whole DOCX package from the LitAST and returns the bytes of a complete file; it never patches an existing file in place.

## Writing the bytes

```ts
// Node
await writeFile("out.docx", await generateDocx(ast));

// Browser: create a download link
const bytes = await generateDocx(ast);
const url = URL.createObjectURL(new Blob([bytes], {
  type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
}));
```

## Hand-written nodes

LitAST nodes are plain objects, and the root is a `document`:

```ts
const ast = {
  type: "document",
  children: [
    {
      type: "paragraph",
      children: [
        { type: "text", text: "Hello " },
        { type: "text", text: "world", marks: [{ type: "bold" }] },
      ],
    },
  ],
};
```

Generating this document and reading it back with `parseDocx` yields exactly the same `children`.

## Using the `docx` helpers

`docx` is a grouped helper object. It returns nodes like the ones above, only shorter to write and type-checked:

| Group | Contents |
| --- | --- |
| `docx.blocks` | `paragraph`, `heading`, `blockquote`, `codeBlock`, `pageBreak`, `callout` |
| `docx.inline` | `text`, `link`, `ruby` |
| `docx.marks` | `bold`, `highlight`, `link`, `textStyle` |
| `docx.tables` | `table`, `row`, `cell`, `shadedCell` |
| `docx.styles` | `paragraphProperties`, `spacing`, `indentation`, `shading`, `border`, `runProperties` |

The helpers do not cover every node. The `document` root, `image`, `section` and `list` are written by hand, and the two styles mix freely. Marks without a helper, such as italic, are written as `{ type: "italic" }`.

```ts
docx.blocks.paragraph({
  children: [
    docx.inline.text("Important", { marks: [docx.marks.bold(), docx.marks.textStyle({ color: "C00000", fontSize: "14pt" })] }),
    docx.inline.text(": see "),
    docx.inline.link("the notes", { href: "https://example.com" }),
  ],
  properties: docx.styles.paragraphProperties({
    textAlign: "center",
    spacing: docx.styles.spacing({ after: 120 }),
  }),
});
```

## Phonetic guides

`docx.inline.ruby` produces native `w:ruby`. `base` is the body text and `annotation` is the
phonetic text; each layer can carry its own formatting. The caller supplies the pronunciation;
lit-docx does not include an automatic pinyin dictionary.

```ts
const guide = docx.inline.ruby({
  marks: [docx.marks.textStyle({ fontSize: "12pt" })],
  base: [docx.inline.text("汉", {
    marks: [docx.marks.textStyle({ fontSize: "12pt" })],
  })],
  annotation: [docx.inline.text("hàn", {
    marks: [docx.marks.textStyle({ fontFamily: "Arial", fontSize: "6pt" })],
  })],
  rubyAlign: "center",
  hps: 12,
  hpsRaise: 24,
  hpsBaseText: 24,
  lid: "zh-CN",
});
const bytes = await generateDocx({
  type: "document",
  children: [docx.blocks.paragraph({ children: [guide] })],
});
```

All five properties are required when authoring. Numeric `hps`, `hpsRaise` and `hpsBaseText`
values are half-points. `hpsRaise` is the stored raise, **not** the distance shown in Word's
Phonetic Guide dialog. This example follows a Word 16.109.2 sample with 12pt base text and
6pt annotation; it is not an automatic spacing algorithm for arbitrary fonts.
Keep the outer `marks` size, base-text size and `hpsBaseText` consistent; the reference size does not replace run formatting.
Alignment values are `center`, `left`, `right`, `distributeLetter`, `distributeSpace` and `rightVertical`.

Parsed nodes store base text in `children` and annotation in `attrs.rubyText`. Edit those
nodes or properties before generating again. Unchanged or unmodeled source properties
follow the [preservation rules](/en/guide/round-trip#phonetic-guides). Place a hyperlink in the
complete ruby node's `marks`; do not put `docx.inline.link` directly in either text layer.

## Tables

Tables nest as `table > tableRow > tableCell > paragraph`. A cell needs at least one block; cells created with the helper get an empty paragraph automatically.

```ts
docx.tables.table({
  columns: [3000, 3000],
  rows: [
    docx.tables.row({ isHeader: true, cells: [docx.tables.cell({ children: [docx.blocks.paragraph({ children: [docx.inline.text("Region")] })] })] }),
  ],
});
```

## Images, sections, headers and footers

- **Images**: `{ type: "image", attrs: { src, width, height } }`. `src` is the image data URL; width and height are in pixels at 96 DPI.
- **Sections**: a `section` is a sibling inside `document.children`. It ends the section formed by the content **before** it and carries that section's page setup.
- **Headers and footers**: children of a `section`. A `header` uses `attrs.headerType` and a `footer` uses `attrs.footerType`, each `"default"`, `"first"` or `"even"`.

The report in [Quick start](/en) uses all three.

## Units

| Value | Unit |
| --- | --- |
| Page size, margins, paragraph indents, spacing before and after, table column widths | twips, 1/20 of a point; 1 inch = 1440, 1 cm ≈ 567 |
| Image width and height | pixels at 96 DPI |
| `fontSize` in `textStyle` | a string such as `"12pt"`, or a number of points |
| Ruby `hps`, `hpsRaise`, `hpsBaseText` | Numeric half-points, or preserved explicit OOXML measurement strings |
| Colors | 6-digit hex RGB; `"C00000"` and `"#C00000"` both work; parsed results include `#` |

A4 is `pageWidth: 11906, pageHeight: 16838`; US Letter is `12240 × 15840`.

## Parts added for new documents

A document built from scratch has no original `styles.xml`, `numbering.xml` and similar parts, so the generator adds the numbering, comment and note parts the content actually uses. LitAST read from an existing file writes back the original parts kept in `ast.meta.rawXmlFiles`; see [Round trips and preservation](/en/guide/round-trip).
