From debbee0fe4f5f62823673f59dc82afccf33dc08f Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sat, 22 Mar 2008 18:11:17 +0000 Subject: [PATCH] Implemented a Pango back end. GTK 2 PuTTY can now switch seamlessly back and forth between X fonts and Pango fonts, provided you're willing to type in the names of the former by hand. [originally from svn r7937] --- unix/gtkfont.c | 196 ++++++++++++++++++++++++++++++++++++++++++++----- unix/gtkfont.h | 4 +- unix/gtkwin.c | 19 +++-- 3 files changed, 191 insertions(+), 28 deletions(-) diff --git a/unix/gtkfont.c b/unix/gtkfont.c index 8fabdd23..24c19dbf 100644 --- a/unix/gtkfont.c +++ b/unix/gtkfont.c @@ -25,10 +25,11 @@ /* * To do: * - * - import flags to do VT100 double-width, and import the icky - * pixmap stretch code for it. + * - import flags to do VT100 double-width; import the icky + * pixmap stretch code on to the X11 side, and do something + * nicer in Pango. * - * - add the Pango back end! + * - unified font selector dialog, arrgh! */ /* @@ -56,12 +57,12 @@ struct unifont_vtable { /* * `Methods' of the `class'. */ - unifont *(*create)(char *name, int wide, int bold, + unifont *(*create)(GtkWidget *widget, char *name, int wide, int bold, int shadowoffset, int shadowalways); void (*destroy)(unifont *font); void (*draw_text)(GdkDrawable *target, GdkGC *gc, unifont *font, int x, int y, const char *string, int len, int wide, - int bold); + int bold, int cellwidth); /* * `Static data members' of the `class'. */ @@ -74,8 +75,9 @@ struct unifont_vtable { static void x11font_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, int x, int y, const char *string, int len, - int wide, int bold); -static unifont *x11font_create(char *name, int wide, int bold, + int wide, int bold, int cellwidth); +static unifont *x11font_create(GtkWidget *widget, char *name, + int wide, int bold, int shadowoffset, int shadowalways); static void x11font_destroy(unifont *font); @@ -99,11 +101,6 @@ struct x11font { * whether we use gdk_draw_text_wc() or gdk_draw_text(). */ int sixteen_bit; - /* - * Font charsets. public_charset and real_charset can differ - * for X11 fonts, because many X fonts use CS_ISO8859_1_X11. - */ - int public_charset, real_charset; /* * Data passed in to unifont_create(). */ @@ -181,7 +178,8 @@ static int x11_font_width(GdkFont *font, int sixteen_bit) } } -static unifont *x11font_create(char *name, int wide, int bold, +static unifont *x11font_create(GtkWidget *widget, char *name, + int wide, int bold, int shadowoffset, int shadowalways) { struct x11font *xfont; @@ -299,7 +297,7 @@ static void x11_alloc_subfont(struct x11font *xfont, int sfid) static void x11font_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, int x, int y, const char *string, int len, - int wide, int bold) + int wide, int bold, int cellwidth) { struct x11font *xfont = (struct x11font *)font; int sfid; @@ -374,6 +372,164 @@ static void x11font_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, } } +/* ---------------------------------------------------------------------- + * Pango font implementation. + */ + +static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, + int x, int y, const char *string, int len, + int wide, int bold, int cellwidth); +static unifont *pangofont_create(GtkWidget *widget, char *name, + int wide, int bold, + int shadowoffset, int shadowalways); +static void pangofont_destroy(unifont *font); + +struct pangofont { + struct unifont u; + /* + * Pango objects. + */ + PangoFontDescription *desc; + PangoFontset *fset; + /* + * The containing widget. + */ + GtkWidget *widget; + /* + * Data passed in to unifont_create(). + */ + int bold, shadowoffset, shadowalways; +}; + +static const struct unifont_vtable pangofont_vtable = { + pangofont_create, + pangofont_destroy, + pangofont_draw_text, + "pango" +}; + +static unifont *pangofont_create(GtkWidget *widget, char *name, + int wide, int bold, + int shadowoffset, int shadowalways) +{ + struct pangofont *pfont; + PangoContext *ctx; + PangoFontMap *map; + PangoFontDescription *desc; + PangoFontset *fset; + PangoFontMetrics *metrics; + + desc = pango_font_description_from_string(name); + if (!desc) + return NULL; + ctx = gtk_widget_get_pango_context(widget); + if (!ctx) { + pango_font_description_free(desc); + return NULL; + } + map = pango_context_get_font_map(ctx); + if (!map) { + pango_font_description_free(desc); + return NULL; + } + fset = pango_font_map_load_fontset(map, ctx, desc, + pango_context_get_language(ctx)); + if (!fset) { + pango_font_description_free(desc); + return NULL; + } + metrics = pango_fontset_get_metrics(fset); + if (!metrics || + pango_font_metrics_get_approximate_digit_width(metrics) == 0) { + pango_font_description_free(desc); + g_object_unref(fset); + return NULL; + } + + pfont = snew(struct pangofont); + pfont->u.vt = &pangofont_vtable; + pfont->u.width = + PANGO_PIXELS(pango_font_metrics_get_approximate_digit_width(metrics)); + pfont->u.ascent = PANGO_PIXELS(pango_font_metrics_get_ascent(metrics)); + pfont->u.descent = PANGO_PIXELS(pango_font_metrics_get_descent(metrics)); + pfont->u.height = pfont->u.ascent + pfont->u.descent; + /* The Pango API is hardwired to UTF-8 */ + pfont->u.public_charset = CS_UTF8; + pfont->u.real_charset = CS_UTF8; + pfont->desc = desc; + pfont->fset = fset; + pfont->widget = widget; + pfont->bold = bold; + pfont->shadowoffset = shadowoffset; + pfont->shadowalways = shadowalways; + + return (unifont *)pfont; +} + +static void pangofont_destroy(unifont *font) +{ + struct pangofont *pfont = (struct pangofont *)font; + pfont = pfont; /* FIXME */ + pango_font_description_free(pfont->desc); + g_object_unref(pfont->fset); + sfree(font); +} + +static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, + int x, int y, const char *string, int len, + int wide, int bold, int cellwidth) +{ + struct pangofont *pfont = (struct pangofont *)font; + PangoLayout *layout; + PangoRectangle rect; + int shadowbold = FALSE; + + if (wide) + cellwidth *= 2; + + y -= pfont->u.ascent; + + layout = pango_layout_new(gtk_widget_get_pango_context(pfont->widget)); + pango_layout_set_font_description(layout, pfont->desc); + if (bold > pfont->bold) { + if (pfont->shadowalways) + shadowbold = TRUE; + else { + PangoFontDescription *desc2 = + pango_font_description_copy_static(pfont->desc); + pango_font_description_set_weight(desc2, PANGO_WEIGHT_BOLD); + pango_layout_set_font_description(layout, desc2); + } + } + + while (len > 0) { + int clen; + + /* + * Extract a single UTF-8 character from the string. + */ + clen = 1; + while (clen < len && + (unsigned char)string[clen] >= 0x80 && + (unsigned char)string[clen] < 0xC0) + clen++; + + pango_layout_set_text(layout, string, clen); + pango_layout_get_pixel_extents(layout, NULL, &rect); + gdk_draw_layout(target, gc, x + (cellwidth - rect.width)/2, + y + (pfont->u.height - rect.height)/2, layout); + if (shadowbold) + gdk_draw_layout(target, gc, x + (cellwidth - rect.width)/2 + pfont->shadowoffset, + y + (pfont->u.height - rect.height)/2, layout); + + len -= clen; + string += clen; + x += cellwidth; + } + + g_object_unref(layout); +} + /* ---------------------------------------------------------------------- * Outermost functions which do the vtable dispatch. */ @@ -385,9 +541,10 @@ static void x11font_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, * the font name. */ static const struct unifont_vtable *unifont_types[] = { + &pangofont_vtable, &x11font_vtable, }; -unifont *unifont_create(char *name, int wide, int bold, +unifont *unifont_create(GtkWidget *widget, char *name, int wide, int bold, int shadowoffset, int shadowalways) { int colonpos = strcspn(name, ":"); @@ -405,14 +562,14 @@ unifont *unifont_create(char *name, int wide, int bold, } if (i == lenof(unifont_types)) return NULL; /* prefix not recognised */ - return unifont_types[i]->create(name+colonpos+1, wide, bold, + return unifont_types[i]->create(widget, name+colonpos+1, wide, bold, shadowoffset, shadowalways); } else { /* * No colon prefix, so just go through all the subclasses. */ for (i = 0; i < lenof(unifont_types); i++) { - unifont *ret = unifont_types[i]->create(name, wide, bold, + unifont *ret = unifont_types[i]->create(widget, name, wide, bold, shadowoffset, shadowalways); if (ret) @@ -429,7 +586,8 @@ void unifont_destroy(unifont *font) void unifont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, int x, int y, const char *string, int len, - int wide, int bold) + int wide, int bold, int cellwidth) { - font->vt->draw_text(target, gc, font, x, y, string, len, wide, bold); + font->vt->draw_text(target, gc, font, x, y, string, len, + wide, bold, cellwidth); } diff --git a/unix/gtkfont.h b/unix/gtkfont.h index 9cb66783..5c36fee5 100644 --- a/unix/gtkfont.h +++ b/unix/gtkfont.h @@ -36,11 +36,11 @@ typedef struct unifont { int width, height, ascent, descent; } unifont; -unifont *unifont_create(char *name, int wide, int bold, +unifont *unifont_create(GtkWidget *widget, char *name, int wide, int bold, int shadowoffset, int shadowalways); void unifont_destroy(unifont *font); void unifont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, int x, int y, const char *string, int len, - int wide, int bold); + int wide, int bold, int cellwidth); #endif /* PUTTY_GTKFONT_H */ diff --git a/unix/gtkwin.c b/unix/gtkwin.c index 2972dba1..5e4c6946 100644 --- a/unix/gtkwin.c +++ b/unix/gtkwin.c @@ -1455,7 +1455,7 @@ void palette_reset(void *frontend) /* Since Default Background may have changed, ensure that space * between text area and window border is refreshed. */ set_window_background(inst); - if (inst->area) { + if (inst->area && inst->area->window) { draw_backing_rect(inst); gtk_widget_queue_draw(inst->area); } @@ -2026,7 +2026,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, unifont_draw_text(inst->pixmap, gc, inst->fonts[fontid], x*inst->font_width+inst->cfg.window_border, y*inst->font_height+inst->cfg.window_border+inst->fonts[0]->ascent, - gcs, mblen, widefactor > 1, bold); + gcs, mblen, widefactor > 1, bold, inst->font_width); } sfree(gcs); @@ -2618,7 +2618,8 @@ void setup_fonts_ucs(struct gui_data *inst) if (inst->fonts[3]) unifont_destroy(inst->fonts[3]); - inst->fonts[0] = unifont_create(inst->cfg.font.name, FALSE, FALSE, + inst->fonts[0] = unifont_create(inst->area, inst->cfg.font.name, + FALSE, FALSE, inst->cfg.shadowboldoffset, inst->cfg.shadowbold); if (!inst->fonts[0]) { @@ -2630,7 +2631,8 @@ void setup_fonts_ucs(struct gui_data *inst) if (inst->cfg.shadowbold || !inst->cfg.boldfont.name[0]) { inst->fonts[1] = NULL; } else { - inst->fonts[1] = unifont_create(inst->cfg.boldfont.name, FALSE, TRUE, + inst->fonts[1] = unifont_create(inst->area, inst->cfg.boldfont.name, + FALSE, TRUE, inst->cfg.shadowboldoffset, inst->cfg.shadowbold); if (!inst->fonts[1]) { @@ -2641,7 +2643,8 @@ void setup_fonts_ucs(struct gui_data *inst) } if (inst->cfg.widefont.name[0]) { - inst->fonts[2] = unifont_create(inst->cfg.widefont.name, TRUE, FALSE, + inst->fonts[2] = unifont_create(inst->area, inst->cfg.widefont.name, + TRUE, FALSE, inst->cfg.shadowboldoffset, inst->cfg.shadowbold); if (!inst->fonts[2]) { @@ -2656,7 +2659,8 @@ void setup_fonts_ucs(struct gui_data *inst) if (inst->cfg.shadowbold || !inst->cfg.wideboldfont.name[0]) { inst->fonts[3] = NULL; } else { - inst->fonts[3] = unifont_create(inst->cfg.wideboldfont.name, TRUE, + inst->fonts[3] = unifont_create(inst->area, + inst->cfg.wideboldfont.name, TRUE, TRUE, inst->cfg.shadowboldoffset, inst->cfg.shadowbold); if (!inst->fonts[3]) { @@ -3320,6 +3324,8 @@ int pt_main(int argc, char **argv) if (!utf8_string_atom) utf8_string_atom = gdk_atom_intern("UTF8_STRING", FALSE); + inst->area = gtk_drawing_area_new(); + setup_fonts_ucs(inst); init_cutbuffers(); @@ -3333,7 +3339,6 @@ int pt_main(int argc, char **argv) inst->width = inst->cfg.width; inst->height = inst->cfg.height; - inst->area = gtk_drawing_area_new(); gtk_drawing_area_size(GTK_DRAWING_AREA(inst->area), inst->font_width * inst->cfg.width + 2*inst->cfg.window_border, inst->font_height * inst->cfg.height + 2*inst->cfg.window_border);