diff --git a/lib/Epub/Epub.cpp b/lib/Epub/Epub.cpp index 861764a5..2e1cea0d 100644 --- a/lib/Epub/Epub.cpp +++ b/lib/Epub/Epub.cpp @@ -689,9 +689,9 @@ int Epub::addVirtualSpineItem(const std::string& path) { return newIndex; } -bool Epub::isVirtualSpineItem(int spineIndex) const { - int currentSpineSize = bookMetadataCache ? bookMetadataCache->getSpineCount() : 0; - return spineIndex >= currentSpineSize; +bool Epub::isVirtualSpineItem(int spineIndex) const { + int currentSpineSize = bookMetadataCache ? bookMetadataCache->getSpineCount() : 0; + return spineIndex >= currentSpineSize; } int Epub::findVirtualSpineIndex(const std::string& filename) const { diff --git a/lib/Epub/Epub/Page.cpp b/lib/Epub/Epub/Page.cpp index 3bb809db..7632e1bb 100644 --- a/lib/Epub/Epub/Page.cpp +++ b/lib/Epub/Epub/Page.cpp @@ -53,7 +53,6 @@ bool Page::serialize(FsFile& file) const { file.write(&isInlineFlag, 1); } - return true; } @@ -78,7 +77,7 @@ std::unique_ptr Page::deserialize(FsFile& file) { int32_t footnoteCount; serialization::readPod(file, footnoteCount); - + for (int i = 0; i < footnoteCount; i++) { FootnoteEntry entry; file.read(entry.number, 3); diff --git a/lib/Epub/Epub/Page.h b/lib/Epub/Epub/Page.h index d8929cc8..291ef1d6 100644 --- a/lib/Epub/Epub/Page.h +++ b/lib/Epub/Epub/Page.h @@ -1,7 +1,7 @@ #include -#include #include +#include #include "FootnoteEntry.h" #include "blocks/TextBlock.h" @@ -44,7 +44,7 @@ class Page { entry.number[2] = '\0'; strncpy(entry.href, href, 63); entry.href[63] = '\0'; - entry.isInline = false; // Default + entry.isInline = false; // Default footnotes.push_back(entry); } diff --git a/lib/Epub/Epub/Section.cpp b/lib/Epub/Epub/Section.cpp index b241f671..5409b826 100644 --- a/lib/Epub/Epub/Section.cpp +++ b/lib/Epub/Epub/Section.cpp @@ -163,8 +163,6 @@ bool Section::loadSectionFile(const int fontId, const float lineCompression, con return true; } - - bool Section::clearCache() const { if (!SdMan.exists(filePath.c_str())) { Serial.printf("[%lu] [SCT] Cache does not exist, no action needed\n", millis()); @@ -203,33 +201,33 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c std::string fileToParse = tmpHtmlPath; if (isVirtual) { - Serial.printf("[%lu] [SCT] Processing virtual spine item: %s\n", millis(), localPath.c_str()); - // For virtual items, the path is already on SD, e.g. /sd/cache/... - // But we need to make sure the parser can read it. - // If it starts with /sd/, we might need to strip it if using SdFat with root? - // Assuming absolute path is fine. - fileToParse = localPath; - success = true; - fileSize = 0; // Don't check size for progress bar on virtual items + Serial.printf("[%lu] [SCT] Processing virtual spine item: %s\n", millis(), localPath.c_str()); + // For virtual items, the path is already on SD, e.g. /sd/cache/... + // But we need to make sure the parser can read it. + // If it starts with /sd/, we might need to strip it if using SdFat with root? + // Assuming absolute path is fine. + fileToParse = localPath; + success = true; + fileSize = 0; // Don't check size for progress bar on virtual items } else { // Normal file - stream from zip for (int attempt = 0; attempt < 3 && !success; attempt++) { - if (attempt > 0) delay(50); + if (attempt > 0) delay(50); - if (SdMan.exists(tmpHtmlPath.c_str())) SdMan.remove(tmpHtmlPath.c_str()); + if (SdMan.exists(tmpHtmlPath.c_str())) SdMan.remove(tmpHtmlPath.c_str()); - FsFile tmpHtml; - if (!SdMan.openFileForWrite("SCT", tmpHtmlPath, tmpHtml)) continue; - success = epub->readItemContentsToStream(localPath, tmpHtml, 1024); - fileSize = tmpHtml.size(); - tmpHtml.close(); + FsFile tmpHtml; + if (!SdMan.openFileForWrite("SCT", tmpHtmlPath, tmpHtml)) continue; + success = epub->readItemContentsToStream(localPath, tmpHtml, 1024); + fileSize = tmpHtml.size(); + tmpHtml.close(); - if (!success && SdMan.exists(tmpHtmlPath.c_str())) SdMan.remove(tmpHtmlPath.c_str()); + if (!success && SdMan.exists(tmpHtmlPath.c_str())) SdMan.remove(tmpHtmlPath.c_str()); } if (!success) { - Serial.printf("[%lu] [SCT] Failed to stream item contents\n", millis()); - return false; + Serial.printf("[%lu] [SCT] Failed to stream item contents\n", millis()); + return false; } } @@ -250,7 +248,7 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c viewportHeight, hyphenationEnabled, [this, &lut](std::unique_ptr page) { lut.emplace_back(this->onPageComplete(std::move(page))); }, progressFn)); - + Hyphenator::setPreferredLanguage(epub->getLanguage()); // Track which inline footnotes AND paragraph notes are actually referenced in this file @@ -291,7 +289,7 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c } // --- Footnote Generation Logic (Merged from HEAD) --- - + // Inline footnotes for (int i = 0; i < visitor->inlineFootnoteCount; i++) { const char* inlineId = visitor->inlineFootnotes[i].id; diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp index 964684c0..69cabbb8 100644 --- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp +++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp @@ -54,7 +54,7 @@ const char* getAttribute(const XML_Char** atts, const char* attrName) { std::string replaceHtmlEntities(const char* text) { if (!text) return ""; std::string s(text); - + // Replace common entities size_t pos = 0; while ((pos = s.find("<", pos)) != std::string::npos) { @@ -273,7 +273,6 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* // PASS 2: Normal parsing // ============================================================================ - // Middle of skip if (self->skipUntilDepth < self->depth) { self->depth += 1; @@ -293,9 +292,12 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* if (self->partWordBufferIndex > 0) { // Copy of the existing flush logic EpdFontFamily::Style fontStyle = EpdFontFamily::REGULAR; - if (self->boldUntilDepth < self->depth && self->italicUntilDepth < self->depth) fontStyle = EpdFontFamily::BOLD_ITALIC; - else if (self->boldUntilDepth < self->depth) fontStyle = EpdFontFamily::BOLD; - else if (self->italicUntilDepth < self->depth) fontStyle = EpdFontFamily::ITALIC; + if (self->boldUntilDepth < self->depth && self->italicUntilDepth < self->depth) + fontStyle = EpdFontFamily::BOLD_ITALIC; + else if (self->boldUntilDepth < self->depth) + fontStyle = EpdFontFamily::BOLD; + else if (self->italicUntilDepth < self->depth) + fontStyle = EpdFontFamily::ITALIC; self->partWordBuffer[self->partWordBufferIndex] = '\0'; self->currentTextBlock->addWord(std::move(replaceHtmlEntities(self->partWordBuffer)), fontStyle); @@ -389,7 +391,7 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* return; } } - + // Special handling for tables - show placeholder text instead of dropping silently if (strcmp(name, "table") == 0) { // Add placeholder text @@ -611,7 +613,6 @@ void XMLCALL ChapterHtmlSlimParser::characterData(void* userData, const XML_Char void XMLCALL ChapterHtmlSlimParser::endElement(void* userData, const XML_Char* name) { auto* self = static_cast(userData); - // Closing paragraph note in Pass 1 if (strcmp(name, "p") == 0 && self->insideParagraphNote && self->depth - 1 == self->paragraphNoteDepth) { if (self->isPass1CollectingAsides && self->currentParagraphNoteTextLen > 0 && self->paragraphNoteCount < 32 && @@ -691,7 +692,6 @@ void XMLCALL ChapterHtmlSlimParser::endElement(void* userData, const XML_Char* n // [MODIFIED] 2. Handle 'a' tags (Anchors/Footnotes) // We check "a" generally now, to handle both Noterefs AND resetting regular links if (strcmp(name, "a") == 0) { - // Track if this was a noteref so we can return early later bool wasNoteref = self->insideNoteref; @@ -758,7 +758,6 @@ void XMLCALL ChapterHtmlSlimParser::endElement(void* userData, const XML_Char* n } } - if (self->partWordBufferIndex > 0) { const bool shouldBreakText = matches(name, BLOCK_TAGS, NUM_BLOCK_TAGS) || matches(name, HEADER_TAGS, NUM_HEADER_TAGS) || @@ -844,7 +843,7 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() { file.close(); return false; } - + done = file.available() == 0; if (XML_ParseBuffer(parser1, static_cast(len), done) == XML_STATUS_ERROR) { @@ -925,7 +924,7 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() { file.close(); return false; } - + // Update progress (call every 10% change to avoid too frequent updates) // Only show progress for larger chapters where rendering overhead is worth it bytesRead += len; @@ -989,7 +988,7 @@ void ChapterHtmlSlimParser::addLineToPage(std::shared_ptr line) { currentPageNextY = 0; } - if (currentPage && currentPage->elements.size() < 24) { // Assuming generic capacity check or vector size + if (currentPage && currentPage->elements.size() < 24) { // Assuming generic capacity check or vector size currentPage->elements.push_back(std::make_shared(line, 0, currentPageNextY)); currentPageNextY += lineHeight; } else if (currentPage) { diff --git a/src/activities/reader/EpubReaderActivity.cpp b/src/activities/reader/EpubReaderActivity.cpp index 9f172b6c..11327690 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -8,10 +8,8 @@ #include "CrossPointSettings.h" #include "CrossPointState.h" #include "EpubReaderChapterSelectionActivity.h" - #include "EpubReaderFootnotesActivity.h" #include "EpubReaderMenuActivity.h" - #include "MappedInputManager.h" #include "RecentBooksStore.h" #include "ScreenComponents.h" @@ -140,9 +138,9 @@ void EpubReaderActivity::loop() { [this, currentPage, totalPages](EpubReaderMenuActivity::MenuOption option) { // onSelectOption - handle menu choice if (option == EpubReaderMenuActivity::CHAPTERS) { - // Show chapter selection - exitActivity(); - enterNewActivity(new EpubReaderChapterSelectionActivity( + // Show chapter selection + exitActivity(); + enterNewActivity(new EpubReaderChapterSelectionActivity( this->renderer, this->mappedInput, epub, epub->getPath(), currentSpineIndex, currentPage, totalPages, [this] { exitActivity(); @@ -205,7 +203,6 @@ void EpubReaderActivity::loop() { onGoBack(); return; } - } const bool prevReleased = mappedInput.wasReleased(MappedInputManager::Button::PageBack) || @@ -414,17 +411,18 @@ void EpubReaderActivity::renderScreen() { return renderScreen(); } - Serial.printf("[%lu] [ERS] Page loaded: %d elements, %d footnotes\n", millis(), p->elements.size(), p->footnotes.size()); + Serial.printf("[%lu] [ERS] Page loaded: %d elements, %d footnotes\n", millis(), p->elements.size(), + p->footnotes.size()); // Copy footnotes from page to currentPageFootnotes currentPageFootnotes.clear(); int maxFootnotes = (p->footnotes.size() < 8) ? p->footnotes.size() : 8; for (int i = 0; i < maxFootnotes; i++) { - const FootnoteEntry& footnote = p->footnotes[i]; - if (footnote.href[0] != '\0') { - currentPageFootnotes.addFootnote(footnote.number, footnote.href); - } + const FootnoteEntry& footnote = p->footnotes[i]; + if (footnote.href[0] != '\0') { + currentPageFootnotes.addFootnote(footnote.number, footnote.href); + } } Serial.printf("[%lu] [ERS] Loaded %d footnotes for current page\n", millis(), p->footnotes.size()); @@ -498,7 +496,6 @@ void EpubReaderActivity::renderStatusBar(const int orientedMarginRight, const in // Position status bar near the bottom of the logical screen, regardless of orientation const auto screenHeight = renderer.getScreenHeight(); const auto textY = screenHeight - orientedMarginBottom - 4; - if (showProgress) { // Calculate progress in book diff --git a/src/activities/reader/EpubReaderActivity.h b/src/activities/reader/EpubReaderActivity.h index ab250b9f..16309374 100644 --- a/src/activities/reader/EpubReaderActivity.h +++ b/src/activities/reader/EpubReaderActivity.h @@ -5,8 +5,8 @@ #include #include -#include "activities/ActivityWithSubactivity.h" #include "EpubReaderFootnotesActivity.h" +#include "activities/ActivityWithSubactivity.h" class EpubReaderActivity final : public ActivityWithSubactivity { std::shared_ptr epub; diff --git a/src/activities/reader/EpubReaderChapterSelectionActivity.cpp b/src/activities/reader/EpubReaderChapterSelectionActivity.cpp index 17781493..70435000 100644 --- a/src/activities/reader/EpubReaderChapterSelectionActivity.cpp +++ b/src/activities/reader/EpubReaderChapterSelectionActivity.cpp @@ -14,8 +14,6 @@ constexpr int SKIP_PAGE_MS = 700; bool EpubReaderChapterSelectionActivity::hasSyncOption() const { return KOREADER_STORE.hasCredentials(); } - - int EpubReaderChapterSelectionActivity::getPageItems() const { // Layout constants used in renderScreen constexpr int startY = 60; @@ -90,9 +88,9 @@ void EpubReaderChapterSelectionActivity::onEnter() { // The master logic used TOC indices directly. // Let's adapt: We will display the filtered list. // If sync is enabled, we prepend/append it to the selector range. - + if (hasSyncOption()) { - selectorIndex += 1; // Offset for top sync option + selectorIndex += 1; // Offset for top sync option } // Trigger first update @@ -149,7 +147,7 @@ void EpubReaderChapterSelectionActivity::loop() { const bool skipPage = mappedInput.getHeldTime() > SKIP_PAGE_MS; const int pageItems = getPageItems(); - + // Total items = filtered chapters + sync options const int syncCount = hasSyncOption() ? 2 : 0; const int totalItems = filteredSpineIndices.size() + syncCount; @@ -157,15 +155,15 @@ void EpubReaderChapterSelectionActivity::loop() { if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { // Check if sync option is selected if (hasSyncOption()) { - if (selectorIndex == 0 || selectorIndex == totalItems - 1) { - launchSyncActivity(); - return; - } + if (selectorIndex == 0 || selectorIndex == totalItems - 1) { + launchSyncActivity(); + return; + } } // It's a chapter. Calculate index in filtered list. int filteredIndex = selectorIndex; - if (hasSyncOption()) filteredIndex -= 1; // Remove top sync offset + if (hasSyncOption()) filteredIndex -= 1; // Remove top sync offset if (filteredIndex >= 0 && filteredIndex < filteredSpineIndices.size()) { onSelectSpineIndex(filteredSpineIndices[filteredIndex]); @@ -206,7 +204,7 @@ void EpubReaderChapterSelectionActivity::renderScreen() { const auto pageWidth = renderer.getScreenWidth(); const int pageItems = getPageItems(); - + const int syncCount = hasSyncOption() ? 2 : 0; const int totalItems = filteredSpineIndices.size() + syncCount; @@ -225,33 +223,33 @@ void EpubReaderChapterSelectionActivity::renderScreen() { for (int i = pageStartIndex; i < totalItems && i < pageStartIndex + pageItems; i++) { const int displayY = 60 + (i % pageItems) * 30; - const bool isSelected = (i == selectorIndex); // Use i for comparison + const bool isSelected = (i == selectorIndex); // Use i for comparison // Check for sync item bool isSync = false; if (hasSyncOption()) { - if (i == 0 || i == totalItems - 1) isSync = true; + if (i == 0 || i == totalItems - 1) isSync = true; } if (isSync) { - renderer.drawText(UI_10_FONT_ID, 20, displayY, ">> Sync Progress", !isSelected); + renderer.drawText(UI_10_FONT_ID, 20, displayY, ">> Sync Progress", !isSelected); } else { - // It's a filtered chapter - int filteredIndex = i; - if (hasSyncOption()) filteredIndex -= 1; + // It's a filtered chapter + int filteredIndex = i; + if (hasSyncOption()) filteredIndex -= 1; - const int actualSpineIndex = filteredSpineIndices[filteredIndex]; - const int tocIndex = epub->getTocIndexForSpineIndex(actualSpineIndex); + const int actualSpineIndex = filteredSpineIndices[filteredIndex]; + const int tocIndex = epub->getTocIndexForSpineIndex(actualSpineIndex); - if (tocIndex == -1) { - renderer.drawText(UI_10_FONT_ID, 20, displayY, "Unnamed", !isSelected); - } else { - auto item = epub->getTocItem(tocIndex); - const int indentSize = 20 + (item.level - 1) * 15; - const std::string chapterName = - renderer.truncatedText(UI_10_FONT_ID, item.title.c_str(), pageWidth - 40 - indentSize); - renderer.drawText(UI_10_FONT_ID, indentSize, displayY, chapterName.c_str(), !isSelected); - } + if (tocIndex == -1) { + renderer.drawText(UI_10_FONT_ID, 20, displayY, "Unnamed", !isSelected); + } else { + auto item = epub->getTocItem(tocIndex); + const int indentSize = 20 + (item.level - 1) * 15; + const std::string chapterName = + renderer.truncatedText(UI_10_FONT_ID, item.title.c_str(), pageWidth - 40 - indentSize); + renderer.drawText(UI_10_FONT_ID, indentSize, displayY, chapterName.c_str(), !isSelected); + } } } const auto labels = mappedInput.mapLabels("« Back", "Select", "Up", "Down"); diff --git a/src/activities/reader/EpubReaderChapterSelectionActivity.h b/src/activities/reader/EpubReaderChapterSelectionActivity.h index a5039375..2510452f 100644 --- a/src/activities/reader/EpubReaderChapterSelectionActivity.h +++ b/src/activities/reader/EpubReaderChapterSelectionActivity.h @@ -27,7 +27,6 @@ class EpubReaderChapterSelectionActivity final : public ActivityWithSubactivity // This adapts automatically when switching between portrait and landscape. int getPageItems() const; - // Check if sync option is available (credentials configured) bool hasSyncOption() const; diff --git a/src/activities/reader/EpubReaderFootnotesActivity.cpp b/src/activities/reader/EpubReaderFootnotesActivity.cpp index e08fc633..b61bcd22 100644 --- a/src/activities/reader/EpubReaderFootnotesActivity.cpp +++ b/src/activities/reader/EpubReaderFootnotesActivity.cpp @@ -1,10 +1,10 @@ #include "EpubReaderFootnotesActivity.h" -#include #include +#include -#include "fontIds.h" #include "MappedInputManager.h" +#include "fontIds.h" void EpubReaderFootnotesActivity::onEnter() { selectedIndex = 0; diff --git a/src/activities/reader/EpubReaderMenuActivity.cpp b/src/activities/reader/EpubReaderMenuActivity.cpp index 3bfc8289..bc77c661 100644 --- a/src/activities/reader/EpubReaderMenuActivity.cpp +++ b/src/activities/reader/EpubReaderMenuActivity.cpp @@ -1,10 +1,10 @@ #include "EpubReaderMenuActivity.h" -#include #include +#include -#include "fontIds.h" #include "MappedInputManager.h" +#include "fontIds.h" constexpr int MENU_ITEMS_COUNT = 2; @@ -39,10 +39,10 @@ void EpubReaderMenuActivity::onExit() { } void EpubReaderMenuActivity::loop() { - const bool prevReleased = - mappedInput.wasReleased(MappedInputManager::Button::Up) || mappedInput.wasReleased(MappedInputManager::Button::Left); - const bool nextReleased = - mappedInput.wasReleased(MappedInputManager::Button::Down) || mappedInput.wasReleased(MappedInputManager::Button::Right); + const bool prevReleased = mappedInput.wasReleased(MappedInputManager::Button::Up) || + mappedInput.wasReleased(MappedInputManager::Button::Left); + const bool nextReleased = mappedInput.wasReleased(MappedInputManager::Button::Down) || + mappedInput.wasReleased(MappedInputManager::Button::Right); if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { onSelectOption(static_cast(selectorIndex));