Python API reference¶
Typoprint's library and CLI share one rendering pipeline. Install with
pip install typoprint (see Install). Everything below is importable
from the top-level package:
from typoprint import Chapter, Document, Settings # inputs
from typoprint import render, render_files, plan_files, write_pdf # actions
from typoprint import load_settings # configuration
from typoprint import PrintPlan, PlannedFile, RenderResult # results
from typoprint import Transform, Validator # callback types
from typoprint import TypoprintError, ConfigurationError, RenderError, RuleError
Calls are synchronous, offline, and isolated: each call owns its compiler and temporary files, and never modifies your sources or writes an output file.
Quick start¶
from typoprint import render, write_pdf
result = render("# Hello\n\nReady for print.")
write_pdf(result, "hello.pdf")
RenderResult.content is the PDF as bytes; RenderResult.warnings is a tuple
of strings.
Choose your entry point¶
| You have… | Use |
|---|---|
| A Markdown string in memory | render("# Title…") |
| Several in-memory chapters, maybe with images | render(Document(...), assets=...) |
| Files or folders on disk | render_files(["notes.md"]) |
| A need to inspect order first | plan_files(...) then render_files(plan) |
Render a string¶
from typoprint import Settings, render
result = render("# Hello\n\nReady for print.")
result = render(
"# Hello",
settings=Settings(template="report", lang="en", paper="us-letter", cover=True),
)
Render files and folders¶
render_files takes the same discovery options as the CLI.
from typoprint import Settings, render_files, write_pdf
result = render_files(["notes.md"]) # one file
result = render_files(["intro.md", "usage.md", "faq.md"]) # chosen order
result = render_files(["docs"], recursive=True) # a folder tree
result = render_files(["docs"], recursive=True, by_folder=True) # parts per folder
result = render_files(["docs"], recursive=True, exclude=["drafts", "*/internal"])
result = render_files(order="docs/CONTENTS.md", strict=True) # contents map
result = render_files(
["docs"], recursive=True, settings=Settings(template="report", lang="en")
)
write_pdf(result, "handbook.pdf")
order replaces paths, recursive, and by_folder; strict requires order.
See contents maps for the map format.
Warnings from planning (for example unplaced pages) are included in
result.warnings.
Inspect before printing¶
plan_files is the Python equivalent of --dry-run: it resolves the exact file
order and part openings without compiling. Pass the plan back to render_files
to print precisely what you inspected.
from typoprint import plan_files, render_files, write_pdf
plan = plan_files(["docs"], recursive=True, by_folder=True)
print(plan.root, plan.warnings)
for planned in plan.files:
print(planned.part, planned.path)
write_pdf(render_files(plan), "handbook.pdf")
A PrintPlan replaces discovery options: passing recursive, exclude, order,
by_folder, or strict together with a plan raises ConfigurationError.
Multiple chapters and images in memory¶
from pathlib import Path
from typoprint import Chapter, Document, render
document = Document(
chapters=(
Chapter(
name="intro.md",
part="Basics",
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()})
Chapter.nameis a relative POSIX path (no.., drive, or backslash) and must be unique within aDocument. Chapter names define internal links.Chapter.partopens a named part (book section) at that chapter.- Images resolve relative to each chapter's name. In-memory rendering sees only the
assetsyou supply, never host paths. Remote images are refused. link_baseis unavailable for in-memory documents.
Settings¶
Settings is an immutable, validated model. Unknown fields are rejected.
| Field | Type | Default | Meaning |
|---|---|---|---|
template |
str |
"editorial" |
"editorial", "report", or a template directory |
title, subtitle, author, date, version |
str \| None |
None |
Metadata; None inherits front matter |
lang |
"fr" \| "en" |
"fr" |
Typography language |
paper |
"a4" \| "us-letter" |
"a4" |
Paper format |
cover |
bool |
False |
Cover page |
toc |
bool \| None |
None |
Contents; None shows it for multiple chapters |
toc_depth |
int (1–6) |
2 |
Contents levels |
link_base |
str \| None |
None |
HTTP(S) folder publishing the sources (files only) |
font_paths |
tuple[Path, ...] |
() |
Extra local font directories |
template_options |
dict[str, JSON] |
{} |
Options read by templates as meta.template_options |
Titles come from title, then front matter, then the first H1, then the filename.
Front matter may supply title, subtitle, author, date, and version.
Dates are never inferred from the clock, file timestamps, or Git.
Layering settings¶
render and render_files accept both settings (a base) and overrides. Only
fields you explicitly set on overrides win, so False stays False:
from typoprint import Settings, render
house_style = Settings(template="report", lang="en", cover=True)
result = render("# Memo", settings=house_style, overrides=Settings(cover=False))
template_options replace as a unit rather than merging key by key.
Load a project file¶
Library calls never discover typoprint.toml; load it explicitly. Relative paths in
the TOML belong to the TOML file's folder. Unknown keys and presets raise
ConfigurationError.
from typoprint import Settings, load_settings, render
settings = load_settings("typoprint.toml") # [defaults] only
settings = load_settings("typoprint.toml", preset="handbook") # plus a preset
result = render("# Introduction", settings=settings, overrides=Settings(cover=False))
Precedence: overrides → preset → [defaults] → front matter → built-in defaults.
See the CLI page for the TOML layout.
Custom templates and options¶
from typoprint import Settings, render
result = render(
"# Custom",
settings=Settings(
template="./my-template", # directory with template.typ
template_options={"accent": "#165d8d", "banner": True},
font_paths=("./fonts",),
),
)
See Templates and content rules for writing a template.
Transforms and validators¶
Explicit callbacks run on each chapter's Markdown: all transforms first, in
order, then all validators. Sources are never rewritten.
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],
)
transforms and validators also work with render_files. A transform must return
str. Any other exception from a callback is wrapped as RuleError without the
original message, which may contain confidential Markdown. TOML and Markdown never
import callbacks, and Markdown cannot execute embedded Typst; callbacks and templates
are trusted developer code.
Write the PDF¶
from pathlib import Path
from typoprint import write_pdf
path = write_pdf(result, "build/out.pdf") # returns the resolved Path
Path("build/out.pdf").read_bytes()[:5] # b'%PDF-'
write_pdf flushes a complete PDF to a temporary file then replaces the destination
atomically, so a failure never leaves a truncated file. The folder must exist. Or
skip it and use result.content directly (HTTP response, object storage, …).
Handle errors¶
All library errors derive from TypoprintError (a ValueError).
| Error | Raised for |
|---|---|
ConfigurationError |
Invalid settings, template bundle, or config file |
RenderError |
Invalid inputs, unreadable files, failed compilation, failed write |
RuleError |
A transform or validator refused or failed |
from typoprint import RuleError, TypoprintError, render
try:
result = render("# Doc")
except RuleError:
... # your rule rejected the content
except TypoprintError:
... # anything else the library raised
Compiler diagnostics can quote Markdown, so HTTP adapters should return a generic message and keep details private.
Embedding in applications¶
Rendering is CPU-bound and synchronous. From an async framework, offload it:
import asyncio
from typoprint import render
async def to_pdf(markdown: str) -> bytes:
result = await asyncio.to_thread(render, markdown)
return result.content
For CPU parallelism or hard timeouts use separate processes. Runnable examples: HTTP API and process worker.
Reproducibility¶
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 stay enabled; this does not certify every document against an accessibility standard.
Custom templates can execute Typst and custom Python callbacks have ordinary Python privileges; only deploy extensions you trust. Bundle dependencies locally before offline use. Content-aware layout selection, LaTeX math, arbitrary HTML/CSS, remote asset fetching, a hosted service, and TypeScript bindings are outside this release.