Typoprint
Markdown in. Beautiful PDFs out.
Write in Markdown. Print like a publisher.
Typoprint is a small Python library and CLI for offline Markdown printing, powered by Typst. Use it in a terminal, API, worker, or script. No browser, TeX installation, external compiler process, or network connection is needed to render.
Python 3.13+ is required. Rendering resources, Mermaid support, and fonts ship with the package. Windows, Linux with glibc, and macOS are supported; Alpine/musl is outside the tested wheel platforms.
Install
From PyPI:
uv tool install typoprint # CLI in its own environment (or: pipx install typoprint)
pip install typoprint # library in the current environment (or: uv add typoprint)
From a checkout:
uv sync --locked
uv run typoprint --help
Documentation: https://esandorfi.github.io/typoprint/ (Python API in docs/python.md). Contributions are welcome;
see CONTRIBUTING.md. Releases are published by maintainers; see
releasing.
CLI
typoprint notes.md -o notes.pdf
typoprint docs/ --recursive --cover -o handbook.pdf
typoprint --order docs/README.md --strict --dry-run
typoprint docs/ --config typoprint.toml --preset handbook
typoprint notes.md --template report --lang en --paper us-letter
typoprint - -o - < notes.md > notes.pdf
The last example uses a POSIX shell or cmd.exe for byte-preserving redirection.
In PowerShell, use file inputs and -o notes.pdf; PowerShell versions differ in
how pipelines handle native binary output. Stdin is UTF-8; -o - writes only PDF
bytes to stdout. Paths, warnings, and errors go to stderr. Errors return code 2.
Each file becomes a chapter. A directory contributes README.md, then other
Markdown files in natural order (chapter-2 before chapter-10). --recursive
adds subfolders. --exclude accepts repeatable relative globs; an explicitly named
file is always included. --by-folder opens parts at folder changes.
--order CONTENTS.md reads local Markdown links under each ## heading as chapter
order and part names. Folder links expand recursively. Code, images, web links,
and links outside the contents folder do not place pages. Unplaced pages follow
with a warning; --strict refuses them. The contents file itself prints first
unless excluded. This preserves the source feature's inline-link parser; reference
links do not place pages. See CLI behavior.
Python
from typoprint import Settings, render, render_files, write_pdf
result = render("# Hello\n\nReady for print.")
write_pdf(result, "hello.pdf")
result = render_files(
["docs"],
recursive=True,
exclude=["drafts"],
settings=Settings(template="report", lang="en"),
)
RenderResult.content is PDF bytes; RenderResult.warnings is a tuple of strings.
Rendering never writes an output file. write_pdf replaces the destination
atomically after flushing the complete PDF. It requires an existing output folder.
For multiple in-memory chapters and explicitly supplied images:
from pathlib import Path
from typoprint import Chapter, Document, render
document = Document(
chapters=(
Chapter(name="intro.md", markdown="# Intro\n\n"),
Chapter(name="results.md", markdown="# Results\n\nSee [intro](intro.md)."),
)
)
result = render(document, assets={"images/chart.png": Path("chart.png").read_bytes()})
Asset names are relative POSIX paths. Images resolve relative to each chapter's name. In-memory rendering sees only explicitly supplied assets, never paths on the host. Filesystem rendering resolves images inside the common input root, rejects symlinks escaping it, and snapshots referenced images before compilation. Remote images are refused. Mermaid blocks render locally; invalid or unreadable diagrams fail explicitly.
The public API exports Chapter, Document, Settings, PrintPlan, PlannedFile,
RenderResult, Transform, Validator, TypoprintError, ConfigurationError,
RenderError, RuleError, and the five main functions. python -m typoprint
provides the same CLI. See runnable HTTP API and
process worker examples.
Smart settings
Create typoprint.toml:
[defaults]
template = "editorial"
lang = "fr"
paper = "a4"
[presets.handbook]
cover = true
toc = true
toc_depth = 2
title = "Engineering handbook"
[presets.brief]
template = "report"
cover = false
toc = false
Precedence is explicit overrides → selected preset → defaults → supported
front-matter metadata → built-in defaults. An explicit false stays false.
Unspecified fields inherit; None for optional metadata also inherits.
The CLI looks only for typoprint.toml in its working directory. --config selects
another file; --no-config disables discovery. Library calls never discover a file:
from typoprint import Settings, load_settings, render
settings = load_settings("typoprint.toml", preset="handbook")
result = render("# Introduction", settings=settings, overrides=Settings(cover=False))
Defaults are editorial, French, A4, no cover, contents for multiple chapters,
and contents depth 2. Titles use front matter, then the first H1, then the filename.
Front matter may supply title, subtitle, author, date, and version; chapter
dates and versions remain local to each chapter. Dates are never inferred from
the clock, filesystem timestamps, or Git.
Configuration can also specify link_base, font_paths, and template_options.
Paths in TOML belong to the TOML file's folder. Unknown keys and presets fail.
Nested template_options replace as a unit when layering settings. Dry runs emit
JSON containing input order, parts, effective metadata/settings, and warnings.
Extend
Use built-in editorial for book-style reading or report for compact sans-serif
reports. Supply a template directory with template.typ to define another layout.
It exports document(meta, body), chapter(meta, body), and diagram(body).
Built-in layouts and foundations are reusable through the versioned local Typst
package. Custom options live in meta.template_options.
Python callers may also supply explicit transformations and validators:
from typoprint import RuleError, render
def require_summary(markdown: str) -> None:
if "## Summary" not in markdown:
raise RuleError("A Summary section is required")
result = render(
"# Draft\n\n## Summary\n\n{{product}} is ready.",
transforms=[lambda text: text.replace("{{product}}", "Typoprint")],
validators=[require_summary],
)
All transforms run before validators for each chapter. Sources remain unchanged. Callbacks and templates are trusted developer code, supplied by the application; TOML and Markdown never import Python callbacks. Markdown cannot execute embedded Typst. See the extension guide and starter template.
Reproducibility and embedding
With identical inputs, settings, fonts, templates, and pinned dependencies, repeated renders produce identical PDF bytes. System fonts are ignored. French spacing, local links with page references, footnotes, tables, code highlighting, and Mermaid share one pipeline across layouts. PDF accessibility tags remain enabled; this does not certify every document against an accessibility standard.
Library calls are synchronous. An async API should offload rendering from its event loop. A worker can use separate processes for CPU parallelism and caller-managed timeouts. Each call owns its compiler and temporary files; callback state remains the caller's responsibility. Compiler errors and warnings can quote Markdown, so HTTP adapters should return generic messages and keep detailed diagnostics private. Custom templates can execute Typst and custom Python callbacks have ordinary Python privileges; only deploy extensions you trust. Bundle dependencies locally before offline use. Automatic content-aware layout selection, LaTeX math rendering, arbitrary HTML/CSS, remote asset fetching, a hosted service, and TypeScript bindings are outside this release.
Develop
uv sync --locked
uv run ruff format --check .
uv run ruff check .
uv run ty check
uv run pytest
uv build
Verification describes clean-wheel and offline checks. MIT covers first-party code. Bundled packages and fonts retain their own licenses; see third-party notices.
Authors and maintainers
Created and maintained by Emmanuel Sandorfi, with help from contributors. Issues and pull requests are welcome; see CONTRIBUTING.md and the security policy.
Metadata
Release files for typoprint 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| typoprint-0.1.0.tar.gz | 4.4 MB | With provenance | Details for typoprint-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| typoprint-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for typoprint-0.1.0-py3-none-any.whl |
Total release size: 8.8 MB
Release files / typoprint-0.1.0.tar.gz
| Download URL | typoprint-0.1.0.tar.gz |
|---|---|
| Size | 4.4 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2d10447d98c666457f988e85c8541f87103dfb34a1b6683333551e7cd5b080a6
|
|
BLAKE2b-256 checksum How to use checksums |
9506e0ca91411e15d33b7af46885ba2b517ba3e3f9346116318cdad2d17fa6ca
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 31, 2026.
Transparency logRelease files / typoprint-0.1.0-py3-none-any.whl
| Download URL | typoprint-0.1.0-py3-none-any.whl |
|---|---|
| Size | 4.4 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f3eaee342cf33828e49e07581d8264243bbf1a6fd6814f682b94b8fa6f5c2f9c
|
|
BLAKE2b-256 checksum How to use checksums |
09fb696fb56a4f9023c719e47d82593342dfafa550addca3b4bcd8ff0900ec80
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 31, 2026.
Transparency logSecurity for typoprint 0.1.0
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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for typoprint-0.1.0.tar.gz
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 Oct 31, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.