mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-05 07:07:38 +03:00
Refactor hyphenation logic and update settings for hyphenation toggle
This commit is contained in:
parent
3059cdbc11
commit
be233917ec
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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();
|
||||
};
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user