Refactor hyphenation logic and update settings for hyphenation toggle

This commit is contained in:
Arthur Tazhitdinov 2026-01-03 15:50:51 +05:00
parent 3059cdbc11
commit be233917ec
6 changed files with 10 additions and 95 deletions

View File

@ -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<size_t>::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<uint16_t>(prefixWidth + (needsInsertedHyphen ? hyphenWidth : 0));
chosenAppendHyphen = needsInsertedHyphen;
} else {
break;
}
}
if (chosenOffset == std::numeric_limits<size_t>::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<const unsigned char*>(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<size_t> 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);

View File

@ -27,8 +27,7 @@ class ParsedText {
std::vector<uint16_t> 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;

View File

@ -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<uint32_t> 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> page) { lut.emplace_back(this->onPageComplete(std::move(page))); },
progressFn);
success = visitor.parseAndBuildPages();

View File

@ -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<void()>& progressSetupFn = nullptr,
const std::function<void(int)>& progressFn = nullptr, bool hyphenationEnabled);
const std::function<void(int)>& progressFn = nullptr, bool hyphenationEnabled = false);
std::unique_ptr<Page> loadPageFromSectionFile();
};

View File

@ -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<void(std::unique_ptr<Page>)>& completePageFn,
const std::function<void(int)>& progressFn = nullptr)
: filepath(filepath),

View File

@ -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) {