# md-to-docx — Syntax & Configuration Reference The complete reference for authoring documents with `md-to-docx`: the Markdown subset it understands, the HTML‑comment **directive** system, and every **configuration** key. --- ## Table of contents - [1. Overview](#1-overview) - [2. Markdown syntax](#2-markdown-syntax) - [2.1 Headings](#21-headings) - [2.2 Inline formatting](#22-inline-formatting) - [2.3 Links](#23-links) - [2.4 Line breaks](#24-line-breaks) - [2.5 Lists](#25-lists) - [2.6 Tables](#26-tables) - [2.7 Code blocks](#27-code-blocks) - [2.8 Mermaid diagrams](#28-mermaid-diagrams) - [2.9 Images](#29-images) - [2.10 Horizontal rules](#210-horizontal-rules) - [2.11 Page breaks](#211-page-breaks) - [2.12 Comments](#212-comments) - [2.13 Blockquotes](#213-blockquotes) - [3. Directives (`@`-comments)](#3-directives--comments) - [3.1 Grammar](#31-grammar) - [3.2 `@config` / `@doc` — document configuration](#32-config--doc--document-configuration) - [3.3 `@style` — inline run styling](#33-style--inline-run-styling) - [3.4 `@header` / `@footer` — running header & footer](#34-header--footer--running-header--footer) - [3.5 `@pagebreak`](#35-pagebreak) - [3.6 Variables](#36-variables) - [4. Configuration reference](#4-configuration-reference) - [4.1 Loading & precedence](#41-loading--precedence) - [4.2 Units & value formats](#42-units--value-formats) - [4.3 Keys](#43-keys) - [4.4 Complete annotated example](#44-complete-annotated-example) - [5. Programmatic configuration](#5-programmatic-configuration) --- ## 1. Overview A `md-to-docx` source file has two layers: 1. **Markdown content** — a hand‑rolled, line‑oriented subset of Markdown (headings, lists, tables, code, images, …) described in [§2](#2-markdown-syntax). 2. **Directives** — HTML comments that begin with an `@` sigil (``, ``, …). They are invisible in every Markdown viewer but instruct the converter. See [§3](#3-directives--comments). There is **no YAML frontmatter** (`---`). All document configuration lives in `@config` directives — see [§4](#4-configuration-reference). --- ## 2. Markdown syntax > The parser is line‑based, not full CommonMark. Each line is classified on its > own; consecutive paragraph lines do **not** merge into one paragraph. ### 2.1 Headings ```markdown # Heading 1 ## Heading 2 ### Heading 3 … up to ###### Heading 6 ``` Levels `H1`–`H6` map to Word heading styles (configurable per level — see [`heading`](#heading)). Every heading also becomes an internal‑link target ([§2.3](#23-links)). ### 2.2 Inline formatting | Syntax | Result | |:-------|:-------| | `**bold**` or `__bold__` | **bold** | | `*italic*` or `_italic_` | *italic* | | `` `inline code` `` | monospace run (styled via [`inline_code`](#inline_code)) | `_`/`__` are not matched mid‑word, so `my_var_name` stays literal. Markers may nest, e.g. `**bold with `code` inside**`. ### 2.3 Links **External:** ```markdown [google.com](https://google.com) ``` **Internal** (link to a heading by its slug): ```markdown See the [Architecture Overview](#architecture-overview). ``` Slugs are GitHub‑style: lowercased, punctuation removed, spaces → `-`, and Unicode letters (including Vietnamese diacritics) preserved. Duplicate headings get `-1`, `-2`, … suffixes (a second `## Notes` → `#notes-1`). An unresolved internal link renders as plain text and emits a `link` warning. ### 2.4 Line breaks Use `
` (or `
`) to force a line break inside one block — most useful in table cells, where a literal newline cannot exist: ```markdown | Case | Description | | ------ | ------------------------------------ | | Case A | Line 1
Line 2
**Bold line 3** | ``` Inline markdown is still parsed within each segment, and `

` yields two consecutive breaks. A line that is *only* `
` (outside a table) is treated as a blank paragraph. `
` also works **inside** an `@style` span ([§3.3](#33-style--inline-run-styling)) — e.g. a multi-line styled block can put `
` on its own line to add blank lines while keeping the style: ```markdown A right-aligned, red note

``` ### 2.5 Lists ```markdown - Top level - Nested one - Nested two - Back to top this wrapped line is lazily joined onto the item above 1. First 2. Second ``` - **Nesting** is derived from relative indentation, so both 2‑space and 4‑space schemes work. Bullet depth is capped at three levels (configurable glyphs via [`list.bullets`](#list)). - **Lazy continuation:** a plain line directly after a list item is appended to that item on a new line (the newline becomes a hard line break, not a space). - **Numbered lists restart at 1.** Any real content block (paragraph, heading, table, …) between two numbered lists starts a fresh sequence; blank lines and nested bullets do not break it. ### 2.6 Tables ```markdown | Left | Center | Right | |:-------|:------:|------:| | a | b | c | ``` - The alignment row (`:---`, `:--:`, `--:`) sets per‑column alignment. - Rows alternate fill colors; header and rows are fully styled via [`table`](#table). - Cells support inline markdown, `
` ([§2.4](#24-line-breaks)), internal links, and `@style` ([§3.3](#33-style--inline-run-styling)). Escape a literal pipe as `\|`. - Column widths are auto‑sized from content. ### 2.7 Code blocks ````markdown ```js console.log('hello'); ``` ```` Fenced blocks render in a shaded box with a language label chip (when `code.label.show` is on). Styling via [`code`](#code). A fence with no language is rendered without a label. HTML comments inside a code block are preserved literally. ### 2.8 Mermaid diagrams ````markdown ```mermaid graph TD A[Start] --> B{Decision} B -->|Yes| C[Do it] B -->|No| D[Skip] ``` ```` Mermaid blocks are rendered to PNG and embedded as images. Text is normalized so every diagram shows at roughly `mermaid.font_size` pt for a consistent look. A diagram taller than one page is shrunk to fit (down to `mermaid.min_font_pt`); the `--split-tall-mermaid` CLI flag slices it into page‑height images at full size instead. If rendering is unavailable, the block falls back to a plain code block and a `mermaid` warning is emitted. Tuning keys: [`mermaid`](#mermaid). ### 2.9 Images ```markdown ![Caption](./images/chart.png) ![Caption](./images/chart.png =600x400) ![Caption](./images/chart.png){width=600 height=400} ``` - Size override: `=WxH` (either dimension may be omitted, e.g. `=600x`) or `{width=… height=…}`. - Paths resolve relative to the Markdown file (CLI) or the `baseDir` option (API). - With `image.caption: true` (default), the alt text renders as an italic, centered caption below the image. An unreadable image degrades to an empty paragraph plus an `image` warning. ### 2.10 Horizontal rules ```markdown --- ``` Three or more `-`, `*`, or `_` on their own line render as a bottom border rule. ### 2.11 Page breaks Any of these force a page break: ```markdown ← preferred ([§3.5](#35-pagebreak))
page-break-after: always ``` ### 2.12 Comments An HTML comment whose line **starts** with ` ``` The closing `-->` line is dropped in full, so don't put visible text after it on the same line. A comment that begins **mid‑line** (`text `) is not stripped and renders literally — except a paired `@style` comment, which is interpreted ([§3.3](#33-style--inline-run-styling)). ### 2.13 Blockquotes Lines beginning with `>` form a blockquote: ```markdown > **Key insight:** the buffer lives at a low address while the > return address sits higher up. > > A second paragraph, separated by a blank quote line. ``` Consecutive `>` lines are joined into one paragraph; a `>` line with nothing after it starts a new paragraph within the same quote. The block ends at the first line that does not start with `>`. Inline formatting (`**bold**`, links, `@style`, …) works inside. By default a quote renders as indented italic text; the [`quote.*`](#quote) keys can add a left bar or shaded box. --- ## 3. Directives (`@`-comments) ### 3.1 Grammar A comment whose inner text starts with the `@` sigil is a **directive**. Plain comments (no `@`) are dropped ([§2.12](#212-comments)). Two body shapes are accepted: | Shape | Looks like | Used by | |:------|:-----------|:--------| | **Inline args** (one line) | `` | `@style`, single‑line `@header`/`@footer` | | **Block YAML** (multi‑line) | `` | `@config`, `@doc`, block `@header`/`@footer` | Inline‑argument tokens: | Token | Meaning | |:------|:--------| | `flag` | boolean `true` (e.g. `bold`) | | `key=value` | value without spaces (e.g. `color=red`, `size=9`) | | `key="value"` | quoted value with spaces (e.g. `font="Times New Roman"`) | Directive summary: | Directive | Shape | Purpose | Section | |:----------|:------|:--------|:--------| | `@config` | block | document configuration | [3.2](#32-config--doc--document-configuration) | | `@doc` | block | document metadata → `doc.*` variables | [3.2](#32-config--doc--document-configuration) | | `@style … /style` | inline, paired, or self‑close `/-->` | style a text run (or align a paragraph) | [3.3](#33-style--inline-run-styling) | | `@header` / `@footer` | inline or block | running header / footer | [3.4](#34-header--footer--running-header--footer) | | `@pagebreak` | inline | page break | [3.5](#35-pagebreak) | Variable references (`{doc.title}`, `{vars.x}`, `{date}`, …) work anywhere — see [§3.6](#36-variables). ### 3.2 `@config` / `@doc` — document configuration `@config` carries the document configuration. Its body is mini‑YAML, parsed by the same parser the old `---` frontmatter used. Place it anywhere (conventionally at the top); multiple blocks deep‑merge in document order (later wins). ```markdown ``` `@doc` holds document **metadata**; its body populates the `doc.*` variable namespace ([§3.6](#36-variables)) rather than styling config: ```markdown ``` Reference it anywhere as `{doc.title}`, `{doc.subtitle}`, … The full set of styling keys is in [§4](#4-configuration-reference). > **Cover pages** are not a directive. Write one as ordinary body content — e.g. > a centered `@style` title ([§3.3](#33-style--inline-run-styling)) — followed by a `@pagebreak`, and use > `skip_on_first_page` ([§3.4](#34-header--footer--running-header--footer)) to keep > the header/footer off it. ### 3.3 `@style` — inline run styling Wrap text in a paired `@style … /style` to style just that run. Markdown inside is still parsed, and it works inside table cells. ```markdown This is very important text, with a highlighted note, and H2O. ``` Closing tag: `` (or ``). **Three forms** — pick whichever reads best: ```markdown A wrapped run here. OPS_20260309_134 — a long centered title ): styles the REST OF THE LINE, no close tag needed --> Price: 1,000,000 VND OPS_20260309_134 SHOPEEPAY/ZALOPAY — long title on its own line ``` | Argument | Effect | Notes | |:---------|:-------|:------| | `color=…` | text color | hex `#cc0000` / `cc0000` / `abc`, or a [named color](#named-colors) | | `bg=…` | background fill | same color formats | | `highlight=…` | Word text highlight | a [Word highlight name](#word-highlight-names) (e.g. `yellow`) | | `bold` | bold | flag | | `italic` | italic | flag | | `underline` | single underline | flag | | `strike` | strikethrough | flag | | `sup` | superscript | flag | | `sub` | subscript | flag | | `font="…"` | font family | quote if it contains spaces | | `size=N` | font size in pt | | | `align=center` \| `right` \| `left` \| `justify` | **paragraph** alignment | only when the `@style` *starts the line* — centers/right‑aligns/justifies the whole paragraph. Combine with `size`/`bold` to make a title without using a heading. Overrides the document‑wide `body.align` default ([§7](#7-config-reference)). | Most args style the text *run*; **`align` is the exception** — it aligns the whole paragraph, so it only takes effect when the `@style` (wrapping or self‑close) is the first thing on the line. Used elsewhere it is ignored. ```markdown Document Title ``` Invalid values (bad color, non‑numeric size) are skipped, and a `style` warning is emitted. An unclosed wrapping `@style` renders literally (a self‑close `/-->` never needs a close). **Named colors:** `black`, `white`, `red`, `green`, `blue`, `yellow`, `orange`, `purple`, `gray`/`grey`, `silver`, `maroon`, `olive`, `lime`, `aqua`/`cyan`, `teal`, `navy`, `fuchsia`/`magenta`, `pink`. Any other color must be hex. **Word highlight names:** `yellow`, `green`, `cyan`, `magenta`, `blue`, `red`, `darkBlue`, `darkCyan`, `darkGreen`, `darkMagenta`, `darkRed`, `darkYellow`, `darkGray`, `lightGray`, `black`. (These are Word's fixed highlight palette; unlike `color`/`bg`, highlight does not accept arbitrary hex.) ### 3.4 `@header` / `@footer` — running header & footer A running header/footer has three zones — `left`, `center`, `right` — laid out with tab stops, and may embed tokens. Inline form: ```markdown ``` Block form (recommended when you set several options): ```markdown ``` **Tokens.** Zones may embed any [variable](#36-variables) (`{doc.*}`, `{vars.*}`, `{date}`, …). Two are page‑number **fields**, meaningful only here (in body text they stay literal): | Token | Expands to | |:------|:-----------| | `{page}` | current page number | | `{pages}` | total page count | **Options:** | Key | Type | Default | Description | |:----|:-----|:--------|:------------| | `left` / `center` / `right` | string | — | zone content (may contain tokens) | | `font` | string | `body.font` | font family | | `size` | number (pt) | `body.size` | font size | | `color` | hex | `body.color` | text color (6‑digit hex, no `#`; quote in YAML to keep leading zeros) | | `border_top` | bool | `false` | rule above the line (footer) | | `border_bottom` | bool | `false` | rule below the line (header) | | `skip_on_first_page` | bool \| int | `false` | leave the first page(s) un‑numbered. `true` (=`1`) blanks page 1 via a Word title‑page section. An integer **N** splits the doc into its own section after the **N‑th `@pagebreak`**: the leading section gets no header/footer at all (incl. borders) and the body section **restarts page numbering at 1**. N counts page‑break‑delimited segments, not rendered pages — set N on `@header`/`@footer`; the larger of the two wins and applies to both. With fewer than N page breaks it falls back to blanking page 1 only (and warns). | | `page_number` | bool | `false` | shorthand: when set with no zones, places `{page}` in the right zone | A `@footer` directive overrides the legacy [`footer.page_number`](#footer) config. ### 3.5 `@pagebreak` A readable alias for a page break: ```markdown ``` ### 3.6 Variables Declare variables once and reference them as `{path}` **anywhere** in the document — body text, headings, list items, table cells, and header/footer zones. **Declaring.** Two namespaces, both written in `@doc`/`@config`: ```markdown ``` | Namespace | Source | Reference | |:----------|:-------|:----------| | `doc.*` | the `@doc` body (and any `doc:` section in `@config`) | `{doc.title}`, `{doc.subtitle}`, `{doc.author}`, … | | `vars.*` | the `vars:` section of `@config` | `{vars.version}`, `{vars.org}`, … | Keys are arbitrary and may nest (`{doc.client.name}` from a nested `client:` map). **Built‑in variables:** | Variable | Value | |:---------|:------| | `{date}` | build date, `YYYY-MM-DD` | | `{now}` | alias of `{date}` | **Reserved fields:** `{page}` and `{pages}` are page‑number fields — they resolve only in a header/footer ([§3.4](#34-header--footer--running-header--footer)); elsewhere they stay literal. **Referencing:** ```markdown # {doc.title} Version {vars.version}, prepared by {doc.author} on {date}. ``` **Resolution rules:** - A `{path}` that resolves to a scalar is replaced by its value; markdown around it (e.g. `*{doc.title}*`) still applies. - An **unknown** variable is left literal and emits a `var` warning — useful for catching typos. (So a literal `{word}` in prose will warn; wrap it in inline code to silence it.) - Variables are **not** expanded inside inline code (`` `{doc.title}` ``) or fenced code blocks — code is verbatim. --- ## 4. Configuration reference ### 4.1 Loading & precedence Configuration comes from three layers, **highest priority first**: 1. **In‑document `@config` / `@doc`** directives. 2. **Programmatic overrides** — the `config` option of the API ([§5](#5-programmatic-configuration)). 3. **Built‑in defaults.** Note the deliberate precedence: **in‑document config wins over programmatic overrides**, so a document can always pin its own look. ### 4.2 Units & value formats | Kind | Format | Example | |:-----|:-------|:--------| | Length | centimetres | `margin: 2.5` | | Font size / spacing | points (pt) | `size: 11`, `space_before: 20` | | Line spacing | multiple of a line | `line_spacing: 1.5` | | Color | 6‑digit hex, **no** `#` | `color: 1F272E` | | Boolean | `true` / `false` | `bold: true` | > Quote hex colors that are all digits or have leading zeros (`color: "008000"`) > so the YAML parser keeps them as strings. The `@style` directive — but **not** > `@config` — additionally accepts `#`‑prefixed and named colors. ### 4.3 Keys #### `title` | Key | Type | Default | Description | |:----|:-----|:--------|:------------| | `title` | string | `""` | Legacy document‑title config key; not rendered. Prefer declaring the title in `@doc` and referencing it as `{doc.title}` ([§3.6](#36-variables)). | #### `page` | Key | Type | Default | Description | |:----|:-----|:--------|:------------| | `page.size` | `A4` \| `Letter` | `A4` | Paper size. | | `page.margin` | number (cm) or map | `2` | All‑sides margin, or a map `{ top, right, bottom, left }` for per‑side margins. | #### `body` | Key | Type | Default | Description | |:----|:-----|:--------|:------------| | `body.font` | string | `Arial` | Default body font. | | `body.size` | number (pt) | `11` | Default body font size. | | `body.color` | hex | `1F272E` | Default text color. | | `body.line_spacing` | number (×) | `1.5` | Line spacing (a multiple: `1` = single, `1.5`, `2` = double). Also the document‑wide default for any block without its own. | | `body.space_before` | number (pt) | `0` | Space before each paragraph. | | `body.space_after` | number (pt) | `6` | Space after each paragraph. | > **Spacing keys are uniform across block types.** `body`, `heading` (+ each `h1`…`h6`), > `list`, `quote`, `table`, and `code` all accept `line_spacing`, `space_before`, and > `space_after`. `code` and `table` default to `line_spacing: 1.0` (tight); the rest default > to `1.5`. #### `heading` `heading.font` (default `Arial`) sets the font for all headings. `heading.line_spacing` (number, default `1.5`) sets the line spacing for **all** heading levels at once; a per‑level `h{N}.line_spacing` overrides it for that level. `heading.skip_blank_after` (bool, default `true`) drops the single blank line you conventionally leave right after a heading, so it doesn't render as an extra empty paragraph below the heading. Only the *first* blank line is dropped — a second blank line is kept, so you can still add deliberate spacing. Set it to `false` to keep the blank line. (Note: the blank line right after a `@pagebreak` is *always* dropped, independent of this key.) Each level `h1`…`h6` accepts the same sub‑keys: | Sub‑key | Type | Description | |:--------|:-----|:------------| | `size` | number (pt) | font size | | `bold` | bool | bold | | `italic` | bool | italic | | `color` | hex | text color | | `line_spacing` | number (×) | line spacing (overrides `heading.line_spacing`) | | `space_before` | number (pt) | space before | | `space_after` | number (pt) | space after | | `align` | `null` \| `center` \| `right` | alignment (`null` = left) | Per‑level defaults (all levels: `line_spacing` `1.5`): | Level | size | bold | italic | color | space_before | space_after | |:------|----:|:----:|:------:|:------|------:|-----:| | h1 | 20 | ✓ | — | `1F272E` | 20 | 8 | | h2 | 16 | ✓ | — | `1F272E` | 16 | 7 | | h3 | 13 | ✓ | — | `1F272E` | 13 | 6 | | h4 | 11 | ✓ | — | `1F272E` | 10 | 5 | | h5 | 11 | ✓ | ✓ | `1F272E` | 8 | 4 | | h6 | 10 | — | ✓ | `555555` | 6 | 3 | **Heading numbering** — `heading.numbering` turns on native Word multilevel numbering (`1`, `1.1`, `1.2.3` …). The numbers are *live* Word fields: they renumber automatically if you add/move/delete headings in Word, and the prefix inherits each heading's own size/bold/colour. It is **off by default**. | Sub‑key | Type | Default | Description | |:--------|:-----|:--------|:------------| | `enabled` | bool | `false` | turn heading numbering on | | `from` | number (1–6) | `1` | shallowest level to number | | `to` | number (1–6) | `3` | deepest level to number | | `trailing_dot` | bool | `true` | `1.2.3.` instead of `1.2.3` | | `separator` | `space` \| `tab` \| `none` | `space` | gap between the number and the heading text | ```yaml heading: numbering: enabled: true from: 1 to: 3 ``` Headings outside the `from`…`to` range are left unnumbered. Numbering is **continuous across the whole document** and follows Word's rules, so deeper counters reset under each shallower heading. If the document *skips* a level inside the numbered range (e.g. an `h1` followed directly by an `h3`), Word will show gaps like `1.0.1`; the converter emits a `heading-numbering` warning so the structural jump isn't silent. An out‑of‑range `from`/`to` is clamped (with a warning), and `from > to` disables numbering (with a warning). #### `table` | Key | Type | Default | Description | |:----|:-----|:--------|:------------| | `table.header.fill` | hex | *(none)* | Header row fill; empty = no fill. | | `table.header.color` | hex | `1F272E` | Header text color. | | `table.header.bold` | bool | `true` | Header bold. | | `table.header.size` | number (pt) | `10` | Header font size. | | `table.row.odd_fill` | hex | `F0F4F8` | Odd‑row fill. | | `table.row.even_fill` | hex | `FFFFFF` | Even‑row fill. | | `table.row.color` | hex | `1F272E` | Row text color. | | `table.row.size` | number (pt) | `10` | Row font size. | | `table.border` | hex | `C0C8D0` | Border color. | | `table.border_size` | number | `4` | Border thickness (⅛ pt). | | `table.cell_padding` | number (cm) | `0.15` | Cell padding. | | `table.line_spacing` | number (×) | `1.0` | Cell line spacing (tight by default). | | `table.space_before` | number (pt) | `0` | Space before cell paragraphs. | | `table.space_after` | number (pt) | `0` | Space after cell paragraphs. | #### `quote` Styling for blockquotes ([§2.13](#213-blockquotes)). Defaults to indented italic text; set `quote.border.color` for a left bar or `quote.fill` for a shaded box. | Key | Type | Default | Description | |:----|:-----|:--------|:------------| | `quote.color` | hex | `1F272E` | Quote text color. | | `quote.italic` | bool | `true` | Render the quote italic. | | `quote.indent` | number (cm) | `0.63` | Left indent. | | `quote.line_spacing` | number (×) | `1.5` | Line spacing. | | `quote.space_before` | number (pt) | `0` | Space before each quote paragraph. | | `quote.space_after` | number (pt) | `6` | Space after each quote paragraph. | | `quote.border.color` | hex | *(none)* | Left bar color; empty = no bar. | | `quote.border.size` | number | `24` | Left bar thickness (⅛ pt). | | `quote.fill` | hex | *(none)* | Background fill; empty = none. | #### `code` | Key | Type | Default | Description | |:----|:-----|:--------|:------------| | `code.font` | string | `Courier New` | Code font. | | `code.size` | number (pt) | `9` | Code font size. | | `code.color` | hex | `333333` | Code text color. | | `code.fill` | hex | `F3F4F5` | Code block background. | | `code.indent` | number (cm) | `0.63` | Left/right indent of the block. | | `code.line_spacing` | number (×) | `1.0` | Line spacing (tight by default). | | `code.space_before` | number (pt) | `0` | Space before the block. | | `code.space_after` | number (pt) | `0` | Space after the block. | | `code.label.show` | bool | `true` | Show the language label chip. | | `code.label.fill` | hex | `E8E8E8` | Label background. | | `code.label.color` | hex | `666666` | Label text color. | | `code.label.size` | number (pt) | `8` | Label font size. | #### `inline_code` | Key | Type | Default | Description | |:----|:-----|:--------|:------------| | `inline_code.font` | string | `Courier New` | Inline‑code font. | | `inline_code.size` | number (pt) | `0` | Font size; `0` = follow `body.size`. | | `inline_code.color` | hex | `555555` | Inline‑code text color. | #### `mermaid` | Key | Type | Default | Description | |:----|:-----|:--------|:------------| | `mermaid.render_scale` | number | `2` | Render PNG at N× resolution (`mmdc -s`). | | `mermaid.base_font_px` | number (px) | `16` | Mermaid's base font at scale 1. | | `mermaid.font_size` | number (pt) | `9.5` | Target on‑page text size for every diagram; `0` = follow `body.size`. | | `mermaid.min_font_pt` | number (pt) | `7.5` | Floor when shrinking a tall diagram. | | `mermaid.fit_page` | bool | `true` | Shrink a too‑tall diagram to fit one page. | | `mermaid.fit_tolerance` | number | `0.06` | Slack before slicing (absorbs render jitter). | The styling of mermaid **source text** shown by `--keep-mermaid-text` is set by `mermaid_code.{font,size,color,fill}` (defaults: `Courier New`, `7`, `555555`, `F3F4F5`). #### `image` | Key | Type | Default | Description | |:----|:-----|:--------|:------------| | `image.caption` | bool | `true` | Render the alt text as an italic caption below the image. | #### `footer` Legacy page‑number footer. For richer control use the `@footer` directive ([§3.4](#34-header--footer--running-header--footer)), which overrides this. | Key | Type | Default | Description | |:----|:-----|:--------|:------------| | `footer.page_number` | bool | `false` | Show a right‑aligned page number. | | `footer.font` | string | `body.font` | Footer font. | | `footer.size` | number (pt) | `body.size` | Footer font size. | | `footer.color` | hex | `body.color` | Footer text color. | #### `list` | Key | Type | Default | Description | |:----|:-----|:--------|:------------| | `list.indent` | number (cm) | `0.63` | Indent per nesting level. | | `list.bullets` | string[] | `["•", "◦", "▪"]` | Bullet glyphs by depth. | | `list.line_spacing` | number (×) | `1.5` | Line spacing for list items. | | `list.space_before` | number (pt) | `0` | Space before each item. | | `list.space_after` | number (pt) | `2` | Space after each item. | #### `link` | Key | Type | Default | Description | |:----|:-----|:--------|:------------| | `link.color` | hex | `0563C1` | Hyperlink color. | #### `output` | Key | Type | Default | Description | |:----|:-----|:--------|:------------| | `output.filename` | string | `""` | Output filename without extension (CLI). Empty = use the input filename. | ### 4.4 Complete annotated example ```markdown # Document starts here ``` > The config parser is a minimal YAML subset: **indented** nested maps and `- ` > list items only — inline `{ … }` maps and `[ … ]` arrays are not supported. For > per‑side page margins, nest them: > > ```markdown > page: > margin: > top: 2 > right: 1.5 > bottom: 2 > left: 1.5 > ``` --- ## 5. Programmatic configuration The API accepts a `config` object of the same keys (nested), at lower priority than in‑document `@config`: ```js import { convert } from '@luytbq43/md-to-docx'; const { buffer, warnings, meta } = await convert(markdown, { baseDir: '/path/to/assets', config: { body: { font: 'Times New Roman', size: 12 }, footer: { page_number: true }, }, }); ``` See the [README](../README.md#programmatic-api) for the full API surface (`convert`, `convertFile`, `warnings`, `meta`).