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