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