diff --git a/lib/Epub/Epub/Section.cpp b/lib/Epub/Epub/Section.cpp index 6e5aadf9..bc04d475 100644 --- a/lib/Epub/Epub/Section.cpp +++ b/lib/Epub/Epub/Section.cpp @@ -10,10 +10,10 @@ #include "parsers/ChapterHtmlSlimParser.h" namespace { -constexpr uint8_t SECTION_FILE_VERSION = 16; +constexpr uint8_t SECTION_FILE_VERSION = 17; constexpr uint32_t HEADER_SIZE = sizeof(uint8_t) + sizeof(int) + sizeof(float) + sizeof(bool) + sizeof(uint8_t) + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(bool) + sizeof(bool) + - sizeof(uint32_t); + sizeof(uint8_t) + sizeof(uint32_t); } // namespace uint32_t Section::onPageComplete(std::unique_ptr page) { @@ -36,7 +36,7 @@ uint32_t Section::onPageComplete(std::unique_ptr page) { void Section::writeSectionFileHeader(const int fontId, const float lineCompression, const bool extraParagraphSpacing, const uint8_t paragraphAlignment, const uint16_t viewportWidth, const uint16_t viewportHeight, const bool hyphenationEnabled, - const bool embeddedStyle) { + const bool embeddedStyle, const uint8_t imageRendering) { if (!file) { LOG_DBG("SCT", "File not open for writing header"); return; @@ -44,7 +44,7 @@ void Section::writeSectionFileHeader(const int fontId, const float lineCompressi static_assert(HEADER_SIZE == sizeof(SECTION_FILE_VERSION) + sizeof(fontId) + sizeof(lineCompression) + sizeof(extraParagraphSpacing) + sizeof(paragraphAlignment) + sizeof(viewportWidth) + sizeof(viewportHeight) + sizeof(pageCount) + sizeof(hyphenationEnabled) + - sizeof(embeddedStyle) + sizeof(uint32_t), + sizeof(embeddedStyle) + sizeof(imageRendering) + sizeof(uint32_t), "Header size mismatch"); serialization::writePod(file, SECTION_FILE_VERSION); serialization::writePod(file, fontId); @@ -55,13 +55,15 @@ void Section::writeSectionFileHeader(const int fontId, const float lineCompressi serialization::writePod(file, viewportHeight); serialization::writePod(file, hyphenationEnabled); serialization::writePod(file, embeddedStyle); + serialization::writePod(file, imageRendering); serialization::writePod(file, pageCount); // Placeholder for page count (will be initially 0 when written) serialization::writePod(file, static_cast(0)); // Placeholder for LUT offset } bool Section::loadSectionFile(const int fontId, const float lineCompression, const bool extraParagraphSpacing, const uint8_t paragraphAlignment, const uint16_t viewportWidth, - const uint16_t viewportHeight, const bool hyphenationEnabled, const bool embeddedStyle) { + const uint16_t viewportHeight, const bool hyphenationEnabled, const bool embeddedStyle, + const uint8_t imageRendering) { if (!Storage.openFileForRead("SCT", filePath, file)) { return false; } @@ -84,6 +86,7 @@ bool Section::loadSectionFile(const int fontId, const float lineCompression, con uint8_t fileParagraphAlignment; bool fileHyphenationEnabled; bool fileEmbeddedStyle; + uint8_t fileImageRendering; serialization::readPod(file, fileFontId); serialization::readPod(file, fileLineCompression); serialization::readPod(file, fileExtraParagraphSpacing); @@ -92,11 +95,13 @@ bool Section::loadSectionFile(const int fontId, const float lineCompression, con serialization::readPod(file, fileViewportHeight); serialization::readPod(file, fileHyphenationEnabled); serialization::readPod(file, fileEmbeddedStyle); + serialization::readPod(file, fileImageRendering); if (fontId != fileFontId || lineCompression != fileLineCompression || extraParagraphSpacing != fileExtraParagraphSpacing || paragraphAlignment != fileParagraphAlignment || viewportWidth != fileViewportWidth || viewportHeight != fileViewportHeight || - hyphenationEnabled != fileHyphenationEnabled || embeddedStyle != fileEmbeddedStyle) { + hyphenationEnabled != fileHyphenationEnabled || embeddedStyle != fileEmbeddedStyle || + imageRendering != fileImageRendering) { file.close(); LOG_ERR("SCT", "Deserialization failed: Parameters do not match"); clearCache(); @@ -129,7 +134,7 @@ bool Section::clearCache() const { bool Section::createSectionFile(const int fontId, const float lineCompression, const bool extraParagraphSpacing, const uint8_t paragraphAlignment, const uint16_t viewportWidth, const uint16_t viewportHeight, const bool hyphenationEnabled, const bool embeddedStyle, - const std::function& popupFn) { + const uint8_t imageRendering, const std::function& popupFn) { const auto localPath = epub->getSpineItem(spineIndex).href; const auto tmpHtmlPath = epub->getCachePath() + "/.tmp_" + std::to_string(spineIndex) + ".html"; @@ -179,7 +184,7 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c return false; } writeSectionFileHeader(fontId, lineCompression, extraParagraphSpacing, paragraphAlignment, viewportWidth, - viewportHeight, hyphenationEnabled, embeddedStyle); + viewportHeight, hyphenationEnabled, embeddedStyle, imageRendering); std::vector lut = {}; // Derive the content base directory and image cache path prefix for the parser @@ -201,7 +206,7 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c epub, tmpHtmlPath, renderer, fontId, lineCompression, extraParagraphSpacing, paragraphAlignment, viewportWidth, viewportHeight, hyphenationEnabled, [this, &lut](std::unique_ptr page) { lut.emplace_back(this->onPageComplete(std::move(page))); }, - embeddedStyle, contentBase, imageBasePath, popupFn, cssParser); + embeddedStyle, contentBase, imageBasePath, imageRendering, popupFn, cssParser); Hyphenator::setPreferredLanguage(epub->getLanguage()); success = visitor.parseAndBuildPages(); diff --git a/lib/Epub/Epub/Section.h b/lib/Epub/Epub/Section.h index 42a6d993..70fd0fe5 100644 --- a/lib/Epub/Epub/Section.h +++ b/lib/Epub/Epub/Section.h @@ -16,7 +16,7 @@ class Section { void writeSectionFileHeader(int fontId, float lineCompression, bool extraParagraphSpacing, uint8_t paragraphAlignment, uint16_t viewportWidth, uint16_t viewportHeight, bool hyphenationEnabled, - bool embeddedStyle); + bool embeddedStyle, uint8_t imageRendering); uint32_t onPageComplete(std::unique_ptr page); public: @@ -30,10 +30,11 @@ class Section { filePath(epub->getCachePath() + "/sections/" + std::to_string(spineIndex) + ".bin") {} ~Section() = default; bool loadSectionFile(int fontId, float lineCompression, bool extraParagraphSpacing, uint8_t paragraphAlignment, - uint16_t viewportWidth, uint16_t viewportHeight, bool hyphenationEnabled, bool embeddedStyle); + uint16_t viewportWidth, uint16_t viewportHeight, bool hyphenationEnabled, bool embeddedStyle, + uint8_t imageRendering); bool clearCache() const; bool createSectionFile(int fontId, float lineCompression, bool extraParagraphSpacing, uint8_t paragraphAlignment, uint16_t viewportWidth, uint16_t viewportHeight, bool hyphenationEnabled, bool embeddedStyle, - const std::function& popupFn = nullptr); + uint8_t imageRendering, const std::function& popupFn = nullptr); std::unique_ptr loadPageFromSectionFile(); }; diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp index 2753f928..6a0636bb 100644 --- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp +++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp @@ -243,7 +243,14 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* } } - if (!src.empty()) { + // imageRendering: 0=display, 1=placeholder (alt text only), 2=suppress entirely + if (self->imageRendering == 2) { + self->skipUntilDepth = self->depth; + self->depth += 1; + return; + } + + if (!src.empty() && self->imageRendering != 1) { LOG_DBG("EHP", "Found image: src=%s", src.c_str()); { diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h index 663ce860..ce530797 100644 --- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h +++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h @@ -48,6 +48,7 @@ class ChapterHtmlSlimParser { bool hyphenationEnabled; const CssParser* cssParser; bool embeddedStyle; + uint8_t imageRendering; std::string contentBase; std::string imageBasePath; int imageCounter = 0; @@ -94,8 +95,8 @@ class ChapterHtmlSlimParser { const uint16_t viewportHeight, const bool hyphenationEnabled, const std::function)>& completePageFn, const bool embeddedStyle, const std::string& contentBase, - const std::string& imageBasePath, const std::function& popupFn = nullptr, - const CssParser* cssParser = nullptr) + const std::string& imageBasePath, const uint8_t imageRendering = 0, + const std::function& popupFn = nullptr, const CssParser* cssParser = nullptr) : epub(epub), filepath(filepath), @@ -111,6 +112,7 @@ class ChapterHtmlSlimParser { popupFn(popupFn), cssParser(cssParser), embeddedStyle(embeddedStyle), + imageRendering(imageRendering), contentBase(contentBase), imageBasePath(imageBasePath) {} diff --git a/lib/I18n/translations/english.yaml b/lib/I18n/translations/english.yaml index f5b80b8e..38192cc9 100644 --- a/lib/I18n/translations/english.yaml +++ b/lib/I18n/translations/english.yaml @@ -92,6 +92,10 @@ STR_STATUS_BAR: "Status Bar" STR_HIDE_BATTERY: "Hide Battery %" STR_EXTRA_SPACING: "Extra Paragraph Spacing" STR_TEXT_AA: "Text Anti-Aliasing" +STR_IMAGES: "Images" +STR_IMAGES_DISPLAY: "Display" +STR_IMAGES_PLACEHOLDER: "Placeholder" +STR_IMAGES_SUPPRESS: "Suppress" STR_SHORT_PWR_BTN: "Short Power Button Click" STR_ORIENTATION: "Reading Orientation" STR_FRONT_BTN_LAYOUT: "Front Button Layout" diff --git a/src/CrossPointSettings.h b/src/CrossPointSettings.h index 5ba7bde2..9a0e298b 100644 --- a/src/CrossPointSettings.h +++ b/src/CrossPointSettings.h @@ -134,6 +134,9 @@ class CrossPointSettings { // UI Theme enum UI_THEME { CLASSIC = 0, LYRA = 1, LYRA_3_COVERS = 2 }; + // Image rendering in EPUB reader + enum IMAGE_RENDERING { IMAGES_DISPLAY = 0, IMAGES_PLACEHOLDER = 1, IMAGES_SUPPRESS = 2, IMAGE_RENDERING_COUNT }; + // Sleep screen settings uint8_t sleepScreen = DARK; // Sleep screen cover mode settings @@ -192,6 +195,10 @@ class CrossPointSettings { uint8_t fadingFix = 0; // Use book's embedded CSS styles for EPUB rendering (1 = enabled, 0 = disabled) uint8_t embeddedStyle = 1; + // Show hidden files/directories (starting with '.') in the file browser (0 = hidden, 1 = show) + uint8_t showHiddenFiles = 0; + // Image rendering mode in EPUB reader + uint8_t imageRendering = IMAGES_DISPLAY; ~CrossPointSettings() = default; diff --git a/src/SettingsList.h b/src/SettingsList.h index c10d58c6..f69c9e93 100644 --- a/src/SettingsList.h +++ b/src/SettingsList.h @@ -62,6 +62,9 @@ inline const std::vector& getSettingsList() { StrId::STR_CAT_READER), SettingInfo::Toggle(StrId::STR_TEXT_AA, &CrossPointSettings::textAntiAliasing, "textAntiAliasing", StrId::STR_CAT_READER), + SettingInfo::Enum(StrId::STR_IMAGES, &CrossPointSettings::imageRendering, + {StrId::STR_IMAGES_DISPLAY, StrId::STR_IMAGES_PLACEHOLDER, StrId::STR_IMAGES_SUPPRESS}, + "imageRendering", StrId::STR_CAT_READER), // --- Controls --- SettingInfo::Enum(StrId::STR_SIDE_BTN_LAYOUT, &CrossPointSettings::sideButtonLayout, {StrId::STR_PREV_NEXT, StrId::STR_NEXT_PREV}, "sideButtonLayout", StrId::STR_CAT_CONTROLS), diff --git a/src/activities/reader/EpubReaderActivity.cpp b/src/activities/reader/EpubReaderActivity.cpp index d889df5f..059d3ea1 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -571,14 +571,16 @@ void EpubReaderActivity::render(RenderLock&& lock) { if (!section->loadSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(), SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth, - viewportHeight, SETTINGS.hyphenationEnabled, SETTINGS.embeddedStyle)) { + viewportHeight, SETTINGS.hyphenationEnabled, SETTINGS.embeddedStyle, + SETTINGS.imageRendering)) { LOG_DBG("ERS", "Cache not found, building..."); const auto popupFn = [this]() { GUI.drawPopup(renderer, tr(STR_INDEXING)); }; if (!section->createSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(), SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth, - viewportHeight, SETTINGS.hyphenationEnabled, SETTINGS.embeddedStyle, popupFn)) { + viewportHeight, SETTINGS.hyphenationEnabled, SETTINGS.embeddedStyle, + SETTINGS.imageRendering, popupFn)) { LOG_ERR("ERS", "Failed to persist page data to SD"); section.reset(); return;