-- v12: gdiplus: Implement font linking for gdiplus.
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 | 21 +++++++++++++++++++-- dlls/mlang/tests/mlang.c | 8 ++++---- 2 files changed, 23 insertions(+), 6 deletions(-)
diff --git a/dlls/mlang/mlang.c b/dlls/mlang/mlang.c index f424c87b860..55e6f6edaa5 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,25 @@ 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) +{ + return SUCCEEDED(MultiLanguage_create(NULL, (void**)&font_link_global)); +} + 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 55e6f6edaa5..c7e7f360f0b 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 | 159 +++++++++++++++++++++++++++++---- dlls/gdiplus/graphicspath.c | 2 +- 4 files changed, 158 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..ed769ce3359 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 || fitaux < to_measure_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,37 @@ 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; + SIZE size; + 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); + 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"); + font_link_get_text_extent_point(font_link_info, graphics, string, i, to_draw_length, 0, NULL, &size); + stat = draw_driver_string(graphics, &string[i], to_draw_length, + section->font, format, args->brush, &position, + DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, NULL); + position.X += size.cx; + i += to_draw_length; + if (stat != Ok || (i - index) >= length) break; + }
if (stat == Ok && underlined_index_count) { @@ -5705,10 +5828,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 Wed Feb 7 19:36:46 2024 +0000, Santino Mazza wrote:
About text rotation in draw_string_callback, should it be included in this merge request? dx and dy refers to direction? And where would they come from in draw_driver_string?
Given that scaling and rotating text using a transform already works, font linking should account for that.
In the case of SOFTWARE_GdipDrawDriverString, I think it should be the sum of the gmCellIncX and gmCellIncY values. I'm not sure how to work it out when using ExtTextOut.
On Wed Feb 7 19:48:00 2024 +0000, Esme Povirk wrote:
Given that scaling and rotating text using a transform already works, font linking should account for that. In the case of SOFTWARE_GdipDrawDriverString, I think it should be the sum of the gmCellIncX and gmCellIncY values. I'm not sure how to work it out when using ExtTextOut.
My knowledge is limited here, I tried to draw a string with `GdipRotateWorldTransform(graphics, 45, MatrixOrderPrepend);` and it rendered it well with font linking, but I don't think that's what you meant. I'm not sure where or how I should handle transforms in the font linking implementation, or what exactly I should be looking for.
On Wed Feb 7 21:20:08 2024 +0000, Santino Mazza wrote:
My knowledge is limited here, I tried to draw a string with `GdipRotateWorldTransform(graphics, 45, MatrixOrderPrepend);` and it rendered it well with font linking, but I don't think that's what you meant. I'm not sure where or how I should handle transforms in the font linking implementation, or what exactly I should be looking for.
I believe the text sizes you're getting from gdi32 are in gdi device coordinates, and draw_driver_string expects world coordinates. This is why measure_ranges_callback has to scale the size/coordinates it gets. I expect that to be needed in draw_string_callback too.
Try it with a scaling transform. I guess rotation on its own may work because the lack of a proper conversion from gdidevice to world coordinates, and the rotation applied as part of the world transform in draw_driver_string, cancel each other out.