diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index 3a6a2a6b7c..7adf87508a 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -146,6 +146,7 @@ bytebuffer cac cacafire CALLCONV +CANDRABINDU capslock CARETBLINKINGENABLED CARRIAGERETURN @@ -156,6 +157,7 @@ CBash cbiex CBN cbt +Ccc CCCBB cch CCHAR @@ -293,7 +295,6 @@ CREATESTRUCT CREATESTRUCTW createvpack crisman -CRLFs crloew CRTLIBS csbi @@ -594,6 +595,7 @@ fesb FFAF ffd FFDE +FFFD FFFDb fgbg FGCOLOR @@ -614,6 +616,7 @@ FINDREGEX FINDSTRINGEXACT FINDUP FIter +FITZPATRICK FIXEDFILEINFO Flg flyouts @@ -882,10 +885,12 @@ jconcpp JLO JOBOBJECT JOBOBJECTINFOCLASS +JONGSEONG JPN jsoncpp jsprovider jumplist +JUNGSEONG KAttrs kawa Kazu @@ -904,6 +909,7 @@ keyups KILLACTIVE KILLFOCUS kinda +KIYEOK KLF KLMNO KLMNOPQRST @@ -1013,6 +1019,7 @@ luma lval LVB LVERTICAL +LVT LWA LWIN lwkmvj @@ -1209,6 +1216,7 @@ ntuser NTVDM ntverp nugetversions +NUKTA nullness nullonfailure nullopts @@ -1471,7 +1479,6 @@ READMODE rectread redef redefinable -Redir redist REDSCROLL REFCLSID @@ -1489,6 +1496,7 @@ renderengine rendersize reparented reparenting +REPH replatformed Replymessage reportfileaccesses @@ -1519,6 +1527,7 @@ rgw RIGHTALIGN RIGHTBUTTON riid +ris RIS roadmap robomac @@ -1924,6 +1933,7 @@ vga vgaoem viewkind viewports +VIRAMA Virt VIRTTERM vkey @@ -1974,8 +1984,8 @@ wchars WCIA WCIW WCSHELPER -wcsicmp wcsrev +wcswidth wddm wddmcon WDDMCONSOLECONTEXT @@ -2131,6 +2141,7 @@ XFORM XIn XManifest XMath +XNamespace xorg XPan XResource @@ -2162,6 +2173,7 @@ Zabcdefghijklmn Zabcdefghijklmnopqrstuvwxyz ZCmd ZCtrl +ZWJs zxcvbnm ZYXWVU ZYXWVUTd diff --git a/src/buffer/out/Row.cpp b/src/buffer/out/Row.cpp index aaf2dcab20..a58f1cbf02 100644 --- a/src/buffer/out/Row.cpp +++ b/src/buffer/out/Row.cpp @@ -5,10 +5,8 @@ #include "Row.hpp" #include -#include -#include "textBuffer.hpp" -#include "../../types/inc/GlyphWidth.hpp" +#include "../../types/inc/CodepointWidthDetector.hpp" // It would be nice to add checked array access in the future, but it's a little annoying to do so without impacting // performance (including Debug performance). Other languages are a little bit more ergonomic there than C++. @@ -568,6 +566,7 @@ void ROW::ReplaceAttributes(const til::CoordType beginIndex, const til::CoordTyp void ROW::ReplaceCharacters(til::CoordType columnBegin, til::CoordType width, const std::wstring_view& chars) try { + assert(width >= 1 && width <= 2); WriteHelper h{ *this, columnBegin, _columnCount, chars }; if (!h.IsValid()) { @@ -666,56 +665,91 @@ catch (...) [[msvc::forceinline]] void ROW::WriteHelper::_replaceTextUnicode(size_t ch, std::wstring_view::const_iterator it) noexcept { - const auto end = chars.end(); + auto& cwd = CodepointWidthDetector::Singleton(); - while (it != end) + // Check if the new text joins with the existing contents of the row to form a single grapheme cluster. + if (it == chars.begin()) { - unsigned int width = 1; - auto ptr = &*it; - const auto wch = *ptr; - size_t advance = 1; - - ++it; - - // Even in our slow-path we can avoid calling IsGlyphFullWidth if the current character is ASCII. - // It also allows us to skip the surrogate pair decoding at the same time. - if (wch >= 0x80) + auto colPrev = colBeg; + while (colPrev > 0 && row._uncheckedIsTrailer(--colPrev)) { - if (til::is_surrogate(wch)) + } + + const auto chPrev = row._uncheckedCharOffset(colPrev); + const std::wstring_view charsPrev{ row._chars.data() + chPrev, ch - chPrev }; + + GraphemeState state; + cwd.GraphemeNext(state, charsPrev); + cwd.GraphemeNext(state, chars); + + if (state.len > 0) + { + colBegDirty = colPrev; + colEnd = colPrev; + + const auto width = std::max(1, state.width); + const auto colEndNew = gsl::narrow_cast(colEnd + width); + if (colEndNew > colLimit) { - if (it != end && til::is_leading_surrogate(wch) && til::is_trailing_surrogate(*it)) - { - advance = 2; - ++it; - } - else - { - ptr = &UNICODE_REPLACEMENT; - } + colEndDirty = colLimit; + charsConsumed = ch - chBeg; + return; } - width = IsGlyphFullWidth({ ptr, advance }) + 1u; - } + // Fill our char-offset buffer with 1 entry containing the mapping from the + // current column (colEnd) to the start of the glyph in the string (ch)... + til::at(row._charOffsets, colEnd++) = gsl::narrow_cast(chPrev); + // ...followed by 0-N entries containing an indication that the + // columns are just a wide-glyph extension of the preceding one. + while (colEnd < colEndNew) + { + til::at(row._charOffsets, colEnd++) = gsl::narrow_cast(chPrev | CharOffsetsTrailer); + } - const auto colEndNew = gsl::narrow_cast(colEnd + width); - if (colEndNew > colLimit) + ch += state.len; + it += state.len; + } + } + else + { + // The non-ASCII character we have encountered may be a combining mark, like "a^" which is then displayed as "â". + // In order to recognize both characters as a single grapheme, we need to back up by 1 ASCII character + // and let MeasureNext() find the next proper grapheme boundary. + --colEnd; + --ch; + --it; + } + + if (const auto end = chars.end(); it != end) + { + GraphemeState state{ .beg = &*it }; + + do { - colEndDirty = colLimit; - charsConsumed = ch - chBeg; - return; - } + cwd.GraphemeNext(state, chars); - // Fill our char-offset buffer with 1 entry containing the mapping from the - // current column (colEnd) to the start of the glyph in the string (ch)... - til::at(row._charOffsets, colEnd++) = gsl::narrow_cast(ch); - // ...followed by 0-N entries containing an indication that the - // columns are just a wide-glyph extension of the preceding one. - while (colEnd < colEndNew) - { - til::at(row._charOffsets, colEnd++) = gsl::narrow_cast(ch | CharOffsetsTrailer); - } + const auto width = std::max(1, state.width); + const auto colEndNew = gsl::narrow_cast(colEnd + width); + if (colEndNew > colLimit) + { + colEndDirty = colLimit; + charsConsumed = ch - chBeg; + return; + } - ch += advance; + // Fill our char-offset buffer with 1 entry containing the mapping from the + // current column (colEnd) to the start of the glyph in the string (ch)... + til::at(row._charOffsets, colEnd++) = gsl::narrow_cast(ch); + // ...followed by 0-N entries containing an indication that the + // columns are just a wide-glyph extension of the preceding one. + while (colEnd < colEndNew) + { + til::at(row._charOffsets, colEnd++) = gsl::narrow_cast(ch | CharOffsetsTrailer); + } + + ch += state.len; + it += state.len; + } while (it != end); } colEndDirty = colEnd; @@ -1058,7 +1092,7 @@ std::wstring_view ROW::GetText() const noexcept std::wstring_view ROW::GetText(til::CoordType columnBegin, til::CoordType columnEnd) const noexcept { - const til::CoordType columns = _columnCount; + const auto columns = GetReadableColumnCount(); const auto colBeg = clamp(columnBegin, 0, columns); const auto colEnd = clamp(columnEnd, colBeg, columns); const size_t chBeg = _uncheckedCharOffset(gsl::narrow_cast(colBeg)); diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 3aa6e8b312..b54d0d8837 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -2,14 +2,12 @@ // Licensed under the MIT license. #include "precomp.h" - #include "textBuffer.hpp" #include -#include #include "UTextAdapter.h" -#include "../../types/inc/GlyphWidth.hpp" +#include "../../types/inc/CodepointWidthDetector.hpp" #include "../renderer/base/renderer.hpp" #include "../types/inc/utils.hpp" #include "search.h" @@ -376,17 +374,23 @@ TextBufferCellIterator TextBuffer::GetCellDataAt(const til::point at, const View // Given the character offset `position` in the `chars` string, this function returns the starting position of the next grapheme. // For instance, given a `chars` of L"x\uD83D\uDE42y" and a `position` of 1 it'll return 3. // GraphemePrev would do the exact inverse of this operation. -// In the future, these functions are expected to also deliver information about how many columns a grapheme occupies. -// (I know that mere UTF-16 code point iteration doesn't handle graphemes, but that's what we're working towards.) size_t TextBuffer::GraphemeNext(const std::wstring_view& chars, size_t position) noexcept { - return til::utf16_iterate_next(chars, position); + auto& cwd = CodepointWidthDetector::Singleton(); +#pragma warning(suppress : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1). + GraphemeState state{ .beg = chars.data() + position }; + cwd.GraphemeNext(state, chars); + return position + state.len; } // It's the counterpart to GraphemeNext. See GraphemeNext. size_t TextBuffer::GraphemePrev(const std::wstring_view& chars, size_t position) noexcept { - return til::utf16_iterate_prev(chars, position); + auto& cwd = CodepointWidthDetector::Singleton(); +#pragma warning(suppress : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1). + GraphemeState state{ .beg = chars.data() + position }; + cwd.GraphemePrev(state, chars); + return position - state.len; } // Ever wondered how much space a piece of text needs before inserting it? This function will tell you! @@ -413,7 +417,7 @@ size_t TextBuffer::FitTextIntoColumns(const std::wstring_view& chars, til::Coord { } - const auto dist = gsl::narrow_cast(it - beg); + auto dist = gsl::narrow_cast(it - beg); auto col = gsl::narrow_cast(dist); if (it == asciiEnd) [[likely]] @@ -423,33 +427,26 @@ size_t TextBuffer::FitTextIntoColumns(const std::wstring_view& chars, til::Coord } // Unicode slow-path where we need to count text and columns separately. - for (;;) + auto& cwd = CodepointWidthDetector::Singleton(); + const auto len = chars.size(); + + // The non-ASCII character we have encountered may be a combining mark, like "a^" which is then displayed as "â". + // In order to recognize both characters as a single grapheme, we need to back up by 1 ASCII character + // and let GraphemeNext() find the next proper grapheme boundary. + if (dist != 0) { - auto ptr = &*it; - const auto wch = *ptr; - size_t len = 1; + dist--; + col--; + } - col++; +#pragma warning(suppress : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1). + GraphemeState state{ .beg = chars.data() + dist }; - // Even in our slow-path we can avoid calling IsGlyphFullWidth if the current character is ASCII. - // It also allows us to skip the surrogate pair decoding at the same time. - if (wch >= 0x80) - { - if (til::is_surrogate(wch)) - { - const auto it2 = it + 1; - if (til::is_leading_surrogate(wch) && it2 != end && til::is_trailing_surrogate(*it2)) - { - len = 2; - } - else - { - ptr = &UNICODE_REPLACEMENT; - } - } - - col += IsGlyphFullWidth({ ptr, len }); - } + while (dist < len) + { + cwd.GraphemeNext(state, chars); + dist += state.len; + col += state.width; // If we ran out of columns, we need to always return `columnLimit` and not `cols`, // because if we tried inserting a wide glyph into just 1 remaining column it will @@ -458,17 +455,13 @@ size_t TextBuffer::FitTextIntoColumns(const std::wstring_view& chars, til::Coord if (col > columnLimit) { columns = columnLimit; - return gsl::narrow_cast(it - beg); - } - - // But if we simply ran out of text we just need to return the actual number of columns. - it += len; - if (it == end) - { - columns = col; - return chars.size(); + return dist; } } + + // But if we simply ran out of text we just need to return the actual number of columns. + columns = col; + return chars.size(); } // Pretend as if `position` is a regular cursor in the TextBuffer. diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 7980f0cda4..faad3d5ace 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -4,18 +4,11 @@ #include "pch.h" #include "TerminalPage.h" -#include "TerminalPage.g.cpp" -#include "RenameWindowRequestedArgs.g.cpp" -#include "RequestMoveContentArgs.g.cpp" -#include "RequestReceiveContentArgs.g.cpp" -#include "LaunchPositionRequest.g.cpp" -#include - -#include #include #include #include +#include #include "../../types/inc/utils.hpp" #include "App.h" @@ -24,7 +17,12 @@ #include "SettingsPaneContent.h" #include "ScratchpadContent.h" #include "TabRowControl.h" -#include "Utils.h" + +#include "TerminalPage.g.cpp" +#include "RenameWindowRequestedArgs.g.cpp" +#include "RequestMoveContentArgs.g.cpp" +#include "RequestReceiveContentArgs.g.cpp" +#include "LaunchPositionRequest.g.cpp" using namespace winrt; using namespace winrt::Microsoft::Terminal::Control; @@ -1258,6 +1256,20 @@ namespace winrt::TerminalApp::implementation TerminalSettings settings, const bool inheritCursor) { + static const auto textMeasurement = [&]() -> std::wstring_view { + switch (_settings.GlobalSettings().TextMeasurement()) + { + case TextMeasurement::Graphemes: + return L"graphemes"; + case TextMeasurement::Wcswidth: + return L"wcswidth"; + case TextMeasurement::Console: + return L"console"; + default: + return {}; + } + }(); + TerminalConnection::ITerminalConnection connection{ nullptr }; auto connectionType = profile.ConnectionType(); @@ -1329,6 +1341,11 @@ namespace winrt::TerminalApp::implementation } } + if (!textMeasurement.empty()) + { + valueSet.Insert(L"textMeasurement", Windows::Foundation::PropertyValue::CreateString(textMeasurement)); + } + if (const auto id = settings.SessionId(); id != winrt::guid{}) { valueSet.Insert(L"sessionId", Windows::Foundation::PropertyValue::CreateGuid(id)); diff --git a/src/cascadia/TerminalConnection/ConptyConnection.cpp b/src/cascadia/TerminalConnection/ConptyConnection.cpp index 3f2c87a8ba..c70a85e1dc 100644 --- a/src/cascadia/TerminalConnection/ConptyConnection.cpp +++ b/src/cascadia/TerminalConnection/ConptyConnection.cpp @@ -5,7 +5,6 @@ #include "ConptyConnection.h" #include -#include #include #include "CTerminalHandoff.h" @@ -259,11 +258,39 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation _cols = unbox_prop_or(settings, L"initialCols", _cols); _sessionId = unbox_prop_or(settings, L"sessionId", _sessionId); _environment = settings.TryLookup(L"environment").try_as(); - _inheritCursor = unbox_prop_or(settings, L"inheritCursor", _inheritCursor); _profileGuid = unbox_prop_or(settings, L"profileGuid", _profileGuid); - const auto& initialEnvironment{ unbox_prop_or(settings, L"initialEnvironment", L"") }; + _flags = PSEUDOCONSOLE_RESIZE_QUIRK; + // If we're using an existing buffer, we want the new connection + // to reuse the existing cursor. When not setting this flag, the + // PseudoConsole sends a clear screen VT code which our renderer + // interprets into making all the previous lines be outside the + // current viewport. + const auto inheritCursor = unbox_prop_or(settings, L"inheritCursor", false); + if (inheritCursor) + { + _flags |= PSEUDOCONSOLE_INHERIT_CURSOR; + } + + const auto textMeasurement = unbox_prop_or(settings, L"textMeasurement", winrt::hstring{}); + if (!textMeasurement.empty()) + { + if (textMeasurement == L"graphemes") + { + _flags |= PSEUDOCONSOLE_GLYPH_WIDTH_GRAPHEMES; + } + else if (textMeasurement == L"wcswidth") + { + _flags |= PSEUDOCONSOLE_GLYPH_WIDTH_WCSWIDTH; + } + else if (textMeasurement == L"console") + { + _flags |= PSEUDOCONSOLE_GLYPH_WIDTH_CONSOLE; + } + } + + const auto& initialEnvironment{ unbox_prop_or(settings, L"initialEnvironment", L"") }; const bool reloadEnvironmentVariables = unbox_prop_or(settings, L"reloadEnvironmentVariables", false); if (reloadEnvironmentVariables) @@ -318,19 +345,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation // handoff from an already-started PTY process. if (!_inPipe) { - DWORD flags = PSEUDOCONSOLE_RESIZE_QUIRK; - - // If we're using an existing buffer, we want the new connection - // to reuse the existing cursor. When not setting this flag, the - // PseudoConsole sends a clear screen VT code which our renderer - // interprets into making all the previous lines be outside the - // current viewport. - if (_inheritCursor) - { - flags |= PSEUDOCONSOLE_INHERIT_CURSOR; - } - - THROW_IF_FAILED(_CreatePseudoConsoleAndPipes(til::unwrap_coord_size(dimensions), flags, &_inPipe, &_outPipe, &_hPC)); + THROW_IF_FAILED(_CreatePseudoConsoleAndPipes(til::unwrap_coord_size(dimensions), _flags, &_inPipe, &_outPipe, &_hPC)); if (_initialParentHwnd != 0) { diff --git a/src/cascadia/TerminalConnection/ConptyConnection.h b/src/cascadia/TerminalConnection/ConptyConnection.h index 246621c5ee..1ce3b72ba9 100644 --- a/src/cascadia/TerminalConnection/ConptyConnection.h +++ b/src/cascadia/TerminalConnection/ConptyConnection.h @@ -90,7 +90,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation til::u8state _u8State{}; std::wstring _u16Str{}; std::array _buffer{}; - bool _inheritCursor{ false }; + DWORD _flags{ 0 }; til::env _initialEnv{}; guid _profileGuid{}; diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 146fb99b68..cd75d0277d 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -18,6 +18,7 @@ #include "../../renderer/atlas/AtlasEngine.h" #include "../../renderer/base/renderer.hpp" #include "../../renderer/uia/UiaRenderer.hpp" +#include "../../types/inc/CodepointWidthDetector.hpp" #include "ControlCore.g.cpp" #include "SelectionColor.g.cpp" @@ -71,6 +72,23 @@ namespace winrt::Microsoft::Terminal::Control::implementation _desiredFont{ DEFAULT_FONT_FACE, 0, DEFAULT_FONT_WEIGHT, DEFAULT_FONT_SIZE, CP_UTF8 }, _actualFont{ DEFAULT_FONT_FACE, 0, DEFAULT_FONT_WEIGHT, { 0, DEFAULT_FONT_SIZE }, CP_UTF8, false } { + static const auto textMeasurementInit = [&]() { + TextMeasurementMode mode = TextMeasurementMode::Graphemes; + switch (settings.TextMeasurement()) + { + case TextMeasurement::Wcswidth: + mode = TextMeasurementMode::Wcswidth; + break; + case TextMeasurement::Console: + mode = TextMeasurementMode::Console; + break; + default: + break; + } + CodepointWidthDetector::Singleton().Reset(mode); + return true; + }(); + _settings = winrt::make_self(settings, unfocusedAppearance); _terminal = std::make_shared<::Microsoft::Terminal::Core::Terminal>(); const auto lock = _terminal->LockForWriting(); diff --git a/src/cascadia/TerminalControl/EventArgs.idl b/src/cascadia/TerminalControl/EventArgs.idl index 6b0bf11e35..8521f13f97 100644 --- a/src/cascadia/TerminalControl/EventArgs.idl +++ b/src/cascadia/TerminalControl/EventArgs.idl @@ -18,6 +18,13 @@ namespace Microsoft.Terminal.Control Direct3D11, }; + enum TextMeasurement + { + Graphemes, + Wcswidth, + Console, + }; + runtimeclass FontSizeChangedArgs { Int32 Width { get; }; diff --git a/src/cascadia/TerminalControl/IControlSettings.idl b/src/cascadia/TerminalControl/IControlSettings.idl index 4d5badb454..a7c7581ed5 100644 --- a/src/cascadia/TerminalControl/IControlSettings.idl +++ b/src/cascadia/TerminalControl/IControlSettings.idl @@ -62,6 +62,7 @@ namespace Microsoft.Terminal.Control Microsoft.Terminal.Control.GraphicsAPI GraphicsAPI { get; }; Boolean DisablePartialInvalidation { get; }; Boolean SoftwareRendering { get; }; + Microsoft.Terminal.Control.TextMeasurement TextMeasurement { get; }; Boolean ShowMarks { get; }; Boolean UseBackgroundImageForWindow { get; }; Boolean RightClickContextMenu { get; }; diff --git a/src/cascadia/TerminalSettingsEditor/Rendering.xaml b/src/cascadia/TerminalSettingsEditor/Rendering.xaml index 4b9534167d..6285ebc4ff 100644 --- a/src/cascadia/TerminalSettingsEditor/Rendering.xaml +++ b/src/cascadia/TerminalSettingsEditor/Rendering.xaml @@ -41,5 +41,13 @@ + + + + diff --git a/src/cascadia/TerminalSettingsEditor/RenderingViewModel.cpp b/src/cascadia/TerminalSettingsEditor/RenderingViewModel.cpp index 74c4d5922a..f369e0f14f 100644 --- a/src/cascadia/TerminalSettingsEditor/RenderingViewModel.cpp +++ b/src/cascadia/TerminalSettingsEditor/RenderingViewModel.cpp @@ -17,5 +17,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation _settings{ std::move(settings) } { INITIALIZE_BINDABLE_ENUM_SETTING(GraphicsAPI, GraphicsAPI, winrt::Microsoft::Terminal::Control::GraphicsAPI, L"Globals_GraphicsAPI_", L"Text"); + INITIALIZE_BINDABLE_ENUM_SETTING(TextMeasurement, TextMeasurement, winrt::Microsoft::Terminal::Control::TextMeasurement, L"Globals_TextMeasurement_", L"Text"); } } diff --git a/src/cascadia/TerminalSettingsEditor/RenderingViewModel.h b/src/cascadia/TerminalSettingsEditor/RenderingViewModel.h index b3042d893a..1eda9e515f 100644 --- a/src/cascadia/TerminalSettingsEditor/RenderingViewModel.h +++ b/src/cascadia/TerminalSettingsEditor/RenderingViewModel.h @@ -16,6 +16,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation GETSET_BINDABLE_ENUM_SETTING(GraphicsAPI, winrt::Microsoft::Terminal::Control::GraphicsAPI, _settings.GlobalSettings().GraphicsAPI); PERMANENT_OBSERVABLE_PROJECTED_SETTING(_settings.GlobalSettings(), DisablePartialInvalidation); PERMANENT_OBSERVABLE_PROJECTED_SETTING(_settings.GlobalSettings(), SoftwareRendering); + GETSET_BINDABLE_ENUM_SETTING(TextMeasurement, winrt::Microsoft::Terminal::Control::TextMeasurement, _settings.GlobalSettings().TextMeasurement); private: Model::CascadiaSettings _settings{ nullptr }; diff --git a/src/cascadia/TerminalSettingsEditor/RenderingViewModel.idl b/src/cascadia/TerminalSettingsEditor/RenderingViewModel.idl index 1ca164fbd9..a9fa2ddb35 100644 --- a/src/cascadia/TerminalSettingsEditor/RenderingViewModel.idl +++ b/src/cascadia/TerminalSettingsEditor/RenderingViewModel.idl @@ -15,5 +15,7 @@ namespace Microsoft.Terminal.Settings.Editor Windows.Foundation.Collections.IObservableVector GraphicsAPIList { get; }; PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, DisablePartialInvalidation); PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, SoftwareRendering); + IInspectable CurrentTextMeasurement; + Windows.Foundation.Collections.IObservableVector TextMeasurementList { get; }; } } diff --git a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw index a18db5349a..cffae127f5 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw @@ -342,6 +342,24 @@ When enabled, the terminal will use a software rasterizer (WARP). This setting should be left disabled under almost all circumstances. {Locked="WARP"} WARP is the "Windows Advanced Rasterization Platform". + + Text measurement mode + This text is shown next to a list of choices. + + + This changes the way incoming text is grouped into cells. The "Grapheme clusters" option is the most modern and Unicode-correct way to do so, while "wcswidth" is a common approach on UNIX, and "Windows Console" replicates the way it used to work on Windows. Changing this setting requires a restart of Windows Terminal and it only applies to applications launched from within it. + + + Grapheme clusters + The default choice between multiple graphics APIs. + + + wcswidth + {Locked="wcswidth"} + + + Windows Console + Columns Header for a control to choose the number of columns in the terminal's text grid. diff --git a/src/cascadia/TerminalSettingsModel/EnumMappings.cpp b/src/cascadia/TerminalSettingsModel/EnumMappings.cpp index 15665908b7..87b65b4fb2 100644 --- a/src/cascadia/TerminalSettingsModel/EnumMappings.cpp +++ b/src/cascadia/TerminalSettingsModel/EnumMappings.cpp @@ -40,6 +40,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation DEFINE_ENUM_MAP(Model::WindowingMode, WindowingMode); DEFINE_ENUM_MAP(Microsoft::Terminal::Core::MatchMode, MatchMode); DEFINE_ENUM_MAP(Microsoft::Terminal::Control::GraphicsAPI, GraphicsAPI); + DEFINE_ENUM_MAP(Microsoft::Terminal::Control::TextMeasurement, TextMeasurement); // Profile Settings DEFINE_ENUM_MAP(Model::CloseOnExitMode, CloseOnExitMode); diff --git a/src/cascadia/TerminalSettingsModel/EnumMappings.h b/src/cascadia/TerminalSettingsModel/EnumMappings.h index 722ce92095..f76274b049 100644 --- a/src/cascadia/TerminalSettingsModel/EnumMappings.h +++ b/src/cascadia/TerminalSettingsModel/EnumMappings.h @@ -36,6 +36,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static winrt::Windows::Foundation::Collections::IMap WindowingMode(); static winrt::Windows::Foundation::Collections::IMap MatchMode(); static winrt::Windows::Foundation::Collections::IMap GraphicsAPI(); + static winrt::Windows::Foundation::Collections::IMap TextMeasurement(); // Profile Settings static winrt::Windows::Foundation::Collections::IMap CloseOnExitMode(); diff --git a/src/cascadia/TerminalSettingsModel/EnumMappings.idl b/src/cascadia/TerminalSettingsModel/EnumMappings.idl index 1180118299..f5def5fbaf 100644 --- a/src/cascadia/TerminalSettingsModel/EnumMappings.idl +++ b/src/cascadia/TerminalSettingsModel/EnumMappings.idl @@ -18,6 +18,7 @@ namespace Microsoft.Terminal.Settings.Model static Windows.Foundation.Collections.IMap WindowingMode { get; }; static Windows.Foundation.Collections.IMap MatchMode { get; }; static Windows.Foundation.Collections.IMap GraphicsAPI { get; }; + static Windows.Foundation.Collections.IMap TextMeasurement { get; }; // Profile Settings static Windows.Foundation.Collections.IMap CloseOnExitMode { get; }; diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp index 7151444ce7..f51ece15e1 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp @@ -243,6 +243,11 @@ Json::Value GlobalAppSettings::ToJson() { _GraphicsAPI.reset(); } + if (_TextMeasurement == Control::TextMeasurement::Graphemes) + { + _TextMeasurement.reset(); + } + if (_DisablePartialInvalidation == false) { _DisablePartialInvalidation.reset(); diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl index 7e46bcc051..1a1bab60b9 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl @@ -79,6 +79,7 @@ namespace Microsoft.Terminal.Settings.Model INHERITABLE_SETTING(Microsoft.Terminal.Control.GraphicsAPI, GraphicsAPI); INHERITABLE_SETTING(Boolean, DisablePartialInvalidation); INHERITABLE_SETTING(Boolean, SoftwareRendering); + INHERITABLE_SETTING(Microsoft.Terminal.Control.TextMeasurement, TextMeasurement); INHERITABLE_SETTING(Boolean, UseBackgroundImageForWindow); INHERITABLE_SETTING(Boolean, ForceVTInput); INHERITABLE_SETTING(Boolean, DebugFeaturesEnabled); diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h index e2cbff9c02..50635920b2 100644 --- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h +++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h @@ -27,6 +27,7 @@ Author(s): X(winrt::Microsoft::Terminal::Control::GraphicsAPI, GraphicsAPI, "rendering.graphicsAPI") \ X(bool, DisablePartialInvalidation, "rendering.disablePartialInvalidation", false) \ X(bool, SoftwareRendering, "rendering.software", false) \ + X(winrt::Microsoft::Terminal::Control::TextMeasurement, TextMeasurement, "compatibility.textMeasurement") \ X(bool, UseBackgroundImageForWindow, "experimental.useBackgroundImageForWindow", false) \ X(bool, ForceVTInput, "experimental.input.forceVT", false) \ X(bool, TrimBlockSelection, "trimBlockSelection", true) \ diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp b/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp index 4afdcc3f25..52b3d030f9 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp @@ -366,6 +366,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation _GraphicsAPI = globalSettings.GraphicsAPI(); _DisablePartialInvalidation = globalSettings.DisablePartialInvalidation(); _SoftwareRendering = globalSettings.SoftwareRendering(); + _TextMeasurement = globalSettings.TextMeasurement(); _UseBackgroundImageForWindow = globalSettings.UseBackgroundImageForWindow(); _ForceVTInput = globalSettings.ForceVTInput(); _TrimBlockSelection = globalSettings.TrimBlockSelection(); diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.h b/src/cascadia/TerminalSettingsModel/TerminalSettings.h index 81bb875898..ee4e0ca939 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.h @@ -159,6 +159,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation INHERITABLE_SETTING(Model::TerminalSettings, Microsoft::Terminal::Control::GraphicsAPI, GraphicsAPI); INHERITABLE_SETTING(Model::TerminalSettings, bool, DisablePartialInvalidation, false); INHERITABLE_SETTING(Model::TerminalSettings, bool, SoftwareRendering, false); + INHERITABLE_SETTING(Model::TerminalSettings, Microsoft::Terminal::Control::TextMeasurement, TextMeasurement); INHERITABLE_SETTING(Model::TerminalSettings, bool, UseBackgroundImageForWindow, false); INHERITABLE_SETTING(Model::TerminalSettings, bool, ForceVTInput, false); diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h index da06d0cfba..76aa31ae59 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h @@ -771,3 +771,12 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Control::GraphicsAPI) pair_type{ "direct3d11", ValueType::Direct3D11 }, }; }; + +JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Control::TextMeasurement) +{ + JSON_MAPPINGS(3) = { + pair_type{ "graphemes", ValueType::Graphemes }, + pair_type{ "wcswidth", ValueType::Wcswidth }, + pair_type{ "console", ValueType::Console }, + }; +}; diff --git a/src/cascadia/inc/ControlProperties.h b/src/cascadia/inc/ControlProperties.h index a3dcb87e9d..6f70a868bf 100644 --- a/src/cascadia/inc/ControlProperties.h +++ b/src/cascadia/inc/ControlProperties.h @@ -77,6 +77,7 @@ X(winrt::Microsoft::Terminal::Control::GraphicsAPI, GraphicsAPI) \ X(bool, DisablePartialInvalidation, false) \ X(bool, SoftwareRendering, false) \ + X(winrt::Microsoft::Terminal::Control::TextMeasurement, TextMeasurement) \ X(bool, UseBackgroundImageForWindow, false) \ X(bool, ShowMarks, false) \ X(winrt::Microsoft::Terminal::Control::CopyFormat, CopyFormatting, 0) \ diff --git a/src/host/ConsoleArguments.cpp b/src/host/ConsoleArguments.cpp index 6a22e8170a..422f563bd4 100644 --- a/src/host/ConsoleArguments.cpp +++ b/src/host/ConsoleArguments.cpp @@ -23,6 +23,7 @@ const std::wstring_view ConsoleArguments::RESIZE_QUIRK = L"--resizeQuirk"; const std::wstring_view ConsoleArguments::FEATURE_ARG = L"--feature"; const std::wstring_view ConsoleArguments::FEATURE_PTY_ARG = L"pty"; const std::wstring_view ConsoleArguments::COM_SERVER_ARG = L"-Embedding"; +static constexpr std::wstring_view GLYPH_WIDTH{ L"--textMeasurement" }; // NOTE: Thinking about adding more commandline args that control conpty, for // the Terminal? Make sure you add them to the commandline in // ConsoleEstablishHandoff. We use that to initialize the ConsoleArguments for a @@ -507,6 +508,10 @@ void ConsoleArguments::s_ConsumeArg(_Inout_ std::vector& args, _In s_ConsumeArg(args, i); hr = S_OK; } + else if (arg == GLYPH_WIDTH) + { + hr = s_GetArgumentValue(args, i, &_textMeasurement); + } else if (arg == CLIENT_COMMANDLINE_ARG) { // Everything after this is the explicit commandline @@ -630,6 +635,11 @@ std::wstring ConsoleArguments::GetVtMode() const return _vtMode; } +const std::wstring& ConsoleArguments::GetTextMeasurement() const +{ + return _textMeasurement; +} + bool ConsoleArguments::GetForceV1() const { return _forceV1; diff --git a/src/host/ConsoleArguments.hpp b/src/host/ConsoleArguments.hpp index 926bc06392..3d26ae6490 100644 --- a/src/host/ConsoleArguments.hpp +++ b/src/host/ConsoleArguments.hpp @@ -47,6 +47,7 @@ public: std::wstring GetOriginalCommandLine() const; std::wstring GetClientCommandline() const; std::wstring GetVtMode() const; + const std::wstring& GetTextMeasurement() const; bool GetForceV1() const; bool GetForceNoHandoff() const; @@ -123,6 +124,7 @@ private: HANDLE _vtOutHandle; std::wstring _vtMode; + std::wstring _textMeasurement; bool _forceNoHandoff; bool _forceV1; diff --git a/src/host/VtIo.cpp b/src/host/VtIo.cpp index 23500c2f6a..3170d3a818 100644 --- a/src/host/VtIo.cpp +++ b/src/host/VtIo.cpp @@ -9,6 +9,7 @@ #include "../renderer/vt/Xterm256Engine.hpp" #include "../renderer/base/renderer.hpp" +#include "../types/inc/CodepointWidthDetector.hpp" #include "../types/inc/utils.hpp" #include "handle.h" // LockConsole #include "input.h" // ProcessCtrlEvents @@ -73,6 +74,28 @@ VtIo::VtIo() : // If we were already given VT handles, set up the VT IO engine to use those. if (pArgs->InConptyMode()) { + // Honestly, no idea where else to put this. + if (const auto& textMeasurement = pArgs->GetTextMeasurement(); !textMeasurement.empty()) + { + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + SettingsTextMeasurementMode settingsMode = SettingsTextMeasurementMode::Graphemes; + TextMeasurementMode mode = TextMeasurementMode::Graphemes; + + if (textMeasurement == L"wcswidth") + { + settingsMode = SettingsTextMeasurementMode::Wcswidth; + mode = TextMeasurementMode::Wcswidth; + } + else if (textMeasurement == L"console") + { + settingsMode = SettingsTextMeasurementMode::Console; + mode = TextMeasurementMode::Console; + } + + gci.SetTextMeasurementMode(settingsMode); + CodepointWidthDetector::Singleton().Reset(mode); + } + return _Initialize(pArgs->GetVtInHandle(), pArgs->GetVtOutHandle(), pArgs->GetVtMode(), pArgs->GetSignalHandle()); } // Didn't need to initialize if we didn't have VT stuff. It's still OK, but report we did nothing. diff --git a/src/host/screenInfo.cpp b/src/host/screenInfo.cpp index d77a916ab8..3a8141e81e 100644 --- a/src/host/screenInfo.cpp +++ b/src/host/screenInfo.cpp @@ -2,24 +2,13 @@ // Licensed under the MIT license. #include "precomp.h" - #include "screenInfo.hpp" -#include "dbcs.h" + #include "output.h" -#include "_output.h" -#include "misc.h" -#include "handle.h" - -#include #include "../interactivity/inc/ServiceLocator.hpp" -#include "../types/inc/Viewport.hpp" -#include "../types/inc/GlyphWidth.hpp" -#include "../terminal/parser/OutputStateMachineEngine.hpp" - +#include "../types/inc/CodepointWidthDetector.hpp" #include "../types/inc/convert.hpp" -#pragma hdrstop - using namespace Microsoft::Console; using namespace Microsoft::Console::Types; using namespace Microsoft::Console::Render; @@ -524,15 +513,30 @@ void SCREEN_INFORMATION::RefreshFontWithRenderer() { if (IsActiveScreenBuffer()) { - // Hand the handle to our internal structure to the font change trigger in case it updates it based on what's appropriate. - if (ServiceLocator::LocateGlobals().pRender != nullptr) - { - ServiceLocator::LocateGlobals().pRender->TriggerFontChange(ServiceLocator::LocateGlobals().dpi, - GetDesiredFont(), - GetCurrentFont()); + auto& globals = ServiceLocator::LocateGlobals(); + const auto& gci = globals.getConsoleInformation(); - NotifyGlyphWidthFontChanged(); + // Hand the handle to our internal structure to the font change trigger in case it updates it based on what's appropriate. + if (globals.pRender != nullptr) + { + globals.pRender->TriggerFontChange(globals.dpi, GetDesiredFont(), GetCurrentFont()); } + + TextMeasurementMode mode; + switch (gci.GetTextMeasurementMode()) + { + case SettingsTextMeasurementMode::Wcswidth: + mode = TextMeasurementMode::Wcswidth; + break; + case SettingsTextMeasurementMode::Console: + mode = TextMeasurementMode::Console; + break; + default: + mode = TextMeasurementMode::Graphemes; + break; + } + + CodepointWidthDetector::Singleton().Reset(mode); } } @@ -2466,7 +2470,6 @@ Viewport SCREEN_INFORMATION::GetVirtualViewport() const noexcept // Method Description: // - Returns true if the character at the cursor's current position is wide. -// See IsGlyphFullWidth // Arguments: // - // Return Value: diff --git a/src/host/settings.cpp b/src/host/settings.cpp index 0798fe847a..e0b07e38f7 100644 --- a/src/host/settings.cpp +++ b/src/host/settings.cpp @@ -777,6 +777,16 @@ bool Settings::GetCopyColor() const noexcept return _fCopyColor; } +SettingsTextMeasurementMode Settings::GetTextMeasurementMode() const noexcept +{ + return _textMeasurement; +} + +void Settings::SetTextMeasurementMode(const SettingsTextMeasurementMode mode) noexcept +{ + _textMeasurement = mode; +} + bool Settings::GetEnableBuiltinGlyphs() const noexcept { return _fEnableBuiltinGlyphs; diff --git a/src/host/settings.hpp b/src/host/settings.hpp index a254589755..94c38afbf4 100644 --- a/src/host/settings.hpp +++ b/src/host/settings.hpp @@ -24,6 +24,13 @@ constexpr unsigned short MIN_WINDOW_OPACITY = 0x4D; // 0x4D is approximately 30% #include "ConsoleArguments.hpp" #include "../renderer/inc/RenderSettings.hpp" +enum class SettingsTextMeasurementMode : DWORD +{ + Graphemes, + Wcswidth, + Console, +}; + class Settings { using RenderSettings = Microsoft::Console::Render::RenderSettings; @@ -171,6 +178,8 @@ public: bool GetUseDx() const noexcept; bool GetCopyColor() const noexcept; + SettingsTextMeasurementMode GetTextMeasurementMode() const noexcept; + void SetTextMeasurementMode(SettingsTextMeasurementMode mode) noexcept; bool GetEnableBuiltinGlyphs() const noexcept; private: @@ -213,6 +222,7 @@ private: std::wstring _LaunchFaceName; bool _fAllowAltF4Close; DWORD _dwVirtTermLevel; + SettingsTextMeasurementMode _textMeasurement = SettingsTextMeasurementMode::Graphemes; bool _fUseDx; bool _fCopyColor; bool _fEnableBuiltinGlyphs = true; diff --git a/src/host/srvinit.cpp b/src/host/srvinit.cpp index 9f7d7adc78..a29c31ad6f 100644 --- a/src/host/srvinit.cpp +++ b/src/host/srvinit.cpp @@ -2,33 +2,18 @@ // Licensed under the MIT license. #include "precomp.h" - #include "srvinit.h" #include "dbcs.h" #include "handle.h" #include "registry.hpp" #include "renderFontDefaults.hpp" - -#include "ApiRoutines.h" - -#include "../types/inc/GlyphWidth.hpp" - -#include "../server/DeviceHandle.h" -#include "../server/Entrypoints.h" -#include "../server/IoSorter.h" - -#include "../interactivity/inc/ISystemConfigurationProvider.hpp" -#include "../interactivity/inc/ServiceLocator.hpp" #include "../interactivity/base/ApiDetector.hpp" #include "../interactivity/base/RemoteConsoleControl.hpp" - -#include "renderData.hpp" -#include "../renderer/base/renderer.hpp" - -#include "../inc/conint.h" - -#include "tracing.hpp" +#include "../interactivity/inc/ServiceLocator.hpp" +#include "../server/DeviceHandle.h" +#include "../server/IoSorter.h" +#include "../types/inc/CodepointWidthDetector.hpp" #if TIL_FEATURE_RECEIVEINCOMINGHANDOFF_ENABLED #include "ITerminalHandoff.h" @@ -882,8 +867,9 @@ PWSTR TranslateConsoleTitle(_In_ PCWSTR pwszConsoleTitle, const BOOL fUnexpand, // Set up the renderer to be used to calculate the width of a glyph, // should we be unable to figure out its width another way. - auto pfn = [ObjectPtr = static_cast(g.pRender)](auto&& PH1) { return ObjectPtr->IsGlyphWideByFont(std::forward(PH1)); }; - SetGlyphWidthFallback(pfn); + CodepointWidthDetector::Singleton().SetFallbackMethod([](const std::wstring_view& glyph) { + return ServiceLocator::LocateGlobals().pRender->IsGlyphWideByFont(glyph); + }); } catch (...) { diff --git a/src/host/ut_host/CodepointWidthDetectorTests.cpp b/src/host/ut_host/CodepointWidthDetectorTests.cpp deleted file mode 100644 index 9f433eb7c3..0000000000 --- a/src/host/ut_host/CodepointWidthDetectorTests.cpp +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "precomp.h" -#include "WexTestClass.h" -#include "../../inc/consoletaeftemplates.hpp" -#include "CommonState.hpp" - -#include "../types/inc/CodepointWidthDetector.hpp" - -using namespace WEX::Logging; - -static constexpr std::wstring_view emoji = L"\xD83E\xDD22"; // U+1F922 nauseated face - -static constexpr std::wstring_view ambiguous = L"\x414"; // U+0414 cyrillic capital de - -// codepoint and utf16 encoded string -static const std::vector> testData = { - { 0x7, L"\a", CodepointWidth::Narrow }, // BEL - { 0x20, L" ", CodepointWidth::Narrow }, - { 0x39, L"9", CodepointWidth::Narrow }, - { 0x414, L"\x414", CodepointWidth::Narrow }, // U+0414 cyrillic capital de - { 0x1104, L"\x1104", CodepointWidth::Wide }, // U+1104 hangul choseong ssangtikeut - { 0x306A, L"\x306A", CodepointWidth::Wide }, // U+306A hiragana na - { 0x30CA, L"\x30CA", CodepointWidth::Wide }, // U+30CA katakana na - { 0x72D7, L"\x72D7", CodepointWidth::Wide }, // U+72D7 - { 0x1F47E, L"\xD83D\xDC7E", CodepointWidth::Wide }, // U+1F47E alien monster - { 0x1F51C, L"\xD83D\xDD1C", CodepointWidth::Wide } // U+1F51C SOON -}; - -class CodepointWidthDetectorTests -{ - TEST_CLASS(CodepointWidthDetectorTests); - - TEST_METHOD(CanLookUpEmoji) - { - CodepointWidthDetector widthDetector; - VERIFY_IS_TRUE(widthDetector.IsWide(emoji)); - } - - TEST_METHOD(CanGetWidths) - { - CodepointWidthDetector widthDetector; - for (const auto& data : testData) - { - const auto& expected = std::get<2>(data); - const auto& wstr = std::get<1>(data); - const auto result = widthDetector.GetWidth({ wstr.c_str(), wstr.size() }); - VERIFY_ARE_EQUAL(result, expected); - } - } - - static bool FallbackMethod(const std::wstring_view glyph) - { - if (glyph.size() < 1) - { - return false; - } - else - { - return (glyph.at(0) % 2) == 1; - } - } - - TEST_METHOD(AmbiguousCache) - { - // Set up a detector with fallback. - CodepointWidthDetector widthDetector; - widthDetector.SetFallbackMethod(std::bind(&FallbackMethod, std::placeholders::_1)); - - // Ensure fallback cache is empty. - VERIFY_ARE_EQUAL(0u, widthDetector._fallbackCache.size()); - - // Lookup ambiguous width character. - widthDetector.IsWide(ambiguous); - - // Cache should hold it. - VERIFY_ARE_EQUAL(1u, widthDetector._fallbackCache.size()); - - // Cached item should match what we expect - const auto it = widthDetector._fallbackCache.begin(); - VERIFY_ARE_EQUAL(ambiguous[0], it->first); - VERIFY_ARE_EQUAL(FallbackMethod(ambiguous) ? 2u : 1u, it->second); - - // Cache should empty when font changes. - widthDetector.NotifyFontChanged(); - VERIFY_ARE_EQUAL(0u, widthDetector._fallbackCache.size()); - } -}; diff --git a/src/host/ut_host/Host.UnitTests.vcxproj b/src/host/ut_host/Host.UnitTests.vcxproj index d464570c78..89d7e2a65e 100644 --- a/src/host/ut_host/Host.UnitTests.vcxproj +++ b/src/host/ut_host/Host.UnitTests.vcxproj @@ -15,7 +15,6 @@ - diff --git a/src/host/ut_host/Host.UnitTests.vcxproj.filters b/src/host/ut_host/Host.UnitTests.vcxproj.filters index 9e61fb4106..9c946aa560 100644 --- a/src/host/ut_host/Host.UnitTests.vcxproj.filters +++ b/src/host/ut_host/Host.UnitTests.vcxproj.filters @@ -69,9 +69,6 @@ Source Files - - Source Files - Source Files diff --git a/src/host/ut_host/sources b/src/host/ut_host/sources index 1312b3e63f..799bb24a94 100644 --- a/src/host/ut_host/sources +++ b/src/host/ut_host/sources @@ -20,7 +20,6 @@ SOURCES = \ HistoryTests.cpp \ UtilsTests.cpp \ ConsoleArgumentsTests.cpp \ - CodepointWidthDetectorTests.cpp \ DbcsTests.cpp \ ScreenBufferTests.cpp \ TextBufferIteratorTests.cpp \ diff --git a/src/inc/conpty-static.h b/src/inc/conpty-static.h index 1dd9a4123e..d05ab56e8d 100644 --- a/src/inc/conpty-static.h +++ b/src/inc/conpty-static.h @@ -23,7 +23,19 @@ #endif #endif -#define PSEUDOCONSOLE_RESIZE_QUIRK (2u) +// CreatePseudoConsole Flags +#ifndef PSEUDOCONSOLE_INHERIT_CURSOR +#define PSEUDOCONSOLE_INHERIT_CURSOR (0x1) +#endif +#ifndef PSEUDOCONSOLE_RESIZE_QUIRK +#define PSEUDOCONSOLE_RESIZE_QUIRK (0x2) +#endif +#ifndef PSEUDOCONSOLE_GLYPH_WIDTH__MASK +#define PSEUDOCONSOLE_GLYPH_WIDTH__MASK 0x18 +#define PSEUDOCONSOLE_GLYPH_WIDTH_GRAPHEMES 0x08 +#define PSEUDOCONSOLE_GLYPH_WIDTH_WCSWIDTH 0x10 +#define PSEUDOCONSOLE_GLYPH_WIDTH_CONSOLE 0x18 +#endif CONPTY_EXPORT HRESULT WINAPI ConptyCreatePseudoConsole(COORD size, HANDLE hInput, HANDLE hOutput, DWORD dwFlags, HPCON* phPC); CONPTY_EXPORT HRESULT WINAPI ConptyCreatePseudoConsoleAsUser(HANDLE hToken, COORD size, HANDLE hInput, HANDLE hOutput, DWORD dwFlags, HPCON* phPC); diff --git a/src/propslib/RegistrySerialization.cpp b/src/propslib/RegistrySerialization.cpp index a350700c66..37ff1c3471 100644 --- a/src/propslib/RegistrySerialization.cpp +++ b/src/propslib/RegistrySerialization.cpp @@ -62,6 +62,7 @@ const RegistrySerialization::_RegPropertyMap RegistrySerialization::s_PropertyMa { _RegPropertyType::Boolean, CONSOLE_REGISTRY_TERMINALSCROLLING, SET_FIELD_AND_SIZE(_TerminalScrolling) }, { _RegPropertyType::Boolean, CONSOLE_REGISTRY_USEDX, SET_FIELD_AND_SIZE(_fUseDx) }, { _RegPropertyType::Boolean, CONSOLE_REGISTRY_COPYCOLOR, SET_FIELD_AND_SIZE(_fCopyColor) }, + { _RegPropertyType::Dword, L"TextMeasurement", SET_FIELD_AND_SIZE(_textMeasurement) }, #if TIL_FEATURE_CONHOSTATLASENGINE_ENABLED { _RegPropertyType::Boolean, L"EnableBuiltinGlyphs", SET_FIELD_AND_SIZE(_fEnableBuiltinGlyphs) }, #endif diff --git a/src/terminal/adapter/DispatchTypes.hpp b/src/terminal/adapter/DispatchTypes.hpp index a0a74d8c83..46b8a316d3 100644 --- a/src/terminal/adapter/DispatchTypes.hpp +++ b/src/terminal/adapter/DispatchTypes.hpp @@ -545,9 +545,19 @@ namespace Microsoft::Console::VirtualTerminal::DispatchTypes ALTERNATE_SCROLL = DECPrivateMode(1007), ASB_AlternateScreenBuffer = DECPrivateMode(1049), XTERM_BracketedPasteMode = DECPrivateMode(2004), + GCM_GraphemeClusterMode = DECPrivateMode(2027), W32IM_Win32InputMode = DECPrivateMode(9001), }; + enum ModeResponses : VTInt + { + DECRPM_Unsupported = 0, + DECRPM_Enabled = 1, + DECRPM_Disabled = 2, + DECRPM_PermanentlyEnabled = 3, + DECRPM_PermanentlyDisabled = 4, + }; + enum CharacterSets : uint64_t { DecSpecialGraphics = VTID("0"), diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 1a451941e0..1335dccb54 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -4,10 +4,11 @@ #include "precomp.h" #include "adaptDispatch.hpp" -#include "../../renderer/base/renderer.hpp" -#include "../../types/inc/Viewport.hpp" -#include "../../types/inc/utils.hpp" #include "../../inc/unicode.hpp" +#include "../../renderer/base/renderer.hpp" +#include "../../types/inc/CodepointWidthDetector.hpp" +#include "../../types/inc/utils.hpp" +#include "../../types/inc/Viewport.hpp" #include "../parser/ascii.hpp" using namespace Microsoft::Console::Types; @@ -2022,6 +2023,8 @@ bool AdaptDispatch::_ModeParamsHelper(const DispatchTypes::ModeParams param, con case DispatchTypes::ModeParams::XTERM_BracketedPasteMode: _api.SetSystemMode(ITerminalApi::Mode::BracketedPaste, enable); return !_api.IsConsolePty(); + case DispatchTypes::ModeParams::GCM_GraphemeClusterMode: + return true; case DispatchTypes::ModeParams::W32IM_Win32InputMode: _terminalInput.SetInputMode(TerminalInput::Mode::Win32, enable); // ConPTY requests the Win32InputMode on startup and disables it on shutdown. When nesting ConPTY inside @@ -2068,116 +2071,124 @@ bool AdaptDispatch::ResetMode(const DispatchTypes::ModeParams param) // - True if handled successfully. False otherwise. bool AdaptDispatch::RequestMode(const DispatchTypes::ModeParams param) { - auto enabled = std::optional{}; + static constexpr auto mapTemp = [](const bool b) { return b ? DispatchTypes::DECRPM_Enabled : DispatchTypes::DECRPM_Disabled; }; + static constexpr auto mapPerm = [](const bool b) { return b ? DispatchTypes::DECRPM_PermanentlyEnabled : DispatchTypes::DECRPM_PermanentlyDisabled; }; + + VTInt state = DispatchTypes::DECRPM_Unsupported; switch (param) { case DispatchTypes::ModeParams::IRM_InsertReplaceMode: - enabled = _modes.test(Mode::InsertReplace); + state = mapTemp(_modes.test(Mode::InsertReplace)); break; case DispatchTypes::ModeParams::LNM_LineFeedNewLineMode: // VT apps expect that the system and input modes are the same, so if // they become out of sync, we just act as if LNM mode isn't supported. if (_api.GetSystemMode(ITerminalApi::Mode::LineFeed) == _terminalInput.GetInputMode(TerminalInput::Mode::LineFeed)) { - enabled = _terminalInput.GetInputMode(TerminalInput::Mode::LineFeed); + state = mapTemp(_terminalInput.GetInputMode(TerminalInput::Mode::LineFeed)); } break; case DispatchTypes::ModeParams::DECCKM_CursorKeysMode: - enabled = _terminalInput.GetInputMode(TerminalInput::Mode::CursorKey); + state = mapTemp(_terminalInput.GetInputMode(TerminalInput::Mode::CursorKey)); break; case DispatchTypes::ModeParams::DECANM_AnsiMode: - enabled = _api.GetStateMachine().GetParserMode(StateMachine::Mode::Ansi); + state = mapTemp(_api.GetStateMachine().GetParserMode(StateMachine::Mode::Ansi)); break; case DispatchTypes::ModeParams::DECCOLM_SetNumberOfColumns: // DECCOLM is not supported in conpty mode if (!_api.IsConsolePty()) { - enabled = _modes.test(Mode::Column); + state = mapTemp(_modes.test(Mode::Column)); } break; case DispatchTypes::ModeParams::DECSCNM_ScreenMode: - enabled = _renderSettings.GetRenderMode(RenderSettings::Mode::ScreenReversed); + state = mapTemp(_renderSettings.GetRenderMode(RenderSettings::Mode::ScreenReversed)); break; case DispatchTypes::ModeParams::DECOM_OriginMode: - enabled = _modes.test(Mode::Origin); + state = mapTemp(_modes.test(Mode::Origin)); break; case DispatchTypes::ModeParams::DECAWM_AutoWrapMode: - enabled = _api.GetSystemMode(ITerminalApi::Mode::AutoWrap); + state = mapTemp(_api.GetSystemMode(ITerminalApi::Mode::AutoWrap)); break; case DispatchTypes::ModeParams::DECARM_AutoRepeatMode: - enabled = _terminalInput.GetInputMode(TerminalInput::Mode::AutoRepeat); + state = mapTemp(_terminalInput.GetInputMode(TerminalInput::Mode::AutoRepeat)); break; case DispatchTypes::ModeParams::ATT610_StartCursorBlink: - enabled = _pages.ActivePage().Cursor().IsBlinkingAllowed(); + state = mapTemp(_pages.ActivePage().Cursor().IsBlinkingAllowed()); break; case DispatchTypes::ModeParams::DECTCEM_TextCursorEnableMode: - enabled = _pages.ActivePage().Cursor().IsVisible(); + state = mapTemp(_pages.ActivePage().Cursor().IsVisible()); break; case DispatchTypes::ModeParams::XTERM_EnableDECCOLMSupport: // DECCOLM is not supported in conpty mode if (!_api.IsConsolePty()) { - enabled = _modes.test(Mode::AllowDECCOLM); + state = mapTemp(_modes.test(Mode::AllowDECCOLM)); } break; case DispatchTypes::ModeParams::DECPCCM_PageCursorCouplingMode: - enabled = _modes.test(Mode::PageCursorCoupling); + state = mapTemp(_modes.test(Mode::PageCursorCoupling)); break; case DispatchTypes::ModeParams::DECNKM_NumericKeypadMode: - enabled = _terminalInput.GetInputMode(TerminalInput::Mode::Keypad); + state = mapTemp(_terminalInput.GetInputMode(TerminalInput::Mode::Keypad)); break; case DispatchTypes::ModeParams::DECBKM_BackarrowKeyMode: - enabled = _terminalInput.GetInputMode(TerminalInput::Mode::BackarrowKey); + state = mapTemp(_terminalInput.GetInputMode(TerminalInput::Mode::BackarrowKey)); break; case DispatchTypes::ModeParams::DECLRMM_LeftRightMarginMode: - enabled = _modes.test(Mode::AllowDECSLRM); + state = mapTemp(_modes.test(Mode::AllowDECSLRM)); break; case DispatchTypes::ModeParams::DECECM_EraseColorMode: - enabled = _modes.test(Mode::EraseColor); + state = mapTemp(_modes.test(Mode::EraseColor)); break; case DispatchTypes::ModeParams::VT200_MOUSE_MODE: - enabled = _terminalInput.GetInputMode(TerminalInput::Mode::DefaultMouseTracking); + state = mapTemp(_terminalInput.GetInputMode(TerminalInput::Mode::DefaultMouseTracking)); break; case DispatchTypes::ModeParams::BUTTON_EVENT_MOUSE_MODE: - enabled = _terminalInput.GetInputMode(TerminalInput::Mode::ButtonEventMouseTracking); + state = mapTemp(_terminalInput.GetInputMode(TerminalInput::Mode::ButtonEventMouseTracking)); break; case DispatchTypes::ModeParams::ANY_EVENT_MOUSE_MODE: - enabled = _terminalInput.GetInputMode(TerminalInput::Mode::AnyEventMouseTracking); + state = mapTemp(_terminalInput.GetInputMode(TerminalInput::Mode::AnyEventMouseTracking)); break; case DispatchTypes::ModeParams::UTF8_EXTENDED_MODE: - enabled = _terminalInput.GetInputMode(TerminalInput::Mode::Utf8MouseEncoding); + state = mapTemp(_terminalInput.GetInputMode(TerminalInput::Mode::Utf8MouseEncoding)); break; case DispatchTypes::ModeParams::SGR_EXTENDED_MODE: - enabled = _terminalInput.GetInputMode(TerminalInput::Mode::SgrMouseEncoding); + state = mapTemp(_terminalInput.GetInputMode(TerminalInput::Mode::SgrMouseEncoding)); break; case DispatchTypes::ModeParams::FOCUS_EVENT_MODE: - enabled = _terminalInput.GetInputMode(TerminalInput::Mode::FocusEvent); + state = mapTemp(_terminalInput.GetInputMode(TerminalInput::Mode::FocusEvent)); break; case DispatchTypes::ModeParams::ALTERNATE_SCROLL: - enabled = _terminalInput.GetInputMode(TerminalInput::Mode::AlternateScroll); + state = mapTemp(_terminalInput.GetInputMode(TerminalInput::Mode::AlternateScroll)); break; case DispatchTypes::ModeParams::ASB_AlternateScreenBuffer: - enabled = _usingAltBuffer; + state = mapTemp(_usingAltBuffer); break; case DispatchTypes::ModeParams::XTERM_BracketedPasteMode: - enabled = _api.GetSystemMode(ITerminalApi::Mode::BracketedPaste); + state = mapTemp(_api.GetSystemMode(ITerminalApi::Mode::BracketedPaste)); + break; + case DispatchTypes::ModeParams::GCM_GraphemeClusterMode: + state = mapPerm(CodepointWidthDetector::Singleton().GetMode() == TextMeasurementMode::Graphemes); break; case DispatchTypes::ModeParams::W32IM_Win32InputMode: - enabled = _terminalInput.GetInputMode(TerminalInput::Mode::Win32); + state = mapTemp(_terminalInput.GetInputMode(TerminalInput::Mode::Win32)); break; default: - enabled = std::nullopt; break; } - // 1 indicates the mode is enabled, 2 it's disabled, and 0 it's unsupported - const auto state = enabled.has_value() ? (enabled.value() ? 1 : 2) : 0; - const auto isPrivate = param >= DispatchTypes::DECPrivateMode(0); - const auto prefix = isPrivate ? L"?" : L""; - const auto mode = isPrivate ? param - DispatchTypes::DECPrivateMode(0) : param; - const auto response = wil::str_printf(L"\x1b[%s%d;%d$y", prefix, mode, state); - _api.ReturnResponse(response); + VTInt mode = param; + std::wstring_view prefix; + + if (mode >= DispatchTypes::DECPrivateMode(0)) + { + mode -= DispatchTypes::DECPrivateMode(0); + prefix = L"?"; + } + + _api.ReturnResponse(fmt::format(FMT_COMPILE(L"\x1b[{}{};{}$y"), prefix, mode, state)); return true; } diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index 415d22fe86..81bd119abe 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -2059,6 +2059,25 @@ public: _testGetSet->ValidateInputEvent(expectedResponse); } + TEST_METHOD(RequestPermanentModeTests) + { + BEGIN_TEST_METHOD_PROPERTIES() + TEST_METHOD_PROPERTY(L"Data:modeNumber", L"{2027}") + END_TEST_METHOD_PROPERTIES() + + VTInt modeNumber; + VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"modeNumber", modeNumber)); + const auto mode = DispatchTypes::DECPrivateMode(modeNumber); + + _testGetSet->PrepData(); + VERIFY_IS_TRUE(_pDispatch->ResetMode(mode)); // as a test to ensure that it stays permanently enabled (= 3) + VERIFY_IS_TRUE(_pDispatch->RequestMode(mode)); + + wchar_t expectedResponse[20]; + swprintf_s(expectedResponse, ARRAYSIZE(expectedResponse), L"\x1b[?%d;3$y", modeNumber); + _testGetSet->ValidateInputEvent(expectedResponse); + } + TEST_METHOD(RequestChecksumReportTests) { const auto requestChecksumReport = [this](const auto length) { diff --git a/src/tools/GraphemeTableGen/GraphemeTableGen.csproj b/src/tools/GraphemeTableGen/GraphemeTableGen.csproj new file mode 100644 index 0000000000..6f4623a81b --- /dev/null +++ b/src/tools/GraphemeTableGen/GraphemeTableGen.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + diff --git a/src/tools/GraphemeTableGen/Program.cs b/src/tools/GraphemeTableGen/Program.cs new file mode 100644 index 0000000000..780e41cb52 --- /dev/null +++ b/src/tools/GraphemeTableGen/Program.cs @@ -0,0 +1,575 @@ +using System.Text; +using System.Runtime.InteropServices; +using System.Numerics; +using System.Xml.Linq; +using TrieType = uint; + +// JoinRules doesn't quite follow UAX #29, as it states: +// > Note: Testing two adjacent characters is insufficient for determining a boundary. +// +// I completely agree, however it makes the implementation complex and slow, and it only benefits what can be considered +// edge cases in the context of terminals. By using a lookup table anyway this results in a >100MB/s throughput, +// before adding any fast-passes whatsoever. This is 2x as fast as any standards conforming implementation I found. +// +// This affects the following rules: +// * GB9c: \p{InCB=Consonant} [\p{InCB=Extend}\p{InCB=Linker}]* \p{InCB=Linker} [\p{InCB=Extend}\p{InCB=Linker}]* × \p{InCB=Consonant} +// "Do not break within certain combinations with Indic_Conjunct_Break (InCB)=Linker." +// Our implementation does this: +// × \p{InCB=Linker} +// \p{InCB=Linker} × \p{InCB=Consonant} +// In other words, it doesn't check for a leading \p{InCB=Consonant} or a series of Extenders/Linkers in between. +// I suspect that these simplified rules are sufficient for the vast majority of terminal use cases. +// * GB11: \p{Extended_Pictographic} Extend* ZWJ × \p{Extended_Pictographic} +// "Do not break within emoji modifier sequences or emoji zwj sequences." +// Our implementation does this: +// ZWJ × \p{Extended_Pictographic} +// In other words, it doesn't check whether the ZWJ is led by another \p{InCB=Extended_Pictographic}. +// Again, I suspect that a trailing, standalone ZWJ is a rare occurrence and joining it with any Emoji is fine. +// * GB12: sot (RI RI)* RI × RI +// GB13: [^RI] (RI RI)* RI × RI +// "Do not break within emoji flag sequences. That is, do not break between regional indicator +// (RI) symbols if there is an odd number of RI characters before the break point." +// Our implementation does this (this is not a real notation): +// RI ÷ RI × RI ÷ RI +// In other words, it joins any pair of RIs and then immediately aborts further RI joins. +// Unlike the above two cases, this is a bit more risky, because it's much more likely to be encountered in practice. +// Imagine a shell that doesn't understand graphemes for instance. You type 2 flags (= 4 RIs) and backspace. +// You'll now have 3 RIs. If iterating through it forwards, you'd join the first two, then get 1 lone RI at the end, +// whereas if you iterate backwards you'd join the last two, then get 1 lone RI at the start. +// This asymmetry may have some subtle effects, but I suspect that it's still rare enough to not matter much. +// +// This is a great reference for the resulting table: +// https://www.unicode.org/Public/UCD/latest/ucd/auxiliary/GraphemeBreakTest.html +byte[][][] joinRules = +[ + // Base table + [ + /* | leading -> trailing codepoint */ + /* v | cbOther | cbControl | cbExtend | cbRI | cbPrepend | cbHangulL | cbHangulV | cbHangulT | cbHangulLV | cbHangulLVT | cbInCBLinker | cbInCBConsonant | cbExtPic | cbZWJ | */ + /* cbOther | */ [3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */], + /* cbControl | */ [3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */], + /* cbExtend | */ [3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */], + /* cbRI | */ [3 /* | */, 3 /* | */, 0 /* | */, 1 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */], + /* cbPrepend | */ [0 /* | */, 3 /* | */, 0 /* | */, 0 /* | */, 0 /* | */, 0 /* | */, 0 /* | */, 0 /* | */, 0 /* | */, 0 /* | */, 0 /* | */, 0 /* | */, 0 /* | */, 0 /* | */], + /* cbHangulL | */ [3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 0 /* | */, 3 /* | */, 0 /* | */, 0 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */], + /* cbHangulV | */ [3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */], + /* cbHangulT | */ [3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */], + /* cbHangulLV | */ [3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */], + /* cbHangulLVT | */ [3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */], + /* cbInCBLinker | */ [3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 0 /* | */, 3 /* | */, 0 /* | */], + /* cbInCBConsonant | */ [3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */], + /* cbExtPic | */ [3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */], + /* cbZWJ | */ [3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 0 /* | */, 0 /* | */], + ], + // Once we have encountered a Regional Indicator pair we'll enter this table. + // It's a copy of the base table, but further Regional Indicator joins are forbidden. + [ + /* | leading -> trailing codepoint */ + /* v | cbOther | cbControl | cbExtend | cbRI | cbPrepend | cbHangulL | cbHangulV | cbHangulT | cbHangulLV | cbHangulLVT | cbInCBLinker | cbInCBConsonant | cbExtPic | cbZWJ | */ + /* cbOther | */ [3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */], + /* cbControl | */ [3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */], + /* cbExtend | */ [3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */], + /* cbRI | */ [3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */], + /* cbPrepend | */ [0 /* | */, 3 /* | */, 0 /* | */, 0 /* | */, 0 /* | */, 0 /* | */, 0 /* | */, 0 /* | */, 0 /* | */, 0 /* | */, 0 /* | */, 0 /* | */, 0 /* | */, 0 /* | */], + /* cbHangulL | */ [3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 0 /* | */, 3 /* | */, 0 /* | */, 0 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */], + /* cbHangulV | */ [3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */], + /* cbHangulT | */ [3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */], + /* cbHangulLV | */ [3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */], + /* cbHangulLVT | */ [3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */], + /* cbInCBLinker | */ [3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 0 /* | */, 3 /* | */, 0 /* | */], + /* cbInCBConsonant | */ [3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */], + /* cbExtPic | */ [3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 0 /* | */], + /* cbZWJ | */ [3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 3 /* | */, 0 /* | */, 3 /* | */, 0 /* | */, 0 /* | */], + ] +]; + +if (args.Length != 1) +{ + Console.WriteLine( + """ + Usage: GraphemeTableGen + + You can download the latest ucd.nounihan.grouped.xml from: + https://www.unicode.org/Public/UCD/latest/ucdxml/ucd.nounihan.grouped.zip + """ + ); + Environment.Exit(1); +} + +var ucd = ExtractValuesFromUcd(args[0]); + +// Find the best trie configuration over the given block sizes (2^2 - 2^8) and stages (4). +// More stages = Less size. The trajectory roughly follows a+b*c^stages, where c < 1. +// 4 still gives ~30% savings over 3 stages and going beyond 5 gives diminishing returns (<10%). +var trie = BuildBestTrie(ucd.Values, 2, 8, 4); +// The joinRules above has 2 bits per value. This packs it into 32-bit integers to save space. +var rules = PrepareRulesTable(joinRules); +// Each rules item has the same length. Each item is 32 bits = 4 bytes. +var totalSize = trie.TotalSize + rules.Length * rules[0].Length * sizeof(TrieType); + +// Run a quick sanity check to ensure that the trie works as expected. +foreach (var (expected, cp) in ucd.Values.Select((v, i) => (v, i))) +{ + TrieType v = 0; + foreach (var s in trie.Stages) + { + v = s.Values[(int)v + ((cp >> s.Shift) & s.Mask)]; + } + + if (v != expected) + { + throw new Exception($"trie sanity check failed for {cp:X}"); + } +} + +// All the remaining code starting here simply generates the C++ output. +var buf = new StringBuilder(); +buf.Append("// Generated by GraphemeTableGen\n"); +buf.Append($"// on {DateTime.UtcNow.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ssK")}, from {ucd.Description}, {totalSize} bytes\n"); +buf.Append("// clang-format off\n"); + +foreach (var stage in trie.Stages) +{ + var fmt = $" 0x{{0:x{stage.Bits / 4}}},"; + var width = 16; + if (stage.Index != 0) + { + width = stage.Mask + 1; + } + + buf.Append($"static constexpr uint{stage.Bits}_t s_stage{stage.Index}[] = {{"); + foreach (var (value, j) in stage.Values.Select((v, j) => (v, j))) + { + if (j % width == 0) + { + buf.Append("\n "); + } + buf.AppendFormat(fmt, value); + } + buf.Append("\n};\n"); +} + +buf.Append($"static constexpr uint32_t s_joinRules[{rules.Length}][{rules[0].Length}] = {{\n"); +foreach (var table in rules) +{ + buf.Append(" {\n"); + foreach (var r in table) + { + buf.Append($" 0b{r:b32},\n"); + } + buf.Append(" },\n"); +} +buf.Append("};\n"); + +buf.Append($"constexpr uint{trie.Stages[^1].Bits}_t ucdLookup(const char32_t cp) noexcept\n"); +buf.Append("{\n"); +foreach (var stage in trie.Stages) +{ + buf.Append($" const auto s{stage.Index} = s_stage{stage.Index}["); + if (stage.Index == 0) + { + buf.Append($"cp >> {stage.Shift}"); + } + else + { + buf.Append($"s{stage.Index - 1} + ((cp >> {stage.Shift}) & {stage.Mask})"); + } + + buf.Append("];\n"); +} +buf.Append($" return s{trie.Stages.Count - 1};\n"); +buf.Append("}\n"); + +buf.Append("constexpr int ucdGraphemeJoins(const int state, const int lead, const int trail) noexcept\n"); +buf.Append("{\n"); +buf.Append(" const auto l = lead & 15;\n"); +buf.Append(" const auto t = trail & 15;\n"); +buf.Append($" return (s_joinRules[state][l] >> (t * 2)) & 3;\n"); +buf.Append("}\n"); +buf.Append("constexpr bool ucdGraphemeDone(const int state) noexcept\n"); +buf.Append("{\n"); +buf.Append($" return state == 3;\n"); +buf.Append("}\n"); +buf.Append("constexpr int ucdToCharacterWidth(const int val) noexcept\n"); +buf.Append("{\n"); +buf.Append(" return val >> 6;\n"); +buf.Append("}\n"); +buf.Append("// clang-format on\n"); + +Console.Write(buf); + +// This reads the given ucd.nounihan.grouped.xml file and extracts the +// CharacterWidth and ClusterBreak properties for all codepoints. +static Ucd ExtractValuesFromUcd(string path) +{ + var values = new TrieType[1114112]; + Array.Fill(values, TrieValue(ClusterBreak.Other, CharacterWidth.Narrow)); + + XNamespace ns = "http://www.unicode.org/ns/2003/ucd/1.0"; + var doc = XDocument.Load(path); + var root = doc.Root!; + var description = root.Element(ns + "description")!.Value; + + foreach (var group in doc.Root!.Descendants(ns + "group")) + { + var groupGeneralCategory = group.Attribute("gc")?.Value; + var groupGraphemeClusterBreak = group.Attribute("GCB")?.Value; + var groupIndicConjunctBreak = group.Attribute("InCB")?.Value; + var groupExtendedPictographic = group.Attribute("ExtPict")?.Value; + var groupEastAsian = group.Attribute("ea")?.Value; + + foreach (var ch in group.Elements()) + { + int firstCp; + int lastCp; + if (ch.Attribute("cp") is { } val) + { + var cp = Convert.ToInt32(val.Value, 16); + firstCp = cp; + lastCp = cp; + } + else + { + firstCp = Convert.ToInt32(ch.Attribute("first-cp")!.Value, 16); + lastCp = Convert.ToInt32(ch.Attribute("last-cp")!.Value, 16); + } + + var generalCategory = ch.Attribute("gc")?.Value ?? groupGeneralCategory ?? ""; + var graphemeClusterBreak = ch.Attribute("GCB")?.Value ?? groupGraphemeClusterBreak ?? ""; + var indicConjunctBreak = ch.Attribute("InCB")?.Value ?? groupIndicConjunctBreak ?? ""; + var extendedPictographic = ch.Attribute("ExtPict")?.Value ?? groupExtendedPictographic ?? ""; + var eastAsian = ch.Attribute("ea")?.Value ?? groupEastAsian ?? ""; + + var cb = graphemeClusterBreak switch + { + "XX" => ClusterBreak.Other, // Anything else + // We ignore GB3 which demands that CR × LF do not break apart, because + // * these control characters won't normally reach our text storage + // * otherwise we're in a raw write mode and historically conhost stores them in separate cells + "CR" or "LF" or "CN" => ClusterBreak.Control, // Carriage Return, Line Feed, Control + "EX" or "SM" => ClusterBreak.Extend, // Extend, SpacingMark + "PP" => ClusterBreak.Prepend, // Prepend + "ZWJ" => ClusterBreak.ZWJ, // Zero Width Joiner + "RI" => ClusterBreak.RI, // Regional Indicator + "L" => ClusterBreak.HangulL, // Hangul Syllable Type L + "V" => ClusterBreak.HangulV, // Hangul Syllable Type V + "T" => ClusterBreak.HangulT, // Hangul Syllable Type T + "LV" => ClusterBreak.HangulLV, // Hangul Syllable Type LV + "LVT" => ClusterBreak.HangulLVT, // Hangul Syllable Type LVT + _ => throw new Exception($"Unrecognized GCB {graphemeClusterBreak} for {firstCp} to {lastCp}") + }; + + if (extendedPictographic == "Y") + { + // Currently every single Extended_Pictographic codepoint happens to be GCB=XX. + // This is fantastic for us because it means we can stuff it into the ClusterBreak enum + // and treat it as an alias of EXTEND, but with the special GB11 properties. + if (cb != ClusterBreak.Other) + { + throw new Exception( + $"Unexpected GCB {graphemeClusterBreak} with ExtPict=Y for {firstCp} to {lastCp}"); + } + + cb = ClusterBreak.ExtPic; + } + + cb = indicConjunctBreak switch + { + "None" or "Extend" => cb, + "Linker" => ClusterBreak.InCBLinker, + "Consonant" => ClusterBreak.InCBConsonant, + _ => throw new Exception($"Unrecognized InCB {indicConjunctBreak} for {firstCp} to {lastCp}") + }; + + var width = eastAsian switch + { + "N" or "Na" or "H" => CharacterWidth.Narrow, // Half-width, Narrow, Neutral + "F" or "W" => CharacterWidth.Wide, // Wide, Full-width + "A" => CharacterWidth.Ambiguous, // Ambiguous + _ => throw new Exception($"Unrecognized ea {eastAsian} for {firstCp} to {lastCp}") + }; + + // There's no "ea" attribute for "zero width" so we need to do that ourselves. This matches: + // Mc: Mark, spacing combining + // Me: Mark, enclosing + // Mn: Mark, non-spacing + // Cf: Control, format + if (generalCategory.StartsWith("M") || generalCategory == "Cf") + { + width = CharacterWidth.ZeroWidth; + } + + Fill(firstCp, lastCp, TrieValue(cb, width)); + } + } + + // Box-drawing and block elements are ambiguous according to their EastAsian attribute, + // but by convention terminals always consider them to be narrow. + Fill(0x2500, 0x259F, TrieValue(ClusterBreak.Other, CharacterWidth.Narrow)); + + return new Ucd + { + Description = description, + Values = values.ToList(), + }; + + void Fill(int first, int last, TrieType value) + { + Array.Fill(values, value, first, last - first + 1); + } +} + +// Packs the arguments into a single integer that's stored as-is in the final trie stage. +static TrieType TrieValue(ClusterBreak cb, CharacterWidth width) +{ + return (TrieType)((byte)(cb) | (byte)(width) << 6); +} + +// Because each item in the list of 2D rule tables only uses 2 bits and not all 8 in each byte, +// this function packs them into chunks of 32-bit integers to save space. +static uint[][] PrepareRulesTable(byte[][][] rules) +{ + var compressed = new uint[rules.Length][]; + for (var i = 0; i < compressed.Length; i++) + { + compressed[i] = new uint[16]; + } + + foreach (var (table, prevIndex) in rules.Select((v, i) => (v, i))) + { + foreach (var (row, lead) in table.Select((v, i) => (v, i))) + { + if (table[lead].Length > 16) + { + throw new Exception("Can't pack row into 32 bits"); + } + + uint nextIndices = 0; + foreach (var (nextIndex, trail) in row.Select((v, i) => (v, i))) + { + if (nextIndex > 3) + { + throw new Exception("Can't pack table index into 2 bits"); + } + + nextIndices |= (uint)(nextIndex << (trail * 2)); + } + + compressed[prevIndex][lead] = nextIndices; + } + } + + return compressed; +} + +// This tries all possible trie configurations and returns the one with the smallest size. It's brute force. +static Trie BuildBestTrie(List uncompressed, int minShift, int maxShift, int stages) +{ + var depth = stages - 1; + var delta = maxShift - minShift + 1; + var total = 1; + for (var i = 0; i < depth; i++) + { + total *= delta; + } + + var tasks = new int[total][]; + for (var i = 0; i < total; i++) + { + // Given minShift=2, maxShift=3, depth=3 this generates + // [2 2 2] + // [3 2 2] + // [2 3 2] + // [3 3 2] + // [2 2 3] + // [3 2 3] + // [2 3 3] + // [3 3 3] + var shifts = new int[depth]; + for (int j = 0, index = i; j < depth; j++, index /= delta) + { + shifts[j] = minShift + index % delta; + } + + tasks[i] = shifts; + } + + return tasks.AsParallel().Select(shifts => BuildTrie(uncompressed, shifts)).MinBy(t => t.TotalSize)!; +} + +// Compresses the given uncompressed data into a multi-level trie with shifts.Count+1 stages. +// shifts defines the power-of-two sizes of the deduplicated chunks in each stage. +// The final output receives no deduplication which is why this returns shifts.Count+1 stages. +static Trie BuildTrie(List uncompressed, Span shifts) +{ + var cumulativeShift = 0; + var stages = new List(); + + for (int i = 0; i < shifts.Length; i++) + { + var shift = shifts[i]; + var chunkSize = 1 << shift; + var cache = new Dictionary(); + var compressed = new List(); + var offsets = new List(); + + for (var off = 0; off < uncompressed.Count; off += chunkSize) + { + var key = new ReadOnlyTrieTypeSpan(uncompressed, off, Math.Min(chunkSize, uncompressed.Count - off)); + + // Cast the integer slice to a string so that it can be hashed. + + if (!cache.TryGetValue(key, out var offset)) + { + // For a 4-stage trie searching for existing occurrences of chunk in compressed yields a ~10% + // compression improvement. Checking for overlaps with the tail end of compressed yields another ~15%. + // FYI I tried to shuffle the order of compressed chunks but found that this has a negligible impact. + var haystack = CollectionsMarshal.AsSpan(compressed); + var needle = key.AsSpan(); + var existing = FindExisting(haystack, needle); + if (existing >= 0) + { + offset = (TrieType)existing; + cache[key] = offset; + } + else + { + var overlap = MeasureOverlap(CollectionsMarshal.AsSpan(compressed), needle); + compressed.AddRange(needle[overlap..]); + offset = (TrieType)(compressed.Count - needle.Length); + cache[key] = offset; + } + } + + offsets.Add(offset); + } + + stages.Add(new Stage + { + Values = compressed, + Index = shifts.Length - i, + Shift = cumulativeShift, + Mask = chunkSize - 1, + Bits = 0, + }); + + uncompressed = offsets; + cumulativeShift += shift; + } + + stages.Add(new Stage + { + Values = uncompressed, + Index = 0, + Shift = cumulativeShift, + Mask = int.MaxValue, + Bits = 0, + }); + + stages.Reverse(); + + foreach (var s in stages) + { + var m = s.Values.Max(); + s.Bits = m switch + { + <= 0xff => 8, + <= 0xffff => 16, + _ => 32 + }; + } + + return new Trie + { + Stages = stages, + TotalSize = stages.Sum(s => (s.Bits / 8) * s.Values.Count) + }; +} + +// Finds needle in haystack. Returns -1 if it couldn't be found. +static int FindExisting(ReadOnlySpan haystack, ReadOnlySpan needle) +{ + var idx = haystack.IndexOf(needle); + return idx; +} + +// Given two slices, this returns the amount by which `prev`s end overlaps with `next`s start. +// That is, given [0,1,2,3,4] and [2,3,4,5] this returns 3 because [2,3,4] is the "overlap". +static int MeasureOverlap(ReadOnlySpan prev, ReadOnlySpan next) +{ + for (var overlap = Math.Min(prev.Length, next.Length); overlap >= 0; overlap--) + { + if (prev[^overlap..].SequenceEqual(next[..overlap])) + { + return overlap; + } + } + + return 0; +} + +enum CharacterWidth +{ + ZeroWidth, + Narrow, + Wide, + Ambiguous +} + +enum ClusterBreak +{ + Other, // GB999 + Control, // GB3, GB4, GB5 -- includes CR, LF + Extend, // GB9, GB9a -- includes SpacingMark + RI, // GB12, GB13 + Prepend, // GB9b + HangulL, // GB6, GB7, GB8 + HangulV, // GB6, GB7, GB8 + HangulT, // GB6, GB7, GB8 + HangulLV, // GB6, GB7, GB8 + HangulLVT, // GB6, GB7, GB8 + InCBLinker, // GB9c + InCBConsonant, // GB9c + ExtPic, // GB11 + ZWJ, // GB9, GB11 +} + +class Ucd +{ + public required string Description; + public required List Values; +} + +class Stage +{ + public required List Values; + public required int Index; + public required int Shift; + public required int Mask; + public required int Bits; +} + +class Trie +{ + public required List Stages; + public required int TotalSize; +} + +// Because you can't put a Span into a Dictionary. +// This works around that by simply keeping a reference to the List around. +struct ReadOnlyTrieTypeSpan(List list, int start, int length) +{ + public ReadOnlySpan AsSpan() => CollectionsMarshal.AsSpan(list).Slice(start, length); + + public override bool Equals(object? obj) + { + return obj is ReadOnlyTrieTypeSpan other && AsSpan().SequenceEqual(other.AsSpan()); + } + + public override int GetHashCode() + { + HashCode hashCode = default; + hashCode.AddBytes(MemoryMarshal.AsBytes(AsSpan())); + return hashCode.ToHashCode(); + } +} diff --git a/src/tools/GraphemeTestTableGen/GraphemeTestTableGen.csproj b/src/tools/GraphemeTestTableGen/GraphemeTestTableGen.csproj new file mode 100644 index 0000000000..6f4623a81b --- /dev/null +++ b/src/tools/GraphemeTestTableGen/GraphemeTestTableGen.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + diff --git a/src/tools/GraphemeTestTableGen/Program.cs b/src/tools/GraphemeTestTableGen/Program.cs new file mode 100644 index 0000000000..2c45396ddd --- /dev/null +++ b/src/tools/GraphemeTestTableGen/Program.cs @@ -0,0 +1,121 @@ +using System.Text; +using System.Text.RegularExpressions; + +string data; +using (var client = new HttpClient()) +{ + var response = await client.GetAsync("https://www.unicode.org/Public/UCD/latest/ucd/auxiliary/GraphemeBreakTest.txt"); + response.EnsureSuccessStatusCode(); + data = await response.Content.ReadAsStringAsync(); +} + +var testString = new StringBuilder(); +var scanner = new StringReader(data); +var firstLine = true; + +while (await scanner.ReadLineAsync() is { } line) +{ + var parts = line.Split('#'); + var test = parts[0].Trim(); + var comment = parts.Length > 1 ? parts[1].Trim() : ""; + + if (firstLine) + { + firstLine = false; + + var re = new Regex(@"^GraphemeBreakTest-(\d+\.\d+\.\d+)\.txt$"); + var m = re.Match(comment); + if (!m.Success) + { + throw new Exception($"Failed to find version number, got: {comment}"); + } + + testString.Append( + $$""" + // Generated by GraphemeTestTableGen + // on {{DateTime.UtcNow.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ssK")}}, from Unicode {{m.Groups[1].Value}} + struct GraphemeBreakTest + { + const wchar_t* comment; + const wchar_t* graphemes[4]; + }; + static constexpr GraphemeBreakTest s_graphemeBreakTests[] = { + + """ + ); + } + + if (test == "" || comment == "") + { + continue; + } + + var graphemes = test.Split('÷'); + for (var i = 0; i < graphemes.Length; i++) + { + graphemes[i] = graphemes[i].Trim(); + } + + testString.Append($" {{ L\"{comment}\""); + + foreach (var g in graphemes) + { + if (string.IsNullOrEmpty(g)) + { + continue; + } + + testString.Append(", L\""); + + var codepoints = g.Split('×'); + foreach (var c in codepoints) + { + var i = Convert.ToUInt32(c.Trim(), 16); + switch (i) + { + case 0x07: + testString.Append("\\a"); + break; + case 0x08: + testString.Append("\\b"); + break; + case 0x09: + testString.Append("\\t"); + break; + case 0x0A: + testString.Append("\\n"); + break; + case 0x0B: + testString.Append("\\v"); + break; + case 0x0C: + testString.Append("\\f"); + break; + case 0x0D: + testString.Append("\\r"); + break; + case >= 0x20 and <= 0x7e: + testString.Append((char)i); + break; + case <= 0xff: + testString.Append($"\\x{i:X2}"); + break; + case <= 0xffff: + testString.Append($"\\x{i:X4}"); + break; + default: + testString.Append($"\\U{i:X8}"); + break; + } + } + + testString.Append("\""); + } + + testString.Append(" },\n"); +} + +testString.Append("};\n"); + +Console.OutputEncoding = System.Text.Encoding.UTF8; +Console.Write(testString); diff --git a/src/types/CodepointWidthDetector.cpp b/src/types/CodepointWidthDetector.cpp index 6848aa5b61..c7accf328b 100644 --- a/src/types/CodepointWidthDetector.cpp +++ b/src/types/CodepointWidthDetector.cpp @@ -4,398 +4,1136 @@ #include "precomp.h" #include "inc/CodepointWidthDetector.hpp" -namespace +// I was trying to minimize dependencies in this code so that it's easier to port to other terminal applications. +// That's why it doesn't use any of the GSL helpers and makes minimal use of the STL. +#pragma warning(disable : 26446) // Prefer to use gsl::at() instead of unchecked subscript operator (bounds.4). +#pragma warning(disable : 26472) // Don't use a static_cast for arithmetic conversions. Use brace initialization, gsl::narrow_cast or gsl::narrow (type.1). + +// On top of that, this code is optimized for processing input as fast as possible, so it's a little low-level. +#pragma warning(disable : 26438) // Avoid 'goto' (es.76). +#pragma warning(disable : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1). +#pragma warning(disable : 26482) // Only index into arrays using constant expressions (bounds.2). + +// s_stage1/2/3/4 represents a multi-stage table, aka trie. +// The highest bits of the codepoint are an index into s_stage1, which selects a row in s_stage2. +// The next couple bits of the codepoint then select the column in that row. +// This continues until the last stage which contains the final value. +// +// Fundamentally, the trie is generated by taking all 1114112 codepoints and their assigned values and deduplicating +// chunks of e.g. 16 values each. Each deduplicated chunk is assigned its offset in the list of all deduplicated chunks. +// This results in two lists: 1114112/16=7132 IDs and however many deduplicated chunks you have accumulated. +// This is often called a two-stage table. +// +// If you want to look up the value now, you'll first find the deduplicated chunk offset via `offsets[codepoint / 16]`. +// This gives you the location of your chunk. Now you just look up the value with `values[offset + (codepoint & 15)]`. +// +// Since the 7132 offsets take up a lot more space than the deduplicated values (at least in case of the Unicode database), +// this process can be repeated by compressing the offset array the exact same way the values got compressed and so on. + +// s_joinRules represents the UAX #29 extended grapheme cluster rules, however slightly modified to fit our needs. +// Specifically, UAX #29 states: +// > Note: Testing two adjacent characters is insufficient for determining a boundary. +// +// I completely agree, but I really hate it. So this code trades off correctness for simplicity +// by using a simple lookup table anyway. Under most circumstances users won't notice, +// because as far as I can see this only behaves different for degenerate ("invalid") Unicode. +// It reduces our code complexity significantly and is way *way* faster. +// +// This is a great reference for the s_joinRules table: +// https://www.unicode.org/Public/UCD/latest/ucd/auxiliary/GraphemeBreakTest.html + +// Generated by GraphemeTableGen +// on 2024-05-31T21:13:48Z, from Unicode 15.1.0, 8479 bytes +// clang-format off +static constexpr uint16_t s_stage0[] = { + 0x0000, 0x0020, 0x0040, 0x0060, 0x0080, 0x009f, 0x00bf, 0x00ca, 0x00ca, 0x00d3, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, + 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00eb, 0x010b, 0x011d, 0x0121, 0x011e, 0x011b, 0x0126, 0x0146, 0x0166, 0x0166, 0x0166, 0x0182, + 0x01a2, 0x01ba, 0x01da, 0x01fa, 0x0146, 0x0146, 0x0218, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x022d, 0x00ca, 0x00ca, + 0x024d, 0x026d, 0x0146, 0x0146, 0x0146, 0x0282, 0x02a2, 0x02b0, 0x0146, 0x02c3, 0x02e1, 0x02f9, 0x0319, 0x0336, 0x0356, 0x0376, + 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, + 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x0396, + 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, + 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x00ca, 0x0396, + 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, + 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, + 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, + 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, + 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, + 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, + 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, + 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, + 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, + 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, + 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, + 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, + 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, + 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, + 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, + 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, + 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, + 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, + 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, + 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, + 0x03b6, 0x03be, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, + 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, 0x0146, + 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, + 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x03de, + 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, + 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x0166, 0x03de, +}; +static constexpr uint16_t s_stage1[] = { + 0x0000, 0x0004, 0x000c, 0x0014, 0x001c, 0x0024, 0x002a, 0x0031, 0x002a, 0x0037, 0x002a, 0x003f, 0x0047, 0x0049, 0x004f, 0x0057, 0x005f, 0x0065, 0x006d, 0x002a, 0x002a, 0x002a, 0x0073, 0x007b, 0x0083, 0x008a, 0x002a, 0x0091, 0x0098, 0x009f, 0x00a3, 0x00aa, + 0x00b2, 0x00b8, 0x00be, 0x00c5, 0x00cd, 0x00d5, 0x00dd, 0x00e5, 0x00ed, 0x00f5, 0x00fd, 0x0105, 0x00fd, 0x010d, 0x0115, 0x011d, 0x0125, 0x012d, 0x00ed, 0x0135, 0x013d, 0x0145, 0x014d, 0x0154, 0x015b, 0x0163, 0x0165, 0x016d, 0x0172, 0x006f, 0x017a, 0x0182, + 0x0185, 0x018d, 0x0195, 0x002a, 0x019d, 0x01a1, 0x01a5, 0x01aa, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x01b2, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x01b8, 0x01bf, 0x01c6, 0x01cd, + 0x01d4, 0x002a, 0x01dc, 0x002a, 0x01e2, 0x002a, 0x002a, 0x002a, 0x01ea, 0x01f0, 0x01f8, 0x01ff, 0x0207, 0x020f, 0x0217, 0x021d, 0x0224, 0x002a, 0x002a, 0x022b, 0x002a, 0x002a, 0x002a, 0x0047, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, + 0x0233, 0x023b, 0x0243, 0x0249, 0x0251, 0x0259, 0x0261, 0x0269, 0x0271, 0x0279, 0x0281, 0x002a, 0x0289, 0x002a, 0x0290, 0x0297, 0x002a, 0x029f, 0x02a3, 0x02ab, 0x002a, 0x002a, 0x02b3, 0x02bb, 0x02c3, 0x02cb, 0x02d3, 0x02db, 0x02e3, 0x02eb, 0x02f3, 0x002a, + 0x002a, 0x002a, 0x002a, 0x02fb, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x0303, 0x0309, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x030d, 0x002a, 0x0314, 0x002a, 0x0043, 0x002a, 0x002a, 0x031c, 0x0320, 0x0328, 0x0328, 0x0328, 0x032e, 0x0334, + 0x033c, 0x0342, 0x0328, 0x034a, 0x0328, 0x0351, 0x0355, 0x035b, 0x0362, 0x0368, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, + 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x002a, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x036f, 0x0377, 0x002a, + 0x002a, 0x002a, 0x002a, 0x002a, 0x037a, 0x0382, 0x016f, 0x002a, 0x002a, 0x002a, 0x002a, 0x038a, 0x002a, 0x0392, 0x039a, 0x03a2, 0x03aa, 0x03b2, 0x03ba, 0x03bf, 0x03c7, 0x03cf, 0x03d7, 0x002a, 0x002a, 0x002a, 0x03de, 0x03e6, 0x03e7, 0x03e8, 0x03e9, 0x03ea, + 0x03eb, 0x03ec, 0x03e6, 0x03e7, 0x03e8, 0x03e9, 0x03ea, 0x03eb, 0x03ec, 0x03e6, 0x03e7, 0x03e8, 0x03e9, 0x03ea, 0x03eb, 0x03ec, 0x03e6, 0x03e7, 0x03e8, 0x03e9, 0x03ea, 0x03eb, 0x03ec, 0x03e6, 0x03e7, 0x03e8, 0x03e9, 0x03ea, 0x03eb, 0x03ec, 0x03e6, 0x03e7, + 0x03e8, 0x03e9, 0x03ea, 0x03eb, 0x03f3, 0x03fb, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, + 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, + 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0403, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x040b, 0x0411, 0x002a, 0x0417, 0x033c, 0x041f, + 0x0424, 0x0428, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x0430, 0x002a, 0x002a, 0x002a, 0x0438, 0x002a, 0x043d, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, + 0x002a, 0x002a, 0x0445, 0x002a, 0x002a, 0x01d8, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x044d, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x0452, 0x0458, 0x002a, 0x00a7, 0x0460, 0x002a, 0x0468, 0x0470, 0x0478, 0x0480, 0x0488, 0x0490, + 0x0498, 0x04a0, 0x04a3, 0x04ab, 0x002a, 0x04b0, 0x04b8, 0x04c0, 0x002a, 0x002a, 0x04c7, 0x04cf, 0x01f8, 0x04d7, 0x002a, 0x002a, 0x04da, 0x04e2, 0x01f8, 0x04ea, 0x04ed, 0x002a, 0x04f4, 0x002a, 0x002a, 0x002a, 0x04fa, 0x002a, 0x002a, 0x002a, 0x0502, 0x050a, + 0x002a, 0x0510, 0x0518, 0x0520, 0x0528, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x052c, 0x002a, 0x0534, 0x002a, 0x053b, 0x0543, 0x054a, 0x002a, 0x002a, 0x002a, 0x002a, 0x054d, 0x0555, 0x055d, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, + 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x055f, 0x0567, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x0201, 0x056a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, + 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x0571, 0x0578, 0x057c, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, + 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0584, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, + 0x058c, 0x0594, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, + 0x002a, 0x0596, 0x0328, 0x0328, 0x0328, 0x0328, 0x059e, 0x05a5, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x05ab, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, + 0x002a, 0x002a, 0x05b3, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x05bb, + 0x05c3, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x05c7, 0x05cf, 0x002a, 0x002a, 0x05d7, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, + 0x002a, 0x05df, 0x05e7, 0x05ef, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x05f7, 0x002a, 0x05fe, 0x002a, 0x056a, 0x002a, 0x002a, + 0x002a, 0x002a, 0x002a, 0x0601, 0x0607, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x0607, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x060d, 0x002a, 0x0615, 0x002a, 0x002a, 0x002a, 0x002a, + 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x061d, 0x061e, 0x061e, 0x0625, 0x062d, 0x0633, 0x063b, 0x0641, 0x0649, 0x0651, + 0x061e, 0x061e, 0x0659, 0x0660, 0x0668, 0x066f, 0x0677, 0x067f, 0x0680, 0x0681, 0x0689, 0x0691, 0x0699, 0x069e, 0x0680, 0x06a6, 0x0680, 0x06ae, 0x002a, 0x06b6, 0x002a, 0x06be, 0x06c6, 0x06cd, 0x06d4, 0x061e, 0x06dc, 0x06e4, 0x0680, 0x0680, 0x061e, 0x06ec, + 0x06f4, 0x06fc, 0x002a, 0x002a, 0x002a, 0x002a, 0x061e, 0x061e, 0x061e, 0x061e, 0x061e, 0x061e, 0x061e, 0x061e, 0x061e, 0x061e, 0x061e, 0x061e, 0x061e, 0x061e, 0x061e, 0x0704, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, + 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0328, 0x0329, 0x070c, 0x0047, 0x0714, 0x0714, 0x0047, 0x0047, 0x0047, 0x071c, 0x0714, 0x0714, + 0x0714, 0x0714, 0x0714, 0x0714, 0x0714, 0x0714, 0x0714, 0x0714, 0x0714, 0x0714, 0x0714, 0x0714, 0x0714, 0x0714, 0x0714, 0x0714, 0x0714, 0x0714, 0x0714, 0x0714, 0x0714, 0x0714, 0x0714, 0x0714, 0x0714, 0x0714, 0x0714, 0x0714, 0x0714, 0x0714, 0x02a3, 0x02a3, + 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x02a3, 0x0724, +}; +static constexpr uint16_t s_stage2[] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0008, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0009, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0011, 0x0018, 0x0020, 0x0022, 0x002a, 0x0008, 0x0029, 0x0030, + 0x0036, 0x003e, 0x0040, 0x0047, 0x004c, 0x0008, 0x004a, 0x002d, + 0x004e, 0x002d, 0x0053, 0x0029, 0x005b, 0x0063, 0x0034, 0x0008, + 0x004e, 0x002d, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, + 0x0008, 0x0008, 0x002a, 0x006b, 0x0049, 0x0008, 0x0008, 0x0008, + 0x0008, 0x004c, 0x0008, 0x004c, 0x0008, 0x0008, 0x0008, 0x0072, + 0x005a, 0x0079, 0x0081, 0x0008, 0x0008, 0x0008, 0x0008, 0x0089, + 0x0089, 0x0089, 0x0089, 0x0089, 0x0089, 0x0089, 0x0089, 0x0008, + 0x0008, 0x0091, 0x0092, 0x0098, 0x0028, 0x0091, 0x0092, 0x0098, + 0x0028, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x004c, + 0x0008, 0x0092, 0x0092, 0x0092, 0x0092, 0x0092, 0x0092, 0x004c, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x00a0, 0x00a6, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x00ad, 0x0089, 0x0089, + 0x0089, 0x0089, 0x00af, 0x00b5, 0x0008, 0x0008, 0x0008, 0x0008, + 0x0008, 0x0008, 0x0008, 0x00bd, 0x0008, 0x0089, 0x00c5, 0x0008, + 0x0008, 0x0008, 0x0008, 0x00a0, 0x0089, 0x0089, 0x0008, 0x0008, + 0x00cd, 0x0008, 0x0008, 0x00a8, 0x00d5, 0x00dc, 0x00e3, 0x0008, + 0x0008, 0x00e9, 0x00cc, 0x0008, 0x0008, 0x0008, 0x0089, 0x0089, + 0x00a5, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x00a8, + 0x0089, 0x00cd, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x00a0, + 0x00a4, 0x00f1, 0x0008, 0x0008, 0x00a8, 0x00f9, 0x00fd, 0x00a2, + 0x0008, 0x0008, 0x0008, 0x0101, 0x0008, 0x0008, 0x0008, 0x0008, + 0x0109, 0x0089, 0x0008, 0x0008, 0x0008, 0x0008, 0x00ac, 0x0089, + 0x0089, 0x0111, 0x0089, 0x0089, 0x0089, 0x00a4, 0x0008, 0x0119, + 0x011e, 0x011e, 0x011e, 0x011e, 0x0124, 0x0089, 0x012a, 0x00ad, + 0x011e, 0x0132, 0x0008, 0x0008, 0x011e, 0x0101, 0x0008, 0x0119, + 0x011e, 0x011e, 0x013a, 0x0141, 0x0147, 0x00dc, 0x014e, 0x00ce, + 0x0154, 0x0132, 0x0008, 0x015b, 0x015d, 0x0101, 0x0008, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0008, 0x015f, 0x0165, 0x016c, 0x00cc, + 0x0008, 0x0008, 0x0008, 0x0170, 0x0008, 0x0101, 0x0008, 0x0119, + 0x011e, 0x011e, 0x013a, 0x0178, 0x0147, 0x00af, 0x0180, 0x0008, + 0x0008, 0x0132, 0x0008, 0x0008, 0x0187, 0x00dc, 0x014e, 0x00a9, + 0x0154, 0x0132, 0x0008, 0x018f, 0x0008, 0x00cb, 0x0008, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0008, 0x00a8, 0x0197, 0x00e3, 0x00ce, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x00a3, 0x0008, 0x0119, + 0x011e, 0x011e, 0x013a, 0x011e, 0x0147, 0x00b0, 0x019e, 0x01a4, + 0x01ac, 0x0132, 0x0008, 0x0008, 0x0008, 0x00b0, 0x00e3, 0x01a4, + 0x0008, 0x0132, 0x0008, 0x00ca, 0x0008, 0x00a4, 0x0008, 0x0119, + 0x011e, 0x011e, 0x011e, 0x011e, 0x017d, 0x00b0, 0x01b4, 0x00ce, + 0x0008, 0x0132, 0x0008, 0x0008, 0x0008, 0x0101, 0x0008, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x01bb, 0x01c2, 0x0089, + 0x0008, 0x0008, 0x0132, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, + 0x0008, 0x01c7, 0x00a5, 0x00ce, 0x008a, 0x0008, 0x0008, 0x0008, + 0x0008, 0x0008, 0x0008, 0x01c7, 0x00a3, 0x0008, 0x008a, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x00a6, 0x0008, 0x0008, + 0x015e, 0x01bc, 0x00b0, 0x00a9, 0x0089, 0x00ad, 0x0089, 0x0089, + 0x0089, 0x00a3, 0x015d, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, + 0x0008, 0x0008, 0x01cf, 0x0089, 0x01d3, 0x0008, 0x0008, 0x00a8, + 0x01d8, 0x01df, 0x01e6, 0x00e4, 0x0008, 0x01ec, 0x01f3, 0x0008, + 0x01fb, 0x0008, 0x0008, 0x0008, 0x0008, 0x0203, 0x0203, 0x0203, + 0x0203, 0x0203, 0x0203, 0x0203, 0x0203, 0x020b, 0x020b, 0x020b, + 0x020b, 0x020b, 0x0213, 0x0213, 0x0213, 0x0213, 0x0213, 0x0213, + 0x0213, 0x0213, 0x0008, 0x0008, 0x0008, 0x00a9, 0x0008, 0x0008, + 0x0008, 0x0008, 0x021b, 0x0008, 0x0008, 0x0008, 0x016d, 0x0008, + 0x0008, 0x0132, 0x0008, 0x0008, 0x0008, 0x0132, 0x0008, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0008, 0x00aa, 0x0089, 0x0089, 0x00a4, + 0x00f1, 0x0008, 0x0008, 0x0008, 0x0008, 0x0221, 0x0008, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0008, 0x01a4, 0x0008, 0x0008, 0x0008, + 0x0008, 0x00cc, 0x0008, 0x0008, 0x0008, 0x0008, 0x0089, 0x00a4, + 0x0089, 0x00a4, 0x0008, 0x0008, 0x00ce, 0x00a4, 0x0008, 0x0008, + 0x0008, 0x0008, 0x00a9, 0x008a, 0x0228, 0x0089, 0x0089, 0x00dc, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0089, 0x0089, + 0x008a, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x00a3, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x00aa, 0x0089, 0x00a3, + 0x0008, 0x0008, 0x0008, 0x0008, 0x00a0, 0x00a4, 0x0008, 0x00a5, + 0x0008, 0x0008, 0x0008, 0x00ad, 0x01d4, 0x0008, 0x0008, 0x0008, + 0x0008, 0x00a8, 0x0089, 0x00a4, 0x0008, 0x0008, 0x0008, 0x0008, + 0x00aa, 0x0089, 0x0089, 0x0008, 0x0008, 0x0126, 0x0089, 0x0089, + 0x0167, 0x00f2, 0x00a6, 0x0008, 0x0230, 0x0238, 0x023d, 0x0022, + 0x0245, 0x024d, 0x0253, 0x0008, 0x025a, 0x0008, 0x0008, 0x0262, + 0x0268, 0x002c, 0x007a, 0x0025, 0x0008, 0x0008, 0x0008, 0x0008, + 0x002c, 0x0008, 0x0008, 0x0089, 0x0089, 0x0089, 0x0089, 0x00cd, + 0x0008, 0x0270, 0x004c, 0x0073, 0x0008, 0x0277, 0x002d, 0x0008, + 0x025a, 0x0008, 0x0008, 0x0033, 0x0060, 0x0092, 0x0026, 0x0092, + 0x0028, 0x0008, 0x004c, 0x027f, 0x0285, 0x0008, 0x028c, 0x0008, + 0x0028, 0x0008, 0x0008, 0x0292, 0x0008, 0x007a, 0x0008, 0x0008, + 0x0008, 0x0040, 0x029a, 0x0295, 0x029f, 0x0068, 0x02a4, 0x007d, + 0x0032, 0x0008, 0x02aa, 0x002e, 0x0008, 0x02b2, 0x02b0, 0x0008, + 0x0008, 0x02b0, 0x0008, 0x002b, 0x004c, 0x002b, 0x0008, 0x0008, + 0x007a, 0x0008, 0x0008, 0x002e, 0x02ba, 0x0008, 0x02c2, 0x0008, + 0x0008, 0x02ca, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, + 0x02cb, 0x0008, 0x0008, 0x0008, 0x02d3, 0x02db, 0x02e3, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0092, 0x0092, 0x0092, 0x0092, 0x0092, + 0x0092, 0x0092, 0x0092, 0x02eb, 0x0092, 0x0092, 0x0092, 0x0092, + 0x0098, 0x0092, 0x0092, 0x0008, 0x0008, 0x0008, 0x0008, 0x0098, + 0x02f1, 0x02f7, 0x0032, 0x02fd, 0x0304, 0x0028, 0x0008, 0x0239, + 0x007a, 0x0008, 0x030c, 0x0314, 0x031b, 0x0323, 0x0329, 0x0330, + 0x0330, 0x0330, 0x0330, 0x032d, 0x0338, 0x033c, 0x0330, 0x0344, + 0x0347, 0x0330, 0x0331, 0x034f, 0x0008, 0x0357, 0x035b, 0x0359, + 0x0363, 0x0330, 0x0367, 0x0368, 0x036e, 0x0370, 0x0375, 0x034b, + 0x0372, 0x037b, 0x0381, 0x0389, 0x0363, 0x0391, 0x02c5, 0x025a, + 0x0399, 0x028a, 0x002b, 0x039d, 0x03a5, 0x03ac, 0x0008, 0x03b4, + 0x0008, 0x004e, 0x0092, 0x0008, 0x0008, 0x03bc, 0x0008, 0x025a, + 0x0008, 0x0399, 0x03c4, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, + 0x0008, 0x0289, 0x0008, 0x03cc, 0x0008, 0x0008, 0x03d4, 0x0008, + 0x0008, 0x0008, 0x0008, 0x03d8, 0x0028, 0x0008, 0x0008, 0x0008, + 0x0008, 0x0008, 0x00ce, 0x00a6, 0x0008, 0x0008, 0x0008, 0x0008, + 0x0008, 0x0008, 0x0008, 0x00ce, 0x03e0, 0x03e0, 0x03e0, 0x03e6, + 0x03e0, 0x03e0, 0x03e0, 0x03e0, 0x03e0, 0x03e0, 0x03ea, 0x0008, + 0x03e0, 0x03e0, 0x03e0, 0x03e0, 0x03e0, 0x03e0, 0x03e0, 0x03e0, + 0x03f2, 0x0008, 0x0008, 0x0008, 0x03e0, 0x03e0, 0x03e0, 0x03e0, + 0x03e0, 0x03fa, 0x0402, 0x0405, 0x040c, 0x03e0, 0x03e0, 0x03e0, + 0x03e0, 0x03e0, 0x03e0, 0x03e0, 0x03e1, 0x0414, 0x03e0, 0x03e0, + 0x03e0, 0x03e0, 0x041c, 0x03e0, 0x03e0, 0x03e0, 0x03e0, 0x03e0, + 0x040c, 0x03e0, 0x03e1, 0x03e0, 0x03e0, 0x03e0, 0x03e0, 0x03e0, + 0x03e0, 0x03ea, 0x0424, 0x03e0, 0x03e0, 0x03e0, 0x03e1, 0x03e0, + 0x03e0, 0x03e0, 0x03e0, 0x0092, 0x03e0, 0x03e0, 0x03e0, 0x03e0, + 0x03e0, 0x03e0, 0x0403, 0x042b, 0x03e0, 0x03e0, 0x03e0, 0x03e0, + 0x03e9, 0x03e0, 0x03e0, 0x03e0, 0x03e0, 0x03e0, 0x03e0, 0x03e1, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x00ce, + 0x0126, 0x01d4, 0x0008, 0x0008, 0x0008, 0x00a8, 0x0008, 0x0008, + 0x0008, 0x0008, 0x0433, 0x00ca, 0x0008, 0x0008, 0x00a0, 0x043a, + 0x0008, 0x0008, 0x00a6, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, + 0x00aa, 0x0089, 0x01d4, 0x0008, 0x0008, 0x0008, 0x0089, 0x0089, + 0x00a6, 0x00ce, 0x0008, 0x0008, 0x0008, 0x0008, 0x00a8, 0x01d4, + 0x0008, 0x0008, 0x00ce, 0x0089, 0x00a4, 0x0008, 0x0203, 0x0203, + 0x0203, 0x0442, 0x00a4, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, + 0x00a0, 0x0089, 0x00cd, 0x0008, 0x0008, 0x0008, 0x00f1, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0008, 0x00ad, 0x008a, 0x0008, 0x00ca, + 0x0447, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x044d, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0455, 0x045c, 0x00cc, + 0x0008, 0x0008, 0x0008, 0x0008, 0x00a0, 0x01a4, 0x0008, 0x0008, + 0x0008, 0x0008, 0x00a0, 0x0462, 0x0008, 0x0008, 0x046a, 0x046b, + 0x046b, 0x046f, 0x046b, 0x046b, 0x046b, 0x046a, 0x046b, 0x046b, + 0x046f, 0x046b, 0x046b, 0x046b, 0x046a, 0x046b, 0x046b, 0x0474, + 0x0008, 0x020b, 0x020b, 0x047c, 0x0483, 0x0213, 0x0213, 0x0213, + 0x0213, 0x0213, 0x0487, 0x0008, 0x0008, 0x0008, 0x015d, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0089, 0x0089, 0x03e0, 0x0422, 0x0089, + 0x0089, 0x03e0, 0x03e0, 0x03e5, 0x03e0, 0x03e1, 0x03ea, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x048b, 0x03e0, + 0x03e0, 0x03e0, 0x03e0, 0x0423, 0x0008, 0x0008, 0x0008, 0x0493, + 0x0008, 0x0008, 0x0008, 0x0008, 0x03e1, 0x0008, 0x0000, 0x049b, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x00f1, + 0x0008, 0x0008, 0x0008, 0x0008, 0x00cd, 0x0008, 0x0008, 0x0008, + 0x0008, 0x0008, 0x0008, 0x00a8, 0x00a5, 0x0461, 0x00aa, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0165, 0x0008, 0x0008, 0x0008, + 0x0008, 0x00aa, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0448, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x00a9, + 0x021b, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, + 0x00a5, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0089, + 0x008a, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0131, 0x00ce, + 0x00a5, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0089, 0x04a3, + 0x00cb, 0x00eb, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, + 0x00a5, 0x0008, 0x0008, 0x0008, 0x00ce, 0x0089, 0x00a3, 0x0008, + 0x01a4, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x00ca, 0x0008, + 0x00a5, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x00a0, 0x0089, + 0x04ab, 0x04b2, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, + 0x00aa, 0x0089, 0x015d, 0x00cc, 0x0008, 0x0008, 0x0008, 0x0008, + 0x0008, 0x0008, 0x0008, 0x00ce, 0x0089, 0x00a5, 0x0008, 0x0008, + 0x00a4, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x019a, + 0x00dc, 0x016c, 0x00ce, 0x0008, 0x04ba, 0x00a3, 0x00a3, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x00a9, 0x0089, 0x008a, + 0x0008, 0x0008, 0x015d, 0x0008, 0x0008, 0x0008, 0x0008, 0x00a4, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x00ce, + 0x01d4, 0x0089, 0x00cd, 0x0008, 0x0008, 0x0447, 0x0008, 0x0008, + 0x0008, 0x0008, 0x00cd, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, + 0x0008, 0x0008, 0x00a0, 0x0089, 0x0008, 0x0008, 0x0008, 0x00a9, + 0x01d2, 0x00a4, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x00aa, + 0x0089, 0x00a5, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, + 0x00af, 0x04bd, 0x04c3, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, + 0x0008, 0x0008, 0x00ad, 0x00ac, 0x0435, 0x0008, 0x0008, 0x0008, + 0x00ad, 0x00a5, 0x0008, 0x0008, 0x0008, 0x0008, 0x00a0, 0x04cb, + 0x00ce, 0x0008, 0x00ad, 0x00a4, 0x0008, 0x0008, 0x0008, 0x0008, + 0x04d2, 0x04d8, 0x0089, 0x00a6, 0x0008, 0x0008, 0x0008, 0x0008, + 0x0008, 0x00ce, 0x008a, 0x0089, 0x0008, 0x0008, 0x00ac, 0x0089, + 0x0089, 0x00ad, 0x008a, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, + 0x0008, 0x04e0, 0x04e7, 0x04ee, 0x0008, 0x0008, 0x0008, 0x0008, + 0x0008, 0x0008, 0x0008, 0x00a1, 0x00f9, 0x0008, 0x0008, 0x0008, + 0x0008, 0x0008, 0x0008, 0x04b0, 0x0008, 0x04f2, 0x0008, 0x0008, + 0x0008, 0x0008, 0x0008, 0x00aa, 0x0197, 0x00a5, 0x0008, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0268, 0x0268, 0x00a7, + 0x0089, 0x01d4, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, + 0x008a, 0x0008, 0x00ce, 0x00ad, 0x0089, 0x0089, 0x0089, 0x0089, + 0x0089, 0x00ce, 0x00a5, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, + 0x04fa, 0x0008, 0x00a6, 0x0008, 0x03e0, 0x03e0, 0x03e0, 0x03e0, + 0x03e0, 0x03e0, 0x03e0, 0x0008, 0x03e0, 0x03e0, 0x03f2, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0008, 0x03e0, 0x0423, 0x0008, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0008, 0x03e4, 0x0502, 0x03e0, 0x03e0, + 0x03e0, 0x03e0, 0x0419, 0x0008, 0x0509, 0x0008, 0x0008, 0x0511, + 0x0008, 0x03ee, 0x0008, 0x03e0, 0x03e0, 0x03e0, 0x03e0, 0x03e0, + 0x03e0, 0x03e0, 0x03ea, 0x0008, 0x0008, 0x0008, 0x01a4, 0x0519, + 0x0008, 0x0008, 0x0008, 0x0089, 0x0089, 0x0089, 0x0089, 0x0089, + 0x01d4, 0x0089, 0x0089, 0x008a, 0x0008, 0x0008, 0x0008, 0x0008, + 0x0008, 0x0008, 0x0008, 0x00a9, 0x021f, 0x0521, 0x0526, 0x052b, + 0x00a4, 0x0008, 0x0008, 0x0008, 0x021b, 0x0008, 0x0008, 0x016d, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0089, + 0x0089, 0x0089, 0x0089, 0x0089, 0x0089, 0x008a, 0x00a0, 0x0089, + 0x0089, 0x0089, 0x0089, 0x0089, 0x00a3, 0x00f1, 0x0008, 0x043a, + 0x0008, 0x0008, 0x00a0, 0x00ad, 0x0089, 0x0008, 0x0008, 0x008a, + 0x0089, 0x0089, 0x052d, 0x00b3, 0x00a5, 0x0008, 0x0008, 0x00ce, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x015d, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0008, 0x00aa, 0x0008, 0x0008, 0x008a, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x00aa, 0x00a5, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0535, 0x0330, 0x0330, + 0x0330, 0x0330, 0x0330, 0x0330, 0x0330, 0x0330, 0x0331, 0x0330, + 0x0330, 0x0330, 0x0330, 0x0330, 0x0330, 0x0092, 0x053d, 0x0092, + 0x0092, 0x0092, 0x0545, 0x0092, 0x0092, 0x0092, 0x0092, 0x0092, + 0x054d, 0x0555, 0x0557, 0x0092, 0x055f, 0x0566, 0x056b, 0x0092, + 0x056e, 0x0330, 0x0330, 0x0330, 0x0330, 0x0573, 0x0579, 0x0579, + 0x0579, 0x0581, 0x0330, 0x03e0, 0x0589, 0x03e0, 0x0403, 0x058f, + 0x0594, 0x03e0, 0x0597, 0x059f, 0x0330, 0x033a, 0x0330, 0x0330, + 0x0330, 0x0338, 0x0338, 0x0338, 0x0338, 0x05a0, 0x0333, 0x05a8, + 0x0338, 0x0338, 0x0338, 0x0338, 0x0338, 0x0338, 0x0338, 0x05a9, + 0x0338, 0x0338, 0x033c, 0x0330, 0x0338, 0x0338, 0x0338, 0x0338, + 0x05af, 0x033c, 0x0330, 0x0338, 0x0338, 0x05b6, 0x05be, 0x0338, + 0x0338, 0x0338, 0x0338, 0x0338, 0x0338, 0x0338, 0x0339, 0x05c6, + 0x0338, 0x0338, 0x0338, 0x0338, 0x0338, 0x0338, 0x0338, 0x0338, + 0x05c9, 0x0338, 0x0338, 0x0338, 0x0338, 0x0338, 0x0338, 0x0338, + 0x05d0, 0x0287, 0x05d8, 0x0338, 0x0338, 0x0338, 0x0330, 0x0330, + 0x0358, 0x0330, 0x0330, 0x059a, 0x0330, 0x0535, 0x0330, 0x0330, + 0x0330, 0x0330, 0x0330, 0x0330, 0x0330, 0x0335, 0x0338, 0x0338, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x033a, 0x0535, + 0x05cb, 0x0334, 0x0330, 0x059c, 0x0334, 0x033b, 0x0008, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0008, 0x05e0, 0x0330, 0x0008, 0x0008, + 0x03cc, 0x0330, 0x0338, 0x033c, 0x05a0, 0x0330, 0x0008, 0x05e0, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0330, 0x0008, + 0x05e2, 0x0008, 0x0008, 0x0008, 0x0008, 0x0330, 0x0008, 0x0008, + 0x0008, 0x0287, 0x0330, 0x0330, 0x0008, 0x05ea, 0x0338, 0x0338, + 0x0338, 0x0338, 0x0338, 0x05ef, 0x05f3, 0x0338, 0x0338, 0x0338, + 0x0338, 0x0338, 0x0338, 0x0338, 0x0330, 0x0330, 0x0330, 0x0330, + 0x0330, 0x0330, 0x0338, 0x033b, 0x0338, 0x05a0, 0x0338, 0x0338, + 0x0338, 0x0338, 0x0338, 0x05a8, 0x033a, 0x0332, 0x0338, 0x033c, + 0x0338, 0x05a0, 0x0338, 0x05a0, 0x0330, 0x0330, 0x0330, 0x0330, + 0x0330, 0x0330, 0x0330, 0x034f, 0x05fb, 0x0000, 0x0000, 0x0000, + 0x0089, 0x0089, 0x0089, 0x0089, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0089, 0x0089, 0x0089, 0x0089, + 0x0089, 0x0089, 0x0000, 0x0000, 0x0092, 0x0092, 0x0092, 0x0092, + 0x0092, 0x0092, 0x0092, 0x0603, +}; +static constexpr uint8_t s_stage3[] = { + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x41, 0x40, 0xc0, 0x40, 0x40, 0xc0, 0x40, 0x40, + 0xc0, 0x4c, 0xc0, 0x40, 0x40, 0x01, 0xcc, 0x40, + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x40, 0xc0, 0xc0, + 0xc0, 0xc0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0xc0, 0x40, 0x40, 0x40, 0x40, 0x40, 0xc0, 0xc0, + 0x40, 0x40, 0x40, 0x40, 0xc0, 0x40, 0xc0, 0xc0, + 0xc0, 0x40, 0xc0, 0xc0, 0x40, 0x40, 0x40, 0xc0, + 0xc0, 0xc0, 0x40, 0xc0, 0x40, 0xc0, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0xc0, 0xc0, 0xc0, 0x40, + 0x40, 0x40, 0x40, 0xc0, 0xc0, 0xc0, 0x40, 0xc0, + 0x40, 0x40, 0x40, 0xc0, 0xc0, 0xc0, 0xc0, 0x40, + 0xc0, 0x40, 0x40, 0xc0, 0x40, 0xc0, 0x40, 0xc0, + 0x40, 0xc0, 0x40, 0x40, 0x40, 0x40, 0xc0, 0x40, + 0x40, 0xc0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0xc0, 0xc0, 0xc0, 0xc0, 0x40, 0xc0, 0x40, + 0xc0, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x40, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + 0xc0, 0xc0, 0x40, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + 0x40, 0x40, 0x40, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x40, 0x02, 0x02, + 0x40, 0x02, 0x02, 0x40, 0x02, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x40, 0x40, 0x02, 0x02, 0x02, + 0x40, 0x01, 0x40, 0x40, 0x40, 0x02, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x04, 0x40, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x40, 0x40, 0x02, 0x40, 0x02, 0x02, 0x02, + 0x02, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x04, 0x40, 0x40, 0x40, 0x40, 0x40, 0x02, 0x40, + 0x40, 0x02, 0x02, 0x40, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x40, 0x02, 0x02, 0x02, 0x40, 0x40, 0x40, + 0x40, 0x04, 0x04, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x02, 0x02, 0x04, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x40, 0x40, 0x40, 0x40, 0x40, 0x4b, 0x4b, + 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x02, 0x02, + 0x02, 0x40, 0x02, 0x02, 0x02, 0x02, 0x02, 0x0a, + 0x02, 0x02, 0x40, 0x40, 0x02, 0x02, 0x40, 0x40, + 0x40, 0x40, 0x4b, 0x40, 0x4b, 0x4b, 0x4b, 0x4b, + 0x4b, 0x4b, 0x40, 0x4b, 0x40, 0x40, 0x40, 0x4b, + 0x4b, 0x40, 0x40, 0x02, 0x40, 0x02, 0x02, 0x40, + 0x40, 0x02, 0x02, 0x0a, 0x40, 0x40, 0x40, 0x40, + 0x4b, 0x4b, 0x40, 0x4b, 0x4b, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x02, 0x40, 0x02, 0x02, 0x02, + 0x40, 0x40, 0x40, 0x40, 0x02, 0x40, 0x40, 0x02, + 0x02, 0x02, 0x40, 0x40, 0x40, 0x02, 0x40, 0x40, + 0x4b, 0x40, 0x4b, 0x4b, 0x40, 0x4b, 0x4b, 0x4b, + 0x02, 0x02, 0x40, 0x02, 0x02, 0x0a, 0x40, 0x40, + 0x4b, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x40, + 0x4b, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x02, + 0x02, 0x02, 0x40, 0x40, 0x40, 0x02, 0x02, 0x40, + 0x02, 0x02, 0x02, 0x0a, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x02, 0x02, 0x40, 0x4b, 0x4b, 0x4b, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x02, 0x40, 0x02, 0x02, + 0x02, 0x0a, 0x44, 0x40, 0x40, 0x02, 0x40, 0x40, + 0x40, 0x40, 0x02, 0x02, 0x02, 0x02, 0x02, 0x40, + 0x02, 0x40, 0x42, 0x02, 0x02, 0x02, 0x02, 0x40, + 0x40, 0x40, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x40, 0x40, 0x40, 0x40, 0x02, 0x02, + 0x40, 0x00, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x02, 0x00, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x40, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, + 0x02, 0x40, 0x40, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x46, 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x46, 0x47, 0x47, 0x47, 0x47, 0x47, + 0x47, 0x47, 0x47, 0x40, 0x40, 0x02, 0x02, 0x02, + 0x02, 0x40, 0x40, 0x40, 0x02, 0x02, 0x02, 0x01, + 0x02, 0x00, 0x02, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x40, 0x40, 0x40, 0x01, 0x02, 0x0d, 0x01, 0x01, + 0xc0, 0x40, 0x40, 0xc0, 0xc0, 0xc0, 0xc0, 0x40, + 0x40, 0xc0, 0xc0, 0x40, 0x40, 0x41, 0x41, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x40, 0xc0, 0x40, 0xc0, + 0xc0, 0x40, 0xc0, 0x40, 0x40, 0x40, 0xc0, 0x4c, + 0x40, 0xc0, 0x40, 0x4c, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x01, 0x01, 0x01, 0x01, 0x01, 0x41, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x40, 0x40, 0x40, 0xc0, 0x40, 0xc0, 0x40, 0x40, + 0xc0, 0xcc, 0x40, 0x40, 0x40, 0xc0, 0x40, 0xc0, + 0xc0, 0xc0, 0xc0, 0xcc, 0xcc, 0xcc, 0xcc, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x4c, 0x4c, 0x40, + 0x40, 0x40, 0x40, 0x40, 0xc0, 0x40, 0xc0, 0x40, + 0x40, 0x40, 0xc0, 0x40, 0x40, 0xc0, 0x40, 0x40, + 0x40, 0xc0, 0x40, 0x40, 0xc0, 0xc0, 0xc0, 0xc0, + 0xc0, 0x40, 0xc0, 0x40, 0x40, 0x40, 0xc0, 0x40, + 0x40, 0x40, 0xc0, 0xc0, 0x40, 0x40, 0xc0, 0xc0, + 0xc0, 0xc0, 0x40, 0x40, 0x8c, 0x8c, 0x40, 0x40, + 0x40, 0x40, 0x4c, 0x80, 0x80, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x4c, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x4c, 0x40, 0x8c, 0x8c, 0x8c, 0x8c, + 0x4c, 0x4c, 0x4c, 0x8c, 0x4c, 0x4c, 0x8c, 0x40, + 0x40, 0x40, 0x40, 0x4c, 0x4c, 0x4c, 0x40, 0x40, + 0x40, 0x40, 0x40, 0xc0, 0xc0, 0xcc, 0xc0, 0xc0, + 0xc0, 0xc0, 0xc0, 0x4c, 0x4c, 0x40, 0x40, 0x40, + 0x40, 0xc0, 0xc0, 0x40, 0x40, 0xcc, 0xc0, 0x40, + 0x40, 0x40, 0x40, 0xc0, 0xc0, 0x40, 0x40, 0xc0, + 0x40, 0x40, 0xc0, 0xc0, 0x40, 0x40, 0x40, 0x4c, + 0x4c, 0x8c, 0x8c, 0x40, 0x4c, 0x4c, 0x4c, 0x4c, + 0x4c, 0xcc, 0xc0, 0x4c, 0xcc, 0x4c, 0x4c, 0x4c, + 0x4c, 0xcc, 0xcc, 0x4c, 0x4c, 0x4c, 0x40, 0x8c, + 0x8c, 0x4c, 0x4c, 0x4c, 0x4c, 0xcc, 0x4c, 0xcc, + 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x4c, 0x4c, 0x4c, 0x4c, 0xcc, 0xcc, 0x4c, 0xcc, + 0xcc, 0xcc, 0x4c, 0xcc, 0xcc, 0x4c, 0xcc, 0x4c, + 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x40, 0x40, 0x4c, + 0x4c, 0x4c, 0x8c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, + 0x4c, 0xcc, 0xcc, 0x4c, 0x4c, 0x8c, 0x8c, 0x4c, + 0x4c, 0x4c, 0x4c, 0x4c, 0x8c, 0x8c, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0x8c, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x8c, 0x8c, 0xcc, + 0x8c, 0xcc, 0xcc, 0x8c, 0xcc, 0xcc, 0x8c, 0xcc, + 0xcc, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x8c, 0x40, + 0x40, 0x4c, 0x4c, 0x4c, 0x40, 0x4c, 0x40, 0x4c, + 0x40, 0x8c, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x4c, 0x40, 0x40, 0x4c, 0x40, 0x40, 0x40, + 0x40, 0x8c, 0x40, 0x8c, 0x40, 0x40, 0x40, 0x8c, + 0x8c, 0x8c, 0x40, 0x8c, 0x40, 0x40, 0x40, 0x4c, + 0x4c, 0x4c, 0x4c, 0x4c, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x8c, 0x8c, 0x8c, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x8c, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x4c, 0x4c, 0x4c, 0x40, 0x40, 0x40, 0x8c, + 0x8c, 0x40, 0x40, 0x40, 0x40, 0x8c, 0xc0, 0xc0, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x40, 0x80, 0x80, 0x80, 0x80, 0x80, 0x40, 0x40, + 0x40, 0x40, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x40, 0x40, 0x80, 0x80, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x8c, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x8c, 0x80, 0x40, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x40, 0x02, 0x02, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x80, 0x80, 0x80, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x80, 0x8c, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x40, 0x40, 0x02, 0x40, 0x40, + 0x40, 0x02, 0x40, 0x40, 0x40, 0x40, 0x02, 0x40, + 0x40, 0x40, 0x85, 0x85, 0x85, 0x85, 0x85, 0x40, + 0x40, 0x40, 0x40, 0x02, 0x02, 0x40, 0x40, 0x40, + 0x00, 0x02, 0x00, 0x40, 0x40, 0x02, 0x40, 0x02, + 0x02, 0x02, 0x40, 0x40, 0x02, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x02, 0x02, 0x02, 0x40, 0x02, 0x02, + 0x40, 0x40, 0x88, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x88, 0x89, 0x89, 0x89, 0x89, + 0x40, 0x40, 0x40, 0x40, 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x46, 0x40, 0x40, 0x40, 0x47, 0x47, + 0x47, 0x47, 0x47, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x01, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x42, 0x42, 0x41, 0x01, 0x01, 0x01, 0x40, + 0xc0, 0x40, 0x40, 0x02, 0x02, 0x02, 0x40, 0x40, + 0x04, 0x40, 0x40, 0x02, 0x40, 0x44, 0x44, 0x40, + 0x40, 0x40, 0x40, 0x02, 0x02, 0x02, 0x02, 0x40, + 0x02, 0x02, 0x40, 0x40, 0x02, 0x02, 0x40, 0x40, + 0x02, 0x02, 0x02, 0x02, 0x44, 0x02, 0x02, 0x40, + 0x40, 0x40, 0x40, 0x02, 0x02, 0x44, 0x02, 0x02, + 0x02, 0x02, 0x40, 0x40, 0x40, 0x40, 0x44, 0x44, + 0x44, 0x44, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x40, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x40, + 0x40, 0x02, 0x40, 0x02, 0x02, 0x40, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x44, 0x02, 0x40, 0x40, + 0x40, 0x40, 0x80, 0x80, 0x80, 0x80, 0x02, 0x40, + 0x40, 0x40, 0x80, 0x80, 0x80, 0x80, 0x40, 0x80, + 0x80, 0x40, 0x40, 0x80, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x80, 0x80, 0x80, 0x40, 0x40, 0x80, 0x40, + 0x40, 0x01, 0x01, 0x01, 0x01, 0x40, 0x40, 0x40, + 0x40, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x40, 0x40, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x4c, 0x4c, 0x4c, + 0x4c, 0x8c, 0x4c, 0x4c, 0x4c, 0xc0, 0xc0, 0xc0, + 0x40, 0x40, 0x4c, 0x4c, 0x4c, 0xc0, 0xc0, 0xc0, + 0xc0, 0xc0, 0xc0, 0x40, 0x4c, 0xc0, 0xc0, 0x40, + 0x40, 0x4c, 0x4c, 0x4c, 0x4c, 0xcc, 0xcc, 0xc0, + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xcc, 0xcc, 0xc0, + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x8c, 0xc0, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0xc0, 0xc0, + 0xc0, 0xc0, 0xc0, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, + 0x4c, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, + 0x43, 0x80, 0x8c, 0x8c, 0x4c, 0x4c, 0x4c, 0x4c, + 0x4c, 0x80, 0x80, 0x8c, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x80, + 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x8c, + 0x8c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x4c, 0x8c, + 0x8c, 0x8c, 0x4c, 0x4c, 0x4c, 0x4c, 0x8c, 0x4c, + 0x4c, 0x4c, 0x8c, 0x4c, 0x4c, 0x4c, 0x8c, 0x8c, + 0x8c, 0x82, 0x82, 0x82, 0x82, 0x82, 0x8c, 0x4c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x4c, 0x4c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x40, 0x40, + 0x4c, 0x4c, 0x4c, 0x8c, 0x8c, 0x8c, 0x8c, 0x4c, + 0x40, 0x40, 0x40, 0x40, 0x4c, 0x4c, 0x4c, 0x4c, + 0x4c, 0x4c, 0x40, 0x40, 0x40, 0x40, 0x8c, 0x8c, + 0x8c, 0x8c, 0x40, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x40, 0x8c, 0x41, 0x01, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + 0xc0, 0x40, 0x40, +}; +static constexpr uint32_t s_joinRules[2][16] = { + { + 0b00000011110011111111111111001111, + 0b00001111111111111111111111111111, + 0b00000011110011111111111111001111, + 0b00000011110011111111111101001111, + 0b00000000000000000000000000001100, + 0b00000011110000001100001111001111, + 0b00000011110011110000111111001111, + 0b00000011110011110011111111001111, + 0b00000011110011110000111111001111, + 0b00000011110011110011111111001111, + 0b00000011000011111111111111001111, + 0b00000011110011111111111111001111, + 0b00000011110011111111111111001111, + 0b00000000110011111111111111001111, + 0b00000000000000000000000000000000, + 0b00000000000000000000000000000000, + }, + { + 0b00000011110011111111111111001111, + 0b00001111111111111111111111111111, + 0b00000011110011111111111111001111, + 0b00000011110011111111111111001111, + 0b00000000000000000000000000001100, + 0b00000011110000001100001111001111, + 0b00000011110011110000111111001111, + 0b00000011110011110011111111001111, + 0b00000011110011110000111111001111, + 0b00000011110011110011111111001111, + 0b00000011000011111111111111001111, + 0b00000011110011111111111111001111, + 0b00000011110011111111111111001111, + 0b00000000110011111111111111001111, + 0b00000000000000000000000000000000, + 0b00000000000000000000000000000000, + }, +}; +constexpr int ucdLookup(const char32_t cp) noexcept { - // used to store range data in CodepointWidthDetector's internal map - struct UnicodeRange final - { - char32_t lowerBound; - char32_t upperBound : 31; - char32_t isAmbiguous : 1; - }; - - static bool operator<(const UnicodeRange& range, const unsigned int searchTerm) noexcept - { - return range.upperBound < searchTerm; - } - - // Generated by Generate-CodepointWidthsFromUCD.ps1 -Pack:True -Full: -NoOverrides:False - // on 2022-11-15 19:54:23Z from Unicode 15.0.0. - // 321149 (0x4E67D) codepoints covered. - // 240 (0xF0) codepoints overridden. - // Override path: .\src\types\unicode_width_overrides.xml - static constexpr std::array s_wideAndAmbiguousTable{ - UnicodeRange{ 0xa1, 0xa1, 1 }, - UnicodeRange{ 0xa4, 0xa4, 1 }, - UnicodeRange{ 0xa7, 0xa8, 1 }, - UnicodeRange{ 0xaa, 0xaa, 1 }, - UnicodeRange{ 0xad, 0xae, 1 }, - UnicodeRange{ 0xb0, 0xb4, 1 }, - UnicodeRange{ 0xb6, 0xba, 1 }, - UnicodeRange{ 0xbc, 0xbf, 1 }, - UnicodeRange{ 0xc6, 0xc6, 1 }, - UnicodeRange{ 0xd0, 0xd0, 1 }, - UnicodeRange{ 0xd7, 0xd8, 1 }, - UnicodeRange{ 0xde, 0xe1, 1 }, - UnicodeRange{ 0xe6, 0xe6, 1 }, - UnicodeRange{ 0xe8, 0xea, 1 }, - UnicodeRange{ 0xec, 0xed, 1 }, - UnicodeRange{ 0xf0, 0xf0, 1 }, - UnicodeRange{ 0xf2, 0xf3, 1 }, - UnicodeRange{ 0xf7, 0xfa, 1 }, - UnicodeRange{ 0xfc, 0xfc, 1 }, - UnicodeRange{ 0xfe, 0xfe, 1 }, - UnicodeRange{ 0x101, 0x101, 1 }, - UnicodeRange{ 0x111, 0x111, 1 }, - UnicodeRange{ 0x113, 0x113, 1 }, - UnicodeRange{ 0x11b, 0x11b, 1 }, - UnicodeRange{ 0x126, 0x127, 1 }, - UnicodeRange{ 0x12b, 0x12b, 1 }, - UnicodeRange{ 0x131, 0x133, 1 }, - UnicodeRange{ 0x138, 0x138, 1 }, - UnicodeRange{ 0x13f, 0x142, 1 }, - UnicodeRange{ 0x144, 0x144, 1 }, - UnicodeRange{ 0x148, 0x14b, 1 }, - UnicodeRange{ 0x14d, 0x14d, 1 }, - UnicodeRange{ 0x152, 0x153, 1 }, - UnicodeRange{ 0x166, 0x167, 1 }, - UnicodeRange{ 0x16b, 0x16b, 1 }, - UnicodeRange{ 0x1ce, 0x1ce, 1 }, - UnicodeRange{ 0x1d0, 0x1d0, 1 }, - UnicodeRange{ 0x1d2, 0x1d2, 1 }, - UnicodeRange{ 0x1d4, 0x1d4, 1 }, - UnicodeRange{ 0x1d6, 0x1d6, 1 }, - UnicodeRange{ 0x1d8, 0x1d8, 1 }, - UnicodeRange{ 0x1da, 0x1da, 1 }, - UnicodeRange{ 0x1dc, 0x1dc, 1 }, - UnicodeRange{ 0x251, 0x251, 1 }, - UnicodeRange{ 0x261, 0x261, 1 }, - UnicodeRange{ 0x2c4, 0x2c4, 1 }, - UnicodeRange{ 0x2c7, 0x2c7, 1 }, - UnicodeRange{ 0x2c9, 0x2cb, 1 }, - UnicodeRange{ 0x2cd, 0x2cd, 1 }, - UnicodeRange{ 0x2d0, 0x2d0, 1 }, - UnicodeRange{ 0x2d8, 0x2db, 1 }, - UnicodeRange{ 0x2dd, 0x2dd, 1 }, - UnicodeRange{ 0x2df, 0x2df, 1 }, - UnicodeRange{ 0x300, 0x36f, 1 }, - UnicodeRange{ 0x391, 0x3a1, 1 }, - UnicodeRange{ 0x3a3, 0x3a9, 1 }, - UnicodeRange{ 0x3b1, 0x3c1, 1 }, - UnicodeRange{ 0x3c3, 0x3c9, 1 }, - UnicodeRange{ 0x401, 0x401, 1 }, - UnicodeRange{ 0x410, 0x44f, 1 }, - UnicodeRange{ 0x451, 0x451, 1 }, - UnicodeRange{ 0x1100, 0x115f, 0 }, - UnicodeRange{ 0x2010, 0x2010, 1 }, - UnicodeRange{ 0x2013, 0x2016, 1 }, - UnicodeRange{ 0x2018, 0x2019, 1 }, - UnicodeRange{ 0x201c, 0x201d, 1 }, - UnicodeRange{ 0x2020, 0x2022, 1 }, - UnicodeRange{ 0x2024, 0x2027, 1 }, - UnicodeRange{ 0x2030, 0x2030, 1 }, - UnicodeRange{ 0x2032, 0x2033, 1 }, - UnicodeRange{ 0x2035, 0x2035, 1 }, - UnicodeRange{ 0x203b, 0x203b, 1 }, - UnicodeRange{ 0x203e, 0x203e, 1 }, - UnicodeRange{ 0x2074, 0x2074, 1 }, - UnicodeRange{ 0x207f, 0x207f, 1 }, - UnicodeRange{ 0x2081, 0x2084, 1 }, - UnicodeRange{ 0x20ac, 0x20ac, 1 }, - UnicodeRange{ 0x2103, 0x2103, 1 }, - UnicodeRange{ 0x2105, 0x2105, 1 }, - UnicodeRange{ 0x2109, 0x2109, 1 }, - UnicodeRange{ 0x2113, 0x2113, 1 }, - UnicodeRange{ 0x2116, 0x2116, 1 }, - UnicodeRange{ 0x2121, 0x2122, 1 }, - UnicodeRange{ 0x2126, 0x2126, 1 }, - UnicodeRange{ 0x212b, 0x212b, 1 }, - UnicodeRange{ 0x2153, 0x2154, 1 }, - UnicodeRange{ 0x215b, 0x215e, 1 }, - UnicodeRange{ 0x2160, 0x216b, 1 }, - UnicodeRange{ 0x2170, 0x2179, 1 }, - UnicodeRange{ 0x2189, 0x2189, 1 }, - UnicodeRange{ 0x2190, 0x2199, 1 }, - UnicodeRange{ 0x21b8, 0x21b9, 1 }, - UnicodeRange{ 0x21d2, 0x21d2, 1 }, - UnicodeRange{ 0x21d4, 0x21d4, 1 }, - UnicodeRange{ 0x21e7, 0x21e7, 1 }, - UnicodeRange{ 0x2200, 0x2200, 1 }, - UnicodeRange{ 0x2202, 0x2203, 1 }, - UnicodeRange{ 0x2207, 0x2208, 1 }, - UnicodeRange{ 0x220b, 0x220b, 1 }, - UnicodeRange{ 0x220f, 0x220f, 1 }, - UnicodeRange{ 0x2211, 0x2211, 1 }, - UnicodeRange{ 0x2215, 0x2215, 1 }, - UnicodeRange{ 0x221a, 0x221a, 1 }, - UnicodeRange{ 0x221d, 0x2220, 1 }, - UnicodeRange{ 0x2223, 0x2223, 1 }, - UnicodeRange{ 0x2225, 0x2225, 1 }, - UnicodeRange{ 0x2227, 0x222c, 1 }, - UnicodeRange{ 0x222e, 0x222e, 1 }, - UnicodeRange{ 0x2234, 0x2237, 1 }, - UnicodeRange{ 0x223c, 0x223d, 1 }, - UnicodeRange{ 0x2248, 0x2248, 1 }, - UnicodeRange{ 0x224c, 0x224c, 1 }, - UnicodeRange{ 0x2252, 0x2252, 1 }, - UnicodeRange{ 0x2260, 0x2261, 1 }, - UnicodeRange{ 0x2264, 0x2267, 1 }, - UnicodeRange{ 0x226a, 0x226b, 1 }, - UnicodeRange{ 0x226e, 0x226f, 1 }, - UnicodeRange{ 0x2282, 0x2283, 1 }, - UnicodeRange{ 0x2286, 0x2287, 1 }, - UnicodeRange{ 0x2295, 0x2295, 1 }, - UnicodeRange{ 0x2299, 0x2299, 1 }, - UnicodeRange{ 0x22a5, 0x22a5, 1 }, - UnicodeRange{ 0x22bf, 0x22bf, 1 }, - UnicodeRange{ 0x2312, 0x2312, 1 }, - UnicodeRange{ 0x231a, 0x231b, 0 }, - UnicodeRange{ 0x2329, 0x232a, 0 }, - UnicodeRange{ 0x23e9, 0x23ec, 0 }, - UnicodeRange{ 0x23f0, 0x23f0, 0 }, - UnicodeRange{ 0x23f3, 0x23f3, 0 }, - UnicodeRange{ 0x2460, 0x24e9, 1 }, - UnicodeRange{ 0x24eb, 0x24ff, 1 }, - UnicodeRange{ 0x25a0, 0x25a1, 1 }, - UnicodeRange{ 0x25a3, 0x25a9, 1 }, - UnicodeRange{ 0x25b2, 0x25b3, 1 }, - UnicodeRange{ 0x25b6, 0x25b7, 1 }, - UnicodeRange{ 0x25bc, 0x25bd, 1 }, - UnicodeRange{ 0x25c0, 0x25c1, 1 }, - UnicodeRange{ 0x25c6, 0x25c8, 1 }, - UnicodeRange{ 0x25cb, 0x25cb, 1 }, - UnicodeRange{ 0x25ce, 0x25d1, 1 }, - UnicodeRange{ 0x25e2, 0x25e5, 1 }, - UnicodeRange{ 0x25ef, 0x25ef, 1 }, - UnicodeRange{ 0x25fd, 0x25fe, 0 }, - UnicodeRange{ 0x2605, 0x2606, 1 }, - UnicodeRange{ 0x2609, 0x2609, 1 }, - UnicodeRange{ 0x260e, 0x260f, 1 }, - UnicodeRange{ 0x2614, 0x2615, 0 }, - UnicodeRange{ 0x261c, 0x261c, 1 }, - UnicodeRange{ 0x261e, 0x261e, 1 }, - UnicodeRange{ 0x2640, 0x2640, 1 }, - UnicodeRange{ 0x2642, 0x2642, 1 }, - UnicodeRange{ 0x2648, 0x2653, 0 }, - UnicodeRange{ 0x2660, 0x2661, 1 }, - UnicodeRange{ 0x2663, 0x2665, 1 }, - UnicodeRange{ 0x2667, 0x266a, 1 }, - UnicodeRange{ 0x266c, 0x266d, 1 }, - UnicodeRange{ 0x266f, 0x266f, 1 }, - UnicodeRange{ 0x267f, 0x267f, 0 }, - UnicodeRange{ 0x2693, 0x2693, 0 }, - UnicodeRange{ 0x269e, 0x269f, 1 }, - UnicodeRange{ 0x26a1, 0x26a1, 0 }, - UnicodeRange{ 0x26aa, 0x26ab, 0 }, - UnicodeRange{ 0x26bd, 0x26be, 0 }, - UnicodeRange{ 0x26bf, 0x26bf, 1 }, - UnicodeRange{ 0x26c4, 0x26c5, 0 }, - UnicodeRange{ 0x26c6, 0x26cd, 1 }, - UnicodeRange{ 0x26ce, 0x26ce, 0 }, - UnicodeRange{ 0x26cf, 0x26d3, 1 }, - UnicodeRange{ 0x26d4, 0x26d4, 0 }, - UnicodeRange{ 0x26d5, 0x26e1, 1 }, - UnicodeRange{ 0x26e3, 0x26e3, 1 }, - UnicodeRange{ 0x26e8, 0x26e9, 1 }, - UnicodeRange{ 0x26ea, 0x26ea, 0 }, - UnicodeRange{ 0x26eb, 0x26f1, 1 }, - UnicodeRange{ 0x26f2, 0x26f3, 0 }, - UnicodeRange{ 0x26f4, 0x26f4, 1 }, - UnicodeRange{ 0x26f5, 0x26f5, 0 }, - UnicodeRange{ 0x26f6, 0x26f9, 1 }, - UnicodeRange{ 0x26fa, 0x26fa, 0 }, - UnicodeRange{ 0x26fb, 0x26fc, 1 }, - UnicodeRange{ 0x26fd, 0x26fd, 0 }, - UnicodeRange{ 0x26fe, 0x26ff, 1 }, - UnicodeRange{ 0x2705, 0x2705, 0 }, - UnicodeRange{ 0x270a, 0x270b, 0 }, - UnicodeRange{ 0x2728, 0x2728, 0 }, - UnicodeRange{ 0x273d, 0x273d, 1 }, - UnicodeRange{ 0x274c, 0x274c, 0 }, - UnicodeRange{ 0x274e, 0x274e, 0 }, - UnicodeRange{ 0x2753, 0x2755, 0 }, - UnicodeRange{ 0x2757, 0x2757, 0 }, - UnicodeRange{ 0x2776, 0x277f, 1 }, - UnicodeRange{ 0x2795, 0x2797, 0 }, - UnicodeRange{ 0x27b0, 0x27b0, 0 }, - UnicodeRange{ 0x27bf, 0x27bf, 0 }, - UnicodeRange{ 0x2b1b, 0x2b1c, 0 }, - UnicodeRange{ 0x2b50, 0x2b50, 0 }, - UnicodeRange{ 0x2b55, 0x2b55, 0 }, - UnicodeRange{ 0x2b56, 0x2b59, 1 }, - UnicodeRange{ 0x2e80, 0x2e99, 0 }, - UnicodeRange{ 0x2e9b, 0x2ef3, 0 }, - UnicodeRange{ 0x2f00, 0x2fd5, 0 }, - UnicodeRange{ 0x2ff0, 0x2ffb, 0 }, - UnicodeRange{ 0x3000, 0x303e, 0 }, - UnicodeRange{ 0x3041, 0x3096, 0 }, - UnicodeRange{ 0x3099, 0x30ff, 0 }, - UnicodeRange{ 0x3105, 0x312f, 0 }, - UnicodeRange{ 0x3131, 0x318e, 0 }, - UnicodeRange{ 0x3190, 0x31e3, 0 }, - UnicodeRange{ 0x31f0, 0x321e, 0 }, - UnicodeRange{ 0x3220, 0x3247, 0 }, - UnicodeRange{ 0x3248, 0x324f, 1 }, - UnicodeRange{ 0x3250, 0x4dbf, 0 }, - UnicodeRange{ 0x4e00, 0xa48c, 0 }, - UnicodeRange{ 0xa490, 0xa4c6, 0 }, - UnicodeRange{ 0xa960, 0xa97c, 0 }, - UnicodeRange{ 0xac00, 0xd7a3, 0 }, - UnicodeRange{ 0xe000, 0xf8ff, 1 }, - UnicodeRange{ 0xf900, 0xfaff, 0 }, - UnicodeRange{ 0xfe00, 0xfe0f, 1 }, - UnicodeRange{ 0xfe10, 0xfe19, 0 }, - UnicodeRange{ 0xfe30, 0xfe52, 0 }, - UnicodeRange{ 0xfe54, 0xfe66, 0 }, - UnicodeRange{ 0xfe68, 0xfe6b, 0 }, - UnicodeRange{ 0xff01, 0xff60, 0 }, - UnicodeRange{ 0xffe0, 0xffe6, 0 }, - UnicodeRange{ 0xfffd, 0xfffd, 1 }, - UnicodeRange{ 0x16fe0, 0x16fe4, 0 }, - UnicodeRange{ 0x16ff0, 0x16ff1, 0 }, - UnicodeRange{ 0x17000, 0x187f7, 0 }, - UnicodeRange{ 0x18800, 0x18cd5, 0 }, - UnicodeRange{ 0x18d00, 0x18d08, 0 }, - UnicodeRange{ 0x1aff0, 0x1aff3, 0 }, - UnicodeRange{ 0x1aff5, 0x1affb, 0 }, - UnicodeRange{ 0x1affd, 0x1affe, 0 }, - UnicodeRange{ 0x1b000, 0x1b122, 0 }, - UnicodeRange{ 0x1b132, 0x1b132, 0 }, - UnicodeRange{ 0x1b150, 0x1b152, 0 }, - UnicodeRange{ 0x1b155, 0x1b155, 0 }, - UnicodeRange{ 0x1b164, 0x1b167, 0 }, - UnicodeRange{ 0x1b170, 0x1b2fb, 0 }, - UnicodeRange{ 0x1f004, 0x1f004, 0 }, - UnicodeRange{ 0x1f0cf, 0x1f0cf, 0 }, - UnicodeRange{ 0x1f100, 0x1f10a, 1 }, - UnicodeRange{ 0x1f110, 0x1f12d, 1 }, - UnicodeRange{ 0x1f130, 0x1f169, 1 }, - UnicodeRange{ 0x1f170, 0x1f18d, 1 }, - UnicodeRange{ 0x1f18e, 0x1f18e, 0 }, - UnicodeRange{ 0x1f18f, 0x1f190, 1 }, - UnicodeRange{ 0x1f191, 0x1f19a, 0 }, - UnicodeRange{ 0x1f19b, 0x1f1ac, 1 }, - UnicodeRange{ 0x1f1e6, 0x1f202, 0 }, - UnicodeRange{ 0x1f210, 0x1f23b, 0 }, - UnicodeRange{ 0x1f240, 0x1f248, 0 }, - UnicodeRange{ 0x1f250, 0x1f251, 0 }, - UnicodeRange{ 0x1f260, 0x1f265, 0 }, - UnicodeRange{ 0x1f300, 0x1f320, 0 }, - UnicodeRange{ 0x1f32d, 0x1f335, 0 }, - UnicodeRange{ 0x1f337, 0x1f37c, 0 }, - UnicodeRange{ 0x1f37e, 0x1f393, 0 }, - UnicodeRange{ 0x1f3a0, 0x1f3ca, 0 }, - UnicodeRange{ 0x1f3cf, 0x1f3d3, 0 }, - UnicodeRange{ 0x1f3e0, 0x1f3f0, 0 }, - UnicodeRange{ 0x1f3f4, 0x1f3f4, 0 }, - UnicodeRange{ 0x1f3f8, 0x1f43e, 0 }, - UnicodeRange{ 0x1f440, 0x1f440, 0 }, - UnicodeRange{ 0x1f442, 0x1f4fc, 0 }, - UnicodeRange{ 0x1f4ff, 0x1f53d, 0 }, - UnicodeRange{ 0x1f54b, 0x1f54e, 0 }, - UnicodeRange{ 0x1f550, 0x1f567, 0 }, - UnicodeRange{ 0x1f57a, 0x1f57a, 0 }, - UnicodeRange{ 0x1f595, 0x1f596, 0 }, - UnicodeRange{ 0x1f5a4, 0x1f5a4, 0 }, - UnicodeRange{ 0x1f5fb, 0x1f64f, 0 }, - UnicodeRange{ 0x1f680, 0x1f6c5, 0 }, - UnicodeRange{ 0x1f6cc, 0x1f6cc, 0 }, - UnicodeRange{ 0x1f6d0, 0x1f6d2, 0 }, - UnicodeRange{ 0x1f6d5, 0x1f6d7, 0 }, - UnicodeRange{ 0x1f6dc, 0x1f6df, 0 }, - UnicodeRange{ 0x1f6eb, 0x1f6ec, 0 }, - UnicodeRange{ 0x1f6f4, 0x1f6fc, 0 }, - UnicodeRange{ 0x1f7e0, 0x1f7eb, 0 }, - UnicodeRange{ 0x1f7f0, 0x1f7f0, 0 }, - UnicodeRange{ 0x1f90c, 0x1f93a, 0 }, - UnicodeRange{ 0x1f93c, 0x1f945, 0 }, - UnicodeRange{ 0x1f947, 0x1f9ff, 0 }, - UnicodeRange{ 0x1fa70, 0x1fa7c, 0 }, - UnicodeRange{ 0x1fa80, 0x1fa88, 0 }, - UnicodeRange{ 0x1fa90, 0x1fabd, 0 }, - UnicodeRange{ 0x1fabf, 0x1fac5, 0 }, - UnicodeRange{ 0x1face, 0x1fadb, 0 }, - UnicodeRange{ 0x1fae0, 0x1fae8, 0 }, - UnicodeRange{ 0x1faf0, 0x1faf8, 0 }, - UnicodeRange{ 0x20000, 0x2fffd, 0 }, - UnicodeRange{ 0x30000, 0x3fffd, 0 }, - UnicodeRange{ 0xe0100, 0xe01ef, 1 }, - UnicodeRange{ 0xf0000, 0xffffd, 1 }, - UnicodeRange{ 0x100000, 0x10fffd, 1 }, - }; + const auto s0 = s_stage0[cp >> 11]; + const auto s1 = s_stage1[s0 + ((cp >> 6) & 31)]; + const auto s2 = s_stage2[s1 + ((cp >> 3) & 7)]; + const auto s3 = s_stage3[s2 + ((cp >> 0) & 7)]; + return s3; } - -// Routine Description: -// - returns the width type of codepoint as fast as we can by using quick lookup table and fallback cache. -// Arguments: -// - glyph - the utf16 encoded codepoint to search for -// Return Value: -// - the width type of the codepoint -CodepointWidth CodepointWidthDetector::GetWidth(const std::wstring_view& glyph) noexcept +constexpr int ucdGraphemeJoins(const int state, const int lead, const int trail) noexcept { - char32_t codepoint = 0; - - switch (glyph.size()) - { - case 1: - codepoint = til::at(glyph, 0); - break; - case 2: - codepoint = (til::at(glyph, 0) & 0x3FF) << 10; - codepoint |= til::at(glyph, 1) & 0x3FF; - codepoint += 0x10000; - break; - default: - codepoint = 0; - break; - } - - if (codepoint < 0x80) - { - return CodepointWidth::Narrow; - } - - // The return value of _lookupGlyphWidth coincides with the enum value of CodepointWidth - // on purpose to allow for this easy conversion to happen. Optimally, we should probably - // remove CodepointWidth altogether to allow for zero-width joiners and other characters. - static_assert(WI_EnumValue(CodepointWidth::Narrow) == 1); - static_assert(WI_EnumValue(CodepointWidth::Wide) == 2); - return static_cast(_lookupGlyphWidth(codepoint, glyph)); + const auto l = lead & 15; + const auto t = trail & 15; + return (s_joinRules[state][l] >> (t * 2)) & 3; } - -// Routine Description: -// - checks if codepoint is wide. will attempt to fallback as much possible until an answer is determined -// Arguments: -// - glyph - the utf16 encoded codepoint to check width of -// Return Value: -// - true if codepoint is wide -bool CodepointWidthDetector::IsWide(const std::wstring_view& glyph) noexcept +constexpr bool ucdGraphemeDone(const int state) noexcept { - return GetWidth(glyph) == CodepointWidth::Wide; + return state == 3; } - -// GetWidth's slow-path for non-ASCII characters. Returns the number of columns the codepoint takes up in the terminal. -uint8_t CodepointWidthDetector::_lookupGlyphWidth(const char32_t codepoint, const std::wstring_view& glyph) noexcept +constexpr int ucdToCharacterWidth(const int val) noexcept { -#pragma warning(suppress : 26447) // The function is declared 'noexcept' but calls function 'lower_bound<...>()' which may throw exceptions (f.6). - const auto it = std::lower_bound(s_wideAndAmbiguousTable.begin(), s_wideAndAmbiguousTable.end(), codepoint); - uint8_t width = 1; + return val >> 6; +} +// clang-format on - if (it != s_wideAndAmbiguousTable.end() && codepoint >= it->lowerBound && codepoint <= it->upperBound) +// Decodes the next codepoint from the given UTF-16 string. +// Returns the start of the next codepoint. Assumes `it < end`. +[[msvc::forceinline]] constexpr const wchar_t* utf16NextOrFFFD(const wchar_t* it, const wchar_t* end, char32_t& out) +{ + __assume(it != nullptr); + __assume(end != nullptr); + + char32_t c = *it++; + + // Is any surrogate? + if ((c & 0xF800) == 0xD800) { - width = 2; - if (it->isAmbiguous) + const char32_t c1 = c; + c = 0xfffd; + + // Is leading surrogate and not at end? + if ((c1 & 0x400) == 0 && it != end) { - width = _checkFallbackViaCache(codepoint, glyph); + const char32_t c2 = *it; + // Is also trailing surrogate! + if ((c2 & 0xFC00) == 0xDC00) + { + c = (c1 << 10) - 0x35FDC00 + c2; + ++it; + } } } - return width; + out = c; + return it; } -// Call the function specified via SetFallbackMethod() to turn CodepointWidth::Ambiguous into Narrow/Wide. -// Caches the results in _fallbackCache. This is _lookupGlyphWidth's even-slower-path. -uint8_t CodepointWidthDetector::_checkFallbackViaCache(const char32_t codepoint, const std::wstring_view& glyph) noexcept +// Decodes the preceding codepoint from the given UTF-16 string. +// Returns the start of the preceding codepoint. Assumes `it > beg`. +[[msvc::forceinline]] constexpr const wchar_t* utf16PrevOrFFFD(const wchar_t* it, const wchar_t* beg, char32_t& out) +{ + __assume(it != nullptr); + __assume(beg != nullptr); + + char32_t c = *--it; + + // Is any surrogate? + if ((c & 0xF800) == 0xD800) + { + const char32_t c2 = c; + c = 0xfffd; + + // Is trailing surrogate and not at begin? + if ((c2 & 0x400) != 0 && it != beg) + { + const char32_t c1 = it[-1]; + // Is also leading surrogate! + if ((c1 & 0xFC00) == 0xD800) + { + c = (c1 << 10) - 0x35FDC00 + c2; + --it; + } + } + } + + out = c; + return it; +} + +// Returns `reset` if `ptr` is outside the range [beg, end). Otherwise, it returns `ptr` unmodified. +constexpr const wchar_t* resetIfOutOfRange(const wchar_t* beg, const wchar_t* end, const wchar_t* reset, const wchar_t* ptr) +{ + auto ret = ptr; + // This uses individual if-assignments to get the compiler to emit conditional moves. + if (ptr < beg) + { + ret = reset; + } + if (ptr > end) + { + ret = reset; + } + return ret; +} + +static CodepointWidthDetector s_codepointWidthDetector; + +CodepointWidthDetector& CodepointWidthDetector::Singleton() noexcept +{ + return s_codepointWidthDetector; +} + +bool CodepointWidthDetector::GraphemeNext(GraphemeState& s, const std::wstring_view& str) noexcept +{ + if (_mode == TextMeasurementMode::Graphemes) + { + return _graphemeNext(s, str); + } + if (_mode == TextMeasurementMode::Wcswidth) + { + return _graphemeNextWcswidth(s, str); + } + return _graphemeNextConsole(s, str); +} + +bool CodepointWidthDetector::GraphemePrev(GraphemeState& s, const std::wstring_view& str) noexcept +{ + if (_mode == TextMeasurementMode::Graphemes) + { + return _graphemePrev(s, str); + } + if (_mode == TextMeasurementMode::Wcswidth) + { + return _graphemePrevWcswidth(s, str); + } + return _graphemePrevConsole(s, str); +} + +// Parses the next grapheme cluster from the given string. The algorithm largely follows "UAX #29: Unicode Text Segmentation", +// but takes some mild liberties. Returns false if the end of the string was reached. Updates `s` with the cluster. +bool CodepointWidthDetector::_graphemeNext(GraphemeState& s, const std::wstring_view& str) const noexcept +{ + const auto beg = str.data(); + const auto end = beg + str.size(); + auto clusterBeg = s.beg + s.len; + auto width = s.width; + auto state = s._state; + auto lead = s._last; + + // If it's a new string argument, we'll restart at the new string's beginning. + clusterBeg = resetIfOutOfRange(beg, end, beg, clusterBeg); + + auto clusterEnd = clusterBeg; + + // Skip if we're already at the end. + if (clusterEnd < end) + { + char32_t cp; + + // If a previous parsing of a grapheme cluster got interrupted because we reached the end of the string, + // we'll have stored the parser state in `s._state` so that we can continue parsing within the new string. + // The problem is that a `state` of zero is also a valid state parameter for `ucdGraphemeJoins`. + // Thus, we're storing `s._state` bit-flipped so that we can differentiate between it being unset (0) and + // storing a previous state of 0 (0xffff...). + const auto gotState = state != 0; + state = ~state; + if (gotState) + { + goto fetchNext; + } + + clusterEnd = utf16NextOrFFFD(clusterEnd, end, cp); + lead = ucdLookup(cp); + width = 0; + state = 0; + + for (;;) + { + { + auto w = ucdToCharacterWidth(lead); + if (w == 3) + { + w = _ambiguousWidth; + } + + // U+FE0F Variation Selector-16 is used to turn unqualified Emojis into qualified ones. + // By convention, this turns them from being ambiguous width (= narrow) into wide ones. + // We achieve this here by explicitly giving this codepoint a wide width. + // Later down below we'll clamp width back to <= 2. + if (cp == 0xFE0F) + { + w = 2; + } + + width += w; + } + + // If we're at the end of the string, we'll break out of the loop, but leave + // `state` and `lead` as-is, so that we can continue parsing in the next string. + if (clusterEnd >= end) + { + break; + } + + fetchNext: + const auto clusterEndNext = utf16NextOrFFFD(clusterEnd, end, cp); + const auto trail = ucdLookup(cp); + + state = ucdGraphemeJoins(state, lead, trail); + if (ucdGraphemeDone(state)) + { + // We'll later do `state = ~state` which will result in `state == 0`. + state = ~0; + lead = 0; + break; + } + + clusterEnd = clusterEndNext; + lead = trail; + } + + state = ~state; + width = width > 2 ? 2 : width; + + s.beg = clusterBeg; + s.len = static_cast(clusterEnd - clusterBeg); + s.width = width; + s._state = state; + s._last = lead; + } + + return clusterEnd < end; +} + +// Parses the preceding grapheme cluster from the given string. The algorithm largely follows "UAX #29: Unicode Text Segmentation", +// but takes some mild liberties. Returns false if the end of the string was reached. Updates `s` with the cluster. +// This code is identical to _graphemeNext() but with the order of operations reversed since we're iterating backwards. +bool CodepointWidthDetector::_graphemePrev(GraphemeState& s, const std::wstring_view& str) const noexcept +{ + const auto beg = str.data(); + const auto end = beg + str.size(); + auto clusterEnd = s.beg; + auto width = s.width; + auto state = s._state; + auto trail = s._last; + + // If it's a new string argument, we'll restart at the new string's beginning. + clusterEnd = resetIfOutOfRange(beg, end, end, clusterEnd); + + auto clusterBeg = clusterEnd; + + // Skip if we're already at the end. + if (clusterEnd > beg) + { + char32_t cp; + + // If a previous parsing of a grapheme cluster got interrupted because we reached the end of the string, + // we'll have stored the parser state in `s._state` so that we can continue parsing within the new string. + // The problem is that a `state` of zero is also a valid state parameter for `ucdGraphemeJoins`. + // Thus, we're storing `s._state` bit-flipped so that we can differentiate between it being unset (0) and + // storing a previous state of 0 (0xffff...). + const auto gotState = state != 0; + state = ~state; + if (gotState) + { + goto fetchNext; + } + + clusterBeg = utf16PrevOrFFFD(clusterBeg, beg, cp); + trail = ucdLookup(cp); + width = 0; + state = 0; + + for (;;) + { + { + auto w = ucdToCharacterWidth(trail); + if (w == 3) + { + w = _ambiguousWidth; + } + + // U+FE0F Variation Selector-16 is used to turn unqualified Emojis into qualified ones. + // By convention, this turns them from being ambiguous width (= narrow) into wide ones. + // We achieve this here by explicitly giving this codepoint a wide width. + // Later down below we'll clamp width back to <= 2. + if (cp == 0xFE0F) + { + w = 2; + } + + width += w; + } + + // If we're at the end of the string, we'll break out of the loop, but leave + // `state` and `lead` as-is, so that we can continue parsing in the next string. + if (clusterBeg <= beg) + { + break; + } + + fetchNext: + const auto clusterBegNext = utf16PrevOrFFFD(clusterBeg, beg, cp); + const auto lead = ucdLookup(cp); + + state = ucdGraphemeJoins(state, lead, trail); + if (ucdGraphemeDone(state)) + { + // We'll later do `state = ~state` which will result in `state == 0`. + state = ~0; + trail = 0; + break; + } + + clusterBeg = clusterBegNext; + trail = lead; + } + + state = ~state; + width = width > 2 ? 2 : width; + + s.beg = clusterBeg; + s.len = static_cast(clusterEnd - clusterBeg); + s.width = width; + s._state = state; + s._last = trail; + } + + return clusterBeg > beg; +} + +// Implements a clustering algorithm that behaves similar to terminals and applications based on `wcswidth`. +// Such terminals have no actual notion of graphemes or joining characters, but do know zero-width characters. +// During cursor navigation they'll skip over such zero-width characters to reach the target column. +// In effect this means, that a non-zero-width character gets clustered with any number of following zero-width characters. +bool CodepointWidthDetector::_graphemeNextWcswidth(GraphemeState& s, const std::wstring_view& str) const noexcept +{ + const auto beg = str.data(); + const auto end = beg + str.size(); + auto clusterBeg = s.beg + s.len; + auto width = s.width; + auto state = s._state; + + // If it's a new string argument, we'll restart at the new string's beginning. + clusterBeg = resetIfOutOfRange(beg, end, beg, clusterBeg); + + if (clusterBeg >= end) + { + return false; + } + + auto clusterEnd = clusterBeg; + + // Normally we could just append any zero-width characters to the current cluster, + // but theoretically we could have a zero-width character itself as the lead character. + // Because of that we don't use `s.width` to track the state but rather flag + // whether we've encountered our "lead" character in `s._state` (1 if we had one). + if (state == 0) + { + width = 0; + } + + for (;;) + { + char32_t cp; + const auto clusterEndNext = utf16NextOrFFFD(clusterEnd, end, cp); + const auto val = ucdLookup(cp); + + auto w = ucdToCharacterWidth(val); + if (w == 3) + { + w = _ambiguousWidth; + } + + if (state != 0 && w != 0) + { + state = 0; + break; + } + + width += w; + state = 1; + clusterEnd = clusterEndNext; + + if (clusterEnd >= end) + { + break; + } + } + + s.beg = clusterBeg; + s.len = static_cast(clusterEnd - clusterBeg); + s.width = width; + s._state = state; + return clusterEnd < end; +} + +// Implements a clustering algorithm that behaves similar to terminals and applications based on `wcswidth`. +// Such terminals have no actual notion of graphemes or joining characters, but do know zero-width characters. +// During cursor navigation they'll skip over such zero-width characters to reach the target column. +// In effect this means, that a non-zero-width character gets clustered with any number of following zero-width characters. +bool CodepointWidthDetector::_graphemePrevWcswidth(GraphemeState& s, const std::wstring_view& str) const noexcept +{ + const auto beg = str.data(); + const auto end = beg + str.size(); + auto clusterEnd = s.beg; + + // If it's a new string argument, we'll restart at the new string's beginning. + clusterEnd = resetIfOutOfRange(beg, end, end, clusterEnd); + + if (clusterEnd <= beg) + { + return false; + } + + auto clusterBeg = clusterEnd; + auto width = s.width; + int delayedCompletion = 0; + + // In order to conform to the behavior of _graphemePrev(), we need to pretend as if we don't know + // whether the cluster is complete yet (with graphemes there may be prepended concatenation marks). + // As such, we flag `delayedCompletion` to true which gets stored as `s._state = 1` and return false. + // Then, when we get called again with the next input string, we'll finally return false with a `s.len` of 0. + if (s._state == 0) + { + width = 0; + + for (;;) + { + char32_t cp; + clusterBeg = utf16PrevOrFFFD(clusterBeg, beg, cp); + const auto val = ucdLookup(cp); + + auto w = ucdToCharacterWidth(val); + if (w == 3) + { + w = _ambiguousWidth; + } + + width += w; + + const auto hasWidth = width != 0; + const auto atEnd = clusterBeg <= beg; + + if (hasWidth || atEnd) + { + delayedCompletion = hasWidth && atEnd; + break; + } + } + } + + s.beg = clusterBeg; + s.len = static_cast(clusterEnd - clusterBeg); + s.width = width; + s._state = delayedCompletion; + return clusterBeg > beg; +} + +// Implements a clustering algorithm that behaves similar to the old conhost. +// It even asks the text renderer how wide ambiguous width characters are instead of defaulting to 1 (or 2). +bool CodepointWidthDetector::_graphemeNextConsole(GraphemeState& s, const std::wstring_view& str) noexcept +{ + const auto beg = str.data(); + const auto end = beg + str.size(); + auto clusterBeg = s.beg + s.len; + + // If it's a new string argument, we'll restart at the new string's beginning. + clusterBeg = resetIfOutOfRange(beg, end, beg, clusterBeg); + + if (clusterBeg >= end) + { + return false; + } + + auto clusterEnd = clusterBeg; + auto width = s.width; + int delayedCompletion = 0; + + // In order to conform to the behavior of _graphemeNext(), we need to pretend as if we don't know + // whether the cluster is complete yet (with graphemes there may be combining marks, etc.). + // As such, we flag `delayedCompletion` to true which gets stored as `s._state = 1` and return false. + // Then, when we get called again with the next input string, we'll finally return false with a `s.len` of 0. + if (s._state == 0) + { + char32_t cp; + clusterEnd = utf16NextOrFFFD(clusterEnd, end, cp); + + const auto val = ucdLookup(cp); + width = ucdToCharacterWidth(val); + if (width == 3) + { + width = _checkFallbackViaCache(cp); + } + + delayedCompletion = clusterEnd >= end; + } + + s.beg = clusterBeg; + s.len = static_cast(clusterEnd - clusterBeg); + s.width = width; + s._state = delayedCompletion; + return delayedCompletion == 0; +} + +// Implements a clustering algorithm that behaves similar to the old conhost. +// It even asks the text renderer how wide ambiguous width characters are instead of defaulting to 1 (or 2). +bool CodepointWidthDetector::_graphemePrevConsole(GraphemeState& s, const std::wstring_view& str) noexcept +{ + const auto beg = str.data(); + const auto end = beg + str.size(); + auto clusterEnd = s.beg; + + // If it's a new string argument, we'll restart at the new string's beginning. + clusterEnd = resetIfOutOfRange(beg, end, end, clusterEnd); + + if (clusterEnd <= beg) + { + return false; + } + + auto clusterBeg = clusterEnd; + auto width = s.width; + int delayedCompletion = 0; + + // In order to conform to the behavior of _graphemePrev(), we need to pretend as if we don't know + // whether the cluster is complete yet (with graphemes there may be prepended concatenation marks). + // As such, we flag `delayedCompletion` to true which gets stored as `s._state = 1` and return false. + // Then, when we get called again with the next input string, we'll finally return false with a `s.len` of 0. + if (s._state == 0) + { + char32_t cp; + clusterBeg = utf16PrevOrFFFD(clusterEnd, beg, cp); + + const auto val = ucdLookup(cp); + width = ucdToCharacterWidth(val); + if (width == 3) + { + width = _checkFallbackViaCache(cp); + } + + delayedCompletion = clusterBeg <= beg; + } + + s.beg = clusterBeg; + s.len = static_cast(clusterEnd - clusterBeg); + s.width = width; + s._state = delayedCompletion; + return delayedCompletion == 0; +} + +// Call the function specified via SetFallbackMethod() to turn ambiguous (width = 3) into narrow/wide. +// Caches the results in _fallbackCache. +int CodepointWidthDetector::_checkFallbackViaCache(const char32_t codepoint) noexcept try { // Ambiguous glyphs are considered narrow by default. See microsoft/terminal#2066 for more info. @@ -409,7 +1147,21 @@ try return it->second; } - const uint8_t width = _pfnFallbackMethod(glyph) ? 2 : 1; + wchar_t buf[2]; + size_t len; + if (codepoint <= 0xffff) + { + buf[0] = static_cast(codepoint); + len = 1; + } + else + { + buf[0] = static_cast((codepoint >> 10) + 0xD7C0); + buf[1] = static_cast((codepoint & 0x3ff) | 0xDC00); + len = 2; + } + + const int width = _pfnFallbackMethod({ &buf[0], len }) ? 2 : 1; _fallbackCache.insert_or_assign(codepoint, width); return width; } @@ -419,6 +1171,11 @@ catch (...) return 1; } +TextMeasurementMode CodepointWidthDetector::GetMode() const noexcept +{ + return _mode; +} + // Method Description: // - Sets a function that should be used as the fallback mechanism for // determining a particular glyph's width, should the glyph be an ambiguous @@ -434,16 +1191,8 @@ void CodepointWidthDetector::SetFallbackMethod(std::function -// Return Value: -// - -void CodepointWidthDetector::NotifyFontChanged() noexcept +void CodepointWidthDetector::Reset(const TextMeasurementMode mode) noexcept { -#pragma warning(suppress : 26447) // The function is declared 'noexcept' but calls function 'clear()' which may throw exceptions (f.6). + _mode = mode; _fallbackCache.clear(); } diff --git a/src/types/GlyphWidth.cpp b/src/types/GlyphWidth.cpp index 2f7d0578a4..e2af4b591f 100644 --- a/src/types/GlyphWidth.cpp +++ b/src/types/GlyphWidth.cpp @@ -5,16 +5,14 @@ #include "inc/CodepointWidthDetector.hpp" #include "inc/GlyphWidth.hpp" -#pragma warning(suppress : 26426) -// TODO GH 2676 - remove warning suppression and decide what to do re: singleton instance of CodepointWidthDetector -static CodepointWidthDetector widthDetector; - // Function Description: // - determines if the glyph represented by the string of characters should be // wide or not. See CodepointWidthDetector::IsWide bool IsGlyphFullWidth(const std::wstring_view& glyph) noexcept { - return widthDetector.IsWide(glyph); + GraphemeState state; + CodepointWidthDetector::Singleton().GraphemeNext(state, glyph); + return state.width == 2; } // Function Description: @@ -24,29 +22,3 @@ bool IsGlyphFullWidth(const wchar_t wch) noexcept { return wch < 0x80 ? false : IsGlyphFullWidth({ &wch, 1 }); } - -// Function Description: -// - Sets a function that should be used by the global CodepointWidthDetector -// as the fallback mechanism for determining a particular glyph's width, -// should the glyph be an ambiguous width. -// A Terminal could hook in a Renderer's IsGlyphWideByFont method as the -// fallback to ask the renderer for the glyph's width (for example). -// Arguments: -// - pfnFallback - the function to use as the fallback method. -// Return Value: -// - -void SetGlyphWidthFallback(std::function pfnFallback) noexcept -{ - widthDetector.SetFallbackMethod(std::move(pfnFallback)); -} - -// Function Description: -// - Forwards notification about font changing to glyph width detector -// Arguments: -// - -// Return Value: -// - -void NotifyGlyphWidthFontChanged() noexcept -{ - widthDetector.NotifyFontChanged(); -} diff --git a/src/types/inc/CodepointWidthDetector.hpp b/src/types/inc/CodepointWidthDetector.hpp index 79d0f3e858..7429d6a96c 100644 --- a/src/types/inc/CodepointWidthDetector.hpp +++ b/src/types/inc/CodepointWidthDetector.hpp @@ -1,37 +1,75 @@ -/*++ -Copyright (c) Microsoft Corporation - -Module Name: -- CodepointWidthDetector.hpp - -Abstract: -- Object used to measure the width of a codepoint when it's rendered - -Author: -- Austin Diviness (AustDi) 18-May-2018 ---*/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. #pragma once -#include "convert.hpp" - -// use to measure the width of a codepoint -class CodepointWidthDetector final +enum class TextMeasurementMode { -public: - CodepointWidth GetWidth(const std::wstring_view& glyph) noexcept; - bool IsWide(const std::wstring_view& glyph) noexcept; - void SetFallbackMethod(std::function pfnFallback) noexcept; - void NotifyFontChanged() noexcept; + // Uses a method very similar to the official UAX #29 Extended Grapheme Cluster algorithm. + Graphemes, + // wcswidth() is a popular method on UNIX to measure text width. It basically treats + // any zero-width character as a continuation of a preceding non-zero-width character. + Wcswidth, + // The old conhost algorithm is UCS-2 based and assigns a minimum width of 1 to all codepoints. + // It had been extended to support UTF-16 after the introduction of Windows Terminal, + // but retained the behavior of not supporting zero-width characters. + Console, +}; -#ifdef UNIT_TESTING - friend class CodepointWidthDetectorTests; -#endif +// NOTE: The same GraphemeState instance should be passed for a series of GraphemeNext *or* GraphemePrev calls, +// but NOT between GraphemeNext *and* GraphemePrev ("exclusive OR"). They're also not reusable when the +// CodepointWidthDetector::_legacy flag changes. Different functions treat these members differently. +struct GraphemeState +{ + // These are the [out] parameters for GraphemeNext/Prev. + // + // If a previous call returned false (= reached the end of the string), then on the first call with + // the next string, beg/len will contain the parts of the grapheme cluster that are found in that + // new string argument. That's true even if the two strings don't join to form a single cluster. + // In that case beg/len will simply be an empty string. It basically tells you + // "Yup, that cluster in the last string was complete after all". + // + // However, width will always be updated to represent the width of the current cluster. + // + // For instance, if the first string is a narrow emoji and the second one is U+FE0F, the first call will return + // the emoji with a width of 1, and the second call will return U+FE0F with a width of 2. + // You know these two belong together because the first call returned false. + // The total width is not 1+2 but rather just 2. + const wchar_t* beg = nullptr; + int len = 0; + // width will always be between 0 or 2. + int width = 0; + + // If GraphemeNext/Prev return false (= reached the end of the string), they'll fill these struct + // members with some info so that we can check if it joins with the start of the next string argument. + // _state is stored ~flipped, so that we can differentiate between it being unset (0) and it being set to 0 (~0 = 255). + int _state = 0; + int _last = 0; +}; + +struct CodepointWidthDetector +{ + static CodepointWidthDetector& Singleton() noexcept; + + // Returns false if the end of the string has been reached. + bool GraphemeNext(GraphemeState& s, const std::wstring_view& str) noexcept; + bool GraphemePrev(GraphemeState& s, const std::wstring_view& str) noexcept; + + TextMeasurementMode GetMode() const noexcept; + void SetFallbackMethod(std::function pfnFallback) noexcept; + void Reset(TextMeasurementMode mode) noexcept; private: - uint8_t _lookupGlyphWidth(char32_t codepoint, const std::wstring_view& glyph) noexcept; - uint8_t _checkFallbackViaCache(char32_t codepoint, const std::wstring_view& glyph) noexcept; + bool _graphemeNext(GraphemeState& s, const std::wstring_view& str) const noexcept; + bool _graphemePrev(GraphemeState& s, const std::wstring_view& str) const noexcept; + bool _graphemeNextWcswidth(GraphemeState& s, const std::wstring_view& str) const noexcept; + bool _graphemePrevWcswidth(GraphemeState& s, const std::wstring_view& str) const noexcept; + bool _graphemeNextConsole(GraphemeState& s, const std::wstring_view& str) noexcept; + bool _graphemePrevConsole(GraphemeState& s, const std::wstring_view& str) noexcept; + __declspec(noinline) int _checkFallbackViaCache(char32_t codepoint) noexcept; - std::unordered_map _fallbackCache; + std::unordered_map _fallbackCache; std::function _pfnFallbackMethod; + TextMeasurementMode _mode = TextMeasurementMode::Graphemes; + int _ambiguousWidth = 1; }; diff --git a/src/types/inc/GlyphWidth.hpp b/src/types/inc/GlyphWidth.hpp index 11982bf5a0..10b5a966b1 100644 --- a/src/types/inc/GlyphWidth.hpp +++ b/src/types/inc/GlyphWidth.hpp @@ -10,12 +10,5 @@ Abstract: */ #pragma once -#include -#include - -#include "convert.hpp" - bool IsGlyphFullWidth(const std::wstring_view& glyph) noexcept; bool IsGlyphFullWidth(const wchar_t wch) noexcept; -void SetGlyphWidthFallback(std::function pfnFallback) noexcept; -void NotifyGlyphWidthFontChanged() noexcept; diff --git a/src/types/lib/types.vcxproj.filters b/src/types/lib/types.vcxproj.filters index efb65b2bc4..2ecdd122c9 100644 --- a/src/types/lib/types.vcxproj.filters +++ b/src/types/lib/types.vcxproj.filters @@ -30,9 +30,6 @@ Source Files - - Source Files - Source Files @@ -60,6 +57,9 @@ Source Files + + Source Files + @@ -122,5 +122,6 @@ + - + \ No newline at end of file diff --git a/src/types/ut_types/CodepointWidthDetectorTests.cpp b/src/types/ut_types/CodepointWidthDetectorTests.cpp new file mode 100644 index 0000000000..cd84961151 --- /dev/null +++ b/src/types/ut_types/CodepointWidthDetectorTests.cpp @@ -0,0 +1,1422 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "precomp.h" +#include "WexTestClass.h" + +#include "../types/inc/CodepointWidthDetector.hpp" + +// FYI at the time of writing you may have to generate this table in cmd with +// go run CodepointWidthDetectorTests_gen.go > temp.txt +// because PowerShell garbles Unicode text between piped commands. + +// Several test cases are commented out. They're due to us intentionally not handling some rules correctly: +// * GB3: CR × LF +// There's no point in us handling this, because in our implementation +// the text buffer won't ever see these control characters to begin with. +// * GB9c: \p{InCB=Consonant} [ \p{InCB=Extend} \p{InCB=Linker} ]* \p{InCB=Linker} [ \p{InCB=Extend} \p{InCB=Linker} ]* × \p{InCB=Consonant} +// aka: Do not break within certain combinations with InCB=Linker. +// Building a state machine for this hurts performance, and I'm not sure anyone will notice. +// Instead, our implementation just joins any preceding and following character if there's a Linker in between. +// * GB11: ExtPic Extend* ZWJ × ExtPic +// aka: ZWJs should only join if they're in between two ExtPics +// Same thing here. Any ExtPic joins with any preceding ZWJ. +// * GB12/13: [^RI] (RI RI)* RI × RI +// aka: RIs should have an even number. +// Same thing here. Any RI joins with any preceding RI. + +// Generated by GraphemeTestTableGen +// on 2024-05-27T21:06:21Z, from Unicode 15.1.0 +struct GraphemeBreakTest +{ + const wchar_t* comment; + const wchar_t* graphemes[4]; +}; +static constexpr GraphemeBreakTest s_graphemeBreakTests[] = { + { L"÷ [0.2] SPACE (Other) ÷ [999.0] SPACE (Other) ÷ [0.3]", L" ", L" " }, + { L"÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]", L" \x0308", L" " }, + { L"÷ [0.2] SPACE (Other) ÷ [5.0] (CR) ÷ [0.3]", L" ", L"\r" }, + { L"÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3]", L" \x0308", L"\r" }, + { L"÷ [0.2] SPACE (Other) ÷ [5.0] (LF) ÷ [0.3]", L" ", L"\n" }, + { L"÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3]", L" \x0308", L"\n" }, + { L"÷ [0.2] SPACE (Other) ÷ [5.0] (Control) ÷ [0.3]", L" ", L"\x01" }, + { L"÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3]", L" \x0308", L"\x01" }, + { L"÷ [0.2] SPACE (Other) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L" \x034F" }, + { L"÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L" \x0308\x034F" }, + { L"÷ [0.2] SPACE (Other) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L" ", L"\U0001F1E6" }, + { L"÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L" \x0308", L"\U0001F1E6" }, + { L"÷ [0.2] SPACE (Other) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L" ", L"\x0600" }, + { L"÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L" \x0308", L"\x0600" }, + { L"÷ [0.2] SPACE (Other) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L" \x0A03" }, + { L"÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L" \x0308\x0A03" }, + { L"÷ [0.2] SPACE (Other) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L" ", L"\x1100" }, + { L"÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L" \x0308", L"\x1100" }, + { L"÷ [0.2] SPACE (Other) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L" ", L"\x1160" }, + { L"÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L" \x0308", L"\x1160" }, + { L"÷ [0.2] SPACE (Other) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L" ", L"\x11A8" }, + { L"÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L" \x0308", L"\x11A8" }, + { L"÷ [0.2] SPACE (Other) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L" ", L"\xAC00" }, + { L"÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L" \x0308", L"\xAC00" }, + { L"÷ [0.2] SPACE (Other) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L" ", L"\xAC01" }, + { L"÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L" \x0308", L"\xAC01" }, + { L"÷ [0.2] SPACE (Other) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L" \x0900" }, + { L"÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L" \x0308\x0900" }, + { L"÷ [0.2] SPACE (Other) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L" \x0903" }, + { L"÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L" \x0308\x0903" }, + { L"÷ [0.2] SPACE (Other) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L" ", L"\x0904" }, + { L"÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L" \x0308", L"\x0904" }, + { L"÷ [0.2] SPACE (Other) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L" ", L"\x0D4E" }, + { L"÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L" \x0308", L"\x0D4E" }, + { L"÷ [0.2] SPACE (Other) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L" ", L"\x0915" }, + { L"÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L" \x0308", L"\x0915" }, + { L"÷ [0.2] SPACE (Other) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L" ", L"\x231A" }, + { L"÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L" \x0308", L"\x231A" }, + { L"÷ [0.2] SPACE (Other) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L" \x0300" }, + { L"÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L" \x0308\x0300" }, + { L"÷ [0.2] SPACE (Other) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L" \x093C" }, + { L"÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L" \x0308\x093C" }, + { L"÷ [0.2] SPACE (Other) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L" \x094D" }, + { L"÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L" \x0308\x094D" }, + { L"÷ [0.2] SPACE (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L" \x200D" }, + { L"÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L" \x0308\x200D" }, + { L"÷ [0.2] SPACE (Other) ÷ [999.0] (Other) ÷ [0.3]", L" ", L"\x0378" }, + { L"÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3]", L" \x0308", L"\x0378" }, + { L"÷ [0.2] (CR) ÷ [4.0] SPACE (Other) ÷ [0.3]", L"\r", L" " }, + { L"÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\r", L"\x0308", L" " }, + { L"÷ [0.2] (CR) ÷ [4.0] (CR) ÷ [0.3]", L"\r", L"\r" }, + { L"÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3]", L"\r", L"\x0308", L"\r" }, + //{ L"÷ [0.2] (CR) × [3.0] (LF) ÷ [0.3]", L"\r\n" }, + { L"÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3]", L"\r", L"\x0308", L"\n" }, + { L"÷ [0.2] (CR) ÷ [4.0] (Control) ÷ [0.3]", L"\r", L"\x01" }, + { L"÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3]", L"\r", L"\x0308", L"\x01" }, + { L"÷ [0.2] (CR) ÷ [4.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\r", L"\x034F" }, + { L"÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\r", L"\x0308\x034F" }, + { L"÷ [0.2] (CR) ÷ [4.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\r", L"\U0001F1E6" }, + { L"÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\r", L"\x0308", L"\U0001F1E6" }, + { L"÷ [0.2] (CR) ÷ [4.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\r", L"\x0600" }, + { L"÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\r", L"\x0308", L"\x0600" }, + { L"÷ [0.2] (CR) ÷ [4.0] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\r", L"\x0A03" }, + { L"÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\r", L"\x0308\x0A03" }, + { L"÷ [0.2] (CR) ÷ [4.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\r", L"\x1100" }, + { L"÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\r", L"\x0308", L"\x1100" }, + { L"÷ [0.2] (CR) ÷ [4.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\r", L"\x1160" }, + { L"÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\r", L"\x0308", L"\x1160" }, + { L"÷ [0.2] (CR) ÷ [4.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\r", L"\x11A8" }, + { L"÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\r", L"\x0308", L"\x11A8" }, + { L"÷ [0.2] (CR) ÷ [4.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\r", L"\xAC00" }, + { L"÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\r", L"\x0308", L"\xAC00" }, + { L"÷ [0.2] (CR) ÷ [4.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\r", L"\xAC01" }, + { L"÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\r", L"\x0308", L"\xAC01" }, + { L"÷ [0.2] (CR) ÷ [4.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\r", L"\x0900" }, + { L"÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\r", L"\x0308\x0900" }, + { L"÷ [0.2] (CR) ÷ [4.0] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\r", L"\x0903" }, + { L"÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\r", L"\x0308\x0903" }, + { L"÷ [0.2] (CR) ÷ [4.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\r", L"\x0904" }, + { L"÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\r", L"\x0308", L"\x0904" }, + { L"÷ [0.2] (CR) ÷ [4.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\r", L"\x0D4E" }, + { L"÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\r", L"\x0308", L"\x0D4E" }, + { L"÷ [0.2] (CR) ÷ [4.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\r", L"\x0915" }, + { L"÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\r", L"\x0308", L"\x0915" }, + { L"÷ [0.2] (CR) ÷ [4.0] WATCH (ExtPict) ÷ [0.3]", L"\r", L"\x231A" }, + { L"÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\r", L"\x0308", L"\x231A" }, + { L"÷ [0.2] (CR) ÷ [4.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\r", L"\x0300" }, + { L"÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\r", L"\x0308\x0300" }, + { L"÷ [0.2] (CR) ÷ [4.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\r", L"\x093C" }, + { L"÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\r", L"\x0308\x093C" }, + { L"÷ [0.2] (CR) ÷ [4.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\r", L"\x094D" }, + { L"÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\r", L"\x0308\x094D" }, + { L"÷ [0.2] (CR) ÷ [4.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\r", L"\x200D" }, + { L"÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\r", L"\x0308\x200D" }, + { L"÷ [0.2] (CR) ÷ [4.0] (Other) ÷ [0.3]", L"\r", L"\x0378" }, + { L"÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3]", L"\r", L"\x0308", L"\x0378" }, + { L"÷ [0.2] (LF) ÷ [4.0] SPACE (Other) ÷ [0.3]", L"\n", L" " }, + { L"÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\n", L"\x0308", L" " }, + { L"÷ [0.2] (LF) ÷ [4.0] (CR) ÷ [0.3]", L"\n", L"\r" }, + { L"÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3]", L"\n", L"\x0308", L"\r" }, + { L"÷ [0.2] (LF) ÷ [4.0] (LF) ÷ [0.3]", L"\n", L"\n" }, + { L"÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3]", L"\n", L"\x0308", L"\n" }, + { L"÷ [0.2] (LF) ÷ [4.0] (Control) ÷ [0.3]", L"\n", L"\x01" }, + { L"÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3]", L"\n", L"\x0308", L"\x01" }, + { L"÷ [0.2] (LF) ÷ [4.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\n", L"\x034F" }, + { L"÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\n", L"\x0308\x034F" }, + { L"÷ [0.2] (LF) ÷ [4.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\n", L"\U0001F1E6" }, + { L"÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\n", L"\x0308", L"\U0001F1E6" }, + { L"÷ [0.2] (LF) ÷ [4.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\n", L"\x0600" }, + { L"÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\n", L"\x0308", L"\x0600" }, + { L"÷ [0.2] (LF) ÷ [4.0] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\n", L"\x0A03" }, + { L"÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\n", L"\x0308\x0A03" }, + { L"÷ [0.2] (LF) ÷ [4.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\n", L"\x1100" }, + { L"÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\n", L"\x0308", L"\x1100" }, + { L"÷ [0.2] (LF) ÷ [4.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\n", L"\x1160" }, + { L"÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\n", L"\x0308", L"\x1160" }, + { L"÷ [0.2] (LF) ÷ [4.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\n", L"\x11A8" }, + { L"÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\n", L"\x0308", L"\x11A8" }, + { L"÷ [0.2] (LF) ÷ [4.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\n", L"\xAC00" }, + { L"÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\n", L"\x0308", L"\xAC00" }, + { L"÷ [0.2] (LF) ÷ [4.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\n", L"\xAC01" }, + { L"÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\n", L"\x0308", L"\xAC01" }, + { L"÷ [0.2] (LF) ÷ [4.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\n", L"\x0900" }, + { L"÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\n", L"\x0308\x0900" }, + { L"÷ [0.2] (LF) ÷ [4.0] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\n", L"\x0903" }, + { L"÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\n", L"\x0308\x0903" }, + { L"÷ [0.2] (LF) ÷ [4.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\n", L"\x0904" }, + { L"÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\n", L"\x0308", L"\x0904" }, + { L"÷ [0.2] (LF) ÷ [4.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\n", L"\x0D4E" }, + { L"÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\n", L"\x0308", L"\x0D4E" }, + { L"÷ [0.2] (LF) ÷ [4.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\n", L"\x0915" }, + { L"÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\n", L"\x0308", L"\x0915" }, + { L"÷ [0.2] (LF) ÷ [4.0] WATCH (ExtPict) ÷ [0.3]", L"\n", L"\x231A" }, + { L"÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\n", L"\x0308", L"\x231A" }, + { L"÷ [0.2] (LF) ÷ [4.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\n", L"\x0300" }, + { L"÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\n", L"\x0308\x0300" }, + { L"÷ [0.2] (LF) ÷ [4.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\n", L"\x093C" }, + { L"÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\n", L"\x0308\x093C" }, + { L"÷ [0.2] (LF) ÷ [4.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\n", L"\x094D" }, + { L"÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\n", L"\x0308\x094D" }, + { L"÷ [0.2] (LF) ÷ [4.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\n", L"\x200D" }, + { L"÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\n", L"\x0308\x200D" }, + { L"÷ [0.2] (LF) ÷ [4.0] (Other) ÷ [0.3]", L"\n", L"\x0378" }, + { L"÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3]", L"\n", L"\x0308", L"\x0378" }, + { L"÷ [0.2] (Control) ÷ [4.0] SPACE (Other) ÷ [0.3]", L"\x01", L" " }, + { L"÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x01", L"\x0308", L" " }, + { L"÷ [0.2] (Control) ÷ [4.0] (CR) ÷ [0.3]", L"\x01", L"\r" }, + { L"÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3]", L"\x01", L"\x0308", L"\r" }, + { L"÷ [0.2] (Control) ÷ [4.0] (LF) ÷ [0.3]", L"\x01", L"\n" }, + { L"÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3]", L"\x01", L"\x0308", L"\n" }, + { L"÷ [0.2] (Control) ÷ [4.0] (Control) ÷ [0.3]", L"\x01", L"\x01" }, + { L"÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3]", L"\x01", L"\x0308", L"\x01" }, + { L"÷ [0.2] (Control) ÷ [4.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x01", L"\x034F" }, + { L"÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x01", L"\x0308\x034F" }, + { L"÷ [0.2] (Control) ÷ [4.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x01", L"\U0001F1E6" }, + { L"÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x01", L"\x0308", L"\U0001F1E6" }, + { L"÷ [0.2] (Control) ÷ [4.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x01", L"\x0600" }, + { L"÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x01", L"\x0308", L"\x0600" }, + { L"÷ [0.2] (Control) ÷ [4.0] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x01", L"\x0A03" }, + { L"÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x01", L"\x0308\x0A03" }, + { L"÷ [0.2] (Control) ÷ [4.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x01", L"\x1100" }, + { L"÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x01", L"\x0308", L"\x1100" }, + { L"÷ [0.2] (Control) ÷ [4.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x01", L"\x1160" }, + { L"÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x01", L"\x0308", L"\x1160" }, + { L"÷ [0.2] (Control) ÷ [4.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x01", L"\x11A8" }, + { L"÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x01", L"\x0308", L"\x11A8" }, + { L"÷ [0.2] (Control) ÷ [4.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x01", L"\xAC00" }, + { L"÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x01", L"\x0308", L"\xAC00" }, + { L"÷ [0.2] (Control) ÷ [4.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x01", L"\xAC01" }, + { L"÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x01", L"\x0308", L"\xAC01" }, + { L"÷ [0.2] (Control) ÷ [4.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x01", L"\x0900" }, + { L"÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x01", L"\x0308\x0900" }, + { L"÷ [0.2] (Control) ÷ [4.0] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x01", L"\x0903" }, + { L"÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x01", L"\x0308\x0903" }, + { L"÷ [0.2] (Control) ÷ [4.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x01", L"\x0904" }, + { L"÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x01", L"\x0308", L"\x0904" }, + { L"÷ [0.2] (Control) ÷ [4.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x01", L"\x0D4E" }, + { L"÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x01", L"\x0308", L"\x0D4E" }, + { L"÷ [0.2] (Control) ÷ [4.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x01", L"\x0915" }, + { L"÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x01", L"\x0308", L"\x0915" }, + { L"÷ [0.2] (Control) ÷ [4.0] WATCH (ExtPict) ÷ [0.3]", L"\x01", L"\x231A" }, + { L"÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x01", L"\x0308", L"\x231A" }, + { L"÷ [0.2] (Control) ÷ [4.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x01", L"\x0300" }, + { L"÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x01", L"\x0308\x0300" }, + { L"÷ [0.2] (Control) ÷ [4.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x01", L"\x093C" }, + { L"÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x01", L"\x0308\x093C" }, + { L"÷ [0.2] (Control) ÷ [4.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x01", L"\x094D" }, + { L"÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x01", L"\x0308\x094D" }, + { L"÷ [0.2] (Control) ÷ [4.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x01", L"\x200D" }, + { L"÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x01", L"\x0308\x200D" }, + { L"÷ [0.2] (Control) ÷ [4.0] (Other) ÷ [0.3]", L"\x01", L"\x0378" }, + { L"÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3]", L"\x01", L"\x0308", L"\x0378" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x034F", L" " }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x034F\x0308", L" " }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [5.0] (CR) ÷ [0.3]", L"\x034F", L"\r" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3]", L"\x034F\x0308", L"\r" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [5.0] (LF) ÷ [0.3]", L"\x034F", L"\n" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3]", L"\x034F\x0308", L"\n" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [5.0] (Control) ÷ [0.3]", L"\x034F", L"\x01" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3]", L"\x034F\x0308", L"\x01" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x034F\x034F" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x034F\x0308\x034F" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x034F", L"\U0001F1E6" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x034F\x0308", L"\U0001F1E6" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x034F", L"\x0600" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x034F\x0308", L"\x0600" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x034F\x0A03" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x034F\x0308\x0A03" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x034F", L"\x1100" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x034F\x0308", L"\x1100" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x034F", L"\x1160" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x034F\x0308", L"\x1160" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x034F", L"\x11A8" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x034F\x0308", L"\x11A8" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x034F", L"\xAC00" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x034F\x0308", L"\xAC00" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x034F", L"\xAC01" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x034F\x0308", L"\xAC01" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x034F\x0900" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x034F\x0308\x0900" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x034F\x0903" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x034F\x0308\x0903" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x034F", L"\x0904" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x034F\x0308", L"\x0904" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x034F", L"\x0D4E" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x034F\x0308", L"\x0D4E" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x034F", L"\x0915" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x034F\x0308", L"\x0915" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x034F", L"\x231A" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x034F\x0308", L"\x231A" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x034F\x0300" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x034F\x0308\x0300" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x034F\x093C" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x034F\x0308\x093C" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x034F\x094D" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x034F\x0308\x094D" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x034F\x200D" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x034F\x0308\x200D" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] (Other) ÷ [0.3]", L"\x034F", L"\x0378" }, + { L"÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3]", L"\x034F\x0308", L"\x0378" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\U0001F1E6", L" " }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\U0001F1E6\x0308", L" " }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] (CR) ÷ [0.3]", L"\U0001F1E6", L"\r" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3]", L"\U0001F1E6\x0308", L"\r" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] (LF) ÷ [0.3]", L"\U0001F1E6", L"\n" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3]", L"\U0001F1E6\x0308", L"\n" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] (Control) ÷ [0.3]", L"\U0001F1E6", L"\x01" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3]", L"\U0001F1E6\x0308", L"\x01" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\U0001F1E6\x034F" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\U0001F1E6\x0308\x034F" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [12.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\U0001F1E6\U0001F1E6" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\U0001F1E6\x0308", L"\U0001F1E6" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\U0001F1E6", L"\x0600" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\U0001F1E6\x0308", L"\x0600" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\U0001F1E6\x0A03" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\U0001F1E6\x0308\x0A03" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\U0001F1E6", L"\x1100" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\U0001F1E6\x0308", L"\x1100" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\U0001F1E6", L"\x1160" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\U0001F1E6\x0308", L"\x1160" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\U0001F1E6", L"\x11A8" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\U0001F1E6\x0308", L"\x11A8" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\U0001F1E6", L"\xAC00" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\U0001F1E6\x0308", L"\xAC00" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\U0001F1E6", L"\xAC01" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\U0001F1E6\x0308", L"\xAC01" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\U0001F1E6\x0900" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\U0001F1E6\x0308\x0900" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\U0001F1E6\x0903" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\U0001F1E6\x0308\x0903" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\U0001F1E6", L"\x0904" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\U0001F1E6\x0308", L"\x0904" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\U0001F1E6", L"\x0D4E" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\U0001F1E6\x0308", L"\x0D4E" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\U0001F1E6", L"\x0915" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\U0001F1E6\x0308", L"\x0915" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\U0001F1E6", L"\x231A" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\U0001F1E6\x0308", L"\x231A" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\U0001F1E6\x0300" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\U0001F1E6\x0308\x0300" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\U0001F1E6\x093C" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\U0001F1E6\x0308\x093C" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\U0001F1E6\x094D" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\U0001F1E6\x0308\x094D" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\U0001F1E6\x200D" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\U0001F1E6\x0308\x200D" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] (Other) ÷ [0.3]", L"\U0001F1E6", L"\x0378" }, + { L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3]", L"\U0001F1E6\x0308", L"\x0378" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] SPACE (Other) ÷ [0.3]", L"\x0600 " }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x0600\x0308", L" " }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) ÷ [5.0] (CR) ÷ [0.3]", L"\x0600", L"\r" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3]", L"\x0600\x0308", L"\r" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) ÷ [5.0] (LF) ÷ [0.3]", L"\x0600", L"\n" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3]", L"\x0600\x0308", L"\n" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) ÷ [5.0] (Control) ÷ [0.3]", L"\x0600", L"\x01" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3]", L"\x0600\x0308", L"\x01" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x0600\x034F" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x0600\x0308\x034F" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x0600\U0001F1E6" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x0600\x0308", L"\U0001F1E6" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x0600\x0600" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x0600\x0308", L"\x0600" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x0600\x0A03" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x0600\x0308\x0A03" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x0600\x1100" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x0600\x0308", L"\x1100" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x0600\x1160" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x0600\x0308", L"\x1160" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x0600\x11A8" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x0600\x0308", L"\x11A8" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x0600\xAC00" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x0600\x0308", L"\xAC00" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x0600\xAC01" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x0600\x0308", L"\xAC01" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0600\x0900" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0600\x0308\x0900" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x0600\x0903" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x0600\x0308\x0903" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x0600\x0904" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x0600\x0308", L"\x0904" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0600\x0D4E" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0600\x0308", L"\x0D4E" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x0600\x0915" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x0600\x0308", L"\x0915" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] WATCH (ExtPict) ÷ [0.3]", L"\x0600\x231A" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x0600\x0308", L"\x231A" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x0600\x0300" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x0600\x0308\x0300" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x0600\x093C" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x0600\x0308\x093C" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x0600\x094D" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x0600\x0308\x094D" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x0600\x200D" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x0600\x0308\x200D" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] (Other) ÷ [0.3]", L"\x0600\x0378" }, + { L"÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3]", L"\x0600\x0308", L"\x0378" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x0A03", L" " }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x0A03\x0308", L" " }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [5.0] (CR) ÷ [0.3]", L"\x0A03", L"\r" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3]", L"\x0A03\x0308", L"\r" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [5.0] (LF) ÷ [0.3]", L"\x0A03", L"\n" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3]", L"\x0A03\x0308", L"\n" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [5.0] (Control) ÷ [0.3]", L"\x0A03", L"\x01" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3]", L"\x0A03\x0308", L"\x01" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x0A03\x034F" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x0A03\x0308\x034F" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x0A03", L"\U0001F1E6" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x0A03\x0308", L"\U0001F1E6" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x0A03", L"\x0600" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x0A03\x0308", L"\x0600" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x0A03\x0A03" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x0A03\x0308\x0A03" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x0A03", L"\x1100" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x0A03\x0308", L"\x1100" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x0A03", L"\x1160" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x0A03\x0308", L"\x1160" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x0A03", L"\x11A8" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x0A03\x0308", L"\x11A8" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x0A03", L"\xAC00" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x0A03\x0308", L"\xAC00" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x0A03", L"\xAC01" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x0A03\x0308", L"\xAC01" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0A03\x0900" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0A03\x0308\x0900" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x0A03\x0903" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x0A03\x0308\x0903" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x0A03", L"\x0904" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x0A03\x0308", L"\x0904" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0A03", L"\x0D4E" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0A03\x0308", L"\x0D4E" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x0A03", L"\x0915" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x0A03\x0308", L"\x0915" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x0A03", L"\x231A" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x0A03\x0308", L"\x231A" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x0A03\x0300" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x0A03\x0308\x0300" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x0A03\x093C" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x0A03\x0308\x093C" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x0A03\x094D" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x0A03\x0308\x094D" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x0A03\x200D" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x0A03\x0308\x200D" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] (Other) ÷ [0.3]", L"\x0A03", L"\x0378" }, + { L"÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3]", L"\x0A03\x0308", L"\x0378" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x1100", L" " }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x1100\x0308", L" " }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] (CR) ÷ [0.3]", L"\x1100", L"\r" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3]", L"\x1100\x0308", L"\r" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] (LF) ÷ [0.3]", L"\x1100", L"\n" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3]", L"\x1100\x0308", L"\n" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] (Control) ÷ [0.3]", L"\x1100", L"\x01" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3]", L"\x1100\x0308", L"\x01" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x1100\x034F" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x1100\x0308\x034F" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x1100", L"\U0001F1E6" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x1100\x0308", L"\U0001F1E6" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x1100", L"\x0600" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x1100\x0308", L"\x0600" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x1100\x0A03" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x1100\x0308\x0A03" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x1100\x1100" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x1100\x0308", L"\x1100" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x1100\x1160" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x1100\x0308", L"\x1160" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x1100", L"\x11A8" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x1100\x0308", L"\x11A8" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x1100\xAC00" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x1100\x0308", L"\xAC00" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x1100\xAC01" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x1100\x0308", L"\xAC01" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x1100\x0900" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x1100\x0308\x0900" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x1100\x0903" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x1100\x0308\x0903" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x1100", L"\x0904" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x1100\x0308", L"\x0904" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x1100", L"\x0D4E" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x1100\x0308", L"\x0D4E" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x1100", L"\x0915" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x1100\x0308", L"\x0915" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x1100", L"\x231A" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x1100\x0308", L"\x231A" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x1100\x0300" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x1100\x0308\x0300" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x1100\x093C" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x1100\x0308\x093C" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x1100\x094D" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x1100\x0308\x094D" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x1100\x200D" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x1100\x0308\x200D" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] (Other) ÷ [0.3]", L"\x1100", L"\x0378" }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3]", L"\x1100\x0308", L"\x0378" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x1160", L" " }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x1160\x0308", L" " }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] (CR) ÷ [0.3]", L"\x1160", L"\r" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3]", L"\x1160\x0308", L"\r" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] (LF) ÷ [0.3]", L"\x1160", L"\n" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3]", L"\x1160\x0308", L"\n" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] (Control) ÷ [0.3]", L"\x1160", L"\x01" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3]", L"\x1160\x0308", L"\x01" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x1160\x034F" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x1160\x0308\x034F" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x1160", L"\U0001F1E6" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x1160\x0308", L"\U0001F1E6" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x1160", L"\x0600" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x1160\x0308", L"\x0600" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x1160\x0A03" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x1160\x0308\x0A03" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x1160", L"\x1100" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x1160\x0308", L"\x1100" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [7.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x1160\x1160" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x1160\x0308", L"\x1160" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [7.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x1160\x11A8" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x1160\x0308", L"\x11A8" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x1160", L"\xAC00" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x1160\x0308", L"\xAC00" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x1160", L"\xAC01" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x1160\x0308", L"\xAC01" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x1160\x0900" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x1160\x0308\x0900" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x1160\x0903" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x1160\x0308\x0903" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x1160", L"\x0904" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x1160\x0308", L"\x0904" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x1160", L"\x0D4E" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x1160\x0308", L"\x0D4E" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x1160", L"\x0915" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x1160\x0308", L"\x0915" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x1160", L"\x231A" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x1160\x0308", L"\x231A" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x1160\x0300" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x1160\x0308\x0300" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x1160\x093C" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x1160\x0308\x093C" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x1160\x094D" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x1160\x0308\x094D" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x1160\x200D" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x1160\x0308\x200D" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] (Other) ÷ [0.3]", L"\x1160", L"\x0378" }, + { L"÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3]", L"\x1160\x0308", L"\x0378" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x11A8", L" " }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x11A8\x0308", L" " }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] (CR) ÷ [0.3]", L"\x11A8", L"\r" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3]", L"\x11A8\x0308", L"\r" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] (LF) ÷ [0.3]", L"\x11A8", L"\n" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3]", L"\x11A8\x0308", L"\n" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] (Control) ÷ [0.3]", L"\x11A8", L"\x01" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3]", L"\x11A8\x0308", L"\x01" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x11A8\x034F" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x11A8\x0308\x034F" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x11A8", L"\U0001F1E6" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x11A8\x0308", L"\U0001F1E6" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x11A8", L"\x0600" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x11A8\x0308", L"\x0600" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x11A8\x0A03" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x11A8\x0308\x0A03" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x11A8", L"\x1100" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x11A8\x0308", L"\x1100" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x11A8", L"\x1160" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x11A8\x0308", L"\x1160" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [8.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x11A8\x11A8" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x11A8\x0308", L"\x11A8" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x11A8", L"\xAC00" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x11A8\x0308", L"\xAC00" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x11A8", L"\xAC01" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x11A8\x0308", L"\xAC01" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x11A8\x0900" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x11A8\x0308\x0900" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x11A8\x0903" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x11A8\x0308\x0903" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x11A8", L"\x0904" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x11A8\x0308", L"\x0904" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x11A8", L"\x0D4E" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x11A8\x0308", L"\x0D4E" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x11A8", L"\x0915" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x11A8\x0308", L"\x0915" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x11A8", L"\x231A" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x11A8\x0308", L"\x231A" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x11A8\x0300" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x11A8\x0308\x0300" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x11A8\x093C" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x11A8\x0308\x093C" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x11A8\x094D" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x11A8\x0308\x094D" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x11A8\x200D" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x11A8\x0308\x200D" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] (Other) ÷ [0.3]", L"\x11A8", L"\x0378" }, + { L"÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3]", L"\x11A8\x0308", L"\x0378" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\xAC00", L" " }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\xAC00\x0308", L" " }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] (CR) ÷ [0.3]", L"\xAC00", L"\r" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3]", L"\xAC00\x0308", L"\r" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] (LF) ÷ [0.3]", L"\xAC00", L"\n" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3]", L"\xAC00\x0308", L"\n" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] (Control) ÷ [0.3]", L"\xAC00", L"\x01" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3]", L"\xAC00\x0308", L"\x01" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\xAC00\x034F" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\xAC00\x0308\x034F" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\xAC00", L"\U0001F1E6" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\xAC00\x0308", L"\U0001F1E6" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\xAC00", L"\x0600" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\xAC00\x0308", L"\x0600" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\xAC00\x0A03" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\xAC00\x0308\x0A03" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\xAC00", L"\x1100" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\xAC00\x0308", L"\x1100" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [7.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\xAC00\x1160" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\xAC00\x0308", L"\x1160" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [7.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\xAC00\x11A8" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\xAC00\x0308", L"\x11A8" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\xAC00", L"\xAC00" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\xAC00\x0308", L"\xAC00" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\xAC00", L"\xAC01" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\xAC00\x0308", L"\xAC01" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\xAC00\x0900" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\xAC00\x0308\x0900" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\xAC00\x0903" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\xAC00\x0308\x0903" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\xAC00", L"\x0904" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\xAC00\x0308", L"\x0904" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\xAC00", L"\x0D4E" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\xAC00\x0308", L"\x0D4E" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\xAC00", L"\x0915" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\xAC00\x0308", L"\x0915" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\xAC00", L"\x231A" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\xAC00\x0308", L"\x231A" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\xAC00\x0300" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\xAC00\x0308\x0300" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\xAC00\x093C" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\xAC00\x0308\x093C" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\xAC00\x094D" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\xAC00\x0308\x094D" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\xAC00\x200D" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\xAC00\x0308\x200D" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] (Other) ÷ [0.3]", L"\xAC00", L"\x0378" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3]", L"\xAC00\x0308", L"\x0378" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\xAC01", L" " }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\xAC01\x0308", L" " }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] (CR) ÷ [0.3]", L"\xAC01", L"\r" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3]", L"\xAC01\x0308", L"\r" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] (LF) ÷ [0.3]", L"\xAC01", L"\n" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3]", L"\xAC01\x0308", L"\n" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] (Control) ÷ [0.3]", L"\xAC01", L"\x01" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3]", L"\xAC01\x0308", L"\x01" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\xAC01\x034F" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\xAC01\x0308\x034F" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\xAC01", L"\U0001F1E6" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\xAC01\x0308", L"\U0001F1E6" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\xAC01", L"\x0600" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\xAC01\x0308", L"\x0600" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\xAC01\x0A03" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\xAC01\x0308\x0A03" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\xAC01", L"\x1100" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\xAC01\x0308", L"\x1100" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\xAC01", L"\x1160" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\xAC01\x0308", L"\x1160" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [8.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\xAC01\x11A8" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\xAC01\x0308", L"\x11A8" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\xAC01", L"\xAC00" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\xAC01\x0308", L"\xAC00" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\xAC01", L"\xAC01" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\xAC01\x0308", L"\xAC01" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\xAC01\x0900" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\xAC01\x0308\x0900" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\xAC01\x0903" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\xAC01\x0308\x0903" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\xAC01", L"\x0904" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\xAC01\x0308", L"\x0904" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\xAC01", L"\x0D4E" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\xAC01\x0308", L"\x0D4E" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\xAC01", L"\x0915" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\xAC01\x0308", L"\x0915" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\xAC01", L"\x231A" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\xAC01\x0308", L"\x231A" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\xAC01\x0300" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\xAC01\x0308\x0300" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\xAC01\x093C" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\xAC01\x0308\x093C" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\xAC01\x094D" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\xAC01\x0308\x094D" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\xAC01\x200D" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\xAC01\x0308\x200D" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] (Other) ÷ [0.3]", L"\xAC01", L"\x0378" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3]", L"\xAC01\x0308", L"\x0378" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x0900", L" " }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x0900\x0308", L" " }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [5.0] (CR) ÷ [0.3]", L"\x0900", L"\r" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3]", L"\x0900\x0308", L"\r" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [5.0] (LF) ÷ [0.3]", L"\x0900", L"\n" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3]", L"\x0900\x0308", L"\n" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [5.0] (Control) ÷ [0.3]", L"\x0900", L"\x01" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3]", L"\x0900\x0308", L"\x01" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x0900\x034F" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x0900\x0308\x034F" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x0900", L"\U0001F1E6" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x0900\x0308", L"\U0001F1E6" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x0900", L"\x0600" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x0900\x0308", L"\x0600" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x0900\x0A03" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x0900\x0308\x0A03" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x0900", L"\x1100" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x0900\x0308", L"\x1100" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x0900", L"\x1160" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x0900\x0308", L"\x1160" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x0900", L"\x11A8" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x0900\x0308", L"\x11A8" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x0900", L"\xAC00" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x0900\x0308", L"\xAC00" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x0900", L"\xAC01" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x0900\x0308", L"\xAC01" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0900\x0900" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0900\x0308\x0900" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x0900\x0903" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x0900\x0308\x0903" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x0900", L"\x0904" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x0900\x0308", L"\x0904" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0900", L"\x0D4E" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0900\x0308", L"\x0D4E" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x0900", L"\x0915" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x0900\x0308", L"\x0915" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x0900", L"\x231A" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x0900\x0308", L"\x231A" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x0900\x0300" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x0900\x0308\x0300" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x0900\x093C" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x0900\x0308\x093C" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x0900\x094D" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x0900\x0308\x094D" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x0900\x200D" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x0900\x0308\x200D" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [999.0] (Other) ÷ [0.3]", L"\x0900", L"\x0378" }, + { L"÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3]", L"\x0900\x0308", L"\x0378" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x0903", L" " }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x0903\x0308", L" " }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [5.0] (CR) ÷ [0.3]", L"\x0903", L"\r" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3]", L"\x0903\x0308", L"\r" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [5.0] (LF) ÷ [0.3]", L"\x0903", L"\n" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3]", L"\x0903\x0308", L"\n" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [5.0] (Control) ÷ [0.3]", L"\x0903", L"\x01" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3]", L"\x0903\x0308", L"\x01" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x0903\x034F" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x0903\x0308\x034F" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x0903", L"\U0001F1E6" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x0903\x0308", L"\U0001F1E6" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x0903", L"\x0600" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x0903\x0308", L"\x0600" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x0903\x0A03" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x0903\x0308\x0A03" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x0903", L"\x1100" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x0903\x0308", L"\x1100" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x0903", L"\x1160" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x0903\x0308", L"\x1160" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x0903", L"\x11A8" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x0903\x0308", L"\x11A8" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x0903", L"\xAC00" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x0903\x0308", L"\xAC00" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x0903", L"\xAC01" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x0903\x0308", L"\xAC01" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0903\x0900" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0903\x0308\x0900" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x0903\x0903" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x0903\x0308\x0903" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x0903", L"\x0904" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x0903\x0308", L"\x0904" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0903", L"\x0D4E" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0903\x0308", L"\x0D4E" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x0903", L"\x0915" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x0903\x0308", L"\x0915" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x0903", L"\x231A" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x0903\x0308", L"\x231A" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x0903\x0300" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x0903\x0308\x0300" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x0903\x093C" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x0903\x0308\x093C" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x0903\x094D" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x0903\x0308\x094D" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x0903\x200D" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x0903\x0308\x200D" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] (Other) ÷ [0.3]", L"\x0903", L"\x0378" }, + { L"÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3]", L"\x0903\x0308", L"\x0378" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x0904", L" " }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x0904\x0308", L" " }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [5.0] (CR) ÷ [0.3]", L"\x0904", L"\r" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3]", L"\x0904\x0308", L"\r" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [5.0] (LF) ÷ [0.3]", L"\x0904", L"\n" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3]", L"\x0904\x0308", L"\n" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [5.0] (Control) ÷ [0.3]", L"\x0904", L"\x01" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3]", L"\x0904\x0308", L"\x01" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x0904\x034F" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x0904\x0308\x034F" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x0904", L"\U0001F1E6" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x0904\x0308", L"\U0001F1E6" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x0904", L"\x0600" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x0904\x0308", L"\x0600" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x0904\x0A03" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x0904\x0308\x0A03" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x0904", L"\x1100" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x0904\x0308", L"\x1100" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x0904", L"\x1160" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x0904\x0308", L"\x1160" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x0904", L"\x11A8" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x0904\x0308", L"\x11A8" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x0904", L"\xAC00" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x0904\x0308", L"\xAC00" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x0904", L"\xAC01" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x0904\x0308", L"\xAC01" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0904\x0900" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0904\x0308\x0900" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x0904\x0903" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x0904\x0308\x0903" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x0904", L"\x0904" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x0904\x0308", L"\x0904" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0904", L"\x0D4E" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0904\x0308", L"\x0D4E" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x0904", L"\x0915" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x0904\x0308", L"\x0915" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x0904", L"\x231A" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x0904\x0308", L"\x231A" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x0904\x0300" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x0904\x0308\x0300" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x0904\x093C" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x0904\x0308\x093C" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x0904\x094D" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x0904\x0308\x094D" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x0904\x200D" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x0904\x0308\x200D" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] (Other) ÷ [0.3]", L"\x0904", L"\x0378" }, + { L"÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3]", L"\x0904\x0308", L"\x0378" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] SPACE (Other) ÷ [0.3]", L"\x0D4E " }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x0D4E\x0308", L" " }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [5.0] (CR) ÷ [0.3]", L"\x0D4E", L"\r" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3]", L"\x0D4E\x0308", L"\r" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [5.0] (LF) ÷ [0.3]", L"\x0D4E", L"\n" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3]", L"\x0D4E\x0308", L"\n" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [5.0] (Control) ÷ [0.3]", L"\x0D4E", L"\x01" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3]", L"\x0D4E\x0308", L"\x01" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x0D4E\x034F" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x0D4E\x0308\x034F" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x0D4E\U0001F1E6" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x0D4E\x0308", L"\U0001F1E6" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x0D4E\x0600" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x0D4E\x0308", L"\x0600" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x0D4E\x0A03" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x0D4E\x0308\x0A03" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x0D4E\x1100" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x0D4E\x0308", L"\x1100" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x0D4E\x1160" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x0D4E\x0308", L"\x1160" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x0D4E\x11A8" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x0D4E\x0308", L"\x11A8" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x0D4E\xAC00" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x0D4E\x0308", L"\xAC00" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x0D4E\xAC01" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x0D4E\x0308", L"\xAC01" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0D4E\x0900" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0D4E\x0308\x0900" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x0D4E\x0903" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x0D4E\x0308\x0903" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x0D4E\x0904" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x0D4E\x0308", L"\x0904" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0D4E\x0D4E" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0D4E\x0308", L"\x0D4E" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x0D4E\x0915" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x0D4E\x0308", L"\x0915" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] WATCH (ExtPict) ÷ [0.3]", L"\x0D4E\x231A" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x0D4E\x0308", L"\x231A" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x0D4E\x0300" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x0D4E\x0308\x0300" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x0D4E\x093C" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x0D4E\x0308\x093C" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x0D4E\x094D" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x0D4E\x0308\x094D" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x0D4E\x200D" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x0D4E\x0308\x200D" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] (Other) ÷ [0.3]", L"\x0D4E\x0378" }, + { L"÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3]", L"\x0D4E\x0308", L"\x0378" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x0915", L" " }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x0915\x0308", L" " }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [5.0] (CR) ÷ [0.3]", L"\x0915", L"\r" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3]", L"\x0915\x0308", L"\r" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [5.0] (LF) ÷ [0.3]", L"\x0915", L"\n" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3]", L"\x0915\x0308", L"\n" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [5.0] (Control) ÷ [0.3]", L"\x0915", L"\x01" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3]", L"\x0915\x0308", L"\x01" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x0915\x034F" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x0915\x0308\x034F" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x0915", L"\U0001F1E6" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x0915\x0308", L"\U0001F1E6" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x0915", L"\x0600" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x0915\x0308", L"\x0600" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x0915\x0A03" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x0915\x0308\x0A03" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x0915", L"\x1100" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x0915\x0308", L"\x1100" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x0915", L"\x1160" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x0915\x0308", L"\x1160" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x0915", L"\x11A8" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x0915\x0308", L"\x11A8" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x0915", L"\xAC00" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x0915\x0308", L"\xAC00" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x0915", L"\xAC01" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x0915\x0308", L"\xAC01" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0915\x0900" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0915\x0308\x0900" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x0915\x0903" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x0915\x0308\x0903" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x0915", L"\x0904" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x0915\x0308", L"\x0904" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0915", L"\x0D4E" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0915\x0308", L"\x0D4E" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x0915", L"\x0915" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x0915\x0308", L"\x0915" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x0915", L"\x231A" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x0915\x0308", L"\x231A" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x0915\x0300" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x0915\x0308\x0300" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x0915\x093C" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x0915\x0308\x093C" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x0915\x094D" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x0915\x0308\x094D" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x0915\x200D" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x0915\x0308\x200D" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] (Other) ÷ [0.3]", L"\x0915", L"\x0378" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3]", L"\x0915\x0308", L"\x0378" }, + { L"÷ [0.2] WATCH (ExtPict) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x231A", L" " }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x231A\x0308", L" " }, + { L"÷ [0.2] WATCH (ExtPict) ÷ [5.0] (CR) ÷ [0.3]", L"\x231A", L"\r" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3]", L"\x231A\x0308", L"\r" }, + { L"÷ [0.2] WATCH (ExtPict) ÷ [5.0] (LF) ÷ [0.3]", L"\x231A", L"\n" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3]", L"\x231A\x0308", L"\n" }, + { L"÷ [0.2] WATCH (ExtPict) ÷ [5.0] (Control) ÷ [0.3]", L"\x231A", L"\x01" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3]", L"\x231A\x0308", L"\x01" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x231A\x034F" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x231A\x0308\x034F" }, + { L"÷ [0.2] WATCH (ExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x231A", L"\U0001F1E6" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x231A\x0308", L"\U0001F1E6" }, + { L"÷ [0.2] WATCH (ExtPict) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x231A", L"\x0600" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x231A\x0308", L"\x0600" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x231A\x0A03" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x231A\x0308\x0A03" }, + { L"÷ [0.2] WATCH (ExtPict) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x231A", L"\x1100" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x231A\x0308", L"\x1100" }, + { L"÷ [0.2] WATCH (ExtPict) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x231A", L"\x1160" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x231A\x0308", L"\x1160" }, + { L"÷ [0.2] WATCH (ExtPict) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x231A", L"\x11A8" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x231A\x0308", L"\x11A8" }, + { L"÷ [0.2] WATCH (ExtPict) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x231A", L"\xAC00" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x231A\x0308", L"\xAC00" }, + { L"÷ [0.2] WATCH (ExtPict) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x231A", L"\xAC01" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x231A\x0308", L"\xAC01" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x231A\x0900" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x231A\x0308\x0900" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x231A\x0903" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x231A\x0308\x0903" }, + { L"÷ [0.2] WATCH (ExtPict) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x231A", L"\x0904" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x231A\x0308", L"\x0904" }, + { L"÷ [0.2] WATCH (ExtPict) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x231A", L"\x0D4E" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x231A\x0308", L"\x0D4E" }, + { L"÷ [0.2] WATCH (ExtPict) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x231A", L"\x0915" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x231A\x0308", L"\x0915" }, + { L"÷ [0.2] WATCH (ExtPict) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x231A", L"\x231A" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x231A\x0308", L"\x231A" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x231A\x0300" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x231A\x0308\x0300" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x231A\x093C" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x231A\x0308\x093C" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x231A\x094D" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x231A\x0308\x094D" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x231A\x200D" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x231A\x0308\x200D" }, + { L"÷ [0.2] WATCH (ExtPict) ÷ [999.0] (Other) ÷ [0.3]", L"\x231A", L"\x0378" }, + { L"÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3]", L"\x231A\x0308", L"\x0378" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x0300", L" " }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x0300\x0308", L" " }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3]", L"\x0300", L"\r" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3]", L"\x0300\x0308", L"\r" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3]", L"\x0300", L"\n" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3]", L"\x0300\x0308", L"\n" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3]", L"\x0300", L"\x01" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3]", L"\x0300\x0308", L"\x01" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x0300\x034F" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x0300\x0308\x034F" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x0300", L"\U0001F1E6" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x0300\x0308", L"\U0001F1E6" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x0300", L"\x0600" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x0300\x0308", L"\x0600" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x0300\x0A03" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x0300\x0308\x0A03" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x0300", L"\x1100" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x0300\x0308", L"\x1100" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x0300", L"\x1160" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x0300\x0308", L"\x1160" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x0300", L"\x11A8" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x0300\x0308", L"\x11A8" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x0300", L"\xAC00" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x0300\x0308", L"\xAC00" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x0300", L"\xAC01" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x0300\x0308", L"\xAC01" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0300\x0900" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0300\x0308\x0900" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x0300\x0903" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x0300\x0308\x0903" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x0300", L"\x0904" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x0300\x0308", L"\x0904" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0300", L"\x0D4E" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0300\x0308", L"\x0D4E" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x0300", L"\x0915" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x0300\x0308", L"\x0915" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x0300", L"\x231A" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x0300\x0308", L"\x231A" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x0300\x0300" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x0300\x0308\x0300" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x0300\x093C" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x0300\x0308\x093C" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x0300\x094D" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x0300\x0308\x094D" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x0300\x200D" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x0300\x0308\x200D" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3]", L"\x0300", L"\x0378" }, + { L"÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3]", L"\x0300\x0308", L"\x0378" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x093C", L" " }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x093C\x0308", L" " }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3]", L"\x093C", L"\r" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3]", L"\x093C\x0308", L"\r" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3]", L"\x093C", L"\n" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3]", L"\x093C\x0308", L"\n" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3]", L"\x093C", L"\x01" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3]", L"\x093C\x0308", L"\x01" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x093C\x034F" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x093C\x0308\x034F" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x093C", L"\U0001F1E6" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x093C\x0308", L"\U0001F1E6" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x093C", L"\x0600" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x093C\x0308", L"\x0600" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x093C\x0A03" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x093C\x0308\x0A03" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x093C", L"\x1100" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x093C\x0308", L"\x1100" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x093C", L"\x1160" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x093C\x0308", L"\x1160" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x093C", L"\x11A8" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x093C\x0308", L"\x11A8" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x093C", L"\xAC00" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x093C\x0308", L"\xAC00" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x093C", L"\xAC01" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x093C\x0308", L"\xAC01" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x093C\x0900" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x093C\x0308\x0900" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x093C\x0903" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x093C\x0308\x0903" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x093C", L"\x0904" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x093C\x0308", L"\x0904" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x093C", L"\x0D4E" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x093C\x0308", L"\x0D4E" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x093C", L"\x0915" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x093C\x0308", L"\x0915" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x093C", L"\x231A" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x093C\x0308", L"\x231A" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x093C\x0300" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x093C\x0308\x0300" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x093C\x093C" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x093C\x0308\x093C" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x093C\x094D" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x093C\x0308\x094D" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x093C\x200D" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x093C\x0308\x200D" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3]", L"\x093C", L"\x0378" }, + { L"÷ [0.2] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3]", L"\x093C\x0308", L"\x0378" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x094D", L" " }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x094D\x0308", L" " }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3]", L"\x094D", L"\r" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3]", L"\x094D\x0308", L"\r" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3]", L"\x094D", L"\n" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3]", L"\x094D\x0308", L"\n" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3]", L"\x094D", L"\x01" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3]", L"\x094D\x0308", L"\x01" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x094D\x034F" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x094D\x0308\x034F" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x094D", L"\U0001F1E6" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x094D\x0308", L"\U0001F1E6" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x094D", L"\x0600" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x094D\x0308", L"\x0600" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x094D\x0A03" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x094D\x0308\x0A03" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x094D", L"\x1100" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x094D\x0308", L"\x1100" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x094D", L"\x1160" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x094D\x0308", L"\x1160" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x094D", L"\x11A8" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x094D\x0308", L"\x11A8" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x094D", L"\xAC00" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x094D\x0308", L"\xAC00" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x094D", L"\xAC01" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x094D\x0308", L"\xAC01" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x094D\x0900" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x094D\x0308\x0900" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x094D\x0903" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x094D\x0308\x0903" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x094D", L"\x0904" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x094D\x0308", L"\x0904" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x094D", L"\x0D4E" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x094D\x0308", L"\x0D4E" }, + //{ L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x094D", L"\x0915" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x094D\x0308", L"\x0915" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x094D", L"\x231A" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x094D\x0308", L"\x231A" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x094D\x0300" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x094D\x0308\x0300" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x094D\x093C" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x094D\x0308\x093C" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x094D\x094D" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x094D\x0308\x094D" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x094D\x200D" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x094D\x0308\x200D" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3]", L"\x094D", L"\x0378" }, + { L"÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3]", L"\x094D\x0308", L"\x0378" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x200D", L" " }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x200D\x0308", L" " }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3]", L"\x200D", L"\r" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3]", L"\x200D\x0308", L"\r" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3]", L"\x200D", L"\n" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3]", L"\x200D\x0308", L"\n" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3]", L"\x200D", L"\x01" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3]", L"\x200D\x0308", L"\x01" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x200D\x034F" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x200D\x0308\x034F" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x200D", L"\U0001F1E6" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x200D\x0308", L"\U0001F1E6" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x200D", L"\x0600" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x200D\x0308", L"\x0600" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x200D\x0A03" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x200D\x0308\x0A03" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x200D", L"\x1100" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x200D\x0308", L"\x1100" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x200D", L"\x1160" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x200D\x0308", L"\x1160" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x200D", L"\x11A8" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x200D\x0308", L"\x11A8" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x200D", L"\xAC00" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x200D\x0308", L"\xAC00" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x200D", L"\xAC01" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x200D\x0308", L"\xAC01" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x200D\x0900" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x200D\x0308\x0900" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x200D\x0903" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x200D\x0308\x0903" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x200D", L"\x0904" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x200D\x0308", L"\x0904" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x200D", L"\x0D4E" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x200D\x0308", L"\x0D4E" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x200D", L"\x0915" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x200D\x0308", L"\x0915" }, + //{ L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x200D", L"\x231A" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x200D\x0308", L"\x231A" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x200D\x0300" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x200D\x0308\x0300" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x200D\x093C" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x200D\x0308\x093C" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x200D\x094D" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x200D\x0308\x094D" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x200D\x200D" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x200D\x0308\x200D" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3]", L"\x200D", L"\x0378" }, + { L"÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3]", L"\x200D\x0308", L"\x0378" }, + { L"÷ [0.2] (Other) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x0378", L" " }, + { L"÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x0378\x0308", L" " }, + { L"÷ [0.2] (Other) ÷ [5.0] (CR) ÷ [0.3]", L"\x0378", L"\r" }, + { L"÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3]", L"\x0378\x0308", L"\r" }, + { L"÷ [0.2] (Other) ÷ [5.0] (LF) ÷ [0.3]", L"\x0378", L"\n" }, + { L"÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3]", L"\x0378\x0308", L"\n" }, + { L"÷ [0.2] (Other) ÷ [5.0] (Control) ÷ [0.3]", L"\x0378", L"\x01" }, + { L"÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3]", L"\x0378\x0308", L"\x01" }, + { L"÷ [0.2] (Other) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x0378\x034F" }, + { L"÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]", L"\x0378\x0308\x034F" }, + { L"÷ [0.2] (Other) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x0378", L"\U0001F1E6" }, + { L"÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]", L"\x0378\x0308", L"\U0001F1E6" }, + { L"÷ [0.2] (Other) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x0378", L"\x0600" }, + { L"÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]", L"\x0378\x0308", L"\x0600" }, + { L"÷ [0.2] (Other) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x0378\x0A03" }, + { L"÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]", L"\x0378\x0308\x0A03" }, + { L"÷ [0.2] (Other) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x0378", L"\x1100" }, + { L"÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x0378\x0308", L"\x1100" }, + { L"÷ [0.2] (Other) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x0378", L"\x1160" }, + { L"÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]", L"\x0378\x0308", L"\x1160" }, + { L"÷ [0.2] (Other) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x0378", L"\x11A8" }, + { L"÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]", L"\x0378\x0308", L"\x11A8" }, + { L"÷ [0.2] (Other) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x0378", L"\xAC00" }, + { L"÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]", L"\x0378\x0308", L"\xAC00" }, + { L"÷ [0.2] (Other) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x0378", L"\xAC01" }, + { L"÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]", L"\x0378\x0308", L"\xAC01" }, + { L"÷ [0.2] (Other) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0378\x0900" }, + { L"÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0378\x0308\x0900" }, + { L"÷ [0.2] (Other) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x0378\x0903" }, + { L"÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]", L"\x0378\x0308\x0903" }, + { L"÷ [0.2] (Other) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x0378", L"\x0904" }, + { L"÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]", L"\x0378\x0308", L"\x0904" }, + { L"÷ [0.2] (Other) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0378", L"\x0D4E" }, + { L"÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]", L"\x0378\x0308", L"\x0D4E" }, + { L"÷ [0.2] (Other) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x0378", L"\x0915" }, + { L"÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x0378\x0308", L"\x0915" }, + { L"÷ [0.2] (Other) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x0378", L"\x231A" }, + { L"÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]", L"\x0378\x0308", L"\x231A" }, + { L"÷ [0.2] (Other) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x0378\x0300" }, + { L"÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]", L"\x0378\x0308\x0300" }, + { L"÷ [0.2] (Other) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x0378\x093C" }, + { L"÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]", L"\x0378\x0308\x093C" }, + { L"÷ [0.2] (Other) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x0378\x094D" }, + { L"÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]", L"\x0378\x0308\x094D" }, + { L"÷ [0.2] (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x0378\x200D" }, + { L"÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"\x0378\x0308\x200D" }, + { L"÷ [0.2] (Other) ÷ [999.0] (Other) ÷ [0.3]", L"\x0378", L"\x0378" }, + { L"÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3]", L"\x0378\x0308", L"\x0378" }, + //{ L"÷ [0.2] (CR) × [3.0] (LF) ÷ [4.0] LATIN SMALL LETTER A (Other) ÷ [5.0] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [0.3]", L"\r\n", L"a", L"\n", L"\x0308" }, + { L"÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [0.3]", L"a\x0308" }, + { L"÷ [0.2] SPACE (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] ARABIC LETTER NOON (Other) ÷ [0.3]", L" \x200D", L"\x0646" }, + { L"÷ [0.2] ARABIC LETTER NOON (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]", L"\x0646\x200D", L" " }, + { L"÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\x1100\x1100" }, + { L"÷ [0.2] HANGUL SYLLABLE GA (LV) × [7.0] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\xAC00\x11A8", L"\x1100" }, + { L"÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [8.0] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]", L"\xAC01\x11A8", L"\x1100" }, + //{ L"÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [12.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (Other) ÷ [0.3]", L"\U0001F1E6\U0001F1E7", L"\U0001F1E8", L"b" }, + //{ L"÷ [0.2] LATIN SMALL LETTER A (Other) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (Other) ÷ [0.3]", L"a", L"\U0001F1E6\U0001F1E7", L"\U0001F1E8", L"b" }, + { L"÷ [0.2] LATIN SMALL LETTER A (Other) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (Other) ÷ [0.3]", L"a", L"\U0001F1E6\U0001F1E7\x200D", L"\U0001F1E8", L"b" }, + { L"÷ [0.2] LATIN SMALL LETTER A (Other) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (Other) ÷ [0.3]", L"a", L"\U0001F1E6\x200D", L"\U0001F1E7\U0001F1E8", L"b" }, + { L"÷ [0.2] LATIN SMALL LETTER A (Other) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER D (RI) ÷ [999.0] LATIN SMALL LETTER B (Other) ÷ [0.3]", L"a", L"\U0001F1E6\U0001F1E7", L"\U0001F1E8\U0001F1E9", L"b" }, + { L"÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]", L"a\x200D" }, + { L"÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] LATIN SMALL LETTER B (Other) ÷ [0.3]", L"a\x0308", L"b" }, + { L"÷ [0.2] LATIN SMALL LETTER A (Other) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] LATIN SMALL LETTER B (Other) ÷ [0.3]", L"a\x0903", L"b" }, + { L"÷ [0.2] LATIN SMALL LETTER A (Other) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) × [9.2] LATIN SMALL LETTER B (Other) ÷ [0.3]", L"a", L"\x0600b" }, + { L"÷ [0.2] BABY (ExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend) ÷ [999.0] BABY (ExtPict) ÷ [0.3]", L"\U0001F476\U0001F3FF", L"\U0001F476" }, + { L"÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend) ÷ [999.0] BABY (ExtPict) ÷ [0.3]", L"a\U0001F3FF", L"\U0001F476" }, + { L"÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend) ÷ [999.0] BABY (ExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [11.0] OCTAGONAL SIGN (ExtPict) ÷ [0.3]", L"a\U0001F3FF", L"\U0001F476\x200D\U0001F6D1" }, + { L"÷ [0.2] BABY (ExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [11.0] BABY (ExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend) ÷ [0.3]", L"\U0001F476\U0001F3FF\x0308\x200D\U0001F476\U0001F3FF" }, + { L"÷ [0.2] OCTAGONAL SIGN (ExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [11.0] OCTAGONAL SIGN (ExtPict) ÷ [0.3]", L"\U0001F6D1\x200D\U0001F6D1" }, + //{ L"÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] OCTAGONAL SIGN (ExtPict) ÷ [0.3]", L"a\x200D", L"\U0001F6D1" }, + { L"÷ [0.2] UPPER BLADE SCISSORS (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [11.0] UPPER BLADE SCISSORS (Other) ÷ [0.3]", L"\x2701\x200D\x2701" }, + //{ L"÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] UPPER BLADE SCISSORS (Other) ÷ [0.3]", L"a\x200D", L"\x2701" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x0915", L"\x0924" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.3] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x0915\x094D\x0924" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.3] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x0915\x094D\x094D\x0924" }, + //{ L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.3] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x0915\x094D\x200D\x0924" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.3] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x0915\x093C\x200D\x094D\x0924" }, + //{ L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.3] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x0915\x093C\x094D\x200D\x0924" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.3] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.3] DEVANAGARI LETTER YA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x0915\x094D\x0924\x094D\x092F" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] LATIN SMALL LETTER A (Other) ÷ [0.3]", L"\x0915\x094D", L"a" }, + //{ L"÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"a\x094D", L"\x0924" }, + //{ L"÷ [0.2] QUESTION MARK (Other) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"?\x094D", L"\x0924" }, + { L"÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.3] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]", L"\x0915\x094D\x094D\x0924" }, + + // These are additional cases which the official break tests don't cover: + { L"multiple combining marks", L"a\u0363", L"e\u0364\u0364", L"i\u0365" }, + { L"multiple US flag regional indicators", L"\U0001F1FA\U0001F1F8", L"\U0001F1FA\U0001F1F8" }, +}; + +class CodepointWidthDetectorTests +{ + TEST_CLASS(CodepointWidthDetectorTests); + + TEST_METHOD(GraphemeBreakTest) + { + WEX::TestExecution::DisableVerifyExceptions disableVerifyExceptions{}; + WEX::TestExecution::SetVerifyOutput verifyOutputScope{ WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures }; + + auto& cwd = CodepointWidthDetector::Singleton(); + std::vector expected; + std::vector actual; + std::wstring text; + + for (const auto& test : s_graphemeBreakTests) + { + expected.clear(); + for (const auto g : test.graphemes) + { + if (!g) + { + break; + } + expected.emplace_back(g); + } + + text.clear(); + for (const auto& g : expected) + { + text.append(g); + } + + actual.clear(); + for (GraphemeState state;;) + { + const auto ok = cwd.GraphemeNext(state, text); + actual.emplace_back(state.beg, state.len); + if (!ok) + { + break; + } + } + VERIFY_ARE_EQUAL(expected, actual, test.comment); + + actual.clear(); + for (GraphemeState state;;) + { + const auto ok = cwd.GraphemePrev(state, text); + actual.emplace_back(state.beg, state.len); + if (!ok) + { + break; + } + } + std::reverse(actual.begin(), actual.end()); + VERIFY_ARE_EQUAL(expected, actual, test.comment); + } + } + + TEST_METHOD(BasicGraphemes) + { + static constexpr std::wstring_view text{ L"a\u0363e\u0364\u0364i\u0365" }; + + auto& cwd = CodepointWidthDetector::Singleton(); + + const std::vector expectedAdvances{ 2, 3, 2 }; + const std::vector expectedWidths{ 1, 1, 1 }; + std::vector actualAdvances; + std::vector actualWidths; + + for (GraphemeState state;;) + { + const auto ok = cwd.GraphemeNext(state, text); + actualAdvances.emplace_back(state.len); + actualWidths.emplace_back(state.width); + if (!ok) + { + break; + } + } + + VERIFY_ARE_EQUAL(expectedAdvances, actualAdvances); + VERIFY_ARE_EQUAL(expectedWidths, actualWidths); + + actualAdvances.clear(); + actualWidths.clear(); + + for (GraphemeState state;;) + { + const auto ok = cwd.GraphemePrev(state, text); + actualAdvances.emplace_back(state.len); + actualWidths.emplace_back(state.width); + if (!ok) + { + break; + } + } + + std::reverse(actualAdvances.begin(), actualAdvances.end()); + std::reverse(actualWidths.begin(), actualWidths.end()); + + VERIFY_ARE_EQUAL(expectedAdvances, actualAdvances); + VERIFY_ARE_EQUAL(expectedWidths, actualWidths); + } + + TEST_METHOD(ChunkedText) + { + struct Test + { + TextMeasurementMode mode; + std::vector advancesNext; + std::vector widthsNext; + std::vector advancesPrev; + std::vector widthsPrev; + }; + const std::array tests{ + Test{ + .mode = TextMeasurementMode::Graphemes, + .advancesNext = { 3, 3, 0, 1 }, + .widthsNext = { 2, 2, 2, 1 }, + .advancesPrev = { 1, 0, 3, 3 }, + .widthsPrev = { 1, 1, 2, 2 }, + + }, + Test{ + .mode = TextMeasurementMode::Wcswidth, + .advancesNext = { 3, 1, 2, 0, 1 }, + .widthsNext = { 1, 1, 2, 2, 1 }, + .advancesPrev = { 1, 0, 2, 1, 3 }, + .widthsPrev = { 1, 1, 2, 0, 1 }, + }, + Test{ + .mode = TextMeasurementMode::Console, + .advancesNext = { 2, 1, 0, 1, 2, 0, 1 }, + .widthsNext = { 1, 0, 0, 0, 2, 2, 1 }, + .advancesPrev = { 1, 0, 2, 1, 0, 1, 2 }, + .widthsPrev = { 1, 1, 2, 0, 0, 0, 1 }, + }, + }; + + // That's a fully qualified rainbow flag followed by a single "a" character. + static constexpr std::array chunks{ + std::wstring_view{ L"\U0001F3F3\uFE0F" }, + std::wstring_view{ L"\u200D\U0001F308" }, + std::wstring_view{ L"a" }, + }; + + CodepointWidthDetector cwd; + GraphemeState state; + std::vector actualAdvances; + std::vector actualWidths; + + for (const auto& test : tests) + { + cwd.Reset(test.mode); + + state = {}; + actualAdvances.clear(); + actualWidths.clear(); + + for (int i = 0; i < 3; i++) + { + bool ok; + do + { + ok = cwd.GraphemeNext(state, chunks[i]); + actualAdvances.emplace_back(state.len); + actualWidths.emplace_back(state.width); + } while (ok); + } + + VERIFY_ARE_EQUAL(test.advancesNext, actualAdvances); + VERIFY_ARE_EQUAL(test.widthsNext, actualWidths); + + state = {}; + actualAdvances.clear(); + actualWidths.clear(); + + for (int i = 2; i >= 0; i--) + { + bool ok; + do + { + ok = cwd.GraphemePrev(state, chunks[i]); + actualAdvances.emplace_back(state.len); + actualWidths.emplace_back(state.width); + } while (ok); + } + + VERIFY_ARE_EQUAL(test.advancesPrev, actualAdvances); + VERIFY_ARE_EQUAL(test.widthsPrev, actualWidths); + } + } +}; diff --git a/src/types/ut_types/Types.Unit.Tests.vcxproj b/src/types/ut_types/Types.Unit.Tests.vcxproj index 45a486c63b..1f42421f4d 100644 --- a/src/types/ut_types/Types.Unit.Tests.vcxproj +++ b/src/types/ut_types/Types.Unit.Tests.vcxproj @@ -11,6 +11,7 @@ + diff --git a/src/types/ut_types/sources b/src/types/ut_types/sources index 71fce07be3..f5741649fe 100644 --- a/src/types/ut_types/sources +++ b/src/types/ut_types/sources @@ -14,6 +14,7 @@ DLLDEF = SOURCES = \ $(SOURCES) \ + CodepointWidthDetectorTests.cpp \ UuidTests.cpp \ UtilsTests.cpp \ DefaultResource.rc \ diff --git a/src/winconpty/winconpty.cpp b/src/winconpty/winconpty.cpp index 16bb278b7d..e3a915447a 100644 --- a/src/winconpty/winconpty.cpp +++ b/src/winconpty/winconpty.cpp @@ -137,12 +137,31 @@ HRESULT _CreatePseudoConsole(const HANDLE hToken, wchar_t cmd[MAX_PATH]{}; const BOOL bInheritCursor = (dwFlags & PSEUDOCONSOLE_INHERIT_CURSOR) == PSEUDOCONSOLE_INHERIT_CURSOR; const BOOL bResizeQuirk = (dwFlags & PSEUDOCONSOLE_RESIZE_QUIRK) == PSEUDOCONSOLE_RESIZE_QUIRK; + + const wchar_t* textMeasurement; + switch (dwFlags & PSEUDOCONSOLE_GLYPH_WIDTH__MASK) + { + case PSEUDOCONSOLE_GLYPH_WIDTH_GRAPHEMES: + textMeasurement = L"--textMeasurement graphemes "; + break; + case PSEUDOCONSOLE_GLYPH_WIDTH_WCSWIDTH: + textMeasurement = L"--textMeasurement wcswidth "; + break; + case PSEUDOCONSOLE_GLYPH_WIDTH_CONSOLE: + textMeasurement = L"--textMeasurement console "; + break; + default: + textMeasurement = L""; + break; + } + swprintf_s(cmd, MAX_PATH, - L"\"%s\" --headless %s%s--width %hd --height %hd --signal 0x%tx --server 0x%tx", + L"\"%s\" --headless %s%s%s--width %hd --height %hd --signal 0x%tx --server 0x%tx", _ConsoleHostPath(), bInheritCursor ? L"--inheritcursor " : L"", bResizeQuirk ? L"--resizeQuirk " : L"", + textMeasurement, size.X, size.Y, std::bit_cast(signalPipeConhostSide.get()), diff --git a/src/winconpty/winconpty.h b/src/winconpty/winconpty.h index a38fdef859..bd1a6cd190 100644 --- a/src/winconpty/winconpty.h +++ b/src/winconpty/winconpty.h @@ -55,8 +55,11 @@ typedef struct _PseudoConsole #ifndef PSEUDOCONSOLE_RESIZE_QUIRK #define PSEUDOCONSOLE_RESIZE_QUIRK (0x2) #endif -#ifndef PSEUDOCONSOLE_WIN32_INPUT_MODE -#define PSEUDOCONSOLE_WIN32_INPUT_MODE (0x4) +#ifndef PSEUDOCONSOLE_GLYPH_WIDTH__MASK +#define PSEUDOCONSOLE_GLYPH_WIDTH__MASK 0x18 +#define PSEUDOCONSOLE_GLYPH_WIDTH_GRAPHEMES 0x08 +#define PSEUDOCONSOLE_GLYPH_WIDTH_WCSWIDTH 0x10 +#define PSEUDOCONSOLE_GLYPH_WIDTH_CONSOLE 0x18 #endif // Implementations of the various PseudoConsole functions.