Getting "Warning: Missing charsets in String to FontSet conversion"

I'm using X applications over an SSH connection to some machine. When I run an X app, say, xclock for simplicity, I get a console message saying:

Warning: Missing charsets in String to FontSet conversion

... but the app runs. My locales are:

$ locale
LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=

Now, I've found this old suggestion to just set export LC_ALL=C. And - that works, the error message goes away. However, I really don't want to change my locale like that!

Is there something I can do to avoid the error and keep my locale?

Additional info:

  • X server is MobaXterm's integrated X server on Windows.
  • remote machine is SLES GNU/Linux 11 (by SUSE).

Solution 1:

You are correct, you don't want to change your locale like that! I found similar suggestions, and chose the rabbit hole instead.

The single locale variable you could change is LC_CTYPE=C (this is the character classification settings, see POSIX section 7, Locale for details).

The main issue here, I believe, is that old X programs have minor font-related issues with contemporary multi-byte/multi-locale systems.

My own locale, on a desktop X Org (Mobaterm uses X Org under the hood) is en_IE.utf (mapped to the file en_US.UTF-8/XLC_LOCALE via /usr/share/X11/locale/locale.dir). Within that XLC_LOCALE file there are fontset and charset definitions, and it is the presence of some of these that (tend to) trigger the warnings (specifically for Cyrillic and Asian encodings which I do not use, and don't have great font coverage of).

I get several errors from some programs, e.g. xfig:

Warning: Missing charsets in String to FontSet conversion
Warning: Missing charsets in String to FontSet conversion
Warning: Unable to load any usable fontset

Commenting out various unwanted fsN and csN entries will fix that (each index N defines a matching fontset/charset pair, maintain this pairing noting each pair is ~200 lines apart). This may affect presentation of certain texts. Or, setting LC_CTYPE=C before starting xfig removes all warnings.

The warnings are issued from XtCvtStringToFontSet()(libXt), as a result of XCreateFontSet() reporting missing character sets (i.e. character sets used/required by the locale, but not found in the font(s) matching the requested specification).

XCreateFontSet() returns the list of missing character sets, but libXt outputs no details of the request or missing character sets to help you track it down. (I cheated using LD_PRELOAD and a wrapper .so for XCreateFontSet() to find the culprits, see below.)

The real issue then is that a fontspec (e.g. "--helvetica-medium-r-normal--16-------") is checked for all of the encodings that are set in the file XLC_LOCALE, and if some cannot be found then you see these warnings. If none are found at all you will see "Warning: Unable to load any usable fontset" too.

Some fixes then:

  1. set LC_CTYPE=C (or other "simple" encoding, e.g en_US) for errant programs
  2. remove unwanted pairs of fontset/charset from the XLC_LOCALE file in effect (locale), this takes effect without restarting X or your desktop
  3. install more fonts and encodings (something of a game of whack-a-mole)
  4. set a more generous application fontspec (via xrdb or command line)
  5. set a more generous default fontspec (via xrdb)

You may need both of the final two fixes, since the warning can be issued if any of the default fonts don't match an encoding at time of check, even if a subsequent font suffices.

One final problem is that some fallback fontspecs might continue to trigger the problem. Specifically, the hardcoded Xt default of:

-*-*-*-R-*-*-*-120-*-*-*-*,*

can fail completely triggering the same warnings. Some quick testing indicates that fontspecs that don't contain a family (including just a single "*") may not match anything, even though matches are expected, i.e. with:

xlsfonts -fn "*-*-*-R-*-*-*-120-*-*-*-*"

And adding a trailing ",*" as a complete wildcard can break a fontspec that would otherwise work. Observed on X Org 1.18.3.

I use this in my .Xdefaults:

XtDefaultFontSet: -*-fixed-medium-r-normal--16-*-*-*-*-*-*-*,-*-fixed-*-*-*--16-*,-*-*-medium-*-*--16-*

which seems to keep most things quiet for me, no XLC_LOCALE changes required. This fallback manages to match every requested encoding across the melange of fonts I have somehow accreted...


Here's the very simple wrapper (with minimal error checking) I used to help track down what is being requested (as it's often only defined in the application source) and what is not being found. Tested on x86_64 Slackware, other distros/systems may need minor adjustment.

gcc -fPIC -shared -Wl,-soname,xfontset.so -ldl -o xfontset.so xfontset.c

then

LD_PRELOAD=$PWD/xfontset.so xfig

to run this from the directory you compiled it in.

#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
#include <link.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

#include <X11/Xlib.h>

#define DEBUG 1
#define dfprintf(fmt, ...) \
    do { if (DEBUG) fprintf(stderr, "[%14s#%04d:%8s()] " fmt, \
          __FILE__, __LINE__, __func__,##__VA_ARGS__); } while (0)

// gcc -fPIC -shared -Wl,-soname,xfontset.so -ldl -o xfontset.so xfontset.c

// see X11/Xlib.h
typedef XFontSet XCreateFontSet_fp(
  Display *display,
  _Xconst char *base_font_name_list, 
  char ***missing_charset_list_return, 
  int *missing_charset_count_return, 
  char **def_string_return
);

static XCreateFontSet_fp *real_XCreateFontSet;

void myinit(void) __attribute__((constructor));

void myinit(void)
{
    void *dl;
    if ((dl=dlopen(NULL,RTLD_NOW))) {
        dfprintf("found dl at %p ()\n", dl);

        real_XCreateFontSet=dlsym(RTLD_NEXT,"XCreateFontSet");
        if (!real_XCreateFontSet) dfprintf("error: %s\n",dlerror());
        dfprintf("found fn() at %p\n", (void *)real_XCreateFontSet);
    } else {
        dfprintf("dlopen() failed...\n");
    }
}

// https://www.debian.org/doc/manuals/intro-i18n/ch-examples.en.html
// https://invisible-island.net/xterm/xtoolkit/intrinsics.html

XFontSet XCreateFontSet(Display *display, _Xconst char *base_font_name_list, char ***missing_charset_list_return, int *missing_charset_count_return, char **def_string_return)
{
     XFontSet rc;
     int fnum,ii;
     XFontStruct **xfonts;
     char **font_names;

     dfprintf("wrapped XCreateFontSet()\n");
     dfprintf(" base_font_name_list: <%s>\n",base_font_name_list);

     rc=real_XCreateFontSet(display, base_font_name_list, missing_charset_list_return,missing_charset_count_return, def_string_return);

    if (*missing_charset_count_return) {
        int ii=0;
        dfprintf(" #missing charsets: %i\n",*missing_charset_count_return);

        for (ii=0; ii<*missing_charset_count_return; ii++) {
            dfprintf(" %2i: <%s>\n",ii, (*missing_charset_list_return)[ii]);
        }
    }
    if (rc) {
        fnum=XFontsOfFontSet(rc,&xfonts,&font_names);
        for (ii=0; ii<fnum; ii++) {
            dfprintf(" XFontSet[%2i]=<%s>\n",ii,font_names[ii]);  
        }
    } else {
        dfprintf("no FontSet found...\n");
    }
    return rc;
}