[PATCH 7/7] gdi32: Reimplement GetCharacterPlacement on top of Uniscribe.

Sven Baars sbaars at codeweavers.com
Mon Nov 9 05:03:31 CST 2020


Signed-off-by: Sven Baars <sbaars at codeweavers.com>
---
An implementation that also returns the correct lpOrder would require
an implementation of SCRIPT_STATE.fGcpClusters. But this implementation already
seems to be less broken than the previous one.

 dlls/gdi32/font.c       | 218 +++++++++++++++++++++++++++++-----------
 dlls/gdi32/tests/font.c |   8 +-
 2 files changed, 167 insertions(+), 59 deletions(-)

diff --git a/dlls/gdi32/font.c b/dlls/gdi32/font.c
index f0616b6f908..4f4e485220b 100644
--- a/dlls/gdi32/font.c
+++ b/dlls/gdi32/font.c
@@ -31,6 +31,7 @@
 #include "winnls.h"
 #include "winternl.h"
 #include "winreg.h"
+#include "usp10.h"
 #include "gdi_private.h"
 #include "resource.h"
 #include "wine/exception.h"
@@ -4046,6 +4047,89 @@ static void FONT_NewTextMetricExWToA(const NEWTEXTMETRICEXW *ptmW, NEWTEXTMETRIC
     memcpy(&ptmA->ntmFontSig, &ptmW->ntmFontSig, sizeof(FONTSIGNATURE));
 }
 
+/* Compute glyph positions using Uniscribe */
+static BOOL get_glyph_positions( HDC hdc, const WCHAR *string, int str_len, WORD *glyphs_in,
+                                 int *advance_in, unsigned int *max_num_glyphs )
+{
+    unsigned int i, glyph_pos = 0, max_num_items = *max_num_glyphs + 1;
+    int num_items = 0, num_glyphs, num_chars;
+    WORD *glyphs = NULL, *log_clust = NULL;
+    SCRIPT_VISATTR *psva = NULL;
+    SCRIPT_ITEM *items = NULL;
+    GOFFSET *goffset = NULL;
+    SCRIPT_CACHE sc = NULL;
+    int *advance = NULL;
+    BOOL ret = FALSE;
+    HRESULT res;
+
+    items = heap_alloc( sizeof(*items) * (max_num_items + 1) );
+    if (!items)
+        goto cleanup;
+
+    res = ScriptItemize( string, str_len, max_num_items, NULL, NULL, items, &num_items );
+    if (res)
+        WARN( "ScriptItemize failed with %x.\n", res );
+
+    if (glyphs_in)
+        glyphs = glyphs_in;
+    else
+        glyphs = heap_alloc( *max_num_glyphs * sizeof(*glyphs) );
+    if (!glyphs)
+        goto cleanup;
+
+    if (advance_in)
+        advance = advance_in;
+    else
+        advance = heap_alloc( *max_num_glyphs * sizeof(*advance) );
+    if (!advance)
+        goto cleanup;
+
+    log_clust = heap_alloc( str_len * sizeof(*log_clust) );
+    if (!log_clust)
+        goto cleanup;
+
+    goffset = heap_alloc( *max_num_glyphs * sizeof(*goffset) );
+    if (!goffset)
+        goto cleanup;
+
+    psva = heap_alloc( *max_num_glyphs * sizeof(*psva) );
+    if (!psva)
+        goto cleanup;
+
+    for (i = 0; i < num_items; ++i)
+    {
+        num_glyphs = *max_num_glyphs - glyph_pos;
+        num_chars = items[i+1].iCharPos - items[i].iCharPos;
+
+        res = ScriptShape( hdc, &sc, string + items[i].iCharPos, num_chars, num_glyphs,
+                           &items[i].a, glyphs + glyph_pos, log_clust + glyph_pos, psva, &num_glyphs );
+        if (res)
+            WARN( "ScriptShape failed with %x.\n", res );
+
+        res = ScriptPlace( hdc, &sc, glyphs + glyph_pos, num_glyphs, psva, &items[i].a,
+                           advance + glyph_pos, goffset, NULL );
+        if (res)
+            WARN( "ScriptPlace failed with %x.\n", res );
+
+        glyph_pos += num_glyphs;
+    }
+
+    *max_num_glyphs = glyph_pos;
+    ret = TRUE;
+
+cleanup:
+    heap_free( items );
+    if (!glyphs_in)
+        heap_free( glyphs );
+    if (!advance_in)
+        heap_free( advance );
+    heap_free( log_clust );
+    heap_free( goffset );
+    heap_free( psva );
+
+    return ret;
+}
+
 /* compute positions for text rendering, in device coords */
 static BOOL get_char_positions( DC *dc, const WCHAR *str, INT count, INT *dx, SIZE *size )
 {
@@ -7252,32 +7336,32 @@ GetCharacterPlacementW(
         DWORD dwFlags               /* [in] Flags specifying how to process the string */
         )
 {
-    DWORD ret=0;
+    int *kern = NULL, *advance = NULL, kern_total = 0;
+    unsigned int i, nSet, pos = 0;
+    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);
+    TRACE( "%s, %d, %d, 0x%08x\n",
+           debugstr_wn( lpString, uCount ), uCount, nMaxExtent, dwFlags );
 
     if (!uCount)
         return 0;
 
     if (!lpResults)
-        return GetTextExtentPoint32W(hdc, lpString, uCount, &size) ? MAKELONG(size.cx, size.cy) : 0;
+        return GetTextExtentPoint32W( hdc, lpString, uCount, &size ) ? MAKELONG( size.cx, size.cy ) : 0;
 
-    TRACE("lStructSize=%d, lpOutString=%p, lpOrder=%p, lpDx=%p, lpCaretPos=%p\n"
-          "lpClass=%p, lpGlyphs=%p, nGlyphs=%u, nMaxFit=%d\n",
-          lpResults->lStructSize, lpResults->lpOutString, lpResults->lpOrder,
-          lpResults->lpDx, lpResults->lpCaretPos, lpResults->lpClass,
-          lpResults->lpGlyphs, lpResults->nGlyphs, lpResults->nMaxFit);
+    TRACE( "lStructSize=%d, lpOutString=%p, lpOrder=%p, lpDx=%p, lpCaretPos=%p\n"
+           "lpClass=%p, lpGlyphs=%p, nGlyphs=%u, nMaxFit=%d\n",
+           lpResults->lStructSize, lpResults->lpOutString, lpResults->lpOrder,
+           lpResults->lpDx, lpResults->lpCaretPos, lpResults->lpClass,
+           lpResults->lpGlyphs, lpResults->nGlyphs, lpResults->nMaxFit );
 
     if (dwFlags & ~(GCP_REORDER | GCP_USEKERNING))
-        FIXME("flags 0x%08x ignored\n", dwFlags);
+        FIXME( "flags 0x%08x ignored\n", dwFlags );
     if (lpResults->lpClass)
-        FIXME("classes not implemented\n");
+        FIXME( "classes not implemented\n" );
     if (lpResults->lpCaretPos && (dwFlags & GCP_REORDER))
-        FIXME("Caret positions for complex scripts not implemented\n");
+        FIXME( "Caret positions for complex scripts not implemented\n" );
 
     nSet = (UINT)uCount;
     if (nSet > lpResults->nGlyphs)
@@ -7286,72 +7370,94 @@ GetCharacterPlacementW(
     /* return number of initialized fields */
     lpResults->nGlyphs = nSet;
 
-    if (!(dwFlags & GCP_REORDER))
+    if (dwFlags & GCP_USEKERNING)
     {
-        /* 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)
-            memcpy( lpResults->lpOutString, lpString, nSet * sizeof(WCHAR));
-
-        if (lpResults->lpOrder)
+        kern = kern_string( hdc, lpString, nSet, &kern_total );
+        if (!kern)
         {
-            for (i = 0; i < nSet; i++)
-                lpResults->lpOrder[i] = i;
+            SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+            return 0;
         }
     }
-    else
-    {
-        BIDI_Reorder(NULL, lpString, uCount, dwFlags, WINE_GCPW_FORCE_LTR, lpResults->lpOutString,
-                     nSet, lpResults->lpOrder, NULL, NULL );
-    }
 
-    if (dwFlags & GCP_USEKERNING)
+    if (!(dwFlags & GCP_REORDER))
     {
-        kern = kern_string(hdc, lpString, nSet, &kern_total);
-        if (!kern)
+        if (lpResults->lpDx)
+            advance = lpResults->lpDx;
+        else if (lpResults->lpCaretPos)
+            advance = heap_alloc( lpResults->nGlyphs * sizeof(*advance) );
+
+        if (!get_glyph_positions( hdc, lpString, nSet, lpResults->lpGlyphs, advance, &lpResults->nGlyphs ))
         {
-            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+            if (!lpResults->lpDx)
+                heap_free( advance );
             return 0;
         }
-    }
 
-    /* FIXME: Will use the placement chars */
-    if (lpResults->lpDx)
-    {
-        int c;
-        for (i = 0; i < nSet; i++)
+        if (lpResults->lpOutString)
+            memcpy( lpResults->lpOutString, lpString, nSet * sizeof(WCHAR) );
+
+        if (lpResults->lpCaretPos)
         {
-            if (GetCharWidth32W(hdc, lpString[i], lpString[i], &c))
+            for (i = 0; i < lpResults->nGlyphs; i++)
             {
-                lpResults->lpDx[i] = c;
+
+                lpResults->lpCaretPos[i] = pos;
+                pos += advance[i];
+
                 if (dwFlags & GCP_USEKERNING)
-                    lpResults->lpDx[i] += kern[i];
+                    pos += kern[i];
             }
         }
-    }
 
-    if (lpResults->lpCaretPos && !(dwFlags & GCP_REORDER))
+        if (lpResults->lpOrder)
+        {
+            if (lpResults->lpGlyphs)
+                for(i = 0; i < lpResults->nGlyphs; i++)
+                    lpResults->lpOrder[i] = i;
+            else
+                for(i = 0; i < nSet; i++)
+                    lpResults->lpOrder[i] = i;
+        }
+
+        if (!lpResults->lpDx)
+            heap_free( advance );
+    }
+    else
     {
-        int pos = 0;
+        BIDI_Reorder( NULL, lpString, uCount, dwFlags, WINE_GCPW_FORCE_LTR, lpResults->lpOutString,
+                      nSet, lpResults->lpOrder, NULL, NULL );
 
-        lpResults->lpCaretPos[0] = 0;
-        for (i = 0; i < nSet - 1; i++)
+        if (lpResults->lpDx)
         {
-            if (dwFlags & GCP_USEKERNING)
-                pos += kern[i];
-
-            if (GetTextExtentPoint32W(hdc, &lpString[i], 1, &size))
-                lpResults->lpCaretPos[i + 1] = (pos += size.cx);
+            int c;
+            for (i = 0; i < nSet; i++)
+            {
+                if (GetCharWidth32W( hdc, lpString[i], lpString[i], &c ))
+                    lpResults->lpDx[i] = c;
+            }
         }
+
+        if(lpResults->lpGlyphs)
+            GetGlyphIndicesW( hdc, lpString, nSet, lpResults->lpGlyphs, 0 );
+
     }
 
-    if (lpResults->lpGlyphs)
-        GetGlyphIndicesW(hdc, lpString, nSet, lpResults->lpGlyphs, 0);
+    if (lpResults->lpDx && (dwFlags & GCP_USEKERNING))
+    {
+        if (lpResults->nGlyphs != nSet)
+            FIXME( "Kerning not supported with the number of glyphs unequal to the number of input characters\n" );
+        else
+        {
+            for (i = 0; i < lpResults->nGlyphs; i++)
+                lpResults->lpDx[i] += kern[i];
+        }
+    }
 
-    if (GetTextExtentPoint32W(hdc, lpString, uCount, &size))
-        ret = MAKELONG(size.cx + kern_total, size.cy);
+    if (GetTextExtentPoint32W( hdc, lpString, uCount, &size ))
+        ret = MAKELONG( size.cx + kern_total, size.cy );
 
-    heap_free(kern);
+    heap_free( kern );
 
     return ret;
 }
diff --git a/dlls/gdi32/tests/font.c b/dlls/gdi32/tests/font.c
index 62eaab8639c..ea2ae4c523f 100644
--- a/dlls/gdi32/tests/font.c
+++ b/dlls/gdi32/tests/font.c
@@ -5022,7 +5022,8 @@ static inline void _test_character_placement_ok(int valid, HDC hdc, const WCHAR
     size = GetCharacterPlacementW(hdc, string, str_len, 0, &result, 0);
     winetest_ok(size, "GetCharacterPlacementW failed.\n");
     if (valid > 0)
-        winetest_ok(result.nGlyphs == expected_len, "Expected %d, got %d.\n", expected_len, result.nGlyphs);
+        todo_wine_if(expected[expected_len - 1].order != expected_len - 1)
+            winetest_ok(result.nGlyphs == expected_len, "Expected %d, got %d.\n", expected_len, result.nGlyphs);
     else if (result.nGlyphs != expected_len)
         winetest_trace("Expected %d, got %d.\n", expected_len, result.nGlyphs);
     for (i = 0; i < result.nGlyphs; ++i)
@@ -5053,7 +5054,8 @@ static inline void _test_character_placement_ok(int valid, HDC hdc, const WCHAR
         size = GetCharacterPlacementW(hdc, string, str_len, 0, &result, GCP_REORDER);
         winetest_ok(size, "GetCharacterPlacementW failed with GCP_REORDER.\n");
         if (valid > 0)
-            winetest_ok(result.nGlyphs == expected_len, "Expected %d with GCP_REORDER, got %d.\n", expected_len, result.nGlyphs);
+            todo_wine_if(expected[expected_len - 1].order != expected_len - 1)
+                winetest_ok(result.nGlyphs == expected_len, "Expected %d with GCP_REORDER, got %d.\n", expected_len, result.nGlyphs);
         else if (result.nGlyphs != expected_len)
             winetest_trace("Expected %d with GCP_REORDER, got %d.\n", expected_len, result.nGlyphs);
         for (i = 0; i < result.nGlyphs; ++i)
@@ -8164,7 +8166,7 @@ static void test_zero_width_control(void)
         size = GetCharacterPlacementW(hdc, test, 10, 0, &result, 0);
         ok(size, "Test %d: GetCharacterPlacementA failed.\n", i);
         ok(result.nGlyphs == 10, "Test %d: unexpected number of glyphs %u.\n", i, result.nGlyphs);
-        todo_wine ok(glyphs[5] == glyphs[4], "Test %d: unexpected glyphs %s.\n", i, wine_dbgstr_wn(glyphs, result.nGlyphs));
+        ok(glyphs[5] == glyphs[4], "Test %d: unexpected glyphs %s.\n", i, wine_dbgstr_wn(glyphs, result.nGlyphs));
         if (i < 15)
             ok(pos[6] - pos[5] == 0, "Test %d: unexpected width %d.\n", i, pos[6] - pos[5]);
         else
-- 
2.25.1




More information about the wine-devel mailing list