Convert Markdown with Mermaid diagrams and SVG illustrations to professional Word documents
tier
standard
applyTo
**/*docx*,**/*word*,**/*md-to-word*,**/*export*
muscle
.github/muscles/md-to-word.cjs
inheritance
inheritable
currency
2026-04-30T00:00:00.000Z
lastReviewed
2026-04-30T00:00:00.000Z
Markdown to Word Conversion
One command to professional Word documents — diagrams, tables, and formatting done right on first attempt.
Convert any Markdown document into polished Word (.docx) files ready for stakeholders, executives, and external audiences. Supports all standard Markdown formatting, Mermaid diagrams (auto-converted to PNG), and SVG illustrations (auto-embedded).
# Academic paper with TOC
node md-to-word.cjs thesis.md --style academic --toc
# Professional report with cover
node md-to-word.cjs quarterly-report.md --style professional --cover --toc
SVG Image Handling
SVG files are automatically detected and converted to PNG for Word compatibility:
<!-- This SVG reference in your Markdown... -->

<!-- ...becomes this embedded PNG in Word -->
{width=5.8in}
The script automatically fits images to page bounds. The constraints are codified
in md-to-word.cjs:
Page: 8.5" × 11" (Letter)
Margins: 1" each side
Usable area: 6.5" × 9.0"
MAX_IMAGE_WIDTH_RATIO = 0.90 → max width ≈ 5.85"
MAX_IMAGE_HEIGHT_RATIO = 0.60 → max height ≈ 5.40"
These ratios apply width-priority for landscape/wide diagrams (LR flowcharts,
gantts, sequence diagrams) and height-priority for portrait/tall diagrams (TB/TD
flowcharts with multiple subgraphs). The algorithm picks the more restrictive
constraint so the image fits in both dimensions.
Algorithm Steps
Read PNG dimensions from file header (pure Node.js, no dependencies)
Calculate scale factors for width and height constraints
Apply most restrictive — ensures fit in both dimensions
Specify constraining dimension — pandoc preserves aspect ratio
Mermaid Palette and Fidelity
When a Mermaid block has no classDef, no %%{init}%% directive, and no
explicit theme variables, the converter injects a default pastel palette
(GitHub-style soft colors with dark text) before rendering. This gives WYSIWYG
fidelity to authors who don't styled-by-design every diagram.
Why per-diagram-type injection matters
Diagram type
Honors classDef?
Color path
flowchart / graph
Yes
classDef (preferred) or injected init
classDiagram
Partial
classDef or injected init
sequenceDiagram
No
themeVariables only (e.g. actorBkg, noteBkgColor)
stateDiagram-v2
No
themeVariables only (primaryColor, mainBkg, labelBoxBkgColor)
erDiagram
No
themeVariables only
Without diagram-type-aware injection, sequence and state diagrams would render
as flat neutral nodes regardless of any classDef the author wrote.
Behavior
flowchart / graph / classDiagram with classDef → respected, no
injection (author wins)
sequenceDiagram / stateDiagram-v2 without %%{init}%% or explicit
actorBkg / primaryColor → palette injected (only path to colors)
Any block with %%{init}%% already present → respected, no injection
Opting out
Pass --no-default-palette to disable injection. Diagrams without classDef or
explicit theme will then render with mermaid's default neutral theme, and a
warning is emitted per affected diagram.
Lint warnings
During preprocessing the converter emits 💡 nudges for unstyled flowcharts
("inject default pastel palette") and ⚠️ warnings when --no-default-palette
is set on diagrams that would have rendered flat.
Table Formatting
All tables receive professional OOXML styling:
Element
Style
Header row
Microsoft blue (#0078D4), white text, bold 10pt
Even data rows
Light gray (#F0F0F0)
Odd data rows
White (#FFFFFF)
Borders
Gray outer (#666666), light inner (#AAAAAA)
Cell padding
2pt top/bottom, 4pt left/right
Pagination
cantSplit + keepWithNext (no orphan headers)
Repeat headers
Header row repeats on each page for long tables
Professional Features
Page Numbers
Centered page numbers in the footer, gray text (9pt).
node md-to-word.cjs doc.md --debug --keep-temp
# Check _debug_combined.md for preprocessed content# Check images/ folder for generated PNGs
macOS Fallback (No Pandoc)
macOS ships textutil which can convert HTML to DOCX natively:
# Convert markdown to HTML first, then HTML to DOCX
npx marked document.md -o document.html
textutil -convert docx document.html -output document.docx
Feature
Pandoc (primary)
textutil (fallback)
Table styling
Full (via jszip post-processing)
Basic
Mermaid diagrams
Supported (pre-rendered PNG)
Must be pre-rendered
Heading styles
Mapped to Word styles
Basic HTML mapping
Cross-references
Supported
Not supported
Install
brew install pandoc
Built-in (macOS only)
Limitations: textutil needs HTML input (not raw Markdown), produces simpler formatting, and doesn't support the table styling or image sizing that md-to-word.cjs provides. Use only when Pandoc is unavailable and a quick conversion is needed.
Batch Processing
Convert a Folder
# Windows PowerShell
Get-ChildItem docs/*.md | ForEach-Object {
node .github/muscles/md-to-word.cjs $_.FullName --style professional
}
# macOS/Linuxfor f in docs/*.md; do
node .github/muscles/md-to-word.cjs "$f" --style professional
done
Recursive Directory
# All .md files in docs/ and subdirectories
node .github/muscles/md-to-word.cjs docs --recursive --style professional
Watch Mode
# Auto-rebuild when source changes
node .github/muscles/md-to-word.cjs spec.md --watch
Integration Examples
GitHub Actions CI/CD
# Generate Word docs as build artifacts-name:GenerateWordDocumentsrun:|
npm install -g @mermaid-js/mermaid-cli svgexport
node .github/muscles/md-to-word.cjs docs/spec.md --toc --cover
-name:Uploadartifactsuses:actions/upload-artifact@v4with:name:word-documentspath:docs/*.docx
npm Script
{"scripts":{"docs:word":"node .github/muscles/md-to-word.cjs docs/README.md --style professional --toc"}}
For Heir Projects
Copy .github/muscles/md-to-word.cjs to your project
Copy shared modules from .github/muscles/shared/ (markdown-preprocessor, mermaid-pipeline)