[vlc-devel] [RFC PATCH] freetype: font fallback using fontconfig

Salah-Eddin Shaban salshaaban at gmail.com
Wed Jul 8 23:46:21 CEST 2015


---
 modules/text_renderer/freetype.c       | 237 ++++++++++++++++++++++++++-------
 modules/text_renderer/freetype.h       |  29 ++--
 modules/text_renderer/platform_fonts.c |  41 +++++-
 modules/text_renderer/platform_fonts.h |  16 ++-
 modules/text_renderer/text_layout.c    | 210 ++++++++++++++++++++++++++++-
 5 files changed, 454 insertions(+), 79 deletions(-)

diff --git a/modules/text_renderer/freetype.c b/modules/text_renderer/freetype.c
index 2773779..3958c89 100644
--- a/modules/text_renderer/freetype.c
+++ b/modules/text_renderer/freetype.c
@@ -67,12 +67,13 @@
 
 /* FontConfig */
 #ifdef HAVE_FONTCONFIG
+# include <fontconfig/fontconfig.h>
+# include <fontconfig/fcfreetype.h>
 # define HAVE_GET_FONT_BY_FAMILY_NAME
 #endif
 
 #include <assert.h>
 
-#include "text_renderer.h"
 #include "platform_fonts.h"
 #include "freetype.h"
 #include "text_layout.h"
@@ -297,6 +298,35 @@ static int LoadFontsFromAttachments( filter_t *p_filter )
             p_attach->i_data > 0 && p_attach->p_data )
         {
             p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
+
+#ifdef HAVE_FONTCONFIG
+            char buffer[ 10 ];
+            FT_Face p_face;
+            int i_num_faces = 0;
+            int i_index = 0;
+            snprintf( buffer, sizeof( buffer ), ":/%d", p_sys->i_font_attachments - 1 );
+
+            do {
+                if( FT_New_Memory_Face( p_sys->p_library, p_attach->p_data,
+                                        p_attach->i_data, i_index, &p_face ) )
+                    continue;
+
+                i_num_faces = p_face->num_faces;
+
+                FcPattern *p_pattern =
+                    FcFreeTypeQueryFace( p_face,
+                                         ( const FcChar8* )buffer, i_index,
+                                         FcConfigGetBlanks( p_sys->p_config ) );
+
+                FcFontSet *p_set = FcConfigGetFonts( p_sys->p_config, FcSetSystem );
+
+                if( !p_pattern || !p_set || !FcFontSetAdd( p_set, p_pattern ) )
+                    msg_Err( p_filter,
+                             "Error adding font attachment to font database" );
+
+                FT_Done_Face( p_face );
+            } while( ++i_index < i_num_faces );
+#endif
         }
         else
         {
@@ -863,6 +893,7 @@ static inline int RenderAXYZ( filter_t *p_filter,
 
 
 
+#ifndef HAVE_FONTCONFIG
 static FT_Face LoadEmbeddedFace( filter_sys_t *p_sys, const text_style_t *p_style )
 {
     for( int k = 0; k < p_sys->i_font_attachments; k++ )
@@ -894,6 +925,7 @@ static FT_Face LoadEmbeddedFace( filter_sys_t *p_sys, const text_style_t *p_styl
     }
     return NULL;
 }
+#endif
 
 static xml_reader_t *GetXMLReader( filter_t *p_filter, stream_t *p_sub )
 {
@@ -1275,6 +1307,7 @@ static int Create( vlc_object_t *p_this )
     p_sys->style.psz_monofontname = psz_monofontfamily;
 
 #ifdef HAVE_FONTCONFIG
+    p_sys->p_config = FcInitLoadConfigAndFonts();
     p_sys->pf_select = FontConfig_Select;
     FontConfig_BuildCache( p_filter );
 #elif defined( __APPLE__ )
@@ -1289,10 +1322,11 @@ static int Create( vlc_object_t *p_this )
 
     /* */
     psz_fontfile = p_sys->pf_select( p_filter, psz_fontname, false, false,
-                                      p_sys->i_default_font_size, &fontindex );
+                                     p_sys->i_default_font_size, &fontindex,
+                                     NULL, 0 );
     psz_monofontfile = p_sys->pf_select( p_filter, psz_monofontfamily, false,
-                                          false, p_sys->i_default_font_size,
-                                          &monofontindex );
+                                         false, p_sys->i_default_font_size,
+                                         &monofontindex, NULL, 0 );
     msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontname, psz_fontfile );
     msg_Dbg( p_filter, "Using %s as mono-font from file %s", psz_monofontfamily, psz_monofontfile );
 
@@ -1305,11 +1339,7 @@ static int Create( vlc_object_t *p_this )
     if( Init_FT( p_this, psz_fontfile, fontindex, f_outline_thickness ) != VLC_SUCCESS )
         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;
+    vlc_dictionary_init( &p_sys->faces_cache, 20 );
 
     p_sys->pp_font_attachments = NULL;
     p_sys->i_font_attachments = 0;
@@ -1346,6 +1376,17 @@ static void Destroy_FT( vlc_object_t *p_this )
     FT_Done_FreeType( p_sys->p_library );
 }
 
+static void FreeFace( void *p_face, void *p_obj )
+{
+
+    VLC_UNUSED( p_obj );
+
+    //filter_t *p_filter = ( filter_t * ) p_obj;
+    //msg_Dbg( p_filter, "FT_Done_Face: 0x%lx", ( long ) p_face );
+
+    FT_Done_Face( ( FT_Face ) p_face );
+}
+
 /*****************************************************************************
  * Destroy: destroy Clone video thread output method
  *****************************************************************************
@@ -1356,14 +1397,7 @@ static void Destroy( vlc_object_t *p_this )
     filter_t *p_filter = (filter_t *)p_this;
     filter_sys_t *p_sys = p_filter->p_sys;
 
-    faces_cache_t *p_cache = &p_sys->faces_cache;
-    for( int i = 0; i < p_cache->i_faces_count; ++i )
-    {
-        FT_Done_Face( p_cache->p_faces[ i ] );
-        free( p_cache->p_styles[ i ].psz_fontname );
-    }
-    free( p_sys->faces_cache.p_faces );
-    free( p_sys->faces_cache.p_styles );
+    vlc_dictionary_clear( &p_sys->faces_cache, FreeFace, p_filter );
 
     if( p_sys->pp_font_attachments )
     {
@@ -1378,23 +1412,43 @@ static void Destroy( vlc_object_t *p_this )
     free( p_sys->style.psz_monofontname );
 
     Destroy_FT( p_this );
+
+#ifdef HAVE_FONTCONFIG
+    FcConfigDestroy( p_sys->p_config );
+#endif
+
     free( p_sys );
 }
 
+#ifndef HAVE_FONTCONFIG
 FT_Face LoadFace( filter_t *p_filter,
-                  const text_style_t *p_style )
+                  const text_style_t *p_style,
+                  const char *language,
+                  uni_char_t codepoint )
 {
+    VLC_UNUSED(language);
+    VLC_UNUSED(codepoint);
+
     filter_sys_t *p_sys = p_filter->p_sys;
+    char buffer[ 128 ];
+
+    const char *p_bold = (p_style->i_style_flags & STYLE_BOLD) ? " Bold" : "";
+    const char *p_italic = (p_style->i_style_flags & STYLE_ITALIC) ? " Italic" : "";
+    int i_font_width = (p_style->i_style_flags & STYLE_HALFWIDTH) ?
+                        p_style->i_font_size / 2 : p_style->i_font_size;
+    snprintf( buffer, sizeof( buffer ), "%s%s%s - %d - %d",
+              p_style->psz_fontname,
+              p_bold, p_italic,
+              p_style->i_font_size, i_font_width );
+
+    FT_Face p_face = vlc_dictionary_value_for_key( &p_sys->faces_cache,
+                                                   buffer );
+    if( p_face != kVLCDictionaryNotFound )
+        return p_face;
 
-    faces_cache_t *p_cache = &p_sys->faces_cache;
-    for( int i = 0; i < p_cache->i_faces_count; ++i )
-        if( FaceStyleEquals( &p_cache->p_styles[ i ], p_style )
-         && p_cache->p_styles[ i ].i_font_size == p_style->i_font_size
-         && !( ( p_cache->p_styles[ i ].i_style_flags ^ p_style->i_style_flags ) & STYLE_HALFWIDTH ) )
-            return p_cache->p_faces[ i ];
 
     /* Look for a match amongst our attachments first */
-    FT_Face p_face = LoadEmbeddedFace( p_sys, p_style );
+    p_face = LoadEmbeddedFace( p_sys, p_style );
 
     /* Load system wide font otheriwse */
     if( !p_face )
@@ -1407,7 +1461,8 @@ FT_Face LoadFace( filter_t *p_filter,
                                              (p_style->i_style_flags & STYLE_BOLD) != 0,
                                              (p_style->i_style_flags & STYLE_ITALIC) != 0,
                                              -1,
-                                             &i_idx );
+                                             &i_idx,
+                                             NULL, 0 );
         else
             psz_fontfile = NULL;
 
@@ -1443,9 +1498,6 @@ FT_Face LoadFace( filter_t *p_filter,
         return NULL;
     }
 
-    int i_font_width = p_style->i_style_flags & STYLE_HALFWIDTH
-                     ? p_style->i_font_size / 2 : p_style->i_font_size;
-
     if( FT_Set_Pixel_Sizes( p_face, i_font_width, p_style->i_font_size ) )
     {
         msg_Err( p_filter,
@@ -1454,36 +1506,119 @@ FT_Face LoadFace( filter_t *p_filter,
         return NULL;
     }
 
-    if( p_cache->i_faces_count == p_cache->i_cache_size )
+    //msg_Dbg( p_filter, "LoadFace: adding face to cache: 0x%lx %s", ( long ) p_face, buffer );
+
+    vlc_dictionary_insert( &p_sys->faces_cache, buffer, p_face );
+
+    return p_face;
+}
+
+#else
+
+FT_Face LoadFace( filter_t *p_filter,
+                  const text_style_t *p_style,
+                  const char *language,
+                  uni_char_t codepoint )
+{
+    //msg_Dbg( p_filter, "LoadFace: font requested: %s, language: %s, codepoint: %x",
+    //         p_style->psz_fontname, language, codepoint );
+
+    filter_sys_t *p_sys = p_filter->p_sys;
+    char buffer[ 128 ];
+
+    int  i_idx = 0;
+    char *psz_fontfile = NULL;
+    if( p_sys->pf_select )
+        psz_fontfile = p_sys->pf_select( p_filter,
+                                         p_style->psz_fontname,
+                                         (p_style->i_style_flags & STYLE_BOLD) != 0,
+                                         (p_style->i_style_flags & STYLE_ITALIC) != 0,
+                                         -1,
+                                         &i_idx,
+                                         language, codepoint );
+    else
+        psz_fontfile = NULL;
+
+    if( !psz_fontfile )
+        return NULL;
+
+    if( *psz_fontfile == '\0' )
     {
-        FT_Face *p_new_faces =
-                realloc( p_cache->p_faces, p_cache->i_cache_size * 2 * sizeof( *p_cache->p_faces ) );
-        if( !p_new_faces )
-        {
-            FT_Done_Face( p_face );
-            return NULL;
-        }
+        msg_Warn( p_filter,
+                  "We were not able to find a matching font: \"%s\" (%s %s),"
+                  " so using default font",
+                  p_style->psz_fontname,
+                  (p_style->i_style_flags & STYLE_BOLD)   ? "Bold" : "",
+                  (p_style->i_style_flags & STYLE_ITALIC) ? "Italic" : "" );
+        free( psz_fontfile );
+        return NULL;
+    }
+
+    //msg_Dbg( p_filter, "LoadFace: font returned: %s, face index: %d",
+    //         psz_fontfile, i_idx );
 
-        p_cache->p_faces = p_new_faces;
+    int i_font_width = (p_style->i_style_flags & STYLE_HALFWIDTH) ?
+            p_style->i_font_size / 2 : p_style->i_font_size;
+    snprintf( buffer, sizeof( buffer ), "%s - %d - %d - %d",
+              psz_fontfile,
+              i_idx,
+              p_style->i_font_size, i_font_width );
 
-        text_style_t *p_new_styles =
-                realloc( p_cache->p_styles, p_cache->i_cache_size * 2 * sizeof( *p_cache->p_styles ) ) ;
-        if( !p_new_styles )
+    FT_Face p_face = vlc_dictionary_value_for_key( &p_sys->faces_cache,
+                                                   buffer );
+    if( p_face != kVLCDictionaryNotFound )
+    {
+        free( psz_fontfile );
+        return p_face;
+    }
+
+    if( psz_fontfile[0] == ':' && psz_fontfile[1] == '/' )
+    {
+        int i_attach = atoi( psz_fontfile + 2 );
+        if( i_attach < 0 || i_attach >= p_sys->i_font_attachments )
         {
-            FT_Done_Face( p_face );
-            return NULL;
+            msg_Err( p_filter, "Invalid font attachment index" );
+            p_face = NULL;
+        }
+        else
+        {
+            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;
         }
+    }
+    else
+        if( FT_New_Face( p_sys->p_library, psz_fontfile, i_idx, &p_face ) )
+            p_face = NULL;
+
+    free( psz_fontfile );
 
-        p_cache->p_styles = p_new_styles;
-        p_cache->i_cache_size *= 2;
+    if( !p_face )
+        return NULL;
+
+    if( FT_Select_Charmap( p_face, ft_encoding_unicode ) )
+    {
+        /* We've loaded a font face which is unhelpful for actually
+         * rendering text - fallback to the default one.
+         */
+        FT_Done_Face( p_face );
+        return NULL;
     }
 
-    text_style_t *p_face_style = p_cache->p_styles + p_cache->i_faces_count;
-    p_face_style->i_font_size = p_style->i_font_size;
-    p_face_style->i_style_flags = p_style->i_style_flags;
-    p_face_style->psz_fontname = strdup( p_style->psz_fontname );
-    p_cache->p_faces[ p_cache->i_faces_count ] = p_face;
-    ++p_cache->i_faces_count;
+    if( FT_Set_Pixel_Sizes( p_face, i_font_width, p_style->i_font_size ) )
+    {
+        msg_Err( p_filter,
+                 "Failed to set font size to %d", p_style->i_font_size );
+        FT_Done_Face( p_face );
+        return NULL;
+    }
+
+    //msg_Dbg( p_filter, "LoadFace: adding face to cache: 0x%lx %s", ( long ) p_face, buffer );
+
+    vlc_dictionary_insert( &p_sys->faces_cache, buffer, p_face );
 
     return p_face;
 }
+
+#endif
diff --git a/modules/text_renderer/freetype.h b/modules/text_renderer/freetype.h
index d486773..beb1489 100644
--- a/modules/text_renderer/freetype.h
+++ b/modules/text_renderer/freetype.h
@@ -25,13 +25,15 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
 
-typedef struct faces_cache_t
-{
-    FT_Face        *p_faces;
-    text_style_t   *p_styles;
-    int            i_faces_count;
-    int            i_cache_size;
-} faces_cache_t;
+#include <vlc_arrays.h>
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_STROKER_H
+
+#ifdef HAVE_FONTCONFIG
+#include <fontconfig/fontconfig.h>
+#endif
 
 /*****************************************************************************
  * filter_sys_t: freetype local data
@@ -59,12 +61,16 @@ struct filter_sys_t
     int                  i_font_attachments;
 
     /* Font faces cache */
-    faces_cache_t  faces_cache;
+    vlc_dictionary_t     faces_cache;
 
     char * (*pf_select) (filter_t *, const char* family,
-                               bool bold, bool italic, int size,
-                               int *index);
+                         bool bold, bool italic, int size,
+                         int *index, const char *language,
+                         uni_char_t codepoint);
 
+#ifdef HAVE_FONTCONFIG
+    FcConfig *p_config;
+#endif
 };
 
 #define FT_FLOOR(X)     ((X & -64) >> 6)
@@ -73,4 +79,5 @@ struct filter_sys_t
  #define FT_MulFix(v, s) (((v)*(s))>>16)
 #endif
 
-FT_Face LoadFace( filter_t *p_filter, const text_style_t *p_style );
+FT_Face LoadFace( filter_t *p_filter, const text_style_t *p_style,
+                  const char *language, uni_char_t codepoint );
diff --git a/modules/text_renderer/platform_fonts.c b/modules/text_renderer/platform_fonts.c
index 7869dba..543466b 100644
--- a/modules/text_renderer/platform_fonts.c
+++ b/modules/text_renderer/platform_fonts.c
@@ -49,6 +49,7 @@
 
 /* Win32 GDI */
 #ifdef _WIN32
+# undef HAVE_FONTCONFIG
 # include <windows.h>
 # include <shlobj.h>
 # include <vlc_charset.h>                                     /* FromT */
@@ -60,6 +61,7 @@
 #endif
 
 #include "platform_fonts.h"
+#include "freetype.h"
 
 #ifdef HAVE_FONTCONFIG
 void FontConfig_BuildCache( filter_t *p_filter )
@@ -110,22 +112,37 @@ void FontConfig_BuildCache( filter_t *p_filter )
  * \brief Selects a font matching family, bold, italic provided
  ***/
 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_size, int *i_idx,
+                         const char *language, uni_char_t codepoint )
 {
     FcResult result = FcResultMatch;
     FcPattern *pat, *p_pat;
     FcChar8* val_s;
     FcBool val_b;
     char *ret = NULL;
-    FcConfig* config = NULL;
-    VLC_UNUSED(p_filter);
+
+    filter_sys_t *p_sys = p_filter->p_sys;
+    FcConfig* config = p_sys->p_config;
 
     /* Create a pattern and fills it */
     pat = FcPatternCreate();
     if (!pat) return NULL;
 
     /* */
-    FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
+    if ( language )
+        FcPatternAddString( pat, FC_LANG, ( const FcChar8* ) language );
+    if ( codepoint )
+    {
+        FcCharSet *cs = FcCharSetCreate();
+        FcCharSetAddChar( cs, codepoint );
+        FcPatternAddCharSet( pat, FC_CHARSET, cs );
+        FcCharSetDestroy( cs );
+    }
+    FcValue value;
+    value.type = FcTypeString;
+    value.u.s = (const FcChar8*) family;
+    FcPatternAddWeak( pat, FC_FAMILY, value, true );
+
     FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
     FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
     FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
@@ -259,11 +276,14 @@ static char* GetWindowsFontPath()
 }
 
 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_size, int *i_idx,
+                    const char *language, uni_char_t codepoint )
 {
     VLC_UNUSED( i_size );
     VLC_UNUSED( i_idx );
     VLC_UNUSED( p_filter );
+    VLC_UNUSED( language );
+    VLC_UNUSED( codepoint );
 
     if( !family || strlen( family ) < 1 )
         goto fail;
@@ -328,11 +348,15 @@ fail:
 #ifdef __APPLE__
 #if !TARGET_OS_IPHONE
 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_size, int *i_idx,
+                        const char *language, uni_char_t codepoint )
 {
     VLC_UNUSED( b_bold );
     VLC_UNUSED( b_italic );
     VLC_UNUSED( i_size );
+    VLC_UNUSED( language );
+    VLC_UNUSED( codepoint );
+
     FSRef ref;
     unsigned char path[MAXPATHLEN];
     char * psz_path;
@@ -409,13 +433,16 @@ char* MacLegacy_Select( filter_t *p_filter, const char* psz_fontname,
 #endif
 
 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_size, int *i_idx,
+                    const char *language, uni_char_t codepoint )
 {
     VLC_UNUSED(p_filter);
     VLC_UNUSED(b_bold);
     VLC_UNUSED(b_italic);
     VLC_UNUSED(i_size);
     VLC_UNUSED(i_idx);
+    VLC_UNUSED(language);
+    VLC_UNUSED(codepoint);
 
     char *psz_fontname;
 # if defined( _WIN32 ) && !VLC_WINSTORE_APP
diff --git a/modules/text_renderer/platform_fonts.h b/modules/text_renderer/platform_fonts.h
index cff52b1..de58f71 100644
--- a/modules/text_renderer/platform_fonts.h
+++ b/modules/text_renderer/platform_fonts.h
@@ -33,6 +33,8 @@
 # include "config.h"
 #endif
 
+#include "text_renderer.h"
+
 /* Default fonts */
 #ifdef __APPLE__
 # define SYSTEM_DEFAULT_FONT_FILE "/Library/Fonts/Arial Unicode.ttf"
@@ -80,25 +82,29 @@
 
 #ifdef HAVE_FONTCONFIG
 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_size, int *i_idx,
+                         const char *language, uni_char_t codepoint );
 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_size, int *i_idx );
+                    bool b_bold, bool b_italic, int i_size, int *i_idx,
+                    const char *language, uni_char_t codepoint );
 
 #endif /* _WIN32 */
 
 #ifdef __APPLE__
 #if !TARGET_OS_IPHONE
 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_size, int *i_idx,
+                        const char *language, uni_char_t codepoint );
 #endif
 #endif
 
 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_size, int *i_idx,
+                    const char *language, uni_char_t codepoint );
 
-#define File_Select(a) Dummy_Select(NULL, a, 0, 0, 0, NULL)
+#define File_Select(a) Dummy_Select(NULL, a, 0, 0, 0, NULL, NULL, 0)
diff --git a/modules/text_renderer/text_layout.c b/modules/text_renderer/text_layout.c
index bb6019b..da0b981 100644
--- a/modules/text_renderer/text_layout.c
+++ b/modules/text_renderer/text_layout.c
@@ -55,6 +55,10 @@
 # include <hb-ft.h>
 #endif
 
+#if defined(__APPLE__) || defined(_WIN32)
+# undef HAVE_FONTCONFIG
+#endif
+
 #include "text_renderer.h"
 #include "text_layout.h"
 #include "freetype.h"
@@ -105,6 +109,7 @@ typedef struct paragraph_t
     uni_char_t *p_code_points;            //Unicode code points
     int *pi_glyph_indices;                //Glyph index values within the run's font face
     text_style_t **pp_styles;
+    FT_Face *pp_faces;
     int *pi_run_ids;                      //The run to which each glyph belongs
     glyph_bitmaps_t *p_glyph_bitmaps;
     uint8_t *pi_karaoke_bar;
@@ -223,6 +228,8 @@ static paragraph_t *NewParagraph( filter_t *p_filter,
             malloc( i_size * sizeof( *p_paragraph->pi_glyph_indices ) );
     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 =
             calloc( i_size, sizeof( *p_paragraph->pi_run_ids ) );
     p_paragraph->p_glyph_bitmaps =
@@ -235,9 +242,9 @@ static paragraph_t *NewParagraph( filter_t *p_filter,
     p_paragraph->i_runs_count = 0;
 
     if( !p_paragraph->p_code_points || !p_paragraph->pi_glyph_indices
-     || !p_paragraph->pp_styles || !p_paragraph->pi_run_ids
-     || !p_paragraph->p_glyph_bitmaps || !p_paragraph->pi_karaoke_bar
-     || !p_paragraph->p_runs )
+     || !p_paragraph->pp_styles || !p_paragraph->pp_faces
+     || !p_paragraph->pi_run_ids|| !p_paragraph->p_glyph_bitmaps
+     || !p_paragraph->pi_karaoke_bar || !p_paragraph->p_runs )
         goto error;
 
     if( p_code_points )
@@ -286,6 +293,7 @@ error:
     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->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->p_glyph_bitmaps ) free( p_paragraph->p_glyph_bitmaps );
     if (p_paragraph->pi_karaoke_bar ) free( p_paragraph->pi_karaoke_bar );
@@ -310,6 +318,7 @@ static void FreeParagraph( paragraph_t *p_paragraph )
     free( p_paragraph->p_glyph_bitmaps );
     free( p_paragraph->pi_karaoke_bar );
     free( p_paragraph->pi_run_ids );
+    free( p_paragraph->pp_faces );
     free( p_paragraph->pp_styles );
     free( p_paragraph->p_code_points );
 
@@ -431,6 +440,193 @@ static int AddRun( filter_t *p_filter,
 }
 
 /*
+ * This is based on the list in Qt's qfontconfigdatabase.cpp
+ */
+#if defined( HAVE_HARFBUZZ ) && defined( HAVE_FONTCONFIG )
+static const char * languageFromScript( hb_script_t script )
+{
+    switch( script )
+    {
+    case HB_SCRIPT_LATIN: return "en";
+    case HB_SCRIPT_GREEK: return "el";
+    case HB_SCRIPT_CYRILLIC: return "ru";
+    case HB_SCRIPT_ARMENIAN: return "hy";
+    case HB_SCRIPT_HEBREW: return "he";
+    case HB_SCRIPT_ARABIC: return "ar";
+    case HB_SCRIPT_SYRIAC: return "syr";
+    case HB_SCRIPT_THAANA: return "dv";
+    case HB_SCRIPT_DEVANAGARI: return "hi";
+    case HB_SCRIPT_BENGALI: return "bn";
+    case HB_SCRIPT_GURMUKHI: return "pa";
+    case HB_SCRIPT_GUJARATI: return "gu";
+    case HB_SCRIPT_ORIYA: return "or";
+    case HB_SCRIPT_TAMIL: return "ta";
+    case HB_SCRIPT_TELUGU: return "te";
+    case HB_SCRIPT_KANNADA: return "kn";
+    case HB_SCRIPT_MALAYALAM: return "ml";
+    case HB_SCRIPT_SINHALA: return "si";
+    case HB_SCRIPT_THAI: return "th";
+    case HB_SCRIPT_LAO: return "lo";
+    case HB_SCRIPT_TIBETAN: return "bo";
+    case HB_SCRIPT_MYANMAR: return "my";
+    case HB_SCRIPT_GEORGIAN: return "ka";
+    case HB_SCRIPT_HANGUL: return "ko";
+    case HB_SCRIPT_ETHIOPIC: return "am";
+    case HB_SCRIPT_CHEROKEE: return "chr";
+    case HB_SCRIPT_CANADIAN_ABORIGINAL: return "cr";
+    case HB_SCRIPT_OGHAM: return "sga";
+    case HB_SCRIPT_RUNIC: return "non";
+    case HB_SCRIPT_KHMER: return "km";
+    case HB_SCRIPT_MONGOLIAN: return "mn";
+    case HB_SCRIPT_HIRAGANA: return "ja";
+    case HB_SCRIPT_KATAKANA: return "ja";
+    case HB_SCRIPT_BOPOMOFO: return "zh-TW";
+    //case HB_SCRIPT_HAN:
+    case HB_SCRIPT_YI: return "ii";
+    case HB_SCRIPT_OLD_ITALIC: return "ett";
+    case HB_SCRIPT_GOTHIC: return "got";
+    case HB_SCRIPT_DESERET: return "en";
+    case HB_SCRIPT_TAGALOG: return "fil";
+    case HB_SCRIPT_HANUNOO: return "hnn";
+    case HB_SCRIPT_BUHID: return "bku";
+    case HB_SCRIPT_TAGBANWA: return "tbw";
+    case HB_SCRIPT_COPTIC: return "cop";
+    case HB_SCRIPT_LIMBU: return "lif";
+    case HB_SCRIPT_TAI_LE: return "tdd";
+    case HB_SCRIPT_LINEAR_B: return "grc";
+    case HB_SCRIPT_UGARITIC: return "uga";
+    case HB_SCRIPT_SHAVIAN: return "en";
+    case HB_SCRIPT_OSMANYA: return "so";
+    case HB_SCRIPT_CYPRIOT: return "grc";
+    //case HB_SCRIPT_BRAILLE
+    case HB_SCRIPT_BUGINESE: return "bug";
+    case HB_SCRIPT_NEW_TAI_LUE: return "khb";
+    case HB_SCRIPT_GLAGOLITIC: return "cu";
+    case HB_SCRIPT_TIFINAGH: return "shi";
+    case HB_SCRIPT_SYLOTI_NAGRI: return "syl";
+    case HB_SCRIPT_OLD_PERSIAN: return "peo";
+    case HB_SCRIPT_KHAROSHTHI: return "pra";
+    case HB_SCRIPT_BALINESE: return "ban";
+    case HB_SCRIPT_CUNEIFORM: return "akk";
+    case HB_SCRIPT_PHOENICIAN: return "phn";
+    case HB_SCRIPT_PHAGS_PA: return "lzh";
+    case HB_SCRIPT_NKO: return "man";
+    case HB_SCRIPT_SUNDANESE: return "su";
+    case HB_SCRIPT_LEPCHA: return "lep";
+    case HB_SCRIPT_OL_CHIKI: return "sat";
+    case HB_SCRIPT_VAI: return "vai";
+    case HB_SCRIPT_SAURASHTRA: return "saz";
+    case HB_SCRIPT_KAYAH_LI: return "eky";
+    case HB_SCRIPT_REJANG: return "rej";
+    case HB_SCRIPT_LYCIAN: return "xlc";
+    case HB_SCRIPT_CARIAN: return "xcr";
+    case HB_SCRIPT_LYDIAN: return "xld";
+    case HB_SCRIPT_CHAM: return "cjm";
+    case HB_SCRIPT_TAI_THAM: return "nod";
+    case HB_SCRIPT_TAI_VIET: return "blt";
+    case HB_SCRIPT_AVESTAN: return "ae";
+    case HB_SCRIPT_EGYPTIAN_HIEROGLYPHS: return "egy";
+    case HB_SCRIPT_SAMARITAN: return "smp";
+    case HB_SCRIPT_LISU: return "lis";
+    case HB_SCRIPT_BAMUM: return "bax";
+    case HB_SCRIPT_JAVANESE: return "jv";
+    case HB_SCRIPT_MEETEI_MAYEK: return "mni";
+    case HB_SCRIPT_IMPERIAL_ARAMAIC: return "arc";
+    case HB_SCRIPT_OLD_SOUTH_ARABIAN: return "xsa";
+    case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN: return "xpr";
+    case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI: return "pal";
+    case HB_SCRIPT_OLD_TURKIC: return "otk";
+    case HB_SCRIPT_KAITHI: return "bh";
+    case HB_SCRIPT_BATAK: return "bbc";
+    case HB_SCRIPT_BRAHMI: return "pra";
+    case HB_SCRIPT_MANDAIC: return "myz";
+    case HB_SCRIPT_CHAKMA: return "ccp";
+    case HB_SCRIPT_MEROITIC_CURSIVE: return "xmr";
+    case HB_SCRIPT_MEROITIC_HIEROGLYPHS: return "xmr";
+    case HB_SCRIPT_MIAO: return "hmd";
+    case HB_SCRIPT_SHARADA: return "sa";
+    case HB_SCRIPT_SORA_SOMPENG: return "srb";
+    case HB_SCRIPT_TAKRI: return "doi";
+
+    default: return NULL;
+    }
+}
+#endif
+
+/*
+ * 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_FONTCONFIG
+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;
+    }
+
+    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};
+
+#ifdef HAVE_HARFBUZZ
+    pp_faces[ 0 ] =
+        LoadFace( p_filter, p_style,
+                  languageFromScript( p_paragraph->p_scripts[ i_start_offset ] ),
+                  0 );
+#else
+    pp_faces[ 0 ] = LoadFace( p_filter, p_style, NULL, 0 );
+#endif
+
+    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 ] =
+                     LoadFace( p_filter, p_style, NULL,
+                               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;
+
+        } 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 ] ) )
+                return VLC_EGENERIC;
+
+            i_run_start = i;
+        }
+    }
+
+    return VLC_SUCCESS;
+}
+#endif
+
+/*
  * Segment a paragraph into runs
  */
 static int ItemizeParagraph( filter_t *p_filter, paragraph_t *p_paragraph )
@@ -464,7 +660,11 @@ static int ItemizeParagraph( filter_t *p_filter, paragraph_t *p_paragraph )
                  & STYLE_HALFWIDTH )
             ||!FaceStyleEquals( p_last_style, p_paragraph->pp_styles[ i ] ) )
         {
+#ifdef HAVE_FONTCONFIG
+            int i_ret = AddRunWithFallback( p_filter, p_paragraph, i_last_run_start, i );
+#else
             int i_ret = AddRun( p_filter, p_paragraph, i_last_run_start, i, 0 );
+#endif
             if( i_ret )
                 return i_ret;
 
@@ -520,7 +720,7 @@ static int ShapeParagraphHarfBuzz( filter_t *p_filter,
         FT_Face p_face = 0;
         if( !p_run->p_face )
         {
-            p_face = LoadFace( p_filter, p_style );
+            p_face = LoadFace( p_filter, p_style, NULL, 0 );
             if( !p_face )
             {
                 p_face = p_sys->p_face;
@@ -776,7 +976,7 @@ static int LoadGlyphs( filter_t *p_filter, paragraph_t *p_paragraph,
         FT_Face p_face = 0;
         if( !p_run->p_face )
         {
-            p_face = LoadFace( p_filter, p_style );
+            p_face = LoadFace( p_filter, p_style, NULL, 0 );
             if( !p_face )
             {
                 p_face = p_sys->p_face;
-- 
1.9.1




More information about the vlc-devel mailing list