PR #2514 - Don't split up happy emoji sequences

Loosely based on https://phabricator.services.mozilla.com/D25101
Includes changes to `GetEmojiPresentation`, added in e38b57ca.
The `EmojiComponent` response includes hair color, skin tone, ZWJ, VS16, Combining Enclosing Keycap, and the Tag unicode characters used for subdivision flag emojis.
This means that `IsClusterExtender` should support all these characters, and be forward compatible with any future Unicode changes.
This also lets us restructure `ClusterIterator::Next` to check for EmojiComponents after a TextDefault character, without any stupid constants.
This commit is contained in:
Andy 2024-05-09 15:44:02 -07:00 committed by roytam1
commit 52d7e4f3db

View file

@ -152,7 +152,8 @@ 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
(GetEmojiPresentation(aCh) == EmojiComponent) ||
(aCh == 0x200c) || // ZWNJ
(aCh >= 0xff9e && aCh <= 0xff9f)); // katakana sound marks
}
@ -224,8 +225,25 @@ ClusterIterator::Next()
}
}
const uint32_t kZWJ = 0x200d;
uint32_t aNextCh = 0;
if (mPos + 1 < mLimit) {
aNextCh = *mPos;
uint32_t aLowCh = *(mPos + 1);
if (NS_IS_HIGH_SURROGATE(aNextCh) && NS_IS_LOW_SURROGATE(aLowCh)) {
aNextCh = SURROGATE_TO_UCS4(aNextCh, aLowCh);
}
}
bool baseIsEmoji = (GetEmojiPresentation(ch) == EmojiDefault) ||
(GetEmojiPresentation(ch) == EmojiComponent) ||
(GetEmojiPresentation(ch) == TextDefault &&
GetEmojiPresentation(aNextCh) == EmojiComponent);
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 +251,30 @@ 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)) {
uint32_t aExtCh = 0;
if (mPos + chLen < mLimit) {
aExtCh = *(mPos + chLen);
uint32_t aLowCh = *(mPos + chLen + 1);
if (NS_IS_HIGH_SURROGATE(aExtCh) && NS_IS_LOW_SURROGATE(aLowCh)) {
aExtCh = SURROGATE_TO_UCS4(aExtCh, aLowCh);
}
}
bool extendCluster =
IsClusterExtender(ch) ||
(baseIsEmoji && prevWasZwj &&
((GetEmojiPresentation(ch) == EmojiDefault) ||
(GetEmojiPresentation(ch) == EmojiComponent) ||
(GetEmojiPresentation(ch) == TextDefault &&
GetEmojiPresentation(aExtCh) == EmojiComponent)));
if (!extendCluster) {
break;
}
mPos++;
if (!IS_IN_BMP(ch)) {
mPos++;
}
prevWasZwj = (ch == kZWJ);
mPos += chLen;
}
NS_ASSERTION(mText < mPos && mPos <= mLimit,