image tweaks

This commit is contained in:
cottongin 2026-01-27 07:11:18 -05:00
parent 5f4fa3bebe
commit 1a38fd96af
No known key found for this signature in database
GPG Key ID: 0ECC91FE4655C262
4 changed files with 68 additions and 16 deletions

View File

@ -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"

View File

@ -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);

View File

@ -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;
}

View File

@ -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 <image> 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;
}
}