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

freetype: font fallback structs and functions

This will close #5353 #2336 #4973 #5123 #5144 #5847 #7729 #7990 #8641 #12141 #12432
Signed-off-by: default avatarJean-Baptiste Kempf <jb@videolan.org>
parent 290ebe50
...@@ -44,12 +44,6 @@ ...@@ -44,12 +44,6 @@
#include <vlc_text_style.h> /* text_style_t*/ #include <vlc_text_style.h> /* text_style_t*/
#include <vlc_charset.h> #include <vlc_charset.h>
/* Freetype */
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
#include FT_STROKER_H
/* apple stuff */ /* apple stuff */
#ifdef __APPLE__ #ifdef __APPLE__
# include <TargetConditionals.h> # include <TargetConditionals.h>
...@@ -280,6 +274,8 @@ static int LoadFontsFromAttachments( filter_t *p_filter ) ...@@ -280,6 +274,8 @@ static int LoadFontsFromAttachments( filter_t *p_filter )
filter_sys_t *p_sys = p_filter->p_sys; filter_sys_t *p_sys = p_filter->p_sys;
input_attachment_t **pp_attachments; input_attachment_t **pp_attachments;
int i_attachments_cnt; int i_attachments_cnt;
FT_Face p_face = NULL;
char *psz_lc = NULL;
if( filter_GetInputAttachments( p_filter, &pp_attachments, &i_attachments_cnt ) ) if( filter_GetInputAttachments( p_filter, &pp_attachments, &i_attachments_cnt ) )
return VLC_EGENERIC; return VLC_EGENERIC;
...@@ -287,9 +283,15 @@ static int LoadFontsFromAttachments( filter_t *p_filter ) ...@@ -287,9 +283,15 @@ static int LoadFontsFromAttachments( filter_t *p_filter )
p_sys->i_font_attachments = 0; p_sys->i_font_attachments = 0;
p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof(*p_sys->pp_font_attachments)); p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof(*p_sys->pp_font_attachments));
if( !p_sys->pp_font_attachments ) if( !p_sys->pp_font_attachments )
{
for( int i = 0; i < i_attachments_cnt; ++i )
vlc_input_attachment_Delete( pp_attachments[ i ] );
free( pp_attachments );
return VLC_ENOMEM; return VLC_ENOMEM;
}
for( int k = 0; k < i_attachments_cnt; k++ ) int k = 0;
for( ; k < i_attachments_cnt; k++ )
{ {
input_attachment_t *p_attach = pp_attachments[k]; input_attachment_t *p_attach = pp_attachments[k];
...@@ -298,15 +300,100 @@ static int LoadFontsFromAttachments( filter_t *p_filter ) ...@@ -298,15 +300,100 @@ static int LoadFontsFromAttachments( filter_t *p_filter )
p_attach->i_data > 0 && p_attach->p_data ) p_attach->i_data > 0 && p_attach->p_data )
{ {
p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach; p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
int i_font_idx = 0;
while( 0 == FT_New_Memory_Face( p_sys->p_library,
p_attach->p_data,
p_attach->i_data,
i_font_idx,
&p_face ))
{
bool b_bold = p_face->style_flags & FT_STYLE_FLAG_BOLD;
bool b_italic = p_face->style_flags & FT_STYLE_FLAG_ITALIC;
if( p_face->family_name )
psz_lc = ToLower( p_face->family_name );
else
if( asprintf( &psz_lc, FB_NAME"-%02d",
p_sys->i_fallback_counter++ ) < 0 )
psz_lc = NULL;
if( unlikely( !psz_lc ) )
goto error;
vlc_family_t *p_family =
vlc_dictionary_value_for_key( &p_sys->family_map, psz_lc );
if( p_family == kVLCDictionaryNotFound )
{
p_family = NewFamily( p_filter, psz_lc, &p_sys->p_families,
&p_sys->family_map, psz_lc );
if( unlikely( !p_family ) )
goto error;
}
free( psz_lc );
psz_lc = NULL;
char *psz_fontfile;
if( asprintf( &psz_fontfile, ":/%d",
p_sys->i_font_attachments - 1 ) < 0
|| !NewFont( psz_fontfile, i_font_idx, b_bold, b_italic, p_family ) )
goto error;
FT_Done_Face( p_face );
p_face = NULL;
i_font_idx++;
}
} }
else else
{ {
vlc_input_attachment_Delete( p_attach ); vlc_input_attachment_Delete( p_attach );
} }
} }
free( pp_attachments ); free( pp_attachments );
/* Add font attachments to the "attachments" fallback list */
vlc_family_t *p_attachments = NULL;
for( vlc_family_t *p_family = p_sys->p_families; p_family;
p_family = p_family->p_next )
{
vlc_family_t *p_temp = NewFamily( p_filter, p_family->psz_name, &p_attachments,
NULL, NULL );
if( unlikely( !p_temp ) )
{
if( p_attachments )
FreeFamilies( p_attachments, NULL );
return VLC_ENOMEM;
}
else
p_temp->p_fonts = p_family->p_fonts;
}
if( p_attachments )
vlc_dictionary_insert( &p_sys->fallback_map, FB_LIST_ATTACHMENTS, p_attachments );
return VLC_SUCCESS; return VLC_SUCCESS;
error:
if( p_face )
FT_Done_Face( p_face );
if( psz_lc )
free( psz_lc );
for( int i = k + 1; i < i_attachments_cnt; ++i )
vlc_input_attachment_Delete( pp_attachments[ i ] );
free( pp_attachments );
return VLC_ENOMEM;
} }
/***************************************************************************** /*****************************************************************************
...@@ -891,11 +978,10 @@ static void FreeStylesArray( text_style_t **pp_styles, size_t i_styles ) ...@@ -891,11 +978,10 @@ static void FreeStylesArray( text_style_t **pp_styles, size_t i_styles )
} }
static uni_char_t* SegmentsToTextAndStyles( filter_t *p_filter, const text_segment_t *p_segment, size_t *pi_string_length, static uni_char_t* SegmentsToTextAndStyles( filter_t *p_filter, const text_segment_t *p_segment, size_t *pi_string_length,
text_style_t ***ppp_styles, size_t *pi_styles, bool b_grid ) text_style_t ***ppp_styles, size_t *pi_styles )
{ {
text_style_t **pp_styles = NULL; text_style_t **pp_styles = NULL;
uni_char_t *psz_uni = NULL; uni_char_t *psz_uni = NULL;
const int i_scale = ( b_grid ) ? 100 : var_InheritInteger( p_filter, "sub-text-scale");
size_t i_size = 0; size_t i_size = 0;
size_t i_nb_char = 0; size_t i_nb_char = 0;
*pi_styles = 0; *pi_styles = 0;
...@@ -951,12 +1037,6 @@ static uni_char_t* SegmentsToTextAndStyles( filter_t *p_filter, const text_segme ...@@ -951,12 +1037,6 @@ static uni_char_t* SegmentsToTextAndStyles( filter_t *p_filter, const text_segme
/* Overwrite any default or value with forced ones */ /* Overwrite any default or value with forced ones */
text_style_Merge( p_style, p_filter->p_sys->p_forced_style, true ); text_style_Merge( p_style, p_filter->p_sys->p_forced_style, true );
if( i_scale != 100 )
{
p_style->i_font_size = p_style->i_font_size * i_scale / 100;
p_style->f_font_relsize = p_style->f_font_relsize * i_scale / 100;
}
// i_string_bytes is a number of bytes, while here we're going to assign pointer by pointer // i_string_bytes is a number of bytes, while here we're going to assign pointer by pointer
for ( size_t i = 0; i < i_string_bytes / sizeof( *psz_uni ); ++i ) for ( size_t i = 0; i < i_string_bytes / sizeof( *psz_uni ); ++i )
pp_styles[i_nb_char + i] = p_style; pp_styles[i_nb_char + i] = p_style;
...@@ -981,11 +1061,25 @@ static int Render( filter_t *p_filter, subpicture_region_t *p_region_out, ...@@ -981,11 +1061,25 @@ static int Render( filter_t *p_filter, subpicture_region_t *p_region_out,
if( !p_region_in ) if( !p_region_in )
return VLC_EGENERIC; return VLC_EGENERIC;
filter_sys_t *p_sys = p_filter->p_sys;
bool b_grid = p_region_in->b_gridmode;
p_sys->i_scale = ( b_grid ) ? 100 : var_InheritInteger( p_filter, "sub-text-scale");
/*
* Update the default face to reflect changes in video size or text scaling
*/
p_sys->p_face = SelectAndLoadFace( p_filter, p_sys->p_default_style, 0 );
if( !p_sys->p_face )
{
msg_Err( p_filter, "Render(): Error loading default face" );
return VLC_EGENERIC;
}
text_style_t **pp_styles = NULL; text_style_t **pp_styles = NULL;
size_t i_text_length = 0; size_t i_text_length = 0;
size_t i_styles = 0; size_t i_styles = 0;
uni_char_t *psz_text = SegmentsToTextAndStyles( p_filter, p_region_in->p_text, &i_text_length, uni_char_t *psz_text = SegmentsToTextAndStyles( p_filter, p_region_in->p_text, &i_text_length,
&pp_styles, &i_styles, p_region_in->b_gridmode ); &pp_styles, &i_styles );
if( !psz_text || !pp_styles ) if( !psz_text || !pp_styles )
{ {
return VLC_EGENERIC; return VLC_EGENERIC;
...@@ -1065,100 +1159,61 @@ static int Render( filter_t *p_filter, subpicture_region_t *p_region_out, ...@@ -1065,100 +1159,61 @@ static int Render( filter_t *p_filter, subpicture_region_t *p_region_out,
return rv; return rv;
} }
static void FreeFace( void *p_face, void *p_obj )
{
VLC_UNUSED( p_obj );
FT_Done_Face( ( FT_Face ) p_face );
}
/***************************************************************************** /*****************************************************************************
* Create: allocates osd-text video thread output method * Create: allocates osd-text video thread output method
***************************************************************************** *****************************************************************************
* This function allocates and initializes a Clone vout method. * This function allocates and initializes a Clone vout method.
*****************************************************************************/ *****************************************************************************/
static int Init_FT( vlc_object_t *p_this, static int Create( vlc_object_t *p_this )
const char *psz_fontfile,
const int fontindex )
{ {
filter_t *p_filter = (filter_t *)p_this; filter_t *p_filter = ( filter_t * ) p_this;
filter_sys_t *p_sys = p_filter->p_sys; filter_sys_t *p_sys = NULL;
/* */
int i_error = FT_Init_FreeType( &p_sys->p_library );
if( i_error )
{
msg_Err( p_filter, "couldn't initialize freetype" );
goto error;
}
i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
fontindex, &p_sys->p_face );
if( i_error == FT_Err_Unknown_File_Format ) /* Allocate structure */
{ p_filter->p_sys = p_sys = calloc( 1, sizeof( *p_sys ) );
msg_Err( p_filter, "file %s have unknown format", if( !p_sys )
psz_fontfile ? psz_fontfile : "(null)" ); return VLC_ENOMEM;
goto error;
}
else if( i_error )
{
msg_Err( p_filter, "failed to load font file %s",
psz_fontfile ? psz_fontfile : "(null)" );
goto error;
}
i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode ); if( FT_Init_FreeType( &p_sys->p_library ) )
if( i_error )
{ {
msg_Err( p_filter, "font has no unicode translation table" ); msg_Err( p_filter, "Failed to initialize FreeType" );
goto error; free( p_sys );
return VLC_EGENERIC;
} }
if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, STYLE_DEFAULT_FONT_SIZE ) ) if( FT_Stroker_New( p_sys->p_library, &p_sys->p_stroker ) )
{ {
msg_Err( p_filter, "couldn't set font size to %d", STYLE_DEFAULT_FONT_SIZE );
goto error;
}
i_error = FT_Stroker_New( p_sys->p_library, &p_sys->p_stroker );
if( i_error )
msg_Err( p_filter, "Failed to create stroker for outlining" ); msg_Err( p_filter, "Failed to create stroker for outlining" );
p_sys->p_stroker = NULL;
}
return VLC_SUCCESS; p_sys->pp_font_attachments = NULL;
p_sys->i_font_attachments = 0;
error: p_sys->p_families = NULL;
if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
return VLC_EGENERIC;
}
static int Create( vlc_object_t *p_this )
{
filter_t *p_filter = (filter_t *)p_this;
filter_sys_t *p_sys;
char *psz_fontfile = NULL;
char *psz_monofontfile = NULL;
int fontindex = 0, monofontindex = 0;
/* Allocate structure */ vlc_dictionary_init( &p_sys->face_map, 50 );
p_filter->p_sys = p_sys = malloc( sizeof(*p_sys) ); vlc_dictionary_init( &p_sys->family_map, 50 );
if( !p_sys ) vlc_dictionary_init( &p_sys->fallback_map, 20 );
return VLC_ENOMEM;
p_sys->p_face = 0; p_sys->i_fallback_counter = 0;
p_sys->p_library = 0; p_sys->i_scale = 100;
/* default style to apply to uncomplete segmeents styles */ /* default style to apply to uncomplete segmeents styles */
p_sys->p_default_style = text_style_Create( STYLE_FULLY_SET ); p_sys->p_default_style = text_style_Create( STYLE_FULLY_SET );
if(unlikely(!p_sys->p_default_style)) if(unlikely(!p_sys->p_default_style))
{ goto error;
free(p_sys);
return VLC_ENOMEM;
}
/* empty style for style overriding cases */ /* empty style for style overriding cases */
p_sys->p_forced_style = text_style_Create( STYLE_NO_DEFAULTS ); p_sys->p_forced_style = text_style_Create( STYLE_NO_DEFAULTS );
if(unlikely(!p_sys->p_forced_style)) if(unlikely(!p_sys->p_forced_style))
{ goto error;
text_style_Delete( p_sys->p_default_style );
free(p_sys);
return VLC_ENOMEM;
}
/* fills default and forced style */ /* fills default and forced style */
FillDefaultStyles( p_filter ); FillDefaultStyles( p_filter );
...@@ -1200,6 +1255,9 @@ static int Create( vlc_object_t *p_this ) ...@@ -1200,6 +1255,9 @@ static int Create( vlc_object_t *p_this )
#endif #endif
} }
if( LoadFontsFromAttachments( p_filter ) == VLC_ENOMEM )
goto error;
#ifdef HAVE_FONTCONFIG #ifdef HAVE_FONTCONFIG
p_sys->pf_select = FontConfig_Select; p_sys->pf_select = FontConfig_Select;
FontConfig_BuildCache( p_filter ); FontConfig_BuildCache( p_filter );
...@@ -1213,64 +1271,42 @@ static int Create( vlc_object_t *p_this ) ...@@ -1213,64 +1271,42 @@ static int Create( vlc_object_t *p_this )
p_sys->pf_select = Dummy_Select; p_sys->pf_select = Dummy_Select;
#endif #endif
/* */ p_sys->p_face = SelectAndLoadFace( p_filter, p_sys->p_default_style, 0 );
psz_fontfile = p_sys->pf_select( p_filter, p_sys->p_default_style->psz_fontname, if( !p_sys->p_face )
false, false, p_sys->p_default_style->i_font_size, &fontindex ); {
psz_monofontfile = p_sys->pf_select( p_filter, p_sys->p_default_style->psz_monofontname, msg_Err( p_filter, "Error loading default face" );
false, false, p_sys->p_default_style->i_font_size,
&monofontindex );
msg_Dbg( p_filter, "Using %s as font from file %s",
p_sys->p_default_style->psz_fontname, psz_fontfile );
msg_Dbg( p_filter, "Using %s as mono-font from file %s",
p_sys->p_default_style->psz_monofontname, psz_monofontfile );
/* If nothing is found, use the default family */
if( !psz_fontfile )
psz_fontfile = File_Select( p_sys->p_default_style->psz_fontname );
if( !psz_monofontfile )
psz_monofontfile = File_Select( p_sys->p_default_style->psz_monofontname );
if( Init_FT( p_this, psz_fontfile, fontindex ) != VLC_SUCCESS )
goto error; goto error;
}
int i_faces_size = 20;
p_sys->faces_cache.p_faces = malloc( i_faces_size * sizeof( *p_sys->faces_cache.p_faces ) );
p_sys->faces_cache.p_styles = malloc( i_faces_size * sizeof( *p_sys->faces_cache.p_styles ) );
p_sys->faces_cache.i_cache_size = i_faces_size;
p_sys->faces_cache.i_faces_count = 0;
p_sys->pp_font_attachments = NULL;
p_sys->i_font_attachments = 0;
p_filter->pf_render = Render; p_filter->pf_render = Render;
LoadFontsFromAttachments( p_filter );
free( psz_fontfile );
free( psz_monofontfile );
return VLC_SUCCESS; return VLC_SUCCESS;
error: error:
free( psz_fontfile );
free( psz_monofontfile );
text_style_Delete( p_sys->p_default_style ); text_style_Delete( p_sys->p_default_style );
text_style_Delete( p_sys->p_forced_style ); text_style_Delete( p_sys->p_forced_style );
free( p_sys );
return VLC_EGENERIC;
}
vlc_dictionary_clear( &p_sys->fallback_map, FreeFamilies, p_filter );
vlc_dictionary_clear( &p_sys->face_map, FreeFace, p_filter );
vlc_dictionary_clear( &p_sys->family_map, NULL, NULL );
if( p_sys->p_families )
FreeFamiliesAndFonts( p_sys->p_families );
static void Destroy_FT( vlc_object_t *p_this ) if( p_sys->pp_font_attachments )
{ {
filter_t *p_filter = (filter_t *)p_this; for( int k = 0; k < p_sys->i_font_attachments; k++ )
filter_sys_t *p_sys = p_filter->p_sys; vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
free( p_sys->pp_font_attachments );
}
if( p_sys->p_stroker ) if( p_sys->p_stroker )
FT_Stroker_Done( p_sys->p_stroker ); FT_Stroker_Done( p_sys->p_stroker );
FT_Done_Face( p_sys->p_face );
FT_Done_FreeType( p_sys->p_library ); FT_Done_FreeType( p_sys->p_library );
free( p_sys );
return VLC_EGENERIC;
} }
/***************************************************************************** /*****************************************************************************
...@@ -1283,14 +1319,26 @@ static void Destroy( vlc_object_t *p_this ) ...@@ -1283,14 +1319,26 @@ static void Destroy( vlc_object_t *p_this )
filter_t *p_filter = (filter_t *)p_this; filter_t *p_filter = (filter_t *)p_this;
filter_sys_t *p_sys = p_filter->p_sys; filter_sys_t *p_sys = p_filter->p_sys;
faces_cache_t *p_cache = &p_sys->faces_cache; #if 0
for( int i = 0; i < p_cache->i_faces_count; ++i ) msg_Dbg( p_filter, "------------------" );
{ msg_Dbg( p_filter, "p_sys->p_families:" );
FT_Done_Face( p_cache->p_faces[ i ] ); msg_Dbg( p_filter, "------------------" );
free( p_cache->p_styles[ i ].psz_fontname ); DumpFamily( p_filter, p_sys->p_families, true, -1 );
} msg_Dbg( p_filter, "-----------------" );
free( p_sys->faces_cache.p_faces ); msg_Dbg( p_filter, "p_sys->family_map" );
free( p_sys->faces_cache.p_styles ); msg_Dbg( p_filter, "-----------------" );
DumpDictionary( p_filter, &p_sys->family_map, false, 1 );
msg_Dbg( p_filter, "-------------------" );
msg_Dbg( p_filter, "p_sys->fallback_map" );
msg_Dbg( p_filter, "-------------------" );
DumpDictionary( p_filter, &p_sys->fallback_map, true, -1 );
#endif
vlc_dictionary_clear( &p_sys->fallback_map, FreeFamilies, p_filter );
vlc_dictionary_clear( &p_sys->face_map, FreeFace, p_filter );
vlc_dictionary_clear( &p_sys->family_map, NULL, NULL );
if( p_sys->p_families )
FreeFamiliesAndFonts( p_sys->p_families );
if( p_sys->pp_font_attachments ) if( p_sys->pp_font_attachments )
{ {
...@@ -1303,59 +1351,19 @@ static void Destroy( vlc_object_t *p_this ) ...@@ -1303,59 +1351,19 @@ static void Destroy( vlc_object_t *p_this )
text_style_Delete( p_sys->p_default_style ); text_style_Delete( p_sys->p_default_style );
text_style_Delete( p_sys->p_forced_style ); text_style_Delete( p_sys->p_forced_style );
Destroy_FT( p_this ); if( p_sys->p_stroker )
free( p_sys ); FT_Stroker_Done( p_sys->p_stroker );
}
/* Face loading */
bool FaceStyleEquals( const text_style_t *p_style1,
const text_style_t *p_style2 )
{
if( !p_style1 || !p_style2 )
return false;
if( p_style1 == p_style2 )
return true;
const int i_style_mask = STYLE_BOLD | STYLE_ITALIC;
return (p_style1->i_style_flags & i_style_mask) == (p_style2->i_style_flags & i_style_mask) &&
!strcmp( p_style1->psz_fontname, p_style2->psz_fontname );
}
static FT_Face LoadEmbeddedFace( filter_sys_t *p_sys, const char *psz_fontname,
const text_style_t *p_style )
{
for( int k = 0; k < p_sys->i_font_attachments; k++ )
{
input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
int i_font_idx = 0;
FT_Face p_face = NULL;
while( 0 == FT_New_Memory_Face( p_sys->p_library, FT_Done_FreeType( p_sys->p_library );
p_attach->p_data,
p_attach->i_data,
i_font_idx,
&p_face ))
{
if( p_face )
{
int i_style_received = ((p_face->style_flags & FT_STYLE_FLAG_BOLD) ? STYLE_BOLD : 0) |
((p_face->style_flags & FT_STYLE_FLAG_ITALIC ) ? STYLE_ITALIC : 0);
if( p_face->family_name != NULL
&& !strcasecmp( p_face->family_name, psz_fontname )
&& (p_style->i_style_flags & (STYLE_BOLD | STYLE_ITALIC))
== i_style_received )
return p_face;
FT_Done_Face( p_face ); free( p_sys );
}
i_font_idx++;
}
}
return NULL;
} }
/* Face loading */
int ConvertToLiveSize( filter_t *p_filter, const text_style_t *p_style ) int ConvertToLiveSize( filter_t *p_filter, const text_style_t *p_style )
{ {
filter_sys_t *p_sys = p_filter->p_sys;
int i_font_size = STYLE_DEFAULT_FONT_SIZE; int i_font_size = STYLE_DEFAULT_FONT_SIZE;
if( p_style->i_font_size ) if( p_style->i_font_size )
{ {
...@@ -1365,115 +1373,118 @@ int ConvertToLiveSize( filter_t *p_filter, const text_style_t *p_style ) ...@@ -1365,115 +1373,118 @@ int ConvertToLiveSize( filter_t *p_filter, const text_style_t *p_style )
{ {
i_font_size = (int) p_filter->fmt_out.video.i_height * p_style->f_font_relsize; i_font_size = (int) p_filter->fmt_out.video.i_height * p_style->f_font_relsize;
} }
if( p_sys->i_scale != 100 )
i_font_size = i_font_size * p_sys->i_scale / 100;
return i_font_size; return i_font_size;
} }
FT_Face LoadFace( filter_t *p_filter, FT_Face LoadFace( filter_t *p_filter, const char *psz_fontfile, int i_idx,
const text_style_t *p_style, int i_font_size ) const text_style_t *p_style )
{ {
filter_sys_t *p_sys = p_filter->p_sys; filter_sys_t *p_sys = p_filter->p_sys;
char *psz_key = NULL;
faces_cache_t *p_cache = &p_sys->faces_cache; int i_font_size = ConvertToLiveSize( p_filter, p_style );
for( int i = 0; i < p_cache->i_faces_count; ++i ) int i_font_width = p_style->i_style_flags & STYLE_HALFWIDTH ?
if( FaceStyleEquals( &p_cache->p_styles[ i ], p_style ) i_font_size / 2 : i_font_size;
&& p_cache->p_styles[ i ].i_font_size == i_font_size
&& !( ( p_cache->p_styles[ i ].i_style_flags ^ p_style->i_style_flags ) & STYLE_HALFWIDTH ) )
return p_cache->p_faces[ i ];
const char *psz_fontname = (p_style->i_style_flags & STYLE_MONOSPACED) if( asprintf( &psz_key, "%s - %d - %d - %d",
? p_style->psz_monofontname : p_style->psz_fontname; psz_fontfile, i_idx,
i_font_size, i_font_width ) < 0 )
return NULL;
/* Look for a match amongst our attachments first */ FT_Face p_face = vlc_dictionary_value_for_key( &p_sys->face_map, psz_key );
FT_Face p_face = LoadEmbeddedFace( p_sys, psz_fontname, p_style ); if( p_face != kVLCDictionaryNotFound )
goto done;
/* Load system wide font otheriwse */ if( psz_fontfile[0] == ':' && psz_fontfile[1] == '/' )
if( !p_face )
{ {
int i_idx = 0; int i_attach = atoi( psz_fontfile + 2 );
char *psz_fontfile = NULL; if( i_attach < 0 || i_attach >= p_sys->i_font_attachments )
if( p_sys->pf_select )
psz_fontfile = p_sys->pf_select( p_filter,
psz_fontname,
(p_style->i_style_flags & STYLE_BOLD) != 0,
(p_style->i_style_flags & STYLE_ITALIC) != 0,
-1,
&i_idx );
else
psz_fontfile = NULL;
if( !psz_fontfile )
return NULL;
if( *psz_fontfile == '\0' )
{ {
msg_Warn( p_filter, msg_Err( p_filter, "LoadFace: Invalid font attachment index" );
"We were not able to find a matching font: \"%s\" (%s %s),"
" so using default font",
psz_fontname,
(p_style->i_style_flags & STYLE_BOLD) ? "Bold" : "",
(p_style->i_style_flags & STYLE_ITALIC) ? "Italic" : "" );
p_face = NULL; p_face = NULL;
} }
else else
{ {
if( FT_New_Face( p_sys->p_library, psz_fontfile, i_idx, &p_face ) ) input_attachment_t *p_attach = p_sys->pp_font_attachments[ i_attach ];
if( FT_New_Memory_Face( p_sys->p_library, p_attach->p_data,
p_attach->i_data, i_idx, &p_face ) )
p_face = NULL; p_face = NULL;
} }
free( psz_fontfile );
} }
else
if( FT_New_Face( p_sys->p_library, psz_fontfile, i_idx, &p_face ) )
{
msg_Err( p_filter, "LoadFace: Error creating face for %s", psz_key );
p_face = NULL;
}
if( !p_face ) if( !p_face )
return NULL; goto done;
if( FT_Select_Charmap( p_face, ft_encoding_unicode ) ) if( FT_Select_Charmap( p_face, ft_encoding_unicode ) )
{ {
/* We've loaded a font face which is unhelpful for actually /* We've loaded a font face which is unhelpful for actually
* rendering text - fallback to the default one. * rendering text - fallback to the default one.
*/ */
msg_Err( p_filter, "LoadFace: Error selecting charmap for %s", psz_key );
FT_Done_Face( p_face ); FT_Done_Face( p_face );
return NULL; p_face = NULL;
goto done;
} }
int i_font_width = p_style->i_style_flags & STYLE_HALFWIDTH
? i_font_size / 2 : i_font_size;
if( FT_Set_Pixel_Sizes( p_face, i_font_width, i_font_size ) ) if( FT_Set_Pixel_Sizes( p_face, i_font_width, i_font_size ) )
{ {
msg_Err( p_filter, msg_Err( p_filter,
"Failed to set font size to %d", i_font_size ); "LoadFace: Failed to set font size for %s", psz_key );
FT_Done_Face( p_face ); FT_Done_Face( p_face );
return NULL; p_face = NULL;
goto done;
} }
if( p_cache->i_faces_count == p_cache->i_cache_size ) vlc_dictionary_insert( &p_sys->face_map, psz_key, p_face );
{
FT_Face *p_new_faces = done:
realloc( p_cache->p_faces, p_cache->i_cache_size * 2 * sizeof( *p_cache->p_faces ) ); free( psz_key );
if( !p_new_faces ) return p_face;
{ }
FT_Done_Face( p_face );
return NULL; FT_Face SelectAndLoadFace( filter_t *p_filter, const text_style_t *p_style,
} uni_char_t codepoint )
{
filter_sys_t *p_sys = p_filter->p_sys;
const char *psz_fontname = (p_style->i_style_flags & STYLE_MONOSPACED)
? p_style->psz_monofontname : p_style->psz_fontname;
p_cache->p_faces = p_new_faces; bool b_bold = p_style->i_style_flags & STYLE_BOLD;
bool b_italic = p_style->i_style_flags & STYLE_ITALIC;
text_style_t *p_new_styles = FT_Face p_face = NULL;
realloc( p_cache->p_styles, p_cache->i_cache_size * 2 * sizeof( *p_cache->p_styles ) ) ;
if( !p_new_styles )
int i_idx = 0;
char *psz_fontfile = NULL;
if( p_sys->pf_select )
psz_fontfile = p_sys->pf_select( p_filter, psz_fontname, b_bold, b_italic,
&i_idx, codepoint );
else
psz_fontfile = NULL;
if( !psz_fontfile || *psz_fontfile == '\0' )
{ {
FT_Done_Face( p_face ); msg_Warn( p_filter,
"SelectAndLoadFace: no font found for family: %s, codepoint: 0x%x",
psz_fontname, codepoint );
free( psz_fontfile );
return NULL; return NULL;
} }
p_cache->p_styles = p_new_styles; p_face = LoadFace( p_filter, psz_fontfile, i_idx, p_style );
p_cache->i_cache_size *= 2;
}
text_style_t *p_face_style = p_cache->p_styles + p_cache->i_faces_count;
p_face_style->i_font_size = i_font_size;
p_face_style->i_style_flags = p_style->i_style_flags;
p_face_style->psz_fontname = strdup( psz_fontname );
p_cache->p_faces[ p_cache->i_faces_count ] = p_face;
++p_cache->i_faces_count;
free( psz_fontfile );
return p_face; return p_face;
} }
...@@ -29,14 +29,14 @@ ...@@ -29,14 +29,14 @@
#define VLC_FREETYPE_H #define VLC_FREETYPE_H
#include <vlc_text_style.h> /* text_style_t*/ #include <vlc_text_style.h> /* text_style_t*/
#include <vlc_arrays.h>
typedef struct faces_cache_t #include <ft2build.h>
{ #include FT_FREETYPE_H
FT_Face *p_faces; #include FT_GLYPH_H
text_style_t *p_styles; #include FT_STROKER_H
int i_faces_count;
int i_cache_size; #include "platform_fonts.h"
} faces_cache_t;
/***************************************************************************** /*****************************************************************************
* filter_sys_t: freetype local data * filter_sys_t: freetype local data
...@@ -61,13 +61,48 @@ struct filter_sys_t ...@@ -61,13 +61,48 @@ struct filter_sys_t
input_attachment_t **pp_font_attachments; input_attachment_t **pp_font_attachments;
int i_font_attachments; int i_font_attachments;
/* Font faces cache */ /*
faces_cache_t faces_cache; * This is the master family list. It owns the lists of vlc_font_t's
* and should be freed using FreeFamiliesAndFonts()
*/
vlc_family_t *p_families;
char * (*pf_select) (filter_t *, const char* family, /*
bool bold, bool italic, int size, * This maps a family name to a vlc_family_t within the master list
int *index); */
vlc_dictionary_t family_map;
/*
* This maps a family name to a fallback list of vlc_family_t's.
* Fallback lists only reference the lists of vlc_font_t's within the
* master list, so they should be freed using FreeFamilies()
*/
vlc_dictionary_t fallback_map;
/* Font face cache */
vlc_dictionary_t face_map;
int i_fallback_counter;
int i_scale;
char * (*pf_select) (filter_t *, const char* family,
bool bold, bool italic,
int *index, uni_char_t codepoint);
/*
* Get a pointer to the vlc_family_t in the master list that matches psz_family.
* Add this family to the list if it hasn't been added yet.
*/
const vlc_family_t * (*pf_get_family) ( filter_t *p_filter, const char *psz_family );
/*
* Get the fallback list for psz_family from the system and cache
* it in fallback_map.
* On Windows fallback lists are populated progressively as required
* using Uniscribe, so we need the codepoint here.
*/
vlc_family_t * (*pf_get_fallbacks) ( filter_t *p_filter, const char *psz_family,
uni_char_t codepoint );
}; };
#define FT_FLOOR(X) ((X & -64) >> 6) #define FT_FLOOR(X) ((X & -64) >> 6)
...@@ -76,23 +111,12 @@ struct filter_sys_t ...@@ -76,23 +111,12 @@ struct filter_sys_t
#define FT_MulFix(v, s) (((v)*(s))>>16) #define FT_MulFix(v, s) (((v)*(s))>>16)
#endif #endif
#ifdef __OS2__ FT_Face LoadFace( filter_t *p_filter, const char *psz_fontfile, int i_idx,
typedef uint16_t uni_char_t; const text_style_t *p_style );
# define FREETYPE_TO_UCS "UCS-2LE"
#else
typedef uint32_t uni_char_t;
# if defined(WORDS_BIGENDIAN)
# define FREETYPE_TO_UCS "UCS-4BE"
# else
# define FREETYPE_TO_UCS "UCS-4LE"
# endif
#endif
FT_Face SelectAndLoadFace( filter_t *p_filter, const text_style_t *p_style,
uni_char_t codepoint );
FT_Face LoadFace( filter_t *p_filter, const text_style_t *p_style, int );
int ConvertToLiveSize( filter_t *p_filter, const text_style_t *p_style ); int ConvertToLiveSize( filter_t *p_filter, const text_style_t *p_style );
bool FaceStyleEquals( const text_style_t *p_style1,
const text_style_t *p_style2 );
#endif #endif
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
* Bernie Purcell <bitmap@videolan.org> * Bernie Purcell <bitmap@videolan.org>
* Jean-Baptiste Kempf <jb@videolan.org> * Jean-Baptiste Kempf <jb@videolan.org>
* Felix Paul Kühne <fkuehne@videolan.org> * Felix Paul Kühne <fkuehne@videolan.org>
* Salah-Eddin Shaban <salshaaban@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by * under the terms of the GNU Lesser General Public License as published by
...@@ -36,6 +37,7 @@ ...@@ -36,6 +37,7 @@
#include <vlc_common.h> #include <vlc_common.h>
#include <vlc_filter.h> /* filter_sys_t */ #include <vlc_filter.h> /* filter_sys_t */
#include <vlc_text_style.h> /* text_style_t*/ #include <vlc_text_style.h> /* text_style_t*/
#include <ctype.h>
/* apple stuff */ /* apple stuff */
#ifdef __APPLE__ #ifdef __APPLE__
...@@ -60,6 +62,359 @@ ...@@ -60,6 +62,359 @@
#endif #endif
#include "platform_fonts.h" #include "platform_fonts.h"
#include "freetype.h"
static FT_Face GetFace( filter_t *p_filter, vlc_font_t *p_font )
{
filter_sys_t *p_sys = p_filter->p_sys;
if( p_font->p_face )
return p_font->p_face;
p_font->p_face = LoadFace( p_filter, p_font->psz_fontfile, p_font->i_index,
p_sys->p_default_style );
return p_font->p_face;
}
static vlc_font_t *GetBestFont( filter_t *p_filter, const vlc_family_t *p_family,
bool b_bold, bool b_italic, uni_char_t codepoint )
{
int i_best_score = 0;
vlc_font_t *p_best_font = p_family->p_fonts;
for( vlc_font_t *p_font = p_family->p_fonts; p_font; p_font = p_font->p_next )
{
int i_score = 0;
if( codepoint )
{
FT_Face p_face = GetFace( p_filter, p_font );
if( p_face && FT_Get_Char_Index( p_face, codepoint ) )
i_score += 1000;
}
if( !!p_font->b_bold == !!b_bold )
i_score += 100;
if( !!p_font->b_italic == !!b_italic )
i_score += 10;
if( i_score > i_best_score )
{
p_best_font = p_font;
i_best_score = i_score;
}
}
return p_best_font;
}
static vlc_family_t *SearchFallbacks( filter_t *p_filter, vlc_family_t *p_fallbacks,
uni_char_t codepoint )
{
filter_sys_t *p_sys = p_filter->p_sys;
vlc_family_t *p_family = NULL;
for( vlc_family_t *p_fallback = p_fallbacks; p_fallback;
p_fallback = p_fallback->p_next )
{
if( !p_fallback->p_fonts )
{
const vlc_family_t *p_temp =
p_sys->pf_get_family( p_filter, p_fallback->psz_name );
if( !p_temp || !p_temp->p_fonts )
continue;
p_fallback->p_fonts = p_temp->p_fonts;
}
FT_Face p_face = GetFace( p_filter, p_fallback->p_fonts );
if( !p_face || !FT_Get_Char_Index( p_face, codepoint ) )
continue;
p_family = p_fallback;
break;
}
return p_family;
}
vlc_family_t *NewFamily( filter_t *p_filter, const char *psz_family,
vlc_family_t **pp_list, vlc_dictionary_t *p_dict,
const char *psz_key )
{
filter_sys_t *p_sys = p_filter->p_sys;
vlc_family_t *p_family = NULL;
p_family = calloc( 1, sizeof( *p_family ) );
char *psz_name;
if( psz_family && *psz_family )
psz_name = ToLower( psz_family );
else
if( asprintf( &psz_name, FB_NAME"-%02d",
p_sys->i_fallback_counter++ ) < 0 )
psz_name = NULL;
char *psz_lc = NULL;
if( likely( psz_name ) )
{
if( !psz_key )
psz_lc = strdup( psz_name );
else
psz_lc = ToLower( psz_key );
}
if( unlikely( !p_family || !psz_name || !psz_lc ) )
{
free( p_family );
free( psz_name );
free( psz_lc );
return NULL;
}
p_family->psz_name = psz_name;
if( pp_list )
AppendFamily( pp_list, p_family );
if( p_dict )
{
vlc_family_t *p_root = vlc_dictionary_value_for_key( p_dict, psz_lc );
if( p_root )
AppendFamily( &p_root, p_family );
else
vlc_dictionary_insert( p_dict, psz_lc, p_family );
}
free( psz_lc );
return p_family;
}
vlc_font_t *NewFont( char *psz_fontfile, int i_index,
bool b_bold, bool b_italic,
vlc_family_t *p_parent )
{
vlc_font_t *p_font = calloc( 1, sizeof( *p_font ) );
if( unlikely( !p_font ) )
{
free( psz_fontfile );
return NULL;
}
p_font->psz_fontfile = psz_fontfile;
p_font->i_index = i_index;
p_font->b_bold = b_bold;
p_font->b_italic = b_italic;
if( p_parent )
{
/* Keep regular faces first */
if( p_parent->p_fonts
&& ( p_parent->p_fonts->b_bold || p_parent->p_fonts->b_italic )
&& !b_bold && !b_italic )
{
p_font->p_next = p_parent->p_fonts;
p_parent->p_fonts = p_font;
}
else
AppendFont( &p_parent->p_fonts, p_font );
}
return p_font;
}
void FreeFamiliesAndFonts( vlc_family_t *p_family )
{
if( p_family->p_next )
FreeFamiliesAndFonts( p_family->p_next );
for( vlc_font_t *p_font = p_family->p_fonts; p_font; )
{
vlc_font_t *p_temp = p_font->p_next;
free( p_font->psz_fontfile );
free( p_font );
p_font = p_temp;
}
free( p_family->psz_name );
free( p_family );
}
void FreeFamilies( void *p_families, void *p_obj )
{
vlc_family_t *p_family = ( vlc_family_t * ) p_families;
if( p_family->p_next )
FreeFamilies( p_family->p_next, p_obj );
free( p_family->psz_name );
free( p_family );
}
vlc_family_t *InitDefaultList( filter_t *p_filter, const char *const *ppsz_default,
int i_size )
{
vlc_family_t *p_default = NULL;
filter_sys_t *p_sys = p_filter->p_sys;
for( int i = 0; i < i_size; ++i )
{
const vlc_family_t *p_family =
p_sys->pf_get_family( p_filter, ppsz_default[ i ] );
if( p_family )
{
vlc_family_t *p_temp =
NewFamily( p_filter, ppsz_default[ i ], &p_default, NULL, NULL );
if( unlikely( !p_temp ) )
goto error;
p_temp->p_fonts = p_family->p_fonts;
}
}
if( p_default )
vlc_dictionary_insert( &p_sys->fallback_map, FB_LIST_DEFAULT, p_default );
return p_default;
error:
if( p_default ) FreeFamilies( p_default, NULL );
return NULL;
}
void DumpFamily( filter_t *p_filter, const vlc_family_t *p_family,
bool b_dump_fonts, int i_max_families )
{
if( i_max_families < 0 )
i_max_families = INT_MAX;
for( int i = 0; p_family && i < i_max_families ; p_family = p_family->p_next, ++i )
{
msg_Dbg( p_filter, "\t[0x%"PRIxPTR"] %s",
( uintptr_t ) p_family, p_family->psz_name );
if( b_dump_fonts )
{
for( vlc_font_t *p_font = p_family->p_fonts; p_font; p_font = p_font->p_next )
{
const char *psz_style = NULL;
if( !p_font->b_bold && !p_font->b_italic )
psz_style = "Regular";
else if( p_font->b_bold && !p_font->b_italic )
psz_style = "Bold";
else if( !p_font->b_bold && p_font->b_italic )
psz_style = "Italic";
else if( p_font->b_bold && p_font->b_italic )
psz_style = "Bold Italic";
msg_Dbg( p_filter, "\t\t[0x%"PRIxPTR"] (%s): %s - %d",
( uintptr_t ) p_font, psz_style,
p_font->psz_fontfile, p_font->i_index );
}
}
}
}
void DumpDictionary( filter_t *p_filter, const vlc_dictionary_t *p_dict,
bool b_dump_fonts, int i_max_families )
{
char **ppsz_keys = vlc_dictionary_all_keys( p_dict );
for( int i = 0; ppsz_keys[ i ]; ++i )
{
vlc_family_t *p_family = vlc_dictionary_value_for_key( p_dict, ppsz_keys[ i ] );
msg_Dbg( p_filter, "Key: %s", ppsz_keys[ i ] );
if( p_family )
DumpFamily( p_filter, p_family, b_dump_fonts, i_max_families );
free( ppsz_keys[ i ] );
}
free( ppsz_keys );
}
char* ToLower( const char *psz_src )
{
int i_size = strlen( psz_src ) + 1;
char *psz_buffer = malloc( i_size );
if( unlikely( !psz_buffer ) )
return NULL;
for( int i = 0; i < i_size; ++i )
psz_buffer[ i ] = tolower( psz_src[ i ] );
return psz_buffer;
}
char* Generic_Select( filter_t *p_filter, const char* psz_family,
bool b_bold, bool b_italic,
int *i_idx, uni_char_t codepoint )
{
filter_sys_t *p_sys = p_filter->p_sys;
const vlc_family_t *p_family = NULL;
vlc_family_t *p_fallbacks = NULL;
if( codepoint )
{
/*
* Try regular face of the same family first.
* It usually has the best coverage.
*/
const vlc_family_t *p_temp = p_sys->pf_get_family( p_filter, psz_family );
if( p_temp && p_temp->p_fonts )
{
FT_Face p_face = GetFace( p_filter, p_temp->p_fonts );
if( p_face && FT_Get_Char_Index( p_face, codepoint ) )
p_family = p_temp;
}
/* Try font attachments */
if( !p_family )
{
p_fallbacks = vlc_dictionary_value_for_key( &p_sys->fallback_map,
FB_LIST_ATTACHMENTS );
if( p_fallbacks )
p_family = SearchFallbacks( p_filter, p_fallbacks, codepoint );
}
/* Try system fallbacks */
if( !p_family )
{
p_fallbacks = p_sys->pf_get_fallbacks( p_filter, psz_family, codepoint );
if( p_fallbacks )
p_family = SearchFallbacks( p_filter, p_fallbacks, codepoint );
}
/* Try the default fallback list, if any */
if( !p_family )
{
p_fallbacks = vlc_dictionary_value_for_key( &p_sys->fallback_map,
FB_LIST_DEFAULT );
if( p_fallbacks )
p_family = SearchFallbacks( p_filter, p_fallbacks, codepoint );
}
if( !p_family )
return NULL;
}
if( !p_family )
p_family = p_sys->pf_get_family( p_filter, psz_family );
vlc_font_t *p_font;
if( p_family && ( p_font = GetBestFont( p_filter, p_family, b_bold,
b_italic, codepoint ) ) )
{
*i_idx = p_font->i_index;
return strdup( p_font->psz_fontfile );
}
return File_Select( SYSTEM_DEFAULT_FONT_FILE );
}
#ifdef HAVE_FONTCONFIG #ifdef HAVE_FONTCONFIG
void FontConfig_BuildCache( filter_t *p_filter ) void FontConfig_BuildCache( filter_t *p_filter )
...@@ -110,7 +465,8 @@ void FontConfig_BuildCache( filter_t *p_filter ) ...@@ -110,7 +465,8 @@ void FontConfig_BuildCache( filter_t *p_filter )
* \brief Selects a font matching family, bold, italic provided * \brief Selects a font matching family, bold, italic provided
***/ ***/
char* FontConfig_Select( filter_t *p_filter, const char* family, char* FontConfig_Select( filter_t *p_filter, const char* family,
bool b_bold, bool b_italic, int i_size, int *i_idx ) bool b_bold, bool b_italic,
int *i_idx, uni_char_t codepoint )
{ {
FcResult result = FcResultMatch; FcResult result = FcResultMatch;
FcPattern *pat, *p_pat; FcPattern *pat, *p_pat;
...@@ -119,6 +475,7 @@ char* FontConfig_Select( filter_t *p_filter, const char* family, ...@@ -119,6 +475,7 @@ char* FontConfig_Select( filter_t *p_filter, const char* family,
char *ret = NULL; char *ret = NULL;
FcConfig* config = NULL; FcConfig* config = NULL;
VLC_UNUSED(p_filter); VLC_UNUSED(p_filter);
VLC_UNUSED(codepoint);
/* Create a pattern and fills it */ /* Create a pattern and fills it */
pat = FcPatternCreate(); pat = FcPatternCreate();
...@@ -129,10 +486,6 @@ char* FontConfig_Select( filter_t *p_filter, const char* family, ...@@ -129,10 +486,6 @@ char* FontConfig_Select( filter_t *p_filter, const char* family,
FcPatternAddBool( pat, FC_OUTLINE, FcTrue ); FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN ); FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL ); FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
if( i_size > 0 )
{
FcPatternAddDouble( pat, FC_SIZE, (double)i_size );
}
/* */ /* */
FcDefaultSubstitute( pat ); FcDefaultSubstitute( pat );
...@@ -259,9 +612,10 @@ static char* GetWindowsFontPath() ...@@ -259,9 +612,10 @@ static char* GetWindowsFontPath()
} }
char* Win32_Select( filter_t *p_filter, const char* family, char* Win32_Select( filter_t *p_filter, const char* family,
bool b_bold, bool b_italic, int i_size, int *i_idx ) bool b_bold, bool b_italic,
int *i_idx, uni_char_t codepoint )
{ {
VLC_UNUSED( i_size ); VLC_UNUSED( codepoint );
VLC_UNUSED( i_idx ); VLC_UNUSED( i_idx );
VLC_UNUSED( p_filter ); VLC_UNUSED( p_filter );
...@@ -328,11 +682,12 @@ fail: ...@@ -328,11 +682,12 @@ fail:
#ifdef __APPLE__ #ifdef __APPLE__
#if !TARGET_OS_IPHONE #if !TARGET_OS_IPHONE
char* MacLegacy_Select( filter_t *p_filter, const char* psz_fontname, char* MacLegacy_Select( filter_t *p_filter, const char* psz_fontname,
bool b_bold, bool b_italic, int i_size, int *i_idx ) bool b_bold, bool b_italic,
int *i_idx, uni_char_t codepoint )
{ {
VLC_UNUSED( b_bold ); VLC_UNUSED( b_bold );
VLC_UNUSED( b_italic ); VLC_UNUSED( b_italic );
VLC_UNUSED( i_size ); VLC_UNUSED( codepoint );
FSRef ref; FSRef ref;
unsigned char path[MAXPATHLEN]; unsigned char path[MAXPATHLEN];
char * psz_path; char * psz_path;
...@@ -409,12 +764,13 @@ char* MacLegacy_Select( filter_t *p_filter, const char* psz_fontname, ...@@ -409,12 +764,13 @@ char* MacLegacy_Select( filter_t *p_filter, const char* psz_fontname,
#endif #endif
char* Dummy_Select( filter_t *p_filter, const char* psz_font, char* Dummy_Select( filter_t *p_filter, const char* psz_font,
bool b_bold, bool b_italic, int i_size, int *i_idx ) bool b_bold, bool b_italic,
int *i_idx, uni_char_t codepoint )
{ {
VLC_UNUSED(p_filter); VLC_UNUSED(p_filter);
VLC_UNUSED(b_bold); VLC_UNUSED(b_bold);
VLC_UNUSED(b_italic); VLC_UNUSED(b_italic);
VLC_UNUSED(i_size); VLC_UNUSED(codepoint);
VLC_UNUSED(i_idx); VLC_UNUSED(i_idx);
char *psz_fontname; char *psz_fontname;
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
* Bernie Purcell <bitmap@videolan.org> * Bernie Purcell <bitmap@videolan.org>
* Jean-Baptiste Kempf <jb@videolan.org> * Jean-Baptiste Kempf <jb@videolan.org>
* Felix Paul Kühne <fkuehne@videolan.org> * Felix Paul Kühne <fkuehne@videolan.org>
* Salah-Eddin Shaban <salshaaban@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by * under the terms of the GNU Lesser General Public License as published by
...@@ -29,10 +30,28 @@ ...@@ -29,10 +30,28 @@
* Preamble * Preamble
*****************************************************************************/ *****************************************************************************/
#ifndef PLATFORM_FONTS_H
#define PLATFORM_FONTS_H
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
# include "config.h" # include "config.h"
#endif #endif
#include <ft2build.h>
#include FT_FREETYPE_H
#ifdef __OS2__
typedef uint16_t uni_char_t;
# define FREETYPE_TO_UCS "UCS-2LE"
#else
typedef uint32_t uni_char_t;
# if defined(WORDS_BIGENDIAN)
# define FREETYPE_TO_UCS "UCS-4BE"
# else
# define FREETYPE_TO_UCS "UCS-4LE"
# endif
#endif
/* Default fonts */ /* Default fonts */
#ifdef __APPLE__ #ifdef __APPLE__
# define SYSTEM_DEFAULT_FONT_FILE "/Library/Fonts/Arial Unicode.ttf" # define SYSTEM_DEFAULT_FONT_FILE "/Library/Fonts/Arial Unicode.ttf"
...@@ -77,28 +96,100 @@ ...@@ -77,28 +96,100 @@
#define DEFAULT_MONOSPACE_FAMILY SYSTEM_DEFAULT_MONOSPACE_FAMILY #define DEFAULT_MONOSPACE_FAMILY SYSTEM_DEFAULT_MONOSPACE_FAMILY
#endif #endif
typedef struct vlc_font_t vlc_font_t;
struct vlc_font_t
{
vlc_font_t *p_next;
char *psz_fontfile;
int i_index;
bool b_bold;
bool b_italic;
FT_Face p_face;
};
typedef struct vlc_family_t vlc_family_t;
struct vlc_family_t
{
vlc_family_t *p_next;
char *psz_name;
vlc_font_t *p_fonts;
};
#define FB_LIST_ATTACHMENTS "attachments"
#define FB_LIST_DEFAULT "default"
#define FB_NAME "fallback"
#ifdef HAVE_FONTCONFIG #ifdef HAVE_FONTCONFIG
char* FontConfig_Select( filter_t *p_filter, const char* family, char* FontConfig_Select( filter_t *p_filter, const char* family,
bool b_bold, bool b_italic, int i_size, int *i_idx ); bool b_bold, bool b_italic,
int *i_idx, uni_char_t codepoint );
void FontConfig_BuildCache( filter_t *p_filter ); void FontConfig_BuildCache( filter_t *p_filter );
#endif #endif
#if defined( _WIN32 ) && !VLC_WINSTORE_APP #if defined( _WIN32 ) && !VLC_WINSTORE_APP
char* Win32_Select( filter_t *p_filter, const char* family, char* Win32_Select( filter_t *p_filter, const char* family,
bool b_bold, bool b_italic, int i_size, int *i_idx ); bool b_bold, bool b_italic,
int *i_idx, uni_char_t codepoint );
#endif /* _WIN32 */ #endif /* _WIN32 */
#ifdef __APPLE__ #ifdef __APPLE__
#if !TARGET_OS_IPHONE #if !TARGET_OS_IPHONE
char* MacLegacy_Select( filter_t *p_filter, const char* psz_fontname, char* MacLegacy_Select( filter_t *p_filter, const char* psz_fontname,
bool b_bold, bool b_italic, int i_size, int *i_idx ); bool b_bold, bool b_italic,
int *i_idx, uni_char_t codepoint );
#endif #endif
#endif #endif
char* Dummy_Select( filter_t *p_filter, const char* family, char* Dummy_Select( filter_t *p_filter, const char* family,
bool b_bold, bool b_italic, int i_size, int *i_idx ); bool b_bold, bool b_italic,
int *i_idx, uni_char_t codepoint );
#define File_Select(a) Dummy_Select(NULL, a, 0, 0, NULL, 0)
char* Generic_Select( filter_t *p_filter, const char* family,
bool b_bold, bool b_italic,
int *i_idx, uni_char_t codepoint );
static inline void AppendFont( vlc_font_t **pp_list, vlc_font_t *p_font )
{
while( *pp_list )
pp_list = &( *pp_list )->p_next;
*pp_list = p_font;
}
static inline void AppendFamily( vlc_family_t **pp_list, vlc_family_t *p_family )
{
while( *pp_list )
pp_list = &( *pp_list )->p_next;
*pp_list = p_family;
}
vlc_family_t *NewFamily( filter_t *p_filter, const char *psz_family,
vlc_family_t **pp_list, vlc_dictionary_t *p_dict,
const char *psz_key );
/* This function takes ownership of psz_fontfile */
vlc_font_t *NewFont( char *psz_fontfile, int i_index,
bool b_bold, bool b_italic,
vlc_family_t *p_parent );
void FreeFamiliesAndFonts( vlc_family_t *p_family );
void FreeFamilies( void *p_families, void *p_obj );
vlc_family_t *InitDefaultList( filter_t *p_filter, const char *const *ppsz_default,
int i_size );
void DumpFamily( filter_t *p_filter, const vlc_family_t *p_family,
bool b_dump_fonts, int i_max_families );
void DumpDictionary( filter_t *p_filter, const vlc_dictionary_t *p_dict,
bool b_dump_fonts, int i_max_families );
char* ToLower( const char *psz_src );
#define File_Select(a) Dummy_Select(NULL, a, 0, 0, 0, NULL) #endif //PLATFORM_FONTS_H
...@@ -34,7 +34,6 @@ ...@@ -34,7 +34,6 @@
#endif #endif
#include <vlc_common.h> #include <vlc_common.h>
#include <vlc_charset.h>
#include <vlc_filter.h> #include <vlc_filter.h>
#include <vlc_text_style.h> #include <vlc_text_style.h>
...@@ -105,6 +104,7 @@ typedef struct paragraph_t ...@@ -105,6 +104,7 @@ typedef struct paragraph_t
uni_char_t *p_code_points; //Unicode code points uni_char_t *p_code_points; //Unicode code points
int *pi_glyph_indices; //Glyph index values within the run's font face int *pi_glyph_indices; //Glyph index values within the run's font face
text_style_t **pp_styles; text_style_t **pp_styles;
FT_Face *pp_faces;
int *pi_run_ids; //The run to which each glyph belongs int *pi_run_ids; //The run to which each glyph belongs
glyph_bitmaps_t *p_glyph_bitmaps; glyph_bitmaps_t *p_glyph_bitmaps;
uint8_t *pi_karaoke_bar; uint8_t *pi_karaoke_bar;
...@@ -225,6 +225,8 @@ static paragraph_t *NewParagraph( filter_t *p_filter, ...@@ -225,6 +225,8 @@ static paragraph_t *NewParagraph( filter_t *p_filter,
malloc( i_size * sizeof( *p_paragraph->pi_glyph_indices ) ); malloc( i_size * sizeof( *p_paragraph->pi_glyph_indices ) );
p_paragraph->pp_styles = p_paragraph->pp_styles =
malloc( i_size * sizeof( *p_paragraph->pp_styles ) ); malloc( i_size * sizeof( *p_paragraph->pp_styles ) );
p_paragraph->pp_faces =
calloc( i_size, sizeof( *p_paragraph->pp_faces ) );
p_paragraph->pi_run_ids = p_paragraph->pi_run_ids =
calloc( i_size, sizeof( *p_paragraph->pi_run_ids ) ); calloc( i_size, sizeof( *p_paragraph->pi_run_ids ) );
p_paragraph->p_glyph_bitmaps = p_paragraph->p_glyph_bitmaps =
...@@ -237,9 +239,9 @@ static paragraph_t *NewParagraph( filter_t *p_filter, ...@@ -237,9 +239,9 @@ static paragraph_t *NewParagraph( filter_t *p_filter,
p_paragraph->i_runs_count = 0; p_paragraph->i_runs_count = 0;
if( !p_paragraph->p_code_points || !p_paragraph->pi_glyph_indices if( !p_paragraph->p_code_points || !p_paragraph->pi_glyph_indices
|| !p_paragraph->pp_styles || !p_paragraph->pi_run_ids || !p_paragraph->pp_styles || !p_paragraph->pp_faces
|| !p_paragraph->p_glyph_bitmaps || !p_paragraph->pi_karaoke_bar || !p_paragraph->pi_run_ids|| !p_paragraph->p_glyph_bitmaps
|| !p_paragraph->p_runs ) || !p_paragraph->pi_karaoke_bar || !p_paragraph->p_runs )
goto error; goto error;
if( p_code_points ) if( p_code_points )
...@@ -288,6 +290,7 @@ error: ...@@ -288,6 +290,7 @@ error:
if( p_paragraph->p_code_points ) free( p_paragraph->p_code_points ); if( p_paragraph->p_code_points ) free( p_paragraph->p_code_points );
if( p_paragraph->pi_glyph_indices ) free( p_paragraph->pi_glyph_indices ); if( p_paragraph->pi_glyph_indices ) free( p_paragraph->pi_glyph_indices );
if( p_paragraph->pp_styles ) free( p_paragraph->pp_styles ); if( p_paragraph->pp_styles ) free( p_paragraph->pp_styles );
if( p_paragraph->pp_faces ) free( p_paragraph->pp_faces );
if( p_paragraph->pi_run_ids ) free( p_paragraph->pi_run_ids ); if( p_paragraph->pi_run_ids ) free( p_paragraph->pi_run_ids );
if( p_paragraph->p_glyph_bitmaps ) free( p_paragraph->p_glyph_bitmaps ); if( p_paragraph->p_glyph_bitmaps ) free( p_paragraph->p_glyph_bitmaps );
if (p_paragraph->pi_karaoke_bar ) free( p_paragraph->pi_karaoke_bar ); if (p_paragraph->pi_karaoke_bar ) free( p_paragraph->pi_karaoke_bar );
...@@ -312,6 +315,7 @@ static void FreeParagraph( paragraph_t *p_paragraph ) ...@@ -312,6 +315,7 @@ static void FreeParagraph( paragraph_t *p_paragraph )
free( p_paragraph->p_glyph_bitmaps ); free( p_paragraph->p_glyph_bitmaps );
free( p_paragraph->pi_karaoke_bar ); free( p_paragraph->pi_karaoke_bar );
free( p_paragraph->pi_run_ids ); free( p_paragraph->pi_run_ids );
free( p_paragraph->pp_faces );
free( p_paragraph->pp_styles ); free( p_paragraph->pp_styles );
free( p_paragraph->p_code_points ); free( p_paragraph->p_code_points );
...@@ -385,7 +389,8 @@ static int AddRun( filter_t *p_filter, ...@@ -385,7 +389,8 @@ static int AddRun( filter_t *p_filter,
paragraph_t *p_paragraph, paragraph_t *p_paragraph,
int i_start_offset, int i_start_offset,
int i_end_offset, int i_end_offset,
FT_Face p_face ) FT_Face p_face,
const text_style_t *p_style )
{ {
if( i_start_offset >= i_end_offset if( i_start_offset >= i_end_offset
|| i_start_offset < 0 || i_start_offset >= p_paragraph->i_size || i_start_offset < 0 || i_start_offset >= p_paragraph->i_size
...@@ -417,9 +422,13 @@ static int AddRun( filter_t *p_filter, ...@@ -417,9 +422,13 @@ static int AddRun( filter_t *p_filter,
run_desc_t *p_run = p_paragraph->p_runs + p_paragraph->i_runs_count++; run_desc_t *p_run = p_paragraph->p_runs + p_paragraph->i_runs_count++;
p_run->i_start_offset = i_start_offset; p_run->i_start_offset = i_start_offset;
p_run->i_end_offset = i_end_offset; p_run->i_end_offset = i_end_offset;
p_run->p_style = p_paragraph->pp_styles[ i_start_offset ];
p_run->p_face = p_face; p_run->p_face = p_face;
if( p_style )
p_run->p_style = p_style;
else
p_run->p_style = p_paragraph->pp_styles[ i_start_offset ];
#ifdef HAVE_HARFBUZZ #ifdef HAVE_HARFBUZZ
p_run->script = p_paragraph->p_scripts[ i_start_offset ]; p_run->script = p_paragraph->p_scripts[ i_start_offset ];
p_run->direction = p_paragraph->p_levels[ i_start_offset ] & 1 ? p_run->direction = p_paragraph->p_levels[ i_start_offset ] & 1 ?
...@@ -432,6 +441,109 @@ static int AddRun( filter_t *p_filter, ...@@ -432,6 +441,109 @@ static int AddRun( filter_t *p_filter,
return VLC_SUCCESS; return VLC_SUCCESS;
} }
/*
* Add a run with font fallback, possibly breaking the run further
* into runs of glyphs that end up having the same font face.
*/
#ifdef HAVE_FONT_FALLBACK
static int AddRunWithFallback( filter_t *p_filter, paragraph_t *p_paragraph,
int i_start_offset, int i_end_offset )
{
if( i_start_offset >= i_end_offset
|| i_start_offset < 0 || i_start_offset >= p_paragraph->i_size
|| i_end_offset <= 0 || i_end_offset > p_paragraph->i_size )
{
msg_Err( p_filter,
"AddRunWithFallback() invalid parameters. Paragraph size: %d, "
"Start offset: %d, End offset: %d",
p_paragraph->i_size, i_start_offset, i_end_offset );
return VLC_EGENERIC;
}
const text_style_t *p_style = p_paragraph->pp_styles[ i_start_offset ];
/* Maximum number of faces to try for each run */
#define MAX_FACES 5
FT_Face pp_faces[ MAX_FACES ] = {0};
pp_faces[ 0 ] = SelectAndLoadFace( p_filter, p_style, 0 );
for( int i = i_start_offset; i < i_end_offset; ++i )
{
int i_index = 0;
int i_glyph_index = 0;
FT_Face p_face = NULL;
do {
p_face = pp_faces[ i_index ];
if( !p_face )
p_face = pp_faces[ i_index ] =
SelectAndLoadFace( p_filter, p_style,
p_paragraph->p_code_points[ i ] );
if( !p_face )
continue;
i_glyph_index = FT_Get_Char_Index( p_face,
p_paragraph->p_code_points[ i ] );
if( i_glyph_index )
{
p_paragraph->pp_faces[ i ] = p_face;
/*
* Move p_face to the beginning of the array. Otherwise strikethrough
* lines can appear segmented, being rendered at a certain height
* through spaces and at a different height through words
*/
if( i_index > 0 )
{
pp_faces[ i_index ] = pp_faces[ 0 ];
pp_faces[ 0 ] = p_face;
}
}
} while( i_glyph_index == 0 && ++i_index < MAX_FACES );
}
int i_run_start = i_start_offset;
for( int i = i_start_offset; i <= i_end_offset; ++i )
{
if( i == i_end_offset
|| p_paragraph->pp_faces[ i_run_start ] != p_paragraph->pp_faces[ i ] )
{
if( AddRun( p_filter, p_paragraph, i_run_start, i,
p_paragraph->pp_faces[ i_run_start ], NULL ) )
return VLC_EGENERIC;
i_run_start = i;
}
}
return VLC_SUCCESS;
}
#endif
static bool FaceStyleEquals( filter_t *p_filter, const text_style_t *p_style1,
const text_style_t *p_style2 )
{
if( !p_style1 || !p_style2 )
return false;
if( p_style1 == p_style2 )
return true;
const int i_style_mask = STYLE_BOLD | STYLE_ITALIC | STYLE_HALFWIDTH;
const char *psz_fontname1 = p_style1->i_style_flags & STYLE_MONOSPACED
? p_style1->psz_monofontname : p_style1->psz_fontname;
const char *psz_fontname2 = p_style2->i_style_flags & STYLE_MONOSPACED
? p_style2->psz_monofontname : p_style2->psz_fontname;
const int i_size1 = ConvertToLiveSize( p_filter, p_style1 );
const int i_size2 = ConvertToLiveSize( p_filter, p_style2 );
return (p_style1->i_style_flags & i_style_mask) == (p_style2->i_style_flags & i_style_mask)
&& i_size1 == i_size2
&& !strcasecmp( psz_fontname1, psz_fontname2 );
}
/* /*
* Segment a paragraph into runs * Segment a paragraph into runs
*/ */
...@@ -460,16 +572,14 @@ static int ItemizeParagraph( filter_t *p_filter, paragraph_t *p_paragraph ) ...@@ -460,16 +572,14 @@ static int ItemizeParagraph( filter_t *p_filter, paragraph_t *p_paragraph )
|| last_script != p_paragraph->p_scripts[ i ] || last_script != p_paragraph->p_scripts[ i ]
|| last_level != p_paragraph->p_levels[ i ] || last_level != p_paragraph->p_levels[ i ]
#endif #endif
|| ( p_paragraph->pp_styles[ i ] != NULL && ( || !FaceStyleEquals( p_filter, p_last_style, p_paragraph->pp_styles[ i ] ) )
p_last_style->i_font_size != p_paragraph->pp_styles[ i ]->i_font_size
|| ( ( p_last_style->i_style_flags
^ p_paragraph->pp_styles[ i ]->i_style_flags )
& STYLE_HALFWIDTH )
||!FaceStyleEquals( p_last_style, p_paragraph->pp_styles[ i ] ) )
)
)
{ {
int i_ret = AddRun( p_filter, p_paragraph, i_last_run_start, i, 0 ); int i_ret;
#ifdef HAVE_FONT_FALLBACK
i_ret = AddRunWithFallback( p_filter, p_paragraph, i_last_run_start, i );
#else
i_ret = AddRun( p_filter, p_paragraph, i_last_run_start, i, NULL, NULL );
#endif
if( i_ret ) if( i_ret )
return i_ret; return i_ret;
...@@ -516,17 +626,20 @@ static int ShapeParagraphHarfBuzz( filter_t *p_filter, ...@@ -516,17 +626,20 @@ static int ShapeParagraphHarfBuzz( filter_t *p_filter,
{ {
run_desc_t *p_run = p_paragraph->p_runs + i; run_desc_t *p_run = p_paragraph->p_runs + i;
const text_style_t *p_style = p_run->p_style; const text_style_t *p_style = p_run->p_style;
const int i_live_size = ConvertToLiveSize( p_filter, p_style );
/* /*
* When using HarfBuzz, this is where font faces are loaded. * With HarfBuzz and no font fallback, this is where font faces
* In the other two paths (shaping with FriBidi or no * are loaded. In the other two paths (shaping with FriBidi or no
* shaping at all), faces are loaded in LoadGlyphs() * shaping at all), faces are loaded in LoadGlyphs().
*
* If we have font fallback, font faces in all paths will be
* loaded in AddRunWithFallback(), except for runs of codepoints
* for which no font could be found.
*/ */
FT_Face p_face = 0; FT_Face p_face = 0;
if( !p_run->p_face ) if( !p_run->p_face )
{ {
p_face = LoadFace( p_filter, p_style, i_live_size ); p_face = SelectAndLoadFace( p_filter, p_style, 0 );
if( !p_face ) if( !p_face )
{ {
p_face = p_sys->p_face; p_face = p_sys->p_face;
...@@ -637,7 +750,7 @@ static int ShapeParagraphHarfBuzz( filter_t *p_filter, ...@@ -637,7 +750,7 @@ static int ShapeParagraphHarfBuzz( filter_t *p_filter,
++i_index; ++i_index;
} }
if( AddRun( p_filter, p_new_paragraph, i_index - p_run->i_glyph_count, if( AddRun( p_filter, p_new_paragraph, i_index - p_run->i_glyph_count,
i_index, p_run->p_face ) ) i_index, p_run->p_face, p_run->p_style ) )
goto error; goto error;
} }
...@@ -785,7 +898,7 @@ static int LoadGlyphs( filter_t *p_filter, paragraph_t *p_paragraph, ...@@ -785,7 +898,7 @@ static int LoadGlyphs( filter_t *p_filter, paragraph_t *p_paragraph,
FT_Face p_face = 0; FT_Face p_face = 0;
if( !p_run->p_face ) if( !p_run->p_face )
{ {
p_face = LoadFace( p_filter, p_style, i_live_size ); p_face = SelectAndLoadFace( p_filter, p_style, 0 );
if( !p_face ) if( !p_face )
{ {
/* Uses the default font and style */ /* Uses the default font and style */
......
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