Handle non-BMP code points for UTF-16 wchar_t Correctly encodes supplementary (non-BMP) code points in WideString as UTF-16 if wchar_t is 16 bits (as on Windows), rather than 32 bits (as on Linux). Bug: pdfium:2029 Change-Id: I662ac7fb08bd48267e32da5f36fd1c9c2bb70717 Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/107070 Reviewed-by: Nigi <nigi@chromium.org> Commit-Queue: K. Moon <kmoon@chromium.org>
diff --git a/core/fxcrt/cfx_utf8decoder.cpp b/core/fxcrt/cfx_utf8decoder.cpp index 276e186..9661745 100644 --- a/core/fxcrt/cfx_utf8decoder.cpp +++ b/core/fxcrt/cfx_utf8decoder.cpp
@@ -10,6 +10,8 @@ #include <utility> +#include "build/build_config.h" + CFX_UTF8Decoder::CFX_UTF8Decoder(ByteStringView input) { int remaining = 0; char32_t code_point = 0; @@ -54,5 +56,16 @@ return; } +#if defined(WCHAR_T_IS_UTF16) + if (code_point < 0x10000) { + buffer_ += static_cast<wchar_t>(code_point); + } else { + // Encode as UTF-16 surrogate pair. + code_point -= 0x10000; + buffer_ += 0xd800 | (code_point >> 10); + buffer_ += 0xdc00 | (code_point & 0x3ff); + } +#else buffer_ += static_cast<wchar_t>(code_point); +#endif // defined(WCHAR_T_IS_UTF16) }
diff --git a/core/fxcrt/cfx_utf8encoder.cpp b/core/fxcrt/cfx_utf8encoder.cpp index 117f3f7..e0b7ee5 100644 --- a/core/fxcrt/cfx_utf8encoder.cpp +++ b/core/fxcrt/cfx_utf8encoder.cpp
@@ -8,12 +8,36 @@ #include <stdint.h> +#include "build/build_config.h" + CFX_UTF8Encoder::CFX_UTF8Encoder() = default; CFX_UTF8Encoder::~CFX_UTF8Encoder() = default; void CFX_UTF8Encoder::Input(wchar_t code_unit) { - char32_t code_point = static_cast<char32_t>(code_unit); +#if defined(WCHAR_T_IS_UTF16) + if (code_unit >= 0xd800 && code_unit < 0xdc00) { + // High surrogate. + high_surrogate_ = code_unit; + } else if (code_unit >= 0xdc00 && code_unit <= 0xdfff) { + // Low surrogate. + if (high_surrogate_) { + char32_t code_point = code_unit & 0x3ff; + code_point |= (high_surrogate_ & 0x3ff) << 10; + code_point += 0x10000; + high_surrogate_ = 0; + AppendCodePoint(code_point); + } + } else { + high_surrogate_ = 0; + AppendCodePoint(code_unit); + } +#else + AppendCodePoint(code_unit); +#endif // defined(WCHAR_T_IS_UTF16) +} + +void CFX_UTF8Encoder::AppendCodePoint(char32_t code_point) { if (code_point > 0x10ffff) { // Invalid code point above U+10FFFF. return;
diff --git a/core/fxcrt/cfx_utf8encoder.h b/core/fxcrt/cfx_utf8encoder.h index a39f0cb..71b9ac3 100644 --- a/core/fxcrt/cfx_utf8encoder.h +++ b/core/fxcrt/cfx_utf8encoder.h
@@ -7,6 +7,7 @@ #ifndef CORE_FXCRT_CFX_UTF8ENCODER_H_ #define CORE_FXCRT_CFX_UTF8ENCODER_H_ +#include "build/build_config.h" #include "core/fxcrt/data_vector.h" #include "core/fxcrt/string_view_template.h" @@ -26,7 +27,13 @@ } private: + void AppendCodePoint(char32_t code_point); + DataVector<char> buffer_; + +#if defined(WCHAR_T_IS_UTF16) + char16_t high_surrogate_ = 0; +#endif // defined(WCHAR_T_IS_UTF16) }; #endif // CORE_FXCRT_CFX_UTF8ENCODER_H_
diff --git a/core/fxcrt/fx_string_unittest.cpp b/core/fxcrt/fx_string_unittest.cpp index b3f2864..34ffcee 100644 --- a/core/fxcrt/fx_string_unittest.cpp +++ b/core/fxcrt/fx_string_unittest.cpp
@@ -4,6 +4,7 @@ #include <limits> +#include "build/build_config.h" #include "core/fxcrt/fx_string.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/base/span.h" @@ -41,6 +42,25 @@ L"y")); } +TEST(fxstring, FXUTF8EncodeSupplementary) { + EXPECT_EQ( + "\xf0\x90\x80\x80" + "🎨" + "\xf4\x8f\xbf\xbf", + FX_UTF8Encode(L"\U00010000" + L"\U0001f3a8" + L"\U0010ffff")); +} + +#if defined(WCHAR_T_IS_UTF16) +TEST(fxstring, FXUTF8EncodeSurrogateErrorRecovery) { + EXPECT_EQ("()", FX_UTF8Encode(L"(\xd800)")) << "High"; + EXPECT_EQ("()", FX_UTF8Encode(L"(\xdc00)")) << "Low"; + EXPECT_EQ("(🎨)", FX_UTF8Encode(L"(\xd800\xd83c\xdfa8)")) << "High-high"; + EXPECT_EQ("(🎨)", FX_UTF8Encode(L"(\xd83c\xdfa8\xdc00)")) << "Low-low"; +} +#endif // defined(WCHAR_T_IS_UTF16) + TEST(fxstring, FXUTF8Decode) { EXPECT_EQ(L"", FX_UTF8Decode(ByteStringView())); EXPECT_EQ( @@ -62,6 +82,16 @@ "y")); } +TEST(fxstring, FXUTF8DecodeSupplementary) { + EXPECT_EQ( + L"\U00010000" + L"\U0001f3a8" + L"\U0010ffff", + FX_UTF8Decode("\xf0\x90\x80\x80" + "🎨" + "\xf4\x8f\xbf\xbf")); +} + TEST(fxstring, FXUTF8DecodeErrorRecovery) { EXPECT_EQ(L"(A)", FX_UTF8Decode("(\xc2\x41)")) << "Invalid continuation"; EXPECT_EQ(L"()", FX_UTF8Decode("(\xc2\xc2)")) << "Invalid continuation";