From 49df46a2bf9d2a9e8254aab31eeafa5f957d4dcf Mon Sep 17 00:00:00 2001 From: dkf Date: Sat, 10 Oct 2009 17:42:50 +0000 Subject: [Bug 1961455]: Draw underlines and overstrikes when using Xft. --- ChangeLog | 9 ++++ generic/tkFont.c | 11 +++-- unix/tkUnixRFont.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 135 insertions(+), 10 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4a95f15..2814c4e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2009-10-10 Donal K. Fellows + + * unix/tkUnixRFont.c (InitFont, TkpGetFontFromAttributes) + (Tk_DrawChars, TkpDrawAngledChars): [Bug 1961455]: Draw underlines and + overstrikes when using Xft for font rendering. + + * generic/tkFont.c (TkDrawAngledTextLayout): Optimize the zero-angle + case better. + 2009-10-08 Donal K. Fellows * library/iconlist.tcl (Create): [Patch 2870648]: Corrected cursor diff --git a/generic/tkFont.c b/generic/tkFont.c index bd65379..47852ca 100644 --- a/generic/tkFont.c +++ b/generic/tkFont.c @@ -10,7 +10,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkFont.c,v 1.58 2009/09/10 12:16:37 dkf Exp $ + * RCS: @(#) $Id: tkFont.c,v 1.59 2009/10/10 17:42:50 dkf Exp $ */ #include "tkInt.h" @@ -2376,8 +2376,13 @@ TkDrawAngledTextLayout( lastByte = Tcl_UtfAtIndex(chunkPtr->start, numDisplayChars); dx = cosA * (chunkPtr->x + drawX) + sinA * (chunkPtr->y); dy = -sinA * (chunkPtr->x + drawX) + cosA * (chunkPtr->y); - TkpDrawAngledChars(display, drawable, gc, layoutPtr->tkfont, - firstByte, lastByte - firstByte, x+dx, y+dy, angle); + if (angle == 0.0) { + Tk_DrawChars(display, drawable, gc, layoutPtr->tkfont, + firstByte, lastByte - firstByte, x + dx, y + dy); + } else { + TkpDrawAngledChars(display, drawable, gc, layoutPtr->tkfont, + firstByte, lastByte - firstByte, x+dx, y+dy, angle); + } } firstChar -= chunkPtr->numChars; lastChar -= chunkPtr->numChars; diff --git a/unix/tkUnixRFont.c b/unix/tkUnixRFont.c index e1c7e75..5e62ffa 100644 --- a/unix/tkUnixRFont.c +++ b/unix/tkUnixRFont.c @@ -8,7 +8,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkUnixRFont.c,v 1.29 2009/09/09 22:47:36 dkf Exp $ + * RCS: @(#) $Id: tkUnixRFont.c,v 1.30 2009/10/10 17:42:50 dkf Exp $ */ #include "tkUnixInt.h" @@ -16,6 +16,8 @@ #include #include +#define ROUND16(x) ((short)((x) + 0.5)) + typedef struct { XftFont *ftFont; XftFont *ft0Font; @@ -37,7 +39,6 @@ typedef struct { XftDraw *ftDraw; XftColor color; } UnixFtFont; - /* * Package initialization: @@ -229,7 +230,7 @@ InitFont( FcCharSet *charset; FcResult result; XftFont *ftFont; - int i; + int i, iWidth; if (!fontPtr) { fontPtr = (UnixFtFont *) ckalloc(sizeof(UnixFtFont)); @@ -288,6 +289,43 @@ InitFont( GetTkFontAttributes(ftFont, &fontPtr->font.fa); GetTkFontMetrics(ftFont, &fontPtr->font.fm); + /* + * Fontconfig can't report any information about the position or thickness + * of underlines or overstrikes. Thus, we use some defaults that are + * hacked around from backup defaults in tkUnixFont.c, which are in turn + * based on recommendations in the X manual. The comments from that file + * leading to these computations were: + * + * If the XA_UNDERLINE_POSITION property does not exist, the X manual + * recommends using half the descent. + * + * If the XA_UNDERLINE_THICKNESS property does not exist, the X + * manual recommends using the width of the stem on a capital letter. + * I don't know of a way to get the stem width of a letter, so guess + * and use 1/3 the width of a capital I. + * + * Note that nothing corresponding to *either* property is reported by + * Fontconfig at all. [Bug 1961455] + */ + + { + TkFont *fPtr = &fontPtr->font; + + fPtr->underlinePos = fPtr->fm.descent / 2; + Tk_MeasureChars((Tk_Font) fPtr, "I", 1, -1, 0, &iWidth); + fPtr->underlineHeight = iWidth / 3; + if (fPtr->underlineHeight == 0) { + fPtr->underlineHeight = 1; + } + if (fPtr->underlineHeight + fPtr->underlinePos > fPtr->fm.descent) { + fPtr->underlineHeight = fPtr->fm.descent - fPtr->underlinePos; + if (fPtr->underlineHeight == 0) { + fPtr->underlinePos--; + fPtr->underlineHeight = 1; + } + } + } + return fontPtr; } @@ -434,6 +472,9 @@ TkpGetFontFromAttributes( FcPatternDestroy(pattern); return NULL; } + + fontPtr->font.fa.underline = faPtr->underline; + fontPtr->font.fa.overstrike = faPtr->overstrike; return &fontPtr->font; } @@ -705,7 +746,7 @@ Tk_DrawChars( UnixFtFont *fontPtr = (UnixFtFont *) tkfont; XGCValues values; XColor xcolor; - int clen, nspec; + int clen, nspec, xStart = x; XftGlyphFontSpec specs[NUM_SPEC]; XGlyphInfo metrics; @@ -745,7 +786,7 @@ Tk_DrawChars( * This should not happen, but it can. */ - return; + goto doUnderlineStrikeout; } source += clen; numBytes -= clen; @@ -771,6 +812,19 @@ Tk_DrawChars( if (nspec) { XftDrawGlyphFontSpec(fontPtr->ftDraw, &fontPtr->color, specs, nspec); } + + doUnderlineStrikeout: + if (fontPtr->font.fa.underline != 0) { + XFillRectangle(display, drawable, gc, xStart, + y + fontPtr->font.underlinePos, (unsigned) (x - xStart), + (unsigned) fontPtr->font.underlineHeight); + } + if (fontPtr->font.fa.overstrike != 0) { + y -= fontPtr->font.fm.descent + (fontPtr->font.fm.ascent) / 10; + XFillRectangle(display, drawable, gc, xStart, y, + (unsigned) (x - xStart), + (unsigned) fontPtr->font.underlineHeight); + } } void @@ -797,6 +851,7 @@ TkpDrawAngledChars( UnixFtFont *fontPtr = (UnixFtFont *) tkfont; XGCValues values; XColor xcolor; + int xStart = x, yStart = y; #ifdef XFT_HAS_FIXED_ROTATED_PLACEMENT int clen, nglyph; FT_UInt glyphs[NUM_SPEC]; @@ -846,7 +901,7 @@ TkpDrawAngledChars( * This should not happen, but it can. */ - return; + goto doUnderlineStrikeout; } source += clen; numBytes -= clen; @@ -928,7 +983,7 @@ TkpDrawAngledChars( * This should not happen, but it can. */ - return; + goto doUnderlineStrikeout; } source += clen; numBytes -= clen; @@ -956,6 +1011,62 @@ TkpDrawAngledChars( XftDrawGlyphFontSpec(fontPtr->ftDraw, &fontPtr->color, specs, nspec); } #endif /* XFT_HAS_FIXED_ROTATED_PLACEMENT */ + + doUnderlineStrikeout: + if (fontPtr->font.fa.underline || fontPtr->font.fa.overstrike) { + XPoint points[5]; + double width = (x - xStart) * cosA + (yStart - y) * sinA; + double barHeight = fontPtr->font.underlineHeight; + double dy = fontPtr->font.underlinePos; + + if (fontPtr->font.fa.underline != 0) { + if (fontPtr->font.underlineHeight == 1) { + dy++; + } + points[0].x = xStart + ROUND16(dy*sinA); + points[0].y = yStart + ROUND16(dy*cosA); + points[1].x = xStart + ROUND16(dy*sinA + width*cosA); + points[1].y = yStart + ROUND16(dy*cosA - width*sinA); + if (fontPtr->font.underlineHeight == 1) { + XDrawLines(display, drawable, gc, points, 2, CoordModeOrigin); + } else { + points[2].x = xStart + ROUND16(dy*sinA + width*cosA + + barHeight*sinA); + points[2].y = yStart + ROUND16(dy*cosA - width*sinA + + barHeight*cosA); + points[3].x = xStart + ROUND16(dy*sinA + barHeight*sinA); + points[3].y = yStart + ROUND16(dy*cosA + barHeight*cosA); + points[4].x = points[0].x; + points[4].y = points[0].y; + XFillPolygon(display, drawable, gc, points, 5, Complex, + CoordModeOrigin); + XDrawLines(display, drawable, gc, points, 5, CoordModeOrigin); + } + } + if (fontPtr->font.fa.overstrike != 0) { + dy = -fontPtr->font.fm.descent + - (fontPtr->font.fm.ascent) / 10; + points[0].x = xStart + ROUND16(dy*sinA); + points[0].y = yStart + ROUND16(dy*cosA); + points[1].x = xStart + ROUND16(dy*sinA + width*cosA); + points[1].y = yStart + ROUND16(dy*cosA - width*sinA); + if (fontPtr->font.underlineHeight == 1) { + XDrawLines(display, drawable, gc, points, 2, CoordModeOrigin); + } else { + points[2].x = xStart + ROUND16(dy*sinA + width*cosA + + barHeight*sinA); + points[2].y = yStart + ROUND16(dy*cosA - width*sinA + + barHeight*cosA); + points[3].x = xStart + ROUND16(dy*sinA + barHeight*sinA); + points[3].y = yStart + ROUND16(dy*cosA + barHeight*cosA); + points[4].x = points[0].x; + points[4].y = points[0].y; + XFillPolygon(display, drawable, gc, points, 5, Complex, + CoordModeOrigin); + XDrawLines(display, drawable, gc, points, 5, CoordModeOrigin); + } + } + } } /* -- cgit v0.12