-- v8: gdiplus: Implement font linking for gdiplus. gdiplus/tests: Add interactive test for font linking. gdiplus: Replace HDC with GpGraphics in parameters. mlang: Fix bug with codepage priority in GetStrCodePages. mlang/tests: Test codepages priority bug in GetStrCodepages. mlang: Implement GetGlobalFontLinkObject.
From: Santino Mazza smazza@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 ee5ef5af738..99664a0e556 100644 --- a/dlls/mlang/tests/mlang.c +++ b/dlls/mlang/tests/mlang.c @@ -2295,6 +2295,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); @@ -2305,7 +2310,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 S_OK got %#lx\n", ret); + IMLangFontLink_Release(IMLFL); + + ret = IUnknown_QueryInterface((IUnknown*)unknown, &IID_IMLangCodePages, (void**)&IMLCP); + ok(ret == S_OK, "expected S_OK 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) @@ -2778,6 +2802,8 @@ START_TEST(mlang) if (!init_function_ptrs()) return;
+ test_GetGlobalFontLinkObject(); + CoInitialize(NULL); test_Rfc1766ToLcid(); test_LcidToRfc1766(); @@ -2785,8 +2811,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);
From: Santino Mazza smazza@codeweavers.com
--- dlls/mlang/mlang.c | 25 +++++++++++++++++++++++-- dlls/mlang/tests/mlang.c | 8 ++++---- 2 files changed, 27 insertions(+), 6 deletions(-)
diff --git a/dlls/mlang/mlang.c b/dlls/mlang/mlang.c index f424c87b860..1d4ad9f5626 100644 --- a/dlls/mlang/mlang.c +++ b/dlls/mlang/mlang.c @@ -44,6 +44,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(mlang);
#include "initguid.h"
+static INIT_ONCE font_link_global_init_once = INIT_ONCE_STATIC_INIT; +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); @@ -3882,11 +3885,29 @@ HRESULT WINAPI DllCanUnloadNow(void) return dll_count == 0 ? S_OK : S_FALSE; }
+static BOOL WINAPI allocate_font_link_cb(PINIT_ONCE init_once, PVOID args, PVOID *context) +{ + HRESULT hr = S_OK; + if (!font_link_global) + hr = MultiLanguage_create(NULL, (void**)&font_link_global); + + return SUCCEEDED(hr); +} + HRESULT WINAPI GetGlobalFontLinkObject(void **unknown) { + TRACE("%p\n", unknown); + if (!unknown) return E_INVALIDARG;
- FIXME("%p: stub\n", unknown); + if (!InitOnceExecuteOnce(&font_link_global_init_once, allocate_font_link_cb, NULL, NULL)) + { + ERR("Failed to create global font link object.\n"); + return E_FAIL; + }
- 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 99664a0e556..4ab4b579245 100644 --- a/dlls/mlang/tests/mlang.c +++ b/dlls/mlang/tests/mlang.c @@ -2306,18 +2306,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 S_OK got %#lx\n", ret);
From: Santino Mazza smazza@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 4ab4b579245..f8e37565115 100644 --- a/dlls/mlang/tests/mlang.c +++ b/dlls/mlang/tests/mlang.c @@ -1324,6 +1324,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);
From: Santino Mazza smazza@codeweavers.com
--- dlls/mlang/mlang.c | 9 +++------ dlls/mlang/tests/mlang.c | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-)
diff --git a/dlls/mlang/mlang.c b/dlls/mlang/mlang.c index 1d4ad9f5626..3c1702e1572 100644 --- a/dlls/mlang/mlang.c +++ b/dlls/mlang/mlang.c @@ -3336,23 +3336,20 @@ static HRESULT WINAPI fnIMLangFontLink2_GetStrCodePages( IMLangFontLink2* iface,
for (i = 0; i < src_len; i++) { - DWORD cp, next_cp = 0; + DWORD cp = 0; HRESULT ret;
ret = IMLangFontLink2_GetCharCodePages(iface, src[i], &cp); - if (i + 1 < src_len) - ret = IMLangFontLink2_GetCharCodePages(iface, src[i + 1], &next_cp); 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--; break; } - - if ((priority_cp & cps) && !(priority_cp & next_cp)) break; }
if (codepages) *codepages = cps; diff --git a/dlls/mlang/tests/mlang.c b/dlls/mlang/tests/mlang.c index f8e37565115..6bb973dc285 100644 --- a/dlls/mlang/tests/mlang.c +++ b/dlls/mlang/tests/mlang.c @@ -1330,7 +1330,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;
From: Santino Mazza smazza@codeweavers.com
--- dlls/gdiplus/gdiplus_private.h | 4 ++-- dlls/gdiplus/graphics.c | 36 ++++++++++++++++------------------ dlls/gdiplus/graphicspath.c | 8 ++++---- 3 files changed, 23 insertions(+), 25 deletions(-)
diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h index 6f7e72124c2..a72731d79ac 100644 --- a/dlls/gdiplus/gdiplus_private.h +++ b/dlls/gdiplus/gdiplus_private.h @@ -606,13 +606,13 @@ static inline const void *buffer_read(struct memory_buffer *mbuf, INT size) return NULL; }
-typedef GpStatus (*gdip_format_string_callback)(HDC hdc, +typedef GpStatus (*gdip_format_string_callback)(GpGraphics *graphics, GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, INT lineno, const RectF *bounds, INT *underlined_indexes, INT underlined_index_count, void *user_data);
-GpStatus gdip_format_string(HDC hdc, +GpStatus gdip_format_string(GpGraphics *graphics, GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, int ignore_empty_clip, gdip_format_string_callback callback, void *user_data); diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c index 9fd399e3cde..26c41601be6 100644 --- a/dlls/gdiplus/graphics.c +++ b/dlls/gdiplus/graphics.c @@ -5197,7 +5197,7 @@ GpStatus WINGDIPAPI GdipIsVisibleRectI(GpGraphics *graphics, INT x, INT y, INT w return GdipIsVisibleRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, result); }
-GpStatus gdip_format_string(HDC hdc, +GpStatus gdip_format_string(GpGraphics *graphics, GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, int ignore_empty_clip, gdip_format_string_callback callback, void *user_data) @@ -5279,7 +5279,7 @@ GpStatus gdip_format_string(HDC hdc, halign = format->align;
while(sum < length){ - GetTextExtentExPointW(hdc, stringdup + sum, length - sum, + GetTextExtentExPointW(graphics->hdc, stringdup + sum, length - sum, nwidth, &fit, NULL, &size); fitcpy = fit;
@@ -5328,7 +5328,7 @@ GpStatus gdip_format_string(HDC hdc, else lineend = fit;
- GetTextExtentExPointW(hdc, stringdup + sum, lineend, + GetTextExtentExPointW(graphics->hdc, stringdup + sum, lineend, nwidth, &j, NULL, &size);
bounds.Width = size.cx; @@ -5362,7 +5362,7 @@ GpStatus gdip_format_string(HDC hdc, if (hotkeyprefix_offsets[hotkeyprefix_end_pos] >= sum + lineend) break;
- stat = callback(hdc, stringdup, sum, lineend, + stat = callback(graphics, stringdup, sum, lineend, font, rect, format, lineno, &bounds, &hotkeyprefix_offsets[hotkeyprefix_pos], hotkeyprefix_end_pos-hotkeyprefix_pos, user_data); @@ -5428,7 +5428,7 @@ struct measure_ranges_args { REAL rel_width, rel_height; };
-static GpStatus measure_ranges_callback(HDC hdc, +static GpStatus measure_ranges_callback(GpGraphics *graphics, GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, INT lineno, const RectF *bounds, INT *underlined_indexes, @@ -5450,11 +5450,11 @@ static GpStatus measure_ranges_callback(HDC hdc, range_rect.Y = bounds->Y / args->rel_height; range_rect.Height = bounds->Height / args->rel_height;
- GetTextExtentExPointW(hdc, string + index, range_start - index, + GetTextExtentExPointW(graphics->hdc, string + index, range_start - index, INT_MAX, NULL, NULL, &range_size); range_rect.X = (bounds->X + range_size.cx) / args->rel_width;
- GetTextExtentExPointW(hdc, string + index, range_end - index, + GetTextExtentExPointW(graphics->hdc, string + index, range_end - index, INT_MAX, NULL, NULL, &range_size); range_rect.Width = (bounds->X + range_size.cx) / args->rel_width - range_rect.X;
@@ -5532,7 +5532,7 @@ GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
gdi_transform_acquire(graphics);
- stat = gdip_format_string(hdc, string, length, font, &scaled_rect, stringFormat, + stat = gdip_format_string(graphics, string, length, font, &scaled_rect, stringFormat, (stringFormat->attr & StringFormatFlagsNoClip) != 0, measure_ranges_callback, &args);
gdi_transform_release(graphics); @@ -5553,7 +5553,7 @@ struct measure_string_args { REAL rel_width, rel_height; };
-static GpStatus measure_string_callback(HDC hdc, +static GpStatus measure_string_callback(GpGraphics *graphics, GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, INT lineno, const RectF *bounds, INT *underlined_indexes, @@ -5646,7 +5646,7 @@ GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
gdi_transform_acquire(graphics);
- gdip_format_string(hdc, string, length, font, &scaled_rect, format, TRUE, + gdip_format_string(graphics, string, length, font, &scaled_rect, format, TRUE, measure_string_callback, &args);
gdi_transform_release(graphics); @@ -5667,12 +5667,11 @@ GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics, }
struct draw_string_args { - GpGraphics *graphics; GDIPCONST GpBrush *brush; REAL x, y, rel_width, rel_height, ascent; };
-static GpStatus draw_string_callback(HDC hdc, +static GpStatus draw_string_callback(GpGraphics *graphics, GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, INT lineno, const RectF *bounds, INT *underlined_indexes, @@ -5685,7 +5684,7 @@ static GpStatus draw_string_callback(HDC hdc, 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, + stat = draw_driver_string(graphics, &string[index], length, font, format, args->brush, &position, DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, NULL);
@@ -5695,7 +5694,7 @@ static GpStatus draw_string_callback(HDC hdc, REAL underline_y, underline_height; int i;
- GetOutlineTextMetricsW(hdc, sizeof(otm), &otm); + GetOutlineTextMetricsW(graphics->hdc, sizeof(otm), &otm);
underline_height = otm.otmsUnderscoreSize / args->rel_height; underline_y = position.Y - otm.otmsUnderscorePosition / args->rel_height - underline_height / 2; @@ -5706,13 +5705,13 @@ static GpStatus draw_string_callback(HDC hdc, SIZE text_size; INT ofs = underlined_indexes[i] - index;
- GetTextExtentExPointW(hdc, string + index, ofs, INT_MAX, NULL, NULL, &text_size); + GetTextExtentExPointW(graphics->hdc, string + index, ofs, INT_MAX, NULL, NULL, &text_size); start_x = text_size.cx / args->rel_width;
- GetTextExtentExPointW(hdc, string + index, ofs+1, INT_MAX, NULL, NULL, &text_size); + GetTextExtentExPointW(graphics->hdc, string + index, ofs+1, INT_MAX, NULL, NULL, &text_size); end_x = text_size.cx / args->rel_width;
- GdipFillRectangle(args->graphics, (GpBrush*)args->brush, position.X+start_x, underline_y, end_x-start_x, underline_height); + GdipFillRectangle(graphics, (GpBrush*)args->brush, position.X+start_x, underline_y, end_x-start_x, underline_height); } }
@@ -5810,7 +5809,6 @@ GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string get_font_hfont(graphics, font, format, &gdifont, NULL, NULL); SelectObject(hdc, gdifont);
- args.graphics = graphics; args.brush = brush;
args.x = rect->X; @@ -5824,7 +5822,7 @@ GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string GetTextMetricsW(hdc, &textmetric); args.ascent = textmetric.tmAscent / rel_height;
- gdip_format_string(hdc, string, length, font, &scaled_rect, format, TRUE, + gdip_format_string(graphics, string, length, font, &scaled_rect, format, TRUE, draw_string_callback, &args);
gdi_transform_release(graphics); diff --git a/dlls/gdiplus/graphicspath.c b/dlls/gdiplus/graphicspath.c index 09cd99e4467..f377aed98f6 100644 --- a/dlls/gdiplus/graphicspath.c +++ b/dlls/gdiplus/graphicspath.c @@ -997,7 +997,7 @@ struct format_string_args float ascent; };
-static GpStatus format_string_callback(HDC dc, +static GpStatus format_string_callback(GpGraphics *graphics, GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, INT lineno, const RectF *bounds, INT *underlined_indexes, @@ -1023,7 +1023,7 @@ static GpStatus format_string_callback(HDC dc, TTPOLYGONHEADER *ph = NULL, *origph; char *start; DWORD len, ofs = 0; - len = GetGlyphOutlineW(dc, string[i], GGO_BEZIER, &gm, 0, NULL, &identity); + len = GetGlyphOutlineW(graphics->hdc, string[i], GGO_BEZIER, &gm, 0, NULL, &identity); if (len == GDI_ERROR) { status = GenericError; @@ -1037,7 +1037,7 @@ static GpStatus format_string_callback(HDC dc, status = OutOfMemory; break; } - GetGlyphOutlineW(dc, string[i], GGO_BEZIER, &gm, len, start, &identity); + GetGlyphOutlineW(graphics->hdc, string[i], GGO_BEZIER, &gm, len, start, &identity);
ofs = 0; while (ofs < len) @@ -1165,7 +1165,7 @@ GpStatus WINGDIPAPI GdipAddPathString(GpPath* path, GDIPCONST WCHAR* string, INT args.maxY = 0; args.scale = emSize / native_height; args.ascent = textmetric.tmAscent * args.scale; - status = gdip_format_string(dc, string, length, NULL, &scaled_layout_rect, + status = gdip_format_string(graphics, string, length, NULL, &scaled_layout_rect, format, TRUE, format_string_callback, &args);
DeleteDC(dc);
From: Santino Mazza smazza@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 54960beabb3..d72a2c7226b 100644 --- a/dlls/gdiplus/tests/graphics.c +++ b/dlls/gdiplus/tests/graphics.c @@ -2357,10 +2357,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(0xFF000000, (GpSolidFill**)&brush); + expect(Ok, status); + rect.X = 0; + rect.Y = 0; + rect.Width = 0; + rect.Height = 14; + GdipGraphicsClear(graphics, 0xFFFFFFFF); + 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;
From: Santino Mazza smazza@codeweavers.com
--- dlls/gdiplus/Makefile.in | 2 +- dlls/gdiplus/gdiplus_private.h | 16 +++- dlls/gdiplus/graphics.c | 161 +++++++++++++++++++++++++++++---- dlls/gdiplus/graphicspath.c | 2 +- 4 files changed, 160 insertions(+), 21 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 a72731d79ac..2b5dbee43e9 100644 --- a/dlls/gdiplus/gdiplus_private.h +++ b/dlls/gdiplus/gdiplus_private.h @@ -606,8 +606,22 @@ 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 { + struct list entry; + DWORD start; /* The starting index of the string where the font applies. */ + DWORD end; /* The end index of the string. */ + GpFont *font; +}; + +struct gdip_font_link_info { + GDIPCONST GpFont *base_font; + struct list sections; +}; + + typedef GpStatus (*gdip_format_string_callback)(GpGraphics *graphics, - 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 26c41601be6..0861b138b21 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" @@ -5197,6 +5198,110 @@ 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 */ +static void generate_font_link_info(GpGraphics *graphics, WCHAR *string, DWORD length, GDIPCONST GpFont *base_font, + struct gdip_font_link_info *font_link_info) +{ + IUnknown *unk; + IMLangFontLink *iMLFL; + GpFont *gpfont; + HFONT map_hfont, hfont, old_font; + LONG processed, progress = 0; + struct gdip_font_link_section *section; + DWORD font_codepages, string_codepages; + + list_init(&font_link_info->sections); + font_link_info->base_font = base_font; + + GetGlobalFontLinkObject((void**)&unk); + IUnknown_QueryInterface(unk, &IID_IMLangFontLink, (void**)&iMLFL); + IUnknown_Release(unk); + + get_font_hfont(graphics, base_font, NULL, &hfont, NULL, NULL); + IMLangFontLink_GetFontCodePages(iMLFL, graphics->hdc, hfont, &font_codepages); + + while (progress < length) + { + section = calloc(1, sizeof(*section)); + section->start = 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, graphics->hdc, string_codepages, hfont, &map_hfont); + old_font = SelectObject(graphics->hdc, map_hfont); + GdipCreateFontFromDC(graphics->hdc, &gpfont); + SelectObject(graphics->hdc, old_font); + IMLangFontLink_ReleaseFont(iMLFL, map_hfont); + section->font = gpfont; + } + + section->end = section->start + processed; + list_add_tail(&font_link_info->sections, §ion->entry); + progress += processed; + } + + DeleteObject(hfont); + IMLangFontLink_Release(iMLFL); +} + +static void font_link_get_text_extent_point(struct gdip_font_link_info *font_link_info, GpGraphics *graphics, LPCWSTR string, + INT index, int length, int max_ext, LPINT fit, SIZE *size) +{ + DWORD to_measure_length; + HFONT hfont, oldhfont; + SIZE sizeaux = { 0 }; + int i = index, fitaux = 0; + struct gdip_font_link_section *section; + + size->cx = 0; + size->cy = 0; + + if (fit) + *fit = 0; + + LIST_FOR_EACH_ENTRY(section, &font_link_info->sections, struct gdip_font_link_section, entry) + { + if (i >= section->end) continue; + + to_measure_length = min(length - (i - index), section->end - i); + + get_font_hfont(graphics, section->font, NULL, &hfont, NULL, NULL); + oldhfont = SelectObject(graphics->hdc, hfont); + GetTextExtentExPointW(graphics->hdc, &string[i], to_measure_length, max_ext, &fitaux, NULL, &sizeaux); + SelectObject(graphics->hdc, oldhfont); + DeleteObject(hfont); + + max_ext -= sizeaux.cx; + if (fit) + *fit += fitaux; + size->cx += sizeaux.cx; + size->cy = max(size->cy, sizeaux.cy); + + i += to_measure_length; + if ((i - index) >= length) break; + } +} + +static void release_font_link_info(struct gdip_font_link_info *font_link_info) +{ + struct list *entry; + + while ((entry = list_head(&font_link_info->sections))) + { + struct gdip_font_link_section *section = LIST_ENTRY(entry, struct gdip_font_link_section, entry); + list_remove(entry); + if (section->font != font_link_info->base_font) + GdipDeleteFont(section->font); + free(section); + } +} + GpStatus gdip_format_string(GpGraphics *graphics, GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, int ignore_empty_clip, @@ -5213,6 +5318,7 @@ GpStatus gdip_format_string(GpGraphics *graphics, 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); @@ -5278,9 +5384,10 @@ GpStatus gdip_format_string(GpGraphics *graphics,
halign = format->align;
+ generate_font_link_info(graphics, stringdup, length, font, &font_link_info); + while(sum < length){ - GetTextExtentExPointW(graphics->hdc, stringdup + sum, length - sum, - nwidth, &fit, NULL, &size); + font_link_get_text_extent_point(&font_link_info, graphics, stringdup, sum, length - sum, nwidth, &fit, &size); fitcpy = fit;
if(fit == 0) @@ -5328,8 +5435,7 @@ GpStatus gdip_format_string(GpGraphics *graphics, else lineend = fit;
- GetTextExtentExPointW(graphics->hdc, stringdup + sum, lineend, - nwidth, &j, NULL, &size); + font_link_get_text_extent_point(&font_link_info, graphics, stringdup, sum, lineend, nwidth, &j, &size);
bounds.Width = size.cx;
@@ -5363,7 +5469,7 @@ GpStatus gdip_format_string(GpGraphics *graphics, break;
stat = callback(graphics, 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);
@@ -5394,6 +5500,7 @@ GpStatus gdip_format_string(GpGraphics *graphics, break; }
+ release_font_link_info(&font_link_info); free(stringdup); free(hotkeyprefix_offsets);
@@ -5429,7 +5536,8 @@ struct measure_ranges_args { };
static GpStatus measure_ranges_callback(GpGraphics *graphics, - 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) @@ -5450,12 +5558,10 @@ static GpStatus measure_ranges_callback(GpGraphics *graphics, range_rect.Y = bounds->Y / args->rel_height; range_rect.Height = bounds->Height / args->rel_height;
- GetTextExtentExPointW(graphics->hdc, string + index, range_start - index, - INT_MAX, NULL, NULL, &range_size); + font_link_get_text_extent_point(font_link_info, graphics, string, index, range_start - index, INT_MAX, NULL, &range_size); range_rect.X = (bounds->X + range_size.cx) / args->rel_width;
- GetTextExtentExPointW(graphics->hdc, string + index, range_end - index, - INT_MAX, NULL, NULL, &range_size); + font_link_get_text_extent_point(font_link_info, graphics, string, index, range_end - index, INT_MAX, NULL, &range_size); range_rect.Width = (bounds->X + range_size.cx) / args->rel_width - range_rect.X;
stat = GdipCombineRegionRect(args->regions[i], &range_rect, CombineModeUnion); @@ -5554,7 +5660,8 @@ struct measure_string_args { };
static GpStatus measure_string_callback(GpGraphics *graphics, - 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) @@ -5672,21 +5779,39 @@ struct draw_string_args { };
static GpStatus draw_string_callback(GpGraphics *graphics, - 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; + int i = index; 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(graphics, &string[index], length, font, format, - args->brush, &position, - DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, NULL); + GdipStringFormatGetGenericTypographic(&generic_format); + LIST_FOR_EACH_ENTRY(section, &font_link_info->sections, struct gdip_font_link_section, entry) + { + if (i >= section->end) continue; + + to_draw_length = min(length - (i - index), section->end - i); + TRACE("index %d, todraw %ld, used %s\n", i, to_draw_length, section->font == font_link_info->base_font ? "base font" : "map"); + GdipMeasureString(graphics, &string[i], to_draw_length, section->font, rect, generic_format, &bound_box, NULL, NULL); + stat = draw_driver_string(graphics, &string[i], to_draw_length, + section->font, format, args->brush, &position, + DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, NULL); + position.X += bound_box.Width; + i += to_draw_length; + if (stat != Ok || (i - index) >= length) break; + }
if (stat == Ok && underlined_index_count) { @@ -5705,10 +5830,10 @@ static GpStatus draw_string_callback(GpGraphics *graphics, SIZE text_size; INT ofs = underlined_indexes[i] - index;
- GetTextExtentExPointW(graphics->hdc, string + index, ofs, INT_MAX, NULL, NULL, &text_size); + font_link_get_text_extent_point(font_link_info, graphics, string, index, ofs, INT_MAX, NULL, &text_size); start_x = text_size.cx / args->rel_width;
- GetTextExtentExPointW(graphics->hdc, string + index, ofs+1, INT_MAX, NULL, NULL, &text_size); + font_link_get_text_extent_point(font_link_info, graphics, string, index, ofs+1, INT_MAX, NULL, &text_size); end_x = text_size.cx / args->rel_width;
GdipFillRectangle(graphics, (GpBrush*)args->brush, position.X+start_x, underline_y, end_x-start_x, underline_height); diff --git a/dlls/gdiplus/graphicspath.c b/dlls/gdiplus/graphicspath.c index f377aed98f6..439541693b6 100644 --- a/dlls/gdiplus/graphicspath.c +++ b/dlls/gdiplus/graphicspath.c @@ -998,7 +998,7 @@ struct format_string_args };
static GpStatus format_string_callback(GpGraphics *graphics, - 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)
On Fri Feb 2 16:02:05 2024 +0000, Esme Povirk wrote:
Apparently this function doesn't use GpFont, but it probably will eventually have to be aware of the section fonts. Unless it really doesn't need that for some reason, I think a FIXME should be added for the case where there isn't just 1 section containing the base font.
I think with the current revision, the behavior of the measurement functions is also updated, does that seem right?
Esme Povirk (@madewokherd) commented about dlls/mlang/mlang.c:
return dll_count == 0 ? S_OK : S_FALSE;
}
+static BOOL WINAPI allocate_font_link_cb(PINIT_ONCE init_once, PVOID args, PVOID *context) +{
- HRESULT hr = S_OK;
- if (!font_link_global)
I don't think we need the NULL check since this is only running once.
Esme Povirk (@madewokherd) commented about dlls/gdiplus/graphics.c:
to_measure_length = min(length - (i - index), section->end - i);
get_font_hfont(graphics, section->font, NULL, &hfont, NULL, NULL);
oldhfont = SelectObject(graphics->hdc, hfont);
GetTextExtentExPointW(graphics->hdc, &string[i], to_measure_length, max_ext, &fitaux, NULL, &sizeaux);
SelectObject(graphics->hdc, oldhfont);
DeleteObject(hfont);
max_ext -= sizeaux.cx;
if (fit)
*fit += fitaux;
size->cx += sizeaux.cx;
size->cy = max(size->cy, sizeaux.cy);
i += to_measure_length;
if ((i - index) >= length) break;
I think maybe the loop should also be broken in the case where `fitaux < to_measure_length`?
On Sat Feb 3 15:35:23 2024 +0000, Esme Povirk wrote:
I think maybe the loop should also be broken in the case where `fitaux < to_measure_length`?
Should it break? I think maybe it should check for fitaux < to_measure_length and just stop increasing fit, but continue increasing the size struct, so it returns the size of the string and the amount of characters that will actually fit in max_ext. Maybe I'm misunderstanding how GetTextExPoint works when the amount of characters that will fit in max_ext is less than the string length.
Also, I've been thinking about what would happen if the text is written vertically instead of horizontally, should it account for that?
On Mon Feb 5 16:43:52 2024 +0000, Santino Mazza wrote:
Should it break? I think maybe it should check for fitaux < to_measure_length and just stop increasing fit, but continue increasing the size struct, so it returns the size of the string and the amount of characters that will actually fit in max_ext. Maybe I'm misunderstanding how GetTextExPoint works when the amount of characters that will fit in max_ext is less than the string length. Also, I've been thinking about what would happen if the text is written vertically instead of horizontally, should it account for that?
I think the size should include only the characters in fitaux, and if you continue increasing size it may include later characters that would hypothetically fit if you skipped drawing some.
On Mon Feb 5 19:22:12 2024 +0000, Esme Povirk wrote:
I think the size should include only the characters in fitaux, and if you continue increasing size it may include later characters that would hypothetically fit if you skipped drawing some.
While trying to change this I found another bug where for some reason the draw_string_callback function would write the sections over the old one, instead of continuing, however I couldn't reproduce it in a test (it happens in the Brothers: a Tale of Two Sons game launcher, in the key bindings menu, with the Russian language). I accidentally fixed it when I was trying to see if we could use font_link_get_text_extent_point directly instead of GdipMeasureString in draw_string_callback.