]> git.sesse.net Git - vlc/blobdiff - modules/misc/freetype.c
use OK instead of Ok
[vlc] / modules / misc / freetype.c
index ddd3359283170f872b2fdb483656780a46e4eb8f..421c3e943dd085c41d912b9cf8b77f0cc0198fe6 100644 (file)
 /*****************************************************************************
  * Preamble
  *****************************************************************************/
-#include <string.h>
 
-#ifdef HAVE_LINUX_LIMITS_H
-#   include <linux/limits.h>
+#ifdef HAVE_CONFIG_H
+# include "config.h"
 #endif
 
-#include <vlc/vlc.h>
+#include <vlc_common.h>
+#include <vlc_plugin.h>
 #include <vlc_vout.h>
 #include <vlc_osd.h>
 #include <vlc_block.h>
 #include <vlc_input.h>
 
 #include <math.h>
-
-#ifdef HAVE_ERRNO_H
-#   include <errno.h>
-#endif
+#include <errno.h>
 
 #include <ft2build.h>
 #include FT_FREETYPE_H
@@ -76,6 +73,8 @@
 #include <fontconfig/fontconfig.h>
 #endif
 
+#include <assert.h>
+
 typedef struct line_desc_t line_desc_t;
 
 /*****************************************************************************
@@ -93,7 +92,9 @@ static int RenderText( filter_t *, subpicture_region_t *,
 static int RenderHtml( filter_t *, subpicture_region_t *,
                        subpicture_region_t * );
 static char *FontConfig_Select( FcConfig *, const char *,
-                                vlc_bool_t, vlc_bool_t, int * );
+                                bool, bool, int * );
+static int BuildDone( vlc_object_t*, const char *, vlc_value_t, vlc_value_t,
+                        void* );
 #endif
 static line_desc_t *NewLine( int );
 
@@ -126,9 +127,9 @@ static void YUVFromRGB( uint32_t i_argb,
     "fonts that will be rendered on the video. If absolute font size is set, "\
     "relative size will be overriden." )
 
-static int   pi_sizes[] = { 20, 18, 16, 12, 6 };
-static const char *ppsz_sizes_text[] = { N_("Smaller"), N_("Small"), N_("Normal"),
-                                         N_("Large"), N_("Larger") };
+static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
+static const char *const ppsz_sizes_text[] = {
+    N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
 #define YUVP_TEXT N_("Use YUVP renderer")
 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
   "This option is only needed if you want to encode into DVB subtitles" )
@@ -140,49 +141,49 @@ static const char *ppsz_sizes_text[] = { N_("Smaller"), N_("Small"), N_("Normal"
 #define EFFECT_OUTLINE     2
 #define EFFECT_OUTLINE_FAT 3
 
-static int   pi_effects[] = { 1, 2, 3 };
-static const char *ppsz_effects_text[] = { N_("Background"),N_("Outline"),
-                                           N_("Fat Outline") };
-static int pi_color_values[] = {
+static int const pi_effects[] = { 1, 2, 3 };
+static const char *const ppsz_effects_text[] = {
+    N_("Background"),N_("Outline"), N_("Fat Outline") };
+static const int pi_color_values[] = {
   0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
   0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
   0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
 
-static const char *ppsz_color_descriptions[] = {
+static const char *const ppsz_color_descriptions[] = {
   N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
   N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
   N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
 
 vlc_module_begin();
-    set_shortname( _("Text renderer"));
-    set_description( _("Freetype2 font renderer") );
+    set_shortname( N_("Text renderer"));
+    set_description( N_("Freetype2 font renderer") );
     set_category( CAT_VIDEO );
     set_subcategory( SUBCAT_VIDEO_SUBPIC );
 
     add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT,
-              VLC_FALSE );
+              false );
 
     add_integer( "freetype-fontsize", 0, NULL, FONTSIZE_TEXT,
-                 FONTSIZE_LONGTEXT, VLC_TRUE );
+                 FONTSIZE_LONGTEXT, true );
 
     /* opacity valid on 0..255, with default 255 = fully opaque */
     add_integer_with_range( "freetype-opacity", 255, 0, 255, NULL,
-        OPACITY_TEXT, OPACITY_LONGTEXT, VLC_TRUE );
+        OPACITY_TEXT, OPACITY_LONGTEXT, true );
 
     /* hook to the color values list, with default 0x00ffffff = white */
     add_integer( "freetype-color", 0x00FFFFFF, NULL, COLOR_TEXT,
-                 COLOR_LONGTEXT, VLC_FALSE );
+                 COLOR_LONGTEXT, false );
         change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
 
     add_integer( "freetype-rel-fontsize", 16, NULL, FONTSIZER_TEXT,
-                 FONTSIZER_LONGTEXT, VLC_FALSE );
+                 FONTSIZER_LONGTEXT, false );
         change_integer_list( pi_sizes, ppsz_sizes_text, 0 );
     add_integer( "freetype-effect", 2, NULL, EFFECT_TEXT,
-                 EFFECT_LONGTEXT, VLC_FALSE );
+                 EFFECT_LONGTEXT, false );
         change_integer_list( pi_effects, ppsz_effects_text, 0 );
 
     add_bool( "freetype-yuvp", 0, NULL, YUVP_TEXT,
-              YUVP_LONGTEXT, VLC_TRUE );
+              YUVP_LONGTEXT, true );
     set_capability( "text renderer", 100 );
     add_shortcut( "text" );
     set_callbacks( Create, Destroy );
@@ -201,7 +202,7 @@ struct line_desc_t
     uint32_t       *p_fg_rgb;
     uint32_t       *p_bg_rgb;
     uint8_t        *p_fg_bg_ratio; /* 0x00=100% FG --> 0x7F=100% BG */
-    vlc_bool_t      b_new_color_mode;
+    bool      b_new_color_mode;
     /** underline information -- only supplied if text should be underlined */
     uint16_t       *pi_underline_offset;
     uint16_t       *pi_underline_thickness;
@@ -230,15 +231,18 @@ typedef struct
     int         i_font_size;
     uint32_t    i_font_color;         /* ARGB */
     uint32_t    i_karaoke_bg_color;   /* ARGB */
-    vlc_bool_t  b_italic;
-    vlc_bool_t  b_bold;
-    vlc_bool_t  b_underline;
+    bool  b_italic;
+    bool  b_bold;
+    bool  b_underline;
     char       *psz_fontname;
 } ft_style_t;
 
 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
 static void FreeLines( line_desc_t * );
 static void FreeLine( line_desc_t * );
+#ifdef HAVE_FONTCONFIG
+static void FontBuilder( vlc_object_t *p_this);
+#endif
 
 /*****************************************************************************
  * filter_sys_t: freetype local data
@@ -250,7 +254,7 @@ struct filter_sys_t
 {
     FT_Library     p_library;   /* handle to library     */
     FT_Face        p_face;      /* handle to face object */
-    vlc_bool_t     i_use_kerning;
+    bool     i_use_kerning;
     uint8_t        i_font_opacity;
     int            i_font_color;
     int            i_font_size;
@@ -260,6 +264,8 @@ struct filter_sys_t
     int            i_display_height;
 #ifdef HAVE_FONTCONFIG
     FcConfig      *p_fontconfig;
+    bool     b_fontconfig_ok;
+    vlc_mutex_t    fontconfig_lock;
 #endif
 
     input_attachment_t **pp_font_attachments;
@@ -273,19 +279,17 @@ struct filter_sys_t
  *****************************************************************************/
 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;
-    int i_error;
-    vlc_value_t val;
+    filter_t      *p_filter = (filter_t *)p_this;
+    filter_sys_t  *p_sys;
+    char          *psz_fontfile = NULL;
+    int            i_error;
+    vlc_value_t    val;
+    vlc_object_t  *p_fontbuilder;
 
     /* Allocate structure */
     p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
     if( !p_sys )
-    {
-        msg_Err( p_filter, "out of memory" );
         return VLC_ENOMEM;
-    }
     p_sys->p_face = 0;
     p_sys->p_library = 0;
     p_sys->i_font_size = 0;
@@ -314,13 +318,10 @@ static int Create( vlc_object_t *p_this )
     psz_fontfile = val.psz_string;
     if( !psz_fontfile || !*psz_fontfile )
     {
-        if( psz_fontfile ) free( psz_fontfile );
+        free( psz_fontfile );
         psz_fontfile = (char *)malloc( PATH_MAX + 1 );
         if( !psz_fontfile )
-        {
-            msg_Err( p_filter, "out of memory" );
             goto error;
-        }
 #ifdef WIN32
         GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
         strcat( psz_fontfile, "\\fonts\\arial.ttf" );
@@ -332,12 +333,6 @@ static int Create( vlc_object_t *p_this )
 #endif
     }
 
-#ifdef HAVE_FONTCONFIG
-    if( FcInit() )
-        p_sys->p_fontconfig = FcConfigGetCurrent();
-    else
-        p_sys->p_fontconfig = NULL;
-#endif
     i_error = FT_Init_FreeType( &p_sys->p_library );
     if( i_error )
     {
@@ -364,13 +359,71 @@ static int Create( vlc_object_t *p_this )
         goto error;
     }
 
+#ifdef HAVE_FONTCONFIG
+    vlc_mutex_init( &p_sys->fontconfig_lock );
+    p_sys->b_fontconfig_ok = false;
+    p_sys->p_fontconfig    = NULL;
+
+    /* Check for an existing Fontbuilder thread */
+    vlc_mutex_t *lock = var_AcquireMutex( "fontbuilder" );
+    p_fontbuilder = vlc_object_find_name( p_filter->p_libvlc,
+                                          "fontlist builder",
+                                          FIND_CHILD );
+
+    if( ! p_fontbuilder )
+    {
+        /* Create the FontBuilder thread as a child of a top-level
+         * object, so that it can survive the destruction of the
+         * freetype object - the fontlist only needs to be built once,
+         * and calling the fontbuild a second time while the first is
+         * still in progress can cause thread instabilities.
+         */
+
+        p_fontbuilder = vlc_object_create( p_filter->p_libvlc,
+                                           sizeof(vlc_object_t) );
+        if( p_fontbuilder )
+        {
+            p_fontbuilder->psz_object_name = strdup( "fontlist builder" );
+            vlc_object_attach( p_fontbuilder, p_filter->p_libvlc );
+
+            var_Create( p_fontbuilder, "build-done", VLC_VAR_BOOL );
+            var_SetBool( p_fontbuilder, "build-done", false );
+            var_AddCallback( p_fontbuilder, "build-done", BuildDone, p_sys );
+
+            if( vlc_thread_create( p_fontbuilder,
+                                   "fontlist builder",
+                                   FontBuilder,
+                                   VLC_THREAD_PRIORITY_LOW,
+                                   false ) )
+            {
+                /* Don't destroy the fontconfig object - we won't be able to do
+                 * italics or bold or change the font face, but we will still
+                 * be able to do underline and change the font size.
+                 */
+                msg_Warn( p_filter, "fontconfig database builder thread can't "
+                        "be launched. Font styling support will be limited." );
+            }
+        }
+        else
+        {
+            vlc_object_release( p_fontbuilder );
+        }
+    }
+    else
+    {
+        vlc_object_release( p_fontbuilder );
+    }
+    vlc_mutex_unlock( lock );
+
+#endif
+
     p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
 
     var_Get( p_filter, "freetype-fontsize", &val );
     p_sys->i_default_font_size = val.i_int;
     if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
 
-    if( psz_fontfile ) free( psz_fontfile );
+    free( psz_fontfile );
 
     p_sys->pp_font_attachments = NULL;
     p_sys->i_font_attachments = 0;
@@ -389,7 +442,7 @@ static int Create( vlc_object_t *p_this )
  error:
     if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
     if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
-    if( psz_fontfile ) free( psz_fontfile );
+    free( psz_fontfile );
     free( p_sys );
     return VLC_EGENERIC;
 }
@@ -417,8 +470,23 @@ static void Destroy( vlc_object_t *p_this )
     }
 
 #ifdef HAVE_FONTCONFIG
-    FcConfigDestroy( p_sys->p_fontconfig );
-    p_sys->p_fontconfig = NULL;
+    vlc_mutex_t *lock = var_AcquireMutex( "fontbuilder" );
+    vlc_object_t *p_fontbuilder = vlc_object_find_name( p_filter->p_libvlc,
+                                    "fontlist builder", FIND_CHILD );
+    if( p_fontbuilder )
+    {
+        var_DelCallback( p_fontbuilder, "build-done", BuildDone, p_sys );
+        vlc_object_release( p_fontbuilder );
+    }
+    vlc_mutex_unlock( lock );
+
+    vlc_mutex_destroy( &p_sys->fontconfig_lock );
+
+    if( p_sys->p_fontconfig )
+    {
+        FcConfigDestroy( p_sys->p_fontconfig );
+        p_sys->p_fontconfig = NULL;
+    }
     /* FcFini asserts calling the subfunction FcCacheFini()
      * even if no other library functions have been made since FcInit(),
      * so don't call it. */
@@ -428,6 +496,48 @@ static void Destroy( vlc_object_t *p_this )
     free( p_sys );
 }
 
+#ifdef HAVE_FONTCONFIG
+
+static void FontBuilder( vlc_object_t *p_this )
+{
+    FcConfig      *p_fontconfig = FcInitLoadConfig();
+    vlc_mutex_t   *lock;
+
+    vlc_thread_ready( p_this );
+
+    if( p_fontconfig )
+    {
+        mtime_t    t1, t2;
+
+        msg_Dbg( p_this, "Building font database..." );
+        t1 = mdate();
+        if(! FcConfigBuildFonts( p_fontconfig ))
+        {
+            /* Don't destroy the fontconfig object - we won't be able to do
+             * italics or bold or change the font face, but we will still
+             * be able to do underline and change the font size.
+             */
+            msg_Err( p_this, "fontconfig database can't be built. "
+                                    "Font styling won't be available" );
+        }
+        t2 = mdate();
+
+        msg_Dbg( p_this, "Finished building font database." );
+        msg_Dbg( p_this, "Took %ld seconds", (long)((t2 - t1)/1000000) );
+
+        lock = var_AcquireMutex( "fontbuilder" );
+
+        var_SetBool( p_this, "build-done", true );
+
+        FcConfigDestroy( p_fontconfig );
+        vlc_mutex_unlock( lock );
+    }
+    vlc_object_detach( p_this );
+    vlc_object_release( p_this );
+}
+
+#endif
+
 /*****************************************************************************
  * Make any TTF/OTF fonts present in the attachments of the media file
  * and store them for later use by the FreeType Engine
@@ -444,9 +554,12 @@ static int LoadFontsFromAttachments( filter_t *p_filter )
     p_input = (input_thread_t *)vlc_object_find( p_filter, VLC_OBJECT_INPUT, FIND_PARENT );
     if( ! p_input )
         return VLC_EGENERIC;
-    
+
     if( VLC_SUCCESS != input_Control( p_input, INPUT_GET_ATTACHMENTS, &pp_attachments, &i_attachments_cnt ))
+    {
+        vlc_object_release(p_input);
         return VLC_EGENERIC;
+    }
 
     p_sys->i_font_attachments = 0;
     p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof( input_attachment_t * ));
@@ -476,7 +589,9 @@ static int LoadFontsFromAttachments( filter_t *p_filter )
             vlc_input_attachment_Delete( p_attach );
         }
     }
-    free( pp_attachments );        
+    free( pp_attachments );
+
+    vlc_object_release(p_input);
 
     return rv;
 }
@@ -628,11 +743,11 @@ static int Render( filter_t *p_filter, subpicture_region_t *p_region,
     return VLC_SUCCESS;
 }
 
-static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, vlc_bool_t b_ul_next_char,
+static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, bool b_ul_next_char,
                                 FT_BitmapGlyph  p_this_glyph, FT_Vector *p_this_glyph_pos,
                                 FT_BitmapGlyph  p_next_glyph, FT_Vector *p_next_glyph_pos,
                                 int i_glyph_tmax, int i_align_offset,
-                                uint8_t i_y, uint8_t i_u, uint8_t i_v, uint8_t i_alpha,
+                                uint8_t i_y, uint8_t i_u, uint8_t i_v,
                                 subpicture_region_t *p_region)
 {
     int y, x, z;
@@ -659,7 +774,7 @@ static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, vlc_boo
         }
         for( x = 0; x < i_extra; x++ )
         {
-            vlc_bool_t b_ok = VLC_TRUE;
+            bool b_ok = true;
 
             /* break the underline around the tails of any glyphs which cross it */
             for( z = x - i_line_thickness;
@@ -673,7 +788,7 @@ static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, vlc_boo
                     if( ( p_next_glyph->bitmap.rows > i_row ) &&
                         p_next_glyph->bitmap.buffer[p_next_glyph->bitmap.width * i_row + z-i_extra] )
                     {
-                        b_ok = VLC_FALSE;
+                        b_ok = false;
                     }
                 }
                 else if ((z > 0 ) && (z < p_this_glyph->bitmap.width))
@@ -683,7 +798,7 @@ static void UnderlineGlyphYUVA( int i_line_thickness, int i_line_offset, vlc_boo
                     if( ( p_this_glyph->bitmap.rows > i_row ) &&
                         p_this_glyph->bitmap.buffer[p_this_glyph->bitmap.width * i_row + z] )
                     {
-                        b_ok = VLC_FALSE;
+                        b_ok = false;
                     }
                 }
             }
@@ -934,7 +1049,7 @@ static int RenderYUVA( filter_t *p_filter, subpicture_region_t *p_region,
                                     p_line->pp_glyphs[i], &(p_line->p_glyph_pos[i]),
                                     p_line->pp_glyphs[i+1], &(p_line->p_glyph_pos[i+1]),
                                     i_glyph_tmax, i_align_offset,
-                                    i_y, i_u, i_v, i_alpha,
+                                    i_y, i_u, i_v,
                                     p_region);
             }
         }
@@ -979,7 +1094,7 @@ static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
 
     if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
         i_scale = val.i_int;
+
     if( p_region_in->p_style )
     {
         i_font_color = __MAX( __MIN( p_region_in->p_style->i_font_color, 0xFFFFFF ), 0 );
@@ -1007,10 +1122,7 @@ static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
     psz_unicode = psz_unicode_orig =
         malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
     if( psz_unicode == NULL )
-    {
-        msg_Err( p_filter, "out of memory" );
         goto error;
-    }
 #if defined(WORDS_BIGENDIAN)
     iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
 #else
@@ -1030,15 +1142,16 @@ static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
         i_out_bytes = i_in_bytes * sizeof( uint32_t );
         i_out_bytes_left = i_out_bytes;
         p_out_buffer = (char *)psz_unicode;
-        i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer, &i_in_bytes,
+        i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer,
+                           &i_in_bytes,
                            &p_out_buffer, &i_out_bytes_left );
 
         vlc_iconv_close( iconv_handle );
 
         if( i_in_bytes )
         {
-            msg_Warn( p_filter, "failed to convert string to unicode (%s), "
-                      "bytes left %d", strerror(errno), (int)i_in_bytes );
+            msg_Warn( p_filter, "failed to convert string to unicode (%m), "
+                      "bytes left %u", (unsigned)i_in_bytes );
             goto error;
         }
         *(uint32_t*)p_out_buffer = 0;
@@ -1048,19 +1161,17 @@ static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
 #if defined(HAVE_FRIBIDI)
     {
         uint32_t *p_fribidi_string;
-        int start_pos, pos = 0;
+        int32_t start_pos, pos = 0;
 
         p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
         if( !p_fribidi_string )
-        {
-            msg_Err( p_filter, "out of memory" );
             goto error;
-        }
 
         /* Do bidi conversion line-by-line */
-        while(pos < i_string_length)
+        while( pos < i_string_length )
         {
-            while(pos < i_string_length) {
+            while( pos < i_string_length ) 
+            {
                 i_char = psz_unicode[pos];
                 if (i_char != '\r' && i_char != '\n')
                     break;
@@ -1068,7 +1179,8 @@ static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
                 ++pos;
             }
             start_pos = pos;
-            while(pos < i_string_length) {
+            while( pos < i_string_length )
+            {
                 i_char = psz_unicode[pos];
                 if (i_char == '\r' || i_char == '\n')
                     break;
@@ -1077,8 +1189,11 @@ static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
             if (pos > start_pos)
             {
                 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
-                fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos, pos - start_pos,
-                                &base_dir, (FriBidiChar*)p_fribidi_string + start_pos, 0, 0, 0);
+                fribidi_log2vis((FriBidiChar*)psz_unicode + start_pos,
+                                pos - start_pos,
+                                &base_dir,
+                                (FriBidiChar*)p_fribidi_string + start_pos,
+                                0, 0, 0);
             }
         }
 
@@ -1091,10 +1206,7 @@ static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
     /* Calculate relative glyph positions and a bounding box for the
      * entire string */
     if( !(p_line = NewLine( strlen( psz_string ))) )
-    {
-        msg_Err( p_filter, "out of memory" );
         goto error;
-    }
     p_lines = p_line;
     i_pen_x = i_pen_y = 0;
     i_previous = i = 0;
@@ -1115,10 +1227,7 @@ static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
         {
             psz_line_start = psz_unicode;
             if( !(p_next = NewLine( strlen( psz_string ))) )
-            {
-                msg_Err( p_filter, "out of memory" );
                 goto error;
-            }
             p_line->p_next = p_next;
             p_line->i_width = line.xMax;
             p_line->i_height = face->size->metrics.height >> 6;
@@ -1181,17 +1290,29 @@ static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
             glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
         if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
         {
+            FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
             p_line->pp_glyphs[ i ] = NULL;
             FreeLine( p_line );
             p_line = NewLine( strlen( psz_string ));
             if( p_prev ) p_prev->p_next = p_line;
             else p_lines = p_line;
 
+            uint32_t *psz_unicode_saved = psz_unicode;
             while( psz_unicode > psz_line_start && *psz_unicode != ' ' )
             {
                 psz_unicode--;
             }
             if( psz_unicode == psz_line_start )
+            {   /* try harder to break that line */
+                psz_unicode = psz_unicode_saved;
+                while( psz_unicode > psz_line_start &&
+                    *psz_unicode != '_'  && *psz_unicode != '/' &&
+                    *psz_unicode != '\\' && *psz_unicode != '.' )
+                {
+                    psz_unicode--;
+                }
+            }
+            if( psz_unicode == psz_line_start )
             {
                 msg_Warn( p_filter, "unbreakable string" );
                 goto error;
@@ -1235,20 +1356,20 @@ static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
     else
         RenderYUVA( p_filter, p_region_out, p_lines, result.x, result.y );
 
-    if( psz_unicode_orig ) free( psz_unicode_orig );
+    free( psz_unicode_orig );
     FreeLines( p_lines );
     return VLC_SUCCESS;
 
  error:
-    if( psz_unicode_orig ) free( psz_unicode_orig );
+    free( psz_unicode_orig );
     FreeLines( p_lines );
     return VLC_EGENERIC;
 }
 
 #ifdef HAVE_FONTCONFIG
 static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size,
-        uint32_t i_font_color, uint32_t i_karaoke_bg_color, vlc_bool_t b_bold,
-        vlc_bool_t b_italic, vlc_bool_t b_uline )
+        uint32_t i_font_color, uint32_t i_karaoke_bg_color, bool b_bold,
+        bool b_italic, bool b_uline )
 {
     ft_style_t  *p_style = malloc( sizeof( ft_style_t ));
 
@@ -1270,18 +1391,17 @@ static void DeleteStyle( ft_style_t *p_style )
 {
     if( p_style )
     {
-        if( p_style->psz_fontname )
-            free( p_style->psz_fontname );
+        free( p_style->psz_fontname );
         free( p_style );
     }
 }
 
-static vlc_bool_t StyleEquals( ft_style_t *s1, ft_style_t *s2 )
+static bool StyleEquals( ft_style_t *s1, ft_style_t *s2 )
 {
     if( !s1 || !s2 )
-        return VLC_FALSE;
+        return false;
     if( s1 == s2 )
-        return VLC_TRUE;
+        return true;
 
     if(( s1->i_font_size  == s2->i_font_size ) &&
        ( s1->i_font_color == s2->i_font_color ) &&
@@ -1290,9 +1410,9 @@ static vlc_bool_t StyleEquals( ft_style_t *s1, ft_style_t *s2 )
        ( s1->b_underline  == s2->b_underline ) &&
        ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
     {
-        return VLC_TRUE;
+        return true;
     }
-    return VLC_FALSE;
+    return false;
 }
 
 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
@@ -1421,8 +1541,8 @@ static void IconvText( filter_t *p_filter, const char *psz_string,
 
             if( i_in_bytes )
             {
-                msg_Warn( p_filter, "failed to convert string to unicode (%s), "
-                          "bytes left %d", strerror(errno), (int)i_in_bytes );
+                msg_Warn( p_filter, "failed to convert string to unicode (%m), "
+                          "bytes left %u", (unsigned)i_in_bytes );
             }
             else
             {
@@ -1439,8 +1559,8 @@ static void IconvText( filter_t *p_filter, const char *psz_string,
 }
 
 static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
-        font_stack_t **p_fonts, vlc_bool_t b_bold, vlc_bool_t b_italic, 
-        vlc_bool_t b_uline )
+        font_stack_t **p_fonts, bool b_bold, bool b_italic,
+        bool b_uline )
 {
     ft_style_t   *p_style = NULL;
 
@@ -1459,7 +1579,7 @@ static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
 }
 
 static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
-                      vlc_bool_t b_uline, int i_karaoke_bgcolor,
+                      bool b_uline, int i_karaoke_bgcolor,
                       line_desc_t *p_line, uint32_t *psz_unicode,
                       int *pi_pen_x, int i_pen_y, int *pi_start,
                       FT_Vector *p_result )
@@ -1467,7 +1587,7 @@ static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
     FT_BBox      line;
     int          i_yMin, i_yMax;
     int          i;
-    vlc_bool_t   b_first_on_line = VLC_TRUE;
+    bool   b_first_on_line = true;
 
     int          i_previous = 0;
     int          i_pen_x_start = *pi_pen_x;
@@ -1493,7 +1613,7 @@ static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
     i_yMax = line.yMax;
 
     if( line.xMax > 0 )
-        b_first_on_line = VLC_FALSE;
+        b_first_on_line = false;
 
     while( *psz_unicode && ( *psz_unicode != '\n' ) )
     {
@@ -1626,9 +1746,8 @@ static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
     {
         *psz_unicode_start = '\0';
     }
-    else
+    else if( psz_unicode > psz_unicode_start )
     {
-        psz_unicode++;
         for( i=0; psz_unicode[ i ]; i++ )
             psz_unicode_start[ i ] = psz_unicode[ i ];
         psz_unicode_start[ i ] = '\0';
@@ -1671,7 +1790,7 @@ static int HandleFontAttributes( xml_reader_t *p_xml_reader,
         {
             if( !strcasecmp( "face", psz_name ) )
             {
-                if( psz_fontname ) free( psz_fontname );
+                free( psz_fontname );
                 psz_fontname = strdup( psz_value );
             }
             else if( !strcasecmp( "size", psz_name ) )
@@ -1841,13 +1960,13 @@ static void SetupKaraoke( xml_reader_t *p_xml_reader, uint32_t *pi_k_runs,
                 }
                 if( *ppi_k_durations )
                     (*ppi_k_durations)[ *pi_k_runs - 1 ] = atoi( psz_value );
-                
+
                 if( *ppi_k_run_lengths )
                     (*ppi_k_run_lengths)[ *pi_k_runs - 1 ] = 0;
             }
         }
-        if( psz_name )  free( psz_name );
-        if( psz_value ) free( psz_value );
+        free( psz_name );
+        free( psz_value );
     }
 }
 
@@ -1861,7 +1980,7 @@ static int ProcessNodes( filter_t *p_filter,
                          uint32_t **ppi_run_lengths,
                          ft_style_t ***ppp_styles,
 
-                         vlc_bool_t b_karaoke,
+                         bool b_karaoke,
                          uint32_t *pi_k_runs,
                          uint32_t **ppi_k_run_lengths,
                          uint32_t **ppi_k_durations )
@@ -1875,13 +1994,13 @@ static int ProcessNodes( filter_t *p_filter,
 
     char *psz_node  = NULL;
 
-    vlc_bool_t b_italic = VLC_FALSE;
-    vlc_bool_t b_bold   = VLC_FALSE;
-    vlc_bool_t b_uline  = VLC_FALSE;
+    bool b_italic = false;
+    bool b_bold   = false;
+    bool b_uline  = false;
 
     if( VLC_SUCCESS == var_Get( p_filter, "scale", &val ))
         i_scale = val.i_int;
+
     if( p_font_style )
     {
         rv = PushFont( &p_fonts,
@@ -1893,11 +2012,11 @@ static int ProcessNodes( filter_t *p_filter,
                    ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
 
         if( p_font_style->i_style_flags & STYLE_BOLD )
-            b_bold = VLC_TRUE;
+            b_bold = true;
         if( p_font_style->i_style_flags & STYLE_ITALIC )
-            b_italic = VLC_TRUE;
+            b_italic = true;
         if( p_font_style->i_style_flags & STYLE_UNDERLINE )
-            b_uline = VLC_TRUE;
+            b_uline = true;
     }
     else
     {
@@ -1924,11 +2043,11 @@ static int ProcessNodes( filter_t *p_filter,
                     if( !strcasecmp( "font", psz_node ) )
                         PopFont( &p_fonts );
                     else if( !strcasecmp( "b", psz_node ) )
-                        b_bold   = VLC_FALSE;
+                        b_bold   = false;
                     else if( !strcasecmp( "i", psz_node ) )
-                        b_italic = VLC_FALSE;
+                        b_italic = false;
                     else if( !strcasecmp( "u", psz_node ) )
-                        b_uline  = VLC_FALSE;
+                        b_uline  = false;
 
                     free( psz_node );
                 }
@@ -1940,11 +2059,11 @@ static int ProcessNodes( filter_t *p_filter,
                     if( !strcasecmp( "font", psz_node ) )
                         rv = HandleFontAttributes( p_xml_reader, &p_fonts, i_scale );
                     else if( !strcasecmp( "b", psz_node ) )
-                        b_bold = VLC_TRUE;
+                        b_bold = true;
                     else if( !strcasecmp( "i", psz_node ) )
-                        b_italic = VLC_TRUE;
+                        b_italic = true;
                     else if( !strcasecmp( "u", psz_node ) )
-                        b_uline = VLC_TRUE;
+                        b_uline = true;
                     else if( !strcasecmp( "br", psz_node ) )
                     {
                         SetupLine( p_filter, "\n", &psz_text,
@@ -2039,7 +2158,7 @@ static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style
         {
             if( p_face )
             {
-                vlc_bool_t match = !strcasecmp( p_face->family_name,
+                bool match = !strcasecmp( p_face->family_name,
                                                 p_style->psz_fontname );
 
                 if( p_face->style_flags & FT_STYLE_FLAG_BOLD )
@@ -2057,7 +2176,7 @@ static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style
                     *pp_face = p_face;
                     return VLC_SUCCESS;
                 }
-                
+
                 FT_Done_Face( p_face );
             }
             i_font_idx++;
@@ -2066,6 +2185,17 @@ static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style
     return VLC_EGENERIC;
 }
 
+static int BuildDone( vlc_object_t *p_this, const char *psz_var,
+                       vlc_value_t oldval, vlc_value_t newval, void *param )
+{
+    (void)p_this;
+    (void)psz_var;
+    (void)oldval;
+    ((filter_sys_t*)param)->b_fontconfig_ok = newval.b_bool;
+    assert( newval.b_bool );
+    return VLC_SUCCESS;
+}
+
 static int ProcessLines( filter_t *p_filter,
                          uint32_t *psz_text,
                          int i_len,
@@ -2074,13 +2204,13 @@ static int ProcessLines( filter_t *p_filter,
                          uint32_t *pi_run_lengths,
                          ft_style_t **pp_styles,
                          line_desc_t **pp_lines,
-                         
+
                          FT_Vector *p_result,
 
-                         vlc_bool_t b_karaoke,
+                         bool b_karaoke,
                          uint32_t i_k_runs,
                          uint32_t *pi_k_run_lengths,
-                         uint32_t *pi_k_durations ) 
+                         uint32_t *pi_k_durations )
 {
     filter_sys_t   *p_sys = p_filter->p_sys;
     ft_style_t    **pp_char_styles;
@@ -2137,13 +2267,12 @@ static int ProcessLines( filter_t *p_filter,
             ! p_new_positions ||
             ! p_levels )
         {
-            msg_Err( p_filter, "out of memory" );
-            if( p_levels )           free( p_levels );
-            if( p_old_positions )    free( p_old_positions );
-            if( p_new_positions )    free( p_new_positions );
-            if( p_fribidi_string )   free( p_fribidi_string );
-            if( pp_char_styles_new ) free( pp_char_styles_new );
-            if( pi_karaoke_bar )     free( pi_karaoke_bar );
+            free( p_levels );
+            free( p_old_positions );
+            free( p_new_positions );
+            free( p_fribidi_string );
+            free( pp_char_styles_new );
+            free( pi_karaoke_bar );
 
             free( pp_char_styles );
             return VLC_ENOMEM;
@@ -2198,7 +2327,7 @@ static int ProcessLines( filter_t *p_filter,
         int64_t i_duration = 0;
         int64_t i_start_pos = 0;
         int64_t i_elapsed  = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
-        
+
         for( k = 0; k< i_k_runs; k++ )
         {
              double fraction = 0.0;
@@ -2246,13 +2375,13 @@ static int ProcessLines( filter_t *p_filter,
                                    ((p_levels ? (p_levels[ j ] % 2) : 0 ) << 7);
                  }
              }
-             
+
              i_last_duration = i_duration;
              i_start_pos += pi_k_run_lengths[ k ];
         }
     }
-    if( p_levels )         free( p_levels );
-    if( p_new_positions )  free( p_new_positions );
+    free( p_levels );
+    free( p_new_positions );
 
     FT_Vector tmp_result;
 
@@ -2282,25 +2411,40 @@ static int ProcessLines( filter_t *p_filter,
             /* Look for a match amongst our attachments first */
             CheckForEmbeddedFont( p_sys, &p_face, p_style );
 
-            if( ! p_face )
+            if( ! p_face && p_sys->b_fontconfig_ok )
             {
-                char *psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
-                                                        p_style->psz_fontname,
-                                                        p_style->b_bold,
-                                                        p_style->b_italic,
-                                                        &i_idx );
+                char *psz_fontfile;
+                vlc_mutex_lock( &p_sys->fontconfig_lock );
+
+                psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
+                                                  p_style->psz_fontname,
+                                                  p_style->b_bold,
+                                                  p_style->b_italic,
+                                                  &i_idx );
+                vlc_mutex_unlock( &p_sys->fontconfig_lock );
+
+                if( psz_fontfile && ! *psz_fontfile )
+                {
+                    msg_Warn( p_filter, "Fontconfig was unable to find a font: \"%s\" %s"
+                        " so using default font", p_style->psz_fontname,
+                        ((p_style->b_bold && p_style->b_italic) ? "(Bold,Italic)" :
+                                               (p_style->b_bold ? "(Bold)" :
+                                             (p_style->b_italic ? "(Italic)" : ""))) );
+                    free( psz_fontfile );
+                    psz_fontfile = NULL;
+                }
+
                 if( psz_fontfile )
                 {
                     if( FT_New_Face( p_sys->p_library,
-                                psz_fontfile ? psz_fontfile : "", i_idx, &p_face ) )
+                                psz_fontfile, i_idx, &p_face ) )
                     {
                         free( psz_fontfile );
                         free( pp_char_styles );
 #if defined(HAVE_FRIBIDI)
                         free( psz_text );
 #endif
-                        if( pi_karaoke_bar )
-                            free( pi_karaoke_bar );
+                        free( pi_karaoke_bar );
                         return VLC_EGENERIC;
                     }
                     free( psz_fontfile );
@@ -2326,8 +2470,7 @@ static int ProcessLines( filter_t *p_filter,
 #if defined(HAVE_FRIBIDI)
                 free( psz_text );
 #endif
-                if( pi_karaoke_bar )
-                    free( pi_karaoke_bar );
+                free( pi_karaoke_bar );
                 return VLC_EGENERIC;
             }
             p_sys->i_use_kerning =
@@ -2338,15 +2481,13 @@ static int ProcessLines( filter_t *p_filter,
                               malloc( (k - i_prev + 1) * sizeof( uint32_t ));
             if( !psz_unicode )
             {
-                msg_Err( p_filter, "out of memory" );
                 if( p_face ) FT_Done_Face( p_face );
                 free( pp_char_styles );
                 free( psz_unicode );
 #if defined(HAVE_FRIBIDI)
                 free( psz_text );
 #endif
-                if( pi_karaoke_bar )
-                    free( pi_karaoke_bar );
+                free( pi_karaoke_bar );
                 return VLC_ENOMEM;
             }
             memcpy( psz_unicode, psz_text + i_prev,
@@ -2358,22 +2499,20 @@ static int ProcessLines( filter_t *p_filter,
                 {
                     if( !(p_line = NewLine( i_len - i_prev)) )
                     {
-                        msg_Err( p_filter, "out of memory" );
                         if( p_face ) FT_Done_Face( p_face );
                         free( pp_char_styles );
                         free( psz_unicode );
 #if defined(HAVE_FRIBIDI)
                         free( psz_text );
 #endif
-                        if( pi_karaoke_bar )
-                            free( pi_karaoke_bar );
+                        free( pi_karaoke_bar );
                         return VLC_ENOMEM;
                     }
                     /* New Color mode only works in YUVA rendering mode --
                      * (RGB mode has palette constraints on it). We therefore
                      * need to populate the legacy colour fields also.
                      */
-                    p_line->b_new_color_mode = VLC_TRUE;
+                    p_line->b_new_color_mode = true;
                     p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
                     p_line->i_red   = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
                     p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >>  8;
@@ -2400,8 +2539,7 @@ static int ProcessLines( filter_t *p_filter,
 #if defined(HAVE_FRIBIDI)
                     free( psz_text );
 #endif
-                    if( pi_karaoke_bar )
-                        free( pi_karaoke_bar );
+                    free( pi_karaoke_bar );
                     return VLC_EGENERIC;
                 }
 
@@ -2412,6 +2550,16 @@ static int ProcessLines( filter_t *p_filter,
 
                     p_prev = p_line;
                     p_line = NULL;
+
+                    if( *psz_unicode == '\n')
+                    {
+                        uint32_t *c_ptr;
+
+                        for( c_ptr = psz_unicode; *c_ptr; c_ptr++ )
+                        {
+                            *c_ptr = *(c_ptr+1);
+                        }
+                    }
                 }
             }
             free( psz_unicode );
@@ -2489,13 +2637,13 @@ static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
     p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
                               (uint8_t *) p_region_in->psz_html,
                               strlen( p_region_in->psz_html ),
-                              VLC_TRUE );
+                              true );
     if( p_sub )
     {
         p_xml = xml_Create( p_filter );
         if( p_xml )
         {
-            vlc_bool_t b_karaoke = VLC_FALSE;
+            bool b_karaoke = false;
 
             p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
             if( p_xml_reader )
@@ -2510,16 +2658,17 @@ static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
                         /* We're going to have to render the text a number
                          * of times to show the progress marker on the text.
                          */
-                        var_SetBool( p_filter, "text-rerender", VLC_TRUE );
-                        b_karaoke = VLC_TRUE;
+                        var_SetBool( p_filter, "text-rerender", true );
+                        b_karaoke = true;
                     }
                     else if( !strcasecmp( "text", psz_node ) )
                     {
-                        b_karaoke = VLC_FALSE;
+                        b_karaoke = false;
                     }
                     else
                     {
                         /* Only text and karaoke tags are supported */
+                        msg_Dbg( p_filter, "Unsupported top-level tag '%s' ignored.", psz_node );
                         xml_ReaderDelete( p_xml, p_xml_reader );
                         p_xml_reader = NULL;
                         rv = VLC_EGENERIC;
@@ -2532,7 +2681,7 @@ static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
             if( p_xml_reader )
             {
                 uint32_t   *psz_text;
-                int         i_len;
+                int         i_len = 0;
                 uint32_t    i_runs = 0;
                 uint32_t    i_k_runs = 0;
                 uint32_t   *pi_run_lengths = NULL;
@@ -2601,7 +2750,7 @@ static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
 }
 
 static char* FontConfig_Select( FcConfig* priv, const char* family,
-                          vlc_bool_t b_bold, vlc_bool_t b_italic, int *i_idx )
+                          bool b_bold, bool b_italic, int *i_idx )
 {
     FcResult result;
     FcPattern *pat, *p_pat;
@@ -2717,19 +2866,18 @@ static line_desc_t *NewLine( int i_count )
         ( p_line->pi_underline_offset == NULL ) ||
         ( p_line->pi_underline_thickness == NULL ) )
     {
-        if( p_line->pi_underline_thickness )
-            free( p_line->pi_underline_thickness );
-        if( p_line->pi_underline_offset ) free( p_line->pi_underline_offset );
-        if( p_line->p_fg_rgb ) free( p_line->p_fg_rgb );
-        if( p_line->p_bg_rgb ) free( p_line->p_bg_rgb );
-        if( p_line->p_fg_bg_ratio ) free( p_line->p_fg_bg_ratio );
-        if( p_line->p_glyph_pos ) free( p_line->p_glyph_pos );
-        if( p_line->pp_glyphs ) free( p_line->pp_glyphs );
+        free( p_line->pi_underline_thickness );
+        free( p_line->pi_underline_offset );
+        free( p_line->p_fg_rgb );
+        free( p_line->p_bg_rgb );
+        free( p_line->p_fg_bg_ratio );
+        free( p_line->p_glyph_pos );
+        free( p_line->pp_glyphs );
         free( p_line );
         return NULL;
     }
     p_line->pp_glyphs[0] = NULL;
-    p_line->b_new_color_mode = VLC_FALSE;
+    p_line->b_new_color_mode = false;
 
     return p_line;
 }