- Consolidate module configs into root config.example.json with ocr, vision, and epub sections - Split LLM OCR workflows into novel_ocr.py (prose) and manga_ocr_llm.py (manga) - Remove gemini_direct_ocr.py in favor of OpenAI-compatible API endpoints - Support direct manga translation via --translate, --target-lang, and glossary.md - Add bidirectional context support: past translations (--context-pages) and lookahead Japanese text (--context-pages-ahead) - Add per-page JSON audit logging under logs/ and expose OpenAI sampling parameters
EPUB builder module
Assembles a chapters/ folder — laid out by hand into per-chapter
subfolders — into a finished, valid .epub, with furigana rendered as
proper <ruby> markup, an embedded font, a cover image, and metadata from
the epub section of the repo's root config.json.
This is step 2 of the pipeline; step 1 is the ocr
module, which produces the per-page .txt files you'll sort into
chapters/.
Layout
config.json <- shared with the ocr module; see below for the "epub" section
epub_builder/
chapters/
cover.jpg <- cover image (optional, any "cover.*" directly in chapters/)
ch00_frontmatter/ <- everything before the main text (title page, TOC, ...)
001.txt
002.jpg
ch01_chapter00/
003.txt
004.txt
005.jpg
ch02_chapter01/
006.txt
ch08_backmatter/ <- everything after the main text (afterword, ...)
...
font/
YourFont.ttf <- font to embed (optional)
build_epub.py
furigana.py
Chapter folder naming: chNN_name
chNN— sequence number, determines sort order (ch00→ch01→ ... →ch10; use leading zeros if you have more than 9 chapters so plain alphabetical sort stays correct).name— default chapter title (override viaconfig.json'sepub.chapter_titles).- Folders with
frontmatterorbackmatteranywhere in their name are always ignored: no title, no TOC entry — regardless of any flag or config setting. Their content is still included in the book, just without a section label.
Files inside a folder are sorted strictly by the number in the filename
(extension doesn't matter — an image 004.jpg will land between
003.txt and 005.txt).
Page layout
- The first line of each
.txtpage (after stripping a leading page number) is rendered as a larger, bold "page header" — typically an in-story date/time/chapter marker (relevant if you're OCR-ing a novel where that detail matters to the plot, e.g. one involving time travel). - If nothing is left after stripping the page number, two blank lines are shown instead, so the page's vertical rhythm stays consistent.
- A blank line always follows the header, then the text.
- Each paragraph starts with a full-width space (
) — the standard indent in Japanese typography, embedded directly in the text rather than relying on CSStext-indent(which not every reader honors).
config.json
This module reads the epub section of the shared config.json at the
repo root (copy config.example.json there if you haven't already):
{
"epub": {
"title": "Your Book Title",
"author": "Author Name",
"language": "ja",
"identifier": "",
"publisher": "",
"description": "",
"date": "",
"rights": "",
"series": "",
"series_index": "",
"font_family": "",
"vertical": false,
"show_chapter_titles": false,
"output": "book.epub",
"chapter_titles": {
"ch00_frontmatter": "",
"ch01_chapter00": "Chapter 00",
"ch08_backmatter": ""
}
}
}
title,author,language— core metadata (titleis required).identifier,publisher,description,date,rights— optional Dublin Core metadata, only added if non-empty. Leaveidentifierempty to get a random UUID generated on every build; set it once and keep it stable if you plan to rebuild the file repeatedly and want reading apps to recognize it as the same book.series/series_index— Calibre-style series metadata (calibre:series/calibre:series_index), recognized by Calibre, KOReader, and several other reading apps.font_family— explicit name for the embedded font family (see "Font" below). Optional — without it, the name is guessed automatically.vertical— vertical Japanese writing-mode (also toggleable via--vertical).show_chapter_titles— whether to print the chapter title (<h1>) at the start of each chapter. Defaults tofalse— titles still show up in the table of contents either way. Toggle via--show-chapter-titles.output— path for the resulting epub, used if--outputisn't passed.chapter_titles— folder name -> display title mapping. An empty string""means "no heading, no TOC entry" (this is already the default behavior forfrontmatter/backmatterfolders, so listing them here is optional, just for clarity).
frontmatter/backmatter entries in chapter_titles are ignored no
matter what — they can't be "turned back on" via config.
Font
Drop font file(s) (.ttf/.otf/.woff/.woff2) into font/. Name them
Regular.ttf / Bold.ttf / Italic.ttf / BoldItalic.ttf (case
doesn't matter, and the keyword can be part of a longer name, e.g.
NotoSerifJP-Bold.ttf) and they'll automatically be unified under one
font-family name with the correct font-weight/font-style. That means a
bold chapter heading (<h1>, and the page-header line) will automatically
pick up Bold.ttf, and italics (<em>), if any ever show up in the text,
will pick up Italic.ttf.
If the filenames don't match this pattern, the old behavior kicks in:
each file gets its own font-family name, and only the first one
(alphabetically) is wired up automatically in body { font-family }; the
rest are still embedded in the epub but need manual CSS edits to use.
The script tries to guess a shared family name from the common part of the
filenames (e.g. NotoSerifJP-Regular.ttf + NotoSerifJP-Bold.ttf ->
NotoSerifJP). To set it explicitly, use config.json's epub section:
{ "epub": { "font_family": "MyBookFont" } }
Check the font's license before embedding it in a file you intend to share or publish — personal use on your own devices is usually fine, but redistribution terms vary by font.
Running
Simplest case — no arguments, if everything is where it's expected
(config.json one level up, at the repo root):
python build_epub.py
Or with explicit paths/overrides:
python build_epub.py \
--pages-dir ./chapters \
--font-dir ./font \
--config ../config.json \
--output ./my_book.epub \
--show-chapter-titles \
--vertical
--split-pages makes each file its own xhtml document inside the epub,
instead of merging every file in a folder into one.
furigana.py as a standalone preview tool
Useful for quickly checking how a single OCR'd page will render before running the full build:
python furigana.py --input page.txt --output preview.xhtml
# or a quick one-liner check:
echo "本文《ほんぶん》デザイン" | python furigana.py --stdin
Requirements
pip install natsort