diff --git a/modules/juce_core/streams/juce_OutputStream.cpp b/modules/juce_core/streams/juce_OutputStream.cpp index 3d1682fefa..ab7ca29d3e 100644 --- a/modules/juce_core/streams/juce_OutputStream.cpp +++ b/modules/juce_core/streams/juce_OutputStream.cpp @@ -242,8 +242,14 @@ bool OutputStream::writeText (const String& text, bool asUTF16, bool writeUTF16B continue; } - if (! writeShort ((short) c)) - return false; + CharPointer_UTF16::CharType buffer[2]{}; + CharPointer_UTF16 begin { buffer }; + auto end = begin; + end.write (c); + + for (const auto unit : makeRange (begin.getAddress(), end.getAddress())) + if (! writeShort ((short) unit)) + return false; } } else @@ -403,4 +409,43 @@ JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const New return stream << stream.getNewLineString(); } +#if JUCE_UNIT_TESTS + +class OutputStreamUTF16Test final : public UnitTest +{ +public: + OutputStreamUTF16Test() : UnitTest { "OutputStream::writeText UTF16", UnitTestCategories::streams } {} + + void runTest() final + { + beginTest ("writeText UTF16 - Can support full unicode codepoints"); + { + static constexpr juce_wchar stringA[] { 0x1F600, 0x00 }; // Grinning face emoji + static constexpr juce_wchar stringB[] { 0xA, 0xB, 0xC, 0x0 }; // ASCII + static constexpr juce_wchar stringC[] { 0xAAAA, 0xBBBB, 0xCCCC, 0x0 }; // two-byte characters + + CharPointer_UTF32 pointers[] { CharPointer_UTF32 (stringA), + CharPointer_UTF32 (stringB), + CharPointer_UTF32 (stringC) }; + + for (auto originalPtr : pointers) + { + MemoryOutputStream stream; + stream.writeText (originalPtr, true, false, "\n"); + + expect (stream.getDataSize() != 0); + + CharPointer_UTF16 writtenPtr { reinterpret_cast (stream.getData()) }; + + for (; *originalPtr != 0; ++originalPtr, ++writtenPtr) + expect (*originalPtr == *writtenPtr); + } + } + } +}; + +static OutputStreamUTF16Test outputStreamUTF16Test; + +#endif + } // namespace juce