Symptom
In the EPUB view, Read Aloud's highlights (the active word in word mode, and the sentence highlight) go dark for whole stretches of professionally produced EPUB3 books, while the console floods pairs of
No startContainer found for epubcfi(/6/16!/4/4/12/3,:32,:168)
Unable to get range for CFI epubcfi(/6/16!/4/4/12/3,:32,:168)
— tens per second while rendering. Playback continues normally. Reproduced on Zotero 10.0 and 10.0.1-beta.2 (their resource/reader/reader.js is byte-identical).
Root cause
The affected paragraphs start with a printed-page anchor, which mainstream EPUB3 productions emit for every printed page:
<p class="para-p"><span epub:type="pagebreak" id="page_8" role="doc-pagebreak" title="8"/>We live in the woods in Oregon. …</p>
The displayed DOM is correct — the paragraph's children are [span (empty), text] (verified live via the view's document). The generator and the resolver just disagree about how that text node is numbered:
- The reader's position→CFI generation numbers text steps per the CFI spec — odd indices address the text between element children (EPUB CFI § Path Child References) — so the text after the leading span is step
/3.
EpubCFI.findNode() (epubjs submodule) resolves a text step /n as the (n-1)/2-th member of the parent's text-node list: stepsToXpath() emits text()[(n-1)/2 + 1] and walkToNode() indexes this.textNodes(container)[step.index]. For /3 it looks for a second text node, which does not exist — resolution fails.
The two numbering schemes agree only while text and element children strictly alternate starting with text; any paragraph that begins with an element breaks the round trip. Verified live: interpreting the failing selector's text step per the spec (…/12/1 under epubjs's convention) resolves to exactly the expected sentence:
/6/16!/4/4/12/3,:32,:168 -> No startContainer (as generated)
/6/16!/4/4/12/1,:32,:168 -> "Wine and hazelnut country, somewhere in that stran…"
Minimal reproduction
Any spec-numbered range CFI into <p><span/>text</p>:
// children [span, text]: the text is step /3 per the CFI spec
new EpubCFI().findNode(new EpubCFI().parse('epubcfi(/6/2!/4/2/3:4)').path.steps, doc) // -> undefined
Suggested fix
zotero/epub.js#4 makes findNode() fall back to the spec interpretation (first text node preceded by exactly step.index element children) when the existing resolution finds nothing. The legacy interpretation still runs first, so CFIs stored with epubjs's own numbering — e.g. existing annotations — resolve exactly as before; only previously-unresolvable spec-numbered CFIs gain a result. (The alternative — teaching the reader's generator to emit epubjs-style numbering — would fix Read Aloud too, but would keep producing non-spec CFIs.)
Symptom
In the EPUB view, Read Aloud's highlights (the active word in word mode, and the sentence highlight) go dark for whole stretches of professionally produced EPUB3 books, while the console floods pairs of
— tens per second while rendering. Playback continues normally. Reproduced on Zotero 10.0 and 10.0.1-beta.2 (their
resource/reader/reader.jsis byte-identical).Root cause
The affected paragraphs start with a printed-page anchor, which mainstream EPUB3 productions emit for every printed page:
The displayed DOM is correct — the paragraph's children are
[span (empty), text](verified live via the view's document). The generator and the resolver just disagree about how that text node is numbered:/3.EpubCFI.findNode()(epubjs submodule) resolves a text step/nas the(n-1)/2-th member of the parent's text-node list:stepsToXpath()emitstext()[(n-1)/2 + 1]andwalkToNode()indexesthis.textNodes(container)[step.index]. For/3it looks for a second text node, which does not exist — resolution fails.The two numbering schemes agree only while text and element children strictly alternate starting with text; any paragraph that begins with an element breaks the round trip. Verified live: interpreting the failing selector's text step per the spec (
…/12/1under epubjs's convention) resolves to exactly the expected sentence:Minimal reproduction
Any spec-numbered range CFI into
<p><span/>text</p>:Suggested fix
zotero/epub.js#4 makes
findNode()fall back to the spec interpretation (first text node preceded by exactlystep.indexelement children) when the existing resolution finds nothing. The legacy interpretation still runs first, so CFIs stored with epubjs's own numbering — e.g. existing annotations — resolve exactly as before; only previously-unresolvable spec-numbered CFIs gain a result. (The alternative — teaching the reader's generator to emit epubjs-style numbering — would fix Read Aloud too, but would keep producing non-spec CFIs.)