diff --git a/intl/unicharutil/util/nsUnicodeProperties.cpp b/intl/unicharutil/util/nsUnicodeProperties.cpp index 8a2b04a8b0..e78725b036 100644 --- a/intl/unicharutil/util/nsUnicodeProperties.cpp +++ b/intl/unicharutil/util/nsUnicodeProperties.cpp @@ -153,7 +153,9 @@ IsClusterExtender(uint32_t aCh, uint8_t aCategory) return ((aCategory >= HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK && aCategory <= HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) || (aCh >= 0x200c && aCh <= 0x200d) || // ZWJ, ZWNJ - (aCh >= 0xff9e && aCh <= 0xff9f)); // katakana sound marks + (aCh >= 0xff9e && aCh <= 0xff9f) || // katakana sound marks + (aCh >= 0x1F3FB && aCh <= 0x1F3FF) || // fitzpatrick skin tone modifiers + (aCh >= 0xe0020 && aCh <= 0xe007f)); // emoji (flag) tag characters } enum HSType { @@ -224,8 +226,25 @@ ClusterIterator::Next() } } + const uint32_t kVS16 = 0xfe0f; + const uint32_t kZWJ = 0x200d; + // UTF-16 surrogate values for Fitzpatrick type modifiers + const uint32_t kFitzpatrickHigh = 0xD83C; + const uint32_t kFitzpatrickLowFirst = 0xDFFB; + const uint32_t kFitzpatrickLowLast = 0xDFFF; + + bool baseIsEmoji = (GetEmojiPresentation(ch) == EmojiDefault) || + (GetEmojiPresentation(ch) == TextDefault && + ((mPos < mLimit && *mPos == kVS16) || + (mPos + 1 < mLimit && + *mPos == kFitzpatrickHigh && + *(mPos + 1) >= kFitzpatrickLowFirst && + *(mPos + 1) <= kFitzpatrickLowLast))); + bool prevWasZwj = false; + while (mPos < mLimit) { ch = *mPos; + size_t chLen = 1; // Check for surrogate pairs; note that isolated surrogates will just // be treated as generic (non-cluster-extending) characters here, @@ -233,16 +252,21 @@ ClusterIterator::Next() if (NS_IS_HIGH_SURROGATE(ch) && mPos < mLimit - 1 && NS_IS_LOW_SURROGATE(*(mPos + 1))) { ch = SURROGATE_TO_UCS4(ch, *(mPos + 1)); + chLen = 2; } - if (!IsClusterExtender(ch)) { + bool extendCluster = IsClusterExtender(ch) || + (baseIsEmoji && prevWasZwj && + ((GetEmojiPresentation(ch) == EmojiDefault) || + (GetEmojiPresentation(ch) == TextDefault && + mPos + chLen < mLimit && + *(mPos + chLen) == kVS16))); + if (!extendCluster) { break; } - mPos++; - if (!IS_IN_BMP(ch)) { - mPos++; - } + prevWasZwj = (ch == kZWJ); + mPos += chLen; } NS_ASSERTION(mText < mPos && mPos <= mLimit, diff --git a/intl/unicharutil/util/nsUnicodeProperties.h b/intl/unicharutil/util/nsUnicodeProperties.h index 38fa230625..eaa066b7f6 100644 --- a/intl/unicharutil/util/nsUnicodeProperties.h +++ b/intl/unicharutil/util/nsUnicodeProperties.h @@ -180,17 +180,14 @@ IsDefaultIgnorable(uint32_t aCh) inline EmojiPresentation GetEmojiPresentation(uint32_t aCh) { - if (u_hasBinaryProperty(aCh, UCHAR_EMOJI_COMPONENT)) { - return EmojiComponent; + if (!u_hasBinaryProperty(aCh, UCHAR_EMOJI)) { + return TextOnly; } - if (u_hasBinaryProperty(aCh, UCHAR_EMOJI) && - !u_hasBinaryProperty(aCh, UCHAR_EMOJI_PRESENTATION)) { - return TextDefault; - } - if (u_hasBinaryProperty(aCh, UCHAR_EXTENDED_PICTOGRAPHIC)) { + + if (u_hasBinaryProperty(aCh, UCHAR_EMOJI_PRESENTATION)) { return EmojiDefault; } - return TextOnly; + return TextDefault; } // returns the simplified Gen Category as defined in nsIUGenCategory