#pragma once #include #include #include #include #include "CssStyle.h" /** * Lightweight CSS parser for EPUB stylesheets * * Parses CSS files and extracts styling information relevant for e-ink display. * Uses a two-phase approach: first tokenizes the CSS content, then builds * a rule database that can be queried during HTML parsing. * * Supported selectors: * - Element selectors: p, div, h1, etc. * - Class selectors: .classname * - Combined: element.classname * - Grouped: selector1, selector2 { } * * Not supported (silently ignored): * - Descendant/child selectors * - Pseudo-classes and pseudo-elements * - Media queries (content is skipped) * - @import, @font-face, etc. */ class CssParser { public: CssParser() = default; ~CssParser() = default; // Non-copyable CssParser(const CssParser&) = delete; CssParser& operator=(const CssParser&) = delete; /** * Load and parse CSS from a file stream. * Can be called multiple times to accumulate rules from multiple stylesheets. * @param source Open file handle to read from * @return true if parsing completed (even if no rules found) */ bool loadFromStream(FsFile& source); /** * Look up the style for an HTML element, considering tag name and class attributes. * Applies CSS cascade: element style < class style < element.class style * * @param tagName The HTML element name (e.g., "p", "div") * @param classAttr The class attribute value (may contain multiple space-separated classes) * @return Combined style with all applicable rules merged */ [[nodiscard]] CssStyle resolveStyle(const std::string& tagName, const std::string& classAttr) const; /** * Parse an inline style attribute string. * @param styleValue The value of a style="" attribute * @return Parsed style properties */ [[nodiscard]] static CssStyle parseInlineStyle(const std::string& styleValue); /** * Check if any rules have been loaded */ [[nodiscard]] bool empty() const { return rulesBySelector_.empty(); } /** * Get count of loaded rule sets */ [[nodiscard]] size_t ruleCount() const { return rulesBySelector_.size(); } /** * Clear all loaded rules */ void clear() { rulesBySelector_.clear(); } /** * Save parsed CSS rules to a cache file. * @param file Open file handle to write to * @return true if cache was written successfully */ bool saveToCache(FsFile& file) const; /** * Load CSS rules from a cache file. * Clears any existing rules before loading. * @param file Open file handle to read from * @return true if cache was loaded successfully */ bool loadFromCache(FsFile& file); private: // Storage: maps normalized selector -> style properties std::unordered_map rulesBySelector_; // Internal parsing helpers void processRuleBlock(const std::string& selectorGroup, const std::string& declarations); static CssStyle parseDeclarations(const std::string& declBlock); // Individual property value parsers static CssTextAlign interpretAlignment(const std::string& val); static CssFontStyle interpretFontStyle(const std::string& val); static CssFontWeight interpretFontWeight(const std::string& val); static CssTextDecoration interpretDecoration(const std::string& val); static CssLength interpretLength(const std::string& val); static int8_t interpretSpacing(const std::string& val); // String utilities static std::string normalized(const std::string& s); static std::vector splitOnChar(const std::string& s, char delimiter); static std::vector splitWhitespace(const std::string& s); };