FontCacheFreeType.cpp [plain text]
#include "config.h"
#include "FontCache.h"
#include "FcUniquePtr.h"
#include "Font.h"
#include "RefPtrCairo.h"
#include "UTF16UChar32Iterator.h"
#include <cairo-ft.h>
#include <cairo.h>
#include <fontconfig/fcfreetype.h>
#include <wtf/Assertions.h>
#include <wtf/text/CString.h>
namespace WebCore {
void FontCache::platformInit()
{
if (!FcInit())
ASSERT_NOT_REACHED();
}
static RefPtr<FcPattern> createFontConfigPatternForCharacters(const UChar* characters, int bufferLength)
{
RefPtr<FcPattern> pattern = adoptRef(FcPatternCreate());
FcUniquePtr<FcCharSet> fontConfigCharSet(FcCharSetCreate());
UTF16UChar32Iterator iterator(characters, bufferLength);
UChar32 character = iterator.next();
while (character != iterator.end()) {
FcCharSetAddChar(fontConfigCharSet.get(), character);
character = iterator.next();
}
FcPatternAddCharSet(pattern.get(), FC_CHARSET, fontConfigCharSet.get());
FcPatternAddBool(pattern.get(), FC_SCALABLE, FcTrue);
FcConfigSubstitute(nullptr, pattern.get(), FcMatchPattern);
FcDefaultSubstitute(pattern.get());
return pattern;
}
static RefPtr<FcPattern> findBestFontGivenFallbacks(const FontPlatformData& fontData, FcPattern* pattern)
{
FcFontSet* fallbacks = fontData.fallbacks();
if (!fallbacks)
return nullptr;
FcResult fontConfigResult;
return FcFontSetMatch(nullptr, &fallbacks, 1, pattern, &fontConfigResult);
}
RefPtr<Font> FontCache::systemFallbackForCharacters(const FontDescription& description, const Font* originalFontData, bool, const UChar* characters, unsigned length)
{
RefPtr<FcPattern> pattern = createFontConfigPatternForCharacters(characters, length);
const FontPlatformData& fontData = originalFontData->platformData();
RefPtr<FcPattern> fallbackPattern = findBestFontGivenFallbacks(fontData, pattern.get());
if (fallbackPattern) {
FontPlatformData alternateFontData(fallbackPattern.get(), description);
return fontForPlatformData(alternateFontData);
}
FcResult fontConfigResult;
RefPtr<FcPattern> resultPattern = adoptRef(FcFontMatch(nullptr, pattern.get(), &fontConfigResult));
if (!resultPattern)
return nullptr;
FontPlatformData alternateFontData(resultPattern.get(), description);
return fontForPlatformData(alternateFontData);
}
static Vector<String> patternToFamilies(FcPattern& pattern)
{
char* patternChars = reinterpret_cast<char*>(FcPatternFormat(&pattern, reinterpret_cast<const FcChar8*>("%{family}")));
String patternString = String::fromUTF8(patternChars);
free(patternChars);
Vector<String> results;
patternString.split(',', results);
return results;
}
Vector<String> FontCache::systemFontFamilies()
{
RefPtr<FcPattern> scalablesOnlyPattern = adoptRef(FcPatternCreate());
FcPatternAddBool(scalablesOnlyPattern.get(), FC_SCALABLE, FcTrue);
FcUniquePtr<FcObjectSet> familiesOnly(FcObjectSetBuild(FC_FAMILY, nullptr));
FcUniquePtr<FcFontSet> fontSet(FcFontList(nullptr, scalablesOnlyPattern.get(), familiesOnly.get()));
Vector<String> fontFamilies;
for (int i = 0; i < fontSet->nfont; i++) {
FcPattern* pattern = fontSet->fonts[i];
FcChar8* family = nullptr;
FcPatternGetString(pattern, FC_FAMILY, 0, &family);
if (family)
fontFamilies.appendVector(patternToFamilies(*pattern));
}
return fontFamilies;
}
Ref<Font> FontCache::lastResortFallbackFont(const FontDescription& fontDescription)
{
static AtomicString timesStr("serif");
return *fontForFamily(fontDescription, timesStr);
}
Vector<FontTraitsMask> FontCache::getTraitsInFamily(const AtomicString&)
{
return { };
}
static String getFamilyNameStringFromFamily(const AtomicString& family)
{
if (family.length() && !family.startsWith("-webkit-"))
return family.string();
if (family == standardFamily || family == serifFamily)
return "serif";
if (family == sansSerifFamily)
return "sans-serif";
if (family == monospaceFamily)
return "monospace";
if (family == cursiveFamily)
return "cursive";
if (family == fantasyFamily)
return "fantasy";
return "";
}
static int fontWeightToFontconfigWeight(FontWeight weight)
{
switch (weight) {
case FontWeight100:
return FC_WEIGHT_THIN;
case FontWeight200:
return FC_WEIGHT_ULTRALIGHT;
case FontWeight300:
return FC_WEIGHT_LIGHT;
case FontWeight400:
return FC_WEIGHT_REGULAR;
case FontWeight500:
return FC_WEIGHT_MEDIUM;
case FontWeight600:
return FC_WEIGHT_SEMIBOLD;
case FontWeight700:
return FC_WEIGHT_BOLD;
case FontWeight800:
return FC_WEIGHT_EXTRABOLD;
case FontWeight900:
return FC_WEIGHT_ULTRABLACK;
default:
ASSERT_NOT_REACHED();
return FC_WEIGHT_REGULAR;
}
}
enum class AliasStrength {
Weak,
Strong,
Done
};
static AliasStrength strengthOfFirstAlias(const FcPattern& original)
{
FcValue value;
FcResult result = FcPatternGet(&original, FC_FAMILY, 0, &value);
if (result != FcResultMatch)
return AliasStrength::Done;
RefPtr<FcPattern> pattern = adoptRef(FcPatternDuplicate(&original));
FcBool hasMultipleFamilies = true;
while (hasMultipleFamilies)
hasMultipleFamilies = FcPatternRemove(pattern.get(), FC_FAMILY, 1);
FcUniquePtr<FcFontSet> fontSet(FcFontSetCreate());
FcUniquePtr<FcLangSet> strongLangSet(FcLangSetCreate());
FcLangSetAdd(strongLangSet.get(), reinterpret_cast<const FcChar8*>("nomatchlang"));
FcPattern* strong = FcPatternDuplicate(pattern.get());
FcPatternAddLangSet(strong, FC_LANG, strongLangSet.get());
FcUniquePtr<FcLangSet> weakLangSet(FcLangSetCreate());
FcLangSetAdd(weakLangSet.get(), reinterpret_cast<const FcChar8*>("matchlang"));
FcPattern* weak = FcPatternCreate();
FcPatternAddString(weak, FC_FAMILY, reinterpret_cast<const FcChar8*>("nomatchstring"));
FcPatternAddLangSet(weak, FC_LANG, weakLangSet.get());
FcFontSetAdd(fontSet.get(), strong);
FcFontSetAdd(fontSet.get(), weak);
FcPatternAddLangSet(pattern.get(), FC_LANG, weakLangSet.get());
RefPtr<FcConfig> config = adoptRef(FcConfigCreate());
FcFontSet* fontSets[1] = { fontSet.get() };
RefPtr<FcPattern> match = adoptRef(FcFontSetMatch(config.get(), fontSets, 1, pattern.get(), &result));
FcLangSet* matchLangSet;
FcPatternGetLangSet(match.get(), FC_LANG, 0, &matchLangSet);
return FcLangEqual == FcLangSetHasLang(matchLangSet, reinterpret_cast<const FcChar8*>("matchlang"))
? AliasStrength::Weak : AliasStrength::Strong;
}
static Vector<String> strongAliasesForFamily(const String& family)
{
RefPtr<FcPattern> pattern = adoptRef(FcPatternCreate());
if (!FcPatternAddString(pattern.get(), FC_FAMILY, reinterpret_cast<const FcChar8*>(family.utf8().data())))
return Vector<String>();
FcConfigSubstitute(nullptr, pattern.get(), FcMatchPattern);
FcDefaultSubstitute(pattern.get());
FcUniquePtr<FcObjectSet> familiesOnly(FcObjectSetBuild(FC_FAMILY, nullptr));
RefPtr<FcPattern> minimal = adoptRef(FcPatternFilter(pattern.get(), familiesOnly.get()));
int lastStrongId = -1;
int numIds = 0;
for (int id = 0; ; ++id) {
AliasStrength result = strengthOfFirstAlias(*minimal);
if (result == AliasStrength::Done) {
numIds = id;
break;
}
if (result == AliasStrength::Strong)
lastStrongId = id;
if (!FcPatternRemove(minimal.get(), FC_FAMILY, 0))
return Vector<String>();
}
if (lastStrongId < 0)
return Vector<String>();
for (int id = lastStrongId + 1; id < numIds; ++id) {
if (!FcPatternRemove(pattern.get(), FC_FAMILY, lastStrongId + 1)) {
ASSERT_NOT_REACHED();
return Vector<String>();
}
}
return patternToFamilies(*pattern);
}
static bool areStronglyAliased(const String& familyA, const String& familyB)
{
for (auto& family : strongAliasesForFamily(familyA)) {
if (family == familyB)
return true;
}
return false;
}
static inline bool isCommonlyUsedGenericFamily(const String& familyNameString)
{
return equalLettersIgnoringASCIICase(familyNameString, "sans")
|| equalLettersIgnoringASCIICase(familyNameString, "sans-serif")
|| equalLettersIgnoringASCIICase(familyNameString, "serif")
|| equalLettersIgnoringASCIICase(familyNameString, "monospace")
|| equalLettersIgnoringASCIICase(familyNameString, "fantasy")
|| equalLettersIgnoringASCIICase(familyNameString, "cursive");
}
std::unique_ptr<FontPlatformData> FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family, const FontFeatureSettings*, const FontVariantSettings*)
{
RefPtr<FcPattern> pattern = adoptRef(FcPatternCreate());
FcPatternAddBool(pattern.get(), FC_SCALABLE, FcTrue);
String familyNameString(getFamilyNameStringFromFamily(family));
if (!FcPatternAddString(pattern.get(), FC_FAMILY, reinterpret_cast<const FcChar8*>(familyNameString.utf8().data())))
return nullptr;
bool italic = fontDescription.italic();
if (!FcPatternAddInteger(pattern.get(), FC_SLANT, italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN))
return nullptr;
if (!FcPatternAddInteger(pattern.get(), FC_WEIGHT, fontWeightToFontconfigWeight(fontDescription.weight())))
return nullptr;
if (!FcPatternAddDouble(pattern.get(), FC_PIXEL_SIZE, fontDescription.computedPixelSize()))
return nullptr;
FcConfigSubstitute(nullptr, pattern.get(), FcMatchPattern);
FcDefaultSubstitute(pattern.get());
FcChar8* fontConfigFamilyNameAfterConfiguration;
FcPatternGetString(pattern.get(), FC_FAMILY, 0, &fontConfigFamilyNameAfterConfiguration);
String familyNameAfterConfiguration = String::fromUTF8(reinterpret_cast<char*>(fontConfigFamilyNameAfterConfiguration));
FcResult fontConfigResult;
RefPtr<FcPattern> resultPattern = adoptRef(FcFontMatch(nullptr, pattern.get(), &fontConfigResult));
if (!resultPattern) return nullptr;
FcChar8* fontConfigFamilyNameAfterMatching;
FcPatternGetString(resultPattern.get(), FC_FAMILY, 0, &fontConfigFamilyNameAfterMatching);
String familyNameAfterMatching = String::fromUTF8(reinterpret_cast<char*>(fontConfigFamilyNameAfterMatching));
if (!equalIgnoringASCIICase(familyNameAfterConfiguration, familyNameAfterMatching) && !isCommonlyUsedGenericFamily(familyNameString) && !areStronglyAliased(familyNameAfterConfiguration, familyNameAfterMatching))
return nullptr;
auto platformData = std::make_unique<FontPlatformData>(resultPattern.get(), fontDescription);
if (!platformData->hasCompatibleCharmap())
return nullptr;
return platformData;
}
const AtomicString& FontCache::platformAlternateFamilyName(const AtomicString&)
{
return nullAtom;
}
}