Overview
Converts Markdown documents into Word documents that comply with Chinese typography standards. Supports the full Markdown syntax and automatically applies professional formatting.
Dependency Requirements
Python Dependencies
pip install python-docx Pillow beautifulsoup4 PyYAML
Optional Dependencies
npm install -g @mermaid-js/mermaid-cli # Mermaid 图表渲染
brew install librsvg # SVG→PNG(推荐,rsvg-convert)
# 或 pip install cairosvg # SVG→PNG 备选
# 或 npm install puppeteer # SVG→PNG 备选(scripts/svg2png.js)
Inline in the body
<svg>...</svg>blocks are automatically rendered as PNG and embedded. Rendering priority is rsvg-convert → cairosvg → svg2png.js (puppeteer); any one of the three is sufficient. When none are available, it degrades to displaying the SVG source code in a code block.
Quick Start
Main conversion script:scripts/md2word.py
# 基本转换
python scripts/md2word.py input.md output.docx
# 使用预设格式
python scripts/md2word.py input.md --preset=academic
# 使用自定义配置
python scripts/md2word.py input.md --config=my-config.yaml
# 脚注/尾注模式(默认 footnote 页面脚注;endnote=文档末注释+上标编号)
python scripts/md2word.py input.md --notes=endnote
# 全书合并:多章 md → 单 docx(目录+按输入文件分章+页眉,配合 -o 指定输出)
python scripts/md2word.py --book ch01.md ch02.md ch03.md -o book.docx --preset=book-publish
--bookOnly converts the boundaries of adjacent input files into new sections. Markdown horizontal rules in chapter body text---、***、___are preserved as horizontal rules in both single-chapter and full-book modes, and do not carry pagination or chapter-splitting semantics. Starting from 1.3.8, horizontal rules are by default rendered as a native Word paragraph bottom border (empty paragraph +w:pBdr/w:bottomsingle line), with the width adapting to the body text column, so 55─characters are no longer stretched to wrap due to font fallback;horizontal_rule.style: charactercan revert to the old behavior.
--bookBefore merging, local relative image paths are resolved according to the directory of each chapter's own Markdown, and then written into the temporary merged draft. Both Markdown images and HTML<img src>are supported; HTTP/HTTPS, data URIs, anchors, and absolute paths remain unchanged. The image resolution process for single-chapter conversion remains unchanged.
book-publishBy default, Markdown headings whose heading text is exactly "Chapter Summary" or "Hands-on Exercise" start on a new page; the converter writes a native WordpageBreakBeforein the heading paragraph itself, without inserting empty paragraphs, page-break runs, or new sections. This rule also applies to single-chapter conversion, and is disabled by default in other presets; it can be overridden viapagination.page_break_before_headings.
When the same
[^label]appears repeatedly in the body text, the nativefootnotemode generates a separate Word footnote for each occurrence and repeats the same definition text, ensuring that the footnote is visible at every citation location;endnotemode still reuses the same number and a single endnote definition.
In native
footnotemode, when two footnote markers are directly adjacent in the source text, the output inserts an NBSP between the two superscripts that is also 9pt superscript; if the source already has a space or punctuation, nothing extra is added. Page footnote paragraphs are fixed at 0 before and after the paragraph and single automatic line spacing;endnotedoes not apply these two rules.
Page footnotes can be used in both ordinary body text and Markdown blockquotes (
>);[^label]inside blockquotes generates native Word footnote references and will not remain in the body text as literal markers, while preserving the blockquote paragraph and inline formatting such as bold.
All Markdown blockquotes uniformly read one set of
quoteconfiguration, and are not routed separately by text labels such as "Chapter Introduction" or "Case". The built-in presets' blockquote boxes and fenced code blocks share the neutral light gray visual token#F5F5F5, styled as a full-body-width paragraph shading with no visible border; no table container is used, so Word's "View Gridlines" will not show a dashed outer frame. Text usespaddingto preserve left, right, and leading/trailing padding; internal empty quote lines generate the same background colorparagraph_spacingExact empty paragraph, so multi-paragraph callouts remain one continuous gray-background block. Multiple consecutive internal empty reference lines are deterministically collapsed into one, and leading and trailing empty reference lines are ignored.
Inline code first protects backtick ranges: the
_、*and other Markdown markers within them are preserved literally and will not combine with adjacent code spans to form italics or bold; for example,`law_keyword`will be output completely as one code run.
Underscores inside ordinary technical identifiers are preserved literally: in body text and Markdown tables,
payment_instance_id、API_SERVER_KEYand other names will not be interpreted as italics or bold. Underscore emphasis must be at word boundaries; explicit_斜体_、__粗体__and___粗斜体___are still rendered with their original semantics.
All fenced code blocks (including
text、markdownand language-less fences) continue to use the existing monospaced, compact code content style; you can usecode_block.content.space_beforeandspace_afterto adjust only the vertical spacing between the entire box and the surrounding body text.
Markdown tables are fixed within the available width of the page body; multi-column long headers automatically wrap, and the converter unifies the table's total width, grid column widths, and cell widths to prevent the table from crossing the left and right page margins.
Both Markdown and HTML data tables have
table.space_afterfixed-height spacing appended by the table component itself (default 6pt exact); subsequent body text retains normal body text 1.5 line spacing and 0 space before paragraph. Images and captions do not use this table spacing.
Ordinary Markdown table captions (such as
**表 10-5:标题**) are automatically horizontally centered and have first-line indentation removed; explicit<div align="center">...</div>can still continue to be used. The caption's original font size and bold formatting are unaffected by automatic alignment.
Configuration System
Built-in Presets
Preset information is read dynamically from YAML files. Run the following command to view the complete list:
python scripts/config.py --list
Common presets:
- legal — Legal document format (default)
- service-plan — Legal service proposal (with layered color scheme)
- minimal — Minimal format
- academic — Academic paper format
- report — Work report format
- book-publish — Chinese book publishing format (body text in Songti, headings in Heiti, TOC/header book title, with
--bookfull-book merged export)
For the complete configuration, see
assets/presets/*.yaml; for design notes, seeassets/theme-notes/
Custom Configuration
Copy the configuration template and modify it:
cp assets/config-template.yaml my-config.yaml
Word Template File
Place the .docx template into the assets/templates/ directory, or use --template to specify it.
Word Template vs Configuration File:
- Word template: controls visual elements (header, footer, logo)
- Configuration file: controls formatting parameters (font size, line spacing, page margins)
Reference Documentation
- Configuration Reference: references/config-reference.md
- Style Mapping: references/style-mappings.md
- Usage Examples: references/examples.md
Required Permissions and Security Notes
This skill calls local scripts to perform document conversion, involving the following capability boundaries. Please be aware before use:
Local Code Execution
scripts/md2word.pythroughsubprocess.runinvokes external rendering tools to render charts and SVG:- Mermaid charts: calls
mmdc(MMDCCMD environment variable → script same directorynode_modules/.bin/mmdc→ system PATH), rendering only the user-input mermaid code; commands are assembled as argument arrays and do not go through shell string concatenation. - SVG rendering: calls by priority
rsvg-convert→cairosvg→node scripts/svg2png.js(Puppeteer). When inline SVG is untrusted input, it may trigger renderer parsing issues or resource consumption; please only convert documents from trusted sources.
- Mermaid charts: calls
Network access (enabled by default)
- When converting Markdown containing images with external URLs, it willautomatically initiate requests to any HTTP/HTTPS address(
urllib.request, timeout 20s), used to download images and embed them into Word. - This isdefault behavior: external images will be downloaded normally and embedded into the document; if download fails, it degrades to a text placeholder.
- Please be aware of the risks: processing untrusted Markdown may trigger SSRF(accessing intranet addresses), leaking metadata such as the converter's IP/time to third parties, and introducing malicious or oversized image payloads. Please only convert documents from trusted sources.
- The above download requests will not upload document content; they only fetch images according to the image URLs in the Markdown.
Environment variable reading
chart_handler.pyreads theMMDCCMDenvironment variable to locate the mermaid-cli executable (optional; if not set, falls back to node_modules in the same directory as the script and the system PATH).
File access
- Reads the user-specified Markdown input file, the Word template under
assets/templates/, and the YAML configuration underassets/presets/. - Generates a Word document in the output directory (
--bookmode will generate a temporary merged Markdown, which is automatically deleted after conversion ends; local relative images in chapters will first be relocated according to their respective source file directories).
Error handling
- File encoding: automatically detects UTF-8 and GBK
- Template not found: creates a new document using the default format
- Mermaid failure: degrades to a text description
- Image too large: automatically compresses and resizes
Directory structure
md2word/
├── SKILL.md # 本文档
├── CHANGELOG.md # 版本记录
├── references/ # 参考文档
│ ├── config-reference.md
│ ├── style-mappings.md
│ └── examples.md
├── scripts/ # 转换脚本
│ ├── md2word.py # 主脚本
│ ├── config.py # 配置模块(含 --list 查看预设)
│ ├── extract_template_config.py # 从 Word 模板提取配置
│ ├── formatter.py # 文本格式化模块
│ ├── table_handler.py # 表格处理模块
│ └── chart_handler.py # 图表渲染模块
└── assets/ # 资源文件
├── presets/ # YAML 预设配置
├── theme-notes/ # 预设设计说明文档
├── templates/ # Word 模板文件
└── config-template.yaml