CLI ยท commit 38d7ae2

bookforge

Markdown to beautiful EPUB 3 & print-ready HTML book builder

Download bookforge.zip
Quickstart
unzip bookforge.zip && cd bookforge
uv venv .venv && uv pip install -p .venv/bin/python -e '.[dev]'
.venv/bin/python -m pytest -q

bookforge

Turn a folder of Markdown chapters into a valid, good-looking EPUB 3 for e-readers (Kindle accepts it via Send to Kindle) and a print-ready HTML/PDF book, without fighting Calibre or Pandoc templates. One book.toml, one command, one self-checking build.

Features

Install

Requires Python 3.11+ (developed on 3.12).

cd bookforge
uv venv .venv && uv pip install -p .venv/bin/python -e '.[dev]'
source .venv/bin/activate        # or call .venv/bin/bookforge directly

bookforge pdf needs Chrome or Chromium (google-chrome, chromium, ...; or --chrome PATH / $BOOKFORGE_CHROME). epubcheck is optional.

Quickstart

bookforge new mybook --title "My Book" --author "Me"
cd mybook
bookforge build          # build/my-book.epub + build/my-book-print.html, then validates
bookforge pdf            # build/my-book.pdf via headless Chrome

Or try the bundled novella (outputs already committed in examples/sample-book/build/):

bookforge build examples/sample-book
bookforge pdf examples/sample-book

Usage

bookforge new DIRECTORY [--title T] [--author A]
bookforge build [PATH] [-o OUT] [-f epub|print|all]... [--strict] [--epubcheck/--no-epubcheck]
bookforge validate FILE.epub [--strict] [--epubcheck/--no-epubcheck]
bookforge pdf [PATH] [-o OUT] [--chrome EXE]
bookforge stats [PATH]
bookforge watch [PATH] [-o OUT] [--interval SECONDS]

PATH is the directory containing book.toml (default .), output goes to PATH/build unless -o is given. Errors in the book (missing files, unknown cross-reference, bad config) print error: ... and exit 1; validation errors exit 1, and warnings do too with --strict.

build

$ bookforge build examples/sample-book
wrote examples/sample-book/build/the-salt-ledger.epub (89 KB)
epubcheck not installed; skipped (built-in checks only)
validation: 0 error(s), 0 warning(s)
wrote examples/sample-book/build/the-salt-ledger-print.html (148 KB)

validate

Works on any EPUB. Output for a copy of the sample with a stored-as-deflated mimetype, an unclosed </section> and a duplicated id:

$ bookforge validate broken.epub
epubcheck not installed; skipped (built-in checks only)
ERROR   mimetype: must be stored uncompressed
ERROR   OEBPS/text/chapter-high-water.xhtml: not well-formed XML: Opening and ending tag mismatch: section line 10 and body, line 34, column 8 (<string>, line 34)
ERROR   OEBPS/text/chapter-salvage.xhtml: duplicate id 'dup'
validation: 3 error(s), 0 warning(s)

pdf

$ bookforge pdf examples/sample-book
wrote examples/sample-book/build/the-salt-ledger-print.html
wrote examples/sample-book/build/the-salt-ledger.pdf (24 pages, 306 KB)

stats

$ bookforge stats examples/sample-book
Title                        Kind               Words    Read Notes Imgs
------------------------------------------------------------------------
Copyright                    copyright             45   1 min     0    0
Dedication                   dedication            15   1 min     0    0
The Ninth Entry              chapter              444   2 min     1    0
R.W.                         chapter              342   2 min     1    0
The Shape of Water           chapter              353   2 min     0    1
Salvage                      chapter              350   2 min     0    0
What Pellam Knew             chapter              319   2 min     0    0
High Water                   chapter              389   2 min     1    0
Appendix: Predicting a Tide  appendix             118   1 min     0    0
Acknowledgements             acknowledgements      72   1 min     0    0
About the Author             about                 47   1 min     0    0
------------------------------------------------------------------------
Total                                           2,494  11 min     3    1

2,197 words in 6 chapters; reading time at 230 wpm: 11 min

watch

$ bookforge watch mybook
[20:15:12] building...
wrote mybook/build/my-book.epub (6 KB)
validation: 0 error(s), 0 warning(s)
wrote mybook/build/my-book-print.html (13 KB)
[20:15:13] building...      <- after saving a chapter

Writing a chapter

# The Ninth Entry

> The sea keeps no accounts, which is why we keep them for it.
>
> --- Harbour Office motto

"Six feet, two inches," she read aloud.[^1]

***

See also [@salvage] for what happened next.

[^1]: Footnotes become pop-up notes in the EPUB.

See docs/book-toml.md for every setting.

Configuration

Everything lives in book.toml; the complete reference is docs/book-toml.md. The sample project's file is a good template: examples/sample-book/book.toml. Environment: SOURCE_DATE_EPOCH (reproducible timestamps), BOOKFORGE_CHROME (Chrome path).

Other docs: theming guide, EPUB internals, typography, validation and the print/PDF pipeline.

Architecture

src/bookforge/
  cli.py         click commands, error reporting
  config.py      book.toml -> Config (validation, glob expansion, identifiers)
  model.py       Project/Doc loading (front matter, ids, titles), render_project(target)
  render.py      markdown-it-py parser + rules: typography, notes, xrefs, images, epigraphs, code
  typography.py  language-aware smart quotes, dashes, ellipses
  images.py      resize/re-encode, content-hashed names
  styles.py      CSS assembly (themes + Pygments + fonts + user CSS)
  themes/        base.css, classic.css, modern.css, manuscript.css
  epub.py        EPUB 3 writer (OPF, nav, NCX, XHTML pages, zip)
  validate.py    built-in EPUB checks + optional epubcheck
  printhtml.py   single-file paged-media HTML
  pdf.py         headless Chrome driver with two-pass TOC numbering
  stats.py  watch.py  scaffold.py

Tests

.venv/bin/pytest -q          # 131 tests, ~2 s

Covers Markdown conversions (footnotes, smart quotes per language, scene breaks), EPUB structure (zip order, stored mimetype, OPF/nav/NCX parsed with lxml, spine order), validator checks against planted errors, cross-references, print HTML structure, and CLI end-to-end including a real headless-Chrome PDF run when Chrome is installed. ebooklib is used in tests as an independent EPUB reader.

Limitations