v0.4.0
Two new fixers, and the writer stops quietly rewriting your container.
Why this is 0.4.0 and not 0.3.3: serialize()'s output changes for every
book. Entries are no longer decompressed and recompressed, so a repaired file's
bytes differ from what 0.3.x produced, and packaging — including a mimetype
entry that violates OCF — is now preserved rather than normalized on the way out.
Anything downstream that relied on writing output to quietly correct packaging
must now approve fix.mimetype_packaging instead. No API was removed.
Added
-
A ninth fixer:
RSC-005/htm.epub2_dom.bare_text_in_body
(fix.bare_text_in_body, ConfirmNeeded). Wraps text sitting directly inside an
EPUB 2<body>— which XHTML 1.1 forbids, since it wants block-level content
there — in a<div>, one proposal per document. The text itself is not
altered, and the wrapper goes around its non-whitespace span only, so the
document's existing line breaks and indentation stay exactly where they were.<div>rather than<p>on purpose: it claims nothing about what the text
is (in the corpus it is chapter titles and converter leftovers alike), and it
reproduces the anonymous block a reading system already lays bare text out in,
so nothing moves on the page. That choice of default is what makes this
ConfirmNeeded rather than AutoSafe.Whitespace-only text nodes are never wrapped. They are the line breaks
between sibling elements, epubveri does not report them, and across the six
affected corpus books<body>holds 7,594 of them against 54 real
ones — a fixer that wrapped them all would add thousands of empty<div>s per
book. Corpus, every fix approved: 12 proposals over 6 books clear all 54
findings, 5 more books become fully valid (21 → 26), no regressions. -
An eighth fixer:
PKG-006—mimetypeis not the first entry
(fix.mimetype_packaging, AutoSafe). Re-emits themimetypeentry first and
stored uncompressed, as OCF requires so a reading system can identify the file
from its opening bytes. It is the first fixer that touches no content at
all — not one byte of any entry,mimetypeincluded; only that entry's
position and compression method change, and OCF allows exactly one answer for
each. Declines when there is nomimetypeentry to move: inventing one would
assert what the file is rather than repair how it is packaged. Dispatches on
the bareid(likeNCX-001), whichPKG-006can carry alone — it says one
thing and its subject is the container itself, so nothing needs
disambiguating.This is the repair the writer used to perform invisibly (see below). The
round-trip is now honest end to end: on the corpus the same 2 books of 171
are repaired as before, but as a proposal you can see, approve, or decline.
Fixed
- Untouched entries are no longer decompressed and recompressed. The writer
rebuilt every entry from scratch, so writing any output re-deflated the whole
container: measured across a 171-book corpus, not one book survived a
no-op load-and-write unchanged — 166 grew, 13 had entries silently switch
compression method, andMETA-INF/directory entries were dropped outright.
The original archive is now retained and any entry a fix did not rewrite is
raw-copied: same compressed bytes, method, timestamp, order, directories
included. An entry a fix does rewrite keeps the compression method the
original used, rather than defaulting to deflate. - The container is no longer normalized behind your back.
serialize()
always re-emittedmimetypefirst and stored, which repairedPKG-005/
PKG-007as a side effect of writing any output — with no fix item, no
proposal and no approval. That directly contradicted the crate's own "no
mutation without an approved fix" guarantee. Packaging is now preserved
exactly as it arrived; a book whosemimetypeviolates OCF keeps saying so
until a fix proposes otherwise. On the corpus this affects 2 books of 171
(the other 169 already packagemimetypecorrectly), whose real packaging
defect epubveri now reports instead of epubsana quietly laundering it.
Changed
docs/USAGE.md's safety guarantees now state what is actually true. They
claimed "every other byte of the container round-trips unchanged" — false for
every book measured. A repaired container is not byte-identical to its
input: the zip writer derives local headers rather than copying them (the
version-needed field and general-purpose hint bits come out as its own, ~180
bytes per book). Every byte of every entry's data is preserved, which is the
guarantee that was meant, and nothing semantic is lost — bit 11, the UTF-8
entry-name flag, is re-derived from the name.
Full notes: CHANGELOG.md. Also on crates.io.