| /* |
| * Copyright (C) 2009 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: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * 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. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT |
| * OWNER OR 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 "sky/engine/public/platform/linux/WebFontInfo.h" |
| |
| #include <fontconfig/fontconfig.h> |
| #include <string.h> |
| #include <unicode/utf16.h> |
| #include "sky/engine/public/platform/linux/WebFallbackFont.h" |
| #include "sky/engine/wtf/HashMap.h" |
| #include "sky/engine/wtf/Noncopyable.h" |
| #include "sky/engine/wtf/OwnPtr.h" |
| #include "sky/engine/wtf/PassOwnPtr.h" |
| #include "sky/engine/wtf/Vector.h" |
| #include "sky/engine/wtf/text/AtomicString.h" |
| #include "sky/engine/wtf/text/AtomicStringHash.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 WebFallbackFont& fallbackFont() const { return m_fallbackFont; } |
| bool hasGlyphForCharacter(WebUChar32 c) |
| { |
| return m_supportedCharacters && FcCharSetHasChar(m_supportedCharacters, c); |
| } |
| |
| private: |
| static WebCString fontName(FcPattern* pattern) |
| { |
| FcChar8* familyName = nullptr; |
| if (FcPatternGetString(pattern, FC_FAMILY, 0, &familyName) != FcResultMatch) |
| return WebCString(); |
| |
| // FCChar8 is unsigned char, so we cast to char for WebCString. |
| const char* charFamily = reinterpret_cast<char*>(familyName); |
| return WebCString(charFamily, strlen(charFamily)); |
| } |
| |
| static WebCString fontFilename(FcPattern* pattern) |
| { |
| FcChar8* cFilename = nullptr; |
| if (FcPatternGetString(pattern, FC_FILE, 0, &cFilename) != FcResultMatch) |
| return WebCString(); |
| const char* fontFilename = reinterpret_cast<char*>(cFilename); |
| return WebCString(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; |
| } |
| |
| WebFallbackFont 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<CachedFontSet> createForLocale(const char* locale) |
| { |
| FcFontSet* fontSet = createFcFontSetForLocale(locale); |
| return adoptPtr(new CachedFontSet(fontSet)); |
| } |
| |
| ~CachedFontSet() |
| { |
| m_fallbackList.clear(); |
| FcFontSetDestroy(m_fontSet); |
| } |
| |
| WebFallbackFont fallbackFontForChar(WebUChar32 c) |
| { |
| Vector<CachedFont>::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 WebFallbackFont(); |
| } |
| |
| 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<const FcChar8*>(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<char*>(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<CachedFont> m_fallbackList; |
| }; |
| |
| class FontSetCache { |
| public: |
| static FontSetCache& shared() |
| { |
| DEFINE_STATIC_LOCAL(FontSetCache, cache, ()); |
| return cache; |
| } |
| |
| WebFallbackFont fallbackFontForCharInLocale(WebUChar32 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<CachedFontSet> 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<const char*> isn't smart enough |
| // to hash the string (only does pointer compares). |
| typedef HashMap<AtomicString, OwnPtr<CachedFontSet> > LocaleToCachedFont; |
| LocaleToCachedFont m_setsByLocale; |
| }; |
| |
| void WebFontInfo::fallbackFontForChar(WebUChar32 c, const char* locale, WebFallbackFont* fallbackFont) |
| { |
| *fallbackFont = FontSetCache::shared().fallbackFontForCharInLocale(c, locale); |
| } |
| |
| } // namespace blink |