[PATCH v3 0/6] MR4082: gdiplus: Implement font linking in GdipDrawString.
-- v3: gdiplus: Implement font linking for gdiplus. gdiplus/tests: Add interactive test for font linking. mlang: Fix bug with codepage priority in GetStrCodePages. mlang/tests: Test codepages priority bug in GetStrCodepages. mlang: Implement GetGlobalFontLinkObject. mlang/tests: Test for GetGlobalFontLinkObject. https://gitlab.winehq.org/wine/wine/-/merge_requests/4082
From: Santino Mazza <smazza(a)codeweavers.com> --- dlls/mlang/tests/mlang.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/dlls/mlang/tests/mlang.c b/dlls/mlang/tests/mlang.c index 704576005d2..12116d770ad 100644 --- a/dlls/mlang/tests/mlang.c +++ b/dlls/mlang/tests/mlang.c @@ -2298,6 +2298,11 @@ static void test_GetGlobalFontLinkObject(void) { HRESULT ret; void *unknown; + LONG refcount; + IMLangFontLink2 *IMLFL2; + IMLangFontLink *IMLFL; + IMLangCodePages *IMLCP; + IMultiLanguage *IML; ret = GetGlobalFontLinkObject(NULL); ok(ret == E_INVALIDARG, "expected E_INVALIDARG got %#lx\n", ret); @@ -2308,7 +2313,26 @@ todo_wine { ok(ret == S_OK, "expected S_OK got %#lx\n", ret); ok(unknown != NULL && unknown != (void *)0xdeadbeef, "GetGlobalFontLinkObject() returned %p\n", unknown); + if (unknown == (void *)0xdeadbeef || !unknown) return; } + + ret = IUnknown_QueryInterface((IUnknown*)unknown, &IID_IMLangFontLink2, (void**)&IMLFL2); + ok(ret == E_NOINTERFACE, "expected E_NOINTERFACE got %#lx\n", ret); + + ret = IUnknown_QueryInterface((IUnknown*)unknown, &IID_IMultiLanguage, (void**)&IML); + ok(ret == E_NOINTERFACE, "expected E_NOINTERFACE got %#lx\n", ret); + + ret = IUnknown_QueryInterface((IUnknown*)unknown, &IID_IMLangFontLink, (void**)&IMLFL); + ok(ret == S_OK, "expected E_NOINTERFACE got %#lx\n", ret); + IMLangFontLink_Release(IMLFL); + + ret = IUnknown_QueryInterface((IUnknown*)unknown, &IID_IMLangCodePages, (void**)&IMLCP); + ok(ret == S_OK, "expected E_NOINTERFACE got %#lx\n", ret); + IMLangCodePages_Release(IMLCP); + + + refcount = IUnknown_Release((IUnknown*)unknown); + ok(refcount == 1, "Got refcount %ld\n", refcount); } static void test_IMLangConvertCharset(IMultiLanguage *ml) @@ -2781,6 +2805,8 @@ START_TEST(mlang) if (!init_function_ptrs()) return; + test_GetGlobalFontLinkObject(); + CoInitialize(NULL); test_Rfc1766ToLcid(); test_LcidToRfc1766(); @@ -2788,8 +2814,6 @@ START_TEST(mlang) test_ConvertINetUnicodeToMultiByte(); test_JapaneseConversion(); - test_GetGlobalFontLinkObject(); - trace("IMultiLanguage\n"); ret = CoCreateInstance(&CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER, &IID_IMultiLanguage, (void **)&iML); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/4082
From: Santino Mazza <smazza(a)codeweavers.com> --- dlls/mlang/mlang.c | 12 ++++++++++-- dlls/mlang/tests/mlang.c | 8 ++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/dlls/mlang/mlang.c b/dlls/mlang/mlang.c index 9846e3ad0e4..19df817d4fe 100644 --- a/dlls/mlang/mlang.c +++ b/dlls/mlang/mlang.c @@ -44,6 +44,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(mlang); #include "initguid.h" +static IUnknown *font_link_global = NULL; + static HRESULT MultiLanguage_create(IUnknown *pUnkOuter, LPVOID *ppObj); static HRESULT MLangConvertCharset_create(IUnknown *outer, void **obj); static HRESULT EnumRfc1766_create(LANGID LangId, IEnumRfc1766 **ppEnum); @@ -3884,9 +3886,15 @@ HRESULT WINAPI DllCanUnloadNow(void) HRESULT WINAPI GetGlobalFontLinkObject(void **unknown) { + TRACE("%p\n", unknown); + if (!unknown) return E_INVALIDARG; - FIXME("%p: stub\n", unknown); + if (!font_link_global) + MultiLanguage_create(NULL, (void**)&font_link_global); - return S_FALSE; + IUnknown_AddRef(font_link_global); + *unknown = font_link_global; + + return S_OK; } diff --git a/dlls/mlang/tests/mlang.c b/dlls/mlang/tests/mlang.c index 12116d770ad..f07a0a3a2f7 100644 --- a/dlls/mlang/tests/mlang.c +++ b/dlls/mlang/tests/mlang.c @@ -2309,18 +2309,18 @@ static void test_GetGlobalFontLinkObject(void) unknown = (void *)0xdeadbeef; ret = GetGlobalFontLinkObject(&unknown); -todo_wine { ok(ret == S_OK, "expected S_OK got %#lx\n", ret); ok(unknown != NULL && unknown != (void *)0xdeadbeef, "GetGlobalFontLinkObject() returned %p\n", unknown); if (unknown == (void *)0xdeadbeef || !unknown) return; - } ret = IUnknown_QueryInterface((IUnknown*)unknown, &IID_IMLangFontLink2, (void**)&IMLFL2); - ok(ret == E_NOINTERFACE, "expected E_NOINTERFACE got %#lx\n", ret); + todo_wine ok(ret == E_NOINTERFACE, "expected E_NOINTERFACE got %#lx\n", ret); + if (ret == S_OK) IMLangFontLink2_Release(IMLFL2); ret = IUnknown_QueryInterface((IUnknown*)unknown, &IID_IMultiLanguage, (void**)&IML); - ok(ret == E_NOINTERFACE, "expected E_NOINTERFACE got %#lx\n", ret); + todo_wine ok(ret == E_NOINTERFACE, "expected E_NOINTERFACE got %#lx\n", ret); + if (ret == S_OK) IMultiLanguage_Release(IML); ret = IUnknown_QueryInterface((IUnknown*)unknown, &IID_IMLangFontLink, (void**)&IMLFL); ok(ret == S_OK, "expected E_NOINTERFACE got %#lx\n", ret); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/4082
From: Santino Mazza <smazza(a)codeweavers.com> --- dlls/mlang/tests/mlang.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dlls/mlang/tests/mlang.c b/dlls/mlang/tests/mlang.c index f07a0a3a2f7..97097d9da34 100644 --- a/dlls/mlang/tests/mlang.c +++ b/dlls/mlang/tests/mlang.c @@ -1327,6 +1327,14 @@ static void IMLangFontLink_Test(IMLangFontLink* iMLFL) ok(dwCodePages == dwCmpCodePages, "expected %lx, got %lx\n", dwCmpCodePages, dwCodePages); ok(processed == 2, "expected 2, got %ld\n", processed); + dwCmpCodePages = FS_JISJAPAN; + dwCodePages = 0; + processed = 0; + ret = IMLangFontLink_GetStrCodePages(iMLFL, L"\uff90a", 2, FS_LATIN1, &dwCodePages, &processed); + ok(ret == S_OK, "IMLangFontLink_GetStrCodePages error %lx\n", ret); + ok(dwCodePages == dwCmpCodePages, "expected %lx, got %lx\n", dwCmpCodePages, dwCodePages); + todo_wine ok(processed == 1, "expected 1, got %ld\n", processed); + dwCodePages = 0xffff; processed = -1; ret = IMLangFontLink_GetStrCodePages(iMLFL, &str[2], 1, 0, &dwCodePages, &processed); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/4082
From: Santino Mazza <smazza(a)codeweavers.com> --- dlls/mlang/mlang.c | 3 ++- dlls/mlang/tests/mlang.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/dlls/mlang/mlang.c b/dlls/mlang/mlang.c index 19df817d4fe..91bc1e005ed 100644 --- a/dlls/mlang/mlang.c +++ b/dlls/mlang/mlang.c @@ -3344,7 +3344,8 @@ static HRESULT WINAPI fnIMLangFontLink2_GetStrCodePages( IMLangFontLink2* iface, if (ret != S_OK) return E_FAIL; if (!cps) cps = cp; - else if ((cps & cp) != 0) cps &= cp; + else if ((cps & cp) != 0 && + ((priority_cp & cps) || !(priority_cp & cp))) cps &= cp; else { i--; diff --git a/dlls/mlang/tests/mlang.c b/dlls/mlang/tests/mlang.c index 97097d9da34..91f77dbc562 100644 --- a/dlls/mlang/tests/mlang.c +++ b/dlls/mlang/tests/mlang.c @@ -1333,7 +1333,7 @@ static void IMLangFontLink_Test(IMLangFontLink* iMLFL) ret = IMLangFontLink_GetStrCodePages(iMLFL, L"\uff90a", 2, FS_LATIN1, &dwCodePages, &processed); ok(ret == S_OK, "IMLangFontLink_GetStrCodePages error %lx\n", ret); ok(dwCodePages == dwCmpCodePages, "expected %lx, got %lx\n", dwCmpCodePages, dwCodePages); - todo_wine ok(processed == 1, "expected 1, got %ld\n", processed); + ok(processed == 1, "expected 1, got %ld\n", processed); dwCodePages = 0xffff; processed = -1; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/4082
From: Santino Mazza <smazza(a)codeweavers.com> --- dlls/gdiplus/tests/graphics.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/dlls/gdiplus/tests/graphics.c b/dlls/gdiplus/tests/graphics.c index d71bc78c2ae..f23a26ad0c8 100644 --- a/dlls/gdiplus/tests/graphics.c +++ b/dlls/gdiplus/tests/graphics.c @@ -2305,10 +2305,26 @@ static void test_GdipDrawString(void) } expect(Ok, status); - status = GdipCreateSolidFill((ARGB)0xdeadbeef, (GpSolidFill**)&brush); + status = GdipCreateStringFormat(0,0,&format); expect(Ok, status); - status = GdipCreateStringFormat(0,0,&format); + if (winetest_interactive) + { + status = GdipCreateSolidFill(0xF0000000, (GpSolidFill**)&brush); + expect(Ok, status); + rect.X = 0; + rect.Y = 0; + rect.Width = 0; + rect.Height = 14; + GdipGraphicsClear(graphics, 0xF0FFFFFF); + status = GdipDrawString(graphics, L"\u8336Hola\u8336", 6, fnt, &rect, format, brush); + expect(Ok, status); + RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); /* FIXME: In Windows this test works without this line. */ + Sleep(4000); + GdipDeleteBrush(brush); + } + + status = GdipCreateSolidFill((ARGB)0xdeadbeef, (GpSolidFill**)&brush); expect(Ok, status); rect.X = 0; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/4082
From: Santino Mazza <smazza(a)codeweavers.com> --- dlls/gdiplus/Makefile.in | 2 +- dlls/gdiplus/gdiplus_private.h | 24 ++++++- dlls/gdiplus/graphics.c | 119 ++++++++++++++++++++++++++++++--- dlls/gdiplus/graphicspath.c | 2 +- 4 files changed, 136 insertions(+), 11 deletions(-) diff --git a/dlls/gdiplus/Makefile.in b/dlls/gdiplus/Makefile.in index 382033be901..76474f2e086 100644 --- a/dlls/gdiplus/Makefile.in +++ b/dlls/gdiplus/Makefile.in @@ -1,6 +1,6 @@ MODULE = gdiplus.dll IMPORTLIB = gdiplus -IMPORTS = uuid shlwapi ole32 oleaut32 user32 gdi32 +IMPORTS = uuid shlwapi ole32 oleaut32 user32 gdi32 mlang DELAYIMPORTS = windowscodecs SOURCES = \ diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h index 1bc058b3413..29b5220d5c8 100644 --- a/dlls/gdiplus/gdiplus_private.h +++ b/dlls/gdiplus/gdiplus_private.h @@ -607,8 +607,30 @@ static inline const void *buffer_read(struct memory_buffer *mbuf, INT size) return NULL; } +/* Represents a string section and the font it should use. */ +struct gdip_font_link_section { + DWORD index; /* The starting index of the string where the font applies. */ + DWORD len; /* The length of the section. */ + GpFont *font; +}; + +struct gdip_font_link_info { + GDIPCONST GpFont *base_font; + struct gdip_font_link_section *sections; + INT count; +}; + +/* Get the corresponding section for the specified index */ + +void generate_font_link_info(HDC hdc, WCHAR *string, DWORD length, GDIPCONST GpFont *base_font, + struct gdip_font_link_info *font_link_info); + +struct gdip_font_link_section *get_font_link_section(struct gdip_font_link_info *font_link_info, DWORD index); + +void release_font_link_info(struct gdip_font_link_info *font_link_info); + typedef GpStatus (*gdip_format_string_callback)(HDC hdc, - GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font, + GDIPCONST WCHAR *string, INT index, INT length, struct gdip_font_link_info *sections, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, INT lineno, const RectF *bounds, INT *underlined_indexes, INT underlined_index_count, void *user_data); diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c index cc8411d54f7..3e6252f9f47 100644 --- a/dlls/gdiplus/graphics.c +++ b/dlls/gdiplus/graphics.c @@ -34,6 +34,7 @@ #include "winreg.h" #include "shlwapi.h" +#include "mlang.h" #include "gdiplus.h" #include "gdiplus_private.h" #include "wine/debug.h" @@ -5153,6 +5154,88 @@ GpStatus WINGDIPAPI GdipIsVisibleRectI(GpGraphics *graphics, INT x, INT y, INT w return GdipIsVisibleRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, result); } +/* Populates gdip_font_link_info struct based on the base_font and input string */ +void generate_font_link_info(HDC hdc, WCHAR *string, DWORD length, GDIPCONST GpFont *base_font, + struct gdip_font_link_info *font_link_info) +{ + IUnknown *unk; + IMLangFontLink *iMLFL; + GpFont *gpfont; + GpGraphics *graphics; + HFONT map_hfont, hfont, old_font; + LONG processed, progress = 0; + DWORD sections_count = 0; + struct gdip_font_link_section section; + DWORD font_codepages, string_codepages; + + font_link_info->base_font = base_font; + + GetGlobalFontLinkObject((void**)&unk); + IUnknown_QueryInterface(unk, &IID_IMLangFontLink, (void**)&iMLFL); + IUnknown_Release(unk); + + GdipCreateFromHDC(hdc, &graphics); + get_font_hfont(graphics, base_font, NULL, &hfont, NULL, NULL); + IMLangFontLink_GetFontCodePages(iMLFL, hdc, hfont, &font_codepages); + GdipDeleteGraphics(graphics); + + while (progress < length) + { + section.index = progress; + IMLangFontLink_GetStrCodePages(iMLFL, &string[progress], length - progress, + font_codepages, &string_codepages, &processed); + + if (font_codepages & string_codepages) + { + section.font = (GpFont *)base_font; + } + else + { + IMLangFontLink_MapFont(iMLFL, hdc, string_codepages, hfont, &map_hfont); + old_font = SelectObject(hdc, map_hfont); + GdipCreateFontFromDC(hdc, &gpfont); + SelectObject(hdc, old_font); + IMLangFontLink_ReleaseFont(iMLFL, map_hfont); + section.font = gpfont; + } + + section.len = processed; + sections_count = ++font_link_info->count; + font_link_info->sections = heap_realloc(font_link_info->sections, sizeof(section) * sections_count); + font_link_info->sections[sections_count - 1] = section; + progress += processed; + } + + DeleteObject(hfont); + IMLangFontLink_Release(iMLFL); +} + +struct gdip_font_link_section *get_font_link_section(struct gdip_font_link_info *font_link_info, DWORD index) +{ + struct gdip_font_link_section *section = &font_link_info->sections[0]; + for (int i = 0; i < font_link_info->count; ++i) + { + section = &font_link_info->sections[i]; + if (index <= section->index && (index - section->index) < section->len) + break; + } + + return section; +} + +void release_font_link_info(struct gdip_font_link_info *font_link_info) +{ + for (int i = 0; i < font_link_info->count; ++i) + { + if (font_link_info->sections[i].font != font_link_info->base_font) + GdipDeleteFont(font_link_info->sections[i].font); + } + + heap_free(font_link_info->sections); + font_link_info->count = 0; + font_link_info->sections = NULL; +} + GpStatus gdip_format_string(HDC hdc, GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, int ignore_empty_clip, @@ -5169,6 +5252,7 @@ GpStatus gdip_format_string(HDC hdc, INT *hotkeyprefix_offsets=NULL; INT hotkeyprefix_count=0; INT hotkeyprefix_pos=0, hotkeyprefix_end_pos=0; + struct gdip_font_link_info font_link_info = { 0 }; BOOL seen_prefix = FALSE, unixstyle_newline = TRUE; if(length == -1) length = lstrlenW(string); @@ -5234,6 +5318,8 @@ GpStatus gdip_format_string(HDC hdc, halign = format->align; + generate_font_link_info(hdc, stringdup, length, font, &font_link_info); + while(sum < length){ GetTextExtentExPointW(hdc, stringdup + sum, length - sum, nwidth, &fit, NULL, &size); @@ -5319,7 +5405,7 @@ GpStatus gdip_format_string(HDC hdc, break; stat = callback(hdc, stringdup, sum, lineend, - font, rect, format, lineno, &bounds, + &font_link_info, rect, format, lineno, &bounds, &hotkeyprefix_offsets[hotkeyprefix_pos], hotkeyprefix_end_pos-hotkeyprefix_pos, user_data); @@ -5350,6 +5436,7 @@ GpStatus gdip_format_string(HDC hdc, break; } + release_font_link_info(&font_link_info); heap_free(stringdup); heap_free(hotkeyprefix_offsets); @@ -5387,7 +5474,8 @@ struct measure_ranges_args { }; static GpStatus measure_ranges_callback(HDC hdc, - GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font, + GDIPCONST WCHAR *string, INT index, INT length, + struct gdip_font_link_info *font_link_info, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, INT lineno, const RectF *bounds, INT *underlined_indexes, INT underlined_index_count, void *user_data) @@ -5512,7 +5600,8 @@ struct measure_string_args { }; static GpStatus measure_string_callback(HDC hdc, - GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font, + GDIPCONST WCHAR *string, INT index, INT length, + struct gdip_font_link_info *font_link_info, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, INT lineno, const RectF *bounds, INT *underlined_indexes, INT underlined_index_count, void *user_data) @@ -5631,21 +5720,35 @@ struct draw_string_args { }; static GpStatus draw_string_callback(HDC hdc, - GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font, + GDIPCONST WCHAR *string, INT index, INT length, + struct gdip_font_link_info *font_link_info, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, INT lineno, const RectF *bounds, INT *underlined_indexes, INT underlined_index_count, void *user_data) { struct draw_string_args *args = user_data; PointF position; - GpStatus stat; + GpStringFormat *generic_format; + RectF bound_box; + DWORD to_draw_length; + struct gdip_font_link_section *section; + GpStatus stat = Ok; position.X = args->x + bounds->X / args->rel_width; position.Y = args->y + bounds->Y / args->rel_height + args->ascent; - stat = draw_driver_string(args->graphics, &string[index], length, font, format, - args->brush, &position, - DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, NULL); + GdipStringFormatGetGenericTypographic(&generic_format); + for (int i = 0; i < length; i += to_draw_length) + { + section = get_font_link_section(font_link_info, index + i); + to_draw_length = min(length, section->len - ((index + i) - section->index)); + TRACE("i %d, todraw %ld, used %s\n", index + i, to_draw_length, section->font == font_link_info->base_font ? "base font" : "map"); + GdipMeasureString(args->graphics, &string[index + i], to_draw_length, section->font, rect, generic_format, &bound_box, NULL, NULL); + stat = draw_driver_string(args->graphics, &string[index + i], to_draw_length, + section->font, format, args->brush, &position, + DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, NULL); + position.X += bound_box.Width; + } if (stat == Ok && underlined_index_count) { diff --git a/dlls/gdiplus/graphicspath.c b/dlls/gdiplus/graphicspath.c index 63f120a6aba..cc71a4b54be 100644 --- a/dlls/gdiplus/graphicspath.c +++ b/dlls/gdiplus/graphicspath.c @@ -950,7 +950,7 @@ struct format_string_args }; static GpStatus format_string_callback(HDC dc, - GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font, + GDIPCONST WCHAR *string, INT index, INT length, struct gdip_font_link_info *font_link_info, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, INT lineno, const RectF *bounds, INT *underlined_indexes, INT underlined_index_count, void *priv) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/4082
Hi, It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated. The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=141108 Your paranoid android. === build (build log) === error: patch failed: dlls/gdiplus/graphics.c:5350 Task: Patch failed to apply === debian11 (build log) === error: patch failed: dlls/gdiplus/graphics.c:5350 Task: Patch failed to apply === debian11b (build log) === error: patch failed: dlls/gdiplus/graphics.c:5350 Task: Patch failed to apply
participants (3)
-
Marvin -
Santino Mazza -
Santino Mazza (@tati1454)