diff --git a/modules/juce_core/text/juce_CharacterFunctions.h b/modules/juce_core/text/juce_CharacterFunctions.h index fde96a0563..4487b09b09 100644 --- a/modules/juce_core/text/juce_CharacterFunctions.h +++ b/modules/juce_core/text/juce_CharacterFunctions.h @@ -761,6 +761,36 @@ public: return -1; } + /** Given a CharacterPointer range and a predicate, returns a pointer to the first + character in the range that does not satisfy the predicate. + */ + template + static Type trimBegin (Type begin, const Type end, Predicate&& shouldTrim) + { + while (begin != end && shouldTrim (begin)) + ++begin; + + return begin; + } + + /** Given a CharacterPointer range and a predicate, returns a pointer one-past the + last character in the range that does not satisfy the predicate. + */ + template + static Type trimEnd (const Type begin, Type end, Predicate&& shouldTrim) + { + while (end > begin) + { + if (! shouldTrim (--end)) + { + ++end; + break; + } + } + + return end; + } + /** Increments a pointer until it points to the first non-whitespace character in a string. diff --git a/modules/juce_core/text/juce_String.cpp b/modules/juce_core/text/juce_String.cpp index d85cfd3009..cebff47d96 100644 --- a/modules/juce_core/text/juce_String.cpp +++ b/modules/juce_core/text/juce_String.cpp @@ -1668,64 +1668,53 @@ String String::quoted (juce_wchar quoteCharacter) const } //============================================================================== -static String::CharPointerType findTrimmedEnd (const String::CharPointerType start, - String::CharPointerType end) -{ - while (end > start) - { - if (! (--end).isWhitespace()) - { - ++end; - break; - } - } - - return end; -} - String String::trim() const { - if (isNotEmpty()) - { - auto start = text.findEndOfWhitespace(); - auto end = start.findTerminatingNull(); - auto trimmedEnd = findTrimmedEnd (start, end); + if (isEmpty()) + return *this; - if (trimmedEnd <= start) - return {}; + const auto b = begin(); + const auto e = end(); - if (text < start || trimmedEnd < end) - return String (start, trimmedEnd); - } + const auto shouldTrim = [] (auto ptr) { return ptr.isWhitespace(); }; + const auto trimmedBegin = CharacterFunctions::trimBegin (b, e, shouldTrim); + const auto trimmedEnd = CharacterFunctions::trimEnd (trimmedBegin, e, shouldTrim); - return *this; + if (trimmedBegin == b && trimmedEnd == e) + return *this; + + return String (trimmedBegin, trimmedEnd); } String String::trimStart() const { - if (isNotEmpty()) - { - auto t = text.findEndOfWhitespace(); + if (isEmpty()) + return *this; - if (t != text) - return String (t); - } + const auto shouldTrim = [] (auto ptr) { return ptr.isWhitespace(); }; + const auto b = begin(); + const auto t = CharacterFunctions::trimBegin (b, end(), shouldTrim); - return *this; + if (t == b) + return *this; + + return String (t); } String String::trimEnd() const { - if (isNotEmpty()) - { - auto end = text.findTerminatingNull(); - auto trimmedEnd = findTrimmedEnd (text, end); + if (isEmpty()) + return *this; - if (trimmedEnd < end) - return String (text, trimmedEnd); - } + const auto shouldTrim = [] (auto ptr) { return ptr.isWhitespace(); }; + const auto b = begin(); + const auto e = end(); + const auto t = CharacterFunctions::trimEnd (b, e, shouldTrim); - return *this; + if (t == e) + return *this; + + return String (b, t); } String String::trimCharactersAtStart (StringRef charactersToTrim) const diff --git a/modules/juce_graphics/fonts/juce_GlyphArrangement.cpp b/modules/juce_graphics/fonts/juce_GlyphArrangement.cpp index 9178456a2e..768b72ee32 100644 --- a/modules/juce_graphics/fonts/juce_GlyphArrangement.cpp +++ b/modules/juce_graphics/fonts/juce_GlyphArrangement.cpp @@ -35,12 +35,26 @@ namespace juce { -static constexpr bool isNonBreakingSpace (const juce_wchar c) +static String portableTrim (String toTrim) { - return c == 0x00a0 - || c == 0x2007 - || c == 0x202f - || c == 0x2060; + if (toTrim.isEmpty()) + return toTrim; + + const auto b = toTrim.begin(); + const auto e = toTrim.end(); + + const auto shouldTrim = [] (auto ptr) + { + return SBCodepointGetBidiType ((SBCodepoint) *ptr) == SBBidiTypeWS; + }; + + const auto trimmedBegin = CharacterFunctions::trimBegin (b, e, shouldTrim); + const auto trimmedEnd = CharacterFunctions::trimEnd (trimmedBegin, e, shouldTrim); + + if (trimmedBegin == b && trimmedEnd == e) + return toTrim; + + return String (trimmedBegin, trimmedEnd); } static bool areAllRequiredWidthsSmallerThanMax (const detail::ShapedText& shapedText, float width) @@ -268,7 +282,7 @@ static auto createFittedText (const Font& f, return st; } - const auto trimmed = text.trim(); + const auto trimmed = portableTrim (text); constexpr auto widthFittingTolerance = 0.01f;