Reported for a Dostoevsky collection: nine TOC entries that point into the middle of one chapter file all resolve to the same page, both in the Bookmarks tree and when clicked.
РОМАН В ДЕВЯТИ ПИСЬМАХ OPS/ch1-24.xhtml#id17 -> page 263
I OPS/ch1-24.xhtml#id18 -> page 263
II OPS/ch1-24.xhtml#id19 -> page 263
...
VIII OPS/ch1-24.xhtml#id25 -> page 263
The chapter is 11 pages long (the next TOC entry is page 274) and the anchors are spread over 45 KB of XHTML, so they should land on different pages.
Repro
Minimal EPUB attached (uploaded as .zip, rename to .epub). It has one chapter with two kinds of anchor, so the difference is visible in one file:
| TOC entry |
page |
|
| span-anchored section 1 |
1 |
|
| span-anchored section 2 |
1 |
wrong, should be ~6 |
| span-anchored section 3 |
1 |
wrong, should be ~12 |
| div-anchored section 1 |
3 |
correct |
| div-anchored section 2 |
9 |
correct |
| div-anchored section 3 |
14 |
correct |
Both anchor kinds sit at the same places in the document; only the element carrying the id differs.
Cause
This is a MuPDF bug, not our link handling — the wrong page is already in what fz_resolve_link_dest() hands back.
The book puts every id on an inline <span> that wraps a block <div>, and nests them:
<span id="id17"><div class="title3"><p>РОМАН В ДЕВЯТИ ПИСЬМАХ</p></div>
<span id="id18"><div class="title4"><p>I</p></div> ...content...
</span><span id="id19"><div class="title4"><p>II</p></div> ...content...
</span>...
</span>
That is invalid nesting (block inside inline). Tracing it:
EngineMupdf.cpp ResolveLink() gets chapter=25, page=0, y=11.0 from fz_resolve_link_dest() for #id17 and for #id18 … #id25.
- In
epub_resolve_link() (source/html/epub-doc.c), fz_find_html_target() returns y = 11.0 for every id, in every chapter, with page_h = 435. So page = y / ph is 0 every time and the destination is the chapter's first page.
- The match happens in
find_flow_target() (source/html/html-outline.c), and the flow node it finds for each id is:
id=id17 type=6 y=11.0 x=11.0 w=0.0 h=11.0 boxtype=2 tag=span
type=6 is FLOW_ANCHOR, boxtype=2 is BOX_INLINE. MuPDF does emit a zero-width anchor node per id, but because the <span> has no inline content of its own it is never positioned by layout and keeps the initial line position (11, 11) — the top-left of the chapter's content box — for all of them.
find_box_target()'s box->s.layout.y fallback never applies either: inline boxes are only reachable through the flow list, so the box branch never visits them.
A flat sequence of <span id><div>…</div></span> siblings resolves fine; it takes the outer <span> wrapping the inner ones to trigger it, which is why the attached repro nests them.
Fix
Would have to be in MuPDF. The flow node's position is the broken input, so find_flow_target() returning flow->y can't be patched in isolation — the plausible fix is: when the id is found on an anchor whose box is BOX_INLINE with no laid-out content, walk from that box in document order to the next box that does have a position and use its y. That's surgery in html layout affecting every reflowable format, so filing this first rather than patching speculatively.
Reported for a Dostoevsky collection: nine TOC entries that point into the middle of one chapter file all resolve to the same page, both in the Bookmarks tree and when clicked.
The chapter is 11 pages long (the next TOC entry is page 274) and the anchors are spread over 45 KB of XHTML, so they should land on different pages.
Repro
Minimal EPUB attached (uploaded as
.zip, rename to.epub). It has one chapter with two kinds of anchor, so the difference is visible in one file:Both anchor kinds sit at the same places in the document; only the element carrying the
iddiffers.Cause
This is a MuPDF bug, not our link handling — the wrong page is already in what
fz_resolve_link_dest()hands back.The book puts every
idon an inline<span>that wraps a block<div>, and nests them:That is invalid nesting (block inside inline). Tracing it:
EngineMupdf.cppResolveLink()getschapter=25, page=0, y=11.0fromfz_resolve_link_dest()for#id17and for#id18…#id25.epub_resolve_link()(source/html/epub-doc.c),fz_find_html_target()returns y = 11.0 for every id, in every chapter, withpage_h = 435. Sopage = y / phis 0 every time and the destination is the chapter's first page.find_flow_target()(source/html/html-outline.c), and the flow node it finds for each id is:type=6isFLOW_ANCHOR,boxtype=2isBOX_INLINE. MuPDF does emit a zero-width anchor node perid, but because the<span>has no inline content of its own it is never positioned by layout and keeps the initial line position (11, 11) — the top-left of the chapter's content box — for all of them.find_box_target()'sbox->s.layout.yfallback never applies either: inline boxes are only reachable through the flow list, so the box branch never visits them.A flat sequence of
<span id><div>…</div></span>siblings resolves fine; it takes the outer<span>wrapping the inner ones to trigger it, which is why the attached repro nests them.Fix
Would have to be in MuPDF. The flow node's position is the broken input, so
find_flow_target()returningflow->ycan't be patched in isolation — the plausible fix is: when the id is found on an anchor whose box isBOX_INLINEwith no laid-out content, walk from that box in document order to the next box that does have a position and use itsy. That's surgery in html layout affecting every reflowable format, so filing this first rather than patching speculatively.