Kindle Comics KFX Gen
Convert EPUB files or comic archives (ZIP / CBZ / RAR / CBR) to Kindle fixed-layout comic KFX without Kindle Previewer.
Compared with MOBI-based comics, KFX comics usually turn pages and load faster with large, high-resolution artwork, and are less likely to freeze the device. Fixed layout keeps pages tidy (e.g. centered, fewer stray margins). When building the KDF, each page gets an orientation field (portrait / landscape) from the image dimensions after applying EXIF orientation, and book metadata declares support for both portrait and landscape so mixed vertical and horizontal pages behave correctly.
Each successful run writes one 书名-作者.kfx (sanitized title–author; collision suffixes _2, _3, …) into the chosen output directory. By default the intermediate .kpf lives only in a temp folder and is deleted; use --keep-kpf to copy a same-stem .kpf next to the .kfx for debugging or Kindle Previewer.
- Python 3.10+
- Required (pip):
amazon-ion,lxml,pillow - Python stdlib
sqlite3backed by SQLite ≥ 3.8.2 (required forWITHOUT ROWIDinbook.kdf; current CPython builds on Windows/macOS/Linux satisfy this) - Optional
- Double-page split (
--split-spreads):numpy,pillow(detect blank centre gutter before centre-splitting wide pages; parallel) - RAR / CBR:
rarfileplus a working UnRAR backend (extraction fails otherwise)
- Double-page split (
pip install -r requirements.txt
# Or minimal CLI-only (no numpy / rar): pip install amazon-ion lxml pillow- Run locally:
python gui.py(tkinter). - Build binaries: PyInstaller cannot cross-compile; run
python scripts/build_gui.pyon Windows fordist/kckfxgen-gui.exe(one-file) and on macOS fordist/kckfxgen-gui.app(bundle). Optional--zipzips that artifact underdist/. - CI: Push a tag
v*or use Actions → Build GUI; workflow.github/workflows/build-gui.ymlbuilds both platforms and uploads zip artifacts.
| Kind | Extensions | Notes |
|---|---|---|
| EPUB | .epub |
Images collected from spine / manifest (unchanged behaviour) |
| Comic archive | .zip, .cbz |
Extracted with the stdlib; images ordered by natural path sort |
| Comic archive | .rar, .cbr |
Requires rarfile + UnRAR |
path may be any single file above, or a directory (recursive scan; multiple files run with a thread pool).
| Option | Description |
|---|---|
path |
Input: one file (.epub / .zip / .cbz / .rar / .cbr) or a directory |
-o / --output |
Single file only: KFX output directory (default: same folder as the input) |
--output-dir |
Batch: write all .kfx files into one directory (name pattern above); cannot be combined with -o |
-j / --jobs |
Worker threads (default about min(8, CPU count)) |
-d / --debug |
DEBUG logging |
--split-spreads |
Wide images (width ≥ height×1.25): split at the horizontal centre only if a blank binding gutter is detected; otherwise keep the full image (off by default) |
--split-page-order |
With --split-spreads: right-left (default, right half then left) or left-right |
--rotate-landscape-90 |
Before writing KDF: rotate landscape pages (width > height) 90° counter‑clockwise so they display as portrait; portrait pages unchanged |
--page-progression |
KPF / KDF reading direction: ltr (default) or rtl (manga-style). Sets book.kdf document_data.direction and book.kcb book_reading_direction (1 = LTR, 2 = RTL, aligned with Kindle Create comics) |
--layout-view |
KDF page structure: fixed (default, whole-page scale) or virtual (Kindle Create–style virtual panels: virtual_panel: enabled, pan_zoom, yj.authoring chain, three-slot SPM, yj_non_pdf_fixed_layout in content_features) |
--virtual-panel-axis |
Only when --layout-view virtual: middle container layout — vertical (default) or horizontal for virtual strip direction |
--keep-kpf |
After a successful KFX build, also write 同名.kpf in the same directory as the .kfx |
--title |
Override title (see below for comic archives; for EPUB overrides OPF dc:title) |
--author |
Override author (comic archives: parsed from filename; EPUB overrides dc:creator) |
--publisher |
Override publisher (comic archives: parsed from filename; EPUB overrides dc:publisher) |
Archives without OPF get title / author / publisher from the stem (filename without extension):
- If the stem ends with
[…], the inside text is publisher and that suffix is removed; otherwise if it ends with(…), that inner text is publisher. - On what remains, if there is
—,–, or-(hyphen/dash with spaces on both sides), split on the first occurrence: left = title, right = author. - If there is no such separator, the whole remainder is title; author and publisher stay empty unless taken from brackets in step 1.
Example: Attack on Titan - Hajime Isayama [Kodansha].cbz → title=Attack on Titan, author=Hajime Isayama, publisher=Kodansha.
When batch-processing a directory, --title / --author / --publisher apply the same values to every file found in that run (handy for uniform test batches; use per-book runs or rely on filename parsing when each book differs).
# Single EPUB → KFX next to the EPUB (default)
python main.py path/to/comic.epub
# Single CBZ / ZIP (title/author/publisher parsed from filename; see above)
python main.py path/to/comic.cbz
# Manual metadata (comic archive or EPUB)
python main.py path/to/comic.zip --title "Custom title" --author "Some Author" --publisher "Some Press"
# Single EPUB → KFX into a chosen directory
python main.py path/to/comic.epub -o output_dir
# Directory: recurse and convert all supported files (thread pool); default KFX next to each input
python main.py path/to/folder
# Batch: all KFX into one directory
python main.py path/to/folder --output-dir output_dir
# Thread count
python main.py path/to/folder -j 4
# Wide spreads: centre split + right-then-left (common for manga)
python main.py path/to/comic.epub --split-spreads
# After split, left page then right
python main.py path/to/comic.zip --split-spreads --split-page-order left-right
# Right-to-left page progression (manga-style; KCB + KDF)
python main.py path/to/manga.cbz --page-progression rtl
# Virtual-panel KDF (optional; default remains fixed whole-page)
python main.py path/to/comic.cbz --layout-view virtual --virtual-panel-axis vertical
# Keep intermediate .kpf beside the .kfx (e.g. for Kindle Previewer)
python main.py path/to/comic.epub --keep-kpf
# Debug log
python main.py path/to/comic.epub -dkckfxgen.pipeline.convert_to_kfx, epub_to_kpf, comic_archive_to_kpf, and convert_epub_to_kfx support:
page_progression:"ltr"(default) or"rtl"layout_view:"fixed"(default) or"virtual"virtual_panel_axis:"vertical"(default) or"horizontal"(whenlayout_view="virtual")keep_kpf:Trueto copy the temp.kpfnext to the output.kfx
Run these from the repository root.
scripts/kdf_book_metadata_dump.py decodes the book_metadata Ion blob to a tree or JSON (handles fingerprint-stripped KDF SQLite the same way as the writer). Example:
python scripts/kdf_book_metadata_dump.py path/to/book.kdf --jsonkckfxgen/spread_split.py can detect and split loose PNG/JPEG files without building KFX:
python -m kckfxgen.spread_split some.png -o output_dir
# Or, from repo root (so `kckfxgen` imports):
python src/comic_spread_split.py some.png --dry-runRegression test for split logic: python test/run_spread_split_tests.py (expects sample images under test/ named with cut* / nocut* prefixes).