1 /* 2 * Copyright (C) 2008 Alp Toker <alp (at) atoker.com> 3 * Copyright (C) 2010 Igalia S.L. 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Library General Public 7 * License as published by the Free Software Foundation; either 8 * version 2 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Library General Public License for more details. 14 * 15 * You should have received a copy of the GNU Library General Public License 16 * along with this library; see the file COPYING.LIB. If not, write to 17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 * Boston, MA 02110-1301, USA. 19 * 20 */ 21 22 #include "config.h" 23 #include "FontCache.h" 24 25 #include "CString.h" 26 #include "Font.h" 27 #include "OwnPtrCairo.h" 28 #include "RefPtrCairo.h" 29 #include "SimpleFontData.h" 30 #include <cairo-ft.h> 31 #include <cairo.h> 32 #include <fontconfig/fcfreetype.h> 33 #include <wtf/Assertions.h> 34 35 namespace WebCore { 36 37 void FontCache::platformInit() 38 { 39 // It's fine to call FcInit multiple times per the documentation. 40 if (!FcInit()) 41 ASSERT_NOT_REACHED(); 42 } 43 44 FcPattern* createFontConfigPatternForCharacters(const UChar* characters, int length) 45 { 46 FcPattern* pattern = FcPatternCreate(); 47 48 FcCharSet* fontConfigCharSet = FcCharSetCreate(); 49 for (int i = 0; i < length; ++i) { 50 if (U16_IS_SURROGATE(characters[i]) && U16_IS_SURROGATE_LEAD(characters[i]) 51 && i != length - 1 && U16_IS_TRAIL(characters[i + 1])) { 52 FcCharSetAddChar(fontConfigCharSet, U16_GET_SUPPLEMENTARY(characters[i], characters[i+1])); 53 i++; 54 } else 55 FcCharSetAddChar(fontConfigCharSet, characters[i]); 56 } 57 FcPatternAddCharSet(pattern, FC_CHARSET, fontConfigCharSet); 58 FcCharSetDestroy(fontConfigCharSet); 59 60 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue); 61 FcConfigSubstitute(0, pattern, FcMatchPattern); 62 FcDefaultSubstitute(pattern); 63 return pattern; 64 } 65 66 FcPattern* findBestFontGivenFallbacks(const FontPlatformData& fontData, FcPattern* pattern) 67 { 68 if (!fontData.m_pattern) 69 return 0; 70 71 if (!fontData.m_fallbacks) { 72 FcResult fontConfigResult; 73 fontData.m_fallbacks = FcFontSort(0, fontData.m_pattern.get(), FcTrue, 0, &fontConfigResult); 74 } 75 76 if (!fontData.m_fallbacks) 77 return 0; 78 79 FcFontSet* sets[] = { fontData.m_fallbacks }; 80 FcResult fontConfigResult; 81 return FcFontSetMatch(0, sets, 1, pattern, &fontConfigResult); 82 } 83 84 const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, const UChar* characters, int length) 85 { 86 RefPtr<FcPattern> pattern = adoptRef(createFontConfigPatternForCharacters(characters, length)); 87 const FontPlatformData& fontData = font.primaryFont()->platformData(); 88 89 RefPtr<FcPattern> fallbackPattern = adoptRef(findBestFontGivenFallbacks(fontData, pattern.get())); 90 if (fallbackPattern) { 91 FontPlatformData alternateFontData(fallbackPattern.get(), font.fontDescription()); 92 return getCachedFontData(&alternateFontData); 93 } 94 95 FcResult fontConfigResult; 96 RefPtr<FcPattern> resultPattern = adoptRef(FcFontMatch(0, pattern.get(), &fontConfigResult)); 97 if (!resultPattern) 98 return 0; 99 FontPlatformData alternateFontData(resultPattern.get(), font.fontDescription()); 100 return getCachedFontData(&alternateFontData); 101 } 102 103 SimpleFontData* FontCache::getSimilarFontPlatformData(const Font& font) 104 { 105 return 0; 106 } 107 108 SimpleFontData* FontCache::getLastResortFallbackFont(const FontDescription& fontDescription) 109 { 110 // We want to return a fallback font here, otherwise the logic preventing FontConfig 111 // matches for non-fallback fonts might return 0. See isFallbackFontAllowed. 112 static AtomicString timesStr("serif"); 113 return getCachedFontData(fontDescription, timesStr); 114 } 115 116 void FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigned>& traitsMasks) 117 { 118 } 119 120 static String getFamilyNameStringFromFontDescriptionAndFamily(const FontDescription& fontDescription, const AtomicString& family) 121 { 122 // If we're creating a fallback font (e.g. "-webkit-monospace"), convert the name into 123 // the fallback name (like "monospace") that fontconfig understands. 124 if (family.length() && !family.startsWith("-webkit-")) 125 return family.string(); 126 127 switch (fontDescription.genericFamily()) { 128 case FontDescription::StandardFamily: 129 case FontDescription::SerifFamily: 130 return "serif"; 131 case FontDescription::SansSerifFamily: 132 return "sans-serif"; 133 case FontDescription::MonospaceFamily: 134 return "monospace"; 135 case FontDescription::CursiveFamily: 136 return "cursive"; 137 case FontDescription::FantasyFamily: 138 return "fantasy"; 139 case FontDescription::NoFamily: 140 default: 141 return ""; 142 } 143 } 144 145 int fontWeightToFontconfigWeight(FontWeight weight) 146 { 147 switch (weight) { 148 case FontWeight100: 149 return FC_WEIGHT_THIN; 150 case FontWeight200: 151 return FC_WEIGHT_ULTRALIGHT; 152 case FontWeight300: 153 return FC_WEIGHT_LIGHT; 154 case FontWeight400: 155 return FC_WEIGHT_REGULAR; 156 case FontWeight500: 157 return FC_WEIGHT_MEDIUM; 158 case FontWeight600: 159 return FC_WEIGHT_SEMIBOLD; 160 case FontWeight700: 161 return FC_WEIGHT_BOLD; 162 case FontWeight800: 163 return FC_WEIGHT_EXTRABOLD; 164 case FontWeight900: 165 return FC_WEIGHT_ULTRABLACK; 166 default: 167 ASSERT_NOT_REACHED(); 168 return FC_WEIGHT_REGULAR; 169 } 170 } 171 172 FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family) 173 { 174 // The CSS font matching algorithm (http://www.w3.org/TR/css3-fonts/#font-matching-algorithm) 175 // says that we must find an exact match for font family, slant (italic or oblique can be used) 176 // and font weight (we only match bold/non-bold here). 177 RefPtr<FcPattern> pattern = adoptRef(FcPatternCreate()); 178 String familyNameString(getFamilyNameStringFromFontDescriptionAndFamily(fontDescription, family)); 179 if (!FcPatternAddString(pattern.get(), FC_FAMILY, reinterpret_cast<const FcChar8*>(familyNameString.utf8().data()))) 180 return 0; 181 182 bool italic = fontDescription.italic(); 183 if (!FcPatternAddInteger(pattern.get(), FC_SLANT, italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN)) 184 return 0; 185 if (!FcPatternAddInteger(pattern.get(), FC_WEIGHT, fontWeightToFontconfigWeight(fontDescription.weight()))) 186 return 0; 187 if (!FcPatternAddDouble(pattern.get(), FC_PIXEL_SIZE, fontDescription.computedPixelSize())) 188 return 0; 189 190 // The strategy is originally from Skia (src/ports/SkFontHost_fontconfig.cpp): 191 192 // Allow Fontconfig to do pre-match substitution. Unless we are accessing a "fallback" 193 // family like "sans," this is the only time we allow Fontconfig to substitute one 194 // family name for another (i.e. if the fonts are aliased to each other). 195 FcConfigSubstitute(0, pattern.get(), FcMatchPattern); 196 FcDefaultSubstitute(pattern.get()); 197 198 FcChar8* fontConfigFamilyNameAfterConfiguration; 199 FcPatternGetString(pattern.get(), FC_FAMILY, 0, &fontConfigFamilyNameAfterConfiguration); 200 String familyNameAfterConfiguration = String::fromUTF8(reinterpret_cast<char*>(fontConfigFamilyNameAfterConfiguration)); 201 202 FcResult fontConfigResult; 203 RefPtr<FcPattern> resultPattern = adoptRef(FcFontMatch(0, pattern.get(), &fontConfigResult)); 204 if (!resultPattern) // No match. 205 return 0; 206 207 FcChar8* fontConfigFamilyNameAfterMatching; 208 FcPatternGetString(resultPattern.get(), FC_FAMILY, 0, &fontConfigFamilyNameAfterMatching); 209 String familyNameAfterMatching = String::fromUTF8(reinterpret_cast<char*>(fontConfigFamilyNameAfterMatching)); 210 211 // If Fontconfig gave use a different font family than the one we requested, we should ignore it 212 // and allow WebCore to give us the next font on the CSS fallback list. The only exception is if 213 // this family name is a commonly used generic family. 214 if (!equalIgnoringCase(familyNameAfterConfiguration, familyNameAfterMatching) 215 && !(equalIgnoringCase(familyNameString, "sans") || equalIgnoringCase(familyNameString, "sans-serif") 216 || equalIgnoringCase(familyNameString, "serif") || equalIgnoringCase(familyNameString, "monospace") 217 || equalIgnoringCase(familyNameString, "fantasy") || equalIgnoringCase(familyNameString, "cursive"))) 218 return 0; 219 220 // Verify that this font has an encoding compatible with Fontconfig. Fontconfig currently 221 // supports three encodings in FcFreeTypeCharIndex: Unicode, Symbol and AppleRoman. 222 // If this font doesn't have one of these three encodings, don't select it. 223 FontPlatformData* platformData = new FontPlatformData(resultPattern.get(), fontDescription); 224 if (!platformData->hasCompatibleCharmap()) { 225 delete platformData; 226 return 0; 227 } 228 229 return platformData; 230 } 231 232 } 233