FontCollection.cpp revision cee83d40324b1f3b8e113eb8c9eda8e12ef36923
1/* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17// #define VERBOSE_DEBUG 18 19#define LOG_TAG "Minikin" 20#include <cutils/log.h> 21#include <algorithm> 22 23#include "unicode/unistr.h" 24#include "unicode/unorm2.h" 25 26#include "FontLanguage.h" 27#include "FontLanguageListCache.h" 28#include "MinikinInternal.h" 29#include <minikin/FontCollection.h> 30 31using std::vector; 32 33namespace android { 34 35template <typename T> 36static inline T max(T a, T b) { 37 return a>b ? a : b; 38} 39 40uint32_t FontCollection::sNextId = 0; 41 42FontCollection::FontCollection(const vector<FontFamily*>& typefaces) : 43 mMaxChar(0) { 44 AutoMutex _l(gMinikinLock); 45 mId = sNextId++; 46 vector<uint32_t> lastChar; 47 size_t nTypefaces = typefaces.size(); 48#ifdef VERBOSE_DEBUG 49 ALOGD("nTypefaces = %zd\n", nTypefaces); 50#endif 51 const FontStyle defaultStyle; 52 for (size_t i = 0; i < nTypefaces; i++) { 53 FontFamily* family = typefaces[i]; 54 MinikinFont* typeface = family->getClosestMatch(defaultStyle).font; 55 if (typeface == NULL) { 56 continue; 57 } 58 family->RefLocked(); 59 const SparseBitSet* coverage = family->getCoverage(); 60 if (coverage == nullptr) { 61 family->UnrefLocked(); 62 continue; 63 } 64 mFamilies.push_back(family); // emplace_back would be better 65 if (family->hasVSTable()) { 66 mVSFamilyVec.push_back(family); 67 } 68 mMaxChar = max(mMaxChar, coverage->length()); 69 lastChar.push_back(coverage->nextSetBit(0)); 70 } 71 nTypefaces = mFamilies.size(); 72 LOG_ALWAYS_FATAL_IF(nTypefaces == 0, 73 "Font collection must have at least one valid typeface"); 74 size_t nPages = (mMaxChar + kPageMask) >> kLogCharsPerPage; 75 size_t offset = 0; 76 // TODO: Use variation selector map for mRanges construction. 77 // A font can have a glyph for a base code point and variation selector pair but no glyph for 78 // the base code point without variation selector. The family won't be listed in the range in 79 // this case. 80 for (size_t i = 0; i < nPages; i++) { 81 Range dummy; 82 mRanges.push_back(dummy); 83 Range* range = &mRanges.back(); 84#ifdef VERBOSE_DEBUG 85 ALOGD("i=%zd: range start = %zd\n", i, offset); 86#endif 87 range->start = offset; 88 for (size_t j = 0; j < nTypefaces; j++) { 89 if (lastChar[j] < (i + 1) << kLogCharsPerPage) { 90 FontFamily* family = mFamilies[j]; 91 mFamilyVec.push_back(family); 92 offset++; 93 uint32_t nextChar = family->getCoverage()->nextSetBit((i + 1) << kLogCharsPerPage); 94#ifdef VERBOSE_DEBUG 95 ALOGD("nextChar = %d (j = %zd)\n", nextChar, j); 96#endif 97 lastChar[j] = nextChar; 98 } 99 } 100 range->end = offset; 101 } 102} 103 104FontCollection::~FontCollection() { 105 for (size_t i = 0; i < mFamilies.size(); i++) { 106 mFamilies[i]->UnrefLocked(); 107 } 108} 109 110// Special scores for the font fallback. 111const uint32_t kUnsupportedFontScore = 0; 112const uint32_t kFirstFontScore = UINT32_MAX; 113 114// Calculates a font score. 115// The score of the font family is based on three subscores. 116// - Coverage Score: How well the font family covers the given character or variation sequence. 117// - Language Score: How well the font family is appropriate for the language. 118// - Variant Score: Whether the font family matches the variant. Note that this variant is not the 119// one in BCP47. This is our own font variant (e.g., elegant, compact). 120// 121// Then, there is a priority for these three subscores as follow: 122// Coverage Score > Language Score > Variant Score 123// The returned score reflects this priority order. 124// 125// Note that there are two special scores. 126// - kUnsupportedFontScore: When the font family doesn't support the variation sequence or even its 127// base character. 128// - kFirstFontScore: When the font is the first font family in the collection and it supports the 129// given character or variation sequence. 130uint32_t FontCollection::calcFamilyScore(uint32_t ch, uint32_t vs, int variant, uint32_t langListId, 131 FontFamily* fontFamily) const { 132 133 const uint32_t coverageScore = calcCoverageScore(ch, vs, fontFamily); 134 if (coverageScore == kFirstFontScore || coverageScore == kUnsupportedFontScore) { 135 // No need to calculate other scores. 136 return coverageScore; 137 } 138 139 const uint32_t languageScore = calcLanguageMatchingScore(langListId, *fontFamily); 140 const uint32_t variantScore = calcVariantMatchingScore(variant, *fontFamily); 141 142 // Subscores are encoded into 31 bits representation to meet the subscore priority. 143 // The highest 2 bits are for coverage score, then following 28 bits are for language score, 144 // then the last 1 bit is for variant score. 145 return coverageScore << 29 | languageScore << 1 | variantScore; 146} 147 148// Calculates a font score based on variation sequence coverage. 149// - Returns kUnsupportedFontScore if the font doesn't support the variation sequence or its base 150// character. 151// - Returns kFirstFontScore if the font family is the first font family in the collection and it 152// supports the given character or variation sequence. 153// - Returns 3 if the font family supports the variation sequence. 154// - Returns 2 if the vs is a color variation selector (U+FE0F) and if the font is an emoji font. 155// - Returns 2 if the vs is a text variation selector (U+FE0E) and if the font is not an emoji font. 156// - Returns 1 if the variation selector is not specified or if the font family only supports the 157// variation sequence's base character. 158uint32_t FontCollection::calcCoverageScore(uint32_t ch, uint32_t vs, FontFamily* fontFamily) const { 159 const bool hasVSGlyph = (vs != 0) && fontFamily->hasVariationSelector(ch, vs); 160 if (!hasVSGlyph && !fontFamily->getCoverage()->get(ch)) { 161 // The font doesn't support either variation sequence or even the base character. 162 return kUnsupportedFontScore; 163 } 164 165 if ((vs == 0 || hasVSGlyph) && mFamilies[0] == fontFamily) { 166 // If the first font family supports the given character or variation sequence, always use 167 // it. 168 return kFirstFontScore; 169 } 170 171 if (vs == 0) { 172 return 1; 173 } 174 175 if (hasVSGlyph) { 176 return 3; 177 } 178 179 if (vs == 0xFE0F || vs == 0xFE0E) { 180 // TODO use all language in the list. 181 const FontLanguage lang = FontLanguageListCache::getById(fontFamily->langId())[0]; 182 const bool hasEmojiFlag = lang.hasEmojiFlag(); 183 if (vs == 0xFE0F) { 184 return hasEmojiFlag ? 2 : 1; 185 } else { // vs == 0xFE0E 186 return hasEmojiFlag ? 1 : 2; 187 } 188 } 189 return 1; 190} 191 192// Calculates font scores based on the script matching and primary langauge matching. 193// 194// If the font's script doesn't support the requested script, the font gets a score of 0. If the 195// font's script supports the requested script and the font has the same primary language as the 196// requested one, the font gets a score of 2. If the font's script supports the requested script 197// but the primary language is different from the requested one, the font gets a score of 1. 198// 199// If two languages in the requested list have the same language score, the font matching with 200// higher priority language gets a higher score. For example, in the case the user requested 201// language list is "ja-Jpan,en-Latn". The score of for the font of "ja-Jpan" gets a higher score 202// than the font of "en-Latn". 203// 204// To achieve the above two conditions, the language score is determined as follows: 205// LanguageScore = s(0) * 3^(m - 1) + s(1) * 3^(m - 2) + ... + s(m - 2) * 3 + s(m - 1) 206// Here, m is the maximum number of languages to be compared, and s(i) is the i-th language's 207// matching score. The possible values of s(i) are 0, 1 and 2. 208uint32_t FontCollection::calcLanguageMatchingScore( 209 uint32_t userLangListId, const FontFamily& fontFamily) { 210 const FontLanguages& langList = FontLanguageListCache::getById(userLangListId); 211 // TODO use all language in the list. 212 FontLanguage fontLanguage = FontLanguageListCache::getById(fontFamily.langId())[0]; 213 214 const size_t maxCompareNum = std::min(langList.size(), FONT_LANGUAGES_LIMIT); 215 uint32_t score = fontLanguage.getScoreFor(langList[0]); // maxCompareNum can't be zero. 216 for (size_t i = 1; i < maxCompareNum; ++i) { 217 score = score * 3u + fontLanguage.getScoreFor(langList[i]); 218 } 219 return score; 220} 221 222// Calculates a font score based on variant ("compact" or "elegant") matching. 223// - Returns 1 if the font doesn't have variant or the variant matches with the text style. 224// - No score if the font has a variant but it doesn't match with the text style. 225uint32_t FontCollection::calcVariantMatchingScore(int variant, const FontFamily& fontFamily) { 226 return (fontFamily.variant() == 0 || fontFamily.variant() == variant) ? 1 : 0; 227} 228 229// Implement heuristic for choosing best-match font. Here are the rules: 230// 1. If first font in the collection has the character, it wins. 231// 2. Calculate a score for the font family. See comments in calcFamilyScore for the detail. 232// 3. Highest score wins, with ties resolved to the first font. 233// This method never returns nullptr. 234FontFamily* FontCollection::getFamilyForChar(uint32_t ch, uint32_t vs, 235 uint32_t langListId, int variant) const { 236 if (ch >= mMaxChar) { 237 return mFamilies[0]; 238 } 239 240 const std::vector<FontFamily*>* familyVec = &mFamilyVec; 241 Range range = mRanges[ch >> kLogCharsPerPage]; 242 243 std::vector<FontFamily*> familyVecForVS; 244 if (vs != 0) { 245 // If variation selector is specified, need to search for both the variation sequence and 246 // its base codepoint. Compute the union vector of them. 247 familyVecForVS = mVSFamilyVec; 248 familyVecForVS.insert(familyVecForVS.end(), 249 mFamilyVec.begin() + range.start, mFamilyVec.begin() + range.end); 250 std::sort(familyVecForVS.begin(), familyVecForVS.end()); 251 auto last = std::unique(familyVecForVS.begin(), familyVecForVS.end()); 252 familyVecForVS.erase(last, familyVecForVS.end()); 253 254 familyVec = &familyVecForVS; 255 range = { 0, familyVecForVS.size() }; 256 } 257 258#ifdef VERBOSE_DEBUG 259 ALOGD("querying range %zd:%zd\n", range.start, range.end); 260#endif 261 FontFamily* bestFamily = nullptr; 262 uint32_t bestScore = kUnsupportedFontScore; 263 for (size_t i = range.start; i < range.end; i++) { 264 FontFamily* family = (*familyVec)[i]; 265 const uint32_t score = calcFamilyScore(ch, vs, variant, langListId, family); 266 if (score == kFirstFontScore) { 267 // If the first font family supports the given character or variation sequence, always 268 // use it. 269 return family; 270 } 271 if (score > bestScore) { 272 bestScore = score; 273 bestFamily = family; 274 } 275 } 276 if (bestFamily == nullptr) { 277 UErrorCode errorCode = U_ZERO_ERROR; 278 const UNormalizer2* normalizer = unorm2_getNFDInstance(&errorCode); 279 if (U_SUCCESS(errorCode)) { 280 UChar decomposed[4]; 281 int len = unorm2_getRawDecomposition(normalizer, ch, decomposed, 4, &errorCode); 282 if (U_SUCCESS(errorCode) && len > 0) { 283 int off = 0; 284 U16_NEXT_UNSAFE(decomposed, off, ch); 285 return getFamilyForChar(ch, vs, langListId, variant); 286 } 287 } 288 bestFamily = mFamilies[0]; 289 } 290 return bestFamily; 291} 292 293const uint32_t NBSP = 0xa0; 294const uint32_t ZWJ = 0x200c; 295const uint32_t ZWNJ = 0x200d; 296const uint32_t KEYCAP = 0x20e3; 297const uint32_t HYPHEN = 0x2010; 298const uint32_t NB_HYPHEN = 0x2011; 299 300// Characters where we want to continue using existing font run instead of 301// recomputing the best match in the fallback list. 302static const uint32_t stickyWhitelist[] = { '!', ',', '-', '.', ':', ';', '?', NBSP, ZWJ, ZWNJ, 303 KEYCAP, HYPHEN, NB_HYPHEN }; 304 305static bool isStickyWhitelisted(uint32_t c) { 306 for (size_t i = 0; i < sizeof(stickyWhitelist) / sizeof(stickyWhitelist[0]); i++) { 307 if (stickyWhitelist[i] == c) return true; 308 } 309 return false; 310} 311 312static bool isVariationSelector(uint32_t c) { 313 return (0xFE00 <= c && c <= 0xFE0F) || (0xE0100 <= c && c <= 0xE01EF); 314} 315 316bool FontCollection::hasVariationSelector(uint32_t baseCodepoint, 317 uint32_t variationSelector) const { 318 if (!isVariationSelector(variationSelector)) { 319 return false; 320 } 321 if (baseCodepoint >= mMaxChar) { 322 return false; 323 } 324 if (variationSelector == 0) { 325 return false; 326 } 327 328 // Currently mRanges can not be used here since it isn't aware of the variation sequence. 329 for (size_t i = 0; i < mVSFamilyVec.size(); i++) { 330 AutoMutex _l(gMinikinLock); 331 if (mVSFamilyVec[i]->hasVariationSelector(baseCodepoint, variationSelector)) { 332 return true; 333 } 334 } 335 return false; 336} 337 338void FontCollection::itemize(const uint16_t *string, size_t string_size, FontStyle style, 339 vector<Run>* result) const { 340 const uint32_t langListId = style.getLanguageListId(); 341 int variant = style.getVariant(); 342 FontFamily* lastFamily = NULL; 343 Run* run = NULL; 344 345 if (string_size == 0) { 346 return; 347 } 348 349 const uint32_t kEndOfString = 0xFFFFFFFF; 350 351 uint32_t nextCh = 0; 352 uint32_t prevCh = 0; 353 size_t nextUtf16Pos = 0; 354 size_t readLength = 0; 355 U16_NEXT(string, readLength, string_size, nextCh); 356 357 do { 358 const uint32_t ch = nextCh; 359 const size_t utf16Pos = nextUtf16Pos; 360 nextUtf16Pos = readLength; 361 if (readLength < string_size) { 362 U16_NEXT(string, readLength, string_size, nextCh); 363 } else { 364 nextCh = kEndOfString; 365 } 366 367 bool shouldContinueRun = false; 368 if (lastFamily != nullptr) { 369 if (isStickyWhitelisted(ch)) { 370 // Continue using existing font as long as it has coverage and is whitelisted 371 shouldContinueRun = lastFamily->getCoverage()->get(ch); 372 } else if (isVariationSelector(ch)) { 373 // Always continue if the character is a variation selector. 374 shouldContinueRun = true; 375 } 376 } 377 378 if (!shouldContinueRun) { 379 FontFamily* family = getFamilyForChar(ch, isVariationSelector(nextCh) ? nextCh : 0, 380 langListId, variant); 381 if (utf16Pos == 0 || family != lastFamily) { 382 size_t start = utf16Pos; 383 // Workaround for Emoji keycap and emoji modifier until we implement per-cluster 384 // font selection: if a keycap or an emoji modifier is found in a different font 385 // that also supports previous char, attach previous char to the new run. 386 // Bug 7557244. 387 if (utf16Pos != 0 && 388 (ch == KEYCAP || (isEmojiModifier(ch) && isEmojiBase(prevCh))) && 389 family && family->getCoverage()->get(prevCh)) { 390 const size_t prevChLength = U16_LENGTH(prevCh); 391 run->end -= prevChLength; 392 if (run->start == run->end) { 393 result->pop_back(); 394 } 395 start -= prevChLength; 396 } 397 Run dummy; 398 result->push_back(dummy); 399 run = &result->back(); 400 run->fakedFont = family->getClosestMatch(style); 401 lastFamily = family; 402 run->start = start; 403 } 404 } 405 prevCh = ch; 406 run->end = nextUtf16Pos; // exclusive 407 } while (nextCh != kEndOfString); 408} 409 410MinikinFont* FontCollection::baseFont(FontStyle style) { 411 return baseFontFaked(style).font; 412} 413 414FakedFont FontCollection::baseFontFaked(FontStyle style) { 415 return mFamilies[0]->getClosestMatch(style); 416} 417 418uint32_t FontCollection::getId() const { 419 return mId; 420} 421 422} // namespace android 423