diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 7c50a30..aa66d18 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -3,11 +3,40 @@ This document show most common issues and possible solutions while using the device features. - [Troubleshooting](#troubleshooting) + - [Images Not Displaying in EPUBs](#images-not-displaying-in-epubs) - [Cannot See the Device on the Network](#cannot-see-the-device-on-the-network) - [Connection Drops or Times Out](#connection-drops-or-times-out) - [Upload Fails](#upload-fails) - [Saved Password Not Working](#saved-password-not-working) +### Images Not Displaying in EPUBs + +**Problem:** Some images in EPUB books show as placeholders like "[Image: filename.jpg]" instead of the actual image + +**Possible Causes:** + +1. **Progressive JPEGs are not supported** + - The device uses a minimal JPEG decoder optimized for embedded systems + - Progressive/multi-scan JPEGs cannot be decoded due to memory constraints + - This affects some professionally published EPUBs, especially maps and high-quality photos + - **Workaround:** Use Calibre or another EPUB editor to convert progressive JPEGs to baseline JPEGs + +2. **Unsupported image format** + - Only JPEG and PNG images are supported + - Other formats (GIF, WebP, SVG graphics) will show placeholders + +3. **Image extraction failed** + - The image file may be corrupted or the EPUB structure malformed + - Try re-downloading the EPUB or converting it with Calibre + +**How to check if an image is progressive JPEG:** + +```python +from PIL import Image +print(Image.open("image.jpg").info.get('progressive', 0)) +# Output: 1 = progressive (not supported), 0 = baseline (supported) +``` + ### Cannot See the Device on the Network **Problem:** Browser shows "Cannot connect" or "Site can't be reached" diff --git a/lib/Epub/Epub/converters/ImageToFramebufferDecoder.h b/lib/Epub/Epub/converters/ImageToFramebufferDecoder.h index 92dc474..6750873 100644 --- a/lib/Epub/Epub/converters/ImageToFramebufferDecoder.h +++ b/lib/Epub/Epub/converters/ImageToFramebufferDecoder.h @@ -33,8 +33,10 @@ class ImageToFramebufferDecoder { protected: // Size validation helpers - static constexpr int MAX_SOURCE_WIDTH = 2048; - static constexpr int MAX_SOURCE_HEIGHT = 1536; + // These limits are generous since picojpeg decodes MCU-by-MCU (no full image buffer needed) + // Memory usage depends on OUTPUT size, not source size + static constexpr int MAX_SOURCE_WIDTH = 3072; + static constexpr int MAX_SOURCE_HEIGHT = 3072; bool validateImageDimensions(int width, int height, const std::string& format); void warnUnsupportedFeature(const std::string& feature, const std::string& imagePath); diff --git a/lib/Epub/Epub/converters/JpegToFramebufferConverter.cpp b/lib/Epub/Epub/converters/JpegToFramebufferConverter.cpp index b46cb7f..02e83fe 100644 --- a/lib/Epub/Epub/converters/JpegToFramebufferConverter.cpp +++ b/lib/Epub/Epub/converters/JpegToFramebufferConverter.cpp @@ -99,7 +99,16 @@ bool JpegToFramebufferConverter::getDimensionsStatic(const std::string& imagePat file.close(); if (status != 0) { - Serial.printf("[%lu] [JPG] Failed to init JPEG for dimensions: %d\n", millis(), status); + // Provide more informative error messages for common JPEG issues + // Error codes from picojpeg.h: + // 37 = PJPG_NOT_SINGLE_SCAN (multi-scan/progressive JPEG) + // 49 = PJPG_UNSUPPORTED_MODE (progressive JPEG) + if (status == 37 || status == 49) { + Serial.printf("[%lu] [JPG] Progressive/multi-scan JPEG not supported (error %d): %s\n", millis(), status, + imagePath.c_str()); + } else { + Serial.printf("[%lu] [JPG] Failed to init JPEG (error %d): %s\n", millis(), status, imagePath.c_str()); + } return false; } @@ -160,7 +169,12 @@ bool JpegToFramebufferConverter::decodeToFramebuffer(const std::string& imagePat int status = pjpeg_decode_init(&imageInfo, jpegReadCallback, &context, 0); if (status != 0) { - Serial.printf("[%lu] [JPG] picojpeg init failed: %d\n", millis(), status); + // Error 37 = PJPG_NOT_SINGLE_SCAN, 49 = PJPG_UNSUPPORTED_MODE (progressive JPEG) + if (status == 37 || status == 49) { + Serial.printf("[%lu] [JPG] Progressive/multi-scan JPEG not supported (error %d)\n", millis(), status); + } else { + Serial.printf("[%lu] [JPG] picojpeg init failed (error %d)\n", millis(), status); + } file.close(); return false; } diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp index 9b4bb83..fdc059c 100644 --- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp +++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp @@ -28,7 +28,8 @@ constexpr int NUM_ITALIC_TAGS = sizeof(ITALIC_TAGS) / sizeof(ITALIC_TAGS[0]); const char* UNDERLINE_TAGS[] = {"u", "ins"}; constexpr int NUM_UNDERLINE_TAGS = sizeof(UNDERLINE_TAGS) / sizeof(UNDERLINE_TAGS[0]); -const char* IMAGE_TAGS[] = {"img"}; +// Include "image" for SVG elements (common in Calibre-generated covers) +const char* IMAGE_TAGS[] = {"img", "image"}; constexpr int NUM_IMAGE_TAGS = sizeof(IMAGE_TAGS) / sizeof(IMAGE_TAGS[0]); const char* SKIP_TAGS[] = {"head"}; @@ -137,7 +138,8 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* std::string alt; if (atts != nullptr) { for (int i = 0; atts[i]; i += 2) { - if (strcmp(atts[i], "src") == 0) { + // Standard HTML img uses "src", SVG image uses "xlink:href" or "href" + if (strcmp(atts[i], "src") == 0 || strcmp(atts[i], "xlink:href") == 0 || strcmp(atts[i], "href") == 0) { src = atts[i + 1]; } else if (strcmp(atts[i], "alt") == 0) { alt = atts[i + 1]; @@ -255,19 +257,24 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* } } - // Fallback to alt text if image processing fails + // Fallback: show placeholder text when image processing fails + // This handles progressive JPEGs, unsupported formats, memory issues, etc. + std::string placeholder; if (!alt.empty()) { - alt = "[Image: " + alt + "]"; - self->startNewTextBlock(TextBlock::CENTER_ALIGN); - self->italicUntilDepth = std::min(self->italicUntilDepth, self->depth); - self->depth += 1; - self->characterData(userData, alt.c_str(), alt.length()); - return; + placeholder = "[Image: " + alt + "]"; + } else if (!src.empty()) { + // Extract filename from path for a more informative placeholder + size_t lastSlash = src.find_last_of('/'); + std::string filename = (lastSlash != std::string::npos) ? src.substr(lastSlash + 1) : src; + placeholder = "[Image: " + filename + "]"; + } else { + placeholder = "[Image unavailable]"; } - - // No alt text, skip - self->skipUntilDepth = self->depth; + + self->startNewTextBlock(TextBlock::CENTER_ALIGN); + self->italicUntilDepth = std::min(self->italicUntilDepth, self->depth); self->depth += 1; + self->characterData(userData, placeholder.c_str(), placeholder.length()); return; } }