• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "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 
platformInit()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 
createFontConfigPatternForCharacters(const UChar * characters,int length)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 
findBestFontGivenFallbacks(const FontPlatformData & fontData,FcPattern * pattern)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 
getFontDataForCharacters(const Font & font,const UChar * characters,int length)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 
getSimilarFontPlatformData(const Font & font)103 SimpleFontData* FontCache::getSimilarFontPlatformData(const Font& font)
104 {
105     return 0;
106 }
107 
getLastResortFallbackFont(const FontDescription & fontDescription)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 
getTraitsInFamily(const AtomicString & familyName,Vector<unsigned> & traitsMasks)116 void FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigned>& traitsMasks)
117 {
118 }
119 
getFamilyNameStringFromFontDescriptionAndFamily(const FontDescription & fontDescription,const AtomicString & family)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 
fontWeightToFontconfigWeight(FontWeight weight)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 
createFontPlatformData(const FontDescription & fontDescription,const AtomicString & family)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