[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