#1070454 Fallback fonts do not work

Package:
gargoyle-free
Source:
gargoyle-free
Description:
graphical player for Interactive Fiction games
Submitter:
Chris Spiegel
Date:
2026-03-15 05:45:02 UTC
Severity:
normal
#1070454#5
Date:
2024-05-05 16:04:22 UTC
From:
To:
I am the maintainer of Gargoyle. For reference, see
https://github.com/garglk/garglk/issues/822.

Gargoyle used to embed fonts in the binary, so that it always had
fonts to fall back on, no matter what. However, the SIL Open Font
License is incompatible with the GPL. Gargoyle needed new fonts with
wide Unicode support, and inevitably fonts with the OFL had to be
chosen, meaning Gargoyle can no longer embed these fonts.

Instead, Gargoyle has its own fonts that are installed and it
hard-codes paths to these fonts, which are used as fallback fonts. In
addition, Gargoyle hard-codes the path to GNU Unifont, which it also
installs.

The Debian package does not install these fonts, instead apparently
relying on system packages. But Gargoyle does not look at system fonts
for fallbacks, because it can't rely on them being installed. That's
why it includes its own copies of the fonts.  Note that fontconfig IS
used in normal operation to find fonts; it's just the fallback fonts
that it needs to find directly.

The following is what I wrote up in the issue referenced above as a
recommendation:

8< --------------------

• Gargoyle needs to know the paths of fallback fonts. If you don't
want its fonts in /usr/share/fonts, then you can place them in
/usr/share/io.github.garglk/Gargoyle and modify
font_path_fallback_system in garglk/draw.cpp to unconditionally look
for them there. If you really want system fonts to be the fallback
instead, modify the gli_initialize_fonts function to include whatever
fonts you want, ensuring that font_path_fallback_system is looking in
the right place for them.

    ◦ Gargoyle used to embed fonts in the binary so that fallback
fonts always worked. But the fonts it was using lacked decent Unicode
coverage, and almost all modern fonts use the SIL Open Font License
which does not allow embedding in a GPL program. So Gargoyle now has
to find fonts externally. If a user specifies an invalid font,
Gargoyle will look for its fallback fonts, and if it can't find them,
it aborts.

• Either install Gargoyle's Unifont fonts (which by default go into
/usr/share/io.github.garglk/Gargoyle, so shouldn't interfere with
anything), or make the system's unifont package a dependency, and
modify make_substitution_fonts to point to the system unifont.

8< --------------------

In short, to work properly, Gargoyle needs to know where its fonts
are, and won't rely on fontconfig to find them.  These fallback fonts
will only ever be used if the user's specified fonts can't be found,
or in the case of Unifont, it will be used if the selected font is
missing a glyph.

#1070454#10
Date:
2026-03-15 05:43:04 UTC
From:
To:
Howdy Maintainer,

In a related issue, the Gargoyle fonts aren't being found even when
searching FontConfig.

I had wanted to zoom the font size to be a bit larger. The man page
says to run `gargoyle-free -e` to edit the configuration. However, if
you do that and save the file without making changes, gargoyle will
no longer function at all. Instead, a dialog box pops up reading,

	Unable to find font "Gargoyle Mono" for Mono Regular,
	and fallback Gargoyle-Mono.ttf not found

As a workaround, I extracted the fonts directory from the
gargoyle-2026.1.1.tar.gz file and placed it in
~/.local/share/fonts/gargoyle/. It contains the following
files:

    Gargoyle-Mono-Bold-Italic.ttf   Gargoyle-Serif-Italic.ttf
    Gargoyle-Mono-Bold.ttf          Gargoyle-Serif.ttf
    Gargoyle-Mono-Italic.ttf        README
    Gargoyle-Mono.ttf               unifont.otf
    Gargoyle-Serif-Bold-Italic.ttf  unifont_upper.otf
    Gargoyle-Serif-Bold.ttf

Please install the fallback fonts so that they work out of the box.
------

I am not sure if this is a good idea, but since this package already
depends on fonts-go and fonts-sil-charis, it may make sense to use
aliases in /etc/fonts/conf.d instead of duplicating the font files.
Something like this ought to work:
--------------8<----------CUT HERE--------------------8<-------------- <?xml version="1.0"?> <!DOCTYPE fontconfig SYSTEM "fonts.dtd"> <fontconfig> <alias binding="same"> <family>Gargoyle Serif</family> <prefer> <family>Charis SIL</family> <family>serif</family> </prefer> </alias> <alias binding="same"> <family>Gargoyle Mono</family> <accept> <family>Go Mono</family> <family>monospace</family> </accept> </alias> </fontconfig> --------------8<----------CUT HERE--------------------8<-------------- However, gargoyle currently seems to have a odd design choice in its FontConfig code, or possibly a bug. The problem is that it uses FcFontList (unsorted list of results) when it should use FcFontSort (sorted list). Even better since gargoyle only looks at the first result anyhow, one can use FcFontMatch (just the single font that matches the criteria best) without needing to mess with FcObjectSets. Gargoyle could be fixed so that fontconf aliases work as expect by patching the file garglk/fontfc.cpp like so:
--------------8<----------CUT HERE--------------------8<-------------- --- garglk/fontfc.cpp.orig 2026-02-07 20:18:56.000000000 -0800 +++ garglk/fontfc.cpp 2026-03-14 14:15:27.660518305 -0700 @@ -34,23 +34,23 @@ static nonstd::optional<std::string> findfont(const std::string &fontname) { FcChar8 *strval = nullptr; + FcResult result; auto p = garglk::unique(FcNameParse(reinterpret_cast<const FcChar8 *>(fontname.c_str())), FcPatternDestroy); if (p == nullptr) { return nonstd::nullopt; } - auto os = garglk::unique(FcObjectSetBuild(FC_FILE, static_cast<char *>(nullptr)), FcObjectSetDestroy); - if (os == nullptr) { + if (FcConfigSubstitute(cfg, p.get(), FcMatchPattern) != FcTrue) { return nonstd::nullopt; } - - auto fs = garglk::unique(FcFontList(cfg, p.get(), os.get()), FcFontSetDestroy); - if (fs->nfont == 0) { + FcConfigSetDefaultSubstitute(cfg, p.get()); + auto fpat = FcFontMatch(cfg, p.get(), &result); + if (result != FcResultMatch || fpat == nullptr) { return nonstd::nullopt; } - if (FcPatternGetString(fs->fonts[0], FC_FILE, 0, &strval) == FcResultTypeMismatch || strval == nullptr) { + if (FcPatternGetString(fpat, FC_FILE, 0, &strval) == FcResultTypeMismatch || strval == nullptr) { return nonstd::nullopt; }
--------------8<----------CUT HERE--------------------8<-------------- Thanks,