Signed-off-by: Sven Baars sbaars@codeweavers.com --- ../dlls/gdi32/tests/font.c:1361:19: warning: ‘code’ may be used uninitialized in this function [-Wmaybe-uninitialized] 1361 | ret = GetCharABCWidthsA(hdc, code, code, abc); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
dlls/gdi32/tests/font.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/dlls/gdi32/tests/font.c b/dlls/gdi32/tests/font.c index 4c8b1cbb91b..d3ec971dce0 100644 --- a/dlls/gdi32/tests/font.c +++ b/dlls/gdi32/tests/font.c @@ -1330,17 +1330,16 @@ static void test_GetCharABCWidths(void)
memset(&lf, 0, sizeof(lf)); lf.lfHeight = 20; - switch(i) + if (i == 1) { - case 1: strcpy(lf.lfFaceName, "Tahoma"); code = 'a'; - break; - case 2: + } + else + { strcpy(lf.lfFaceName, "Times New Roman"); lf.lfItalic = TRUE; code = 'f'; - break; } if (!is_truetype_font_installed(lf.lfFaceName)) {
Signed-off-by: Sven Baars sbaars@codeweavers.com --- dlls/gdi32/tests/font.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
diff --git a/dlls/gdi32/tests/font.c b/dlls/gdi32/tests/font.c index d3ec971dce0..2592db2cc06 100644 --- a/dlls/gdi32/tests/font.c +++ b/dlls/gdi32/tests/font.c @@ -1505,17 +1505,33 @@ static void test_text_extents(void) ok(extents[i-1] <= extents[i], "GetTextExtentExPointW generated a non-increasing sequence of partial extents (at position %d)\n", i); + todo_wine ok(extents[2] == extents[3], "GetTextExtentExPointW doesn't return 0 width for a newline\n"); ok(extents[len-1] == sz1.cx, "GetTextExtentExPointW extents and size don't match\n"); ok(0 <= fit1 && fit1 <= len, "GetTextExtentExPointW generated illegal value %d for fit\n", fit1); ok(0 < fit1, "GetTextExtentExPointW says we can't even fit one letter in 32767 logical units\n"); + GetTextExtentExPointW(hdc, wt, len, extents[2], &fit2, NULL, &sz2); ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy, "GetTextExtentExPointW returned different sizes for the same string\n"); ok(fit2 == 3, "GetTextExtentExPointW extents isn't consistent with fit\n"); + + extents2[2] = -1; + GetTextExtentExPointW(hdc, wt, len, extents[2], &fit2, extents2, &sz2); + ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy, "GetTextExtentExPointW returned different sizes for the same string\n"); + ok(fit2 == 3, "GetTextExtentExPointW extents isn't consistent with fit\n"); + ok(extents2[2] == extents[2], "GetTextExtentExPointW last extent isn't set\n"); + GetTextExtentExPointW(hdc, wt, len, extents[2]-1, &fit2, NULL, &sz2); ok(fit2 == 2, "GetTextExtentExPointW extents isn't consistent with fit\n"); + + extents2[1] = -1; + GetTextExtentExPointW(hdc, wt, len, extents[2]-1, &fit2, extents2, &sz2); + ok(fit2 == 2, "GetTextExtentExPointW extents isn't consistent with fit\n"); + ok(extents2[1] == extents[1], "GetTextExtentExPointW extent isn't set\n"); + GetTextExtentExPointW(hdc, wt, 2, 0, NULL, extents + 2, &sz2); ok(extents[0] == extents[2] && extents[1] == extents[3], "GetTextExtentExPointW with lpnFit == NULL returns incorrect results\n"); + GetTextExtentExPointW(hdc, wt, 2, 0, NULL, NULL, &sz1); ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy, "GetTextExtentExPointW with lpnFit and alpDx both NULL returns incorrect results\n");
Signed-off-by: Sven Baars sbaars@codeweavers.com --- dlls/gdi32/font.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/dlls/gdi32/font.c b/dlls/gdi32/font.c index 6ad03df176b..4a6ca1916ac 100644 --- a/dlls/gdi32/font.c +++ b/dlls/gdi32/font.c @@ -4897,6 +4897,11 @@ BOOL WINAPI GetTextExtentExPointI( HDC hdc, const WORD *indices, INT count, INT unsigned int dx = abs( INTERNAL_XDSTOWS( dc, pos[i] )) + (i + 1) * dc->charExtra; if (nfit && dx > (unsigned int)max_ext) break; if (dxs) dxs[i] = dx; + if (nfit && dx == (unsigned int)max_ext) + { + i++; + break; + } } if (nfit) *nfit = i; } @@ -5033,7 +5038,12 @@ BOOL WINAPI GetTextExtentExPointW( HDC hdc, LPCWSTR str, INT count, INT max_ext, { unsigned int dx = abs( INTERNAL_XDSTOWS( dc, pos[i] )) + (i + 1) * dc->charExtra; if (nfit && dx > (unsigned int)max_ext) break; - if (dxs) dxs[i] = dx; + if (dxs) dxs[i] = dx; + if (nfit && dx == (unsigned int)max_ext) + { + i++; + break; + } } if (nfit) *nfit = i; }
On Mon, Nov 09, 2020 at 03:07:15PM +0100, Sven Baars wrote:
Signed-off-by: Sven Baars sbaars@codeweavers.com
dlls/gdi32/font.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/dlls/gdi32/font.c b/dlls/gdi32/font.c index 6ad03df176b..4a6ca1916ac 100644 --- a/dlls/gdi32/font.c +++ b/dlls/gdi32/font.c @@ -4897,6 +4897,11 @@ BOOL WINAPI GetTextExtentExPointI( HDC hdc, const WORD *indices, INT count, INT unsigned int dx = abs( INTERNAL_XDSTOWS( dc, pos[i] )) + (i + 1) * dc->charExtra; if (nfit && dx > (unsigned int)max_ext) break; if (dxs) dxs[i] = dx;
if (nfit && dx == (unsigned int)max_ext)
{
i++;
break;
} } if (nfit) *nfit = i; }
@@ -5033,7 +5038,12 @@ BOOL WINAPI GetTextExtentExPointW( HDC hdc, LPCWSTR str, INT count, INT max_ext, { unsigned int dx = abs( INTERNAL_XDSTOWS( dc, pos[i] )) + (i + 1) * dc->charExtra; if (nfit && dx > (unsigned int)max_ext) break;
if (dxs) dxs[i] = dx;
if (dxs) dxs[i] = dx;
if (nfit && dx == (unsigned int)max_ext)
{
i++;
break;
} } if (nfit) *nfit = i; }
These will need tests. Also, combining the new if()s with the test two lines above would most likely make things simpler.
Huw.
On 12-11-2020 10:25, Huw Davies wrote:
On Mon, Nov 09, 2020 at 03:07:15PM +0100, Sven Baars wrote:
Signed-off-by: Sven Baars sbaars@codeweavers.com
dlls/gdi32/font.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/dlls/gdi32/font.c b/dlls/gdi32/font.c index 6ad03df176b..4a6ca1916ac 100644 --- a/dlls/gdi32/font.c +++ b/dlls/gdi32/font.c @@ -4897,6 +4897,11 @@ BOOL WINAPI GetTextExtentExPointI( HDC hdc, const WORD *indices, INT count, INT unsigned int dx = abs( INTERNAL_XDSTOWS( dc, pos[i] )) + (i + 1) * dc->charExtra; if (nfit && dx > (unsigned int)max_ext) break; if (dxs) dxs[i] = dx;
if (nfit && dx == (unsigned int)max_ext)
{
i++;
break;
} } if (nfit) *nfit = i; }
@@ -5033,7 +5038,12 @@ BOOL WINAPI GetTextExtentExPointW( HDC hdc, LPCWSTR str, INT count, INT max_ext, { unsigned int dx = abs( INTERNAL_XDSTOWS( dc, pos[i] )) + (i + 1) * dc->charExtra; if (nfit && dx > (unsigned int)max_ext) break;
if (dxs) dxs[i] = dx;
if (dxs) dxs[i] = dx;
if (nfit && dx == (unsigned int)max_ext)
{
i++;
break;
} } if (nfit) *nfit = i; }
These will need tests. Also, combining the new if()s with the test two lines above would most likely make things simpler.
Huw.
Hi Huw,
The tests are already present in
https://source.winehq.org/git/wine.git/blob/HEAD:/dlls/gdi32/tests/font.c#l1...
and I further extended these in patch 2. They currently wrongfully succeed because the characters that are supposed to have width 0 do not have width 0. They would start failing after patch 5 without this patch. If this is not sufficient, are there any characters with width 0 that are currently reported correctly to have width 0 by wine that can be used for this test?
As for the if statement, it would look like this:
if (dxs) dxs[i] = dx; if (nfit && dx >= (unsigned int)max_ext) { if (dx == (unsigned int)max_ext) i++; break; }
however, that would mean that dx is also set in the > case, which is not the case on Windows. I could add a test for this, but that will currently fail on wine because of the earlier call to get_char_positions(), so it will not detect a wrong implementation of this if statement.
Cheers, Sven
Signed-off-by: Sven Baars sbaars@codeweavers.com --- v2: Fixed test failures.
dlls/gdi32/tests/font.c | 134 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+)
diff --git a/dlls/gdi32/tests/font.c b/dlls/gdi32/tests/font.c index 2592db2cc06..8985476ed82 100644 --- a/dlls/gdi32/tests/font.c +++ b/dlls/gdi32/tests/font.c @@ -7666,6 +7666,139 @@ done: ReleaseDC(0, hdc); }
+static void test_zero_width_control(void) +{ + int len, dx, nfit, pos[20]; + DWORD size, charcount; + GCP_RESULTSW result; + WCHAR space_glyph; + WCHAR glyphs[20]; + unsigned int i; + LOGFONTA lf; + HFONT hfont; + BOOL ret; + SIZE sz; + HDC hdc; + ABC abc; + + static const WCHAR nul[] = {0}; + static const WCHAR space[] = {' '}; + static const WCHAR zero_width_control[] = { + 0x0009, /* \t */ + 0x000a, /* \n */ + 0x000d, /* \r */ + 0x001c, /* FS */ + 0x001d, /* GS */ + 0x001e, /* RS */ + 0x001f, /* US */ + 0x200b, /* ZWSP */ + 0x200c, /* ZWNJ */ + 0x200d, /* ZWJ */ + 0x200e, /* LRM */ + 0x200f, /* RLM */ + 0x202a, /* LRE */ + 0x202c, /* PDF */ + 0x202d, /* LRO */ + 0x202b, /* RLE */ + 0x202e, /* RLO */ + }; + + memset(&lf, 0, sizeof(lf)); + strcpy(lf.lfFaceName, "Tahoma"); + lf.lfHeight = 20; + + hfont = CreateFontIndirectA(&lf); + hdc = GetDC(NULL); + hfont = SelectObject(hdc, hfont); + + memset(&result, 0, sizeof(result)); + result.lStructSize = sizeof(result); + result.lpCaretPos = pos; + result.lpGlyphs = glyphs; + + for (i = 0; i < ARRAY_SIZE(zero_width_control); ++i) + { + WCHAR test[] = {'W', 'i', 'n', 'e', ' ', zero_width_control[i], 'T', 'e', 's', 't', 0}; + + /* Some control characters are treated as space */ + result.nGlyphs = 20; + 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)); + if (i < 15) + todo_wine ok(pos[6] - pos[5] == 0, "Test %d: unexpected width %d.\n", i, pos[6] - pos[5]); + else + ok(pos[6] - pos[5] > 0, "Test %d: unexpected width %d.\n", i, pos[6] - pos[5]); + ok(pos[5] - pos[4] > 0, "Test %d: unexpected width %d.\n", i, pos[5] - pos[4]); + + /* They all have zero width in GetTextExtentExPoint */ + nfit = -1; + dx = -1; + sz.cx = -1; + ret = GetTextExtentExPointW(hdc, zero_width_control + i, 1, 1000, &nfit, &dx, &sz); + ok(ret, "Test %d: expected TRUE.\n", i); + ok(nfit == 1, "Test %d: got %d.\n", i, nfit); + todo_wine ok(dx == 0, "Test %d: got %d.\n", i, dx); + todo_wine ok(sz.cx == 0, "Test %d: got %d.\n", i, sz.cx); + + /* They do not all have zero width in GetCharWidth32 */ + len = -1; + ret = GetCharWidth32W(hdc, zero_width_control[i], zero_width_control[i], &len); + ok(ret, "Test %d: expected TRUE.\n", i); + + /* Because ABC does not have zero width */ + memset(&abc, 0, sizeof(abc)); + ret = GetCharABCWidthsW(hdc, zero_width_control[i], zero_width_control[i], &abc); + ok(ret, "Test %d: expected TRUE.\n", i); + + if (i < 7) + { + ok(len > 0, "Test %d: got %d.\n", i, len); + ok(abc.abcA + abc.abcB + abc.abcC <= len && abc.abcA + abc.abcB + abc.abcC > 0, + "Test %d: expected %d >= %d > 0.\n", i, len, abc.abcA + abc.abcB + abc.abcC); + ok(abc.abcB > 0, "Test %d: got %d.\n", i, abc.abcB); + } + else + { + todo_wine ok(len == 0 || broken(i > 11) /* before Win10 */, "Test %d: got %d.\n", i, len); + ok(abc.abcA + abc.abcB + abc.abcC == len, + "Test %d: expected %d == 0.\n", i, abc.abcA + abc.abcB + abc.abcC); + ok(abc.abcB > 0, "Test %d: got %d.\n", i, abc.abcB); + } + } + + /* The NUL character does not have zero width */ + nfit = -1; + dx = -1; + sz.cx = -1; + ret = GetTextExtentExPointW(hdc, nul, 1, 1000, &nfit, &dx, &sz); + ok(ret, "Expected TRUE.\n"); + ok(nfit == 1, "Got %d.\n", nfit); + ok(dx > 0, "Got %d.\n", dx); + ok(sz.cx > 0, "Got %d.\n", sz.cx); + + memset(&abc, 0, sizeof(abc)); + ret = GetCharABCWidthsW(hdc, 0, 0, &abc); + ok(ret, "Expected TRUE.\n"); + todo_wine ok(abc.abcA == 0, "Got %d.\n", abc.abcA); + ok(abc.abcB > 0, "Got %d.\n", abc.abcB); + ok(abc.abcC >= 0, "Got %d.\n", abc.abcC); + + /* Test that characters that are treated as spaces by GetCharacterPlacement */ + /* are not treated like spaces by GetGlyphIndices. */ + charcount = GetGlyphIndicesW(hdc, space, 1, &space_glyph, 0); + ok(charcount == 1, "Wrong character count %d.\n", charcount); + + charcount = GetGlyphIndicesW(hdc, zero_width_control, ARRAY_SIZE(zero_width_control), glyphs, 0); + ok(charcount == ARRAY_SIZE(zero_width_control), "Wrong character count %d.\n", charcount); + for (i = 0; i < ARRAY_SIZE(zero_width_control); ++i) + ok(glyphs[i] != space_glyph, "Test %d: glyph is a space: %04x\n", i, glyphs[i]); + + DeleteObject(hfont); + ReleaseDC(NULL, hdc); +} + START_TEST(font) { static const char *test_names[] = @@ -7752,6 +7885,7 @@ START_TEST(font) test_ttf_names(); test_lang_names(); test_char_width(); + test_zero_width_control();
/* These tests should be last test until RemoveFontResource * is properly implemented.
Signed-off-by: Sven Baars sbaars@codeweavers.com --- dlls/d3dx9_36/tests/core.c | 2 +- dlls/gdi32/font.c | 16 ++++++++++++++++ dlls/gdi32/tests/font.c | 16 +++++++++------- 3 files changed, 26 insertions(+), 8 deletions(-)
diff --git a/dlls/d3dx9_36/tests/core.c b/dlls/d3dx9_36/tests/core.c index 020f18e622c..14629ce9eb6 100644 --- a/dlls/d3dx9_36/tests/core.c +++ b/dlls/d3dx9_36/tests/core.c @@ -807,7 +807,7 @@ static void test_ID3DXFont(IDirect3DDevice9 *device) todo_wine ok(height == 0, "Got unexpected height %d.\n", height);
height = ID3DXFont_DrawTextW(font, NULL, L"\t\t\t\t\t\t\t\t\t\ta", -1, &rect, DT_WORDBREAK, 0xff00ff); - todo_wine ok(height == 12, "Got unexpected height %d.\n", height); + ok(height == 12, "Got unexpected height %d.\n", height);
height = ID3DXFont_DrawTextW(font, NULL, L"\taaaaaaaaaa", -1, &rect, DT_WORDBREAK, 0xff00ff); todo_wine ok(height == 24, "Got unexpected height %d.\n", height); diff --git a/dlls/gdi32/font.c b/dlls/gdi32/font.c index 4a6ca1916ac..f0616b6f908 100644 --- a/dlls/gdi32/font.c +++ b/dlls/gdi32/font.c @@ -2886,6 +2886,16 @@ static UINT get_glyph_index_linked( struct gdi_font **font, UINT glyph ) return 0; }
+static BOOL is_zero_width_control_char( UINT glyph ) +{ + return glyph == 0x0009 /* \t */ || glyph == 0x000a /* \n */ || glyph == 0x000d /* \r */ || + glyph == 0x001c /* FS */ || glyph == 0x001d /* GS */ || glyph == 0x001e /* RS */ || + glyph == 0x001f /* US */ || glyph == 0x200b /* ZWSP */ || glyph == 0x200c /* ZWNJ */ || + glyph == 0x200d /* ZWJ */ || glyph == 0x200e /* LRM */ || glyph == 0x200f /* RLM */ || + glyph == 0x202a /* LRE */ || glyph == 0x202b /* RLE */ || glyph == 0x202c /* PDF */ || + glyph == 0x202d /* LRO */ || glyph == 0x202e /* RLO */; +} + static DWORD get_glyph_outline( struct gdi_font *font, UINT glyph, UINT format, GLYPHMETRICS *gm_ret, ABC *abc_ret, DWORD buflen, void *buf, const MAT2 *mat ) @@ -2908,6 +2918,12 @@ static DWORD get_glyph_outline( struct gdi_font *font, UINT glyph, UINT format, else { index = get_glyph_index_linked( &font, glyph ); + if (glyph && !index && is_zero_width_control_char( glyph )) + { + memset( abc_ret, 0, sizeof(*abc_ret) ); + return GDI_ERROR; + } + if (tategaki) { UINT orig = index; diff --git a/dlls/gdi32/tests/font.c b/dlls/gdi32/tests/font.c index 8985476ed82..5b5efc88743 100644 --- a/dlls/gdi32/tests/font.c +++ b/dlls/gdi32/tests/font.c @@ -1505,7 +1505,7 @@ static void test_text_extents(void) ok(extents[i-1] <= extents[i], "GetTextExtentExPointW generated a non-increasing sequence of partial extents (at position %d)\n", i); - todo_wine ok(extents[2] == extents[3], "GetTextExtentExPointW doesn't return 0 width for a newline\n"); + ok(extents[2] == extents[3], "GetTextExtentExPointW doesn't return 0 width for a newline\n"); ok(extents[len-1] == sz1.cx, "GetTextExtentExPointW extents and size don't match\n"); ok(0 <= fit1 && fit1 <= len, "GetTextExtentExPointW generated illegal value %d for fit\n", fit1); ok(0 < fit1, "GetTextExtentExPointW says we can't even fit one letter in 32767 logical units\n"); @@ -7727,9 +7727,9 @@ static void test_zero_width_control(void) 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)); if (i < 15) - todo_wine ok(pos[6] - pos[5] == 0, "Test %d: unexpected width %d.\n", i, pos[6] - pos[5]); + ok(pos[6] - pos[5] == 0, "Test %d: unexpected width %d.\n", i, pos[6] - pos[5]); else - ok(pos[6] - pos[5] > 0, "Test %d: unexpected width %d.\n", i, pos[6] - pos[5]); + todo_wine ok(pos[6] - pos[5] > 0, "Test %d: unexpected width %d.\n", i, pos[6] - pos[5]); ok(pos[5] - pos[4] > 0, "Test %d: unexpected width %d.\n", i, pos[5] - pos[4]);
/* They all have zero width in GetTextExtentExPoint */ @@ -7739,8 +7739,8 @@ static void test_zero_width_control(void) ret = GetTextExtentExPointW(hdc, zero_width_control + i, 1, 1000, &nfit, &dx, &sz); ok(ret, "Test %d: expected TRUE.\n", i); ok(nfit == 1, "Test %d: got %d.\n", i, nfit); - todo_wine ok(dx == 0, "Test %d: got %d.\n", i, dx); - todo_wine ok(sz.cx == 0, "Test %d: got %d.\n", i, sz.cx); + ok(dx == 0, "Test %d: got %d.\n", i, dx); + ok(sz.cx == 0, "Test %d: got %d.\n", i, sz.cx);
/* They do not all have zero width in GetCharWidth32 */ len = -1; @@ -7754,17 +7754,19 @@ static void test_zero_width_control(void)
if (i < 7) { + todo_wine { ok(len > 0, "Test %d: got %d.\n", i, len); ok(abc.abcA + abc.abcB + abc.abcC <= len && abc.abcA + abc.abcB + abc.abcC > 0, "Test %d: expected %d >= %d > 0.\n", i, len, abc.abcA + abc.abcB + abc.abcC); ok(abc.abcB > 0, "Test %d: got %d.\n", i, abc.abcB); + } } else { - todo_wine ok(len == 0 || broken(i > 11) /* before Win10 */, "Test %d: got %d.\n", i, len); + ok(len == 0 || broken(i > 11) /* before Win10 */, "Test %d: got %d.\n", i, len); ok(abc.abcA + abc.abcB + abc.abcC == len, "Test %d: expected %d == 0.\n", i, abc.abcA + abc.abcB + abc.abcC); - ok(abc.abcB > 0, "Test %d: got %d.\n", i, abc.abcB); + todo_wine ok(abc.abcB > 0, "Test %d: got %d.\n", i, abc.abcB); } }
On Mon, Nov 09, 2020 at 03:07:17PM +0100, Sven Baars wrote:
{
todo_wine { ok(len > 0, "Test %d: got %d.\n", i, len); ok(abc.abcA + abc.abcB + abc.abcC <= len && abc.abcA + abc.abcB + abc.abcC > 0, "Test %d: expected %d >= %d > 0.\n", i, len, abc.abcA + abc.abcB + abc.abcC); ok(abc.abcB > 0, "Test %d: got %d.\n", i, abc.abcB);
} }
I haven't looked at this in any detail, but if your changes require adding todo_wine blocks that's going to require a lot of justification.
Huw.
On 12-11-2020 10:38, Huw Davies wrote:
On Mon, Nov 09, 2020 at 03:07:17PM +0100, Sven Baars wrote:
{
todo_wine { ok(len > 0, "Test %d: got %d.\n", i, len); ok(abc.abcA + abc.abcB + abc.abcC <= len && abc.abcA + abc.abcB + abc.abcC > 0, "Test %d: expected %d >= %d > 0.\n", i, len, abc.abcA + abc.abcB + abc.abcC); ok(abc.abcB > 0, "Test %d: got %d.\n", i, abc.abcB);
} }
I haven't looked at this in any detail, but if your changes require adding todo_wine blocks that's going to require a lot of justification.
Huw.
Hi Huw,
Right, I added these tests to cover everything that is affected by changes to get_glyph_outline(). The change in this patch fixes the behavior of GetCharacterPlacementW() in 15/17 cases, GetTextExtentExPointW() in 17/17 cases and GetCharWidth32W() and GetCharABCWidthsW() in 10/17 cases. In wine they all depend on the same function (get_glyph_outline()), on Windows they clearly do not. I'm not sure if it's worth it to change these functions to not all use get_glyph_outline() so we can attempt to make then behave correctly in all cases. Especially since it's so inconsistent between these functions, and apparently also across different Windows version. Note that this may be easier now after the recent restructuring of gdi32 than it was at the time when I wrote these patches.
Some of these issues can also be attributed to the different implementation of Tahoma that we have in wine, making testing this more difficult. If I use the windows one I get quite some "Test succeeded inside todo block" failures.
Cheers, Sven
Signed-off-by: Sven Baars sbaars@codeweavers.com --- dlls/d3dx9_36/tests/core.c | 2 +- dlls/gdi32/font.c | 16 ++++++++++++++++ dlls/gdi32/tests/font.c | 16 +++++++++------- 3 files changed, 26 insertions(+), 8 deletions(-)
diff --git a/dlls/d3dx9_36/tests/core.c b/dlls/d3dx9_36/tests/core.c index 020f18e622c..14629ce9eb6 100644 --- a/dlls/d3dx9_36/tests/core.c +++ b/dlls/d3dx9_36/tests/core.c @@ -807,7 +807,7 @@ static void test_ID3DXFont(IDirect3DDevice9 *device) todo_wine ok(height == 0, "Got unexpected height %d.\n", height);
height = ID3DXFont_DrawTextW(font, NULL, L"\t\t\t\t\t\t\t\t\t\ta", -1, &rect, DT_WORDBREAK, 0xff00ff); - todo_wine ok(height == 12, "Got unexpected height %d.\n", height); + ok(height == 12, "Got unexpected height %d.\n", height);
height = ID3DXFont_DrawTextW(font, NULL, L"\taaaaaaaaaa", -1, &rect, DT_WORDBREAK, 0xff00ff); todo_wine ok(height == 24, "Got unexpected height %d.\n", height); diff --git a/dlls/gdi32/font.c b/dlls/gdi32/font.c index 4a6ca1916ac..f0616b6f908 100644 --- a/dlls/gdi32/font.c +++ b/dlls/gdi32/font.c @@ -2886,6 +2886,16 @@ static UINT get_glyph_index_linked( struct gdi_font **font, UINT glyph ) return 0; }
+static BOOL is_zero_width_control_char( UINT glyph ) +{ + return glyph == 0x0009 /* \t */ || glyph == 0x000a /* \n */ || glyph == 0x000d /* \r */ || + glyph == 0x001c /* FS */ || glyph == 0x001d /* GS */ || glyph == 0x001e /* RS */ || + glyph == 0x001f /* US */ || glyph == 0x200b /* ZWSP */ || glyph == 0x200c /* ZWNJ */ || + glyph == 0x200d /* ZWJ */ || glyph == 0x200e /* LRM */ || glyph == 0x200f /* RLM */ || + glyph == 0x202a /* LRE */ || glyph == 0x202b /* RLE */ || glyph == 0x202c /* PDF */ || + glyph == 0x202d /* LRO */ || glyph == 0x202e /* RLO */; +} + static DWORD get_glyph_outline( struct gdi_font *font, UINT glyph, UINT format, GLYPHMETRICS *gm_ret, ABC *abc_ret, DWORD buflen, void *buf, const MAT2 *mat ) @@ -2908,6 +2918,12 @@ static DWORD get_glyph_outline( struct gdi_font *font, UINT glyph, UINT format, else { index = get_glyph_index_linked( &font, glyph ); + if (glyph && !index && is_zero_width_control_char( glyph )) + { + memset( abc_ret, 0, sizeof(*abc_ret) ); + return GDI_ERROR; + } + if (tategaki) { UINT orig = index; diff --git a/dlls/gdi32/tests/font.c b/dlls/gdi32/tests/font.c index 8985476ed82..5b5efc88743 100644 --- a/dlls/gdi32/tests/font.c +++ b/dlls/gdi32/tests/font.c @@ -1505,7 +1505,7 @@ static void test_text_extents(void) ok(extents[i-1] <= extents[i], "GetTextExtentExPointW generated a non-increasing sequence of partial extents (at position %d)\n", i); - todo_wine ok(extents[2] == extents[3], "GetTextExtentExPointW doesn't return 0 width for a newline\n"); + ok(extents[2] == extents[3], "GetTextExtentExPointW doesn't return 0 width for a newline\n"); ok(extents[len-1] == sz1.cx, "GetTextExtentExPointW extents and size don't match\n"); ok(0 <= fit1 && fit1 <= len, "GetTextExtentExPointW generated illegal value %d for fit\n", fit1); ok(0 < fit1, "GetTextExtentExPointW says we can't even fit one letter in 32767 logical units\n"); @@ -7727,9 +7727,9 @@ static void test_zero_width_control(void) 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)); if (i < 15) - todo_wine ok(pos[6] - pos[5] == 0, "Test %d: unexpected width %d.\n", i, pos[6] - pos[5]); + ok(pos[6] - pos[5] == 0, "Test %d: unexpected width %d.\n", i, pos[6] - pos[5]); else - ok(pos[6] - pos[5] > 0, "Test %d: unexpected width %d.\n", i, pos[6] - pos[5]); + todo_wine ok(pos[6] - pos[5] > 0, "Test %d: unexpected width %d.\n", i, pos[6] - pos[5]); ok(pos[5] - pos[4] > 0, "Test %d: unexpected width %d.\n", i, pos[5] - pos[4]);
/* They all have zero width in GetTextExtentExPoint */ @@ -7739,8 +7739,8 @@ static void test_zero_width_control(void) ret = GetTextExtentExPointW(hdc, zero_width_control + i, 1, 1000, &nfit, &dx, &sz); ok(ret, "Test %d: expected TRUE.\n", i); ok(nfit == 1, "Test %d: got %d.\n", i, nfit); - todo_wine ok(dx == 0, "Test %d: got %d.\n", i, dx); - todo_wine ok(sz.cx == 0, "Test %d: got %d.\n", i, sz.cx); + ok(dx == 0, "Test %d: got %d.\n", i, dx); + ok(sz.cx == 0, "Test %d: got %d.\n", i, sz.cx);
/* They do not all have zero width in GetCharWidth32 */ len = -1; @@ -7754,17 +7754,19 @@ static void test_zero_width_control(void)
if (i < 7) { + todo_wine { ok(len > 0, "Test %d: got %d.\n", i, len); ok(abc.abcA + abc.abcB + abc.abcC <= len && abc.abcA + abc.abcB + abc.abcC > 0, "Test %d: expected %d >= %d > 0.\n", i, len, abc.abcA + abc.abcB + abc.abcC); ok(abc.abcB > 0, "Test %d: got %d.\n", i, abc.abcB); + } } else { - todo_wine ok(len == 0 || broken(i > 11) /* before Win10 */, "Test %d: got %d.\n", i, len); + ok(len == 0 || broken(i > 11) /* before Win10 */, "Test %d: got %d.\n", i, len); ok(abc.abcA + abc.abcB + abc.abcC == len, "Test %d: expected %d == 0.\n", i, abc.abcA + abc.abcB + abc.abcC); - ok(abc.abcB > 0, "Test %d: got %d.\n", i, abc.abcB); + todo_wine ok(abc.abcB > 0, "Test %d: got %d.\n", i, abc.abcB); } }
Similar to the tests already present for usp10.
Signed-off-by: Sven Baars sbaars@codeweavers.com --- v2: Fixed test failures.
dlls/gdi32/tests/font.c | 444 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 443 insertions(+), 1 deletion(-)
diff --git a/dlls/gdi32/tests/font.c b/dlls/gdi32/tests/font.c index 5b5efc88743..f08b42ff66e 100644 --- a/dlls/gdi32/tests/font.c +++ b/dlls/gdi32/tests/font.c @@ -4895,12 +4895,310 @@ static void test_GetTextMetrics2(const char *fontname, int font_height) ok(ratio >= 90 && ratio <= 110, "expected width/height ratio 90-110, got %d\n", ratio); }
+struct enum_font_range_param { + BYTE range; + LOGFONTA lf; +}; + +static int CALLBACK enum_font_range_proc(const LOGFONTA *lf, const TEXTMETRICA *tm, DWORD type, LPARAM param) +{ + NEWTEXTMETRICEXA *ntme = (NEWTEXTMETRICEXA *)tm; + struct enum_font_range_param *efrp = (struct enum_font_range_param *)param; + int idx = 0; + DWORD i; + DWORD mask = 0; + + if (type != TRUETYPE_FONTTYPE) + return 1; + + i = efrp->range; + while (i >= sizeof(DWORD)*8) + { + idx++; + i -= (sizeof(DWORD)*8); + } + if (idx > 3) + return 0; + + mask = 1 << i; + + if (ntme->ntmFontSig.fsUsb[idx] & mask) + { + memcpy(&(efrp->lf), lf, sizeof(LOGFONTA)); + return 0; + } + return 1; +} + +static int _find_font_for_range(HDC hdc, const CHAR *recommended, BYTE range, const WCHAR check, HFONT *hfont, HFONT *hfont_orig) +{ + int rc = 0; + struct enum_font_range_param param; + + param.range = range; + memset(¶m.lf, 0, sizeof(LOGFONTA)); + *hfont = NULL; + + if (recommended) + { + lstrcpyA(param.lf.lfFaceName, recommended); + if (!EnumFontFamiliesExA(hdc, ¶m.lf, enum_font_range_proc, (LPARAM)¶m, 0)) + { + *hfont = CreateFontIndirectA(¶m.lf); + if (*hfont) + { + winetest_trace("Using font %s.\n", param.lf.lfFaceName); + rc = 1; + } + } + if (!rc) + winetest_skip("Font %s is not available.\n", recommended); + } + + if (!*hfont) + { + memset(¶m.lf, 0, sizeof(LOGFONTA)); + param.lf.lfCharSet = DEFAULT_CHARSET; + + if (!EnumFontFamiliesExA(hdc, ¶m.lf, enum_font_range_proc, (LPARAM)¶m, 0) && param.lf.lfFaceName[0]) + { + *hfont = CreateFontIndirectA(¶m.lf); + if (*hfont) + winetest_trace("Trying font %s: failures will only be warnings.\n",param.lf.lfFaceName); + } + } + + if (*hfont) + { + WORD glyph = 0; + + *hfont_orig = SelectObject(hdc, *hfont); + if (GetGlyphIndicesW(hdc, &check, 1, &glyph, 0) == GDI_ERROR || glyph == 0) + { + winetest_trace("Font fails to contain required glyphs.\n"); + SelectObject(hdc, *hfont_orig); + DeleteObject(*hfont); + *hfont = NULL; + rc = 0; + } + else if (!rc) + rc = -1; + } + else + winetest_trace("Failed to find usable font.\n"); + + return rc; +} + +#define find_font_for_range(a, b, c, d, e, f) \ + (winetest_set_location(__FILE__, __LINE__), 0) ? 0 : \ + _find_font_for_range(a, b, c, d, e, f) + +struct expected_character_placement +{ + int order; + int dx; + int reorder; +}; + +static inline void _test_character_placement_ok(int valid, HDC hdc, const WCHAR *string, unsigned int str_len, + const struct expected_character_placement *expected, unsigned int expected_len) +{ + GCP_RESULTSW result; + unsigned int i, max_glyphs = 1.5 * str_len; + DWORD size; + int dx; + + memset(&result, 0, sizeof(result)); + result.lStructSize = sizeof(result); + result.lpCaretPos = heap_alloc(max_glyphs * sizeof(*result.lpCaretPos)); + result.lpGlyphs = heap_alloc(max_glyphs * sizeof(*result.lpGlyphs)); + result.lpOrder = heap_alloc(max_glyphs * sizeof(*result.lpOrder)); + result.lpDx = heap_alloc(max_glyphs * sizeof(*result.lpDx)); + result.nGlyphs = max_glyphs; + + memset(result.lpOrder, -1, max_glyphs * sizeof(*result.lpOrder)); + memset(result.lpDx, -1, max_glyphs * sizeof(*result.lpDx)); + + 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); + else if (result.nGlyphs != expected_len) + winetest_trace("Expected %d, got %d.\n", expected_len, result.nGlyphs); + for (i = 0; i < result.nGlyphs; ++i) + { + if (valid > 0) + todo_wine_if(expected[i].order != i) + winetest_ok(result.lpOrder[i] == expected[i].order || + broken(result.lpOrder[i] == expected[expected_len - i - 1].reorder) /* Win2008 */, + "Character %d, expected order %d, got %d.\n", + i, expected[i].order, result.lpOrder[i]); + else if (result.lpOrder[i] != expected[i].reorder) + winetest_trace("Character %d, expected order %d, got %d.\n", i, expected[i].reorder, result.lpOrder[i]); + if (expected[i].dx) + { + GetCharWidthI(hdc, result.lpGlyphs[result.lpOrder[i]], 1, NULL, &dx); + if (valid > 0) + winetest_ok(result.lpDx[result.lpOrder[i]] == dx, "Character %d, expected dx %d, got %d.\n", + i, dx, result.lpDx[result.lpOrder[i]]); + else if (result.lpDx[result.lpOrder[i]] != dx) + winetest_trace("Character %d, expected dx %d, got %d.\n", + i, dx, result.lpDx[result.lpOrder[i]]); + } + } + + if (expected[0].reorder >= 0) + { + memset(result.lpOrder, -1, max_glyphs * sizeof(*result.lpOrder)); + memset(result.lpDx, -1, max_glyphs * sizeof(*result.lpDx)); + + 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); + 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) + { + if (valid > 0) + todo_wine_if(expected[i].order != i) + winetest_ok(result.lpOrder[i] == expected[i].reorder, "Character %d, expected GCP_REORDER order %d, got %d.\n", + i, expected[i].reorder, result.lpOrder[i]); + else if (result.lpOrder[i] != expected[i].reorder) + winetest_trace("Character %d, expected GCP_REORDER order %d, got %d.\n", i, expected[i].reorder, result.lpOrder[i]); + if (expected[i].dx) + { + GetCharWidthI(hdc, result.lpGlyphs[result.lpOrder[i]], 1, NULL, &dx); + if (valid > 0) + winetest_ok(result.lpDx[result.lpOrder[i]] == dx, "Character %d, expected GCP_REORDER dx %d, got %d.\n", + i, dx, result.lpDx[result.lpOrder[i]]); + else if (result.lpDx[result.lpOrder[i]] != dx) + winetest_trace("Character %d, expected GCP_REORDER dx %d, got %d.\n", + i, dx, result.lpDx[result.lpOrder[i]]); + } + } + } + + heap_free(result.lpCaretPos); + heap_free(result.lpGlyphs); + heap_free(result.lpOrder); + heap_free(result.lpDx); +} + +#define test_character_placement_ok(a, b, c, d, e, f) \ + (winetest_set_location(__FILE__, __LINE__), 0) ? 0 : \ + _test_character_placement_ok(a, b, c, d, e, f) + static void test_GetCharacterPlacement(void) { + static const WCHAR test1[] = {'w', 'i', 'n', 'e'}; + static const struct expected_character_placement t1_expected[] = + {{0,1,0},{1,1,1},{2,1,2},{3,1,3}}; + + static const WCHAR test2[] = {0x202B, 'i', 'n', 0x202C}; + static const struct expected_character_placement t2_expected[] = + {{0,0,0},{1,1,1},{2,1,2},{3,0,3}}; + + static const WCHAR test3[] = {'t', 't', 'f', 'f', 'f', 'i',}; + static const struct expected_character_placement t3_expected[] = + {{0,1,0},{0,1,0},{0,1,0},{1,1,1},{1,1,1},{1,1,1}}; + + /* Hebrew */ + static const WCHAR test_hebrew[] = + {0x05e9, 0x05dc, 0x05d5, 0x05dd}; + static const struct expected_character_placement hebrew_expected[] = + {{0,1,3},{1,1,2},{2,1,1},{3,1,0}}; + + /* Arabic */ + static const WCHAR test_arabic[] = + {0x0633, 0x0644, 0x0627, 0x0645}; + static const struct expected_character_placement arabic_expected[] = + {{0,1,2},{1,1,1},{2,1,1},{3,1,0}}; + + /* Thai */ + static const WCHAR test_thai[] = + {0x0e2a, 0x0e04, 0x0e23, 0x0e34, 0x0e1b, 0x0e15, 0x0e4c, 0x0e44, 0x0e17, 0x0e22}; + static const struct expected_character_placement thai_expected[] = + {{0,1,0},{1,1,1},{2,1,2},{3,1,3},{4,1,4},{5,1,5},{6,1,6},{7,1,7},{8,1,8},{9,1,9}}; + + /* Thaana */ + static const WCHAR test_thaana[] = + {0x078a, 0x07ae, 0x0792, 0x07b0, 0x0020, 0x0796, 0x07aa, 0x0789, 0x07b0, 0x0795, 0x07ac, 0x0791, 0x07b0}; + static const struct expected_character_placement thaana_expected[] = + {{0,1,12},{1,1,11},{2,0,10},{3,1,9},{4,1,8},{5,1,7},{6,1,6},{7,1,5},{8,1,4},{9,1,3},{10,1,2},{11,0,1},{12,1,0}}; + + /* Phags-pa */ + static const WCHAR test_phagspa[] = + {0xa84f, 0xa861, 0xa843, 0x0020, 0xa863, 0xa861, 0xa859, 0x0020, 0xa850, 0xa85c, 0xa85e}; + static const struct expected_character_placement phagspa_expected[] = + {{0,1,-1},{1,1,-1},{2,1,-1},{3,1,-1},{4,1,-1},{5,1,-1},{6,1,-1},{7,1,-1},{8,1,-1},{9,1,-1},{10,1,-1}}; + + /* Lao */ + static const WCHAR test_lao[] = + {0x0ead, 0x0eb1, 0x0e81, 0x0eaa, 0x0ead, 0x0e99, 0x0ea5, 0x0eb2, 0x0ea7}; + static const struct expected_character_placement lao_expected[] = + {{0,1,0},{1,1,1},{2,1,2},{3,1,3},{4,1,4},{5,1,5},{6,1,6},{7,1,7},{8,1,8}}; + + /* Tibetan */ + static const WCHAR test_tibetan[] = + {0x0f04, 0x0f05, 0x0f0e, 0x0020, 0x0f51, 0x0f7c, 0x0f53, 0x0f0b, 0x0f5a, 0x0f53, 0x0f0b, 0x0f51, 0x0f44, 0x0f0b, 0x0f54, 0x0f7c, 0x0f0d}; + static const struct expected_character_placement tibetan_expected[] = + {{0,1,-1},{1,1,-1},{2,1,-1},{3,1,-1},{4,1,-1},{5,1,-1},{6,1,-1},{7,1,-1},{8,1,-1},{9,1,-1},{10,1,-1},{11,1,-1},{12,1,-1},{13,1,-1},{14,1,-1},{15,1,-1},{16,1,-1}}; + + /* Devanagari */ + static const WCHAR test_devanagari[] = + {0x0926, 0x0947, 0x0935, 0x0928, 0x093e, 0x0917, 0x0930, 0x0940}; + static const struct expected_character_placement devanagari_expected[] = + {{0,1,-1},{1,1,-1},{2,1,-1},{3,1,-1},{4,1,-1},{5,1,-1},{6,1,-1},{7,1,-1}}; + + /* Bengali */ + static const WCHAR test_bengali[] = + {0x09ac, 0x09be, 0x0982, 0x09b2, 0x09be}; + static const struct expected_character_placement bengali_expected[] = + {{0,1,-1},{1,1,-1},{2,1,-1},{3,1,-1},{4,1,-1}}; + + /* Gurmukhi */ + static const WCHAR test_gurmukhi[] = + {0x0a17, 0x0a41, 0x0a30, 0x0a2e, 0x0a41, 0x0a16, 0x0a40}; + static const struct expected_character_placement gurmukhi_expected[] = + {{0,1,-1},{1,1,-1},{2,1,-1},{3,1,-1},{4,1,-1},{5,1,-1},{6,1,-1}}; + + /* Gujarati */ + static const WCHAR test_gujarati[] = + {0x0a97, 0x0ac1, 0x0a9c, 0x0ab0, 0x0abe, 0x0aa4, 0x0ac0}; + static const struct expected_character_placement gujarati_expected[] = + {{0,1,-1},{1,1,-1},{2,1,-1},{3,1,-1},{4,1,-1},{5,1,-1},{6,1,-1}}; + + /* Oriya */ + static const WCHAR test_oriya[] = + {0x0b13, 0x0b21, 0x0b3c, 0x0b3f, 0x0b06}; + static const struct expected_character_placement oriya_expected[] = + {{0,1,0},{1,1,1},{2,1,2},{2,1,2},{3,1,3}}; + + /* Tamil */ + static const WCHAR test_tamil[] = + {0x0ba4, 0x0bae, 0x0bbf, 0x0bb4, 0x0bcd}; + static const struct expected_character_placement tamil_expected[] = + {{0,1,0},{1,1,1},{2,1,2},{3,1,3},{3,1,3}}; + + /* Malayalam */ + static const WCHAR test_malayalam[] = + {0x0d2e, 0x0d32, 0x0d2f, 0x0d3e, 0x0d33, 0x0d02}; + static const struct expected_character_placement malayalam_expected[] = + {{0,1,-1},{1,1,-1},{2,1,-1},{3,1,-1},{4,1,-1},{5,1,-1}}; + + /* Kannada */ + static const WCHAR test_kannada[] = + {0x0c95, 0x0ca8, 0x0ccd, 0x0ca8, 0x0ca1}; + static const struct expected_character_placement kannada_expected[] = + {{0,1,0},{1,1,1},{2,1,2},{2,1,2},{3,1,3}}; + + HFONT hfont, hfont_orig; GCP_RESULTSA result; DWORD size, size2; WCHAR glyphs[20]; - int pos[20]; + int test_valid, pos[20]; HDC hdc;
hdc = CreateCompatibleDC(0); @@ -4947,6 +5245,150 @@ static void test_GetCharacterPlacement(void) ok(glyphs[0] == 'W', "Unexpected first glyph %s\n", wine_dbgstr_wn(glyphs, 1)); todo_wine ok(pos[0] == 0, "Unexpected caret position %d\n", pos[0]);
+ test_valid = find_font_for_range(hdc, "Tahoma", 0, test1[0], &hfont, &hfont_orig); + if (hfont != NULL) + { + test_character_placement_ok(test_valid, hdc, test1, 4, t1_expected, ARRAY_SIZE(t1_expected)); + SelectObject(hdc, hfont_orig); + DeleteObject(hfont); + } + + test_valid = find_font_for_range(hdc, "Tahoma", 0, test2[0], &hfont, &hfont_orig); + if (hfont != NULL) + { + test_character_placement_ok(test_valid, hdc, test2, 4, t2_expected, ARRAY_SIZE(t2_expected)); + SelectObject(hdc, hfont_orig); + DeleteObject(hfont); + } + + test_valid = find_font_for_range(hdc, "Calibri", 0, test3[0], &hfont, &hfont_orig); + if (hfont != NULL) + { + test_character_placement_ok(test_valid, hdc, test3, 6, t3_expected, ARRAY_SIZE(t3_expected)); + SelectObject(hdc, hfont_orig); + DeleteObject(hfont); + } + + test_valid = find_font_for_range(hdc, "Microsoft Sans Serif", 11, test_hebrew[0], &hfont, &hfont_orig); + if (hfont != NULL) + { + test_character_placement_ok(test_valid, hdc, test_hebrew, 4, hebrew_expected, ARRAY_SIZE(hebrew_expected)); + SelectObject(hdc, hfont_orig); + DeleteObject(hfont); + } + + test_valid = find_font_for_range(hdc, "Microsoft Sans Serif", 13, test_arabic[0], &hfont, &hfont_orig); + if (hfont != NULL) + { + test_character_placement_ok(test_valid, hdc, test_arabic, 4, arabic_expected, ARRAY_SIZE(arabic_expected)); + SelectObject(hdc, hfont_orig); + DeleteObject(hfont); + } + + test_valid = find_font_for_range(hdc, "Microsoft Sans Serif", 24, test_thai[0], &hfont, &hfont_orig); + if (hfont != NULL) + { + test_character_placement_ok(test_valid, hdc, test_thai, 10, thai_expected, ARRAY_SIZE(thai_expected)); + SelectObject(hdc, hfont_orig); + DeleteObject(hfont); + } + + test_valid = find_font_for_range(hdc, "MV Boli", 72, test_thaana[0], &hfont, &hfont_orig); + if (hfont != NULL) + { + test_character_placement_ok(test_valid, hdc, test_thaana, 13, thaana_expected, ARRAY_SIZE(thaana_expected)); + SelectObject(hdc, hfont_orig); + DeleteObject(hfont); + } + + test_valid = find_font_for_range(hdc, "Microsoft PhagsPa", 53, test_phagspa[0], &hfont, &hfont_orig); + if (hfont != NULL) + { + test_character_placement_ok(test_valid, hdc, test_phagspa, 11, phagspa_expected, ARRAY_SIZE(phagspa_expected)); + SelectObject(hdc, hfont_orig); + DeleteObject(hfont); + } + + test_valid = find_font_for_range(hdc, "DokChampa", 25, test_lao[0], &hfont, &hfont_orig); + if (hfont != NULL) + { + test_character_placement_ok(test_valid, hdc, test_lao, 9, lao_expected, ARRAY_SIZE(lao_expected)); + SelectObject(hdc, hfont_orig); + DeleteObject(hfont); + } + + test_valid = find_font_for_range(hdc, "Microsoft Himalaya", 70, test_tibetan[0], &hfont, &hfont_orig); + if (hfont != NULL) + { + test_character_placement_ok(test_valid, hdc, test_tibetan, 17, tibetan_expected, ARRAY_SIZE(tibetan_expected)); + SelectObject(hdc, hfont_orig); + DeleteObject(hfont); + } + + test_valid = find_font_for_range(hdc, "Mangal", 15, test_devanagari[0], &hfont, &hfont_orig); + if (hfont != NULL) + { + test_character_placement_ok(test_valid, hdc, test_devanagari, 8, devanagari_expected, ARRAY_SIZE(devanagari_expected)); + SelectObject(hdc, hfont_orig); + DeleteObject(hfont); + } + + test_valid = find_font_for_range(hdc, "Vrinda", 16, test_bengali[0], &hfont, &hfont_orig); + if (hfont != NULL) + { + test_character_placement_ok(test_valid, hdc, test_bengali, 5, bengali_expected, ARRAY_SIZE(bengali_expected)); + SelectObject(hdc, hfont_orig); + DeleteObject(hfont); + } + + test_valid = find_font_for_range(hdc, "Raavi", 17, test_gurmukhi[0], &hfont, &hfont_orig); + if (hfont != NULL) + { + test_character_placement_ok(test_valid, hdc, test_gurmukhi, 7, gurmukhi_expected, ARRAY_SIZE(gurmukhi_expected)); + SelectObject(hdc, hfont_orig); + DeleteObject(hfont); + } + + test_valid = find_font_for_range(hdc, "Shruti", 18, test_gujarati[0], &hfont, &hfont_orig); + if (hfont != NULL) + { + test_character_placement_ok(test_valid, hdc, test_gujarati, 7, gujarati_expected, ARRAY_SIZE(gujarati_expected)); + SelectObject(hdc, hfont_orig); + DeleteObject(hfont); + } + + test_valid = find_font_for_range(hdc, "Kalinga", 19, test_oriya[0], &hfont, &hfont_orig); + if (hfont != NULL) + { + test_character_placement_ok(test_valid, hdc, test_oriya, 5, oriya_expected, ARRAY_SIZE(oriya_expected)); + SelectObject(hdc, hfont_orig); + DeleteObject(hfont); + } + + test_valid = find_font_for_range(hdc, "Latha", 20, test_tamil[0], &hfont, &hfont_orig); + if (hfont != NULL) + { + test_character_placement_ok(test_valid, hdc, test_tamil, 5, tamil_expected, ARRAY_SIZE(tamil_expected)); + SelectObject(hdc, hfont_orig); + DeleteObject(hfont); + } + + test_valid = find_font_for_range(hdc, "Kartika", 23, test_malayalam[0], &hfont, &hfont_orig); + if (hfont != NULL) + { + test_character_placement_ok(test_valid, hdc, test_malayalam, 6, malayalam_expected, ARRAY_SIZE(malayalam_expected)); + SelectObject(hdc, hfont_orig); + DeleteObject(hfont); + } + + test_valid = find_font_for_range(hdc, "Tunga", 22, test_kannada[0], &hfont, &hfont_orig); + if (hfont != NULL) + { + test_character_placement_ok(test_valid, hdc, test_kannada, 5, kannada_expected, ARRAY_SIZE(kannada_expected)); + SelectObject(hdc, hfont_orig); + DeleteObject(hfont); + } + DeleteDC(hdc); }
Signed-off-by: Sven Baars sbaars@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 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
On Mon, Nov 09, 2020 at 03:07:13PM +0100, Sven Baars wrote:
diff --git a/dlls/gdi32/tests/font.c b/dlls/gdi32/tests/font.c index 4c8b1cbb91b..d3ec971dce0 100644 --- a/dlls/gdi32/tests/font.c +++ b/dlls/gdi32/tests/font.c @@ -1330,17 +1330,16 @@ static void test_GetCharABCWidths(void)
memset(&lf, 0, sizeof(lf)); lf.lfHeight = 20;
switch(i)
if (i == 1) {
case 1: strcpy(lf.lfFaceName, "Tahoma"); code = 'a';
break;
case 2:
}
else
{ strcpy(lf.lfFaceName, "Times New Roman"); lf.lfItalic = TRUE; code = 'f';
break; } if (!is_truetype_font_installed(lf.lfFaceName)) {
Ideally this would be moved to a table driven test, but it's just about small enough to get away with it...
Signed-off-by: Huw Davies huw@codeweavers.com