Sam Edwards : gdi32: Clip font glyphs to fit within text metrics.

Alexandre Julliard julliard at winehq.org
Thu May 2 14:07:02 CDT 2013


Module: wine
Branch: master
Commit: 21dbe1c949af569daf0a4db4eef45e40fc780f0b
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=21dbe1c949af569daf0a4db4eef45e40fc780f0b

Author: Sam Edwards <CFSworks at gmail.com>
Date:   Tue Apr 30 01:20:57 2013 -0600

gdi32: Clip font glyphs to fit within text metrics.

---

 dlls/gdi32/freetype.c          |   41 ++++++++++++++++++--------
 dlls/gdi32/tests/font.c        |   41 +++++++++++++++++++++++++++
 dlls/gdi32/tests/wine_test.sfd |   60 +++++++++++++++++++++++++++++++++------
 dlls/gdi32/tests/wine_test.ttf |  Bin 1652 -> 1760 bytes
 4 files changed, 119 insertions(+), 23 deletions(-)

diff --git a/dlls/gdi32/freetype.c b/dlls/gdi32/freetype.c
index 30ab574..c30a358 100644
--- a/dlls/gdi32/freetype.c
+++ b/dlls/gdi32/freetype.c
@@ -538,6 +538,7 @@ static BOOL use_default_fallback = FALSE;
 
 static BOOL get_glyph_index_linked(GdiFont *font, UINT c, GdiFont **linked_font, FT_UInt *glyph);
 static BOOL get_outline_text_metrics(GdiFont *font);
+static BOOL get_bitmap_text_metrics(GdiFont *font);
 static BOOL get_text_metrics(GdiFont *font, LPTEXTMETRICW ptm);
 static void remove_face_from_cache( Face *face );
 
@@ -5868,6 +5869,7 @@ static DWORD get_glyph_outline(GdiFont *incoming_font, UINT glyph, UINT format,
     static const FT_Matrix identityMat = {(1 << 16), 0, 0, (1 << 16)};
     FT_Face ft_face = incoming_font->ft_face;
     GdiFont *font = incoming_font;
+    FT_Glyph_Metrics metrics;
     FT_UInt glyph_index;
     DWORD width, height, pitch, needed = 0;
     FT_Bitmap ft_bitmap;
@@ -6019,6 +6021,22 @@ static DWORD get_glyph_outline(GdiFont *incoming_font, UINT glyph, UINT format,
         return GDI_ERROR;
     }
 
+    /* Some poorly-created fonts contain glyphs that exceed the boundaries set
+     * by the text metrics. The proper behavior is to clip the glyph metrics to
+     * fit within the maximums specified in the text metrics. */
+    metrics = ft_face->glyph->metrics;
+    if(incoming_font->potm || get_outline_text_metrics(incoming_font) ||
+        get_bitmap_text_metrics(incoming_font)) {
+        TEXTMETRICW *ptm = &incoming_font->potm->otmTextMetrics;
+        top = min( metrics.horiBearingY, ptm->tmAscent << 6 );
+        bottom = max( metrics.horiBearingY - metrics.height, -(ptm->tmDescent << 6) );
+        metrics.horiBearingY = top;
+        metrics.height = top - bottom;
+
+        /* TODO: Are we supposed to clip the width as well...? */
+        /* metrics.width = min( metrics.width, ptm->tmMaxCharWidth << 6 ); */
+    }
+
     if(FT_IS_SCALABLE(incoming_font->ft_face)) {
         TEXTMETRICW tm;
         if (get_text_metrics(incoming_font, &tm) &&
@@ -6026,7 +6044,7 @@ static DWORD get_glyph_outline(GdiFont *incoming_font, UINT glyph, UINT format,
             em_scale = MulDiv(incoming_font->ppem, 1 << 16, incoming_font->ft_face->units_per_EM);
             avgAdvance = pFT_MulFix(incoming_font->ntmAvgWidth, em_scale);
             if (avgAdvance &&
-                (ft_face->glyph->metrics.horiAdvance+63) >> 6 == pFT_MulFix(incoming_font->ntmAvgWidth*2, em_scale))
+                (metrics.horiAdvance+63) >> 6 == pFT_MulFix(incoming_font->ntmAvgWidth*2, em_scale))
                 TRACE("Fixed-pitch full-width character detected\n");
             else
                 avgAdvance = 0; /* cancel this feature */
@@ -6034,16 +6052,15 @@ static DWORD get_glyph_outline(GdiFont *incoming_font, UINT glyph, UINT format,
     }
 
     if(!needsTransform) {
-        left = (INT)(ft_face->glyph->metrics.horiBearingX) & -64;
-        right = (INT)((ft_face->glyph->metrics.horiBearingX + ft_face->glyph->metrics.width) + 63) & -64;
+        left = (INT)(metrics.horiBearingX) & -64;
+        right = (INT)((metrics.horiBearingX + metrics.width) + 63) & -64;
         if (!avgAdvance)
-            adv = (INT)(ft_face->glyph->metrics.horiAdvance + 63) >> 6;
+            adv = (INT)(metrics.horiAdvance + 63) >> 6;
         else
             adv = (INT)avgAdvance * 2;
 
-	top = (ft_face->glyph->metrics.horiBearingY + 63) & -64;
-	bottom = (ft_face->glyph->metrics.horiBearingY -
-		  ft_face->glyph->metrics.height) & -64;
+	top = (metrics.horiBearingY + 63) & -64;
+	bottom = (metrics.horiBearingY - metrics.height) & -64;
 	lpgm->gmCellIncX = adv;
 	lpgm->gmCellIncY = 0;
     } else {
@@ -6054,10 +6071,8 @@ static DWORD get_glyph_outline(GdiFont *incoming_font, UINT glyph, UINT format,
 
 	for(xc = 0; xc < 2; xc++) {
 	    for(yc = 0; yc < 2; yc++) {
-	        vec.x = (ft_face->glyph->metrics.horiBearingX +
-		  xc * ft_face->glyph->metrics.width);
-		vec.y = ft_face->glyph->metrics.horiBearingY -
-		  yc * ft_face->glyph->metrics.height;
+	        vec.x = metrics.horiBearingX + xc * metrics.width;
+		vec.y = metrics.horiBearingY - yc * metrics.height;
 		TRACE("Vec %ld,%ld\n", vec.x, vec.y);
 		pFT_Vector_Transform(&vec, &transMat);
 		if(xc == 0 && yc == 0) {
@@ -6077,7 +6092,7 @@ static DWORD get_glyph_outline(GdiFont *incoming_font, UINT glyph, UINT format,
 	top = (top + 63) & -64;
 
 	TRACE("transformed box: (%d,%d - %d,%d)\n", left, top, right, bottom);
-	vec.x = ft_face->glyph->metrics.horiAdvance;
+	vec.x = metrics.horiAdvance;
 	vec.y = 0;
 	pFT_Vector_Transform(&vec, &transMat);
 	lpgm->gmCellIncY = -((vec.y+63) >> 6);
@@ -6090,7 +6105,7 @@ static DWORD get_glyph_outline(GdiFont *incoming_font, UINT glyph, UINT format,
 	    lpgm->gmCellIncX = pFT_MulFix(vec.x, em_scale) * 2;
 	}
 
-        vec.x = ft_face->glyph->metrics.horiAdvance;
+        vec.x = metrics.horiAdvance;
         vec.y = 0;
         pFT_Vector_Transform(&vec, &transMatUnrotated);
         if (!avgAdvance || vec.y)
diff --git a/dlls/gdi32/tests/font.c b/dlls/gdi32/tests/font.c
index e5e7ff9..bc4e614 100644
--- a/dlls/gdi32/tests/font.c
+++ b/dlls/gdi32/tests/font.c
@@ -4561,6 +4561,46 @@ static void test_GetGlyphOutline_empty_contour(void)
     ReleaseDC(NULL, hdc);
 }
 
+static void test_GetGlyphOutline_metric_clipping(void)
+{
+    HDC hdc;
+    LOGFONTA lf;
+    HFONT hfont, hfont_prev;
+    GLYPHMETRICS gm;
+    TEXTMETRICA tm;
+    DWORD ret;
+
+    memset(&lf, 0, sizeof(lf));
+    lf.lfHeight = 72;
+    lstrcpyA(lf.lfFaceName, "wine_test");
+
+    SetLastError(0xdeadbeef);
+    hfont = CreateFontIndirectA(&lf);
+    ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
+
+    hdc = GetDC(NULL);
+
+    hfont_prev = SelectObject(hdc, hfont);
+    ok(hfont_prev != NULL, "SelectObject failed\n");
+
+    SetLastError(0xdeadbeef);
+    ret = GetTextMetrics(hdc, &tm);
+    ok(ret, "GetTextMetrics error %u\n", GetLastError());
+
+    GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
+    ok(gm.gmptGlyphOrigin.y <= tm.tmAscent,
+        "Glyph top(%d) exceeds ascent(%d)\n",
+        gm.gmptGlyphOrigin.y, tm.tmAscent);
+    GetGlyphOutlineA(hdc, 'D', GGO_METRICS, &gm, 0, NULL, &mat);
+    ok(gm.gmptGlyphOrigin.y - gm.gmBlackBoxY >= -tm.tmDescent,
+        "Glyph bottom(%d) exceeds descent(%d)\n",
+        gm.gmptGlyphOrigin.y - gm.gmBlackBoxY, -tm.tmDescent);
+
+    SelectObject(hdc, hfont_prev);
+    DeleteObject(hfont);
+    ReleaseDC(NULL, hdc);
+}
+
 static void test_CreateScalableFontResource(void)
 {
     char ttf_name[MAX_PATH];
@@ -4643,6 +4683,7 @@ static void test_CreateScalableFontResource(void)
     ok(ret, "font wine_test should be enumerated\n");
 
     test_GetGlyphOutline_empty_contour();
+    test_GetGlyphOutline_metric_clipping();
 
     ret = pRemoveFontResourceExA(fot_name, FR_PRIVATE, 0);
     ok(!ret, "RemoveFontResourceEx() with not matching flags should fail\n");
diff --git a/dlls/gdi32/tests/wine_test.sfd b/dlls/gdi32/tests/wine_test.sfd
index 5adf7cf..4c19377 100644
--- a/dlls/gdi32/tests/wine_test.sfd
+++ b/dlls/gdi32/tests/wine_test.sfd
@@ -20,7 +20,7 @@ OS2Version: 2
 OS2_WeightWidthSlopeOnly: 0
 OS2_UseTypoMetrics: 1
 CreationTime: 1288336343
-ModificationTime: 1352483620
+ModificationTime: 1366465321
 PfmFamily: 17
 TTFWeight: 500
 TTFWidth: 5
@@ -32,10 +32,10 @@ OS2TypoAOffset: 1
 OS2TypoDescent: 0
 OS2TypoDOffset: 1
 OS2TypoLinegap: 184
-OS2WinAscent: 0
-OS2WinAOffset: 1
-OS2WinDescent: 0
-OS2WinDOffset: 1
+OS2WinAscent: 1638
+OS2WinAOffset: 0
+OS2WinDescent: 410
+OS2WinDOffset: 0
 HheadAscent: 0
 HheadAOffset: 1
 HheadDescent: 0
@@ -78,15 +78,17 @@ ShortTable: maxp 16
   0
 EndShort
 LangName: 1033 "" "" "" "Wine : wine_test : 4-11-2010"
-GaspTable: 1 65535 2
+GaspTable: 1 65535 2 0
 Encoding: UnicodeBmp
 UnicodeInterp: none
 NameList: Adobe Glyph List
 DisplaySize: -24
 AntiAlias: 1
 FitToEm: 1
-WinInfo: 65 65 19
-BeginChars: 65539 5
+WinInfo: 48 16 4
+BeginPrivate: 0
+EndPrivate
+BeginChars: 65539 7
 
 StartChar: .notdef
 Encoding: 65536 -1 0
@@ -178,10 +180,10 @@ LayerCount: 2
 EndChar
 
 StartChar: dieresis
-Encoding: 168 168 0
+Encoding: 168 168 4
 Width: 1000
 VWidth: 0
-Flags: HW
+Flags: W
 LayerCount: 2
 Fore
 SplineSet
@@ -201,5 +203,43 @@ SplineSet
  310.707 834.805 310.707 834.805 254.492 773.213 c 1,12,13
 EndSplineSet
 EndChar
+
+StartChar: A
+Encoding: 65 65 5
+Width: 1000
+VWidth: 0
+Flags: WO
+LayerCount: 2
+Fore
+SplineSet
+459 1258 m 29,0,-1
+ 462 1639 l 5,1,-1
+ 389 1638 l 5,2,-1
+ 492 1815 l 5,3,-1
+ 609 1638.5 l 5,4,-1
+ 531 1637.5 l 5,5,-1
+ 523 1258 l 5,6,-1
+ 459 1258 l 29,0,-1
+EndSplineSet
+EndChar
+
+StartChar: D
+Encoding: 68 68 6
+Width: 1000
+VWidth: 0
+Flags: WO
+LayerCount: 2
+Fore
+SplineSet
+461 -30.7998 m 29,0,-1
+ 464 -411.8 l 5,1,-1
+ 391 -410.8 l 5,2,-1
+ 494 -587.8 l 5,3,-1
+ 611 -411.3 l 5,4,-1
+ 533 -410.3 l 5,5,-1
+ 525 -30.7998 l 5,6,-1
+ 461 -30.7998 l 29,0,-1
+EndSplineSet
+EndChar
 EndChars
 EndSplineFont
diff --git a/dlls/gdi32/tests/wine_test.ttf b/dlls/gdi32/tests/wine_test.ttf
index 1e546eb..0868802 100644
Binary files a/dlls/gdi32/tests/wine_test.ttf and b/dlls/gdi32/tests/wine_test.ttf differ




More information about the wine-cvs mailing list