reepub
v1.1.0
Published
Scanned PDFs, web pages and broken EPUBs into clean, reflowable EPUB3 that passes official epubcheck at zero errors. Apple Vision OCR on your own Mac, and the OCR ships as its own CLI. 100% offline.
Maintainers
Readme
reepub
Bind what you already own — paper you scanned, pages you saved, ebooks that arrived broken — into a personal library of clean, reflowable books that pass the official validator at zero errors. Natively on your Mac, 100% offline. The OCR is a tool of its own, if the OCR is all you came for.
No API keys · No subscriptions · No internet · Your files never leave your machine.
What reepub is for. reepub is a tool for digitizing documents you own or have the right to digitize — your own writing, notes and correspondence, public-domain works, or books you physically own — into a personal ebook library you keep locally. Everything is processed on your own Mac; nothing is ever uploaded. Please respect copyright and the rights of authors and publishers.
Why reepub?
You've got paper worth keeping — your own notes, a stack of letters, an
out-of-print book you own. Most "PDF to EPUB" tools either upload it to a cloud
service, charge per page, or spit out a fixed-layout EPUB that's really just
images glued together — unreadable on a phone. reepub is different — and the
difference is ownership:
- You own it — you don't rent it. Cloud OCR is a borrowed library card:
revocable, priced per page, and your files pass through someone else's servers.
reepubis the book on your own shelf — free, offline, and yours; no one can reprice it, gate it, or switch it off. - OCR you already paid for. It unlocks Apple's Vision framework and the Neural Engine already in your Mac (M1–M4+) — so a tiny MIT tool matches paid cloud OCR, fully on-device.
- Reflowable output, not image-glued fake EPUB — text is reconstructed into real paragraphs and chapters, so it reflows on any screen size, not a frozen page image.
- There is no pipe. No API key, no account, no network call — your books physically cannot leave the machine. Privacy that's structural, not a promise.
- Traditional Chinese & English recognition out of the box (
zh-Hant+en-US). - Validated EPUB3 — every book is run through a built-in structural
validator before it's handed back: no Java, no download, nothing to install,
so it always runs. One that fails is deleted and the command exits non-zero.
npm testgoes further and holds a freshly built book to the official epubcheck at 0 errors / 0 warnings. - Repairs the books you already have —
reepub healfixes a broken EPUB and tells you exactly what it changed. Vertical right-to-left CJK volumes carrying four epubcheck errors each came out at zero, with every chapter and their reading direction intact. See Healing. - MIT-licensed, self-contained, forkable, free forever.
Features
Smart paragraph stitching — uses line bounding boxes, vertical gaps, indents, and punctuation cues to merge OCR lines back into clean paragraphs.
Automatic cover — renders page 1 at 2× and wraps it as the EPUB cover.
Hybrid text + image pages — pages with little text (illustrations, plates) are preserved as images instead of garbled OCR.
Automatic chapter detection — splits on heading cues (e.g.
第一章,Chapter).Four ways to use it — downloaded command-line tools that need no build, a one-click Mac app, a local web UI, or the Node CLI.
Localized app UI — English / 繁體中文 / 日本語
Prerequisites
Nothing, if you take Option A — the released binaries are universal and self-contained. Everything below is for building from source.
- macOS 13+ (Apple Silicon strongly recommended) for the native app
- Xcode Command Line Tools — for the Swift compiler (
xcode-select --install). No full Xcode required. - Node.js v20+ — only for the optional web UI / CLI path
zip/unzip/xmllint— preinstalled on macOS
Build
git clone https://github.com/CVERInc/reepub.git
cd reepub
make app # builds macos/build/Reepub.app (Command Line Tools only)Usage
Option A — Download the command-line tools (nothing to build)
Latest release — universal binaries (Apple Silicon and Intel), macOS 13+.
tar xzf reepub-cli-*-macos-universal.tar.gz
xattr -d com.apple.quarantine scan-ocr epub-kit # unsigned; see the release notes
./scan-ocr book.pdf > pages.json # OCR, and nothing else
./epub-kit pages.json book.epub --title "…" --author "…"scan-ocr is useful on its own: a PDF in, every recognized line with its text
and its box on stdout. If the text and the layout are all you came for, you can
stop there — nothing downstream is required.
Option B — Native macOS app
make app
open macos/build/Reepub.appPick a PDF (or drag one onto the window), let Vision OCR run, optionally set a title and author, then Save as EPUB… to save the finished book. Everything — OCR, assembly, and validation — happens in the app, fully offline.
Option C — Local web UI
make build # compiles the Swift OCR CLI (bin/scan-ocr) used by the server
npm start # serves http://localhost:30232Open the page, drop in a PDF, enter a title/author, and download the finished EPUB once conversion completes. The conversion log streams live.
Option D — Command line (Node)
make build
node src/builder.js <input.pdf> <output.epub> [book-title] [book-author]Example:
node src/builder.js ~/Documents/scanned_book.pdf ~/Desktop/my_book.epub "我的書名" "作者"How it works
- OCR extraction —
bin/scan-ocr(Swift) loads the PDF via PDFKit, renders each page to a bitmap at 2× scale, and runs Apple'sVNRecognizeTextRequest. It emits JSON of every recognized line with normalized bounding boxes, saves page 1 as the cover, and saves low-text pages as image plates. - Text reassembly —
src/builder.jsfilters out headers/footers, stitches lines into paragraphs using geometry + punctuation heuristics, detects headings, and groups everything into chapters. - EPUB packaging — writes a standards-compliant EPUB3 (
content.opf,toc.ncx, per-chapter XHTML, cover) and zips it with the uncompressedmimetypeentry first. - Validation —
src/validator.jschecks the ZIP mimetype layout,container.xml, the OPF manifest/spine, XHTML well-formedness, that every content document actually has a<body>, that no internal reference dangles, and that nothing escapes the container. A book that fails is deleted and the command exits non-zero.
Every package document, table of contents and navigation document comes from
src/binder.js — the single place allowed to emit one. See
PRINCIPLES.md for why that boundary exists and what CI does to
keep it.
Every threshold those heuristics use — why a page counts as a plate at 120
characters, why a paragraph breaks at 1.8 line-heights, why the cover grey is
#111111 and not black — is written down with its evidence in
docs/measured-constants.md. None of them are
values anybody publishes; each was measured or earned by watching something
specific go wrong.
Healing broken books
Ebooks in the wild are broken in ways their owners never see: a forgiving reader
shows the book anyway, so the damage only surfaces when something strict refuses
it. The hardest case is a vertical, right-to-left CJK volume: the reading
direction it needs is an EPUB 3 attribute, so a book packaged as EPUB 2 has to
choose between validating and opening the right way round. heal gives it both.
node src/heal.js broken.epub healed.epubHealing broken.epub → healed.epub
healed: EPUB 2.0 spine carried page-progression-direction → rebuilt as EPUB 3.0
healed: table of contents identifier disagreed with the package → unified
healed: every chapter declared the XHTML 1.1 doctype → <!DOCTYPE html>
healed: dropped @font-face "DroidFont", serif, sans-serif → res:///system/fonts/DroidSansFallback.ttf cannot load in an EPUB
✓ EPUB validEach of those four is a defect class, not a one-off. The last one names the reader that wrote the file rather than the file itself, which is the level this example stays at: counts and byte sizes would describe somebody's shelf.
Healing never edits in place, and a repair that fails validation is deleted
rather than handed back. reepub merge performs the same repairs on the volumes
it combines — it is the same engine, so the two cannot drift apart.
What gets repaired:
- EPUB 2 packages using an EPUB 3 spine attribute — the merged book is EPUB 3,
where
page-progression-directionis legal, so vertical right-to-left series keep their reading direction and validate. - A table of contents whose identifier disagrees with the package — one identifier is minted for the merged book and used in both.
- Chapters still declaring the XHTML 1.1 doctype — brought forward to
<!DOCTYPE html>, with the entities that doctype used to define ( ,—) rewritten as numeric references so nothing stops parsing. - Stylesheets pointing at fonts that do not exist — an
@font-facewhose only source isres:///system/fonts/DroidSansFallback.ttfcannot load anywhere except the Android reader that wrote it, and epubcheck rejects it outright.
Repair is never silent, and never a guess — an entity or reference reepub cannot resolve stops the run rather than being mangled into something that merely looks right.
Validation & tests
npm test # unit, spec and conformance suites
npm run validate <file.epub> # validate any EPUB (or unpacked dir)
npm run epubcheck # fetch the official epubcheck jar (cached)npm test builds a real book and runs the official epubcheck against it. The
jar is fetched once into ~/.cache/reepub/ and reused; CI does the same, so the
local command and the pipeline check exactly the same thing.
License
MIT — see LICENSE. © 2026 CVER Inc.
