Hey guys.
This is my first post, so I hope i'm doing it right and that's the right place (couldn't find a better one).
I am using fontconfig to get a font and a related list of fallback fonts for styles such as regular/bold/italic/bold-italic but also a font with font family "emoji".
While it works in general, I wonder why - not matter what - it seems that "Noto Color Emoji" font family is placed on second place of the fonts returned by FcFontSort.
This is what I do:
auto const family = string(_family);
auto pat = unique_ptr<FcPattern, void(*)(FcPattern*)>(FcPatternCreate(), [](auto p) { FcPatternDestroy(p); });
FcPatternAddBool(pat.get(), FC_OUTLINE, true);
FcPatternAddBool(pat.get(), FC_SCALABLE, true);
//FcPatternAddBool(pat.get(), FC_EMBEDDED_BITMAP, false);
FcPatternAddBool(pat.get(), FC_COLOR, _color); // with _color being bool (either true or false)
if (!_family.empty()) // such as "Fira Code" or "emoji"
FcPatternAddString(pat.get(), FC_FAMILY, (FcChar8 const*) family.c_str());
if (_monospace)
{
if (_family != "monospace")
FcPatternAddString(pat.get(), FC_FAMILY, (FcChar8 const*) "monospace");
FcPatternAddInteger(pat.get(), FC_SPACING, FC_MONO); // in monospace (non-emoji), mono and dual are okay.
FcPatternAddInteger(pat.get(), FC_SPACING, FC_DUAL); // tried with and without this line
}
if (int(_style) & int(FontStyle::Bold))
FcPatternAddInteger(pat.get(), FC_WEIGHT, FC_WEIGHT_BOLD);
if (int(_style) & int(FontStyle::Italic))
FcPatternAddInteger(pat.get(), FC_SLANT, FC_SLANT_ITALIC);
FcConfigSubstitute(nullptr, pat.get(), FcMatchPattern);
FcDefaultSubstitute(pat.get());
FcResult result = FcResultNoMatch;
auto fs = unique_ptr<FcFontSet, void(*)(FcFontSet*)>(
FcFontSort(nullptr, pat.get(), /*unicode-trim*/FcTrue, /*FcCharSet***/nullptr, &result),
[](auto p) { FcFontSetDestroy(p); });
auto pat = unique_ptr<FcPattern, void(*)(FcPattern*)>(FcPatternCreate(), [](auto p) { FcPatternDestroy(p); });
FcPatternAddBool(pat.get(), FC_OUTLINE, true);
FcPatternAddBool(pat.get(), FC_SCALABLE, true);
//FcPatternAddBool(pat.get(), FC_EMBEDDED_BITMAP, false);
FcPatternAddBool(pat.get(), FC_COLOR, _color); // with _color being bool (either true or false)
if (!_family.empty()) // such as "Fira Code" or "emoji"
FcPatternAddString(pat.get(), FC_FAMILY, (FcChar8 const*) family.c_str());
if (_monospace)
{
if (_family != "monospace")
FcPatternAddString(pat.get(), FC_FAMILY, (FcChar8 const*) "monospace");
FcPatternAddInteger(pat.get(), FC_SPACING, FC_MONO); // in monospace (non-emoji), mono and dual are okay.
FcPatternAddInteger(pat.get(), FC_SPACING, FC_DUAL); // tried with and without this line
}
if (int(_style) & int(FontStyle::Bold))
FcPatternAddInteger(pat.get(), FC_WEIGHT, FC_WEIGHT_BOLD);
if (int(_style) & int(FontStyle::Italic))
FcPatternAddInteger(pat.get(), FC_SLANT, FC_SLANT_ITALIC);
FcConfigSubstitute(nullptr, pat.get(), FcMatchPattern);
FcDefaultSubstitute(pat.get());
FcResult result = FcResultNoMatch;
auto fs = unique_ptr<FcFontSet, void(*)(FcFontSet*)>(
FcFontSort(nullptr, pat.get(), /*unicode-trim*/FcTrue, /*FcCharSet***/nullptr, &result),
[](auto p) { FcFontSetDestroy(p); });
Now, fs contains the FontSet prioritized by closeness (that's what the docs are saying), so I believe that means, I can use that as font fallback list.
With first font being the requested one (or close match), and a prioritized list of fallbacks to walk through in case needed.
For some reason, the second font in the list for regular fonts (font family is NOT "emoji" and _color is set to false) is actually "Noto Color Emoji Regular".
I really tried hard to get it to a point where FcFontSort gets me only what I requested via FcPatternAddXXX, but it sometimes looks like
all my FcPatternAddXXX don't mean anything.
Secondly, when I request monospace fonts (_monospace == true, _color = false), then FcFontSort also returns fonts
whose FC_SPACING is not FC_MONO or FC_MONO, in fact:
int spacing = -1; // ignore font if we cannot retrieve spacing information
FcPatternGetInteger(font, FC_SPACING, 0, &spacing);
if (_monospace && spacing < FC_DUAL) continue; /* don't include non-monospace fonts if not requested).
FcPatternGetInteger(font, FC_SPACING, 0, &spacing);
if (_monospace && spacing < FC_DUAL) continue; /* don't include non-monospace fonts if not requested).
This call to FcPatternGetInteger(... FC_SPACING, ...) seems to fail quite often (does not store the value in spacing, as it remains -1.
Whereas the following:
FcBool color = FcFalse;
FcPatternGetInteger(font, FC_COLOR, 0, &color);
FcPatternGetInteger(font, FC_COLOR, 0, &color);
never yields true/FcTrue not even for Noto Color Emoji.
(If that matters, this was tested on Ubuntu 20.10).
It is really not the easiest to find good examples on how to use this API, so I may as well just be doing it wrong.
But if so, I'd really appreciate some hint or indication of what I could do to achieve that.
The purpose of all this is, that I am developing a terminal emulator where I use fontconfig to find monospace fonts (with font fallback)
as well as emoji in color and emoji text versions (for emoji text versions, it seems I can use use the regular monospace font,
iff it wouldn't include "Noto Color Emoji" so far on top in the priority list. Explicitely hardcoding it out via a hacky if-statement
yields the expected result, but that can't be it.
At the same time, gnome-terminal & kitty chose the right fonts (with everything configured the same).
For kitty I know it's not using FcFontSort, as it priorizes manually, maybe because of the above?
I'd realy appreciate some feedback here,
Many thanks in advance,
Christian Parpart.
_______________________________________________ Fontconfig mailing list Fontconfig@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/fontconfig