Markdown to HTML/PDF document converter
Project description
mddoco
A CLI tool that converts markdown files to a single HTML (or future PDF) document.
Installation
pip install mddoco
playwright install chromium
Or for development:
pip install -e .
playwright install chromium
Playwright (Chromium) is required for PDF output only. HTML output works without it.
Usage
mddoco [OPTIONS] INPUT_PATH
INPUT_PATH can be a directory (all *.md and *.md.j2 files are found recursively and sorted together) or a single .md or .md.j2 file.
Options
| Option | Short | Default | Description |
|---|---|---|---|
--output PATH |
-o |
. |
Directory to write the output file |
--format [html|pdf] |
-f |
html |
Output format |
--title TEXT |
-t |
(none) | Document title shown at the top of the page |
--theme NAME |
default |
Theme to use for rendering | |
--toc / --no-toc |
off | Generate a table of contents | |
--toc-depth N |
3 |
Maximum heading depth included in the TOC (1–6) |
Examples
Convert all markdown files in a directory:
mddoco ./docs
Convert a single file:
mddoco README.md
With a title, TOC, and custom output directory:
mddoco ./docs --title "My Project" --toc --output ./out
Output
All matched markdown files are combined into a single HTML document in the output directory. The filename is derived from the input folder or file name.
Jinja2 templates
Files ending in .md.j2 are Jinja2 templates that produce Markdown. They are sorted alongside regular .md files (numeric prefixes such as 01_, 02_ determine order) and converted through the same pipeline.
Context variables (JSON and CSV)
Place *.json or *.csv files in the same input directory. Each file's stem becomes a top-level variable in the Jinja2 context — report.json is available as report, people.csv as people, and so on. If both data.json and data.csv exist, the tool will exit with an error.
docs/
01_intro.md
02_summary.md.j2 ← Jinja2 template
report.json ← available as {{ report }}
people.csv ← available as {{ people }}
JSON files are loaded as-is; the variable holds whatever structure the JSON contains.
CSV files are loaded as a list of row dicts, one dict per row:
{% for person in people %}
- {{ person.name }} ({{ person.role }})
{% endfor %}
Cell values containing ; are automatically split into a list:
name,skills
Alice,python;flask;sql
Bob,java
{{ people[0].skills }} {# → ["python", "flask", "sql"] #}
{{ people[1].skills }} {# → "java" (plain string — no ;) #}
Quoting a field in the CSV prevents ; splitting — "python;flask" remains a single string.
Excluding files
Files whose name starts with _ are excluded from scanning. Use this for Jinja2 macro files that should be imported but not rendered as documents:
docs/
_macros.j2 ← excluded; safe to use with {% import %}
01_intro.md
02_report.md.j2
Using macros
Use {% import %} or {% from ... import %} to make macros from another file callable — not {% include %}. {% include %} injects rendered output only; macros defined in an included file are not visible to the calling template and will raise an undefined error.
{# correct — macro is callable after this #}
{% import "_macros.j2" as macros %}
{{ macros.val(item) }}
{# also correct #}
{% from "_macros.j2" import val %}
{{ val(item) }}
{# wrong — val will be undefined #}
{% include "_macros.j2" %}
{{ val(item) }}
Themes
Themes are self-contained Jinja2 HTML files with embedded CSS. Pass a theme name with --theme NAME.
| Theme | Description |
|---|---|
default |
Clean GitHub-style layout, 860 px centred column |
default-wide |
Same as default but full viewport width |
professional |
Corporate blue palette, dark title banner, card layout, 900 px centred |
professional-wide |
Same as professional but full viewport width |
dark |
Dark background, blue accent, light text — good for technical docs, 860 px centred |
dark-wide |
Same as dark but full viewport width |
academic |
Serif (Georgia) typography, justified text, print-optimised, 720 px centred |
academic-wide |
Same as academic but full viewport width |
vanilla |
Minimal HTML with no styling — useful for testing or custom CSS |
paged-professional |
Corporate style with Paged.js pagination for print-ready PDFs |
All themes support:
- Optional document title
- Optional table of contents
- Mermaid.js diagram support (loaded only when diagrams are present)
- Graph diagram blocks
Graph diagrams
Use ```graph fenced blocks with a JSON payload. The only required field is data.
Minimal example — all defaults applied:
```graph
{
"data": {
"x": ["Jan", "Feb", "Mar"],
"Sales": [100, 150, 120]
}
}
```
Series are inferred from the data keys (everything except x). Each series defaults to a line chart in blue.
Full schema:
| Field | Required | Default | Description |
|---|---|---|---|
data.x |
yes | — | Category labels |
data.<name> |
yes | — | Values for each series (one key per series) |
title |
no | (none) | Chart title |
orientation |
no | vertical |
vertical or horizontal |
show_legend |
no | true |
Show the legend |
width_px |
no | 640 |
Width in pixels |
height_px |
no | 480 |
Height in pixels |
min / max |
no | — | Axis bounds |
series |
no | (auto) | Override series definitions (see below) |
Series fields (all optional when auto-generated):
| Field | Default | Description |
|---|---|---|
label |
(data key) | Must match a key in data |
type |
line |
bar, line (solid), or line2 (dotted) |
colour |
(palette) | Colour string, or list of colours per bar |
marker |
false |
Show point markers (line types only) |
Full example:
```graph
{
"title": "Sales vs Target",
"orientation": "vertical",
"show_legend": true,
"data": {
"x": ["Jan", "Feb", "Mar", "Apr", "May"],
"Sales": [85, 92, 78, 96, 110],
"Target": [90, 90, 90, 90, 90]
},
"series": [
{ "label": "Sales", "type": "bar", "colour": "#3498db" },
{ "label": "Target", "type": "line", "colour": "#e74c3c", "marker": false }
]
}
```
Mermaid diagrams
Use standard fenced code blocks in your markdown:
```mermaid
graph TD
A[Start] --> B[End]
```
mddoco detects mermaid blocks automatically and loads the Mermaid.js CDN only when needed.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mddoco-2.0.1.tar.gz.
File metadata
- Download URL: mddoco-2.0.1.tar.gz
- Upload date:
- Size: 21.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ac816b4de5f39f06355e105f8a884919d2d299df0c29a9698f2238dd82e2b1a8
|
|
| MD5 |
8f8622314156f46a24965299c664b8dd
|
|
| BLAKE2b-256 |
9737d2ad558dce96f365558e48e2169517dca4d3b52941888d0d9882c6f168b6
|
File details
Details for the file mddoco-2.0.1-py3-none-any.whl.
File metadata
- Download URL: mddoco-2.0.1-py3-none-any.whl
- Upload date:
- Size: 31.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
86579cbf3e874da585bb2e2e94b5f4d88df6f27049c4910bc5014b73aff0b511
|
|
| MD5 |
7a169fd22272eb9dbdb17d5dedd9a64a
|
|
| BLAKE2b-256 |
7e171c644b989b495437c8c0c5941b41bed0c7465233be20169d7c3333f94081
|