This commit is contained in:
Poison Flower
2026-09-01 00:30:34 +03:00
committed by GitHub
commit 41c6e97cd4
15 changed files with 1912 additions and 0 deletions
+182
View File
@@ -0,0 +1,182 @@
# 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
`config.json`.
This is step 2 of the pipeline; step 1 is the [`ocr`](../ocr/README.md)
module, which produces the per-page `.txt` files you'll sort into
`chapters/`.
## Layout
```
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
config.json
```
## 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 via `config.json`
`chapter_titles`).
- Folders with `frontmatter` or `backmatter` anywhere 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 `.txt` page (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 CSS `text-indent` (which not every reader honors).
## config.json
```json
{
"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 (`title` is required).
- `identifier`, `publisher`, `description`, `date`, `rights` — optional
Dublin Core metadata, only added if non-empty. Leave `identifier` empty
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 to `false` — titles still show up in
the table of contents either way. Toggle via `--show-chapter-titles`.
- `output` — path for the resulting epub, used if `--output` isn't passed.
- `chapter_titles` — folder name -> display title mapping. An empty string
`""` means "no heading, no TOC entry" (this is already the default
behavior for `frontmatter`/`backmatter` folders, 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`:
```json
{ "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:
```bash
python build_epub.py
```
Or with explicit paths/overrides:
```bash
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:
```bash
python furigana.py --input page.txt --output preview.xhtml
# or a quick one-liner check:
echo "本文《ほんぶん》デザイン" | python furigana.py --stdin
```
## Requirements
```bash
pip install natsort
```