flutter_flutter/tests/FontCollectionItemizeTest.cpp
Seigo Nonaka 6f9966ea7c Introduce multiple language based font fallback.
The motivation of this CL is enhance the font fallback score design
to support multiple language font fallback.

This CL contains following changes:
- Break language based font score into two: script-based score and
  primary-language-based score.
- The primary-language-based score is 0 if the script-based score is 0.
  If the script-based score is not 0 and the primary language is the
  as same as the requested one, the font gets an extra score of 1.
- The language score gets a higher multiplier for languages higher in
  the locale list.

Bug: 25122318
Bug: 26168983
Change-Id: Ib999997a88e6977e341f4c325e2a1b41a59db2d5
2016-01-15 14:35:14 +09:00

1358 lines
59 KiB
C++

/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <gtest/gtest.h>
#include "FontLanguageListCache.h"
#include "FontLanguage.h"
#include "FontTestUtils.h"
#include "ICUTestBase.h"
#include "MinikinFontForTest.h"
#include "MinikinInternal.h"
#include "UnicodeUtils.h"
#include "minikin/FontFamily.h"
using android::AutoMutex;
using android::FontCollection;
using android::FontFamily;
using android::FontLanguage;
using android::FontLanguages;
using android::FontLanguageListCache;
using android::FontStyle;
using android::MinikinFont;
using android::gMinikinLock;
const char kItemizeFontXml[] = kTestFontDir "itemize.xml";
const char kEmojiFont[] = kTestFontDir "Emoji.ttf";
const char kJAFont[] = kTestFontDir "Ja.ttf";
const char kKOFont[] = kTestFontDir "Ko.ttf";
const char kLatinBoldFont[] = kTestFontDir "Bold.ttf";
const char kLatinBoldItalicFont[] = kTestFontDir "BoldItalic.ttf";
const char kLatinFont[] = kTestFontDir "Regular.ttf";
const char kLatinItalicFont[] = kTestFontDir "Italic.ttf";
const char kZH_HansFont[] = kTestFontDir "ZhHans.ttf";
const char kZH_HantFont[] = kTestFontDir "ZhHant.ttf";
const char kEmojiXmlFile[] = kTestFontDir "emoji.xml";
const char kNoGlyphFont[] = kTestFontDir "NoGlyphFont.ttf";
const char kColorEmojiFont[] = kTestFontDir "ColorEmojiFont.ttf";
const char kTextEmojiFont[] = kTestFontDir "TextEmojiFont.ttf";
const char kMixedEmojiFont[] = kTestFontDir "ColorTextMixedEmojiFont.ttf";
typedef ICUTestBase FontCollectionItemizeTest;
// Utility function for calling itemize function.
void itemize(FontCollection* collection, const char* str, FontStyle style,
std::vector<FontCollection::Run>* result) {
const size_t BUF_SIZE = 256;
uint16_t buf[BUF_SIZE];
size_t len;
result->clear();
ParseUnicode(buf, BUF_SIZE, str, &len, NULL);
AutoMutex _l(gMinikinLock);
collection->itemize(buf, len, style, result);
}
// Utility function to obtain font path associated with run.
const std::string& getFontPath(const FontCollection::Run& run) {
EXPECT_NE(nullptr, run.fakedFont.font);
return ((MinikinFontForTest*)run.fakedFont.font)->fontPath();
}
// Utility function to obtain FontLanguages from string.
const FontLanguages& registerAndGetFontLanguages(const std::string& lang_string) {
AutoMutex _l(gMinikinLock);
return FontLanguageListCache::getById(FontLanguageListCache::getId(lang_string));
}
TEST_F(FontCollectionItemizeTest, itemize_latin) {
std::unique_ptr<FontCollection> collection = getFontCollection(kTestFontDir, kItemizeFontXml);
std::vector<FontCollection::Run> runs;
const FontStyle kRegularStyle = FontStyle();
const FontStyle kItalicStyle = FontStyle(4, true);
const FontStyle kBoldStyle = FontStyle(7, false);
const FontStyle kBoldItalicStyle = FontStyle(7, true);
itemize(collection.get(), "'a' 'b' 'c' 'd' 'e'", kRegularStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(5, runs[0].end);
EXPECT_EQ(kLatinFont, getFontPath(runs[0]));
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
itemize(collection.get(), "'a' 'b' 'c' 'd' 'e'", kItalicStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(5, runs[0].end);
EXPECT_EQ(kLatinItalicFont, getFontPath(runs[0]));
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
itemize(collection.get(), "'a' 'b' 'c' 'd' 'e'", kBoldStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(5, runs[0].end);
EXPECT_EQ(kLatinBoldFont, getFontPath(runs[0]));
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
itemize(collection.get(), "'a' 'b' 'c' 'd' 'e'", kBoldItalicStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(5, runs[0].end);
EXPECT_EQ(kLatinBoldItalicFont, getFontPath(runs[0]));
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
// Continue if the specific characters (e.g. hyphen, comma, etc.) is
// followed.
itemize(collection.get(), "'a' ',' '-' 'd' '!'", kRegularStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(5, runs[0].end);
EXPECT_EQ(kLatinFont, getFontPath(runs[0]));
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
itemize(collection.get(), "'a' ',' '-' 'd' '!'", kRegularStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(5, runs[0].end);
EXPECT_EQ(kLatinFont, getFontPath(runs[0]));
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
// U+0301(COMBINING ACUTE ACCENT) must be in the same run with preceding
// chars if the font supports it.
itemize(collection.get(), "'a' U+0301", kRegularStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
EXPECT_EQ(kLatinFont, getFontPath(runs[0]));
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
}
TEST_F(FontCollectionItemizeTest, itemize_emoji) {
std::unique_ptr<FontCollection> collection = getFontCollection(kTestFontDir, kItemizeFontXml);
std::vector<FontCollection::Run> runs;
itemize(collection.get(), "U+1F469 U+1F467", FontStyle(), &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(4, runs[0].end);
EXPECT_EQ(kEmojiFont, getFontPath(runs[0]));
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
// U+20E3(COMBINING ENCLOSING KEYCAP) must be in the same run with preceding
// character if the font supports.
itemize(collection.get(), "'0' U+20E3", FontStyle(), &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
EXPECT_EQ(kEmojiFont, getFontPath(runs[0]));
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
itemize(collection.get(), "U+1F470 U+20E3", FontStyle(), &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(3, runs[0].end);
EXPECT_EQ(kEmojiFont, getFontPath(runs[0]));
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
itemize(collection.get(), "U+242EE U+1F470 U+20E3", FontStyle(), &runs);
ASSERT_EQ(2U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
EXPECT_EQ(kJAFont, getFontPath(runs[0]));
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
EXPECT_EQ(2, runs[1].start);
EXPECT_EQ(5, runs[1].end);
EXPECT_EQ(kEmojiFont, getFontPath(runs[1]));
EXPECT_FALSE(runs[1].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[1].fakedFont.fakery.isFakeItalic());
// Currently there is no fonts which has a glyph for 'a' + U+20E3, so they
// are splitted into two.
itemize(collection.get(), "'a' U+20E3", FontStyle(), &runs);
ASSERT_EQ(2U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(1, runs[0].end);
EXPECT_EQ(kLatinFont, getFontPath(runs[0]));
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
EXPECT_EQ(1, runs[1].start);
EXPECT_EQ(2, runs[1].end);
EXPECT_EQ(kEmojiFont, getFontPath(runs[1]));
EXPECT_FALSE(runs[1].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[1].fakedFont.fakery.isFakeItalic());
}
TEST_F(FontCollectionItemizeTest, itemize_non_latin) {
std::unique_ptr<FontCollection> collection = getFontCollection(kTestFontDir, kItemizeFontXml);
std::vector<FontCollection::Run> runs;
FontStyle kJAStyle = FontStyle(FontStyle::registerLanguageList("ja_JP"));
FontStyle kUSStyle = FontStyle(FontStyle::registerLanguageList("en_US"));
FontStyle kZH_HansStyle = FontStyle(FontStyle::registerLanguageList("zh_Hans"));
// All Japanese Hiragana characters.
itemize(collection.get(), "U+3042 U+3044 U+3046 U+3048 U+304A", kUSStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(5, runs[0].end);
EXPECT_EQ(kJAFont, getFontPath(runs[0]));
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
// All Korean Hangul characters.
itemize(collection.get(), "U+B300 U+D55C U+BBFC U+AD6D", kUSStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(4, runs[0].end);
EXPECT_EQ(kKOFont, getFontPath(runs[0]));
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
// All Han characters ja, zh-Hans font having.
// Japanese font should be selected if the specified language is Japanese.
itemize(collection.get(), "U+81ED U+82B1 U+5FCD", kJAStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(3, runs[0].end);
EXPECT_EQ(kJAFont, getFontPath(runs[0]));
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
// Simplified Chinese font should be selected if the specified language is Simplified
// Chinese.
itemize(collection.get(), "U+81ED U+82B1 U+5FCD", kZH_HansStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(3, runs[0].end);
EXPECT_EQ(kZH_HansFont, getFontPath(runs[0]));
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
// Fallbacks to other fonts if there is no glyph in the specified language's
// font. There is no character U+4F60 in Japanese.
itemize(collection.get(), "U+81ED U+4F60 U+5FCD", kJAStyle, &runs);
ASSERT_EQ(3U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(1, runs[0].end);
EXPECT_EQ(kJAFont, getFontPath(runs[0]));
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
EXPECT_EQ(1, runs[1].start);
EXPECT_EQ(2, runs[1].end);
EXPECT_EQ(kZH_HansFont, getFontPath(runs[1]));
EXPECT_FALSE(runs[1].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[1].fakedFont.fakery.isFakeItalic());
EXPECT_EQ(2, runs[2].start);
EXPECT_EQ(3, runs[2].end);
EXPECT_EQ(kJAFont, getFontPath(runs[2]));
EXPECT_FALSE(runs[2].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[2].fakedFont.fakery.isFakeItalic());
// Tone mark.
itemize(collection.get(), "U+4444 U+302D", FontStyle(), &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
EXPECT_EQ(kZH_HansFont, getFontPath(runs[0]));
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
// Both zh-Hant and ja fonts support U+242EE, but zh-Hans doesn't.
// Here, ja and zh-Hant font should have the same score but ja should be selected since it is
// listed before zh-Hant.
itemize(collection.get(), "U+242EE", kZH_HansStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
EXPECT_EQ(kJAFont, getFontPath(runs[0]));
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
}
TEST_F(FontCollectionItemizeTest, itemize_mixed) {
std::unique_ptr<FontCollection> collection = getFontCollection(kTestFontDir, kItemizeFontXml);
std::vector<FontCollection::Run> runs;
FontStyle kUSStyle = FontStyle(FontStyle::registerLanguageList("en_US"));
itemize(collection.get(), "'a' U+4F60 'b' U+4F60 'c'", kUSStyle, &runs);
ASSERT_EQ(5U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(1, runs[0].end);
EXPECT_EQ(kLatinFont, getFontPath(runs[0]));
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
EXPECT_EQ(1, runs[1].start);
EXPECT_EQ(2, runs[1].end);
EXPECT_EQ(kZH_HansFont, getFontPath(runs[1]));
EXPECT_FALSE(runs[1].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[1].fakedFont.fakery.isFakeItalic());
EXPECT_EQ(2, runs[2].start);
EXPECT_EQ(3, runs[2].end);
EXPECT_EQ(kLatinFont, getFontPath(runs[2]));
EXPECT_FALSE(runs[2].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[2].fakedFont.fakery.isFakeItalic());
EXPECT_EQ(3, runs[3].start);
EXPECT_EQ(4, runs[3].end);
EXPECT_EQ(kZH_HansFont, getFontPath(runs[3]));
EXPECT_FALSE(runs[3].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[3].fakedFont.fakery.isFakeItalic());
EXPECT_EQ(4, runs[4].start);
EXPECT_EQ(5, runs[4].end);
EXPECT_EQ(kLatinFont, getFontPath(runs[4]));
EXPECT_FALSE(runs[4].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[4].fakedFont.fakery.isFakeItalic());
}
TEST_F(FontCollectionItemizeTest, itemize_variationSelector) {
std::unique_ptr<FontCollection> collection = getFontCollection(kTestFontDir, kItemizeFontXml);
std::vector<FontCollection::Run> runs;
// A glyph for U+4FAE is provided by both Japanese font and Simplified
// Chinese font. Also a glyph for U+242EE is provided by both Japanese and
// Traditional Chinese font. To avoid effects of device default locale,
// explicitly specify the locale.
FontStyle kZH_HansStyle = FontStyle(FontStyle::registerLanguageList("zh_Hans"));
FontStyle kZH_HantStyle = FontStyle(FontStyle::registerLanguageList("zh_Hant"));
// U+4FAE is available in both zh_Hans and ja font, but U+4FAE,U+FE00 is
// only available in ja font.
itemize(collection.get(), "U+4FAE", kZH_HansStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(1, runs[0].end);
EXPECT_EQ(kZH_HansFont, getFontPath(runs[0]));
itemize(collection.get(), "U+4FAE U+FE00", kZH_HansStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
EXPECT_EQ(kJAFont, getFontPath(runs[0]));
itemize(collection.get(), "U+4FAE U+4FAE U+FE00", kZH_HansStyle, &runs);
ASSERT_EQ(2U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(1, runs[0].end);
EXPECT_EQ(kZH_HansFont, getFontPath(runs[0]));
EXPECT_EQ(1, runs[1].start);
EXPECT_EQ(3, runs[1].end);
EXPECT_EQ(kJAFont, getFontPath(runs[1]));
itemize(collection.get(), "U+4FAE U+4FAE U+FE00 U+4FAE", kZH_HansStyle, &runs);
ASSERT_EQ(3U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(1, runs[0].end);
EXPECT_EQ(kZH_HansFont, getFontPath(runs[0]));
EXPECT_EQ(1, runs[1].start);
EXPECT_EQ(3, runs[1].end);
EXPECT_EQ(kJAFont, getFontPath(runs[1]));
EXPECT_EQ(3, runs[2].start);
EXPECT_EQ(4, runs[2].end);
EXPECT_EQ(kZH_HansFont, getFontPath(runs[2]));
// Validation selector after validation selector.
itemize(collection.get(), "U+4FAE U+FE00 U+FE00", kZH_HansStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(3, runs[0].end);
EXPECT_EQ(kJAFont, getFontPath(runs[1]));
// No font supports U+242EE U+FE0E.
itemize(collection.get(), "U+4FAE U+FE0E", kZH_HansStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
EXPECT_EQ(kZH_HansFont, getFontPath(runs[0]));
// Surrogate pairs handling.
// U+242EE is available in ja font and zh_Hant font.
// U+242EE U+FE00 is available only in ja font.
itemize(collection.get(), "U+242EE", kZH_HantStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
EXPECT_EQ(kZH_HantFont, getFontPath(runs[0]));
itemize(collection.get(), "U+242EE U+FE00", kZH_HantStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(3, runs[0].end);
EXPECT_EQ(kJAFont, getFontPath(runs[0]));
itemize(collection.get(), "U+242EE U+242EE U+FE00", kZH_HantStyle, &runs);
ASSERT_EQ(2U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
EXPECT_EQ(kZH_HantFont, getFontPath(runs[0]));
EXPECT_EQ(2, runs[1].start);
EXPECT_EQ(5, runs[1].end);
EXPECT_EQ(kJAFont, getFontPath(runs[1]));
itemize(collection.get(), "U+242EE U+242EE U+FE00 U+242EE", kZH_HantStyle, &runs);
ASSERT_EQ(3U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
EXPECT_EQ(kZH_HantFont, getFontPath(runs[0]));
EXPECT_EQ(2, runs[1].start);
EXPECT_EQ(5, runs[1].end);
EXPECT_EQ(kJAFont, getFontPath(runs[1]));
EXPECT_EQ(5, runs[2].start);
EXPECT_EQ(7, runs[2].end);
EXPECT_EQ(kZH_HantFont, getFontPath(runs[2]));
// Validation selector after validation selector.
itemize(collection.get(), "U+242EE U+FE00 U+FE00", kZH_HansStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(4, runs[0].end);
EXPECT_EQ(kJAFont, getFontPath(runs[0]));
// No font supports U+242EE U+FE0E
itemize(collection.get(), "U+242EE U+FE0E", kZH_HantStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(3, runs[0].end);
EXPECT_EQ(kZH_HantFont, getFontPath(runs[0]));
// Isolated variation selector supplement.
itemize(collection.get(), "U+FE00", FontStyle(), &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(1, runs[0].end);
EXPECT_TRUE(runs[0].fakedFont.font == nullptr || kLatinFont == getFontPath(runs[0]));
itemize(collection.get(), "U+FE00", kZH_HantStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(1, runs[0].end);
EXPECT_TRUE(runs[0].fakedFont.font == nullptr || kLatinFont == getFontPath(runs[0]));
// First font family (Regular.ttf) supports U+203C but doesn't support U+203C U+FE0F.
// Emoji.ttf font supports U+203C U+FE0F. Emoji.ttf should be selected.
itemize(collection.get(), "U+203C U+FE0F", kZH_HantStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
EXPECT_EQ(kEmojiFont, getFontPath(runs[0]));
// First font family (Regular.ttf) supports U+203C U+FE0E.
itemize(collection.get(), "U+203C U+FE0E", kZH_HantStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
EXPECT_EQ(kLatinFont, getFontPath(runs[0]));
}
TEST_F(FontCollectionItemizeTest, itemize_variationSelectorSupplement) {
std::unique_ptr<FontCollection> collection = getFontCollection(kTestFontDir, kItemizeFontXml);
std::vector<FontCollection::Run> runs;
// A glyph for U+845B is provided by both Japanese font and Simplified
// Chinese font. Also a glyph for U+242EE is provided by both Japanese and
// Traditional Chinese font. To avoid effects of device default locale,
// explicitly specify the locale.
FontStyle kZH_HansStyle = FontStyle(FontStyle::registerLanguageList("zh_Hans"));
FontStyle kZH_HantStyle = FontStyle(FontStyle::registerLanguageList("zh_Hant"));
// U+845B is available in both zh_Hans and ja font, but U+845B,U+E0100 is
// only available in ja font.
itemize(collection.get(), "U+845B", kZH_HansStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(1, runs[0].end);
EXPECT_EQ(kZH_HansFont, getFontPath(runs[0]));
itemize(collection.get(), "U+845B U+E0100", kZH_HansStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(3, runs[0].end);
EXPECT_EQ(kJAFont, getFontPath(runs[0]));
itemize(collection.get(), "U+845B U+845B U+E0100", kZH_HansStyle, &runs);
ASSERT_EQ(2U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(1, runs[0].end);
EXPECT_EQ(kZH_HansFont, getFontPath(runs[0]));
EXPECT_EQ(1, runs[1].start);
EXPECT_EQ(4, runs[1].end);
EXPECT_EQ(kJAFont, getFontPath(runs[1]));
itemize(collection.get(), "U+845B U+845B U+E0100 U+845B", kZH_HansStyle, &runs);
ASSERT_EQ(3U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(1, runs[0].end);
EXPECT_EQ(kZH_HansFont, getFontPath(runs[0]));
EXPECT_EQ(1, runs[1].start);
EXPECT_EQ(4, runs[1].end);
EXPECT_EQ(kJAFont, getFontPath(runs[1]));
EXPECT_EQ(4, runs[2].start);
EXPECT_EQ(5, runs[2].end);
EXPECT_EQ(kZH_HansFont, getFontPath(runs[2]));
// Validation selector after validation selector.
itemize(collection.get(), "U+845B U+E0100 U+E0100", kZH_HansStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(5, runs[0].end);
EXPECT_EQ(kJAFont, getFontPath(runs[0]));
// No font supports U+845B U+E01E0.
itemize(collection.get(), "U+845B U+E01E0", kZH_HansStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(3, runs[0].end);
EXPECT_EQ(kZH_HansFont, getFontPath(runs[0]));
// Isolated variation selector supplement
// Surrogate pairs handling.
// U+242EE is available in ja font and zh_Hant font.
// U+242EE U+E0100 is available only in ja font.
itemize(collection.get(), "U+242EE", kZH_HantStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
EXPECT_EQ(kZH_HantFont, getFontPath(runs[0]));
itemize(collection.get(), "U+242EE U+E0101", kZH_HantStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(4, runs[0].end);
EXPECT_EQ(kJAFont, getFontPath(runs[0]));
itemize(collection.get(), "U+242EE U+242EE U+E0101", kZH_HantStyle, &runs);
ASSERT_EQ(2U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
EXPECT_EQ(kZH_HantFont, getFontPath(runs[0]));
EXPECT_EQ(2, runs[1].start);
EXPECT_EQ(6, runs[1].end);
EXPECT_EQ(kJAFont, getFontPath(runs[1]));
itemize(collection.get(), "U+242EE U+242EE U+E0101 U+242EE", kZH_HantStyle, &runs);
ASSERT_EQ(3U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
EXPECT_EQ(kZH_HantFont, getFontPath(runs[0]));
EXPECT_EQ(2, runs[1].start);
EXPECT_EQ(6, runs[1].end);
EXPECT_EQ(kJAFont, getFontPath(runs[1]));
EXPECT_EQ(6, runs[2].start);
EXPECT_EQ(8, runs[2].end);
EXPECT_EQ(kZH_HantFont, getFontPath(runs[2]));
// Validation selector after validation selector.
itemize(collection.get(), "U+242EE U+E0100 U+E0100", kZH_HantStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(6, runs[0].end);
EXPECT_EQ(kJAFont, getFontPath(runs[0]));
// No font supports U+242EE U+E01E0.
itemize(collection.get(), "U+242EE U+E01E0", kZH_HantStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(4, runs[0].end);
EXPECT_EQ(kZH_HantFont, getFontPath(runs[0]));
// Isolated variation selector supplement.
itemize(collection.get(), "U+E0100", FontStyle(), &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
EXPECT_TRUE(runs[0].fakedFont.font == nullptr || kLatinFont == getFontPath(runs[0]));
itemize(collection.get(), "U+E0100", kZH_HantStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
EXPECT_TRUE(runs[0].fakedFont.font == nullptr || kLatinFont == getFontPath(runs[0]));
}
TEST_F(FontCollectionItemizeTest, itemize_no_crash) {
std::unique_ptr<FontCollection> collection = getFontCollection(kTestFontDir, kItemizeFontXml);
std::vector<FontCollection::Run> runs;
// Broken Surrogate pairs. Check only not crashing.
itemize(collection.get(), "'a' U+D83D 'a'", FontStyle(), &runs);
itemize(collection.get(), "'a' U+DC69 'a'", FontStyle(), &runs);
itemize(collection.get(), "'a' U+D83D U+D83D 'a'", FontStyle(), &runs);
itemize(collection.get(), "'a' U+DC69 U+DC69 'a'", FontStyle(), &runs);
// Isolated variation selector. Check only not crashing.
itemize(collection.get(), "U+FE00 U+FE00", FontStyle(), &runs);
itemize(collection.get(), "U+E0100 U+E0100", FontStyle(), &runs);
itemize(collection.get(), "U+FE00 U+E0100", FontStyle(), &runs);
itemize(collection.get(), "U+E0100 U+FE00", FontStyle(), &runs);
// Tone mark only. Check only not crashing.
itemize(collection.get(), "U+302D", FontStyle(), &runs);
itemize(collection.get(), "U+302D U+302D", FontStyle(), &runs);
// Tone mark and variation selector mixed. Check only not crashing.
itemize(collection.get(), "U+FE00 U+302D U+E0100", FontStyle(), &runs);
}
TEST_F(FontCollectionItemizeTest, itemize_fakery) {
std::unique_ptr<FontCollection> collection = getFontCollection(kTestFontDir, kItemizeFontXml);
std::vector<FontCollection::Run> runs;
FontStyle kJABoldStyle = FontStyle(FontStyle::registerLanguageList("ja_JP"), 0, 7, false);
FontStyle kJAItalicStyle = FontStyle(FontStyle::registerLanguageList("ja_JP"), 0, 5, true);
FontStyle kJABoldItalicStyle =
FontStyle(FontStyle::registerLanguageList("ja_JP"), 0, 7, true);
// Currently there is no italic or bold font for Japanese. FontFakery has
// the differences between desired and actual font style.
// All Japanese Hiragana characters.
itemize(collection.get(), "U+3042 U+3044 U+3046 U+3048 U+304A", kJABoldStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(5, runs[0].end);
EXPECT_EQ(kJAFont, getFontPath(runs[0]));
EXPECT_TRUE(runs[0].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
// All Japanese Hiragana characters.
itemize(collection.get(), "U+3042 U+3044 U+3046 U+3048 U+304A", kJAItalicStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(5, runs[0].end);
EXPECT_EQ(kJAFont, getFontPath(runs[0]));
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeBold());
EXPECT_TRUE(runs[0].fakedFont.fakery.isFakeItalic());
// All Japanese Hiragana characters.
itemize(collection.get(), "U+3042 U+3044 U+3046 U+3048 U+304A", kJABoldItalicStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(5, runs[0].end);
EXPECT_EQ(kJAFont, getFontPath(runs[0]));
EXPECT_TRUE(runs[0].fakedFont.fakery.isFakeBold());
EXPECT_TRUE(runs[0].fakedFont.fakery.isFakeItalic());
}
TEST_F(FontCollectionItemizeTest, itemize_vs_sequence_but_no_base_char) {
// kVSTestFont supports U+717D U+FE02 but doesn't support U+717D.
// kVSTestFont should be selected for U+717D U+FE02 even if it does not support the base code
// point.
const std::string kVSTestFont = kTestFontDir "VarioationSelectorTest-Regular.ttf";
std::vector<android::FontFamily*> families;
FontFamily* family1 = new FontFamily(android::VARIANT_DEFAULT);
family1->addFont(new MinikinFontForTest(kLatinFont));
families.push_back(family1);
FontFamily* family2 = new FontFamily(android::VARIANT_DEFAULT);
family2->addFont(new MinikinFontForTest(kVSTestFont));
families.push_back(family2);
FontCollection collection(families);
std::vector<FontCollection::Run> runs;
itemize(&collection, "U+717D U+FE02", FontStyle(), &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
EXPECT_EQ(kVSTestFont, getFontPath(runs[0]));
family1->Unref();
family2->Unref();
}
TEST_F(FontCollectionItemizeTest, itemize_LanguageScore) {
struct TestCase {
std::string userPreferredLanguages;
std::string fontLanguages;
int selectedFontIndex;
} testCases[] = {
// Single user preferred language.
// Exact match case
{ "en-Latn", "en-Latn,ja-Jpan", 0 },
{ "ja-Jpan", "en-Latn,ja-Jpan", 1 },
{ "en-Latn", "en-Latn,nl-Latn,es-Latn", 0 },
{ "nl-Latn", "en-Latn,nl-Latn,es-Latn", 1 },
{ "es-Latn", "en-Latn,nl-Latn,es-Latn", 2 },
{ "es-Latn", "en-Latn,en-Latn,nl-Latn", 0 },
// Exact script match case
{ "en-Latn", "nl-Latn,be-Latn", 0 },
{ "en-Arab", "nl-Latn,ar-Arab", 1 },
{ "en-Latn", "be-Latn,ar-Arab,bd-Beng", 0 },
{ "en-Arab", "be-Latn,ar-Arab,bd-Beng", 1 },
{ "en-Beng", "be-Latn,ar-Arab,bd-Beng", 2 },
{ "en-Beng", "be-Latn,ar-Beng,bd-Beng", 1 },
{ "zh-Hant", "zh-Hant,zh-Hans", 0 },
{ "zh-Hans", "zh-Hant,zh-Hans", 1 },
// Subscript match case, e.g. Jpan supports Hira.
{ "en-Hira", "ja-Jpan", 0 },
{ "zh-Hani", "zh-Hans,zh-Hant", 0 },
{ "zh-Hani", "zh-Hant,zh-Hans", 0 },
{ "en-Hira", "zh-Hant,ja-Jpan,ja-Jpan", 1 },
// Language match case
{ "ja-Latn", "zh-Latn,ja-Latn", 1 },
{ "zh-Latn", "zh-Latn,ja-Latn", 0 },
{ "ja-Latn", "zh-Latn,ja-Latn", 1 },
{ "ja-Latn", "zh-Latn,ja-Latn,ja-Latn", 1 },
// Mixed case
// Script/subscript match is strongest.
{ "ja-Jpan", "en-Latn,ja-Latn,en-Jpan", 2 },
{ "ja-Hira", "en-Latn,ja-Latn,en-Jpan", 2 },
{ "ja-Hira", "en-Latn,ja-Latn,en-Jpan,en-Jpan", 2 },
// Language match only happens if the script matches.
{ "ja-Hira", "en-Latn,ja-Latn", 0 },
{ "ja-Hira", "en-Jpan,ja-Jpan", 1 },
// Multiple languages.
// Even if all fonts have the same score, use the 2nd language for better selection.
{ "en-Latn,ja-Jpan", "zh-Hant,zh-Hans,ja-Jpan", 2 },
{ "en-Latn,nl-Latn", "es-Latn,be-Latn,nl-Latn", 2 },
{ "en-Latn,br-Latn,nl-Latn", "es-Latn,be-Latn,nl-Latn", 2 },
{ "en-Latn,br-Latn,nl-Latn", "es-Latn,be-Latn,nl-Latn,nl-Latn", 2 },
// Script score.
{ "en-Latn,ja-Jpan", "en-Arab,en-Jpan", 1 },
{ "en-Latn,ja-Jpan", "en-Arab,en-Jpan,en-Jpan", 1 },
// Language match case
{ "en-Latn,ja-Latn", "bd-Latn,ja-Latn", 1 },
{ "en-Latn,ja-Latn", "bd-Latn,ja-Latn,ja-Latn", 1 },
// Language match only happens if the script matches.
{ "en-Latn,ar-Arab", "en-Beng,ar-Arab", 1 },
};
for (auto testCase : testCases) {
SCOPED_TRACE("Test of user preferred languages: \"" + testCase.userPreferredLanguages +
"\" with font languages: " + testCase.fontLanguages);
std::vector<FontFamily*> families;
// Prepare first font which doesn't supports U+9AA8
FontFamily* firstFamily = new FontFamily(
FontStyle::registerLanguageList("und"), 0 /* variant */);
MinikinFont* firstFamilyMinikinFont = new MinikinFontForTest(kNoGlyphFont);
firstFamily->addFont(firstFamilyMinikinFont);
families.push_back(firstFamily);
// Prepare font families
// Each font family is associated with a specified language. All font families except for
// the first font support U+9AA8.
std::unordered_map<MinikinFont*, int> fontLangIdxMap;
const FontLanguages& fontLanguages = registerAndGetFontLanguages(testCase.fontLanguages);
for (size_t i = 0; i < fontLanguages.size(); ++i) {
const FontLanguage& fontLanguage = fontLanguages[i];
FontFamily* family = new FontFamily(
FontStyle::registerLanguageList(fontLanguage.getString()), 0 /* variant */);
MinikinFont* minikin_font = new MinikinFontForTest(kJAFont);
family->addFont(minikin_font);
families.push_back(family);
fontLangIdxMap.insert(std::make_pair(minikin_font, i));
}
FontCollection collection(families);
for (auto family : families) {
family->Unref();
}
// Do itemize
const FontStyle style = FontStyle(
FontStyle::registerLanguageList(testCase.userPreferredLanguages));
std::vector<FontCollection::Run> runs;
itemize(&collection, "U+9AA8", style, &runs);
ASSERT_EQ(1U, runs.size());
ASSERT_NE(nullptr, runs[0].fakedFont.font);
// First family doesn't support U+9AA8 and others support it, so the first font should not
// be selected.
EXPECT_NE(firstFamilyMinikinFont, runs[0].fakedFont.font);
// Lookup used font family by MinikinFont*.
const int usedLangIndex = fontLangIdxMap[runs[0].fakedFont.font];
EXPECT_EQ(testCase.selectedFontIndex, usedLangIndex);
}
}
TEST_F(FontCollectionItemizeTest, itemize_LanguageAndCoverage) {
struct TestCase {
std::string testString;
std::string requestedLanguages;
std::string expectedFont;
} testCases[] = {
// Following test cases verify that following rules in font fallback chain.
// - If the first font in the collection supports the given character or variation sequence,
// it should be selected.
// - If the font doesn't support the given character, variation sequence or its base
// character, it should not be selected.
// - If two or more fonts match the requested languages, the font matches with the highest
// priority language should be selected.
// - If two or more fonts get the same score, the font listed earlier in the XML file
// (here, kItemizeFontXml) should be selected.
// Regardless of language, the first font is always selected if it covers the code point.
{ "'a'", "", kLatinFont},
{ "'a'", "en-Latn", kLatinFont},
{ "'a'", "ja-Jpan", kLatinFont},
{ "'a'", "ja-Jpan,en-Latn", kLatinFont},
{ "'a'", "zh-Hans,zh-Hant,en-Latn,ja-Jpan,fr-Latn", kLatinFont},
// U+81ED is supported by both the ja font and zh-Hans font.
{ "U+81ED", "", kZH_HansFont }, // zh-Hans font is listed before ja font.
{ "U+81ED", "en-Latn", kZH_HansFont }, // zh-Hans font is listed before ja font.
{ "U+81ED", "ja-Jpan", kJAFont },
{ "U+81ED", "zh-Hans", kZH_HansFont },
{ "U+81ED", "ja-Jpan,en-Latn", kJAFont },
{ "U+81ED", "en-Latn,ja-Jpan", kJAFont },
{ "U+81ED", "en-Latn,zh-Hans", kZH_HansFont },
{ "U+81ED", "zh-Hans,en-Latn", kZH_HansFont },
{ "U+81ED", "ja-Jpan,zh-Hans", kJAFont },
{ "U+81ED", "zh-Hans,ja-Jpan", kZH_HansFont },
{ "U+81ED", "en-Latn,zh-Hans,ja-Jpan", kZH_HansFont },
{ "U+81ED", "en-Latn,ja-Jpan,zh-Hans", kJAFont },
{ "U+81ED", "en-Latn,zh-Hans,ja-Jpan", kZH_HansFont },
{ "U+81ED", "ja-Jpan,en-Latn,zh-Hans", kJAFont },
{ "U+81ED", "ja-Jpan,zh-Hans,en-Latn", kJAFont },
{ "U+81ED", "zh-Hans,en-Latn,ja-Jpan", kZH_HansFont },
{ "U+81ED", "zh-Hans,ja-Jpan,en-Latn", kZH_HansFont },
// U+304A is only supported by ja font.
{ "U+304A", "", kJAFont },
{ "U+304A", "ja-Jpan", kJAFont },
{ "U+304A", "zh-Hant", kJAFont },
{ "U+304A", "zh-Hans", kJAFont },
{ "U+304A", "ja-Jpan,zh-Hant", kJAFont },
{ "U+304A", "zh-Hant,ja-Jpan", kJAFont },
{ "U+304A", "zh-Hans,zh-Hant", kJAFont },
{ "U+304A", "zh-Hant,zh-Hans", kJAFont },
{ "U+304A", "zh-Hans,ja-Jpan", kJAFont },
{ "U+304A", "ja-Jpan,zh-Hans", kJAFont },
{ "U+304A", "zh-Hans,ja-Jpan,zh-Hant", kJAFont },
{ "U+304A", "zh-Hans,zh-Hant,ja-Jpan", kJAFont },
{ "U+304A", "ja-Jpan,zh-Hans,zh-Hant", kJAFont },
{ "U+304A", "ja-Jpan,zh-Hant,zh-Hans", kJAFont },
{ "U+304A", "zh-Hant,zh-Hans,ja-Jpan", kJAFont },
{ "U+304A", "zh-Hant,ja-Jpan,zh-Hans", kJAFont },
// U+242EE is supported by both ja font and zh-Hant fonts but not by zh-Hans font.
{ "U+242EE", "", kJAFont }, // ja font is listed before zh-Hant font.
{ "U+242EE", "ja-Jpan", kJAFont },
{ "U+242EE", "zh-Hans", kJAFont },
{ "U+242EE", "zh-Hant", kZH_HantFont },
{ "U+242EE", "ja-Jpan,zh-Hant", kJAFont },
{ "U+242EE", "zh-Hant,ja-Jpan", kZH_HantFont },
{ "U+242EE", "zh-Hans,zh-Hant", kZH_HantFont },
{ "U+242EE", "zh-Hant,zh-Hans", kZH_HantFont },
{ "U+242EE", "zh-Hans,ja-Jpan", kJAFont },
{ "U+242EE", "ja-Jpan,zh-Hans", kJAFont },
{ "U+242EE", "zh-Hans,ja-Jpan,zh-Hant", kJAFont },
{ "U+242EE", "zh-Hans,zh-Hant,ja-Jpan", kZH_HantFont },
{ "U+242EE", "ja-Jpan,zh-Hans,zh-Hant", kJAFont },
{ "U+242EE", "ja-Jpan,zh-Hant,zh-Hans", kJAFont },
{ "U+242EE", "zh-Hant,zh-Hans,ja-Jpan", kZH_HantFont },
{ "U+242EE", "zh-Hant,ja-Jpan,zh-Hans", kZH_HantFont },
// U+9AA8 is supported by all ja-Jpan, zh-Hans, zh-Hant fonts.
{ "U+9AA8", "", kZH_HansFont }, // zh-Hans font is listed before ja and zh-Hant fonts.
{ "U+9AA8", "ja-Jpan", kJAFont },
{ "U+9AA8", "zh-Hans", kZH_HansFont },
{ "U+9AA8", "zh-Hant", kZH_HantFont },
{ "U+9AA8", "ja-Jpan,zh-Hant", kJAFont },
{ "U+9AA8", "zh-Hant,ja-Jpan", kZH_HantFont },
{ "U+9AA8", "zh-Hans,zh-Hant", kZH_HansFont },
{ "U+9AA8", "zh-Hant,zh-Hans", kZH_HantFont },
{ "U+9AA8", "zh-Hans,ja-Jpan", kZH_HansFont },
{ "U+9AA8", "ja-Jpan,zh-Hans", kJAFont },
{ "U+9AA8", "zh-Hans,ja-Jpan,zh-Hant", kZH_HansFont },
{ "U+9AA8", "zh-Hans,zh-Hant,ja-Jpan", kZH_HansFont },
{ "U+9AA8", "ja-Jpan,zh-Hans,zh-Hant", kJAFont },
{ "U+9AA8", "ja-Jpan,zh-Hant,zh-Hans", kJAFont },
{ "U+9AA8", "zh-Hant,zh-Hans,ja-Jpan", kZH_HantFont },
{ "U+9AA8", "zh-Hant,ja-Jpan,zh-Hans", kZH_HantFont },
// U+242EE U+FE00 is supported by ja font but not by zh-Hans or zh-Hant fonts.
{ "U+242EE U+FE00", "", kJAFont },
{ "U+242EE U+FE00", "ja-Jpan", kJAFont },
{ "U+242EE U+FE00", "zh-Hant", kJAFont },
{ "U+242EE U+FE00", "zh-Hans", kJAFont },
{ "U+242EE U+FE00", "ja-Jpan,zh-Hant", kJAFont },
{ "U+242EE U+FE00", "zh-Hant,ja-Jpan", kJAFont },
{ "U+242EE U+FE00", "zh-Hans,zh-Hant", kJAFont },
{ "U+242EE U+FE00", "zh-Hant,zh-Hans", kJAFont },
{ "U+242EE U+FE00", "zh-Hans,ja-Jpan", kJAFont },
{ "U+242EE U+FE00", "ja-Jpan,zh-Hans", kJAFont },
{ "U+242EE U+FE00", "zh-Hans,ja-Jpan,zh-Hant", kJAFont },
{ "U+242EE U+FE00", "zh-Hans,zh-Hant,ja-Jpan", kJAFont },
{ "U+242EE U+FE00", "ja-Jpan,zh-Hans,zh-Hant", kJAFont },
{ "U+242EE U+FE00", "ja-Jpan,zh-Hant,zh-Hans", kJAFont },
{ "U+242EE U+FE00", "zh-Hant,zh-Hans,ja-Jpan", kJAFont },
{ "U+242EE U+FE00", "zh-Hant,ja-Jpan,zh-Hans", kJAFont },
// U+3402 U+E0100 is supported by both zh-Hans and zh-Hant but not by ja font.
{ "U+3402 U+E0100", "", kZH_HansFont }, // zh-Hans font is listed before zh-Hant font.
{ "U+3402 U+E0100", "ja-Jpan", kZH_HansFont }, // zh-Hans font is listed before zh-Hant font.
{ "U+3402 U+E0100", "zh-Hant", kZH_HantFont },
{ "U+3402 U+E0100", "zh-Hans", kZH_HansFont },
{ "U+3402 U+E0100", "ja-Jpan,zh-Hant", kZH_HantFont },
{ "U+3402 U+E0100", "zh-Hant,ja-Jpan", kZH_HantFont },
{ "U+3402 U+E0100", "zh-Hans,zh-Hant", kZH_HansFont },
{ "U+3402 U+E0100", "zh-Hant,zh-Hans", kZH_HantFont },
{ "U+3402 U+E0100", "zh-Hans,ja-Jpan", kZH_HansFont },
{ "U+3402 U+E0100", "ja-Jpan,zh-Hans", kZH_HansFont },
{ "U+3402 U+E0100", "zh-Hans,ja-Jpan,zh-Hant", kZH_HansFont },
{ "U+3402 U+E0100", "zh-Hans,zh-Hant,ja-Jpan", kZH_HansFont },
{ "U+3402 U+E0100", "ja-Jpan,zh-Hans,zh-Hant", kZH_HansFont },
{ "U+3402 U+E0100", "ja-Jpan,zh-Hant,zh-Hans", kZH_HantFont },
{ "U+3402 U+E0100", "zh-Hant,zh-Hans,ja-Jpan", kZH_HantFont },
{ "U+3402 U+E0100", "zh-Hant,ja-Jpan,zh-Hans", kZH_HantFont },
// No font supports U+4444 U+FE00 but only zh-Hans supports its base character U+4444.
{ "U+4444 U+FE00", "", kZH_HansFont },
{ "U+4444 U+FE00", "ja-Jpan", kZH_HansFont },
{ "U+4444 U+FE00", "zh-Hant", kZH_HansFont },
{ "U+4444 U+FE00", "zh-Hans", kZH_HansFont },
{ "U+4444 U+FE00", "ja-Jpan,zh-Hant", kZH_HansFont },
{ "U+4444 U+FE00", "zh-Hant,ja-Jpan", kZH_HansFont },
{ "U+4444 U+FE00", "zh-Hans,zh-Hant", kZH_HansFont },
{ "U+4444 U+FE00", "zh-Hant,zh-Hans", kZH_HansFont },
{ "U+4444 U+FE00", "zh-Hans,ja-Jpan", kZH_HansFont },
{ "U+4444 U+FE00", "ja-Jpan,zh-Hans", kZH_HansFont },
{ "U+4444 U+FE00", "zh-Hans,ja-Jpan,zh-Hant", kZH_HansFont },
{ "U+4444 U+FE00", "zh-Hans,zh-Hant,ja-Jpan", kZH_HansFont },
{ "U+4444 U+FE00", "ja-Jpan,zh-Hans,zh-Hant", kZH_HansFont },
{ "U+4444 U+FE00", "ja-Jpan,zh-Hant,zh-Hans", kZH_HansFont },
{ "U+4444 U+FE00", "zh-Hant,zh-Hans,ja-Jpan", kZH_HansFont },
{ "U+4444 U+FE00", "zh-Hant,ja-Jpan,zh-Hans", kZH_HansFont },
// No font supports U+81ED U+E0100 but ja and zh-Hans support its base character U+81ED.
// zh-Hans font is listed before ja font.
{ "U+81ED U+E0100", "", kZH_HansFont },
{ "U+81ED U+E0100", "ja-Jpan", kJAFont },
{ "U+81ED U+E0100", "zh-Hant", kZH_HansFont },
{ "U+81ED U+E0100", "zh-Hans", kZH_HansFont },
{ "U+81ED U+E0100", "ja-Jpan,zh-Hant", kJAFont },
{ "U+81ED U+E0100", "zh-Hant,ja-Jpan", kJAFont },
{ "U+81ED U+E0100", "zh-Hans,zh-Hant", kZH_HansFont },
{ "U+81ED U+E0100", "zh-Hant,zh-Hans", kZH_HansFont },
{ "U+81ED U+E0100", "zh-Hans,ja-Jpan", kZH_HansFont },
{ "U+81ED U+E0100", "ja-Jpan,zh-Hans", kJAFont },
{ "U+81ED U+E0100", "zh-Hans,ja-Jpan,zh-Hant", kZH_HansFont },
{ "U+81ED U+E0100", "zh-Hans,zh-Hant,ja-Jpan", kZH_HansFont },
{ "U+81ED U+E0100", "ja-Jpan,zh-Hans,zh-Hant", kJAFont },
{ "U+81ED U+E0100", "ja-Jpan,zh-Hant,zh-Hans", kJAFont },
{ "U+81ED U+E0100", "zh-Hant,zh-Hans,ja-Jpan", kZH_HansFont },
{ "U+81ED U+E0100", "zh-Hant,ja-Jpan,zh-Hans", kJAFont },
// No font supports U+9AA8 U+E0100 but all zh-Hans zh-hant ja fonts support its base
// character U+9AA8.
// zh-Hans font is listed before ja and zh-Hant fonts.
{ "U+9AA8 U+E0100", "", kZH_HansFont },
{ "U+9AA8 U+E0100", "ja-Jpan", kJAFont },
{ "U+9AA8 U+E0100", "zh-Hans", kZH_HansFont },
{ "U+9AA8 U+E0100", "zh-Hant", kZH_HantFont },
{ "U+9AA8 U+E0100", "ja-Jpan,zh-Hant", kJAFont },
{ "U+9AA8 U+E0100", "zh-Hant,ja-Jpan", kZH_HantFont },
{ "U+9AA8 U+E0100", "zh-Hans,zh-Hant", kZH_HansFont },
{ "U+9AA8 U+E0100", "zh-Hant,zh-Hans", kZH_HantFont },
{ "U+9AA8 U+E0100", "zh-Hans,ja-Jpan", kZH_HansFont },
{ "U+9AA8 U+E0100", "ja-Jpan,zh-Hans", kJAFont },
{ "U+9AA8 U+E0100", "zh-Hans,ja-Jpan,zh-Hant", kZH_HansFont },
{ "U+9AA8 U+E0100", "zh-Hans,zh-Hant,ja-Jpan", kZH_HansFont },
{ "U+9AA8 U+E0100", "ja-Jpan,zh-Hans,zh-Hant", kJAFont },
{ "U+9AA8 U+E0100", "ja-Jpan,zh-Hant,zh-Hans", kJAFont },
{ "U+9AA8 U+E0100", "zh-Hant,zh-Hans,ja-Jpan", kZH_HantFont },
{ "U+9AA8 U+E0100", "zh-Hant,ja-Jpan,zh-Hans", kZH_HantFont },
// All zh-Hans,zh-Hant,ja fonts support U+35A8 U+E0100 and its base character U+35A8.
// zh-Hans font is listed before ja and zh-Hant fonts.
{ "U+35A8", "", kZH_HansFont },
{ "U+35A8", "ja-Jpan", kJAFont },
{ "U+35A8", "zh-Hans", kZH_HansFont },
{ "U+35A8", "zh-Hant", kZH_HantFont },
{ "U+35A8", "ja-Jpan,zh-Hant", kJAFont },
{ "U+35A8", "zh-Hant,ja-Jpan", kZH_HantFont },
{ "U+35A8", "zh-Hans,zh-Hant", kZH_HansFont },
{ "U+35A8", "zh-Hant,zh-Hans", kZH_HantFont },
{ "U+35A8", "zh-Hans,ja-Jpan", kZH_HansFont },
{ "U+35A8", "ja-Jpan,zh-Hans", kJAFont },
{ "U+35A8", "zh-Hans,ja-Jpan,zh-Hant", kZH_HansFont },
{ "U+35A8", "zh-Hans,zh-Hant,ja-Jpan", kZH_HansFont },
{ "U+35A8", "ja-Jpan,zh-Hans,zh-Hant", kJAFont },
{ "U+35A8", "ja-Jpan,zh-Hant,zh-Hans", kJAFont },
{ "U+35A8", "zh-Hant,zh-Hans,ja-Jpan", kZH_HantFont },
{ "U+35A8", "zh-Hant,ja-Jpan,zh-Hans", kZH_HantFont },
// All zh-Hans,zh-Hant,ja fonts support U+35B6 U+E0100, but zh-Hant and ja fonts support its
// base character U+35B6.
// ja font is listed before zh-Hant font.
{ "U+35B6", "", kJAFont },
{ "U+35B6", "ja-Jpan", kJAFont },
{ "U+35B6", "zh-Hant", kZH_HantFont },
{ "U+35B6", "zh-Hans", kJAFont },
{ "U+35B6", "ja-Jpan,zh-Hant", kJAFont },
{ "U+35B6", "zh-Hant,ja-Jpan", kZH_HantFont },
{ "U+35B6", "zh-Hans,zh-Hant", kZH_HantFont },
{ "U+35B6", "zh-Hant,zh-Hans", kZH_HantFont },
{ "U+35B6", "zh-Hans,ja-Jpan", kJAFont },
{ "U+35B6", "ja-Jpan,zh-Hans", kJAFont },
{ "U+35B6", "zh-Hans,ja-Jpan,zh-Hant", kJAFont },
{ "U+35B6", "zh-Hans,zh-Hant,ja-Jpan", kZH_HantFont },
{ "U+35B6", "ja-Jpan,zh-Hans,zh-Hant", kJAFont },
{ "U+35B6", "ja-Jpan,zh-Hant,zh-Hans", kJAFont },
{ "U+35B6", "zh-Hant,zh-Hans,ja-Jpan", kZH_HantFont },
{ "U+35B6", "zh-Hant,ja-Jpan,zh-Hans", kZH_HantFont },
// All zh-Hans,zh-Hant,ja fonts support U+35C5 U+E0100, but only ja font supports its base
// character U+35C5.
{ "U+35C5", "", kJAFont },
{ "U+35C5", "ja-Jpan", kJAFont },
{ "U+35C5", "zh-Hant", kJAFont },
{ "U+35C5", "zh-Hans", kJAFont },
{ "U+35C5", "ja-Jpan,zh-Hant", kJAFont },
{ "U+35C5", "zh-Hant,ja-Jpan", kJAFont },
{ "U+35C5", "zh-Hans,zh-Hant", kJAFont },
{ "U+35C5", "zh-Hant,zh-Hans", kJAFont },
{ "U+35C5", "zh-Hans,ja-Jpan", kJAFont },
{ "U+35C5", "ja-Jpan,zh-Hans", kJAFont },
{ "U+35C5", "zh-Hans,ja-Jpan,zh-Hant", kJAFont },
{ "U+35C5", "zh-Hans,zh-Hant,ja-Jpan", kJAFont },
{ "U+35C5", "ja-Jpan,zh-Hans,zh-Hant", kJAFont },
{ "U+35C5", "ja-Jpan,zh-Hant,zh-Hans", kJAFont },
{ "U+35C5", "zh-Hant,zh-Hans,ja-Jpan", kJAFont },
{ "U+35C5", "zh-Hant,ja-Jpan,zh-Hans", kJAFont },
// None of ja-Jpan, zh-Hant, zh-Hans font supports U+1F469. Emoji font supports it.
{ "U+1F469", "", kEmojiFont },
{ "U+1F469", "ja-Jpan", kEmojiFont },
{ "U+1F469", "zh-Hant", kEmojiFont },
{ "U+1F469", "zh-Hans", kEmojiFont },
{ "U+1F469", "ja-Jpan,zh-Hant", kEmojiFont },
{ "U+1F469", "zh-Hant,ja-Jpan", kEmojiFont },
{ "U+1F469", "zh-Hans,zh-Hant", kEmojiFont },
{ "U+1F469", "zh-Hant,zh-Hans", kEmojiFont },
{ "U+1F469", "zh-Hans,ja-Jpan", kEmojiFont },
{ "U+1F469", "ja-Jpan,zh-Hans", kEmojiFont },
{ "U+1F469", "zh-Hans,ja-Jpan,zh-Hant", kEmojiFont },
{ "U+1F469", "zh-Hans,zh-Hant,ja-Jpan", kEmojiFont },
{ "U+1F469", "ja-Jpan,zh-Hans,zh-Hant", kEmojiFont },
{ "U+1F469", "ja-Jpan,zh-Hant,zh-Hans", kEmojiFont },
{ "U+1F469", "zh-Hant,zh-Hans,ja-Jpan", kEmojiFont },
{ "U+1F469", "zh-Hant,ja-Jpan,zh-Hans", kEmojiFont },
};
std::unique_ptr<FontCollection> collection = getFontCollection(kTestFontDir, kItemizeFontXml);
for (auto testCase : testCases) {
SCOPED_TRACE("Test for \"" + testCase.testString + "\" with languages " +
testCase.requestedLanguages);
std::vector<FontCollection::Run> runs;
const FontStyle style =
FontStyle(FontStyle::registerLanguageList(testCase.requestedLanguages));
itemize(collection.get(), testCase.testString.c_str(), style, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(testCase.expectedFont, getFontPath(runs[0]));
}
}
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;
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 U+FE0E", kDefaultFontStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
EXPECT_EQ(kTextEmojiFont, getFontPath(runs[0]));
// U+00A9 is a text default emoji which is only available in ColorEmojiFont.ttf.
// ColorEmojiFont.ttf should be selected.
itemize(collection.get(), "U+00AE U+FE0E", kDefaultFontStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
// Text emoji is specified but it is not available. Use color emoji instead.
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 U+FE0E", kDefaultFontStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
EXPECT_EQ(kTextEmojiFont, getFontPath(runs[0]));
// U+2049 is a text default emoji which is not available either TextEmojiFont.ttf or
// ColorEmojiFont.ttf. No font should be selected.
itemize(collection.get(), "U+2049 U+FE0E", kDefaultFontStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, 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 TextEmojifFont.
// TextEmojiFont.ttf sohuld be selected.
itemize(collection.get(), "U+231A U+FE0E", kDefaultFontStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, 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 U+FE0E", kDefaultFontStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
// Text emoji is specified but it is not available. Use color emoji instead.
EXPECT_EQ(kColorEmojiFont, getFontPath(runs[0]));
// U+23E9 is a emoji default emoji which is available in both TextEmojiFont.ttf and
// ColorEmojiFont.ttf. TextEmojiFont.ttf should be selected even if U+23E9 is emoji default
// emoji since U+FE0E is appended.
itemize(collection.get(), "U+23E9 U+FE0E", kDefaultFontStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
EXPECT_EQ(kTextEmojiFont, getFontPath(runs[0]));
// U+23EA is a emoji default emoji but which is not available in either TextEmojiFont.ttf or
// ColorEmojiFont.ttf. No font should be selected.
itemize(collection.get(), "U+23EA U+FE0E", kDefaultFontStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
EXPECT_TRUE(runs[0].fakedFont.font == NULL || kNoGlyphFont == getFontPath(runs[0]));
// U+26FA U+FE0E is specified but ColorTextMixedEmojiFont has a variation sequence U+26F9 U+FE0F
// in its cmap, so ColorTextMixedEmojiFont should be selected instaed of ColorEmojiFont.
itemize(collection.get(), "U+26FA U+FE0E", kDefaultFontStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
EXPECT_EQ(kMixedEmojiFont, getFontPath(runs[0]));
}
TEST_F(FontCollectionItemizeTest, itemize_emojiSelection_withFE0F) {
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 available only in TextEmojiFont.ttf.
// TextEmojiFont.ttf shoudl be selected.
itemize(collection.get(), "U+00A9 U+FE0F", kDefaultFontStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
// Color emoji is specified but it is not available. Use text representaion instead.
EXPECT_EQ(kTextEmojiFont, getFontPath(runs[0]));
// U+00AE is a text default emoji which is available only in ColorEmojiFont.ttf.
// ColorEmojiFont.ttf should be selected.
itemize(collection.get(), "U+00AE U+FE0F", kDefaultFontStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, 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. ColorEmojiFont.ttf should be selected even if U+203C is a text default
// emoji since U+FF0F is appended.
itemize(collection.get(), "U+203C U+FE0F", kDefaultFontStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
EXPECT_EQ(kColorEmojiFont, 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 U+FE0F", kDefaultFontStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, 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 U+FE0F", kDefaultFontStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
// Color emoji is specified but it is not available. Use text representation instead.
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 U+FE0F", kDefaultFontStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, 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.ttf should be selected.
itemize(collection.get(), "U+23E9 U+FE0F", kDefaultFontStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
EXPECT_EQ(kColorEmojiFont, getFontPath(runs[0]));
// U+23EA is a emoji default emoji which is not available in either TextEmojiFont.ttf or
// ColorEmojiFont.ttf. No font should be selected.
itemize(collection.get(), "U+23EA U+FE0F", kDefaultFontStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
EXPECT_TRUE(runs[0].fakedFont.font == NULL || kNoGlyphFont == getFontPath(runs[0]));
// U+26F9 U+FE0F is specified but ColorTextMixedEmojiFont has a variation sequence U+26F9 U+FE0F
// in its cmap, so ColorTextMixedEmojiFont should be selected instaed of ColorEmojiFont.
itemize(collection.get(), "U+26F9 U+FE0F", kDefaultFontStyle, &runs);
ASSERT_EQ(1U, runs.size());
EXPECT_EQ(0, runs[0].start);
EXPECT_EQ(2, runs[0].end);
EXPECT_EQ(kMixedEmojiFont, getFontPath(runs[0]));
}