2 * Unified font management for GTK.
4 * PuTTY is willing to use both old-style X server-side bitmap
5 * fonts _and_ GTK2/Pango client-side fonts. This requires us to
6 * do a bit of work to wrap the two wildly different APIs into
7 * forms the rest of the code can switch between seamlessly, and
8 * also requires a custom font selector capable of handling both
16 #include <gdk/gdkkeysyms.h>
19 #include <X11/Xutil.h>
20 #include <X11/Xatom.h>
28 * - import flags to do VT100 double-width, and import the icky
29 * pixmap stretch code for it.
31 * - add the Pango back end!
37 * - all the GDK font functions used in the x11font subclass are
38 * deprecated, so one day they may go away. When this happens -
39 * or before, if I'm feeling proactive - it oughtn't to be too
40 * difficult in principle to convert the whole thing to use
41 * actual Xlib font calls.
45 * Ad-hoc vtable mechanism to allow font structures to be
48 * Any instance of `unifont' used in the vtable functions will
49 * actually be the first element of a larger structure containing
50 * data specific to the subtype. This is permitted by the ISO C
51 * provision that one may safely cast between a pointer to a
52 * structure and a pointer to its first element.
55 struct unifont_vtable {
57 * `Methods' of the `class'.
59 unifont *(*create)(char *name, int wide, int bold,
60 int shadowoffset, int shadowalways);
61 void (*destroy)(unifont *font);
62 void (*draw_text)(GdkDrawable *target, GdkGC *gc, unifont *font,
63 int x, int y, const char *string, int len, int wide,
66 * `Static data members' of the `class'.
71 /* ----------------------------------------------------------------------
72 * GDK-based X11 font implementation.
75 static void x11font_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
76 int x, int y, const char *string, int len,
78 static unifont *x11font_create(char *name, int wide, int bold,
79 int shadowoffset, int shadowalways);
80 static void x11font_destroy(unifont *font);
85 * Actual font objects. We store a number of these, for
86 * automatically guessed bold and wide variants.
88 * The parallel array `allocated' indicates whether we've
89 * tried to fetch a subfont already (thus distinguishing NULL
90 * because we haven't tried yet from NULL because we tried and
91 * failed, so that we don't keep trying and failing
97 * `sixteen_bit' is true iff the font object is indexed by
98 * values larger than a byte. That is, this flag tells us
99 * whether we use gdk_draw_text_wc() or gdk_draw_text().
103 * Font charsets. public_charset and real_charset can differ
104 * for X11 fonts, because many X fonts use CS_ISO8859_1_X11.
106 int public_charset, real_charset;
108 * Data passed in to unifont_create().
110 int wide, bold, shadowoffset, shadowalways;
113 static const struct unifont_vtable x11font_vtable = {
120 char *x11_guess_derived_font_name(GdkFont *font, int bold, int wide)
122 XFontStruct *xfs = GDK_FONT_XFONT(font);
123 Display *disp = GDK_FONT_XDISPLAY(font);
124 Atom fontprop = XInternAtom(disp, "FONT", False);
126 if (XGetFontProperty(xfs, fontprop, &ret)) {
127 char *name = XGetAtomName(disp, (Atom)ret);
128 if (name && name[0] == '-') {
130 char *dupname, *extrafree = NULL, *ret;
134 p = q = dupname = dupstr(name); /* skip initial minus */
137 while (*p && nstr < lenof(strings)) {
140 strings[nstr++] = p+1;
145 if (nstr < lenof(strings))
146 return NULL; /* XLFD was malformed */
152 /* 4 is `wideness', which obviously may have changed. */
153 /* 5 is additional style, which may be e.g. `ja' or `ko'. */
154 strings[4] = strings[5] = "*";
155 strings[11] = extrafree = dupprintf("%d", 2*atoi(strings[11]));
158 ret = dupcat("-", strings[ 0], "-", strings[ 1], "-", strings[ 2],
159 "-", strings[ 3], "-", strings[ 4], "-", strings[ 5],
160 "-", strings[ 6], "-", strings[ 7], "-", strings[ 8],
161 "-", strings[ 9], "-", strings[10], "-", strings[11],
162 "-", strings[12], NULL);
172 static int x11_font_width(GdkFont *font, int sixteen_bit)
178 return gdk_text_width(font, (const gchar *)&space, 2);
180 return gdk_char_width(font, ' ');
184 static unifont *x11font_create(char *name, int wide, int bold,
185 int shadowoffset, int shadowalways)
187 struct x11font *xfont;
191 Atom charset_registry, charset_encoding;
192 unsigned long registry_ret, encoding_ret;
193 int pubcs, realcs, sixteen_bit;
196 font = gdk_font_load(name);
200 xfs = GDK_FONT_XFONT(font);
201 disp = GDK_FONT_XDISPLAY(font);
203 charset_registry = XInternAtom(disp, "CHARSET_REGISTRY", False);
204 charset_encoding = XInternAtom(disp, "CHARSET_ENCODING", False);
206 pubcs = realcs = CS_NONE;
209 if (XGetFontProperty(xfs, charset_registry, ®istry_ret) &&
210 XGetFontProperty(xfs, charset_encoding, &encoding_ret)) {
212 reg = XGetAtomName(disp, (Atom)registry_ret);
213 enc = XGetAtomName(disp, (Atom)encoding_ret);
215 char *encoding = dupcat(reg, "-", enc, NULL);
216 pubcs = realcs = charset_from_xenc(encoding);
219 * iso10646-1 is the only wide font encoding we
220 * support. In this case, we expect clients to give us
221 * UTF-8, which this module must internally convert
222 * into 16-bit Unicode.
224 if (!strcasecmp(encoding, "iso10646-1")) {
226 pubcs = realcs = CS_UTF8;
230 * Hack for X line-drawing characters: if the primary
231 * font is encoded as ISO-8859-1, and has valid glyphs
232 * in the first 32 char positions, it is assumed that
233 * those glyphs are the VT100 line-drawing character
236 * Actually, we'll hack even harder by only checking
237 * position 0x19 (vertical line, VT100 linedrawing
238 * `x'). Then we can check it easily by seeing if the
239 * ascent and descent differ.
241 if (pubcs == CS_ISO8859_1) {
242 int lb, rb, wid, asc, desc;
247 gdk_string_extents(font, text, &lb, &rb, &wid, &asc, &desc);
249 realcs = CS_ISO8859_1_X11;
256 xfont = snew(struct x11font);
257 xfont->u.vt = &x11font_vtable;
258 xfont->u.width = x11_font_width(font, sixteen_bit);
259 xfont->u.ascent = font->ascent;
260 xfont->u.descent = font->descent;
261 xfont->u.height = xfont->u.ascent + xfont->u.descent;
262 xfont->u.public_charset = pubcs;
263 xfont->u.real_charset = realcs;
264 xfont->fonts[0] = font;
265 xfont->allocated[0] = TRUE;
266 xfont->sixteen_bit = sixteen_bit;
269 xfont->shadowoffset = shadowoffset;
270 xfont->shadowalways = shadowalways;
272 for (i = 1; i < lenof(xfont->fonts); i++) {
273 xfont->fonts[i] = NULL;
274 xfont->allocated[i] = FALSE;
277 return (unifont *)xfont;
280 static void x11font_destroy(unifont *font)
282 struct x11font *xfont = (struct x11font *)font;
285 for (i = 0; i < lenof(xfont->fonts); i++)
287 gdk_font_unref(xfont->fonts[i]);
291 static void x11_alloc_subfont(struct x11font *xfont, int sfid)
293 char *derived_name = x11_guess_derived_font_name
294 (xfont->fonts[0], sfid & 1, !!(sfid & 2));
295 xfont->fonts[sfid] = gdk_font_load(derived_name); /* may be NULL */
296 xfont->allocated[sfid] = TRUE;
300 static void x11font_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
301 int x, int y, const char *string, int len,
304 struct x11font *xfont = (struct x11font *)font;
306 int shadowbold = FALSE;
312 * Decide which subfont we're using, and whether we have to
315 if (xfont->shadowalways && bold) {
319 sfid = 2 * wide + bold;
320 if (!xfont->allocated[sfid])
321 x11_alloc_subfont(xfont, sfid);
322 if (bold && !xfont->fonts[sfid]) {
325 sfid = 2 * wide + bold;
326 if (!xfont->allocated[sfid])
327 x11_alloc_subfont(xfont, sfid);
330 if (!xfont->fonts[sfid])
331 return; /* we've tried our best, but no luck */
333 if (xfont->sixteen_bit) {
335 * This X font has 16-bit character indices, which means
336 * we expect our string to have been passed in UTF-8.
340 int nchars, maxchars, i;
343 * Convert the input string to wide-character Unicode.
346 for (i = 0; i < len; i++)
347 if ((unsigned char)string[i] <= 0x7F ||
348 (unsigned char)string[i] >= 0xC0)
350 wcs = snewn(maxchars+1, wchar_t);
351 nchars = charset_to_unicode((char **)&string, &len, wcs, maxchars,
352 CS_UTF8, NULL, NULL, 0);
353 assert(nchars <= maxchars);
356 xcs = snewn(nchars, XChar2b);
357 for (i = 0; i < nchars; i++) {
358 xcs[i].byte1 = wcs[i] >> 8;
359 xcs[i].byte2 = wcs[i];
362 gdk_draw_text(target, xfont->fonts[sfid], gc,
363 x, y, (gchar *)xcs, nchars*2);
365 gdk_draw_text(target, xfont->fonts[sfid], gc,
366 x + xfont->shadowoffset, y, (gchar *)xcs, nchars*2);
370 gdk_draw_text(target, xfont->fonts[sfid], gc, x, y, string, len);
372 gdk_draw_text(target, xfont->fonts[sfid], gc,
373 x + xfont->shadowoffset, y, string, len);
377 /* ----------------------------------------------------------------------
378 * Outermost functions which do the vtable dispatch.
382 * This function is the only one which needs to know the full set
383 * of font implementations available, because it has to try each
384 * in turn until one works, or condition on an override prefix in
387 static const struct unifont_vtable *unifont_types[] = {
390 unifont *unifont_create(char *name, int wide, int bold,
391 int shadowoffset, int shadowalways)
393 int colonpos = strcspn(name, ":");
396 if (name[colonpos]) {
398 * There's a colon prefix on the font name. Use it to work
399 * out which subclass to try to create.
401 for (i = 0; i < lenof(unifont_types); i++) {
402 if (strlen(unifont_types[i]->prefix) == colonpos &&
403 !strncmp(unifont_types[i]->prefix, name, colonpos))
406 if (i == lenof(unifont_types))
407 return NULL; /* prefix not recognised */
408 return unifont_types[i]->create(name+colonpos+1, wide, bold,
409 shadowoffset, shadowalways);
412 * No colon prefix, so just go through all the subclasses.
414 for (i = 0; i < lenof(unifont_types); i++) {
415 unifont *ret = unifont_types[i]->create(name, wide, bold,
421 return NULL; /* font not found in any scheme */
425 void unifont_destroy(unifont *font)
427 font->vt->destroy(font);
430 void unifont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
431 int x, int y, const char *string, int len,
434 font->vt->draw_text(target, gc, font, x, y, string, len, wide, bold);