ae16d2424af751d95aa7276246ae97e3220bf6b0
[WebKit-https.git] / Source / WebCore / platform / graphics / freetype / FontCacheFreeType.cpp
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 "CairoUtilities.h"
26 #include "FcUniquePtr.h"
27 #include "Font.h"
28 #include "RefPtrCairo.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
36 #if PLATFORM(GTK)
37 #include "GtkUtilities.h"
38 #endif
39
40 namespace WebCore {
41
42 void FontCache::platformInit()
43 {
44     // It's fine to call FcInit multiple times per the documentation.
45     if (!FcInit())
46         ASSERT_NOT_REACHED();
47 }
48
49 static RefPtr<FcPattern> createFontConfigPatternForCharacters(const UChar* characters, int bufferLength)
50 {
51     RefPtr<FcPattern> pattern = adoptRef(FcPatternCreate());
52     FcUniquePtr<FcCharSet> fontConfigCharSet(FcCharSetCreate());
53
54     UTF16UChar32Iterator iterator(characters, bufferLength);
55     UChar32 character = iterator.next();
56     while (character != iterator.end()) {
57         FcCharSetAddChar(fontConfigCharSet.get(), character);
58         character = iterator.next();
59     }
60
61     FcPatternAddCharSet(pattern.get(), FC_CHARSET, fontConfigCharSet.get());
62
63     FcPatternAddBool(pattern.get(), FC_SCALABLE, FcTrue);
64     FcConfigSubstitute(nullptr, pattern.get(), FcMatchPattern);
65     cairo_ft_font_options_substitute(getDefaultCairoFontOptions(), pattern.get());
66     FcDefaultSubstitute(pattern.get());
67     return pattern;
68 }
69
70 static RefPtr<FcPattern> findBestFontGivenFallbacks(const FontPlatformData& fontData, FcPattern* pattern)
71 {
72     FcFontSet* fallbacks = fontData.fallbacks();
73     if (!fallbacks)
74         return nullptr;
75
76     FcResult fontConfigResult;
77     return FcFontSetMatch(nullptr, &fallbacks, 1, pattern, &fontConfigResult);
78 }
79
80 RefPtr<Font> FontCache::systemFallbackForCharacters(const FontDescription& description, const Font* originalFontData, bool, const UChar* characters, unsigned length)
81 {
82     RefPtr<FcPattern> pattern = createFontConfigPatternForCharacters(characters, length);
83     const FontPlatformData& fontData = originalFontData->platformData();
84
85     RefPtr<FcPattern> fallbackPattern = findBestFontGivenFallbacks(fontData, pattern.get());
86     if (fallbackPattern) {
87         FontPlatformData alternateFontData(fallbackPattern.get(), description);
88         return fontForPlatformData(alternateFontData);
89     }
90
91     FcResult fontConfigResult;
92     RefPtr<FcPattern> resultPattern = adoptRef(FcFontMatch(nullptr, pattern.get(), &fontConfigResult));
93     if (!resultPattern)
94         return nullptr;
95     FontPlatformData alternateFontData(resultPattern.get(), description);
96     return fontForPlatformData(alternateFontData);
97 }
98
99 static Vector<String> patternToFamilies(FcPattern& pattern)
100 {
101     char* patternChars = reinterpret_cast<char*>(FcPatternFormat(&pattern, reinterpret_cast<const FcChar8*>("%{family}")));
102     String patternString = String::fromUTF8(patternChars);
103     free(patternChars);
104
105     Vector<String> results;
106     patternString.split(',', results);
107     return results;
108 }
109
110 Vector<String> FontCache::systemFontFamilies()
111 {
112     RefPtr<FcPattern> scalablesOnlyPattern = adoptRef(FcPatternCreate());
113     FcPatternAddBool(scalablesOnlyPattern.get(), FC_SCALABLE, FcTrue);
114
115     FcUniquePtr<FcObjectSet> familiesOnly(FcObjectSetBuild(FC_FAMILY, nullptr));
116     FcUniquePtr<FcFontSet> fontSet(FcFontList(nullptr, scalablesOnlyPattern.get(), familiesOnly.get()));
117
118     Vector<String> fontFamilies;
119     for (int i = 0; i < fontSet->nfont; i++) {
120         FcPattern* pattern = fontSet->fonts[i];
121         FcChar8* family = nullptr;
122         FcPatternGetString(pattern, FC_FAMILY, 0, &family);
123         if (family)
124             fontFamilies.appendVector(patternToFamilies(*pattern));
125     }
126
127     return fontFamilies;
128 }
129
130 Ref<Font> FontCache::lastResortFallbackFont(const FontDescription& fontDescription)
131 {
132     // We want to return a fallback font here, otherwise the logic preventing FontConfig
133     // matches for non-fallback fonts might return 0. See isFallbackFontAllowed.
134     static AtomicString timesStr("serif");
135     if (RefPtr<Font> font = fontForFamily(fontDescription, timesStr))
136         return *font;
137
138     // This could be reached due to improperly-installed or misconfigured fontconfig.
139     RELEASE_ASSERT_NOT_REACHED();
140 }
141
142 Vector<FontSelectionCapabilities> FontCache::getFontSelectionCapabilitiesInFamily(const AtomicString&)
143 {
144     return { };
145 }
146
147 static String getFamilyNameStringFromFamily(const AtomicString& family)
148 {
149     // If we're creating a fallback font (e.g. "-webkit-monospace"), convert the name into
150     // the fallback name (like "monospace") that fontconfig understands.
151     if (family.length() && !family.startsWith("-webkit-"))
152         return family.string();
153
154     if (family == standardFamily || family == serifFamily)
155         return "serif";
156     if (family == sansSerifFamily)
157         return "sans-serif";
158     if (family == monospaceFamily)
159         return "monospace";
160     if (family == cursiveFamily)
161         return "cursive";
162     if (family == fantasyFamily)
163         return "fantasy";
164
165 #if PLATFORM(GTK)
166     if (family == systemUiFamily || family == "-webkit-system-font")
167         return defaultGtkSystemFont();
168 #endif
169
170     return "";
171 }
172
173 static int fontWeightToFontconfigWeight(FontSelectionValue weight)
174 {
175     if (weight < FontSelectionValue(150))
176         return FC_WEIGHT_THIN;
177     if (weight < FontSelectionValue(250))
178         return FC_WEIGHT_ULTRALIGHT;
179     if (weight < FontSelectionValue(350))
180         return FC_WEIGHT_LIGHT;
181     if (weight < FontSelectionValue(450))
182         return FC_WEIGHT_REGULAR;
183     if (weight < FontSelectionValue(550))
184         return FC_WEIGHT_MEDIUM;
185     if (weight < FontSelectionValue(650))
186         return FC_WEIGHT_SEMIBOLD;
187     if (weight < FontSelectionValue(750))
188         return FC_WEIGHT_BOLD;
189     if (weight < FontSelectionValue(850))
190         return FC_WEIGHT_EXTRABOLD;
191     return FC_WEIGHT_ULTRABLACK;
192 }
193
194 // This is based on Chromium BSD code from Skia (src/ports/SkFontMgr_fontconfig.cpp). It is a
195 // hack for lack of API in Fontconfig: https://bugs.freedesktop.org/show_bug.cgi?id=19375
196 // FIXME: This is horrible. It should be deleted once Fontconfig can do this itself.
197 enum class AliasStrength {
198     Weak,
199     Strong,
200     Done
201 };
202
203 static AliasStrength strengthOfFirstAlias(const FcPattern& original)
204 {
205     // Ideally there would exist a call like
206     // FcResult FcPatternIsWeak(pattern, object, id, FcBool* isWeak);
207     //
208     // However, there is no such call and as of Fc 2.11.0 even FcPatternEquals ignores the weak bit.
209     // Currently, the only reliable way of finding the weak bit is by its effect on matching.
210     // The weak bit only affects the matching of FC_FAMILY and FC_POSTSCRIPT_NAME object values.
211     // A element with the weak bit is scored after FC_LANG, without the weak bit is scored before.
212     // Note that the weak bit is stored on the element, not on the value it holds.
213     FcValue value;
214     FcResult result = FcPatternGet(&original, FC_FAMILY, 0, &value);
215     if (result != FcResultMatch)
216         return AliasStrength::Done;
217
218     RefPtr<FcPattern> pattern = adoptRef(FcPatternDuplicate(&original));
219     FcBool hasMultipleFamilies = true;
220     while (hasMultipleFamilies)
221         hasMultipleFamilies = FcPatternRemove(pattern.get(), FC_FAMILY, 1);
222
223     // Create a font set with two patterns.
224     // 1. the same FC_FAMILY as pattern and a lang object with only 'nomatchlang'.
225     // 2. a different FC_FAMILY from pattern and a lang object with only 'matchlang'.
226     FcUniquePtr<FcFontSet> fontSet(FcFontSetCreate());
227
228     FcUniquePtr<FcLangSet> strongLangSet(FcLangSetCreate());
229     FcLangSetAdd(strongLangSet.get(), reinterpret_cast<const FcChar8*>("nomatchlang"));
230     // Ownership of this FcPattern will be transferred with FcFontSetAdd.
231     FcPattern* strong = FcPatternDuplicate(pattern.get());
232     FcPatternAddLangSet(strong, FC_LANG, strongLangSet.get());
233
234     FcUniquePtr<FcLangSet> weakLangSet(FcLangSetCreate());
235     FcLangSetAdd(weakLangSet.get(), reinterpret_cast<const FcChar8*>("matchlang"));
236     // Ownership of this FcPattern will be transferred via FcFontSetAdd.
237     FcPattern* weak = FcPatternCreate();
238     FcPatternAddString(weak, FC_FAMILY, reinterpret_cast<const FcChar8*>("nomatchstring"));
239     FcPatternAddLangSet(weak, FC_LANG, weakLangSet.get());
240
241     FcFontSetAdd(fontSet.get(), strong);
242     FcFontSetAdd(fontSet.get(), weak);
243
244     // Add 'matchlang' to the copy of the pattern.
245     FcPatternAddLangSet(pattern.get(), FC_LANG, weakLangSet.get());
246
247     // Run a match against the copy of the pattern.
248     // If the first element was weak, then we should match the pattern with 'matchlang'.
249     // If the first element was strong, then we should match the pattern with 'nomatchlang'.
250
251     // Note that this config is only used for FcFontRenderPrepare, which we don't even want.
252     // However, there appears to be no way to match/sort without it.
253     RefPtr<FcConfig> config = adoptRef(FcConfigCreate());
254     FcFontSet* fontSets[1] = { fontSet.get() };
255     RefPtr<FcPattern> match = adoptRef(FcFontSetMatch(config.get(), fontSets, 1, pattern.get(), &result));
256
257     FcLangSet* matchLangSet;
258     FcPatternGetLangSet(match.get(), FC_LANG, 0, &matchLangSet);
259     return FcLangEqual == FcLangSetHasLang(matchLangSet, reinterpret_cast<const FcChar8*>("matchlang"))
260         ? AliasStrength::Weak : AliasStrength::Strong;
261 }
262
263 static Vector<String> strongAliasesForFamily(const String& family)
264 {
265     RefPtr<FcPattern> pattern = adoptRef(FcPatternCreate());
266     if (!FcPatternAddString(pattern.get(), FC_FAMILY, reinterpret_cast<const FcChar8*>(family.utf8().data())))
267         return Vector<String>();
268
269     FcConfigSubstitute(nullptr, pattern.get(), FcMatchPattern);
270     cairo_ft_font_options_substitute(getDefaultCairoFontOptions(), pattern.get());
271     FcDefaultSubstitute(pattern.get());
272
273     FcUniquePtr<FcObjectSet> familiesOnly(FcObjectSetBuild(FC_FAMILY, nullptr));
274     RefPtr<FcPattern> minimal = adoptRef(FcPatternFilter(pattern.get(), familiesOnly.get()));
275
276     // We really want to match strong (preferred) and same (acceptable) only here.
277     // If a family name was specified, assume that any weak matches after the last strong match
278     // are weak (default) and ignore them.
279     // The reason for is that after substitution the pattern for 'sans-serif' looks like
280     // "wwwwwwwwwwwwwwswww" where there are many weak but preferred names, followed by defaults.
281     // So it is possible to have weakly matching but preferred names.
282     // In aliases, bindings are weak by default, so this is easy and common.
283     // If no family name was specified, we'll probably only get weak matches, but that's ok.
284     int lastStrongId = -1;
285     int numIds = 0;
286     for (int id = 0; ; ++id) {
287         AliasStrength result = strengthOfFirstAlias(*minimal);
288         if (result == AliasStrength::Done) {
289             numIds = id;
290             break;
291         }
292         if (result == AliasStrength::Strong)
293             lastStrongId = id;
294         if (!FcPatternRemove(minimal.get(), FC_FAMILY, 0))
295             return Vector<String>();
296     }
297
298     // If they were all weak, then leave the pattern alone.
299     if (lastStrongId < 0)
300         return Vector<String>();
301
302     // Remove everything after the last strong.
303     for (int id = lastStrongId + 1; id < numIds; ++id) {
304         if (!FcPatternRemove(pattern.get(), FC_FAMILY, lastStrongId + 1)) {
305             ASSERT_NOT_REACHED();
306             return Vector<String>();
307         }
308     }
309
310     return patternToFamilies(*pattern);
311 }
312
313 static bool areStronglyAliased(const String& familyA, const String& familyB)
314 {
315     for (auto& family : strongAliasesForFamily(familyA)) {
316         if (family == familyB)
317             return true;
318     }
319     return false;
320 }
321
322 static inline bool isCommonlyUsedGenericFamily(const String& familyNameString)
323 {
324     return equalLettersIgnoringASCIICase(familyNameString, "sans")
325         || equalLettersIgnoringASCIICase(familyNameString, "sans-serif")
326         || equalLettersIgnoringASCIICase(familyNameString, "serif")
327         || equalLettersIgnoringASCIICase(familyNameString, "monospace")
328         || equalLettersIgnoringASCIICase(familyNameString, "fantasy")
329 #if PLATFORM(GTK)
330         || equalLettersIgnoringASCIICase(familyNameString, "-webkit-system-font")
331         || equalLettersIgnoringASCIICase(familyNameString, "-webkit-system-ui")
332 #endif
333         || equalLettersIgnoringASCIICase(familyNameString, "cursive");
334 }
335
336 std::unique_ptr<FontPlatformData> FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family, const FontFeatureSettings*, const FontVariantSettings*, FontSelectionSpecifiedCapabilities)
337 {
338     // The CSS font matching algorithm (http://www.w3.org/TR/css3-fonts/#font-matching-algorithm)
339     // says that we must find an exact match for font family, slant (italic or oblique can be used)
340     // and font weight (we only match bold/non-bold here).
341     RefPtr<FcPattern> pattern = adoptRef(FcPatternCreate());
342     // Never choose unscalable fonts, as they pixelate when displayed at different sizes.
343     FcPatternAddBool(pattern.get(), FC_SCALABLE, FcTrue);
344     String familyNameString(getFamilyNameStringFromFamily(family));
345     if (!FcPatternAddString(pattern.get(), FC_FAMILY, reinterpret_cast<const FcChar8*>(familyNameString.utf8().data())))
346         return nullptr;
347
348     bool italic = fontDescription.italic();
349     if (!FcPatternAddInteger(pattern.get(), FC_SLANT, italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN))
350         return nullptr;
351     if (!FcPatternAddInteger(pattern.get(), FC_WEIGHT, fontWeightToFontconfigWeight(fontDescription.weight())))
352         return nullptr;
353     if (!FcPatternAddDouble(pattern.get(), FC_PIXEL_SIZE, fontDescription.computedPixelSize()))
354         return nullptr;
355
356     // The strategy is originally from Skia (src/ports/SkFontHost_fontconfig.cpp):
357     //
358     // We do not normally allow fontconfig to substitute one font family for another, since this
359     // would break CSS font family fallback: the website should be in control of fallback. During
360     // normal font matching, the only font family substitution permitted is for generic families
361     // (sans, serif, monospace) or for strongly-aliased fonts (which are to be treated as
362     // effectively identical). This is because the font matching step is designed to always find a
363     // match for the font, which we don't want.
364     //
365     // Fontconfig is used in two stages: (1) configuration and (2) matching. During the
366     // configuration step, before any matching occurs, we allow arbitrary family substitutions,
367     // since this is an exact matter of respecting the user's font configuration.
368     FcConfigSubstitute(nullptr, pattern.get(), FcMatchPattern);
369     cairo_ft_font_options_substitute(getDefaultCairoFontOptions(), pattern.get());
370     FcDefaultSubstitute(pattern.get());
371
372     FcChar8* fontConfigFamilyNameAfterConfiguration;
373     FcPatternGetString(pattern.get(), FC_FAMILY, 0, &fontConfigFamilyNameAfterConfiguration);
374     String familyNameAfterConfiguration = String::fromUTF8(reinterpret_cast<char*>(fontConfigFamilyNameAfterConfiguration));
375
376     FcResult fontConfigResult;
377     RefPtr<FcPattern> resultPattern = adoptRef(FcFontMatch(nullptr, pattern.get(), &fontConfigResult));
378     if (!resultPattern) // No match.
379         return nullptr;
380
381     // Loop through each font family of the result to see if it fits the one we requested.
382     bool matchedFontFamily = false;
383     FcChar8* fontConfigFamilyNameAfterMatching;
384     for (int i = 0; FcPatternGetString(resultPattern.get(), FC_FAMILY, i, &fontConfigFamilyNameAfterMatching) == FcResultMatch; ++i) {
385         // If Fontconfig gave us a different font family than the one we requested, we should ignore it
386         // and allow WebCore to give us the next font on the CSS fallback list. The exceptions are if
387         // this family name is a commonly-used generic family, or if the families are strongly-aliased.
388         // Checking for a strong alias comes last, since it is slow.
389         String familyNameAfterMatching = String::fromUTF8(reinterpret_cast<char*>(fontConfigFamilyNameAfterMatching));
390         if (equalIgnoringASCIICase(familyNameAfterConfiguration, familyNameAfterMatching) || isCommonlyUsedGenericFamily(familyNameString) || areStronglyAliased(familyNameAfterConfiguration, familyNameAfterMatching)) {
391             matchedFontFamily = true;
392             break;
393         }
394     }
395
396     if (!matchedFontFamily)
397         return nullptr;
398
399     // Verify that this font has an encoding compatible with Fontconfig. Fontconfig currently
400     // supports three encodings in FcFreeTypeCharIndex: Unicode, Symbol and AppleRoman.
401     // If this font doesn't have one of these three encodings, don't select it.
402     auto platformData = std::make_unique<FontPlatformData>(resultPattern.get(), fontDescription);
403     if (!platformData->hasCompatibleCharmap())
404         return nullptr;
405
406     return platformData;
407 }
408
409 const AtomicString& FontCache::platformAlternateFamilyName(const AtomicString&)
410 {
411     return nullAtom();
412 }
413
414 }