A C# command-line converter and Windows WPF interface for Markdown → Word. Both use the same CLI conversion pipeline. Text, tables, lists, links, footnotes and equations stay editable in Word; Mermaid diagrams are embedded as high-resolution PNG images.
Prerequisites: .NET 10 SDK and Node.js 22 or newer. The WPF application requires Windows; the core library and CLI target plain net10.0.
Run in this repository:
./scripts/Install-Dependencies.ps1
dotnet build MD2DOC.sln -c Release
dotnet run --project src/MD2DOC.Cli -c Release --no-build -- doctor
dotnet run --project src/MD2DOC.Cli -c Release --no-build -- convert samples/showcase.md -o artifacts/showcase.docx
dotnet run --project src/MD2DOC.Wpf -c Release --no-buildThe setup script downloads Pandoc 3.11 to .tools, verifies its pinned SHA-256, installs Mermaid CLI 11.17.0 and Puppeteer 24.43.1 using the committed npm lockfile, and explicitly installs Puppeteer's browser. It does not change global npm installations or PATH. Internet access is needed for this one-time setup. The browser cache belongs to the installing Windows user.
Conversion does not install software automatically. doctor checks executable versions and renders a small Mermaid diagram to verify the browser really works. Missing or unusable dependencies produce actionable errors. Only Pandoc is needed for documents without Mermaid, SVG or WebP.
LibreOffice and Microsoft Word are not required for conversion. LibreOffice was used to render the sample for layout verification; there is no hidden LibreOffice fallback.
./artifacts/publish/cli/md2doc.exe convert report.md -o report.docx
./artifacts/publish/cli/md2doc.exe convert report.md -o report.docx --overwrite --toc --number-sections
./artifacts/publish/cli/md2doc.exe convert report.md --from gfm --reference-doc company-style.docx
./artifacts/publish/cli/md2doc.exe convert report.md --offline --timeout 120 --json
./artifacts/publish/cli/md2doc.exe doctor --json--help lists all options. The output defaults to the input path with a .docx extension. Existing documents require --overwrite. Paths with spaces should be quoted in the shell. Use an absolute path or ./filename.md for filenames beginning with a hyphen.
| Option | Meaning |
|---|---|
--from markdown |
Default: extended Pandoc Markdown, including footnotes, definition lists, math, tables and task lists |
--from gfm |
GitHub Flavored Markdown, with dollar-delimited math enabled |
--from commonmark |
Strict CommonMark; extensions such as pipe tables and footnotes are not interpreted |
--reference-doc file.docx |
Reuse Word styles and page setup; the template's body text is not inserted |
--toc |
Add a table-of-contents field; update fields in Word to refresh entries/page numbers |
--number-sections |
Number document headings |
--offline |
Reject HTTP/HTTPS image references before downloading them |
--timeout seconds |
Total conversion limit, including subprocesses and downloads; default 300, maximum 86400 |
--json |
One JSON event per line, with type, message, and optional outputPath |
--cancel-on-stdin |
Cancel on a cancel line or stdin EOF; used by WPF |
Exit codes: 0 success, 1 conversion/file/network failure, 2 invalid arguments, 3 dependency failure, 130 cancellation. Ctrl+C cancels a CLI conversion. A conversion timeout is exit 1; a cancelled/timed-out doctor is exit 130.
Choose or drop a Markdown file, choose the output path, optionally choose a Word style reference, and press Convert to Word. The interface includes dialect selection, timeout, remote-image control, overwrite control, TOC/numbering, dependency checks, live logs, cancellation, and opening the completed document.
The GUI starts the packaged CLI as a child process and reads newline-delimited JSON. It does not implement a second converter. Cancellation is sent through stdin so the CLI can clean up; a process-tree termination is used if it does not exit within five seconds. Closing the window also cancels an active conversion. Dependency paths and the reference-document path are retained in %LOCALAPPDATA%/MD2DOC/settings.json.
| Source feature | Word output |
|---|---|
| Headings, paragraphs, emphasis, bold, strikeout, sub/superscripts | Native styled Word paragraphs/runs |
| Block quotations, nested numbered/bullet lists, definition lists | Word paragraph styles and list structure |
| Task lists | Visible checked/unchecked symbols; not interactive Word form controls |
| Pipe/grid tables supported by the chosen dialect | Editable Word tables with alignment and formatted cell content |
| Links, heading anchors, footnotes | Word hyperlinks, bookmarks and footnotes |
| Inline/fenced code | Monospaced text; syntax highlighting for languages supported by Pandoc |
| Dollar-delimited TeX equations | Word OMML equations where Pandoc supports the TeX syntax |
| Markdown/reference-style images | Embedded images, resolved relative to the Markdown file |
| HTTP/HTTPS images, base64 data images | Downloaded/decoded and embedded, so the finished document is self-contained |
| PNG/JPEG/GIF/BMP/TIFF | Passed to the DOCX writer; static document rendering depends on the Word viewer |
| SVG/WebP | Rasterized to PNG through Puppeteer for consistent Word/LibreOffice display |
| Mermaid fences, including inside lists/quotes | Rendered by Mermaid CLI to PNG at 2× scale |
Basic inline HTML formatting and <img> |
Converted to Word equivalents; <br> becomes a line break |
| HTML blocks such as tables | Parsed into document structure, with an explicit warning about CSS/interactive-content loss |
Markdown has multiple dialects, so “complete Markdown formatting” is defined by the selected reader and Word's available features. This converter does not reproduce arbitrary webpage CSS, scripts, interactive Mermaid links, animated media, Obsidian/plugin syntax, raw LaTeX blocks or raw Open XML injections. Unsupported raw inline markup fails clearly instead of silently disappearing. HTML comments are ignored. Markdown syntax embedded inside raw HTML blocks is not interpreted in the default dialect. Large diagrams are scaled to the page and may require a landscape reference document for readability. Mermaid diagrams are images, not editable Word shapes. Custom fonts/icons must be available to the local renderer.
Local image paths, file: URIs, URL-encoded filenames, relative paths, and paths containing spaces are supported. Images are limited to 25 MiB each and Markdown input to 20 MiB. Remote downloads allow at most five redirects and use a 60-second request timeout in addition to the conversion timeout. Remote servers must return an image content type or a supported extension with a missing/octet-stream content type. Authenticated image services have no credential UI; use a downloaded local image instead. --offline controls image downloading, not an operating-system network sandbox for external renderers.
All subprocess arguments use ProcessStartInfo.ArgumentList; document contents and filenames are never assembled into shell commands. The app does not execute document-supplied filters or commands. Input documents may reference local files and network URLs, so use it with documents whose references you intend to resolve.
Explicit CLI paths take priority, then the corresponding environment variable, then known local/install locations, then PATH for executables:
| CLI option | Environment variable |
|---|---|
--pandoc |
MD2DOC_PANDOC |
--node |
MD2DOC_NODE |
--mermaid-cli |
MD2DOC_MERMAID_CLI |
The local tool search walks upward from the application directory and working directory, finding .tools/pandoc-3.11/pandoc.exe and tools/mermaid/node_modules/@mermaid-js/mermaid-cli/src/cli.js. Windows Pandoc install locations and the user's global npm installation are also checked. Pass Mermaid's actual src/cli.js, not an mmdc.cmd wrapper. Node is invoked directly without a command shell.
For a custom browser, use --puppeteer-config browser.json:
{ "executablePath": "C:/Program Files/Google/Chrome/Application/chrome.exe" }On Linux/macOS, install Pandoc, Node and Mermaid CLI using their official instructions, then build/run only src/MD2DOC.Cli. Specify the Mermaid JS path if a custom npm prefix is used. The CLI uses platform-specific executable names and path rules; Linux/macOS execution has not been tested in this Windows workspace.
dotnet build MD2DOC.sln -c Release
dotnet test MD2DOC.sln -c Release --logger "trx;LogFileName=tests.trx" --results-directory artifacts/test-results
./scripts/Publish.ps1
# Alternatively, include the .NET runtime:
./scripts/Publish.ps1 -SelfContainedRun artifacts/publish/MD2DOC.Wpf.exe or artifacts/publish/cli/md2doc.exe. Keep the entire publish folder together. The default publish needs the .NET 10 Desktop Runtime; self-contained publishing includes .NET but still needs the external conversion tools and browser. Use the publish script to package the CLI next to WPF; publishing WPF alone does not package the CLI. On another machine, install the dependencies and configure their paths, or preserve the documented tool layout beside the publish directory. Retain the upstream licenses when redistributing dependencies.
The tests run real Pandoc/Mermaid conversions and inspect DOCX XML/media. They also exercise remote images through a loopback HTTP server, dependency errors, safe overwrite, invalid diagrams, timeout/cancellation, CLI JSON/exit codes, and child-process cleanup. Install dependencies before running them; missing tools fail the tests rather than silently skipping them. Tests never depend on a public image server.
Project layout:
src/MD2DOC.Core: conversion, image resolution, dependency discovery, process lifecycle.src/MD2DOC.Cli: argument handling, cancellation and event output.src/MD2DOC.Wpf: desktop interface and CLI orchestration.tests/MD2DOC.Tests: integration and CLI tests.samples/showcase.md: representative input with local image and nested Mermaid diagrams.docs/library-research.md: library comparison and primary-source links.docs/validation.md: actual validation results and remaining gaps.
Output is written to a temporary sibling file and moved into place only after successful conversion and DOCX package checks. Failed/cancelled conversions preserve an existing destination. Temporary content is cleaned up in a finally block; cleanup failures are reported. Abrupt termination or power loss can leave temporary files.