Dmitry Timoshkov : gdi32: Add support for GCP_USEKERNING to GetCharacterPlacement().

Alexandre Julliard julliard at winehq.org
Thu Jul 9 17:10:39 CDT 2020


Module: wine
Branch: master
Commit: cb511b82bd55245b55ad208afccf59088f6d6bf1
URL:    https://source.winehq.org/git/wine.git/?a=commit;h=cb511b82bd55245b55ad208afccf59088f6d6bf1

Author: Dmitry Timoshkov <dmitry at baikal.ru>
Date:   Thu Jul  9 15:20:51 2020 +0100

gdi32: Add support for GCP_USEKERNING to GetCharacterPlacement().

Signed-off-by: Dmitry Timoshkov <dmitry at baikal.ru>
Signed-off-by: Huw Davies <huw at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 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 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..ad894dfdca 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();




More information about the wine-cvs mailing list