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

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


---
 modules/text_renderer/freetype.c       |  16 +++
 modules/text_renderer/platform_fonts.c | 225 +++++++++++++++++++++++++++++++++
 modules/text_renderer/platform_fonts.h |  16 ++-
 modules/text_renderer/text_layout.c    |   5 +
 4 files changed, 260 insertions(+), 2 deletions(-)

diff --git a/modules/text_renderer/freetype.c b/modules/text_renderer/freetype.c
index 61b3ec5..97d8ee1 100644
--- a/modules/text_renderer/freetype.c
+++ b/modules/text_renderer/freetype.c
@@ -64,6 +64,11 @@
 # define HAVE_GET_FONT_BY_FAMILY_NAME
 #endif
 
+/* Android */
+#ifdef __ANDROID__
+# define HAVE_GET_FONT_BY_FAMILY_NAME
+#endif
+
 #include <assert.h>
 
 #include "platform_fonts.h"
@@ -1269,6 +1274,17 @@ static int Create( vlc_object_t *p_this )
 #endif
 #elif defined( _WIN32 ) && defined( HAVE_GET_FONT_BY_FAMILY_NAME )
     p_sys->pf_select = Win32_Select;
+#elif defined( __ANDROID__ )
+    p_sys->pf_get_family = Android_GetFamily;
+    p_sys->pf_get_fallbacks = Android_GetFallbacks;
+    p_sys->pf_select = Generic_Select;
+
+    if( Android_ParseSystemFonts( p_filter, ANDROID_SYSTEM_FONTS ) == VLC_ENOMEM )
+        goto error;
+    if( Android_ParseSystemFonts( p_filter, ANDROID_FALLBACK_FONTS ) == VLC_ENOMEM )
+        goto error;
+    if( Android_ParseSystemFonts( p_filter, ANDROID_VENDOR_FONTS ) == VLC_ENOMEM )
+        goto error;
 #else
     p_sys->pf_select = Dummy_Select;
 #endif
diff --git a/modules/text_renderer/platform_fonts.c b/modules/text_renderer/platform_fonts.c
index 93ccdf1..df1a7d1 100644
--- a/modules/text_renderer/platform_fonts.c
+++ b/modules/text_renderer/platform_fonts.c
@@ -61,6 +61,11 @@
 # include <fontconfig/fontconfig.h>
 #endif
 
+#ifdef __ANDROID__
+#include <vlc_xml.h>
+#include <vlc_stream.h>
+#endif
+
 #include "platform_fonts.h"
 #include "freetype.h"
 
@@ -906,3 +911,223 @@ char* Dummy_Select( filter_t *p_filter, const char* psz_font,
 
     return psz_fontname;
 }
+
+#ifdef __ANDROID__
+static int Android_ParseFamily( filter_t *p_filter, xml_reader_t *p_xml )
+{
+    filter_sys_t     *p_sys       = p_filter->p_sys;
+    vlc_dictionary_t *p_dict      = &p_sys->family_map;
+    vlc_family_t     *p_family    = NULL;
+    char             *psz_lc      = NULL;
+    int               i_counter   = 0;
+    bool              b_bold      = false;
+    bool              b_italic    = false;
+    const char       *p_node      = NULL;
+    int               i_type      = 0;
+
+    while( ( i_type = xml_ReaderNextNode( p_xml, &p_node ) ) > 0 )
+    {
+        switch( i_type )
+        {
+        case XML_READER_STARTELEM:
+            /*
+             * Multiple names can reference the same family in Android. When
+             * the first name is encountered we set p_family to the vlc_family_t
+             * in the master list matching this name, and if no such family
+             * exists we create a new one and add it to the master list.
+             * If the master list does contain a family with that name it's one
+             * of the font attachments, and the family will end up having embedded
+             * fonts and system fonts.
+             */
+            if( !strcasecmp( "name", p_node ) )
+            {
+                i_type = xml_ReaderNextNode( p_xml, &p_node );
+
+                if( i_type != XML_READER_TEXT || !p_node || !*p_node )
+                {
+                    msg_Warn( p_filter, "Android_ParseFamily: empty name" );
+                    continue;
+                }
+
+                psz_lc = ToLower( p_node );
+                if( unlikely( !psz_lc ) )
+                    return VLC_ENOMEM;
+
+                if( !p_family )
+                {
+                    p_family = vlc_dictionary_value_for_key( p_dict, psz_lc );
+                    if( p_family == kVLCDictionaryNotFound )
+                    {
+                        p_family =
+                            NewFamily( p_filter, psz_lc, &p_sys->p_families, NULL, NULL );
+
+                        if( unlikely( !p_family ) )
+                        {
+                            free( psz_lc );
+                            return VLC_ENOMEM;
+                        }
+
+                    }
+                }
+
+                if( vlc_dictionary_value_for_key( p_dict, psz_lc ) == kVLCDictionaryNotFound )
+                    vlc_dictionary_insert( p_dict, psz_lc, p_family );
+                free( psz_lc );
+            }
+            /*
+             * If p_family has not been set by the time we encounter the first file,
+             * it means this family has no name, and should be used only as a fallback.
+             * We create a new family for it in the master list and later add it to
+             * the "default" fallback list.
+             */
+            else if( !strcasecmp( "file", p_node ) )
+            {
+                i_type = xml_ReaderNextNode( p_xml, &p_node );
+
+                if( i_type != XML_READER_TEXT || !p_node || !*p_node )
+                {
+                    ++i_counter;
+                    continue;
+                }
+
+                if( !p_family )
+                    p_family = NewFamily( p_filter, NULL, &p_sys->p_families,
+                                          &p_sys->family_map, NULL );
+
+                if( unlikely( !p_family ) )
+                    return VLC_ENOMEM;
+
+                switch( i_counter )
+                {
+                case 0:
+                    b_bold = false;
+                    b_italic = false;
+                    break;
+                case 1:
+                    b_bold = true;
+                    b_italic = false;
+                    break;
+                case 2:
+                    b_bold = false;
+                    b_italic = true;
+                    break;
+                case 3:
+                    b_bold = true;
+                    b_italic = true;
+                    break;
+                default:
+                    msg_Warn( p_filter, "Android_ParseFamily: too many files" );
+                    return VLC_EGENERIC;
+                }
+
+                char *psz_fontfile = NULL;
+                if( asprintf( &psz_fontfile, "%s/%s", ANDROID_FONT_PATH, p_node ) < 0
+                 || !NewFont( psz_fontfile, 0, b_bold, b_italic, p_family ) )
+                    return VLC_ENOMEM;
+
+                ++i_counter;
+            }
+            break;
+
+        case XML_READER_ENDELEM:
+            if( !strcasecmp( "family", p_node ) )
+            {
+                if( !p_family )
+                {
+                    msg_Warn( p_filter, "Android_ParseFamily: empty family" );
+                    return VLC_EGENERIC;
+                }
+
+                if( strcasestr( p_family->psz_name, FB_NAME ) )
+                {
+                    vlc_family_t *p_fallback =
+                        NewFamily( p_filter, p_family->psz_name,
+                                   NULL, &p_sys->fallback_map, FB_LIST_DEFAULT );
+
+                    if( unlikely( !p_fallback ) )
+                        return VLC_ENOMEM;
+
+                    p_fallback->p_fonts = p_family->p_fonts;
+                }
+
+                return VLC_SUCCESS;
+            }
+            break;
+        }
+    }
+
+    msg_Warn( p_filter, "Android_ParseFamily: Corrupt font configuration file" );
+    return VLC_EGENERIC;
+}
+
+int Android_ParseSystemFonts( filter_t *p_filter, const char *psz_path )
+{
+    int i_ret = VLC_SUCCESS;
+    stream_t *p_stream = stream_UrlNew( p_filter, psz_path );
+
+    if( !p_stream )
+        return VLC_EGENERIC;
+
+    xml_reader_t *p_xml = xml_ReaderCreate( p_filter, p_stream );
+
+    if( !p_xml )
+    {
+        stream_Delete( p_stream );
+        return VLC_EGENERIC;
+    }
+
+    const char *p_node;
+    int i_type;
+    while( ( i_type = xml_ReaderNextNode( p_xml, &p_node ) ) > 0 )
+    {
+        if( i_type == XML_READER_STARTELEM && !strcasecmp( "family", p_node ) )
+        {
+            if( ( i_ret = Android_ParseFamily( p_filter, p_xml ) ) )
+                break;
+        }
+    }
+
+    xml_ReaderDelete( p_xml );
+    stream_Delete( p_stream );
+    return i_ret;
+}
+
+const vlc_family_t *Android_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 == kVLCDictionaryNotFound )
+        return NULL;
+
+    return p_family;
+}
+
+vlc_family_t *Android_GetFallbacks( filter_t *p_filter, const char *psz_family,
+                                    uni_char_t codepoint )
+{
+    VLC_UNUSED( codepoint );
+
+    vlc_family_t *p_fallbacks = NULL;
+    filter_sys_t *p_sys = p_filter->p_sys;
+    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 );
+
+    free( psz_lc );
+
+    if( p_fallbacks == kVLCDictionaryNotFound )
+        return NULL;
+
+    return p_fallbacks;
+}
+#endif
diff --git a/modules/text_renderer/platform_fonts.h b/modules/text_renderer/platform_fonts.h
index 9748ce4..b8fe25c 100644
--- a/modules/text_renderer/platform_fonts.h
+++ b/modules/text_renderer/platform_fonts.h
@@ -70,9 +70,9 @@ typedef uint32_t uni_char_t;
 # define SYSTEM_DEFAULT_MONOSPACE_FAMILY "Monotype Sans Duospace WT K"
 #elif defined( __ANDROID__ )
 # define SYSTEM_DEFAULT_FONT_FILE "/system/fonts/DroidSans-Bold.ttf"
-# define SYSTEM_DEFAULT_FAMILY "Droid Sans Bold"
+# define SYSTEM_DEFAULT_FAMILY "Droid Sans"
 # define SYSTEM_DEFAULT_MONOSPACE_FONT_FILE "/system/fonts/DroidSansMono.ttf"
-# define SYSTEM_DEFAULT_MONOSPACE_FAMILY "Droid Sans Mono"
+# define SYSTEM_DEFAULT_MONOSPACE_FAMILY "Monospace"
 #else
 # define SYSTEM_DEFAULT_FONT_FILE "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
 # define SYSTEM_DEFAULT_FAMILY "Serif Bold"
@@ -141,6 +141,18 @@ char* MacLegacy_Select( filter_t *p_filter, const char* psz_fontname,
 #endif
 #endif
 
+#ifdef __ANDROID__
+#define ANDROID_SYSTEM_FONTS "file:///system/etc/system_fonts.xml"
+#define ANDROID_FALLBACK_FONTS "file:///system/etc/fallback_fonts.xml"
+#define ANDROID_VENDOR_FONTS "file:///vendor/etc/fallback_fonts.xml"
+#define ANDROID_FONT_PATH "/system/fonts"
+
+int Android_ParseSystemFonts( filter_t *p_filter, const char *psz_path );
+const vlc_family_t *Android_GetFamily( filter_t *p_filter, const char *psz_family );
+vlc_family_t *Android_GetFallbacks( filter_t *p_filter, const char *psz_family,
+                                    uni_char_t codepoint );
+#endif
+
 char* Dummy_Select( filter_t *p_filter, const char* family,
                     bool b_bold, bool b_italic,
                     int *i_idx, uni_char_t codepoint );
diff --git a/modules/text_renderer/text_layout.c b/modules/text_renderer/text_layout.c
index bd6a62e..545568b 100644
--- a/modules/text_renderer/text_layout.c
+++ b/modules/text_renderer/text_layout.c
@@ -63,6 +63,11 @@
 # define HAVE_FONT_FALLBACK
 #endif
 
+/* Android */
+#ifdef __ANDROID__
+# define HAVE_FONT_FALLBACK
+#endif
+
 /*
  * Within a paragraph, run_desc_t represents a run of characters
  * having the same font face, size, and style, Unicode script
-- 
1.9.1



More information about the vlc-devel mailing list