From 50c35588c1284033589929bdf168163d5f9f7a8d Mon Sep 17 00:00:00 2001 From: vincentdarley Date: Thu, 4 Dec 2003 12:09:55 +0000 Subject: font measurement fix --- ChangeLog | 7 ++ tests/textDisp.test | 25 +++++- win/tkWinFont.c | 251 +++++++++++++++++++++++++++------------------------- 3 files changed, 161 insertions(+), 122 deletions(-) diff --git a/ChangeLog b/ChangeLog index 0bcd3da..121adac 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2003-12-04 Vince Darley + + * win/tkWinFont.c: applied [Patch 852669] which fixes [Bug 478568] + with certain bold or italic fonts on Windows. + * tests/textDisp.test: added test for the font measurement + problem. + 2003-12-02 Jeff Hobbs * generic/tkMenu.c (MenuVarProc): prevent this from triggering diff --git a/tests/textDisp.test b/tests/textDisp.test index 31abdd1..7f1a54a 100644 --- a/tests/textDisp.test +++ b/tests/textDisp.test @@ -6,7 +6,7 @@ # Copyright (c) 1998-1999 by Scriptics Corporation. # All rights reserved. # -# RCS: @(#) $Id: textDisp.test,v 1.22 2003/11/21 18:51:18 vincentdarley Exp $ +# RCS: @(#) $Id: textDisp.test,v 1.23 2003/12/04 12:10:07 vincentdarley Exp $ package require tcltest 2.1 eval tcltest::configure $argv @@ -3579,6 +3579,29 @@ test textDisp-33.4 {one line longer than fits in the widget} { set result } {ok} destroy .tt +test textDisp-33.5 {bold or italic fonts} {winOnly} { + destroy .tt + pack [text .tt -wrap char -font {{MS Sans Serif} 15}] + font create no -family [lindex [.tt cget -font] 0] -size 24 + font create bi -family [lindex [.tt cget -font] 0] -size 24 + font configure bi -weight bold -slant italic + .tt tag configure bi -font bi + .tt tag configure no -font no + .tt insert end abcd no efgh bi ijkl\n no + update + set bb {} + for {set i 0} {$i < 12} {incr i 4} { + lappend bb [lindex [.tt bbox 1.$i] 0] + } + foreach {a b c} $bb {} + unset bb + if {($b - $a) * 1.5 < ($c - $b)} { + set result "italic font has much too much space" + } else { + set result "italic font measurement ok" + } +} {italic font measurement ok} +destroy .tt deleteWindows option clear diff --git a/win/tkWinFont.c b/win/tkWinFont.c index acdd28f..61633d6 100644 --- a/win/tkWinFont.c +++ b/win/tkWinFont.c @@ -11,7 +11,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkWinFont.c,v 1.17 2003/02/26 02:47:05 hobbs Exp $ + * RCS: @(#) $Id: tkWinFont.c,v 1.18 2003/12/04 12:09:55 vincentdarley Exp $ */ #include "tkWinInt.h" @@ -602,16 +602,21 @@ Tk_MeasureChars( HDC hdc; HFONT oldFont; WinFont *fontPtr; - int curX, curByte; + int curX; + Tcl_UniChar ch; + SIZE size; + int moretomeasure; + FontFamily *familyPtr; + Tcl_DString runString; + SubFont *thisSubFontPtr; SubFont *lastSubFontPtr; + CONST char *p, *end, *next, *start; - /* - * According to Microsoft tech support, Windows does not use kerning - * or fractional character widths when displaying text on the screen. - * So that means we can safely measure individual characters or spans - * of characters and add up the widths w/o any "off-by-one-pixel" - * errors. - */ + + if (numBytes == 0) { + *lengthPtr = 0; + return 0; + } fontPtr = (WinFont *) tkfont; @@ -619,17 +624,6 @@ Tk_MeasureChars( lastSubFontPtr = &fontPtr->subFontArray[0]; oldFont = SelectObject(hdc, lastSubFontPtr->hFont); - if (numBytes == 0) { - curX = 0; - curByte = 0; - } else if (maxLength < 0) { - Tcl_UniChar ch; - SIZE size; - FontFamily *familyPtr; - Tcl_DString runString; - SubFont *thisSubFontPtr; - CONST char *p, *end, *next; - /* * A three step process: * 1. Find a contiguous range of characters that can all be @@ -638,142 +632,153 @@ Tk_MeasureChars( * 3. Measure converted chars. */ - curX = 0; - end = source + numBytes; - for (p = source; p < end; ) { + moretomeasure = 0; + curX = 0; + start = source; + end = start + numBytes; + for (p = start; p < end; ) { next = p + Tcl_UtfToUniChar(p, &ch); thisSubFontPtr = FindSubFontForChar(fontPtr, ch); if (thisSubFontPtr != lastSubFontPtr) { familyPtr = lastSubFontPtr->familyPtr; - Tcl_UtfToExternalDString(familyPtr->encoding, source, - (int) (p - source), &runString); + Tcl_UtfToExternalDString(familyPtr->encoding, start, + (int) (p - start), &runString); (*familyPtr->getTextExtentPoint32Proc)(hdc, Tcl_DStringValue(&runString), Tcl_DStringLength(&runString) >> familyPtr->isWideFont, &size); - curX += size.cx; - Tcl_DStringFree(&runString); - lastSubFontPtr = thisSubFontPtr; - source = p; - - SelectObject(hdc, lastSubFontPtr->hFont); + if (maxLength >= 0 && (curX+size.cx) > maxLength) { + moretomeasure = 1; + break; } - p = next; - } + curX += size.cx; + Tcl_DStringFree(&runString); + lastSubFontPtr = thisSubFontPtr; + start = p; + + SelectObject(hdc, lastSubFontPtr->hFont); + } + p = next; + } + + if (!moretomeasure) { + /* + * We get here if the previous loop was just finished + * normally, without a break. Just measure the last run and + * that's it. + */ + familyPtr = lastSubFontPtr->familyPtr; - Tcl_UtfToExternalDString(familyPtr->encoding, source, - (int) (p - source), &runString); + Tcl_UtfToExternalDString(familyPtr->encoding, start, + (int) (p - start), &runString); (*familyPtr->getTextExtentPoint32Proc)(hdc, Tcl_DStringValue(&runString), Tcl_DStringLength(&runString) >> familyPtr->isWideFont, &size); - curX += size.cx; - Tcl_DStringFree(&runString); - curByte = numBytes; - } else { - Tcl_UniChar ch; - SIZE size; - char buf[16]; - FontFamily *familyPtr; - SubFont *thisSubFontPtr; - CONST char *term, *end, *p, *next; - int newX, termX, sawNonSpace, dstWrote; + if (maxLength >= 0 && (curX+size.cx) > maxLength) { + moretomeasure = 1; + } else { + curX += size.cx; + Tcl_DStringFree(&runString); + p = end; + } + } + if (moretomeasure) { /* - * How many chars will fit in the space allotted? - * This first version may be inefficient because it measures - * every character individually. There is a function call that - * can measure multiple characters at once and return the - * offset of each of them, but it only works on NT, even though - * the documentation claims it works for 95. - * TODO: verify that GetTextExtentExPoint is still broken in '95, and - * possibly use it for NT anyway since it should be much faster and - * more accurate. + * We get here if the measurement of the last run was over the + * maxLength limit. We need to restart this run and do it + * char by char, but always in context with the previous text + * to account for kerning (especially italics). */ - next = source + Tcl_UtfToUniChar(source, &ch); - newX = curX = termX = 0; - - term = source; - end = source + numBytes; - - sawNonSpace = (ch > 255) || !isspace(ch); - for (p = source; ; ) { - if (ch < BASE_CHARS) { - newX += fontPtr->widths[ch]; - } else { - thisSubFontPtr = FindSubFontForChar(fontPtr, ch); - if (thisSubFontPtr != lastSubFontPtr) { - SelectObject(hdc, thisSubFontPtr->hFont); - lastSubFontPtr = thisSubFontPtr; - } - familyPtr = lastSubFontPtr->familyPtr; + char buf[16]; + int dstWrote; + int lastSize; + + familyPtr = lastSubFontPtr->familyPtr; + Tcl_DStringInit(&runString); + for (p = start; p < end; ) { + next = p + Tcl_UtfToUniChar(p, &ch); Tcl_UtfToExternal(NULL, familyPtr->encoding, p, (int) (next - p), 0, NULL, buf, sizeof(buf), NULL, &dstWrote, NULL); - (*familyPtr->getTextExtentPoint32Proc)(hdc, buf, - dstWrote >> familyPtr->isWideFont, &size); - newX += size.cx; - } - if (newX > maxLength) { + Tcl_DStringAppend(&runString,buf,dstWrote); + (*familyPtr->getTextExtentPoint32Proc)(hdc, + Tcl_DStringValue(&runString), + Tcl_DStringLength(&runString) >> familyPtr->isWideFont, + &size); + if ((curX+size.cx) > maxLength) { break; } - curX = newX; + lastSize = size.cx; p = next; - if (p >= end) { - term = end; - termX = curX; - break; - } + } + Tcl_DStringFree(&runString); - next += Tcl_UtfToUniChar(next, &ch); - if ((ch < 256) && isspace(ch)) { - if (sawNonSpace) { - term = p; - termX = curX; - sawNonSpace = 0; - } - } else { - sawNonSpace = 1; - } + /* + * "p" points to the first character that doesn't fit in the + * desired span. Look at the flags to figure out whether to + * include this next character. + */ + + if ((p < end) + && (((flags & TK_PARTIAL_OK) && curX != maxLength) + || ((flags & TK_AT_LEAST_ONE) && (curX == 0)))) { + + /* + * Include the first character that didn't quite fit in + * the desired span. The width returned will include the + * width of that extra character. + */ + + p = next; + curX += size.cx; + } else { + curX += lastSize; } + } + + SelectObject(hdc, oldFont); + ReleaseDC(fontPtr->hwnd, hdc); + + if ((flags & TK_WHOLE_WORDS) && (p < end) + && !((ch < 128) && isspace(ch))) { /* - * P points to the first character that doesn't fit in the desired - * span. Use the flags to figure out what to return. + * This is never used by Tk itself, but others may want it. + * Scan the string for the last word break and repeat the + * whole procedure without the maxLength limit or any flags. */ - if ((flags & TK_PARTIAL_OK) && (p < end) && (curX < maxLength)) { - /* - * Include the first character that didn't quite fit in the desired - * span. The width returned will include the width of that extra - * character. - */ + CONST char *lastWordBreak = NULL; + CONST char *nextafter = NULL; + Tcl_UniChar ch2; - curX = newX; - p += Tcl_UtfToUniChar(p, &ch); - } - if ((flags & TK_AT_LEAST_ONE) && (term == source) && (p < end)) { - term = p; - termX = curX; - if (term == source) { - term += Tcl_UtfToUniChar(term, &ch); - termX = newX; + end = p; + start = source; + + next = p + Tcl_UtfToUniChar(p, &ch); + for (p = start; next < end; ) { + nextafter = next + Tcl_UtfToUniChar(next, &ch2); + if (((ch >= 128) || !isspace(ch)) + && ((ch2 < 128) && isspace(ch2))) { + lastWordBreak = next; } - } else if ((p >= end) || !(flags & TK_WHOLE_WORDS)) { - term = p; - termX = curX; + p = next; + next = nextafter; } - curX = termX; - curByte = (int) (term - source); + if (NULL != lastWordBreak) { + return Tk_MeasureChars( + tkfont, source, lastWordBreak-source, -1, 0, lengthPtr ); + } else { + p = end; + } } - SelectObject(hdc, oldFont); - ReleaseDC(fontPtr->hwnd, hdc); - *lengthPtr = curX; - return curByte; + return p - source; } /* @@ -976,9 +981,11 @@ MultiFontTextOut( Tcl_DString runString; CONST char *p, *end, *next; SubFont *lastSubFontPtr, *thisSubFontPtr; + TEXTMETRIC tm; lastSubFontPtr = &fontPtr->subFontArray[0]; oldFont = SelectObject(hdc, lastSubFontPtr->hFont); + GetTextMetrics(hdc, &tm); end = source + numBytes; for (p = source; p < end; ) { @@ -989,7 +996,7 @@ MultiFontTextOut( familyPtr = lastSubFontPtr->familyPtr; Tcl_UtfToExternalDString(familyPtr->encoding, source, (int) (p - source), &runString); - (*familyPtr->textOutProc)(hdc, x, y, + (*familyPtr->textOutProc)(hdc, x-(tm.tmOverhang/2), y, Tcl_DStringValue(&runString), Tcl_DStringLength(&runString) >> familyPtr->isWideFont); (*familyPtr->getTextExtentPoint32Proc)(hdc, @@ -1002,6 +1009,7 @@ MultiFontTextOut( lastSubFontPtr = thisSubFontPtr; source = p; SelectObject(hdc, lastSubFontPtr->hFont); + GetTextMetrics(hdc, &tm); } p = next; } @@ -1009,7 +1017,8 @@ MultiFontTextOut( familyPtr = lastSubFontPtr->familyPtr; Tcl_UtfToExternalDString(familyPtr->encoding, source, (int) (p - source), &runString); - (*familyPtr->textOutProc)(hdc, x, y, Tcl_DStringValue(&runString), + (*familyPtr->textOutProc)(hdc, x-(tm.tmOverhang/2), y, + Tcl_DStringValue(&runString), Tcl_DStringLength(&runString) >> familyPtr->isWideFont); Tcl_DStringFree(&runString); } -- cgit v0.12