mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-03-30 20:15:35 +08:00
[xswiftbus] Fix UTF-8 bug when displaying text messages
std::string is not UTF-8 aware, so it was possible to create malformed strings by splitting in the middle of a code point. The splitting code also did read beyond the end of the input string. Now we use a Unicode-aware iterator adaptor to help find the correct place to split the string across multiple lines.
This commit is contained in:
@@ -17,6 +17,7 @@
|
|||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
// Strict header only X-Plane model parser utils shared between BlackMisc and XSwiftBus.
|
// Strict header only X-Plane model parser utils shared between BlackMisc and XSwiftBus.
|
||||||
// Header only is necessary to no require XSwiftBus to link against BlackMisc.
|
// Header only is necessary to no require XSwiftBus to link against BlackMisc.
|
||||||
@@ -249,6 +250,59 @@ namespace BlackMisc
|
|||||||
acfProperties.modelString = simplifyWhitespace(stringForFlyableModel(acfProperties, filePath));
|
acfProperties.modelString = simplifyWhitespace(stringForFlyableModel(acfProperties, filePath));
|
||||||
return acfProperties;
|
return acfProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! Encoding-aware iterator adaptor for std::u8string
|
||||||
|
template <typename I>
|
||||||
|
struct Utf8Iterator
|
||||||
|
{
|
||||||
|
//! STL compatibility
|
||||||
|
//! @{
|
||||||
|
using value_type = typename std::iterator_traits<I>::value_type;
|
||||||
|
using difference_type = typename std::iterator_traits<I>::difference_type;
|
||||||
|
using reference = typename std::iterator_traits<I>::reference;
|
||||||
|
using pointer = typename std::iterator_traits<I>::pointer;
|
||||||
|
using iterator_category = std::forward_iterator_tag;
|
||||||
|
//! @}
|
||||||
|
|
||||||
|
//! Default constructor
|
||||||
|
Utf8Iterator() = default;
|
||||||
|
|
||||||
|
//! Constructor
|
||||||
|
Utf8Iterator(I base, I end) : base(base), end(end) {}
|
||||||
|
|
||||||
|
//! Equality
|
||||||
|
//! @{
|
||||||
|
friend bool operator ==(Utf8Iterator a, Utf8Iterator b) { return a.base == b.base; }
|
||||||
|
friend bool operator !=(Utf8Iterator a, Utf8Iterator b) { return a.base != b.base; }
|
||||||
|
friend bool operator ==(Utf8Iterator a, I b) { return a.base == b; }
|
||||||
|
friend bool operator !=(Utf8Iterator a, I b) { return a.base != b; }
|
||||||
|
friend bool operator ==(I a, Utf8Iterator b) { return a == b.base; }
|
||||||
|
friend bool operator !=(I a, Utf8Iterator b) { return a != b.base; }
|
||||||
|
//! @}
|
||||||
|
|
||||||
|
//! Dereference (not encoding-aware)
|
||||||
|
reference operator *() const { return *base; }
|
||||||
|
|
||||||
|
//! Pointer indirection (not encoding-aware)
|
||||||
|
pointer operator ->() const { return base.operator ->(); }
|
||||||
|
|
||||||
|
//! Pre-increment
|
||||||
|
Utf8Iterator &operator ++()
|
||||||
|
{
|
||||||
|
constexpr auto isContinuation = [](auto c)
|
||||||
|
{
|
||||||
|
return (c & static_cast<value_type>(0b11000000)) == static_cast<value_type>(0b10000000);
|
||||||
|
};
|
||||||
|
do { ++base; } while (base != end && isContinuation(*base));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Post-increment
|
||||||
|
Utf8Iterator operator ++(int) { auto copy = *this; ++*this; return copy; }
|
||||||
|
|
||||||
|
I base; //!< Underlying iterator
|
||||||
|
I end; //!< Underlying end iterator
|
||||||
|
};
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -146,21 +146,23 @@ namespace XSwiftBus
|
|||||||
{
|
{
|
||||||
if (text.empty()) { return; }
|
if (text.empty()) { return; }
|
||||||
static const CMessage::string ellipsis = u8"\u2026";
|
static const CMessage::string ellipsis = u8"\u2026";
|
||||||
const int lineLength = m_messages.maxLineLength() - static_cast<int>(ellipsis.size());
|
const unsigned lineLength = m_messages.maxLineLength() - 1;
|
||||||
|
|
||||||
|
using U8It = Utf8Iterator<typename CMessage::string::const_iterator>;
|
||||||
|
U8It begin(text.begin(), text.end());
|
||||||
|
auto characters = std::distance(begin, U8It(text.end(), text.end()));
|
||||||
std::vector<CMessage::string> wrappedLines;
|
std::vector<CMessage::string> wrappedLines;
|
||||||
for (size_t i = 0; i < text.size(); i += static_cast<size_t>(lineLength))
|
|
||||||
|
for (; characters > lineLength; characters -= lineLength)
|
||||||
{
|
{
|
||||||
wrappedLines.emplace_back(text.begin() + i, text.begin() + i + static_cast<size_t>(lineLength));
|
auto end = std::next(begin, lineLength);
|
||||||
|
wrappedLines.emplace_back(begin.base, end.base);
|
||||||
wrappedLines.back() += ellipsis;
|
wrappedLines.back() += ellipsis;
|
||||||
|
begin = end;
|
||||||
}
|
}
|
||||||
wrappedLines.back().erase(wrappedLines.back().size() - ellipsis.size());
|
if (characters > 0)
|
||||||
if (wrappedLines.back().empty()) { wrappedLines.pop_back(); }
|
|
||||||
else if (wrappedLines.back().size() == ellipsis.size() && wrappedLines.size() > 1)
|
|
||||||
{
|
{
|
||||||
auto secondLastLine = wrappedLines.end() - 2;
|
wrappedLines.emplace_back(begin.base, text.end());
|
||||||
secondLastLine->erase(wrappedLines.back().size() - ellipsis.size());
|
|
||||||
secondLastLine->append(wrappedLines.back());
|
|
||||||
wrappedLines.pop_back();
|
|
||||||
}
|
}
|
||||||
for (const auto &line : wrappedLines)
|
for (const auto &line : wrappedLines)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user