1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-09 23:34:20 +00:00

FreeType: Make font fallback slightly more robust

On Ubuntu 25.10, which includes Noto Color Emoji, I was seeing that the
FontsDemo would assert when attempting to render non-emoji text using
this font. It appears that FontConfig will tend to return Noto Color
Emoji when this family name is passed, even though the font may not
cover the required character set.

The new strategy is to use FontConfig as before, but then to check the
resolved font for coverage of the string. If the resolved font still
can't render the string, we relax the font matching constraints by
removing the family name from the pattern, then try matching again.
This commit is contained in:
reuk 2025-10-14 20:13:18 +01:00
parent 37cc73ad9f
commit 2f1c74981f
No known key found for this signature in database

View file

@ -429,37 +429,52 @@ public:
if (cache == nullptr)
return {};
FcPatternPtr pattern { FcPatternCreate() };
const auto makeBasicPattern = [&]
{
FcPatternPtr pattern { FcPatternCreate() };
{
FcValue value{};
value.type = FcTypeString;
value.u.s = unalignedPointerCast<const FcChar8*> (ftFace->face->style_name);
FcPatternAdd (pattern.get(), FC_STYLE, value, FcFalse);
}
{
const FcCharSetPtr charset { FcCharSetCreate() };
for (const auto& character : text)
FcCharSetAddChar (charset.get(), (FcChar32) character);
FcPatternAddCharSet (pattern.get(), FC_CHARSET, charset.get());
}
if (language.isNotEmpty())
{
const FcLangSetPtr langset { FcLangSetCreate() };
FcLangSetAdd (langset.get(), unalignedPointerCast<const FcChar8*> (language.toRawUTF8()));
FcPatternAddLangSet (pattern.get(), FC_LANG, langset.get());
}
return pattern;
};
const auto fallbackWithFamily = std::invoke ([&]
{
auto pattern = makeBasicPattern();
FcValue value{};
value.type = FcTypeString;
value.u.s = unalignedPointerCast<const FcChar8*> (ftFace->face->family_name);
FcPatternAddWeak (pattern.get(), FC_FAMILY, value, FcFalse);
}
{
FcValue value{};
value.type = FcTypeString;
value.u.s = unalignedPointerCast<const FcChar8*> (ftFace->face->style_name);
FcPatternAddWeak (pattern.get(), FC_STYLE, value, FcFalse);
}
return fromPattern (pattern.get());
});
{
const FcCharSetPtr charset { FcCharSetCreate() };
for (const auto& character : text)
FcCharSetAddChar (charset.get(), (FcChar32) character);
FcPatternAddCharSet (pattern.get(), FC_CHARSET, charset.get());
}
if (text.isEmpty() || fallbackWithFamily->getNominalGlyphForCodepoint (*text.getCharPointer()).has_value())
return fallbackWithFamily;
if (language.isNotEmpty())
{
const FcLangSetPtr langset { FcLangSetCreate() };
FcLangSetAdd (langset.get(), unalignedPointerCast<const FcChar8*> (language.toRawUTF8()));
FcPatternAddLangSet (pattern.get(), FC_LANG, langset.get());
}
return fromPattern (pattern.get());
const auto fallbackWithoutFamily = fromPattern (makeBasicPattern().get());
jassert (fallbackWithoutFamily->getNominalGlyphForCodepoint (*text.getCharPointer()).has_value());
return fallbackWithoutFamily;
#else
// Font substitution will not work unless fontconfig is enabled.
jassertfalse;