From 06a8d11964d03046e1cfa4eaf0b327b0fc554821 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Thu, 13 Aug 2020 21:08:53 +0100 Subject: [PATCH] Support SGR 9 for strikethrough effect on text. This is mostly easy: it's just like drawing an underline, except that you put it at a different height in the character cell. The only question is _where_ in the character cell. Pango, and Windows GetOutlineTextMetrics, will tell you exactly where the font wants to have it. Following xterm, I fall back to 3/8 of the font's ascent (above the baseline) if either of those is unavailable. --- putty.h | 1 + terminal.c | 6 ++++++ unix/gtkfont.c | 5 +++++ unix/gtkfont.h | 2 +- unix/gtkwin.c | 8 ++++++++ windows/window.c | 45 +++++++++++++++++++++++++++++++-------------- 6 files changed, 52 insertions(+), 15 deletions(-) diff --git a/putty.h b/putty.h index 64b882b8..46c4e54e 100644 --- a/putty.h +++ b/putty.h @@ -106,6 +106,7 @@ #define ATTR_BGMASK 0x003FE00U #define ATTR_COLOURS 0x003FFFFU #define ATTR_DIM 0x1000000U +#define ATTR_STRIKE 0x2000000U #define ATTR_FGSHIFT 0 #define ATTR_BGSHIFT 9 diff --git a/terminal.c b/terminal.c index bdf10ffd..4313a1b8 100644 --- a/terminal.c +++ b/terminal.c @@ -4132,6 +4132,9 @@ static void term_out(Terminal *term) case 7: /* enable reverse video */ term->curr_attr |= ATTR_REVERSE; break; + case 9: /* enable strikethrough */ + term->curr_attr |= ATTR_STRIKE; + break; case 10: /* SCO acs off */ compatibility(SCOANSI); if (term->no_remote_charset) break; @@ -4160,6 +4163,9 @@ static void term_out(Terminal *term) compatibility2(OTHER, VT220); term->curr_attr &= ~ATTR_REVERSE; break; + case 29: /* disable strikethrough */ + term->curr_attr &= ~ATTR_STRIKE; + break; case 30: case 31: case 32: diff --git a/unix/gtkfont.c b/unix/gtkfont.c index 2bce8946..42aadd2d 100644 --- a/unix/gtkfont.c +++ b/unix/gtkfont.c @@ -511,6 +511,7 @@ static unifont *x11font_create(GtkWidget *widget, const char *name, xfont->u.height = xfont->u.ascent + xfont->u.descent; xfont->u.public_charset = pubcs; xfont->u.want_fallback = true; + xfont->u.strikethrough_y = xfont->u.ascent - (xfont->u.ascent * 3 / 8); #ifdef DRAW_TEXT_GDK xfont->u.preferred_drawtype = DRAWTYPE_GDK; #elif defined DRAW_TEXT_CAIRO @@ -1463,6 +1464,9 @@ static unifont *pangofont_create_internal(GtkWidget *widget, pfont->u.descent = PANGO_PIXELS_CEIL(pango_font_metrics_get_descent(metrics)); pfont->u.height = pfont->u.ascent + pfont->u.descent; + pfont->u.strikethrough_y = + PANGO_PIXELS(pango_font_metrics_get_ascent(metrics) - + pango_font_metrics_get_strikethrough_position(metrics)); pfont->u.want_fallback = false; #ifdef DRAW_TEXT_CAIRO pfont->u.preferred_drawtype = DRAWTYPE_CAIRO; @@ -2242,6 +2246,7 @@ unifont *multifont_create(GtkWidget *widget, const char *name, mfont->u.ascent = font->ascent; mfont->u.descent = font->descent; mfont->u.height = font->height; + mfont->u.strikethrough_y = font->strikethrough_y; mfont->u.public_charset = font->public_charset; mfont->u.want_fallback = false; /* shouldn't be needed, but just in case */ mfont->u.preferred_drawtype = font->preferred_drawtype; diff --git a/unix/gtkfont.h b/unix/gtkfont.h index 032460e9..e43a748d 100644 --- a/unix/gtkfont.h +++ b/unix/gtkfont.h @@ -67,7 +67,7 @@ typedef struct unifont { /* * Font dimensions needed by clients. */ - int width, height, ascent, descent; + int width, height, ascent, descent, strikethrough_y; /* * Indicates whether this font is capable of handling all glyphs diff --git a/unix/gtkwin.c b/unix/gtkwin.c index 865dffa0..51de73e3 100644 --- a/unix/gtkwin.c +++ b/unix/gtkwin.c @@ -3887,6 +3887,14 @@ static void do_text_internal( y*inst->font_height + uheight + inst->window_border); } + if (attr & ATTR_STRIKE) { + int sheight = inst->fonts[fontid]->strikethrough_y; + draw_line(inst, x*inst->font_width+inst->window_border, + y*inst->font_height + sheight + inst->window_border, + (x+len)*widefactor*inst->font_width-1+inst->window_border, + y*inst->font_height + sheight + inst->window_border); + } + if ((lattr & LATTR_MODE) != LATTR_NORM) { draw_stretch_after(inst, x*inst->font_width+inst->window_border, diff --git a/windows/window.c b/windows/window.c index a90f713e..3425a11f 100644 --- a/windows/window.c +++ b/windows/window.c @@ -187,7 +187,7 @@ static bool bold_colours; static enum { UND_LINE, UND_FONT } und_mode; -static int descent; +static int descent, font_strikethrough_y; #define NCFGCOLOURS 22 #define NEXTCOLOURS 240 @@ -1490,6 +1490,7 @@ static int get_font_width(HDC hdc, const TEXTMETRIC *tm) static void init_fonts(int pick_width, int pick_height) { TEXTMETRIC tm; + OUTLINETEXTMETRIC otm; CPINFO cpinfo; FontSpec *font; int fontsize[3]; @@ -1539,6 +1540,10 @@ static void init_fonts(int pick_width, int pick_height) SelectObject(hdc, fonts[FONT_NORMAL]); GetTextMetrics(hdc, &tm); + if (GetOutlineTextMetrics(hdc, sizeof(otm), &otm)) + font_strikethrough_y = tm.tmAscent - otm.otmsStrikeoutPosition; + else + font_strikethrough_y = tm.tmAscent - (tm.tmAscent * 3 / 8); GetObject(fonts[FONT_NORMAL], sizeof(LOGFONT), &lfont); @@ -3462,6 +3467,25 @@ static void sys_cursor_update(void) ImmReleaseContext(wgs.term_hwnd, hIMC); } +static void draw_horizontal_line_on_text(int y, int lattr, RECT line_box, + COLORREF colour) +{ + if (lattr == LATTR_TOP || lattr == LATTR_BOT) { + y *= 2; + if (lattr == LATTR_BOT) + y -= font_height; + } + + if (!(0 <= y && y < font_height)) + return; + + HPEN oldpen = SelectObject(wintw_hdc, CreatePen(PS_SOLID, 0, colour)); + MoveToEx(wintw_hdc, line_box.left, line_box.top + y, NULL); + LineTo(wintw_hdc, line_box.right, line_box.top + y); + oldpen = SelectObject(wintw_hdc, oldpen); + DeleteObject(oldpen); +} + /* * Draw a line of text in the window, at given character * coordinates, in given attributes. @@ -3838,20 +3862,13 @@ static void do_text_internal( SetBkMode(wintw_hdc, TRANSPARENT); opaque = false; } - if (lattr != LATTR_TOP && (force_manual_underline || - (und_mode == UND_LINE - && (attr & ATTR_UNDER)))) { - HPEN oldpen; - int dec = descent; - if (lattr == LATTR_BOT) - dec = dec * 2 - font_height; - oldpen = SelectObject(wintw_hdc, CreatePen(PS_SOLID, 0, fg)); - MoveToEx(wintw_hdc, line_box.left, line_box.top + dec, NULL); - LineTo(wintw_hdc, line_box.right, line_box.top + dec); - oldpen = SelectObject(wintw_hdc, oldpen); - DeleteObject(oldpen); - } + if (lattr != LATTR_TOP && (force_manual_underline || + (und_mode == UND_LINE && (attr & ATTR_UNDER)))) + draw_horizontal_line_on_text(descent, lattr, line_box, fg); + + if (attr & ATTR_STRIKE) + draw_horizontal_line_on_text(font_strikethrough_y, lattr, line_box, fg); } /*