From e0e3bd42512cab746024f0090838eeb86557117f Mon Sep 17 00:00:00 2001 From: jpirnay Date: Thu, 23 Apr 2026 13:15:30 +0200 Subject: [PATCH] Fix reindexing on toc-page-break --- .../Epub/parsers/ChapterHtmlSlimParser.cpp | 37 ++++++++----------- lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h | 4 ++ 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp index 4c92e398..64f12878 100644 --- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp +++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp @@ -170,6 +170,17 @@ void ChapterHtmlSlimParser::flushPartWordBuffer() { nextWordContinues = false; } +// Emit the current page, keeping paragraphLutPerPage and completedPageCount in lockstep. +// Callers must ensure currentPage is non-null and carries content; the helper resets +// currentPage to a fresh Page and zeroes currentPageNextY so the caller can keep building. +void ChapterHtmlSlimParser::emitPage(uint32_t xhtmlByteOffset) { + paragraphLutPerPage.push_back({xhtmlByteOffset, xpathParagraphIndex}); + completePageFn(std::move(currentPage)); + completedPageCount++; + currentPage.reset(new Page()); + currentPageNextY = 0; +} + // start a new text block if needed void ChapterHtmlSlimParser::startNewTextBlock(const BlockStyle& blockStyle) { nextWordContinues = false; // New block = new paragraph, no continuation @@ -198,10 +209,7 @@ void ChapterHtmlSlimParser::startNewTextBlock(const BlockStyle& blockStyle) { if (!pendingAnchorId.empty()) { if (std::find(tocAnchors.begin(), tocAnchors.end(), pendingAnchorId) != tocAnchors.end()) { if (currentPage && !currentPage->elements.empty()) { - completePageFn(std::move(currentPage)); - completedPageCount++; - currentPage.reset(new Page()); - currentPageNextY = 0; + emitPage(lastBodyChildByteOffset); } } anchorData.push_back({std::move(pendingAnchorId), static_cast(completedPageCount)}); @@ -218,10 +226,7 @@ void ChapterHtmlSlimParser::startNewTextBlock(const BlockStyle& blockStyle) { if (!pendingAnchorId.empty() && std::find(tocAnchors.begin(), tocAnchors.end(), pendingAnchorId) != tocAnchors.end()) { if (currentPage && !currentPage->elements.empty()) { - completePageFn(std::move(currentPage)); - completedPageCount++; - currentPage.reset(new Page()); - currentPageNextY = 0; + emitPage(lastBodyChildByteOffset); } } // Record deferred anchor after previous block is flushed (and any TOC page break) @@ -593,15 +598,11 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* (self->currentPageNextY + totalImageHeightWithSpacing > self->viewportHeight)) { LOG_DBG("EHP", "Image page break: currentY=%d needed=%d viewportH=%d", self->currentPageNextY, totalImageHeightWithSpacing, self->viewportHeight); - self->paragraphLutPerPage.push_back({self->lastBodyChildByteOffset, self->xpathParagraphIndex}); - self->completePageFn(std::move(self->currentPage)); - self->completedPageCount++; - self->currentPage.reset(new Page()); + self->emitPage(self->lastBodyChildByteOffset); if (!self->currentPage) { LOG_ERR("EHP", "Failed to create new page"); return; } - self->currentPageNextY = 0; } else if (!self->currentPage) { self->currentPage.reset(new Page()); if (!self->currentPage) { @@ -1423,9 +1424,7 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() { anchorData.push_back({std::move(pendingAnchorId), static_cast(completedPageCount)}); pendingAnchorId.clear(); } - paragraphLutPerPage.push_back({0u, xpathParagraphIndex}); // post-parse: no byte offset available - completePageFn(std::move(currentPage)); - completedPageCount++; + emitPage(0u); // post-parse: no byte offset available currentPage.reset(); currentTextBlock.reset(); } @@ -1444,11 +1443,7 @@ ParsedText::LineProcessResult ChapterHtmlSlimParser::addLineToPage(std::shared_p } if (currentPageNextY + lineHeight > viewportHeight) { - paragraphLutPerPage.push_back({lastBodyChildByteOffset, xpathParagraphIndex}); - completePageFn(std::move(currentPage)); - completedPageCount++; - currentPage.reset(new Page()); - currentPageNextY = 0; + emitPage(lastBodyChildByteOffset); } const bool noRoomForAnotherLine = diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h index 44605025..2ce5a1f5 100644 --- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h +++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h @@ -127,6 +127,10 @@ class ChapterHtmlSlimParser { void startNewTextBlock(const BlockStyle& blockStyle); void flushPartWordBuffer(); void makePages(); + // Emit currentPage to the consumer while keeping paragraphLutPerPage and completedPageCount + // in lockstep. Every page break MUST go through this helper; open-coded completePageFn + // calls risk desynchronising paragraphLutPerPage and failing the size check in Section.cpp. + void emitPage(uint32_t xhtmlByteOffset); // XML callbacks static void XMLCALL startElement(void* userData, const XML_Char* name, const XML_Char** atts); static void XMLCALL characterData(void* userData, const XML_Char* s, int len);