mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
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
This commit is contained in:
parent
a69ca2e172
commit
6f9966ea7c
@ -67,6 +67,16 @@ private:
|
||||
|
||||
FontFamily* getFamilyForChar(uint32_t ch, uint32_t vs, uint32_t langListId, int variant) const;
|
||||
|
||||
uint32_t calcFamilyScore(uint32_t ch, uint32_t vs, int variant, uint32_t langListId,
|
||||
FontFamily* fontFamily) const;
|
||||
|
||||
uint32_t calcCoverageScore(uint32_t ch, uint32_t vs, FontFamily* fontFamily) const;
|
||||
|
||||
static uint32_t calcLanguageMatchingScore(uint32_t userLangListId,
|
||||
const FontFamily& fontFamily);
|
||||
|
||||
static uint32_t calcVariantMatchingScore(int variant, const FontFamily& fontFamily);
|
||||
|
||||
// static for allocating unique id's
|
||||
static uint32_t sNextId;
|
||||
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
|
||||
#define LOG_TAG "Minikin"
|
||||
#include <cutils/log.h>
|
||||
#include <algorithm>
|
||||
|
||||
#include "unicode/unistr.h"
|
||||
#include "unicode/unorm2.h"
|
||||
@ -103,29 +104,135 @@ FontCollection::~FontCollection() {
|
||||
}
|
||||
}
|
||||
|
||||
// Special scores for the font fallback.
|
||||
const uint32_t kUnsupportedFontScore = 0;
|
||||
const uint32_t kFirstFontScore = UINT32_MAX;
|
||||
|
||||
// Calculates a font score.
|
||||
// The score of the font family is based on three subscores.
|
||||
// - Coverage Score: How well the font family covers the given character or variation sequence.
|
||||
// - Language Score: How well the font family is appropriate for the language.
|
||||
// - Variant Score: Whether the font family matches the variant. Note that this variant is not the
|
||||
// one in BCP47. This is our own font variant (e.g., elegant, compact).
|
||||
//
|
||||
// Then, there is a priority for these three subscores as follow:
|
||||
// Coverage Score > Language Score > Variant Score
|
||||
// The returned score reflects this priority order.
|
||||
//
|
||||
// Note that there are two special scores.
|
||||
// - kUnsupportedFontScore: When the font family doesn't support the variation sequence or even its
|
||||
// base character.
|
||||
// - kFirstFontScore: When the font is the first font family in the collection and it supports the
|
||||
// given character or variation sequence.
|
||||
uint32_t FontCollection::calcFamilyScore(uint32_t ch, uint32_t vs, int variant, uint32_t langListId,
|
||||
FontFamily* fontFamily) const {
|
||||
|
||||
const uint32_t coverageScore = calcCoverageScore(ch, vs, fontFamily);
|
||||
if (coverageScore == kFirstFontScore || coverageScore == kUnsupportedFontScore) {
|
||||
// No need to calculate other scores.
|
||||
return coverageScore;
|
||||
}
|
||||
|
||||
const uint32_t languageScore = calcLanguageMatchingScore(langListId, *fontFamily);
|
||||
const uint32_t variantScore = calcVariantMatchingScore(variant, *fontFamily);
|
||||
|
||||
// Subscores are encoded into 31 bits representation to meet the subscore priority.
|
||||
// The highest 2 bits are for coverage score, then following 28 bits are for language score,
|
||||
// then the last 1 bit is for variant score.
|
||||
return coverageScore << 29 | languageScore << 1 | variantScore;
|
||||
}
|
||||
|
||||
// Calculates a font score based on variation sequence coverage.
|
||||
// - Returns kUnsupportedFontScore if the font doesn't support the variation sequence or its base
|
||||
// character.
|
||||
// - Returns kFirstFontScore if the font family is the first font family in the collection and it
|
||||
// supports the given character or variation sequence.
|
||||
// - Returns 3 if the font family supports the variation sequence.
|
||||
// - Returns 2 if the vs is a color variation selector (U+FE0F) and if the font is an emoji font.
|
||||
// - Returns 2 if the vs is a text variation selector (U+FE0E) and if the font is not an emoji font.
|
||||
// - Returns 1 if the variation selector is not specified or if the font family only supports the
|
||||
// variation sequence's base character.
|
||||
uint32_t FontCollection::calcCoverageScore(uint32_t ch, uint32_t vs, FontFamily* fontFamily) const {
|
||||
const bool hasVSGlyph = (vs != 0) && fontFamily->hasVariationSelector(ch, vs);
|
||||
if (!hasVSGlyph && !fontFamily->getCoverage()->get(ch)) {
|
||||
// The font doesn't support either variation sequence or even the base character.
|
||||
return kUnsupportedFontScore;
|
||||
}
|
||||
|
||||
if ((vs == 0 || hasVSGlyph) && mFamilies[0] == fontFamily) {
|
||||
// If the first font family supports the given character or variation sequence, always use
|
||||
// it.
|
||||
return kFirstFontScore;
|
||||
}
|
||||
|
||||
if (vs == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (hasVSGlyph) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (vs == 0xFE0F || vs == 0xFE0E) {
|
||||
// TODO use all language in the list.
|
||||
const FontLanguage lang = FontLanguageListCache::getById(fontFamily->langId())[0];
|
||||
const bool hasEmojiFlag = lang.hasEmojiFlag();
|
||||
if (vs == 0xFE0F) {
|
||||
return hasEmojiFlag ? 2 : 1;
|
||||
} else { // vs == 0xFE0E
|
||||
return hasEmojiFlag ? 1 : 2;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Calculates font scores based on the script matching and primary langauge matching.
|
||||
//
|
||||
// If the font's script doesn't support the requested script, the font gets a score of 0. If the
|
||||
// font's script supports the requested script and the font has the same primary language as the
|
||||
// requested one, the font gets a score of 2. If the font's script supports the requested script
|
||||
// but the primary language is different from the requested one, the font gets a score of 1.
|
||||
//
|
||||
// If two languages in the requested list have the same language score, the font matching with
|
||||
// higher priority language gets a higher score. For example, in the case the user requested
|
||||
// language list is "ja-Jpan,en-Latn". The score of for the font of "ja-Jpan" gets a higher score
|
||||
// than the font of "en-Latn".
|
||||
//
|
||||
// To achieve the above two conditions, the language score is determined as follows:
|
||||
// LanguageScore = s(0) * 3^(m - 1) + s(1) * 3^(m - 2) + ... + s(m - 2) * 3 + s(m - 1)
|
||||
// Here, m is the maximum number of languages to be compared, and s(i) is the i-th language's
|
||||
// matching score. The possible values of s(i) are 0, 1 and 2.
|
||||
uint32_t FontCollection::calcLanguageMatchingScore(
|
||||
uint32_t userLangListId, const FontFamily& fontFamily) {
|
||||
const FontLanguages& langList = FontLanguageListCache::getById(userLangListId);
|
||||
// TODO use all language in the list.
|
||||
FontLanguage fontLanguage = FontLanguageListCache::getById(fontFamily.langId())[0];
|
||||
|
||||
const size_t maxCompareNum = std::min(langList.size(), FONT_LANGUAGES_LIMIT);
|
||||
uint32_t score = fontLanguage.getScoreFor(langList[0]); // maxCompareNum can't be zero.
|
||||
for (size_t i = 1; i < maxCompareNum; ++i) {
|
||||
score = score * 3u + fontLanguage.getScoreFor(langList[i]);
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
// Calculates a font score based on variant ("compact" or "elegant") matching.
|
||||
// - Returns 1 if the font doesn't have variant or the variant matches with the text style.
|
||||
// - No score if the font has a variant but it doesn't match with the text style.
|
||||
uint32_t FontCollection::calcVariantMatchingScore(int variant, const FontFamily& fontFamily) {
|
||||
return (fontFamily.variant() == 0 || fontFamily.variant() == variant) ? 1 : 0;
|
||||
}
|
||||
|
||||
// Implement heuristic for choosing best-match font. Here are the rules:
|
||||
// 1. If first font in the collection has the character, it wins.
|
||||
// 2. If a font matches language, it gets a score of 2.
|
||||
// 3. Matching the "compact" or "elegant" variant adds one to the score.
|
||||
// 4. If there is a variation selector and a font supports the complete variation sequence, we add
|
||||
// 8 to the score.
|
||||
// 5. If there is a color variation selector (U+FE0F), we add 4 to the score if the font is an emoji
|
||||
// font. This additional score of 4 is only given if the base character is supported in the font,
|
||||
// but not the whole variation sequence.
|
||||
// 6. If there is a text variation selector (U+FE0E), we add 4 to the score if the font is not an
|
||||
// emoji font. This additional score of 4 is only given if the base character is supported in the
|
||||
// font, but not the whole variation sequence.
|
||||
// 7. Highest score wins, with ties resolved to the first font.
|
||||
// 2. Calculate a score for the font family. See comments in calcFamilyScore for the detail.
|
||||
// 3. Highest score wins, with ties resolved to the first font.
|
||||
FontFamily* FontCollection::getFamilyForChar(uint32_t ch, uint32_t vs,
|
||||
uint32_t langListId, int variant) const {
|
||||
if (ch >= mMaxChar) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const FontLanguages& langList = FontLanguageListCache::getById(langListId);
|
||||
// TODO: use all languages in langList.
|
||||
const FontLanguage lang = (langList.size() == 0) ? FontLanguage() : langList[0];
|
||||
|
||||
// Even if the font supports variation sequence, mRanges isn't aware of the base character of
|
||||
// the sequence. Search all FontFamilies if variation sequence is specified.
|
||||
// TODO: Always use mRanges for font search.
|
||||
@ -141,40 +248,19 @@ FontFamily* FontCollection::getFamilyForChar(uint32_t ch, uint32_t vs,
|
||||
ALOGD("querying range %zd:%zd\n", range.start, range.end);
|
||||
#endif
|
||||
FontFamily* bestFamily = nullptr;
|
||||
int bestScore = -1;
|
||||
uint32_t bestScore = kUnsupportedFontScore;
|
||||
for (size_t i = range.start; i < range.end; i++) {
|
||||
FontFamily* family = familyVec[i];
|
||||
const bool hasVSGlyph = (vs != 0) && family->hasVariationSelector(ch, vs);
|
||||
if (hasVSGlyph || family->getCoverage()->get(ch)) {
|
||||
if ((vs == 0 || hasVSGlyph) && mFamilies[0] == family) {
|
||||
// If the first font family in collection supports the given character or sequence,
|
||||
// always use it.
|
||||
return family;
|
||||
}
|
||||
|
||||
// TODO use all language in the list.
|
||||
FontLanguage fontLang = FontLanguageListCache::getById(family->langId())[0];
|
||||
int score = lang.match(fontLang) * 2;
|
||||
if (family->variant() == 0 || family->variant() == variant) {
|
||||
score++;
|
||||
}
|
||||
if (hasVSGlyph) {
|
||||
score += 8;
|
||||
} else if (((vs == 0xFE0F) && fontLang.hasEmojiFlag()) ||
|
||||
((vs == 0xFE0E) && !fontLang.hasEmojiFlag())) {
|
||||
score += 4;
|
||||
}
|
||||
if (score > bestScore) {
|
||||
bestScore = score;
|
||||
bestFamily = family;
|
||||
}
|
||||
const uint32_t score = calcFamilyScore(ch, vs, variant, langListId, family);
|
||||
if (score == kFirstFontScore) {
|
||||
// If the first font family supports the given character or variation sequence, always
|
||||
// use it.
|
||||
return family;
|
||||
}
|
||||
if (score > bestScore) {
|
||||
bestScore = score;
|
||||
bestFamily = family;
|
||||
}
|
||||
}
|
||||
if (bestFamily == nullptr && vs != 0) {
|
||||
// If no fonts support the codepoint and variation selector pair,
|
||||
// fallback to select a font family that supports just the base
|
||||
// character, ignoring the variation selector.
|
||||
return getFamilyForChar(ch, 0, langListId, variant);
|
||||
}
|
||||
if (bestFamily == nullptr && !mFamilyVec.empty()) {
|
||||
UErrorCode errorCode = U_ZERO_ERROR;
|
||||
|
||||
@ -115,21 +115,29 @@ std::string FontLanguage::getString() const {
|
||||
return std::string(buf, i);
|
||||
}
|
||||
|
||||
bool FontLanguage::isEqualScript(const FontLanguage other) const {
|
||||
bool FontLanguage::isEqualScript(const FontLanguage& other) const {
|
||||
return other.mScript == mScript;
|
||||
}
|
||||
|
||||
bool FontLanguage::supportsScript(uint8_t requestedBits) const {
|
||||
return requestedBits != 0 && (mSubScriptBits & requestedBits) == requestedBits;
|
||||
}
|
||||
|
||||
bool FontLanguage::supportsHbScript(hb_script_t script) const {
|
||||
static_assert(SCRIPT_TAG('J', 'p', 'a', 'n') == HB_TAG('J', 'p', 'a', 'n'),
|
||||
"The Minikin script and HarfBuzz hb_script_t have different encodings.");
|
||||
if (script == mScript) return true;
|
||||
uint8_t requestedBits = scriptToSubScriptBits(script);
|
||||
return requestedBits != 0 && (mSubScriptBits & requestedBits) == requestedBits;
|
||||
return supportsScript(scriptToSubScriptBits(script));
|
||||
}
|
||||
|
||||
int FontLanguage::match(const FontLanguage other) const {
|
||||
// TODO: Use script for matching.
|
||||
return *this == other;
|
||||
int FontLanguage::getScoreFor(const FontLanguage other) const {
|
||||
if (isUnsupported() || other.isUnsupported()) {
|
||||
return 0;
|
||||
} else if (isEqualScript(other) || supportsScript(other.mSubScriptBits)) {
|
||||
return mLanguage == other.mLanguage ? 2 : 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#undef SCRIPT_TAG
|
||||
|
||||
@ -36,7 +36,7 @@ public:
|
||||
FontLanguage(const char* buf, size_t length);
|
||||
|
||||
bool operator==(const FontLanguage other) const {
|
||||
return !isUnsupported() && isEqualScript(other) && isEqualLanguage(other);
|
||||
return !isUnsupported() && isEqualScript(other) && mLanguage == other.mLanguage;
|
||||
}
|
||||
|
||||
bool operator!=(const FontLanguage other) const {
|
||||
@ -46,8 +46,7 @@ public:
|
||||
bool isUnsupported() const { return mLanguage == 0ul; }
|
||||
bool hasEmojiFlag() const { return mSubScriptBits & kEmojiFlag; }
|
||||
|
||||
bool isEqualLanguage(const FontLanguage other) const { return mLanguage == other.mLanguage; }
|
||||
bool isEqualScript(const FontLanguage other) const;
|
||||
bool isEqualScript(const FontLanguage& other) const;
|
||||
|
||||
// Returns true if this script supports the given script. For example, ja-Jpan supports Hira,
|
||||
// ja-Hira doesn't support Jpan.
|
||||
@ -55,8 +54,8 @@ public:
|
||||
|
||||
std::string getString() const;
|
||||
|
||||
// 0 = no match, 1 = language matches
|
||||
int match(const FontLanguage other) const;
|
||||
// 0 = no match, 1 = script match, 2 = script and primary language match.
|
||||
int getScoreFor(const FontLanguage other) const;
|
||||
|
||||
uint64_t getIdentifier() const { return (uint64_t)mScript << 32 | (uint64_t)mLanguage; }
|
||||
|
||||
@ -80,8 +79,11 @@ private:
|
||||
uint8_t mSubScriptBits;
|
||||
|
||||
static uint8_t scriptToSubScriptBits(uint32_t script);
|
||||
bool supportsScript(uint8_t requestedBits) const;
|
||||
};
|
||||
|
||||
// Due to the limit of font fallback cost calculation, we can't use anything more than 17 languages.
|
||||
const size_t FONT_LANGUAGES_LIMIT = 17;
|
||||
typedef std::vector<FontLanguage> FontLanguages;
|
||||
|
||||
} // namespace android
|
||||
|
||||
@ -92,15 +92,20 @@ static FontLanguages constructFontLanguages(const std::string& input) {
|
||||
uint64_t identifier = lang.getIdentifier();
|
||||
if (!lang.isUnsupported() && seen.count(identifier) == 0) {
|
||||
result.push_back(lang);
|
||||
if (result.size() == FONT_LANGUAGES_LIMIT) {
|
||||
break;
|
||||
}
|
||||
seen.insert(identifier);
|
||||
}
|
||||
}
|
||||
locale.assign(input, currentIdx, input.size() - currentIdx);
|
||||
size_t length = toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, locale);
|
||||
FontLanguage lang(langTag, length);
|
||||
uint64_t identifier = lang.getIdentifier();
|
||||
if (!lang.isUnsupported() && seen.count(identifier) == 0) {
|
||||
result.push_back(lang);
|
||||
if (result.size() < FONT_LANGUAGES_LIMIT) {
|
||||
locale.assign(input, currentIdx, input.size() - currentIdx);
|
||||
size_t length = toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, locale);
|
||||
FontLanguage lang(langTag, length);
|
||||
uint64_t identifier = lang.getIdentifier();
|
||||
if (!lang.isUnsupported() && seen.count(identifier) == 0) {
|
||||
result.push_back(lang);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -16,18 +16,23 @@
|
||||
|
||||
#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";
|
||||
@ -68,6 +73,12 @@ const std::string& getFontPath(const FontCollection::Run& run) {
|
||||
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;
|
||||
@ -451,7 +462,7 @@ TEST_F(FontCollectionItemizeTest, itemize_variationSelector) {
|
||||
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 supports U+203C U+FE0F. Emoji.ttf should be selected.
|
||||
// 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);
|
||||
@ -684,6 +695,430 @@ TEST_F(FontCollectionItemizeTest, itemize_vs_sequence_but_no_base_char) {
|
||||
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;
|
||||
|
||||
@ -14,6 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef MINIKIN_TEST_MINIKIN_FONT_FOR_TEST_H
|
||||
#define MINIKIN_TEST_MINIKIN_FONT_FOR_TEST_H
|
||||
|
||||
#include <minikin/MinikinFont.h>
|
||||
|
||||
class SkTypeface;
|
||||
@ -35,3 +38,5 @@ private:
|
||||
SkTypeface *mTypeface;
|
||||
const std::string mFontPath;
|
||||
};
|
||||
|
||||
#endif // MINIKIN_TEST_MINIKIN_FONT_FOR_TEST_H
|
||||
|
||||
Binary file not shown.
@ -30,6 +30,10 @@
|
||||
<GlyphID id="10" name="U+82B1"/>
|
||||
<GlyphID id="11" name="U+242EE"/>
|
||||
<GlyphID id="12" name="U+1F470"/>
|
||||
<GlyphID id="13" name="U+9AA8"/>
|
||||
<GlyphID id="14" name="U+35A8"/>
|
||||
<GlyphID id="15" name="U+35B6"/>
|
||||
<GlyphID id="16" name="U+35C5"/>
|
||||
</GlyphOrder>
|
||||
|
||||
<head>
|
||||
@ -159,6 +163,10 @@
|
||||
<mtx name="U+82B1" width="500" lsb="93"/>
|
||||
<mtx name="U+242EE" width="500" lsb="93"/>
|
||||
<mtx name="U+1F470" width="500" lsb="93"/>
|
||||
<mtx name="U+9AA8" width="500" lsb="93"/>
|
||||
<mtx name="U+35A8" width="500" lsb="93"/>
|
||||
<mtx name="U+35B6" width="500" lsb="93"/>
|
||||
<mtx name="U+35C5" width="500" lsb="93"/>
|
||||
</hmtx>
|
||||
|
||||
<cmap>
|
||||
@ -176,6 +184,10 @@
|
||||
<map code="0x82B1" name="U+82B1" />
|
||||
<map code="0x242EE" name="U+242EE" />
|
||||
<map code="0x1F470" name="U+242EE" />
|
||||
<map code="0x9AA8" name="U+9AA8" />
|
||||
<map code="0x35A8" name="U+35A8" />
|
||||
<map code="0x35B6" name="U+35B6" />
|
||||
<map code="0x35C5" name="U+35C5" />
|
||||
</cmap_format_12>
|
||||
<cmap_format_14 format="14" platformID="0" platEncID="5" length="40" numVarSelectorRecords="3">
|
||||
<map uvs="0xFE00" uv="0x4FAE" name="None" />
|
||||
@ -183,6 +195,9 @@
|
||||
<map uvs="0xE0100" uv="0x845B" name="None" />
|
||||
<map uvs="0xE0100" uv="0x242EE" name="None" />
|
||||
<map uvs="0xE0101" uv="0x242EE" name="None" />
|
||||
<map uvs="0xE0100" uv="0x35A8" name="None" />
|
||||
<map uvs="0xE0100" uv="0x35B6" name="None" />
|
||||
<map uvs="0xE0100" uv="0x35C5" name="None" />
|
||||
</cmap_format_14>
|
||||
</cmap>
|
||||
|
||||
@ -235,6 +250,18 @@
|
||||
<TTGlyph name="U+1F470" xMin="0" yMin="0" xMax="0" yMax="0">
|
||||
<contour></contour><instructions><assembly></assembly></instructions>
|
||||
</TTGlyph>
|
||||
<TTGlyph name="U+9AA8" xMin="0" yMin="0" xMax="0" yMax="0">
|
||||
<contour></contour><instructions><assembly></assembly></instructions>
|
||||
</TTGlyph>
|
||||
<TTGlyph name="U+35A8" xMin="0" yMin="0" xMax="0" yMax="0">
|
||||
<contour></contour><instructions><assembly></assembly></instructions>
|
||||
</TTGlyph>
|
||||
<TTGlyph name="U+35B6" xMin="0" yMin="0" xMax="0" yMax="0">
|
||||
<contour></contour><instructions><assembly></assembly></instructions>
|
||||
</TTGlyph>
|
||||
<TTGlyph name="U+35C5" xMin="0" yMin="0" xMax="0" yMax="0">
|
||||
<contour></contour><instructions><assembly></assembly></instructions>
|
||||
</TTGlyph>
|
||||
</glyf>
|
||||
|
||||
<name>
|
||||
|
||||
Binary file not shown.
@ -26,6 +26,11 @@
|
||||
<GlyphID id="6" name="U+4F60"/>
|
||||
<GlyphID id="7" name="U+4FAE"/>
|
||||
<GlyphID id="8" name="U+845B"/>
|
||||
<GlyphID id="9" name="U+3402"/>
|
||||
<GlyphID id="10" name="U+9AA8"/>
|
||||
<GlyphID id="11" name="U+35A8"/>
|
||||
<GlyphID id="12" name="U+35B6_VS17"/>
|
||||
<GlyphID id="13" name="U+35C5_VS17"/>
|
||||
</GlyphOrder>
|
||||
|
||||
<head>
|
||||
@ -151,6 +156,11 @@
|
||||
<mtx name="U+4F60" width="500" lsb="93"/>
|
||||
<mtx name="U+4FAE" width="500" lsb="93"/>
|
||||
<mtx name="U+845B" width="500" lsb="93"/>
|
||||
<mtx name="U+3402" width="500" lsb="93"/>
|
||||
<mtx name="U+9AA8" width="500" lsb="93"/>
|
||||
<mtx name="U+35A8" width="500" lsb="93"/>
|
||||
<mtx name="U+35B6_VS17" width="500" lsb="93"/>
|
||||
<mtx name="U+35C5_VS17" width="500" lsb="93"/>
|
||||
</hmtx>
|
||||
|
||||
<cmap>
|
||||
@ -164,7 +174,16 @@
|
||||
<map code="0x4F60" name="U+4F60" />
|
||||
<map code="0x4FAE" name="U+4FAE" />
|
||||
<map code="0x845B" name="U+845B" />
|
||||
<map code="0x3402" name="U+3402" />
|
||||
<map code="0x9AA8" name="U+9AA8" />
|
||||
<map code="0x35A8" name="U+35A8" />
|
||||
</cmap_format_12>
|
||||
<cmap_format_14 format="14" platformID="0" platEncID="5" length="40" numVarSelectorRecords="3">
|
||||
<map uvs="0xE0100" uv="0x3402" name="None"/>
|
||||
<map uvs="0xE0100" uv="0x35A8" name="None"/>
|
||||
<map uvs="0xE0100" uv="0x35B6" name="U+35B6_VS17"/>
|
||||
<map uvs="0xE0100" uv="0x35C5" name="U+35C5_VS17"/>
|
||||
</cmap_format_14>
|
||||
</cmap>
|
||||
|
||||
<loca>
|
||||
@ -204,6 +223,21 @@
|
||||
<TTGlyph name="U+845B" xMin="0" yMin="0" xMax="0" yMax="0">
|
||||
<contour></contour><instructions><assembly></assembly></instructions>
|
||||
</TTGlyph>
|
||||
<TTGlyph name="U+3402" xMin="0" yMin="0" xMax="0" yMax="0">
|
||||
<contour></contour><instructions><assembly></assembly></instructions>
|
||||
</TTGlyph>
|
||||
<TTGlyph name="U+9AA8" xMin="0" yMin="0" xMax="0" yMax="0">
|
||||
<contour></contour><instructions><assembly></assembly></instructions>
|
||||
</TTGlyph>
|
||||
<TTGlyph name="U+35A8" xMin="0" yMin="0" xMax="0" yMax="0">
|
||||
<contour></contour><instructions><assembly></assembly></instructions>
|
||||
</TTGlyph>
|
||||
<TTGlyph name="U+35B6_VS17" xMin="0" yMin="0" xMax="0" yMax="0">
|
||||
<contour></contour><instructions><assembly></assembly></instructions>
|
||||
</TTGlyph>
|
||||
<TTGlyph name="U+35C5_VS17" xMin="0" yMin="0" xMax="0" yMax="0">
|
||||
<contour></contour><instructions><assembly></assembly></instructions>
|
||||
</TTGlyph>
|
||||
</glyf>
|
||||
|
||||
<name>
|
||||
|
||||
Binary file not shown.
@ -19,6 +19,11 @@
|
||||
<!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
|
||||
<GlyphID id="0" name=".notdef"/>
|
||||
<GlyphID id="1" name="U+242EE"/>
|
||||
<GlyphID id="2" name="U+3402"/>
|
||||
<GlyphID id="3" name="U+9AA8"/>
|
||||
<GlyphID id="4" name="U+35A8"/>
|
||||
<GlyphID id="5" name="U+35B6"/>
|
||||
<GlyphID id="6" name="U+35C5_VS17"/>
|
||||
</GlyphOrder>
|
||||
|
||||
<head>
|
||||
@ -137,13 +142,28 @@
|
||||
<hmtx>
|
||||
<mtx name=".notdef" width="500" lsb="93"/>
|
||||
<mtx name="U+242EE" width="500" lsb="93"/>
|
||||
<mtx name="U+3402" width="500" lsb="93"/>
|
||||
<mtx name="U+9AA8" width="500" lsb="93"/>
|
||||
<mtx name="U+35A8" width="500" lsb="93"/>
|
||||
<mtx name="U+35B6" width="500" lsb="93"/>
|
||||
<mtx name="U+35C5_VS17" width="500" lsb="93"/>
|
||||
</hmtx>
|
||||
|
||||
<cmap>
|
||||
<tableVersion version="0"/>
|
||||
<cmap_format_12 format="12" reserved="0" length="10" nGroups="1" platformID="3" platEncID="1" language="0">
|
||||
<map code="0x242EE" name="U+242EE" />
|
||||
<map code="0x3402" name="U+3402" />
|
||||
<map code="0x9AA8" name="U+9AA8" />
|
||||
<map code="0x35A8" name="U+35A8" />
|
||||
<map code="0x35B6" name="U+35B6" />
|
||||
</cmap_format_12>
|
||||
<cmap_format_14 format="14" platformID="0" platEncID="5" length="40" numVarSelectorRecords="3">
|
||||
<map uvs="0xE0100" uv="0x3402" name="None" />
|
||||
<map uvs="0xE0100" uv="0x35A8" name="None" />
|
||||
<map uvs="0xE0100" uv="0x35B6" name="None" />
|
||||
<map uvs="0xE0100" uv="0x35C5" name="U+35C5_VS17" />
|
||||
</cmap_format_14>
|
||||
</cmap>
|
||||
|
||||
<loca>
|
||||
@ -161,6 +181,21 @@
|
||||
<TTGlyph name="U+242EE" xMin="0" yMin="0" xMax="0" yMax="0">
|
||||
<contour></contour><instructions><assembly></assembly></instructions>
|
||||
</TTGlyph>
|
||||
<TTGlyph name="U+3402" xMin="0" yMin="0" xMax="0" yMax="0">
|
||||
<contour></contour><instructions><assembly></assembly></instructions>
|
||||
</TTGlyph>
|
||||
<TTGlyph name="U+9AA8" xMin="0" yMin="0" xMax="0" yMax="0">
|
||||
<contour></contour><instructions><assembly></assembly></instructions>
|
||||
</TTGlyph>
|
||||
<TTGlyph name="U+35A8" xMin="0" yMin="0" xMax="0" yMax="0">
|
||||
<contour></contour><instructions><assembly></assembly></instructions>
|
||||
</TTGlyph>
|
||||
<TTGlyph name="U+35B6" xMin="0" yMin="0" xMax="0" yMax="0">
|
||||
<contour></contour><instructions><assembly></assembly></instructions>
|
||||
</TTGlyph>
|
||||
<TTGlyph name="U+35C5_VS17" xMin="0" yMin="0" xMax="0" yMax="0">
|
||||
<contour></contour><instructions><assembly></assembly></instructions>
|
||||
</TTGlyph>
|
||||
</glyf>
|
||||
|
||||
<name>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user