Skip to content

Split chapter TOC anchors point to the last fragment instead of the first (textsplit.py) #51

Description

@brunosabenca

Summary

When textsplit.py splits a large chapter XHTML file into multiple pieces, the TOC/NCX anchor for that chapter ends up pointing at the last split fragment instead of the first — so the chapter's title only becomes "active" right before the next chapter starts, and every page before that shows the previous chapter's title in the reader's status bar.

Root cause

In split_epub_text / the per-spine-item split loop (crosspoint_reader/textsplit.py, around line 396-403):

for k, (name, content) in enumerate(pieces):
    ...
    for frag in re.findall(r'id="([^"]+)"', content):
        anchor_map[(posixpath.basename(href), frag)] = name

The element that originally carried the chapter's anchor id (e.g. a wrapping <div class="chapter" id="TOC-6">) gets duplicated into every resulting fragment so each piece stays self-contained/well-formed. Since this loop walks fragments k = 0, 1, 2, ... and unconditionally overwrites anchor_map on every match, the last fragment that (redundantly) contains the id always wins — sending the anchor to the tail of the chapter instead of its start.

I confirmed this by pulling the actual toc.ncx produced for an epub with several long chapters. Every split chapter showed the same pattern (last fragment wins), e.g.:

Original file Split into TOC points to
..._TOC-6.html (Chapter I) base + _ek1_ek5 (6 pieces) _ek5 (last)
..._TOC-7.html (Chapter II) base + _ek1_ek4 (5 pieces) _ek4 (last)
..._TOC-2.html (Introduction) base + _ek1_ek2 (3 pieces) _ek2 (last)

I also verified id="TOC-6" literally appears in the base file, _ek1, and _ek5 alike — confirming the duplication-across-fragments behavior that makes the last-wins bug bite every time a chapter is split.

Downstream effect on the reader

The CrossPoint/YACP firmware's spine→TOC mapping (BookMetadataCache) intentionally falls back to the previous TOC entry for any spine item that has no direct match (reasonable default behavior for legitimately un-tocced continuation files). Combined with this bug, every fragment before the mis-pointed last fragment has no TOC match of its own, so the reader's chapter-title status bar shows the previous chapter for essentially the whole chapter, correcting only right at its very end — right before the next chapter begins. This reproduces as: "the reader shows the previous chapter as current" while reading normally through a book with long, auto-split chapters.

Fix

Make the anchor assignment first-occurrence-wins instead of last-wins, since the anchor's true target is definitionally wherever the wrapping element originally opens (the first fragment):

for frag in re.findall(r'id="([^"]+)"', content):
    anchor_map.setdefault((posixpath.basename(href), frag), name)

I applied this locally and regenerated the epub for the affected book; the resulting toc.ncx correctly points every chapter at its first fragment (e.g. ..._TOC-6.html#TOC-6 instead of ..._TOC-6_ek5.html#TOC-6), confirmed via a diff of before/after toc.ncx.

Repro

  1. Convert/optimize an EPUB containing at least one chapter whose <body> exceeds SPLIT_LIMIT (triggers split_xhtml_doc). minimal-split-anchor-repro.zip
  2. Inspect the resulting toc.ncx: the chapter's <content src="..."> points at the highest-numbered _ekN fragment rather than the base file.
  3. On-device (or via the firmware's status bar / chapter-title logic), the previous chapter's title is shown for nearly the whole chapter.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions