Skip to main content

mdhtml2docx

Convert MDHTML to Word docx files.

mdhtml renders Markdown to an HTML5 document format shared by format-specific exporters. This package converts its portable core to docx from scratch. It uses fast5ever's mutable WHATWG DOM for input and lxml for the generated WordprocessingML.

MDHTML accepts the full HTML vocabulary. This exporter supports the portable elements and annotations listed below. It preserves the text of unknown inline elements, recurses through block containers, and warns when an unsupported block must become a plain paragraph. HTML parsing and repair belong to mdhtml; this package only walks the resulting tree.

Usage

from mdhtml import to_mdhtml
from mdhtml2docx import convert

html = to_mdhtml(markdown_text)
warnings = convert(html, 'out.docx')

convert takes an MDHTML string, writes a docx, and returns a list of warning strings. It parses strings with mdhtml.parse_mdhtml, so normal HTML5 repair applies and XML well-formedness is irrelevant. It also accepts an existing mutable fast5ever DOM:

from mdhtml import parse_mdhtml

document = parse_mdhtml(html)
document.children[0].attrs['custom-style'] = 'Contract Title'
warnings = convert(document, 'out.docx')

Body-position text and phrasing content become implicit Word paragraphs. Whitespace between blocks remains inert. HTML templates remain inert and are not rendered.

Supported: headings, paragraphs, bold, italic, strikethrough, underline (the u element), highlight, superscript and subscript, inline code, links (external and internal, via bookmarks), images (local files are embedded with correct dimensions; remote URLs degrade to links), block quotes, fenced and indented code blocks, 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, footnotes, horizontal rules, and abbreviations. A details div (the dialect's collapsible block) degrades to its label as a bold line above the body, never a numbered heading — Word has no folding. Math spans and blocks become native Word math zones (m:oMath) holding the source text as-is, whatever the dialect (TeX, UnicodeMath, AsciiMath). Build-up/rendering is left downstream, and pandoc reads the zones back as math.

Code blocks with a language are syntax colored when fastpylight is installed: tokens get Hl* character styles (e.g. Hl Keyword), whose colors come from the theme entry in the reference list below, so a finished document can be restyled in Word like everything else.

Styling

The generated document uses named styles, never inline formatting, so appearance is controlled by restyling. Prose paragraphs get Body Text (First Paragraph directly after a heading or similar block, following pandoc's convention), and the other styles are the ones you would expect: heading 1 through 6, Quote, Source Code, Verbatim Char, Hyperlink, List Paragraph, Compact (table cells), Definition Term, Definition, caption, footnote styles, and Table Grid (plus the author-selectable Borderless Table).

Pass reference='mydoc.docx' to use your own document's styles instead of the built-in template, exactly like pandoc's --reference-doc. reference may also be a list: the first entry supplies the document (page setup, fonts, and all base styles), and each later entry contributes just its styles, replacing same-named earlier ones - either another .docx, or a fastpylight theme name such as 'dracula', which generates the code-color styles on the fly. The default is the built-in template plus 'github_light'; pass a bare reference for plain uncolored code, or mdhtml2docx.styles.theme_ref('dracula', 'dracula.docx') to write a theme's styles as a standalone docx you can inspect or tweak. A custom-style="Name" attribute (from {custom-style="Name"} in Markdown) applies that style from your reference doc; if the style is missing, a stub is injected and a warning returned. A plain class like {.note} applies a style only when your reference doc defines one named note, and is otherwise ignored. Both work on tables too: a table whose custom-style or class names a table style in the reference doc uses it in place of Table Grid - the built-in template ships Borderless Table (no gridlines, for signature blocks and other layout tables).

Tables can mix fixed and proportional column widths with a colwidths attribute, written in Markdown as an attribute list after the table: {: colwidths="10em 2fr 1fr"}. Lengths fix a column; fr values share the remaining width, as in CSS grid.

The built-in template is generated by tools/createref.py from a stock Word document; it defines exactly the styles the converter emits, plus next-paragraph chains so documents stay well-styled while edited by hand in Word.

Raw docx

This converter consumes MDHTML raw data whose data-format is docx:

<script type="application/vnd.mdhtml.raw" data-format="docx"></script>

A ```{=docx} fenced block in Markdown, or inline code followed by {=docx}, produces that carrier. The payload is parsed as WordprocessingML and inserted verbatim. Block payloads supply content such as w:p or w:tbl; inline payloads supply content such as w:r. The prefixes w, r, wp, a, pic, and m are predeclared, so no namespace boilerplate is needed.

Literal payloads have no data-encoding. The exporter performs one character-reference decoding pass for data-encoding="html", and accepts UTF-8 payloads encoded with data-encoding="base64". Malformed payloads and unknown encodings are dropped with a warning. Raw data for other formats is skipped silently.

The canonical example is a page break:

```{=docx}
<w:p><w:r><w:br w:type="page"/></w:r></w:p>
```

Cross-references

Markdown references like [@sec-payment] become MDHTML a elements marked with data-ref, which this exporter turns into live Word REF fields rather than baked text. The default field is REF <bookmark> \w \h, the full-context paragraph number ("3.(c)(iii)") as a hyperlink; settings.xml gets updateFields, so Word refreshes on open. The leaf, rel, text, and page tokens select other fields, while the independent bare token suppresses the prefix word. Unknown or conflicting tokens are conversion errors.

The word before the number comes from the reference's type, the id up to its first -: sec maps to Section/Sections out of the box, and reftypes=dict(exh=('Exhibit', 'Exhibits')) adds more. [Clause @sec-x] overrides the word for one reference; [-@sec-x] suppresses it. Grouped references use a span marked with data-refs and join as "Sections 3.1 and 4.2" with one field per number. They are never collapsed into ranges, because "3.1-3.3" is static text whose meaning silently changes when a clause is inserted. A reference whose target id does not exist, or whose type has no prefix defined when one is needed, raises rather than warning: a lawyer's document must not open showing "Error! Reference source not found."

Number fields need numbered headings. If your reference docx already numbers its heading styles (most firm templates do), nothing more is required, and the converter leaves that numbering alone. Otherwise pass number_headings='legal' for 1. / (a) / (i) numbering or 'decimal' for 1 / 1.1 / 1.1.1 (the names index styles.SCHEMES), or pass your own scheme as a {lvlText: numFmt} dict, one entry per heading level. A reference-list entry ending in .xml is a third route: a raw file of w:style, w:abstractNum, and w:num elements contributing styles and numbering together, with ids remapped to avoid collisions.

Figures and captioned tables number themselves with SEQ fields: a figure renders as its image plus a "Figure 1: caption" paragraph below (caption style), a table caption as "Table 1: caption" above the table, both live. When the element has an id, the label-and-number span is bookmarked, so [@fig-plot] inserts a live "Figure 1" (no extra prefix word; the label is part of the bookmarked text) and [-@tbl-stages] the bare number via a second number-only bookmark. fig and tbl are built-in reftypes alongside sec, and their label words come from the same table. Mixed-type groups render each item with its own singular prefix ("Figure 1 and Table 2"); same-type groups pluralize once. Reference targets must be things that get bookmarks - headings, paragraphs, figures, and tables with ids - and a ref to anything else is an error at convert time.

Validation

Every generated file is checked three ways in the test suite: lxml validation against the ECMA-376 schemas (container, CRC, XML, XSD), a semantic round trip through pandoc's independent docx reader, and occasional acceptance runs in Microsoft Word itself, driven live via AppleScript.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mdhtml2docx-0.1.1.tar.gz (243.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mdhtml2docx-0.1.1-py3-none-any.whl (241.0 kB view details)

Uploaded Python 3

File details

Details for the file mdhtml2docx-0.1.1.tar.gz.

File metadata

  • Download URL: mdhtml2docx-0.1.1.tar.gz
  • Upload date:
  • Size: 243.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.2

File hashes

Hashes for mdhtml2docx-0.1.1.tar.gz
Algorithm Hash digest
SHA256 0658c72712ccc4f4f452ea5389501204a17e69d0b562747df260f46d3973837a
MD5 6a7a14eaffa2392844d444b68c7601b9
BLAKE2b-256 ac7042efe9997a91198c51f9feaa49c46eeee900415b903dc8d5478781e4ce5d

See more details on using hashes here.

File details

Details for the file mdhtml2docx-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: mdhtml2docx-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 241.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.2

File hashes

Hashes for mdhtml2docx-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 1f68abe74670a3548f1cb1d383781df2ad60a9b758ba93f255fd28c1289d340b
MD5 1c33e6ea7140136ac488e4ed3bcc4a3c
BLAKE2b-256 512f1fbd135068c8431e2772d4dc64f868799c4148f5a3c2a319b6d62ebc0778

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page