fix: prevent Serial.printf from blocking when USB disconnected
All checks were successful
CI / build (push) Successful in 2m23s
All checks were successful
CI / build (push) Successful in 2m23s
On ESP32-C3 with USB CDC, Serial.printf() blocks indefinitely when USB is not connected. This caused device freezes when booted without USB. Solution: Call Serial.setTxTimeoutMs(0) after Serial.begin() to make all Serial output non-blocking. Also added if (Serial) guards to high-traffic logging paths in EpubReaderActivity as belt-and-suspenders protection. Includes documentation of the debugging process and Serial call inventory. Also applies clang-format to fix pre-existing formatting issues.
This commit is contained in:
@@ -251,8 +251,8 @@ bool Epub::parseCssFiles() {
|
||||
SdMan.remove(tmpCssPath.c_str());
|
||||
}
|
||||
|
||||
Serial.printf("[%lu] [EBP] Loaded %zu CSS style rules from %zu files (~%zu bytes)\n", millis(), cssParser->ruleCount(),
|
||||
cssFiles.size(), cssParser->estimateMemoryUsage());
|
||||
Serial.printf("[%lu] [EBP] Loaded %zu CSS style rules from %zu files (~%zu bytes)\n", millis(),
|
||||
cssParser->ruleCount(), cssFiles.size(), cssParser->estimateMemoryUsage());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -757,8 +757,8 @@ bool Epub::generateAllCovers(const std::function<void(int)>& progressCallback) c
|
||||
SdMan.openFileForWrite("EBP", getCoverBmpPath(false), coverBmp)) {
|
||||
const int targetWidth = 480;
|
||||
const int targetHeight = (480 * jpegHeight) / jpegWidth;
|
||||
const bool success =
|
||||
JpegToBmpConverter::jpegFileToBmpStreamWithSize(coverJpg, coverBmp, targetWidth, targetHeight, makeSubProgress(50, 75));
|
||||
const bool success = JpegToBmpConverter::jpegFileToBmpStreamWithSize(coverJpg, coverBmp, targetWidth,
|
||||
targetHeight, makeSubProgress(50, 75));
|
||||
coverJpg.close();
|
||||
coverBmp.close();
|
||||
if (!success) {
|
||||
@@ -776,8 +776,8 @@ bool Epub::generateAllCovers(const std::function<void(int)>& progressCallback) c
|
||||
SdMan.openFileForWrite("EBP", getCoverBmpPath(true), coverBmp)) {
|
||||
const int targetHeight = 800;
|
||||
const int targetWidth = (800 * jpegWidth) / jpegHeight;
|
||||
const bool success =
|
||||
JpegToBmpConverter::jpegFileToBmpStreamWithSize(coverJpg, coverBmp, targetWidth, targetHeight, makeSubProgress(75, 100));
|
||||
const bool success = JpegToBmpConverter::jpegFileToBmpStreamWithSize(coverJpg, coverBmp, targetWidth,
|
||||
targetHeight, makeSubProgress(75, 100));
|
||||
coverJpg.close();
|
||||
coverBmp.close();
|
||||
if (!success) {
|
||||
|
||||
@@ -57,12 +57,12 @@ class Page {
|
||||
public:
|
||||
// the list of block index and line numbers on this page
|
||||
std::vector<std::shared_ptr<PageElement>> elements;
|
||||
|
||||
|
||||
// Byte offset in source HTML where this page's content begins
|
||||
// Used for restoring reading position after re-indexing due to font/setting changes
|
||||
// This is stored in the Section file's LUT, not in Page serialization
|
||||
uint32_t firstContentOffset = 0;
|
||||
|
||||
|
||||
void render(GfxRenderer& renderer, int fontId, int xOffset, int yOffset) const;
|
||||
bool serialize(FsFile& file) const;
|
||||
static std::unique_ptr<Page> deserialize(FsFile& file);
|
||||
|
||||
@@ -186,7 +186,7 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c
|
||||
}
|
||||
writeSectionFileHeader(fontId, lineCompression, extraParagraphSpacing, paragraphAlignment, viewportWidth,
|
||||
viewportHeight, hyphenationEnabled);
|
||||
|
||||
|
||||
// LUT entries: { filePosition, contentOffset } pairs
|
||||
struct LutEntry {
|
||||
uint32_t filePos;
|
||||
@@ -202,8 +202,8 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c
|
||||
const uint32_t contentOffset = page->firstContentOffset;
|
||||
const uint32_t filePos = this->onPageComplete(std::move(page));
|
||||
lut.push_back({filePos, contentOffset});
|
||||
}, progressFn,
|
||||
epub->getCssParser());
|
||||
},
|
||||
progressFn, epub->getCssParser());
|
||||
Hyphenator::setPreferredLanguage(epub->getLanguage());
|
||||
success = visitor.parseAndBuildPages();
|
||||
|
||||
@@ -217,7 +217,7 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c
|
||||
// Add placeholder to LUT
|
||||
const uint32_t filePos = this->onPageComplete(std::move(placeholderPage));
|
||||
lut.push_back({filePos, 0});
|
||||
|
||||
|
||||
// If we still have no pages, the placeholder creation failed
|
||||
if (pageCount == 0) {
|
||||
Serial.printf("[%lu] [SCT] Failed to create placeholder page\n", millis());
|
||||
@@ -262,13 +262,13 @@ std::unique_ptr<Page> Section::loadPageFromSectionFile() {
|
||||
file.seek(HEADER_SIZE - sizeof(uint32_t));
|
||||
uint32_t lutOffset;
|
||||
serialization::readPod(file, lutOffset);
|
||||
|
||||
|
||||
// LUT entries are now 8 bytes each: { filePos (4), contentOffset (4) }
|
||||
file.seek(lutOffset + LUT_ENTRY_SIZE * currentPage);
|
||||
uint32_t pagePos;
|
||||
serialization::readPod(file, pagePos);
|
||||
// Skip contentOffset for now - we don't need it when just loading the page
|
||||
|
||||
|
||||
file.seek(pagePos);
|
||||
|
||||
auto page = Page::deserialize(file);
|
||||
@@ -300,15 +300,15 @@ int Section::findPageForContentOffset(uint32_t targetOffset) const {
|
||||
|
||||
while (left <= right) {
|
||||
const int mid = left + (right - left) / 2;
|
||||
|
||||
|
||||
// Read content offset for page 'mid'
|
||||
// LUT entry format: { filePos (4), contentOffset (4) }
|
||||
f.seek(lutOffset + LUT_ENTRY_SIZE * mid + sizeof(uint32_t)); // Skip filePos
|
||||
uint32_t midOffset;
|
||||
serialization::readPod(f, midOffset);
|
||||
|
||||
|
||||
if (midOffset <= targetOffset) {
|
||||
result = mid; // This page could be the answer
|
||||
result = mid; // This page could be the answer
|
||||
left = mid + 1; // Look for a later page that might also qualify
|
||||
} else {
|
||||
right = mid - 1; // Look for an earlier page
|
||||
@@ -322,7 +322,7 @@ int Section::findPageForContentOffset(uint32_t targetOffset) const {
|
||||
f.seek(lutOffset + LUT_ENTRY_SIZE * result + sizeof(uint32_t));
|
||||
uint32_t resultOffset;
|
||||
serialization::readPod(f, resultOffset);
|
||||
|
||||
|
||||
while (result > 0) {
|
||||
f.seek(lutOffset + LUT_ENTRY_SIZE * (result - 1) + sizeof(uint32_t));
|
||||
uint32_t prevOffset;
|
||||
|
||||
@@ -36,7 +36,7 @@ class Section {
|
||||
const std::function<void()>& progressSetupFn = nullptr,
|
||||
const std::function<void(int)>& progressFn = nullptr);
|
||||
std::unique_ptr<Page> loadPageFromSectionFile();
|
||||
|
||||
|
||||
// Methods for content offset-based position tracking
|
||||
// Used to restore reading position after re-indexing due to font/setting changes
|
||||
int findPageForContentOffset(uint32_t targetOffset) const;
|
||||
|
||||
@@ -83,7 +83,7 @@ void ChapterHtmlSlimParser::updateEffectiveInlineStyle() {
|
||||
// Flush the contents of partWordBuffer to currentTextBlock
|
||||
void ChapterHtmlSlimParser::flushPartWordBuffer() {
|
||||
if (partWordBufferIndex == 0) return;
|
||||
|
||||
|
||||
// Determine font style using effective styles
|
||||
EpdFontFamily::Style fontStyle = EpdFontFamily::REGULAR;
|
||||
if (effectiveBold && effectiveItalic) {
|
||||
@@ -93,7 +93,7 @@ void ChapterHtmlSlimParser::flushPartWordBuffer() {
|
||||
} else if (effectiveItalic) {
|
||||
fontStyle = EpdFontFamily::ITALIC;
|
||||
}
|
||||
|
||||
|
||||
// Flush the buffer
|
||||
partWordBuffer[partWordBufferIndex] = '\0';
|
||||
currentTextBlock->addWord(partWordBuffer, fontStyle, effectiveUnderline);
|
||||
@@ -290,7 +290,7 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
|
||||
} else {
|
||||
placeholder = "[Image unavailable]";
|
||||
}
|
||||
|
||||
|
||||
self->startNewTextBlock(TextBlock::CENTER_ALIGN);
|
||||
self->italicUntilDepth = std::min(self->italicUntilDepth, self->depth);
|
||||
self->depth += 1;
|
||||
@@ -478,7 +478,7 @@ void XMLCALL ChapterHtmlSlimParser::characterData(void* userData, const XML_Char
|
||||
if (self->skipUntilDepth < self->depth) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Capture byte offset of this character data for page position tracking
|
||||
if (self->xmlParser) {
|
||||
self->lastCharDataOffset = XML_GetCurrentByteIndex(self->xmlParser);
|
||||
@@ -647,7 +647,7 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() {
|
||||
const size_t totalSize = file.size();
|
||||
size_t bytesRead = 0;
|
||||
int lastProgress = -1;
|
||||
|
||||
|
||||
// Initialize offset tracking - first page starts at offset 0
|
||||
currentPageStartOffset = 0;
|
||||
lastCharDataOffset = 0;
|
||||
@@ -739,7 +739,7 @@ void ChapterHtmlSlimParser::addLineToPage(std::shared_ptr<TextBlock> line) {
|
||||
currentPage->firstContentOffset = static_cast<uint32_t>(currentPageStartOffset);
|
||||
}
|
||||
completePageFn(std::move(currentPage));
|
||||
|
||||
|
||||
// Start new page - offset will be set when first content is added
|
||||
currentPage.reset(new Page());
|
||||
currentPageStartOffset = lastCharDataOffset; // Use offset from when content was parsed
|
||||
|
||||
@@ -58,11 +58,11 @@ class ChapterHtmlSlimParser {
|
||||
bool effectiveBold = false;
|
||||
bool effectiveItalic = false;
|
||||
bool effectiveUnderline = false;
|
||||
|
||||
|
||||
// Byte offset tracking for position restoration after re-indexing
|
||||
XML_Parser xmlParser = nullptr; // Store parser for getting current byte index
|
||||
size_t currentPageStartOffset = 0; // Byte offset when current page was started
|
||||
size_t lastCharDataOffset = 0; // Byte offset of last character data (captured during parsing)
|
||||
XML_Parser xmlParser = nullptr; // Store parser for getting current byte index
|
||||
size_t currentPageStartOffset = 0; // Byte offset when current page was started
|
||||
size_t lastCharDataOffset = 0; // Byte offset of last character data (captured during parsing)
|
||||
|
||||
void updateEffectiveInlineStyle();
|
||||
void startNewTextBlock(TextBlock::Style style);
|
||||
|
||||
@@ -5,13 +5,9 @@
|
||||
// Global high contrast mode flag
|
||||
static bool g_highContrastMode = false;
|
||||
|
||||
void setHighContrastMode(bool enabled) {
|
||||
g_highContrastMode = enabled;
|
||||
}
|
||||
void setHighContrastMode(bool enabled) { g_highContrastMode = enabled; }
|
||||
|
||||
bool isHighContrastMode() {
|
||||
return g_highContrastMode;
|
||||
}
|
||||
bool isHighContrastMode() { return g_highContrastMode; }
|
||||
|
||||
// Brightness/Contrast adjustments:
|
||||
constexpr bool USE_BRIGHTNESS = false; // true: apply brightness/gamma adjustments
|
||||
|
||||
@@ -327,7 +327,8 @@ void GfxRenderer::drawBitmap(const Bitmap& bitmap, const int x, const int y, con
|
||||
// Calculate screen Y position
|
||||
const int screenYStart = y + static_cast<int>(std::floor(logicalY * scale));
|
||||
// For upscaling, calculate the end position for this source row
|
||||
const int screenYEnd = isUpscaling ? (y + static_cast<int>(std::floor((logicalY + 1) * scale))) : (screenYStart + 1);
|
||||
const int screenYEnd =
|
||||
isUpscaling ? (y + static_cast<int>(std::floor((logicalY + 1) * scale))) : (screenYStart + 1);
|
||||
|
||||
// Draw to all Y positions this source row maps to (for upscaling, this fills gaps)
|
||||
for (int screenY = screenYStart; screenY < screenYEnd; screenY++) {
|
||||
@@ -340,7 +341,8 @@ void GfxRenderer::drawBitmap(const Bitmap& bitmap, const int x, const int y, con
|
||||
// Calculate screen X position
|
||||
const int screenXStart = x + static_cast<int>(std::floor(srcX * scale));
|
||||
// For upscaling, calculate the end position for this source pixel
|
||||
const int screenXEnd = isUpscaling ? (x + static_cast<int>(std::floor((srcX + 1) * scale))) : (screenXStart + 1);
|
||||
const int screenXEnd =
|
||||
isUpscaling ? (x + static_cast<int>(std::floor((srcX + 1) * scale))) : (screenXStart + 1);
|
||||
|
||||
const uint8_t val = outputRow[bmpX / 4] >> (6 - ((bmpX * 2) % 8)) & 0x3;
|
||||
|
||||
@@ -409,7 +411,8 @@ void GfxRenderer::drawBitmap1Bit(const Bitmap& bitmap, const int x, const int y,
|
||||
// Calculate screen Y position
|
||||
const int screenYStart = y + static_cast<int>(std::floor(logicalY * scale));
|
||||
// For upscaling, calculate the end position for this source row
|
||||
const int screenYEnd = isUpscaling ? (y + static_cast<int>(std::floor((logicalY + 1) * scale))) : (screenYStart + 1);
|
||||
const int screenYEnd =
|
||||
isUpscaling ? (y + static_cast<int>(std::floor((logicalY + 1) * scale))) : (screenYStart + 1);
|
||||
|
||||
// Draw to all Y positions this source row maps to (for upscaling, this fills gaps)
|
||||
for (int screenY = screenYStart; screenY < screenYEnd; screenY++) {
|
||||
@@ -420,7 +423,8 @@ void GfxRenderer::drawBitmap1Bit(const Bitmap& bitmap, const int x, const int y,
|
||||
// Calculate screen X position
|
||||
const int screenXStart = x + static_cast<int>(std::floor(bmpX * scale));
|
||||
// For upscaling, calculate the end position for this source pixel
|
||||
const int screenXEnd = isUpscaling ? (x + static_cast<int>(std::floor((bmpX + 1) * scale))) : (screenXStart + 1);
|
||||
const int screenXEnd =
|
||||
isUpscaling ? (x + static_cast<int>(std::floor((bmpX + 1) * scale))) : (screenXStart + 1);
|
||||
|
||||
// Get 2-bit value (result of readNextRow quantization)
|
||||
const uint8_t val = outputRow[bmpX / 4] >> (6 - ((bmpX * 2) % 8)) & 0x3;
|
||||
@@ -998,26 +1002,38 @@ int mapPhysicalToLogicalEdge(int bezelEdge, GfxRenderer::Orientation orientation
|
||||
return bezelEdge;
|
||||
case GfxRenderer::LandscapeClockwise:
|
||||
switch (bezelEdge) {
|
||||
case 0: return 2; // Physical bottom -> logical left
|
||||
case 1: return 3; // Physical top -> logical right
|
||||
case 2: return 1; // Physical left -> logical top
|
||||
case 3: return 0; // Physical right -> logical bottom
|
||||
case 0:
|
||||
return 2; // Physical bottom -> logical left
|
||||
case 1:
|
||||
return 3; // Physical top -> logical right
|
||||
case 2:
|
||||
return 1; // Physical left -> logical top
|
||||
case 3:
|
||||
return 0; // Physical right -> logical bottom
|
||||
}
|
||||
break;
|
||||
case GfxRenderer::PortraitInverted:
|
||||
switch (bezelEdge) {
|
||||
case 0: return 1; // Physical bottom -> logical top
|
||||
case 1: return 0; // Physical top -> logical bottom
|
||||
case 2: return 3; // Physical left -> logical right
|
||||
case 3: return 2; // Physical right -> logical left
|
||||
case 0:
|
||||
return 1; // Physical bottom -> logical top
|
||||
case 1:
|
||||
return 0; // Physical top -> logical bottom
|
||||
case 2:
|
||||
return 3; // Physical left -> logical right
|
||||
case 3:
|
||||
return 2; // Physical right -> logical left
|
||||
}
|
||||
break;
|
||||
case GfxRenderer::LandscapeCounterClockwise:
|
||||
switch (bezelEdge) {
|
||||
case 0: return 3; // Physical bottom -> logical right
|
||||
case 1: return 2; // Physical top -> logical left
|
||||
case 2: return 0; // Physical left -> logical bottom
|
||||
case 3: return 1; // Physical right -> logical top
|
||||
case 0:
|
||||
return 3; // Physical bottom -> logical right
|
||||
case 1:
|
||||
return 2; // Physical top -> logical left
|
||||
case 2:
|
||||
return 0; // Physical left -> logical bottom
|
||||
case 3:
|
||||
return 1; // Physical right -> logical top
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1074,23 +1090,34 @@ void GfxRenderer::getOrientedViewableTRBL(int* outTop, int* outRight, int* outBo
|
||||
*outLeft = getViewableMarginLeft();
|
||||
break;
|
||||
case LandscapeClockwise:
|
||||
*outTop = BASE_VIEWABLE_MARGIN_LEFT + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 1 ? bezelCompensation : 0);
|
||||
*outRight = BASE_VIEWABLE_MARGIN_TOP + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 3 ? bezelCompensation : 0);
|
||||
*outBottom = BASE_VIEWABLE_MARGIN_RIGHT + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 0 ? bezelCompensation : 0);
|
||||
*outLeft = BASE_VIEWABLE_MARGIN_BOTTOM + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 2 ? bezelCompensation : 0);
|
||||
*outTop =
|
||||
BASE_VIEWABLE_MARGIN_LEFT + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 1 ? bezelCompensation : 0);
|
||||
*outRight =
|
||||
BASE_VIEWABLE_MARGIN_TOP + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 3 ? bezelCompensation : 0);
|
||||
*outBottom =
|
||||
BASE_VIEWABLE_MARGIN_RIGHT + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 0 ? bezelCompensation : 0);
|
||||
*outLeft =
|
||||
BASE_VIEWABLE_MARGIN_BOTTOM + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 2 ? bezelCompensation : 0);
|
||||
break;
|
||||
case PortraitInverted:
|
||||
*outTop = BASE_VIEWABLE_MARGIN_BOTTOM + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 1 ? bezelCompensation : 0);
|
||||
*outRight = BASE_VIEWABLE_MARGIN_LEFT + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 3 ? bezelCompensation : 0);
|
||||
*outBottom = BASE_VIEWABLE_MARGIN_TOP + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 0 ? bezelCompensation : 0);
|
||||
*outLeft = BASE_VIEWABLE_MARGIN_RIGHT + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 2 ? bezelCompensation : 0);
|
||||
*outTop =
|
||||
BASE_VIEWABLE_MARGIN_BOTTOM + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 1 ? bezelCompensation : 0);
|
||||
*outRight =
|
||||
BASE_VIEWABLE_MARGIN_LEFT + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 3 ? bezelCompensation : 0);
|
||||
*outBottom =
|
||||
BASE_VIEWABLE_MARGIN_TOP + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 0 ? bezelCompensation : 0);
|
||||
*outLeft =
|
||||
BASE_VIEWABLE_MARGIN_RIGHT + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 2 ? bezelCompensation : 0);
|
||||
break;
|
||||
case LandscapeCounterClockwise:
|
||||
*outTop = BASE_VIEWABLE_MARGIN_RIGHT + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 1 ? bezelCompensation : 0);
|
||||
*outRight = BASE_VIEWABLE_MARGIN_BOTTOM + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 3 ? bezelCompensation : 0);
|
||||
*outBottom = BASE_VIEWABLE_MARGIN_LEFT + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 0 ? bezelCompensation : 0);
|
||||
*outLeft = BASE_VIEWABLE_MARGIN_TOP + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 2 ? bezelCompensation : 0);
|
||||
*outTop =
|
||||
BASE_VIEWABLE_MARGIN_RIGHT + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 1 ? bezelCompensation : 0);
|
||||
*outRight =
|
||||
BASE_VIEWABLE_MARGIN_BOTTOM + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 3 ? bezelCompensation : 0);
|
||||
*outBottom =
|
||||
BASE_VIEWABLE_MARGIN_LEFT + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 0 ? bezelCompensation : 0);
|
||||
*outLeft =
|
||||
BASE_VIEWABLE_MARGIN_TOP + (mapPhysicalToLogicalEdge(bezelEdge, orientation) == 2 ? bezelCompensation : 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,10 +94,10 @@ class GfxRenderer {
|
||||
// Handles current render mode (BW, GRAYSCALE_MSB, GRAYSCALE_LSB)
|
||||
void fillRectGray(int x, int y, int width, int height, uint8_t grayLevel) const;
|
||||
void drawImage(const uint8_t bitmap[], int x, int y, int width, int height) const;
|
||||
void drawImageRotated(const uint8_t bitmap[], int x, int y, int width, int height,
|
||||
ImageRotation rotation, bool invert = false) const;
|
||||
void drawBitmap(const Bitmap& bitmap, int x, int y, int maxWidth, int maxHeight, float cropX = 0,
|
||||
float cropY = 0, bool invert = false) const;
|
||||
void drawImageRotated(const uint8_t bitmap[], int x, int y, int width, int height, ImageRotation rotation,
|
||||
bool invert = false) const;
|
||||
void drawBitmap(const Bitmap& bitmap, int x, int y, int maxWidth, int maxHeight, float cropX = 0, float cropY = 0,
|
||||
bool invert = false) const;
|
||||
void drawBitmap1Bit(const Bitmap& bitmap, int x, int y, int maxWidth, int maxHeight, bool invert = false) const;
|
||||
void fillPolygon(const int* xPoints, const int* yPoints, int numPoints, bool state = true) const;
|
||||
|
||||
|
||||
@@ -150,8 +150,7 @@ std::string DictHtmlParser::extractTagName(const std::string& html, size_t start
|
||||
|
||||
std::string tagName = html.substr(nameStart, pos - nameStart);
|
||||
// Convert to lowercase
|
||||
std::transform(tagName.begin(), tagName.end(), tagName.begin(),
|
||||
[](unsigned char c) { return std::tolower(c); });
|
||||
std::transform(tagName.begin(), tagName.end(), tagName.begin(), [](unsigned char c) { return std::tolower(c); });
|
||||
return tagName;
|
||||
}
|
||||
|
||||
@@ -160,17 +159,11 @@ bool DictHtmlParser::isBlockTag(const std::string& tagName) {
|
||||
tagName == "ol" || tagName == "ul" || tagName == "dt" || tagName == "dd" || tagName == "html";
|
||||
}
|
||||
|
||||
bool DictHtmlParser::isBoldTag(const std::string& tagName) {
|
||||
return tagName == "b" || tagName == "strong";
|
||||
}
|
||||
bool DictHtmlParser::isBoldTag(const std::string& tagName) { return tagName == "b" || tagName == "strong"; }
|
||||
|
||||
bool DictHtmlParser::isItalicTag(const std::string& tagName) {
|
||||
return tagName == "i" || tagName == "em";
|
||||
}
|
||||
bool DictHtmlParser::isItalicTag(const std::string& tagName) { return tagName == "i" || tagName == "em"; }
|
||||
|
||||
bool DictHtmlParser::isUnderlineTag(const std::string& tagName) {
|
||||
return tagName == "u" || tagName == "ins";
|
||||
}
|
||||
bool DictHtmlParser::isUnderlineTag(const std::string& tagName) { return tagName == "u" || tagName == "ins"; }
|
||||
|
||||
bool DictHtmlParser::isSuperscriptTag(const std::string& tagName) { return tagName == "sup"; }
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ class GfxRenderer;
|
||||
|
||||
/**
|
||||
* DictHtmlParser parses HTML dictionary definitions into ParsedText.
|
||||
*
|
||||
*
|
||||
* Supports:
|
||||
* - Bold: <b>, <strong>
|
||||
* - Italic: <i>, <em>
|
||||
@@ -25,7 +25,7 @@ class DictHtmlParser {
|
||||
/**
|
||||
* Parse HTML definition and populate ParsedText with styled words.
|
||||
* Each paragraph/block creates a separate ParsedText via the callback.
|
||||
*
|
||||
*
|
||||
* @param html The HTML definition text
|
||||
* @param fontId Font ID for text width calculations
|
||||
* @param renderer Reference to renderer for layout
|
||||
|
||||
@@ -588,8 +588,12 @@ static std::string decodeHtmlEntity(const std::string& html, size_t& i) {
|
||||
const char* replacement;
|
||||
};
|
||||
static const EntityMapping entities[] = {
|
||||
{" ", " "}, {"<", "<"}, {">", ">"},
|
||||
{"&", "&"}, {""", "\""}, {"'", "'"},
|
||||
{" ", " "},
|
||||
{"<", "<"},
|
||||
{">", ">"},
|
||||
{"&", "&"},
|
||||
{""", "\""},
|
||||
{"'", "'"},
|
||||
{"—", "\xe2\x80\x94"}, // —
|
||||
{"–", "\xe2\x80\x93"}, // –
|
||||
{"…", "\xe2\x80\xa6"}, // …
|
||||
@@ -688,8 +692,8 @@ std::string StarDict::stripHtml(const std::string& html) {
|
||||
|
||||
// Extract tag name
|
||||
size_t tagEnd = tagStart;
|
||||
while (tagEnd < html.length() && !std::isspace(static_cast<unsigned char>(html[tagEnd])) &&
|
||||
html[tagEnd] != '>' && html[tagEnd] != '/') {
|
||||
while (tagEnd < html.length() && !std::isspace(static_cast<unsigned char>(html[tagEnd])) && html[tagEnd] != '>' &&
|
||||
html[tagEnd] != '/') {
|
||||
tagEnd++;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ class StarDict {
|
||||
struct DictzipInfo {
|
||||
uint32_t chunkLength = 0; // Uncompressed chunk size (usually 58315)
|
||||
uint16_t chunkCount = 0;
|
||||
uint32_t headerSize = 0; // Total header size to skip
|
||||
uint32_t headerSize = 0; // Total header size to skip
|
||||
uint16_t* chunkSizes = nullptr; // Array of compressed chunk sizes
|
||||
bool loaded = false;
|
||||
};
|
||||
|
||||
@@ -205,8 +205,8 @@ bool Txt::generateThumbBmp() const {
|
||||
}
|
||||
constexpr int THUMB_TARGET_WIDTH = 240;
|
||||
constexpr int THUMB_TARGET_HEIGHT = 400;
|
||||
const bool success =
|
||||
JpegToBmpConverter::jpegFileTo1BitBmpStreamWithSize(coverJpg, thumbBmp, THUMB_TARGET_WIDTH, THUMB_TARGET_HEIGHT);
|
||||
const bool success = JpegToBmpConverter::jpegFileTo1BitBmpStreamWithSize(coverJpg, thumbBmp, THUMB_TARGET_WIDTH,
|
||||
THUMB_TARGET_HEIGHT);
|
||||
coverJpg.close();
|
||||
thumbBmp.close();
|
||||
|
||||
@@ -276,8 +276,8 @@ bool Txt::generateMicroThumbBmp() const {
|
||||
}
|
||||
constexpr int MICRO_THUMB_TARGET_WIDTH = 45;
|
||||
constexpr int MICRO_THUMB_TARGET_HEIGHT = 60;
|
||||
const bool success = JpegToBmpConverter::jpegFileTo1BitBmpStreamWithSize(coverJpg, microThumbBmp,
|
||||
MICRO_THUMB_TARGET_WIDTH, MICRO_THUMB_TARGET_HEIGHT);
|
||||
const bool success = JpegToBmpConverter::jpegFileTo1BitBmpStreamWithSize(
|
||||
coverJpg, microThumbBmp, MICRO_THUMB_TARGET_WIDTH, MICRO_THUMB_TARGET_HEIGHT);
|
||||
coverJpg.close();
|
||||
microThumbBmp.close();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user