clang-format-fix

This commit is contained in:
Jérôme Launay 2025-12-17 13:00:21 +01:00
parent dcf2b257f4
commit c4430793e7
15 changed files with 173 additions and 249 deletions

View File

@ -337,9 +337,7 @@ void Epub::markAsFootnotePage(const std::string& href) {
// Extract filename from href (remove #anchor if present) // Extract filename from href (remove #anchor if present)
size_t hashPos = href.find('#'); size_t hashPos = href.find('#');
std::string filename = (hashPos != std::string::npos) std::string filename = (hashPos != std::string::npos) ? href.substr(0, hashPos) : href;
? href.substr(0, hashPos)
: href;
// Extract just the filename without path // Extract just the filename without path
size_t lastSlash = filename.find_last_of('/'); size_t lastSlash = filename.find_last_of('/');
@ -356,7 +354,6 @@ bool Epub::isFootnotePage(const std::string& filename) const {
return footnotePages->find(filename) != footnotePages->end(); return footnotePages->find(filename) != footnotePages->end();
} }
bool Epub::shouldHideFromToc(int spineIndex) const { bool Epub::shouldHideFromToc(int spineIndex) const {
// Always hide virtual spine items // Always hide virtual spine items
if (isVirtualSpineItem(spineIndex)) { if (isVirtualSpineItem(spineIndex)) {
@ -371,9 +368,7 @@ bool Epub::shouldHideFromToc(int spineIndex) const {
// Extract filename from spine item // Extract filename from spine item
size_t lastSlash = spineItem.find_last_of('/'); size_t lastSlash = spineItem.find_last_of('/');
std::string filename = (lastSlash != std::string::npos) std::string filename = (lastSlash != std::string::npos) ? spineItem.substr(lastSlash + 1) : spineItem;
? spineItem.substr(lastSlash + 1)
: spineItem;
return isFootnotePage(filename); return isFootnotePage(filename);
} }
@ -387,14 +382,11 @@ int Epub::addVirtualSpineItem(const std::string& path) {
virtualSpineItems->push_back(path); virtualSpineItems->push_back(path);
int newIndex = spine.size() + virtualSpineItems->size() - 1; int newIndex = spine.size() + virtualSpineItems->size() - 1;
Serial.printf("[%lu] [EPUB] Added virtual spine item: %s (index %d)\n", Serial.printf("[%lu] [EPUB] Added virtual spine item: %s (index %d)\n", millis(), path.c_str(), newIndex);
millis(), path.c_str(), newIndex);
return newIndex; return newIndex;
} }
bool Epub::isVirtualSpineItem(int spineIndex) const { bool Epub::isVirtualSpineItem(int spineIndex) const { return spineIndex >= static_cast<int>(spine.size()); }
return spineIndex >= static_cast<int>(spine.size());
}
int Epub::findVirtualSpineIndex(const std::string& filename) const { int Epub::findVirtualSpineIndex(const std::string& filename) const {
if (!virtualSpineItems) return -1; if (!virtualSpineItems) return -1;
@ -402,9 +394,7 @@ int Epub::findVirtualSpineIndex(const std::string& filename) const {
for (size_t i = 0; i < virtualSpineItems->size(); i++) { for (size_t i = 0; i < virtualSpineItems->size(); i++) {
std::string virtualPath = (*virtualSpineItems)[i]; std::string virtualPath = (*virtualSpineItems)[i];
size_t lastSlash = virtualPath.find_last_of('/'); size_t lastSlash = virtualPath.find_last_of('/');
std::string virtualFilename = (lastSlash != std::string::npos) std::string virtualFilename = (lastSlash != std::string::npos) ? virtualPath.substr(lastSlash + 1) : virtualPath;
? virtualPath.substr(lastSlash + 1)
: virtualPath;
if (virtualFilename == filename) { if (virtualFilename == filename) {
return spine.size() + i; return spine.size() + i;

View File

@ -30,9 +30,7 @@ class Epub {
public: public:
explicit Epub(std::string filepath, const std::string& cacheDir) explicit Epub(std::string filepath, const std::string& cacheDir)
: filepath(std::move(filepath)), : filepath(std::move(filepath)), footnotePages(nullptr), virtualSpineItems(nullptr) {
footnotePages(nullptr),
virtualSpineItems(nullptr) {
cachePath = cacheDir + "/epub_" + std::to_string(std::hash<std::string>{}(this->filepath)); cachePath = cacheDir + "/epub_" + std::to_string(std::hash<std::string>{}(this->filepath));
} }

View File

@ -1,12 +1,11 @@
#include "Page.h" #include "Page.h"
#include <HardwareSerial.h> #include <HardwareSerial.h>
#include <Serialization.h> #include <Serialization.h>
constexpr uint8_t PAGE_FILE_VERSION = 6; // Incremented constexpr uint8_t PAGE_FILE_VERSION = 6; // Incremented
void PageLine::render(GfxRenderer& renderer, const int fontId) { void PageLine::render(GfxRenderer& renderer, const int fontId) { block->render(renderer, fontId, xPos, yPos); }
block->render(renderer, fontId, xPos, yPos);
}
void PageLine::serialize(std::ostream& os) { void PageLine::serialize(std::ostream& os) {
serialization::writePod(os, xPos); serialization::writePod(os, xPos);

View File

@ -1,8 +1,8 @@
#pragma once #pragma once
#include <cstdlib>
#include <cstring> #include <cstring>
#include <memory> #include <memory>
#include <utility> #include <utility>
#include <cstdlib>
#include "FootnoteEntry.h" #include "FootnoteEntry.h"
#include "blocks/TextBlock.h" #include "blocks/TextBlock.h"
@ -24,7 +24,7 @@ class PageElement {
class PageLine final : public PageElement { class PageLine final : public PageElement {
std::shared_ptr<TextBlock> block; std::shared_ptr<TextBlock> block;
public: public:
PageLine(std::shared_ptr<TextBlock> block, const int16_t xPos, const int16_t yPos) PageLine(std::shared_ptr<TextBlock> block, const int16_t xPos, const int16_t yPos)
: PageElement(xPos, yPos), block(std::move(block)) {} : PageElement(xPos, yPos), block(std::move(block)) {}
void render(GfxRenderer& renderer, int fontId) override; void render(GfxRenderer& renderer, int fontId) override;
@ -33,14 +33,14 @@ public:
}; };
class Page { class Page {
private: private:
std::shared_ptr<PageElement>* elements; std::shared_ptr<PageElement>* elements;
int elementCapacity; int elementCapacity;
FootnoteEntry* footnotes; FootnoteEntry* footnotes;
int footnoteCapacity; int footnoteCapacity;
public: public:
int elementCount; int elementCount;
int footnoteCount; int footnoteCount;

View File

@ -20,7 +20,7 @@ static bool writeEscapedXml(File& file, const char* text) {
static char buffer[2048]; static char buffer[2048];
int bufferPos = 0; int bufferPos = 0;
while (*text && bufferPos < sizeof(buffer) - 10) { // Leave margin for entities while (*text && bufferPos < sizeof(buffer) - 10) { // Leave margin for entities
unsigned char c = (unsigned char)*text; unsigned char c = (unsigned char)*text;
// Only escape the 5 XML special characters // Only escape the 5 XML special characters
@ -178,10 +178,10 @@ bool Section::persistPageDataToSD(const int fontId, const float lineCompression,
const auto sdPath = "/sd" + localPath; const auto sdPath = "/sd" + localPath;
ChapterHtmlSlimParser visitor(sdPath.c_str(), renderer, fontId, lineCompression, marginTop, marginRight, ChapterHtmlSlimParser visitor(
marginBottom, marginLeft, extraParagraphSpacing, sdPath.c_str(), renderer, fontId, lineCompression, marginTop, marginRight, marginBottom, marginLeft,
[this](std::unique_ptr<Page> page) { this->onPageComplete(std::move(page)); }, extraParagraphSpacing, [this](std::unique_ptr<Page> page) { this->onPageComplete(std::move(page)); },
cachePath); cachePath);
bool success = visitor.parseAndBuildPages(); bool success = visitor.parseAndBuildPages();
@ -190,7 +190,8 @@ bool Section::persistPageDataToSD(const int fontId, const float lineCompression,
return false; return false;
} }
writeCacheMetadata(fontId, lineCompression, marginTop, marginRight, marginBottom, marginLeft, extraParagraphSpacing); writeCacheMetadata(fontId, lineCompression, marginTop, marginRight, marginBottom, marginLeft,
extraParagraphSpacing);
return true; return true;
} }
@ -209,10 +210,9 @@ bool Section::persistPageDataToSD(const int fontId, const float lineCompression,
const auto sdTmpHtmlPath = "/sd" + tmpHtmlPath; const auto sdTmpHtmlPath = "/sd" + tmpHtmlPath;
ChapterHtmlSlimParser visitor(sdTmpHtmlPath.c_str(), renderer, fontId, lineCompression, marginTop, marginRight, ChapterHtmlSlimParser visitor(
marginBottom, marginLeft, extraParagraphSpacing, sdTmpHtmlPath.c_str(), renderer, fontId, lineCompression, marginTop, marginRight, marginBottom, marginLeft,
[this](std::unique_ptr<Page> page) { this->onPageComplete(std::move(page)); }, extraParagraphSpacing, [this](std::unique_ptr<Page> page) { this->onPageComplete(std::move(page)); }, cachePath);
cachePath);
// Track which inline footnotes AND paragraph notes are actually referenced in this file // Track which inline footnotes AND paragraph notes are actually referenced in this file
std::set<std::string> rewrittenInlineIds; std::set<std::string> rewrittenInlineIds;
@ -232,10 +232,9 @@ bool Section::persistPageDataToSD(const int fontId, const float lineCompression,
if (underscorePos != std::string::npos && dotPos != std::string::npos) { if (underscorePos != std::string::npos && dotPos != std::string::npos) {
std::string noteId = href.substr(underscorePos + 1, dotPos - underscorePos - 1); std::string noteId = href.substr(underscorePos + 1, dotPos - underscorePos - 1);
rewrittenInlineIds.insert(noteId); rewrittenInlineIds.insert(noteId);
Serial.printf("[%lu] [SCT] Marked note as rewritten: %s\n", Serial.printf("[%lu] [SCT] Marked note as rewritten: %s\n", millis(), noteId.c_str());
millis(), noteId.c_str());
} }
}else { } else {
// Normal external footnote // Normal external footnote
epub->markAsFootnotePage(noteref.href); epub->markAsFootnotePage(noteref.href);
} }
@ -254,8 +253,8 @@ bool Section::persistPageDataToSD(const int fontId, const float lineCompression,
} }
// NOW generate inline footnote HTML files ONLY for rewritten ones // NOW generate inline footnote HTML files ONLY for rewritten ones
Serial.printf("[%lu] [SCT] Found %d inline footnotes, %d were referenced\n", Serial.printf("[%lu] [SCT] Found %d inline footnotes, %d were referenced\n", millis(), visitor.inlineFootnoteCount,
millis(), visitor.inlineFootnoteCount, rewrittenInlineIds.size()); rewrittenInlineIds.size());
for (int i = 0; i < visitor.inlineFootnoteCount; i++) { for (int i = 0; i < visitor.inlineFootnoteCount; i++) {
const char* inlineId = visitor.inlineFootnotes[i].id; const char* inlineId = visitor.inlineFootnotes[i].id;
@ -263,8 +262,7 @@ bool Section::persistPageDataToSD(const int fontId, const float lineCompression,
// Only generate if this inline footnote was actually referenced // Only generate if this inline footnote was actually referenced
if (rewrittenInlineIds.find(std::string(inlineId)) == rewrittenInlineIds.end()) { if (rewrittenInlineIds.find(std::string(inlineId)) == rewrittenInlineIds.end()) {
Serial.printf("[%lu] [SCT] Skipping unreferenced inline footnote: %s\n", Serial.printf("[%lu] [SCT] Skipping unreferenced inline footnote: %s\n", millis(), inlineId);
millis(), inlineId);
continue; continue;
} }
@ -274,8 +272,7 @@ bool Section::persistPageDataToSD(const int fontId, const float lineCompression,
continue; continue;
} }
Serial.printf("[%lu] [SCT] Processing inline footnote: %s (len=%d)\n", Serial.printf("[%lu] [SCT] Processing inline footnote: %s (len=%d)\n", millis(), inlineId, strlen(inlineText));
millis(), inlineId, strlen(inlineText));
char inlineFilename[64]; char inlineFilename[64];
snprintf(inlineFilename, sizeof(inlineFilename), "inline_%s.html", inlineId); snprintf(inlineFilename, sizeof(inlineFilename), "inline_%s.html", inlineId);
@ -325,64 +322,64 @@ bool Section::persistPageDataToSD(const int fontId, const float lineCompression,
} }
} }
// Generate paragraph note HTML files // Generate paragraph note HTML files
Serial.printf("[%lu] [SCT] Found %d paragraph notes\n", millis(), visitor.paragraphNoteCount); Serial.printf("[%lu] [SCT] Found %d paragraph notes\n", millis(), visitor.paragraphNoteCount);
for (int i = 0; i < visitor.paragraphNoteCount; i++) { for (int i = 0; i < visitor.paragraphNoteCount; i++) {
const char* pnoteId = visitor.paragraphNotes[i].id; const char* pnoteId = visitor.paragraphNotes[i].id;
const char* pnoteText = visitor.paragraphNotes[i].text; const char* pnoteText = visitor.paragraphNotes[i].text;
if (!pnoteText || strlen(pnoteText) == 0) { if (!pnoteText || strlen(pnoteText) == 0) {
continue; continue;
}
// Check if this paragraph note was referenced
if (rewrittenInlineIds.find(std::string(pnoteId)) == rewrittenInlineIds.end()) {
Serial.printf("[%lu] [SCT] Skipping unreferenced paragraph note: %s\n", millis(), pnoteId);
continue;
}
// Create filename: pnote_rnote1.html
char pnoteFilename[64];
snprintf(pnoteFilename, sizeof(pnoteFilename), "pnote_%s.html", pnoteId);
std::string fullPath = epub->getCachePath() + "/" + std::string(pnoteFilename);
Serial.printf("[%lu] [SCT] Generating paragraph note file: %s\n", millis(), fullPath.c_str());
File file = SD.open(fullPath.c_str(), FILE_WRITE, true);
if (file) {
file.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
file.println("<!DOCTYPE html>");
file.println("<html xmlns=\"http://www.w3.org/1999/xhtml\">");
file.println("<head>");
file.println("<meta charset=\"UTF-8\"/>");
file.println("<title>Note</title>");
file.println("</head>");
file.println("<body>");
file.print("<p id=\"");
file.print(pnoteId);
file.print("\">");
if (!writeEscapedXml(file, pnoteText)) {
Serial.printf("[%lu] [SCT] Warning: writeEscapedXml may have failed\n", millis());
} }
file.println("</p>"); // Check if this paragraph note was referenced
file.println("</body>"); if (rewrittenInlineIds.find(std::string(pnoteId)) == rewrittenInlineIds.end()) {
file.println("</html>"); Serial.printf("[%lu] [SCT] Skipping unreferenced paragraph note: %s\n", millis(), pnoteId);
file.close(); continue;
}
Serial.printf("[%lu] [SCT] Generated paragraph note file\n", millis()); // Create filename: pnote_rnote1.html
char pnoteFilename[64];
snprintf(pnoteFilename, sizeof(pnoteFilename), "pnote_%s.html", pnoteId);
int virtualIndex = epub->addVirtualSpineItem(fullPath); std::string fullPath = epub->getCachePath() + "/" + std::string(pnoteFilename);
Serial.printf("[%lu] [SCT] Added virtual spine item at index %d\n", millis(), virtualIndex);
char newHref[128]; Serial.printf("[%lu] [SCT] Generating paragraph note file: %s\n", millis(), fullPath.c_str());
snprintf(newHref, sizeof(newHref), "%s#%s", pnoteFilename, pnoteId);
epub->markAsFootnotePage(newHref); File file = SD.open(fullPath.c_str(), FILE_WRITE, true);
if (file) {
file.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
file.println("<!DOCTYPE html>");
file.println("<html xmlns=\"http://www.w3.org/1999/xhtml\">");
file.println("<head>");
file.println("<meta charset=\"UTF-8\"/>");
file.println("<title>Note</title>");
file.println("</head>");
file.println("<body>");
file.print("<p id=\"");
file.print(pnoteId);
file.print("\">");
if (!writeEscapedXml(file, pnoteText)) {
Serial.printf("[%lu] [SCT] Warning: writeEscapedXml may have failed\n", millis());
}
file.println("</p>");
file.println("</body>");
file.println("</html>");
file.close();
Serial.printf("[%lu] [SCT] Generated paragraph note file\n", millis());
int virtualIndex = epub->addVirtualSpineItem(fullPath);
Serial.printf("[%lu] [SCT] Added virtual spine item at index %d\n", millis(), virtualIndex);
char newHref[128];
snprintf(newHref, sizeof(newHref), "%s#%s", pnoteFilename, pnoteId);
epub->markAsFootnotePage(newHref);
}
} }
}
Serial.printf("[%lu] [SCT] Total noterefs found: %d\n", millis(), noterefCount); Serial.printf("[%lu] [SCT] Total noterefs found: %d\n", millis(), noterefCount);

View File

@ -90,7 +90,7 @@ void ChapterHtmlSlimParser::addFootnoteToCurrentPage(const char* number, const c
} }
} }
//Check if we have this as a paragraph note // Check if we have this as a paragraph note
if (!foundInline) { if (!foundInline) {
for (int i = 0; i < paragraphNoteCount; i++) { for (int i = 0; i < paragraphNoteCount; i++) {
if (strcmp(paragraphNotes[i].id, inlineId) == 0) { if (strcmp(paragraphNotes[i].id, inlineId) == 0) {
@ -120,10 +120,9 @@ void ChapterHtmlSlimParser::addFootnoteToCurrentPage(const char* number, const c
currentPageFootnoteCount++; currentPageFootnoteCount++;
Serial.printf("[%lu] [ADDFT] Stored as: num=%s, href=%s\n", Serial.printf("[%lu] [ADDFT] Stored as: num=%s, href=%s\n", millis(),
millis(), currentPageFootnotes[currentPageFootnoteCount - 1].number,
currentPageFootnotes[currentPageFootnoteCount-1].number, currentPageFootnotes[currentPageFootnoteCount - 1].href);
currentPageFootnotes[currentPageFootnoteCount-1].href);
} }
void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* name, const XML_Char** atts) { void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* name, const XML_Char** atts) {
@ -173,8 +172,8 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
if (epubType && strcmp(epubType, "footnote") == 0 && id) { if (epubType && strcmp(epubType, "footnote") == 0 && id) {
if (self->isPass1CollectingAsides) { if (self->isPass1CollectingAsides) {
// Pass 1: Collect aside // Pass 1: Collect aside
Serial.printf("[%lu] [ASIDE] Found inline footnote: id=%s (pass1=%d)\n", Serial.printf("[%lu] [ASIDE] Found inline footnote: id=%s (pass1=%d)\n", millis(), id,
millis(), id, self->isPass1CollectingAsides); self->isPass1CollectingAsides);
self->insideAsideFootnote = true; self->insideAsideFootnote = true;
self->asideDepth = self->depth; self->asideDepth = self->depth;
@ -189,8 +188,7 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
// Find the inline footnote text // Find the inline footnote text
for (int i = 0; i < self->inlineFootnoteCount; i++) { for (int i = 0; i < self->inlineFootnoteCount; i++) {
if (strcmp(self->inlineFootnotes[i].id, id) == 0 && if (strcmp(self->inlineFootnotes[i].id, id) == 0 && self->inlineFootnotes[i].text) {
self->inlineFootnotes[i].text) {
// Output the footnote text as normal text // Output the footnote text as normal text
const char* text = self->inlineFootnotes[i].text; const char* text = self->inlineFootnotes[i].text;
int textLen = strlen(text); int textLen = strlen(text);
@ -198,10 +196,9 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
// Process it through characterData // Process it through characterData
self->characterData(self, text, textLen); self->characterData(self, text, textLen);
Serial.printf("[%lu] [ASIDE] Rendered aside text: %.80s...\n", Serial.printf("[%lu] [ASIDE] Rendered aside text: %.80s...\n", millis(), text);
millis(), text);
break; break;
} }
} }
// Skip the aside element itself // Skip the aside element itself
@ -332,13 +329,13 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
void XMLCALL ChapterHtmlSlimParser::characterData(void* userData, const XML_Char* s, const int len) { void XMLCALL ChapterHtmlSlimParser::characterData(void* userData, const XML_Char* s, const int len) {
auto* self = static_cast<ChapterHtmlSlimParser*>(userData); auto* self = static_cast<ChapterHtmlSlimParser*>(userData);
//Collect paragraph note text in Pass 1 // Collect paragraph note text in Pass 1
if (self->insideParagraphNote && self->isPass1CollectingAsides) { if (self->insideParagraphNote && self->isPass1CollectingAsides) {
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
if (self->currentParagraphNoteTextLen >= self->MAX_PNOTE_BUFFER - 2) { if (self->currentParagraphNoteTextLen >= self->MAX_PNOTE_BUFFER - 2) {
if (self->currentParagraphNoteTextLen == self->MAX_PNOTE_BUFFER - 2) { if (self->currentParagraphNoteTextLen == self->MAX_PNOTE_BUFFER - 2) {
Serial.printf("[%lu] [PNOTE] WARNING: Note text truncated at %d chars\n", Serial.printf("[%lu] [PNOTE] WARNING: Note text truncated at %d chars\n", millis(),
millis(), self->MAX_PNOTE_BUFFER - 2); self->MAX_PNOTE_BUFFER - 2);
} }
break; break;
} }
@ -349,7 +346,7 @@ void XMLCALL ChapterHtmlSlimParser::characterData(void* userData, const XML_Char
if (self->currentParagraphNoteTextLen > 0 && if (self->currentParagraphNoteTextLen > 0 &&
self->currentParagraphNoteText[self->currentParagraphNoteTextLen - 1] != ' ') { self->currentParagraphNoteText[self->currentParagraphNoteTextLen - 1] != ' ') {
self->currentParagraphNoteText[self->currentParagraphNoteTextLen++] = ' '; self->currentParagraphNoteText[self->currentParagraphNoteTextLen++] = ' ';
} }
} else if (c >= 32 || c >= 0x80) { // Accept printable ASCII AND UTF-8 } else if (c >= 32 || c >= 0x80) { // Accept printable ASCII AND UTF-8
self->currentParagraphNoteText[self->currentParagraphNoteTextLen++] = c; self->currentParagraphNoteText[self->currentParagraphNoteTextLen++] = c;
} }
@ -367,8 +364,8 @@ void XMLCALL ChapterHtmlSlimParser::characterData(void* userData, const XML_Char
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
if (self->currentAsideTextLen >= self->MAX_ASIDE_BUFFER - 2) { if (self->currentAsideTextLen >= self->MAX_ASIDE_BUFFER - 2) {
if (self->currentAsideTextLen == self->MAX_ASIDE_BUFFER - 2) { if (self->currentAsideTextLen == self->MAX_ASIDE_BUFFER - 2) {
Serial.printf("[%lu] [ASIDE] WARNING: Footnote text truncated at %d chars (id=%s)\n", Serial.printf("[%lu] [ASIDE] WARNING: Footnote text truncated at %d chars (id=%s)\n", millis(),
millis(), self->MAX_ASIDE_BUFFER - 2, self->currentAsideId); self->MAX_ASIDE_BUFFER - 2, self->currentAsideId);
} }
break; break;
} }
@ -376,10 +373,9 @@ void XMLCALL ChapterHtmlSlimParser::characterData(void* userData, const XML_Char
unsigned char c = (unsigned char)s[i]; // Cast to unsigned char unsigned char c = (unsigned char)s[i]; // Cast to unsigned char
if (isWhitespace(c)) { if (isWhitespace(c)) {
if (self->currentAsideTextLen > 0 && if (self->currentAsideTextLen > 0 && self->currentAsideText[self->currentAsideTextLen - 1] != ' ') {
self->currentAsideText[self->currentAsideTextLen - 1] != ' ') {
self->currentAsideText[self->currentAsideTextLen++] = ' '; self->currentAsideText[self->currentAsideTextLen++] = ' ';
} }
} else if (c >= 32 || c >= 0x80) { // Accept printable ASCII AND UTF-8 bytes } else if (c >= 32 || c >= 0x80) { // Accept printable ASCII AND UTF-8 bytes
self->currentAsideText[self->currentAsideTextLen++] = c; self->currentAsideText[self->currentAsideTextLen++] = c;
} }
@ -443,36 +439,27 @@ void XMLCALL ChapterHtmlSlimParser::characterData(void* userData, const XML_Char
void XMLCALL ChapterHtmlSlimParser::endElement(void* userData, const XML_Char* name) { void XMLCALL ChapterHtmlSlimParser::endElement(void* userData, const XML_Char* name) {
auto* self = static_cast<ChapterHtmlSlimParser*>(userData); auto* self = static_cast<ChapterHtmlSlimParser*>(userData);
//Closing paragraph note in Pass 1 // Closing paragraph note in Pass 1
if (strcmp(name, "p") == 0 && self->insideParagraphNote && if (strcmp(name, "p") == 0 && self->insideParagraphNote && self->depth - 1 == self->paragraphNoteDepth) {
self->depth - 1 == self->paragraphNoteDepth) { if (self->isPass1CollectingAsides && self->currentParagraphNoteTextLen > 0 && self->paragraphNoteCount < 32 &&
if (self->isPass1CollectingAsides &&
self->currentParagraphNoteTextLen > 0 &&
self->paragraphNoteCount < 32 &&
self->currentParagraphNoteId[0] != '\0') { self->currentParagraphNoteId[0] != '\0') {
// Copy ID // Copy ID
strncpy(self->paragraphNotes[self->paragraphNoteCount].id, strncpy(self->paragraphNotes[self->paragraphNoteCount].id, self->currentParagraphNoteId, 15);
self->currentParagraphNoteId, 15);
self->paragraphNotes[self->paragraphNoteCount].id[15] = '\0'; self->paragraphNotes[self->paragraphNoteCount].id[15] = '\0';
// Allocate memory for text // Allocate memory for text
size_t textLen = strlen(self->currentParagraphNoteText); size_t textLen = strlen(self->currentParagraphNoteText);
self->paragraphNotes[self->paragraphNoteCount].text = self->paragraphNotes[self->paragraphNoteCount].text = static_cast<char*>(malloc(textLen + 1));
static_cast<char*>(malloc(textLen + 1));
if (self->paragraphNotes[self->paragraphNoteCount].text) { if (self->paragraphNotes[self->paragraphNoteCount].text) {
strcpy(self->paragraphNotes[self->paragraphNoteCount].text, strcpy(self->paragraphNotes[self->paragraphNoteCount].text, self->currentParagraphNoteText);
self->currentParagraphNoteText);
Serial.printf("[%lu] [PNOTE] Stored: %s -> %.80s... (allocated %d bytes)\n", Serial.printf("[%lu] [PNOTE] Stored: %s -> %.80s... (allocated %d bytes)\n", millis(),
millis(), self->currentParagraphNoteId, self->currentParagraphNoteId, self->currentParagraphNoteText, textLen + 1);
self->currentParagraphNoteText, textLen + 1);
self->paragraphNoteCount++; self->paragraphNoteCount++;
} }
} }
self->insideParagraphNote = false; self->insideParagraphNote = false;
self->depth -= 1; self->depth -= 1;
@ -480,35 +467,27 @@ void XMLCALL ChapterHtmlSlimParser::endElement(void* userData, const XML_Char* n
} }
// Closing aside - handle differently for Pass 1 vs Pass 2 // Closing aside - handle differently for Pass 1 vs Pass 2
if (strcmp(name, "aside") == 0 && self->insideAsideFootnote && if (strcmp(name, "aside") == 0 && self->insideAsideFootnote && self->depth - 1 == self->asideDepth) {
self->depth - 1 == self->asideDepth) {
// Store footnote ONLY in Pass 1 // Store footnote ONLY in Pass 1
if (self->isPass1CollectingAsides && if (self->isPass1CollectingAsides && self->currentAsideTextLen > 0 && self->inlineFootnoteCount < 16) {
self->currentAsideTextLen > 0 &&
self->inlineFootnoteCount < 16) {
// Copy ID (max 2 digits) // Copy ID (max 2 digits)
strncpy(self->inlineFootnotes[self->inlineFootnoteCount].id, strncpy(self->inlineFootnotes[self->inlineFootnoteCount].id, self->currentAsideId, 2);
self->currentAsideId, 2);
self->inlineFootnotes[self->inlineFootnoteCount].id[2] = '\0'; self->inlineFootnotes[self->inlineFootnoteCount].id[2] = '\0';
// DYNAMIC ALLOCATION: allocate exactly the needed size + 1 // DYNAMIC ALLOCATION: allocate exactly the needed size + 1
size_t textLen = strlen(self->currentAsideText); size_t textLen = strlen(self->currentAsideText);
self->inlineFootnotes[self->inlineFootnoteCount].text = self->inlineFootnotes[self->inlineFootnoteCount].text = static_cast<char*>(malloc(textLen + 1));
static_cast<char*>(malloc(textLen + 1));
if (self->inlineFootnotes[self->inlineFootnoteCount].text) { if (self->inlineFootnotes[self->inlineFootnoteCount].text) {
strcpy(self->inlineFootnotes[self->inlineFootnoteCount].text, strcpy(self->inlineFootnotes[self->inlineFootnoteCount].text, self->currentAsideText);
self->currentAsideText);
Serial.printf("[%lu] [ASIDE] Stored: %s -> %.80s... (allocated %d bytes)\n", Serial.printf("[%lu] [ASIDE] Stored: %s -> %.80s... (allocated %d bytes)\n", millis(), self->currentAsideId,
millis(), self->currentAsideId, self->currentAsideText, textLen + 1); self->currentAsideText, textLen + 1);
self->inlineFootnoteCount++; self->inlineFootnoteCount++;
} else { } else {
Serial.printf("[%lu] [ASIDE] ERROR: Failed to allocate %d bytes for footnote %s\n", Serial.printf("[%lu] [ASIDE] ERROR: Failed to allocate %d bytes for footnote %s\n", millis(), textLen + 1,
millis(), textLen + 1, self->currentAsideId); self->currentAsideId);
} }
} }
@ -529,9 +508,7 @@ void XMLCALL ChapterHtmlSlimParser::endElement(void* userData, const XML_Char* n
self->insideNoteref = false; self->insideNoteref = false;
if (self->currentNoterefTextLen > 0) { if (self->currentNoterefTextLen > 0) {
Serial.printf("[%lu] [NOTEREF] %s -> %s\n", millis(), Serial.printf("[%lu] [NOTEREF] %s -> %s\n", millis(), self->currentNoterefText, self->currentNoterefHref);
self->currentNoterefText,
self->currentNoterefHref);
// Add footnote first (this does the rewriting) // Add footnote first (this does the rewriting)
self->addFootnoteToCurrentPage(self->currentNoterefText, self->currentNoterefHref); self->addFootnoteToCurrentPage(self->currentNoterefText, self->currentNoterefHref);
@ -668,8 +645,7 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() {
done = feof(file); done = feof(file);
if (XML_ParseBuffer(parser1, static_cast<int>(len), done) == XML_STATUS_ERROR) { if (XML_ParseBuffer(parser1, static_cast<int>(len), done) == XML_STATUS_ERROR) {
Serial.printf("[%lu] [EHP] Parse error at line %lu:\n%s\n", millis(), Serial.printf("[%lu] [EHP] Parse error at line %lu:\n%s\n", millis(), XML_GetCurrentLineNumber(parser1),
XML_GetCurrentLineNumber(parser1),
XML_ErrorString(XML_GetErrorCode(parser1))); XML_ErrorString(XML_GetErrorCode(parser1)));
XML_ParserFree(parser1); XML_ParserFree(parser1);
fclose(file); fclose(file);
@ -680,11 +656,9 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() {
XML_ParserFree(parser1); XML_ParserFree(parser1);
fclose(file); fclose(file);
Serial.printf("[%lu] [PARSER] Pass 1 complete: found %d inline footnotes\n", Serial.printf("[%lu] [PARSER] Pass 1 complete: found %d inline footnotes\n", millis(), inlineFootnoteCount);
millis(), inlineFootnoteCount);
for (int i = 0; i < inlineFootnoteCount; i++) { for (int i = 0; i < inlineFootnoteCount; i++) {
Serial.printf("[%lu] [PARSER] - %s: %.80s\n", Serial.printf("[%lu] [PARSER] - %s: %.80s\n", millis(), inlineFootnotes[i].id, inlineFootnotes[i].text);
millis(), inlineFootnotes[i].id, inlineFootnotes[i].text);
} }
// ============================================================================ // ============================================================================
@ -743,8 +717,7 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() {
done = feof(file); done = feof(file);
if (XML_ParseBuffer(parser2, static_cast<int>(len), done) == XML_STATUS_ERROR) { if (XML_ParseBuffer(parser2, static_cast<int>(len), done) == XML_STATUS_ERROR) {
Serial.printf("[%lu] [EHP] Parse error at line %lu:\n%s\n", millis(), Serial.printf("[%lu] [EHP] Parse error at line %lu:\n%s\n", millis(), XML_GetCurrentLineNumber(parser2),
XML_GetCurrentLineNumber(parser2),
XML_ErrorString(XML_GetErrorCode(parser2))); XML_ErrorString(XML_GetErrorCode(parser2)));
XML_ParserFree(parser2); XML_ParserFree(parser2);
fclose(file); fclose(file);
@ -819,4 +792,3 @@ void ChapterHtmlSlimParser::makePages() {
currentPageNextY += lineHeight / 2; currentPageNextY += lineHeight / 2;
} }
} }

View File

@ -1,14 +1,15 @@
#pragma once #pragma once
#include <expat.h> #include <expat.h>
#include <climits> #include <climits>
#include <cstring> #include <cstring>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include "../FootnoteEntry.h"
#include "../ParsedText.h" #include "../ParsedText.h"
#include "../blocks/TextBlock.h" #include "../blocks/TextBlock.h"
#include "../FootnoteEntry.h"
class Page; class Page;
class GfxRenderer; class GfxRenderer;
@ -25,19 +26,15 @@ struct InlineFootnote {
char id[3]; char id[3];
char* text; char* text;
InlineFootnote() : text(nullptr) { InlineFootnote() : text(nullptr) { id[0] = '\0'; }
id[0] = '\0';
}
}; };
// Struct to store collected inline footnotes from <p class="note"> // Struct to store collected inline footnotes from <p class="note">
struct ParagraphNote { struct ParagraphNote {
char id[16]; // ID from <a id="rnote1"> char id[16]; // ID from <a id="rnote1">
char* text; // Pointer to dynamically allocated text char* text; // Pointer to dynamically allocated text
ParagraphNote() : text(nullptr) { ParagraphNote() : text(nullptr) { id[0] = '\0'; }
id[0] = '\0';
}
~ParagraphNote() { ~ParagraphNote() {
if (text) { if (text) {
@ -88,7 +85,7 @@ class ChapterHtmlSlimParser {
int asideDepth = 0; int asideDepth = 0;
char currentAsideId[3] = {0}; char currentAsideId[3] = {0};
//Paragraph note tracking // Paragraph note tracking
bool insideParagraphNote = false; bool insideParagraphNote = false;
int paragraphNoteDepth = 0; int paragraphNoteDepth = 0;
char currentParagraphNoteId[16] = {0}; char currentParagraphNoteId[16] = {0};
@ -120,7 +117,7 @@ class ChapterHtmlSlimParser {
// inline footnotes // inline footnotes
InlineFootnote inlineFootnotes[16]; InlineFootnote inlineFootnotes[16];
int inlineFootnoteCount = 0; int inlineFootnoteCount = 0;
//paragraph notes // paragraph notes
ParagraphNote paragraphNotes[32]; ParagraphNote paragraphNotes[32];
int paragraphNoteCount = 0; int paragraphNoteCount = 0;
@ -161,7 +158,5 @@ class ChapterHtmlSlimParser {
bool parseAndBuildPages(); bool parseAndBuildPages();
void addLineToPage(std::shared_ptr<TextBlock> line); void addLineToPage(std::shared_ptr<TextBlock> line);
void setNoterefCallback(const std::function<void(Noteref&)>& callback) { void setNoterefCallback(const std::function<void(Noteref&)>& callback) { noterefCallback = callback; }
noterefCallback = callback;
}
}; };

View File

@ -73,8 +73,8 @@ void EpubReaderChapterSelectionScreen::buildFilteredChapterList() {
filteredSpineIndices.push_back(i); filteredSpineIndices.push_back(i);
} }
Serial.printf("[%lu] [CHAP] Filtered chapters: %d out of %d\n", Serial.printf("[%lu] [CHAP] Filtered chapters: %d out of %d\n", millis(), filteredSpineIndices.size(),
millis(), filteredSpineIndices.size(), epub->getSpineItemsCount()); epub->getSpineItemsCount());
} }
void EpubReaderChapterSelectionScreen::handleInput() { void EpubReaderChapterSelectionScreen::handleInput() {

View File

@ -27,7 +27,7 @@ class EpubReaderChapterSelectionScreen final : public Screen {
void renderScreen(); void renderScreen();
void buildFilteredChapterList(); void buildFilteredChapterList();
public: public:
explicit EpubReaderChapterSelectionScreen(GfxRenderer& renderer, InputManager& inputManager, explicit EpubReaderChapterSelectionScreen(GfxRenderer& renderer, InputManager& inputManager,
const std::shared_ptr<Epub>& epub, const int currentSpineIndex, const std::shared_ptr<Epub>& epub, const int currentSpineIndex,
const std::function<void()>& onGoBack, const std::function<void()>& onGoBack,

View File

@ -1,7 +1,9 @@
#include "EpubReaderFootnotesScreen.h" #include "EpubReaderFootnotesScreen.h"
#include "config.h"
#include <GfxRenderer.h> #include <GfxRenderer.h>
#include "config.h"
void EpubReaderFootnotesScreen::onEnter() { void EpubReaderFootnotesScreen::onEnter() {
selectedIndex = 0; selectedIndex = 0;
render(); render();
@ -20,8 +22,7 @@ void EpubReaderFootnotesScreen::handleInput() {
if (inputManager.wasPressed(InputManager::BTN_CONFIRM)) { if (inputManager.wasPressed(InputManager::BTN_CONFIRM)) {
const FootnoteEntry* entry = footnotes.getEntry(selectedIndex); const FootnoteEntry* entry = footnotes.getEntry(selectedIndex);
if (entry) { if (entry) {
Serial.printf("[%lu] [FNS] Selected footnote: %s -> %s\n", Serial.printf("[%lu] [FNS] Selected footnote: %s -> %s\n", millis(), entry->number, entry->href);
millis(), entry->number, entry->href);
// Appeler le callback - EpubReaderScreen gère la navigation // Appeler le callback - EpubReaderScreen gère la navigation
onSelectFootnote(entry->href); onSelectFootnote(entry->href);
@ -83,9 +84,8 @@ void EpubReaderFootnotesScreen::render() {
} }
// Instructions at bottom // Instructions at bottom
renderer.drawText(SMALL_FONT_ID, marginLeft, renderer.drawText(SMALL_FONT_ID, marginLeft, GfxRenderer::getScreenHeight() - 40,
GfxRenderer::getScreenHeight() - 40, "UP/DOWN: Select CONFIRM: Go to footnote BACK: Return");
"UP/DOWN: Select CONFIRM: Go to footnote BACK: Return");
renderer.displayBuffer(); renderer.displayBuffer();
} }

View File

@ -1,16 +1,17 @@
#pragma once #pragma once
#include "Screen.h" #include <cstring>
#include "../../lib/Epub/Epub/FootnoteEntry.h"
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <cstring>
#include "../../lib/Epub/Epub/FootnoteEntry.h"
#include "Screen.h"
class FootnotesData { class FootnotesData {
private: private:
FootnoteEntry entries[32]; FootnoteEntry entries[32];
int count; int count;
public: public:
FootnotesData() : count(0) {} FootnotesData() : count(0) {}
void addFootnote(const char* number, const char* href) { void addFootnote(const char* number, const char* href) {
@ -23,13 +24,9 @@ public:
} }
} }
void clear() { void clear() { count = 0; }
count = 0;
}
int getCount() const { int getCount() const { return count; }
return count;
}
const FootnoteEntry* getEntry(int index) const { const FootnoteEntry* getEntry(int index) const {
if (index >= 0 && index < count) { if (index >= 0 && index < count) {
@ -45,13 +42,10 @@ class EpubReaderFootnotesScreen final : public Screen {
const std::function<void(const char*)> onSelectFootnote; const std::function<void(const char*)> onSelectFootnote;
int selectedIndex; int selectedIndex;
public: public:
EpubReaderFootnotesScreen( EpubReaderFootnotesScreen(GfxRenderer& renderer, InputManager& inputManager, const FootnotesData& footnotes,
GfxRenderer& renderer, const std::function<void()>& onGoBack,
InputManager& inputManager, const std::function<void(const char*)>& onSelectFootnote)
const FootnotesData& footnotes,
const std::function<void()>& onGoBack,
const std::function<void(const char*)>& onSelectFootnote)
: Screen(renderer, inputManager), : Screen(renderer, inputManager),
footnotes(footnotes), footnotes(footnotes),
onGoBack(onGoBack), onGoBack(onGoBack),
@ -62,6 +56,6 @@ public:
void onExit() override; void onExit() override;
void handleInput() override; void handleInput() override;
private: private:
void render(); void render();
}; };

View File

@ -2,7 +2,9 @@
// Created by jlaunay on 13/12/2025. // Created by jlaunay on 13/12/2025.
// //
#include "EpubReaderMenuScreen.h" #include "EpubReaderMenuScreen.h"
#include <GfxRenderer.h> #include <GfxRenderer.h>
#include "config.h" #include "config.h"
constexpr int MENU_ITEMS_COUNT = 2; constexpr int MENU_ITEMS_COUNT = 2;
@ -74,10 +76,7 @@ void EpubReaderMenuScreen::renderScreen() {
const auto pageWidth = renderer.getScreenWidth(); const auto pageWidth = renderer.getScreenWidth();
renderer.drawCenteredText(READER_FONT_ID, 10, "Menu", true, BOLD); renderer.drawCenteredText(READER_FONT_ID, 10, "Menu", true, BOLD);
const char* menuItems[MENU_ITEMS_COUNT] = { const char* menuItems[MENU_ITEMS_COUNT] = {"Go to chapter", "View footnotes"};
"Go to chapter",
"View footnotes"
};
const int startY = 100; const int startY = 100;
const int itemHeight = 40; const int itemHeight = 40;

View File

@ -12,13 +12,10 @@
#include "Screen.h" #include "Screen.h"
class EpubReaderMenuScreen final : public Screen { class EpubReaderMenuScreen final : public Screen {
public: public:
enum MenuOption { enum MenuOption { CHAPTERS, FOOTNOTES };
CHAPTERS,
FOOTNOTES
};
private: private:
TaskHandle_t displayTaskHandle = nullptr; TaskHandle_t displayTaskHandle = nullptr;
SemaphoreHandle_t renderingMutex = nullptr; SemaphoreHandle_t renderingMutex = nullptr;
int selectorIndex = 0; int selectorIndex = 0;
@ -30,13 +27,11 @@ private:
[[noreturn]] void displayTaskLoop(); [[noreturn]] void displayTaskLoop();
void renderScreen(); void renderScreen();
public: public:
explicit EpubReaderMenuScreen(GfxRenderer& renderer, InputManager& inputManager, explicit EpubReaderMenuScreen(GfxRenderer& renderer, InputManager& inputManager,
const std::function<void()>& onGoBack, const std::function<void()>& onGoBack,
const std::function<void(MenuOption option)>& onSelectOption) const std::function<void(MenuOption option)>& onSelectOption)
: Screen(renderer, inputManager), : Screen(renderer, inputManager), onGoBack(onGoBack), onSelectOption(onSelectOption) {}
onGoBack(onGoBack),
onSelectOption(onSelectOption) {}
void onEnter() override; void onEnter() override;
void onExit() override; void onExit() override;

View File

@ -1,6 +1,5 @@
#include "EpubReaderScreen.h" #include "EpubReaderScreen.h"
#include "EpubReaderFootnotesScreen.h"
#include <Epub/Page.h> #include <Epub/Page.h>
#include <GfxRenderer.h> #include <GfxRenderer.h>
#include <SD.h> #include <SD.h>
@ -8,6 +7,7 @@
#include "Battery.h" #include "Battery.h"
#include "CrossPointSettings.h" #include "CrossPointSettings.h"
#include "EpubReaderChapterSelectionScreen.h" #include "EpubReaderChapterSelectionScreen.h"
#include "EpubReaderFootnotesScreen.h"
#include "EpubReaderMenuScreen.h" #include "EpubReaderMenuScreen.h"
#include "config.h" #include "config.h"
@ -46,11 +46,8 @@ void EpubReaderScreen::onEnter() {
updateRequired = true; updateRequired = true;
xTaskCreate(&EpubReaderScreen::taskTrampoline, "EpubReaderScreenTask", xTaskCreate(&EpubReaderScreen::taskTrampoline, "EpubReaderScreenTask",
24576, //32768 24576, // 32768
this, this, 1, &displayTaskHandle);
1,
&displayTaskHandle
);
} }
void EpubReaderScreen::onExit() { void EpubReaderScreen::onExit() {
@ -126,8 +123,7 @@ void EpubReaderScreen::handleInput() {
subScreen->onExit(); subScreen->onExit();
subScreen.reset(new EpubReaderFootnotesScreen( subScreen.reset(new EpubReaderFootnotesScreen(
this->renderer, this->renderer, this->inputManager,
this->inputManager,
currentPageFootnotes, // Pass collected footnotes (reference) currentPageFootnotes, // Pass collected footnotes (reference)
[this] { [this] {
// onGoBack from footnotes // onGoBack from footnotes
@ -460,8 +456,7 @@ void EpubReaderScreen::navigateToHref(const char* href, bool savePosition) {
savedSpineIndex = currentSpineIndex; savedSpineIndex = currentSpineIndex;
savedPageNumber = section->currentPage; savedPageNumber = section->currentPage;
isViewingFootnote = true; isViewingFootnote = true;
Serial.printf("[%lu] [ERS] Saved position: spine %d, page %d\n", Serial.printf("[%lu] [ERS] Saved position: spine %d, page %d\n", millis(), savedSpineIndex, savedPageNumber);
millis(), savedSpineIndex, savedPageNumber);
} }
// Parse href: "filename.html#anchor" // Parse href: "filename.html#anchor"
@ -483,8 +478,7 @@ void EpubReaderScreen::navigateToHref(const char* href, bool savePosition) {
filename = filename.substr(lastSlash + 1); filename = filename.substr(lastSlash + 1);
} }
Serial.printf("[%lu] [ERS] Navigate to: %s (anchor: %s)\n", Serial.printf("[%lu] [ERS] Navigate to: %s (anchor: %s)\n", millis(), filename.c_str(), anchor.c_str());
millis(), filename.c_str(), anchor.c_str());
int targetSpineIndex = -1; int targetSpineIndex = -1;
@ -492,23 +486,20 @@ void EpubReaderScreen::navigateToHref(const char* href, bool savePosition) {
if (!anchor.empty()) { if (!anchor.empty()) {
// Try inline footnote first // Try inline footnote first
std::string inlineFilename = "inline_" + anchor + ".html"; std::string inlineFilename = "inline_" + anchor + ".html";
Serial.printf("[%lu] [ERS] Looking for inline footnote: %s\n", Serial.printf("[%lu] [ERS] Looking for inline footnote: %s\n", millis(), inlineFilename.c_str());
millis(), inlineFilename.c_str());
targetSpineIndex = epub->findVirtualSpineIndex(inlineFilename); targetSpineIndex = epub->findVirtualSpineIndex(inlineFilename);
// If not found, try paragraph note // If not found, try paragraph note
if (targetSpineIndex == -1) { if (targetSpineIndex == -1) {
std::string pnoteFilename = "pnote_" + anchor + ".html"; std::string pnoteFilename = "pnote_" + anchor + ".html";
Serial.printf("[%lu] [ERS] Looking for paragraph note: %s\n", Serial.printf("[%lu] [ERS] Looking for paragraph note: %s\n", millis(), pnoteFilename.c_str());
millis(), pnoteFilename.c_str());
targetSpineIndex = epub->findVirtualSpineIndex(pnoteFilename); targetSpineIndex = epub->findVirtualSpineIndex(pnoteFilename);
} }
if (targetSpineIndex != -1) { if (targetSpineIndex != -1) {
Serial.printf("[%lu] [ERS] Found note at virtual index: %d\n", Serial.printf("[%lu] [ERS] Found note at virtual index: %d\n", millis(), targetSpineIndex);
millis(), targetSpineIndex);
// Navigate to the note // Navigate to the note
xSemaphoreTake(renderingMutex, portMAX_DELAY); xSemaphoreTake(renderingMutex, portMAX_DELAY);
@ -520,8 +511,7 @@ void EpubReaderScreen::navigateToHref(const char* href, bool savePosition) {
updateRequired = true; updateRequired = true;
return; return;
} else { } else {
Serial.printf("[%lu] [ERS] No virtual note found, trying normal navigation\n", Serial.printf("[%lu] [ERS] No virtual note found, trying normal navigation\n", millis());
millis());
} }
} }
@ -531,9 +521,7 @@ void EpubReaderScreen::navigateToHref(const char* href, bool savePosition) {
std::string spineItem = epub->getSpineItem(i); std::string spineItem = epub->getSpineItem(i);
size_t lastSlash = spineItem.find_last_of('/'); size_t lastSlash = spineItem.find_last_of('/');
std::string spineFilename = (lastSlash != std::string::npos) std::string spineFilename = (lastSlash != std::string::npos) ? spineItem.substr(lastSlash + 1) : spineItem;
? spineItem.substr(lastSlash + 1)
: spineItem;
if (spineFilename == filename) { if (spineFilename == filename) {
targetSpineIndex = i; targetSpineIndex = i;
@ -542,8 +530,7 @@ void EpubReaderScreen::navigateToHref(const char* href, bool savePosition) {
} }
if (targetSpineIndex == -1) { if (targetSpineIndex == -1) {
Serial.printf("[%lu] [ERS] Could not find spine index for: %s\n", Serial.printf("[%lu] [ERS] Could not find spine index for: %s\n", millis(), filename.c_str());
millis(), filename.c_str());
return; return;
} }
@ -556,15 +543,13 @@ void EpubReaderScreen::navigateToHref(const char* href, bool savePosition) {
updateRequired = true; updateRequired = true;
Serial.printf("[%lu] [ERS] Navigated to spine index: %d\n", Serial.printf("[%lu] [ERS] Navigated to spine index: %d\n", millis(), targetSpineIndex);
millis(), targetSpineIndex);
} }
// Method to restore saved position // Method to restore saved position
void EpubReaderScreen::restoreSavedPosition() { void EpubReaderScreen::restoreSavedPosition() {
if (savedSpineIndex >= 0 && savedPageNumber >= 0) { if (savedSpineIndex >= 0 && savedPageNumber >= 0) {
Serial.printf("[%lu] [ERS] Restoring position: spine %d, page %d\n", Serial.printf("[%lu] [ERS] Restoring position: spine %d, page %d\n", millis(), savedSpineIndex, savedPageNumber);
millis(), savedSpineIndex, savedPageNumber);
xSemaphoreTake(renderingMutex, portMAX_DELAY); xSemaphoreTake(renderingMutex, portMAX_DELAY);
currentSpineIndex = savedSpineIndex; currentSpineIndex = savedSpineIndex;

View File

@ -4,8 +4,8 @@
#include <freertos/FreeRTOS.h> #include <freertos/FreeRTOS.h>
#include <freertos/semphr.h> #include <freertos/semphr.h>
#include <freertos/task.h> #include <freertos/task.h>
#include "EpubReaderFootnotesScreen.h"
#include "EpubReaderFootnotesScreen.h"
#include "Screen.h" #include "Screen.h"
class EpubReaderScreen final : public Screen { class EpubReaderScreen final : public Screen {
@ -35,7 +35,7 @@ class EpubReaderScreen final : public Screen {
void navigateToHref(const char* href, bool savePosition = false); void navigateToHref(const char* href, bool savePosition = false);
void restoreSavedPosition(); void restoreSavedPosition();
public: public:
explicit EpubReaderScreen(GfxRenderer& renderer, InputManager& inputManager, std::unique_ptr<Epub> epub, explicit EpubReaderScreen(GfxRenderer& renderer, InputManager& inputManager, std::unique_ptr<Epub> epub,
const std::function<void()>& onGoBack) const std::function<void()>& onGoBack)
: Screen(renderer, inputManager), epub(std::move(epub)), onGoBack(onGoBack) {} : Screen(renderer, inputManager), epub(std::move(epub)), onGoBack(onGoBack) {}