Modify kerning scaling algorithm to the one which appears to better match what Windows does

Dmitry Timoshkov dmitry at codeweavers.com
Wed Sep 27 09:43:23 CDT 2006


Hello,

this patch adds kerning test data for font heights 12 and 1024, and
some data to see how font metrics get scaled for different font heights.
The test fails for height 1024 because our code returns wrong text metrics:
otm.otmTextMetrics.tmHeight = 1023 is one of them.

Changelog:
    - Add more kerning test data.
    - Modify kerning scaling algorithm to the one which appears to better
    match what Windows does, it makes the test to pass with font height 12.

diff -up cvs/hq/wine/dlls/gdi/font.c wine/dlls/gdi/font.c
--- cvs/hq/wine/dlls/gdi/font.c	2006-09-27 16:06:30.000000000 +0900
+++ wine/dlls/gdi/font.c	2006-09-27 16:29:47.000000000 +0900
@@ -2583,11 +2583,18 @@ DWORD WINAPI GetKerningPairsA( HDC hDC, 
 DWORD WINAPI GetKerningPairsW( HDC hDC, DWORD cPairs,
                                  LPKERNINGPAIR lpKerningPairs )
 {
-    DC *dc = DC_GetDCPtr(hDC);
+    DC *dc;
     DWORD ret = 0;
 
     TRACE("(%p,%ld,%p)\n", hDC, cPairs, lpKerningPairs);
 
+    if (!cPairs && lpKerningPairs)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return 0;
+    }
+
+    dc = DC_GetDCPtr(hDC);
     if (!dc) return 0;
 
     if (dc->gdiFont)
diff -up cvs/hq/wine/dlls/gdi/freetype.c wine/dlls/gdi/freetype.c
--- cvs/hq/wine/dlls/gdi/freetype.c	2006-09-27 16:06:30.000000000 +0900
+++ wine/dlls/gdi/freetype.c	2006-09-27 23:16:04.000000000 +0900
@@ -4176,7 +4176,19 @@ static DWORD parse_format0_kern_subtable
     {
         kern_pair->wFirst = glyph_to_char[GET_BE_WORD(tt_kern_pair[i].left)];
         kern_pair->wSecond = glyph_to_char[GET_BE_WORD(tt_kern_pair[i].right)];
-        kern_pair->iKernAmount = MulDiv((short)GET_BE_WORD(tt_kern_pair[i].value), font->ppem, font->ft_face->units_per_EM);
+        /* this algorithm appears to better match what Windows does */
+        kern_pair->iKernAmount = (short)GET_BE_WORD(tt_kern_pair[i].value) * font->ppem;
+        if (kern_pair->iKernAmount < 0)
+        {
+            kern_pair->iKernAmount -= font->ft_face->units_per_EM / 2;
+            kern_pair->iKernAmount -= font->ppem;
+        }
+        else if (kern_pair->iKernAmount > 0)
+        {
+            kern_pair->iKernAmount += font->ft_face->units_per_EM / 2;
+            kern_pair->iKernAmount += font->ppem;
+        }
+        kern_pair->iKernAmount /= font->ft_face->units_per_EM;
 
         TRACE("left %u right %u value %d\n",
                kern_pair->wFirst, kern_pair->wSecond, kern_pair->iKernAmount);
diff -up cvs/hq/wine/dlls/gdi/tests/font.c wine/dlls/gdi/tests/font.c
--- cvs/hq/wine/dlls/gdi/tests/font.c	2006-09-27 16:06:30.000000000 +0900
+++ wine/dlls/gdi/tests/font.c	2006-09-27 23:22:07.000000000 +0900
@@ -433,7 +433,7 @@ static void test_text_extents(void)
     if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
     {
         trace("Skipping remainder of text extents test on a Win9x platform\n");
-        SelectObject(hdc, hfont);
+        hfont = SelectObject(hdc, hfont);
         DeleteObject(hfont);
         ReleaseDC(0, hdc);
         return;
@@ -473,7 +473,7 @@ static void test_text_extents(void)
        "GetTextExtentExPointW with lpnFit and alpDx both NULL returns incorrect results\n");
     HeapFree(GetProcessHeap(), 0, extents);
 
-    SelectObject(hdc, hfont);
+    hfont = SelectObject(hdc, hfont);
     DeleteObject(hfont);
     ReleaseDC(NULL, hdc);
 }
@@ -528,29 +528,77 @@ static void test_GetKerningPairs(void)
     {
         const char face_name[LF_FACESIZE];
         LONG height;
+        /* some interesting fields from OUTLINETEXTMETRIC */
+        LONG tmHeight, tmAscent, tmDescent;
+        UINT otmEMSquare;
+        INT  otmAscent;
+        INT  otmDescent;
+        UINT otmLineGap;
+        UINT otmsCapEmHeight;
+        UINT otmsXHeight;
+        INT  otmMacAscent;
+        INT  otmMacDescent;
+        UINT otmMacLineGap;
+        UINT otmusMinimumPPEM;
         /* small subset of kerning pairs to test */
         DWORD total_kern_pairs;
-        const KERNINGPAIR kern_pair[20];
+        const KERNINGPAIR kern_pair[26];
     } kd[] =
     {
-        {"Arial", -34, 20,
+        {"Arial", 12, 12, 9, 3,
+                  2048, 7, -2, 1, 5, 2, 8, -2, 0, 9,
+                  26,
+            {
+                {' ','A',-1},{' ','T',0},{' ','Y',0},{'1','1',-1},
+                {'A',' ',-1},{'A','T',-1},{'A','V',-1},{'A','W',0},
+                {'A','Y',-1},{'A','v',0},{'A','w',0},{'A','y',0},
+                {'F',',',-1},{'F','.',-1},{'F','A',-1},{'L',' ',0},
+                {'L','T',-1},{'L','V',-1},{'L','W',-1},{'L','Y',-1},
+                {915,912,+1},{915,913,-1},{910,912,+1},{910,913,-1},
+                {933,970,+1},{933,972,-1}
+                }
+        },
+        {"Arial", -34, 39, 32, 7,
+                  2048, 25, -7, 5, 17, 9, 31, -7, 1, 9,
+                  26,
             {
                 {' ','A',-2},{' ','T',-1},{' ','Y',-1},{'1','1',-3},
                 {'A',' ',-2},{'A','T',-3},{'A','V',-3},{'A','W',-1},
                 {'A','Y',-3},{'A','v',-1},{'A','w',-1},{'A','y',-1},
                 {'F',',',-4},{'F','.',-4},{'F','A',-2},{'L',' ',-1},
-                {'L','T',-3},{'L','V',-3},{'L','W',-3},{'L','Y',-3}
+                {'L','T',-3},{'L','V',-3},{'L','W',-3},{'L','Y',-3},
+                {915,912,+3},{915,913,-3},{910,912,+3},{910,913,-3},
+                {933,970,+2},{933,972,-3}
             }
         },
-        { "Arial", 120, 20,
+        { "Arial", 120, 120, 97, 23,
+                   2048, 79, -23, 16, 54, 27, 98, -23, 4, 9,
+                   26,
             {
                 {' ','A',-6},{' ','T',-2},{' ','Y',-2},{'1','1',-8},
                 {'A',' ',-6},{'A','T',-8},{'A','V',-8},{'A','W',-4},
                 {'A','Y',-8},{'A','v',-2},{'A','w',-2},{'A','y',-2},
                 {'F',',',-12},{'F','.',-12},{'F','A',-6},{'L',' ',-4},
-                {'L','T',-8},{'L','V',-8},{'L','W',-8},{'L','Y',-8}
+                {'L','T',-8},{'L','V',-8},{'L','W',-8},{'L','Y',-8},
+                {915,912,+9},{915,913,-10},{910,912,+9},{910,913,-8},
+                {933,970,+6},{933,972,-10}
+            }
+        },
+#if 0 /* this set fails due to +1/-1 errors (rounding bug?), needs investigation. */
+        { "Arial", 1024 /* usually 1/2 of EM Square */, 1024, 830, 194,
+                   2048, 668, -193, 137, 459, 229, 830, -194, 30, 9,
+                   26,
+            {
+                {' ','A',-51},{' ','T',-17},{' ','Y',-17},{'1','1',-68},
+                {'A',' ',-51},{'A','T',-68},{'A','V',-68},{'A','W',-34},
+                {'A','Y',-68},{'A','v',-17},{'A','w',-17},{'A','y',-17},
+                {'F',',',-102},{'F','.',-102},{'F','A',-51},{'L',' ',-34},
+                {'L','T',-68},{'L','V',-68},{'L','W',-68},{'L','Y',-68},
+                {915,912,+73},{915,913,-84},{910,912,+76},{910,913,-68},
+                {933,970,+54},{933,972,-83}
             }
         }
+#endif
     };
     LOGFONT lf;
     HFONT hfont, hfont_old;
@@ -574,12 +622,16 @@ static void test_GetKerningPairs(void)
 
     for (i = 0; i < sizeof(kd)/sizeof(kd[0]); i++)
     {
+        OUTLINETEXTMETRICW otm;
+
         if (!is_font_installed(kd[i].face_name))
         {
             trace("%s is not installed so skipping this test\n", kd[i].face_name);
             continue;
         }
 
+        trace("testing font %s, height %ld\n", kd[i].face_name, kd[i].height);
+
         memset(&lf, 0, sizeof(lf));
         strcpy(lf.lfFaceName, kd[i].face_name);
         lf.lfHeight = kd[i].height;
@@ -588,9 +640,44 @@ static void test_GetKerningPairs(void)
 
         hfont_old = SelectObject(hdc, hfont);
 
+        SetLastError(0xdeadbeef);
+        otm.otmSize = sizeof(otm); /* just in case for Win9x compatibility */
+        ok(GetOutlineTextMetricsW(hdc, sizeof(otm), &otm) == sizeof(otm), "GetOutlineTextMetricsW error %ld\n", GetLastError());
+
+        ok(kd[i].tmHeight == otm.otmTextMetrics.tmHeight, "expected %ld, got %ld\n",
+           kd[i].tmHeight, otm.otmTextMetrics.tmHeight);
+        ok(kd[i].tmAscent == otm.otmTextMetrics.tmAscent, "expected %ld, got %ld\n",
+           kd[i].tmAscent, otm.otmTextMetrics.tmAscent);
+        ok(kd[i].tmDescent == otm.otmTextMetrics.tmDescent, "expected %ld, got %ld\n",
+           kd[i].tmDescent, otm.otmTextMetrics.tmDescent);
+
+        ok(kd[i].otmEMSquare == otm.otmEMSquare, "expected %u, got %u\n",
+           kd[i].otmEMSquare, otm.otmEMSquare);
+        ok(kd[i].otmAscent == otm.otmAscent, "expected %d, got %d\n",
+           kd[i].otmAscent, otm.otmAscent);
+        ok(kd[i].otmDescent == otm.otmDescent, "expected %d, got %d\n",
+           kd[i].otmDescent, otm.otmDescent);
+        ok(kd[i].otmLineGap == otm.otmLineGap, "expected %u, got %u\n",
+           kd[i].otmLineGap, otm.otmLineGap);
+todo_wine {
+        ok(kd[i].otmsCapEmHeight == otm.otmsCapEmHeight, "expected %u, got %u\n",
+           kd[i].otmsCapEmHeight, otm.otmsCapEmHeight);
+        ok(kd[i].otmsXHeight == otm.otmsXHeight, "expected %u, got %u\n",
+           kd[i].otmsXHeight, otm.otmsXHeight);
+        ok(kd[i].otmMacAscent == otm.otmMacAscent, "expected %d, got %d\n",
+           kd[i].otmMacAscent, otm.otmMacAscent);
+        ok(kd[i].otmMacDescent == otm.otmMacDescent, "expected %d, got %d\n",
+           kd[i].otmMacDescent, otm.otmMacDescent);
+#if 0 /* this one succeeds due to expected 0, enable it when removing todo */
+        ok(kd[i].otmMacLineGap == otm.otmMacLineGap, "expected %u, got %u\n",
+           kd[i].otmMacLineGap, otm.otmMacLineGap);
+#endif
+        ok(kd[i].otmusMinimumPPEM == otm.otmusMinimumPPEM, "expected %u, got %u\n",
+           kd[i].otmusMinimumPPEM, otm.otmusMinimumPPEM);
+}
+
         total_kern_pairs = GetKerningPairsW(hdc, 0, NULL);
-        trace("font %s, height %ld, total_kern_pairs %lu\n",
-              kd[i].face_name, kd[i].height, total_kern_pairs);
+        trace("total_kern_pairs %lu\n", total_kern_pairs);
         kern_pair = HeapAlloc(GetProcessHeap(), 0, total_kern_pairs * sizeof(*kern_pair));
 
 #if 0 /* Win98 (GetKerningPairsA) and XP behave differently here, the test passes on XP */
@@ -617,16 +704,18 @@ static void test_GetKerningPairs(void)
             DWORD j;
 #if 0
             if (kern_pair[n].wFirst < 127 && kern_pair[n].wSecond < 127)
-                trace("wFirst '%c', wSecond '%c', iKernAmount %d\n",
+                trace("{'%c','%c',%d},\n",
                       kern_pair[n].wFirst, kern_pair[n].wSecond, kern_pair[n].iKernAmount);
 #endif
             for (j = 0; j < kd[i].total_kern_pairs; j++)
             {
                 if (kern_pair[n].wFirst == kd[i].kern_pair[j].wFirst &&
-                    kern_pair[n].wSecond == kd[i].kern_pair[j].wSecond &&
-                    kern_pair[n].iKernAmount == kd[i].kern_pair[j].iKernAmount)
+                    kern_pair[n].wSecond == kd[i].kern_pair[j].wSecond)
                 {
-                    /*trace("match\n");*/
+                    ok(kern_pair[n].iKernAmount == kd[i].kern_pair[j].iKernAmount,
+                       "pair %d:%d got %d, expected %d\n",
+                       kern_pair[n].wFirst, kern_pair[n].wSecond,
+                       kern_pair[n].iKernAmount, kd[i].kern_pair[j].iKernAmount);
                     matches++;
                 }
             }





More information about the wine-patches mailing list