Merge remote-tracking branch 'origin' into feature/add-epub-css-parsing

* origin:
  fix: truncate chapter names that are too long (#422)
  feat: dict based Hyphenation (#305)
  fix: render U+FFFD replacement character instead of ? (#366)
  fix: Invert colors on home screen cover overlay when recent book is selected (#390)
  Adds KOReader Sync support (#232)
  feat: Change keyboard "caps" to "shift" & Wrap Keyboard (#377)
  fix: XTC 1-bit thumb BMP polarity inversion (#373)
This commit is contained in:
Jake Kenneally
2026-01-19 22:37:37 -06:00
97 changed files with 39108 additions and 222 deletions

View File

@@ -88,7 +88,7 @@ void ChapterHtmlSlimParser::startNewTextBlock(const TextBlock::Style style, cons
makePages();
}
currentTextBlock.reset(new ParsedText(style, extraParagraphSpacing, blockStyle));
currentTextBlock.reset(new ParsedText(style, extraParagraphSpacing, hyphenationEnabled, blockStyle));
}
void ChapterHtmlSlimParser::startNewTextBlock(const TextBlock::Style style) { startNewTextBlock(style, BlockStyle{}); }
@@ -356,21 +356,6 @@ void XMLCALL ChapterHtmlSlimParser::characterData(void* userData, const XML_Char
continue;
}
// Skip soft-hyphen with UTF-8 representation (U+00AD) = 0xC2 0xAD
const XML_Char SHY_BYTE_1 = static_cast<XML_Char>(0xC2);
const XML_Char SHY_BYTE_2 = static_cast<XML_Char>(0xAD);
// 1. Check for the start of the 2-byte Soft Hyphen sequence
if (s[i] == SHY_BYTE_1) {
// 2. Check if the next byte exists AND if it completes the sequence
// We must check i + 1 < len to prevent reading past the end of the buffer.
if ((i + 1 < len) && (s[i + 1] == SHY_BYTE_2)) {
// Sequence 0xC2 0xAD found!
// Skip the current byte (0xC2) and the next byte (0xAD)
i++; // Increment 'i' one more time to skip the 0xAD byte
continue; // Skip the rest of the loop and move to the next iteration
}
}
// Skip Zero Width No-Break Space / BOM (U+FEFF) = 0xEF 0xBB 0xBF
const XML_Char FEFF_BYTE_1 = static_cast<XML_Char>(0xEF);
const XML_Char FEFF_BYTE_2 = static_cast<XML_Char>(0xBB);

View File

@@ -39,6 +39,7 @@ class ChapterHtmlSlimParser {
uint8_t paragraphAlignment;
uint16_t viewportWidth;
uint16_t viewportHeight;
bool hyphenationEnabled;
const CssParser* cssParser;
// Style tracking (replaces depth-based approach)
@@ -67,7 +68,7 @@ class ChapterHtmlSlimParser {
explicit ChapterHtmlSlimParser(const std::string& filepath, GfxRenderer& renderer, const int fontId,
const float lineCompression, const bool extraParagraphSpacing,
const uint8_t paragraphAlignment, const uint16_t viewportWidth,
const uint16_t viewportHeight,
const uint16_t viewportHeight, const bool hyphenationEnabled,
const std::function<void(std::unique_ptr<Page>)>& completePageFn,
const std::function<void(int)>& progressFn = nullptr,
const CssParser* cssParser = nullptr)
@@ -79,6 +80,7 @@ class ChapterHtmlSlimParser {
paragraphAlignment(paragraphAlignment),
viewportWidth(viewportWidth),
viewportHeight(viewportHeight),
hyphenationEnabled(hyphenationEnabled),
completePageFn(completePageFn),
progressFn(progressFn),
cssParser(cssParser) {}

View File

@@ -108,6 +108,11 @@ void XMLCALL ContentOpfParser::startElement(void* userData, const XML_Char* name
return;
}
if (self->state == IN_METADATA && strcmp(name, "dc:language") == 0) {
self->state = IN_BOOK_LANGUAGE;
return;
}
if (self->state == IN_PACKAGE && (strcmp(name, "manifest") == 0 || strcmp(name, "opf:manifest") == 0)) {
self->state = IN_MANIFEST;
if (!SdMan.openFileForWrite("COF", self->cachePath + itemCacheFile, self->tempItemStore)) {
@@ -272,6 +277,11 @@ void XMLCALL ContentOpfParser::characterData(void* userData, const XML_Char* s,
self->author.append(s, len);
return;
}
if (self->state == IN_BOOK_LANGUAGE) {
self->language.append(s, len);
return;
}
}
void XMLCALL ContentOpfParser::endElement(void* userData, const XML_Char* name) {
@@ -306,6 +316,11 @@ void XMLCALL ContentOpfParser::endElement(void* userData, const XML_Char* name)
return;
}
if (self->state == IN_BOOK_LANGUAGE && strcmp(name, "dc:language") == 0) {
self->state = IN_METADATA;
return;
}
if (self->state == IN_METADATA && (strcmp(name, "metadata") == 0 || strcmp(name, "opf:metadata") == 0)) {
self->state = IN_PACKAGE;
return;

View File

@@ -15,6 +15,7 @@ class ContentOpfParser final : public Print {
IN_METADATA,
IN_BOOK_TITLE,
IN_BOOK_AUTHOR,
IN_BOOK_LANGUAGE,
IN_MANIFEST,
IN_SPINE,
IN_GUIDE,
@@ -36,6 +37,7 @@ class ContentOpfParser final : public Print {
public:
std::string title;
std::string author;
std::string language;
std::string tocNcxPath;
std::string tocNavPath; // EPUB 3 nav document path
std::string coverItemHref;