Commit fd07f90e authored by Salah-Eddin Shaban's avatar Salah-Eddin Shaban Committed by Jean-Baptiste Kempf

freetype: font fallback for Windows

Signed-off-by: Jean-Baptiste Kempf's avatarJean-Baptiste Kempf <jb@videolan.org>
parent 195da069
......@@ -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
......@@ -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;
......
......@@ -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 */
......
......@@ -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__
......
......@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment