Signed-off-by: Jactry Zeng jzeng@codeweavers.com
-- v4: riched20: Add font fallbacks for CJK. riched20: Add font fallback support. riched20: Use OpenType functions for painting and generating script tags.
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 | 52 ++++++++++++++++++++++++++++++++--------- 4 files changed, 55 insertions(+), 11 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 db219d7e4df..f11058012e9 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 f6e6f08d12a..533c61da01c 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 8cfb1692ecd..7010b6ffc35 100644 --- a/dlls/riched20/wrap.c +++ b/dlls/riched20/wrap.c @@ -63,6 +63,8 @@ static BOOL get_run_glyph_buffers( ME_Run *run )
static HRESULT shape_run( ME_Context *c, ME_Run *run ) { + SCRIPT_GLYPHPROP *glyph_props = NULL; + SCRIPT_CHARPROP *char_props; HRESULT hr; int i;
@@ -73,6 +75,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 ); @@ -83,8 +88,15 @@ static HRESULT shape_run( ME_Context *c, ME_Run *run ) select_style( c, run->style ); while (1) { - 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 ); + heap_free( glyph_props ); + if (!(glyph_props = heap_alloc( run->max_glyphs * sizeof( *glyph_props )))) + { + hr = E_OUTOFMEMORY; + break; + } + 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, 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; @@ -92,15 +104,22 @@ 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, - &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, + glyph_props, run->num_glyphs, 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->vis_attrs[i] = glyph_props[i].sva; + } }
+ heap_free( glyph_props ); + heap_free( char_props ); + return hr; }
@@ -725,6 +744,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 }; @@ -738,16 +758,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;
@@ -760,9 +786,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 */ @@ -772,6 +799,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 */
@@ -784,15 +812,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 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++)
From: Jactry Zeng jzeng@codeweavers.com
Signed-off-by: Jactry Zeng jzeng@codeweavers.com --- dlls/riched20/wrap.c | 65 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-)
diff --git a/dlls/riched20/wrap.c b/dlls/riched20/wrap.c index da077045189..dceb8cf9cdf 100644 --- a/dlls/riched20/wrap.c +++ b/dlls/riched20/wrap.c @@ -61,16 +61,79 @@ 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; +static CRITICAL_SECTION fallbacks_cs; +static CRITICAL_SECTION_DEBUG critsect_debug = +{ + 0, 0, &fallbacks_cs, + { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": fallbacks_cs") } +}; +static CRITICAL_SECTION fallbacks_cs = { &critsect_debug, -1, 0, 0, 0, 0 }; + 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; + + EnterCriticalSection( &fallbacks_cs ); + + if (fallbacks_initialized) + { + LeaveCriticalSection( &fallbacks_cs ); + 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: + wcscpy( hani_font, L"PMingLiU" ); + break; + case SUBLANG_CHINESE_SIMPLIFIED: + wcscpy( hani_font, L"Simsun" ); + break; + } + break; + } + case LANG_JAPANESE: + wcscpy( hani_font, L"MS UI Gothic" ); + break; + case LANG_KOREAN: + wcscpy( hani_font, L"Gulim" ); + break; + } + + fallbacks_initialized = TRUE; + LeaveCriticalSection( &fallbacks_cs ); +}
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)
v4: - Use temporary glyph_props instead. - Make initialization of font fallbacks thread safe. - Use wcscpy()/wcscmp() instead of lstrcpyW()/lstrcmpW().
Huw Davies (@huw) commented about dlls/riched20/wrap.c:
- 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;
- }
This doesn't seem like the right way to do this. I'd have expected a fallback font to be used if `ScriptShape(OpenType)()` returned missing glyphs.
Nikolay Sivov (@nsivov) commented about dlls/riched20/wrap.c:
case SUBLANG_CHINESE_TRADITIONAL:
wcscpy( hani_font, L"PMingLiU" );
break;
case SUBLANG_CHINESE_SIMPLIFIED:
wcscpy( hani_font, L"Simsun" );
break;
}
break;
}
case LANG_JAPANESE:
wcscpy( hani_font, L"MS UI Gothic" );
break;
case LANG_KOREAN:
wcscpy( hani_font, L"Gulim" );
break;
- }
I wonder if this really is necessary, or accurate. You'll be initializing this mapping once, but there is also CHARFORMAT2W.lcid.
On Wed Aug 17 11:23:57 2022 +0000, Huw Davies wrote:
This doesn't seem like the right way to do this. I'd have expected a fallback font to be used if `ScriptShape(OpenType)()` returned missing glyphs.
This was probably taken from requires_fallback() in uniscribe. But I agree, it's assuming too much, and I suspect is incorrect in uniscribe too. For example it doesn't consider that not every character has to have a glyph to produce meaningful output, control characters are one example.
This was probably taken from requires_fallback() in uniscribe. But I agree, it's assuming too much, and I suspect is incorrect in uniscribe too. For example it doesn't consider that not every character has to have a glyph to produce meaningful output, control characters are one example.
You are right. I wrote two programs for rendering the word "Wine" with a .ttf font which only "implemented" glyph for 'W', one uses uniscribe APIs, and the other uses richedit. (The font actually was copied from dlls/gdi32/tests/vertical.ttf.) On Windows, both uniscribe and richedit use vertical.ttf for rendering the character 'W' and use a fallback font for the rest. I think this is enough to indicate that native uniscribe and richedit does fallback character by character:
![Screenshot_from_2023-07-25_17-44-23](/uploads/b7cd8608915bb6ad4b4fe626791ddc07/Screenshot_from_2023-07-25_17-44-23.png)
Thanks!
[font_fallback_tests.zip](/uploads/1cb1c10015eca367b4c17a2a09b9ffaf/font_fallback_tests.zip)
On Tue Jul 25 10:01:53 2023 +0000, Jactry Zeng wrote:
This was probably taken from requires_fallback() in uniscribe. But I
agree, it's assuming too much, and I suspect is incorrect in uniscribe too. For example it doesn't consider that not every character has to have a glyph to produce meaningful output, control characters are one example. You are right. I wrote two programs for rendering the word "Wine" with a .ttf font which only "implemented" glyph for 'W', one uses uniscribe APIs, and the other uses richedit. (The font actually was copied from dlls/gdi32/tests/vertical.ttf.) On Windows, both uniscribe and richedit use vertical.ttf for rendering the character 'W' and use a fallback font for the rest. I think this is enough to indicate that native uniscribe and richedit does fallback character by character: ![Screenshot_from_2023-07-25_17-44-23](/uploads/b7cd8608915bb6ad4b4fe626791ddc07/Screenshot_from_2023-07-25_17-44-23.png) Thanks! [font_fallback_tests.zip](/uploads/1cb1c10015eca367b4c17a2a09b9ffaf/font_fallback_tests.zip)
So wouldn't that imply that the fix should be in uniscribe and not richedit?
So wouldn't that imply that the fix should be in uniscribe and not richedit?
I think they are separated but very similar issues (at least for my previous fallback implementation for richedit, that approach was inspired by Wine's current uniscribe actually.) The fallback mechanism in uniscribe only makes an effect on ScriptStringAnalyse(), but I didn't use it for fallback in richedit, I used ScriptItemizeOpenType()/ScriptShapeOpenType()/ScriptPlaceOpenType() directly. So improvement for uniscribe won't make my previous fallback implementation for richedit better.