From: Sven Baars Subject: [PATCH v2 6/7] gdi32/tests: Add more tests for GetCharacterPlacement. Message-Id: <20201109140720.472712-7-sbaars@codeweavers.com> Date: Mon, 9 Nov 2020 15:07:19 +0100 In-Reply-To: <20201109140720.472712-1-sbaars@codeweavers.com> References: <20201109140720.472712-1-sbaars@codeweavers.com> Similar to the tests already present for usp10. Signed-off-by: Sven Baars --- 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); } -- 2.25.1