From: Sven Baars Subject: [PATCH v2 7/7] gdi32: Reimplement GetCharacterPlacement on top of Uniscribe. Message-Id: <20201109140720.472712-8-sbaars@codeweavers.com> Date: Mon, 9 Nov 2020 15:07:20 +0100 In-Reply-To: <20201109140720.472712-1-sbaars@codeweavers.com> References: <20201109140720.472712-1-sbaars@codeweavers.com> Signed-off-by: Sven Baars --- 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 f08b42ff66e..bd7339ea209 100644 --- a/dlls/gdi32/tests/font.c +++ b/dlls/gdi32/tests/font.c @@ -5023,7 +5023,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) @@ -5056,7 +5057,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) @@ -8167,7 +8169,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