Markdown to PDF converter in the style of Obsidian Markdown export.
2.5K
A Docker image that turns Markdown into a clean, print-ready, Obsidian-style PDF. Two modes from one image:
serve) — web UI + HTTP API with options for font, zoom, line width and orientation.Both share the same pipeline: Markdown → HTML (markdown-it) → PDF via Chromium (page.pdf(), A4, printBackground). Custom callouts with a raw SVG icon work exactly like the built-in ones.
docker build -t md2pdf .
Mount your working directory, file in, PDF out:
docker run --rm -v "$PWD:/data" md2pdf note.md
# -> note.pdf
docker run --rm -v "$PWD:/data" md2pdf note.md output.pdf
Paths are relative to the mounted /data.
| Option | Short | Env | Default | Values |
|---|---|---|---|---|
--font | -f | MD2PDF_FONT | arial | arial (Liberation Sans), inter, plex (IBM Plex Sans) |
--zoom | -z | MD2PDF_ZOOM | 75 | 10–200 (percent; lower = more content per page) |
--measure | -m | MD2PDF_MEASURE | readable | readable (~80 chars, centered reading column), full (full width) |
--orientation | -l / --landscape | MD2PDF_ORIENTATION | portrait | portrait (A4 tall), landscape (A4 wide) |
The matching monospace font is selected automatically for the chosen font.
docker run --rm -v "$PWD:/data" md2pdf note.md --font inter --zoom 90 --landscape
md2pdf() { docker run --rm -v "$PWD:/data" -v "$HOME/.md2pdf-snippets:/snippets" md2pdf "$@"; }
# then: md2pdf note.md --font inter --zoom 90
The same image starts an HTTP service with serve (default port 8080), serving both a web UI and an API. A Chromium instance stays warm (no cold start per request).
docker compose up -d --build # -> http://localhost:8080
# or without Compose:
docker run --rm -p 8080:8080 md2pdf serve
In the web UI, drag one or more .md files onto the drop zone — each is converted and downloaded immediately. Font, zoom, line width and orientation are selectable.
POST /convert returns application/pdf. Options font, zoom, measure, orientation work as in the CLI. Markdown can be sent raw, as a file upload, or as JSON:
# Raw Markdown (options as query parameters)
curl -X POST "http://localhost:8080/convert?font=inter&zoom=90&orientation=landscape" \
--data-binary @note.md -H "Content-Type: text/markdown" -o note.pdf
# File upload (multipart)
curl -X POST http://localhost:8080/convert \
-F [email protected] -F font=arial -F zoom=75 -o note.pdf
# JSON
curl -X POST http://localhost:8080/convert \
-H "Content-Type: application/json" \
-d '{"markdown":"# Hello","font":"inter","zoom":100}' -o note.pdf
Errors (missing Markdown, invalid option, file too large) come back as JSON { "error": "…" } with an appropriate HTTP status.
| Route | Purpose |
|---|---|
GET / | Web UI |
POST /convert | Markdown → PDF (multipart, JSON or raw text) |
GET /healthz | Liveness check |
| Variable | Purpose | Default |
|---|---|---|
PORT | HTTP port | 8080 |
MD2PDF_MAX_BYTES | max upload/body size (bytes) | 5242880 (5 MB) |
MD2PDF_DEFAULT_FONT | default font (arial|inter|plex) | arial |
MD2PDF_DEFAULT_ZOOM | default zoom (10–200) | 75 |
MD2PDF_DEFAULT_MEASURE | default line width (readable|full) | readable |
The DEFAULT_* variables only set the starting values; every option stays overridable per request.
Callouts use the GitHub alert syntax:
> <p data-quote-type="tip">Tip</p> Title
> Callout content.
Custom types with their color and icon come from CSS snippets. Example snippets (including AI callouts) are baked into the image; mount your own folder to /snippets — mounted files override the baked-in ones:
docker run --rm -v "$PWD:/data" -v "$HOME/my-snippets:/snippets" md2pdf note.md
.callout[data-callout="key"] {
--callout-color: 222, 115, 86; /* R, G, B */
--callout-icon: 'lucide-sparkles'; /* Lucide name OR raw SVG */
}
title: or first #) and page number[[Target]], [[Target|Alias]] rendered as text links[^1] as a numbered list at the end of the document==text== as <mark>%%…%% (inline and block) are removedNot included: Mermaid, LaTeX math, and resolving embedded file contents.
Zoom, line width and orientation are controllable via options. Paper format and margins live in src/render.js (ORIENTATIONS, PAGE, format: "A4"); rebuild after changes.
Content type
Image
Digest
sha256:d9b4bc87e…
Size
243 MB
Last updated
25 days ago
docker pull dettmering/md2pdf