diff options
-rw-r--r-- | libs/minikin/FontCollection.cpp | 10 | ||||
-rw-r--r-- | libs/minikin/GraphemeBreak.cpp | 43 | ||||
-rw-r--r-- | libs/minikin/MinikinInternal.cpp | 42 | ||||
-rw-r--r-- | libs/minikin/MinikinInternal.h | 6 | ||||
-rw-r--r-- | tests/FontCollectionItemizeTest.cpp | 110 | ||||
-rw-r--r-- | tests/data/ColorEmojiFont.ttf | bin | 948 -> 1020 bytes | |||
-rw-r--r-- | tests/data/ColorEmojiFont.ttx | 16 | ||||
-rw-r--r-- | tests/data/TextEmojiFont.ttf | bin | 940 -> 964 bytes | |||
-rw-r--r-- | tests/data/TextEmojiFont.ttx | 6 | ||||
-rw-r--r-- | tests/data/emoji.xml | 6 |
10 files changed, 116 insertions, 123 deletions
diff --git a/libs/minikin/FontCollection.cpp b/libs/minikin/FontCollection.cpp index dd905a3..4541af8 100644 --- a/libs/minikin/FontCollection.cpp +++ b/libs/minikin/FontCollection.cpp @@ -379,11 +379,13 @@ void FontCollection::itemize(const uint16_t *string, size_t string_size, FontSty langListId, variant); if (utf16Pos == 0 || family != lastFamily) { size_t start = utf16Pos; - // Workaround for Emoji keycap until we implement per-cluster font - // selection: if keycap is found in a different font that also - // supports previous char, attach previous char to the new run. + // Workaround for Emoji keycap and emoji modifier until we implement per-cluster + // font selection: if a keycap or an emoji modifier is found in a different font + // that also supports previous char, attach previous char to the new run. // Bug 7557244. - if (ch == KEYCAP && utf16Pos != 0 && family && family->getCoverage()->get(prevCh)) { + if (utf16Pos != 0 && + (ch == KEYCAP || (isEmojiModifier(ch) && isEmojiBase(prevCh))) && + family && family->getCoverage()->get(prevCh)) { const size_t prevChLength = U16_LENGTH(prevCh); run->end -= prevChLength; if (run->start == run->end) { diff --git a/libs/minikin/GraphemeBreak.cpp b/libs/minikin/GraphemeBreak.cpp index 4141091..ef323d5 100644 --- a/libs/minikin/GraphemeBreak.cpp +++ b/libs/minikin/GraphemeBreak.cpp @@ -19,6 +19,7 @@ #include <unicode/utf16.h> #include <minikin/GraphemeBreak.h> +#include "MinikinInternal.h" namespace android { @@ -77,48 +78,6 @@ bool isZwjEmoji(uint32_t c) { || c == 0x1F5E8); // LEFT SPEECH BUBBLE } -// Based on Modifiers from http://www.unicode.org/L2/L2016/16011-data-file.txt -bool isEmojiModifier(uint32_t c) { - return (0x1F3FB <= c && c <= 0x1F3FF); -} - -// Based on Emoji_Modifier_Base from -// http://www.unicode.org/Public/emoji/3.0/emoji-data.txt -bool isEmojiBase(uint32_t c) { - if (0x261D <= c && c <= 0x270D) { - return (c == 0x261D || c == 0x26F9 || (0x270A <= c && c <= 0x270D)); - } else if (0x1F385 <= c && c <= 0x1F93E) { - return (c == 0x1F385 - || (0x1F3C3 <= c || c <= 0x1F3C4) - || (0x1F3CA <= c || c <= 0x1F3CB) - || (0x1F442 <= c || c <= 0x1F443) - || (0x1F446 <= c || c <= 0x1F450) - || (0x1F466 <= c || c <= 0x1F469) - || c == 0x1F46E - || (0x1F470 <= c || c <= 0x1F478) - || c == 0x1F47C - || (0x1F481 <= c || c <= 0x1F483) - || (0x1F485 <= c || c <= 0x1F487) - || c == 0x1F4AA - || c == 0x1F575 - || c == 0x1F57A - || c == 0x1F590 - || (0x1F595 <= c || c <= 0x1F596) - || (0x1F645 <= c || c <= 0x1F647) - || (0x1F64B <= c || c <= 0x1F64F) - || c == 0x1F6A3 - || (0x1F6B4 <= c || c <= 0x1F6B6) - || c == 0x1F6C0 - || (0x1F918 <= c || c <= 0x1F91E) - || c == 0x1F926 - || c == 0x1F930 - || (0x1F933 <= c || c <= 0x1F939) - || (0x1F93B <= c || c <= 0x1F93E)); - } else { - return false; - } -} - bool GraphemeBreak::isGraphemeBreak(const uint16_t* buf, size_t start, size_t count, size_t offset) { // This implementation closely follows Unicode Standard Annex #29 on diff --git a/libs/minikin/MinikinInternal.cpp b/libs/minikin/MinikinInternal.cpp index c2aa01a..7fa7ce9 100644 --- a/libs/minikin/MinikinInternal.cpp +++ b/libs/minikin/MinikinInternal.cpp @@ -30,4 +30,46 @@ void assertMinikinLocked() { #endif } +// Based on Modifiers from http://www.unicode.org/L2/L2016/16011-data-file.txt +bool isEmojiModifier(uint32_t c) { + return (0x1F3FB <= c && c <= 0x1F3FF); +} + +// Based on Emoji_Modifier_Base from +// http://www.unicode.org/Public/emoji/3.0/emoji-data.txt +bool isEmojiBase(uint32_t c) { + if (0x261D <= c && c <= 0x270D) { + return (c == 0x261D || c == 0x26F9 || (0x270A <= c && c <= 0x270D)); + } else if (0x1F385 <= c && c <= 0x1F93E) { + return (c == 0x1F385 + || (0x1F3C3 <= c || c <= 0x1F3C4) + || (0x1F3CA <= c || c <= 0x1F3CB) + || (0x1F442 <= c || c <= 0x1F443) + || (0x1F446 <= c || c <= 0x1F450) + || (0x1F466 <= c || c <= 0x1F469) + || c == 0x1F46E + || (0x1F470 <= c || c <= 0x1F478) + || c == 0x1F47C + || (0x1F481 <= c || c <= 0x1F483) + || (0x1F485 <= c || c <= 0x1F487) + || c == 0x1F4AA + || c == 0x1F575 + || c == 0x1F57A + || c == 0x1F590 + || (0x1F595 <= c || c <= 0x1F596) + || (0x1F645 <= c || c <= 0x1F647) + || (0x1F64B <= c || c <= 0x1F64F) + || c == 0x1F6A3 + || (0x1F6B4 <= c || c <= 0x1F6B6) + || c == 0x1F6C0 + || (0x1F918 <= c || c <= 0x1F91E) + || c == 0x1F926 + || c == 0x1F930 + || (0x1F933 <= c || c <= 0x1F939) + || (0x1F93B <= c || c <= 0x1F93E)); + } else { + return false; + } +} + } diff --git a/libs/minikin/MinikinInternal.h b/libs/minikin/MinikinInternal.h index 34a95bb..3d68691 100644 --- a/libs/minikin/MinikinInternal.h +++ b/libs/minikin/MinikinInternal.h @@ -32,6 +32,12 @@ extern Mutex gMinikinLock; // Aborts if gMinikinLock is not acquired. Do nothing on the release build. void assertMinikinLocked(); +// Returns true if c is emoji modifier base. +bool isEmojiBase(uint32_t c); + +// Returns true if c is emoji modifier. +bool isEmojiModifier(uint32_t c); + } #endif // MINIKIN_INTERNAL_H diff --git a/tests/FontCollectionItemizeTest.cpp b/tests/FontCollectionItemizeTest.cpp index 446efc6..031f3f9 100644 --- a/tests/FontCollectionItemizeTest.cpp +++ b/tests/FontCollectionItemizeTest.cpp @@ -1119,78 +1119,6 @@ TEST_F(FontCollectionItemizeTest, itemize_LanguageAndCoverage) { } } -TEST_F(FontCollectionItemizeTest, itemize_emojiSelection) { - std::unique_ptr<FontCollection> collection = getFontCollection(kTestFontDir, kEmojiXmlFile); - std::vector<FontCollection::Run> runs; - - const FontStyle kDefaultFontStyle; - - // U+00A9 is a text default emoji which is only available in TextEmojiFont.ttf. - // TextEmojiFont.ttf should be selected. - itemize(collection.get(), "U+00A9", kDefaultFontStyle, &runs); - ASSERT_EQ(1U, runs.size()); - EXPECT_EQ(0, runs[0].start); - EXPECT_EQ(1, runs[0].end); - EXPECT_EQ(kTextEmojiFont, getFontPath(runs[0])); - - // U+00AE is a text default emoji which is only available in ColorEmojiFont.ttf. - // ColorEmojiFont.ttf should be selected. - itemize(collection.get(), "U+00AE", kDefaultFontStyle, &runs); - ASSERT_EQ(1U, runs.size()); - EXPECT_EQ(0, runs[0].start); - EXPECT_EQ(1, runs[0].end); - EXPECT_EQ(kColorEmojiFont, getFontPath(runs[0])); - - // U+203C is a text default emoji which is available in both TextEmojiFont.ttf and - // ColorEmojiFont.ttf. TextEmojiFont.ttf should be selected. - itemize(collection.get(), "U+203C", kDefaultFontStyle, &runs); - ASSERT_EQ(1U, runs.size()); - EXPECT_EQ(0, runs[0].start); - EXPECT_EQ(1, runs[0].end); - // TODO: use text font for text default emoji. - // EXPECT_EQ(kTextEmojiFont, getFontPath(runs[0])); - - // U+2049 is a text default emoji which is not available in either TextEmojiFont.ttf or - // ColorEmojiFont.ttf. No font should be selected. - itemize(collection.get(), "U+2049", kDefaultFontStyle, &runs); - ASSERT_EQ(1U, runs.size()); - EXPECT_EQ(0, runs[0].start); - EXPECT_EQ(1, runs[0].end); - EXPECT_TRUE(runs[0].fakedFont.font == NULL || kNoGlyphFont == getFontPath(runs[0])); - - // U+231A is a emoji default emoji which is available only in TextEmojiFont.ttf. - // TextEmojiFont.ttf should be selected. - itemize(collection.get(), "U+231A", kDefaultFontStyle, &runs); - ASSERT_EQ(1U, runs.size()); - EXPECT_EQ(0, runs[0].start); - EXPECT_EQ(1, runs[0].end); - EXPECT_EQ(kTextEmojiFont, getFontPath(runs[0])); - - // U+231B is a emoji default emoji which is available only in ColorEmojiFont.ttf. - // ColorEmojiFont.ttf should be selected. - itemize(collection.get(), "U+231B", kDefaultFontStyle, &runs); - ASSERT_EQ(1U, runs.size()); - EXPECT_EQ(0, runs[0].start); - EXPECT_EQ(1, runs[0].end); - EXPECT_EQ(kColorEmojiFont, getFontPath(runs[0])); - - // U+23E9 is a emoji default emoji which is available in both TextEmojiFont.ttf and - // ColorEmojiFont.ttf. ColorEmojiFont should be selected. - itemize(collection.get(), "U+23E9", kDefaultFontStyle, &runs); - ASSERT_EQ(1U, runs.size()); - EXPECT_EQ(0, runs[0].start); - EXPECT_EQ(1, runs[0].end); - EXPECT_EQ(kColorEmojiFont, getFontPath(runs[0])); - - // U+23EA is a emoji default emoji which is not avaialble in either TextEmojiFont.ttf and - // ColorEmojiFont.ttf. No font should b e selected. - itemize(collection.get(), "U+23EA", kDefaultFontStyle, &runs); - ASSERT_EQ(1U, runs.size()); - EXPECT_EQ(0, runs[0].start); - EXPECT_EQ(1, runs[0].end); - EXPECT_TRUE(runs[0].fakedFont.font == NULL || kNoGlyphFont == getFontPath(runs[0])); -} - TEST_F(FontCollectionItemizeTest, itemize_emojiSelection_withFE0E) { std::unique_ptr<FontCollection> collection = getFontCollection(kTestFontDir, kEmojiXmlFile); std::vector<FontCollection::Run> runs; @@ -1355,3 +1283,41 @@ TEST_F(FontCollectionItemizeTest, itemize_emojiSelection_withFE0F) { EXPECT_EQ(kMixedEmojiFont, getFontPath(runs[0])); } +TEST_F(FontCollectionItemizeTest, itemize_emojiSelection_with_skinTone) { + std::unique_ptr<FontCollection> collection = getFontCollection(kTestFontDir, kEmojiXmlFile); + std::vector<FontCollection::Run> runs; + + const FontStyle kDefaultFontStyle; + + // TextEmoji font is selected since it is listed before ColorEmoji font. + itemize(collection.get(), "U+261D", kDefaultFontStyle, &runs); + ASSERT_EQ(1U, runs.size()); + EXPECT_EQ(0, runs[0].start); + EXPECT_EQ(1, runs[0].end); + EXPECT_EQ(kTextEmojiFont, getFontPath(runs[0])); + + // If skin tone is specified, it should be colored. + itemize(collection.get(), "U+261D U+1F3FD", kDefaultFontStyle, &runs); + ASSERT_EQ(1U, runs.size()); + EXPECT_EQ(0, runs[0].start); + EXPECT_EQ(3, runs[0].end); + EXPECT_EQ(kColorEmojiFont, getFontPath(runs[0])); + + // Still color font is selected if an emoji variation selector is specified. + itemize(collection.get(), "U+261D U+FE0F U+1F3FD", kDefaultFontStyle, &runs); + ASSERT_EQ(1U, runs.size()); + EXPECT_EQ(0, runs[0].start); + EXPECT_EQ(4, runs[0].end); + EXPECT_EQ(kColorEmojiFont, getFontPath(runs[0])); + + // Text font should be selected if a text variation selector is specified and skin tone is + // rendered by itself. + itemize(collection.get(), "U+261D U+FE0E U+1F3FD", kDefaultFontStyle, &runs); + ASSERT_EQ(2U, runs.size()); + EXPECT_EQ(0, runs[0].start); + EXPECT_EQ(2, runs[0].end); + EXPECT_EQ(kTextEmojiFont, getFontPath(runs[0])); + EXPECT_EQ(2, runs[1].start); + EXPECT_EQ(4, runs[1].end); + EXPECT_EQ(kColorEmojiFont, getFontPath(runs[1])); +} diff --git a/tests/data/ColorEmojiFont.ttf b/tests/data/ColorEmojiFont.ttf Binary files differindex dd72eea..e0b34a1 100644 --- a/tests/data/ColorEmojiFont.ttf +++ b/tests/data/ColorEmojiFont.ttf diff --git a/tests/data/ColorEmojiFont.ttx b/tests/data/ColorEmojiFont.ttx index b077339..58b3d83 100644 --- a/tests/data/ColorEmojiFont.ttx +++ b/tests/data/ColorEmojiFont.ttx @@ -27,6 +27,8 @@ <GlyphID id="7" name="Emoji7"/> <GlyphID id="8" name="Emoji8"/> <GlyphID id="9" name="Emoji9"/> + <GlyphID id="10" name="WHITE_UP_POINTING_INDEX" /> + <GlyphID id="11" name="EMOJI_MODIFIER_FITZPATRICK_TYPE_4" /> </GlyphOrder> <head> @@ -153,17 +155,21 @@ <mtx name="Emoji7" width="500" lsb="93"/> <mtx name="Emoji8" width="500" lsb="93"/> <mtx name="Emoji9" width="500" lsb="93"/> + <mtx name="WHITE_UP_POINTING_INDEX" width="500" lsb="93" /> + <mtx name="EMOJI_MODIFIER_FITZPATRICK_TYPE_4" width="500" lsb="93" /> </hmtx> <cmap> <tableVersion version="0"/> - <cmap_format_4 platformID="3" platEncID="10" language="0"> + <cmap_format_12 format="12" reserved="0" length="7" nGroups="1" platformID="3" platEncID="10" language="0"> <map code="0x00AE" name="Emoji2" /> <!-- Text Default --> <map code="0x203C" name="Emoji3" /> <!-- Text Default --> <map code="0x231B" name="Emoji6" /> <!-- Emoji Default --> <map code="0x23E9" name="Emoji7" /> <!-- Emoji Default --> <map code="0x26F9" name="Emoji9" /> <!-- U+26F9 U+FE0F is in ColorTextMixedEmojiFont.ttf --> - </cmap_format_4> + <map code="0x261D" name="WHITE_UP_POINTING_INDEX" /> + <map code="0x1F3FD" name="EMOJI_MODIFIER_FITZPATRICK_TYPE_4" /> + </cmap_format_12> </cmap> <loca> @@ -206,6 +212,12 @@ <TTGlyph name="Emoji9" xMin="0" yMin="0" xMax="0" yMax="0"> <contour></contour><instructions><assembly></assembly></instructions> </TTGlyph> + <TTGlyph name="WHITE_UP_POINTING_INDEX" xMin="0" yMin="0" xMax="0" yMax="0"> + <contour></contour><instructions><assembly></assembly></instructions> + </TTGlyph> + <TTGlyph name="EMOJI_MODIFIER_FITZPATRICK_TYPE_4" xMin="0" yMin="0" xMax="0" yMax="0"> + <contour></contour><instructions><assembly></assembly></instructions> + </TTGlyph> </glyf> <name> diff --git a/tests/data/TextEmojiFont.ttf b/tests/data/TextEmojiFont.ttf Binary files differindex 21013a0..21b6801 100644 --- a/tests/data/TextEmojiFont.ttf +++ b/tests/data/TextEmojiFont.ttf diff --git a/tests/data/TextEmojiFont.ttx b/tests/data/TextEmojiFont.ttx index 35e7d98..83cc9bc 100644 --- a/tests/data/TextEmojiFont.ttx +++ b/tests/data/TextEmojiFont.ttx @@ -27,6 +27,7 @@ <GlyphID id="7" name="Emoji7"/> <GlyphID id="8" name="Emoji8"/> <GlyphID id="9" name="Emoji9"/> + <GlyphID id="10" name="WHITE_UP_POINTING_INDEX" /> </GlyphOrder> <head> @@ -153,6 +154,7 @@ <mtx name="Emoji7" width="500" lsb="93"/> <mtx name="Emoji8" width="500" lsb="93"/> <mtx name="Emoji9" width="500" lsb="93"/> + <mtx name="WHITE_UP_POINTING_INDEX" width="500" lsb="93"/> </hmtx> <cmap> @@ -163,6 +165,7 @@ <map code="0x231A" name="Emoji5" /> <!-- Emoji Default --> <map code="0x23E9" name="Emoji7" /> <!-- Emoji Default --> <map code="0x26FA" name="Emoji9" /> <!-- U+26FA U+FE0E is in ColorTextMixedEmojiFont.ttf --> + <map code="0x261D" name="WHITE_UP_POINTING_INDEX" /> </cmap_format_4> </cmap> @@ -206,6 +209,9 @@ <TTGlyph name="Emoji9" xMin="0" yMin="0" xMax="0" yMax="0"> <contour></contour><instructions><assembly></assembly></instructions> </TTGlyph> + <TTGlyph name="WHITE_UP_POINTING_INDEX" xMin="0" yMin="0" xMax="0" yMax="0"> + <contour></contour><instructions><assembly></assembly></instructions> + </TTGlyph> </glyf> <name> diff --git a/tests/data/emoji.xml b/tests/data/emoji.xml index a908b46..796a0f1 100644 --- a/tests/data/emoji.xml +++ b/tests/data/emoji.xml @@ -19,12 +19,12 @@ <family> <font weight="400" style="normal">NoGlyphFont.ttf</font> </family> - <family lang="und-Zsye"> - <font weight="400" style="normal">ColorEmojiFont.ttf</font> - </family> <family> <font weight="400" style="normal">TextEmojiFont.ttf</font> </family> + <family lang="und-Zsye"> + <font weight="400" style="normal">ColorEmojiFont.ttf</font> + </family> <family> <font weight="400" style="normal">ColorTextMixedEmojiFont.ttf</font> </family> |