From 22f577bfa564c60c562e1e0720624585dbc69e30 Mon Sep 17 00:00:00 2001 From: hobbs Date: Sat, 14 Feb 2004 01:54:48 +0000 Subject: * win/tkWinFont.c (Tk_MeasureChars): backport fixes to bold/italic font handling. [Patch 852669] [Bug 478568] * tests/textDisp.test: added test for the font measurement problem. --- ChangeLog | 7 ++ tests/textDisp.test | 26 ++++- win/tkWinFont.c | 303 +++++++++++++++++++++++++++------------------------- 3 files changed, 191 insertions(+), 145 deletions(-) diff --git a/ChangeLog b/ChangeLog index ab85052..efd14ee 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2004-02-13 Jeff Hobbs + + * win/tkWinFont.c (Tk_MeasureChars): backport fixes to bold/italic + font handling. [Patch 852669] [Bug 478568] + * tests/textDisp.test: added test for the font measurement + problem. + 2004-02-13 Jim Ingham * tkMacOSXDialog.c (Tk_GetOpenFileObjCmd): Use CFStringRef for diff --git a/tests/textDisp.test b/tests/textDisp.test index 14b3b4a..aa0bdef 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.8 2002/11/22 23:25:20 hobbs Exp $ +# RCS: @(#) $Id: textDisp.test,v 1.8.2.1 2004/02/14 01:54:49 hobbs Exp $ package require tcltest 2.1 namespace import -force tcltest::configure @@ -2869,6 +2869,30 @@ test textDisp-29.3 {miscellaneous: lines wrap but are still too long} {fonts} { list [.t2.t xview] [winfo geom .t2.t.f] [.t2.t bbox 1.3] } {{0.536667 1} 300x50+-156+18 {}} +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..09c9b86 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.17.2.1 2004/02/14 01:54:49 hobbs Exp $ */ #include "tkWinInt.h" @@ -591,27 +591,34 @@ Tk_MeasureChars( * ignored. */ int flags, /* Various flag bits OR-ed together: * TK_PARTIAL_OK means include the last char - * which only partially fit on this line. + * which only partially fits on this line. * TK_WHOLE_WORDS means stop on a word * boundary, if possible. - * TK_AT_LEAST_ONE means return at least one - * character even if no characters fit. */ + * TK_AT_LEAST_ONE means return at least one + * character (or at least the first partial + * word in case TK_WHOLE_WORDS is also set) + * even if no characters (words) fit. */ int *lengthPtr) /* Filled with x-location just after the * terminating character. */ { 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,161 +626,165 @@ 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 - * represented by a single screen font. - * 2. Convert those chars to the encoding of that font. - * 3. Measure converted chars. - */ - - curX = 0; - end = source + numBytes; - for (p = source; 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); - (*familyPtr->getTextExtentPoint32Proc)(hdc, - Tcl_DStringValue(&runString), - Tcl_DStringLength(&runString) >> familyPtr->isWideFont, - &size); - curX += size.cx; - Tcl_DStringFree(&runString); - lastSubFontPtr = thisSubFontPtr; - source = p; + /* + * A three step process: + * 1. Find a contiguous range of characters that can all be + * represented by a single screen font. + * 2. Convert those chars to the encoding of that font. + * 3. Measure converted chars. + */ - SelectObject(hdc, lastSubFontPtr->hFont); + 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, start, + (int) (p - start), &runString); + size.cx = 0; + (*familyPtr->getTextExtentPoint32Proc)(hdc, + Tcl_DStringValue(&runString), + Tcl_DStringLength(&runString) >> familyPtr->isWideFont, + &size); + Tcl_DStringFree(&runString); + if (maxLength >= 0 && (curX+size.cx) > maxLength) { + moretomeasure = 1; + break; } - p = next; + curX += size.cx; + 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); + size.cx = 0; (*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; + Tcl_DStringFree(&runString); + if (maxLength >= 0 && (curX+size.cx) > maxLength) { + moretomeasure = 1; + } else { + curX += size.cx; + 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; - 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) { + char buf[16]; + int dstWrote; + int lastSize = 0; + + 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); + Tcl_DStringAppend(&runString,buf,dstWrote); + size.cx = 0; + (*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; - } - - next += Tcl_UtfToUniChar(next, &ch); - if ((ch < 256) && isspace(ch)) { - if (sawNonSpace) { - term = p; - termX = curX; - sawNonSpace = 0; - } - } else { - sawNonSpace = 1; - } } + Tcl_DStringFree(&runString); - /* - * P points to the first character that doesn't fit in the desired - * span. Use the flags to figure out what to return. - */ + /* + * "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 ((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. - */ + if ((p < end) + && (((flags & TK_PARTIAL_OK) && (curX != maxLength)) + || ((p == source) && (flags & TK_AT_LEAST_ONE) + && (curX == 0)))) { - 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; - } - } else if ((p >= end) || !(flags & TK_WHOLE_WORDS)) { - term = p; - termX = curX; - } + /* + * Include the first character that didn't quite fit in + * the desired span. The width returned will include the + * width of that extra character. + */ - curX = termX; - curByte = (int) (term - source); + p = next; + curX += size.cx; + } else { + curX += lastSize; + } } SelectObject(hdc, oldFont); ReleaseDC(fontPtr->hwnd, hdc); + if ((flags & TK_WHOLE_WORDS) && (p < end)) { + + /* + * Scan the string for the last word break and than repeat the + * whole procedure without the maxLength limit or any flags. + */ + + CONST char *lastWordBreak = NULL; + Tcl_UniChar ch2; + + end = p; + p = source; + ch = 0; + while (p < end) { + next = p + Tcl_UtfToUniChar(p, &ch2); + if ((ch != ' ') && (ch2 == ' ')) { + lastWordBreak = p; + } + p = next; + ch = ch2; + } + + if (lastWordBreak != NULL) { + return Tk_MeasureChars( + tkfont, source, lastWordBreak-source, -1, 0, lengthPtr); + } else { + if (flags & TK_AT_LEAST_ONE) { + p = end; + } else { + p = source; + curX = 0; + } + } + } + *lengthPtr = curX; - return curByte; + return p - source; } /* @@ -841,7 +852,7 @@ Tk_DrawChars( SIZE size; if (twdPtr->type != TWD_BITMAP) { - panic("unexpected drawable type in stipple"); + Tcl_Panic("unexpected drawable type in stipple"); } /* @@ -976,9 +987,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 +1002,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 +1015,7 @@ MultiFontTextOut( lastSubFontPtr = thisSubFontPtr; source = p; SelectObject(hdc, lastSubFontPtr->hFont); + GetTextMetrics(hdc, &tm); } p = next; } @@ -1009,7 +1023,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