[vlc-devel] [PATCH 4/4] freetype: font fallback for Windows

Salah-Eddin Shaban salshaaban at gmail.com
Thu Oct 22 13:53:50 CEST 2015


---
 modules/text_renderer/Makefile.am      |   2 +-
 modules/text_renderer/freetype.c       |   8 +-
 modules/text_renderer/platform_fonts.c | 332 ++++++++++++++++++++++++++-------
 modules/text_renderer/platform_fonts.h |   6 +-
 modules/text_renderer/text_layout.c    |   8 +
 5 files changed, 279 insertions(+), 77 deletions(-)

diff --git a/modules/text_renderer/Makefile.am b/modules/text_renderer/Makefile.am
index 889ccd8..14e049c 100644
--- a/modules/text_renderer/Makefile.am
+++ b/modules/text_renderer/Makefile.am
@@ -62,7 +62,7 @@ libwin32text_plugin_la_LIBADD = -lgdi32
 if HAVE_WIN32
 libfreetype_plugin_la_LIBADD += -liconv -lz
 if !HAVE_WINSTORE
-libfreetype_plugin_la_LIBADD += -lgdi32
+libfreetype_plugin_la_LIBADD += -lgdi32 -lusp10
 text_LTLIBRARIES += libwin32text_plugin.la
 endif
 endif
diff --git a/modules/text_renderer/freetype.c b/modules/text_renderer/freetype.c
index 97d8ee1..9a80891 100644
--- a/modules/text_renderer/freetype.c
+++ b/modules/text_renderer/freetype.c
@@ -1273,7 +1273,13 @@ static int Create( vlc_object_t *p_this )
     p_sys->pf_select = MacLegacy_Select;
 #endif
 #elif defined( _WIN32 ) && defined( HAVE_GET_FONT_BY_FAMILY_NAME )
-    p_sys->pf_select = Win32_Select;
+    const char *const ppsz_win32_default[] =
+        { "Tahoma", "FangSong", "SimHei", "KaiTi" };
+    p_sys->pf_get_family = Win32_GetFamily;
+    p_sys->pf_get_fallbacks = Win32_GetFallbacks;
+    p_sys->pf_select = Generic_Select;
+    InitDefaultList( p_filter, ppsz_win32_default,
+                     sizeof( ppsz_win32_default ) / sizeof( *ppsz_win32_default ) );
 #elif defined( __ANDROID__ )
     p_sys->pf_get_family = Android_GetFamily;
     p_sys->pf_get_fallbacks = Android_GetFallbacks;
diff --git a/modules/text_renderer/platform_fonts.c b/modules/text_renderer/platform_fonts.c
index df1a7d1..a8f8917 100644
--- a/modules/text_renderer/platform_fonts.c
+++ b/modules/text_renderer/platform_fonts.c
@@ -53,6 +53,7 @@
 #ifdef _WIN32
 # include <windows.h>
 # include <shlobj.h>
+# include <usp10.h>
 # include <vlc_charset.h>                                     /* FromT */
 #endif
 
@@ -656,7 +657,54 @@ vlc_family_t *FontConfig_GetFallbacks( filter_t *p_filter, const char *psz_famil
 #if defined( _WIN32 ) && !VLC_WINSTORE_APP
 #define FONT_DIR_NT _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts")
 
-static int GetFileFontByName( LPCTSTR font_name, char **psz_filename )
+static char *Trim( char *psz_text )
+{
+    int i_first_char = -1;
+    int i_last_char = -1;
+    int i_len = strlen( psz_text );
+
+    for( int i = 0; i < i_len; ++i )
+    {
+        if( psz_text[i] != ' ')
+        {
+            if( i_first_char == -1 )
+                i_first_char = i;
+            i_last_char = i;
+        }
+    }
+
+    psz_text[ i_last_char + 1 ] = 0;
+    if( i_first_char != -1 ) psz_text = psz_text + i_first_char;
+
+    return psz_text;
+}
+
+static int ConcatenatedIndex( char *psz_haystack, const char *psz_needle )
+{
+    char *psz_family = psz_haystack;
+    char *psz_terminator = psz_haystack + strlen( psz_haystack );
+    int i_index = 0;
+
+    while( psz_family < psz_terminator )
+    {
+        char *psz_amp = strchr( psz_family, '&' );
+
+        if( !psz_amp ) psz_amp = psz_terminator;
+
+        *psz_amp = 0;
+
+        psz_family = Trim( psz_family );
+        if( !strcasecmp( psz_family, psz_needle ) )
+            return i_index;
+
+        psz_family = psz_amp + 1;
+        ++i_index;
+    }
+
+    return -1;
+}
+
+static int GetFileFontByName( LPCTSTR font_name, char **psz_filename, int *i_index )
 {
     HKEY hKey;
     TCHAR vbuffer[MAX_PATH];
@@ -667,7 +715,6 @@ static int GetFileFontByName( LPCTSTR font_name, char **psz_filename )
         return 1;
 
     char *font_name_temp = FromT( font_name );
-    size_t fontname_len = strlen( font_name_temp );
 
     for( int index = 0;; index++ )
     {
@@ -678,6 +725,7 @@ static int GetFileFontByName( LPCTSTR font_name, char **psz_filename )
                                       NULL, NULL, (LPBYTE)dbuffer, &dbuflen);
         if( i_result != ERROR_SUCCESS )
         {
+            free( font_name_temp );
             RegCloseKey( hKey );
             return i_result;
         }
@@ -687,41 +735,23 @@ static int GetFileFontByName( LPCTSTR font_name, char **psz_filename )
         char *s = strchr( psz_value,'(' );
         if( s != NULL && s != psz_value ) s[-1] = '\0';
 
-        /* Manage concatenated font names */
-        if( strchr( psz_value, '&') ) {
-            if( strcasestr( psz_value, font_name_temp ) != NULL )
-            {
-                free( psz_value );
-                break;
-            }
-        }
-        else {
-            if( strncasecmp( psz_value, font_name_temp, fontname_len ) == 0 )
-            {
-                free( psz_value );
-                break;
-            }
+        int i_concat_idx = 0;
+        if( ( i_concat_idx = ConcatenatedIndex( psz_value, font_name_temp ) ) != -1 )
+        {
+            *i_index = i_concat_idx;
+            *psz_filename = FromT( dbuffer );
+            free( psz_value );
+            break;
         }
 
         free( psz_value );
     }
 
-    *psz_filename = FromT( dbuffer );
     free( font_name_temp );
     RegCloseKey( hKey );
     return 0;
 }
 
-static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *lpelfe, const NEWTEXTMETRICEX *metric,
-                                     DWORD type, LPARAM lParam)
-{
-    VLC_UNUSED( metric );
-    if( (type & RASTER_FONTTYPE) ) return 1;
-    // if( lpelfe->elfScript ) FIXME
-
-    return GetFileFontByName( (LPCTSTR)lpelfe->elfFullName, (char **)lParam );
-}
-
 static char* GetWindowsFontPath()
 {
     wchar_t wdir[MAX_PATH];
@@ -733,71 +763,229 @@ static char* GetWindowsFontPath()
     return FromWide( wdir );
 }
 
-char* Win32_Select( filter_t *p_filter, const char* family,
-                    bool b_bold, bool b_italic,
-                    int *i_idx, uni_char_t codepoint )
+static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *lpelfe, const NEWTEXTMETRICEX *metric,
+                                     DWORD type, LPARAM lParam)
 {
-    VLC_UNUSED( codepoint );
-    VLC_UNUSED( i_idx );
-    VLC_UNUSED( p_filter );
+    VLC_UNUSED( metric );
+    if( (type & RASTER_FONTTYPE) ) return 1;
 
-    if( !family || strlen( family ) < 1 )
-        goto fail;
+    vlc_family_t *p_family = ( vlc_family_t * ) lParam;
+
+    bool b_bold = ( lpelfe->elfLogFont.lfWeight == FW_BOLD );
+    bool b_italic = ( lpelfe->elfLogFont.lfItalic != 0 );
+
+    for( vlc_font_t *p_font = p_family->p_fonts; p_font; p_font = p_font->p_next )
+        if( !!p_font->b_bold == !!b_bold && !!p_font->b_italic == !!b_italic )
+            return 1;
+
+    char *psz_filename = NULL;
+    char *psz_fontfile = NULL;
+    int   i_index      = 0;
+
+    if( GetFileFontByName( (LPCTSTR)lpelfe->elfFullName, &psz_filename, &i_index ) )
+        return 1;
+
+    if( strchr( psz_filename, DIR_SEP_CHAR ) )
+        psz_fontfile = psz_filename;
+    else
+    {
+        /* Get Windows Font folder */
+        char *psz_win_fonts_path = GetWindowsFontPath();
+        if( asprintf( &psz_fontfile, "%s\\%s", psz_win_fonts_path, psz_filename ) == -1 )
+        {
+            free( psz_filename );
+            free( psz_win_fonts_path );
+            return 1;
+        }
+        free( psz_filename );
+        free( psz_win_fonts_path );
+    }
+
+    NewFont( psz_fontfile, i_index, b_bold, b_italic, p_family );
+
+    return 1;
+}
+
+const vlc_family_t *Win32_GetFamily( filter_t *p_filter, const char *psz_family )
+{
+    filter_sys_t *p_sys = p_filter->p_sys;
+    char *psz_lc = ToLower( psz_family );
+
+    if( unlikely( !psz_lc ) )
+        return NULL;
+
+    vlc_family_t *p_family =
+        vlc_dictionary_value_for_key( &p_sys->family_map, psz_lc );
+
+    free( psz_lc );
+
+    if( p_family )
+        return p_family;
+
+    p_family = NewFamily( p_filter, psz_family, &p_sys->p_families,
+                          &p_sys->family_map, psz_family );
+
+    if( unlikely( !p_family ) )
+        return NULL;
 
-    /* */
     LOGFONT lf;
     lf.lfCharSet = DEFAULT_CHARSET;
-    if( b_italic )
-        lf.lfItalic = true;
-    if( b_bold )
-        lf.lfWeight = FW_BOLD;
 
-    LPTSTR psz_fbuffer = ToT( family );
+    LPTSTR psz_fbuffer = ToT( psz_family );
     _tcsncpy( (LPTSTR)&lf.lfFaceName, psz_fbuffer, LF_FACESIZE );
     free( psz_fbuffer );
 
     /* */
-    char *psz_filename = NULL;
     HDC hDC = GetDC( NULL );
-    EnumFontFamiliesEx(hDC, &lf, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&psz_filename, 0);
+    EnumFontFamiliesEx(hDC, &lf, (FONTENUMPROC)&EnumFontCallback, (LPARAM)p_family, 0);
     ReleaseDC(NULL, hDC);
 
-    /* */
-    if( psz_filename != NULL )
+    return p_family;
+}
+
+static int CALLBACK MetaFileEnumProc( HDC hdc, HANDLETABLE* table,
+                                      CONST ENHMETARECORD* record,
+                                      int table_entries, LPARAM log_font )
+{
+    VLC_UNUSED( hdc );
+    VLC_UNUSED( table );
+    VLC_UNUSED( table_entries );
+
+    if( record->iType == EMR_EXTCREATEFONTINDIRECTW )
     {
-        /* FIXME: increase i_idx, when concatenated strings  */
-        i_idx = 0;
+        const EMREXTCREATEFONTINDIRECTW* create_font_record =
+                ( const EMREXTCREATEFONTINDIRECTW * ) record;
 
-        /* Prepend the Windows Font path, when only a filename was provided */
-        if( strchr( psz_filename, DIR_SEP_CHAR ) )
-            return psz_filename;
-        else
-        {
-            /* Get Windows Font folder */
-            char *psz_win_fonts_path = GetWindowsFontPath();
-            char *psz_tmp;
-            if( asprintf( &psz_tmp, "%s\\%s", psz_win_fonts_path, psz_filename ) == -1 )
-            {
-                free( psz_filename );
-                free( psz_win_fonts_path );
-                return NULL;
-            }
-            free( psz_filename );
-                free( psz_win_fonts_path );
+        *( ( LOGFONT * ) log_font ) = create_font_record->elfw.elfLogFont;
+    }
+    return 1;
+}
 
-            return psz_tmp;
-        }
+/*
+ * This is a hack used by Chrome and WebKit to expose the fallback font used
+ * by Uniscribe for some given text for use with custom shapers / font engines.
+ */
+static char *UniscribeFallback( const char *psz_family, uni_char_t codepoint )
+{
+    HDC          hdc          = NULL;
+    HDC          meta_file_dc = NULL;
+    HENHMETAFILE meta_file    = NULL;
+    LPTSTR       psz_fbuffer  = NULL;
+    char        *psz_result   = NULL;
+
+    hdc = CreateCompatibleDC( NULL );
+    if( !hdc )
+        return NULL;
+
+    meta_file_dc = CreateEnhMetaFile( hdc, NULL, NULL, NULL );
+    if( !meta_file_dc )
+        goto error;
+
+    LOGFONT lf;
+    memset( &lf, 0, sizeof( lf ) );
+
+    psz_fbuffer = ToT( psz_family );
+    if( !psz_fbuffer )
+        goto error;
+    _tcsncpy( ( LPTSTR ) &lf.lfFaceName, psz_fbuffer, LF_FACESIZE );
+    free( psz_fbuffer );
+
+    lf.lfCharSet = DEFAULT_CHARSET;
+    HFONT hFont = CreateFontIndirect( &lf );
+    if( !hFont )
+        goto error;
+
+    HFONT hOriginalFont = SelectObject( meta_file_dc, hFont );
+
+    TCHAR text = codepoint;
+
+    SCRIPT_STRING_ANALYSIS script_analysis;
+    HRESULT hresult = ScriptStringAnalyse( meta_file_dc, &text, 1, 0, -1,
+                            SSA_METAFILE | SSA_FALLBACK | SSA_GLYPHS | SSA_LINK,
+                            0, NULL, NULL, NULL, NULL, NULL, &script_analysis );
+
+    if( SUCCEEDED( hresult ) )
+    {
+        hresult = ScriptStringOut( script_analysis, 0, 0, 0, NULL, 0, 0, FALSE );
+        ScriptStringFree( &script_analysis );
     }
-    else /* Let's take any font we can */
-fail:
+
+    SelectObject( meta_file_dc, hOriginalFont );
+    DeleteObject( hFont );
+    meta_file = CloseEnhMetaFile( meta_file_dc );
+
+    if( SUCCEEDED( hresult ) )
     {
-        char *psz_win_fonts_path = GetWindowsFontPath();
-        char *psz_tmp;
-        if( asprintf( &psz_tmp, "%s\\%s", psz_win_fonts_path, SYSTEM_DEFAULT_FONT_FILE ) == -1 )
-            return NULL;
+        LOGFONT log_font;
+        log_font.lfFaceName[ 0 ] = 0;
+        EnumEnhMetaFile( 0, meta_file, MetaFileEnumProc, &log_font, NULL );
+        if( log_font.lfFaceName[ 0 ] )
+            psz_result = FromT( log_font.lfFaceName );
+    }
+
+    DeleteEnhMetaFile(meta_file);
+    DeleteDC( hdc );
+    return psz_result;
+
+error:
+    if( meta_file_dc ) DeleteEnhMetaFile( CloseEnhMetaFile( meta_file_dc ) );
+    if( hdc ) DeleteDC( hdc );
+    return NULL;
+}
+
+vlc_family_t *Win32_GetFallbacks( filter_t *p_filter, const char *psz_family,
+                                  uni_char_t codepoint )
+{
+    vlc_family_t  *p_family      = NULL;
+    vlc_family_t  *p_fallbacks   = NULL;
+    filter_sys_t  *p_sys         = p_filter->p_sys;
+    char          *psz_uniscribe = NULL;
+
+
+    char *psz_lc = ToLower( psz_family );
+
+    if( unlikely( !psz_lc ) )
+        return NULL;
+
+    p_fallbacks = vlc_dictionary_value_for_key( &p_sys->fallback_map, psz_lc );
+
+    if( p_fallbacks )
+        p_family = SearchFallbacks( p_filter, p_fallbacks, codepoint );
+
+    if( !p_family )
+    {
+        psz_uniscribe = UniscribeFallback( psz_lc, codepoint );
+
+        if( !psz_uniscribe )
+            goto done;
+
+        const vlc_family_t *p_uniscribe = Win32_GetFamily( p_filter, psz_uniscribe );
+        if( !p_uniscribe || !p_uniscribe->p_fonts )
+            goto done;
+
+        FT_Face p_face = GetFace( p_filter, p_uniscribe->p_fonts );
+
+        if( !p_face || !FT_Get_Char_Index( p_face, codepoint ) )
+            goto done;
+
+        p_family = NewFamily( p_filter, psz_uniscribe, NULL, NULL, NULL );
+
+        if( unlikely( !p_family ) )
+            goto done;
+
+        p_family->p_fonts = p_uniscribe->p_fonts;
+
+        if( p_fallbacks )
+            AppendFamily( &p_fallbacks, p_family );
         else
-            return psz_tmp;
+            vlc_dictionary_insert( &p_sys->fallback_map,
+                                   psz_lc, p_family );
     }
+
+done:
+    free( psz_lc );
+    free( psz_uniscribe );
+    return p_family;
 }
 #endif /* _WIN32 */
 
diff --git a/modules/text_renderer/platform_fonts.h b/modules/text_renderer/platform_fonts.h
index b8fe25c..bc32596 100644
--- a/modules/text_renderer/platform_fonts.h
+++ b/modules/text_renderer/platform_fonts.h
@@ -127,10 +127,10 @@ void FontConfig_BuildCache( filter_t *p_filter );
 #endif
 
 #if defined( _WIN32 ) && !VLC_WINSTORE_APP
-char* Win32_Select( filter_t *p_filter, const char* family,
-                    bool b_bold, bool b_italic,
-                    int *i_idx, uni_char_t codepoint );
+vlc_family_t *Win32_GetFallbacks( filter_t *p_filter, const char *psz_family,
+                                  uni_char_t codepoint );
 
+const vlc_family_t *Win32_GetFamily( filter_t *p_filter, const char *psz_family );
 #endif /* _WIN32 */
 
 #ifdef __APPLE__
diff --git a/modules/text_renderer/text_layout.c b/modules/text_renderer/text_layout.c
index 545568b..65326d2 100644
--- a/modules/text_renderer/text_layout.c
+++ b/modules/text_renderer/text_layout.c
@@ -58,6 +58,14 @@
 #include "text_layout.h"
 #include "freetype.h"
 
+/* Win32 */
+#ifdef _WIN32
+# undef HAVE_FONTCONFIG
+# if !VLC_WINSTORE_APP
+#  define HAVE_FONT_FALLBACK
+# endif
+#endif
+
 /* FontConfig */
 #ifdef HAVE_FONTCONFIG
 # define HAVE_FONT_FALLBACK
-- 
1.9.1



More information about the vlc-devel mailing list