v2: Add tests.
Signed-off-by: Dmitry Timoshkov <dmitry(a)baikal.ru>
---
dlls/gdi32/font.c | 97 ++++++++++++++++++++++++++++++++-----
dlls/gdi32/tests/font.c | 103 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 187 insertions(+), 13 deletions(-)
diff --git a/dlls/gdi32/font.c b/dlls/gdi32/font.c
index 8788426a2c..e099bec5e8 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 ae4a643aa5..3be9a2ae79 100644
--- a/dlls/gdi32/tests/font.c
+++ b/dlls/gdi32/tests/font.c
@@ -7168,6 +7168,108 @@ 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;
+ trace("kern[%d] = %d\n", idx, kern[idx]);
+ idx++;
+ kern_amount += kp[i].iKernAmount;
+ if (size >= ARRAY_SIZE(str)) break;
+ }
+ }
+
+ HeapFree(GetProcessHeap(), 0, kp);
+
+ count = size;
+ trace("str: %s, kern_amount = %d\n", wine_dbgstr_wn(str, count), kern_amount);
+
+ 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 +7338,7 @@ START_TEST(font)
test_GetTextMetrics2("Arial", -55);
test_GetTextMetrics2("Arial", -110);
test_GetCharacterPlacement();
+ test_GetCharacterPlacement_kerning();
test_GetCharWidthInfo();
test_CreateFontIndirect();
test_CreateFontIndirectEx();
--
2.26.2