This commit is contained in:
Dave Allie
2025-12-30 23:18:51 +11:00
parent 3abcd0d05d
commit 52a0b5bbe9
5 changed files with 37 additions and 24 deletions

View File

@@ -2,8 +2,7 @@
#include <Utf8.h> #include <Utf8.h>
inline int min(const int a, const int b) { return a < b ? a : b; } #include <algorithm>
inline int max(const int a, const int b) { return a < b ? b : a; }
void EpdFont::getTextBounds(const char* string, const int startX, const int startY, int* minX, int* minY, int* maxX, void EpdFont::getTextBounds(const char* string, const int startX, const int startY, int* minX, int* minY, int* maxX,
int* maxY) const { int* maxY) const {
@@ -32,10 +31,10 @@ void EpdFont::getTextBounds(const char* string, const int startX, const int star
continue; continue;
} }
*minX = min(*minX, cursorX + glyph->left); *minX = std::min(*minX, cursorX + glyph->left);
*maxX = max(*maxX, cursorX + glyph->left + glyph->width); *maxX = std::max(*maxX, cursorX + glyph->left + glyph->width);
*minY = min(*minY, cursorY + glyph->top - glyph->height); *minY = std::min(*minY, cursorY + glyph->top - glyph->height);
*maxY = max(*maxY, cursorY + glyph->top); *maxY = std::max(*maxY, cursorY + glyph->top);
cursorX += glyph->advanceX; cursorX += glyph->advanceX;
} }
} }

View File

@@ -109,17 +109,20 @@ bool Epub::parseTocNcxFile() const {
if (!ncxParser.setup()) { if (!ncxParser.setup()) {
Serial.printf("[%lu] [EBP] Could not setup toc ncx parser\n", millis()); Serial.printf("[%lu] [EBP] Could not setup toc ncx parser\n", millis());
tempNcxFile.close();
return false; return false;
} }
const auto ncxBuffer = static_cast<uint8_t*>(malloc(1024)); const auto ncxBuffer = static_cast<uint8_t*>(malloc(1024));
if (!ncxBuffer) { if (!ncxBuffer) {
Serial.printf("[%lu] [EBP] Could not allocate memory for toc ncx parser\n", millis()); Serial.printf("[%lu] [EBP] Could not allocate memory for toc ncx parser\n", millis());
tempNcxFile.close();
return false; return false;
} }
while (tempNcxFile.available()) { while (tempNcxFile.available()) {
const auto readSize = tempNcxFile.read(ncxBuffer, 1024); const auto readSize = tempNcxFile.read(ncxBuffer, 1024);
if (readSize == 0) break;
const auto processedSize = ncxParser.write(ncxBuffer, readSize); const auto processedSize = ncxParser.write(ncxBuffer, readSize);
if (processedSize != readSize) { if (processedSize != readSize) {

View File

@@ -57,7 +57,6 @@ void ChapterHtmlSlimParser::startNewTextBlock(const TextBlock::BLOCK_STYLE style
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) {
auto* self = static_cast<ChapterHtmlSlimParser*>(userData); auto* self = static_cast<ChapterHtmlSlimParser*>(userData);
(void)atts;
// Middle of skip // Middle of skip
if (self->skipUntilDepth < self->depth) { if (self->skipUntilDepth < self->depth) {
@@ -93,7 +92,7 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
if (matches(name, HEADER_TAGS, NUM_HEADER_TAGS)) { if (matches(name, HEADER_TAGS, NUM_HEADER_TAGS)) {
self->startNewTextBlock(TextBlock::CENTER_ALIGN); self->startNewTextBlock(TextBlock::CENTER_ALIGN);
self->boldUntilDepth = min(self->boldUntilDepth, self->depth); self->boldUntilDepth = std::min(self->boldUntilDepth, self->depth);
} else if (matches(name, BLOCK_TAGS, NUM_BLOCK_TAGS)) { } else if (matches(name, BLOCK_TAGS, NUM_BLOCK_TAGS)) {
if (strcmp(name, "br") == 0) { if (strcmp(name, "br") == 0) {
self->startNewTextBlock(self->currentTextBlock->getStyle()); self->startNewTextBlock(self->currentTextBlock->getStyle());
@@ -101,9 +100,9 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
self->startNewTextBlock(TextBlock::JUSTIFIED); self->startNewTextBlock(TextBlock::JUSTIFIED);
} }
} else if (matches(name, BOLD_TAGS, NUM_BOLD_TAGS)) { } else if (matches(name, BOLD_TAGS, NUM_BOLD_TAGS)) {
self->boldUntilDepth = min(self->boldUntilDepth, self->depth); self->boldUntilDepth = std::min(self->boldUntilDepth, self->depth);
} else if (matches(name, ITALIC_TAGS, NUM_ITALIC_TAGS)) { } else if (matches(name, ITALIC_TAGS, NUM_ITALIC_TAGS)) {
self->italicUntilDepth = min(self->italicUntilDepth, self->depth); self->italicUntilDepth = std::min(self->italicUntilDepth, self->depth);
} }
self->depth += 1; self->depth += 1;
@@ -162,7 +161,6 @@ 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);
(void)name;
if (self->partWordBufferIndex > 0) { if (self->partWordBufferIndex > 0) {
// Only flush out part word buffer if we're closing a block tag or are at the top of the HTML file. // Only flush out part word buffer if we're closing a block tag or are at the top of the HTML file.
@@ -245,9 +243,9 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() {
return false; return false;
} }
const size_t len = file.read(static_cast<uint8_t*>(buf), 1024); const size_t len = file.read(buf, 1024);
if (len == 0) { if (len == 0 && file.available() > 0) {
Serial.printf("[%lu] [EHP] File read error\n", millis()); Serial.printf("[%lu] [EHP] File read error\n", millis());
XML_StopParser(parser, XML_FALSE); // Stop any pending processing XML_StopParser(parser, XML_FALSE); // Stop any pending processing
XML_SetElementHandler(parser, nullptr, nullptr); // Clear callbacks XML_SetElementHandler(parser, nullptr, nullptr); // Clear callbacks

View File

@@ -37,7 +37,7 @@ class GfxRenderer {
public: public:
explicit GfxRenderer(EInkDisplay& einkDisplay) : einkDisplay(einkDisplay), renderMode(BW), orientation(Portrait) {} explicit GfxRenderer(EInkDisplay& einkDisplay) : einkDisplay(einkDisplay), renderMode(BW), orientation(Portrait) {}
~GfxRenderer() = default; ~GfxRenderer() { freeBwBufferChunks(); }
static constexpr int VIEWABLE_MARGIN_TOP = 9; static constexpr int VIEWABLE_MARGIN_TOP = 9;
static constexpr int VIEWABLE_MARGIN_RIGHT = 3; static constexpr int VIEWABLE_MARGIN_RIGHT = 3;

View File

@@ -50,6 +50,14 @@ void CrossPointWebServer::begin() {
Serial.printf("[%lu] [WEB] Creating web server on port %d...\n", millis(), port); Serial.printf("[%lu] [WEB] Creating web server on port %d...\n", millis(), port);
server.reset(new WebServer(port)); server.reset(new WebServer(port));
// Disable WiFi sleep to improve responsiveness and prevent 'unreachable' errors.
// This is critical for reliable web server operation on ESP32.
WiFi.setSleep(false);
// Note: WebServer class doesn't have setNoDelay() in the standard ESP32 library.
// We rely on disabling WiFi sleep for responsiveness.
Serial.printf("[%lu] [WEB] [MEM] Free heap after WebServer allocation: %d bytes\n", millis(), ESP.getFreeHeap()); Serial.printf("[%lu] [WEB] [MEM] Free heap after WebServer allocation: %d bytes\n", millis(), ESP.getFreeHeap());
if (!server) { if (!server) {
@@ -157,15 +165,16 @@ void CrossPointWebServer::handleStatus() const {
// Get correct IP based on AP vs STA mode // Get correct IP based on AP vs STA mode
const String ipAddr = apMode ? WiFi.softAPIP().toString() : WiFi.localIP().toString(); const String ipAddr = apMode ? WiFi.softAPIP().toString() : WiFi.localIP().toString();
String json = "{"; JsonDocument doc;
json += "\"version\":\"" + String(CROSSPOINT_VERSION) + "\","; doc["version"] = CROSSPOINT_VERSION;
json += "\"ip\":\"" + ipAddr + "\","; doc["ip"] = ipAddr;
json += "\"mode\":\"" + String(apMode ? "AP" : "STA") + "\","; doc["mode"] = apMode ? "AP" : "STA";
json += "\"rssi\":" + String(apMode ? 0 : WiFi.RSSI()) + ","; // RSSI not applicable in AP mode doc["rssi"] = apMode ? 0 : WiFi.RSSI();
json += "\"freeHeap\":" + String(ESP.getFreeHeap()) + ","; doc["freeHeap"] = ESP.getFreeHeap();
json += "\"uptime\":" + String(millis() / 1000); doc["uptime"] = millis() / 1000;
json += "}";
String json;
serializeJson(doc, json);
server->send(200, "application/json", json); server->send(200, "application/json", json);
} }
@@ -220,6 +229,7 @@ void CrossPointWebServer::scanFiles(const char* path, const std::function<void(F
} }
file.close(); file.close();
yield(); // Yield to allow WiFi and other tasks to process during long scans
file = root.openNextFile(); file = root.openNextFile();
} }
root.close(); root.close();
@@ -254,12 +264,15 @@ void CrossPointWebServer::handleFileListData() const {
char output[512]; char output[512];
constexpr size_t outputSize = sizeof(output); constexpr size_t outputSize = sizeof(output);
bool seenFirst = false; bool seenFirst = false;
scanFiles(currentPath.c_str(), [this, &output, seenFirst](const FileInfo& info) mutable { JsonDocument doc;
JsonDocument doc;
scanFiles(currentPath.c_str(), [this, &output, &doc, seenFirst](const FileInfo& info) mutable {
doc.clear();
doc["name"] = info.name; doc["name"] = info.name;
doc["size"] = info.size; doc["size"] = info.size;
doc["isDirectory"] = info.isDirectory; doc["isDirectory"] = info.isDirectory;
doc["isEpub"] = info.isEpub; doc["isEpub"] = info.isEpub;
const size_t written = serializeJson(doc, output, outputSize); const size_t written = serializeJson(doc, output, outputSize);
if (written >= outputSize) { if (written >= outputSize) {
// JSON output truncated; skip this entry to avoid sending malformed JSON // JSON output truncated; skip this entry to avoid sending malformed JSON