Skip to main content

mddoco

A CLI tool that converts markdown files to a single HTML (or future PDF) document.

Publish to PyPI PyPI Version PyPI Downloads

Installation

pip install mddoco

Or for development:

pip install -e .

Playwright (Chromium) is required for PDF output only. HTML output works without it. The playwright Python package is installed automatically, but the Chromium browser it drives is not. The first time you render a PDF, mddoco downloads Chromium for you (~150 MB, one time). To do it ahead of time, or if the automatic download fails, run it yourself:

playwright install chromium

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.

Examples

The examples/ directory contains four runnable input folders, each covering a different feature:

Folder Demonstrates
examples/01_basic Plain Markdown, file ordering, underscore-excluded files
examples/02_rich_content Mermaid diagrams, syntax highlighting, graph charts
examples/03_themed_report Themes and a table of contents
examples/04_jinja_data Jinja2 templates with JSON and CSV data files
mddoco examples/03_themed_report --theme professional --title "Q3 Platform Review" --toc --output ./out

See examples/README.md for the full list of commands.

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.

Metadata

Release files for mddoco 2.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mddoco 2.1.0
File Size Uploaded Provenance
mddoco-2.1.0.tar.gz 24.0 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for mddoco 2.1.0
File Interpreter ABI Platform
mddoco-2.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 60.2 kB

Release files / mddoco-2.1.0.tar.gz

Download URL mddoco-2.1.0.tar.gz
Size 24.0 kB
Tags Source
SHA-256 checksum
How to use checksums
8144fe6dc0453ba3c4fdc4ef96a72769e48bd10d40f26cf4535174d5f6ebfc2f
BLAKE2b-256 checksum
How to use checksums
c7178ccefb31cae501318efcc6e87df50cc53a4a1eac7dd56c85c2037742bfd8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / mddoco-2.1.0-py3-none-any.whl

Download URL mddoco-2.1.0-py3-none-any.whl
Size 36.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f4e1bd5760368a7283c44c3eee47c8c3df1d95d4587f6f69607b0ce3f86c2021
BLAKE2b-256 checksum
How to use checksums
684851d4cb82685566de9853012ca1ff5fddbc4e7266e80d3646e81dfa9ea1ed
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Security for mddoco 2.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page