X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=unix%2Fgtkfont.c;h=d9980880fcab421a47bda70911df15516612ee23;hb=HEAD;hp=260c960237e821d47537470272e0525a461b110b;hpb=ad994bab57c06bff0c6d06d0ca8806be0bb2afd2;p=PuTTY.git diff --git a/unix/gtkfont.c b/unix/gtkfont.c index 260c9602..d9980880 100644 --- a/unix/gtkfont.c +++ b/unix/gtkfont.c @@ -83,11 +83,15 @@ struct unifont_vtable { void (*draw_text)(unifont_drawctx *ctx, unifont *font, int x, int y, const wchar_t *string, int len, int wide, int bold, int cellwidth); + void (*draw_combining)(unifont_drawctx *ctx, unifont *font, + int x, int y, const wchar_t *string, int len, + int wide, int bold, int cellwidth); void (*enum_fonts)(GtkWidget *widget, fontsel_add_entry callback, void *callback_ctx); char *(*canonify_fontname)(GtkWidget *widget, const char *name, int *size, int *flags, int resolve_aliases); char *(*scale_fontname)(GtkWidget *widget, const char *name, int size); + char *(*size_increment)(unifont *font, int increment); /* * `Static data members' of the `class'. @@ -107,6 +111,9 @@ static int x11font_has_glyph(unifont *font, wchar_t glyph); static void x11font_draw_text(unifont_drawctx *ctx, unifont *font, int x, int y, const wchar_t *string, int len, int wide, int bold, int cellwidth); +static void x11font_draw_combining(unifont_drawctx *ctx, unifont *font, + int x, int y, const wchar_t *string, + int len, int wide, int bold, int cellwidth); static unifont *x11font_create(GtkWidget *widget, const char *name, int wide, int bold, int shadowoffset, int shadowalways); @@ -118,6 +125,7 @@ static char *x11font_canonify_fontname(GtkWidget *widget, const char *name, int resolve_aliases); static char *x11font_scale_fontname(GtkWidget *widget, const char *name, int size); +static char *x11font_size_increment(unifont *font, int increment); #ifdef DRAW_TEXT_CAIRO struct cairo_cached_glyph { @@ -206,12 +214,100 @@ static const struct unifont_vtable x11font_vtable = { x11font_destroy, x11font_has_glyph, x11font_draw_text, + x11font_draw_combining, x11font_enum_fonts, x11font_canonify_fontname, x11font_scale_fontname, + x11font_size_increment, "server", }; +#define XLFD_STRING_PARTS_LIST(S,I) \ + S(foundry) \ + S(family_name) \ + S(weight_name) \ + S(slant) \ + S(setwidth_name) \ + S(add_style_name) \ + I(pixel_size) \ + I(point_size) \ + I(resolution_x) \ + I(resolution_y) \ + S(spacing) \ + I(average_width) \ + S(charset_registry) \ + S(charset_encoding) \ + /* end of list */ + +/* Special value for int fields that xlfd_recompose will render as "*" */ +#define XLFD_INT_WILDCARD INT_MIN + +struct xlfd_decomposed { +#define STR_FIELD(f) const char *f; +#define INT_FIELD(f) int f; + XLFD_STRING_PARTS_LIST(STR_FIELD, INT_FIELD) +#undef STR_FIELD +#undef INT_FIELD +}; + +static struct xlfd_decomposed *xlfd_decompose(const char *xlfd) +{ + void *mem; + char *p, *components[14]; + struct xlfd_decomposed *dec; + int i; + + if (!xlfd) + return NULL; + + mem = smalloc(sizeof(struct xlfd_decomposed) + strlen(xlfd) + 1); + p = ((char *)mem) + sizeof(struct xlfd_decomposed); + strcpy(p, xlfd); + dec = (struct xlfd_decomposed *)mem; + + for (i = 0; i < 14; i++) { + if (*p != '-') { + /* Malformed XLFD: not enough '-' */ + sfree(mem); + return NULL; + } + *p++ = '\0'; + components[i] = p; + p += strcspn(p, "-"); + } + if (*p) { + /* Malformed XLFD: too many '-' */ + sfree(mem); + return NULL; + } + + i = 0; +#define STORE_STR(f) dec->f = components[i++]; +#define STORE_INT(f) dec->f = atoi(components[i++]); + XLFD_STRING_PARTS_LIST(STORE_STR, STORE_INT) +#undef STORE_STR +#undef STORE_INT + + return dec; +} + +static char *xlfd_recompose(const struct xlfd_decomposed *dec) +{ +#define FMT_STR(f) "-%s" +#define ARG_STR(f) , dec->f +#define FMT_INT(f) "%s%.*d" +#define ARG_INT(f) \ + , dec->f == XLFD_INT_WILDCARD ? "-*" : "-" \ + , dec->f == XLFD_INT_WILDCARD ? 0 : 1 \ + , dec->f == XLFD_INT_WILDCARD ? 0 : dec->f + return dupprintf(XLFD_STRING_PARTS_LIST(FMT_STR, FMT_INT) + XLFD_STRING_PARTS_LIST(ARG_STR, ARG_INT)); +#undef FMT_STR +#undef ARG_STR +#undef FMT_INT +#undef ARG_INT +} + static char *x11_guess_derived_font_name(XFontStruct *xfs, int bold, int wide) { Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); @@ -219,48 +315,27 @@ static char *x11_guess_derived_font_name(XFontStruct *xfs, int bold, int wide) unsigned long ret; if (XGetFontProperty(xfs, fontprop, &ret)) { char *name = XGetAtomName(disp, (Atom)ret); - if (name && name[0] == '-') { - const char *strings[13]; - char *dupname, *extrafree = NULL, *ret; - char *p, *q; - int nstr; - - p = q = dupname = dupstr(name); /* skip initial minus */ - nstr = 0; - - while (*p && nstr < lenof(strings)) { - if (*p == '-') { - *p = '\0'; - strings[nstr++] = p+1; - } - p++; - } + struct xlfd_decomposed *xlfd = xlfd_decompose(name); + if (!xlfd) + return NULL; - if (nstr < lenof(strings)) { - sfree(dupname); - return NULL; /* XLFD was malformed */ - } + if (bold) + xlfd->weight_name = "bold"; - if (bold) - strings[2] = "bold"; + if (wide) { + /* Width name obviously may have changed. */ + /* Additional style may now become e.g. `ja' or `ko'. */ + xlfd->setwidth_name = xlfd->add_style_name = "*"; - if (wide) { - /* 4 is `wideness', which obviously may have changed. */ - /* 5 is additional style, which may be e.g. `ja' or `ko'. */ - strings[4] = strings[5] = "*"; - strings[11] = extrafree = dupprintf("%d", 2*atoi(strings[11])); - } - - ret = dupcat("-", strings[ 0], "-", strings[ 1], "-", strings[ 2], - "-", strings[ 3], "-", strings[ 4], "-", strings[ 5], - "-", strings[ 6], "-", strings[ 7], "-", strings[ 8], - "-", strings[ 9], "-", strings[10], "-", strings[11], - "-", strings[12], NULL); - sfree(extrafree); - sfree(dupname); + /* Expect to double the average width. */ + xlfd->average_width *= 2; + } - return ret; - } + { + char *ret = xlfd_recompose(xlfd); + sfree(xlfd); + return ret; + } } return NULL; } @@ -277,22 +352,12 @@ static int x11_font_width(XFontStruct *xfs, int sixteen_bit) } } -static int x11_font_has_glyph(XFontStruct *xfs, int byte1, int byte2) +static const XCharStruct *x11_char_struct( + XFontStruct *xfs, unsigned char byte1, unsigned char byte2) { int index; /* - * Not to be confused with x11font_has_glyph, which is a method of - * the x11font 'class' and hence takes a unifont as argument. This - * is the low-level function which grubs about in an actual - * XFontStruct to see if a given glyph exists. - * - * We must do this ourselves rather than letting Xlib's - * XTextExtents16 do the job, because XTextExtents will helpfully - * substitute the font's default_char for any missing glyph and - * not tell us it did so, which precisely won't help us find out - * which glyphs _are_ missing. - * * The man page for XQueryFont is rather confusing about how the * per_char array in the XFontStruct is laid out, because it gives * formulae for determining the two-byte X character code _from_ @@ -333,10 +398,28 @@ static int x11_font_has_glyph(XFontStruct *xfs, int byte1, int byte2) } if (!xfs->per_char) /* per_char NULL => everything in range exists */ - return TRUE; + return &xfs->max_bounds; - return (xfs->per_char[index].ascent + xfs->per_char[index].descent > 0 || - xfs->per_char[index].width > 0); + return &xfs->per_char[index]; +} + +static int x11_font_has_glyph( + XFontStruct *xfs, unsigned char byte1, unsigned char byte2) +{ + /* + * Not to be confused with x11font_has_glyph, which is a method of + * the x11font 'class' and hence takes a unifont as argument. This + * is the low-level function which grubs about in an actual + * XFontStruct to see if a given glyph exists. + * + * We must do this ourselves rather than letting Xlib's + * XTextExtents16 do the job, because XTextExtents will helpfully + * substitute the font's default_char for any missing glyph and + * not tell us it did so, which precisely won't help us find out + * which glyphs _are_ missing. + */ + const XCharStruct *xcs = x11_char_struct(xfs, byte1, byte2); + return xcs && (xcs->ascent + xcs->descent > 0 || xcs->width > 0); } static unifont *x11font_create(GtkWidget *widget, const char *name, @@ -616,14 +699,18 @@ static void x11font_cairo_cache_glyph(x11font_individual *xfi, int glyphindex) int x, y; unsigned char *bitmap; Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); + const XCharStruct *xcs = x11_char_struct(xfi->xfs, glyphindex >> 8, + glyphindex & 0xFF); bitmap = snewn(xfi->allsize, unsigned char); memset(bitmap, 0, xfi->allsize); image = XGetImage(disp, xfi->pixmap, 0, 0, xfi->pixwidth, xfi->pixheight, AllPlanes, XYPixmap); - for (y = 0; y < xfi->pixheight; y++) { - for (x = 0; x < xfi->pixwidth; x++) { + for (y = xfi->pixoriginy - xcs->ascent; + y < xfi->pixoriginy + xcs->descent; y++) { + for (x = xfi->pixoriginx + xcs->lbearing; + x < xfi->pixoriginx + xcs->rbearing; x++) { unsigned long pixel = XGetPixel(image, x, y); if (pixel) { int byteindex = y * xfi->rowsize + x/8; @@ -868,6 +955,20 @@ static void x11font_draw_text(unifont_drawctx *ctx, unifont *font, } } +static void x11font_draw_combining(unifont_drawctx *ctx, unifont *font, + int x, int y, const wchar_t *string, + int len, int wide, int bold, int cellwidth) +{ + /* + * For server-side fonts, there's no sophisticated system for + * combining characters intelligently, so the best we can do is to + * overprint them on each other in the obvious way. + */ + int i; + for (i = 0; i < len; i++) + x11font_draw_text(ctx, font, x, y, string+i, 1, wide, bold, cellwidth); +} + static void x11font_enum_fonts(GtkWidget *widget, fontsel_add_entry callback, void *callback_ctx) { @@ -889,73 +990,65 @@ static void x11font_enum_fonts(GtkWidget *widget, tmpsize = 0; for (i = 0; i < nnames; i++) { - if (fontnames[i][0] == '-') { + struct xlfd_decomposed *xlfd = xlfd_decompose(fontnames[i]); + if (xlfd) { + char *p, *font, *style, *stylekey, *charset; + int weightkey, slantkey, setwidthkey; + int thistmpsize; + /* - * Dismember an XLFD and convert it into the format - * we'll be using in the font selector. + * Convert a dismembered XLFD into the format we'll be + * using in the font selector. */ - char *components[14]; - char *p, *font, *style, *stylekey, *charset; - int j, weightkey, slantkey, setwidthkey; - int thistmpsize, fontsize, flags; - - thistmpsize = 4 * strlen(fontnames[i]) + 256; - if (tmpsize < thistmpsize) { - tmpsize = thistmpsize; - tmp = sresize(tmp, tmpsize, char); - } - strcpy(tmp, fontnames[i]); + thistmpsize = 4 * strlen(fontnames[i]) + 256; + if (tmpsize < thistmpsize) { + tmpsize = thistmpsize; + tmp = sresize(tmp, tmpsize, char); + } p = tmp; - for (j = 0; j < 14; j++) { - if (*p) - *p++ = '\0'; - components[j] = p; - while (*p && *p != '-') - p++; - } - *p++ = '\0'; /* - * Font name is made up of fields 0 and 1, in reverse - * order with parentheses. (This is what the GTK 1.2 X - * font selector does, and it seems to come out - * looking reasonably sensible.) + * Font name is in the form "family (foundry)". (This is + * what the GTK 1.2 X font selector does, and it seems to + * come out looking reasonably sensible.) */ font = p; - p += 1 + sprintf(p, "%s (%s)", components[1], components[0]); + p += 1 + sprintf(p, "%s (%s)", xlfd->family_name, xlfd->foundry); /* - * Charset is made up of fields 12 and 13. + * Character set. */ charset = p; - p += 1 + sprintf(p, "%s-%s", components[12], components[13]); + p += 1 + sprintf(p, "%s-%s", xlfd->charset_registry, + xlfd->charset_encoding); /* * Style is a mixture of quite a lot of the fields, * with some strange formatting. */ style = p; - p += sprintf(p, "%s", components[2][0] ? components[2] : + p += sprintf(p, "%s", xlfd->weight_name[0] ? xlfd->weight_name : "regular"); - if (!g_ascii_strcasecmp(components[3], "i")) + if (!g_ascii_strcasecmp(xlfd->slant, "i")) p += sprintf(p, " italic"); - else if (!g_ascii_strcasecmp(components[3], "o")) + else if (!g_ascii_strcasecmp(xlfd->slant, "o")) p += sprintf(p, " oblique"); - else if (!g_ascii_strcasecmp(components[3], "ri")) + else if (!g_ascii_strcasecmp(xlfd->slant, "ri")) p += sprintf(p, " reverse italic"); - else if (!g_ascii_strcasecmp(components[3], "ro")) + else if (!g_ascii_strcasecmp(xlfd->slant, "ro")) p += sprintf(p, " reverse oblique"); - else if (!g_ascii_strcasecmp(components[3], "ot")) + else if (!g_ascii_strcasecmp(xlfd->slant, "ot")) p += sprintf(p, " other-slant"); - if (components[4][0] && g_ascii_strcasecmp(components[4], "normal")) - p += sprintf(p, " %s", components[4]); - if (!g_ascii_strcasecmp(components[10], "m")) + if (xlfd->setwidth_name[0] && + g_ascii_strcasecmp(xlfd->setwidth_name, "normal")) + p += sprintf(p, " %s", xlfd->setwidth_name); + if (!g_ascii_strcasecmp(xlfd->spacing, "m")) p += sprintf(p, " [M]"); - if (!g_ascii_strcasecmp(components[10], "c")) + if (!g_ascii_strcasecmp(xlfd->spacing, "c")) p += sprintf(p, " [C]"); - if (components[5][0]) - p += sprintf(p, " %s", components[5]); + if (xlfd->add_style_name[0]) + p += sprintf(p, " %s", xlfd->add_style_name); /* * Style key is the same stuff as above, but with a @@ -964,63 +1057,59 @@ static void x11font_enum_fonts(GtkWidget *widget, */ p++; stylekey = p; - if (!g_ascii_strcasecmp(components[2], "medium") || - !g_ascii_strcasecmp(components[2], "regular") || - !g_ascii_strcasecmp(components[2], "normal") || - !g_ascii_strcasecmp(components[2], "book")) + if (!g_ascii_strcasecmp(xlfd->weight_name, "medium") || + !g_ascii_strcasecmp(xlfd->weight_name, "regular") || + !g_ascii_strcasecmp(xlfd->weight_name, "normal") || + !g_ascii_strcasecmp(xlfd->weight_name, "book")) weightkey = 0; - else if (!g_ascii_strncasecmp(components[2], "demi", 4) || - !g_ascii_strncasecmp(components[2], "semi", 4)) + else if (!g_ascii_strncasecmp(xlfd->weight_name, "demi", 4) || + !g_ascii_strncasecmp(xlfd->weight_name, "semi", 4)) weightkey = 1; else weightkey = 2; - if (!g_ascii_strcasecmp(components[3], "r")) + if (!g_ascii_strcasecmp(xlfd->slant, "r")) slantkey = 0; - else if (!g_ascii_strncasecmp(components[3], "r", 1)) + else if (!g_ascii_strncasecmp(xlfd->slant, "r", 1)) slantkey = 2; else slantkey = 1; - if (!g_ascii_strcasecmp(components[4], "normal")) + if (!g_ascii_strcasecmp(xlfd->setwidth_name, "normal")) setwidthkey = 0; else setwidthkey = 1; - p += sprintf(p, "%04d%04d%s%04d%04d%s%04d%04d%s%04d%s%04d%s", - weightkey, - (int)strlen(components[2]), components[2], - slantkey, - (int)strlen(components[3]), components[3], - setwidthkey, - (int)strlen(components[4]), components[4], - (int)strlen(components[10]), components[10], - (int)strlen(components[5]), components[5]); + p += sprintf( + p, "%04d%04d%s%04d%04d%s%04d%04d%s%04d%s%04d%s", + weightkey, (int)strlen(xlfd->weight_name), xlfd->weight_name, + slantkey, (int)strlen(xlfd->slant), xlfd->slant, + setwidthkey, + (int)strlen(xlfd->setwidth_name), xlfd->setwidth_name, + (int)strlen(xlfd->spacing), xlfd->spacing, + (int)strlen(xlfd->add_style_name), xlfd->add_style_name); assert(p - tmp < thistmpsize); - /* - * Size is in pixels, for our application, so we - * derive it directly from the pixel size field, - * number 6. - */ - fontsize = atoi(components[6]); - /* * Flags: we need to know whether this is a monospaced * font, which we do by examining the spacing field * again. */ flags = FONTFLAG_SERVERSIDE; - if (!strchr("CcMm", components[10][0])) + if (!strchr("CcMm", xlfd->spacing[0])) flags |= FONTFLAG_NONMONOSPACED; /* - * Not sure why, but sometimes the X server will - * deliver dummy font types in which fontsize comes - * out as zero. Filter those out. + * Some fonts have a pixel size of zero, meaning they're + * treated as scalable. For these purposes, we only want + * fonts whose pixel size we actually know, so filter + * those out. */ - if (fontsize) - callback(callback_ctx, fontnames[i], font, charset, - style, stylekey, fontsize, flags, &x11font_vtable); + if (xlfd->pixel_size) + callback(callback_ctx, fontnames[i], font, charset, + style, stylekey, xlfd->pixel_size, flags, + &x11font_vtable); + + sfree(xlfd); } else { /* * This isn't an XLFD, so it must be an alias. @@ -1032,9 +1121,12 @@ static void x11font_enum_fonts(GtkWidget *widget, */ callback(callback_ctx, fontnames[i], fontnames[i], NULL, NULL, NULL, 0, FONTFLAG_SERVERALIAS, &x11font_vtable); + + sfree(xlfd); } } XFreeFontNames(fontnames); + sfree(tmp); } static char *x11font_canonify_fontname(GtkWidget *widget, const char *name, @@ -1096,6 +1188,94 @@ static char *x11font_scale_fontname(GtkWidget *widget, const char *name, return NULL; /* shan't */ } +static char *x11font_size_increment(unifont *font, int increment) +{ + struct x11font *xfont = (struct x11font *)font; + Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); + Atom fontprop = XInternAtom(disp, "FONT", False); + char *returned_name = NULL; + unsigned long ret; + if (XGetFontProperty(xfont->fonts[0].xfs, fontprop, &ret)) { + struct xlfd_decomposed *xlfd; + struct xlfd_decomposed *xlfd_best; + char *wc; + char **fontnames; + int nnames, i, max; + + xlfd = xlfd_decompose(XGetAtomName(disp, (Atom)ret)); + if (!xlfd) + return NULL; + + /* + * Form a wildcard consisting of everything in the + * original XLFD except for the size-related fields. + */ + { + struct xlfd_decomposed xlfd_wc = *xlfd; /* structure copy */ + xlfd_wc.pixel_size = XLFD_INT_WILDCARD; + xlfd_wc.point_size = XLFD_INT_WILDCARD; + xlfd_wc.average_width = XLFD_INT_WILDCARD; + wc = xlfd_recompose(&xlfd_wc); + } + + /* + * Fetch all the font names matching that wildcard. + */ + max = 32768; + while (1) { + fontnames = XListFonts(disp, wc, max, &nnames); + if (nnames >= max) { + XFreeFontNames(fontnames); + max *= 2; + } else + break; + } + + sfree(wc); + + /* + * Iterate over those to find the one closest in size to the + * original font, in the correct direction. + */ + +#define FLIPPED_SIZE(xlfd) \ + (((xlfd)->pixel_size + (xlfd)->point_size) * \ + (increment < 0 ? -1 : +1)) + + xlfd_best = NULL; + for (i = 0; i < nnames; i++) { + struct xlfd_decomposed *xlfd2 = xlfd_decompose(fontnames[i]); + if (!xlfd2) + continue; + + if (xlfd2->pixel_size != 0 && + FLIPPED_SIZE(xlfd2) > FLIPPED_SIZE(xlfd) && + (!xlfd_best || FLIPPED_SIZE(xlfd2)u.vt->prefix, ":", bare_returned_name, + (const char *)NULL); + sfree(bare_returned_name); + } + + XFreeFontNames(fontnames); + sfree(xlfd); + sfree(xlfd_best); + } + return returned_name; +} + #endif /* NOT_X_WINDOWS */ #if GTK_CHECK_VERSION(2,0,0) @@ -1112,6 +1292,10 @@ static int pangofont_has_glyph(unifont *font, wchar_t glyph); static void pangofont_draw_text(unifont_drawctx *ctx, unifont *font, int x, int y, const wchar_t *string, int len, int wide, int bold, int cellwidth); +static void pangofont_draw_combining(unifont_drawctx *ctx, unifont *font, + int x, int y, const wchar_t *string, + int len, int wide, int bold, + int cellwidth); static unifont *pangofont_create(GtkWidget *widget, const char *name, int wide, int bold, int shadowoffset, int shadowalways); @@ -1126,6 +1310,7 @@ static char *pangofont_canonify_fontname(GtkWidget *widget, const char *name, int resolve_aliases); static char *pangofont_scale_fontname(GtkWidget *widget, const char *name, int size); +static char *pangofont_size_increment(unifont *font, int increment); struct pangofont { struct unifont u; @@ -1157,9 +1342,11 @@ static const struct unifont_vtable pangofont_vtable = { pangofont_destroy, pangofont_has_glyph, pangofont_draw_text, + pangofont_draw_combining, pangofont_enum_fonts, pangofont_canonify_fontname, pangofont_scale_fontname, + pangofont_size_increment, "client", }; @@ -1384,9 +1571,10 @@ static void pango_cairo_draw_layout(unifont_drawctx *ctx, } #endif -static void pangofont_draw_text(unifont_drawctx *ctx, unifont *font, - int x, int y, const wchar_t *string, int len, - int wide, int bold, int cellwidth) +static void pangofont_draw_internal(unifont_drawctx *ctx, unifont *font, + int x, int y, const wchar_t *string, + int len, int wide, int bold, int cellwidth, + int combining) { struct pangofont *pfont = (struct pangofont *)font; PangoLayout *layout; @@ -1463,45 +1651,56 @@ static void pangofont_draw_text(unifont_drawctx *ctx, unifont *font, * them to do that. */ - /* - * Start by extracting a single UTF-8 character from the - * string. - */ - clen = 1; - while (clen < utflen && - (unsigned char)utfptr[clen] >= 0x80 && - (unsigned char)utfptr[clen] < 0xC0) - clen++; - n = 1; - - if (is_rtl(string[0]) || - pangofont_char_width(layout, pfont, string[n-1], - utfptr, clen) != desired) { + if (combining) { /* - * If this character is a right-to-left one, or has an - * unusual width, then we must display it on its own. + * For a character with combining stuff, we just dump the + * whole lot in one go, and expect it to take up just one + * character cell. */ + clen = utflen; + n = 1; } else { /* - * Try to amalgamate a contiguous string of characters - * with the expected sensible width, for the common case - * in which we're using a monospaced font and everything - * works as expected. + * Start by extracting a single UTF-8 character from the + * string. */ - while (clen < utflen) { - int oldclen = clen; - clen++; /* skip UTF-8 introducer byte */ - while (clen < utflen && - (unsigned char)utfptr[clen] >= 0x80 && - (unsigned char)utfptr[clen] < 0xC0) - clen++; - n++; - if (pangofont_char_width(layout, pfont, - string[n-1], utfptr + oldclen, - clen - oldclen) != desired) { - clen = oldclen; - n--; - break; + clen = 1; + while (clen < utflen && + (unsigned char)utfptr[clen] >= 0x80 && + (unsigned char)utfptr[clen] < 0xC0) + clen++; + n = 1; + + if (is_rtl(string[0]) || + pangofont_char_width(layout, pfont, string[n-1], + utfptr, clen) != desired) { + /* + * If this character is a right-to-left one, or has an + * unusual width, then we must display it on its own. + */ + } else { + /* + * Try to amalgamate a contiguous string of characters + * with the expected sensible width, for the common case + * in which we're using a monospaced font and everything + * works as expected. + */ + while (clen < utflen) { + int oldclen = clen; + clen++; /* skip UTF-8 introducer byte */ + while (clen < utflen && + (unsigned char)utfptr[clen] >= 0x80 && + (unsigned char)utfptr[clen] < 0xC0) + clen++; + n++; + if (is_rtl(string[n-1]) || + pangofont_char_width(layout, pfont, + string[n-1], utfptr + oldclen, + clen - oldclen) != desired) { + clen = oldclen; + n--; + break; + } } } } @@ -1528,6 +1727,37 @@ static void pangofont_draw_text(unifont_drawctx *ctx, unifont *font, g_object_unref(layout); } +static void pangofont_draw_text(unifont_drawctx *ctx, unifont *font, + int x, int y, const wchar_t *string, int len, + int wide, int bold, int cellwidth) +{ + pangofont_draw_internal(ctx, font, x, y, string, len, wide, bold, + cellwidth, FALSE); +} + +static void pangofont_draw_combining(unifont_drawctx *ctx, unifont *font, + int x, int y, const wchar_t *string, + int len, int wide, int bold, + int cellwidth) +{ + wchar_t *tmpstring = NULL; + if (mk_wcwidth(string[0]) == 0) { + /* + * If we've been told to draw a sequence of _only_ combining + * characters, prefix a space so that they have something to + * combine with. + */ + tmpstring = snewn(len+1, wchar_t); + memcpy(tmpstring+1, string, len * sizeof(wchar_t)); + tmpstring[0] = L' '; + string = tmpstring; + len++; + } + pangofont_draw_internal(ctx, font, x, y, string, len, wide, bold, + cellwidth, TRUE); + sfree(tmpstring); +} + /* * Dummy size value to be used when converting a * PangoFontDescription of a scalable font to a string for @@ -1775,6 +2005,32 @@ static char *pangofont_scale_fontname(GtkWidget *widget, const char *name, return retname; } +static char *pangofont_size_increment(unifont *font, int increment) +{ + struct pangofont *pfont = (struct pangofont *)font; + PangoFontDescription *desc; + int size; + char *newname, *retname; + + desc = pango_font_description_copy_static(pfont->desc); + + size = pango_font_description_get_size(desc); + size += PANGO_SCALE * increment; + + if (size <= 0) { + retname = NULL; + } else { + pango_font_description_set_size(desc, size); + newname = pango_font_description_to_string(desc); + retname = dupcat(pfont->u.vt->prefix, ":", + newname, (const char *)NULL); + g_free(newname); + } + + pango_font_description_free(desc); + return retname; +} + #endif /* GTK_CHECK_VERSION(2,0,0) */ /* ---------------------------------------------------------------------- @@ -1873,6 +2129,19 @@ void unifont_draw_text(unifont_drawctx *ctx, unifont *font, font->vt->draw_text(ctx, font, x, y, string, len, wide, bold, cellwidth); } +void unifont_draw_combining(unifont_drawctx *ctx, unifont *font, + int x, int y, const wchar_t *string, int len, + int wide, int bold, int cellwidth) +{ + font->vt->draw_combining(ctx, font, x, y, string, len, wide, bold, + cellwidth); +} + +char *unifont_size_increment(unifont *font, int increment) +{ + return font->vt->size_increment(font, increment); +} + /* ---------------------------------------------------------------------- * Multiple-font wrapper. This is a type of unifont which encapsulates * up to two other unifonts, permitting missing glyphs in the main @@ -1889,7 +2158,12 @@ void unifont_draw_text(unifont_drawctx *ctx, unifont *font, static void multifont_draw_text(unifont_drawctx *ctx, unifont *font, int x, int y, const wchar_t *string, int len, int wide, int bold, int cellwidth); +static void multifont_draw_combining(unifont_drawctx *ctx, unifont *font, + int x, int y, const wchar_t *string, + int len, int wide, int bold, + int cellwidth); static void multifont_destroy(unifont *font); +static char *multifont_size_increment(unifont *font, int increment); struct multifont { struct unifont u; @@ -1903,9 +2177,11 @@ static const struct unifont_vtable multifont_vtable = { multifont_destroy, NULL, multifont_draw_text, + multifont_draw_combining, NULL, NULL, NULL, + multifont_size_increment, "client", }; @@ -1963,9 +2239,15 @@ static void multifont_destroy(unifont *font) sfree(font); } -static void multifont_draw_text(unifont_drawctx *ctx, unifont *font, int x, +typedef void (*unifont_draw_func_t)(unifont_drawctx *ctx, unifont *font, + int x, int y, const wchar_t *string, + int len, int wide, int bold, + int cellwidth); + +static void multifont_draw_main(unifont_drawctx *ctx, unifont *font, int x, int y, const wchar_t *string, int len, - int wide, int bold, int cellwidth) + int wide, int bold, int cellwidth, + int cellinc, unifont_draw_func_t draw) { struct multifont *mfont = (struct multifont *)font; unifont *f; @@ -1987,13 +2269,36 @@ static void multifont_draw_text(unifont_drawctx *ctx, unifont *font, int x, */ f = ok ? mfont->main : mfont->fallback; if (f) - unifont_draw_text(ctx, f, x, y, string, i, wide, bold, cellwidth); + draw(ctx, f, x, y, string, i, wide, bold, cellwidth); string += i; len -= i; - x += i * cellwidth; + x += i * cellinc; } } +static void multifont_draw_text(unifont_drawctx *ctx, unifont *font, int x, + int y, const wchar_t *string, int len, + int wide, int bold, int cellwidth) +{ + multifont_draw_main(ctx, font, x, y, string, len, wide, bold, + cellwidth, cellwidth, unifont_draw_text); +} + +static void multifont_draw_combining(unifont_drawctx *ctx, unifont *font, + int x, int y, const wchar_t *string, + int len, int wide, int bold, + int cellwidth) +{ + multifont_draw_main(ctx, font, x, y, string, len, wide, bold, + cellwidth, 0, unifont_draw_combining); +} + +static char *multifont_size_increment(unifont *font, int increment) +{ + struct multifont *mfont = (struct multifont *)font; + return unifont_size_increment(mfont->main, increment); +} + #if GTK_CHECK_VERSION(2,0,0) /* ---------------------------------------------------------------------- @@ -2251,7 +2556,7 @@ static void unifontsel_setup_stylelist(unifontsel_internal *fs, gtk_list_store_append(fs->style_model, &iter); gtk_list_store_set(fs->style_model, &iter, 0, currstyle, 1, minpos, 2, maxpos+1, - 3, TRUE, -1); + 3, TRUE, 4, PANGO_WEIGHT_NORMAL, -1); listindex++; } if (info) { @@ -2260,7 +2565,7 @@ static void unifontsel_setup_stylelist(unifontsel_internal *fs, gtk_list_store_append(fs->style_model, &iter); gtk_list_store_set(fs->style_model, &iter, 0, info->charset, 1, -1, 2, -1, - 3, FALSE, -1); + 3, FALSE, 4, PANGO_WEIGHT_BOLD, -1); listindex++; } currcs = info->charset; @@ -3100,19 +3405,21 @@ unifontsel *unifontsel_new(const char *wintitle) #endif /* - * The Style list box can contain insensitive elements - * (character set headings for server-side fonts), so we add - * an extra column to the list store to hold that information. + * The Style list box can contain insensitive elements (character + * set headings for server-side fonts), so we add an extra column + * to the list store to hold that information. Also, since GTK3 at + * least doesn't seem to display insensitive elements differently + * by default, we add a further column to change their style. */ - model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT, - G_TYPE_BOOLEAN); + model = gtk_list_store_new(5, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT, + G_TYPE_BOOLEAN, G_TYPE_INT); w = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(w), FALSE); gtk_label_set_mnemonic_widget(GTK_LABEL(label), w); gtk_widget_show(w); column = gtk_tree_view_column_new_with_attributes ("Style", gtk_cell_renderer_text_new(), - "text", 0, "sensitive", 3, (char *)NULL); + "text", 0, "sensitive", 3, "weight", 4, (char *)NULL); gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); gtk_tree_view_append_column(GTK_TREE_VIEW(w), column); g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(w))),