Skip to main content

md2pdf logo

Convert Markdown files to PDF with styles.

GitHub Actions Workflow Status PyPI - Version

Installation

The easiest way to test md2pdf is to use uv:

$ uv tool install md2pdf[cli]

Nota bene: ensure, Weasyprint is fully functional before using md2pdf. You will find installation instructions in the project documentation: https://doc.courtbouillon.org/weasyprint/stable/first_steps.html#installation

Usage

As a CLI

 Usage: md2pdf [OPTIONS]                                                                              
                                                                                                      
 Markdown to PDF conversion tool with styles… and templates!                                          
                                                                                                      
╭─ Options ──────────────────────────────────────────────────────────────────────────────────────────╮
│ --input               -i      PATH     Markdown source file path (can be used multiple times).     │
│ --output              -o      PATH     PDF output file path (when a single md input is used).      │
│ --css                 -c      PATH     Input CSS file.                                             │
│ --extras              -e      TEXT     Extra markdown extension to activate (cam be used multiple  │
│                                        times).                                                     │
│ --config              -C      TEXT     Markdown extensions configuration (as a JSON string).       │
│ --workers             -W      INTEGER  Number of parallel workers to start. [default: 4]           │
│ --version             -V               Display program version.                                    │
│ --install-completion                   Install completion for the current shell.                   │
│ --show-completion                      Show completion for the current shell, to copy it or        │
│                                        customize the installation.                                 │
│ --help                                 Show this message and exit.                                 │
╰────────────────────────────────────────────────────────────────────────────────────────────────────╯

For example, try to generate the project documentation with:

$ md2pdf -i README.md 

A README.pdf should have been generated.

Optionally, you may load an external style sheet:

$ md2pdf \
    --css examples/custom-styles.css \
    -i README.md

And activate markdown extensions from PyMdown:

$ md2pdf \
    --css examples/custom-styles-with-pygments.css \
    --extras 'pymdownx.emoji' \
    -i README.md

Code blocks should be properly rendered when this extension is active.

As a library

If you have added md2pdf as a dependency for your python project, you can use md2pdf in your code, like:

from md2pdf.core import md2pdf

md2pdf(pdf,
       md=None,
       raw=None,
       css=None,
       base_url=None,
       extras=[],
       context={"foo": 1}
)

Function arguments:

  • pdf: output PDF file path
  • raw: input markdown raw string content (can contain Jinja instructions)
  • md: input markdown file path (can contain Jinja instructions)
  • css: input styles path (CSS)
  • base_url: absolute base path for markdown linked content (as images)
  • extras: markdown extra extensions that should be activated
  • context: variables to inject to rendered Jinja template

With Docker

Considering docker is installed, pull the latest Debian-based image:

$ docker pull jmaupetit/md2pdf:latest

And try to run a smoke test with this image:

$ docker run --rm -t \
    -v $PWD:/wrk \
    -u "$(id -u):$(id -g)" \
    -w /wrk \
    jmaupetit/md2pdf:latest -i README.md

There is also a smaller Alpine-based image tagged alpine. For a full list of available tags, check the project's DockerHub repository.

Use Jinja templates as input

Your input markdown file or raw content can include Jinja template tags, and context can be given in a frontmatter header:

---
groceries:
  - name: apple
    quantity: 4 
  - name: orange 
    quantity: 10
  - name: banana 
    quantity: 6
---

# Groceries

| Item | Quantity |
| ---- | -------- |
{% for item in groceries -%}
| {{ item.name }} | {{ item.quantity }} |
{% endfor %}

Or directly as a md2pdf argument (see library usage).

You can test this example using:

$ md2pdf \
    --css examples/gutenberg-modern.min.css \
    -i examples/my-music.md.j2 \
    -o examples/my-music.pdf

Contributing

Hacking

Clone this project first:

$ git clone git@github.com:jmaupetit/md2pdf.git

Install md2pdf along with its dependencies (using uv):

$ cd md2pdf
$ make bootstrap

Running the test suite

To run the test suite:

$ make test

Lint the code via:

$ make lint

Ease your life

If you are familiar with GNU Make, we also automate daily tasks using this lovely tool:

$ make help

License

md2pdf is released under the MIT License. See the bundled LICENSE file for details.

Release files for md2pdf 3.1.1

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

Source distribution (sdist)

Source distribution for md2pdf 3.1.1
File Size Uploaded
md2pdf-3.1.1.tar.gz 128.0 kB Details

Built distribution (wheel)

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

Total release size: 137.2 kB

Release files / md2pdf-3.1.1.tar.gz

Download URL md2pdf-3.1.1.tar.gz
Size 128.0 kB
Tags Source
SHA-256 checksum
How to use checksums
6772d02468c131ff315601564d60c55b8e7e5e084ea7224f8f4315958df32bcf
BLAKE2b-256 checksum
How to use checksums
8ebdc26f2b30c22466df118340faa9d08e0308feee17ca290ae3d831d5ddd12c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 31, 2026.

Transparency log

Release files / md2pdf-3.1.1-py3-none-any.whl

Download URL md2pdf-3.1.1-py3-none-any.whl
Size 9.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1d91509954b10cb6de0d2f079aa312177a55a39e28816acbf2ac785cb498f84f
BLAKE2b-256 checksum
How to use checksums
c3652c782f72152f53a4c16c80ebeed195ffc41688b6ad2a67ae1b7185767dd4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 31, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

3.1.1 This release

2 release files

3.1.0

2 release files

3.0.1

2 release files

3.0.0

2 release files

2.1.0

2 release files

2.0.0

2 release files

1.0.1

1 release file

0.6

1 release file

0.5

2 release files

0.4

2 release files

0.3

1 release file

0.2.4

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