Skip to content

convert CBZ comic ZIP archives to fixed-layout EPUB - #3892

Merged
panaC merged 4 commits into
developfrom
feat/cbz-to-epub-fxl
Oct 5, 2026
Merged

panaC merged 4 commits into
developfrom
feat/cbz-to-epub-fxl

Conversation

@panaC

@panaC panaC commented Sep 18, 2026 •

Copy link
Copy Markdown
Member

Fixes #3816

  • Accept .cbz files through the file picker, drag-and-drop, CLI import, and OS file associations.
  • Convert each comic image into a fixed-layout XHTML page.
  • Generate a valid EPUB container with an uncompressed, first-position mimetype entry.
  • Naturally sort image filenames so pages such as 1, 2, and 10 appear in the expected order.
  • Read available ComicInfo.xml metadata:
    • title, writer, summary, and year
    • cover designation and page bookmarks
    • right-to-left manga progression
  • Generate EPUB navigation, page-list, cover, viewport, and page-spread metadata.
  • Remove temporary conversion files after import, including when importing fails.
  • Add automated coverage for archive structure and the existing EPUB parsing pipeline.

Tested with samples from https://www.contrapositivediary.com/?p=1197 and https://github.com/clach04/sample_reading_media

@panaC
panaC requested a review from a team as a code owner September 18, 2026 14:36
Comment thread src/main/zip/create.ts
// https://github.com/readium/readium-lcp-server/blob/e2c484f571a8013faf13a335c007890150751d79/pack/pack.go#L265-L267
// const doDeflate = (zipPath: string) => !/\.(PDF|PNG|JPE?G|MPE?G|HEIC|WEBP|MP3|MP4|WAV|OGG|AVI)$/i.test(zipPath);
const doDeflate = (_zipPath: string) => true;
const doDeflate = (zipPath: string) => zipPath !== "mimetype";

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EPUB’s mimetype entry has special ZIP requirements:

  • It must be the first archive entry.
  • Its content must be exactly application/epub+zip.
  • It must be stored without compression.

createZip() previously compressed every entry. With:

const doDeflate = (zipPath: string) => zipPath !== "mimetype";

Yazl uses ZIP method STORE (method === 0) for the root mimetype file while continuing to deflate every other resource. The accompanying buffersFirst option ensures it is also written first.

The condition matches only the exact root entry named mimetype, so existing WebPub and customization archives are otherwise unchanged. Without it, the converted file may render but is not a conforming EPUB and can fail validation or be rejected by stricter readers.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Official reference: W3C EPUB 3.3 — §4.3.3 OCF ZIP container media type identification.
It requires that the mimetype file:

  • Is the first ZIP entry.
  • Contains exactly application/epub+zip.
  • Is not compressed or encrypted.
  • Has no ZIP-header extra field.

@panaC
panaC merged commit e0dacd5 into develop Oct 5, 2026
9 checks passed
@panaC
panaC deleted the feat/cbz-to-epub-fxl branch October 5, 2026 17:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CBZ POC to convert to EPUB FXL and test existing renderer

2 participants