One command gives agents a controlled Markdown-to-PDF path.
Agents are good at writing Markdown. The problem is the handoff: PDFs exported
through ad hoc paths rarely share the same style. mdtopdf gives the user a
command-line interface where the style can be defined up front.
- Agent-friendly -
mdtopdf --helpis an interface description an agent can read. - JSON when it matters - conversion, HTML preview, environment checks, and theme listing can return machine-readable output.
- Local files in, local files out - no browser dependency, no upload step, no remote rendering service.
- More than plain Markdown - Obsidian links, highlights, frontmatter, comments, and callouts are rendered.
Install from PyPI:
python -m pip install agent-markdown-pdfThe PyPI distribution is agent-markdown-pdf; it installs the mdtopdf command.
Do not use mdtopdf as the PyPI package name; the distribution name is
intentionally different from the command name.
| Use case | Name |
|---|---|
| Install from PyPI | agent-markdown-pdf |
| Run the CLI | mdtopdf |
| Import in Python | mdtopdf |
Check the machine:
mdtopdf doctor --jsonConvert a file:
mdtopdf convert report.md -o report.pdf --overwriteTry the bundled visual test document:
git clone https://github.com/ABClize/mdtopdf.git
cd mdtopdf
python -m pip install -e .[dev]
mdtopdf html examples/visual-test-en.md -o visual-test-en.html --overwrite
mdtopdf convert examples/visual-test-en.md -o visual-test-en.pdf --overwrite --jsonThe same visual test is also available in Chinese at
examples/visual-test-cn.md.
The bundled agent skill lives at
mdtopdf/skills/SKILL.md.
Use that file when another agent needs a compact runtime guide for mdtopdf.
mdtopdf doctor --json
mdtopdf convert report.md -o report.pdf --overwrite --jsonUse HTML preview when layout needs a quick look:
mdtopdf html report.md -o report.html --overwrite --json
mdtopdf convert report.md -o report.pdf --overwrite --jsonconvert --json returns the input path, output path, file size, theme, font
check summary, warnings, and render method. If conversion fails in JSON mode,
the error is structured enough for an agent to show the command, explain the
likely cause, and retry after a fix.
The gallery below is rendered from the final PDF produced by
examples/visual-test-en.md. It shows the actual pages an agent can hand back
to a user: headings, callouts, tables, code, math, images, Mermaid, and
pagination.
| Page 1 | Page 2 |
|---|---|
![]() |
![]() |
| Page 3 | Page 4 |
![]() |
![]() |
| Page 5 | Page 6 |
![]() |
![]() |
Markdown -> markdown-it-py HTML -> theme/custom CSS -> WeasyPrint PDF
Mermaid rendering is optional. If a local mmdc command exists, Mermaid blocks
render to SVG. If it is missing, conversion still succeeds and Mermaid blocks
remain visible as highlighted code.
| Feature | Notes |
|---|---|
| JSON output | --json is available for conversion, HTML preview, doctor, and theme listing. |
| Environment checks | doctor --json checks Python imports, native WeasyPrint libraries, Windows DLL paths, Mermaid availability, and recommended fonts. |
| Local rendering | Markdown, CSS, math, Mermaid SVG generation, and PDF export stay on the machine. |
| HTML preview | Generate standalone HTML before PDF export for fast visual inspection. |
| Obsidian compatibility | Wikilinks, aliases, frontmatter hiding, comments, highlights, and typed callouts. |
| Document Markdown | Tables, task lists, footnotes, heading anchors, fenced code, and Pygments highlighting. |
| KaTeX math | Inline and block TeX render with bundled KaTeX assets, without a CDN. |
| Safe HTML default | Common inline document tags are allowed; unsafe raw HTML stays escaped unless opted in. |
| Python API | Convert Markdown strings or files from your own code. |
Render a PDF:
mdtopdf convert report.md -o report.pdf
mdtopdf convert report.md -o report.pdf --overwritePreview HTML:
mdtopdf html report.md -o report.html --overwriteSet document metadata and page chrome:
mdtopdf convert report.md -o report.pdf --title "Report"
mdtopdf convert report.md -o report.pdf --header "Report" --footer "Draft"
mdtopdf convert report.md -o report.pdf --no-header --no-footerUse extra CSS or resource lookup paths:
mdtopdf convert report.md -o report.pdf --css print.css
mdtopdf convert report.md -o report.pdf --base-url assets
mdtopdf convert report.md -o report.pdf --resource-dir attachmentsCustom CSS is the style extension point. Put document-specific rules in a CSS
file; fonts, spacing, colors, page rules, and code block styling all live there.
Use a system-installed font directly, or define @font-face for a local font
file. Relative URLs in CSS are resolved from the Markdown file's base URL, so
use --base-url when those assets live next to your document:
@font-face {
font-family: "Report Sans";
src: url("fonts/NotoSansSC-Regular.otf");
}
:root {
font-family: "Report Sans", "Noto Sans SC", "Source Han Sans SC", sans-serif;
}
code,
pre {
font-family: "Cascadia Code", "Liberation Mono", monospace;
}Then pass the CSS file:
mdtopdf convert report.md -o report.pdf --css print.css --base-url .During export, mdtopdf checks the final CSS font stacks. Missing fonts do not
stop PDF generation, but they are reported in CLI warnings and in the JSON
warnings field.
Return JSON:
mdtopdf --json convert report.md -o report.pdf --overwrite
mdtopdf doctor --json
mdtopdf themes list --jsonAllow raw HTML only for trusted local Markdown:
mdtopdf convert trusted.md -o trusted.pdf --unsafe-htmlfrom mdtopdf import (
markdown_file_to_html,
markdown_file_to_pdf,
markdown_to_html,
markdown_to_pdf,
)
rendered = markdown_to_html("# Report\n\n==highlight==")
print(rendered.html)
markdown_to_pdf("# Report\n\nBody", "report.pdf", title="Report", overwrite=True)
markdown_file_to_html("report.md", output_path="report.html", overwrite=True)
markdown_file_to_pdf("report.md", output_path="report.pdf", overwrite=True)mdtopdf supports:
- CommonMark
- Tables
- Strikethrough
- Task lists
- Footnotes
- Heading anchors
- Fenced code blocks with Pygments highlighting
- Obsidian-style
==highlight==marks - Obsidian-style
[[target|alias]]wikilinks - Obsidian-style
%%comment%%comments outside code - Obsidian/YAML frontmatter hiding at the start of the file
- Obsidian-style callouts such as
> [!note] Title - Safe inline HTML tags such as
<br>,<kbd>,<mark>,<sup>, and<sub> - TeX math through
$inline$,$$block$$, and commonamsmathenvironments - Mermaid diagrams through local
mmdc, when installed
Raw HTML is disabled by default except for the safe subset above. For trusted
local Markdown, pass --unsafe-html.
Install a persistent local renderer:
npm install -g @mermaid-js/mermaid-climdtopdf does not call Mermaid.ink and does not download Mermaid CLI through
npx during conversion. Run mdtopdf doctor --json to check whether Mermaid
rendering is available.
mdtopdf requires Python 3.10+ and installs its Python dependencies from PyPI:
click, markdown-it-py, mdit-py-plugins, pygments, latex2mathml,
matplotlib, mini-racer, and weasyprint.
WeasyPrint also needs native libraries such as Pango, GLib, and Cairo. Linux and macOS package managers usually provide them through system packages.
The default theme uses a PDFium-safe Latin-first font stack. Latin fonts are listed before CJK fonts so ASCII digits, dates, versions, and page numbers are not embedded into CJK font subsets that some Chrome/PDFium renderers handle poorly. Chinese text still falls back to the CJK side of the stack.
For Linux containers or agent sandboxes, use open fonts that can be installed from the distribution package manager. The recommended baseline is:
sudo apt-get install -y --no-install-recommends \
fontconfig \
fonts-liberation \
fonts-dejavu-core \
fonts-noto-cjk \
fonts-stix
fc-cache -fWith that setup, Linux output uses Liberation Sans / DejaVu Sans for Latin
text and digits, Noto Sans CJK SC for Chinese, and STIX for math fallback.
Microsoft YaHei and Segoe UI Emoji are not Linux runtime requirements and are
not installed or distributed by mdtopdf.
Code blocks prefer Cascadia Mono / Cascadia Code, then Consolas,
Noto Sans Mono CJK SC, Liberation Mono, and DejaVu Sans Mono. On Debian,
fonts-cascadia-code provides Cascadia fonts; on other Linux images, provide
Cascadia Code yourself or let the theme fall back to the installed monospace
fonts.
Emoji are rendered through the system emoji font. On Linux, prefer monochrome
Noto Emoji for stable PDF layout. Noto Color Emoji is easier to install from
many distro package managers and is safe to use as a fallback, but color emoji
often render too small or misaligned in WeasyPrint/Pango/Cairo/PDFium output.
On Windows, install the native libraries separately. A common MSYS2 setup is:
winget install MSYS2.MSYS2Then install Pango from an MSYS2 MINGW64 shell:
pacman -S mingw-w64-x86_64-pangoFinally, point WeasyPrint at the DLL directory from PowerShell. Adjust the path if MSYS2 is installed somewhere else:
setx WEASYPRINT_DLL_DIRECTORIES "C:\msys64\mingw64\bin"Run this after installation. The JSON output also reports whether recommended Latin, CJK, emoji, monospace, and math fallback fonts are present:
mdtopdf doctor --jsongit clone https://github.com/ABClize/mdtopdf.git
cd mdtopdf
python -m pip install -e .[dev]
python -m pytest tests/ -qBuild and check the package:
python -m build
python -m twine check dist/*MIT. Bundled KaTeX assets are also distributed under the MIT license; see
mdtopdf/vendor/katex/LICENSE.
mdtopdf does not bundle CJK body fonts, emoji fonts, or proprietary system
fonts. The default theme references local system fonts such as Segoe UI,
Microsoft YaHei, PingFang SC, Segoe UI Emoji, Noto Sans CJK SC,
Noto Emoji, Noto Color Emoji, Cascadia Code, and Consolas, but those font files come from
the user's operating system or runtime environment. Public Linux images should
prefer the open-font baseline above.






