Skip to content

epub-codec: EPUB 2/3 reading and deterministic EPUB 3 writing against the shared content schema #801

Description

@Mearman

Proposes a new format codec — epub-codec — converting EPUB to and from document-schema.js's ContentDocument. EPUB is the one mainstream reflowable-document format the family doesn't touch, and it decomposes into two things the family already does well: a ZIP package with a manifest and a fixed first entry (structurally odf.js/ooxml.js territory) whose payload is flowable block content (semantically markdown-codec territory). The dependency stack is the one both package codecs already run on — fast-xml-parser, fflate, zod, document-schema.js — because EPUB content documents are XHTML, which EPUB 3.3 requires to be well-formed XML rather than tag-soup HTML.

  • OCF container (ZIP): an EPUB is a ZIP whose very first entry must be mimetype, stored uncompressed, containing exactly application/epub+zip — the same first-entry/stored/deterministic-byte-layout discipline odf.js's zipPackage already pins over fflate (fixed mtime, caller-ordered entries), so epub-codec takes its zip layer from that pattern rather than inventing a new one. Reading starts at META-INF/container.xml, which names the OPF rootfile.
  • OPF package document: Dublin Core metadata (title, creator, language, identifier, date), the manifest (id → href + media type, including the cover image), and the spine (linear reading order). Metadata lands in the ContentDocument's front matter; manifest + spine drive content order.
  • XHTML content documents: each spine itemref is one XHTML document mapped to ContentDocument blocks. The family has no HTML parser today — markdown-codec's src/html/ is deliberately a bounded recogniser, not a parser — and needs none for this: XHTML content documents are XML by spec, so the reader is the fast-xml-parser stack the other codecs already use plus an element→schema mapping. That mapping covers headings, paragraphs, lists (ul/ol/dl), tables, images, hyperlinks, strong/em/code/sub/sup text styles, blockquote, pre, hr, figure/figcaption, and footnotes (EPUB 3 epub:type="footnote" asides; the EPUB 2 linked-anchor footnote idiom). Real-world files that violate well-formedness are a corpus-tolerance matter, carried the same way the other codecs carry producer quirks.
  • Navigation: the EPUB 3 nav document and the EPUB 2 NCX both project onto the outline; where they disagree with the spine, the spine wins and the difference is recorded as residue.
  • Assets: manifest images decode through byte-codec as everywhere else; CSS and fonts are residue (the schema is content, not styling) — recorded, never interpreted.
  • Writing: minimal valid EPUB 3 — mimetype, container.xml, OPF with a generated identifier, nav.xhtml, one XHTML document per top-level section, embedded images — byte-deterministic per the family's reproducible-serialisation doctrine.
  • Conventions, same as every codec: Worker-isomorphic published src/ (fflate and fast-xml-parser are both isomorphic; the per-package ESLint no-restricted-imports guard plus the workerd suite prove it), diagnostics sink + residue rather than silent loss, MIT, dependency-minimal hand-written mapping over heavyweight format libraries, real-world corpus under test:corpus (gitignored, documented in the codec's own README). Publishing needs the one-time npm trusted-publisher registration for the new name — org ExaDev, this repository, workflow ci.yml — the same manual step document-compute.js needed.

Naming: epub-codec follows the markdown-codec/pdf-codec/archive-codec pattern rather than ooxml.js/odf.js's <format>.js, because EPUB names one format rather than a format family — and epub.js on npm is taken by an unrelated project anyway, so the -codec suffix is also the collision-free choice.

Scope line: flowable EPUB only. Fixed-layout EPUB (FXL) is fixed-page geometry — closer to pdf-codec's private layout representation than to flowable content — and would be its own issue if a real need appears. Writing targets EPUB 3 exclusively; EPUB 2 is read-only.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions