From 099688a7efac5741591ea19096a7054d46ecf666 Mon Sep 17 00:00:00 2001 From: jpirnay Date: Tue, 12 May 2026 11:36:56 +0200 Subject: [PATCH] Fixes --- lib/EpdFont/FontDecompressor.cpp | 2 + lib/Epub/Epub/Section.cpp | 37 +++++++++++-------- lib/Epub/Epub/Section.h | 2 +- .../converters/JpegToFramebufferConverter.cpp | 3 +- lib/Epub/Epub/css/CssParser.cpp | 20 +++++++--- lib/GfxRenderer/GfxRenderer.cpp | 16 ++++++++ lib/hal/HalTiltSensor.cpp | 2 +- 7 files changed, 57 insertions(+), 25 deletions(-) diff --git a/lib/EpdFont/FontDecompressor.cpp b/lib/EpdFont/FontDecompressor.cpp index ee8215c2..3c05e77e 100644 --- a/lib/EpdFont/FontDecompressor.cpp +++ b/lib/EpdFont/FontDecompressor.cpp @@ -327,6 +327,8 @@ int FontDecompressor::prewarmCache(const EpdFontData* fontData, const char* utf8 int32_t outIdx = findGlyphIndex(fontData, fontData->ligaturePairs[li].ligatureCp); if (outIdx < 0) continue; + const EpdGlyph& outGlyph = fontData->glyph[outIdx]; + if (outGlyph.dataLength == 0 || outGlyph.width == 0 || outGlyph.height == 0) continue; bool found = false; for (uint16_t i = 0; i < glyphCount; i++) { diff --git a/lib/Epub/Epub/Section.cpp b/lib/Epub/Epub/Section.cpp index 3fe1fa8f..7c02f2ac 100644 --- a/lib/Epub/Epub/Section.cpp +++ b/lib/Epub/Epub/Section.cpp @@ -32,6 +32,13 @@ constexpr uint32_t HEADER_SIZE = sizeof(uint8_t) + // SECTION_FILE_VERSION sizeof(uint32_t) + // anchor map offset sizeof(uint32_t); // paragraph LUT offset +constexpr uint32_t HEADER_TAIL_PARSE_COMPLETE_OFFSET = + HEADER_SIZE - sizeof(uint32_t) * 3 - sizeof(uint16_t) - sizeof(bool); +constexpr uint32_t HEADER_TAIL_PAGE_COUNT_OFFSET = HEADER_SIZE - sizeof(uint32_t) * 3 - sizeof(uint16_t); +constexpr uint32_t HEADER_TAIL_PAGE_LUT_OFFSET = HEADER_SIZE - sizeof(uint32_t) * 3; +constexpr uint32_t HEADER_TAIL_ANCHOR_OFFSET = HEADER_SIZE - sizeof(uint32_t) * 2; +constexpr uint32_t HEADER_TAIL_PARAGRAPH_LUT_OFFSET = HEADER_SIZE - sizeof(uint32_t); + // On-disk paragraph LUT entry: u32 xhtmlByteOffset + u16 paragraphIndex + u16 listItemIndex. // listItemIndex is the running
  • count at page-break time; together with // paragraphIndex it lets KOReader-supplied

    - and

  • -anchored XPaths snap to @@ -255,7 +262,7 @@ bool Section::loadSectionFile(const int fontId, const float lineCompression, con if (Storage.openFileForRead("SCT", fallbackPath, file)) { filePath = fallbackPath; usingEmbeddedStyleFallback = true; - LOG_ERR("SCT", "Using no-CSS section cache fallback: %s", filePath.c_str()); + LOG_INF("SCT", "Using no-CSS section cache fallback: %s", filePath.c_str()); } else { return false; } @@ -372,7 +379,10 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c const uint8_t paragraphAlignment, const uint16_t viewportWidth, const uint16_t viewportHeight, const bool hyphenationEnabled, const bool embeddedStyle, const bool bionicReadingEnabled, const uint8_t imageRendering, - const std::function& progressFn) { + const std::function& progressFn, const bool skipEviction) { + if (!skipEviction) { + evictOldVariants(); + } if (embeddedStyle) { const uint32_t freeHeap = esp_get_free_heap_size(); const uint32_t contigHeap = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_DEFAULT); @@ -384,7 +394,7 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c static_cast(EMBEDDED_STYLE_MIN_CONTIG_HEAP_BYTES)); return createSectionFile(fontId, lineCompression, extraParagraphSpacing, paragraphAlignment, viewportWidth, viewportHeight, hyphenationEnabled, false, bionicReadingEnabled, imageRendering, - progressFn); + progressFn, true); } } @@ -410,9 +420,6 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c return false; } - // Evict old variants for this spine to keep cache size controlled BEFORE creating the new one - evictOldVariants(); - if (!Storage.openFileForWrite("SCT", filePath, file)) { return false; } @@ -494,8 +501,8 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c } else if (embeddedStyle) { LOG_ERR("SCT", "Parse failed with embedded CSS enabled; retrying section creation with embeddedStyle=0 " - "(stream=%d finalize=%d)", - streamOk ? 1 : 0, finalizeOk ? 1 : 0); + "(stream=%d finalize=%d parser=%d)", + streamOk ? 1 : 0, finalizeOk ? 1 : 0, parserStreamOk ? 1 : 0); file.close(); Storage.remove(filePath.c_str()); if (cssParser) { @@ -503,10 +510,10 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c } return createSectionFile(fontId, lineCompression, extraParagraphSpacing, paragraphAlignment, viewportWidth, viewportHeight, hyphenationEnabled, false, bionicReadingEnabled, imageRendering, - progressFn); + progressFn, true); } else { - LOG_ERR("SCT", "Failed to parse XML and build pages (stream=%d finalize=%d)", streamOk ? 1 : 0, - finalizeOk ? 1 : 0); + LOG_ERR("SCT", "Failed to parse XML and build pages (stream=%d finalize=%d parser=%d)", streamOk ? 1 : 0, + finalizeOk ? 1 : 0, parserStreamOk ? 1 : 0); file.close(); Storage.remove(filePath.c_str()); if (cssParser) { @@ -564,7 +571,7 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c } // Patch header with final parseComplete/pageCount and offsets. - file.seek(HEADER_SIZE - sizeof(uint32_t) * 3 - sizeof(pageCount) - sizeof(bool)); + file.seek(HEADER_TAIL_PARSE_COMPLETE_OFFSET); serialization::writePod(file, parseComplete); serialization::writePod(file, pageCount); serialization::writePod(file, lutOffset); @@ -707,7 +714,7 @@ void Section::buildTocBoundariesFromFile(FsFile& f) { // Single pass through on-disk anchors, matching against cached TOC anchors. // Stop early once all TOC anchors are resolved. // Header layout: ... | lutOffset (u32) | anchorMapOffset (u32) | paragraphLutOffset (u32) | - f.seek(HEADER_SIZE - sizeof(uint32_t) * 2); + f.seek(HEADER_TAIL_ANCHOR_OFFSET); uint32_t anchorMapOffset; serialization::readPod(f, anchorMapOffset); @@ -777,7 +784,7 @@ std::optional Section::getPageForAnchor(const std::string& anchor) con } const uint32_t fileSize = f.size(); - f.seek(HEADER_SIZE - sizeof(uint32_t) * 2); + f.seek(HEADER_TAIL_ANCHOR_OFFSET); uint32_t anchorMapOffset; serialization::readPod(f, anchorMapOffset); if (anchorMapOffset == 0 || anchorMapOffset >= fileSize) { @@ -810,7 +817,7 @@ bool Section::readParagraphLutHeader(FsFile& outFile, uint16_t& outCount, uint32 const uint32_t fileSize = outFile.size(); - outFile.seek(HEADER_SIZE - sizeof(uint32_t)); + outFile.seek(HEADER_TAIL_PARAGRAPH_LUT_OFFSET); uint32_t paragraphLutOffset; serialization::readPod(outFile, paragraphLutOffset); if (fileSize < sizeof(uint16_t) || paragraphLutOffset == 0 || paragraphLutOffset > fileSize - sizeof(uint16_t)) { diff --git a/lib/Epub/Epub/Section.h b/lib/Epub/Epub/Section.h index e2b840d1..08be6a35 100644 --- a/lib/Epub/Epub/Section.h +++ b/lib/Epub/Epub/Section.h @@ -67,7 +67,7 @@ class Section { bool createSectionFile(int fontId, float lineCompression, bool extraParagraphSpacing, uint8_t paragraphAlignment, uint16_t viewportWidth, uint16_t viewportHeight, bool hyphenationEnabled, bool embeddedStyle, bool bionicReadingEnabled, uint8_t imageRendering, - const std::function& progressFn = nullptr); + const std::function& progressFn = nullptr, bool skipEviction = false); std::unique_ptr loadPageFromSectionFile(); bool isTruncatedCache() const { return truncatedCache; } diff --git a/lib/Epub/Epub/converters/JpegToFramebufferConverter.cpp b/lib/Epub/Epub/converters/JpegToFramebufferConverter.cpp index a8c09c03..cbfe1735 100644 --- a/lib/Epub/Epub/converters/JpegToFramebufferConverter.cpp +++ b/lib/Epub/Epub/converters/JpegToFramebufferConverter.cpp @@ -210,8 +210,7 @@ bool shouldEnableJpegCache(const RenderConfig& config, int width, int height) { #if JPEG_ENABLE_FIRST_RENDER_NO_CACHE if (!Storage.exists(config.cachePath.c_str())) { - LOG_DBG("JPG", "Skipping cache on first render (compile-time policy): %s", config.cachePath.c_str()); - return false; + LOG_DBG("JPG", "No existing JPEG cache file on first render; enabling cache write: %s", config.cachePath.c_str()); } #endif diff --git a/lib/Epub/Epub/css/CssParser.cpp b/lib/Epub/Epub/css/CssParser.cpp index cf7909a7..b68f7618 100644 --- a/lib/Epub/Epub/css/CssParser.cpp +++ b/lib/Epub/Epub/css/CssParser.cpp @@ -70,6 +70,9 @@ constexpr size_t CSS_LENGTH_FIELD_COUNT = 11; constexpr size_t CSS_LENGTH_BYTES = sizeof(float) + sizeof(uint8_t); constexpr size_t CSS_FIXED_STYLE_BYTES = 4 * sizeof(uint8_t) + (CSS_LENGTH_FIELD_COUNT * CSS_LENGTH_BYTES) + sizeof(uint8_t) + sizeof(uint16_t); +static_assert(CSS_FIXED_STYLE_BYTES == 4 * sizeof(uint8_t) + (CSS_LENGTH_FIELD_COUNT * CSS_LENGTH_BYTES) + + sizeof(uint8_t) + sizeof(uint16_t), + "CSS_FIXED_STYLE_BYTES must match the compiled style payload layout"); // Cache file name (version is CssParser::CSS_CACHE_VERSION) constexpr char rulesCache[] = "/css_rules.cache"; @@ -490,6 +493,7 @@ void CssParser::processRuleBlockWithStyle(const std::string& selectorGroup, cons continue; } + compileTempFile_.flush(); CssStyle merged = style; auto existingOffsetIt = compileSelectorOffsets_.find(key); if (existingOffsetIt != compileSelectorOffsets_.end()) { @@ -499,6 +503,9 @@ void CssParser::processRuleBlockWithStyle(const std::string& selectorGroup, cons readCssStylePayload(tempRead, existing)) { existing.applyOver(merged); merged = existing; + } else { + LOG_ERR("CSS", "Failed to read compiled style for selector '%s' at offset %u", key.c_str(), + existingOffsetIt->second); } if (tempRead) { tempRead.close(); @@ -790,8 +797,13 @@ size_t CssParser::ruleCount() const { void CssParser::clear() { if (compileTempFile_) { + compileTempFile_.flush(); compileTempFile_.close(); } + if (!compileTempPath_.empty()) { + Storage.remove(compileTempPath_.c_str()); + compileTempPath_.clear(); + } rulesBySelector_.clear(); cacheRuleOffsets_.clear(); hotRuleCache_.clear(); @@ -1135,6 +1147,8 @@ CssStyle CssParser::resolveStyle(const std::string& tagName, const std::string& std::string classToken; std::string classKey; classKey.reserve(32); + std::string combinedKey; + combinedKey.reserve(tag.size() + 1 + 32); forEachNormalizedClassToken(classAttr, classToken, [&](const std::string& cls) { classKey.clear(); @@ -1148,13 +1162,7 @@ CssStyle CssParser::resolveStyle(const std::string& tagName, const std::string& } result.applyOver(classStyle); } - }); - // TODO: Support combinations of classes (e.g. style on p.class1.class2) - // 3. Apply element.class styles (higher priority) - std::string combinedKey; - combinedKey.reserve(tag.size() + 1 + 32); - forEachNormalizedClassToken(classAttr, classToken, [&](const std::string& cls) { combinedKey.clear(); combinedKey.append(tag); combinedKey.push_back('.'); diff --git a/lib/GfxRenderer/GfxRenderer.cpp b/lib/GfxRenderer/GfxRenderer.cpp index edb49ba9..194ad19c 100644 --- a/lib/GfxRenderer/GfxRenderer.cpp +++ b/lib/GfxRenderer/GfxRenderer.cpp @@ -2157,6 +2157,10 @@ bool GfxRenderer::storeBwBuffer() { return storeBwBufferRect(0, 0, getScreenWidt bool GfxRenderer::storeBwBufferRect(const int x, const int y, const int width, const int height) { if (width <= 0 || height <= 0) { + freeBwBufferChunks(); + bwSnapshotRowStart = 0; + bwSnapshotRowEnd = 0; + bwSnapshotSizeBytes = 0; LOG_ERR("GFX", "!! BW buffer store rect invalid: x=%d y=%d w=%d h=%d", x, y, width, height); return false; } @@ -2164,6 +2168,10 @@ bool GfxRenderer::storeBwBufferRect(const int x, const int y, const int width, c const int screenWidth = getScreenWidth(); const int screenHeight = getScreenHeight(); if (screenWidth <= 0 || screenHeight <= 0 || panelWidthBytes == 0 || panelHeight == 0 || !frameBuffer) { + freeBwBufferChunks(); + bwSnapshotRowStart = 0; + bwSnapshotRowEnd = 0; + bwSnapshotSizeBytes = 0; LOG_ERR("GFX", "!! BW buffer store unavailable (screen=%dx%d panelHeight=%u rowBytes=%u fb=%p)", screenWidth, screenHeight, panelHeight, panelWidthBytes, frameBuffer); return false; @@ -2174,6 +2182,10 @@ bool GfxRenderer::storeBwBufferRect(const int x, const int y, const int width, c const int clampedX1 = std::min(screenWidth - 1, x + width - 1); const int clampedY1 = std::min(screenHeight - 1, y + height - 1); if (clampedX0 > clampedX1 || clampedY0 > clampedY1) { + freeBwBufferChunks(); + bwSnapshotRowStart = 0; + bwSnapshotRowEnd = 0; + bwSnapshotSizeBytes = 0; LOG_ERR("GFX", "!! BW buffer store rect outside screen: x=%d y=%d w=%d h=%d", x, y, width, height); return false; } @@ -2202,6 +2214,10 @@ bool GfxRenderer::storeBwBufferRect(const int x, const int y, const int width, c rowStart = std::max(0, rowStart); rowEnd = std::min(static_cast(panelHeight) - 1, rowEnd); if (rowStart > rowEnd) { + freeBwBufferChunks(); + bwSnapshotRowStart = 0; + bwSnapshotRowEnd = 0; + bwSnapshotSizeBytes = 0; LOG_ERR("GFX", "!! BW buffer store row-band invalid after orientation mapping: rows=%d..%d", rowStart, rowEnd); return false; } diff --git a/lib/hal/HalTiltSensor.cpp b/lib/hal/HalTiltSensor.cpp index b744d27d..09214d2c 100644 --- a/lib/hal/HalTiltSensor.cpp +++ b/lib/hal/HalTiltSensor.cpp @@ -88,7 +88,7 @@ void HalTiltSensor::begin() { _i2cAddr = TILT_I2C_ADDR; if (!readReg(REG_WHO_AM_I, &whoami) || whoami != TILT_WHO_AM_I_VALUE) { _i2cAddr = TILT_I2C_ADDR_ALT; - if (!readReg(REG_WHO_AM_I, &whoami) || whoami != QMI8658_WHO_AM_I_VALUE) { + if (!readReg(REG_WHO_AM_I, &whoami) || whoami != TILT_WHO_AM_I_VALUE) { LOG_INF("TILT", "QMI8658 IMU not found"); _available = false; return;