diff --git a/lib/GfxRenderer/GfxRenderer.cpp b/lib/GfxRenderer/GfxRenderer.cpp index 88e69598..f9f35efc 100644 --- a/lib/GfxRenderer/GfxRenderer.cpp +++ b/lib/GfxRenderer/GfxRenderer.cpp @@ -2,18 +2,6 @@ #include -namespace { -size_t utf8RemoveLastChar(std::string& str) { - if (str.empty()) return 0; - size_t pos = str.size() - 1; - while (pos > 0 && (static_cast(str[pos]) & 0xC0) == 0x80) { - --pos; - } - str.resize(pos); - return pos; -} -} // namespace - void GfxRenderer::insertFont(const int fontId, EpdFontFamily font) { fontMap.insert({fontId, font}); } void GfxRenderer::rotateCoordinates(const int x, const int y, int* rotatedX, int* rotatedY) const { diff --git a/lib/Utf8/Utf8.cpp b/lib/Utf8/Utf8.cpp index d5f7ebce..f77cce55 100644 --- a/lib/Utf8/Utf8.cpp +++ b/lib/Utf8/Utf8.cpp @@ -29,3 +29,20 @@ uint32_t utf8NextCodepoint(const unsigned char** string) { return cp; } + +size_t utf8RemoveLastChar(std::string& str) { + if (str.empty()) return 0; + size_t pos = str.size() - 1; + while (pos > 0 && (static_cast(str[pos]) & 0xC0) == 0x80) { + --pos; + } + str.resize(pos); + return pos; +} + +// Truncate string by removing N UTF-8 characters from the end +void utf8TruncateChars(std::string& str, const size_t numChars) { + for (size_t i = 0; i < numChars && !str.empty(); ++i) { + utf8RemoveLastChar(str); + } +} diff --git a/lib/Utf8/Utf8.h b/lib/Utf8/Utf8.h index 095c1584..23d63a4e 100644 --- a/lib/Utf8/Utf8.h +++ b/lib/Utf8/Utf8.h @@ -1,7 +1,11 @@ #pragma once #include - +#include #define REPLACEMENT_GLYPH 0xFFFD uint32_t utf8NextCodepoint(const unsigned char** string); +// Remove the last UTF-8 codepoint from a std::string and return the new size. +size_t utf8RemoveLastChar(std::string& str); +// Truncate string by removing N UTF-8 codepoints from the end. +void utf8TruncateChars(std::string& str, size_t numChars); diff --git a/src/activities/home/HomeActivity.cpp b/src/activities/home/HomeActivity.cpp index 58b29505..670f7cff 100644 --- a/src/activities/home/HomeActivity.cpp +++ b/src/activities/home/HomeActivity.cpp @@ -17,6 +17,8 @@ #include "fontIds.h" #include "util/StringUtils.h" +#include + void HomeActivity::taskTrampoline(void* param) { auto* self = static_cast(param); self->displayTaskLoop(); @@ -366,7 +368,7 @@ void HomeActivity::render() { while (!lines.back().empty() && renderer.getTextWidth(UI_12_FONT_ID, lines.back().c_str()) > maxLineWidth) { // Remove "..." first, then remove one UTF-8 char, then add "..." back lines.back().resize(lines.back().size() - 3); // Remove "..." - StringUtils::utf8RemoveLastChar(lines.back()); + utf8RemoveLastChar(lines.back()); lines.back().append("..."); } break; @@ -375,7 +377,7 @@ void HomeActivity::render() { int wordWidth = renderer.getTextWidth(UI_12_FONT_ID, i.c_str()); while (wordWidth > maxLineWidth && !i.empty()) { // Word itself is too long, trim it (UTF-8 safe) - StringUtils::utf8RemoveLastChar(i); + utf8RemoveLastChar(i); // Check if we have room for ellipsis std::string withEllipsis = i + "..."; wordWidth = renderer.getTextWidth(UI_12_FONT_ID, withEllipsis.c_str()); @@ -428,7 +430,7 @@ void HomeActivity::render() { if (!lastBookAuthor.empty()) { std::string trimmedAuthor = lastBookAuthor; while (renderer.getTextWidth(UI_10_FONT_ID, trimmedAuthor.c_str()) > maxLineWidth && !trimmedAuthor.empty()) { - StringUtils::utf8RemoveLastChar(trimmedAuthor); + utf8RemoveLastChar(trimmedAuthor); } if (renderer.getTextWidth(UI_10_FONT_ID, trimmedAuthor.c_str()) < renderer.getTextWidth(UI_10_FONT_ID, lastBookAuthor.c_str())) { @@ -462,14 +464,14 @@ void HomeActivity::render() { // Trim author if too long (UTF-8 safe) bool wasTrimmed = false; while (renderer.getTextWidth(UI_10_FONT_ID, trimmedAuthor.c_str()) > maxLineWidth && !trimmedAuthor.empty()) { - StringUtils::utf8RemoveLastChar(trimmedAuthor); + utf8RemoveLastChar(trimmedAuthor); wasTrimmed = true; } if (wasTrimmed && !trimmedAuthor.empty()) { // Make room for ellipsis while (renderer.getTextWidth(UI_10_FONT_ID, (trimmedAuthor + "...").c_str()) > maxLineWidth && !trimmedAuthor.empty()) { - StringUtils::utf8RemoveLastChar(trimmedAuthor); + utf8RemoveLastChar(trimmedAuthor); } trimmedAuthor.append("..."); } diff --git a/src/util/StringUtils.cpp b/src/util/StringUtils.cpp index 2426b687..8e2ce58e 100644 --- a/src/util/StringUtils.cpp +++ b/src/util/StringUtils.cpp @@ -61,23 +61,4 @@ bool checkFileExtension(const String& fileName, const char* extension) { return localFile.endsWith(localExtension); } -size_t utf8RemoveLastChar(std::string& str) { - if (str.empty()) return 0; - size_t pos = str.size() - 1; - // Walk back to find the start of the last UTF-8 character - // UTF-8 continuation bytes start with 10xxxxxx (0x80-0xBF) - while (pos > 0 && (static_cast(str[pos]) & 0xC0) == 0x80) { - --pos; - } - str.resize(pos); - return pos; -} - -// Truncate string by removing N UTF-8 characters from the end -void utf8TruncateChars(std::string& str, const size_t numChars) { - for (size_t i = 0; i < numChars && !str.empty(); ++i) { - utf8RemoveLastChar(str); - } -} - } // namespace StringUtils diff --git a/src/util/StringUtils.h b/src/util/StringUtils.h index 5c8332f0..4b93729b 100644 --- a/src/util/StringUtils.h +++ b/src/util/StringUtils.h @@ -19,10 +19,4 @@ std::string sanitizeFilename(const std::string& name, size_t maxLength = 100); bool checkFileExtension(const std::string& fileName, const char* extension); bool checkFileExtension(const String& fileName, const char* extension); -// UTF-8 safe string truncation - removes one character from the end -// Returns the new size after removing one UTF-8 character -size_t utf8RemoveLastChar(std::string& str); - -// Truncate string by removing N UTF-8 characters from the end -void utf8TruncateChars(std::string& str, size_t numChars); } // namespace StringUtils