From be233917ec8f24b5ad03ea80107fd878928c7e9f Mon Sep 17 00:00:00 2001 From: Arthur Tazhitdinov Date: Sat, 3 Jan 2026 15:50:51 +0500 Subject: [PATCH] Refactor hyphenation logic and update settings for hyphenation toggle --- lib/Epub/Epub/ParsedText.cpp | 86 +------------------ lib/Epub/Epub/ParsedText.h | 3 +- lib/Epub/Epub/Section.cpp | 7 +- lib/Epub/Epub/Section.h | 2 +- lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h | 3 +- src/activities/settings/SettingsActivity.cpp | 4 +- 6 files changed, 10 insertions(+), 95 deletions(-) diff --git a/lib/Epub/Epub/ParsedText.cpp b/lib/Epub/Epub/ParsedText.cpp index 08738d9f..9a6162d7 100644 --- a/lib/Epub/Epub/ParsedText.cpp +++ b/lib/Epub/Epub/ParsedText.cpp @@ -52,90 +52,6 @@ bool endsWithExplicitHyphen(const std::string& word, const size_t offset) { return isExplicitHyphen(cp); } -bool chooseSplitForWidth(const GfxRenderer& renderer, const int fontId, const std::string& word, - const EpdFontStyle style, const int availableWidth, const bool includeFallback, - HyphenSplitDecision* decision) { - if (!decision || availableWidth <= 0) { - return false; - } - - const int hyphenWidth = renderer.getTextWidth(fontId, "-", style); - - auto offsets = Hyphenator::breakOffsets(word, includeFallback); - if (offsets.empty()) { - return false; - } - - size_t chosenOffset = std::numeric_limits::max(); - uint16_t chosenWidth = 0; - bool chosenAppendHyphen = true; - - for (const size_t offset : offsets) { - const bool needsInsertedHyphen = !endsWithExplicitHyphen(word, offset); - const int budget = availableWidth - (needsInsertedHyphen ? hyphenWidth : 0); - if (budget <= 0) { - continue; - } - const std::string prefix = word.substr(0, offset); - const int prefixWidth = renderer.getTextWidth(fontId, prefix.c_str(), style); - if (prefixWidth <= budget) { - chosenOffset = offset; - chosenWidth = static_cast(prefixWidth + (needsInsertedHyphen ? hyphenWidth : 0)); - chosenAppendHyphen = needsInsertedHyphen; - } else { - break; - } - } - - if (chosenOffset == std::numeric_limits::max()) { - return false; - } - - decision->byteOffset = chosenOffset; - decision->prefixWidth = chosenWidth; - decision->appendHyphen = chosenAppendHyphen; - return true; -} - -} // namespace - -namespace { - -struct HyphenSplitDecision { - size_t byteOffset; - uint16_t prefixWidth; - bool appendHyphen; // true when we must draw an extra hyphen after the prefix glyphs -}; - -// Verifies whether the substring ending at `offset` already contains a literal hyphen glyph, so we can avoid -// drawing a duplicate hyphen when breaking the word. -bool endsWithExplicitHyphen(const std::string& word, const size_t offset) { - if (offset == 0 || offset > word.size()) { - return false; - } - - const unsigned char* base = reinterpret_cast(word.data()); - const unsigned char* ptr = base; - const unsigned char* target = base + offset; - const unsigned char* lastStart = nullptr; - - while (ptr < target) { - lastStart = ptr; - utf8NextCodepoint(&ptr); - if (ptr > target) { - return false; - } - } - - if (!lastStart || ptr != target) { - return false; - } - - const unsigned char* tmp = lastStart; - const uint32_t cp = utf8NextCodepoint(&tmp); // decode the codepoint immediately prior to the break - return isExplicitHyphen(cp); -} - bool chooseSplitForWidth(const GfxRenderer& renderer, const int fontId, const std::string& word, const EpdFontFamily::Style style, const int availableWidth, const bool includeFallback, HyphenSplitDecision* decision) { @@ -373,7 +289,7 @@ std::vector ParsedText::computeLineBreaks(const GfxRenderer& renderer, c prefix += "-"; } - const EpdFontStyle styleForSplit = *styleNodeIt; + const EpdFontFamily::Style styleForSplit = *styleNodeIt; *wordNodeIt = tail; words.insert(wordNodeIt, prefix); wordStyles.insert(styleNodeIt, styleForSplit); diff --git a/lib/Epub/Epub/ParsedText.h b/lib/Epub/Epub/ParsedText.h index 13fc51f4..f19b26cb 100644 --- a/lib/Epub/Epub/ParsedText.h +++ b/lib/Epub/Epub/ParsedText.h @@ -27,8 +27,7 @@ class ParsedText { std::vector calculateWordWidths(const GfxRenderer& renderer, int fontId, int pageWidth); public: - explicit ParsedText(const TextBlock::Style style, const bool extraParagraphSpacing, - const bool hyphenationEnabled) + explicit ParsedText(const TextBlock::Style style, const bool extraParagraphSpacing, const bool hyphenationEnabled) : style(style), extraParagraphSpacing(extraParagraphSpacing), hyphenationEnabled(hyphenationEnabled) {} ~ParsedText() = default; diff --git a/lib/Epub/Epub/Section.cpp b/lib/Epub/Epub/Section.cpp index 90ffea88..da9430e0 100644 --- a/lib/Epub/Epub/Section.cpp +++ b/lib/Epub/Epub/Section.cpp @@ -86,7 +86,8 @@ bool Section::loadSectionFile(const int fontId, const float lineCompression, con if (fontId != fileFontId || lineCompression != fileLineCompression || extraParagraphSpacing != fileExtraParagraphSpacing || paragraphAlignment != fileParagraphAlignment || - viewportWidth != fileViewportWidth || viewportHeight != fileViewportHeight || hyphenationEnabled != fileHyphenationEnabled) { + viewportWidth != fileViewportWidth || viewportHeight != fileViewportHeight || + hyphenationEnabled != fileHyphenationEnabled) { file.close(); Serial.printf("[%lu] [SCT] Deserialization failed: Parameters do not match\n", millis()); clearCache(); @@ -179,8 +180,8 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c std::vector lut = {}; ChapterHtmlSlimParser visitor( - tmpHtmlPath, renderer, fontId, lineCompression, extraParagraphSpacing, hyphenationEnabled, paragraphAlignment, viewportWidth, - viewportHeight, + tmpHtmlPath, renderer, fontId, lineCompression, extraParagraphSpacing, hyphenationEnabled, paragraphAlignment, + viewportWidth, viewportHeight, [this, &lut](std::unique_ptr page) { lut.emplace_back(this->onPageComplete(std::move(page))); }, progressFn); success = visitor.parseAndBuildPages(); diff --git a/lib/Epub/Epub/Section.h b/lib/Epub/Epub/Section.h index 39127d7d..cd7ad7a7 100644 --- a/lib/Epub/Epub/Section.h +++ b/lib/Epub/Epub/Section.h @@ -34,6 +34,6 @@ class Section { bool createSectionFile(int fontId, float lineCompression, bool extraParagraphSpacing, uint8_t paragraphAlignment, uint16_t viewportWidth, uint16_t viewportHeight, const std::function& progressSetupFn = nullptr, - const std::function& progressFn = nullptr, bool hyphenationEnabled); + const std::function& progressFn = nullptr, bool hyphenationEnabled = false); std::unique_ptr loadPageFromSectionFile(); }; diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h index de17d4e2..5355211a 100644 --- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h +++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h @@ -49,8 +49,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 bool hyphenationEnabled, + const uint16_t viewportHeight, const bool hyphenationEnabled, const std::function)>& completePageFn, const std::function& progressFn = nullptr) : filepath(filepath), diff --git a/src/activities/settings/SettingsActivity.cpp b/src/activities/settings/SettingsActivity.cpp index 1d9346e3..d7cf52df 100644 --- a/src/activities/settings/SettingsActivity.cpp +++ b/src/activities/settings/SettingsActivity.cpp @@ -38,7 +38,7 @@ const SettingInfo settingsList[settingsCount] = { SettingType::ENUM, &CrossPointSettings::paragraphAlignment, {"Justify", "Left", "Center", "Right"}}, - {"Hyphenation", SettingType::TOGGLE, &CrossPointSettings::hyphenationEnabled, {}}}, + {"Hyphenation", SettingType::TOGGLE, &CrossPointSettings::hyphenationEnabled, {}}, {"Time to Sleep", SettingType::ENUM, &CrossPointSettings::sleepTimeout, @@ -47,7 +47,7 @@ const SettingInfo settingsList[settingsCount] = { SettingType::ENUM, &CrossPointSettings::refreshFrequency, {"1 page", "5 pages", "10 pages", "15 pages", "30 pages"}}, - {"Check for updates", SettingType::ACTION, nullptr, {}}; + {"Check for updates", SettingType::ACTION, nullptr, {}}}; } // namespace void SettingsActivity::taskTrampoline(void* param) {