GetTextMetricsA doesn't return proper First/Last Char for Wingdings Font

jonathan jonathan at corvu.com.au
Mon Jul 31 19:17:21 CDT 2006


Hi,

We found a problem in Wine's implementation of GetTextMetricsA. A patch 
is provided as a solution.

In Wine GetTextMetricsA returns 255 & 255 as First/Last char for 
Wingdings font. Same function under Windows returns 30 & 255 instead.

Further investigation found that in Wine implementation, GetTextMetricsW 
takes First/Last characters index from OS2 table from TrueTypeFont file. 
However FONT_TextMetricWToA will make sure these 2 numbers in 
TEXTMETRICSA are no greater than 255. According to TrueTypeFont 
specification, Symbol font runs character in Unicode range 0x0F000 ~ 
0x0F0FF (inclusive). In Wingdings font case, OS2 table returns 0x0F020 
and 0x0F0FF as first/last char index. So after calling 
FONT_TextMetricWToA, both First/Last chars in TEXTMETRICSA are set to 255.

The test package discovered following Window behaviors:
For Symbol fonts:
    1. GetTextMetricsW always return 0x0 and 0x0F0FF as first/last 
characters, which is different from OS2 values
    2. GetTextMetricsA mask 0x0F000 off the first/last char from OS2 
table. In Wingdings case, 0x0F020 & 0x0F0FF become 0x020 and 0x0FF.
    3. If last char from OS2 table is greater than 0x0F0FF, 
GetTextMetricsA set last char to 0x0FF
    4. GetTextMetricsW tmBreakChar equals OS2 table First Char & 0x0FF
For non Symbol fonts:
    1. GetTextMetricsW uses First/Last char from OS2 table in 
TrueTypeFont file.
    2. GetTextMetricsA First/Last char has 255 as upper limit
    3. GetTextMetricsW use First char from OS2 table as tmBreakChar
General rules:
    1. TEXTMETRICSA.tmBreakChar equals to TEXTMETRICSW.tmBreakChar
    2. A version tmFirstChar is always A version tmBreakChar minus 2. If 
tmBreakChar is less than 2, tmBreakChar in both A & W version are 
automatically increased by 2.
    3. Generally tmDefaultChar is 1 less than tmBreakChar in both A & W 
version.
    4. If W version tmDefaultChar is greater than 255 then A version 
tmDefaultChar equals to tmBreakChar

We also have an interesting finding:
Although Symbol font is determined by a symbol bit flag in OS2 table 
ulCodePageRange1 combined with a symbol cmap subtable in TTF file, 
Window seems not respecting this rule. Practically, Windows treat any 
fonts with symbol cmap subtable as symbol font (sample TT fonts are MT 
Extra, CityBlueprint, CommercialPi BT, CountryBlueprint, EuroRoman, 
PanRoman, Romantic, SansSerif, SuperFrench, Technic, UniversalMath1 BT, 
etc). In the test package, statement "P20" proves this point. So in the 
patch, we specifically check whether symbol subtable is available or not.

Attached test package can be built under Windows and Linux (define linux 
when compiling with wine). tm_window.txt and tm_linux.txt will be 
generated to compare tmFirstChar, tmLastChar, tmBreakChar, 
tmDefaultChar, and tmCharSet.

If you have any further questions, please feel free to contact me via 
this email. Thanks!


Best Regards
Jonathan


-- 
________________________________________
Jonathan Jie Zhu

CorVu Corporation (www.corvu.com)
Sydney Office: +61 2 9495 5400

If you wish to receive no further email from CorVu: <http://emb.corvu.com/>

-------------- next part --------------
A non-text attachment was scrubbed...
Name: ttfontfile.zip
Type: application/x-zip-compressed
Size: 14055 bytes
Desc: not available
Url : http://www.winehq.org/pipermail/wine-patches/attachments/20060801/43af170d/ttfontfile-0001.bin
-------------- next part --------------
diff --git a/dlls/gdi/font.c b/dlls/gdi/font.c
index 33a03f6..9fc01df 100644
--- a/dlls/gdi/font.c
+++ b/dlls/gdi/font.c
@@ -289,10 +289,18 @@ static void FONT_TextMetricWToA(const TE
     ptmA->tmOverhang = ptmW->tmOverhang;
     ptmA->tmDigitizedAspectX = ptmW->tmDigitizedAspectX;
     ptmA->tmDigitizedAspectY = ptmW->tmDigitizedAspectY;
-    ptmA->tmFirstChar = ptmW->tmFirstChar > 255 ? 255 : ptmW->tmFirstChar;
-    ptmA->tmLastChar = ptmW->tmLastChar > 255 ? 255 : ptmW->tmLastChar;
-    ptmA->tmDefaultChar = ptmW->tmDefaultChar > 255 ? 255 : ptmW->tmDefaultChar;
-    ptmA->tmBreakChar = ptmW->tmBreakChar > 255 ? 255 : ptmW->tmBreakChar;
+    ptmA->tmBreakChar = ptmW->tmBreakChar;
+    ptmA->tmFirstChar = ptmA->tmBreakChar - 2;
+    if (ptmW->tmCharSet == SYMBOL_CHARSET)
+    {
+	    ptmA->tmLastChar = ptmW->tmLastChar > 0x0F0FF ? 0x0FF : ptmW->tmLastChar & 0x0FF;
+    }
+    else
+    {
+	    ptmA->tmLastChar = ptmW->tmLastChar > 255 ? 255 : ptmW->tmLastChar;
+    }
+    ptmA->tmDefaultChar = ptmW->tmDefaultChar > 255 ? ptmA->tmBreakChar : ptmW->tmDefaultChar;
+
     ptmA->tmItalic = ptmW->tmItalic;
     ptmA->tmUnderlined = ptmW->tmUnderlined;
     ptmA->tmStruckOut = ptmW->tmStruckOut;
@@ -314,10 +322,18 @@ static void FONT_NewTextMetricExWTo16(co
     ptm16->ntmTm.tmOverhang = ptmW->ntmTm.tmOverhang;
     ptm16->ntmTm.tmDigitizedAspectX = ptmW->ntmTm.tmDigitizedAspectX;
     ptm16->ntmTm.tmDigitizedAspectY = ptmW->ntmTm.tmDigitizedAspectY;
-    ptm16->ntmTm.tmFirstChar = ptmW->ntmTm.tmFirstChar > 255 ? 255 : ptmW->ntmTm.tmFirstChar;
-    ptm16->ntmTm.tmLastChar = ptmW->ntmTm.tmLastChar > 255 ? 255 : ptmW->ntmTm.tmLastChar;
-    ptm16->ntmTm.tmDefaultChar = ptmW->ntmTm.tmDefaultChar > 255 ? 255 : ptmW->ntmTm.tmDefaultChar;
-    ptm16->ntmTm.tmBreakChar = ptmW->ntmTm.tmBreakChar > 255 ? 255 : ptmW->ntmTm.tmBreakChar;
+    ptm16->ntmTm.tmBreakChar = ptmW->ntmTm.tmBreakChar;
+    ptm16->ntmTm.tmFirstChar = ptm16->ntmTm.tmBreakChar - 2;
+    if (ptmW->ntmTm.tmCharSet == SYMBOL_CHARSET)
+    {
+	    ptm16->ntmTm.tmLastChar = ptmW->ntmTm.tmLastChar > 0x0F0FF ? 0x0FF : ptmW->ntmTm.tmLastChar & 0x0FF;
+    }
+    else
+    {
+	    ptm16->ntmTm.tmLastChar = ptmW->ntmTm.tmLastChar > 255 ? 255 : ptmW->ntmTm.tmLastChar;
+    }
+    ptm16->ntmTm.tmDefaultChar = ptmW->ntmTm.tmDefaultChar > 255 ? ptm16->ntmTm.tmBreakChar : ptmW->ntmTm.tmDefaultChar;
+
     ptm16->ntmTm.tmItalic = ptmW->ntmTm.tmItalic;
     ptm16->ntmTm.tmUnderlined = ptmW->ntmTm.tmUnderlined;
     ptm16->ntmTm.tmStruckOut = ptmW->ntmTm.tmStruckOut;
@@ -1239,21 +1255,9 @@ done:
 }
 
 /***********************************************************************
- *           GetTextMetricsA    (GDI32.@)
+ *           get_text_metrics_w    (GDI32.@)
  */
-BOOL WINAPI GetTextMetricsA( HDC hdc, TEXTMETRICA *metrics )
-{
-    TEXTMETRICW tm32;
-
-    if (!GetTextMetricsW( hdc, &tm32 )) return FALSE;
-    FONT_TextMetricWToA( &tm32, metrics );
-    return TRUE;
-}
-
-/***********************************************************************
- *           GetTextMetricsW    (GDI32.@)
- */
-BOOL WINAPI GetTextMetricsW( HDC hdc, TEXTMETRICW *metrics )
+static BOOL get_text_metrics_w( HDC hdc, TEXTMETRICW *metrics )
 {
     BOOL ret = FALSE;
     DC * dc = DC_GetDCPtr( hdc );
@@ -1284,6 +1288,22 @@ BOOL WINAPI GetTextMetricsW( HDC hdc, TE
     metrics->tmAveCharWidth     = WDPTOLP(metrics->tmAveCharWidth);
     metrics->tmMaxCharWidth     = WDPTOLP(metrics->tmMaxCharWidth);
     metrics->tmOverhang         = WDPTOLP(metrics->tmOverhang);
+    if (metrics->tmCharSet == SYMBOL_CHARSET)
+    {
+	    metrics->tmBreakChar = metrics->tmFirstChar & 0x0FF;
+	    if (metrics->tmBreakChar < 2)
+		    metrics->tmBreakChar = metrics->tmBreakChar + 2;
+	    metrics->tmFirstChar = 0;
+	    //metrics->tmLastChar = 0x0F0FF;
+    }
+    else
+    {
+	    metrics->tmBreakChar = metrics->tmFirstChar;
+	    if (metrics->tmBreakChar < 2)
+		    metrics->tmBreakChar = metrics->tmFirstChar + 2;
+    }
+    if (!metrics->tmDefaultChar)
+	    metrics->tmDefaultChar = metrics->tmBreakChar - 1;
         ret = TRUE;
 #undef WDPTOLP
 #undef HDPTOLP
@@ -1312,6 +1332,35 @@ BOOL WINAPI GetTextMetricsW( HDC hdc, TE
     return ret;
 }
 
+/***********************************************************************
+ *           GetTextMetricsA    (GDI32.@)
+ */
+BOOL WINAPI GetTextMetricsA( HDC hdc, TEXTMETRICA *metrics )
+{
+    TEXTMETRICW tm32;
+
+    if (!get_text_metrics_w( hdc, &tm32 )) return FALSE;
+    FONT_TextMetricWToA( &tm32, metrics );
+    return TRUE;
+}
+
+/***********************************************************************
+ *           GetTextMetricsW    (GDI32.@)
+ */
+BOOL WINAPI GetTextMetricsW( HDC hdc, TEXTMETRICW *metrics )
+{
+	if (get_text_metrics_w(hdc, metrics))
+	{
+		if (metrics->tmCharSet == SYMBOL_CHARSET)
+		{
+			metrics->tmLastChar = 0x0F0FF;
+		}
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
 
 /***********************************************************************
  * GetOutlineTextMetrics [GDI.308]  Gets metrics for TrueType fonts.
@@ -1561,7 +1610,7 @@ UINT WINAPI GetOutlineTextMetricsW(
 	    else {
 	        memset(lpOTM, 0, ret);
 		lpOTM->otmSize = sizeof(*lpOTM);
-		GetTextMetricsW(hdc, &lpOTM->otmTextMetrics);
+		get_text_metrics_w(hdc, &lpOTM->otmTextMetrics);
 		/*
 		  Further fill of the structure not implemented,
 		  Needs real values for the structure members
diff --git a/dlls/gdi/freetype.c b/dlls/gdi/freetype.c
index 9f52e60..25ac508 100644
--- a/dlls/gdi/freetype.c
+++ b/dlls/gdi/freetype.c
@@ -2508,6 +2508,13 @@ found:
         return 0;
     }
 
+    /* *
+     * In windows version, some true type fonts don't have proper ulCodeRange flag for SYMBOL_CHARSET,
+     * but it does have MS_SYMBOL cmap section. They should be treated as symbol fonts.
+     * */
+    if (!pFT_Select_Charmap(ret->ft_face, FT_ENCODING_MS_SYMBOL))
+        ret->charset = SYMBOL_CHARSET;
+
     if (ret->charset == SYMBOL_CHARSET && 
         !pFT_Select_Charmap(ret->ft_face, FT_ENCODING_MS_SYMBOL)) {
         /* No ops */


More information about the wine-patches mailing list