Markdown → PDF via Pandoc + Typst
This skill describes the open-source pandoc + typst workflow. (The youwu.today article it was modeled on advertises a paid Typst template; this skill works without that template, and explains how to plug one in if the user has it.)
Prerequisites
pandoc --version # need >= 3.1.7 for built-in --pdf-engine=typst
typst --version # any recent version (0.10+)
If missing on NixOS, run via nix-shell -p pandoc typst --run '...'.
On other systems suggest the user install via their package manager
(brew install pandoc typst, apt install pandoc && cargo install typst-cli,
etc.) — do not install silently.
Basic conversion
pandoc input.md -o output.pdf --pdf-engine=typst
That single command handles: headings, lists, tables, code blocks with
syntax highlighting (via Pandoc's built-in highlighter), images, footnotes,
blockquotes, and inline/display math ($...$ and $$...$$).
Common options
Pass Typst document variables with -V:
pandoc input.md -o output.pdf \
--pdf-engine=typst \
-V papersize=a4 \
-V margin-top=2cm -V margin-bottom=2cm \
-V margin-left=2.5cm -V margin-right=2.5cm \
-V mainfont="DejaVu Serif" \
-V monofont="JetBrains Mono" \
-V fontsize=11pt \
-V linkcolor=blue \
--toc --toc-depth=3 \
--number-sections
Equivalent settings can live in a YAML metadata block at the top of the .md:
---
title: "My Document"
author: "Author Name"
date: "2026-05-11"
papersize: a4
mainfont: "DejaVu Serif"
monofont: "JetBrains Mono"
fontsize: 11pt
toc: true
toc-depth: 3
numbersections: true
---
CJK text
Typst handles CJK natively — just pick a font that has the glyphs. Do not
use the LaTeX-style CJKmainfont variable; Typst's pandoc writer uses
mainfont for everything. For mixed CJK + Latin, choose a CJK font that
also covers Latin (e.g. Noto Sans CJK), or set mainfont to a CJK font:
pandoc cn.md -o cn.pdf --pdf-engine=typst \
-V mainfont="Noto Serif CJK SC" \
-V monofont="Noto Sans Mono CJK SC"
List installed fonts Typst can see:
typst fonts
Code highlighting
Pandoc's default highlight style works. Pick a different one with
--highlight-style=<name>. Available: pygments, tango, espresso,
zenburn, kate, monochrome, breezedark, haddock, or a path to a
custom .theme file. Disable with --no-highlight.
pandoc input.md -o out.pdf --pdf-engine=typst --highlight-style=tango
Custom Typst template
For full control over layout, pass --template=path/to/template.typ. Pandoc
fills the template with metadata and the rendered body. A minimal template:
#set document(title: "$title$", author: "$author$")
#set page(paper: "$papersize$", margin: 2cm)
#set text(font: "$mainfont$", size: $fontsize$)
#align(center)[
#text(size: 1.6em, weight: "bold")[$title$] \
#text(size: 1em)[$author$ · $date$]
]
$body$
Use pandoc --print-default-template=typst > default.typ to dump and
customize Pandoc's stock template.
If the user has the paid youwu.today themekit package, they install it
as a Typst local package at $XDG_DATA_HOME/typst/packages/local/themekit/<version>/
(or platform equivalent: ~/.local/share/typst/packages/local/... on Linux,
~/Library/Application Support/typst/packages/local/... on macOS,
%APPDATA%\typst\packages\local\... on Windows) and pass
--template=$XDG_DATA_HOME/typst/packages/local/themekit/<version>/pandoc/<theme>.typ.
Math, tables, images
- Math: works out of the box. Pandoc translates LaTeX math into Typst
math. Display:
$$...$$. Inline:$...$. - Tables: grid tables and pipe tables both render. For column widths,
use a YAML block or Pandoc's
{tbl-colwidths="[20, 80]"}attribute. - Images:
{width=80%}— relative paths are resolved against the input file's directory. SVG, PNG, and JPEG are supported.
Gotchas
-
mainfontis mandatory, even for Latin-only docs. Omitting it triggerserror: font fallback list must not be emptyfrom Typst's pandoc template. Set e.g.-V mainfont="DejaVu Serif". -
Nested template keys (
margin.x,margin.y) need a YAML metadata file. Pandoc's-M margin.x=1.5cmdoes not create nested structures — it sets a flat key called literallymargin.x. To set margins use:# meta.yaml margin: x: 1.5cm y: 1.5cmthen pass
--metadata-file=meta.yaml. -
Pandoc wraps every table in
align(center)[#table(...)], so columns declaredalign: autoinherit center alignment from the wrapper — text columns look centered. Override with a header file (-H header.typ):#show figure.where(kind: table): it => align(left, it.body) #show table: set align(left)Explicit
align: rightcells (numeric columns) keep their right alignment. -
Variable fonts (e.g. Noto Sans CJK on NixOS) work despite a warning. Typst prints
variable fonts are not currently supported and may render incorrectly, but the PDF compiles and CJK glyphs render correctly. Do not abort on this warning. -
Prefer this skill over
any2pdfwhen CJK fonts are only available as variable OTF/TTC. ReportLab (any2pdf's engine) cannot load fonts with PostScript (CFF) outlines and falls back to Helvetica, producing □/black rectangles for CJK. Typst handles them natively.
Debugging
If conversion fails, run with --verbose and inspect the intermediate
Typst source:
pandoc input.md -o out.typ -t typst # produce .typ instead of .pdf
typst compile out.typ # then compile manually to see errors
Common errors:
- "font not found" — check
typst fontsfor the exact name; Typst is picky about font family strings (case- and space-sensitive). - "unknown variable" — a
-Vname doesn't match anything Pandoc's typst template references. Dump the template (see above) to see valid names. - Pandoc < 3.1.7 —
--pdf-engine=typstis unrecognized; upgrade Pandoc.