Signed-off-by: Sven Baars sbaars@codeweavers.com --- Some tests with zero-width control characters for inconsistent behavior that I observed while trying to fix the ID3DXFont implementation. In our implementation these functions all use the same internal function, but on Windows none of them seem to do exactly the same.
dlls/gdi32/tests/font.c | 151 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+)
diff --git a/dlls/gdi32/tests/font.c b/dlls/gdi32/tests/font.c index 86ccddd05ea..75a41f69ab9 100644 --- a/dlls/gdi32/tests/font.c +++ b/dlls/gdi32/tests/font.c @@ -7693,6 +7693,156 @@ done: ReleaseDC(0, hdc); }
+static void test_zero_width_control(void) +{ + int len, dx[20], 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[0] = -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] == 0, "Test %d: got %d.\n", i, dx[0]); + todo_wine ok(sz.cx == 0, "Test %d: got %d.\n", i, sz.cx); + + /* But are treated differently for the fit */ + nfit = -1; + sz.cx = -1; + ret = GetTextExtentExPointW(hdc, test, 10, 1000, &nfit, dx, &sz); + ok(ret, "Test %d: expected TRUE.\n", i); + ok(nfit == 10, "Test %d: got %d.\n", i, nfit); + todo_wine ok(dx[4] == dx[5], "Test %d: expected %d got %d.\n", i, dx[4], dx[5]); + + /* The ZW characters do fit, the others don't */ + ret = GetTextExtentExPointW(hdc, test, 10, dx[5], &nfit, NULL, &sz); + ok(ret, "Test %d: expected TRUE.\n", i); + if (i < 7 || i > 9) + todo_wine ok(nfit == 5, "Test %d: got %d.\n", i, nfit); + else + ok(nfit == 6, "Test %d: got %d.\n", i, nfit); + + /* 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); + + /* In which case ABC also does not have zero width, but A+B+C + * is not always equal to the length from GetCharWidth32 */ + 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[0] = -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] > 0, "Got %d.\n", dx[0]); + 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); + + /* Characters that are treated as spaces by GetCharacterPlacement */ + /* are not treated as 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[] = @@ -7779,6 +7929,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.
On 12/14/20 5:53 PM, Sven Baars wrote:
Signed-off-by: Sven Baars sbaars@codeweavers.com
Some tests with zero-width control characters for inconsistent behavior that I observed while trying to fix the ID3DXFont implementation. In our implementation these functions all use the same internal function, but on Windows none of them seem to do exactly the same.
Have you tried with different fonts? Especially with fonts not shipped with Windows.
As I remember certain system fonts were updated in Windows 10 to have empty glyphs for control characters. So what you see and this whole test might be unnecessary tailored for Tahoma (including broken parts reflecting font changes across windows releases).
Easy way to test is to use some modified Noto font to see when metrics are used and when metrics are ignored for certain characters.