A Python command-line tool that converts an EPUB into one HTML document. It keeps spine order when available, rewrites EPUB-internal links to work after merging, and either embeds images for a portable single file or extracts them beside the HTML.
EPUB files are ZIP archives containing HTML, CSS, metadata, and images. By default, the converter preserves source markup for fidelity. Treat the resulting HTML as active content and use it only with EPUBs you understand and trust.
If you do not trust an EPUB or its source, pass --safe-mode. Safe mode removes active
elements, event handlers, inline CSS, SVG/XML, unsafe URL schemes, and external image/resource
loads before content is emitted. Normal reading markup, headings, tables, footnotes, navigation
links, and supported raster images are preserved where possible.
Safe mode validates embedded and extracted raster image signatures and excludes SVG. EPUB-originated
<style> elements and style attributes are removed; a file supplied with --css is explicitly
trusted local input and is inlined unchanged.
- Python 3.12 or higher
-
Create a virtual environment (recommended):
# Create the venv in the project folder py -3 -m venv .venvActivate using the command appropriate for your shell/OS:
-
Windows - PowerShell
.\.venv\Scripts\Activate.ps1
-
Windows - Command Prompt (cmd.exe)
.\.venv\Scripts\activate.bat
-
Unix‑style shells (Linux, macOS, WSL, Git‑bash, etc.)
source .venv/bin/activate
-
-
Install dependencies:
python -m pip install -r requirements.txt
-
Convert a book:
python main.py "path/to/book.epub" -o output.htmlThis is the normal workflow.
Installation is not required for local conversion. If desired, install the tool:
python -m pip install .This creates the epub-to-html command. It is useful for invoking the converter
from any directory, automation or shell scripts, isolated virtual environments,
or other Python programs.
epub-to-html "path/to/book.epub" -o output.htmlInstall the project and development tools:
python -m pip install -e ".[dev]"See CONTRIBUTING.md for the canonical Nox commands used for formatting, linting, type checks, and tests.
Convert an EPUB to a single HTML file with embedded images:
python main.py "path/to/book.epub" -o output.htmlThe following reference covers every option exposed by the CLI. epub_path is the required input
EPUB; -h/--help prints this reference.
| Option | Purpose | Example |
|---|---|---|
epub_path |
Input EPUB file or directory containing EPUB files. | python main.py "book.epub" |
-h, --help |
Show help and exit. | python main.py --help |
--version |
Show the installed tool version and exit. | epub-to-html --version |
--print-completion {bash,zsh,fish,powershell} |
Print a complete shell-completion script and exit; it does not modify shell configuration automatically. | epub-to-html --print-completion powershell |
-o, --output PATH |
Output HTML file for one EPUB, or output directory for an EPUB directory. Defaults to output.html for a file and output for a directory. |
python main.py book.epub --output converted.html |
--page-list |
Extract EPUB 3 page-list navigation and render it in wrapped output (requires --navigation). |
python main.py book.epub --navigation --page-list |
--workers N |
Maximum directory-input workers; defaults conservatively to 1. |
python main.py books --output converted --workers 2 |
--worker-backend {thread,process} |
Parallel execution backend for batch mode; defaults to thread. |
python main.py books --output converted --workers 4 --worker-backend process |
--inspect |
Print EPUB metadata, spine, media inventory, layout signals, and unsupported features without writing output. | python main.py book.epub --inspect |
--landmarks |
Extract EPUB 3 landmarks navigation and render it in wrapped output (requires --navigation). |
python main.py book.epub --navigation --landmarks |
-s, --strategy {embed,extract} |
Embed images as data URLs or extract them beside the HTML; defaults to embed. |
python main.py book.epub --strategy extract |
-w, --wrap |
Add a complete HTML document shell and default styling. It is optional when using any option that needs wrapped output. | python main.py book.epub --wrap |
-c, --css PATH |
Inline a trusted local stylesheet; automatically enables wrapping. | python main.py book.epub --css styles.css |
--css-var KEY=VALUE |
Inject a CSS custom property into :root of wrapped output; repeatable. |
python main.py book.epub --css-var accent-color=#1a1a2e --css-var link-color=cornflowerblue --wrap |
--remove-toc |
Remove detected table-of-contents elements. | python main.py book.epub --remove-toc |
--remove-cover |
Remove detected cover elements. | python main.py book.epub --remove-cover |
--spine-range START:END |
Convert a one-based inclusive chapter range; either bound may be omitted. | python main.py book.epub --spine-range 2:8 |
--exclude-content CATEGORY |
Exclude cover, navigation, front-matter, endnotes, or appendices; repeatable. |
python main.py book.epub --exclude-content appendices |
--images-dir-name NAME |
Extracted image directory basename; {stem} expands to the output filename stem. |
python main.py book.epub --strategy extract --images-dir-name assets |
--chunked |
Write prepared documents incrementally to staging; navigation requires collecting the generated sections first. | python main.py book.epub --chunked |
--safe-mode |
Remove active markup, unsafe URLs, EPUB CSS, SVG, and invalid raster images. | python main.py book.epub --safe-mode --wrap |
--preserve-internal-css |
Inline EPUB stylesheets and rewrite their registered local asset URLs; ignored by safe mode. | python main.py book.epub --preserve-internal-css --wrap |
--preserve-media-overlays |
Preserve EPUB 3 media-overlay SMIL references as data-media-overlays on sections. |
python main.py book.epub --preserve-media-overlays --wrap |
--preserve-scripts |
Keep <script> elements that would normally be stripped by safe mode. |
python main.py book.epub --safe-mode --preserve-scripts |
--svg-policy {omit,extract,preserve} |
Select SVG handling; safe mode always removes SVG. | python main.py book.epub --svg-policy preserve |
--mathml-policy {omit,preserve} |
Select MathML handling; safe mode remains restrictive. | python main.py book.epub --mathml-policy preserve |
--media-policy {omit,extract,preserve} |
Choose audio/video resource treatment. Extraction copies resources only with --strategy extract. |
python main.py book.epub --strategy extract --media-policy extract |
--font-policy {omit,extract,preserve} |
Choose embedded-font resource treatment. Extraction copies resources only with --strategy extract. |
python main.py book.epub --strategy extract --font-policy extract |
--navigation |
Add an automatically generated table of contents and back-to-top links; automatically enables wrapping. | python main.py book.epub --navigation |
--navigation-depth N |
Include headings through level N in generated navigation (1–6); defaults to 1. |
python main.py book.epub --navigation --navigation-depth 2 |
--reader-theme {auto,light,dark} |
Select the wrapped reader color theme; defaults to auto. |
python main.py book.epub --reader-theme dark --wrap |
--reader-max-width CSS_VALUE |
Set wrapped reading width and automatically enable wrapping; defaults to 72ch when wrapping is enabled. |
python main.py book.epub --reader-max-width 65ch |
--reader-font-family CSS_VALUE |
Set wrapped reading font and automatically enable wrapping; defaults to Georgia, serif when wrapping is enabled. |
python main.py book.epub --reader-font-family system-ui |
--force |
Replace existing HTML and extracted-image output. | python main.py book.epub --force |
--deadline-seconds N |
Cancel conversion after the cooperative deadline. | python main.py book.epub --deadline-seconds 30 |
--fail-on-warning |
Abort without publishing if conversion warnings occur. | python main.py book.epub --fail-on-warning |
--no-validate-output |
Skip staged duplicate-ID and local-reference checks. | python main.py book.epub --no-validate-output |
--no-resolve-switch |
Keep all branches of EPUB 3 <switch> elements instead of resolving them. |
python main.py book.epub --no-resolve-switch |
--stable-mime-types |
Use known filename-extension MIME types instead of host-dependent MIME guessing. | python main.py book.epub --stable-mime-types |
--newline {lf,crlf} |
Select output line endings; defaults to lf. |
python main.py book.epub --newline crlf |
--report-json PATH |
Write a local machine-readable conversion report. | python main.py book.epub --report-json report.json |
--report-html PATH |
Write a companion HTML report with chapters, warnings, and output facts. | python main.py book.epub --report-html report.html |
--no-progress |
Disable progress bars. | python main.py book.epub --no-progress |
--force-progress |
Show progress bars even when stderr is not a TTY. | python main.py book.epub --force-progress |
--verbose |
Show unexpected error tracebacks. | python main.py book.epub --verbose |
--max-archive-entries N |
Maximum ZIP member count; defaults to 10000. |
python main.py book.epub --max-archive-entries 2000 |
--max-compressed-bytes N |
Maximum compressed archive size; defaults to 268435456. |
python main.py book.epub --max-compressed-bytes 50000000 |
--max-expanded-bytes N |
Maximum expanded archive size; defaults to 1073741824. |
python main.py book.epub --max-expanded-bytes 500000000 |
--max-entry-bytes N |
Maximum expanded size of one archive member; defaults to 104857600. |
python main.py book.epub --max-entry-bytes 50000000 |
--max-compression-ratio N |
Maximum ZIP compression ratio; defaults to 1000. |
python main.py book.epub --max-compression-ratio 500 |
--max-documents N |
Maximum EPUB document items; defaults to 5000. |
python main.py book.epub --max-documents 1000 |
--max-images N |
Maximum EPUB image items; defaults to 10000. |
python main.py book.epub --max-images 2000 |
--max-output-bytes N |
Maximum generated output size; defaults to 1073741824. |
python main.py book.epub --max-output-bytes 500000000 |
Normal conversion preserves source markup and is intended for EPUBs you understand and trust. For an EPUB from an unknown source, explicitly enable safe mode:
python main.py "unknown-source.epub" --safe-mode --wrap -o "output.html"Safe mode strips browser-active markup and unsafe resource URLs. It is a content filter, not a complete sandbox.
Completion output is intentionally printed rather than silently editing a shell profile. Each generated script covers every CLI option, choice value, and the EPUB positional file. Install it once using the command appropriate for your shell, then completion is automatic in future sessions:
- PowerShell:
epub-to-html --print-completion powershell | Out-File $PROFILE\u005cepub-to-html.ps1 -Encoding utf8, then add. $PROFILE\u005cepub-to-html.ps1to your profile. - Bash:
epub-to-html --print-completion bash > ~/.local/share/bash-completion/completions/epub-to-html, then start a new shell. - Zsh:
epub-to-html --print-completion zsh > ~/.zfunc/_epub-to-html, and add that directory tofpath. - Fish:
epub-to-html --print-completion fish > ~/.config/fish/completions/epub-to-html.fish.
The converter itself does not need a completion script to run; this is only shell help for typing commands.
Python integrations can call the documented library API without subprocesses:
from api import convert
from model import ConversionOptions
result = convert(
"book.epub",
"book.html",
ConversionOptions(input_path="unused.epub", output_path="unused.html", safe_html=True),
)
print(result.output_path)The explicit path arguments always win; the supplied ConversionOptions contributes
only conversion policy. ConversionResult supplies paths, counts, warnings, duration,
and selected chapter names.
The converter reads EPUB document items in spine order when the EPUB supplies a spine; otherwise it uses the document order provided by the publication. It gives each merged section and source ID a deterministic unique anchor, then rewrites resolvable local document links to those anchors. Links with query strings, external URLs, root-relative URLs, and unresolved paths remain unchanged so the conversion does not silently invent a destination.
Image references are resolved against their EPUB-relative paths. Ambiguous image
basenames are deliberately not guessed and produce a warning. UTF-8 is preferred
for chapter text; when it fails, the converter samples up to 64 KiB for encoding
detection and records a decode-fallback warning.
This tool targets reflowable, HTML-based EPUB content. Fixed-layout publications, complex CSS layouts, JavaScript-driven books, audio/video, fonts, SVG, MathML, form controls, and non-image assets are not faithfully preserved. Safe mode intentionally removes SVG, MathML, CSS, active elements, and unsafe URLs. For trusted EPUBs, normal mode preserves more source markup but does not guarantee that the source will render identically outside its original reader.
--strategy embed is the default. It produces one self-contained HTML file that
is easy to move or share, but base64-encoded images can make it substantially
larger. --strategy extract keeps the HTML smaller by writing an image directory
beside it. Keep that directory and the HTML together when moving, copying, or
publishing the result; the generated references are relative paths.
Before parsing, the converter rejects archives that exceed its entry, compressed
size, expanded size, member size, compression ratio, document, image, or output
size limits. The defaults are listed in --help and can be made stricter or
looser with the corresponding --max-* options. A deadline can be supplied with
--deadline-seconds; output is staged and published only after conversion checks
complete.
Warnings cover skipped images/documents, ambiguous or unresolved images, and
encoding fallbacks. Use --fail-on-warning to prevent publication when any
warning occurs, or --report-json report.json to save the full warning
list, paths, counts, sizes, duration, conversion policy, and tool version for automation.
| Code | Meaning |
|---|---|
0 |
Conversion completed successfully. |
1 |
Invalid input or another expected conversion failure. |
2 |
Invalid command-line usage (reported by argparse). |
3 |
Unexpected internal failure; use --verbose for a traceback. |
4 |
Policy or staged-output validation rejection, including archive limits. |
5 |
Output or report write failure. |
130 |
Conversion was cancelled or exceeded its deadline. |
| Symptom | What to check |
|---|---|
| “Input is not a valid ZIP/EPUB” or missing container files | Confirm the path points to a complete EPUB, not a renamed download, and try opening it in a standard EPUB reader. |
| Images are missing | Review conversion warnings. Verify that the EPUB manifest contains the image and that its relative path is unambiguous. For extracted output, keep the generated asset directory with the HTML. |
| A local link does not work | Only resolvable, query-free EPUB-local links are rewritten. Check the warning report and whether the target source document or fragment exists. |
| Garbled characters | Look for a decode-fallback warning. The source may declare or contain an incorrect encoding |
| HTML is unexpectedly large | Use --strategy extract, remove unneeded cover/TOC content, or inspect large images in the source EPUB. Embedded images make the single output portable at the cost of size. |
| “Output already exists” or access denied | Pick a new path or use --force only when replacement is intended. Ensure the output directory is writable. |
| Windows path or filename error | Use a valid filename and a safe --images-dir-name basename; avoid .., separators, and device names such as CON or LPT1. |
| File is locked by antivirus, sync, or another application | Close browser/editor windows holding the output, pause the interfering process if appropriate, and retry in a local writable directory. |
The project uses Nox for repeatable development commands. After installing the
development extras, run python -m nox to format, lint, type-check, test, and
build the project. To run one activity, use python -m nox -- tests,
python -m nox -- lint, or another task listed in CONTRIBUTING.md.
The test suite covers archive policy, conversion transforms, image handling,
output staging, and CLI behavior. Contributions should include a focused regression
test when they change observable conversion behavior.
- docs/ARCHITECTURE.md — Module overview, data flow, design decisions, and extension points.
- docs/API.md — Full library API reference for Python integrations.
- docs/WARNINGS.md — Warning codes reference for troubleshooting conversion diagnostics.
- CONTRIBUTING.md — Development setup, testing strategy, and code style guide.
See LICENSE file for details.