FontCollection.cpp revision 7564b7fd5b78e3fb985b593545625112a0afbd56
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 22#include "unicode/unistr.h" 23#include "unicode/unorm2.h" 24 25#include "FontLanguage.h" 26#include "FontLanguageListCache.h" 27#include "MinikinInternal.h" 28#include <minikin/FontCollection.h> 29 30using std::vector; 31 32namespace android { 33 34template <typename T> 35static inline T max(T a, T b) { 36 return a>b ? a : b; 37} 38 39uint32_t FontCollection::sNextId = 0; 40 41FontCollection::FontCollection(const vector<FontFamily*>& typefaces) : 42 mMaxChar(0) { 43 AutoMutex _l(gMinikinLock); 44 mId = sNextId++; 45 vector<uint32_t> lastChar; 46 size_t nTypefaces = typefaces.size(); 47#ifdef VERBOSE_DEBUG 48 ALOGD("nTypefaces = %zd\n", nTypefaces); 49#endif 50 const FontStyle defaultStyle; 51 for (size_t i = 0; i < nTypefaces; i++) { 52 FontFamily* family = typefaces[i]; 53 MinikinFont* typeface = family->getClosestMatch(defaultStyle).font; 54 if (typeface == NULL) { 55 continue; 56 } 57 family->RefLocked(); 58 const SparseBitSet* coverage = family->getCoverage(); 59 if (coverage == nullptr) { 60 family->UnrefLocked(); 61 continue; 62 } 63 mFamilies.push_back(family); // emplace_back would be better 64 mMaxChar = max(mMaxChar, coverage->length()); 65 lastChar.push_back(coverage->nextSetBit(0)); 66 } 67 nTypefaces = mFamilies.size(); 68 LOG_ALWAYS_FATAL_IF(nTypefaces == 0, 69 "Font collection must have at least one valid typeface"); 70 size_t nPages = (mMaxChar + kPageMask) >> kLogCharsPerPage; 71 size_t offset = 0; 72 // TODO: Use variation selector map for mRanges construction. 73 // A font can have a glyph for a base code point and variation selector pair but no glyph for 74 // the base code point without variation selector. The family won't be listed in the range in 75 // this case. 76 for (size_t i = 0; i < nPages; i++) { 77 Range dummy; 78 mRanges.push_back(dummy); 79 Range* range = &mRanges.back(); 80#ifdef VERBOSE_DEBUG 81 ALOGD("i=%zd: range start = %zd\n", i, offset); 82#endif 83 range->start = offset; 84 for (size_t j = 0; j < nTypefaces; j++) { 85 if (lastChar[j] < (i + 1) << kLogCharsPerPage) { 86 FontFamily* family = mFamilies[j]; 87 mFamilyVec.push_back(family); 88 offset++; 89 uint32_t nextChar = family->getCoverage()->nextSetBit((i + 1) << kLogCharsPerPage); 90#ifdef VERBOSE_DEBUG 91 ALOGD("nextChar = %d (j = %zd)\n", nextChar, j); 92#endif 93 lastChar[j] = nextChar; 94 } 95 } 96 range->end = offset; 97 } 98} 99 100FontCollection::~FontCollection() { 101 for (size_t i = 0; i < mFamilies.size(); i++) { 102 mFamilies[i]->UnrefLocked(); 103 } 104} 105 106// Implement heuristic for choosing best-match font. Here are the rules: 107// 1. If first font in the collection has the character, it wins. 108// 2. If a font matches language, it gets a score of 2. 109// 3. Matching the "compact" or "elegant" variant adds one to the score. 110// 4. If there is a variation selector and a font supports the complete variation sequence, we add 111// 8 to the score. 112// 5. If there is a color variation selector (U+FE0F), we add 4 to the score if the font is an emoji 113// font. This additional score of 4 is only given if the base character is supported in the font, 114// but not the whole variation sequence. 115// 6. If there is a text variation selector (U+FE0E), we add 4 to the score if the font is not an 116// emoji font. This additional score of 4 is only given if the base character is supported in the 117// font, but not the whole variation sequence. 118// 7. Highest score wins, with ties resolved to the first font. 119FontFamily* FontCollection::getFamilyForChar(uint32_t ch, uint32_t vs, 120 uint32_t langListId, int variant) const { 121 if (ch >= mMaxChar) { 122 return NULL; 123 } 124 125 const FontLanguages& langList = FontLanguageListCache::getById(langListId); 126 // TODO: use all languages in langList. 127 const FontLanguage lang = (langList.size() == 0) ? FontLanguage() : langList[0]; 128 129 // Even if the font supports variation sequence, mRanges isn't aware of the base character of 130 // the sequence. Search all FontFamilies if variation sequence is specified. 131 // TODO: Always use mRanges for font search. 132 const std::vector<FontFamily*>& familyVec = (vs == 0) ? mFamilyVec : mFamilies; 133 Range range; 134 if (vs == 0) { 135 range = mRanges[ch >> kLogCharsPerPage]; 136 } else { 137 range = { 0, mFamilies.size() }; 138 } 139 140#ifdef VERBOSE_DEBUG 141 ALOGD("querying range %zd:%zd\n", range.start, range.end); 142#endif 143 FontFamily* bestFamily = nullptr; 144 int bestScore = -1; 145 for (size_t i = range.start; i < range.end; i++) { 146 FontFamily* family = familyVec[i]; 147 const bool hasVSGlyph = (vs != 0) && family->hasVariationSelector(ch, vs); 148 if (hasVSGlyph || family->getCoverage()->get(ch)) { 149 if ((vs == 0 || hasVSGlyph) && mFamilies[0] == family) { 150 // If the first font family in collection supports the given character or sequence, 151 // always use it. 152 return family; 153 } 154 155 // TODO use all language in the list. 156 FontLanguage fontLang = FontLanguageListCache::getById(family->langId())[0]; 157 int score = lang.match(fontLang) * 2; 158 if (family->variant() == 0 || family->variant() == variant) { 159 score++; 160 } 161 if (hasVSGlyph) { 162 score += 8; 163 } else if (((vs == 0xFE0F) && fontLang.hasEmojiFlag()) || 164 ((vs == 0xFE0E) && !fontLang.hasEmojiFlag())) { 165 score += 4; 166 } 167 if (score > bestScore) { 168 bestScore = score; 169 bestFamily = family; 170 } 171 } 172 } 173 if (bestFamily == nullptr && vs != 0) { 174 // If no fonts support the codepoint and variation selector pair, 175 // fallback to select a font family that supports just the base 176 // character, ignoring the variation selector. 177 return getFamilyForChar(ch, 0, langListId, variant); 178 } 179 if (bestFamily == nullptr && !mFamilyVec.empty()) { 180 UErrorCode errorCode = U_ZERO_ERROR; 181 const UNormalizer2* normalizer = unorm2_getNFDInstance(&errorCode); 182 if (U_SUCCESS(errorCode)) { 183 UChar decomposed[4]; 184 int len = unorm2_getRawDecomposition(normalizer, ch, decomposed, 4, &errorCode); 185 if (U_SUCCESS(errorCode) && len > 0) { 186 int off = 0; 187 U16_NEXT_UNSAFE(decomposed, off, ch); 188 return getFamilyForChar(ch, vs, langListId, variant); 189 } 190 } 191 bestFamily = mFamilies[0]; 192 } 193 return bestFamily; 194} 195 196const uint32_t NBSP = 0xa0; 197const uint32_t ZWJ = 0x200c; 198const uint32_t ZWNJ = 0x200d; 199const uint32_t KEYCAP = 0x20e3; 200const uint32_t HYPHEN = 0x2010; 201const uint32_t NB_HYPHEN = 0x2011; 202 203// Characters where we want to continue using existing font run instead of 204// recomputing the best match in the fallback list. 205static const uint32_t stickyWhitelist[] = { '!', ',', '-', '.', ':', ';', '?', NBSP, ZWJ, ZWNJ, 206 KEYCAP, HYPHEN, NB_HYPHEN }; 207 208static bool isStickyWhitelisted(uint32_t c) { 209 for (size_t i = 0; i < sizeof(stickyWhitelist) / sizeof(stickyWhitelist[0]); i++) { 210 if (stickyWhitelist[i] == c) return true; 211 } 212 return false; 213} 214 215static bool isVariationSelector(uint32_t c) { 216 return (0xFE00 <= c && c <= 0xFE0F) || (0xE0100 <= c && c <= 0xE01EF); 217} 218 219bool FontCollection::hasVariationSelector(uint32_t baseCodepoint, 220 uint32_t variationSelector) const { 221 if (!isVariationSelector(variationSelector)) { 222 return false; 223 } 224 if (baseCodepoint >= mMaxChar) { 225 return false; 226 } 227 // Currently mRanges can not be used here since it isn't aware of the variation sequence. 228 // TODO: Use mRanges for narrowing down the search range. 229 for (size_t i = 0; i < mFamilies.size(); i++) { 230 AutoMutex _l(gMinikinLock); 231 if (mFamilies[i]->hasVariationSelector(baseCodepoint, variationSelector)) { 232 return true; 233 } 234 } 235 return false; 236} 237 238void FontCollection::itemize(const uint16_t *string, size_t string_size, FontStyle style, 239 vector<Run>* result) const { 240 const uint32_t langListId = style.getLanguageListId(); 241 int variant = style.getVariant(); 242 FontFamily* lastFamily = NULL; 243 Run* run = NULL; 244 245 if (string_size == 0) { 246 return; 247 } 248 249 const uint32_t kEndOfString = 0xFFFFFFFF; 250 251 uint32_t nextCh = 0; 252 uint32_t prevCh = 0; 253 size_t nextUtf16Pos = 0; 254 size_t readLength = 0; 255 U16_NEXT(string, readLength, string_size, nextCh); 256 257 do { 258 const uint32_t ch = nextCh; 259 const size_t utf16Pos = nextUtf16Pos; 260 nextUtf16Pos = readLength; 261 if (readLength < string_size) { 262 U16_NEXT(string, readLength, string_size, nextCh); 263 } else { 264 nextCh = kEndOfString; 265 } 266 267 bool shouldContinueRun = false; 268 if (lastFamily != nullptr) { 269 if (isStickyWhitelisted(ch)) { 270 // Continue using existing font as long as it has coverage and is whitelisted 271 shouldContinueRun = lastFamily->getCoverage()->get(ch); 272 } else if (isVariationSelector(ch)) { 273 // Always continue if the character is a variation selector. 274 shouldContinueRun = true; 275 } 276 } 277 278 if (!shouldContinueRun) { 279 FontFamily* family = getFamilyForChar(ch, isVariationSelector(nextCh) ? nextCh : 0, 280 langListId, variant); 281 if (utf16Pos == 0 || family != lastFamily) { 282 size_t start = utf16Pos; 283 // Workaround for Emoji keycap until we implement per-cluster font 284 // selection: if keycap is found in a different font that also 285 // supports previous char, attach previous char to the new run. 286 // Bug 7557244. 287 if (ch == KEYCAP && utf16Pos != 0 && family && family->getCoverage()->get(prevCh)) { 288 const size_t prevChLength = U16_LENGTH(prevCh); 289 run->end -= prevChLength; 290 if (run->start == run->end) { 291 result->pop_back(); 292 } 293 start -= prevChLength; 294 } 295 Run dummy; 296 result->push_back(dummy); 297 run = &result->back(); 298 if (family == NULL) { 299 run->fakedFont.font = NULL; 300 } else { 301 run->fakedFont = family->getClosestMatch(style); 302 } 303 lastFamily = family; 304 run->start = start; 305 } 306 } 307 prevCh = ch; 308 run->end = nextUtf16Pos; // exclusive 309 } while (nextCh != kEndOfString); 310} 311 312MinikinFont* FontCollection::baseFont(FontStyle style) { 313 return baseFontFaked(style).font; 314} 315 316FakedFont FontCollection::baseFontFaked(FontStyle style) { 317 if (mFamilies.empty()) { 318 return FakedFont(); 319 } 320 return mFamilies[0]->getClosestMatch(style); 321} 322 323uint32_t FontCollection::getId() const { 324 return mId; 325} 326 327void FontCollection::purgeFontFamilyHbFontCache() const { 328 assertMinikinLocked(); 329 for (size_t i = 0; i < mFamilies.size(); ++i) { 330 mFamilies[i]->purgeHbFontCache(); 331 } 332} 333 334} // namespace android 335