Files

172 lines
6.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# EPUB → PDF Converter — User Guide
A small, self-hosted web app that converts EPUB3 ebooks into typeset PDFs.
Everything runs locally (or on your own server); **no book ever leaves your machine**.
---
## 1. What it does (and doesn't)
| Does | Doesn't |
|---|---|
| Preserves the EPUB's own CSS, images, headings, tables, code blocks | Re-flow or re-typeset like a print publisher would |
| Renders CJK (Chinese / Japanese / Korean) correctly via Noto fonts | Handle DRM-protected (e.g. protected) EPUBs |
| Supports right-to-left books (Arabic, Hebrew) | Convert EPUB → other formats (only PDF out) |
| Keeps book title/author as PDF metadata | — |
**Limits:** one book at a time per job (you can queue several, they render one after
another in the same server process), max upload size **400 MB**.
---
## 2. Quick start
### Option A — Run directly (no Docker)
```bash
# 1. install dependencies (Python 3.11+; WeasyPrint also needs the pango
# system libraries, e.g. on Debian/Ubuntu: sudo apt install libpango-1.0-0
# libpangoft2-1.0-0 libharfbuzz0b fontconfig fonts-noto-cjk shared-mime-info)
pip install -r requirements.txt
# 2. start the web server
python app.py # -> http://localhost:8030
```
### Option B — Docker (recommended for a persistent install)
```bash
docker build -t epub2pdf .
docker run -d --name epub2pdf \
-p 8030:8030 \
-v epub2pdf-data:/app/data \
epub2pdf
```
The volume `epub2pdf-data` keeps uploaded EPUBs and generated PDFs across restarts.
### Option C — Coolify
See `COOLIFY.md` in this directory (3 steps: Dockerfile service, port 8030, volume at `/app/data`).
---
## 3. Using the web UI
1. Open `http://<host>:8030` in your browser.
2. **Drop an `.epub` file** onto the upload box (or click to browse).
- Only `.epub` files are accepted; anything else is rejected with a message.
- Files over 400 MB are rejected before upload finishes.
3. The job starts immediately — a progress bar shows which chapter is being
rendered (e.g. `Rendering chapter-04.xhtml (4/12)`), then `Merging pages…`.
4. When it finishes, a **Download PDF** button appears.
- The PDF is named after the book's title (e.g. `My_Book.pdf`).
5. Repeat for the next book. Old jobs stay listed so you can re-download
recent PDFs (the server keeps the 50 most recent jobs and their outputs).
### What can go wrong in the UI
| Symptom | Meaning / fix |
|---|---|
| `File too large (max 400 MB)` | Shrink or split the book, or raise `MAX_UPLOAD_MB` in `app.py` |
| `Not a valid EPUB (bad zip container)` | File is corrupted or renamed — re-export from your ebook store/library |
| `No readable content found in EPUB spine` | The EPUB has no HTML chapters (images-only books, broken exports) |
| `DRM protected` style errors | Remove DRM first (Calibre / Calibre-DB tools) — this app can't decrypt |
| Stuck at `Working…` forever | The server restarted mid-job; refresh the page and re-upload |
---
## 4. Command-line usage (no server)
```bash
# convert one file and exit
python app.py --cli book.epub
# choose where the PDF lands
python app.py --cli book.epub --output /path/to/dir
# inside a running container
docker exec epub2pdf python app.py --cli /app/data/uploads/book.epub --output /app/data/output
```
Progress lines like `[3/12] chapter-03.xhtml` print as each chapter renders.
---
## 5. HTTP API (for automation)
Three endpoints, all JSON:
```bash
# 1. upload (multipart form, field name: file) -> starts a job
curl -X POST -F "file=@book.epub" http://localhost:8030/api/convert
# -> {"job_id": "c524be5a9d85"}
# 2. poll status
curl http://localhost:8030/api/jobs/c524be5a9d85
# -> {"status": "working", "progress": 42.5, "message": "Rendering ch-05.xhtml (5/12)"}
# -> {"status": "done", "progress": 100, "filename": "Book_Title-20260828-203943.pdf"}
# -> {"status": "error", "error": "Not a valid EPUB (bad zip container)"}
# 3. download the finished PDF
curl -OJ http://localhost:8030/download/Book_Title-20260828-203943.pdf
```
Other endpoints:
- `GET /` — the web UI
- `GET /health``{"status":"ok","max_upload_mb":400}` (use for healthchecks)
---
## 6. Configuration
Everything works with defaults. Optional overrides:
| Setting | Default | How to change |
|---|---|---|
| Port | `8030` | Env `PORT`, or `--port` (web mode) |
| Data location | `<project>/data` | Env `DATA_DIR` (Docker: volume at `/app/data`) — holds `uploads/`, `output/`, `work/` |
| Max upload size | `400 MB` | Constant `MAX_UPLOAD_MB` in `app.py` (edit + restart) |
| Page geometry | A4, 20×18 mm margins | Edit `BASE_CSS` in `app.py` (e.g. `size: A4``size: Letter`) |
| Base typography | Georgia/serif 10.5 pt, line-height 1.65 | Edit `BASE_CSS` in `app.py` — the EPUB's own CSS still layers on top of it |
| RTL handling | auto (reads EPUB `page-progression-direction`) | automatic; no setting |
### Fonts (affects what renders correctly)
- The Docker image ships **DejaVu** + **Noto CJK** fonts.
- When running without Docker, install at least: `fontconfig fonts-dejavu-core fonts-noto-cjk`
(plus `libpango-1.0-0 libpangoft2-1.0-0 libharfbuzz0b shared-mime-info` for WeasyPrint itself).
- If a book uses an exotic font not installed, WeasyPrint falls back to the
nearest available font — layout stays intact, letterforms may differ.
---
## 7. Where files live
```
<DATA_DIR>/
├── uploads/ # your uploaded .epub files (kept after conversion)
├── output/ # generated PDFs (kept; served by /download/<name>)
└── work/ # per-job temp dir (extracted EPUB + chunk PDFs; deleted after each job)
```
You can delete anything in `uploads/` and `output/` at any time (the server
just returns 404 for downloads of deleted files). `work/` is always empty when
idle.
---
## 8. Troubleshooting
| Problem | Check |
|---|---|
| Server won't start: `OSError: cannot load library 'libpango-1.0-0'` | Missing system libraries — see §2 Option A note |
| CJK text shows as boxes (□□□) | `fonts-noto-cjk` not installed — install, then run `fc-cache -f`, restart server |
| Strange characters (`—`) instead of em dashes | This build reads EPUB text as UTF-8 explicitly; if you see this, an old server is running — restart it |
| Conversion is slow | Normal: WeasyPrint typesets chapter-by-chapter in-process. Big/illustrated books can take minutes; gunicorn timeout is 1200 s by default |
| Port already in use | `--port 9000` or change env `PORT` |
---
## 9. Security notes (self-hosted)
- The app has **no login**. Anyone who can reach the port can upload and convert.
On a LAN, that's usually fine; on the public internet, put it behind a
reverse proxy with auth, or firewall the port.
- Uploads are stored under a random job ID (not your filename), and downloads
are restricted to the output directory (path traversal is blocked).
- Nothing is transmitted anywhere: conversion happens entirely in-process.