Add paragraph index LUT for accurate KOReader position sync

Store per-page paragraph indices in section cache to enable precise
XPath-to-page and page-to-XPath mapping without reparsing XHTML.

Forward path (upload): generates XPath directly from paragraph LUT
instead of byte-offset estimation, eliminating drift in chapters
with non-uniform content density.

Reverse path (download): resolves incoming KOReader XPath p[N] to
the exact page via paragraph LUT lookup.

Paragraph counter counts all <p> elements including display:none
to match ChapterXPathIndexer and crengine's standard XPath counting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jpirnay
2026-03-22 12:29:26 +01:00
co-authored by Claude Opus 4.6
parent dccb82642d
commit b625b8bd26
17 changed files with 354 additions and 43 deletions
+25 -1
View File
@@ -395,9 +395,18 @@ void EpubReaderActivity::onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction
if (KOREADER_STORE.hasCredentials()) {
const int currentPage = section ? section->currentPage : 0;
const int totalPages = section ? section->pageCount : 0;
// Look up paragraph index from section cache for accurate XPath generation on upload
uint16_t paragraphIdx = 0;
bool hasParagraphIdx = false;
if (section) {
if (const auto pIdx = section->getParagraphIndexForPage(currentPage)) {
paragraphIdx = *pIdx;
hasParagraphIdx = true;
}
}
startActivityForResult(
std::make_unique<KOReaderSyncActivity>(renderer, mappedInput, epub, epub->getPath(), currentSpineIndex,
currentPage, totalPages),
currentPage, totalPages, paragraphIdx, hasParagraphIdx),
[this](const ActivityResult& result) {
if (!result.isCancelled) {
const auto& sync = std::get<SyncResult>(result.data);
@@ -405,6 +414,10 @@ void EpubReaderActivity::onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction
RenderLock lock(*this);
currentSpineIndex = sync.spineIndex;
nextPageNumber = sync.page;
if (sync.hasParagraphIndex) {
pendingParagraphLookup = true;
pendingParagraphIndex = sync.paragraphIndex;
}
section.reset();
}
}
@@ -623,6 +636,17 @@ void EpubReaderActivity::render(RenderLock&& lock) {
pendingAnchor.clear();
}
// Resolve pending KOReader sync paragraph index to accurate page via Section paragraph LUT
if (pendingParagraphLookup) {
if (const auto page = section->getPageForParagraphIndex(pendingParagraphIndex)) {
section->currentPage = *page;
LOG_DBG("ERS", "Resolved p[%u] to page %d (was %d)", pendingParagraphIndex, *page, nextPageNumber);
} else {
LOG_DBG("ERS", "Paragraph LUT not available, using estimated page %d", nextPageNumber);
}
pendingParagraphLookup = false;
}
// handles changes in reader settings and reset to approximate position based on cached progress
if (cachedChapterTotalPageCount > 0) {
// only goes to relative position if spine index matches cached value