1/* 2 * Copyright (C) 2008 Alp Toker <alp@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 "Font.h" 26#include "OwnPtrCairo.h" 27#include "RefPtrCairo.h" 28#include "SimpleFontData.h" 29#include "UTF16UChar32Iterator.h" 30#include <cairo-ft.h> 31#include <cairo.h> 32#include <fontconfig/fcfreetype.h> 33#include <wtf/Assertions.h> 34#include <wtf/text/CString.h> 35 36namespace WebCore { 37 38void FontCache::platformInit() 39{ 40 // It's fine to call FcInit multiple times per the documentation. 41 if (!FcInit()) 42 ASSERT_NOT_REACHED(); 43} 44 45FcPattern* createFontConfigPatternForCharacters(const UChar* characters, int bufferLength) 46{ 47 FcPattern* pattern = FcPatternCreate(); 48 FcCharSet* fontConfigCharSet = FcCharSetCreate(); 49 50 UTF16UChar32Iterator iterator(characters, bufferLength); 51 UChar32 character = iterator.next(); 52 while (character != iterator.end()) { 53 FcCharSetAddChar(fontConfigCharSet, character); 54 character = iterator.next(); 55 } 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 66FcPattern* 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 84PassRefPtr<SimpleFontData> FontCache::systemFallbackForCharacters(const FontDescription& description, const SimpleFontData* originalFontData, bool, const UChar* characters, int length) 85{ 86 RefPtr<FcPattern> pattern = adoptRef(createFontConfigPatternForCharacters(characters, length)); 87 const FontPlatformData& fontData = originalFontData->platformData(); 88 89 RefPtr<FcPattern> fallbackPattern = adoptRef(findBestFontGivenFallbacks(fontData, pattern.get())); 90 if (fallbackPattern) { 91 FontPlatformData alternateFontData(fallbackPattern.get(), description); 92 return getCachedFontData(&alternateFontData, DoNotRetain); 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(), description); 100 return getCachedFontData(&alternateFontData, DoNotRetain); 101} 102 103PassRefPtr<SimpleFontData> FontCache::getLastResortFallbackFont(const FontDescription& fontDescription, ShouldRetain shouldRetain) 104{ 105 // We want to return a fallback font here, otherwise the logic preventing FontConfig 106 // matches for non-fallback fonts might return 0. See isFallbackFontAllowed. 107 static AtomicString timesStr("serif"); 108 return getCachedFontData(fontDescription, timesStr, false, shouldRetain); 109} 110 111void FontCache::getTraitsInFamily(const AtomicString&, Vector<unsigned>&) 112{ 113} 114 115static String getFamilyNameStringFromFontDescriptionAndFamily(const FontDescription& fontDescription, const AtomicString& family) 116{ 117 // If we're creating a fallback font (e.g. "-webkit-monospace"), convert the name into 118 // the fallback name (like "monospace") that fontconfig understands. 119 if (family.length() && !family.startsWith("-webkit-")) 120 return family.string(); 121 122 switch (fontDescription.genericFamily()) { 123 case FontDescription::StandardFamily: 124 case FontDescription::SerifFamily: 125 return "serif"; 126 case FontDescription::SansSerifFamily: 127 return "sans-serif"; 128 case FontDescription::MonospaceFamily: 129 return "monospace"; 130 case FontDescription::CursiveFamily: 131 return "cursive"; 132 case FontDescription::FantasyFamily: 133 return "fantasy"; 134 case FontDescription::NoFamily: 135 default: 136 return ""; 137 } 138} 139 140int fontWeightToFontconfigWeight(FontWeight weight) 141{ 142 switch (weight) { 143 case FontWeight100: 144 return FC_WEIGHT_THIN; 145 case FontWeight200: 146 return FC_WEIGHT_ULTRALIGHT; 147 case FontWeight300: 148 return FC_WEIGHT_LIGHT; 149 case FontWeight400: 150 return FC_WEIGHT_REGULAR; 151 case FontWeight500: 152 return FC_WEIGHT_MEDIUM; 153 case FontWeight600: 154 return FC_WEIGHT_SEMIBOLD; 155 case FontWeight700: 156 return FC_WEIGHT_BOLD; 157 case FontWeight800: 158 return FC_WEIGHT_EXTRABOLD; 159 case FontWeight900: 160 return FC_WEIGHT_ULTRABLACK; 161 default: 162 ASSERT_NOT_REACHED(); 163 return FC_WEIGHT_REGULAR; 164 } 165} 166 167PassOwnPtr<FontPlatformData> FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family) 168{ 169 // The CSS font matching algorithm (http://www.w3.org/TR/css3-fonts/#font-matching-algorithm) 170 // says that we must find an exact match for font family, slant (italic or oblique can be used) 171 // and font weight (we only match bold/non-bold here). 172 RefPtr<FcPattern> pattern = adoptRef(FcPatternCreate()); 173 String familyNameString(getFamilyNameStringFromFontDescriptionAndFamily(fontDescription, family)); 174 if (!FcPatternAddString(pattern.get(), FC_FAMILY, reinterpret_cast<const FcChar8*>(familyNameString.utf8().data()))) 175 return nullptr; 176 177 bool italic = fontDescription.italic(); 178 if (!FcPatternAddInteger(pattern.get(), FC_SLANT, italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN)) 179 return nullptr; 180 if (!FcPatternAddInteger(pattern.get(), FC_WEIGHT, fontWeightToFontconfigWeight(fontDescription.weight()))) 181 return nullptr; 182 if (!FcPatternAddDouble(pattern.get(), FC_PIXEL_SIZE, fontDescription.computedPixelSize())) 183 return nullptr; 184 185 // The strategy is originally from Skia (src/ports/SkFontHost_fontconfig.cpp): 186 187 // Allow Fontconfig to do pre-match substitution. Unless we are accessing a "fallback" 188 // family like "sans," this is the only time we allow Fontconfig to substitute one 189 // family name for another (i.e. if the fonts are aliased to each other). 190 FcConfigSubstitute(0, pattern.get(), FcMatchPattern); 191 FcDefaultSubstitute(pattern.get()); 192 193 FcChar8* fontConfigFamilyNameAfterConfiguration; 194 FcPatternGetString(pattern.get(), FC_FAMILY, 0, &fontConfigFamilyNameAfterConfiguration); 195 String familyNameAfterConfiguration = String::fromUTF8(reinterpret_cast<char*>(fontConfigFamilyNameAfterConfiguration)); 196 197 FcResult fontConfigResult; 198 RefPtr<FcPattern> resultPattern = adoptRef(FcFontMatch(0, pattern.get(), &fontConfigResult)); 199 if (!resultPattern) // No match. 200 return nullptr; 201 202 FcChar8* fontConfigFamilyNameAfterMatching; 203 FcPatternGetString(resultPattern.get(), FC_FAMILY, 0, &fontConfigFamilyNameAfterMatching); 204 String familyNameAfterMatching = String::fromUTF8(reinterpret_cast<char*>(fontConfigFamilyNameAfterMatching)); 205 206 // If Fontconfig gave use a different font family than the one we requested, we should ignore it 207 // and allow WebCore to give us the next font on the CSS fallback list. The only exception is if 208 // this family name is a commonly used generic family. 209 if (!equalIgnoringCase(familyNameAfterConfiguration, familyNameAfterMatching) 210 && !(equalIgnoringCase(familyNameString, "sans") || equalIgnoringCase(familyNameString, "sans-serif") 211 || equalIgnoringCase(familyNameString, "serif") || equalIgnoringCase(familyNameString, "monospace") 212 || equalIgnoringCase(familyNameString, "fantasy") || equalIgnoringCase(familyNameString, "cursive"))) 213 return nullptr; 214 215 // Verify that this font has an encoding compatible with Fontconfig. Fontconfig currently 216 // supports three encodings in FcFreeTypeCharIndex: Unicode, Symbol and AppleRoman. 217 // If this font doesn't have one of these three encodings, don't select it. 218 OwnPtr<FontPlatformData> platformData = adoptPtr(new FontPlatformData(resultPattern.get(), fontDescription)); 219 if (!platformData->hasCompatibleCharmap()) 220 return nullptr; 221 222 return platformData.release(); 223} 224 225} 226