From: Jactry Zeng <jzeng(a)codeweavers.com>
Signed-off-by: Jactry Zeng <jzeng(a)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++)
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/417