Turn Markdown into beautiful PDFs via named use-case profiles — so humans,
CLIs, and AI agents (MCP) get consistent, predictable output without the
pandoc/LaTeX iteration pain. Binary name: symprint. Part of the Symaira
ecosystem.
You write semantic Markdown + a small frontmatter contract. The profile owns every visual decision (colours, heading sizes, cover page, page numbers, DIN 5008 geometry, PDF/A + PDF/UA). What you write is what you get.
Output of symprint render examples/behoerde.md -o anhoerung.pdf — DIN 5008
layout, PDF/A-2a + PDF/UA-1, rendered with the behoerde profile.
$ symprint render brief.md
✓ brief.pdf
profile brief · engine typst 0.15.0 · PDF (tagged) · 42.3 kB · 180 ms
$ symprint render anhoerung.md -p behoerde
✓ anhoerung.pdf
profile behoerde · engine typst 0.15.0 · PDF/A-2A+UA-1 · 38.7 kB · 195 ms
$ symprint validate report.md
✓ valid for profile "report"
- Profiles, not knobs. Pick
brief,behoerde,report,rechnung, ormeeting. The profile fixes the look; the document stays clean. - One engine, no TeX Live. Renders with Typst — a
single Apache-2.0 binary reached over
PATH. symprint itself stays a single CGO-free Go binary. - Behörde-grade output. The
behoerdeprofile emits PDF/A-2a + PDF/UA-1 (tagged, accessible, archivable) in one step and fails closed if the document isn't accessible — exactly what E-Government / BITV 2.0 needs. - DIN 5008 letters.
briefandbehoerdelay out the Anschriftfeld, Infoblock, and fold/hole marks for window envelopes. - Strict, discoverable contract. Unknown frontmatter keys are rejected, so typos fail loudly instead of silently changing output.
- Reproducible.
--reproducibleyields byte-identical PDFs. - MCP server for AI agents —
render_pdf,list_profiles,validate_document,doctorover stdio.
This is an early scaffold (v0.3.0). What works today, verified end-to-end against Typst 0.15.0:
- The full Go pipeline: strict frontmatter contract, profile registry, validation, engine detection, Typst shell-out, reproducible output.
- CLI:
render,profiles,validate,doctor,config,mcp,version. - MCP stdio server with four tools.
- All five profiles render.
reportproduces cover + TOC + headers + page numbers;behoerdeandmeetingproduce a verified PDF/A-2a + PDF/UA-1 file (pdfaid+pdfuaid+StructTreeRootpresent). - DIN 5008 letter geometry validated against KOMA-Script (LPPL) source values.
veraPDFCI gating validates PDF/A-2a + PDF/UA-1 conformance on every push/PR.- Brand fonts (Inter) embedded via
go:embedfor machine-independent output.
What is not done yet (see docs/architecture.md):
- The
rechnungVAT/GiroCode logic (currently a scaffold). - More profiles, and the pandoc/WeasyPrint fallback paths, are roadmap items.
# From source
git clone https://github.com/danieljustus/symaira-print.git
cd symaira-print
make build # → ./symprint (CGO_ENABLED=0)
# Engine (required for rendering)
brew install typst # macOS
# Windows: winget install --id Typst.Typst
# Cross-platform: cargo install typst-cli
./symprint doctor # checks typst + optional toolsAfter the first release, install via Go or Homebrew:
go install github.com/danieljustus/symaira-print/cmd/symprint@latest
brew install danieljustus/tap/symprint# 1. Write a document (frontmatter selects the profile)
cat > brief.md <<'EOF'
---
profile: brief
date: 30.06.2026
recipient:
name: "Firma Beispiel GmbH"
address: ["Industrieweg 7", "54321 Ort"]
betreff: "Angebot Nr. 2026-1"
---
Sehr geehrte Damen und Herren,
vielen Dank für Ihre Anfrage …
EOF
# 2. Render
symprint render brief.md # → brief.pdf
symprint render anhoerung.md -p behoerde # PDF/A-2a + PDF/UA-1
symprint validate report.md # check the contract without rendering
symprint profiles # list profiles + guaranteesSee examples/ for one document per profile.
symprint render <input.md> # render Markdown to PDF
symprint validate <input.md> # validate the frontmatter contract
symprint profiles [name] # list profiles or inspect one profile
symprint doctor # check typst, pandoc, and veraPDF availability
symprint config init # write ~/.config/symprint/config.toml
symprint config path # print the active config path
symprint mcp # start the stdio JSON-RPC server
symprint version # print the injected versionGlobal --json emits machine-readable output where supported.
| Profile | Use case | Output guarantees |
|---|---|---|
brief |
DIN 5008 letter (Form B) | tagged PDF |
behoerde |
Authority letter (DIN 5008 Form A) | PDF/A-2a + PDF/UA-1, DIN window |
report |
Report with cover + TOC | tagged PDF, themed headings, page numbers |
rechnung |
German invoice (data-driven) | tagged PDF (scaffold) |
meeting |
Meeting minutes | PDF/A-2a + PDF/UA-1 |
Full reference: docs/profiles.md · contract: docs/markdown-contract.md.
symprint mcp # stdio JSON-RPC 2.0 serverTools: render_pdf(markdown, output_path, [profile], [pdf_standard], [reproducible]),
list_profiles(), validate_document(markdown, [profile]), doctor().
All logs go to stderr; stdout carries only protocol messages.
Configuration follows XDG and environment-variable conventions:
- file:
~/.config/symprint/config.toml - prefix:
SYMPRINT_* - defaults: profile
report,typstresolved fromPATH
Create a starter file with symprint config init. CLI flags and MCP arguments
always win over config defaults.
symprint is a thin Go CLI/MCP shell around a typesetting engine:
Markdown + frontmatter ─▶ internal/press ─▶ Typst (PATH) ─▶ PDF
│ contract │ cmarker (MD→Typst, in-engine)
│ profiles │ --pdf-standard a-2a,ua-1
│ validation └ SOURCE_DATE_EPOCH (reproducible)
└─ engine detect + graceful fallback
Design rationale, the engine decision (Typst vs pandoc/LaTeX vs CSS), and the phased roadmap are in docs/architecture.md.
A native SwiftUI GUI lives in client/, built on the shared
symaira-appkit package. It wraps the symprint binary (via
SymairaCLIRunner) for a point-and-click render flow.
make client-gen # xcodegen generate (needs xcodegen)
make client-build # xcodebuild the Symprint scheme
make client-dmg # package a .dmg (scripts/package-dmg.sh)See AGENTS.md for the client's conventions.
make build # build ./symprint
make test # go test ./...
make test-race # race detector
make lint # go fmt + go vet
make examples # render example PDFs; requires typst on PATHThe public core intentionally contains no Cloud/Pro/billing code. Keep the
rendering pipeline standalone-first: Typst is executed from PATH, not bundled
or linked.
Apache-2.0 (see LICENSE and NOTICE). The Typst engine is not bundled; it is installed separately and is itself Apache-2.0.
