cottongin c2a966a6ea
refactor: Memory optimization and XTC format removal
Memory optimization:
- Add LOG_STACK_WATERMARK macro for task stack monitoring
- Add freeCoverBufferIfAllocated() and preloadCoverBuffer() for memory management
- Improve cover buffer reuse to reduce heap fragmentation
- Add grayscale buffer cleanup safety in GfxRenderer
- Make grayscale rendering conditional on successful buffer allocation
- Add detailed heap fragmentation logging with ESP-IDF API
- Add CSS parser memory usage estimation

XTC format removal:
- Remove entire lib/Xtc library (XTC parser and types)
- Remove XtcReaderActivity and XtcReaderChapterSelectionActivity
- Remove XTC file handling from HomeActivity, SleepActivity, ReaderActivity
- Remove .xtc/.xtch from supported extensions in BookManager
- Remove XTC cache prefix from Md5Utils
- Update web server and file browser to exclude XTC format
- Clear in-memory caches when disk cache is cleared
2026-01-27 20:35:32 -05:00

119 lines
3.9 KiB
C++

#pragma once
#include <SdFat.h>
#include <string>
#include <unordered_map>
#include <vector>
#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(); }
/**
* Estimate memory usage of loaded rules (for debugging)
* Returns approximate bytes used by selector strings and style data
*/
[[nodiscard]] size_t estimateMemoryUsage() const {
size_t bytes = 0;
// unordered_map overhead: roughly 8 bytes per bucket + per-entry overhead
bytes += rulesBySelector_.bucket_count() * sizeof(void*);
for (const auto& entry : rulesBySelector_) {
// String storage: capacity + SSO overhead (~24 bytes) + actual chars
bytes += sizeof(std::string) + entry.first.capacity();
// CssStyle is ~16 bytes
bytes += sizeof(CssStyle);
// Per-entry node overhead in unordered_map (~24-32 bytes)
bytes += 32;
}
return bytes;
}
/**
* Clear all loaded rules
*/
void clear() { rulesBySelector_.clear(); }
private:
// Storage: maps normalized selector -> style properties
std::unordered_map<std::string, CssStyle> 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 TextAlign 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 float interpretLength(const std::string& val, float emSize = 16.0f);
static int8_t interpretSpacing(const std::string& val);
// String utilities
static std::string normalized(const std::string& s);
static std::vector<std::string> splitOnChar(const std::string& s, char delimiter);
static std::vector<std::string> splitWhitespace(const std::string& s);
};