]> git.sesse.net Git - vlc/blob - modules/text_renderer/freetype.c
freetype: do not cache variables for relative font size, background opacity, backgrou...
[vlc] / modules / text_renderer / freetype.c
1 /*****************************************************************************
2  * freetype.c : Put text on the video, using freetype2
3  *****************************************************************************
4  * Copyright (C) 2002 - 2012 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8  *          Gildas Bazin <gbazin@videolan.org>
9  *          Bernie Purcell <bitmap@videolan.org>
10  *          Jean-Baptiste Kempf <jb@videolan.org>
11  *          Felix Paul Kühne <fkuehne@videolan.org>
12  *
13  * This program is free software; you can redistribute it and/or modify it
14  * under the terms of the GNU Lesser General Public License as published by
15  * the Free Software Foundation; either version 2.1 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU Lesser General Public License for more details.
22  *
23  * You should have received a copy of the GNU Lesser General Public License
24  * along with this program; if not, write to the Free Software Foundation, Inc.,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26  *****************************************************************************/
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31
32 #ifdef HAVE_CONFIG_H
33 # include "config.h"
34 #endif
35 #include <math.h>
36
37 #include <vlc_common.h>
38 #include <vlc_plugin.h>
39 #include <vlc_stream.h>                        /* stream_MemoryNew */
40 #include <vlc_input.h>                         /* vlc_input_attachment_* */
41 #include <vlc_xml.h>                           /* xml_reader */
42 #include <vlc_strings.h>                       /* resolve_xml_special_chars */
43 #include <vlc_charset.h>                       /* ToCharset */
44 #include <vlc_dialog.h>                        /* FcCache dialog */
45 #include <vlc_filter.h>                                      /* filter_sys_t */
46 #include <vlc_text_style.h>                                   /* text_style_t*/
47
48 /* Default fonts */
49 #ifdef __APPLE__
50 # define SYSTEM_DEFAULT_FONT_FILE "/Library/Fonts/Arial Unicode.ttf"
51 # define SYSTEM_DEFAULT_FAMILY "Arial Unicode MS"
52 # define SYSTEM_DEFAULT_MONOSPACE_FONT_FILE "/System/Library/Fonts/Monaco.dfont"
53 # define SYSTEM_DEFAULT_MONOSPACE_FAMILY "Monaco"
54 #elif defined( WIN32 )
55 # define SYSTEM_DEFAULT_FONT_FILE "arial.ttf" /* Default path font found at run-time */
56 # define SYSTEM_DEFAULT_FAMILY "Arial"
57 # define SYSTEM_DEFAULT_MONOSPACE_FONT_FILE "cour.ttf"
58 # define SYSTEM_DEFAULT_MONOSPACE_FAMILY "Courier New"
59 #elif defined( __OS2__ )
60 # define SYSTEM_DEFAULT_FONT_FILE "/psfonts/tnrwt_k.ttf"
61 # define SYSTEM_DEFAULT_FAMILY "Times New Roman WT K"
62 # define SYSTEM_DEFAULT_MONOSPACE_FONT_FILE "/psfonts/mtsansdk.ttf"
63 # define SYSTEM_DEFAULT_MONOSPACE_FAMILY "Monotype Sans Duospace WT K"
64 #elif defined( __ANDROID__ )
65 # define SYSTEM_DEFAULT_FONT_FILE "/system/fonts/DroidSans-Bold.ttf"
66 # define SYSTEM_DEFAULT_FAMILY "Droid Sans Bold"
67 # define SYSTEM_DEFAULT_MONOSPACE_FONT_FILE "/system/fonts/DroidSansMono.ttf"
68 # define SYSTEM_DEFAULT_MONOSPACE_FAMILY "Droid Sans Mono"
69 #else
70 # define SYSTEM_DEFAULT_FONT_FILE "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
71 # define SYSTEM_DEFAULT_FAMILY "Serif Bold"
72 # define SYSTEM_DEFAULT_MONOSPACE_FONT_FILE "/usr/share/fonts/truetype/freefont/FreeMono.ttf"
73 # define SYSTEM_DEFAULT_MONOSPACE_FAMILY "Monospace"
74 #endif
75
76 #ifndef DEFAULT_FONT_FILE
77 #define DEFAULT_FONT_FILE SYSTEM_DEFAULT_FONT_FILE
78 #endif
79
80 #ifndef DEFAULT_FAMILY
81 #define DEFAULT_FAMILY SYSTEM_DEFAULT_FAMILY
82 #endif
83
84 #ifndef DEFAULT_MONOSPACE_FONT_FILE
85 #define DEFAULT_MONOSPACE_FONT_FILE SYSTEM_DEFAULT_MONOSPACE_FONT_FILE
86 #endif
87
88 #ifndef DEFAULT_MONOSPACE_FAMILY
89 #define DEFAULT_MONOSPACE_FAMILY SYSTEM_DEFAULT_MONOSPACE_FAMILY
90 #endif
91
92 /* Freetype */
93 #include <freetype/ftsynth.h>
94 #include FT_FREETYPE_H
95 #include FT_GLYPH_H
96 #include FT_STROKER_H
97
98 #define FT_FLOOR(X)     ((X & -64) >> 6)
99 #define FT_CEIL(X)      (((X + 63) & -64) >> 6)
100 #ifndef FT_MulFix
101  #define FT_MulFix(v, s) (((v)*(s))>>16)
102 #endif
103
104 /* apple stuff */
105 #ifdef __APPLE__
106 #include <TargetConditionals.h>
107 #if !TARGET_OS_IPHONE
108 #include <Carbon/Carbon.h>
109 #endif
110 #include <sys/param.h>                         /* for MAXPATHLEN */
111 #undef HAVE_FONTCONFIG
112 #define HAVE_STYLES
113 #endif
114
115 /* RTL */
116 #if defined(HAVE_FRIBIDI)
117 # include <fribidi/fribidi.h>
118 #endif
119
120 /* Win32 GDI */
121 #ifdef WIN32
122 # include <windows.h>
123 # include <shlobj.h>
124 # define HAVE_STYLES
125 # undef HAVE_FONTCONFIG
126 #endif
127
128 /* FontConfig */
129 #ifdef HAVE_FONTCONFIG
130 # include <fontconfig/fontconfig.h>
131 # define HAVE_STYLES
132 #endif
133
134 #include <assert.h>
135
136 #ifdef __OS2__
137 typedef uint16_t uni_char_t;
138 # define FREETYPE_TO_UCS    "UCS-2LE"
139 #else
140 typedef uint32_t uni_char_t;
141 # if defined(WORDS_BIGENDIAN)
142 #  define FREETYPE_TO_UCS   "UCS-4BE"
143 # else
144 #  define FREETYPE_TO_UCS   "UCS-4LE"
145 # endif
146 #endif
147
148 /*****************************************************************************
149  * Module descriptor
150  *****************************************************************************/
151 static int  Create ( vlc_object_t * );
152 static void Destroy( vlc_object_t * );
153
154 #define FONT_TEXT N_("Font")
155 #define MONOSPACE_FONT_TEXT N_("Monospace Font")
156
157 #define FAMILY_LONGTEXT N_("Font family for the font you want to use")
158 #define FONT_LONGTEXT N_("Font file for the font you want to use")
159
160 #define FONTSIZE_TEXT N_("Font size in pixels")
161 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
162     "that will be rendered on the video. " \
163     "If set to something different than 0 this option will override the " \
164     "relative font size." )
165 #define OPACITY_TEXT N_("Text opacity")
166 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
167     "text that will be rendered on the video. 0 = transparent, " \
168     "255 = totally opaque. " )
169 #define COLOR_TEXT N_("Text default color")
170 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
171     "the video. This must be an hexadecimal (like HTML colors). The first two "\
172     "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
173     " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
174 #define FONTSIZER_TEXT N_("Relative font size")
175 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
176     "fonts that will be rendered on the video. If absolute font size is set, "\
177     "relative size will be overridden." )
178 #define BOLD_TEXT N_("Force bold")
179
180 #define BG_OPACITY_TEXT N_("Background opacity")
181 #define BG_COLOR_TEXT N_("Background color")
182
183 #define OUTLINE_OPACITY_TEXT N_("Outline opacity")
184 #define OUTLINE_COLOR_TEXT N_("Outline color")
185 #define OUTLINE_THICKNESS_TEXT N_("Outline thickness")
186
187 #define SHADOW_OPACITY_TEXT N_("Shadow opacity")
188 #define SHADOW_COLOR_TEXT N_("Shadow color")
189 #define SHADOW_ANGLE_TEXT N_("Shadow angle")
190 #define SHADOW_DISTANCE_TEXT N_("Shadow distance")
191
192
193 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
194 static const char *const ppsz_sizes_text[] = {
195     N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
196 #define YUVP_TEXT N_("Use YUVP renderer")
197 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
198   "This option is only needed if you want to encode into DVB subtitles" )
199
200 static const int pi_color_values[] = {
201   0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
202   0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
203   0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
204
205 static const char *const ppsz_color_descriptions[] = {
206   N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
207   N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
208   N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
209
210 static const int pi_outline_thickness[] = {
211     0, 2, 4, 6,
212 };
213 static const char *const ppsz_outline_thickness[] = {
214     N_("None"), N_("Thin"), N_("Normal"), N_("Thick"),
215 };
216
217 vlc_module_begin ()
218     set_shortname( N_("Text renderer"))
219     set_description( N_("Freetype2 font renderer") )
220     set_category( CAT_VIDEO )
221     set_subcategory( SUBCAT_VIDEO_SUBPIC )
222
223 #ifdef HAVE_STYLES
224     add_font( "freetype-font", DEFAULT_FAMILY, FONT_TEXT, FAMILY_LONGTEXT, false )
225     add_font( "freetype-monofont", DEFAULT_MONOSPACE_FAMILY, MONOSPACE_FONT_TEXT, FAMILY_LONGTEXT, false )
226 #else
227     add_loadfile( "freetype-font", DEFAULT_FONT_FILE, FONT_TEXT, FONT_LONGTEXT, false )
228     add_loadfile( "freetype-monofont", DEFAULT_MONOSPACE_FONT_FILE, MONOSPACE_FONT_TEXT, FONT_LONGTEXT, false )
229 #endif
230
231     add_integer( "freetype-fontsize", 0, FONTSIZE_TEXT,
232                  FONTSIZE_LONGTEXT, true )
233         change_integer_range( 0, 4096)
234         change_safe()
235
236     add_integer( "freetype-rel-fontsize", 16, FONTSIZER_TEXT,
237                  FONTSIZER_LONGTEXT, false )
238         change_integer_list( pi_sizes, ppsz_sizes_text )
239         change_safe()
240
241     /* opacity valid on 0..255, with default 255 = fully opaque */
242     add_integer_with_range( "freetype-opacity", 255, 0, 255,
243         OPACITY_TEXT, OPACITY_LONGTEXT, false )
244         change_safe()
245
246     /* hook to the color values list, with default 0x00ffffff = white */
247     add_rgb( "freetype-color", 0x00FFFFFF, COLOR_TEXT,
248                  COLOR_LONGTEXT, false )
249         change_integer_list( pi_color_values, ppsz_color_descriptions )
250         change_safe()
251
252     add_bool( "freetype-bold", false, BOLD_TEXT, NULL, false )
253         change_safe()
254
255     add_integer_with_range( "freetype-background-opacity", 0, 0, 255,
256                             BG_OPACITY_TEXT, NULL, false )
257         change_safe()
258     add_rgb( "freetype-background-color", 0x00000000, BG_COLOR_TEXT,
259              NULL, false )
260         change_integer_list( pi_color_values, ppsz_color_descriptions )
261         change_safe()
262
263     add_integer_with_range( "freetype-outline-opacity", 255, 0, 255,
264                             OUTLINE_OPACITY_TEXT, NULL, false )
265         change_safe()
266     add_rgb( "freetype-outline-color", 0x00000000, OUTLINE_COLOR_TEXT,
267              NULL, false )
268         change_integer_list( pi_color_values, ppsz_color_descriptions )
269         change_safe()
270     add_integer_with_range( "freetype-outline-thickness", 4, 0, 50, OUTLINE_THICKNESS_TEXT,
271              NULL, false )
272         change_integer_list( pi_outline_thickness, ppsz_outline_thickness )
273         change_safe()
274
275     add_integer_with_range( "freetype-shadow-opacity", 128, 0, 255,
276                             SHADOW_OPACITY_TEXT, NULL, false )
277         change_safe()
278     add_rgb( "freetype-shadow-color", 0x00000000, SHADOW_COLOR_TEXT,
279              NULL, false )
280         change_integer_list( pi_color_values, ppsz_color_descriptions )
281         change_safe()
282     add_float_with_range( "freetype-shadow-angle", -45, -360, 360,
283                           SHADOW_ANGLE_TEXT, NULL, false )
284         change_safe()
285     add_float_with_range( "freetype-shadow-distance", 0.06, 0.0, 1.0,
286                           SHADOW_DISTANCE_TEXT, NULL, false )
287         change_safe()
288
289     add_obsolete_integer( "freetype-effect" );
290
291     add_bool( "freetype-yuvp", false, YUVP_TEXT,
292               YUVP_LONGTEXT, true )
293     set_capability( "text renderer", 100 )
294     add_shortcut( "text" )
295     set_callbacks( Create, Destroy )
296 vlc_module_end ()
297
298
299 /*****************************************************************************
300  * Local prototypes
301  *****************************************************************************/
302
303 typedef struct
304 {
305     FT_BitmapGlyph p_glyph;
306     FT_BitmapGlyph p_outline;
307     FT_BitmapGlyph p_shadow;
308     uint32_t       i_color;             /* ARGB color */
309     int            i_line_offset;       /* underline/strikethrough offset */
310     int            i_line_thickness;    /* underline/strikethrough thickness */
311 } line_character_t;
312
313 typedef struct line_desc_t line_desc_t;
314 struct line_desc_t
315 {
316     line_desc_t      *p_next;
317
318     int              i_width;
319     int              i_height;
320     int              i_base_line;
321     int              i_character_count;
322     line_character_t *p_character;
323 };
324
325 typedef struct font_stack_t font_stack_t;
326 struct font_stack_t
327 {
328     char          *psz_name;
329     int            i_size;
330     uint32_t       i_color;            /* ARGB */
331     uint32_t       i_karaoke_bg_color; /* ARGB */
332
333     font_stack_t  *p_next;
334 };
335
336 /*****************************************************************************
337  * filter_sys_t: freetype local data
338  *****************************************************************************
339  * This structure is part of the video output thread descriptor.
340  * It describes the freetype specific properties of an output thread.
341  *****************************************************************************/
342 struct filter_sys_t
343 {
344     FT_Library     p_library;   /* handle to library     */
345     FT_Face        p_face;      /* handle to face object */
346     FT_Stroker     p_stroker;
347     uint8_t        i_font_opacity;
348     int            i_font_size;
349     bool           b_font_bold;
350
351     uint8_t        i_outline_opacity;
352     int            i_outline_color;
353
354     float          f_shadow_vector_x;
355     float          f_shadow_vector_y;
356     uint8_t        i_shadow_opacity;
357     int            i_shadow_color;
358
359     int            i_default_font_size;
360     int            i_display_height;
361     char*          psz_fontfamily;
362     char*          psz_monofontfamily;
363     xml_reader_t  *p_xml;
364 #ifdef WIN32
365     char*          psz_win_fonts_path;
366 #endif
367
368     input_attachment_t **pp_font_attachments;
369     int                  i_font_attachments;
370 };
371
372 /* */
373 static void YUVFromRGB( uint32_t i_argb,
374                     uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
375 {
376     int i_red   = ( i_argb & 0x00ff0000 ) >> 16;
377     int i_green = ( i_argb & 0x0000ff00 ) >>  8;
378     int i_blue  = ( i_argb & 0x000000ff );
379
380     *pi_y = (uint8_t)__MIN(abs( 2104 * i_red  + 4130 * i_green +
381                       802 * i_blue + 4096 + 131072 ) >> 13, 235);
382     *pi_u = (uint8_t)__MIN(abs( -1214 * i_red  + -2384 * i_green +
383                      3598 * i_blue + 4096 + 1048576) >> 13, 240);
384     *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
385                       -585 * i_blue + 4096 + 1048576) >> 13, 240);
386 }
387 static void RGBFromRGB( uint32_t i_argb,
388                         uint8_t *pi_r, uint8_t *pi_g, uint8_t *pi_b )
389 {
390     *pi_r = ( i_argb & 0x00ff0000 ) >> 16;
391     *pi_g = ( i_argb & 0x0000ff00 ) >>  8;
392     *pi_b = ( i_argb & 0x000000ff );
393 }
394 /*****************************************************************************
395  * Make any TTF/OTF fonts present in the attachments of the media file
396  * and store them for later use by the FreeType Engine
397  *****************************************************************************/
398 static int LoadFontsFromAttachments( filter_t *p_filter )
399 {
400     filter_sys_t         *p_sys = p_filter->p_sys;
401     input_attachment_t  **pp_attachments;
402     int                   i_attachments_cnt;
403
404     if( filter_GetInputAttachments( p_filter, &pp_attachments, &i_attachments_cnt ) )
405         return VLC_EGENERIC;
406
407     p_sys->i_font_attachments = 0;
408     p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof(*p_sys->pp_font_attachments));
409     if( !p_sys->pp_font_attachments )
410         return VLC_ENOMEM;
411
412     for( int k = 0; k < i_attachments_cnt; k++ )
413     {
414         input_attachment_t *p_attach = pp_attachments[k];
415
416         if( ( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
417               !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) &&    // OTF
418             p_attach->i_data > 0 && p_attach->p_data )
419         {
420             p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
421         }
422         else
423         {
424             vlc_input_attachment_Delete( p_attach );
425         }
426     }
427     free( pp_attachments );
428
429     return VLC_SUCCESS;
430 }
431
432 static int GetFontSize( filter_t *p_filter )
433 {
434     filter_sys_t *p_sys = p_filter->p_sys;
435     int           i_size = 0;
436
437     if( p_sys->i_default_font_size )
438     {
439         i_size = p_sys->i_default_font_size;
440     }
441     else
442     {
443         int i_ratio = var_InheritInteger( p_filter, "freetype-rel-fontsize" );
444         if( i_ratio > 0 )
445         {
446             i_size = (int)p_filter->fmt_out.video.i_height / i_ratio;
447             p_filter->p_sys->i_display_height = p_filter->fmt_out.video.i_height;
448         }
449     }
450     if( i_size <= 0 )
451     {
452         msg_Warn( p_filter, "invalid fontsize, using 12" );
453         i_size = 12;
454     }
455     return i_size;
456 }
457
458 static int SetFontSize( filter_t *p_filter, int i_size )
459 {
460     filter_sys_t *p_sys = p_filter->p_sys;
461
462     if( !i_size )
463     {
464         i_size = GetFontSize( p_filter );
465
466         msg_Dbg( p_filter, "using fontsize: %i", i_size );
467     }
468
469     p_sys->i_font_size = i_size;
470
471     if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
472     {
473         msg_Err( p_filter, "couldn't set font size to %d", i_size );
474         return VLC_EGENERIC;
475     }
476
477     return VLC_SUCCESS;
478 }
479
480 #ifdef HAVE_STYLES
481 #ifdef HAVE_FONTCONFIG
482 static void FontConfig_BuildCache( filter_t *p_filter )
483 {
484     /* */
485     msg_Dbg( p_filter, "Building font databases.");
486     mtime_t t1, t2;
487     t1 = mdate();
488
489 #ifdef __OS2__
490     FcInit();
491 #endif
492
493 #if defined( WIN32 ) || defined( __APPLE__ )
494     dialog_progress_bar_t *p_dialog = NULL;
495     FcConfig *fcConfig = FcInitLoadConfig();
496
497     p_dialog = dialog_ProgressCreate( p_filter,
498             _("Building font cache"),
499             _("Please wait while your font cache is rebuilt.\n"
500                 "This should take less than a few minutes."), NULL );
501
502 /*    if( p_dialog )
503         dialog_ProgressSet( p_dialog, NULL, 0.5 ); */
504
505     FcConfigBuildFonts( fcConfig );
506 #if defined( __APPLE__ )
507     // By default, scan only the directory /System/Library/Fonts.
508     // So build the set of available fonts under another directories,
509     // and add the set to the current configuration.
510     FcConfigAppFontAddDir( NULL, "~/Library/Fonts" );
511     FcConfigAppFontAddDir( NULL, "/Library/Fonts" );
512     FcConfigAppFontAddDir( NULL, "/Network/Library/Fonts" );
513     //FcConfigAppFontAddDir( NULL, "/System/Library/Fonts" );
514 #endif
515     if( p_dialog )
516     {
517 //        dialog_ProgressSet( p_dialog, NULL, 1.0 );
518         dialog_ProgressDestroy( p_dialog );
519         p_dialog = NULL;
520     }
521 #endif
522     t2 = mdate();
523     msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
524 }
525
526 /***
527  * \brief Selects a font matching family, bold, italic provided
528  ***/
529 static char* FontConfig_Select( FcConfig* config, const char* family,
530                           bool b_bold, bool b_italic, int i_size, int *i_idx )
531 {
532     FcResult result = FcResultMatch;
533     FcPattern *pat, *p_pat;
534     FcChar8* val_s;
535     FcBool val_b;
536     char *ret = NULL;
537
538     /* Create a pattern and fills it */
539     pat = FcPatternCreate();
540     if (!pat) return NULL;
541
542     /* */
543     FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
544     FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
545     FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
546     FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
547     if( i_size != -1 )
548     {
549         char *psz_fontsize;
550         if( asprintf( &psz_fontsize, "%d", i_size ) != -1 )
551         {
552             FcPatternAddString( pat, FC_SIZE, (const FcChar8 *)psz_fontsize );
553             free( psz_fontsize );
554         }
555     }
556
557     /* */
558     FcDefaultSubstitute( pat );
559     if( !FcConfigSubstitute( config, pat, FcMatchPattern ) )
560     {
561         FcPatternDestroy( pat );
562         return NULL;
563     }
564
565     /* Find the best font for the pattern, destroy the pattern */
566     p_pat = FcFontMatch( config, pat, &result );
567     FcPatternDestroy( pat );
568     if( !p_pat || result == FcResultNoMatch ) return NULL;
569
570     /* Check the new pattern */
571     if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
572         || ( val_b != FcTrue ) )
573     {
574         FcPatternDestroy( p_pat );
575         return NULL;
576     }
577     if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
578     {
579         *i_idx = 0;
580     }
581
582     if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
583     {
584         FcPatternDestroy( p_pat );
585         return NULL;
586     }
587
588     /* if( strcasecmp((const char*)val_s, family ) != 0 )
589         msg_Warn( p_filter, "fontconfig: selected font family is not"
590                             "the requested one: '%s' != '%s'\n",
591                             (const char*)val_s, family );   */
592
593     if( FcResultMatch == FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
594         ret = strdup( (const char*)val_s );
595
596     FcPatternDestroy( p_pat );
597     return ret;
598 }
599 #endif
600
601 #ifdef WIN32
602 #define FONT_DIR_NT _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts")
603
604 static int GetFileFontByName( LPCTSTR font_name, char **psz_filename )
605 {
606     HKEY hKey;
607     TCHAR vbuffer[MAX_PATH];
608     TCHAR dbuffer[256];
609
610     if( RegOpenKeyEx(HKEY_LOCAL_MACHINE, FONT_DIR_NT, 0, KEY_READ, &hKey)
611             != ERROR_SUCCESS )
612         return 1;
613
614     char *font_name_temp = FromT( font_name );
615     size_t fontname_len = strlen( font_name_temp );
616
617     for( int index = 0;; index++ )
618     {
619         DWORD vbuflen = MAX_PATH - 1;
620         DWORD dbuflen = 255;
621
622         LONG i_result = RegEnumValue( hKey, index, vbuffer, &vbuflen,
623                                       NULL, NULL, (LPBYTE)dbuffer, &dbuflen);
624         if( i_result != ERROR_SUCCESS )
625         {
626             RegCloseKey( hKey );
627             return i_result;
628         }
629
630         char *psz_value = FromT( vbuffer );
631
632         char *s = strchr( psz_value,'(' );
633         if( s != NULL && s != psz_value ) s[-1] = '\0';
634
635         /* Manage concatenated font names */
636         if( strchr( psz_value, '&') ) {
637             if( strcasestr( psz_value, font_name_temp ) != NULL )
638             {
639                 free( psz_value );
640                 break;
641             }
642         }
643         else {
644             if( strncasecmp( psz_value, font_name_temp, fontname_len ) == 0 )
645             {
646                 free( psz_value );
647                 break;
648             }
649         }
650
651         free( psz_value );
652     }
653
654     *psz_filename = FromT( dbuffer );
655     free( font_name_temp );
656     RegCloseKey( hKey );
657     return 0;
658 }
659
660
661 static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *lpelfe, const NEWTEXTMETRICEX *metric,
662                                      DWORD type, LPARAM lParam)
663 {
664     VLC_UNUSED( metric );
665     if( (type & RASTER_FONTTYPE) ) return 1;
666     // if( lpelfe->elfScript ) FIXME
667
668     return GetFileFontByName( (LPCTSTR)lpelfe->elfFullName, (char **)lParam );
669 }
670
671 static char* Win32_Select( filter_t *p_filter, const char* family,
672                            bool b_bold, bool b_italic, int i_size, int *i_idx )
673 {
674     VLC_UNUSED( i_size );
675
676     if( !family || strlen( family ) < 1 )
677         goto fail;
678
679     /* */
680     LOGFONT lf;
681     lf.lfCharSet = DEFAULT_CHARSET;
682     if( b_italic )
683         lf.lfItalic = true;
684     if( b_bold )
685         lf.lfWeight = FW_BOLD;
686
687     LPTSTR psz_fbuffer = ToT( family );
688     _tcsncpy( (LPTSTR)&lf.lfFaceName, psz_fbuffer, LF_FACESIZE );
689     free( psz_fbuffer );
690
691     /* */
692     char *psz_filename = NULL;
693     HDC hDC = GetDC( NULL );
694     EnumFontFamiliesEx(hDC, &lf, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&psz_filename, 0);
695     ReleaseDC(NULL, hDC);
696
697     /* */
698     if( psz_filename != NULL )
699     {
700         /* FIXME: increase i_idx, when concatenated strings  */
701         i_idx = 0;
702
703         /* Prepend the Windows Font path, when only a filename was provided */
704         if( strchr( psz_filename, DIR_SEP_CHAR ) )
705             return psz_filename;
706         else
707         {
708             char *psz_tmp;
709             if( asprintf( &psz_tmp, "%s\\%s", p_filter->p_sys->psz_win_fonts_path, psz_filename ) == -1 )
710             {
711                 free( psz_filename );
712                 return NULL;
713             }
714             free( psz_filename );
715             return psz_tmp;
716         }
717     }
718     else /* Let's take any font we can */
719 fail:
720     {
721         char *psz_tmp;
722         if( asprintf( &psz_tmp, "%s\\%s", p_filter->p_sys->psz_win_fonts_path, "arial.ttf" ) == -1 )
723             return NULL;
724         else
725             return psz_tmp;
726     }
727 }
728 #endif /* HAVE_WIN32 */
729
730 #ifdef __APPLE__
731 #if !TARGET_OS_IPHONE
732 static char* MacLegacy_Select( filter_t *p_filter, const char* psz_fontname,
733                           bool b_bold, bool b_italic, int i_size, int *i_idx )
734 {
735     VLC_UNUSED( b_bold );
736     VLC_UNUSED( b_italic );
737     VLC_UNUSED( i_size );
738     FSRef ref;
739     unsigned char path[MAXPATHLEN];
740     char * psz_path;
741
742     CFStringRef  cf_fontName;
743     ATSFontRef   ats_font_id;
744
745     *i_idx = 0;
746
747     if( psz_fontname == NULL )
748         return NULL;
749
750     msg_Dbg( p_filter, "looking for %s", psz_fontname );
751     cf_fontName = CFStringCreateWithCString( kCFAllocatorDefault, psz_fontname, kCFStringEncodingUTF8 );
752
753     ats_font_id = ATSFontFindFromName( cf_fontName, kATSOptionFlagsIncludeDisabledMask );
754
755     if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL )
756     {
757         msg_Dbg( p_filter, "ATS couldn't find %s by name, checking family", psz_fontname );
758         ats_font_id = ATSFontFamilyFindFromName( cf_fontName, kATSOptionFlagsDefault );
759
760         if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL )
761         {
762             msg_Dbg( p_filter, "ATS couldn't find either %s nor its family, checking PS name", psz_fontname );
763             ats_font_id = ATSFontFindFromPostScriptName( cf_fontName, kATSOptionFlagsDefault );
764
765             if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL )
766             {
767                 msg_Err( p_filter, "ATS couldn't find %s (no font name, family or PS name)", psz_fontname );
768                 CFRelease( cf_fontName );
769                 return NULL;
770             }
771         }
772     }
773     CFRelease( cf_fontName );
774
775     if ( noErr != ATSFontGetFileReference( ats_font_id, &ref ) )
776     {
777         msg_Err( p_filter, "ATS couldn't get file ref for %s", psz_fontname );
778         return NULL;
779     }
780
781     /* i_idx calculation by searching preceding fontIDs */
782     /* with same FSRef                                       */
783     {
784         ATSFontRef  id2 = ats_font_id - 1;
785         FSRef       ref2;
786
787         while ( id2 > 0 )
788         {
789             if ( noErr != ATSFontGetFileReference( id2, &ref2 ) )
790                 break;
791             if ( noErr != FSCompareFSRefs( &ref, &ref2 ) )
792                 break;
793
794             id2 --;
795         }
796         *i_idx = ats_font_id - ( id2 + 1 );
797     }
798
799     if ( noErr != FSRefMakePath( &ref, path, sizeof(path) ) )
800     {
801         msg_Err( p_filter, "failure when getting path from FSRef" );
802         return NULL;
803     }
804     msg_Dbg( p_filter, "found %s", path );
805
806     psz_path = strdup( (char *)path );
807
808     return psz_path;
809 }
810 #endif
811 #endif
812
813 #endif /* HAVE_STYLES */
814
815
816 /*****************************************************************************
817  * RenderYUVP: place string in picture
818  *****************************************************************************
819  * This function merges the previously rendered freetype glyphs into a picture
820  *****************************************************************************/
821 static int RenderYUVP( filter_t *p_filter, subpicture_region_t *p_region,
822                        line_desc_t *p_line,
823                        FT_BBox *p_bbox )
824 {
825     VLC_UNUSED(p_filter);
826     static const uint8_t pi_gamma[16] =
827         {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
828           0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
829
830     uint8_t *p_dst;
831     video_format_t fmt;
832     int i, x, y, i_pitch;
833     uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
834
835     /* Create a new subpicture region */
836     video_format_Init( &fmt, VLC_CODEC_YUVP );
837     fmt.i_width          =
838     fmt.i_visible_width  = p_bbox->xMax - p_bbox->xMin + 4;
839     fmt.i_height         =
840     fmt.i_visible_height = p_bbox->yMax - p_bbox->yMin + 4;
841
842     assert( !p_region->p_picture );
843     p_region->p_picture = picture_NewFromFormat( &fmt );
844     if( !p_region->p_picture )
845         return VLC_EGENERIC;
846     fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
847     p_region->fmt = fmt;
848
849     /* Calculate text color components
850      * Only use the first color */
851     int i_alpha = (p_line->p_character[0].i_color >> 24) & 0xff;
852     YUVFromRGB( p_line->p_character[0].i_color, &i_y, &i_u, &i_v );
853
854     /* Build palette */
855     fmt.p_palette->i_entries = 16;
856     for( i = 0; i < 8; i++ )
857     {
858         fmt.p_palette->palette[i][0] = 0;
859         fmt.p_palette->palette[i][1] = 0x80;
860         fmt.p_palette->palette[i][2] = 0x80;
861         fmt.p_palette->palette[i][3] = pi_gamma[i];
862         fmt.p_palette->palette[i][3] =
863             (int)fmt.p_palette->palette[i][3] * i_alpha / 255;
864     }
865     for( i = 8; i < fmt.p_palette->i_entries; i++ )
866     {
867         fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
868         fmt.p_palette->palette[i][1] = i_u;
869         fmt.p_palette->palette[i][2] = i_v;
870         fmt.p_palette->palette[i][3] = pi_gamma[i];
871         fmt.p_palette->palette[i][3] =
872             (int)fmt.p_palette->palette[i][3] * i_alpha / 255;
873     }
874
875     p_dst = p_region->p_picture->Y_PIXELS;
876     i_pitch = p_region->p_picture->Y_PITCH;
877
878     /* Initialize the region pixels */
879     memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
880
881     for( ; p_line != NULL; p_line = p_line->p_next )
882     {
883         int i_align_left = 0;
884         if( p_line->i_width < (int)fmt.i_visible_width )
885         {
886             if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
887                 i_align_left = ( fmt.i_visible_width - p_line->i_width );
888             else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
889                 i_align_left = ( fmt.i_visible_width - p_line->i_width ) / 2;
890         }
891         int i_align_top = 0;
892
893         for( i = 0; i < p_line->i_character_count; i++ )
894         {
895             const line_character_t *ch = &p_line->p_character[i];
896             FT_BitmapGlyph p_glyph = ch->p_glyph;
897
898             int i_glyph_y = i_align_top  - p_glyph->top  + p_bbox->yMax + p_line->i_base_line;
899             int i_glyph_x = i_align_left + p_glyph->left - p_bbox->xMin;
900
901             for( y = 0; y < p_glyph->bitmap.rows; y++ )
902             {
903                 for( x = 0; x < p_glyph->bitmap.width; x++ )
904                 {
905                     if( p_glyph->bitmap.buffer[y * p_glyph->bitmap.width + x] )
906                         p_dst[(i_glyph_y + y) * i_pitch + (i_glyph_x + x)] =
907                             (p_glyph->bitmap.buffer[y * p_glyph->bitmap.width + x] + 8)/16;
908                 }
909             }
910         }
911     }
912
913     /* Outlining (find something better than nearest neighbour filtering ?) */
914     if( 1 )
915     {
916         uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
917         uint8_t *p_top = p_dst; /* Use 1st line as a cache */
918         uint8_t left, current;
919
920         for( y = 1; y < (int)fmt.i_height - 1; y++ )
921         {
922             if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
923             p_dst += p_region->p_picture->Y_PITCH;
924             left = 0;
925
926             for( x = 1; x < (int)fmt.i_width - 1; x++ )
927             {
928                 current = p_dst[x];
929                 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
930                              p_dst[x -1 + p_region->p_picture->Y_PITCH ] + p_dst[x + p_region->p_picture->Y_PITCH] + p_dst[x + 1 + p_region->p_picture->Y_PITCH]) / 16;
931                 left = current;
932             }
933         }
934         memset( p_top, 0, fmt.i_width );
935     }
936
937     return VLC_SUCCESS;
938 }
939
940 /*****************************************************************************
941  * RenderYUVA: place string in picture
942  *****************************************************************************
943  * This function merges the previously rendered freetype glyphs into a picture
944  *****************************************************************************/
945 static void FillYUVAPicture( picture_t *p_picture,
946                              int i_a, int i_y, int i_u, int i_v )
947 {
948     memset( p_picture->p[0].p_pixels, i_y,
949             p_picture->p[0].i_pitch * p_picture->p[0].i_lines );
950     memset( p_picture->p[1].p_pixels, i_u,
951             p_picture->p[1].i_pitch * p_picture->p[1].i_lines );
952     memset( p_picture->p[2].p_pixels, i_v,
953             p_picture->p[2].i_pitch * p_picture->p[2].i_lines );
954     memset( p_picture->p[3].p_pixels, i_a,
955             p_picture->p[3].i_pitch * p_picture->p[3].i_lines );
956 }
957
958 static inline void BlendYUVAPixel( picture_t *p_picture,
959                                    int i_picture_x, int i_picture_y,
960                                    int i_a, int i_y, int i_u, int i_v,
961                                    int i_alpha )
962 {
963     int i_an = i_a * i_alpha / 255;
964
965     uint8_t *p_y = &p_picture->p[0].p_pixels[i_picture_y * p_picture->p[0].i_pitch + i_picture_x];
966     uint8_t *p_u = &p_picture->p[1].p_pixels[i_picture_y * p_picture->p[1].i_pitch + i_picture_x];
967     uint8_t *p_v = &p_picture->p[2].p_pixels[i_picture_y * p_picture->p[2].i_pitch + i_picture_x];
968     uint8_t *p_a = &p_picture->p[3].p_pixels[i_picture_y * p_picture->p[3].i_pitch + i_picture_x];
969
970     int i_ao = *p_a;
971     if( i_ao == 0 )
972     {
973         *p_y = i_y;
974         *p_u = i_u;
975         *p_v = i_v;
976         *p_a = i_an;
977     }
978     else
979     {
980         *p_a = 255 - (255 - *p_a) * (255 - i_an) / 255;
981         if( *p_a != 0 )
982         {
983             *p_y = ( *p_y * i_ao * (255 - i_an) / 255 + i_y * i_an ) / *p_a;
984             *p_u = ( *p_u * i_ao * (255 - i_an) / 255 + i_u * i_an ) / *p_a;
985             *p_v = ( *p_v * i_ao * (255 - i_an) / 255 + i_v * i_an ) / *p_a;
986         }
987     }
988 }
989
990 static void FillRGBAPicture( picture_t *p_picture,
991                              int i_a, int i_r, int i_g, int i_b )
992 {
993     for( int dy = 0; dy < p_picture->p[0].i_visible_lines; dy++ )
994     {
995         for( int dx = 0; dx < p_picture->p[0].i_visible_pitch; dx += 4 )
996         {
997             uint8_t *p_rgba = &p_picture->p->p_pixels[dy * p_picture->p->i_pitch + dx];
998             p_rgba[0] = i_r;
999             p_rgba[1] = i_g;
1000             p_rgba[2] = i_b;
1001             p_rgba[3] = i_a;
1002         }
1003     }
1004 }
1005
1006 static inline void BlendRGBAPixel( picture_t *p_picture,
1007                                    int i_picture_x, int i_picture_y,
1008                                    int i_a, int i_r, int i_g, int i_b,
1009                                    int i_alpha )
1010 {
1011     int i_an = i_a * i_alpha / 255;
1012
1013     uint8_t *p_rgba = &p_picture->p->p_pixels[i_picture_y * p_picture->p->i_pitch + 4 * i_picture_x];
1014
1015     int i_ao = p_rgba[3];
1016     if( i_ao == 0 )
1017     {
1018         p_rgba[0] = i_r;
1019         p_rgba[1] = i_g;
1020         p_rgba[2] = i_b;
1021         p_rgba[3] = i_an;
1022     }
1023     else
1024     {
1025         p_rgba[3] = 255 - (255 - p_rgba[3]) * (255 - i_an) / 255;
1026         if( p_rgba[3] != 0 )
1027         {
1028             p_rgba[0] = ( p_rgba[0] * i_ao * (255 - i_an) / 255 + i_r * i_an ) / p_rgba[3];
1029             p_rgba[1] = ( p_rgba[1] * i_ao * (255 - i_an) / 255 + i_g * i_an ) / p_rgba[3];
1030             p_rgba[2] = ( p_rgba[2] * i_ao * (255 - i_an) / 255 + i_b * i_an ) / p_rgba[3];
1031         }
1032     }
1033 }
1034
1035 static inline void BlendAXYZGlyph( picture_t *p_picture,
1036                                    int i_picture_x, int i_picture_y,
1037                                    int i_a, int i_x, int i_y, int i_z,
1038                                    FT_BitmapGlyph p_glyph,
1039                                    void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
1040
1041 {
1042     for( int dy = 0; dy < p_glyph->bitmap.rows; dy++ )
1043     {
1044         for( int dx = 0; dx < p_glyph->bitmap.width; dx++ )
1045             BlendPixel( p_picture, i_picture_x + dx, i_picture_y + dy,
1046                         i_a, i_x, i_y, i_z,
1047                         p_glyph->bitmap.buffer[dy * p_glyph->bitmap.width + dx] );
1048     }
1049 }
1050
1051 static inline void BlendAXYZLine( picture_t *p_picture,
1052                                   int i_picture_x, int i_picture_y,
1053                                   int i_a, int i_x, int i_y, int i_z,
1054                                   const line_character_t *p_current,
1055                                   const line_character_t *p_next,
1056                                   void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
1057 {
1058     int i_line_width = p_current->p_glyph->bitmap.width;
1059     if( p_next )
1060         i_line_width = p_next->p_glyph->left - p_current->p_glyph->left;
1061
1062     for( int dx = 0; dx < i_line_width; dx++ )
1063     {
1064         for( int dy = 0; dy < p_current->i_line_thickness; dy++ )
1065             BlendPixel( p_picture,
1066                         i_picture_x + dx,
1067                         i_picture_y + p_current->i_line_offset + dy,
1068                         i_a, i_x, i_y, i_z, 0xff );
1069     }
1070 }
1071
1072 static inline void RenderBackground( subpicture_region_t *p_region,
1073                                      line_desc_t *p_line_head,
1074                                      FT_BBox *p_bbox,
1075                                      int i_margin,
1076                                      picture_t *p_picture,
1077                                      int i_text_width,
1078                                      void (*ExtractComponents)( uint32_t, uint8_t *, uint8_t *, uint8_t * ),
1079                                      void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
1080 {
1081     for( line_desc_t *p_line = p_line_head; p_line != NULL; p_line = p_line->p_next )
1082     {
1083         int i_align_left = i_margin;
1084         int i_align_top = i_margin;
1085         int line_start = 0;
1086         int line_end = 0;
1087         unsigned line_top = 0;
1088         int line_bottom = 0;
1089         int max_height = 0;
1090
1091         if( p_line->i_width < i_text_width )
1092         {
1093             /* Left offset to take into account alignment */
1094             if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1095                 i_align_left += ( i_text_width - p_line->i_width );
1096             else if( (p_region->i_align & 0x10) == SUBPICTURE_ALIGN_LEAVETEXT)
1097                 i_align_left = i_margin; /* Keep it the way it is */
1098             else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1099                 i_align_left += ( i_text_width - p_line->i_width ) / 2;
1100         }
1101
1102         /* Find the tallest character in the line */
1103         for( int i = 0; i < p_line->i_character_count; i++ ) {
1104             const line_character_t *ch = &p_line->p_character[i];
1105             FT_BitmapGlyph p_glyph = ch->p_outline ? ch->p_outline : ch->p_glyph;
1106             if (p_glyph->top > max_height)
1107                 max_height = p_glyph->top;
1108         }
1109
1110         /* Compute the background for the line (identify leading/trailing space) */
1111         for( int i = 0; i < p_line->i_character_count; i++ ) {
1112             const line_character_t *ch = &p_line->p_character[i];
1113             FT_BitmapGlyph p_glyph = ch->p_outline ? ch->p_outline : ch->p_glyph;
1114             if (p_glyph && p_glyph->bitmap.rows > 0) {
1115                 // Found a non-whitespace character
1116                 line_start = i_align_left + p_glyph->left - p_bbox->xMin;
1117                 break;
1118             }
1119         }
1120
1121         /* Fudge factor to make sure caption background edges are left aligned
1122            despite variable font width */
1123         if (line_start < 12)
1124             line_start = 0;
1125
1126         /* Find right boundary for bounding box for background */
1127         for( int i = p_line->i_character_count; i > 0; i-- ) {
1128             const line_character_t *ch = &p_line->p_character[i - 1];
1129             FT_BitmapGlyph p_glyph = ch->p_shadow ? ch->p_shadow : ch->p_glyph;
1130             if (p_glyph && p_glyph->bitmap.rows > 0) {
1131                 // Found a non-whitespace character
1132                 line_end = i_align_left + p_glyph->left - p_bbox->xMin + p_glyph->bitmap.width;
1133                 break;
1134             }
1135         }
1136
1137         /* Setup color for the background */
1138         uint8_t i_x, i_y, i_z;
1139         ExtractComponents( 0x000000, &i_x, &i_y, &i_z );
1140
1141         /* Compute the upper boundary for the background */
1142         if ((i_align_top + p_line->i_base_line - max_height) < 0)
1143             line_top = i_align_top + p_line->i_base_line;
1144         else
1145             line_top = i_align_top + p_line->i_base_line - max_height;
1146
1147         /* Compute lower boundary for the background */
1148         line_bottom =  __MIN(line_top + p_line->i_height, p_region->fmt.i_visible_height);
1149
1150         /* Render the actual background */
1151         for( int dy = line_top; dy < line_bottom; dy++ )
1152         {
1153             for( int dx = line_start; dx < line_end; dx++ )
1154                 BlendPixel( p_picture, dx, dy, 0xff, i_x, i_y, i_z, 0xff );
1155         }
1156     }
1157 }
1158
1159 static inline int RenderAXYZ( filter_t *p_filter,
1160                               subpicture_region_t *p_region,
1161                               line_desc_t *p_line_head,
1162                               FT_BBox *p_bbox,
1163                               int i_margin,
1164                               vlc_fourcc_t i_chroma,
1165                               void (*ExtractComponents)( uint32_t, uint8_t *, uint8_t *, uint8_t * ),
1166                               void (*FillPicture)( picture_t *p_picture, int, int, int, int ),
1167                               void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
1168 {
1169     filter_sys_t *p_sys = p_filter->p_sys;
1170
1171     /* Create a new subpicture region */
1172     const int i_text_width  = p_bbox->xMax - p_bbox->xMin;
1173     const int i_text_height = p_bbox->yMax - p_bbox->yMin;
1174     video_format_t fmt;
1175     video_format_Init( &fmt, i_chroma );
1176     fmt.i_width          =
1177     fmt.i_visible_width  = i_text_width  + 2 * i_margin;
1178     fmt.i_height         =
1179     fmt.i_visible_height = i_text_height + 2 * i_margin;
1180
1181     picture_t *p_picture = p_region->p_picture = picture_NewFromFormat( &fmt );
1182     if( !p_region->p_picture )
1183         return VLC_EGENERIC;
1184     p_region->fmt = fmt;
1185
1186     /* Initialize the picture background */
1187     uint8_t i_a = var_InheritInteger( p_filter, "freetype-background-opacity" );
1188     i_a = VLC_CLIP( i_a, 0, 255 );
1189     uint8_t i_x, i_y, i_z;
1190
1191     if (p_region->b_renderbg) {
1192         /* Render the background just under the text */
1193         FillPicture( p_picture, 0x00, 0x00, 0x00, 0x00 );
1194         RenderBackground(p_region, p_line_head, p_bbox, i_margin, p_picture, i_text_width,
1195                          ExtractComponents, BlendPixel);
1196     } else {
1197         /* Render background under entire subpicture block */
1198         int i_background_color = var_InheritInteger( p_filter, "freetype-background-color" );
1199         i_background_color = VLC_CLIP( i_background_color, 0, 0xFFFFFF );
1200         ExtractComponents( i_background_color, &i_x, &i_y, &i_z );
1201         FillPicture( p_picture, i_a, i_x, i_y, i_z );
1202     }
1203
1204     /* Render shadow then outline and then normal glyphs */
1205     for( int g = 0; g < 3; g++ )
1206     {
1207         /* Render all lines */
1208         for( line_desc_t *p_line = p_line_head; p_line != NULL; p_line = p_line->p_next )
1209         {
1210             int i_align_left = i_margin;
1211             if( p_line->i_width < i_text_width )
1212             {
1213                 /* Left offset to take into account alignment */
1214                 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1215                     i_align_left += ( i_text_width - p_line->i_width );
1216                 else if( (p_region->i_align & 0x10) == SUBPICTURE_ALIGN_LEAVETEXT)
1217                     i_align_left = i_margin; /* Keep it the way it is */
1218                 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1219                     i_align_left += ( i_text_width - p_line->i_width ) / 2;
1220             }
1221             int i_align_top = i_margin;
1222
1223             /* Render all glyphs and underline/strikethrough */
1224             for( int i = 0; i < p_line->i_character_count; i++ )
1225             {
1226                 const line_character_t *ch = &p_line->p_character[i];
1227                 FT_BitmapGlyph p_glyph = g == 0 ? ch->p_shadow : g == 1 ? ch->p_outline : ch->p_glyph;
1228                 if( !p_glyph )
1229                     continue;
1230
1231                 i_a = (ch->i_color >> 24) & 0xff;
1232                 uint32_t i_color;
1233                 switch (g) {
1234                 case 0:
1235                     i_a     = i_a * p_sys->i_shadow_opacity / 255;
1236                     i_color = p_sys->i_shadow_color;
1237                     break;
1238                 case 1:
1239                     i_a     = i_a * p_sys->i_outline_opacity / 255;
1240                     i_color = p_sys->i_outline_color;
1241                     break;
1242                 default:
1243                     i_color = ch->i_color;
1244                     break;
1245                 }
1246                 ExtractComponents( i_color, &i_x, &i_y, &i_z );
1247
1248                 int i_glyph_y = i_align_top  - p_glyph->top  + p_bbox->yMax + p_line->i_base_line;
1249                 int i_glyph_x = i_align_left + p_glyph->left - p_bbox->xMin;
1250
1251                 BlendAXYZGlyph( p_picture,
1252                                 i_glyph_x, i_glyph_y,
1253                                 i_a, i_x, i_y, i_z,
1254                                 p_glyph,
1255                                 BlendPixel );
1256
1257                 /* underline/strikethrough are only rendered for the normal glyph */
1258                 if( g == 2 && ch->i_line_thickness > 0 )
1259                     BlendAXYZLine( p_picture,
1260                                    i_glyph_x, i_glyph_y + p_glyph->top,
1261                                    i_a, i_x, i_y, i_z,
1262                                    &ch[0],
1263                                    i + 1 < p_line->i_character_count ? &ch[1] : NULL,
1264                                    BlendPixel );
1265             }
1266         }
1267     }
1268
1269     return VLC_SUCCESS;
1270 }
1271
1272 static text_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1273                                   uint32_t i_font_color, uint32_t i_karaoke_bg_color,
1274                                   int i_style_flags )
1275 {
1276     text_style_t *p_style = text_style_New();
1277     if( !p_style )
1278         return NULL;
1279
1280     p_style->psz_fontname = psz_fontname ? strdup( psz_fontname ) : NULL;
1281     p_style->i_font_size  = i_font_size;
1282     p_style->i_font_color = (i_font_color & 0x00ffffff) >>  0;
1283     p_style->i_font_alpha = (i_font_color & 0xff000000) >> 24;
1284     p_style->i_karaoke_background_color = (i_karaoke_bg_color & 0x00ffffff) >>  0;
1285     p_style->i_karaoke_background_alpha = (i_karaoke_bg_color & 0xff000000) >> 24;
1286     p_style->i_style_flags |= i_style_flags;
1287     return p_style;
1288 }
1289
1290 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1291                      uint32_t i_color, uint32_t i_karaoke_bg_color )
1292 {
1293     if( !p_font )
1294         return VLC_EGENERIC;
1295
1296     font_stack_t *p_new = malloc( sizeof(*p_new) );
1297     if( !p_new )
1298         return VLC_ENOMEM;
1299
1300     p_new->p_next = NULL;
1301
1302     if( psz_name )
1303         p_new->psz_name = strdup( psz_name );
1304     else
1305         p_new->psz_name = NULL;
1306
1307     p_new->i_size              = i_size;
1308     p_new->i_color             = i_color;
1309     p_new->i_karaoke_bg_color  = i_karaoke_bg_color;
1310
1311     if( !*p_font )
1312     {
1313         *p_font = p_new;
1314     }
1315     else
1316     {
1317         font_stack_t *p_last;
1318
1319         for( p_last = *p_font;
1320              p_last->p_next;
1321              p_last = p_last->p_next )
1322         ;
1323
1324         p_last->p_next = p_new;
1325     }
1326     return VLC_SUCCESS;
1327 }
1328
1329 static int PopFont( font_stack_t **p_font )
1330 {
1331     font_stack_t *p_last, *p_next_to_last;
1332
1333     if( !p_font || !*p_font )
1334         return VLC_EGENERIC;
1335
1336     p_next_to_last = NULL;
1337     for( p_last = *p_font;
1338          p_last->p_next;
1339          p_last = p_last->p_next )
1340     {
1341         p_next_to_last = p_last;
1342     }
1343
1344     if( p_next_to_last )
1345         p_next_to_last->p_next = NULL;
1346     else
1347         *p_font = NULL;
1348
1349     free( p_last->psz_name );
1350     free( p_last );
1351
1352     return VLC_SUCCESS;
1353 }
1354
1355 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1356                      uint32_t *i_color, uint32_t *i_karaoke_bg_color )
1357 {
1358     font_stack_t *p_last;
1359
1360     if( !p_font || !*p_font )
1361         return VLC_EGENERIC;
1362
1363     for( p_last=*p_font;
1364          p_last->p_next;
1365          p_last=p_last->p_next )
1366     ;
1367
1368     *psz_name            = p_last->psz_name;
1369     *i_size              = p_last->i_size;
1370     *i_color             = p_last->i_color;
1371     *i_karaoke_bg_color  = p_last->i_karaoke_bg_color;
1372
1373     return VLC_SUCCESS;
1374 }
1375
1376 static const struct {
1377     const char *psz_name;
1378     uint32_t   i_value;
1379 } p_html_colors[] = {
1380     /* Official html colors */
1381     { "Aqua",    0x00FFFF },
1382     { "Black",   0x000000 },
1383     { "Blue",    0x0000FF },
1384     { "Fuchsia", 0xFF00FF },
1385     { "Gray",    0x808080 },
1386     { "Green",   0x008000 },
1387     { "Lime",    0x00FF00 },
1388     { "Maroon",  0x800000 },
1389     { "Navy",    0x000080 },
1390     { "Olive",   0x808000 },
1391     { "Purple",  0x800080 },
1392     { "Red",     0xFF0000 },
1393     { "Silver",  0xC0C0C0 },
1394     { "Teal",    0x008080 },
1395     { "White",   0xFFFFFF },
1396     { "Yellow",  0xFFFF00 },
1397
1398     /* Common ones */
1399     { "AliceBlue", 0xF0F8FF },
1400     { "AntiqueWhite", 0xFAEBD7 },
1401     { "Aqua", 0x00FFFF },
1402     { "Aquamarine", 0x7FFFD4 },
1403     { "Azure", 0xF0FFFF },
1404     { "Beige", 0xF5F5DC },
1405     { "Bisque", 0xFFE4C4 },
1406     { "Black", 0x000000 },
1407     { "BlanchedAlmond", 0xFFEBCD },
1408     { "Blue", 0x0000FF },
1409     { "BlueViolet", 0x8A2BE2 },
1410     { "Brown", 0xA52A2A },
1411     { "BurlyWood", 0xDEB887 },
1412     { "CadetBlue", 0x5F9EA0 },
1413     { "Chartreuse", 0x7FFF00 },
1414     { "Chocolate", 0xD2691E },
1415     { "Coral", 0xFF7F50 },
1416     { "CornflowerBlue", 0x6495ED },
1417     { "Cornsilk", 0xFFF8DC },
1418     { "Crimson", 0xDC143C },
1419     { "Cyan", 0x00FFFF },
1420     { "DarkBlue", 0x00008B },
1421     { "DarkCyan", 0x008B8B },
1422     { "DarkGoldenRod", 0xB8860B },
1423     { "DarkGray", 0xA9A9A9 },
1424     { "DarkGrey", 0xA9A9A9 },
1425     { "DarkGreen", 0x006400 },
1426     { "DarkKhaki", 0xBDB76B },
1427     { "DarkMagenta", 0x8B008B },
1428     { "DarkOliveGreen", 0x556B2F },
1429     { "Darkorange", 0xFF8C00 },
1430     { "DarkOrchid", 0x9932CC },
1431     { "DarkRed", 0x8B0000 },
1432     { "DarkSalmon", 0xE9967A },
1433     { "DarkSeaGreen", 0x8FBC8F },
1434     { "DarkSlateBlue", 0x483D8B },
1435     { "DarkSlateGray", 0x2F4F4F },
1436     { "DarkSlateGrey", 0x2F4F4F },
1437     { "DarkTurquoise", 0x00CED1 },
1438     { "DarkViolet", 0x9400D3 },
1439     { "DeepPink", 0xFF1493 },
1440     { "DeepSkyBlue", 0x00BFFF },
1441     { "DimGray", 0x696969 },
1442     { "DimGrey", 0x696969 },
1443     { "DodgerBlue", 0x1E90FF },
1444     { "FireBrick", 0xB22222 },
1445     { "FloralWhite", 0xFFFAF0 },
1446     { "ForestGreen", 0x228B22 },
1447     { "Fuchsia", 0xFF00FF },
1448     { "Gainsboro", 0xDCDCDC },
1449     { "GhostWhite", 0xF8F8FF },
1450     { "Gold", 0xFFD700 },
1451     { "GoldenRod", 0xDAA520 },
1452     { "Gray", 0x808080 },
1453     { "Grey", 0x808080 },
1454     { "Green", 0x008000 },
1455     { "GreenYellow", 0xADFF2F },
1456     { "HoneyDew", 0xF0FFF0 },
1457     { "HotPink", 0xFF69B4 },
1458     { "IndianRed", 0xCD5C5C },
1459     { "Indigo", 0x4B0082 },
1460     { "Ivory", 0xFFFFF0 },
1461     { "Khaki", 0xF0E68C },
1462     { "Lavender", 0xE6E6FA },
1463     { "LavenderBlush", 0xFFF0F5 },
1464     { "LawnGreen", 0x7CFC00 },
1465     { "LemonChiffon", 0xFFFACD },
1466     { "LightBlue", 0xADD8E6 },
1467     { "LightCoral", 0xF08080 },
1468     { "LightCyan", 0xE0FFFF },
1469     { "LightGoldenRodYellow", 0xFAFAD2 },
1470     { "LightGray", 0xD3D3D3 },
1471     { "LightGrey", 0xD3D3D3 },
1472     { "LightGreen", 0x90EE90 },
1473     { "LightPink", 0xFFB6C1 },
1474     { "LightSalmon", 0xFFA07A },
1475     { "LightSeaGreen", 0x20B2AA },
1476     { "LightSkyBlue", 0x87CEFA },
1477     { "LightSlateGray", 0x778899 },
1478     { "LightSlateGrey", 0x778899 },
1479     { "LightSteelBlue", 0xB0C4DE },
1480     { "LightYellow", 0xFFFFE0 },
1481     { "Lime", 0x00FF00 },
1482     { "LimeGreen", 0x32CD32 },
1483     { "Linen", 0xFAF0E6 },
1484     { "Magenta", 0xFF00FF },
1485     { "Maroon", 0x800000 },
1486     { "MediumAquaMarine", 0x66CDAA },
1487     { "MediumBlue", 0x0000CD },
1488     { "MediumOrchid", 0xBA55D3 },
1489     { "MediumPurple", 0x9370D8 },
1490     { "MediumSeaGreen", 0x3CB371 },
1491     { "MediumSlateBlue", 0x7B68EE },
1492     { "MediumSpringGreen", 0x00FA9A },
1493     { "MediumTurquoise", 0x48D1CC },
1494     { "MediumVioletRed", 0xC71585 },
1495     { "MidnightBlue", 0x191970 },
1496     { "MintCream", 0xF5FFFA },
1497     { "MistyRose", 0xFFE4E1 },
1498     { "Moccasin", 0xFFE4B5 },
1499     { "NavajoWhite", 0xFFDEAD },
1500     { "Navy", 0x000080 },
1501     { "OldLace", 0xFDF5E6 },
1502     { "Olive", 0x808000 },
1503     { "OliveDrab", 0x6B8E23 },
1504     { "Orange", 0xFFA500 },
1505     { "OrangeRed", 0xFF4500 },
1506     { "Orchid", 0xDA70D6 },
1507     { "PaleGoldenRod", 0xEEE8AA },
1508     { "PaleGreen", 0x98FB98 },
1509     { "PaleTurquoise", 0xAFEEEE },
1510     { "PaleVioletRed", 0xD87093 },
1511     { "PapayaWhip", 0xFFEFD5 },
1512     { "PeachPuff", 0xFFDAB9 },
1513     { "Peru", 0xCD853F },
1514     { "Pink", 0xFFC0CB },
1515     { "Plum", 0xDDA0DD },
1516     { "PowderBlue", 0xB0E0E6 },
1517     { "Purple", 0x800080 },
1518     { "Red", 0xFF0000 },
1519     { "RosyBrown", 0xBC8F8F },
1520     { "RoyalBlue", 0x4169E1 },
1521     { "SaddleBrown", 0x8B4513 },
1522     { "Salmon", 0xFA8072 },
1523     { "SandyBrown", 0xF4A460 },
1524     { "SeaGreen", 0x2E8B57 },
1525     { "SeaShell", 0xFFF5EE },
1526     { "Sienna", 0xA0522D },
1527     { "Silver", 0xC0C0C0 },
1528     { "SkyBlue", 0x87CEEB },
1529     { "SlateBlue", 0x6A5ACD },
1530     { "SlateGray", 0x708090 },
1531     { "SlateGrey", 0x708090 },
1532     { "Snow", 0xFFFAFA },
1533     { "SpringGreen", 0x00FF7F },
1534     { "SteelBlue", 0x4682B4 },
1535     { "Tan", 0xD2B48C },
1536     { "Teal", 0x008080 },
1537     { "Thistle", 0xD8BFD8 },
1538     { "Tomato", 0xFF6347 },
1539     { "Turquoise", 0x40E0D0 },
1540     { "Violet", 0xEE82EE },
1541     { "Wheat", 0xF5DEB3 },
1542     { "White", 0xFFFFFF },
1543     { "WhiteSmoke", 0xF5F5F5 },
1544     { "Yellow", 0xFFFF00 },
1545     { "YellowGreen", 0x9ACD32 },
1546
1547     { NULL, 0 }
1548 };
1549
1550 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1551                                  font_stack_t **p_fonts )
1552 {
1553     int        rv;
1554     char      *psz_fontname = NULL;
1555     uint32_t   i_font_color = 0xffffff;
1556     int        i_font_alpha = 255;
1557     uint32_t   i_karaoke_bg_color = 0x00ffffff;
1558     int        i_font_size  = 24;
1559
1560     /* Default all attributes to the top font in the stack -- in case not
1561      * all attributes are specified in the sub-font
1562      */
1563     if( VLC_SUCCESS == PeekFont( p_fonts,
1564                                  &psz_fontname,
1565                                  &i_font_size,
1566                                  &i_font_color,
1567                                  &i_karaoke_bg_color ))
1568     {
1569         psz_fontname = strdup( psz_fontname );
1570     }
1571     i_font_alpha = (i_font_color >> 24) & 0xff;
1572     i_font_color &= 0x00ffffff;
1573
1574     const char *name, *value;
1575     while( (name = xml_ReaderNextAttr( p_xml_reader, &value )) != NULL )
1576     {
1577         if( !strcasecmp( "face", name ) )
1578         {
1579             free( psz_fontname );
1580             psz_fontname = strdup( value );
1581         }
1582         else if( !strcasecmp( "size", name ) )
1583         {
1584             if( ( *value == '+' ) || ( *value == '-' ) )
1585             {
1586                 int i_value = atoi( value );
1587
1588                 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
1589                     i_font_size += ( i_value * i_font_size ) / 10;
1590                 else if( i_value < -5 )
1591                     i_font_size = - i_value;
1592                 else if( i_value > 5 )
1593                     i_font_size = i_value;
1594             }
1595             else
1596                 i_font_size = atoi( value );
1597         }
1598         else if( !strcasecmp( "color", name ) )
1599         {
1600             if( value[0] == '#' )
1601             {
1602                 i_font_color = strtol( value + 1, NULL, 16 );
1603                 i_font_color &= 0x00ffffff;
1604             }
1605             else
1606             {
1607                 char *end;
1608                 uint32_t i_value = strtol( value, &end, 16 );
1609                 if( *end == '\0' || *end == ' ' )
1610                     i_font_color = i_value & 0x00ffffff;
1611
1612                 for( int i = 0; p_html_colors[i].psz_name != NULL; i++ )
1613                 {
1614                     if( !strncasecmp( value, p_html_colors[i].psz_name, strlen(p_html_colors[i].psz_name) ) )
1615                     {
1616                         i_font_color = p_html_colors[i].i_value;
1617                         break;
1618                     }
1619                 }
1620             }
1621         }
1622         else if( !strcasecmp( "alpha", name ) && ( value[0] == '#' ) )
1623         {
1624             i_font_alpha = strtol( value + 1, NULL, 16 );
1625             i_font_alpha &= 0xff;
1626         }
1627     }
1628     rv = PushFont( p_fonts,
1629                    psz_fontname,
1630                    i_font_size,
1631                    (i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24),
1632                    i_karaoke_bg_color );
1633
1634     free( psz_fontname );
1635
1636     return rv;
1637 }
1638
1639 static int HandleTT(font_stack_t **p_fonts, const char *p_fontfamily )
1640 {
1641     char      *psz_unused_fontname = NULL;
1642     uint32_t   i_font_color = 0xffffff;
1643     uint32_t   i_karaoke_bg_color = 0x00ffffff;
1644     int        i_font_size  = 24;
1645
1646     /* Default all attributes to the top font in the stack -- in case not
1647      * all attributes are specified in the sub-font
1648      */
1649     PeekFont( p_fonts,
1650              &psz_unused_fontname,
1651              &i_font_size,
1652              &i_font_color,
1653              &i_karaoke_bg_color );
1654
1655     /* Keep all the parent's font attributes, but change to a monospace font */
1656     return PushFont( p_fonts,
1657                    p_fontfamily,
1658                    i_font_size,
1659                    i_font_color,
1660                    i_karaoke_bg_color );
1661 }
1662
1663 /* Turn any multiple-whitespaces into single spaces */
1664 static void HandleWhiteSpace( char *psz_node )
1665 {
1666     char *s = strpbrk( psz_node, "\t\r\n " );
1667     while( s )
1668     {
1669         int i_whitespace = strspn( s, "\t\r\n " );
1670
1671         if( i_whitespace > 1 )
1672             memmove( &s[1],
1673                      &s[i_whitespace],
1674                      strlen( s ) - i_whitespace + 1 );
1675         *s++ = ' ';
1676
1677         s = strpbrk( s, "\t\r\n " );
1678     }
1679 }
1680
1681
1682 static text_style_t *GetStyleFromFontStack( filter_t *p_filter,
1683                                             font_stack_t **p_fonts,
1684                                             int i_style_flags )
1685 {
1686     char       *psz_fontname = NULL;
1687     uint32_t    i_font_color = var_InheritInteger( p_filter, "freetype-color" );
1688     i_font_color = VLC_CLIP( i_font_color, 0, 0xFFFFFF );
1689     i_font_color = i_font_color & 0x00ffffff;
1690     int         i_font_size  = p_filter->p_sys->i_font_size;
1691     uint32_t    i_karaoke_bg_color = i_font_color;
1692
1693     if( PeekFont( p_fonts, &psz_fontname, &i_font_size,
1694                   &i_font_color, &i_karaoke_bg_color ) )
1695         return NULL;
1696
1697     return CreateStyle( psz_fontname, i_font_size, i_font_color,
1698                         i_karaoke_bg_color,
1699                         i_style_flags );
1700 }
1701
1702 static unsigned SetupText( filter_t *p_filter,
1703                            uni_char_t *psz_text_out,
1704                            text_style_t **pp_styles,
1705                            uint32_t *pi_k_dates,
1706
1707                            const char *psz_text_in,
1708                            text_style_t *p_style,
1709                            uint32_t i_k_date )
1710 {
1711     size_t i_string_length;
1712
1713     size_t i_string_bytes;
1714     uni_char_t *psz_tmp = ToCharset( FREETYPE_TO_UCS, psz_text_in, &i_string_bytes );
1715     if( psz_tmp )
1716     {
1717         memcpy( psz_text_out, psz_tmp, i_string_bytes );
1718         i_string_length = i_string_bytes / sizeof( *psz_tmp );
1719         free( psz_tmp );
1720     }
1721     else
1722     {
1723         msg_Warn( p_filter, "failed to convert string to unicode (%m)" );
1724         i_string_length = 0;
1725     }
1726
1727     if( i_string_length > 0 )
1728     {
1729         for( unsigned i = 0; i < i_string_length; i++ )
1730             pp_styles[i] = p_style;
1731     }
1732     else
1733     {
1734         text_style_Delete( p_style );
1735     }
1736     if( i_string_length > 0 && pi_k_dates )
1737     {
1738         for( unsigned i = 0; i < i_string_length; i++ )
1739             pi_k_dates[i] = i_k_date;
1740     }
1741     return i_string_length;
1742 }
1743
1744 static int ProcessNodes( filter_t *p_filter,
1745                          uni_char_t *psz_text,
1746                          text_style_t **pp_styles,
1747                          uint32_t *pi_k_dates,
1748                          int *pi_len,
1749                          xml_reader_t *p_xml_reader,
1750                          text_style_t *p_font_style )
1751 {
1752     int           rv      = VLC_SUCCESS;
1753     filter_sys_t *p_sys   = p_filter->p_sys;
1754     int i_text_length     = 0;
1755     font_stack_t *p_fonts = NULL;
1756     uint32_t i_k_date     = 0;
1757
1758     int i_style_flags = 0;
1759
1760     if( p_font_style )
1761     {
1762         /* If the font is not specified in the style, assume the system font */
1763         if(!p_font_style->psz_fontname)
1764              p_font_style->psz_fontname = strdup(p_sys->psz_fontfamily);
1765
1766         rv = PushFont( &p_fonts,
1767                p_font_style->psz_fontname,
1768                p_font_style->i_font_size > 0 ? p_font_style->i_font_size
1769                                              : p_sys->i_font_size,
1770                (p_font_style->i_font_color & 0xffffff) |
1771                    ((p_font_style->i_font_alpha & 0xff) << 24),
1772                (p_font_style->i_karaoke_background_color & 0xffffff) |
1773                    ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
1774
1775         i_style_flags = p_font_style->i_style_flags & (STYLE_BOLD |
1776                                                        STYLE_ITALIC |
1777                                                        STYLE_UNDERLINE |
1778                                                        STYLE_STRIKEOUT);
1779     }
1780     else
1781     {
1782         uint32_t i_font_size = p_sys->i_font_size;
1783         uint32_t i_font_color = var_InheritInteger( p_filter, "freetype-color" );
1784         i_font_color = VLC_CLIP( i_font_color, 0, 0xFFFFFF );
1785         uint32_t i_font_opacity = p_sys->i_font_opacity;
1786         rv = PushFont( &p_fonts,
1787                        p_sys->psz_fontfamily,
1788                        i_font_size,
1789                        (i_font_color & 0xffffff) |
1790                           ((i_font_opacity & 0xff) << 24),
1791                        0x00ffffff );
1792     }
1793     if( p_sys->b_font_bold )
1794         i_style_flags |= STYLE_BOLD;
1795
1796     if( rv != VLC_SUCCESS )
1797         return rv;
1798
1799     const char *node;
1800     int type;
1801
1802     while ( (type = xml_ReaderNextNode( p_xml_reader, &node )) > 0 )
1803     {
1804         switch ( type )
1805         {
1806             case XML_READER_ENDELEM:
1807                 if( !strcasecmp( "font", node ) )
1808                     PopFont( &p_fonts );
1809                 else if( !strcasecmp( "tt", node ) )
1810                     PopFont( &p_fonts );
1811                 else if( !strcasecmp( "b", node ) )
1812                     i_style_flags &= ~STYLE_BOLD;
1813                else if( !strcasecmp( "i", node ) )
1814                     i_style_flags &= ~STYLE_ITALIC;
1815                 else if( !strcasecmp( "u", node ) )
1816                     i_style_flags &= ~STYLE_UNDERLINE;
1817                 else if( !strcasecmp( "s", node ) )
1818                     i_style_flags &= ~STYLE_STRIKEOUT;
1819                 break;
1820
1821             case XML_READER_STARTELEM:
1822                 if( !strcasecmp( "font", node ) )
1823                     HandleFontAttributes( p_xml_reader, &p_fonts );
1824                 else if( !strcasecmp( "tt", node ) )
1825                     HandleTT( &p_fonts, p_sys->psz_monofontfamily );
1826                 else if( !strcasecmp( "b", node ) )
1827                     i_style_flags |= STYLE_BOLD;
1828                 else if( !strcasecmp( "i", node ) )
1829                     i_style_flags |= STYLE_ITALIC;
1830                 else if( !strcasecmp( "u", node ) )
1831                     i_style_flags |= STYLE_UNDERLINE;
1832                 else if( !strcasecmp( "s", node ) )
1833                     i_style_flags |= STYLE_STRIKEOUT;
1834                 else if( !strcasecmp( "br", node ) )
1835                 {
1836                     i_text_length += SetupText( p_filter,
1837                                                 &psz_text[i_text_length],
1838                                                 &pp_styles[i_text_length],
1839                                                 pi_k_dates ? &pi_k_dates[i_text_length] : NULL,
1840                                                 "\n",
1841                                                 GetStyleFromFontStack( p_filter,
1842                                                                        &p_fonts,
1843                                                                        i_style_flags ),
1844                                                 i_k_date );
1845                 }
1846                 else if( !strcasecmp( "k", node ) )
1847                 {
1848                     /* Karaoke tags */
1849                     const char *name, *value;
1850                     while( (name = xml_ReaderNextAttr( p_xml_reader, &value )) != NULL )
1851                     {
1852                         if( !strcasecmp( "t", name ) && value )
1853                             i_k_date += atoi( value );
1854                     }
1855                 }
1856                 break;
1857
1858             case XML_READER_TEXT:
1859             {
1860                 char *psz_node = strdup( node );
1861                 if( unlikely(!psz_node) )
1862                     break;
1863
1864                 HandleWhiteSpace( psz_node );
1865                 resolve_xml_special_chars( psz_node );
1866
1867                 i_text_length += SetupText( p_filter,
1868                                             &psz_text[i_text_length],
1869                                             &pp_styles[i_text_length],
1870                                             pi_k_dates ? &pi_k_dates[i_text_length] : NULL,
1871                                             psz_node,
1872                                             GetStyleFromFontStack( p_filter,
1873                                                                    &p_fonts,
1874                                                                    i_style_flags ),
1875                                             i_k_date );
1876                 free( psz_node );
1877                 break;
1878             }
1879         }
1880     }
1881
1882     *pi_len = i_text_length;
1883
1884     while( VLC_SUCCESS == PopFont( &p_fonts ) );
1885
1886     return VLC_SUCCESS;
1887 }
1888
1889 static void FreeLine( line_desc_t *p_line )
1890 {
1891     for( int i = 0; i < p_line->i_character_count; i++ )
1892     {
1893         line_character_t *ch = &p_line->p_character[i];
1894         FT_Done_Glyph( (FT_Glyph)ch->p_glyph );
1895         if( ch->p_outline )
1896             FT_Done_Glyph( (FT_Glyph)ch->p_outline );
1897         if( ch->p_shadow )
1898             FT_Done_Glyph( (FT_Glyph)ch->p_shadow );
1899     }
1900
1901     free( p_line->p_character );
1902     free( p_line );
1903 }
1904
1905 static void FreeLines( line_desc_t *p_lines )
1906 {
1907     for( line_desc_t *p_line = p_lines; p_line != NULL; )
1908     {
1909         line_desc_t *p_next = p_line->p_next;
1910         FreeLine( p_line );
1911         p_line = p_next;
1912     }
1913 }
1914
1915 static line_desc_t *NewLine( int i_count )
1916 {
1917     line_desc_t *p_line = malloc( sizeof(*p_line) );
1918
1919     if( !p_line )
1920         return NULL;
1921
1922     p_line->p_next = NULL;
1923     p_line->i_width = 0;
1924     p_line->i_base_line = 0;
1925     p_line->i_character_count = 0;
1926
1927     p_line->p_character = calloc( i_count, sizeof(*p_line->p_character) );
1928     if( !p_line->p_character )
1929     {
1930         free( p_line );
1931         return NULL;
1932     }
1933     return p_line;
1934 }
1935
1936 static FT_Face LoadEmbeddedFace( filter_sys_t *p_sys, const text_style_t *p_style )
1937 {
1938     for( int k = 0; k < p_sys->i_font_attachments; k++ )
1939     {
1940         input_attachment_t *p_attach   = p_sys->pp_font_attachments[k];
1941         int                 i_font_idx = 0;
1942         FT_Face             p_face = NULL;
1943
1944         while( 0 == FT_New_Memory_Face( p_sys->p_library,
1945                                         p_attach->p_data,
1946                                         p_attach->i_data,
1947                                         i_font_idx,
1948                                         &p_face ))
1949         {
1950             if( p_face )
1951             {
1952                 int i_style_received = ((p_face->style_flags & FT_STYLE_FLAG_BOLD)    ? STYLE_BOLD   : 0) |
1953                                        ((p_face->style_flags & FT_STYLE_FLAG_ITALIC ) ? STYLE_ITALIC : 0);
1954                 if( p_face->family_name != NULL
1955                  && !strcasecmp( p_face->family_name, p_style->psz_fontname )
1956                  && (p_style->i_style_flags & (STYLE_BOLD | STYLE_ITALIC))
1957                                                           == i_style_received )
1958                     return p_face;
1959
1960                 FT_Done_Face( p_face );
1961             }
1962             i_font_idx++;
1963         }
1964     }
1965     return NULL;
1966 }
1967
1968 static FT_Face LoadFace( filter_t *p_filter,
1969                          const text_style_t *p_style )
1970 {
1971     filter_sys_t *p_sys = p_filter->p_sys;
1972
1973     /* Look for a match amongst our attachments first */
1974     FT_Face p_face = LoadEmbeddedFace( p_sys, p_style );
1975
1976     /* Load system wide font otheriwse */
1977     if( !p_face )
1978     {
1979         int  i_idx = 0;
1980         char *psz_fontfile = NULL;
1981 #ifdef HAVE_FONTCONFIG
1982         psz_fontfile = FontConfig_Select( NULL,
1983                                           p_style->psz_fontname,
1984                                           (p_style->i_style_flags & STYLE_BOLD) != 0,
1985                                           (p_style->i_style_flags & STYLE_ITALIC) != 0,
1986                                           -1,
1987                                           &i_idx );
1988 #elif defined( __APPLE__ )
1989 #if !TARGET_OS_IPHONE
1990         psz_fontfile = MacLegacy_Select( p_filter, p_style->psz_fontname, false, false, -1, &i_idx );
1991 #endif
1992 #elif defined( WIN32 )
1993         psz_fontfile = Win32_Select( p_filter,
1994                                     p_style->psz_fontname,
1995                                     (p_style->i_style_flags & STYLE_BOLD) != 0,
1996                                     (p_style->i_style_flags & STYLE_ITALIC) != 0,
1997                                     -1,
1998                                     &i_idx );
1999 #else
2000         psz_fontfile = NULL;
2001 #endif
2002         if( !psz_fontfile )
2003             return NULL;
2004
2005         if( *psz_fontfile == '\0' )
2006         {
2007             msg_Warn( p_filter,
2008                       "We were not able to find a matching font: \"%s\" (%s %s),"
2009                       " so using default font",
2010                       p_style->psz_fontname,
2011                       (p_style->i_style_flags & STYLE_BOLD)   ? "Bold" : "",
2012                       (p_style->i_style_flags & STYLE_ITALIC) ? "Italic" : "" );
2013             p_face = NULL;
2014         }
2015         else
2016         {
2017             if( FT_New_Face( p_sys->p_library, psz_fontfile, i_idx, &p_face ) )
2018                 p_face = NULL;
2019         }
2020         free( psz_fontfile );
2021     }
2022     if( !p_face )
2023         return NULL;
2024
2025     if( FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2026     {
2027         /* We've loaded a font face which is unhelpful for actually
2028          * rendering text - fallback to the default one.
2029          */
2030         FT_Done_Face( p_face );
2031         return NULL;
2032     }
2033     return p_face;
2034 }
2035
2036 static bool FaceStyleEquals( const text_style_t *p_style1,
2037                              const text_style_t *p_style2 )
2038 {
2039     if( !p_style1 || !p_style2 )
2040         return false;
2041     if( p_style1 == p_style2 )
2042         return true;
2043
2044     const int i_style_mask = STYLE_BOLD | STYLE_ITALIC;
2045     return (p_style1->i_style_flags & i_style_mask) == (p_style2->i_style_flags & i_style_mask) &&
2046            !strcmp( p_style1->psz_fontname, p_style2->psz_fontname );
2047 }
2048
2049 static int GetGlyph( filter_t *p_filter,
2050                      FT_Glyph *pp_glyph,   FT_BBox *p_glyph_bbox,
2051                      FT_Glyph *pp_outline, FT_BBox *p_outline_bbox,
2052                      FT_Glyph *pp_shadow,  FT_BBox *p_shadow_bbox,
2053
2054                      FT_Face  p_face,
2055                      int i_glyph_index,
2056                      int i_style_flags,
2057                      FT_Vector *p_pen,
2058                      FT_Vector *p_pen_shadow )
2059 {
2060     if( FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_DEFAULT ) &&
2061         FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT ) )
2062     {
2063         msg_Err( p_filter, "unable to render text FT_Load_Glyph failed" );
2064         return VLC_EGENERIC;
2065     }
2066
2067     /* Do synthetic styling now that Freetype supports it;
2068      * ie. if the font we have loaded is NOT already in the
2069      * style that the tags want, then switch it on; if they
2070      * are then don't. */
2071     if ((i_style_flags & STYLE_BOLD) && !(p_face->style_flags & FT_STYLE_FLAG_BOLD))
2072         FT_GlyphSlot_Embolden( p_face->glyph );
2073     if ((i_style_flags & STYLE_ITALIC) && !(p_face->style_flags & FT_STYLE_FLAG_ITALIC))
2074         FT_GlyphSlot_Oblique( p_face->glyph );
2075
2076     FT_Glyph glyph;
2077     if( FT_Get_Glyph( p_face->glyph, &glyph ) )
2078     {
2079         msg_Err( p_filter, "unable to render text FT_Get_Glyph failed" );
2080         return VLC_EGENERIC;
2081     }
2082
2083     FT_Glyph outline = NULL;
2084     if( p_filter->p_sys->p_stroker )
2085     {
2086         outline = glyph;
2087         if( FT_Glyph_StrokeBorder( &outline, p_filter->p_sys->p_stroker, 0, 0 ) )
2088             outline = NULL;
2089     }
2090
2091     FT_Glyph shadow = NULL;
2092     if( p_filter->p_sys->i_shadow_opacity > 0 )
2093     {
2094         shadow = outline ? outline : glyph;
2095         if( FT_Glyph_To_Bitmap( &shadow, FT_RENDER_MODE_NORMAL, p_pen_shadow, 0  ) )
2096         {
2097             shadow = NULL;
2098         }
2099         else
2100         {
2101             FT_Glyph_Get_CBox( shadow, ft_glyph_bbox_pixels, p_shadow_bbox );
2102         }
2103     }
2104     *pp_shadow = shadow;
2105
2106     if( FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, p_pen, 1) )
2107     {
2108         FT_Done_Glyph( glyph );
2109         if( outline )
2110             FT_Done_Glyph( outline );
2111         if( shadow )
2112             FT_Done_Glyph( shadow );
2113         return VLC_EGENERIC;
2114     }
2115     FT_Glyph_Get_CBox( glyph, ft_glyph_bbox_pixels, p_glyph_bbox );
2116     *pp_glyph = glyph;
2117
2118     if( outline )
2119     {
2120         FT_Glyph_To_Bitmap( &outline, FT_RENDER_MODE_NORMAL, p_pen, 1 );
2121         FT_Glyph_Get_CBox( outline, ft_glyph_bbox_pixels, p_outline_bbox );
2122     }
2123     *pp_outline = outline;
2124
2125     return VLC_SUCCESS;
2126 }
2127
2128 static void FixGlyph( FT_Glyph glyph, FT_BBox *p_bbox, FT_Face face, const FT_Vector *p_pen )
2129 {
2130     FT_BitmapGlyph glyph_bmp = (FT_BitmapGlyph)glyph;
2131     if( p_bbox->xMin >= p_bbox->xMax )
2132     {
2133         p_bbox->xMin = FT_CEIL(p_pen->x);
2134         p_bbox->xMax = FT_CEIL(p_pen->x + face->glyph->advance.x);
2135         glyph_bmp->left = p_bbox->xMin;
2136     }
2137     if( p_bbox->yMin >= p_bbox->yMax )
2138     {
2139         p_bbox->yMax = FT_CEIL(p_pen->y);
2140         p_bbox->yMin = FT_CEIL(p_pen->y + face->glyph->advance.y);
2141         glyph_bmp->top  = p_bbox->yMax;
2142     }
2143 }
2144
2145 static void BBoxEnlarge( FT_BBox *p_max, const FT_BBox *p )
2146 {
2147     p_max->xMin = __MIN(p_max->xMin, p->xMin);
2148     p_max->yMin = __MIN(p_max->yMin, p->yMin);
2149     p_max->xMax = __MAX(p_max->xMax, p->xMax);
2150     p_max->yMax = __MAX(p_max->yMax, p->yMax);
2151 }
2152
2153 static int ProcessLines( filter_t *p_filter,
2154                          line_desc_t **pp_lines,
2155                          FT_BBox     *p_bbox,
2156                          int         *pi_max_face_height,
2157
2158                          uni_char_t *psz_text,
2159                          text_style_t **pp_styles,
2160                          uint32_t *pi_k_dates,
2161                          int i_len )
2162 {
2163     filter_sys_t   *p_sys = p_filter->p_sys;
2164     uni_char_t     *p_fribidi_string = NULL;
2165     text_style_t   **pp_fribidi_styles = NULL;
2166     int            *p_new_positions = NULL;
2167
2168 #if defined(HAVE_FRIBIDI)
2169     {
2170         int    *p_old_positions;
2171         int start_pos, pos = 0;
2172
2173         pp_fribidi_styles = calloc( i_len, sizeof(*pp_fribidi_styles) );
2174
2175         p_fribidi_string  = malloc( (i_len + 1) * sizeof(*p_fribidi_string) );
2176         p_old_positions   = malloc( (i_len + 1) * sizeof(*p_old_positions) );
2177         p_new_positions   = malloc( (i_len + 1) * sizeof(*p_new_positions) );
2178
2179         if( ! pp_fribidi_styles ||
2180             ! p_fribidi_string ||
2181             ! p_old_positions ||
2182             ! p_new_positions )
2183         {
2184             free( p_old_positions );
2185             free( p_new_positions );
2186             free( p_fribidi_string );
2187             free( pp_fribidi_styles );
2188             return VLC_ENOMEM;
2189         }
2190
2191         /* Do bidi conversion line-by-line */
2192         while(pos < i_len)
2193         {
2194             while(pos < i_len) {
2195                 if (psz_text[pos] != '\n')
2196                     break;
2197                 p_fribidi_string[pos] = psz_text[pos];
2198                 pp_fribidi_styles[pos] = pp_styles[pos];
2199                 p_new_positions[pos] = pos;
2200                 ++pos;
2201             }
2202             start_pos = pos;
2203             while(pos < i_len) {
2204                 if (psz_text[pos] == '\n')
2205                     break;
2206                 ++pos;
2207             }
2208             if (pos > start_pos)
2209             {
2210 #if (FRIBIDI_MINOR_VERSION < 19) && (FRIBIDI_MAJOR_VERSION == 0)
2211                 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
2212 #else
2213                 FriBidiParType base_dir = FRIBIDI_PAR_LTR;
2214 #endif
2215                 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
2216                         pos - start_pos, &base_dir,
2217                         (FriBidiChar*)p_fribidi_string + start_pos,
2218                         p_new_positions + start_pos,
2219                         p_old_positions,
2220                         NULL );
2221                 for( int j = start_pos; j < pos; j++ )
2222                 {
2223                     pp_fribidi_styles[ j ] = pp_styles[ start_pos + p_old_positions[j - start_pos] ];
2224                     p_new_positions[ j ] += start_pos;
2225                 }
2226             }
2227         }
2228         p_fribidi_string[ i_len ] = 0;
2229         free( p_old_positions );
2230
2231         pp_styles = pp_fribidi_styles;
2232         psz_text = p_fribidi_string;
2233     }
2234 #endif
2235     /* Work out the karaoke */
2236     uint8_t *pi_karaoke_bar = NULL;
2237     if( pi_k_dates )
2238     {
2239         pi_karaoke_bar = malloc( i_len * sizeof(*pi_karaoke_bar));
2240         if( pi_karaoke_bar )
2241         {
2242             int64_t i_elapsed  = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
2243             for( int i = 0; i < i_len; i++ )
2244             {
2245                 unsigned i_bar = p_new_positions ? p_new_positions[i] : i;
2246                 pi_karaoke_bar[i_bar] = pi_k_dates[i] >= i_elapsed;
2247             }
2248         }
2249     }
2250     free( p_new_positions );
2251
2252     *pi_max_face_height = 0;
2253     *pp_lines = NULL;
2254     line_desc_t **pp_line_next = pp_lines;
2255
2256     FT_BBox bbox = {
2257         .xMin = INT_MAX,
2258         .yMin = INT_MAX,
2259         .xMax = INT_MIN,
2260         .yMax = INT_MIN,
2261     };
2262     int i_face_height_previous = 0;
2263     int i_base_line = 0;
2264     const text_style_t *p_previous_style = NULL;
2265     FT_Face p_face = NULL;
2266     for( int i_start = 0; i_start < i_len; )
2267     {
2268         /* Compute the length of the current text line */
2269         int i_length = 0;
2270         while( i_start + i_length < i_len && psz_text[i_start + i_length] != '\n' )
2271             i_length++;
2272
2273         /* Render the text line (or the begining if too long) into 0 or 1 glyph line */
2274         line_desc_t *p_line = i_length > 0 ? NewLine( i_length ) : NULL;
2275         int i_index = i_start;
2276         FT_Vector pen = {
2277             .x = 0,
2278             .y = 0,
2279         };
2280         int i_face_height = 0;
2281         FT_BBox line_bbox = {
2282             .xMin = INT_MAX,
2283             .yMin = INT_MAX,
2284             .xMax = INT_MIN,
2285             .yMax = INT_MIN,
2286         };
2287         int i_ul_offset = 0;
2288         int i_ul_thickness = 0;
2289         typedef struct {
2290             int       i_index;
2291             FT_Vector pen;
2292             FT_BBox   line_bbox;
2293             int i_face_height;
2294             int i_ul_offset;
2295             int i_ul_thickness;
2296         } break_point_t;
2297         break_point_t break_point;
2298         break_point_t break_point_fallback;
2299
2300 #define SAVE_BP(dst) do { \
2301         dst.i_index = i_index; \
2302         dst.pen = pen; \
2303         dst.line_bbox = line_bbox; \
2304         dst.i_face_height = i_face_height; \
2305         dst.i_ul_offset = i_ul_offset; \
2306         dst.i_ul_thickness = i_ul_thickness; \
2307     } while(0)
2308
2309         SAVE_BP( break_point );
2310         SAVE_BP( break_point_fallback );
2311
2312         while( i_index < i_start + i_length )
2313         {
2314             /* Split by common FT_Face + Size */
2315             const text_style_t *p_current_style = pp_styles[i_index];
2316             int i_part_length = 0;
2317             while( i_index + i_part_length < i_start + i_length )
2318             {
2319                 const text_style_t *p_style = pp_styles[i_index + i_part_length];
2320                 if( !FaceStyleEquals( p_style, p_current_style ) ||
2321                     p_style->i_font_size != p_current_style->i_font_size )
2322                     break;
2323                 i_part_length++;
2324             }
2325
2326             /* (Re)load/reconfigure the face if needed */
2327             if( !FaceStyleEquals( p_current_style, p_previous_style ) )
2328             {
2329                 if( p_face )
2330                     FT_Done_Face( p_face );
2331                 p_previous_style = NULL;
2332
2333                 p_face = LoadFace( p_filter, p_current_style );
2334             }
2335             FT_Face p_current_face = p_face ? p_face : p_sys->p_face;
2336             if( !p_previous_style || p_previous_style->i_font_size != p_current_style->i_font_size )
2337             {
2338                 if( FT_Set_Pixel_Sizes( p_current_face, 0, p_current_style->i_font_size ) )
2339                     msg_Err( p_filter, "Failed to set font size to %d", p_current_style->i_font_size );
2340                 if( p_sys->p_stroker )
2341                 {
2342                     double f_outline_thickness = var_InheritInteger( p_filter, "freetype-outline-thickness" ) / 100.0;
2343                     f_outline_thickness = VLC_CLIP( f_outline_thickness, 0.0, 0.5 );
2344                     int i_radius = (p_current_style->i_font_size << 6) * f_outline_thickness;
2345                     FT_Stroker_Set( p_sys->p_stroker,
2346                                     i_radius,
2347                                     FT_STROKER_LINECAP_ROUND,
2348                                     FT_STROKER_LINEJOIN_ROUND, 0 );
2349                 }
2350             }
2351             p_previous_style = p_current_style;
2352
2353             i_face_height = __MAX(i_face_height, FT_CEIL(FT_MulFix(p_current_face->height,
2354                                                                    p_current_face->size->metrics.y_scale)));
2355
2356             /* Render the part */
2357             bool b_break_line = false;
2358             int i_glyph_last = 0;
2359             while( i_part_length > 0 )
2360             {
2361                 const text_style_t *p_glyph_style = pp_styles[i_index];
2362                 uni_char_t character = psz_text[i_index];
2363                 int i_glyph_index = FT_Get_Char_Index( p_current_face, character );
2364
2365                 /* Get kerning vector */
2366                 FT_Vector kerning = { .x = 0, .y = 0 };
2367                 if( FT_HAS_KERNING( p_current_face ) && i_glyph_last != 0 && i_glyph_index != 0 )
2368                     FT_Get_Kerning( p_current_face, i_glyph_last, i_glyph_index, ft_kerning_default, &kerning );
2369
2370                 /* Get the glyph bitmap and its bounding box and all the associated properties */
2371                 FT_Vector pen_new = {
2372                     .x = pen.x + kerning.x,
2373                     .y = pen.y + kerning.y,
2374                 };
2375                 FT_Vector pen_shadow_new = {
2376                     .x = pen_new.x + p_sys->f_shadow_vector_x * (p_current_style->i_font_size << 6),
2377                     .y = pen_new.y + p_sys->f_shadow_vector_y * (p_current_style->i_font_size << 6),
2378                 };
2379                 FT_Glyph glyph;
2380                 FT_BBox  glyph_bbox;
2381                 FT_Glyph outline;
2382                 FT_BBox  outline_bbox;
2383                 FT_Glyph shadow;
2384                 FT_BBox  shadow_bbox;
2385
2386                 if( GetGlyph( p_filter,
2387                               &glyph, &glyph_bbox,
2388                               &outline, &outline_bbox,
2389                               &shadow, &shadow_bbox,
2390                               p_current_face, i_glyph_index, p_glyph_style->i_style_flags,
2391                               &pen_new, &pen_shadow_new ) )
2392                     goto next;
2393
2394                 FixGlyph( glyph, &glyph_bbox, p_current_face, &pen_new );
2395                 if( outline )
2396                     FixGlyph( outline, &outline_bbox, p_current_face, &pen_new );
2397                 if( shadow )
2398                     FixGlyph( shadow, &shadow_bbox, p_current_face, &pen_shadow_new );
2399
2400                 /* FIXME and what about outline */
2401
2402                 bool     b_karaoke = pi_karaoke_bar && pi_karaoke_bar[i_index] != 0;
2403                 uint32_t i_color = b_karaoke ? (p_glyph_style->i_karaoke_background_color |
2404                                                 (p_glyph_style->i_karaoke_background_alpha << 24))
2405                                              : (p_glyph_style->i_font_color |
2406                                                 (p_glyph_style->i_font_alpha << 24));
2407                 int i_line_offset    = 0;
2408                 int i_line_thickness = 0;
2409                 if( p_glyph_style->i_style_flags & (STYLE_UNDERLINE | STYLE_STRIKEOUT) )
2410                 {
2411                     i_line_offset = abs( FT_FLOOR(FT_MulFix(p_current_face->underline_position,
2412                                                             p_current_face->size->metrics.y_scale)) );
2413
2414                     i_line_thickness = abs( FT_CEIL(FT_MulFix(p_current_face->underline_thickness,
2415                                                               p_current_face->size->metrics.y_scale)) );
2416
2417                     if( p_glyph_style->i_style_flags & STYLE_STRIKEOUT )
2418                     {
2419                         /* Move the baseline to make it strikethrough instead of
2420                          * underline. That means that strikethrough takes precedence
2421                          */
2422                         i_line_offset -= abs( FT_FLOOR(FT_MulFix(p_current_face->descender*2,
2423                                                                  p_current_face->size->metrics.y_scale)) );
2424                     }
2425                     else if( i_line_thickness > 0 )
2426                     {
2427                         glyph_bbox.yMin = __MIN( glyph_bbox.yMin, - i_line_offset - i_line_thickness );
2428
2429                         /* The real underline thickness and position are
2430                          * updated once the whole line has been parsed */
2431                         i_ul_offset = __MAX( i_ul_offset, i_line_offset );
2432                         i_ul_thickness = __MAX( i_ul_thickness, i_line_thickness );
2433                         i_line_thickness = -1;
2434                     }
2435                 }
2436                 FT_BBox line_bbox_new = line_bbox;
2437                 BBoxEnlarge( &line_bbox_new, &glyph_bbox );
2438                 if( outline )
2439                     BBoxEnlarge( &line_bbox_new, &outline_bbox );
2440                 if( shadow )
2441                     BBoxEnlarge( &line_bbox_new, &shadow_bbox );
2442
2443                 b_break_line = i_index > i_start &&
2444                                line_bbox_new.xMax - line_bbox_new.xMin >= (int)p_filter->fmt_out.video.i_visible_width;
2445                 if( b_break_line )
2446                 {
2447                     FT_Done_Glyph( glyph );
2448                     if( outline )
2449                         FT_Done_Glyph( outline );
2450                     if( shadow )
2451                         FT_Done_Glyph( shadow );
2452
2453                     break_point_t *p_bp = NULL;
2454                     if( break_point.i_index > i_start )
2455                         p_bp = &break_point;
2456                     else if( break_point_fallback.i_index > i_start )
2457                         p_bp = &break_point_fallback;
2458
2459                     if( p_bp )
2460                     {
2461                         msg_Dbg( p_filter, "Breaking line");
2462                         for( int i = p_bp->i_index; i < i_index; i++ )
2463                         {
2464                             line_character_t *ch = &p_line->p_character[i - i_start];
2465                             FT_Done_Glyph( (FT_Glyph)ch->p_glyph );
2466                             if( ch->p_outline )
2467                                 FT_Done_Glyph( (FT_Glyph)ch->p_outline );
2468                             if( ch->p_shadow )
2469                                 FT_Done_Glyph( (FT_Glyph)ch->p_shadow );
2470                         }
2471                         p_line->i_character_count = p_bp->i_index - i_start;
2472
2473                         i_index = p_bp->i_index;
2474                         pen = p_bp->pen;
2475                         line_bbox = p_bp->line_bbox;
2476                         i_face_height = p_bp->i_face_height;
2477                         i_ul_offset = p_bp->i_ul_offset;
2478                         i_ul_thickness = p_bp->i_ul_thickness;
2479                     }
2480                     else
2481                     {
2482                         msg_Err( p_filter, "Breaking unbreakable line");
2483                     }
2484                     break;
2485                 }
2486
2487                 assert( p_line->i_character_count == i_index - i_start);
2488                 p_line->p_character[p_line->i_character_count++] = (line_character_t){
2489                     .p_glyph = (FT_BitmapGlyph)glyph,
2490                     .p_outline = (FT_BitmapGlyph)outline,
2491                     .p_shadow = (FT_BitmapGlyph)shadow,
2492                     .i_color = i_color,
2493                     .i_line_offset = i_line_offset,
2494                     .i_line_thickness = i_line_thickness,
2495                 };
2496
2497                 pen.x = pen_new.x + p_current_face->glyph->advance.x;
2498                 pen.y = pen_new.y + p_current_face->glyph->advance.y;
2499                 line_bbox = line_bbox_new;
2500             next:
2501                 i_glyph_last = i_glyph_index;
2502                 i_part_length--;
2503                 i_index++;
2504
2505                 if( character == ' ' || character == '\t' )
2506                     SAVE_BP( break_point );
2507                 else if( character == 160 )
2508                     SAVE_BP( break_point_fallback );
2509             }
2510             if( b_break_line )
2511                 break;
2512         }
2513 #undef SAVE_BP
2514         /* Update our baseline */
2515         if( i_face_height_previous > 0 )
2516             i_base_line += __MAX(i_face_height, i_face_height_previous);
2517         if( i_face_height > 0 )
2518             i_face_height_previous = i_face_height;
2519
2520         /* Update the line bbox with the actual base line */
2521         if (line_bbox.yMax > line_bbox.yMin) {
2522             line_bbox.yMin -= i_base_line;
2523             line_bbox.yMax -= i_base_line;
2524         }
2525         BBoxEnlarge( &bbox, &line_bbox );
2526
2527         /* Terminate and append the line */
2528         if( p_line )
2529         {
2530             p_line->i_width  = __MAX(line_bbox.xMax - line_bbox.xMin, 0);
2531             p_line->i_base_line = i_base_line;
2532             p_line->i_height = __MAX(i_face_height, i_face_height_previous);
2533             if( i_ul_thickness > 0 )
2534             {
2535                 for( int i = 0; i < p_line->i_character_count; i++ )
2536                 {
2537                     line_character_t *ch = &p_line->p_character[i];
2538                     if( ch->i_line_thickness < 0 )
2539                     {
2540                         ch->i_line_offset    = i_ul_offset;
2541                         ch->i_line_thickness = i_ul_thickness;
2542                     }
2543                 }
2544             }
2545
2546             *pp_line_next = p_line;
2547             pp_line_next = &p_line->p_next;
2548         }
2549
2550         *pi_max_face_height = __MAX( *pi_max_face_height, i_face_height );
2551
2552         /* Skip what we have rendered and the line delimitor if present */
2553         i_start = i_index;
2554         if( i_start < i_len && psz_text[i_start] == '\n' )
2555             i_start++;
2556
2557         if( bbox.yMax - bbox.yMin >= (int)p_filter->fmt_out.video.i_visible_height )
2558         {
2559             msg_Err( p_filter, "Truncated too high subtitle" );
2560             break;
2561         }
2562     }
2563     if( p_face )
2564         FT_Done_Face( p_face );
2565
2566     free( pp_fribidi_styles );
2567     free( p_fribidi_string );
2568     free( pi_karaoke_bar );
2569
2570     *p_bbox = bbox;
2571     return VLC_SUCCESS;
2572 }
2573
2574 /**
2575  * This function renders a text subpicture region into another one.
2576  * It also calculates the size needed for this string, and renders the
2577  * needed glyphs into memory. It is used as pf_add_string callback in
2578  * the vout method by this module
2579  */
2580 static int RenderCommon( filter_t *p_filter, subpicture_region_t *p_region_out,
2581                          subpicture_region_t *p_region_in, bool b_html,
2582                          const vlc_fourcc_t *p_chroma_list )
2583 {
2584     filter_sys_t *p_sys = p_filter->p_sys;
2585
2586     if( !p_region_in )
2587         return VLC_EGENERIC;
2588     if( b_html && !p_region_in->psz_html )
2589         return VLC_EGENERIC;
2590     if( !b_html && !p_region_in->psz_text )
2591         return VLC_EGENERIC;
2592
2593     const size_t i_text_max = strlen( b_html ? p_region_in->psz_html
2594                                              : p_region_in->psz_text );
2595
2596     uni_char_t *psz_text = calloc( i_text_max, sizeof( *psz_text ) );
2597     text_style_t **pp_styles = calloc( i_text_max, sizeof( *pp_styles ) );
2598     if( !psz_text || !pp_styles )
2599     {
2600         free( psz_text );
2601         free( pp_styles );
2602         return VLC_EGENERIC;
2603     }
2604
2605     /* Reset the default fontsize in case screen metrics have changed */
2606     p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2607
2608     /* */
2609     int rv = VLC_SUCCESS;
2610     int i_text_length = 0;
2611     FT_BBox bbox;
2612     int i_max_face_height;
2613     line_desc_t *p_lines = NULL;
2614
2615     uint32_t *pi_k_durations   = NULL;
2616
2617     if( b_html )
2618     {
2619         stream_t *p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2620                                             (uint8_t *) p_region_in->psz_html,
2621                                             strlen( p_region_in->psz_html ),
2622                                             true );
2623         if( unlikely(p_sub == NULL) )
2624             return VLC_SUCCESS;
2625
2626         xml_reader_t *p_xml_reader = p_filter->p_sys->p_xml;
2627         if( !p_xml_reader )
2628             p_xml_reader = xml_ReaderCreate( p_filter, p_sub );
2629         else
2630             p_xml_reader = xml_ReaderReset( p_xml_reader, p_sub );
2631         p_filter->p_sys->p_xml = p_xml_reader;
2632
2633         if( !p_xml_reader )
2634             rv = VLC_EGENERIC;
2635
2636         if( !rv )
2637         {
2638             /* Look for Root Node */
2639             const char *node;
2640
2641             if( xml_ReaderNextNode( p_xml_reader, &node ) == XML_READER_STARTELEM )
2642             {
2643                 if( strcasecmp( "karaoke", node ) == 0 )
2644                 {
2645                     pi_k_durations = calloc( i_text_max, sizeof( *pi_k_durations ) );
2646                 }
2647                 else if( strcasecmp( "text", node ) != 0 )
2648                 {
2649                     /* Only text and karaoke tags are supported */
2650                     msg_Dbg( p_filter, "Unsupported top-level tag <%s> ignored.",
2651                              node );
2652                     rv = VLC_EGENERIC;
2653                 }
2654             }
2655             else
2656             {
2657                 msg_Err( p_filter, "Malformed HTML subtitle" );
2658                 rv = VLC_EGENERIC;
2659             }
2660         }
2661         if( !rv )
2662         {
2663             rv = ProcessNodes( p_filter,
2664                                psz_text, pp_styles, pi_k_durations, &i_text_length,
2665                                p_xml_reader, p_region_in->p_style );
2666         }
2667
2668         if( p_xml_reader )
2669             p_filter->p_sys->p_xml = xml_ReaderReset( p_xml_reader, NULL );
2670
2671         stream_Delete( p_sub );
2672     }
2673     else
2674     {
2675         text_style_t *p_style;
2676         if( p_region_in->p_style )
2677             p_style = CreateStyle( p_region_in->p_style->psz_fontname,
2678                                    p_region_in->p_style->i_font_size > 0 ? p_region_in->p_style->i_font_size
2679                                                                          : p_sys->i_font_size,
2680                                    (p_region_in->p_style->i_font_color & 0xffffff) |
2681                                    ((p_region_in->p_style->i_font_alpha & 0xff) << 24),
2682                                    0x00ffffff,
2683                                    p_region_in->p_style->i_style_flags & (STYLE_BOLD |
2684                                                                           STYLE_ITALIC |
2685                                                                           STYLE_UNDERLINE |
2686                                                                           STYLE_STRIKEOUT) );
2687         else
2688         {
2689             uint32_t i_font_color = var_InheritInteger( p_filter, "freetype-color" );
2690             i_font_color = VLC_CLIP( i_font_color, 0, 0xFFFFFF );
2691             p_style = CreateStyle( p_sys->psz_fontfamily,
2692                                    p_sys->i_font_size,
2693                                    (i_font_color & 0xffffff) |
2694                                    ((p_sys->i_font_opacity & 0xff) << 24),
2695                                    0x00ffffff, 0);
2696         }
2697         if( p_sys->b_font_bold )
2698             p_style->i_style_flags |= STYLE_BOLD;
2699
2700         i_text_length = SetupText( p_filter,
2701                                    psz_text,
2702                                    pp_styles,
2703                                    NULL,
2704                                    p_region_in->psz_text, p_style, 0 );
2705     }
2706
2707     if( !rv && i_text_length > 0 )
2708     {
2709         rv = ProcessLines( p_filter,
2710                            &p_lines, &bbox, &i_max_face_height,
2711                            psz_text, pp_styles, pi_k_durations, i_text_length );
2712     }
2713
2714     p_region_out->i_x = p_region_in->i_x;
2715     p_region_out->i_y = p_region_in->i_y;
2716
2717     /* Don't attempt to render text that couldn't be layed out
2718      * properly. */
2719     if( !rv && i_text_length > 0 && bbox.xMin < bbox.xMax && bbox.yMin < bbox.yMax )
2720     {
2721         const vlc_fourcc_t p_chroma_list_yuvp[] = { VLC_CODEC_YUVP, 0 };
2722         const vlc_fourcc_t p_chroma_list_rgba[] = { VLC_CODEC_RGBA, 0 };
2723
2724         if( var_InheritBool( p_filter, "freetype-yuvp" ) )
2725             p_chroma_list = p_chroma_list_yuvp;
2726         else if( !p_chroma_list || *p_chroma_list == 0 )
2727             p_chroma_list = p_chroma_list_rgba;
2728
2729         uint8_t i_background_opacity = var_InheritInteger( p_filter, "freetype-background-opacity" );
2730         i_background_opacity = VLC_CLIP( i_background_opacity, 0, 255 );
2731         const int i_margin = i_background_opacity > 0 ? i_max_face_height / 4 : 0;
2732         for( const vlc_fourcc_t *p_chroma = p_chroma_list; *p_chroma != 0; p_chroma++ )
2733         {
2734             rv = VLC_EGENERIC;
2735             if( *p_chroma == VLC_CODEC_YUVP )
2736                 rv = RenderYUVP( p_filter, p_region_out, p_lines, &bbox );
2737             else if( *p_chroma == VLC_CODEC_YUVA )
2738                 rv = RenderAXYZ( p_filter, p_region_out, p_lines, &bbox, i_margin,
2739                                  VLC_CODEC_YUVA,
2740                                  YUVFromRGB,
2741                                  FillYUVAPicture,
2742                                  BlendYUVAPixel );
2743             else if( *p_chroma == VLC_CODEC_RGBA )
2744                 rv = RenderAXYZ( p_filter, p_region_out, p_lines, &bbox, i_margin,
2745                                  VLC_CODEC_RGBA,
2746                                  RGBFromRGB,
2747                                  FillRGBAPicture,
2748                                  BlendRGBAPixel );
2749             if( !rv )
2750                 break;
2751         }
2752
2753         /* With karaoke, we're going to have to render the text a number
2754          * of times to show the progress marker on the text.
2755          */
2756         if( pi_k_durations )
2757             var_SetBool( p_filter, "text-rerender", true );
2758     }
2759
2760     FreeLines( p_lines );
2761
2762     free( psz_text );
2763     for( int i = 0; i < i_text_length; i++ )
2764     {
2765         if( pp_styles[i] && ( i + 1 == i_text_length || pp_styles[i] != pp_styles[i + 1] ) )
2766             text_style_Delete( pp_styles[i] );
2767     }
2768     free( pp_styles );
2769     free( pi_k_durations );
2770
2771     return rv;
2772 }
2773
2774 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
2775                        subpicture_region_t *p_region_in,
2776                        const vlc_fourcc_t *p_chroma_list )
2777 {
2778     return RenderCommon( p_filter, p_region_out, p_region_in, false, p_chroma_list );
2779 }
2780
2781 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2782                        subpicture_region_t *p_region_in,
2783                        const vlc_fourcc_t *p_chroma_list )
2784 {
2785     return RenderCommon( p_filter, p_region_out, p_region_in, true, p_chroma_list );
2786 }
2787
2788 /*****************************************************************************
2789  * Create: allocates osd-text video thread output method
2790  *****************************************************************************
2791  * This function allocates and initializes a Clone vout method.
2792  *****************************************************************************/
2793 static int Create( vlc_object_t *p_this )
2794 {
2795     filter_t      *p_filter = (filter_t *)p_this;
2796     filter_sys_t  *p_sys;
2797     char          *psz_fontfile   = NULL;
2798     char          *psz_fontfamily = NULL;
2799     char          *psz_monofontfile   = NULL;
2800     char          *psz_monofontfamily = NULL;
2801     int            i_error = 0, fontindex = 0, monofontindex = 0;
2802
2803     /* Allocate structure */
2804     p_filter->p_sys = p_sys = malloc( sizeof(*p_sys) );
2805     if( !p_sys )
2806         return VLC_ENOMEM;
2807
2808     p_sys->psz_fontfamily   = NULL;
2809     p_sys->p_xml            = NULL;
2810     p_sys->p_face           = 0;
2811     p_sys->p_library        = 0;
2812     p_sys->i_font_size      = 0;
2813     p_sys->i_display_height = 0;
2814
2815     /*
2816      * The following variables should not be cached, as they might be changed on-the-fly:
2817      * freetype-rel-fontsize, freetype-background-opacity, freetype-background-color,
2818      * freetype-outline-thickness, freetype-color
2819      *
2820      */
2821
2822     psz_fontfamily = var_InheritString( p_filter, "freetype-font" );
2823     psz_monofontfamily = var_InheritString( p_filter, "freetype-monofont" );
2824     p_sys->i_default_font_size = var_InheritInteger( p_filter, "freetype-fontsize" );
2825     p_sys->i_font_opacity = var_InheritInteger( p_filter,"freetype-opacity" );
2826     p_sys->i_font_opacity = VLC_CLIP( p_sys->i_font_opacity, 0, 255 );
2827     p_sys->b_font_bold = var_InheritBool( p_filter, "freetype-bold" );
2828
2829     double f_outline_thickness = var_InheritInteger( p_filter, "freetype-outline-thickness" ) / 100.0;
2830     f_outline_thickness = VLC_CLIP( f_outline_thickness, 0.0, 0.5 );
2831     p_sys->i_outline_opacity = var_InheritInteger( p_filter, "freetype-outline-opacity" );
2832     p_sys->i_outline_opacity = VLC_CLIP( p_sys->i_outline_opacity, 0, 255 );
2833     p_sys->i_outline_color = var_InheritInteger( p_filter, "freetype-outline-color" );
2834     p_sys->i_outline_color = VLC_CLIP( p_sys->i_outline_color, 0, 0xFFFFFF );
2835
2836     p_sys->i_shadow_opacity = var_InheritInteger( p_filter, "freetype-shadow-opacity" );
2837     p_sys->i_shadow_opacity = VLC_CLIP( p_sys->i_shadow_opacity, 0, 255 );
2838     p_sys->i_shadow_color = var_InheritInteger( p_filter, "freetype-shadow-color" );
2839     p_sys->i_shadow_color = VLC_CLIP( p_sys->i_shadow_color, 0, 0xFFFFFF );
2840     float f_shadow_angle = var_InheritFloat( p_filter, "freetype-shadow-angle" );
2841     float f_shadow_distance = var_InheritFloat( p_filter, "freetype-shadow-distance" );
2842     f_shadow_distance = VLC_CLIP( f_shadow_distance, 0, 1 );
2843     p_sys->f_shadow_vector_x = f_shadow_distance * cos(2 * M_PI * f_shadow_angle / 360);
2844     p_sys->f_shadow_vector_y = f_shadow_distance * sin(2 * M_PI * f_shadow_angle / 360);
2845
2846 #ifdef WIN32
2847     /* Get Windows Font folder */
2848     wchar_t wdir[MAX_PATH];
2849     if( S_OK != SHGetFolderPathW( NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, wdir ) )
2850     {
2851         GetWindowsDirectoryW( wdir, MAX_PATH );
2852         wcscat( wdir, L"\\fonts" );
2853     }
2854     p_sys->psz_win_fonts_path = FromWide( wdir );
2855 #endif
2856
2857     /* Set default psz_fontfamily */
2858     if( !psz_fontfamily || !*psz_fontfamily )
2859     {
2860         free( psz_fontfamily );
2861 #ifdef HAVE_STYLES
2862         psz_fontfamily = strdup( DEFAULT_FAMILY );
2863 #else
2864 # ifdef WIN32
2865         if( asprintf( &psz_fontfamily, "%s"DEFAULT_FONT_FILE, p_sys->psz_win_fonts_path ) == -1 )
2866             goto error;
2867 # else
2868         psz_fontfamily = strdup( DEFAULT_FONT_FILE );
2869 # endif
2870         msg_Err( p_filter,"User specified an empty fontfile, using %s", psz_fontfamily );
2871 #endif
2872     }
2873
2874     /* Set the current font file */
2875     p_sys->psz_fontfamily = psz_fontfamily;
2876 #ifdef HAVE_STYLES
2877 #ifdef HAVE_FONTCONFIG
2878     FontConfig_BuildCache( p_filter );
2879
2880     /* */
2881     psz_fontfile = FontConfig_Select( NULL, psz_fontfamily, false, false,
2882                                       p_sys->i_default_font_size, &fontindex );
2883     psz_monofontfile = FontConfig_Select( NULL, psz_monofontfamily, false,
2884                                           false, p_sys->i_default_font_size,
2885                                           &monofontindex );
2886 #elif defined(__APPLE__)
2887 #if !TARGET_OS_IPHONE
2888     psz_fontfile = MacLegacy_Select( p_filter, psz_fontfamily, false, false, 0, &fontindex );
2889 #endif
2890 #elif defined(WIN32)
2891     psz_fontfile = Win32_Select( p_filter, psz_fontfamily, false, false,
2892                                  p_sys->i_default_font_size, &fontindex );
2893
2894 #endif
2895     msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily, psz_fontfile );
2896
2897     /* If nothing is found, use the default family */
2898     if( !psz_fontfile )
2899         psz_fontfile = strdup( psz_fontfamily );
2900     if( !psz_monofontfile )
2901         psz_monofontfile = strdup( psz_monofontfamily );
2902
2903 #else /* !HAVE_STYLES */
2904     /* Use the default file */
2905     psz_fontfile = psz_fontfamily;
2906     psz_monofontfile = psz_monofontfamily;
2907 #endif
2908     p_sys->psz_monofontfamily = psz_monofontfamily;
2909
2910     /* */
2911     i_error = FT_Init_FreeType( &p_sys->p_library );
2912     if( i_error )
2913     {
2914         msg_Err( p_filter, "couldn't initialize freetype" );
2915         goto error;
2916     }
2917
2918     i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
2919                            fontindex, &p_sys->p_face );
2920
2921     if( i_error == FT_Err_Unknown_File_Format )
2922     {
2923         msg_Err( p_filter, "file %s have unknown format",
2924                  psz_fontfile ? psz_fontfile : "(null)" );
2925         goto error;
2926     }
2927     else if( i_error )
2928     {
2929         msg_Err( p_filter, "failed to load font file %s",
2930                  psz_fontfile ? psz_fontfile : "(null)" );
2931         goto error;
2932     }
2933
2934     i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
2935     if( i_error )
2936     {
2937         msg_Err( p_filter, "font has no unicode translation table" );
2938         goto error;
2939     }
2940
2941     if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
2942
2943     p_sys->p_stroker = NULL;
2944     if( f_outline_thickness > 0.001 )
2945     {
2946         i_error = FT_Stroker_New( p_sys->p_library, &p_sys->p_stroker );
2947         if( i_error )
2948             msg_Err( p_filter, "Failed to create stroker for outlining" );
2949     }
2950
2951     p_sys->pp_font_attachments = NULL;
2952     p_sys->i_font_attachments = 0;
2953
2954     p_filter->pf_render_text = RenderText;
2955     p_filter->pf_render_html = RenderHtml;
2956
2957     LoadFontsFromAttachments( p_filter );
2958
2959 #ifdef HAVE_STYLES
2960     free( psz_fontfile );
2961     free( psz_monofontfile );
2962 #endif
2963
2964     return VLC_SUCCESS;
2965
2966 error:
2967     if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
2968     if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
2969 #ifdef HAVE_STYLES
2970     free( psz_fontfile );
2971     free( psz_monofontfile );
2972 #endif
2973     free( psz_fontfamily );
2974     free( psz_monofontfamily );
2975     free( p_sys );
2976     return VLC_EGENERIC;
2977 }
2978
2979 /*****************************************************************************
2980  * Destroy: destroy Clone video thread output method
2981  *****************************************************************************
2982  * Clean up all data and library connections
2983  *****************************************************************************/
2984 static void Destroy( vlc_object_t *p_this )
2985 {
2986     filter_t *p_filter = (filter_t *)p_this;
2987     filter_sys_t *p_sys = p_filter->p_sys;
2988
2989     if( p_sys->pp_font_attachments )
2990     {
2991         for( int k = 0; k < p_sys->i_font_attachments; k++ )
2992             vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
2993
2994         free( p_sys->pp_font_attachments );
2995     }
2996
2997     if( p_sys->p_xml ) xml_ReaderDelete( p_sys->p_xml );
2998     free( p_sys->psz_fontfamily );
2999
3000 #ifdef WIN32
3001     free( p_sys->psz_win_fonts_path );
3002 #endif
3003
3004     /* FcFini asserts calling the subfunction FcCacheFini()
3005      * even if no other library functions have been made since FcInit(),
3006      * so don't call it. */
3007
3008     if( p_sys->p_stroker )
3009         FT_Stroker_Done( p_sys->p_stroker );
3010     FT_Done_Face( p_sys->p_face );
3011     FT_Done_FreeType( p_sys->p_library );
3012     free( p_sys );
3013 }