Commit 98b2e55b authored by Dmitry Timoshkov's avatar Dmitry Timoshkov Committed by Alexandre Julliard

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

parent 8b125182
...@@ -2583,11 +2583,18 @@ DWORD WINAPI GetKerningPairsA( HDC hDC, DWORD cPairs, ...@@ -2583,11 +2583,18 @@ DWORD WINAPI GetKerningPairsA( HDC hDC, DWORD cPairs,
DWORD WINAPI GetKerningPairsW( HDC hDC, DWORD cPairs, DWORD WINAPI GetKerningPairsW( HDC hDC, DWORD cPairs,
LPKERNINGPAIR lpKerningPairs ) LPKERNINGPAIR lpKerningPairs )
{ {
DC *dc = DC_GetDCPtr(hDC); DC *dc;
DWORD ret = 0; DWORD ret = 0;
TRACE("(%p,%ld,%p)\n", hDC, cPairs, lpKerningPairs); 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) return 0;
if (dc->gdiFont) if (dc->gdiFont)
......
...@@ -4176,7 +4176,19 @@ static DWORD parse_format0_kern_subtable(GdiFont font, ...@@ -4176,7 +4176,19 @@ static DWORD parse_format0_kern_subtable(GdiFont font,
{ {
kern_pair->wFirst = glyph_to_char[GET_BE_WORD(tt_kern_pair[i].left)]; 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->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", TRACE("left %u right %u value %d\n",
kern_pair->wFirst, kern_pair->wSecond, kern_pair->iKernAmount); kern_pair->wFirst, kern_pair->wSecond, kern_pair->iKernAmount);
......
...@@ -433,7 +433,7 @@ static void test_text_extents(void) ...@@ -433,7 +433,7 @@ static void test_text_extents(void)
if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
{ {
trace("Skipping remainder of text extents test on a Win9x platform\n"); trace("Skipping remainder of text extents test on a Win9x platform\n");
SelectObject(hdc, hfont); hfont = SelectObject(hdc, hfont);
DeleteObject(hfont); DeleteObject(hfont);
ReleaseDC(0, hdc); ReleaseDC(0, hdc);
return; return;
...@@ -473,7 +473,7 @@ static void test_text_extents(void) ...@@ -473,7 +473,7 @@ static void test_text_extents(void)
"GetTextExtentExPointW with lpnFit and alpDx both NULL returns incorrect results\n"); "GetTextExtentExPointW with lpnFit and alpDx both NULL returns incorrect results\n");
HeapFree(GetProcessHeap(), 0, extents); HeapFree(GetProcessHeap(), 0, extents);
SelectObject(hdc, hfont); hfont = SelectObject(hdc, hfont);
DeleteObject(hfont); DeleteObject(hfont);
ReleaseDC(NULL, hdc); ReleaseDC(NULL, hdc);
} }
...@@ -528,29 +528,77 @@ static void test_GetKerningPairs(void) ...@@ -528,29 +528,77 @@ static void test_GetKerningPairs(void)
{ {
const char face_name[LF_FACESIZE]; const char face_name[LF_FACESIZE];
LONG height; 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 */ /* small subset of kerning pairs to test */
DWORD total_kern_pairs; DWORD total_kern_pairs;
const KERNINGPAIR kern_pair[20]; const KERNINGPAIR kern_pair[26];
} kd[] = } 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},{' ','T',-1},{' ','Y',-1},{'1','1',-3},
{'A',' ',-2},{'A','T',-3},{'A','V',-3},{'A','W',-1}, {'A',' ',-2},{'A','T',-3},{'A','V',-3},{'A','W',-1},
{'A','Y',-3},{'A','v',-1},{'A','w',-1},{'A','y',-1}, {'A','Y',-3},{'A','v',-1},{'A','w',-1},{'A','y',-1},
{'F',',',-4},{'F','.',-4},{'F','A',-2},{'L',' ',-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},{' ','T',-2},{' ','Y',-2},{'1','1',-8},
{'A',' ',-6},{'A','T',-8},{'A','V',-8},{'A','W',-4}, {'A',' ',-6},{'A','T',-8},{'A','V',-8},{'A','W',-4},
{'A','Y',-8},{'A','v',-2},{'A','w',-2},{'A','y',-2}, {'A','Y',-8},{'A','v',-2},{'A','w',-2},{'A','y',-2},
{'F',',',-12},{'F','.',-12},{'F','A',-6},{'L',' ',-4}, {'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; LOGFONT lf;
HFONT hfont, hfont_old; HFONT hfont, hfont_old;
...@@ -574,12 +622,16 @@ static void test_GetKerningPairs(void) ...@@ -574,12 +622,16 @@ static void test_GetKerningPairs(void)
for (i = 0; i < sizeof(kd)/sizeof(kd[0]); i++) for (i = 0; i < sizeof(kd)/sizeof(kd[0]); i++)
{ {
OUTLINETEXTMETRICW otm;
if (!is_font_installed(kd[i].face_name)) if (!is_font_installed(kd[i].face_name))
{ {
trace("%s is not installed so skipping this test\n", kd[i].face_name); trace("%s is not installed so skipping this test\n", kd[i].face_name);
continue; continue;
} }
trace("testing font %s, height %ld\n", kd[i].face_name, kd[i].height);
memset(&lf, 0, sizeof(lf)); memset(&lf, 0, sizeof(lf));
strcpy(lf.lfFaceName, kd[i].face_name); strcpy(lf.lfFaceName, kd[i].face_name);
lf.lfHeight = kd[i].height; lf.lfHeight = kd[i].height;
...@@ -588,9 +640,44 @@ static void test_GetKerningPairs(void) ...@@ -588,9 +640,44 @@ static void test_GetKerningPairs(void)
hfont_old = SelectObject(hdc, hfont); 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); total_kern_pairs = GetKerningPairsW(hdc, 0, NULL);
trace("font %s, height %ld, total_kern_pairs %lu\n", trace("total_kern_pairs %lu\n", total_kern_pairs);
kd[i].face_name, kd[i].height, total_kern_pairs);
kern_pair = HeapAlloc(GetProcessHeap(), 0, total_kern_pairs * sizeof(*kern_pair)); 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 */ #if 0 /* Win98 (GetKerningPairsA) and XP behave differently here, the test passes on XP */
...@@ -617,16 +704,18 @@ static void test_GetKerningPairs(void) ...@@ -617,16 +704,18 @@ static void test_GetKerningPairs(void)
DWORD j; DWORD j;
#if 0 #if 0
if (kern_pair[n].wFirst < 127 && kern_pair[n].wSecond < 127) 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); kern_pair[n].wFirst, kern_pair[n].wSecond, kern_pair[n].iKernAmount);
#endif #endif
for (j = 0; j < kd[i].total_kern_pairs; j++) for (j = 0; j < kd[i].total_kern_pairs; j++)
{ {
if (kern_pair[n].wFirst == kd[i].kern_pair[j].wFirst && if (kern_pair[n].wFirst == kd[i].kern_pair[j].wFirst &&
kern_pair[n].wSecond == kd[i].kern_pair[j].wSecond && kern_pair[n].wSecond == kd[i].kern_pair[j].wSecond)
kern_pair[n].iKernAmount == kd[i].kern_pair[j].iKernAmount)
{ {
/*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++; matches++;
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment