epub2pdf — Document → PDF Converter

A small, self-hosted web app that turns 27 document formats — EPUB3 ebooks, Office documents, TeX/LaTeX, Org, AsciiDoc, RST, Markdown, HTML, plain text, CSV/TSV, JSON, XML, SVG, images, and existing PDFs — into PDFs. No file ever leaves your machine.

Built for people who want a private, on-premise conversion pipeline: run it behind a firewall, on your own server, with no data egress and no account required.


Contents


What it does

doc2pdf takes a single uploaded file and produces a single PDF. It is self-hosted: you run the container on your own hardware, so nothing you upload is processed anywhere else.

The web UI accepts a drop-and-go upload; an HTTP API and a CLI let you automate it. Every format is detected from the file's real bytes (magic-byte detection), not its extension.

For a complete format-by-format breakdown, the web UI tour, troubleshooting tables, and the full settings reference, read the USER_GUIDE.md. It is the primary, detailed documentation — this README is only the front door.


Features

  • 27 input formats → PDF. EPUB3, HTML/HTML5, Markdown, Office (.docx, .odt, .rtf), TeX/LaTeX, Org, AsciiDoc, reStructuredText, plain text, CSV/TSV, JSON, XML, SVG, images (PNG/JPEG/WebP/TIFF/BMP), and existing PDF.
  • No file ever leaves your machine. There is no data egress — uploads and generated PDFs stay on the host that runs the app.
  • Self-hosted and private. No account, no login, no telemetry. Deploy behind your firewall.
  • CJK support. Ships with Noto CJK fonts so Chinese, Japanese, and Korean render correctly.
  • RTL support. Right-to-left books render correctly for Arabic and Hebrew.
  • Faithful Office rendering. The Office trio renders with Rich mode (default) through LibreOffice — preserving fonts, page size, and colors — or falls back to Standard mode through pandoc.
  • Fast and concurrency-safe. A single gunicorn worker with 8 threads provides 4 concurrent conversion slots. Uploads cap at 400 MB, output caps at 200 MB.
  • Tested. 27 formats supported with 152/152 tests green.

How it works

A browser upload becomes a server-side job that flows through a small, well-defined pipeline:

flowchart TD
    Browser[Browser — upload a file] --> API[/api/convert/]
    API --> Queue[Job queue: 1 worker · 4 slots]
    Queue --> Family{File family?}
    Family -->|EPUB, HTML, Markdown, JSON, XML, SVG, images, text| W[WeasyPrint renderer]
    Family --> Office[".docx · .odt · .rtf"]
    Office --> Mode{Render mode?}
    Mode -->|Rich (default)| LO[LibreOffice headless]
    Mode -->|Standard| PC[Pandoc]
    W --> Guards[Security guards]
    LO --> Guards
    PC --> Guards
    Guards --> Finalize[finalize PDF tail]
    Finalize --> Download[Download PDF]

Every path — whether EPUB/HTML/Markdown renders through WeasyPrint, or the Office trio renders through LibreOffice or pandoc — passes through the same security guards before the final PDF is written. See the security section for details.


Security

This release ships with every previously identified finding fixed. The pipeline applies these guards before writing output:

  • Decompression-bomb guard. Uploads stream through a byte-counted extractor that aborts mid-stream if extraction exceeds its budget.
  • SSRF / data-egress blocking. Remote resources are validated before rendering, and remote schemes (http, https, ftp, ws, wss, file) are blocked — local data: resources still render.
  • Stored-XSS stripping. <script> tags are stripped from HTML, SVG, EPUB, Markdown, CSV, XML, and other render paths before they reach the renderer.
  • Output cap. Output PDFs are capped at 200 MB; uploads are capped at 400 MB.

Quick start

Build the image, then run it with a persistent data volume:

docker build -t epub2pdf .
docker run -d --name epub2pdf \
    -p 8030:8030 \
    -v epub2pdf-data:/app/data \
    epub2pdf

The epub2pdf-data volume keeps uploads and generated PDFs across restarts. Open http://localhost:8030 in a browser.

To iterate locally without committing to Docker, ./start.sh runs the app in the foreground with an auto-restart loop:

./start.sh              # http://0.0.0.0:8030
PORT=9000 ./start.sh    # custom port

Local (no Docker)

# 1. install dependencies (Python 3.11+)
pip install -r requirements.txt

# 2. start the web server
python app.py   # -> http://localhost:8030

WeasyPrint also needs the pango system libraries. On Debian/Ubuntu, install at least libpango-1.0-0 libpangoft2-1.0-0 libharfbuzz0b fontconfig fonts-noto-cjk shared-mime-info plus pandoc for the office/TeX/markup formats.

Coolify

Deploy as a Dockerfile-based service: publish port 8030 and mount a volume at /app/data. For the exact three-step walkthrough, read COOLIFY.md.


Command line

Convert a file and exit, without the web server:

python app.py --cli book.epub --output ./out

Live sites

Environment URL
Production (main) http://epub2pdf.209.126.10.202.sslip.io
Testing (DEV-TEST) http://epub2pdftest.209.126.10.202.sslip.io

Both run on Coolify and report running:healthy.


Documentation

  • USER_GUIDE.md — the primary, full documentation: format reference, web UI tour, rendering modes, HTTP API, configuration, fonts, and troubleshooting.
  • COOLIFY.md — deployment guide for Coolify (Dockerfile service, port, volume).

Source repository

The source lives at:

https://gitea.network-ewee.com/sirdave73/epub2pdf.git

Gitea Actions runs the pytest suite on every push and pull request to main.


Attribution

epub2pdf is a project of Network-Ewee.com.

S
Description
EPUB to PDF converter (WeasyPress engine)
Readme
370 KiB
Languages
Python 87.7%
HTML 10.8%
Dockerfile 0.9%
Shell 0.6%