Signed-off-by: Jactry Zeng jzeng@codeweavers.com
-- v3: riched20: Add font fallbacks for CJK. riched20: Add font fallback support. riched20: Use OpenType functions for painting and generating script tags. riched20: Save glyph attributes in SCRIPT_GLYPHPROP instead.
From: Jactry Zeng jzeng@codeweavers.com
Signed-off-by: Jactry Zeng jzeng@codeweavers.com --- dlls/riched20/editstr.h | 2 +- dlls/riched20/run.c | 23 ++++++++++++++++++----- dlls/riched20/wrap.c | 20 ++++++++++++++------ 3 files changed, 33 insertions(+), 12 deletions(-)
diff --git a/dlls/riched20/editstr.h b/dlls/riched20/editstr.h index db219d7e4df..35e2417298f 100644 --- a/dlls/riched20/editstr.h +++ b/dlls/riched20/editstr.h @@ -171,7 +171,7 @@ typedef struct tagME_Run SCRIPT_ANALYSIS script_analysis; int num_glyphs, max_glyphs; WORD *glyphs; - SCRIPT_VISATTR *vis_attrs; + SCRIPT_GLYPHPROP *glyph_props; int *advances; GOFFSET *offsets; int max_clusters; diff --git a/dlls/riched20/run.c b/dlls/riched20/run.c index f6e6f08d12a..fe889c6ece2 100644 --- a/dlls/riched20/run.c +++ b/dlls/riched20/run.c @@ -357,7 +357,7 @@ ME_Run *run_create( ME_Style *s, int flags ) run->num_glyphs = 0; run->max_glyphs = 0; run->glyphs = NULL; - run->vis_attrs = NULL; + run->glyph_props = NULL; run->advances = NULL; run->offsets = NULL; run->max_clusters = 0; @@ -533,11 +533,17 @@ int ME_CharFromPointContext(ME_Context *c, int cx, ME_Run *run, BOOL closest, BO
if (run->para->nFlags & MEPF_COMPLEX) { - int cp, trailing; + SCRIPT_VISATTR *script_visattrs; + int cp, trailing, i; if (visual_order && run->script_analysis.fRTL) cx = run->nWidth - cx - 1;
- ScriptXtoCP( cx, run->len, run->num_glyphs, run->clusters, run->vis_attrs, run->advances, &run->script_analysis, + script_visattrs = heap_alloc( run->num_glyphs * sizeof(SCRIPT_VISATTR) ); + for (i = 0; i < run->num_glyphs; i++) + script_visattrs[i] = run->glyph_props[i].sva; + ScriptXtoCP( cx, run->len, run->num_glyphs, run->clusters, script_visattrs, run->advances, &run->script_analysis, &cp, &trailing ); + heap_free( script_visattrs ); + TRACE("x %d cp %d trailing %d (run width %d) rtl %d log order %d\n", cx, cp, trailing, run->nWidth, run->script_analysis.fRTL, run->script_analysis.fLogicalOrder); return closest ? cp + trailing : cp; @@ -622,9 +628,16 @@ int ME_PointFromCharContext(ME_Context *c, ME_Run *pRun, int nOffset, BOOL visua
if (pRun->para->nFlags & MEPF_COMPLEX) { - int x; + SCRIPT_VISATTR *script_visattrs; + int x, i; + + script_visattrs = heap_alloc( pRun->num_glyphs * sizeof(SCRIPT_VISATTR) ); + for (i = 0; i < pRun->num_glyphs; i++) + script_visattrs[i] = pRun->glyph_props[i].sva; ScriptCPtoX( nOffset, FALSE, pRun->len, pRun->num_glyphs, pRun->clusters, - pRun->vis_attrs, pRun->advances, &pRun->script_analysis, &x ); + script_visattrs, pRun->advances, &pRun->script_analysis, &x ); + heap_free( script_visattrs ); + if (visual_order && pRun->script_analysis.fRTL) x = pRun->nWidth - x - 1; return x; } diff --git a/dlls/riched20/wrap.c b/dlls/riched20/wrap.c index 8cfb1692ecd..31910486558 100644 --- a/dlls/riched20/wrap.c +++ b/dlls/riched20/wrap.c @@ -51,18 +51,19 @@ typedef struct tagME_WrapContext static BOOL get_run_glyph_buffers( ME_Run *run ) { heap_free( run->glyphs ); - run->glyphs = heap_alloc( run->max_glyphs * (sizeof(WORD) + sizeof(SCRIPT_VISATTR) + sizeof(int) + sizeof(GOFFSET)) ); + run->glyphs = heap_alloc( run->max_glyphs * (sizeof(WORD) + sizeof(SCRIPT_GLYPHPROP) + sizeof(int) + sizeof(GOFFSET)) ); if (!run->glyphs) return FALSE;
- run->vis_attrs = (SCRIPT_VISATTR*)((char*)run->glyphs + run->max_glyphs * sizeof(WORD)); - run->advances = (int*)((char*)run->glyphs + run->max_glyphs * (sizeof(WORD) + sizeof(SCRIPT_VISATTR))); - run->offsets = (GOFFSET*)((char*)run->glyphs + run->max_glyphs * (sizeof(WORD) + sizeof(SCRIPT_VISATTR) + sizeof(int))); + run->glyph_props = (SCRIPT_GLYPHPROP*)((char*)run->glyphs + run->max_glyphs * sizeof(WORD)); + run->advances = (int*)((char*)run->glyphs + run->max_glyphs * (sizeof(WORD) + sizeof(SCRIPT_GLYPHPROP))); + run->offsets = (GOFFSET*)((char*)run->glyphs + run->max_glyphs * (sizeof(WORD) + sizeof(SCRIPT_GLYPHPROP) + sizeof(int)));
return TRUE; }
static HRESULT shape_run( ME_Context *c, ME_Run *run ) { + SCRIPT_VISATTR *script_visattrs = NULL; HRESULT hr; int i;
@@ -83,8 +84,11 @@ static HRESULT shape_run( ME_Context *c, ME_Run *run ) select_style( c, run->style ); while (1) { + heap_free( script_visattrs ); + script_visattrs = heap_alloc( run->max_glyphs * sizeof(SCRIPT_VISATTR) ); + hr = ScriptShape( c->hDC, &run->style->script_cache, get_text( run, 0 ), run->len, run->max_glyphs, - &run->script_analysis, run->glyphs, run->clusters, run->vis_attrs, &run->num_glyphs ); + &run->script_analysis, run->glyphs, run->clusters, script_visattrs, &run->num_glyphs ); if (hr != E_OUTOFMEMORY) break; if (run->max_glyphs > 10 * run->len) break; /* something has clearly gone wrong */ run->max_glyphs *= 2; @@ -92,14 +96,18 @@ static HRESULT shape_run( ME_Context *c, ME_Run *run ) }
if (SUCCEEDED(hr)) - hr = ScriptPlace( c->hDC, &run->style->script_cache, run->glyphs, run->num_glyphs, run->vis_attrs, + hr = ScriptPlace( c->hDC, &run->style->script_cache, run->glyphs, run->num_glyphs, script_visattrs, &run->script_analysis, run->advances, run->offsets, NULL );
if (SUCCEEDED(hr)) { for (i = 0, run->nWidth = 0; i < run->num_glyphs; i++) + { run->nWidth += run->advances[i]; + run->glyph_props[i].sva = script_visattrs[i]; + } } + heap_free( script_visattrs );
return hr; }
From: Jactry Zeng jzeng@codeweavers.com
Signed-off-by: Jactry Zeng jzeng@codeweavers.com --- dlls/riched20/editor.h | 11 +++++++++ dlls/riched20/editstr.h | 1 + dlls/riched20/run.c | 2 ++ dlls/riched20/wrap.c | 49 +++++++++++++++++++++++++---------------- 4 files changed, 44 insertions(+), 19 deletions(-)
diff --git a/dlls/riched20/editor.h b/dlls/riched20/editor.h index 072d0f62782..965eb44bba6 100644 --- a/dlls/riched20/editor.h +++ b/dlls/riched20/editor.h @@ -36,6 +36,12 @@ extern HINSTANCE dll_instance DECLSPEC_HIDDEN; (fe).lindex=-1;\ };
+#define MAKE_OPENTYPE_TAG( _x1, _x2, _x3, _x4 ) \ + ( ( (ULONG)_x4 << 24 ) | \ + ( (ULONG)_x3 << 16 ) | \ + ( (ULONG)_x2 << 8 ) | \ + (ULONG)_x1 ) + static inline WCHAR *get_text( const ME_Run *run, int offset ) { return run->para->text->szData + run->nCharOfs + offset; @@ -46,6 +52,11 @@ static inline const char *debugstr_run( const ME_Run *run ) return debugstr_wn( get_text( run, 0 ), run->len ); }
+static inline const char *debugstr_tag( OPENTYPE_TAG tag ) +{ + return debugstr_an( (char *)&tag, 4 ); +} + /* style.c */ ME_Style *style_get_insert_style( ME_TextEditor *editor, ME_Cursor *cursor ) DECLSPEC_HIDDEN; ME_Style *ME_MakeStyle(CHARFORMAT2W *style) DECLSPEC_HIDDEN; diff --git a/dlls/riched20/editstr.h b/dlls/riched20/editstr.h index 35e2417298f..d8857eddd30 100644 --- a/dlls/riched20/editstr.h +++ b/dlls/riched20/editstr.h @@ -176,6 +176,7 @@ typedef struct tagME_Run GOFFSET *offsets; int max_clusters; WORD *clusters; + OPENTYPE_TAG script_tag; } ME_Run;
typedef struct tagME_Border diff --git a/dlls/riched20/run.c b/dlls/riched20/run.c index fe889c6ece2..7b9908cc6b9 100644 --- a/dlls/riched20/run.c +++ b/dlls/riched20/run.c @@ -314,6 +314,7 @@ ME_Run *run_split( ME_TextEditor *editor, ME_Cursor *cursor ) new_run->nCharOfs = run->nCharOfs + nOffset; new_run->len = run->len - nOffset; new_run->para = run->para; + new_run->script_tag = run->script_tag; run->len = nOffset; cursor->run = new_run; cursor->nOffset = 0; @@ -362,6 +363,7 @@ ME_Run *run_create( ME_Style *s, int flags ) run->offsets = NULL; run->max_clusters = 0; run->clusters = NULL; + run->script_tag = 0; return run; }
diff --git a/dlls/riched20/wrap.c b/dlls/riched20/wrap.c index 31910486558..16bff3fae3e 100644 --- a/dlls/riched20/wrap.c +++ b/dlls/riched20/wrap.c @@ -63,7 +63,7 @@ static BOOL get_run_glyph_buffers( ME_Run *run )
static HRESULT shape_run( ME_Context *c, ME_Run *run ) { - SCRIPT_VISATTR *script_visattrs = NULL; + SCRIPT_CHARPROP *char_props; HRESULT hr; int i;
@@ -74,6 +74,9 @@ static HRESULT shape_run( ME_Context *c, ME_Run *run ) get_run_glyph_buffers( run ); }
+ if (!(char_props = heap_alloc( run->len * sizeof( *char_props ) ))) + return E_OUTOFMEMORY; + if (run->max_clusters < run->len) { heap_free( run->clusters ); @@ -84,11 +87,9 @@ static HRESULT shape_run( ME_Context *c, ME_Run *run ) select_style( c, run->style ); while (1) { - heap_free( script_visattrs ); - script_visattrs = heap_alloc( run->max_glyphs * sizeof(SCRIPT_VISATTR) ); - - hr = ScriptShape( c->hDC, &run->style->script_cache, get_text( run, 0 ), run->len, run->max_glyphs, - &run->script_analysis, run->glyphs, run->clusters, script_visattrs, &run->num_glyphs ); + hr = ScriptShapeOpenType( c->hDC, &run->style->script_cache, &run->script_analysis, run->script_tag, 0, + NULL, NULL, 0, get_text( run, 0 ), run->len, run->max_glyphs, run->clusters, + char_props, run->glyphs, run->glyph_props, &run->num_glyphs ); if (hr != E_OUTOFMEMORY) break; if (run->max_glyphs > 10 * run->len) break; /* something has clearly gone wrong */ run->max_glyphs *= 2; @@ -96,18 +97,17 @@ static HRESULT shape_run( ME_Context *c, ME_Run *run ) }
if (SUCCEEDED(hr)) - hr = ScriptPlace( c->hDC, &run->style->script_cache, run->glyphs, run->num_glyphs, script_visattrs, - &run->script_analysis, run->advances, run->offsets, NULL ); + hr = ScriptPlaceOpenType( c->hDC, &run->style->script_cache, &run->script_analysis, run->script_tag, 0, + NULL, NULL, 0, get_text( run, 0 ), run->clusters, char_props, run->len, run->glyphs, + run->glyph_props, run->num_glyphs, run->advances, run->offsets, NULL ); + + heap_free( char_props );
if (SUCCEEDED(hr)) { for (i = 0, run->nWidth = 0; i < run->num_glyphs; i++) - { run->nWidth += run->advances[i]; - run->glyph_props[i].sva = script_visattrs[i]; - } } - heap_free( script_visattrs );
return hr; } @@ -733,6 +733,7 @@ static HRESULT itemize_para( ME_Context *c, ME_Paragraph *para ) { ME_Run *run; SCRIPT_ITEM buf[16], *items = buf; + OPENTYPE_TAG tags[16], *script_tags = tags; int items_passed = ARRAY_SIZE( buf ), num_items, cur_item; SCRIPT_CONTROL control = { LANG_USER_DEFAULT, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, 0 }; @@ -746,16 +747,22 @@ static HRESULT itemize_para( ME_Context *c, ME_Paragraph *para )
while (1) { - hr = ScriptItemize( para->text->szData, para->text->nLen, items_passed, &control, - &state, items, &num_items ); + hr = ScriptItemizeOpenType( para->text->szData, para->text->nLen, items_passed, &control, + &state, items, script_tags, &num_items ); if (hr != E_OUTOFMEMORY) break; /* may not be enough items if hr == E_OUTOFMEMORY */ if (items_passed > para->text->nLen + 1) break; /* something else has gone wrong */ items_passed *= 2; if (items == buf) + { items = heap_alloc( items_passed * sizeof( *items ) ); + script_tags = heap_alloc( items_passed * sizeof( *script_tags ) ); + } else + { items = heap_realloc( items, items_passed * sizeof( *items ) ); - if (!items) break; + script_tags = heap_realloc( script_tags, items_passed * sizeof( *script_tags ) ); + } + if (!items || !script_tags) break; } if (FAILED( hr )) goto end;
@@ -768,9 +775,10 @@ static HRESULT itemize_para( ME_Context *c, ME_Paragraph *para ) items[cur_item].a.fRTL, items[cur_item].a.s.uBidiLevel ); }
- TRACE( "before splitting runs into ranges\n" ); + TRACE( "before splitting runs into ranges:\n" ); for (run = para_first_run( para ); run; run = run_next( run )) - TRACE( "\t%d: %s\n", run->nCharOfs, debugstr_run( run ) ); + TRACE( "\t%d %s: %s\n", run->nCharOfs, run->script_tag ? debugstr_tag( run->script_tag ) : " ", + debugstr_run( run ) ); }
/* split runs into ranges at item boundaries */ @@ -780,6 +788,7 @@ static HRESULT itemize_para( ME_Context *c, ME_Paragraph *para )
items[cur_item].a.fLogicalOrder = TRUE; run->script_analysis = items[cur_item].a; + run->script_tag = script_tags[cur_item];
if (run->nFlags & MERF_ENDPARA) break; /* don't split eop runs */
@@ -792,15 +801,17 @@ static HRESULT itemize_para( ME_Context *c, ME_Paragraph *para )
if (TRACE_ON( richedit )) { - TRACE( "after splitting into ranges\n" ); + TRACE( "after splitting runs into ranges:\n" ); for (run = para_first_run( para ); run; run = run_next( run )) - TRACE( "\t%d: %s\n", run->nCharOfs, debugstr_run( run ) ); + TRACE( "\t%d %s: %s\n", run->nCharOfs, run->script_tag ? debugstr_tag( run->script_tag ) : " ", + debugstr_run( run ) ); }
para->nFlags |= MEPF_COMPLEX;
end: if (items != buf) heap_free( items ); + if (script_tags != tags) heap_free( script_tags ); return hr; }
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 d8857eddd30..5462c0f64a2 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 7b9908cc6b9..447543dfc41 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 16bff3fae3e..0748cb0b2d5 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_CHARPROP *char_props; HRESULT hr; int i; @@ -85,6 +130,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 ( lstrcmpW( 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; + lstrcpyW( 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) { hr = ScriptShapeOpenType( c->hDC, &run->style->script_cache, &run->script_analysis, run->script_tag, 0, @@ -101,6 +173,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, run->glyph_props, run->num_glyphs, run->advances, run->offsets, NULL );
+ if (old_font) + { + SelectObject( c->hDC, old_font ); + DeleteObject( fallback ); + } + heap_free( char_props );
if (SUCCEEDED(hr))
From: Jactry Zeng jzeng@codeweavers.com
Signed-off-by: Jactry Zeng jzeng@codeweavers.com --- dlls/riched20/wrap.c | 51 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-)
diff --git a/dlls/riched20/wrap.c b/dlls/riched20/wrap.c index 0748cb0b2d5..9ea507511b2 100644 --- a/dlls/riched20/wrap.c +++ b/dlls/riched20/wrap.c @@ -61,16 +61,65 @@ static BOOL get_run_glyph_buffers( ME_Run *run ) return TRUE; }
+static WCHAR hang_font[LF_FACESIZE] = L"Gulim"; +static WCHAR hani_font[LF_FACESIZE] = L"Simsun"; +static WCHAR kana_font[LF_FACESIZE] = L"MS UI Gothic"; +static BOOL fallbacks_initialized = FALSE; + struct richedit_fallback { OPENTYPE_TAG script_tag; WCHAR *font_name; -} richedit_fallbacks[] = {}; +} richedit_fallbacks[] = +{ + { MAKE_OPENTYPE_TAG( 'h','a','n','g' ), hang_font }, + { MAKE_OPENTYPE_TAG( 'h','a','n','i' ), hani_font }, + { MAKE_OPENTYPE_TAG( 'k','a','n','a' ), kana_font }, +}; + +static void init_richedit_fallbacks( void ) +{ + LCID lcid; + + if (fallbacks_initialized) + return; + + /* Setting proper font for "hani". */ + lcid = GetSystemDefaultLangID(); + switch (PRIMARYLANGID( lcid )) + { + case LANG_CHINESE: + { + switch (SUBLANGID( lcid )) + { + case SUBLANG_CHINESE_HONGKONG: + case SUBLANG_CHINESE_MACAU: + case SUBLANG_CHINESE_TRADITIONAL: + lstrcpyW( hani_font, L"PMingLiU" ); + break; + case SUBLANG_CHINESE_SIMPLIFIED: + lstrcpyW( hani_font, L"Simsun" ); + break; + } + break; + } + case LANG_JAPANESE: + lstrcpyW( hani_font, L"MS UI Gothic" ); + break; + case LANG_KOREAN: + lstrcpyW( hani_font, L"Gulim" ); + break; + } + + fallbacks_initialized = TRUE; +}
static const WCHAR *find_fallback_font( OPENTYPE_TAG script_tag ) { int count;
+ init_richedit_fallbacks(); + count = ARRAYSIZE( richedit_fallbacks ); while (count--) if (richedit_fallbacks[count].script_tag == script_tag)
v3: - Discard changes to ME_AddRefStyle(). - Use SCRIPT_GLYPHPROP for saving glyph attributes instead. - Adding font fallbacks for CJK in a separate commit.
Huw Davies (@huw) commented about dlls/riched20/run.c:
if (run->para->nFlags & MEPF_COMPLEX) {
int cp, trailing;
SCRIPT_VISATTR *script_visattrs;
int cp, trailing, i; if (visual_order && run->script_analysis.fRTL) cx = run->nWidth - cx - 1;
ScriptXtoCP( cx, run->len, run->num_glyphs, run->clusters, run->vis_attrs, run->advances, &run->script_analysis,
script_visattrs = heap_alloc( run->num_glyphs * sizeof(SCRIPT_VISATTR) );
for (i = 0; i < run->num_glyphs; i++)
script_visattrs[i] = run->glyph_props[i].sva;
ScriptXtoCP( cx, run->len, run->num_glyphs, run->clusters, script_visattrs, run->advances, &run->script_analysis, &cp, &trailing );
heap_free( script_visattrs );
Ah right, this copying is unfortunate. Let's stick with keeping the vis_attrs in the run and allocate temporary glyph_props when calling `ScriptShapeOpenType()` which are then used to initialize the run's vis_attrs.
Huw Davies (@huw) commented about dlls/riched20/wrap.c:
case SUBLANG_CHINESE_SIMPLIFIED:
lstrcpyW( hani_font, L"Simsun" );
break;
}
break;
}
case LANG_JAPANESE:
lstrcpyW( hani_font, L"MS UI Gothic" );
break;
case LANG_KOREAN:
lstrcpyW( hani_font, L"Gulim" );
break;
- }
- fallbacks_initialized = TRUE;
+}
This isn't thread safe. Also, let's use `wcscpy()`.