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)