提交 bbdd73ec 编写于 作者: S Seigo Nonaka

Support Variation Selector in font selection.

This CL contains the following changes:
- Add a variation selector argument into getFamilyForChar to be able to
  select fonts which support variation selector.
- In case no fonts support the codepoint and variation selector pair,
  add a fallback rule which selects font family with ignoring variation
  selector.
- Change FontCollection::itemize to not change the font family
  immediately preceding a variation selector.
- Introduce unit tests for variation selectors.

With this CL, TextView can render the variation selectors correctly.

Bug: 11256006
Change-Id: I22ce0e9eadc941f84e3a9b23462f194e51dd7180
上级 786eac9c
...@@ -60,7 +60,7 @@ private: ...@@ -60,7 +60,7 @@ private:
size_t end; size_t end;
}; };
FontFamily* getFamilyForChar(uint32_t ch, FontLanguage lang, int variant) const; FontFamily* getFamilyForChar(uint32_t ch, uint32_t vs, FontLanguage lang, int variant) const;
// static for allocating unique id's // static for allocating unique id's
static uint32_t sNextId; static uint32_t sNextId;
......
...@@ -103,7 +103,7 @@ FontCollection::~FontCollection() { ...@@ -103,7 +103,7 @@ FontCollection::~FontCollection() {
// 3. If a font matches just language, it gets a score of 2. // 3. If a font matches just language, it gets a score of 2.
// 4. Matching the "compact" or "elegant" variant adds one to the score. // 4. Matching the "compact" or "elegant" variant adds one to the score.
// 5. Highest score wins, with ties resolved to the first font. // 5. Highest score wins, with ties resolved to the first font.
FontFamily* FontCollection::getFamilyForChar(uint32_t ch, FontFamily* FontCollection::getFamilyForChar(uint32_t ch, uint32_t vs,
FontLanguage lang, int variant) const { FontLanguage lang, int variant) const {
if (ch >= mMaxChar) { if (ch >= mMaxChar) {
return NULL; return NULL;
...@@ -112,11 +112,11 @@ FontFamily* FontCollection::getFamilyForChar(uint32_t ch, ...@@ -112,11 +112,11 @@ FontFamily* FontCollection::getFamilyForChar(uint32_t ch,
#ifdef VERBOSE_DEBUG #ifdef VERBOSE_DEBUG
ALOGD("querying range %d:%d\n", range.start, range.end); ALOGD("querying range %d:%d\n", range.start, range.end);
#endif #endif
FontFamily* bestFamily = NULL; FontFamily* bestFamily = nullptr;
int bestScore = -1; int bestScore = -1;
for (size_t i = range.start; i < range.end; i++) { for (size_t i = range.start; i < range.end; i++) {
FontFamily* family = mFamilyVec[i]; FontFamily* family = mFamilyVec[i];
if (family->getCoverage()->get(ch)) { if (vs == 0 ? family->getCoverage()->get(ch) : family->hasVariationSelector(ch, vs)) {
// First font family in collection always matches // First font family in collection always matches
if (mFamilies[0] == family) { if (mFamilies[0] == family) {
return family; return family;
...@@ -131,7 +131,13 @@ FontFamily* FontCollection::getFamilyForChar(uint32_t ch, ...@@ -131,7 +131,13 @@ FontFamily* FontCollection::getFamilyForChar(uint32_t ch,
} }
} }
} }
if (bestFamily == NULL && !mFamilyVec.empty()) { 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, lang, variant);
}
if (bestFamily == nullptr && !mFamilyVec.empty()) {
UErrorCode errorCode = U_ZERO_ERROR; UErrorCode errorCode = U_ZERO_ERROR;
const UNormalizer2* normalizer = unorm2_getNFDInstance(&errorCode); const UNormalizer2* normalizer = unorm2_getNFDInstance(&errorCode);
if (U_SUCCESS(errorCode)) { if (U_SUCCESS(errorCode)) {
...@@ -140,7 +146,7 @@ FontFamily* FontCollection::getFamilyForChar(uint32_t ch, ...@@ -140,7 +146,7 @@ FontFamily* FontCollection::getFamilyForChar(uint32_t ch,
if (U_SUCCESS(errorCode) && len > 0) { if (U_SUCCESS(errorCode) && len > 0) {
int off = 0; int off = 0;
U16_NEXT_UNSAFE(decomposed, off, ch); U16_NEXT_UNSAFE(decomposed, off, ch);
return getFamilyForChar(ch, lang, variant); return getFamilyForChar(ch, vs, lang, variant);
} }
} }
bestFamily = mFamilies[0]; bestFamily = mFamilies[0];
...@@ -167,35 +173,61 @@ static bool isStickyWhitelisted(uint32_t c) { ...@@ -167,35 +173,61 @@ static bool isStickyWhitelisted(uint32_t c) {
return false; return false;
} }
static bool isVariationSelector(uint32_t c) {
return (0xFE00 <= c && c <= 0xFE0F) || (0xE0100 <= c && c <= 0xE01EF);
}
void FontCollection::itemize(const uint16_t *string, size_t string_size, FontStyle style, void FontCollection::itemize(const uint16_t *string, size_t string_size, FontStyle style,
vector<Run>* result) const { vector<Run>* result) const {
FontLanguage lang = style.getLanguage(); FontLanguage lang = style.getLanguage();
int variant = style.getVariant(); int variant = style.getVariant();
FontFamily* lastFamily = NULL; FontFamily* lastFamily = NULL;
Run* run = NULL; Run* run = NULL;
int nShorts;
for (size_t i = 0; i < string_size; i += nShorts) { if (string_size == 0) {
nShorts = 1; return;
uint32_t ch = string[i]; }
// sigh, decode UTF-16 by hand here
if ((ch & 0xfc00) == 0xd800) { const uint32_t kEndOfString = 0xFFFFFFFF;
if ((i + 1) < string_size) {
ch = 0x10000 + ((ch & 0x3ff) << 10) + (string[i + 1] & 0x3ff); uint32_t nextCh = 0;
nShorts = 2; uint32_t prevCh = 0;
size_t nextUtf16Pos = 0;
size_t readLength = 0;
U16_NEXT(string, readLength, string_size, nextCh);
do {
const uint32_t ch = nextCh;
const size_t utf16Pos = nextUtf16Pos;
nextUtf16Pos = readLength;
if (readLength < string_size) {
U16_NEXT(string, readLength, string_size, nextCh);
} else {
nextCh = kEndOfString;
}
bool shouldContinueRun = false;
if (lastFamily != nullptr) {
if (isStickyWhitelisted(ch)) {
// Continue using existing font as long as it has coverage and is whitelisted
shouldContinueRun = lastFamily->getCoverage()->get(ch);
} else if (isVariationSelector(ch)) {
// Always continue if the character is a variation selector.
shouldContinueRun = true;
} }
} }
// Continue using existing font as long as it has coverage and is whitelisted
if (lastFamily == NULL if (!shouldContinueRun) {
|| !(isStickyWhitelisted(ch) && lastFamily->getCoverage()->get(ch))) { FontFamily* family =
FontFamily* family = getFamilyForChar(ch, lang, variant); getFamilyForChar(ch, isVariationSelector(nextCh) ? nextCh : 0, lang, variant);
if (i == 0 || family != lastFamily) { if (utf16Pos == 0 || family != lastFamily) {
size_t start = i; size_t start = utf16Pos;
// Workaround for Emoji keycap until we implement per-cluster font // Workaround for Emoji keycap until we implement per-cluster font
// selection: if keycap is found in a different font that also // selection: if keycap is found in a different font that also
// supports previous char, attach previous char to the new run. // supports previous char, attach previous char to the new run.
// Only handles non-surrogate characters. // Only handles non-surrogate characters.
// Bug 7557244. // Bug 7557244.
if (ch == KEYCAP && i && family && family->getCoverage()->get(string[i - 1])) { if (ch == KEYCAP && utf16Pos != 0 && family && family->getCoverage()->get(prevCh)) {
run->end--; run->end--;
if (run->start == run->end) { if (run->start == run->end) {
result->pop_back(); result->pop_back();
...@@ -214,8 +246,9 @@ void FontCollection::itemize(const uint16_t *string, size_t string_size, FontSty ...@@ -214,8 +246,9 @@ void FontCollection::itemize(const uint16_t *string, size_t string_size, FontSty
run->start = start; run->start = start;
} }
} }
run->end = i + nShorts; prevCh = ch;
} run->end = nextUtf16Pos; // exclusive
} while (nextCh != kEndOfString);
} }
MinikinFont* FontCollection::baseFont(FontStyle style) { MinikinFont* FontCollection::baseFont(FontStyle style) {
......
...@@ -277,6 +277,255 @@ TEST(FontCollectionItemizeTest, itemize_mixed) { ...@@ -277,6 +277,255 @@ TEST(FontCollectionItemizeTest, itemize_mixed) {
EXPECT_FALSE(runs[4].fakedFont.fakery.isFakeItalic()); EXPECT_FALSE(runs[4].fakedFont.fakery.isFakeItalic());
} }
TEST(FontCollectionItemizeTest, itemize_variationSelector) {
std::unique_ptr<FontCollection> collection = getFontCollection();
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(FontLanguage("zh_Hans", 7));
FontStyle kZH_HantStyle = FontStyle(FontLanguage("zh_Hant", 7));
// 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_EQ(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_EQ(kLatinFont, getFontPath(runs[0]));
}
TEST(FontCollectionItemizeTest, itemize_variationSelectorSupplement) {
std::unique_ptr<FontCollection> collection = getFontCollection();
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(FontLanguage("zh_Hans", 7));
FontStyle kZH_HantStyle = FontStyle(FontLanguage("zh_Hant", 7));
// 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_EQ(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_EQ(kLatinFont, getFontPath(runs[0]));
}
TEST(FontCollectionItemizeTest, itemize_no_crash) { TEST(FontCollectionItemizeTest, itemize_no_crash) {
std::unique_ptr<FontCollection> collection = getFontCollection(); std::unique_ptr<FontCollection> collection = getFontCollection();
std::vector<FontCollection::Run> runs; std::vector<FontCollection::Run> runs;
...@@ -340,4 +589,3 @@ TEST(FontCollectionItemizeTest, itemize_fakery) { ...@@ -340,4 +589,3 @@ TEST(FontCollectionItemizeTest, itemize_fakery) {
EXPECT_TRUE(runs[0].fakedFont.fakery.isFakeItalic()); EXPECT_TRUE(runs[0].fakedFont.fakery.isFakeItalic());
} }
// TODO(11256006): Add Variation Selector test cases once it is supported.
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册