(userData);
+ if (self->streamFailed) {
+ return;
+ }
+
// Middle of skip
if (self->skipUntilDepth < self->depth) {
self->depth += 1;
@@ -447,7 +458,7 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
}
// Flush any pending text before starting the table
if (self->partWordBufferIndex > 0) {
- self->flushPartWordBuffer();
+ if (!self->flushPartWordBuffer()) return;
}
if (self->currentTextBlock && !self->currentTextBlock->isEmpty()) {
self->makePages();
@@ -469,7 +480,7 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
if (self->currentTable && self->currentTable->depth == 1 && (strcmp(name, "td") == 0 || strcmp(name, "th") == 0)) {
if (self->partWordBufferIndex > 0) {
- self->flushPartWordBuffer();
+ if (!self->flushPartWordBuffer()) return;
}
if (self->currentTable->rows.empty()) {
self->currentTable->rows.emplace_back();
@@ -753,7 +764,7 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
// Flush any pending text block so it appears before the image
if (self->partWordBufferIndex > 0) {
- self->flushPartWordBuffer();
+ if (!self->flushPartWordBuffer()) return;
}
if (self->currentTextBlock && !self->currentTextBlock->isEmpty()) {
const BlockStyle parentBlockStyle = self->currentTextBlock->getBlockStyle();
@@ -921,7 +932,7 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
// Flush buffer before style change
if (self->partWordBufferIndex > 0) {
const bool endsAtDashBreak = bufferEndsWithBreakableDash(self->partWordBuffer, self->partWordBufferIndex);
- self->flushPartWordBuffer();
+ if (!self->flushPartWordBuffer()) return;
if (!endsAtDashBreak) {
self->nextWordContinues = true;
}
@@ -960,7 +971,7 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
// Otherwise tags like ..."item?" can carry the final word into the next paragraph.
if (self->partWordBufferIndex > 0 && ((matches(name, HEADER_TAGS, NUM_HEADER_TAGS)) ||
(matches(name, BLOCK_TAGS, NUM_BLOCK_TAGS) && strcmp(name, "br") != 0))) {
- self->flushPartWordBuffer();
+ if (!self->flushPartWordBuffer()) return;
}
if (matches(name, HEADER_TAGS, NUM_HEADER_TAGS)) {
@@ -993,7 +1004,7 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
if (strcmp(name, "br") == 0) {
if (self->partWordBufferIndex > 0) {
// flush word preceding
to currentTextBlock before calling startNewTextBlock
- self->flushPartWordBuffer();
+ if (!self->flushPartWordBuffer()) return;
}
// Tag the new block so startNewTextBlock can inject a full line-height gap if
// the block remains empty (i.e.
is a section separator between paragraphs).
@@ -1039,7 +1050,7 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
// Flush buffer before style change so preceding text gets current style
if (self->partWordBufferIndex > 0) {
const bool endsAtDashBreak = bufferEndsWithBreakableDash(self->partWordBuffer, self->partWordBufferIndex);
- self->flushPartWordBuffer();
+ if (!self->flushPartWordBuffer()) return;
if (!endsAtDashBreak) {
self->nextWordContinues = true;
}
@@ -1088,7 +1099,7 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
// Flush buffer before style change so preceding text gets current style
if (self->partWordBufferIndex > 0) {
const bool endsAtDashBreak = bufferEndsWithBreakableDash(self->partWordBuffer, self->partWordBufferIndex);
- self->flushPartWordBuffer();
+ if (!self->flushPartWordBuffer()) return;
if (!endsAtDashBreak) {
self->nextWordContinues = true;
}
@@ -1120,7 +1131,7 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
// Flush buffer before style change so preceding text gets current style
if (self->partWordBufferIndex > 0) {
const bool endsAtDashBreak = bufferEndsWithBreakableDash(self->partWordBuffer, self->partWordBufferIndex);
- self->flushPartWordBuffer();
+ if (!self->flushPartWordBuffer()) return;
if (!endsAtDashBreak) {
self->nextWordContinues = true;
}
@@ -1154,7 +1165,7 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
// Flush buffer before style change so preceding text gets current style
if (self->partWordBufferIndex > 0) {
const bool endsAtDashBreak = bufferEndsWithBreakableDash(self->partWordBuffer, self->partWordBufferIndex);
- self->flushPartWordBuffer();
+ if (!self->flushPartWordBuffer()) return;
if (!endsAtDashBreak) {
self->nextWordContinues = true;
}
@@ -1199,6 +1210,10 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
void XMLCALL ChapterHtmlSlimParser::characterData(void* userData, const XML_Char* s, const int len) {
auto* self = static_cast(userData);
+ if (self->streamFailed) {
+ return;
+ }
+
// Skip content of nested tables (depth > 1 means we're inside a nested table)
if (self->currentTable && self->currentTable->depth > 1) {
return;
@@ -1261,7 +1276,7 @@ void XMLCALL ChapterHtmlSlimParser::characterData(void* userData, const XML_Char
if (self->partWordBufferIndex >= MAX_WORD_SIZE) {
// Buffer is full — flush before appending. Pure ASCII means no
// partial multi-byte sequence can be at the boundary.
- self->flushPartWordBuffer();
+ if (!self->flushPartWordBuffer()) return;
}
self->partWordBuffer[self->partWordBufferIndex++] = s[i];
continue;
@@ -1271,7 +1286,7 @@ void XMLCALL ChapterHtmlSlimParser::characterData(void* userData, const XML_Char
// Inside : treat \n as a hard line break.
if (s[i] == '\n' && self->preUntilDepth < self->depth) {
if (self->partWordBufferIndex > 0) {
- self->flushPartWordBuffer();
+ if (!self->flushPartWordBuffer()) return;
}
// Blank line: the current block is empty, but we still need to emit a visible
// empty line. Add a single space so the block is non-empty and makePages()
@@ -1285,7 +1300,7 @@ void XMLCALL ChapterHtmlSlimParser::characterData(void* userData, const XML_Char
}
// Currently looking at whitespace, if there's anything in the partWordBuffer, flush it
if (self->partWordBufferIndex > 0) {
- self->flushPartWordBuffer();
+ if (!self->flushPartWordBuffer()) return;
}
// Whitespace is a real word boundary — reset continuation state
self->nextWordContinues = false;
@@ -1313,14 +1328,14 @@ void XMLCALL ChapterHtmlSlimParser::characterData(void* userData, const XML_Char
// "200 Quadrat-" / "kilometer" instead of the unusable "200" / "Quadratkilometer".
if (static_cast(s[i]) == 0xC2 && i + 1 < len && static_cast(s[i + 1]) == 0xA0) {
if (self->partWordBufferIndex > 0) {
- self->flushPartWordBuffer();
+ if (!self->flushPartWordBuffer()) return;
}
self->partWordBuffer[0] = ' ';
self->partWordBuffer[1] = '\0';
self->partWordBufferIndex = 1;
self->nextWordContinues = true; // Attach space to previous word (no break).
- self->flushPartWordBuffer();
+ if (!self->flushPartWordBuffer()) return;
self->nextWordContinues = true; // Next real word attaches to this space (no break).
@@ -1332,14 +1347,14 @@ void XMLCALL ChapterHtmlSlimParser::characterData(void* userData, const XML_Char
if (static_cast(s[i]) == 0xE2 && i + 2 < len && static_cast(s[i + 1]) == 0x80 &&
static_cast(s[i + 2]) == 0xAF) {
if (self->partWordBufferIndex > 0) {
- self->flushPartWordBuffer();
+ if (!self->flushPartWordBuffer()) return;
}
self->partWordBuffer[0] = ' ';
self->partWordBuffer[1] = '\0';
self->partWordBufferIndex = 1;
self->nextWordContinues = true;
- self->flushPartWordBuffer();
+ if (!self->flushPartWordBuffer()) return;
self->nextWordContinues = true;
@@ -1377,13 +1392,13 @@ void XMLCALL ChapterHtmlSlimParser::characterData(void* userData, const XML_Char
saved[j] = self->partWordBuffer[safeLen + j];
}
self->partWordBufferIndex = safeLen;
- self->flushPartWordBuffer();
+ if (!self->flushPartWordBuffer()) return;
for (int j = 0; j < overflow; j++) {
self->partWordBuffer[j] = saved[j];
}
self->partWordBufferIndex = overflow;
} else {
- self->flushPartWordBuffer();
+ if (!self->flushPartWordBuffer()) return;
}
}
@@ -1410,6 +1425,10 @@ void XMLCALL ChapterHtmlSlimParser::defaultHandlerExpand(void* userData, const X
void XMLCALL ChapterHtmlSlimParser::endElement(void* userData, const XML_Char* name) {
auto* self = static_cast(userData);
+ if (self->streamFailed) {
+ return;
+ }
+
// Check if any style state will change after we decrement depth
// If so, we MUST flush the partWordBuffer with the CURRENT style first
// Note: depth hasn't been decremented yet, so we check against (depth - 1)
@@ -1446,7 +1465,7 @@ void XMLCALL ChapterHtmlSlimParser::endElement(void* userData, const XML_Char* n
if (shouldFlush) {
const bool endsAtDashBreak = bufferEndsWithBreakableDash(self->partWordBuffer, self->partWordBufferIndex);
- self->flushPartWordBuffer();
+ if (!self->flushPartWordBuffer()) return;
// If closing an inline element, the next word fragment continues the same visual word —
// unless the buffered text ended at a dash that should allow a line break (em/en dash, etc.).
if (isInlineTag && !endsAtDashBreak) {
@@ -1489,7 +1508,7 @@ void XMLCALL ChapterHtmlSlimParser::endElement(void* userData, const XML_Char* n
if (self->currentTable && self->currentTable->depth == 1 && (strcmp(name, "td") == 0 || strcmp(name, "th") == 0)) {
if (self->partWordBufferIndex > 0) {
- self->flushPartWordBuffer();
+ if (!self->flushPartWordBuffer()) return;
}
// Determine if the whole row consists of header cells
if (!self->currentTable->rows.empty()) {
@@ -1513,7 +1532,7 @@ void XMLCALL ChapterHtmlSlimParser::endElement(void* userData, const XML_Char* n
if (self->currentTable && self->currentTable->depth == 1 && strcmp(name, "table") == 0) {
if (self->partWordBufferIndex > 0) {
- self->flushPartWordBuffer();
+ if (!self->flushPartWordBuffer()) return;
}
self->currentTableCell = nullptr;
self->emitBufferedTable();
diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h
index 9f47ff27..85c4608e 100644
--- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h
+++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h
@@ -158,7 +158,7 @@ class ChapterHtmlSlimParser final : public Print {
void updateEffectiveInlineStyle();
bool ensureHeapForTextLayout(const char* phase);
void startNewTextBlock(const BlockStyle& blockStyle);
- void flushPartWordBuffer();
+ bool flushPartWordBuffer();
void makePages();
void emitBufferedTable();
void emitTableAsFragments(BufferedTable& table);