std::unique_ptr<FontPlatformData> FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family) { // The CSS font matching algorithm (http://www.w3.org/TR/css3-fonts/#font-matching-algorithm) // says that we must find an exact match for font family, slant (italic or oblique can be used) // and font weight (we only match bold/non-bold here). RefPtr<FcPattern> pattern = adoptRef(FcPatternCreate()); // Never choose unscalable fonts, as they pixelate when displayed at different sizes. 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; // The strategy is originally from Skia (src/ports/SkFontHost_fontconfig.cpp): // Allow Fontconfig to do pre-match substitution. Unless we are accessing a "fallback" // family like "sans," this is the only time we allow Fontconfig to substitute one // family name for another (i.e. if the fonts are aliased to each other). FcConfigSubstitute(0, 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(0, pattern.get(), &fontConfigResult)); if (!resultPattern) // No match. return nullptr; FcChar8* fontConfigFamilyNameAfterMatching; FcPatternGetString(resultPattern.get(), FC_FAMILY, 0, &fontConfigFamilyNameAfterMatching); String familyNameAfterMatching = String::fromUTF8(reinterpret_cast<char*>(fontConfigFamilyNameAfterMatching)); // If Fontconfig gave use a different font family than the one we requested, we should ignore it // and allow WebCore to give us the next font on the CSS fallback list. The only exception is if // this family name is a commonly used generic family. if (!equalIgnoringCase(familyNameAfterConfiguration, familyNameAfterMatching) && !(equalIgnoringCase(familyNameString, "sans") || equalIgnoringCase(familyNameString, "sans-serif") || equalIgnoringCase(familyNameString, "serif") || equalIgnoringCase(familyNameString, "monospace") || equalIgnoringCase(familyNameString, "fantasy") || equalIgnoringCase(familyNameString, "cursive"))) return nullptr; // Verify that this font has an encoding compatible with Fontconfig. Fontconfig currently // supports three encodings in FcFreeTypeCharIndex: Unicode, Symbol and AppleRoman. // If this font doesn't have one of these three encodings, don't select it. auto platformData = std::make_unique<FontPlatformData>(resultPattern.get(), fontDescription); if (!platformData->hasCompatibleCharmap()) return nullptr; return platformData; }
std::unique_ptr<FontPlatformData> FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family) { // The CSS font matching algorithm (http://www.w3.org/TR/css3-fonts/#font-matching-algorithm) // says that we must find an exact match for font family, slant (italic or oblique can be used) // and font weight (we only match bold/non-bold here). RefPtr<FcPattern> pattern = adoptRef(FcPatternCreate()); // Never choose unscalable fonts, as they pixelate when displayed at different sizes. 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; // The strategy is originally from Skia (src/ports/SkFontHost_fontconfig.cpp): // // We do not normally allow fontconfig to substitute one font family for another, since this // would break CSS font family fallback: the website should be in control of fallback. During // normal font matching, the only font family substitution permitted is for generic families // (sans, serif, monospace) or for strongly-aliased fonts (which are to be treated as // effectively identical). This is because the font matching step is designed to always find a // match for the font, which we don't want. // // Fontconfig is used in two stages: (1) configuration and (2) matching. During the // configuration step, before any matching occurs, we allow arbitrary family substitutions, // since this is an exact matter of respecting the user's font configuration. 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) // No match. return nullptr; FcChar8* fontConfigFamilyNameAfterMatching; FcPatternGetString(resultPattern.get(), FC_FAMILY, 0, &fontConfigFamilyNameAfterMatching); String familyNameAfterMatching = String::fromUTF8(reinterpret_cast<char*>(fontConfigFamilyNameAfterMatching)); // If Fontconfig gave us a different font family than the one we requested, we should ignore it // and allow WebCore to give us the next font on the CSS fallback list. The exceptions are if // this family name is a commonly-used generic family, or if the families are strongly-aliased. // Checking for a strong alias comes last, since it is slow. if (!equalIgnoringCase(familyNameAfterConfiguration, familyNameAfterMatching) && !(equalIgnoringCase(familyNameString, "sans") || equalIgnoringCase(familyNameString, "sans-serif") || equalIgnoringCase(familyNameString, "serif") || equalIgnoringCase(familyNameString, "monospace") || equalIgnoringCase(familyNameString, "fantasy") || equalIgnoringCase(familyNameString, "cursive")) && !areStronglyAliased(familyNameAfterConfiguration, familyNameAfterMatching)) return nullptr; // Verify that this font has an encoding compatible with Fontconfig. Fontconfig currently // supports three encodings in FcFreeTypeCharIndex: Unicode, Symbol and AppleRoman. // If this font doesn't have one of these three encodings, don't select it. auto platformData = std::make_unique<FontPlatformData>(resultPattern.get(), fontDescription); if (!platformData->hasCompatibleCharmap()) return nullptr; return platformData; }