1 /* 2 * Copyright (C) 2009 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 #include "public/platform/linux/WebFontInfo.h" 33 34 #include "public/platform/linux/WebFallbackFont.h" 35 #include "wtf/HashMap.h" 36 #include "wtf/Noncopyable.h" 37 #include "wtf/OwnPtr.h" 38 #include "wtf/PassOwnPtr.h" 39 #include "wtf/Vector.h" 40 #include "wtf/text/AtomicString.h" 41 #include "wtf/text/AtomicStringHash.h" 42 #include <fontconfig/fontconfig.h> 43 #include <string.h> 44 #include <unicode/utf16.h> 45 46 namespace blink { 47 48 class CachedFont { 49 public: 50 // Note: We pass the charset explicitly as callers 51 // should not create CachedFont entries without knowing 52 // that the FcPattern contains a valid charset. 53 CachedFont(FcPattern* pattern, FcCharSet* charSet) 54 : m_supportedCharacters(charSet) 55 { 56 ASSERT(pattern); 57 ASSERT(charSet); 58 m_fallbackFont.name = fontName(pattern); 59 m_fallbackFont.filename = fontFilename(pattern); 60 m_fallbackFont.ttcIndex = fontTtcIndex(pattern); 61 m_fallbackFont.isBold = fontIsBold(pattern); 62 m_fallbackFont.isItalic = fontIsItalic(pattern); 63 } 64 const WebFallbackFont& fallbackFont() const { return m_fallbackFont; } 65 bool hasGlyphForCharacter(WebUChar32 c) 66 { 67 return m_supportedCharacters && FcCharSetHasChar(m_supportedCharacters, c); 68 } 69 70 private: 71 static WebCString fontName(FcPattern* pattern) 72 { 73 FcChar8* familyName = nullptr; 74 if (FcPatternGetString(pattern, FC_FAMILY, 0, &familyName) != FcResultMatch) 75 return WebCString(); 76 77 // FCChar8 is unsigned char, so we cast to char for WebCString. 78 const char* charFamily = reinterpret_cast<char*>(familyName); 79 return WebCString(charFamily, strlen(charFamily)); 80 } 81 82 static WebCString fontFilename(FcPattern* pattern) 83 { 84 FcChar8* cFilename = nullptr; 85 if (FcPatternGetString(pattern, FC_FILE, 0, &cFilename) != FcResultMatch) 86 return WebCString(); 87 const char* fontFilename = reinterpret_cast<char*>(cFilename); 88 return WebCString(fontFilename, strlen(fontFilename)); 89 } 90 91 static int fontTtcIndex(FcPattern* pattern) 92 { 93 int ttcIndex = -1; 94 if (FcPatternGetInteger(pattern, FC_INDEX, 0, &ttcIndex) != FcResultMatch || ttcIndex < 0) 95 return 0; 96 return ttcIndex; 97 } 98 99 static bool fontIsBold(FcPattern* pattern) 100 { 101 int weight = 0; 102 if (FcPatternGetInteger(pattern, FC_WEIGHT, 0, &weight) != FcResultMatch) 103 return false; 104 return weight >= FC_WEIGHT_BOLD; 105 } 106 107 static bool fontIsItalic(FcPattern* pattern) 108 { 109 int slant = 0; 110 if (FcPatternGetInteger(pattern, FC_SLANT, 0, &slant) != FcResultMatch) 111 return false; 112 return slant != FC_SLANT_ROMAN; 113 } 114 115 WebFallbackFont m_fallbackFont; 116 // m_supportedCharaters is owned by the parent 117 // FcFontSet and should never be freed. 118 FcCharSet* m_supportedCharacters; 119 }; 120 121 122 class CachedFontSet { 123 WTF_MAKE_NONCOPYABLE(CachedFontSet); 124 public: 125 // CachedFontSet takes ownership of the passed FcFontSet. 126 static PassOwnPtr<CachedFontSet> createForLocale(const char* locale) 127 { 128 FcFontSet* fontSet = createFcFontSetForLocale(locale); 129 return adoptPtr(new CachedFontSet(fontSet)); 130 } 131 132 ~CachedFontSet() 133 { 134 m_fallbackList.clear(); 135 FcFontSetDestroy(m_fontSet); 136 } 137 138 WebFallbackFont fallbackFontForChar(WebUChar32 c) 139 { 140 Vector<CachedFont>::iterator itr = m_fallbackList.begin(); 141 for (; itr != m_fallbackList.end(); itr++) { 142 if (itr->hasGlyphForCharacter(c)) 143 return itr->fallbackFont(); 144 } 145 // The previous code just returned garbage if the user didn't 146 // have the necessary fonts, this seems better than garbage. 147 // Current callers happen to ignore any values with an empty family string. 148 return WebFallbackFont(); 149 } 150 151 private: 152 static FcFontSet* createFcFontSetForLocale(const char* locale) 153 { 154 FcPattern* pattern = FcPatternCreate(); 155 156 if (locale) { 157 // FcChar* is unsigned char* so we have to cast. 158 FcPatternAddString(pattern, FC_LANG, reinterpret_cast<const FcChar8*>(locale)); 159 } 160 161 FcValue fcvalue; 162 fcvalue.type = FcTypeBool; 163 fcvalue.u.b = FcTrue; 164 FcPatternAdd(pattern, FC_SCALABLE, fcvalue, FcFalse); 165 166 FcConfigSubstitute(0, pattern, FcMatchPattern); 167 FcDefaultSubstitute(pattern); 168 169 if (!locale) 170 FcPatternDel(pattern, FC_LANG); 171 172 // The result parameter returns if any fonts were found. 173 // We already handle 0 fonts correctly, so we ignore the param. 174 FcResult result; 175 FcFontSet* fontSet = FcFontSort(0, pattern, 0, 0, &result); 176 FcPatternDestroy(pattern); 177 178 // The caller will take ownership of this FcFontSet. 179 return fontSet; 180 } 181 182 CachedFontSet(FcFontSet* fontSet) 183 : m_fontSet(fontSet) 184 { 185 fillFallbackList(); 186 } 187 188 void fillFallbackList() 189 { 190 ASSERT(m_fallbackList.isEmpty()); 191 if (!m_fontSet) 192 return; 193 194 for (int i = 0; i < m_fontSet->nfont; ++i) { 195 FcPattern* pattern = m_fontSet->fonts[i]; 196 197 // Ignore any bitmap fonts users may still have installed from last century. 198 FcBool isScalable; 199 if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &isScalable) != FcResultMatch || !isScalable) 200 continue; 201 202 // Ignore any fonts FontConfig knows about, but that we don't have permission to read. 203 FcChar8* cFilename; 204 if (FcPatternGetString(pattern, FC_FILE, 0, &cFilename) != FcResultMatch) 205 continue; 206 if (access(reinterpret_cast<char*>(cFilename), R_OK)) 207 continue; 208 209 // Make sure this font can tell us what characters it has glyphs for. 210 FcCharSet* charSet; 211 if (FcPatternGetCharSet(pattern, FC_CHARSET, 0, &charSet) != FcResultMatch) 212 continue; 213 214 m_fallbackList.append(CachedFont(pattern, charSet)); 215 } 216 } 217 218 FcFontSet* m_fontSet; // Owned by this object. 219 // CachedFont has a FcCharset* which points into the FcFontSet. 220 // If the FcFontSet is ever destroyed, the fallbackList 221 // must be cleared first. 222 Vector<CachedFont> m_fallbackList; 223 }; 224 225 class FontSetCache { 226 public: 227 static FontSetCache& shared() 228 { 229 DEFINE_STATIC_LOCAL(FontSetCache, cache, ()); 230 return cache; 231 } 232 233 WebFallbackFont fallbackFontForCharInLocale(WebUChar32 c, const char* locale) 234 { 235 DEFINE_STATIC_LOCAL(AtomicString, kNoLocale, ("NO_LOCALE_SPECIFIED", AtomicString::ConstructFromLiteral)); 236 AtomicString localeKey; 237 if (locale && strlen(locale)) { 238 localeKey = AtomicString(locale); 239 } else { 240 // String hash computation the m_setsByLocale map needs 241 // a non-empty string. 242 localeKey = kNoLocale; 243 } 244 245 LocaleToCachedFont::iterator itr = m_setsByLocale.find(localeKey); 246 if (itr == m_setsByLocale.end()) { 247 OwnPtr<CachedFontSet> newEntry = CachedFontSet::createForLocale(strlen(locale) ? locale : 0); 248 return m_setsByLocale.add(localeKey, newEntry.release()).storedValue->value->fallbackFontForChar(c); 249 } 250 return itr.get()->value->fallbackFontForChar(c); 251 } 252 // FIXME: We may wish to add a way to prune the cache at a later time. 253 254 private: 255 // FIXME: This shouldn't need to be AtomicString, but 256 // currently HashTraits<const char*> isn't smart enough 257 // to hash the string (only does pointer compares). 258 typedef HashMap<AtomicString, OwnPtr<CachedFontSet> > LocaleToCachedFont; 259 LocaleToCachedFont m_setsByLocale; 260 }; 261 262 void WebFontInfo::fallbackFontForChar(WebUChar32 c, const char* locale, WebFallbackFont* fallbackFont) 263 { 264 *fallbackFont = FontSetCache::shared().fallbackFontForCharInLocale(c, locale); 265 } 266 267 } // namespace blink 268