diff --git a/lib/Epub/Epub/css/CssParser.cpp b/lib/Epub/Epub/css/CssParser.cpp index 8ad59148..d89d8713 100644 --- a/lib/Epub/Epub/css/CssParser.cpp +++ b/lib/Epub/Epub/css/CssParser.cpp @@ -317,6 +317,9 @@ void CssParser::parseDeclarationIntoStyle(const std::string& decl, CssStyle& sty style.imageWidth = len; style.defined.imageWidth = 1; } + } else if (propNameBuf == "display") { + style.display = (propValueBuf == "none") ? CssDisplay::None : CssDisplay::Block; + style.defined.display = 1; } } @@ -692,6 +695,7 @@ bool CssParser::saveToCache() const { writeLength(style.paddingRight); writeLength(style.imageHeight); writeLength(style.imageWidth); + file.write(static_cast(style.display)); // Write defined flags as uint16_t uint16_t definedBits = 0; @@ -710,6 +714,7 @@ bool CssParser::saveToCache() const { if (style.defined.paddingRight) definedBits |= 1 << 12; if (style.defined.imageHeight) definedBits |= 1 << 13; if (style.defined.imageWidth) definedBits |= 1 << 14; + if (style.defined.display) definedBits |= 1 << 15; file.write(reinterpret_cast(&definedBits), sizeof(definedBits)); } @@ -820,6 +825,15 @@ bool CssParser::loadFromCache() { return false; } + // Read display value + uint8_t displayVal; + if (file.read(&displayVal, 1) != 1) { + rulesBySelector_.clear(); + file.close(); + return false; + } + style.display = static_cast(displayVal); + // Read defined flags uint16_t definedBits = 0; if (file.read(&definedBits, sizeof(definedBits)) != sizeof(definedBits)) { @@ -842,6 +856,7 @@ bool CssParser::loadFromCache() { style.defined.paddingRight = (definedBits & 1 << 12) != 0; style.defined.imageHeight = (definedBits & 1 << 13) != 0; style.defined.imageWidth = (definedBits & 1 << 14) != 0; + style.defined.display = (definedBits & 1 << 15) != 0; rulesBySelector_[selector] = style; } diff --git a/lib/Epub/Epub/css/CssParser.h b/lib/Epub/Epub/css/CssParser.h index 74dfaef1..69bc3ec2 100644 --- a/lib/Epub/Epub/css/CssParser.h +++ b/lib/Epub/Epub/css/CssParser.h @@ -31,7 +31,7 @@ class CssParser { public: // Bump when CSS cache format or rules change; section caches are invalidated when this changes - static constexpr uint8_t CSS_CACHE_VERSION = 3; + static constexpr uint8_t CSS_CACHE_VERSION = 4; explicit CssParser(std::string cachePath) : cachePath(std::move(cachePath)) {} ~CssParser() = default; diff --git a/lib/Epub/Epub/css/CssStyle.h b/lib/Epub/Epub/css/CssStyle.h index bac858e0..7b129eaf 100644 --- a/lib/Epub/Epub/css/CssStyle.h +++ b/lib/Epub/Epub/css/CssStyle.h @@ -54,6 +54,9 @@ enum class CssFontWeight : uint8_t { Normal = 0, Bold = 1 }; // Text decoration options enum class CssTextDecoration : uint8_t { None = 0, Underline = 1 }; +// Display options - only None and Block are relevant for e-ink rendering +enum class CssDisplay : uint8_t { Block = 0, None = 1 }; + // Bitmask for tracking which properties have been explicitly set struct CssPropertyFlags { uint16_t textAlign : 1; @@ -71,6 +74,7 @@ struct CssPropertyFlags { uint16_t paddingRight : 1; uint16_t imageHeight : 1; uint16_t imageWidth : 1; + uint16_t display : 1; CssPropertyFlags() : textAlign(0), @@ -87,19 +91,20 @@ struct CssPropertyFlags { paddingLeft(0), paddingRight(0), imageHeight(0), - imageWidth(0) {} + imageWidth(0), + display(0) {} [[nodiscard]] bool anySet() const { return textAlign || fontStyle || fontWeight || textDecoration || textIndent || marginTop || marginBottom || marginLeft || marginRight || paddingTop || paddingBottom || paddingLeft || paddingRight || imageHeight || - imageWidth; + imageWidth || display; } void clearAll() { textAlign = fontStyle = fontWeight = textDecoration = textIndent = 0; marginTop = marginBottom = marginLeft = marginRight = 0; paddingTop = paddingBottom = paddingLeft = paddingRight = 0; - imageHeight = imageWidth = 0; + imageHeight = imageWidth = display = 0; } }; @@ -123,6 +128,7 @@ struct CssStyle { CssLength paddingRight; // Padding right CssLength imageHeight; // Height for img (e.g. 2em) – width derived from aspect ratio when only height set CssLength imageWidth; // Width for img when both or only width set + CssDisplay display = CssDisplay::Block; // display property (Block or None) CssPropertyFlags defined; // Tracks which properties were explicitly set @@ -189,6 +195,10 @@ struct CssStyle { imageWidth = base.imageWidth; defined.imageWidth = 1; } + if (base.hasDisplay()) { + display = base.display; + defined.display = 1; + } } [[nodiscard]] bool hasTextAlign() const { return defined.textAlign; } @@ -206,6 +216,7 @@ struct CssStyle { [[nodiscard]] bool hasPaddingRight() const { return defined.paddingRight; } [[nodiscard]] bool hasImageHeight() const { return defined.imageHeight; } [[nodiscard]] bool hasImageWidth() const { return defined.imageWidth; } + [[nodiscard]] bool hasDisplay() const { return defined.display; } void reset() { textAlign = CssTextAlign::Left; @@ -216,6 +227,7 @@ struct CssStyle { marginTop = marginBottom = marginLeft = marginRight = CssLength{}; paddingTop = paddingBottom = paddingLeft = paddingRight = CssLength{}; imageHeight = imageWidth = CssLength{}; + display = CssDisplay::Block; defined.clearAll(); } }; diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp index 8e014c07..ad964593 100644 --- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp +++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp @@ -264,6 +264,19 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* return; } + // Skip image if CSS display:none + if (self->cssParser) { + CssStyle imgDisplayStyle = self->cssParser->resolveStyle("img", classAttr); + if (!styleAttr.empty()) { + imgDisplayStyle.applyOver(CssParser::parseInlineStyle(styleAttr)); + } + if (imgDisplayStyle.hasDisplay() && imgDisplayStyle.display == CssDisplay::None) { + self->skipUntilDepth = self->depth; + self->depth += 1; + return; + } + } + if (!src.empty() && self->imageRendering != 1) { LOG_DBG("EHP", "Found image: src=%s", src.c_str()); @@ -526,6 +539,13 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* } } + // Skip elements with display:none + if (cssStyle.hasDisplay() && cssStyle.display == CssDisplay::None) { + self->skipUntilDepth = self->depth; + self->depth += 1; + return; + } + const float emSize = static_cast(self->renderer.getFontAscenderSize(self->fontId)); const auto userAlignmentBlockStyle = BlockStyle::fromCssStyle( cssStyle, emSize, static_cast(self->paragraphAlignment), self->viewportWidth);