Hi,
I'm trying to improve LilyPond's font selection (see here), and feeling a bit lost. LilyPond uses Fontconfig + Pango to find fonts. It has custom Fontconfig config files and adds app font directories.
I think we are probably misusing the Fontconfig API in a number of ways, but I'm not sure what the correct way is. I have a lot of questions, sorry.
-
At the beginning of its processing, LilyPond calls
FcInitLoadConfig();
and discards the result. I think this might be an artifact of history. Does this do anything relevant if the result is discarded? -
At the end, after having constructed its desired configuration called
font_config_global
, LilyPond callsFcConfigBuildFonts(font_config_global);
. I see in the documentation ofFcConfigBuildFonts
that changes to the configuration have undefined effect, and in fact there are functions in LilyPond to add custom fonts, which do exactly this. So far they work, but given the documentation, this isn't guaranteed. So I'm trying to understand what the last moment we can do these modifications (essentiallyFcConfigAppFontAddDir
) is. IsFcConfigBuildFonts
implicitly called when you doFcFontMatch
or similar? Does Pango call it internally the first time a font is requested? Does this call interact with caching somehow? -
As you can see in the LilyPond merge request, removing a call
FcConfigSetCurrent(font_config_global);
and passing the config explicitly everywhere made aliases from our custom config files not recognized anymore. I triple checked and I don't think we're still using NULL somewhere as config, the custom config should always be properly used. Does this ring a bell? -
Attached is a test C program where I tried to reduce LilyPond's case to a minimal example. Unfortunately, it doesn't exhibit the behavior we're seeing in LilyPond, but I still don't understand it.
I have used this command to compile and test it:
gcc $(pkg-config --cflags fontconfig) $(pkg-config --libs fontconfig) -o fcpb fcpb.c && ./fcpb
pkg-config --modversion fontconfig
outputs 2.14.1.
The program creates a config, parses the default config file plus a custom XML string defining an alias into this config, then makes a request for the alias.
The result of the program as-is is:
Default conf file: /etc/fonts/fonts.conf
Match result is: 1
Get result is: 1
Font file: (null)
4.1. Why is there no match? I would have expected it to find the C059 font (which is found by fc-list
on my system).
4.2. If I uncomment the call FcConfigSetCurrent(conf);
, the result is different:
Default conf file: /etc/fonts/fonts.conf
Match result is: 0
Get result is: 0
Font file: /usr/share/fonts/gnu-free/FreeSans.ttf
Still not the expected result, but a fallback is chosen. Why does this make a difference, given that I'm not using the global config?
Thanks!
Jean
#include <stdlib.h> #include <assert.h> #include <stdio.h> #include <fontconfig/fontconfig.h> int main() { FcConfig *conf = FcConfigCreate(); FcChar8 *default_conf_file = FcConfigFilename(NULL); assert(default_conf_file); printf("Default conf file: %s\n", default_conf_file); assert(FcConfigParseAndLoad(conf, default_conf_file, FcTrue)); FcChar8 *my_conf = (FcChar8 *) "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<!DOCTYPE fontconfig SYSTEM \"fonts.dtd\">\n" "<fontconfig>\n" " <alias binding=\"strong\">\n" " <family>Custom Family</family>\n" " <prefer>\n" " <family>C059</family>\n" " </prefer>\n" " <default>\n" " <family>serif</family>\n" " </default>\n" " </alias>\n" "</fontconfig>\n"; assert(FcConfigParseAndLoadFromMemory(conf, my_conf, FcTrue)); //FcConfigSetCurrent(conf); FcPattern *pat = FcPatternCreate(); FcPatternAddString(pat, FC_FAMILY, (FcChar8 *) "Custom Family"); FcConfigSubstitute(conf, pat, FcMatchFont); FcDefaultSubstitute(pat); FcResult result; FcPattern *found_pat = FcFontMatch(conf, pat, &result); printf("Match result is: %d\n", result); FcChar8 *filename; FcResult get_result = FcPatternGetString(found_pat, FC_FILE, 0, &filename); printf("Get result is: %d\n", get_result); printf("Font file: %s\n", (char *) filename); return 0; }
Attachment:
signature.asc
Description: This is a digitally signed message part