Convert Markdown to PDF in pure Python; no Pandoc, Node.js, or headless Chrome required
Project description
md2pdf — Markdown to PDF in Pure Python
md2pdf converts your Markdown documents into beautiful, print-ready PDFs using pure Python — no Pandoc, Node.js, or headless Chrome needed. Powered by ReportLab and mistletoe.
Why md2pdf?
Most Markdown-to-PDF tools require heavy system dependencies — and even then, getting diagrams or math to render often means installing additional packages. md2pdf works out of the box:
| Tool | Requires Pandoc | Requires Chrome | Diagrams & Math | Pure Python |
|---|---|---|---|---|
| Pandoc-based | ✅ Yes | ❌ No | ⚠️ Extra packages (e.g. mermaid-filter, LaTeX distribution) | ❌ No |
| Puppeteer/Playwright | ❌ No | ✅ Yes | ⚠️ Depends on setup | ❌ No |
| md2pdf | ❌ No | ❌ No | ✅ Built-in (via Kroki and matplotlib) | ✅ Yes |
Beyond zero system dependencies, you also get:
- Diagrams & Math — Mermaid and LaTeX via Kroki (with SHA-256 disk caching); matplotlib used for offline LaTeX rendering when configured
- PDF bookmarks — every heading clickable in your PDF viewer's navigation panel
- Multi-page tables — split cleanly across pages with repeated headers
- Colour emoji — Twemoji-powered PNG emoji in your PDFs
- Extensible plugins — custom handlers, themes, and preprocessors
Installation
Using uv (recommended):
uv tool install pymd2pdf
Using pip:
pip install pymd2pdf
Heavy math documents (physics, engineering)? Install the optional matplotlib backend for faster offline LaTeX rendering:
pip install pymd2pdf[matplotlib]
Note: The PyPI package name is
pymd2pdf, but the CLI command and Python import are bothmd2pdf.
Quick Start
# Convert a Markdown file to PDF
md2pdf input.md -o output.pdf
# Run validation checks without producing a PDF
md2pdf input.md --validate-only
# Work offline (diagrams show source code placeholder instead)
md2pdf input.md -o output.pdf --offline
That's it. For most use cases, the first command is all you need.
Key Features
- Standard Elements: Headings (H1–H6), paragraphs, lists, blockquotes, horizontal rules, and hyperlinks.
- Multi-page Tables: Tables split cleanly across page boundaries with repeated column headers on each page.
- Diagrams & Math: Mermaid diagrams and LaTeX math blocks rendered via the Kroki API, with transparent margin cropping, offline fallbacks, and SHA-256 disk caching.
- PDF Bookmarks & Outline Panel: Every heading (H1–H6) becomes a clickable, correctly nested entry in your PDF viewer's navigation/bookmarks panel.
- Colour Emoji: Twemoji-powered PNG emoji rendering in PDFs, with graceful network fallbacks.
- Admonitions & Callouts: GitHub-style alerts (
> [!NOTE],> [!WARNING]) and MkDocs/Obsidian fenced containers (:::note). - Broad Unicode Support: Bundled DejaVu Sans fonts cover Latin Extended, Greek, Cyrillic, math operators, arrows, and box-drawing — no system font dependency needed.
- Custom Fonts & Themes: Supply your own TTF fonts and TOML-based theme overrides.
- Extensible Plugin System: Load custom element handlers, preprocessors, post-processors, and stylesheet/theme layers.
- Typesetting Safeguards: Orphaned heading prevention, ghost page elimination, and widow/orphan protection.
- Pre-render Validation: Catches nested tables, empty diagrams, and unsupported elements before committing to a render.
CLI Options
| Flag | Shortcut | Description |
|---|---|---|
--output |
-o |
Output PDF path (default: <input>.pdf). |
--config |
-c |
Path to a custom md2pdf.toml config file. |
--theme |
-t |
Theme name to apply (default: default). |
--offline |
Skip Kroki API calls; use source code placeholders instead. | |
--validate-only |
Run validation and exit without producing a PDF. | |
--verbose |
-v |
Output debug-level logging to stderr. |
--toc |
Prepend a dynamically generated Table of Contents page. | |
--header |
Running header template (supports {title} and {section}). |
|
--header-on-first-page |
Render the running header on the first page too. | |
--min-image-scale |
Minimum image scale before moving to a new page (default: 0.8). |
|
--emoji / --no-emoji |
Enable or disable Twemoji colour emoji (default: enabled). | |
--progress / --no-progress |
Show stage-level progress output on stderr (default: enabled). |
Python API
from md2pdf import convert, Config, Pipeline
# One-liner conversion
convert("input.md", "output.pdf")
# Advanced: custom config and pipeline
config = Config(
offline=False,
cache_dir=".md2pdf_cache",
output_file="my_document.pdf"
)
pipeline = Pipeline(config)
# Validate before rendering
issues = pipeline.validate("# Hello World")
for issue in issues:
print(f"[{issue.severity}] {issue.code}: {issue.message}")
# Render
pipeline.run(raw_md="# Document Title\n\nSome body text.")
Configuration
md2pdf auto-discovers a md2pdf.toml config file in these locations (in order):
./md2pdf.toml(project-local)~/.config/md2pdf/md2pdf.toml~/.md2pdf.toml
You can also pass one explicitly with --config. See the annotated md2pdf.toml.example for all available options.
Documentation & Examples
| Resource | Description |
|---|---|
| User Guide | Comprehensive features reference |
| Themes & Styling | Custom themes, fonts, and stylesheet overrides |
| Plugin Authoring | Writing custom handlers and preprocessors |
| User Manual (PDF) | Consolidated print-ready manual |
| Examples | Four production-grade templates with rendered PDFs |
Example Templates
| Example | Showcases |
|---|---|
academic_paper/ |
LaTeX math, citations, Mermaid flowcharts |
business_invoice/ |
Tables, custom Corporate Blue theme |
project_roadmap/ |
Task lists, admonitions, Gantt charts, code highlighting |
simple_cv/ |
Resume layout, structural tables, clean margins |
Tech Stack
| Component | Library | Description |
|---|---|---|
| Core Language | Python >= 3.11 | Strict type-hinting throughout |
| PDF Generation | ReportLab >= 4.0 | Low-level document layout engine |
| Markdown Parsing | mistletoe >= 1.3 | Fast, extensible Markdown AST parser |
| HTTP Requests | requests >= 2.31 | Kroki API communication |
| CLI Framework | typer >= 0.12 | CLI builder with validation |
| Image Processing | Pillow >= 10.0 | Auto-cropping and dimension detection |
| LaTeX Rendering (optional) | matplotlib >= 3.8 | Offline LaTeX math rendering (pip install pymd2pdf[matplotlib]) |
Development Setup
# Clone the repository
git clone https://github.com/Hari31416/md2pdf.git
cd md2pdf
# Create virtual environment and install all dependencies (including dev)
uv sync
source .venv/bin/activate
# Install pre-commit hooks
uv run pre-commit install
# Run the test suite
uv run pytest
To rebuild all documentation PDFs and example renders:
make docs
Architecture Overview
md2pdf runs as a four-stage pipeline: preprocessing → parsing/validation → element rendering → layout composition.
graph TD
Input[Markdown File] --> Pre[Preprocessors]
Pre --> Parser[MarkdownParser]
Parser --> Tokens[Token Stream]
Tokens --> Val[DocumentValidator]
Tokens --> Reg[Plugin/Handler Registry]
Reg --> Handlers[Element Handlers]
Handlers --> Kroki[Kroki API / Cache]
Handlers --> Flowables[ReportLab Flowables]
Flowables --> Layout[LayoutComposer]
Layout --> Post[Postprocessors]
Post --> Renderer[ReportLab PDF Engine]
Renderer --> Output[Output PDF]
For detailed sequence-level logic, see the User Guide.
Project Structure
md2pdf/
├── docs/ # Documentation suite (Markdown & consolidated PDF)
├── examples/ # Production-grade example templates with rendered PDFs
├── md2pdf/ # Core source package
│ ├── assets/ # Kroki client, caching, and fallback elements
│ ├── core/ # Engine pipeline, parser, validator, layout, registry
│ ├── handlers/ # Element-specific flowable generators
│ ├── styles/ # Default stylesheet and theme configs
│ └── cli.py # CLI entry point
├── scripts/ # Helper scripts (e.g. build_docs.py)
├── tests/ # Automated test suite
├── md2pdf.toml.example # Annotated configuration reference
└── pyproject.toml # Build system and dependency declaration
Project details
Release history Release notifications | RSS feed
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 pymd2pdf-0.4.3.tar.gz.
File metadata
- Download URL: pymd2pdf-0.4.3.tar.gz
- Upload date:
- Size: 3.6 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0d8a0e861de9ce0350585299ab9ae4d1cfb51e624db403b19e89e84f4d8d391a
|
|
| MD5 |
9f268fed909a784f201870c1fb688bd9
|
|
| BLAKE2b-256 |
2f5b8e586ec994149b6d0e23b38e86f18030239810a61189496f0e8fd52a98d8
|
Provenance
The following attestation bundles were made for pymd2pdf-0.4.3.tar.gz:
Publisher:
release.yml on Hari31416/md2pdf
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pymd2pdf-0.4.3.tar.gz -
Subject digest:
0d8a0e861de9ce0350585299ab9ae4d1cfb51e624db403b19e89e84f4d8d391a - Sigstore transparency entry: 1815313373
- Sigstore integration time:
-
Permalink:
Hari31416/md2pdf@3cb05b77b23ed57f0fde908d0c118f8e81832894 -
Branch / Tag:
refs/tags/v0.4.3 - Owner: https://github.com/Hari31416
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@3cb05b77b23ed57f0fde908d0c118f8e81832894 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pymd2pdf-0.4.3-py3-none-any.whl.
File metadata
- Download URL: pymd2pdf-0.4.3-py3-none-any.whl
- Upload date:
- Size: 1.7 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3aa959c46db002470daba9d6f84291f5f473df859615d4d8cc62a2c92d25f644
|
|
| MD5 |
88939f1c49fd6033630e694f1ff86ca4
|
|
| BLAKE2b-256 |
d8a5881d9bd2929ecaba83a460a02a2f4ea46cfa4f4965eda5ac1ada66a7ec64
|
Provenance
The following attestation bundles were made for pymd2pdf-0.4.3-py3-none-any.whl:
Publisher:
release.yml on Hari31416/md2pdf
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pymd2pdf-0.4.3-py3-none-any.whl -
Subject digest:
3aa959c46db002470daba9d6f84291f5f473df859615d4d8cc62a2c92d25f644 - Sigstore transparency entry: 1815313554
- Sigstore integration time:
-
Permalink:
Hari31416/md2pdf@3cb05b77b23ed57f0fde908d0c118f8e81832894 -
Branch / Tag:
refs/tags/v0.4.3 - Owner: https://github.com/Hari31416
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@3cb05b77b23ed57f0fde908d0c118f8e81832894 -
Trigger Event:
push
-
Statement type: