FontCollection.cpp revision 198b46f1fea3f47ef8eb6317799c0d77aaec52f6
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 if (mFamilies[i]->hasVariationSelector(baseCodepoint, variationSelector)) { 231 return true; 232 } 233 } 234 return false; 235} 236 237void FontCollection::itemize(const uint16_t *string, size_t string_size, FontStyle style, 238 vector<Run>* result) const { 239 const uint32_t langListId = style.getLanguageListId(); 240 int variant = style.getVariant(); 241 FontFamily* lastFamily = NULL; 242 Run* run = NULL; 243 244 if (string_size == 0) { 245 return; 246 } 247 248 const uint32_t kEndOfString = 0xFFFFFFFF; 249 250 uint32_t nextCh = 0; 251 uint32_t prevCh = 0; 252 size_t nextUtf16Pos = 0; 253 size_t readLength = 0; 254 U16_NEXT(string, readLength, string_size, nextCh); 255 256 do { 257 const uint32_t ch = nextCh; 258 const size_t utf16Pos = nextUtf16Pos; 259 nextUtf16Pos = readLength; 260 if (readLength < string_size) { 261 U16_NEXT(string, readLength, string_size, nextCh); 262 } else { 263 nextCh = kEndOfString; 264 } 265 266 bool shouldContinueRun = false; 267 if (lastFamily != nullptr) { 268 if (isStickyWhitelisted(ch)) { 269 // Continue using existing font as long as it has coverage and is whitelisted 270 shouldContinueRun = lastFamily->getCoverage()->get(ch); 271 } else if (isVariationSelector(ch)) { 272 // Always continue if the character is a variation selector. 273 shouldContinueRun = true; 274 } 275 } 276 277 if (!shouldContinueRun) { 278 FontFamily* family = getFamilyForChar(ch, isVariationSelector(nextCh) ? nextCh : 0, 279 langListId, variant); 280 if (utf16Pos == 0 || family != lastFamily) { 281 size_t start = utf16Pos; 282 // Workaround for Emoji keycap until we implement per-cluster font 283 // selection: if keycap is found in a different font that also 284 // supports previous char, attach previous char to the new run. 285 // Bug 7557244. 286 if (ch == KEYCAP && utf16Pos != 0 && family && family->getCoverage()->get(prevCh)) { 287 const size_t prevChLength = U16_LENGTH(prevCh); 288 run->end -= prevChLength; 289 if (run->start == run->end) { 290 result->pop_back(); 291 } 292 start -= prevChLength; 293 } 294 Run dummy; 295 result->push_back(dummy); 296 run = &result->back(); 297 if (family == NULL) { 298 run->fakedFont.font = NULL; 299 } else { 300 run->fakedFont = family->getClosestMatch(style); 301 } 302 lastFamily = family; 303 run->start = start; 304 } 305 } 306 prevCh = ch; 307 run->end = nextUtf16Pos; // exclusive 308 } while (nextCh != kEndOfString); 309} 310 311MinikinFont* FontCollection::baseFont(FontStyle style) { 312 return baseFontFaked(style).font; 313} 314 315FakedFont FontCollection::baseFontFaked(FontStyle style) { 316 if (mFamilies.empty()) { 317 return FakedFont(); 318 } 319 return mFamilies[0]->getClosestMatch(style); 320} 321 322uint32_t FontCollection::getId() const { 323 return mId; 324} 325 326void FontCollection::purgeFontFamilyHbFontCache() const { 327 assertMinikinLocked(); 328 for (size_t i = 0; i < mFamilies.size(); ++i) { 329 mFamilies[i]->purgeHbFontCache(); 330 } 331} 332 333} // namespace android 334