FcFontSet *fcinfo_fontformat_family_index(const FcChar8 *format,
const FcPattern *filter)
{
FcFontSet *fontset, *result;
FcPattern *pattern;
FcPattern *font;
FcResult r;
int f;
pattern = FcPatternDuplicate(filter);
FcPatternAddString(pattern, FC_FONTFORMAT, format);
fontset = fcinfo(NULL, pattern, FcTrue, 1, FC_FAMILY);
result = FcFontSetCreate();
for (f = 0; f < fontset->nfont; f++)
{
font = FcFontMatch(NULL, fontset->fonts[f], &r);
assert(r == FcResultMatch);
/* don't add ethio16f-uni.pcf, johabg16.pcf and similar with reported
empty charset */
/* could be done with fcinfo_match(), but that is superfluous
there like for fcinfo_language_font_index() - it will be needed
when we will need to display some kind of specimen in fontformat
index */
if (!empty_charset(font))
FcFontSetAdd(result, FcPatternDuplicate(fontset->fonts[f]));
FcPatternDestroy(font);
}
FcPatternDestroy(pattern);
FcFontSetDestroy(fontset);
return result;
}
static AliasStrength strengthOfFirstAlias(const FcPattern& original)
{
// Ideally there would exist a call like
// FcResult FcPatternIsWeak(pattern, object, id, FcBool* isWeak);
//
// However, there is no such call and as of Fc 2.11.0 even FcPatternEquals ignores the weak bit.
// Currently, the only reliable way of finding the weak bit is by its effect on matching.
// The weak bit only affects the matching of FC_FAMILY and FC_POSTSCRIPT_NAME object values.
// A element with the weak bit is scored after FC_LANG, without the weak bit is scored before.
// Note that the weak bit is stored on the element, not on the value it holds.
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);
// Create a font set with two patterns.
// 1. the same FC_FAMILY as pattern and a lang object with only 'nomatchlang'.
// 2. a different FC_FAMILY from pattern and a lang object with only 'matchlang'.
FcUniquePtr<FcFontSet> fontSet(FcFontSetCreate());
FcUniquePtr<FcLangSet> strongLangSet(FcLangSetCreate());
FcLangSetAdd(strongLangSet.get(), reinterpret_cast<const FcChar8*>("nomatchlang"));
// Ownership of this FcPattern will be transferred with FcFontSetAdd.
FcPattern* strong = FcPatternDuplicate(pattern.get());
FcPatternAddLangSet(strong, FC_LANG, strongLangSet.get());
FcUniquePtr<FcLangSet> weakLangSet(FcLangSetCreate());
FcLangSetAdd(weakLangSet.get(), reinterpret_cast<const FcChar8*>("matchlang"));
// Ownership of this FcPattern will be transferred via FcFontSetAdd.
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);
// Add 'matchlang' to the copy of the pattern.
FcPatternAddLangSet(pattern.get(), FC_LANG, weakLangSet.get());
// Run a match against the copy of the pattern.
// If the first element was weak, then we should match the pattern with 'matchlang'.
// If the first element was strong, then we should match the pattern with 'nomatchlang'.
// Note that this config is only used for FcFontRenderPrepare, which we don't even want.
// However, there appears to be no way to match/sort without it.
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;
}
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());
String familyNameString(getFamilyNameStringFromFontDescriptionAndFamily(fontDescription, family));
if (!FcPatternAddString(pattern.get(), FC_FAMILY, reinterpret_cast<const FcChar8*>(familyNameString.utf8().data())))
return 0;
bool italic = fontDescription.italic();
if (!FcPatternAddInteger(pattern.get(), FC_SLANT, italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN))
return 0;
if (!FcPatternAddInteger(pattern.get(), FC_WEIGHT, fontWeightToFontconfigWeight(fontDescription.weight())))
return 0;
if (!FcPatternAddDouble(pattern.get(), FC_PIXEL_SIZE, fontDescription.computedPixelSize()))
return 0;
// 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 0;
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 0;
// 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.
FontPlatformData* platformData = new FontPlatformData(resultPattern.get(), fontDescription);
if (!platformData->hasCompatibleCharmap()) {
delete platformData;
return 0;
}
return platformData;
}
/* Ask the fontgod for a generic, standard issue "Arial" font */
const char *graph_init_fontconfig(void)
{
/* Offer fontgod sacrificial pointers to hold his highness */
FcConfig *fc_config = FcInitLoadConfigAndFonts();
FcPattern *fc_pattern = FcPatternCreate();
/* Ask the deity for a user-specified gift of typography */
FcPatternAddString(fc_pattern, FC_FAMILY, (const FcChar8 *)option->fontname);
/* Ask fontgod not to blind our eyes for our insolence */
FcPatternAddBool(fc_pattern, FC_ANTIALIAS, 1);
/* Summon a fontdemon which shall transmit the gifts of our god */
FcResult fc_result;
/* Incantation for our omnipotence to recognize our request: */
FcDefaultSubstitute(fc_pattern);
FcConfigSubstitute(fc_config, fc_pattern, FcMatchPattern);
/* "We ask you, oh you in the sky, for your attention..." */
FcPattern *fc_font_chosen = FcFontMatch(fc_config, fc_pattern, &fc_result);
FcValue fc_value;
/* SHOW US YOUR POWER, INVOKE ANCIENT KNOWLEDGE, GIVE US THE LOCATION! */
FcPatternGet(fc_font_chosen, "file", 0, &fc_value);
/* Fontgod has given us a sacred filename, hail FONTCONFIG! */
pprintf(PRI_SPAM, "[FC] Font path received = %s\n", (char *)fc_value.u.s);
return (const char *)fc_value.u.s;
}
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;
}
请发表评论