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