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