From: Jactry Zeng jzeng@codeweavers.com
Signed-off-by: Jactry Zeng jzeng@codeweavers.com --- dlls/riched20/editor.h | 1 + dlls/riched20/editstr.h | 1 + dlls/riched20/paint.c | 17 ++++++++ dlls/riched20/run.c | 9 ++++- dlls/riched20/style.c | 12 ++++++ dlls/riched20/tests/editor.c | 52 ++++++++++++++++++++++++ dlls/riched20/wrap.c | 78 ++++++++++++++++++++++++++++++++++++ 7 files changed, 168 insertions(+), 2 deletions(-)
diff --git a/dlls/riched20/editor.h b/dlls/riched20/editor.h index 965eb44bba6..751f51027ca 100644 --- a/dlls/riched20/editor.h +++ b/dlls/riched20/editor.h @@ -74,6 +74,7 @@ BOOL cfany_to_cf2w(CHARFORMAT2W *to, const CHARFORMAT2W *from) DECLSPEC_HIDDEN; BOOL cf2w_to_cfany(CHARFORMAT2W *to, const CHARFORMAT2W *from) DECLSPEC_HIDDEN; void ME_CopyCharFormat(CHARFORMAT2W *pDest, const CHARFORMAT2W *pSrc) DECLSPEC_HIDDEN; /* only works with 2W structs */ void ME_CharFormatFromLogFont(HDC hDC, const LOGFONTW *lf, CHARFORMAT2W *fmt) DECLSPEC_HIDDEN; /* ditto */ +ME_Style *duplicate_style( ME_Style *style );
/* list.c */ void ME_InsertBefore(ME_DisplayItem *diWhere, ME_DisplayItem *diWhat) DECLSPEC_HIDDEN; diff --git a/dlls/riched20/editstr.h b/dlls/riched20/editstr.h index f11058012e9..65a7f2c5a75 100644 --- a/dlls/riched20/editstr.h +++ b/dlls/riched20/editstr.h @@ -76,6 +76,7 @@ typedef struct tagME_Style int nRefs; /* reference count */ SCRIPT_CACHE script_cache; struct list entry; + LOGFONTW fallback_font; } ME_Style;
typedef enum { diff --git a/dlls/riched20/paint.c b/dlls/riched20/paint.c index 245afff77dc..ebedd19b321 100644 --- a/dlls/riched20/paint.c +++ b/dlls/riched20/paint.c @@ -363,9 +363,26 @@ static void draw_text( ME_Context *c, ME_Run *run, int x, int y, BOOL selected, if (paint_bg) old_back = SetBkColor( c->hDC, back_color );
if (run->para->nFlags & MEPF_COMPLEX) + { + HFONT old_font = NULL, fallback = NULL; + + if (run->style->fallback_font.lfFaceName[0]) + { + fallback = CreateFontIndirectW( &run->style->fallback_font ); + if (fallback) + old_font = SelectObject( c->hDC, fallback ); + } + ScriptTextOut( c->hDC, &run->style->script_cache, x, y, paint_bg ? ETO_OPAQUE : 0, sel_rect, &run->script_analysis, NULL, 0, run->glyphs, run->num_glyphs, run->advances, NULL, run->offsets ); + + if (old_font) + { + SelectObject( c->hDC, old_font ); + DeleteObject( fallback ); + } + } else ExtTextOutW( c->hDC, x, y, paint_bg ? ETO_OPAQUE : 0, sel_rect, text, run->len, NULL );
diff --git a/dlls/riched20/run.c b/dlls/riched20/run.c index 533c61da01c..e468d441da4 100644 --- a/dlls/riched20/run.c +++ b/dlls/riched20/run.c @@ -348,8 +348,13 @@ ME_Run *run_create( ME_Style *s, int flags )
if (!item) return NULL;
- ME_AddRefStyle( s ); - run->style = s; + if (s->fallback_font.lfFaceName[0]) + run->style = duplicate_style( s ); + else + { + ME_AddRefStyle( s ); + run->style = s; + } run->reobj = NULL; run->nFlags = flags; run->nCharOfs = -1; diff --git a/dlls/riched20/style.c b/dlls/riched20/style.c index 9c9b5cbb4e0..8540e407610 100644 --- a/dlls/riched20/style.c +++ b/dlls/riched20/style.c @@ -129,12 +129,24 @@ ME_Style *ME_MakeStyle(CHARFORMAT2W *style) memset(&s->tm, 0, sizeof(s->tm)); s->tm.tmAscent = -1; s->script_cache = NULL; + memset( &s->fallback_font, 0, sizeof( s->fallback_font ) ); list_init(&s->entry); all_refs++; TRACE_(richedit_style)("ME_MakeStyle %p, total refs=%d\n", s, all_refs); return s; }
+ME_Style *duplicate_style( ME_Style *style ) +{ + ME_Style *new_style; + + new_style = ME_MakeStyle( &style->fmt ); + new_style->tm = style->tm; + new_style->fallback_font = style->fallback_font; + + return new_style; +} + #define COPY_STYLE_ITEM(mask, member) \ if (mod->dwMask & mask) { \ fmt.dwMask |= mask;\ diff --git a/dlls/riched20/tests/editor.c b/dlls/riched20/tests/editor.c index 03c07829ee4..7e6325da1a9 100644 --- a/dlls/riched20/tests/editor.c +++ b/dlls/riched20/tests/editor.c @@ -9026,6 +9026,57 @@ static void test_window_classes(void) } }
+static void test_font_fallback(void) +{ + static const WCHAR text[] = L"English\x65e5\x672c\x8a9e\x306b\x307b\x3093\x3054\x30cb\x30db\x30f3\x30b4" + "\xd55c\xad6d\xc5b4\x7b80\x4f53\x5b57\x7e41\x9ad4\x5b57"; + CHARFORMAT2W format; + WCHAR buffer[1024]; + LRESULT result; + HWND hwnd; + int i; + struct font_fallback_test + { + int start, end; + const WCHAR *expected_string; + } font_fallback_tests[] = + { + { 0, 7, L"English" }, + { 7, 10, L"\x65e5\x672c\x8a9e" }, /* Kanji */ + { 10, 14, L"\x306b\x307b\x3093\x3054" }, /* Hiragana */ + { 14, 18, L"\x30cb\x30db\x30f3\x30b4" }, /* Katakana */ + { 18, 21, L"\xd55c\xad6d\xc5b4" }, /* Hangeul */ + { 21, 24, L"\x7b80\x4f53\x5b57" }, /* Simplified Chinese */ + { 24, 27, L"\x7e41\x9ad4\x5b57" }, /* Traditional Chinese */ + { 5, 9, L"sh\x65e5\x672c" }, /* Latin and Kanji */ + }; + + hwnd = new_richeditW(NULL); + + SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)text); + memset(&format, 0, sizeof(format)); + format.cbSize = sizeof(format); + format.dwMask = CFM_FACE; + lstrcpyW(format.szFaceName, L"Tahoma"); + result = SendMessageW(hwnd, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&format); + ok(result, "Failed to set default format.\n"); + + for (i = 0; i < ARRAYSIZE(font_fallback_tests); i++) + { + SendMessageW(hwnd, EM_SETSEL, font_fallback_tests[i].start, font_fallback_tests[i].end); + memset(buffer, 0, sizeof(buffer)); + result = SendMessageW(hwnd, EM_GETSELTEXT, sizeof(buffer), (LPARAM)buffer); + ok(!lstrcmpW(buffer, font_fallback_tests[i].expected_string), "Got wrong string %s.\n", debugstr_w(buffer)); + memset(&format, 0, sizeof(format)); + format.cbSize = sizeof(format); + result = SendMessageW(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&format); + ok(result, "Failed to get format.\n"); + ok(!lstrcmpW(format.szFaceName, L"Tahoma"), "Got wrong font name %s.\n", debugstr_w(format.szFaceName)); + } + + DestroyWindow(hwnd); +} + START_TEST( editor ) { BOOL ret; @@ -9102,6 +9153,7 @@ START_TEST( editor ) test_eop_char_fmt(); test_para_numbering(); test_EM_SELECTIONTYPE(); + test_font_fallback();
/* Set the environment variable WINETEST_RICHED20 to keep windows * responsive and open for 30 seconds. This is useful for debugging. diff --git a/dlls/riched20/wrap.c b/dlls/riched20/wrap.c index 7010b6ffc35..da077045189 100644 --- a/dlls/riched20/wrap.c +++ b/dlls/riched20/wrap.c @@ -61,8 +61,53 @@ static BOOL get_run_glyph_buffers( ME_Run *run ) return TRUE; }
+struct richedit_fallback +{ + OPENTYPE_TAG script_tag; + WCHAR *font_name; +} richedit_fallbacks[] = {}; + +static const WCHAR *find_fallback_font( OPENTYPE_TAG script_tag ) +{ + int count; + + count = ARRAYSIZE( richedit_fallbacks ); + while (count--) + if (richedit_fallbacks[count].script_tag == script_tag) + return richedit_fallbacks[count].font_name; + + WARN( "Failed to find a fallback font for %s.\n", debugstr_tag( script_tag ) ); + return NULL; +} + +static BOOL requires_fallback( HDC dc, ME_Run *run ) +{ + SCRIPT_CACHE script_cache = NULL; + static const WCHAR *text; + WORD *glyphs; + int len; + + if (!run->script_tag) + return FALSE; + + len = run->len; + if (!(glyphs = heap_calloc( len, sizeof( *glyphs ) ))) + return FALSE; + + text = get_text( run, 0 ); + if (ScriptGetCMap( dc, &script_cache, text, len, 0, glyphs ) != S_OK) + { + heap_free( glyphs ); + return TRUE; + } + heap_free( glyphs ); + + return FALSE; +} + static HRESULT shape_run( ME_Context *c, ME_Run *run ) { + HFONT old_font = NULL, fallback = NULL; SCRIPT_GLYPHPROP *glyph_props = NULL; SCRIPT_CHARPROP *char_props; HRESULT hr; @@ -86,6 +131,33 @@ static HRESULT shape_run( ME_Context *c, ME_Run *run ) }
select_style( c, run->style ); + if (requires_fallback( c->hDC, run )) + { + static const WCHAR *font_name; + + font_name = find_fallback_font( run->script_tag ); + if (font_name) + { + if (wcscmp( run->style->fallback_font.lfFaceName, font_name )) + { + ME_Style *style; + + GetObjectW( GetCurrentObject( c->hDC, OBJ_FONT ), sizeof( run->style->fallback_font ), + &run->style->fallback_font ); + style = duplicate_style( run->style ); + ME_ReleaseStyle( run->style ); + run->style = style; + wcscpy( run->style->fallback_font.lfFaceName, font_name ); + } + TRACE( "Falling back to %s font for %s.\n", debugstr_w( font_name ), debugstr_tag( run->script_tag ) ); + fallback = CreateFontIndirectW( &run->style->fallback_font ); + if (fallback) + old_font = SelectObject( c->hDC, fallback ); + } + } + else + run->style->fallback_font.lfFaceName[0] = 0; + while (1) { heap_free( glyph_props ); @@ -108,6 +180,12 @@ static HRESULT shape_run( ME_Context *c, ME_Run *run ) NULL, NULL, 0, get_text( run, 0 ), run->clusters, char_props, run->len, run->glyphs, glyph_props, run->num_glyphs, run->advances, run->offsets, NULL );
+ if (old_font) + { + SelectObject( c->hDC, old_font ); + DeleteObject( fallback ); + } + if (SUCCEEDED(hr)) { for (i = 0, run->nWidth = 0; i < run->num_glyphs; i++)