| 1 | /* xscreensaver, Copyright (c) 2018-2020 by Jamie Zawinski <jwz@jwz.org> |
| 2 | * |
| 3 | * Permission to use, copy, modify, distribute, and sell this software and its |
| 4 | * documentation for any purpose is hereby granted without fee, provided that |
| 5 | * the above copyright notice appear in all copies and that both that |
| 6 | * copyright notice and this permission notice appear in supporting |
| 7 | * documentation. No representations are made about the suitability of this |
| 8 | * software for any purpose. It is provided "as is" without express or |
| 9 | * implied warranty. |
| 10 | */ |
| 11 | |
| 12 | /* Like XLoadQueryFont, but if it fails, it tries some heuristics to |
| 13 | load something close. |
| 14 | */ |
| 15 | |
| 16 | #define _GNU_SOURCE |
| 17 | |
| 18 | #include "utils.h" |
| 19 | #include "visual.h" |
| 20 | #include "xft.h" |
| 21 | #include "font-retry.h" |
| 22 | |
| 23 | extern const char *progname; |
| 24 | |
| 25 | #undef countof |
| 26 | #define countof(x) (sizeof((x))/sizeof((*x))) |
| 27 | |
| 28 | #undef DEBUG |
| 29 | |
| 30 | static void * |
| 31 | load_font_retry_1 (Display *dpy, int screen, const char *xlfd, Bool xft_p) |
| 32 | { |
| 33 | |
| 34 | # ifdef USE_XFT |
| 35 | # define LOADFONT(F) (xft_p \ |
| 36 | ? (void *) XftFontOpenXlfd (dpy, screen, (F)) \ |
| 37 | : (void *) XLoadQueryFont (dpy, (F))) |
| 38 | # else |
| 39 | # define LOADFONT(F) ((void *) XLoadQueryFont (dpy, (F))) |
| 40 | # endif |
| 41 | |
| 42 | void *f = xlfd ? LOADFONT(xlfd) : 0; |
| 43 | |
| 44 | # ifndef USE_XFT |
| 45 | if (xft_p) abort(); |
| 46 | # endif |
| 47 | |
| 48 | # ifdef HAVE_JWXYZ |
| 49 | return f; |
| 50 | # else /* !HAVE_JWXYZ */ |
| 51 | if (! xlfd) xlfd = "<null>"; |
| 52 | if (f) |
| 53 | { |
| 54 | # ifdef DEBUG |
| 55 | fprintf (stderr, "%s: loaded %s\n", progname, xlfd); |
| 56 | # endif |
| 57 | return f; |
| 58 | } |
| 59 | else |
| 60 | { |
| 61 | Bool bold_p = (!!strcasestr (xlfd, "-bold-") || |
| 62 | !!strcasestr (xlfd, "-ocr")); |
| 63 | Bool italic_p = (!!strcasestr (xlfd, "-i-") || |
| 64 | !!strcasestr (xlfd, "-o-")); |
| 65 | Bool fixed_p = (!!strcasestr (xlfd, "courier") || |
| 66 | !!strcasestr (xlfd, "-ocr") || |
| 67 | !!strcasestr (xlfd, "-m-") || |
| 68 | !!strcasestr (xlfd, "-c-")); |
| 69 | int size = 0; |
| 70 | |
| 71 | # ifdef DEBUG |
| 72 | fprintf (stderr, "%s: failed %s\n", progname, xlfd); |
| 73 | # endif |
| 74 | |
| 75 | if (!strcmp (xlfd, "vga")) /* BSOD uses this: it has no XLFD name. */ |
| 76 | fixed_p = True, size = 120; |
| 77 | |
| 78 | /* Look for the first number in the string like "-180-" */ |
| 79 | if (! size) |
| 80 | { |
| 81 | const char *s; |
| 82 | for (s = xlfd; *s; s++) |
| 83 | if (s[0] == '-' && s[1] >= '0' && s[1] <= '9') |
| 84 | { |
| 85 | int i = s[1] - '0'; |
| 86 | const char *s2 = s+2; |
| 87 | while (*s2 >= '0' && *s2 <= '9') |
| 88 | { |
| 89 | i = i * 10 + *s2 - '0'; |
| 90 | s2++; |
| 91 | } |
| 92 | if (*s2 != '-') continue; /* Number ends with dash */ |
| 93 | if (i < 60 || i >= 2000) continue; /* In range 6pt - 200pt */ |
| 94 | if (i % 10) continue; /* Multiple of 10 */ |
| 95 | |
| 96 | size = i; |
| 97 | break; |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | if (! size) |
| 102 | { |
| 103 | fprintf (stderr, "%s: unloadable, unparsable font: \"%s\"\n", |
| 104 | progname, xlfd); |
| 105 | xlfd = "fixed"; |
| 106 | return LOADFONT(xlfd); |
| 107 | } |
| 108 | else |
| 109 | { |
| 110 | const char *variable[] = { |
| 111 | "helvetica", |
| 112 | "arial", |
| 113 | "bitstream vera sans", /* sometimes foundry is in family */ |
| 114 | "vera sans", /* sometimes not? */ |
| 115 | "gill sans", |
| 116 | "times", |
| 117 | "times new roman", |
| 118 | "new century schoolbook", |
| 119 | "utopia", |
| 120 | "palatino", |
| 121 | "lucida", |
| 122 | "clearlyu", |
| 123 | "bitstream charter", /* sometimes foundry is in family */ |
| 124 | "charter", /* sometimes not? */ |
| 125 | "utopia", |
| 126 | "luxi sans", |
| 127 | "latin modern roman", |
| 128 | |
| 129 | /* Don't use a wildcard family. If none of the above worked, then |
| 130 | then almost none of the X11 fonts are installed, and it's not |
| 131 | unlikely that "-*-*-medium-r-*-*-*-140-*-*-*-10646-1" will |
| 132 | match an Arabic or or Japanese font that contains no Latin |
| 133 | glyphs at all, even in a Latin locale. So in that case, just |
| 134 | let "helvetica" fall back to "fixed". |
| 135 | */ |
| 136 | /* "*" */ |
| 137 | }; |
| 138 | const char *fixed[] = { |
| 139 | "courier", |
| 140 | "courier new", |
| 141 | "courier 10 pitch", |
| 142 | "lucidatypewriter", |
| 143 | "american typewriter", |
| 144 | "luxi mono", |
| 145 | "fixed", |
| 146 | "ocr a std", |
| 147 | /* As above, but "can't happen" because we already tried fixed? */ |
| 148 | /* "*" */ |
| 149 | }; |
| 150 | const char *charsets[] = { "iso10646-1", "iso8859-1", "*-*" }; |
| 151 | const char *weights[] = { "bold", "medium" }; |
| 152 | const char *slants[] = { "o", "i", "r" }; |
| 153 | const char *spacings[] = { "m", "c", "p" }; |
| 154 | int a, b, c, d, e, g; |
| 155 | char buf[1024]; |
| 156 | |
| 157 | for (a = 0; a < countof(charsets); a++) |
| 158 | for (b = (bold_p ? 0 : 1); b < countof(weights); b++) |
| 159 | for (c = (italic_p ? 0 : 2); c < countof(slants); c++) |
| 160 | for (d = 0; |
| 161 | d < (fixed_p ? countof(fixed) : countof(variable)); |
| 162 | d++) |
| 163 | for (g = size; g >= 60; g -= 10) |
| 164 | for (e = (fixed_p ? 0 : 2); e < countof(spacings); e++) |
| 165 | { |
| 166 | sprintf (buf, |
| 167 | "-%s-%s-%s-%s-%s-%s-%s-%d-%s-%s-%s-%s-%s", |
| 168 | "*", /* foundry */ |
| 169 | (fixed_p ? fixed[d] : variable[d]), |
| 170 | weights[b], |
| 171 | slants[c], |
| 172 | "*", /* set width */ |
| 173 | "*", /* add style */ |
| 174 | "*", /* pixel size */ |
| 175 | g, /* point size */ |
| 176 | "*", /* x resolution */ |
| 177 | "*", /* y resolution */ |
| 178 | spacings[e], |
| 179 | "*", /* average width */ |
| 180 | charsets[a]); |
| 181 | # ifdef DEBUG |
| 182 | fprintf(stderr, "%s: trying %s\n", progname, buf); |
| 183 | # endif |
| 184 | f = LOADFONT(buf); |
| 185 | if (f) |
| 186 | { |
| 187 | # ifdef DEBUG |
| 188 | fprintf (stderr, |
| 189 | "%s: substituted \"%s\" for \"%s\"\n", |
| 190 | progname, buf, xlfd); |
| 191 | # endif |
| 192 | return f; |
| 193 | } |
| 194 | } |
| 195 | |
| 196 | fprintf (stderr, "%s: unable to find any alternatives to \"%s\"\n", |
| 197 | progname, xlfd); |
| 198 | xlfd = "fixed"; |
| 199 | return LOADFONT(xlfd); |
| 200 | } |
| 201 | } |
| 202 | # endif /* !HAVE_JWXYZ */ |
| 203 | } |
| 204 | |
| 205 | XFontStruct * |
| 206 | load_font_retry (Display *dpy, const char *xlfd) |
| 207 | { |
| 208 | return (XFontStruct *) load_font_retry_1 (dpy, 0, xlfd, 0); |
| 209 | } |
| 210 | |
| 211 | #ifdef USE_XFT |
| 212 | XftFont * |
| 213 | load_xft_font_retry (Display *dpy, int screen, const char *xlfd) |
| 214 | { |
| 215 | return (XftFont *) load_font_retry_1 (dpy, screen, xlfd, 1); |
| 216 | } |
| 217 | |
| 218 | #elif defined(HAVE_JWXYZ) |
| 219 | |
| 220 | XftFont * |
| 221 | load_xft_font_retry (Display *dpy, int screen, const char *xlfd) |
| 222 | { |
| 223 | return XftFontOpenXlfd (dpy, screen, xlfd); |
| 224 | } |
| 225 | |
| 226 | #endif /* !HAVE_JWXYZ */ |