English · Русский
Universal FB2 → beautiful PDF converter with book-grade Russian / English typography, built on Typst.
Parses FB2 (including .fb2.zip), builds a
typed Document IR, applies pluggable typography (Russian, English,
passthrough), renders a Typst source via the classic.typ template, and
compiles the PDF.
Sample output from a short Pushkin collection — title page, prose with epigraph and footnote reference, a poem with stanzas:
- Full FB2 coverage — sections nested 5+ levels,
<part>,<subtitle>,<epigraph>,<cite>,<poem>,<table>(flattened with inline formatting preserved),<image>/<binary>, footnotes (<a type="note">+<body name="notes">), and every inline tag. - Pluggable typography — Russian (Russian quotes, non-breaking spaces
after short prepositions, em-dashes), English (smart quotes,
--→ em-dash), passthrough for the rest. Adding a language = one module + one registry line. - Layered config — FB2 metadata →
<book_stem>.toml→ CLI flags. - Two CLI modes — single-shot (
book-converter <input>) and batch (book-converter build [dir]) with per-book error isolation. - Hardened parser —
defusedxmlagainst XXE, path-traversal guards, defensive handling of empty sections and malformed footnotes. - Localized template — Contents / Source / Series / typeset notice
strings are pulled from an
l10ndict keyed by the book's language.
- Python ≥ 3.11 and uv
- Typst CLI ≥ 0.12 on
PATH - Fonts in
templates/fonts/— PT Serif ships under the SIL OFL 1.1
git clone https://github.com/maxyotka/book-converter.git
cd book-converter
uv sync# single shot
uv run book-converter books/one.fb2.zip # → build/one.pdf
# batch — every .fb2 / .fb2.zip under books/
uv run book-converter build books --out-dir build
# convenience wrapper
./build.shDrop <stem>.toml next to <stem>.fb2.zip:
title = "Correct Title"
author = "Correct Author"
lang = "en"
publisher = "ACME"
year = "2026"
isbn = "978-..."
fonts = ["EB Garamond", "Liberation Serif"]Flags (--title, --author, --lang, --font, --publisher, --year,
--isbn, --config) override TOML, which overrides FB2 metadata. Empty
publisher / year / ISBN are skipped on the copyright page.
src/book_converter/
cli.py single-shot + batch dispatch
config.py BookConfig: FB2 ↔ TOML ↔ CLI merge
batch.py books/ scan with per-book error isolation
ir.py Pydantic v2 Document IR
fb2/
loader.py .fb2 / .fb2.zip → defusedxml tree
parser.py ET.Element → Document IR
typography/
base.py Protocol + Passthrough + flatten/rebuild helpers
russian.py RussianTypography
english.py EnglishTypography
registry.py lang-code → plugin, fallback Passthrough
render/
typst.py Document IR → Typst source + binary assets
escape.py _typst_escape / _typst_string
templates/
classic.typ book template (part/chapter/subsection/epigraph/poem)
fonts/ PT Serif (OFL)
books/ input FB2 (gitignored)
build/ output PDF (gitignored)
tests/ pytest suite + fixtures
uv run pytest # 90 fast tests (parser, IR, render, typography)
uv run pytest -m slow # + integration compile via typst CLIIntegration smoke (tests/test_integration_smoke.py) expects two FB2 files
in books/; both are gitignored, so drop them in yourself — slow tests skip
when they are absent.
- Create
src/book_converter/typography/<lang>.pywith a class implementingTypographyPlugin.transform_paragraph(inlines) -> inlines. - Register it in
typography/registry.pyby its ISO 639-1 code. - Mirror
tests/test_typography_russian.pyfor coverage. - Add localized strings to the
l10ndict intemplates/classic.typif needed (Contents, Source, Series, …).
unknown font family: pt serif— pass--font-path templates/fontsto Typst, or use thebook-converterCLI which wires it up automatically.program not found: book-converter— re-runuv syncafter pulling; the entry point is installed by the Hatchling build backend.- Slow tests skip — expected unless you drop the two referenced FB2
files into
books/.
PRs welcome. See CONTRIBUTING.md for dev setup, commit style, and guidance on adding new languages or template variants.
Source code: MIT.
Bundled PT Serif fonts under templates/fonts/: SIL Open Font License 1.1 —
see templates/fonts/OFL.txt and
templates/fonts/README.md for full
attribution.


