Files
epub2pdf/USER_GUIDE.md
T

200 lines
8.8 KiB
Markdown
Raw 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.
# Document → PDF Converter — User Guide
A small, self-hosted web app that converts common document formats —
EPUB3 ebooks, office documents (DOCX/ODT/RTF), TeX/LaTeX, Org,
AsciiDoc, RST, Markdown, HTML, plain text, CSV/TSV, JSON, XML, SVG,
images, and existing PDFs — into PDFs.
Everything runs locally (or on your own server); **no file 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 *to* other formats (only PDF out) |
| Keeps book title/author as PDF metadata | PPTX, XLSX and other Office formats — planned, not yet |
**Supported input formats (27):**
| Family | Extensions | Notes |
|---|---|---|
| Ebooks | `.epub` | Full CSS / images / metadata fidelity (the original mode) |
| Web | `.html` `.htm` | Native WeasyPrint rendering; `<script>` tags stripped |
| Markup | `.md` `.markdown` | Headers, tables, fenced code, links |
| Office | `.docx` `.odt` `.rtf` | Via pandoc; title from document metadata (DOCX/ODT) or the H1 |
| TeX / LaTeX | `.tex` `.latex` | Via pandoc; needs `\documentclass` or a leading `%`, else treated as plain text |
| Extended markup | `.org` `.adoc` `.rst` | Org mode, AsciiDoc, reStructuredText via pandoc |
| Text | `.txt` `.log` | UTF-8 safe, paginated |
| Data | `.csv` `.tsv` | Auto-detected delimiter → styled table; **truncated to 5000 rows × 50 cols** (extra data dropped with a note; split to include more) |
| Structured | `.json` `.xml` | Cleanly formatted listing |
| Vector | `.svg` | Embedded via WeasyPrint |
| Images | `.png` `.jpg` `.jpeg` `.webp` `.tiff` `.bmp` | One PDF page per image; max side 3000 px |
| PDF | `.pdf` | Pass-through with metadata re-stamping; max 500 pages; encrypted PDFs rejected |
Format is detected from the file's real bytes (magic), not the extension.
Naming: EPUBs use the book's title; everything else uses the file name (or the
document's H1 heading for Markdown/HTML), with `-2`, `-3` collision suffixes.
**Limits:** one file at a time per job (you can queue several, they render one after
another in the same server process), max upload **400 MB**, max output **200 MB**.
---
## 2. Quick start
### Option A — Run directly (no Docker)
```bash
# 1. install dependencies (Python 3.11+; WeasyPrint also needs the pango
# system libraries; pandoc for office/TeX/markup formats — e.g. on Debian/Ubuntu: sudo apt install libpango-1.0-0
# libpangoft2-1.0-0 libharfbuzz0b fontconfig fonts-noto-cjk shared-mime-info pandoc)
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 any supported file** onto the upload box (or click to browse).
- The 27 formats in §1 are accepted; anything else is rejected with a message.
- Files over 400 MB are rejected before upload finishes.
3. The job starts immediately — progress updates show the current stage
(e.g. `Rendering chapter-04.xhtml (4/12)` for EPUBs), then `Done`.
4. When it finishes, a **Download PDF** button appears.
- The PDF is named after the book's title (EPUBs) or the file name / H1 heading
(everything else), 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 |
| `Unsupported format` | File isn't one of the 27 supported types — see §1 |
| CSV/TSV truncated | Over 5000 rows or 50 columns — automatically truncated with a note; split the file to include more |
| PDF rejected | Encrypted/password-protected, or over 500 pages |
---
## 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,"supported_formats":[...]}` (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.