Add md-to-docx: markdown to DOCX converter using pandoc

Converts markdown files to DOCX with optional reference doc styling.
Supports front matter stripping and custom output paths.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Paul O'Reilly
2026-03-17 11:14:30 +13:00
parent cbde292dd7
commit 77af82c88c
3 changed files with 501 additions and 0 deletions

98
specs/md-to-docx.spec.md Normal file
View File

@@ -0,0 +1,98 @@
# md-to-docx
## Purpose
Convert a Markdown file to a professionally styled Word document (.docx) suitable for sharing with non-technical stakeholders (managers, C-levels, partners).
## Dependencies
- `pandoc` (3.x+) — Markdown to docx conversion
- `python3` with `python-docx` package — post-processing and styling
## Usage
```
md-to-docx [OPTIONS] <input.md>
```
### Arguments
| Argument | Description |
|---|---|
| `<input.md>` | Path to the Markdown file to convert (required) |
### Options
| Flag | Description |
|---|---|
| `--output`, `-o` | Output file path (default: same name as input with `.docx` extension) |
| `--title`, `-t` | Override document title (default: first `# heading` in the markdown) |
| `--toc` | Include a table of contents (default: off) |
| `--toc-depth` | TOC depth level, 1-3 (default: 2, only applies when `--toc` is set) |
| `--dryrun`, `-n` | Preview what would happen without creating files |
| `--help`, `-h` | Show usage information |
## Behaviour
1. **Validate inputs.** Check that `<input.md>` exists and is readable. Check that `pandoc` is on PATH. Check that `python-docx` is importable. Exit 1 with a clear message if any check fails.
2. **Extract title.** If `--title` is not provided, scan the markdown for the first `# heading` and use its text as the document title. If no heading found, use the filename (without extension).
3. **Create styled reference doc.** Generate a temporary pandoc reference.docx and apply professional styling:
- Font: Calibri throughout
- Headings: navy (#1A1A2E) for H1, professional blue (#1B4D89) for H2/H3
- Body: dark grey (#333333), 11pt, 16pt line spacing
- Comfortable margins (2.5cm all sides)
- Hyperlinks: blue (#1B4D89), underlined
4. **Run pandoc.** Convert the markdown to docx using the styled reference doc. Include `--toc` if requested.
5. **Post-process the docx.** Using python-docx:
- Style tables: blue header row with white text, alternating row shading, light grey borders
- Add page breaks before the "References" heading (if present)
- Ensure all runs have Calibri font set
6. **Clean up.** Remove temporary reference doc.
7. **Report.** Print the output path on success.
## Dryrun Behaviour
When `--dryrun` / `-n` is passed:
```
[dryrun] Input: /path/to/PROPOSAL.md
[dryrun] Output: /path/to/PROPOSAL.docx
[dryrun] Title: Building an Agentic Development Platform...
[dryrun] TOC: yes (depth: 2)
[dryrun] Dependencies OK: pandoc 3.1.3, python-docx 1.2.0
[dryrun] Would generate styled .docx file
```
No files are created or modified.
## Edge Cases
- **Input file not found:** Exit 1 with `Error: file not found: <path>`
- **pandoc not installed:** Exit 1 with `Error: pandoc not found. Install with: sudo apt install pandoc`
- **python-docx not installed:** Exit 1 with `Error: python-docx not installed. Install with: pip install python-docx`
- **Output file already exists:** Overwrite without prompting (standard pipeline behaviour)
- **Markdown has no headings:** Use filename as title, skip TOC even if requested
- **Markdown has no tables:** Table styling step is a no-op
- **Markdown has no "References" section:** Page break insertion is a no-op
## Examples
```bash
# Basic conversion
md-to-docx PROPOSAL.md
# Custom output path and title
md-to-docx -o ~/Documents/proposal-v2.docx -t "Agent Platform Proposal" PROPOSAL.md
# With table of contents
md-to-docx --toc --toc-depth 3 SPEC.md
# Preview without creating files
md-to-docx --dryrun PROPOSAL.md
```