Compare commits

...
2 changed files with 23 additions and 15 deletions
+19 -13
View File
@@ -21,6 +21,7 @@
// Minimum file size (in bytes) to show indexing popup - smaller chapters don't benefit from it // 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 MIN_SIZE_FOR_POPUP = 10 * 1024; // 10KB
constexpr size_t PARSE_BUFFER_SIZE = 1024; 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 // 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 // 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 // flush the contents of partWordBuffer to currentTextBlock
void ChapterHtmlSlimParser::flushPartWordBuffer() { void ChapterHtmlSlimParser::flushPartWordBuffer() {
flushLongTextBlockIfNeeded();
// Determine font style from depth-based tracking and CSS effective style // Determine font style from depth-based tracking and CSS effective style
const bool isBold = boldUntilDepth < depth || effectiveBold; const bool isBold = boldUntilDepth < depth || effectiveBold;
const bool isItalic = italicUntilDepth < depth || effectiveItalic; const bool isItalic = italicUntilDepth < depth || effectiveItalic;
@@ -228,6 +231,20 @@ void ChapterHtmlSlimParser::flushPartWordBuffer() {
listItemBulletOnly = false; 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<uint16_t>(viewportWidth - horizontalInset) : viewportWidth;
currentTextBlock->layoutAndExtractLines(
renderer, fontId, effectiveWidth,
[this](const std::shared_ptr<TextBlock>& textBlock) { this->addLineToPage(textBlock); }, false);
}
// start a new text block if needed // start a new text block if needed
void ChapterHtmlSlimParser::startNewTextBlock(const BlockStyle& blockStyle) { void ChapterHtmlSlimParser::startNewTextBlock(const BlockStyle& blockStyle) {
nextWordContinues = false; // New block = new paragraph, no continuation 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]; self->partWordBuffer[self->partWordBufferIndex++] = s[i];
} }
// If we have > 750 words buffered up, perform the layout and consume out all but the last line // If we have a large number of 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.
// Spotted when reading Intermezzo, there are some really long text blocks in there. // Spotted when reading Intermezzo, there are some really long text blocks in there.
if (self->currentTextBlock->size() > 750) { self->flushLongTextBlockIfNeeded();
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<uint16_t>(self->viewportWidth - horizontalInset)
: self->viewportWidth;
self->currentTextBlock->layoutAndExtractLines(
self->renderer, self->fontId, effectiveWidth,
[self](const std::shared_ptr<TextBlock>& textBlock) { self->addLineToPage(textBlock); }, false);
}
} }
void XMLCALL ChapterHtmlSlimParser::defaultHandlerExpand(void* userData, const XML_Char* s, const int len) { void XMLCALL ChapterHtmlSlimParser::defaultHandlerExpand(void* userData, const XML_Char* s, const int len) {
@@ -4,6 +4,7 @@
#include <expat.h> #include <expat.h>
#include <climits> #include <climits>
#include <deque>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <string> #include <string>
@@ -84,7 +85,7 @@ class ChapterHtmlSlimParser {
// Anchor-to-page mapping: tracks which page each HTML id attribute lands on // Anchor-to-page mapping: tracks which page each HTML id attribute lands on
int completedPageCount = 0; int completedPageCount = 0;
std::vector<std::pair<std::string, uint16_t>> anchorData; std::deque<std::pair<std::string, uint16_t>> anchorData;
std::string pendingAnchorId; // deferred until after previous text block is flushed std::string pendingAnchorId; // deferred until after previous text block is flushed
std::vector<std::string> tocAnchors; // the list of anchors that are TOC chapter boundaries std::vector<std::string> tocAnchors; // the list of anchors that are TOC chapter boundaries
uint16_t xpathParagraphIndex = 0; uint16_t xpathParagraphIndex = 0;
@@ -112,6 +113,7 @@ class ChapterHtmlSlimParser {
void startNewTextBlock(const BlockStyle& blockStyle); void startNewTextBlock(const BlockStyle& blockStyle);
void flushPendingAnchor(); void flushPendingAnchor();
void flushPartWordBuffer(); void flushPartWordBuffer();
void flushLongTextBlockIfNeeded();
void makePages(); void makePages();
static EpdFontFamily::Style fontStyleForTextDecoration(CssTextDecoration decoration); static EpdFontFamily::Style fontStyleForTextDecoration(CssTextDecoration decoration);
static void applyDirectionToEntry(StyleStackEntry& entry, const CssStyle& css); static void applyDirectionToEntry(StyleStackEntry& entry, const CssStyle& css);
@@ -173,7 +175,7 @@ class ChapterHtmlSlimParser {
void abortParse(); // tear down without flushing (error / abandon) void abortParse(); // tear down without flushing (error / abandon)
void addLineToPage(std::shared_ptr<TextBlock> line); void addLineToPage(std::shared_ptr<TextBlock> line);
const std::vector<std::pair<std::string, uint16_t>>& getAnchors() const { return anchorData; } const std::deque<std::pair<std::string, uint16_t>>& getAnchors() const { return anchorData; }
// Byte progress of the in-flight parse, used to estimate a still-building section's total page // 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 // count (a giant single-spine book never fully lays out, so its real count is unknown). Valid