From: Dmitry Timoshkov dmitry@baikal.ru
Signed-off-by: Dmitry Timoshkov dmitry@baikal.ru Signed-off-by: Huw Davies huw@codeweavers.com --- dlls/gdi32/font.c | 97 ++++++++++++++++++++++++++++++++------ dlls/gdi32/tests/font.c | 101 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 185 insertions(+), 13 deletions(-)
diff --git a/dlls/gdi32/font.c b/dlls/gdi32/font.c index 8788426a2c2..e099bec5e81 100644 --- a/dlls/gdi32/font.c +++ b/dlls/gdi32/font.c @@ -3278,6 +3278,55 @@ GetCharacterPlacementA(HDC hdc, LPCSTR lpString, INT uCount, return ret; }
+static int kern_pair(const KERNINGPAIR *kern, int count, WCHAR c1, WCHAR c2) +{ + int i; + + for (i = 0; i < count; i++) + { + if (kern[i].wFirst == c1 && kern[i].wSecond == c2) + return kern[i].iKernAmount; + } + + return 0; +} + +static int *kern_string(HDC hdc, const WCHAR *str, int len, int *kern_total) +{ + int i, count; + KERNINGPAIR *kern = NULL; + int *ret; + + *kern_total = 0; + + ret = heap_alloc(len * sizeof(*ret)); + if (!ret) return NULL; + + count = GetKerningPairsW(hdc, 0, NULL); + if (count) + { + kern = heap_alloc(count * sizeof(*kern)); + if (!kern) + { + heap_free(ret); + return NULL; + } + + GetKerningPairsW(hdc, count, kern); + } + + for (i = 0; i < len - 1; i++) + { + ret[i] = kern_pair(kern, count, str[i], str[i + 1]); + *kern_total += ret[i]; + } + + ret[len - 1] = 0; /* no kerning for last element */ + + heap_free(kern); + return ret; +} + /************************************************************************* * GetCharacterPlacementW [GDI32.@] * @@ -3309,6 +3358,7 @@ GetCharacterPlacementW( DWORD ret=0; SIZE size; UINT i, nSet; + int *kern = NULL, kern_total = 0;
TRACE("%s, %d, %d, 0x%08x\n", debugstr_wn(lpString, uCount), uCount, nMaxExtent, dwFlags); @@ -3325,30 +3375,30 @@ GetCharacterPlacementW( lpResults->lpDx, lpResults->lpCaretPos, lpResults->lpClass, lpResults->lpGlyphs, lpResults->nGlyphs, lpResults->nMaxFit);
- if(dwFlags&(~GCP_REORDER)) + if (dwFlags & ~(GCP_REORDER | GCP_USEKERNING)) FIXME("flags 0x%08x ignored\n", dwFlags); - if(lpResults->lpClass) + if (lpResults->lpClass) FIXME("classes not implemented\n"); if (lpResults->lpCaretPos && (dwFlags & GCP_REORDER)) FIXME("Caret positions for complex scripts not implemented\n");
nSet = (UINT)uCount; - if(nSet > lpResults->nGlyphs) + if (nSet > lpResults->nGlyphs) nSet = lpResults->nGlyphs;
/* return number of initialized fields */ lpResults->nGlyphs = nSet;
- if((dwFlags&GCP_REORDER)==0 ) + if (!(dwFlags & GCP_REORDER)) { /* Treat the case where no special handling was requested in a fastpath way */ /* copy will do if the GCP_REORDER flag is not set */ - if(lpResults->lpOutString) + if (lpResults->lpOutString) memcpy( lpResults->lpOutString, lpString, nSet * sizeof(WCHAR));
- if(lpResults->lpOrder) + if (lpResults->lpOrder) { - for(i = 0; i < nSet; i++) + for (i = 0; i < nSet; i++) lpResults->lpOrder[i] = i; } } @@ -3358,6 +3408,16 @@ GetCharacterPlacementW( nSet, lpResults->lpOrder, NULL, NULL ); }
+ if (dwFlags & GCP_USEKERNING) + { + kern = kern_string(hdc, lpString, nSet, &kern_total); + if (!kern) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return 0; + } + } + /* FIXME: Will use the placement chars */ if (lpResults->lpDx) { @@ -3365,7 +3425,11 @@ GetCharacterPlacementW( for (i = 0; i < nSet; i++) { if (GetCharWidth32W(hdc, lpString[i], lpString[i], &c)) - lpResults->lpDx[i]= c; + { + lpResults->lpDx[i] = c; + if (dwFlags & GCP_USEKERNING) + lpResults->lpDx[i] += kern[i]; + } } }
@@ -3374,16 +3438,23 @@ GetCharacterPlacementW( int pos = 0;
lpResults->lpCaretPos[0] = 0; - for (i = 1; i < nSet; i++) - if (GetTextExtentPoint32W(hdc, &(lpString[i - 1]), 1, &size)) - lpResults->lpCaretPos[i] = (pos += size.cx); + for (i = 0; i < nSet - 1; i++) + { + if (dwFlags & GCP_USEKERNING) + pos += kern[i]; + + if (GetTextExtentPoint32W(hdc, &lpString[i], 1, &size)) + lpResults->lpCaretPos[i + 1] = (pos += size.cx); + } }
- if(lpResults->lpGlyphs) + if (lpResults->lpGlyphs) GetGlyphIndicesW(hdc, lpString, nSet, lpResults->lpGlyphs, 0);
if (GetTextExtentPoint32W(hdc, lpString, uCount, &size)) - ret = MAKELONG(size.cx, size.cy); + ret = MAKELONG(size.cx + kern_total, size.cy); + + heap_free(kern);
return ret; } diff --git a/dlls/gdi32/tests/font.c b/dlls/gdi32/tests/font.c index ae4a643aa54..ad894dfdcad 100644 --- a/dlls/gdi32/tests/font.c +++ b/dlls/gdi32/tests/font.c @@ -7168,6 +7168,106 @@ static void test_char_width(void) ReleaseDC(NULL, dc); }
+static void test_GetCharacterPlacement_kerning(void) +{ + LOGFONTA lf; + HFONT hfont, hfont_old; + KERNINGPAIR *kp; + HDC hdc; + DWORD count, ret, i, size, width, width_kern, idx; + WCHAR str[30]; + GCP_RESULTSW result; + int kern[30], pos[30], pos_kern[30], dx[30], dx_kern[30], kern_amount; + + if (!is_font_installed("Arial")) + { + skip("Arial is not installed, skipping the test\n"); + return; + } + + hdc = GetDC(0); + + memset(&lf, 0, sizeof(lf)); + strcpy(lf.lfFaceName, "Arial"); + lf.lfHeight = 120; + hfont = CreateFontIndirectA(&lf); + ok(hfont != NULL, "CreateFontIndirect failed\n"); + + hfont_old = SelectObject(hdc, hfont); + + count = GetKerningPairsW(hdc, 0, NULL); + kp = HeapAlloc(GetProcessHeap(), 0, count * sizeof(*kp)); + + ret = GetKerningPairsW(hdc, count, kp); + ok(ret == count, "got %u, expected %u\n", ret, count); + + size = kern_amount = idx = 0; + for (i = 0; i < count; i++) + { + if (kp[i].wFirst >= 'A' && kp[i].wFirst <= 'z' && + kp[i].wSecond >= 'A' && kp[i].wSecond <= 'z') + { + str[size++] = kp[i].wFirst; + str[size++] = kp[i].wSecond; + str[size++] = 0; + kern[idx] = kp[i].iKernAmount; + idx++; + kern_amount += kp[i].iKernAmount; + if (size >= ARRAY_SIZE(str)) break; + } + } + + HeapFree(GetProcessHeap(), 0, kp); + + count = size; + + memset(&result, 0, sizeof(result)); + result.lStructSize = sizeof(result); + result.lpCaretPos = pos; + result.lpDx = dx; + result.nGlyphs = count; + ret = GetCharacterPlacementW(hdc, str, count, 0, &result, 0); + ok(ret, "GetCharacterPlacement failed\n"); + ok(result.nGlyphs == count, "got %u\n", result.nGlyphs); + width = LOWORD(ret); + + memset(&result, 0, sizeof(result)); + result.lStructSize = sizeof(result); + result.lpCaretPos = pos_kern; + result.lpDx = dx_kern; + result.nGlyphs = count; + ret = GetCharacterPlacementW(hdc, str, count, 0, &result, GCP_USEKERNING); + ok(ret, "GetCharacterPlacement failed\n"); + ok(result.nGlyphs == count, "got %u\n", result.nGlyphs); + width_kern = LOWORD(ret); + + if (width == width_kern) + { + win_skip("GCP_USEKERNING is broken on this platform\n"); + goto done; + } + + ok(width + kern_amount == width_kern, "%d + %d != %d\n", width, kern_amount, width_kern); + + kern_amount = idx = 0; + for (i = 0; i < count; i += 3, idx++) + { + ok(pos[i] + kern_amount == pos_kern[i], "%d: %d + %d != %d\n", i, pos[i], kern_amount, pos_kern[i]); + kern_amount += kern[idx]; + ok(pos[i+1] + kern_amount == pos_kern[i+1], "%d: %d + %d != %d\n", i, pos[i+1], kern_amount, pos_kern[i+1]); + ok(pos[i+2] + kern_amount == pos_kern[i+2], "%d: %d + %d != %d\n", i, pos[i+2], kern_amount, pos_kern[i+2]); + + ok(dx[i] + kern[idx] == dx_kern[i], "%d: %d + %d != %d\n", i, dx[i], kern[idx], dx_kern[i]); + ok(dx[i+1] == dx_kern[i+1], "%d: %d != %d\n", i, dx[i+1], dx_kern[i+1]); + ok(dx[i+2] == dx_kern[i+2], "%d: %d != %d\n", i, dx[i+2], dx_kern[i+2]); + } + +done: + SelectObject(hdc, hfont_old); + DeleteObject(hfont); + ReleaseDC(0, hdc); +} + START_TEST(font) { static const char *test_names[] = @@ -7236,6 +7336,7 @@ START_TEST(font) test_GetTextMetrics2("Arial", -55); test_GetTextMetrics2("Arial", -110); test_GetCharacterPlacement(); + test_GetCharacterPlacement_kerning(); test_GetCharWidthInfo(); test_CreateFontIndirect(); test_CreateFontIndirectEx();