Home | History | Annotate | Download | only in freetype
      1 /*
      2  * Copyright (C) 2008 Alp Toker <alp (at) 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 
     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 
     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 
     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 
     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 
    103 SimpleFontData* FontCache::getSimilarFontPlatformData(const Font& font)
    104 {
    105     return 0;
    106 }
    107 
    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 
    116 void FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigned>& traitsMasks)
    117 {
    118 }
    119 
    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 
    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 
    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