fix: Don't extract unsupported formats (#977)

## Summary

* During chapter parsing, every <img> tag triggered ZIP decompression
and an SD card write regardless of whether the image format was
supported. The mandatory delay(50) after each SD write compounded the
cost. A chapter with 6 GIF images (a common decorative element in older
EPUBs) wasted ~750 ms before any text rendering began.
* **What changes are included?**
Added an ``ImageDecoderFactory::isFormatSupported()`` check before any
file I/O in the img-handler. Only JPEG and PNG proceed to extraction;
all other formats (GIF, SVG, WebP, etc.) fall through immediately to
alt-text rendering with no SD card access.
## Additional Context


Measured impact on a representative chapter with 6 GIF decorations:
|  | Before | After|
|-- | -- | --|
|Total parse time | ~882 ms | ~207 ms|
|Image handling | ~750 ms | ~76 ms|
---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _**NO**_
This commit is contained in:
jpirnay
2026-02-19 10:35:13 +01:00
committed by GitHub
parent 47aa0dda76
commit 6be4413c97

View File

@@ -17,6 +17,7 @@ constexpr int NUM_HEADER_TAGS = sizeof(HEADER_TAGS) / sizeof(HEADER_TAGS[0]);
// 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;
const char* BLOCK_TAGS[] = {"p", "li", "div", "br", "blockquote"}; const char* BLOCK_TAGS[] = {"p", "li", "div", "br", "blockquote"};
constexpr int NUM_BLOCK_TAGS = sizeof(BLOCK_TAGS) / sizeof(BLOCK_TAGS[0]); constexpr int NUM_BLOCK_TAGS = sizeof(BLOCK_TAGS) / sizeof(BLOCK_TAGS[0]);
@@ -178,6 +179,7 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
// Resolve the image path relative to the HTML file // Resolve the image path relative to the HTML file
std::string resolvedPath = FsHelpers::normalisePath(self->contentBase + src); std::string resolvedPath = FsHelpers::normalisePath(self->contentBase + src);
if (ImageDecoderFactory::isFormatSupported(resolvedPath)) {
// Create a unique filename for the cached image // Create a unique filename for the cached image
std::string ext; std::string ext;
size_t extPos = resolvedPath.rfind('.'); size_t extPos = resolvedPath.rfind('.');
@@ -259,6 +261,7 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
} else { } else {
LOG_ERR("EHP", "Failed to extract image"); LOG_ERR("EHP", "Failed to extract image");
} }
} // isFormatSupported
} }
} }
@@ -638,8 +641,10 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() {
XML_SetElementHandler(parser, startElement, endElement); XML_SetElementHandler(parser, startElement, endElement);
XML_SetCharacterDataHandler(parser, characterData); XML_SetCharacterDataHandler(parser, characterData);
// Compute the time taken to parse and build pages
const uint32_t chapterStartTime = millis();
do { do {
void* const buf = XML_GetBuffer(parser, 1024); void* const buf = XML_GetBuffer(parser, PARSE_BUFFER_SIZE);
if (!buf) { if (!buf) {
LOG_ERR("EHP", "Couldn't allocate memory for buffer"); LOG_ERR("EHP", "Couldn't allocate memory for buffer");
XML_StopParser(parser, XML_FALSE); // Stop any pending processing XML_StopParser(parser, XML_FALSE); // Stop any pending processing
@@ -650,7 +655,7 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() {
return false; return false;
} }
const size_t len = file.read(buf, 1024); const size_t len = file.read(buf, PARSE_BUFFER_SIZE);
if (len == 0 && file.available() > 0) { if (len == 0 && file.available() > 0) {
LOG_ERR("EHP", "File read error"); LOG_ERR("EHP", "File read error");
@@ -675,6 +680,7 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() {
return false; return false;
} }
} while (!done); } while (!done);
LOG_DBG("EHP", "Time to parse and build pages: %lu ms", millis() - chapterStartTime);
XML_StopParser(parser, XML_FALSE); // Stop any pending processing XML_StopParser(parser, XML_FALSE); // Stop any pending processing
XML_SetElementHandler(parser, nullptr, nullptr); // Clear callbacks XML_SetElementHandler(parser, nullptr, nullptr); // Clear callbacks