Turn PDFs, web pages, Word documents, EPUBs and transcripts into files that are actually readable on a small screen — an e-ink reader or a phone.
Built as tooling for agents (there is a CLI with --json on every command, a
Python API, an MCP server and a skill file), but perfectly usable by hand.
You open a PDF on a Boox, a Kobo, in KOReader, or on your phone, and the type is the size of ants. You press the font-size button and nothing happens. Headings look like body text. Lines run off the edge of the screen.
None of that is the reader's fault. A PDF is a fixed page — usually A4, laid out for paper. A 6" screen can only shrink that page to fit. It cannot re-flow it, so the font-size, margin and line-spacing controls have nothing to act on.
The fix is to recover the structure of the document — headings, paragraphs, lists — and emit a format that re-flows. That is EPUB. Once the text re-flows, every control on the device starts working again.
When a PDF really is required, this tool makes one whose page is the exact size of the target screen, so the text is already the right size and nothing needs zooming.
- Hard line breaks. PDF text is already broken at a fixed width; those breaks are healed so the text can re-wrap to a narrow screen.
- Hyphens.
inter-/nationalbecomesinternational, whileAnglo-Saxonkeeps its hyphen. - Running headers, footers and page numbers. Text repeated in the margin band across pages is dropped.
- Lost headings. Font-size clustering recovers the heading hierarchy, so the reader's table of contents works.
- Two-column papers. Columns are detected and read in order.
- Tables. Ruled tables in PDFs are recovered as real tables, and a table too wide for the screen is re-laid out row by row instead of being squeezed into unreadable columns. See Tables.
- Web page clutter. Navigation, footers, cookie bars and share widgets
are stripped. Article images are downloaded and embedded, so the EPUB reads
offline;
--no-imagesskips them. - Transcripts. SRT, WebVTT and
[00:12] Speaker: textshapes become paragraphs by speaker, with a heading every N minutes. - Typography for small screens. Left-aligned, never justified; generous
line height; code wraps instead of scrolling sideways. No base font size
is set at all, so the reader's own font-size button actually works —
everything else is in
emand scales with whatever you choose. E-ink gets forced black on white because grey vanishes on a low-contrast panel; phones and tablets get no colour at all, so dark mode keeps working. - Links. Kept from every input that has them — web pages, Markdown, EPUB
and DOCX — with relative URLs resolved to absolute, so "sign up here" still
goes somewhere. Markdown keeps its emphasis and code spans too. Bare URLs in
plain text become tappable links. Scripts, styles and
javascript:URLs are stripped.
pip install reader-readyFrom a clone:
pip install -e ".[dev]"Python 3.10+. Depends on PyMuPDF and BeautifulSoup; the EPUB is written directly, with no packaging library involved.
reader-ready convert paper.pdf --device boox-note-air -o ~/booksreader-ready convert https://example.com/essay --to epub,md --device kobo-clara
reader-ready convert standup.vtt --chapter-minutes 5 --to epub
reader-ready batch ./inbox --recursive --to epub -o ./ready
reader-ready inspect paper.pdf # dry run: what would be extracted
reader-ready devices # list screen profilesOutput formats: epub (default), pdf, md, html, txt.
For EPUB, use the reader's own font-size control — that is the point of
converting, and the stylesheet deliberately does not set a base size so the
control works. --line-height 1.8 gives the text more air without touching
the size.
--base-px pins the base size instead, which disables the reader's control.
Use it only when you want a fixed size:
reader-ready convert paper.pdf --line-height 1.8 # more air, size still yours
reader-ready convert paper.pdf --base-px 24 # pin it, control stops working
reader-ready convert paper.pdf --to pdf --base-pt 14 # PDF is fixed anyway| Profile | Screen |
|---|---|
boox-note-air (default) |
Boox Note Air / Tab Ultra, 10.3" |
boox-palma |
Boox Palma / Palma 2, 6.13" |
boox-page |
Boox Page / Tab Mini C, 7.8" |
boox-tab-x |
Boox Tab X / Max Lumi, 13.3" |
remarkable2 |
reMarkable 2, 10.3" |
supernote-a5x |
Supernote A5X / Manta, 10.3" |
kindle-paperwhite |
Kindle Paperwhite / Signature, 6.8" |
kindle-scribe |
Kindle Scribe, 10.2" |
kobo-clara |
Kobo Clara / Nia, 6" |
kobo-libra |
Kobo Libra / Clara BW, 7" |
kobo-sage |
Kobo Sage, 8" |
phone, iphone |
phone reading apps |
ipad-mini, ipad |
tablets |
generic-6, generic-10 |
fall-backs |
The profile only changes the PDF page size, the default type size, and whether colour is forced. An EPUB re-flows and will open anywhere.
Yes, this works for reading on a phone. The problem is identical — a fixed A4 page shrunk onto a screen the size of your hand — and so is the fix.
The EPUB output is device-independent: the same file opens in Apple Books,
Google Play Books, Moon+ Reader, Lithium, KOReader for Android, Calibre and
every e-ink reader. Nothing about it is e-ink specific, so there is no need to
convert twice. --device matters only when you ask for pdf.
Two things do adapt to a backlit screen: dark mode, described below, and
PDF page size — --device phone gives you a page the size of a phone
screen rather than the size of a Boox.
reader-ready convert paper.pdf --to epub # opens anywhere
reader-ready convert paper.pdf --to pdf --device iphone # phone-sized pagesThe html output is also responsive and reads well in a phone browser or in
Pocket-style apps.
Reading apps implement night mode by restyling the book's HTML. An EPUB whose
stylesheet insists on color: #000; background: #fff is fighting that, and in
several apps — Moon+ Reader, Lithium, older Apple Books — the book wins. You
get black text on a black page, or a white page that never dims.
So for phone, iphone, ipad-mini and ipad the generated stylesheet:
- Declares no text or background colour anywhere on the body. Not
inherit, nottransparent— nothing at all. Any declaration is something the app has to override; the only reliably safe amount is none. - Paints every border and rule in
currentColor— blockquote bars, table grids, code frames, horizontal rules — so they follow the text colour instead of staying black on a black page. - Sets
color-scheme: light dark, which tells the rendering engine both themes are supported, so its own default white page flips too. - Never uses
!important, so nothing is un-overridable. - Gives images a light backing under
prefers-color-scheme: dark. Line art pulled out of a PDF is black on transparent, which on a dark page is black on black. This is the one place a colour is deliberately forced.
E-ink profiles do the opposite on purpose — forced black on white, and
color-scheme: only light — because grey text disappears on a low-contrast
panel.
Override the automatic choice either way:
reader-ready convert paper.pdf --contrast theme # let the app theme it
reader-ready convert paper.pdf --contrast high # force black on whiteThe contrast field in the --json output tells you which was applied.
There is no way to test this from here, so if a particular app still gets it wrong, that is worth an issue — the fix belongs in the stylesheet, not in your workflow.
Two separate problems, both handled.
Getting the table out. A PDF has no idea it contains a table — just glyphs and lines. Ruled tables are detected from their borders and come out as real tables, with the cells that formed them removed from the surrounding prose. Without that second half you get the table and a stream of its cells as nonsense paragraphs, which is what most converters do.
reader-ready convert report.pdf --table-strategy lines # default: ruled tables
reader-ready convert report.pdf --table-strategy text # also borderless ones
reader-ready convert report.pdf --table-strategy off # don't detect at alltext infers columns from alignment alone, so it finds borderless tables but
can mistake a two-column page for one. That is why it is not the default. Run
inspect to see what it found before committing.
Making it readable. A seven-column table is unreadable on a 6" screen at any font size. So a table that will not fit is re-laid out one row at a time:
Boox Palma 2
Diagonal: 6.13
Resolution: 824x1648
DPI: 300
Whether it fits is worked out from the column count and the widest cell in each, against the actual width of the target screen — so the same table stays a grid on a 13" Boox and stacks on a Palma.
reader-ready convert report.pdf --tables auto # default: decide per table
reader-ready convert report.pdf --tables grid # always a grid
reader-ready convert report.pdf --tables stacked # always row by rowThe --json output reports what happened:
"tables": { "found": 3, "as_grid": 2, "stacked": 1 }Markdown output always keeps pipe tables, since it is the machine-readable format; plain text always stacks, since it has no grid worth the name.
Still not solved: tables with merged cells lose the merge, and a table split across two PDF pages comes out as two tables.
Every output carries one line: made using Reader Ready.
- EPUB and PDF — at the foot of the title page.
- HTML, Markdown, plain text — at the end of the document, since a scrolling file has no front page.
Converting a file that already carries the line does not stack up copies: the trailing credit is recognised on the way in and dropped, so Markdown stays safe to edit and feed back through.
Every command takes --json and prints one object on stdout. Errors are
{"ok": false, "error": "..."} with a non-zero exit code.
AGENTS.md— the operating instructions to drop into an agent's context: when to pick EPUB over PDF, what to check, what not to do.agents/SKILL.md— the same as a skill file.agents/tool-schema.json— function-calling schemas forconvert_for_reader,inspect_documentandlist_devices.
Python:
from readerready import convert
result = convert("paper.pdf", out_dir="out", formats=["epub"],
device="boox-note-air")
print(result["outputs"]["epub"], result["stats"]["reading_minutes"], "min")MCP:
pip install "reader-ready[mcp]"
python -m readerready.mcp_serverRegister it as a stdio server:
{ "command": "python", "args": ["-m", "readerready.mcp_server"] }- Boox — USB, or drop into BooxDrop / OneDrive sync.
- Kobo — USB into the root folder, or Dropbox.
- Kindle — email the EPUB to your Send-to-Kindle address; Amazon converts it.
- reMarkable / Supernote — their desktop or web upload.
- KOReader — anywhere on the device; it reads EPUB, HTML and Markdown.
- iPhone / iPad — AirDrop or share the EPUB, then "Copy to Books".
- Android — any folder; open it with Moon+ Reader, Lithium or KOReader.
- No OCR. A scanned PDF has no text layer.
inspectwill show ~0 words; run it through OCRmyPDF first. - Equations and figure captions in academic PDFs come out as loose text.
- Borderless tables need
--table-strategy text; merged cells lose their merge, and a table spanning two PDF pages comes out as two tables. - Right-to-left scripts are extracted but not specially typeset.
python -m pytestBump version in pyproject.toml, then push a matching tag:
git tag v1.0.0 && git push origin v1.0.0.github/workflows/publish.yml runs the tests, builds, checks that the tag
matches the version, and publishes to PyPI via Trusted Publishing — no API
token is stored anywhere. Configure the publisher once at
https://pypi.org/manage/account/publishing/.
The tests build a deliberately awful PDF — 9pt justified type, running header, footer page numbers, hyphenated line breaks — and assert that all of it is cleaned up.
MIT.