summaryrefslogtreecommitdiffstats
path: root/utils/font-retry.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/font-retry.c')
-rw-r--r--utils/font-retry.c630
1 files changed, 476 insertions, 154 deletions
diff --git a/utils/font-retry.c b/utils/font-retry.c
index f8e6507..3122969 100644
--- a/utils/font-retry.c
+++ b/utils/font-retry.c
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 2018 by Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 2018-2021 by Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
@@ -9,11 +9,56 @@
* implied warranty.
*/
-/* Like XLoadQueryFont, but if it fails, it tries some heuristics to
- load something close.
+/* The value of XScreenSaver's font resource strings is a comma-separated
+ list of font names that look like this:
+
+ Helvetica Bold Italic 12
+
+ We parse that into an XLFD and pass that along to the underlying systems:
+ XftFontOpenXlfd or jwxyz.
+
+ Xft does aggressive substitution of unknown fonts, so if we don't get an
+ exact match on the font name, we move on to the next element in the list.
+ For the last element, we take whatever substitution we got.
+
+ XftNameParse uses this syntax:
+
+ "Helvetica Neue-12:style=Bold"
+ "Helvetica Neue-12:style=Bold Italic"
+ "Helvetica Neue-12:slant=110:weight=200"
+
+ Cocoa uses PostScript names, with hyphens:
+
+ [NSFont fontWithName:@"Helvetica-BoldOblique" size:12];
+ [NSFont fontWithName:@"Times-Roman" size:12];
+
+ Alternately, with separated styles:
+
+ [[NSFontManager sharedFontManager]
+ fontWithFamily:@"Helvetica" traits:NSBoldFontMask weight:5 size:12];
+
+ Android separates names from styles:
+
+ Paint.setTypeface (Typeface.create ("Helvetica", Typeface.BOLD));
+ Paint.setTextSize (12);
+
+ See android/xscreensaver/src/org/jwz/xscreensaver/jwxyz.java
+
+
+ In XScreenSaver 6, USE_XFT is always true, as all programs now use Xft.
+
+ In XScreenSaver 5, this file was compiled in two different ways:
+ - As font-retry.o for programs that did not link with libXft;
+ - As font-retry-xft.o for programs that did.
+
+ #ifdef USE_XFT is whether Xft code should be called.
+ #ifdef HAVE_XFT is whether libXft is natively available.
+
+ The reason there are two defines is that if HAVE_XFT is not defined,
+ the Xft API is still available through emulation provided by "xft.h".
*/
-#define _GNU_SOURCE
+#define _GNU_SOURCE /* Why is this here? */
#include "utils.h"
#include "visual.h"
@@ -26,194 +71,471 @@ extern const char *progname;
#define countof(x) (sizeof((x))/sizeof((*x)))
#undef DEBUG
+#undef SELFTEST
+
+#ifdef HAVE_JWXYZ
+# define USE_XFT 1
+#endif
+
+#ifdef SELFTEST
+static void xft_selftest (Display *dpy, int screen);
+#endif
+
+
+/* Parse font names of the form "Helvetica Neue Bold Italic 12".
+ */
+static char *
+font_name_to_xlfd (const char *font_name)
+{
+ char *name = strdup (font_name);
+ char *xlfd = 0;
+ char *s, *s2, *b, *i, *o, c;
+ float size;
+
+ if (name[0] == '-' || name[0] == '*')
+ return name; /* Already an XLFD */
+
+ /* Downcase ASCII; dash to space */
+ for (s = name; *s; s++)
+ if (*s >= 'A' && *s <= 'Z')
+ *s += 'a'-'A';
+ else if (*s == '-')
+ *s = ' ';
+
+ /* "Family NN" or "Family-NN" */
+ s = strrchr (name, ' ');
+ s2 = strrchr (name, '-');
+ if (s && s2 && s2 > s) s = s2;
+ if (!s) goto FAIL;
+ if (1 != sscanf (s+1, " %f %c", &size, &c)) goto FAIL;
+ if (size <= 0) goto FAIL;
+ *s = 0;
+
+ /* "Family Bold", etc. */
+ b = strstr (name, " bold");
+ i = strstr (name, " italic");
+ o = strstr (name, " oblique");
+ if (b) *b = 0;
+ if (i) *i = 0;
+ if (o) *o = 0;
+
+ xlfd = (char *) malloc (strlen(name) + 80);
+ sprintf (xlfd, "-*-%s-%s-%s-*-*-*-%d-*-*-*-*-*-*",
+ name,
+ (b ? "bold" : "medium"),
+ (i ? "i" : o ? "o" : "r"),
+ (int) (size * 10));
+ free (name);
+ return xlfd;
+
+ FAIL:
+ fprintf (stderr, "%s: XFT: unparsable: \"%s\"\n", progname, font_name);
+ if (name) free (name);
+ if (xlfd) free (xlfd);
+ return 0;
+}
+
+
+#ifdef USE_XFT
+
+/* Xft silently substitutes fonts if the one you requested wasn't available.
+ This leads to the deplorable situation where we ask for a fixed width font
+ and are given a variable width font instead. This doesn't happen with
+ Courier, since Xft special-cases that one, but it happens with any other
+ fixed width font that is not similarly privileged.
+
+ Even worse: when Xft substitutes fonts, it makes no attempt to return fonts
+ that are capable of displaying the language of the current locale. For
+ example: if your locale is set to Japanese and you request "Helvetica", Xft
+ silently substitutes "Nimbus Sans" -- a font which is not capable of
+ displaying Japanese characters. If instead you requested "asdfasdfghjkl",
+ you get "Noto Sans CJK JP", which does work. So that's just spectacular.
+
+ Since there does not appear to be a way to ask Xft whether a particular
+ font exists, we load the font and then check to see if the name we got
+ is the name we requested.
+
+ In more detail:
+
+ - XftFontOpenXlfd is defined as:
+ XftFontOpenPattern (XftFontMatch (XftXlfdParse (xlfd)))
+ - XftFontOpenName is defined as:
+ XftFontOpenPattern (XftFontMatch (XftNameParse (name)))
+ - Calling XftFontOpenPattern with a pattern that has not been filtered
+ through XftFontMatch does not work.
+ - XftFontMatch substitutes another font if the pattern doesn't match.
+ - If the pattern has family "Courier", it substitutes a fixed width
+ font, e.g. "Liberation Mono".
+ - Otherwise it substitutes a variable width font, e.g. "DejaVu Sans".
+ It does this even if the pattern contained "spacing=100" or "110",
+ indicating a monospace or charcell font.
+ - XftXlfdParse does not translate "spacing" from XLFD to XftPattern,
+ but that doesn't matter since XftFontMatch ignores it anyway.
+
+ Xft source is here:
+ https://opensource.apple.com/source/X11ForMacOSXSource/X11ForMacOSXSource-1.0/xc/lib/Xft1/
+
+ Courier, Helvetica and the other historical PostScript font names seem to
+ be special-cased in /etc/fonts/conf.d/ in the files 30-metric-aliases.conf,
+ 45-latin.conf and/or 60-latin.conf, which uses an idiosyncratic scripting
+ language implemented as XML! Some incomplete documentation on that baroque
+ mess is here:
+ https://www.freedesktop.org/software/fontconfig/fontconfig-user.html
+
+ The Xft configuration files seem to special-case the names "monospace",
+ "serif" and "sans-serif" as generic fallback fonts.
+
+ However, "sans-serif" is problematic because it does not survive the trip
+ through XftXlfdParse and XftFontOpenPattern -- XLFD font families cannot
+ include hyphens. So we have no choice but to turn it into "sans serif",
+ which is *not* special-cased by the Xft config files.
+
+ In summary, everything is terrible, and it's a wonder anything works at all.
+ */
+static Bool
+xlfd_substituted_p (XftFont *f, const char *xlfd)
+{
+# ifndef HAVE_XFT
+ return False; /* No substitutions in the Xft emulator. */
+# else /* HAVE_XFT */
+
+ Bool ret = True;
+ const char *oxlfd = xlfd;
+ char *s = 0;
+ char *xname = 0;
+ char fname[1024];
+
+ if (*xlfd == '-' || strchr (xlfd, '*')) /* it's an xlfd */
+ {
+ if (*xlfd != '-') goto FAIL;
+ xlfd++;
+ s = strchr (xlfd, '-'); if (!s) goto FAIL; /* skip foundry */
+ xlfd = s+1;
+ s = strchr (xlfd, '-'); if (!s) goto FAIL; /* skip family */
+
+ {
+ char buf[1024];
+ XftPattern *pat = XftXlfdParse (oxlfd, True, True);
+ XftNameUnparse (pat, buf, sizeof(buf)-1);
+ }
+ }
+ else /* It's an Xft name */
+ {
+ s = strchr (xlfd, '-'); if (!s) goto FAIL; /* skip family */
+ }
+
+ xname = strdup (xlfd);
+ xname[s - xlfd] = 0;
+
+ *fname = 0;
+ XftNameUnparse (f->pattern, fname, sizeof(fname)-1);
+ s = strchr (fname, ':'); /* Strip to "Family-NN" */
+ if (s) *s = 0;
+ s = strrchr (fname, '-'); /* Strip to family */
+ if (s) *s = 0;
+
+ ret = !strcasestr (fname, xname);
+
+# ifdef DEBUG
+ if (ret)
+ fprintf (stderr, "%s: XFT: requested \"%s\" but got \"%s\"\n",
+ progname, xname, fname);
+# endif
+
+ FAIL:
+
+ if (!s)
+ fprintf (stderr, "%s: unparsable XLFD: %s\n", progname, oxlfd);
+ if (xname) free (xname);
+ return ret;
+# endif /* HAVE_XFT */
+}
+#endif /* USE_XFT */
+
static void *
-load_font_retry_1 (Display *dpy, int screen, const char *xlfd, Bool xft_p)
+load_font_retry_1 (Display *dpy, int screen, const char *font_list, Bool xft_p)
{
# ifdef USE_XFT
# define LOADFONT(F) (xft_p \
- ? (void *) XftFontOpenXlfd (dpy, screen, (F)) \
+ ? (void *) XftFontOpenXlfd (dpy, screen, (F)) \
: (void *) XLoadQueryFont (dpy, (F)))
+# define UNLOADFONT(F) (xft_p \
+ ? (void) XftFontClose (dpy, (F)) \
+ : (void) XFreeFont (dpy, (F)))
# else
# define LOADFONT(F) ((void *) XLoadQueryFont (dpy, (F)))
+# define UNLOADFONT(F) XFreeFont (dpy, (F))
# endif
- void *f = xlfd ? LOADFONT(xlfd) : 0;
+ char *font_name = 0;
+ void *f = 0;
+ void *fallback = 0;
# ifndef USE_XFT
if (xft_p) abort();
# endif
-# ifdef HAVE_JWXYZ
- return f;
-# else /* !HAVE_JWXYZ */
- if (! xlfd) xlfd = "<null>";
- if (f)
- {
-# ifdef DEBUG
- fprintf (stderr, "%s: loaded %s\n", progname, xlfd);
+# ifdef SELFTEST
+ xft_selftest (dpy, screen);
# endif
- return f;
- }
- else
+
+ if (! font_list) font_list = "<null>";
+
+ /* Treat the string as a comma-separated list of font names.
+ Names are XLFDs or the XScreenSaver syntax described above.
+ Try to load each of them in order.
+ If a substitution was made, keep going, unless this is the last.
+ */
+ if (font_list)
{
- Bool bold_p = (!!strcasestr (xlfd, "-bold-") ||
- !!strcasestr (xlfd, "-ocr"));
- Bool italic_p = (!!strcasestr (xlfd, "-i-") ||
- !!strcasestr (xlfd, "-o-"));
- Bool fixed_p = (!!strcasestr (xlfd, "courier") ||
- !!strcasestr (xlfd, "-ocr") ||
- !!strcasestr (xlfd, "-m-") ||
- !!strcasestr (xlfd, "-c-"));
- int size = 0;
+ char *token = strdup (font_list);
+ char *otoken = token;
+ char *name2, *lasts;
+ if (!token) abort();
+ while ((name2 = strtok_r (token, ",", &lasts)))
+ {
+ int L;
+ token = 0;
+
+ /* Strip leading and trailing whitespace */
+ while (*name2 == ' ' || *name2 == '\t' || *name2 == '\n')
+ name2++;
+ L = strlen(name2);
+ while (L && (name2[L-1] == ' ' || name2[L-1] == '\t' ||
+ name2[L-1] == '\n'))
+ name2[--L] = 0;
+
+ if (font_name) free (font_name);
+ font_name = font_name_to_xlfd (name2);
# ifdef DEBUG
- fprintf (stderr, "%s: failed %s\n", progname, xlfd);
+ fprintf (stderr, "%s: trying \"%s\" as \"%s\"\n", progname,
+ name2, font_name);
# endif
- if (!strcmp (xlfd, "vga")) /* BSOD uses this: it has no XLFD name. */
- fixed_p = True, size = 120;
+ f = LOADFONT (font_name);
- /* Look for the first number in the string like "-180-" */
- if (! size)
- {
- const char *s;
- for (s = xlfd; *s; s++)
- if (s[0] == '-' && s[1] >= '0' && s[1] <= '9')
- {
- int i = s[1] - '0';
- const char *s2 = s+2;
- while (*s2 >= '0' && *s2 <= '9')
- {
- i = i * 10 + *s2 - '0';
- s2++;
- }
- if (*s2 != '-') continue; /* Number ends with dash */
- if (i < 60 || i >= 2000) continue; /* In range 6pt - 200pt */
- if (i % 10) continue; /* Multiple of 10 */
-
- size = i;
- break;
- }
- }
+# ifdef USE_XFT
+ /* If we did not get an exact match for the font family we requested,
+ reject this font and try the next one in the list. */
+ if (f && xft_p && xlfd_substituted_p (f, font_name))
+ {
+ if (fallback)
+ UNLOADFONT (fallback);
+ fallback = f;
+ f = 0;
+ }
+# ifdef DEBUG
+ else if (!f)
+ fprintf (stderr, "%s: no match for \"%s\"\n", progname, font_name);
+# endif
+# endif /* USE_XFT */
- if (! size)
- {
- fprintf (stderr, "%s: unloadable, unparsable font: \"%s\"\n",
- progname, xlfd);
- xlfd = "fixed";
- return LOADFONT(xlfd);
+ if (f) break;
}
- else
+ free (otoken);
+ if (!font_name) abort();
+
+ /* If the last font in the list was an Xft pattern that matched but
+ was inexact, use it. */
+ if (!f)
{
- const char *variable[] = {
- "helvetica",
- "arial",
- "bitstream vera sans",
- "gill sans",
- "times",
- "times new roman",
- "new century schoolbook",
- "utopia",
- "palatino",
- "lucida",
- "bitstream charter",
-
- /* Don't use a wildcard family. If none of the above worked, then
- then almost none of the X11 fonts are installed, and it's not
- unlikely that "-*-*-medium-r-*-*-*-140-*-*-*-10646-1" will
- match an Arabic or or Japanese font that contains no Latin
- glyphs at all, even in a Latin locale. So in that case, just
- let "helvetica" fall back to "fixed".
- */
- /* "*" */
- };
- const char *fixed[] = {
- "courier",
- "courier new",
- "courier 10 pitch",
- "lucidatypewriter",
- "american typewriter",
- "fixed",
- "ocr a std",
- /* As above, but "can't happen" because we already tried fixed? */
- /* "*" */
- };
- const char *charsets[] = { "iso10646-1", "iso8859-1", "*-*" };
- const char *weights[] = { "bold", "medium" };
- const char *slants[] = { "o", "i", "r" };
- const char *spacings[] = { "m", "c", "p" };
- int a, b, c, d, e, g;
- char buf[1024];
-
- for (a = 0; a < countof(charsets); a++)
- for (b = (bold_p ? 0 : 1); b < countof(weights); b++)
- for (c = (italic_p ? 0 : 2); c < countof(slants); c++)
- for (d = 0;
- d < (fixed_p ? countof(fixed) : countof(variable));
- d++)
- for (g = size; g >= 60; g -= 10)
- for (e = (fixed_p ? 0 : 2); e < countof(spacings); e++)
- {
- sprintf (buf,
- "-%s-%s-%s-%s-%s-%s-%s-%d-%s-%s-%s-%s-%s",
- "*", /* foundry */
- (fixed_p ? fixed[d] : variable[d]),
- weights[b],
- slants[c],
- "*", /* set width */
- "*", /* add style */
- "*", /* pixel size */
- g, /* point size */
- "*", /* x resolution */
- "*", /* y resolution */
- spacings[e],
- "*", /* average width */
- charsets[a]);
-# ifdef DEBUG
- fprintf(stderr, "%s: trying %s\n", progname, buf);
-# endif
- f = LOADFONT(buf);
- if (f)
- {
-# ifdef DEBUG
- fprintf (stderr,
- "%s: substituted \"%s\" for \"%s\"\n",
- progname, buf, xlfd);
-# endif
- return f;
- }
- }
-
- fprintf (stderr, "%s: unable to find any alternatives to \"%s\"\n",
- progname, xlfd);
- xlfd = "fixed";
- return LOADFONT(xlfd);
+ f = fallback;
+ fallback = 0;
}
}
-# endif /* !HAVE_JWXYZ */
+
+ if (!font_name) abort();
+ if (!f) abort();
+
+# ifdef DEBUG
+ if (f && font_name)
+ fprintf (stderr, "%s:%s loaded %s\n", progname,
+ (xft_p ? " XFT:" : ""), font_name);
+# if defined(USE_XFT) && defined(HAVE_XFT)
+ if (xft_p && f)
+ {
+ XftPattern *p = ((XftFont *) f)->pattern;
+ char name[1024];
+ char *s, *s1, *s2, *s3;
+ XftNameUnparse (p, name, sizeof(name)-1);
+ s = strstr (name, ":style=");
+ s1 = (s ? strstr (s+1, ",") : 0);
+ s2 = (s ? strstr (s+1, ":") : 0);
+ s3 = (s1 && s1 < s2 ? s1 : s2);
+ if (s3) strcpy (s3+1, " [...]");
+ fprintf (stderr, "%s: XFT name: %s\n", progname, name);
+ }
+# endif /* USE_XFT && HAVE_XFT */
+# endif /* DEBUG */
+
+ if (fallback) UNLOADFONT (fallback);
+ if (font_name) free (font_name);
+ return f;
}
+
+#if 1 /* No longer used in XScreenSaver 6.
+ (Used by retired flag, juggle, xsublim) */
XFontStruct *
-load_font_retry (Display *dpy, const char *xlfd)
+load_font_retry (Display *dpy, const char *font_list)
{
- return (XFontStruct *) load_font_retry_1 (dpy, 0, xlfd, 0);
+ return (XFontStruct *) load_font_retry_1 (dpy, 0, font_list, 0);
}
+#endif
-#ifdef USE_XFT
+#if defined(USE_XFT) || defined(HAVE_JWXYZ)
XftFont *
-load_xft_font_retry (Display *dpy, int screen, const char *xlfd)
+load_xft_font_retry (Display *dpy, int screen, const char *font_list)
{
- return (XftFont *) load_font_retry_1 (dpy, screen, xlfd, 1);
+ return (XftFont *) load_font_retry_1 (dpy, screen, font_list, 1);
}
+#endif
-#elif defined(HAVE_JWXYZ)
-XftFont *
-load_xft_font_retry (Display *dpy, int screen, const char *xlfd)
+
+#ifdef SELFTEST
+static void
+xft_selftest (Display *dpy, int screen)
{
- return XftFontOpenXlfd (dpy, screen, xlfd);
-}
+ int i;
+ const char *tests[] = {
+ "-*-ocr a std-medium-r-*-*-*-480-*-*-m-*-*-*",
+ "OCR A Std-48",
+ "OCR A Std-48:style=Bold Italic",
+ "OCR A Std-48:spacing=100",
+ "OCR A Std-48:spacing=110",
+ "-*-courier-medium-r-*-*-*-480-*-*-m-*-*-*",
+ "-*-courier-bold-o-*-*-*-480-*-*-m-*-*-*",
+ "Courier-48:style=Bold Italic",
+ "Courier-48:style=Italic Bold", /* seems to be illegal */
+ "Courier-48:spacing=100",
+ "Courier-48:spacing=110",
+ "-*-helvetica-bold-o-*-*-*-480-*-*-m-*-*-*",
+ "Helvetica-48:style=Bold Italic",
+ "Liberation Mono-48:style=Bold Italic",
+ "Liberation Sans-48:style=Bold Italic",
+ "-*-sans serif-bold-o-*-*-*-480-*-*-m-*-*-*",
+ "-*-sans-serif-bold-o-*-*-*-480-*-*-m-*-*-*",
+ "-*-sans\\-serif-bold-o-*-*-*-480-*-*-m-*-*-*",
+ };
+
+ const char *tests2[] = { "Helvetica 10",
+ "Helvetica Bold 10",
+ "Helvetica Bold Italic 10",
+ "Helvetica Oblique Bold-10.5",
+ "Times New Roman-10",
+ "Times New Roman Bold-10",
+ "Times New Roman-Bold Oblique Italic 10",
+ "Times New Roman-Oblique Italic Bold 10",
+ "Times-20:style=Bold",
+ "Times-Oblique-20:style=Bold",
+ "sans serif-20:style=Bold",
+ "sans-serif-20:style=Bold",
+ "sans\\-serif-20:style=Bold",
+ };
+
+ fprintf (stderr, "\n");
+ for (i = 0; i < countof(tests2); i++)
+ fprintf (stderr, "%s\n%s\n\n", tests2[i], font_name_to_xlfd (tests2[i]));
+
+ fprintf (stderr, "\n");
+ for (i = 0; i < countof(tests); i++) {
+ const char *name1 = tests[i];
+ XftResult ret;
+ XftPattern *pat1 = 0, *pat2 = 0, *pat3 = 0;
+ char name2[1024], name3[1024];
+ XftFont *ff;
+ Bool xlfd_p = (*name1 == '-');
-#endif /* !HAVE_JWXYZ */
+# define TRUNC(V) do { \
+ char *s = strstr (V, ":style="); \
+ char *s1 = (s ? strstr (s+1, ",") : 0); \
+ char *s2 = (s ? strstr (s+1, ":") : 0); \
+ char *s3 = (s1 && s1 < s2 ? s1 : s2); \
+ if (s3) strcpy (s3+1, " [...]"); \
+ } while(0)
+
+ *name2 = 0;
+
+ /* Name to Parse */
+
+ if (xlfd_p)
+ pat1 = XftXlfdParse (name1, True, True);
+ else
+ pat1 = XftNameParse (name1);
+ XftNameUnparse (pat1, name2, sizeof(name2)-1);
+ TRUNC(name2);
+ fprintf (stderr, "%s (\"%s\")\n\t-> \"%s\"\n",
+ (xlfd_p ? "XftXlfdParse" : "XftNameParse"),
+ name1, name2);
+
+
+ /* Name to pattern to Open */
+
+ ff = XftFontOpenPattern (dpy, pat1);
+ if (ff) {
+ pat2 = ff->pattern;
+ XftNameUnparse (pat2, name3, sizeof(name3)-1);
+ } else {
+ pat2 = 0;
+ strcpy (name3, "NULL");
+ }
+ TRUNC(name3);
+ fprintf (stderr, "XftFontOpenPattern (\"%s\")\n\t-> \"%s\"\n",
+ name2, name3);
+
+
+ /* Name to pattern to Match */
+
+ pat2 = XftFontMatch (dpy, screen, pat1, &ret);
+ XftNameUnparse (pat2, name3, sizeof(name3)-1);
+ TRUNC(name3);
+ fprintf (stderr, "XftFontMatch (\"%s\")\n\t-> \"%s\", %s\n",
+ name2, name3,
+ (ret == XftResultMatch ? "Match" :
+ ret == XftResultNoMatch ? "NoMatch" :
+ ret == XftResultTypeMismatch ? "TypeMismatch" :
+ ret == XftResultNoId ? "NoId" : "???"));
+
+
+ /* Name to pattern to Match to Open */
+
+ strcpy (name2, name3);
+ ff = XftFontOpenPattern (dpy, pat2);
+ if (ff) {
+ pat3 = ff->pattern;
+ XftNameUnparse (pat3, name3, sizeof(name3)-1);
+ } else {
+ pat3 = 0;
+ strcpy (name3, "NULL");
+ }
+ TRUNC(name3);
+ fprintf (stderr, "XftFontOpenPattern (\"%s\")\n\t-> \"%s\"\n",
+ name2, name3);
+
+
+ /* Name to Open */
+
+ ff = (xlfd_p
+ ? XftFontOpenXlfd (dpy, screen, name1)
+ : XftFontOpenName (dpy, screen, name1));
+ *name2 = 0;
+ if (ff) {
+ pat1 = ff->pattern;
+ XftNameUnparse (pat1, name2, sizeof(name2)-1);
+ } else {
+ strcpy (name2, "NULL");
+ }
+ TRUNC(name2);
+ fprintf (stderr, "%s (\"%s\")\n\t-> \"%s\"\n",
+ (xlfd_p ? "XftFontOpenXlfd" : "XftFontOpenName"),
+ name1, name2);
+ fprintf (stderr, "\n");
+ }
+
+ exit (0);
+}
+#endif /* SELFTEST */