Akihiro Sagawa : gdi32: Ensure a fixed-pitch full-width character has double advance of a half-width character .

Alexandre Julliard julliard at winehq.org
Thu Feb 7 14:07:24 CST 2013


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

Author: Akihiro Sagawa <sagawa.aki at gmail.com>
Date:   Thu Feb  7 22:13:04 2013 +0900

gdi32: Ensure a fixed-pitch full-width character has double advance of a half-width character.

---

 dlls/gdi32/freetype.c   |   37 ++++++++++++++++++--
 dlls/gdi32/tests/font.c |   88 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 122 insertions(+), 3 deletions(-)

diff --git a/dlls/gdi32/freetype.c b/dlls/gdi32/freetype.c
index 40cf080..b977b0d 100644
--- a/dlls/gdi32/freetype.c
+++ b/dlls/gdi32/freetype.c
@@ -5821,6 +5821,7 @@ static DWORD get_glyph_outline(GdiFont *incoming_font, UINT glyph, UINT format,
     BOOL needsTransform = FALSE;
     BOOL tategaki = (font->GSUB_Table != NULL);
     UINT original_index;
+    FT_Fixed avgAdvance = 0;
 
     TRACE("%p, %04x, %08x, %p, %08x, %p, %p\n", font, glyph, format, lpgm,
 	  buflen, buf, lpmat);
@@ -5957,10 +5958,26 @@ static DWORD get_glyph_outline(GdiFont *incoming_font, UINT glyph, UINT format,
         return GDI_ERROR;
     }
 
+    if(FT_IS_SCALABLE(incoming_font->ft_face)) {
+        TEXTMETRICW tm;
+        if (get_text_metrics(incoming_font, &tm) &&
+            !(tm.tmPitchAndFamily & TMPF_FIXED_PITCH)) {
+            avgAdvance = pFT_MulFix(incoming_font->ntmAvgWidth,
+                                    incoming_font->ft_face->size->metrics.x_scale);
+            if (avgAdvance && (ft_face->glyph->metrics.horiAdvance+63) >> 6 == (avgAdvance*2+63) >> 6)
+                TRACE("Fixed-pitch full-width character detected\n");
+            else
+                avgAdvance = 0; /* cancel this feature */
+        }
+    }
+
     if(!needsTransform) {
         left = (INT)(ft_face->glyph->metrics.horiBearingX) & -64;
         right = (INT)((ft_face->glyph->metrics.horiBearingX + ft_face->glyph->metrics.width) + 63) & -64;
-        adv = (INT)(ft_face->glyph->metrics.horiAdvance + 63) >> 6;
+        if (!avgAdvance)
+            adv = (INT)(ft_face->glyph->metrics.horiAdvance + 63) >> 6;
+        else
+            adv = (INT)((avgAdvance + 32) >> 6) * 2;
 
 	top = (ft_face->glyph->metrics.horiBearingY + 63) & -64;
 	bottom = (ft_face->glyph->metrics.horiBearingY -
@@ -6001,13 +6018,27 @@ static DWORD get_glyph_outline(GdiFont *incoming_font, UINT glyph, UINT format,
 	vec.x = ft_face->glyph->metrics.horiAdvance;
 	vec.y = 0;
 	pFT_Vector_Transform(&vec, &transMat);
-	lpgm->gmCellIncX = (vec.x+63) >> 6;
 	lpgm->gmCellIncY = -((vec.y+63) >> 6);
+	if (!avgAdvance || vec.y)
+	    lpgm->gmCellIncX = (vec.x+63) >> 6;
+	else {
+	    vec.x = avgAdvance;
+	    vec.y = 0;
+	    pFT_Vector_Transform(&vec, &transMat);
+	    lpgm->gmCellIncX = ((vec.x+32) >> 6) * 2;
+	}
 
         vec.x = ft_face->glyph->metrics.horiAdvance;
         vec.y = 0;
         pFT_Vector_Transform(&vec, &transMatUnrotated);
-        adv = (vec.x+63) >> 6;
+        if (!avgAdvance || vec.y)
+            adv = (vec.x+63) >> 6;
+        else {
+            vec.x = avgAdvance;
+            vec.y = 0;
+            pFT_Vector_Transform(&vec, &transMatUnrotated);
+            adv = ((vec.x+32) >> 6) * 2;
+        }
     }
 
     lpgm->gmBlackBoxX = (right - left) >> 6;
diff --git a/dlls/gdi32/tests/font.c b/dlls/gdi32/tests/font.c
index f234b62..943f584 100644
--- a/dlls/gdi32/tests/font.c
+++ b/dlls/gdi32/tests/font.c
@@ -3552,6 +3552,38 @@ todo_wine
     DeleteDC(hdc);
 }
 
+static int CALLBACK create_fixed_pitch_font_proc(const LOGFONT *lpelfe,
+                                                 const TEXTMETRIC *lpntme,
+                                                 DWORD FontType, LPARAM lParam)
+{
+    const NEWTEXTMETRICEX *lpntmex = (const NEWTEXTMETRICEX *)lpntme;
+    CHARSETINFO csi;
+    LOGFONT lf = *lpelfe;
+    HFONT hfont;
+
+    /* skip bitmap, proportional or vertical font */
+    if ((FontType & TRUETYPE_FONTTYPE) == 0 ||
+        (lf.lfPitchAndFamily & 0xf) != FIXED_PITCH ||
+        lf.lfFaceName[0] == '@')
+        return 1;
+
+    /* skip linked font */
+    if (!TranslateCharsetInfo((DWORD*)(INT_PTR)lpelfe->lfCharSet, &csi, TCI_SRCCHARSET) ||
+        (lpntmex->ntmFontSig.fsCsb[0] & csi.fs.fsCsb[0]) == 0)
+        return 1;
+
+    /* test with an odd height */
+    lf.lfHeight = -19;
+    lf.lfWidth = 0;
+    hfont = CreateFontIndirect(&lf);
+    if (hfont)
+    {
+        *(HFONT *)lParam = hfont;
+        return 0;
+    }
+    return 1;
+}
+
 static void test_GetGlyphOutline(void)
 {
     HDC hdc;
@@ -3640,6 +3672,8 @@ static void test_GetGlyphOutline(void)
 
     for (i = 0; i < sizeof c / sizeof c[0]; ++i)
     {
+        static const MAT2 rotate_mat = {{0, 0}, {0, -1}, {0, 1}, {0, 0}};
+
         lf.lfFaceName[0] = '\0';
         lf.lfCharSet = c[i].cs;
         lf.lfPitchAndFamily = 0;
@@ -3670,6 +3704,60 @@ static void test_GetGlyphOutline(void)
         ret2 = GetGlyphOutlineW(hdc, c[i].w, GGO_BITMAP, &gm2, 0, NULL, &mat);
         ok(ret == ret2 && memcmp(&gm, &gm2, sizeof gm) == 0, "%d %d\n", ret, ret2);
 
+        if (EnumFontFamiliesEx(hdc, &lf, create_fixed_pitch_font_proc, (LPARAM)&hfont, 0))
+        {
+            skip("Fixed-pitch TrueType font for charset %u is not available\n", c[i].cs);
+            continue;
+        }
+        DeleteObject(SelectObject(hdc, hfont));
+        if (c[i].a <= 0xff)
+        {
+            DeleteObject(SelectObject(hdc, old_hfont));
+            continue;
+        }
+
+        ret = GetObject(hfont, sizeof lf, &lf);
+        ok(ret > 0, "GetObject error %u\n", GetLastError());
+
+        ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
+        ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
+        ret = GetGlyphOutlineA(hdc, c[i].a, GGO_METRICS, &gm2, 0, NULL, &mat);
+        ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
+        trace("Tests with height=%d,half=%d,full=%d,face=%s,charset=%d\n",
+              -lf.lfHeight, gm.gmCellIncX, gm2.gmCellIncX, lf.lfFaceName, lf.lfCharSet);
+        ok(gm2.gmCellIncX == gm.gmCellIncX * 2 || broken(gm2.gmCellIncX == -lf.lfHeight),
+           "expected %d, got %d (%s:%d)\n",
+           gm.gmCellIncX * 2, gm2.gmCellIncX, lf.lfFaceName, lf.lfCharSet);
+
+        ret = GetGlyphOutlineA(hdc, c[i].a, GGO_METRICS, &gm2, 0, NULL, &rotate_mat);
+        ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
+        ok(gm2.gmCellIncY == -lf.lfHeight,
+           "expected %d, got %d (%s:%d)\n",
+           -lf.lfHeight, gm2.gmCellIncY, lf.lfFaceName, lf.lfCharSet);
+
+        lf.lfItalic = TRUE;
+        hfont = CreateFontIndirect(&lf);
+        ok(hfont != NULL, "CreateFontIndirect error %u\n", GetLastError());
+        DeleteObject(SelectObject(hdc, hfont));
+        ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
+        ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
+        ret = GetGlyphOutlineA(hdc, c[i].a, GGO_METRICS, &gm2, 0, NULL, &mat);
+        ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
+        ok(gm2.gmCellIncX == gm.gmCellIncX * 2 || broken(gm2.gmCellIncX == -lf.lfHeight),
+           "expected %d, got %d (%s:%d)\n",
+           gm.gmCellIncX * 2, gm2.gmCellIncX, lf.lfFaceName, lf.lfCharSet);
+
+        lf.lfItalic = FALSE;
+        lf.lfEscapement = lf.lfOrientation = 2700;
+        hfont = CreateFontIndirect(&lf);
+        ok(hfont != NULL, "CreateFontIndirect error %u\n", GetLastError());
+        DeleteObject(SelectObject(hdc, hfont));
+        ret = GetGlyphOutlineA(hdc, c[i].a, GGO_METRICS, &gm2, 0, NULL, &mat);
+        ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
+        ok(gm2.gmCellIncY == -lf.lfHeight,
+           "expected %d, got %d (%s:%d)\n",
+           -lf.lfHeight, gm2.gmCellIncY, lf.lfFaceName, lf.lfCharSet);
+
         hfont = SelectObject(hdc, old_hfont);
         DeleteObject(hfont);
     }




More information about the wine-cvs mailing list