diff --git a/base/string_utils.cpp b/base/string_utils.cpp index 076ad4459..0dcb6d15b 100644 --- a/base/string_utils.cpp +++ b/base/string_utils.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -269,6 +270,48 @@ void Trim(std::string & s, std::string_view anyOf) boost::trim_if(s, boost::is_any_of(anyOf)); } +bool Truncate(std::string & utf8, size_t const maxTextLengthPlus1) +{ + size_t codePoints = 0; + + for (size_t i = 0; i < utf8.length(); ++i) + { + if ((utf8[i] & 0xC0) != 0x80) + { + ++codePoints; + + if (codePoints == maxTextLengthPlus1) + { + --i; + + unsigned char byte = utf8[i]; + uint8_t bytesInCodepoint = 1; + + if ((byte & 0x80) == 0x00) + { + bytesInCodepoint = 1; + } + else if ((byte & 0xE0) == 0xC0) + { + bytesInCodepoint = 2; + } + else if ((byte & 0xF0) == 0xE0) + { + bytesInCodepoint = 3; + } + else if ((byte & 0xF8) == 0xF0) + { + bytesInCodepoint = 4; + } + + utf8.resize(i + bytesInCodepoint); + return true; + } + } + } + return false; +} + bool ReplaceFirst(std::string & str, std::string const & from, std::string const & to) { auto const pos = str.find(from); diff --git a/base/string_utils.hpp b/base/string_utils.hpp index ce8f59eb8..8e56457b7 100644 --- a/base/string_utils.hpp +++ b/base/string_utils.hpp @@ -114,6 +114,8 @@ void Trim(std::string_view & sv); void Trim(std::string_view & s, std::string_view anyOf); void Trim(std::string & s, std::string_view anyOf); +bool Truncate(std::string & utf8, size_t const maxTextLengthPlus1); + // Replace the first match of the search substring in the input with the format string. // str - An input string // from - A substring to be searched for diff --git a/drape_frontend/stylist.cpp b/drape_frontend/stylist.cpp index 61e03b935..32ef71b52 100644 --- a/drape_frontend/stylist.cpp +++ b/drape_frontend/stylist.cpp @@ -6,6 +6,8 @@ #include "indexer/feature_visibility.hpp" #include "indexer/scales.hpp" +#include "base/string_utils.hpp" + #include #include @@ -72,10 +74,10 @@ void CaptionDescription::Init(FeatureType & f, int8_t deviceLang, int zoomLevel, return; } - // Set max text size to avoid VB/IB overflow in rendering. - size_t constexpr kMaxTextSize = 200; - if (m_mainText.size() > kMaxTextSize) - m_mainText = m_mainText.substr(0, kMaxTextSize) + "..."; + // Set max text length to avoid VB/IB overflow in rendering. + size_t constexpr kMaxTextLength = 65; + if (strings::Truncate(&m_mainText, kMaxTextLength)) + m_mainText += "…"; // TODO(pastk) : its better to determine housenumbers minZoom once upon drules load and cache it, // but it'd mean a lot of housenumbers-specific logic in otherwise generic RulesHolder..