/* * Copyright (C) 2012 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND * ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR * ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "flutter/sky/engine/platform/fonts/FontCache.h" #include #include #include #include #include "flutter/sky/engine/public/platform/Platform.h" #include "flutter/sky/engine/wtf/HashMap.h" #include "flutter/sky/engine/wtf/Noncopyable.h" #include "flutter/sky/engine/wtf/OwnPtr.h" #include "flutter/sky/engine/wtf/PassOwnPtr.h" #include "flutter/sky/engine/wtf/Vector.h" #include "flutter/sky/engine/wtf/text/AtomicString.h" #include "flutter/sky/engine/wtf/text/AtomicStringHash.h" #include "flutter/sky/engine/wtf/text/CString.h" namespace blink { class CachedFont { public: // Note: We pass the charset explicitly as callers // should not create CachedFont entries without knowing // that the FcPattern contains a valid charset. CachedFont(FcPattern* pattern, FcCharSet* charSet) : m_supportedCharacters(charSet) { ASSERT(pattern); ASSERT(charSet); m_fallbackFont.name = fontName(pattern); m_fallbackFont.filename = fontFilename(pattern); m_fallbackFont.ttcIndex = fontTtcIndex(pattern); m_fallbackFont.isBold = fontIsBold(pattern); m_fallbackFont.isItalic = fontIsItalic(pattern); } const FontCache::PlatformFallbackFont& fallbackFont() const { return m_fallbackFont; } bool hasGlyphForCharacter(UChar32 c) { return m_supportedCharacters && FcCharSetHasChar(m_supportedCharacters, c); } private: static String fontName(FcPattern* pattern) { FcChar8* familyName = nullptr; if (FcPatternGetString(pattern, FC_FAMILY, 0, &familyName) != FcResultMatch) return String(); // FCChar8 is unsigned char, so we cast to char for CString. const char* charFamily = reinterpret_cast(familyName); return String::fromUTF8(charFamily, strlen(charFamily)); } static CString fontFilename(FcPattern* pattern) { FcChar8* cFilename = nullptr; if (FcPatternGetString(pattern, FC_FILE, 0, &cFilename) != FcResultMatch) return CString(); const char* fontFilename = reinterpret_cast(cFilename); return CString(fontFilename, strlen(fontFilename)); } static int fontTtcIndex(FcPattern* pattern) { int ttcIndex = -1; if (FcPatternGetInteger(pattern, FC_INDEX, 0, &ttcIndex) != FcResultMatch || ttcIndex < 0) return 0; return ttcIndex; } static bool fontIsBold(FcPattern* pattern) { int weight = 0; if (FcPatternGetInteger(pattern, FC_WEIGHT, 0, &weight) != FcResultMatch) return false; return weight >= FC_WEIGHT_BOLD; } static bool fontIsItalic(FcPattern* pattern) { int slant = 0; if (FcPatternGetInteger(pattern, FC_SLANT, 0, &slant) != FcResultMatch) return false; return slant != FC_SLANT_ROMAN; } FontCache::PlatformFallbackFont m_fallbackFont; // m_supportedCharaters is owned by the parent // FcFontSet and should never be freed. FcCharSet* m_supportedCharacters; }; class CachedFontSet { WTF_MAKE_NONCOPYABLE(CachedFontSet); public: // CachedFontSet takes ownership of the passed FcFontSet. static PassOwnPtr createForLocale(const char* locale) { FcFontSet* fontSet = createFcFontSetForLocale(locale); return adoptPtr(new CachedFontSet(fontSet)); } ~CachedFontSet() { m_fallbackList.clear(); FcFontSetDestroy(m_fontSet); } FontCache::PlatformFallbackFont fallbackFontForChar(UChar32 c) { Vector::iterator itr = m_fallbackList.begin(); for (; itr != m_fallbackList.end(); itr++) { if (itr->hasGlyphForCharacter(c)) return itr->fallbackFont(); } // The previous code just returned garbage if the user didn't // have the necessary fonts, this seems better than garbage. // Current callers happen to ignore any values with an empty family string. return FontCache::PlatformFallbackFont(); } private: static FcFontSet* createFcFontSetForLocale(const char* locale) { FcPattern* pattern = FcPatternCreate(); if (locale) { // FcChar* is unsigned char* so we have to cast. FcPatternAddString(pattern, FC_LANG, reinterpret_cast(locale)); } FcValue fcvalue; fcvalue.type = FcTypeBool; fcvalue.u.b = FcTrue; FcPatternAdd(pattern, FC_SCALABLE, fcvalue, FcFalse); FcConfigSubstitute(0, pattern, FcMatchPattern); FcDefaultSubstitute(pattern); if (!locale) FcPatternDel(pattern, FC_LANG); // The result parameter returns if any fonts were found. // We already handle 0 fonts correctly, so we ignore the param. FcResult result; FcFontSet* fontSet = FcFontSort(0, pattern, 0, 0, &result); FcPatternDestroy(pattern); // The caller will take ownership of this FcFontSet. return fontSet; } CachedFontSet(FcFontSet* fontSet) : m_fontSet(fontSet) { fillFallbackList(); } void fillFallbackList() { ASSERT(m_fallbackList.isEmpty()); if (!m_fontSet) return; for (int i = 0; i < m_fontSet->nfont; ++i) { FcPattern* pattern = m_fontSet->fonts[i]; // Ignore any bitmap fonts users may still have installed from last // century. FcBool isScalable; if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &isScalable) != FcResultMatch || !isScalable) continue; // Ignore any fonts FontConfig knows about, but that we don't have // permission to read. FcChar8* cFilename; if (FcPatternGetString(pattern, FC_FILE, 0, &cFilename) != FcResultMatch) continue; if (access(reinterpret_cast(cFilename), R_OK)) continue; // Make sure this font can tell us what characters it has glyphs for. FcCharSet* charSet; if (FcPatternGetCharSet(pattern, FC_CHARSET, 0, &charSet) != FcResultMatch) continue; m_fallbackList.append(CachedFont(pattern, charSet)); } } FcFontSet* m_fontSet; // Owned by this object. // CachedFont has a FcCharset* which points into the FcFontSet. // If the FcFontSet is ever destroyed, the fallbackList // must be cleared first. Vector m_fallbackList; }; class FontSetCache { public: static FontSetCache& shared() { DEFINE_STATIC_LOCAL(FontSetCache, cache, ()); return cache; } FontCache::PlatformFallbackFont fallbackFontForCharInLocale( UChar32 c, const char* locale) { DEFINE_STATIC_LOCAL(AtomicString, kNoLocale, ("NO_LOCALE_SPECIFIED")); AtomicString localeKey; if (locale && strlen(locale)) { localeKey = AtomicString(locale); } else { // String hash computation the m_setsByLocale map needs // a non-empty string. localeKey = kNoLocale; } LocaleToCachedFont::iterator itr = m_setsByLocale.find(localeKey); if (itr == m_setsByLocale.end()) { OwnPtr newEntry = CachedFontSet::createForLocale(strlen(locale) ? locale : 0); return m_setsByLocale.add(localeKey, newEntry.release()) .storedValue->value->fallbackFontForChar(c); } return itr.get()->value->fallbackFontForChar(c); } // FIXME: We may wish to add a way to prune the cache at a later time. private: // FIXME: This shouldn't need to be AtomicString, but // currently HashTraits isn't smart enough // to hash the string (only does pointer compares). typedef HashMap> LocaleToCachedFont; LocaleToCachedFont m_setsByLocale; }; void FontCache::getFontForCharacter( UChar32 c, const char* locale, FontCache::PlatformFallbackFont* fallbackFont) { *fallbackFont = FontSetCache::shared().fallbackFontForCharInLocale(c, locale); } } // namespace blink