// Copyright 2019 Google LLC. #include "modules/skparagraph/include/FontCollection.h" #include "include/core/SkTypeface.h" #include "modules/skparagraph/include/Paragraph.h" #include "modules/skparagraph/src/ParagraphImpl.h" #include "modules/skshaper/include/SkShaper_harfbuzz.h" namespace { #if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) const char* kColorEmojiFontMac = "Apple Color Emoji"; #else const char* kColorEmojiLocale = "und-Zsye"; #endif } namespace skia { namespace textlayout { bool FontCollection::FamilyKey::operator==(const FontCollection::FamilyKey& other) const { return fFamilyNames == other.fFamilyNames && fFontStyle == other.fFontStyle && fFontArguments == other.fFontArguments; } size_t FontCollection::FamilyKey::Hasher::operator()(const FontCollection::FamilyKey& key) const { size_t hash = 0; for (const SkString& family : key.fFamilyNames) { hash ^= std::hash()(family.c_str()); } return hash ^ std::hash()(key.fFontStyle.weight()) ^ std::hash()(key.fFontStyle.slant()) ^ std::hash>()(key.fFontArguments); } FontCollection::FontCollection() : fEnableFontFallback(true) , fDefaultFamilyNames({SkString(DEFAULT_FONT_FAMILY)}) { } size_t FontCollection::getFontManagersCount() const { return this->getFontManagerOrder().size(); } void FontCollection::setAssetFontManager(sk_sp font_manager) { fAssetFontManager = std::move(font_manager); } void FontCollection::setDynamicFontManager(sk_sp font_manager) { fDynamicFontManager = std::move(font_manager); } void FontCollection::setTestFontManager(sk_sp font_manager) { fTestFontManager = std::move(font_manager); } void FontCollection::setDefaultFontManager(sk_sp fontManager, const char defaultFamilyName[]) { fDefaultFontManager = std::move(fontManager); fDefaultFamilyNames.emplace_back(defaultFamilyName); } void FontCollection::setDefaultFontManager(sk_sp fontManager, const std::vector& defaultFamilyNames) { fDefaultFontManager = std::move(fontManager); fDefaultFamilyNames = defaultFamilyNames; } void FontCollection::setDefaultFontManager(sk_sp fontManager) { fDefaultFontManager = std::move(fontManager); } // Return the available font managers in the order they should be queried. std::vector> FontCollection::getFontManagerOrder() const { std::vector> order; if (fDynamicFontManager) { order.push_back(fDynamicFontManager); } if (fAssetFontManager) { order.push_back(fAssetFontManager); } if (fTestFontManager) { order.push_back(fTestFontManager); } if (fDefaultFontManager && fEnableFontFallback) { order.push_back(fDefaultFontManager); } return order; } std::vector> FontCollection::findTypefaces(const std::vector& familyNames, SkFontStyle fontStyle) { return findTypefaces(familyNames, fontStyle, std::nullopt); } std::vector> FontCollection::findTypefaces(const std::vector& familyNames, SkFontStyle fontStyle, const std::optional& fontArgs) { // Look inside the font collections cache first FamilyKey familyKey(familyNames, fontStyle, fontArgs); auto found = fTypefaces.find(familyKey); if (found) { return *found; } std::vector> typefaces; for (const SkString& familyName : familyNames) { sk_sp match = matchTypeface(familyName, fontStyle); if (match && fontArgs) { match = fontArgs->CloneTypeface(match); } if (match) { typefaces.emplace_back(std::move(match)); } } if (typefaces.empty()) { sk_sp match; for (const SkString& familyName : fDefaultFamilyNames) { match = matchTypeface(familyName, fontStyle); if (match) { break; } } if (!match) { for (const auto& manager : this->getFontManagerOrder()) { match = manager->legacyMakeTypeface(nullptr, fontStyle); if (match) { break; } } } if (match) { typefaces.emplace_back(std::move(match)); } } fTypefaces.set(familyKey, typefaces); return typefaces; } sk_sp FontCollection::matchTypeface(const SkString& familyName, SkFontStyle fontStyle) { for (const auto& manager : this->getFontManagerOrder()) { sk_sp set(manager->matchFamily(familyName.c_str())); if (!set || set->count() == 0) { continue; } sk_sp match(set->matchStyle(fontStyle)); if (match) { return match; } } return nullptr; } // Find ANY font in available font managers that resolves the unicode codepoint sk_sp FontCollection::defaultFallback(SkUnichar unicode, SkFontStyle fontStyle, const SkString& locale) { for (const auto& manager : this->getFontManagerOrder()) { std::vector bcp47; if (!locale.isEmpty()) { bcp47.push_back(locale.c_str()); } sk_sp typeface(manager->matchFamilyStyleCharacter( nullptr, fontStyle, bcp47.data(), bcp47.size(), unicode)); if (typeface != nullptr) { return typeface; } } return nullptr; } // Find ANY font in available font managers that resolves this emojiStart sk_sp FontCollection::defaultEmojiFallback(SkUnichar emojiStart, SkFontStyle fontStyle, const SkString& locale) { for (const auto& manager : this->getFontManagerOrder()) { std::vector bcp47; #if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) sk_sp emojiTypeface = fDefaultFontManager->matchFamilyStyle(kColorEmojiFontMac, SkFontStyle()); if (emojiTypeface != nullptr) { return emojiTypeface; } #else bcp47.push_back(kColorEmojiLocale); #endif if (!locale.isEmpty()) { bcp47.push_back(locale.c_str()); } // Not really ideal since the first codepoint may not be the best one // but we start from a good colored emoji at least sk_sp typeface(manager->matchFamilyStyleCharacter( nullptr, fontStyle, bcp47.data(), bcp47.size(), emojiStart)); if (typeface != nullptr) { // ... and stop as soon as we find something in hope it will work for all of them return typeface; } } return nullptr; } sk_sp FontCollection::defaultFallback() { if (fDefaultFontManager == nullptr) { return nullptr; } for (const SkString& familyName : fDefaultFamilyNames) { sk_sp match = fDefaultFontManager->matchFamilyStyle(familyName.c_str(), SkFontStyle()); if (match) { return match; } } return nullptr; } void FontCollection::disableFontFallback() { fEnableFontFallback = false; } void FontCollection::enableFontFallback() { fEnableFontFallback = true; } void FontCollection::clearCaches() { fParagraphCache.reset(); fTypefaces.reset(); SkShapers::HB::PurgeCaches(); } } // namespace textlayout } // namespace skia