diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp
index 76a6901d..b9aad8d2 100644
--- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp
+++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp
@@ -21,6 +21,7 @@
// Minimum file size (in bytes) to show indexing popup - smaller chapters don't benefit from it
constexpr size_t MIN_SIZE_FOR_POPUP = 10 * 1024; // 10KB
constexpr size_t PARSE_BUFFER_SIZE = 1024;
+constexpr size_t MAX_BUFFERED_TEXT_WORDS = 300;
// Hard cap on the number of anchor IDs recorded per chapter. Legitimate navigation
// anchors (TOC entries, footnotes, cross-references) rarely exceed a few hundred per
@@ -201,6 +202,8 @@ void ChapterHtmlSlimParser::flushPendingAnchor() {
// flush the contents of partWordBuffer to currentTextBlock
void ChapterHtmlSlimParser::flushPartWordBuffer() {
+ flushLongTextBlockIfNeeded();
+
// Determine font style from depth-based tracking and CSS effective style
const bool isBold = boldUntilDepth < depth || effectiveBold;
const bool isItalic = italicUntilDepth < depth || effectiveItalic;
@@ -228,6 +231,20 @@ void ChapterHtmlSlimParser::flushPartWordBuffer() {
listItemBulletOnly = false;
}
+void ChapterHtmlSlimParser::flushLongTextBlockIfNeeded() {
+ if (!currentTextBlock || currentTextBlock->size() <= MAX_BUFFERED_TEXT_WORDS) {
+ return;
+ }
+
+ LOG_DBG("EHP", "Text block too long, splitting into multiple pages");
+ const int horizontalInset = currentTextBlock->getBlockStyle().totalHorizontalInset();
+ const uint16_t effectiveWidth =
+ (horizontalInset < viewportWidth) ? static_cast(viewportWidth - horizontalInset) : viewportWidth;
+ currentTextBlock->layoutAndExtractLines(
+ renderer, fontId, effectiveWidth,
+ [this](const std::shared_ptr& textBlock) { this->addLineToPage(textBlock); }, false);
+}
+
// start a new text block if needed
void ChapterHtmlSlimParser::startNewTextBlock(const BlockStyle& blockStyle) {
nextWordContinues = false; // New block = new paragraph, no continuation
@@ -1155,20 +1172,9 @@ void XMLCALL ChapterHtmlSlimParser::characterData(void* userData, const XML_Char
self->partWordBuffer[self->partWordBufferIndex++] = s[i];
}
- // If we have > 750 words buffered up, perform the layout and consume out all but the last line
- // There should be enough here to build out 1-2 full pages and doing this will free up a lot of
- // memory.
+ // If we have a large number of words buffered up, perform the layout and consume out all but the last line.
// Spotted when reading Intermezzo, there are some really long text blocks in there.
- if (self->currentTextBlock->size() > 750) {
- LOG_DBG("EHP", "Text block too long, splitting into multiple pages");
- const int horizontalInset = self->currentTextBlock->getBlockStyle().totalHorizontalInset();
- const uint16_t effectiveWidth = (horizontalInset < self->viewportWidth)
- ? static_cast(self->viewportWidth - horizontalInset)
- : self->viewportWidth;
- self->currentTextBlock->layoutAndExtractLines(
- self->renderer, self->fontId, effectiveWidth,
- [self](const std::shared_ptr& textBlock) { self->addLineToPage(textBlock); }, false);
- }
+ self->flushLongTextBlockIfNeeded();
}
void XMLCALL ChapterHtmlSlimParser::defaultHandlerExpand(void* userData, const XML_Char* s, const int len) {
diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h
index 9cc01e69..e9496a12 100644
--- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h
+++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h
@@ -4,6 +4,7 @@
#include
#include
+#include
#include
#include
#include
@@ -84,7 +85,7 @@ class ChapterHtmlSlimParser {
// Anchor-to-page mapping: tracks which page each HTML id attribute lands on
int completedPageCount = 0;
- std::vector> anchorData;
+ std::deque> anchorData;
std::string pendingAnchorId; // deferred until after previous text block is flushed
std::vector tocAnchors; // the list of anchors that are TOC chapter boundaries
uint16_t xpathParagraphIndex = 0;
@@ -112,6 +113,7 @@ class ChapterHtmlSlimParser {
void startNewTextBlock(const BlockStyle& blockStyle);
void flushPendingAnchor();
void flushPartWordBuffer();
+ void flushLongTextBlockIfNeeded();
void makePages();
static EpdFontFamily::Style fontStyleForTextDecoration(CssTextDecoration decoration);
static void applyDirectionToEntry(StyleStackEntry& entry, const CssStyle& css);
@@ -173,7 +175,7 @@ class ChapterHtmlSlimParser {
void abortParse(); // tear down without flushing (error / abandon)
void addLineToPage(std::shared_ptr line);
- const std::vector>& getAnchors() const { return anchorData; }
+ const std::deque>& getAnchors() const { return anchorData; }
// Byte progress of the in-flight parse, used to estimate a still-building section's total page
// count (a giant single-spine book never fully lays out, so its real count is unknown). Valid