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; import the icky
29 * pixmap stretch code on to the X11 side, and do something
32 * - unified font selector dialog, arrgh!
38 * - all the GDK font functions used in the x11font subclass are
39 * deprecated, so one day they may go away. When this happens -
40 * or before, if I'm feeling proactive - it oughtn't to be too
41 * difficult in principle to convert the whole thing to use
42 * actual Xlib font calls.
46 * Ad-hoc vtable mechanism to allow font structures to be
49 * Any instance of `unifont' used in the vtable functions will
50 * actually be the first element of a larger structure containing
51 * data specific to the subtype. This is permitted by the ISO C
52 * provision that one may safely cast between a pointer to a
53 * structure and a pointer to its first element.
56 struct unifont_vtable {
58 * `Methods' of the `class'.
60 unifont *(*create)(GtkWidget *widget, char *name, int wide, int bold,
61 int shadowoffset, int shadowalways);
62 void (*destroy)(unifont *font);
63 void (*draw_text)(GdkDrawable *target, GdkGC *gc, unifont *font,
64 int x, int y, const char *string, int len, int wide,
65 int bold, int cellwidth);
67 * `Static data members' of the `class'.
72 /* ----------------------------------------------------------------------
73 * GDK-based X11 font implementation.
76 static void x11font_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
77 int x, int y, const char *string, int len,
78 int wide, int bold, int cellwidth);
79 static unifont *x11font_create(GtkWidget *widget, char *name,
81 int shadowoffset, int shadowalways);
82 static void x11font_destroy(unifont *font);
87 * Actual font objects. We store a number of these, for
88 * automatically guessed bold and wide variants.
90 * The parallel array `allocated' indicates whether we've
91 * tried to fetch a subfont already (thus distinguishing NULL
92 * because we haven't tried yet from NULL because we tried and
93 * failed, so that we don't keep trying and failing
99 * `sixteen_bit' is true iff the font object is indexed by
100 * values larger than a byte. That is, this flag tells us
101 * whether we use gdk_draw_text_wc() or gdk_draw_text().
105 * Data passed in to unifont_create().
107 int wide, bold, shadowoffset, shadowalways;
110 static const struct unifont_vtable x11font_vtable = {
117 char *x11_guess_derived_font_name(GdkFont *font, int bold, int wide)
119 XFontStruct *xfs = GDK_FONT_XFONT(font);
120 Display *disp = GDK_FONT_XDISPLAY(font);
121 Atom fontprop = XInternAtom(disp, "FONT", False);
123 if (XGetFontProperty(xfs, fontprop, &ret)) {
124 char *name = XGetAtomName(disp, (Atom)ret);
125 if (name && name[0] == '-') {
127 char *dupname, *extrafree = NULL, *ret;
131 p = q = dupname = dupstr(name); /* skip initial minus */
134 while (*p && nstr < lenof(strings)) {
137 strings[nstr++] = p+1;
142 if (nstr < lenof(strings))
143 return NULL; /* XLFD was malformed */
149 /* 4 is `wideness', which obviously may have changed. */
150 /* 5 is additional style, which may be e.g. `ja' or `ko'. */
151 strings[4] = strings[5] = "*";
152 strings[11] = extrafree = dupprintf("%d", 2*atoi(strings[11]));
155 ret = dupcat("-", strings[ 0], "-", strings[ 1], "-", strings[ 2],
156 "-", strings[ 3], "-", strings[ 4], "-", strings[ 5],
157 "-", strings[ 6], "-", strings[ 7], "-", strings[ 8],
158 "-", strings[ 9], "-", strings[10], "-", strings[11],
159 "-", strings[12], NULL);
169 static int x11_font_width(GdkFont *font, int sixteen_bit)
175 return gdk_text_width(font, (const gchar *)&space, 2);
177 return gdk_char_width(font, ' ');
181 static unifont *x11font_create(GtkWidget *widget, char *name,
183 int shadowoffset, int shadowalways)
185 struct x11font *xfont;
189 Atom charset_registry, charset_encoding;
190 unsigned long registry_ret, encoding_ret;
191 int pubcs, realcs, sixteen_bit;
194 font = gdk_font_load(name);
198 xfs = GDK_FONT_XFONT(font);
199 disp = GDK_FONT_XDISPLAY(font);
201 charset_registry = XInternAtom(disp, "CHARSET_REGISTRY", False);
202 charset_encoding = XInternAtom(disp, "CHARSET_ENCODING", False);
204 pubcs = realcs = CS_NONE;
207 if (XGetFontProperty(xfs, charset_registry, ®istry_ret) &&
208 XGetFontProperty(xfs, charset_encoding, &encoding_ret)) {
210 reg = XGetAtomName(disp, (Atom)registry_ret);
211 enc = XGetAtomName(disp, (Atom)encoding_ret);
213 char *encoding = dupcat(reg, "-", enc, NULL);
214 pubcs = realcs = charset_from_xenc(encoding);
217 * iso10646-1 is the only wide font encoding we
218 * support. In this case, we expect clients to give us
219 * UTF-8, which this module must internally convert
220 * into 16-bit Unicode.
222 if (!strcasecmp(encoding, "iso10646-1")) {
224 pubcs = realcs = CS_UTF8;
228 * Hack for X line-drawing characters: if the primary
229 * font is encoded as ISO-8859-1, and has valid glyphs
230 * in the first 32 char positions, it is assumed that
231 * those glyphs are the VT100 line-drawing character
234 * Actually, we'll hack even harder by only checking
235 * position 0x19 (vertical line, VT100 linedrawing
236 * `x'). Then we can check it easily by seeing if the
237 * ascent and descent differ.
239 if (pubcs == CS_ISO8859_1) {
240 int lb, rb, wid, asc, desc;
245 gdk_string_extents(font, text, &lb, &rb, &wid, &asc, &desc);
247 realcs = CS_ISO8859_1_X11;
254 xfont = snew(struct x11font);
255 xfont->u.vt = &x11font_vtable;
256 xfont->u.width = x11_font_width(font, sixteen_bit);
257 xfont->u.ascent = font->ascent;
258 xfont->u.descent = font->descent;
259 xfont->u.height = xfont->u.ascent + xfont->u.descent;
260 xfont->u.public_charset = pubcs;
261 xfont->u.real_charset = realcs;
262 xfont->fonts[0] = font;
263 xfont->allocated[0] = TRUE;
264 xfont->sixteen_bit = sixteen_bit;
267 xfont->shadowoffset = shadowoffset;
268 xfont->shadowalways = shadowalways;
270 for (i = 1; i < lenof(xfont->fonts); i++) {
271 xfont->fonts[i] = NULL;
272 xfont->allocated[i] = FALSE;
275 return (unifont *)xfont;
278 static void x11font_destroy(unifont *font)
280 struct x11font *xfont = (struct x11font *)font;
283 for (i = 0; i < lenof(xfont->fonts); i++)
285 gdk_font_unref(xfont->fonts[i]);
289 static void x11_alloc_subfont(struct x11font *xfont, int sfid)
291 char *derived_name = x11_guess_derived_font_name
292 (xfont->fonts[0], sfid & 1, !!(sfid & 2));
293 xfont->fonts[sfid] = gdk_font_load(derived_name); /* may be NULL */
294 xfont->allocated[sfid] = TRUE;
298 static void x11font_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
299 int x, int y, const char *string, int len,
300 int wide, int bold, int cellwidth)
302 struct x11font *xfont = (struct x11font *)font;
304 int shadowbold = FALSE;
310 * Decide which subfont we're using, and whether we have to
313 if (xfont->shadowalways && bold) {
317 sfid = 2 * wide + bold;
318 if (!xfont->allocated[sfid])
319 x11_alloc_subfont(xfont, sfid);
320 if (bold && !xfont->fonts[sfid]) {
323 sfid = 2 * wide + bold;
324 if (!xfont->allocated[sfid])
325 x11_alloc_subfont(xfont, sfid);
328 if (!xfont->fonts[sfid])
329 return; /* we've tried our best, but no luck */
331 if (xfont->sixteen_bit) {
333 * This X font has 16-bit character indices, which means
334 * we expect our string to have been passed in UTF-8.
338 int nchars, maxchars, i;
341 * Convert the input string to wide-character Unicode.
344 for (i = 0; i < len; i++)
345 if ((unsigned char)string[i] <= 0x7F ||
346 (unsigned char)string[i] >= 0xC0)
348 wcs = snewn(maxchars+1, wchar_t);
349 nchars = charset_to_unicode((char **)&string, &len, wcs, maxchars,
350 CS_UTF8, NULL, NULL, 0);
351 assert(nchars <= maxchars);
354 xcs = snewn(nchars, XChar2b);
355 for (i = 0; i < nchars; i++) {
356 xcs[i].byte1 = wcs[i] >> 8;
357 xcs[i].byte2 = wcs[i];
360 gdk_draw_text(target, xfont->fonts[sfid], gc,
361 x, y, (gchar *)xcs, nchars*2);
363 gdk_draw_text(target, xfont->fonts[sfid], gc,
364 x + xfont->shadowoffset, y, (gchar *)xcs, nchars*2);
368 gdk_draw_text(target, xfont->fonts[sfid], gc, x, y, string, len);
370 gdk_draw_text(target, xfont->fonts[sfid], gc,
371 x + xfont->shadowoffset, y, string, len);
375 /* ----------------------------------------------------------------------
376 * Pango font implementation.
379 static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
380 int x, int y, const char *string, int len,
381 int wide, int bold, int cellwidth);
382 static unifont *pangofont_create(GtkWidget *widget, char *name,
384 int shadowoffset, int shadowalways);
385 static void pangofont_destroy(unifont *font);
392 PangoFontDescription *desc;
395 * The containing widget.
399 * Data passed in to unifont_create().
401 int bold, shadowoffset, shadowalways;
404 static const struct unifont_vtable pangofont_vtable = {
411 static unifont *pangofont_create(GtkWidget *widget, char *name,
413 int shadowoffset, int shadowalways)
415 struct pangofont *pfont;
418 PangoFontDescription *desc;
420 PangoFontMetrics *metrics;
422 desc = pango_font_description_from_string(name);
425 ctx = gtk_widget_get_pango_context(widget);
427 pango_font_description_free(desc);
430 map = pango_context_get_font_map(ctx);
432 pango_font_description_free(desc);
435 fset = pango_font_map_load_fontset(map, ctx, desc,
436 pango_context_get_language(ctx));
438 pango_font_description_free(desc);
441 metrics = pango_fontset_get_metrics(fset);
443 pango_font_metrics_get_approximate_digit_width(metrics) == 0) {
444 pango_font_description_free(desc);
445 g_object_unref(fset);
449 pfont = snew(struct pangofont);
450 pfont->u.vt = &pangofont_vtable;
452 PANGO_PIXELS(pango_font_metrics_get_approximate_digit_width(metrics));
453 pfont->u.ascent = PANGO_PIXELS(pango_font_metrics_get_ascent(metrics));
454 pfont->u.descent = PANGO_PIXELS(pango_font_metrics_get_descent(metrics));
455 pfont->u.height = pfont->u.ascent + pfont->u.descent;
456 /* The Pango API is hardwired to UTF-8 */
457 pfont->u.public_charset = CS_UTF8;
458 pfont->u.real_charset = CS_UTF8;
461 pfont->widget = widget;
463 pfont->shadowoffset = shadowoffset;
464 pfont->shadowalways = shadowalways;
466 return (unifont *)pfont;
469 static void pangofont_destroy(unifont *font)
471 struct pangofont *pfont = (struct pangofont *)font;
472 pfont = pfont; /* FIXME */
473 pango_font_description_free(pfont->desc);
474 g_object_unref(pfont->fset);
478 static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
479 int x, int y, const char *string, int len,
480 int wide, int bold, int cellwidth)
482 struct pangofont *pfont = (struct pangofont *)font;
485 int shadowbold = FALSE;
490 y -= pfont->u.ascent;
492 layout = pango_layout_new(gtk_widget_get_pango_context(pfont->widget));
493 pango_layout_set_font_description(layout, pfont->desc);
494 if (bold > pfont->bold) {
495 if (pfont->shadowalways)
498 PangoFontDescription *desc2 =
499 pango_font_description_copy_static(pfont->desc);
500 pango_font_description_set_weight(desc2, PANGO_WEIGHT_BOLD);
501 pango_layout_set_font_description(layout, desc2);
509 * Extract a single UTF-8 character from the string.
513 (unsigned char)string[clen] >= 0x80 &&
514 (unsigned char)string[clen] < 0xC0)
517 pango_layout_set_text(layout, string, clen);
518 pango_layout_get_pixel_extents(layout, NULL, &rect);
519 gdk_draw_layout(target, gc, x + (cellwidth - rect.width)/2,
520 y + (pfont->u.height - rect.height)/2, layout);
522 gdk_draw_layout(target, gc, x + (cellwidth - rect.width)/2 + pfont->shadowoffset,
523 y + (pfont->u.height - rect.height)/2, layout);
530 g_object_unref(layout);
533 /* ----------------------------------------------------------------------
534 * Outermost functions which do the vtable dispatch.
538 * This function is the only one which needs to know the full set
539 * of font implementations available, because it has to try each
540 * in turn until one works, or condition on an override prefix in
543 static const struct unifont_vtable *unifont_types[] = {
547 unifont *unifont_create(GtkWidget *widget, char *name, int wide, int bold,
548 int shadowoffset, int shadowalways)
550 int colonpos = strcspn(name, ":");
553 if (name[colonpos]) {
555 * There's a colon prefix on the font name. Use it to work
556 * out which subclass to try to create.
558 for (i = 0; i < lenof(unifont_types); i++) {
559 if (strlen(unifont_types[i]->prefix) == colonpos &&
560 !strncmp(unifont_types[i]->prefix, name, colonpos))
563 if (i == lenof(unifont_types))
564 return NULL; /* prefix not recognised */
565 return unifont_types[i]->create(widget, name+colonpos+1, wide, bold,
566 shadowoffset, shadowalways);
569 * No colon prefix, so just go through all the subclasses.
571 for (i = 0; i < lenof(unifont_types); i++) {
572 unifont *ret = unifont_types[i]->create(widget, name, wide, bold,
578 return NULL; /* font not found in any scheme */
582 void unifont_destroy(unifont *font)
584 font->vt->destroy(font);
587 void unifont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
588 int x, int y, const char *string, int len,
589 int wide, int bold, int cellwidth)
591 font->vt->draw_text(target, gc, font, x, y, string, len,
592 wide, bold, cellwidth);