Am 30.05.2020 um 21:37 schrieb Josh Freeman <gnustep_lists@twilightedge.com
>:
Some users of Kubuntu (official flavor of Ubuntu with the KDE
Plasma desktop) reported that PikoPixel's missing most of its UI
text; Numbers are the only characters displayed:
https://old.reddit.com/r/Kubuntu/comments/gas88l/pikopixel_strange_graphics_bug_any_suggestions_on/
I can reproduce this on Kubuntu 20.04, and the missing-text issue
affects all GNUstep gui apps, whether the apps & GNUstep are from
packages or from current sources.
This might be the same issue that Patryk found last August with
KDE Neon:
https://lists.gnu.org/archive/html/gnustep-dev/2019-08/msg00019.html
(I couldn't test this, because the current x86_64 builds of KDE
Neon fail to install on a VirtualBox VM, and older versions don't
seem to be available).
Manually installing a different desktop on Kubuntu doesn't fix the
issue; Text is still missing on GS gui apps when run under GNOME or
Window Maker, however, switching GS's backend from Cairo to Art
fixes the text.
Text displays correctly on Ubuntu 20.04 - even under Plasma - and
also on a daily build of Ubuntu Studio 20.10 (Plasma is Studio's
installed desktop as of 20.10).
The issue's caused by a combination of two factors:
A) The Cairo backend sets up fontconfig patterns for font matching
by calling the fontconfig library's Fc...Substitute() functions
twice on each pattern instead of once:
The backend's FCFaceInfo class has two methods, matchedPattern &
characterSet, that each call FcConfigSubstitute() &
FcDefaultSubstitute(), passing the FCFaceInfo instance member,
_pattern, to be modified. Both methods in turn are called from
within a single method, -[CairoFontInfo setupAttributes] (itself
called from the FcFontInfo initializer):
1. [CairoFontInfo setupAttributes]:[FcFontInfo (super)
setupAttributes]:[FCFaceInfo characterSet]
2. [CairoFontInfo setupAttributes]:[CairoFaceInfo fontFace]:
[FCFaceInfo matchedPattern]
Note that the font face is only generated after the second set of
Fc...Substitute() calls.
B) The fontconfig library's font-matching functionality behaves
differently on Kubuntu vs. other distros (even with the same
version installed - Kubuntu 20.04 & Ubuntu 20.04 both have
libfontconfig1 2.13.1-2ubuntu3).
Alone, neither factor causes the issue, however calling the
Fc...Substitute() functions twice on the same pattern on Kubuntu
leaves a modified pattern that - when passed to FcFontMatch() -
always resolves to the "Noto Color Emoji" font. (That font has no
alphabet characters, but does have digits, which explains why only
numbers appear in the above linked screenshots).
To demonstrate this, the attached C file, fc-pattern-check.c,
builds a standalone diagnostic tool using only the fontconfig
library - no GNUstep. It creates a fontconfig pattern for matching
the "DejaVu Sans" font and makes two sets of calls to
Fc...Substitute() & FcFontMatch(), duplicating the calls made by
the Cairo backend. After each call, the resulting pattern is printed.
The attached textfiles, fcpc_out_kubuntu.txt &
fcpc_out_ubuntu.txt, are the outputs from running the fc-pattern-
check tool on Kubuntu 20.04 & Ubuntu 20.04.
Comparing the two output files, the first difference comes after
the first call to FcConfigSubstitute(): On Kubuntu, the pattern has
a "rgba" (pixel-format) field inserted - this may not contribute to
the incorrect match, but it already shows a different behavior
between Kubuntu & Ubuntu.
Following the first set of Fc...Substitute() calls, the first call
to FcFontMatch() correctly returns a resolved pattern for the
"DejaVu Sans" font on both Ubuntu & Kubuntu. When these same first
calls are made by the Cairo backend, the resolved pattern's used
only to get the font's character set, not to generate the font face
for drawing.
After the second call to FcConfigSubstitute() on Kubuntu, the
pattern's "lang" field for some reason now contains "und-
zsye" (emoji locale) before its earlier value, "en" (english). The
pattern now also contains a "color" field with a value of
"True" (requiring a match to a font that contains color
information). With those field values present, the second call to
FcFontMatch() returns a resolved pattern for "Noto Color Emoji"; On
Ubuntu, its second FcConfigSubstitute() call doesn't add those
"lang" & "color" values to the pattern, and its second call to
FcFontMatch() correctly returns a "Deja Vu Sans" resolved pattern.
The attached patch, libs-back_fontconfig_pattern_fix.diff, fixes
the missing text by limiting FCFaceInfo's Fc...Substitute() &
FcFontMatch() calls to once per pattern. The resolved pattern
returned by FcFontMatch() is now stored in FcFaceInfo's _pattern
member (which previously only held a pre-matched pattern), and a
new bool member, _patternIsResolved, keeps track of _pattern's pre-
matched/resolved state.
The characterSet method now gets the resolved pattern by calling
the matchedPattern method instead of calling the fontconfig
functions directly; Outside of the initializer, dealloc, &
matchedPattern methods, the _pattern member is now only accessed by
the displayName method (which just reads the pattern's fullname,
postscriptname, & style fields - if those were present in the pre-
matched pattern, they should remain in the resolved pattern), so
there should be no side-effects from storing a resolved pattern
instead of a pre-matched pattern in the _pattern member.
Loading a large set of fonts will probably also go faster now, as
FcFontMatch() is only called half as often.
Cheers,
Josh
<fc-pattern-check.c> <fcpc_out_kubuntu.txt> <fcpc_out_ubuntu.txt>
<libs-back_fontconfig_pattern_fix.diff>