Write mdhtml documents as Word docx files.
mdhtml2docx consumes mdhtml's semantic Document API. The shared importer handles HTML5 parsing, raw-data decoding, and document structure. The shared reference index validates and plans references. oxml provides WordprocessingML construction, reference editing, DOCX packaging, and atomic saving. XML construction uses namespace-bound factories such as e.tcW(type='dxa', w=2400), backed by fastcore's XML builder. Conversion requires neither lxml nor a .NET or Office runtime.
MDHTML accepts the full HTML vocabulary. This exporter supports the elements and annotations listed below. It preserves the text of unknown inline elements and recurses through block containers. HTML parsing and repair belong to mdhtml.
from mdhtml import parse_md
from mdhtml2docx import mdhtml2docx
document = parse_md(markdown_text)
warnings = mdhtml2docx(document, 'out.docx')mdhtml2docx accepts a semantic Document or symbolic MDHTML, writes a docx, and returns warning strings. Normal HTML5 repair applies when importing MDHTML; the input need not be well-formed XML.
Import existing MDHTML through the same document API:
from mdhtml import Document
document = Document.from_mdhtml(html)
warnings = mdhtml2docx(document, 'out.docx')The importer groups body-position text and phrasing content into paragraphs. The exporter reads ordinary dictionary snapshots of the semantic blocks, inlines, and note definitions. Operation arguments remain inert unless an operation renderer selects them.
The exporter supports these elements:
- Headings, paragraphs, block quotes, fenced and indented code blocks, and horizontal rules.
- Bold, italic, strikethrough, underline (
u), highlight, superscript, subscript, inline code, and abbreviations. - External links and internal links through bookmarks.
- Images from local files, embedded with their dimensions. Remote image URLs become links.
- Bullet and numbered lists, including nesting,
start, and task-list checkboxes. - Pipe and grid tables, including row and column spans and header rows.
- Definition lists and footnotes.
A details div becomes its label as a bold line above the body. It does not become a numbered heading or retain its collapsible behavior.
Math spans and blocks become native Word math zones (m:oMath). These contain the source text unchanged, whether TeX, UnicodeMath, or AsciiMath. Math build-up and rendering are left to downstream tools. Pandoc reads the zones back as math.
An id-bearing display equation receives a live Equation N caption below its math zone. Unlabelled equations remain unnumbered.
When fastpylight is installed, code blocks with a language use syntax coloring. Tokens receive Hl* character styles such as Hl Keyword. Their colors come from the reference theme described below. You can restyle them in Word.
The generated document uses named styles instead of inline formatting. Change the styles to change its appearance.
Markdown headings map to Word styles as follows:
- h1 uses Title and displays no number. It carries the numbering's invisible level 0, which restarts the count below each title. A file containing several documents can therefore number each from 1.
- h2 through h6 use Heading 1 through Heading 5.
Prose uses Body Text. The first paragraph after a heading or similar block uses First Paragraph, following Pandoc's convention. Other styles include Quote, Source Code, Verbatim Char, Hyperlink, List Paragraph, Compact for table cells, Definition Term, Definition, caption, and footnote styles. Tables default to Table Grid. The built-in template also provides Borderless Table for signature blocks and other layout tables.
Pass reference='mydoc.docx' to use your own document's styles, as with Pandoc's --reference-doc.
Reference parts are located through their relationships, not assumed filenames. Existing reference media and unrelated parts are preserved; new images receive non-colliding part names, and relationship IDs are allocated in each source part's scope.
reference also accepts a list. The first entry supplies the document, including page setup, fonts, and base styles. Later entries contribute styles and replace earlier styles with the same name. Each can be another .docx or a fastpylight theme name such as 'dracula'.
The default is the built-in template plus 'github_light'. Pass a reference document without a theme for plain, uncolored code. To write a theme's styles to a standalone docx for inspection or editing, use mdhtml2docx.styles.theme_ref('dracula', 'dracula.docx').
The built-in template is generated by tools/createref.py from a stock Word document. It defines the styles the converter emits and next-paragraph chains for continued editing in Word.
A custom-style="Name" attribute, written as {custom-style="Name"} in Markdown, applies that style from your reference document. If the style is missing, the converter inserts a stub and returns a warning.
A plain class such as {.note} applies a style only when the reference document defines one named note. Otherwise it is ignored. Both annotations also work on tables. A named table style replaces Table Grid.
Tables can mix fixed and proportional column widths. Add an attribute list after the table: {: colwidths="10em 2fr 1fr"}. Lengths fix a column's width. The fr values divide the remaining width proportionally, as in CSS grid.
The converter accepts MDHTML raw data with data-format="docx":
<script type="application/vnd.mdhtml.raw" data-format="docx">…</script>A ```{=docx} fenced block in Markdown, or inline code followed by {=docx}, produces this element. The payload is parsed as WordprocessingML and inserted verbatim. Block payloads contain elements such as w:p or w:tbl. Inline payloads contain elements such as w:r. The prefixes w, r, wp, a, pic, and m are predeclared.
The shared mdhtml importer decodes payloads according to its raw-data contract. This exporter parses decoded docx payloads as WordprocessingML. Invalid encodings and malformed XML produce warnings. Other formats are skipped.
For example, insert a page break with:
```{=docx}
<w:p><w:r><w:br w:type="page"/></w:r></w:p>
```
Bibliographies and citations use mdhtml's shared Rust preparation. ::: bibliography entries contain citation-author terms with ids and optional year attributes, followed by their formatted definitions. Citations use [@key], narrative {ref=text}, author-suppressing -@, and per-item prefix/suffix text. cite_style='numeric' selects numbers in bibliography order; 'author-year' selects author-year text. None inherits frontmatter. Citations become static linked text and bibliography entries retain their bookmarks. Section, page, figure and table cross-references remain native Word fields. Re-export the original document to change citation style.
Markdown references such as [@sec-payment] become live Word REF fields. MDHTML represents them as a elements with data-ref.
The default field is REF <bookmark> \w \h. It displays the full-context paragraph number, such as "3.(c)(iii)", as a hyperlink. The converter sets updateFields in settings.xml so Word refreshes fields on open.
The leaf, rel, text, and page tokens select other fields. The independent bare token suppresses the prefix word. Unknown or conflicting tokens are conversion errors.
The reference type is the part of the target id before its first -. It determines the word before the number. The built-in sec type uses Section or Sections. Add types with reftypes=dict(exh=('Exhibit', 'Exhibits')).
Use [Clause @sec-x] to override the word for one reference, or [-@sec-x] to suppress it.
Grouped references use a span with data-refs. They render as "Sections 3.1 and 4.2", with one field per number. Groups never collapse into static ranges such as "3.1-3.3", whose meaning could change when a clause is inserted.
A missing target id raises a conversion error. A reference type with no defined prefix also raises when a prefix is required. A lawyer's document must not open showing "Error! Reference source not found."
Number fields require numbered headings. If your reference docx already numbers its heading styles, the converter leaves that numbering unchanged.
The shared reference index resolves number_headings against document metadata. Numeric heading references infer decimal numbering when no scheme is supplied. You can select a scheme explicitly:
'legal'uses 1. / (a) / (i) numbering.'decimal'uses 1. / 1.1. / 1.1.1. numbering.- A
{lvlText: numFmt}dictionary supplies a custom scheme, with one entry per heading level from h1 down. Level 0 is the h1 title with an emptylvlText.%2is the h2 counter.
The named schemes come from mdhtml's SCHEMES. The custom dictionary uses the same format as mdhtml.
A reference-list entry ending in .xml can also supply styles and numbering. It contains raw w:style, w:abstractNum, and w:num elements. Each contributor's ids and references are remapped before merging, so separate contributors can reuse the same original numbering ids; later styles still win.
Figures, captioned tables, and id-bearing display equations use independent live SEQ fields. A figure's caption appears below its body as "Figure 1: caption". A table's caption appears above it as "Table 1: caption". Equations receive an "Equation 1" caption below the math zone. Labels come from reftypes; changing a label never merges the counters.
When the element has an id, the converter bookmarks its label, number, and authored caption separately. The shared reference planner supplies prefix words; each Word REF field supplies only the live number. Thus [@fig-plot] displays "Figure 1", while [-@tbl-stages] displays only the number.
Figure and table {ref=text} references use a separate bookmark containing the authored caption text. Generated bookmark names are reserved through the same collision-safe allocator as authored targets, including forward references. Equation references support full labels, bare numbers, and pages. {ref=text} to equation source is not supported by the Word exporter.
fig, tbl, and eq are built-in reference types alongside sec. Grouped references use the shared planner's plural prefixes and separators, such as "Figures 1 and 2".
Reference targets include headings, paragraphs, figures, tables, equations, spans, and definition terms with ids. Paragraphs support text and page references; spans and definition terms support text and page references. Target and variant validation belongs to mdhtml.
Independent schema validation is optional. Install mdhtml2docx[validation] to use mdhtml2docx.validate.fast_checks(path); this extra requires lxml. Normal conversion does not import it.
The test suite checks docx containers, CRCs, and XML, validates against the ECMA-376 schemas with lxml, and performs semantic round trips through Pandoc's independent docx reader. Periodic acceptance runs open documents in Microsoft Word through AppleScript.
Live Word scripting is provided by macscript, not the converter. The development-only tools/check_docx.py combines oxml validation with a live Word acceptance check; see "Live Word checks" in DEV.md. Normal installation and conversion do not require macscript or Office.