]> git.sesse.net Git - vlc/blob - modules/text_renderer/freetype.c
Qt: open disk: fix tab access order
[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 Unicode.ttf"
50 # define DEFAULT_FAMILY "Arial Unicode MS"
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 FONT_DIR_NT "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"
568
569 static int GetFileFontByName( const char *font_name, char **psz_filename )
570 {
571     HKEY hKey;
572     wchar_t vbuffer[MAX_PATH];
573     wchar_t dbuffer[256];
574
575     if( RegOpenKeyEx(HKEY_LOCAL_MACHINE, FONT_DIR_NT, 0, KEY_READ, &hKey)
576             != ERROR_SUCCESS )
577         return 1;
578
579     MultiByteToWideChar( CP_ACP, 0, font_name, -1, dbuffer, 256 );
580     char *font_name_temp = FromWide( dbuffer );
581     size_t fontname_len = strlen( font_name_temp );
582
583     for( int index = 0;; index++ )
584     {
585         DWORD vbuflen = MAX_PATH - 1;
586         DWORD dbuflen = 255;
587
588         LONG i_result = RegEnumValueW( hKey, index, vbuffer, &vbuflen,
589                                        NULL, NULL, (LPBYTE)dbuffer, &dbuflen);
590         if( i_result != ERROR_SUCCESS )
591         {
592             RegCloseKey( hKey );
593             return i_result;
594         }
595
596         char *psz_value = FromWide( vbuffer );
597
598         char *s = strchr( psz_value,'(' );
599         if( s != NULL && s != psz_value ) s[-1] = '\0';
600
601         /* Manage concatenated font names */
602         if( strchr( psz_value, '&') ) {
603             if( strcasestr( psz_value, font_name_temp ) != NULL )
604             {
605                 free( psz_value );
606                 break;
607             }
608         }
609         else {
610             if( strncasecmp( psz_value, font_name_temp, fontname_len ) == 0 )
611             {
612                 free( psz_value );
613                 break;
614             }
615         }
616
617         free( psz_value );
618     }
619
620     *psz_filename = FromWide( dbuffer );
621     free( font_name_temp );
622     RegCloseKey( hKey );
623     return 0;
624 }
625
626
627 static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *lpelfe, const NEWTEXTMETRICEX *metric,
628                                      DWORD type, LPARAM lParam)
629 {
630     VLC_UNUSED( metric );
631     if( (type & RASTER_FONTTYPE) ) return 1;
632     // if( lpelfe->elfScript ) FIXME
633
634     return GetFileFontByName( (const char *)lpelfe->elfFullName, (char **)lParam );
635 }
636
637 static char* Win32_Select( filter_t *p_filter, const char* family,
638                            bool b_bold, bool b_italic, int i_size, int *i_idx )
639 {
640     VLC_UNUSED( i_size );
641
642     if( !family || strlen( family ) < 1 )
643         goto fail;
644
645     /* */
646     LOGFONT lf;
647     lf.lfCharSet = DEFAULT_CHARSET;
648     if( b_italic )
649         lf.lfItalic = true;
650     if( b_bold )
651         lf.lfWeight = FW_BOLD;
652
653     char facename[32];
654     wchar_t* psz_fbuffer = ToWide( family );
655     WideCharToMultiByte( CP_ACP, 0, psz_fbuffer, -1, facename, 32, " ", 0 );
656     strncpy( (LPSTR)&lf.lfFaceName, facename, 32 );
657     free( psz_fbuffer );
658
659     /* */
660     char *psz_filename = NULL;
661     HDC hDC = GetDC( NULL );
662     EnumFontFamiliesEx(hDC, &lf, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&psz_filename, 0);
663     ReleaseDC(NULL, hDC);
664
665     /* */
666     if( psz_filename != NULL )
667     {
668         /* FIXME: increase i_idx, when concatenated strings  */
669         i_idx = 0;
670
671         /* Prepend the Windows Font path, when only a filename was provided */
672         if( strchr( psz_filename, DIR_SEP_CHAR ) )
673             return psz_filename;
674         else
675         {
676             char *psz_tmp;
677             if( asprintf( &psz_tmp, "%s\\%s", p_filter->p_sys->psz_win_fonts_path, psz_filename ) == -1 )
678             {
679                 free( psz_filename );
680                 return NULL;
681             }
682             free( psz_filename );
683             return psz_tmp;
684         }
685     }
686     else /* Let's take any font we can */
687 fail:
688     {
689         char *psz_tmp;
690         if( asprintf( &psz_tmp, "%s\\%s", p_filter->p_sys->psz_win_fonts_path, "arial.ttf" ) == -1 )
691             return NULL;
692         else
693             return psz_tmp;
694     }
695 }
696 #endif /* HAVE_WIN32 */
697
698 #endif /* HAVE_STYLES */
699
700
701 /*****************************************************************************
702  * RenderYUVP: place string in picture
703  *****************************************************************************
704  * This function merges the previously rendered freetype glyphs into a picture
705  *****************************************************************************/
706 static int RenderYUVP( filter_t *p_filter, subpicture_region_t *p_region,
707                        line_desc_t *p_line,
708                        FT_BBox *p_bbox )
709 {
710     VLC_UNUSED(p_filter);
711     static const uint8_t pi_gamma[16] =
712         {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
713           0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
714
715     uint8_t *p_dst;
716     video_format_t fmt;
717     int i, x, y, i_pitch;
718     uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
719
720     /* Create a new subpicture region */
721     video_format_Init( &fmt, VLC_CODEC_YUVP );
722     fmt.i_width          =
723     fmt.i_visible_width  = p_bbox->xMax - p_bbox->xMin + 4;
724     fmt.i_height         =
725     fmt.i_visible_height = p_bbox->yMax - p_bbox->yMin + 4;
726
727     assert( !p_region->p_picture );
728     p_region->p_picture = picture_NewFromFormat( &fmt );
729     if( !p_region->p_picture )
730         return VLC_EGENERIC;
731     fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
732     p_region->fmt = fmt;
733
734     /* Calculate text color components
735      * Only use the first color */
736     int i_alpha = (p_line->p_character[0].i_color >> 24) & 0xff;
737     YUVFromRGB( p_line->p_character[0].i_color, &i_y, &i_u, &i_v );
738
739     /* Build palette */
740     fmt.p_palette->i_entries = 16;
741     for( i = 0; i < 8; i++ )
742     {
743         fmt.p_palette->palette[i][0] = 0;
744         fmt.p_palette->palette[i][1] = 0x80;
745         fmt.p_palette->palette[i][2] = 0x80;
746         fmt.p_palette->palette[i][3] = pi_gamma[i];
747         fmt.p_palette->palette[i][3] =
748             (int)fmt.p_palette->palette[i][3] * i_alpha / 255;
749     }
750     for( i = 8; i < fmt.p_palette->i_entries; i++ )
751     {
752         fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
753         fmt.p_palette->palette[i][1] = i_u;
754         fmt.p_palette->palette[i][2] = i_v;
755         fmt.p_palette->palette[i][3] = pi_gamma[i];
756         fmt.p_palette->palette[i][3] =
757             (int)fmt.p_palette->palette[i][3] * i_alpha / 255;
758     }
759
760     p_dst = p_region->p_picture->Y_PIXELS;
761     i_pitch = p_region->p_picture->Y_PITCH;
762
763     /* Initialize the region pixels */
764     memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
765
766     for( ; p_line != NULL; p_line = p_line->p_next )
767     {
768         int i_align_left = 0;
769         if( p_line->i_width < (int)fmt.i_visible_width )
770         {
771             if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
772                 i_align_left = ( fmt.i_visible_width - p_line->i_width );
773             else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
774                 i_align_left = ( fmt.i_visible_width - p_line->i_width ) / 2;
775         }
776         int i_align_top = 0;
777
778         for( i = 0; i < p_line->i_character_count; i++ )
779         {
780             const line_character_t *ch = &p_line->p_character[i];
781             FT_BitmapGlyph p_glyph = ch->p_glyph;
782
783             int i_glyph_y = i_align_top  - p_glyph->top  + p_bbox->yMax + p_line->i_base_line;
784             int i_glyph_x = i_align_left + p_glyph->left - p_bbox->xMin;
785
786             for( y = 0; y < p_glyph->bitmap.rows; y++ )
787             {
788                 for( x = 0; x < p_glyph->bitmap.width; x++ )
789                 {
790                     if( p_glyph->bitmap.buffer[y * p_glyph->bitmap.width + x] )
791                         p_dst[(i_glyph_y + y) * i_pitch + (i_glyph_x + x)] =
792                             (p_glyph->bitmap.buffer[y * p_glyph->bitmap.width + x] + 8)/16;
793                 }
794             }
795         }
796     }
797
798     /* Outlining (find something better than nearest neighbour filtering ?) */
799     if( 1 )
800     {
801         uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
802         uint8_t *p_top = p_dst; /* Use 1st line as a cache */
803         uint8_t left, current;
804
805         for( y = 1; y < (int)fmt.i_height - 1; y++ )
806         {
807             if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
808             p_dst += p_region->p_picture->Y_PITCH;
809             left = 0;
810
811             for( x = 1; x < (int)fmt.i_width - 1; x++ )
812             {
813                 current = p_dst[x];
814                 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
815                              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;
816                 left = current;
817             }
818         }
819         memset( p_top, 0, fmt.i_width );
820     }
821
822     return VLC_SUCCESS;
823 }
824
825 /*****************************************************************************
826  * RenderYUVA: place string in picture
827  *****************************************************************************
828  * This function merges the previously rendered freetype glyphs into a picture
829  *****************************************************************************/
830 static void FillYUVAPicture( picture_t *p_picture,
831                              int i_a, int i_y, int i_u, int i_v )
832 {
833     memset( p_picture->p[0].p_pixels, i_y,
834             p_picture->p[0].i_pitch * p_picture->p[0].i_lines );
835     memset( p_picture->p[1].p_pixels, i_u,
836             p_picture->p[1].i_pitch * p_picture->p[1].i_lines );
837     memset( p_picture->p[2].p_pixels, i_v,
838             p_picture->p[2].i_pitch * p_picture->p[2].i_lines );
839     memset( p_picture->p[3].p_pixels, i_a,
840             p_picture->p[3].i_pitch * p_picture->p[3].i_lines );
841 }
842
843 static inline void BlendYUVAPixel( picture_t *p_picture,
844                                    int i_picture_x, int i_picture_y,
845                                    int i_a, int i_y, int i_u, int i_v,
846                                    int i_alpha )
847 {
848     int i_an = i_a * i_alpha / 255;
849
850     uint8_t *p_y = &p_picture->p[0].p_pixels[i_picture_y * p_picture->p[0].i_pitch + i_picture_x];
851     uint8_t *p_u = &p_picture->p[1].p_pixels[i_picture_y * p_picture->p[1].i_pitch + i_picture_x];
852     uint8_t *p_v = &p_picture->p[2].p_pixels[i_picture_y * p_picture->p[2].i_pitch + i_picture_x];
853     uint8_t *p_a = &p_picture->p[3].p_pixels[i_picture_y * p_picture->p[3].i_pitch + i_picture_x];
854
855     int i_ao = *p_a;
856     if( i_ao == 0 )
857     {
858         *p_y = i_y;
859         *p_u = i_u;
860         *p_v = i_v;
861         *p_a = i_an;
862     }
863     else
864     {
865         *p_a = 255 - (255 - *p_a) * (255 - i_an) / 255;
866         if( *p_a != 0 )
867         {
868             *p_y = ( *p_y * i_ao * (255 - i_an) / 255 + i_y * i_an ) / *p_a;
869             *p_u = ( *p_u * i_ao * (255 - i_an) / 255 + i_u * i_an ) / *p_a;
870             *p_v = ( *p_v * i_ao * (255 - i_an) / 255 + i_v * i_an ) / *p_a;
871         }
872     }
873 }
874
875 static void FillRGBAPicture( picture_t *p_picture,
876                              int i_a, int i_r, int i_g, int i_b )
877 {
878     for( int dy = 0; dy < p_picture->p[0].i_visible_lines; dy++ )
879     {
880         for( int dx = 0; dx < p_picture->p[0].i_visible_pitch; dx += 4 )
881         {
882             uint8_t *p_rgba = &p_picture->p->p_pixels[dy * p_picture->p->i_pitch + dx];
883             p_rgba[0] = i_r;
884             p_rgba[1] = i_g;
885             p_rgba[2] = i_b;
886             p_rgba[3] = i_a;
887         }
888     }
889 }
890
891 static inline void BlendRGBAPixel( picture_t *p_picture,
892                                    int i_picture_x, int i_picture_y,
893                                    int i_a, int i_r, int i_g, int i_b,
894                                    int i_alpha )
895 {
896     int i_an = i_a * i_alpha / 255;
897
898     uint8_t *p_rgba = &p_picture->p->p_pixels[i_picture_y * p_picture->p->i_pitch + 4 * i_picture_x];
899
900     int i_ao = p_rgba[3];
901     if( i_ao == 0 )
902     {
903         p_rgba[0] = i_r;
904         p_rgba[1] = i_g;
905         p_rgba[2] = i_b;
906         p_rgba[3] = i_an;
907     }
908     else
909     {
910         p_rgba[3] = 255 - (255 - p_rgba[3]) * (255 - i_an) / 255;
911         if( p_rgba[3] != 0 )
912         {
913             p_rgba[0] = ( p_rgba[0] * i_ao * (255 - i_an) / 255 + i_r * i_an ) / p_rgba[3];
914             p_rgba[1] = ( p_rgba[1] * i_ao * (255 - i_an) / 255 + i_g * i_an ) / p_rgba[3];
915             p_rgba[2] = ( p_rgba[2] * i_ao * (255 - i_an) / 255 + i_b * i_an ) / p_rgba[3];
916         }
917     }
918 }
919
920 static inline void BlendAXYZGlyph( picture_t *p_picture,
921                                    int i_picture_x, int i_picture_y,
922                                    int i_a, int i_x, int i_y, int i_z,
923                                    FT_BitmapGlyph p_glyph,
924                                    void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
925
926 {
927     for( int dy = 0; dy < p_glyph->bitmap.rows; dy++ )
928     {
929         for( int dx = 0; dx < p_glyph->bitmap.width; dx++ )
930             BlendPixel( p_picture, i_picture_x + dx, i_picture_y + dy,
931                         i_a, i_x, i_y, i_z,
932                         p_glyph->bitmap.buffer[dy * p_glyph->bitmap.width + dx] );
933     }
934 }
935
936 static inline void BlendAXYZLine( picture_t *p_picture,
937                                   int i_picture_x, int i_picture_y,
938                                   int i_a, int i_x, int i_y, int i_z,
939                                   const line_character_t *p_current,
940                                   const line_character_t *p_next,
941                                   void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
942 {
943     int i_line_width = p_current->p_glyph->bitmap.width;
944     if( p_next )
945         i_line_width = p_next->p_glyph->left - p_current->p_glyph->left;
946
947     for( int dx = 0; dx < i_line_width; dx++ )
948     {
949         for( int dy = 0; dy < p_current->i_line_thickness; dy++ )
950             BlendPixel( p_picture,
951                         i_picture_x + dx,
952                         i_picture_y + p_current->i_line_offset + dy,
953                         i_a, i_x, i_y, i_z, 0xff );
954     }
955 }
956
957 static inline int RenderAXYZ( filter_t *p_filter,
958                               subpicture_region_t *p_region,
959                               line_desc_t *p_line_head,
960                               FT_BBox *p_bbox,
961                               int i_margin,
962                               vlc_fourcc_t i_chroma,
963                               void (*ExtractComponents)( uint32_t, uint8_t *, uint8_t *, uint8_t * ),
964                               void (*FillPicture)( picture_t *p_picture, int, int, int, int ),
965                               void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
966 {
967     filter_sys_t *p_sys = p_filter->p_sys;
968
969     /* Create a new subpicture region */
970     const int i_text_width  = p_bbox->xMax - p_bbox->xMin;
971     const int i_text_height = p_bbox->yMax - p_bbox->yMin;
972     video_format_t fmt;
973     video_format_Init( &fmt, i_chroma );
974     fmt.i_width          =
975     fmt.i_visible_width  = i_text_width  + 2 * i_margin;
976     fmt.i_height         =
977     fmt.i_visible_height = i_text_height + 2 * i_margin;
978
979     picture_t *p_picture = p_region->p_picture = picture_NewFromFormat( &fmt );
980     if( !p_region->p_picture )
981         return VLC_EGENERIC;
982     p_region->fmt = fmt;
983
984     /* Initialize the picture background */
985     uint8_t i_a = p_sys->i_background_opacity;
986     uint8_t i_x, i_y, i_z;
987     ExtractComponents( p_sys->i_background_color, &i_x, &i_y, &i_z );
988
989     FillPicture( p_picture, i_a, i_x, i_y, i_z );
990
991     /* Render shadow then outline and then normal glyphs */
992     for( int g = 0; g < 3; g++ )
993     {
994         /* Render all lines */
995         for( line_desc_t *p_line = p_line_head; p_line != NULL; p_line = p_line->p_next )
996         {
997             int i_align_left = i_margin;
998             if( p_line->i_width < i_text_width )
999             {
1000                 /* Left offset to take into account alignment */
1001                 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1002                     i_align_left += ( i_text_width - p_line->i_width );
1003                 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1004                     i_align_left += ( i_text_width - p_line->i_width ) / 2;
1005             }
1006             int i_align_top = i_margin;
1007
1008             /* Render all glyphs and underline/strikethrough */
1009             for( int i = 0; i < p_line->i_character_count; i++ )
1010             {
1011                 const line_character_t *ch = &p_line->p_character[i];
1012                 FT_BitmapGlyph p_glyph = g == 0 ? ch->p_shadow : g == 1 ? ch->p_outline : ch->p_glyph;
1013                 if( !p_glyph )
1014                     continue;
1015
1016                 i_a = (ch->i_color >> 24) & 0xff;
1017                 uint32_t i_color;
1018                 switch (g) {
1019                 case 0:
1020                     i_a     = i_a * p_sys->i_shadow_opacity / 255;
1021                     i_color = p_sys->i_shadow_color;
1022                     break;
1023                 case 1:
1024                     i_a     = i_a * p_sys->i_outline_opacity / 255;
1025                     i_color = p_sys->i_outline_color;
1026                     break;
1027                 default:
1028                     i_color = ch->i_color;
1029                     break;
1030                 }
1031                 ExtractComponents( i_color, &i_x, &i_y, &i_z );
1032
1033                 int i_glyph_y = i_align_top  - p_glyph->top  + p_bbox->yMax + p_line->i_base_line;
1034                 int i_glyph_x = i_align_left + p_glyph->left - p_bbox->xMin;
1035
1036                 BlendAXYZGlyph( p_picture,
1037                                 i_glyph_x, i_glyph_y,
1038                                 i_a, i_x, i_y, i_z,
1039                                 p_glyph,
1040                                 BlendPixel );
1041
1042                 /* underline/strikethrough are only rendered for the normal glyph */
1043                 if( g == 2 && ch->i_line_thickness > 0 )
1044                     BlendAXYZLine( p_picture,
1045                                    i_glyph_x, i_glyph_y + p_glyph->top,
1046                                    i_a, i_x, i_y, i_z,
1047                                    &ch[0],
1048                                    i + 1 < p_line->i_character_count ? &ch[1] : NULL,
1049                                    BlendPixel );
1050             }
1051         }
1052     }
1053
1054     return VLC_SUCCESS;
1055 }
1056
1057 static text_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1058                                   uint32_t i_font_color, uint32_t i_karaoke_bg_color,
1059                                   int i_style_flags )
1060 {
1061     text_style_t *p_style = text_style_New();
1062     if( !p_style )
1063         return NULL;
1064
1065     p_style->psz_fontname = psz_fontname ? strdup( psz_fontname ) : NULL;
1066     p_style->i_font_size  = i_font_size;
1067     p_style->i_font_color = (i_font_color & 0x00ffffff) >>  0;
1068     p_style->i_font_alpha = (i_font_color & 0xff000000) >> 24;
1069     p_style->i_karaoke_background_color = (i_karaoke_bg_color & 0x00ffffff) >>  0;
1070     p_style->i_karaoke_background_alpha = (i_karaoke_bg_color & 0xff000000) >> 24;
1071     p_style->i_style_flags |= i_style_flags;
1072     return p_style;
1073 }
1074
1075 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1076                      uint32_t i_color, uint32_t i_karaoke_bg_color )
1077 {
1078     if( !p_font )
1079         return VLC_EGENERIC;
1080
1081     font_stack_t *p_new = malloc( sizeof(*p_new) );
1082     if( !p_new )
1083         return VLC_ENOMEM;
1084
1085     p_new->p_next = NULL;
1086
1087     if( psz_name )
1088         p_new->psz_name = strdup( psz_name );
1089     else
1090         p_new->psz_name = NULL;
1091
1092     p_new->i_size              = i_size;
1093     p_new->i_color             = i_color;
1094     p_new->i_karaoke_bg_color  = i_karaoke_bg_color;
1095
1096     if( !*p_font )
1097     {
1098         *p_font = p_new;
1099     }
1100     else
1101     {
1102         font_stack_t *p_last;
1103
1104         for( p_last = *p_font;
1105              p_last->p_next;
1106              p_last = p_last->p_next )
1107         ;
1108
1109         p_last->p_next = p_new;
1110     }
1111     return VLC_SUCCESS;
1112 }
1113
1114 static int PopFont( font_stack_t **p_font )
1115 {
1116     font_stack_t *p_last, *p_next_to_last;
1117
1118     if( !p_font || !*p_font )
1119         return VLC_EGENERIC;
1120
1121     p_next_to_last = NULL;
1122     for( p_last = *p_font;
1123          p_last->p_next;
1124          p_last = p_last->p_next )
1125     {
1126         p_next_to_last = p_last;
1127     }
1128
1129     if( p_next_to_last )
1130         p_next_to_last->p_next = NULL;
1131     else
1132         *p_font = NULL;
1133
1134     free( p_last->psz_name );
1135     free( p_last );
1136
1137     return VLC_SUCCESS;
1138 }
1139
1140 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1141                      uint32_t *i_color, uint32_t *i_karaoke_bg_color )
1142 {
1143     font_stack_t *p_last;
1144
1145     if( !p_font || !*p_font )
1146         return VLC_EGENERIC;
1147
1148     for( p_last=*p_font;
1149          p_last->p_next;
1150          p_last=p_last->p_next )
1151     ;
1152
1153     *psz_name            = p_last->psz_name;
1154     *i_size              = p_last->i_size;
1155     *i_color             = p_last->i_color;
1156     *i_karaoke_bg_color  = p_last->i_karaoke_bg_color;
1157
1158     return VLC_SUCCESS;
1159 }
1160
1161 static const struct {
1162     const char *psz_name;
1163     uint32_t   i_value;
1164 } p_html_colors[] = {
1165     /* Official html colors */
1166     { "Aqua",    0x00FFFF },
1167     { "Black",   0x000000 },
1168     { "Blue",    0x0000FF },
1169     { "Fuchsia", 0xFF00FF },
1170     { "Gray",    0x808080 },
1171     { "Green",   0x008000 },
1172     { "Lime",    0x00FF00 },
1173     { "Maroon",  0x800000 },
1174     { "Navy",    0x000080 },
1175     { "Olive",   0x808000 },
1176     { "Purple",  0x800080 },
1177     { "Red",     0xFF0000 },
1178     { "Silver",  0xC0C0C0 },
1179     { "Teal",    0x008080 },
1180     { "White",   0xFFFFFF },
1181     { "Yellow",  0xFFFF00 },
1182
1183     /* Common ones */
1184     { "AliceBlue", 0xF0F8FF },
1185     { "AntiqueWhite", 0xFAEBD7 },
1186     { "Aqua", 0x00FFFF },
1187     { "Aquamarine", 0x7FFFD4 },
1188     { "Azure", 0xF0FFFF },
1189     { "Beige", 0xF5F5DC },
1190     { "Bisque", 0xFFE4C4 },
1191     { "Black", 0x000000 },
1192     { "BlanchedAlmond", 0xFFEBCD },
1193     { "Blue", 0x0000FF },
1194     { "BlueViolet", 0x8A2BE2 },
1195     { "Brown", 0xA52A2A },
1196     { "BurlyWood", 0xDEB887 },
1197     { "CadetBlue", 0x5F9EA0 },
1198     { "Chartreuse", 0x7FFF00 },
1199     { "Chocolate", 0xD2691E },
1200     { "Coral", 0xFF7F50 },
1201     { "CornflowerBlue", 0x6495ED },
1202     { "Cornsilk", 0xFFF8DC },
1203     { "Crimson", 0xDC143C },
1204     { "Cyan", 0x00FFFF },
1205     { "DarkBlue", 0x00008B },
1206     { "DarkCyan", 0x008B8B },
1207     { "DarkGoldenRod", 0xB8860B },
1208     { "DarkGray", 0xA9A9A9 },
1209     { "DarkGrey", 0xA9A9A9 },
1210     { "DarkGreen", 0x006400 },
1211     { "DarkKhaki", 0xBDB76B },
1212     { "DarkMagenta", 0x8B008B },
1213     { "DarkOliveGreen", 0x556B2F },
1214     { "Darkorange", 0xFF8C00 },
1215     { "DarkOrchid", 0x9932CC },
1216     { "DarkRed", 0x8B0000 },
1217     { "DarkSalmon", 0xE9967A },
1218     { "DarkSeaGreen", 0x8FBC8F },
1219     { "DarkSlateBlue", 0x483D8B },
1220     { "DarkSlateGray", 0x2F4F4F },
1221     { "DarkSlateGrey", 0x2F4F4F },
1222     { "DarkTurquoise", 0x00CED1 },
1223     { "DarkViolet", 0x9400D3 },
1224     { "DeepPink", 0xFF1493 },
1225     { "DeepSkyBlue", 0x00BFFF },
1226     { "DimGray", 0x696969 },
1227     { "DimGrey", 0x696969 },
1228     { "DodgerBlue", 0x1E90FF },
1229     { "FireBrick", 0xB22222 },
1230     { "FloralWhite", 0xFFFAF0 },
1231     { "ForestGreen", 0x228B22 },
1232     { "Fuchsia", 0xFF00FF },
1233     { "Gainsboro", 0xDCDCDC },
1234     { "GhostWhite", 0xF8F8FF },
1235     { "Gold", 0xFFD700 },
1236     { "GoldenRod", 0xDAA520 },
1237     { "Gray", 0x808080 },
1238     { "Grey", 0x808080 },
1239     { "Green", 0x008000 },
1240     { "GreenYellow", 0xADFF2F },
1241     { "HoneyDew", 0xF0FFF0 },
1242     { "HotPink", 0xFF69B4 },
1243     { "IndianRed", 0xCD5C5C },
1244     { "Indigo", 0x4B0082 },
1245     { "Ivory", 0xFFFFF0 },
1246     { "Khaki", 0xF0E68C },
1247     { "Lavender", 0xE6E6FA },
1248     { "LavenderBlush", 0xFFF0F5 },
1249     { "LawnGreen", 0x7CFC00 },
1250     { "LemonChiffon", 0xFFFACD },
1251     { "LightBlue", 0xADD8E6 },
1252     { "LightCoral", 0xF08080 },
1253     { "LightCyan", 0xE0FFFF },
1254     { "LightGoldenRodYellow", 0xFAFAD2 },
1255     { "LightGray", 0xD3D3D3 },
1256     { "LightGrey", 0xD3D3D3 },
1257     { "LightGreen", 0x90EE90 },
1258     { "LightPink", 0xFFB6C1 },
1259     { "LightSalmon", 0xFFA07A },
1260     { "LightSeaGreen", 0x20B2AA },
1261     { "LightSkyBlue", 0x87CEFA },
1262     { "LightSlateGray", 0x778899 },
1263     { "LightSlateGrey", 0x778899 },
1264     { "LightSteelBlue", 0xB0C4DE },
1265     { "LightYellow", 0xFFFFE0 },
1266     { "Lime", 0x00FF00 },
1267     { "LimeGreen", 0x32CD32 },
1268     { "Linen", 0xFAF0E6 },
1269     { "Magenta", 0xFF00FF },
1270     { "Maroon", 0x800000 },
1271     { "MediumAquaMarine", 0x66CDAA },
1272     { "MediumBlue", 0x0000CD },
1273     { "MediumOrchid", 0xBA55D3 },
1274     { "MediumPurple", 0x9370D8 },
1275     { "MediumSeaGreen", 0x3CB371 },
1276     { "MediumSlateBlue", 0x7B68EE },
1277     { "MediumSpringGreen", 0x00FA9A },
1278     { "MediumTurquoise", 0x48D1CC },
1279     { "MediumVioletRed", 0xC71585 },
1280     { "MidnightBlue", 0x191970 },
1281     { "MintCream", 0xF5FFFA },
1282     { "MistyRose", 0xFFE4E1 },
1283     { "Moccasin", 0xFFE4B5 },
1284     { "NavajoWhite", 0xFFDEAD },
1285     { "Navy", 0x000080 },
1286     { "OldLace", 0xFDF5E6 },
1287     { "Olive", 0x808000 },
1288     { "OliveDrab", 0x6B8E23 },
1289     { "Orange", 0xFFA500 },
1290     { "OrangeRed", 0xFF4500 },
1291     { "Orchid", 0xDA70D6 },
1292     { "PaleGoldenRod", 0xEEE8AA },
1293     { "PaleGreen", 0x98FB98 },
1294     { "PaleTurquoise", 0xAFEEEE },
1295     { "PaleVioletRed", 0xD87093 },
1296     { "PapayaWhip", 0xFFEFD5 },
1297     { "PeachPuff", 0xFFDAB9 },
1298     { "Peru", 0xCD853F },
1299     { "Pink", 0xFFC0CB },
1300     { "Plum", 0xDDA0DD },
1301     { "PowderBlue", 0xB0E0E6 },
1302     { "Purple", 0x800080 },
1303     { "Red", 0xFF0000 },
1304     { "RosyBrown", 0xBC8F8F },
1305     { "RoyalBlue", 0x4169E1 },
1306     { "SaddleBrown", 0x8B4513 },
1307     { "Salmon", 0xFA8072 },
1308     { "SandyBrown", 0xF4A460 },
1309     { "SeaGreen", 0x2E8B57 },
1310     { "SeaShell", 0xFFF5EE },
1311     { "Sienna", 0xA0522D },
1312     { "Silver", 0xC0C0C0 },
1313     { "SkyBlue", 0x87CEEB },
1314     { "SlateBlue", 0x6A5ACD },
1315     { "SlateGray", 0x708090 },
1316     { "SlateGrey", 0x708090 },
1317     { "Snow", 0xFFFAFA },
1318     { "SpringGreen", 0x00FF7F },
1319     { "SteelBlue", 0x4682B4 },
1320     { "Tan", 0xD2B48C },
1321     { "Teal", 0x008080 },
1322     { "Thistle", 0xD8BFD8 },
1323     { "Tomato", 0xFF6347 },
1324     { "Turquoise", 0x40E0D0 },
1325     { "Violet", 0xEE82EE },
1326     { "Wheat", 0xF5DEB3 },
1327     { "White", 0xFFFFFF },
1328     { "WhiteSmoke", 0xF5F5F5 },
1329     { "Yellow", 0xFFFF00 },
1330     { "YellowGreen", 0x9ACD32 },
1331
1332     { NULL, 0 }
1333 };
1334
1335 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1336                                  font_stack_t **p_fonts )
1337 {
1338     int        rv;
1339     char      *psz_fontname = NULL;
1340     uint32_t   i_font_color = 0xffffff;
1341     int        i_font_alpha = 255;
1342     uint32_t   i_karaoke_bg_color = 0x00ffffff;
1343     int        i_font_size  = 24;
1344
1345     /* Default all attributes to the top font in the stack -- in case not
1346      * all attributes are specified in the sub-font
1347      */
1348     if( VLC_SUCCESS == PeekFont( p_fonts,
1349                                  &psz_fontname,
1350                                  &i_font_size,
1351                                  &i_font_color,
1352                                  &i_karaoke_bg_color ))
1353     {
1354         psz_fontname = strdup( psz_fontname );
1355         i_font_size = i_font_size;
1356     }
1357     i_font_alpha = (i_font_color >> 24) & 0xff;
1358     i_font_color &= 0x00ffffff;
1359
1360     const char *name, *value;
1361     while( (name = xml_ReaderNextAttr( p_xml_reader, &value )) != NULL )
1362     {
1363         if( !strcasecmp( "face", name ) )
1364         {
1365             free( psz_fontname );
1366             psz_fontname = strdup( value );
1367         }
1368         else if( !strcasecmp( "size", name ) )
1369         {
1370             if( ( *value == '+' ) || ( *value == '-' ) )
1371             {
1372                 int i_value = atoi( value );
1373
1374                 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
1375                     i_font_size += ( i_value * i_font_size ) / 10;
1376                 else if( i_value < -5 )
1377                     i_font_size = - i_value;
1378                 else if( i_value > 5 )
1379                     i_font_size = i_value;
1380             }
1381             else
1382                 i_font_size = atoi( value );
1383         }
1384         else if( !strcasecmp( "color", name ) )
1385         {
1386             if( value[0] == '#' )
1387             {
1388                 i_font_color = strtol( value + 1, NULL, 16 );
1389                 i_font_color &= 0x00ffffff;
1390             }
1391             else
1392             {
1393                 char *end;
1394                 uint32_t i_value = strtol( value, &end, 16 );
1395                 if( *end == '\0' || *end == ' ' )
1396                     i_font_color = i_value & 0x00ffffff;
1397
1398                 for( int i = 0; p_html_colors[i].psz_name != NULL; i++ )
1399                 {
1400                     if( !strncasecmp( value, p_html_colors[i].psz_name, strlen(p_html_colors[i].psz_name) ) )
1401                     {
1402                         i_font_color = p_html_colors[i].i_value;
1403                         break;
1404                     }
1405                 }
1406             }
1407         }
1408         else if( !strcasecmp( "alpha", name ) && ( value[0] == '#' ) )
1409         {
1410             i_font_alpha = strtol( value + 1, NULL, 16 );
1411             i_font_alpha &= 0xff;
1412         }
1413     }
1414     rv = PushFont( p_fonts,
1415                    psz_fontname,
1416                    i_font_size,
1417                    (i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24),
1418                    i_karaoke_bg_color );
1419
1420     free( psz_fontname );
1421
1422     return rv;
1423 }
1424
1425 /* Turn any multiple-whitespaces into single spaces */
1426 static void HandleWhiteSpace( char *psz_node )
1427 {
1428     char *s = strpbrk( psz_node, "\t\r\n " );
1429     while( s )
1430     {
1431         int i_whitespace = strspn( s, "\t\r\n " );
1432
1433         if( i_whitespace > 1 )
1434             memmove( &s[1],
1435                      &s[i_whitespace],
1436                      strlen( s ) - i_whitespace + 1 );
1437         *s++ = ' ';
1438
1439         s = strpbrk( s, "\t\r\n " );
1440     }
1441 }
1442
1443
1444 static text_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1445                                             font_stack_t **p_fonts,
1446                                             int i_style_flags )
1447 {
1448     char       *psz_fontname = NULL;
1449     uint32_t    i_font_color = p_sys->i_font_color & 0x00ffffff;
1450     uint32_t    i_karaoke_bg_color = i_font_color;
1451     int         i_font_size  = p_sys->i_font_size;
1452
1453     if( PeekFont( p_fonts, &psz_fontname, &i_font_size,
1454                   &i_font_color, &i_karaoke_bg_color ) )
1455         return NULL;
1456
1457     return CreateStyle( psz_fontname, i_font_size, i_font_color,
1458                         i_karaoke_bg_color,
1459                         i_style_flags );
1460 }
1461
1462 static unsigned SetupText( filter_t *p_filter,
1463                            uni_char_t *psz_text_out,
1464                            text_style_t **pp_styles,
1465                            uint32_t *pi_k_dates,
1466
1467                            const char *psz_text_in,
1468                            text_style_t *p_style,
1469                            uint32_t i_k_date )
1470 {
1471     size_t i_string_length;
1472
1473     size_t i_string_bytes;
1474     uni_char_t *psz_tmp = ToCharset( FREETYPE_TO_UCS, psz_text_in, &i_string_bytes );
1475     if( psz_tmp )
1476     {
1477         memcpy( psz_text_out, psz_tmp, i_string_bytes );
1478         i_string_length = i_string_bytes / sizeof( *psz_tmp );
1479         free( psz_tmp );
1480     }
1481     else
1482     {
1483         msg_Warn( p_filter, "failed to convert string to unicode (%m)" );
1484         i_string_length = 0;
1485     }
1486
1487     if( i_string_length > 0 )
1488     {
1489         for( unsigned i = 0; i < i_string_length; i++ )
1490             pp_styles[i] = p_style;
1491     }
1492     else
1493     {
1494         text_style_Delete( p_style );
1495     }
1496     if( i_string_length > 0 && pi_k_dates )
1497     {
1498         for( unsigned i = 0; i < i_string_length; i++ )
1499             pi_k_dates[i] = i_k_date;
1500     }
1501     return i_string_length;
1502 }
1503
1504 static int ProcessNodes( filter_t *p_filter,
1505                          uni_char_t *psz_text,
1506                          text_style_t **pp_styles,
1507                          uint32_t *pi_k_dates,
1508                          int *pi_len,
1509                          xml_reader_t *p_xml_reader,
1510                          text_style_t *p_font_style )
1511 {
1512     int           rv      = VLC_SUCCESS;
1513     filter_sys_t *p_sys   = p_filter->p_sys;
1514     int i_text_length     = 0;
1515     font_stack_t *p_fonts = NULL;
1516     uint32_t i_k_date     = 0;
1517
1518     int i_style_flags = 0;
1519
1520     if( p_font_style )
1521     {
1522         rv = PushFont( &p_fonts,
1523                p_font_style->psz_fontname,
1524                p_font_style->i_font_size > 0 ? p_font_style->i_font_size
1525                                              : p_sys->i_font_size,
1526                (p_font_style->i_font_color & 0xffffff) |
1527                    ((p_font_style->i_font_alpha & 0xff) << 24),
1528                (p_font_style->i_karaoke_background_color & 0xffffff) |
1529                    ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
1530
1531         i_style_flags = p_font_style->i_style_flags & (STYLE_BOLD |
1532                                                        STYLE_ITALIC |
1533                                                        STYLE_UNDERLINE |
1534                                                        STYLE_STRIKEOUT);
1535     }
1536 #ifdef HAVE_STYLES
1537     else
1538     {
1539         rv = PushFont( &p_fonts,
1540                        p_sys->psz_fontfamily,
1541                        p_sys->i_font_size,
1542                        (p_sys->i_font_color & 0xffffff) |
1543                           ((p_sys->i_font_opacity & 0xff) << 24),
1544                        0x00ffffff );
1545     }
1546 #endif
1547     if( p_sys->b_font_bold )
1548         i_style_flags |= STYLE_BOLD;
1549
1550     if( rv != VLC_SUCCESS )
1551         return rv;
1552
1553     const char *node;
1554     int type;
1555
1556     while ( (type = xml_ReaderNextNode( p_xml_reader, &node )) > 0 )
1557     {
1558         switch ( type )
1559         {
1560             case XML_READER_ENDELEM:
1561                 if( !strcasecmp( "font", node ) )
1562                     PopFont( &p_fonts );
1563                 else if( !strcasecmp( "b", node ) )
1564                     i_style_flags &= ~STYLE_BOLD;
1565                else if( !strcasecmp( "i", node ) )
1566                     i_style_flags &= ~STYLE_ITALIC;
1567                 else if( !strcasecmp( "u", node ) )
1568                     i_style_flags &= ~STYLE_UNDERLINE;
1569                 else if( !strcasecmp( "s", node ) )
1570                     i_style_flags &= ~STYLE_STRIKEOUT;
1571                 break;
1572
1573             case XML_READER_STARTELEM:
1574                 if( !strcasecmp( "font", node ) )
1575                     HandleFontAttributes( p_xml_reader, &p_fonts );
1576                 else if( !strcasecmp( "b", node ) )
1577                     i_style_flags |= STYLE_BOLD;
1578                 else if( !strcasecmp( "i", node ) )
1579                     i_style_flags |= STYLE_ITALIC;
1580                 else if( !strcasecmp( "u", node ) )
1581                     i_style_flags |= STYLE_UNDERLINE;
1582                 else if( !strcasecmp( "s", node ) )
1583                     i_style_flags |= STYLE_STRIKEOUT;
1584                 else if( !strcasecmp( "br", node ) )
1585                 {
1586                     i_text_length += SetupText( p_filter,
1587                                                 &psz_text[i_text_length],
1588                                                 &pp_styles[i_text_length],
1589                                                 pi_k_dates ? &pi_k_dates[i_text_length] : NULL,
1590                                                 "\n",
1591                                                 GetStyleFromFontStack( p_sys,
1592                                                                        &p_fonts,
1593                                                                        i_style_flags ),
1594                                                 i_k_date );
1595                 }
1596                 else if( !strcasecmp( "k", node ) )
1597                 {
1598                     /* Karaoke tags */
1599                     const char *name, *value;
1600                     while( (name = xml_ReaderNextAttr( p_xml_reader, &value )) != NULL )
1601                     {
1602                         if( !strcasecmp( "t", name ) && value )
1603                             i_k_date += atoi( value );
1604                     }
1605                 }
1606                 break;
1607
1608             case XML_READER_TEXT:
1609             {
1610                 char *psz_node = strdup( node );
1611                 if( unlikely(!psz_node) )
1612                     break;
1613
1614                 HandleWhiteSpace( psz_node );
1615                 resolve_xml_special_chars( psz_node );
1616
1617                 i_text_length += SetupText( p_filter,
1618                                             &psz_text[i_text_length],
1619                                             &pp_styles[i_text_length],
1620                                             pi_k_dates ? &pi_k_dates[i_text_length] : NULL,
1621                                             psz_node,
1622                                             GetStyleFromFontStack( p_sys,
1623                                                                    &p_fonts,
1624                                                                    i_style_flags ),
1625                                             i_k_date );
1626                 free( psz_node );
1627                 break;
1628             }
1629         }
1630     }
1631
1632     *pi_len = i_text_length;
1633
1634     while( VLC_SUCCESS == PopFont( &p_fonts ) );
1635
1636     return VLC_SUCCESS;
1637 }
1638
1639 static void FreeLine( line_desc_t *p_line )
1640 {
1641     for( int i = 0; i < p_line->i_character_count; i++ )
1642     {
1643         line_character_t *ch = &p_line->p_character[i];
1644         FT_Done_Glyph( (FT_Glyph)ch->p_glyph );
1645         if( ch->p_outline )
1646             FT_Done_Glyph( (FT_Glyph)ch->p_outline );
1647         if( ch->p_shadow )
1648             FT_Done_Glyph( (FT_Glyph)ch->p_shadow );
1649     }
1650
1651     free( p_line->p_character );
1652     free( p_line );
1653 }
1654
1655 static void FreeLines( line_desc_t *p_lines )
1656 {
1657     for( line_desc_t *p_line = p_lines; p_line != NULL; )
1658     {
1659         line_desc_t *p_next = p_line->p_next;
1660         FreeLine( p_line );
1661         p_line = p_next;
1662     }
1663 }
1664
1665 static line_desc_t *NewLine( int i_count )
1666 {
1667     line_desc_t *p_line = malloc( sizeof(*p_line) );
1668
1669     if( !p_line )
1670         return NULL;
1671
1672     p_line->p_next = NULL;
1673     p_line->i_width = 0;
1674     p_line->i_base_line = 0;
1675     p_line->i_character_count = 0;
1676
1677     p_line->p_character = calloc( i_count, sizeof(*p_line->p_character) );
1678     if( !p_line->p_character )
1679     {
1680         free( p_line );
1681         return NULL;
1682     }
1683     return p_line;
1684 }
1685
1686 static FT_Face LoadEmbeddedFace( filter_sys_t *p_sys, const text_style_t *p_style )
1687 {
1688     for( int k = 0; k < p_sys->i_font_attachments; k++ )
1689     {
1690         input_attachment_t *p_attach   = p_sys->pp_font_attachments[k];
1691         int                 i_font_idx = 0;
1692         FT_Face             p_face = NULL;
1693
1694         while( 0 == FT_New_Memory_Face( p_sys->p_library,
1695                                         p_attach->p_data,
1696                                         p_attach->i_data,
1697                                         i_font_idx,
1698                                         &p_face ))
1699         {
1700             if( p_face )
1701             {
1702                 int i_style_received = ((p_face->style_flags & FT_STYLE_FLAG_BOLD)    ? STYLE_BOLD   : 0) |
1703                                        ((p_face->style_flags & FT_STYLE_FLAG_ITALIC ) ? STYLE_ITALIC : 0);
1704                 if( !strcasecmp( p_face->family_name, p_style->psz_fontname ) &&
1705                     (p_style->i_style_flags & (STYLE_BOLD | STYLE_ITALIC)) == i_style_received )
1706                     return p_face;
1707
1708                 FT_Done_Face( p_face );
1709             }
1710             i_font_idx++;
1711         }
1712     }
1713     return NULL;
1714 }
1715
1716 static FT_Face LoadFace( filter_t *p_filter,
1717                          const text_style_t *p_style )
1718 {
1719     filter_sys_t *p_sys = p_filter->p_sys;
1720
1721     /* Look for a match amongst our attachments first */
1722     FT_Face p_face = LoadEmbeddedFace( p_sys, p_style );
1723
1724     /* Load system wide font otheriwse */
1725     if( !p_face )
1726     {
1727         int  i_idx = 0;
1728         char *psz_fontfile;
1729 #ifdef HAVE_FONTCONFIG
1730         psz_fontfile = FontConfig_Select( NULL,
1731                                           p_style->psz_fontname,
1732                                           (p_style->i_style_flags & STYLE_BOLD) != 0,
1733                                           (p_style->i_style_flags & STYLE_ITALIC) != 0,
1734                                           -1,
1735                                           &i_idx );
1736 #elif defined( WIN32 )
1737         psz_fontfile = Win32_Select( p_filter,
1738                                     p_style->psz_fontname,
1739                                     (p_style->i_style_flags & STYLE_BOLD) != 0,
1740                                     (p_style->i_style_flags & STYLE_ITALIC) != 0,
1741                                     -1,
1742                                     &i_idx );
1743 #else
1744         psz_fontfile = NULL;
1745 #endif
1746         if( !psz_fontfile )
1747             return NULL;
1748
1749         if( *psz_fontfile == '\0' )
1750         {
1751             msg_Warn( p_filter,
1752                       "We were not able to find a matching font: \"%s\" (%s %s),"
1753                       " so using default font",
1754                       p_style->psz_fontname,
1755                       (p_style->i_style_flags & STYLE_BOLD)   ? "Bold" : "",
1756                       (p_style->i_style_flags & STYLE_ITALIC) ? "Italic" : "" );
1757             p_face = NULL;
1758         }
1759         else
1760         {
1761             if( FT_New_Face( p_sys->p_library, psz_fontfile, i_idx, &p_face ) )
1762                 p_face = NULL;
1763         }
1764         free( psz_fontfile );
1765     }
1766     if( !p_face )
1767         return NULL;
1768
1769     if( FT_Select_Charmap( p_face, ft_encoding_unicode ) )
1770     {
1771         /* We've loaded a font face which is unhelpful for actually
1772          * rendering text - fallback to the default one.
1773          */
1774         FT_Done_Face( p_face );
1775         return NULL;
1776     }
1777     return p_face;
1778 }
1779
1780 static bool FaceStyleEquals( const text_style_t *p_style1,
1781                              const text_style_t *p_style2 )
1782 {
1783     if( !p_style1 || !p_style2 )
1784         return false;
1785     if( p_style1 == p_style2 )
1786         return true;
1787
1788     const int i_style_mask = STYLE_BOLD | STYLE_ITALIC;
1789     return (p_style1->i_style_flags & i_style_mask) == (p_style2->i_style_flags & i_style_mask) &&
1790            !strcmp( p_style1->psz_fontname, p_style2->psz_fontname );
1791 }
1792
1793 static int GetGlyph( filter_t *p_filter,
1794                      FT_Glyph *pp_glyph,   FT_BBox *p_glyph_bbox,
1795                      FT_Glyph *pp_outline, FT_BBox *p_outline_bbox,
1796                      FT_Glyph *pp_shadow,  FT_BBox *p_shadow_bbox,
1797
1798                      FT_Face  p_face,
1799                      int i_glyph_index,
1800                      int i_style_flags,
1801                      FT_Vector *p_pen,
1802                      FT_Vector *p_pen_shadow )
1803 {
1804     if( FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_DEFAULT ) &&
1805         FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT ) )
1806     {
1807         msg_Err( p_filter, "unable to render text FT_Load_Glyph failed" );
1808         return VLC_EGENERIC;
1809     }
1810
1811     /* Do synthetic styling now that Freetype supports it;
1812      * ie. if the font we have loaded is NOT already in the
1813      * style that the tags want, then switch it on; if they
1814      * are then don't. */
1815     if ((i_style_flags & STYLE_BOLD) && !(p_face->style_flags & FT_STYLE_FLAG_BOLD))
1816         FT_GlyphSlot_Embolden( p_face->glyph );
1817     if ((i_style_flags & STYLE_ITALIC) && !(p_face->style_flags & FT_STYLE_FLAG_ITALIC))
1818         FT_GlyphSlot_Oblique( p_face->glyph );
1819
1820     FT_Glyph glyph;
1821     if( FT_Get_Glyph( p_face->glyph, &glyph ) )
1822     {
1823         msg_Err( p_filter, "unable to render text FT_Get_Glyph failed" );
1824         return VLC_EGENERIC;
1825     }
1826
1827     FT_Glyph outline = NULL;
1828     if( p_filter->p_sys->p_stroker )
1829     {
1830         outline = glyph;
1831         if( FT_Glyph_StrokeBorder( &outline, p_filter->p_sys->p_stroker, 0, 0 ) )
1832             outline = NULL;
1833     }
1834
1835     FT_Glyph shadow = NULL;
1836     if( p_filter->p_sys->i_shadow_opacity > 0 )
1837     {
1838         shadow = outline ? outline : glyph;
1839         if( FT_Glyph_To_Bitmap( &shadow, FT_RENDER_MODE_NORMAL, p_pen_shadow, 0  ) )
1840         {
1841             shadow = NULL;
1842         }
1843         else
1844         {
1845             FT_Glyph_Get_CBox( shadow, ft_glyph_bbox_pixels, p_shadow_bbox );
1846         }
1847     }
1848     *pp_shadow = shadow;
1849
1850     if( FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, p_pen, 1) )
1851     {
1852         FT_Done_Glyph( glyph );
1853         if( outline )
1854             FT_Done_Glyph( outline );
1855         if( shadow )
1856             FT_Done_Glyph( shadow );
1857         return VLC_EGENERIC;
1858     }
1859     FT_Glyph_Get_CBox( glyph, ft_glyph_bbox_pixels, p_glyph_bbox );
1860     *pp_glyph = glyph;
1861
1862     if( outline )
1863     {
1864         FT_Glyph_To_Bitmap( &outline, FT_RENDER_MODE_NORMAL, p_pen, 1 );
1865         FT_Glyph_Get_CBox( outline, ft_glyph_bbox_pixels, p_outline_bbox );
1866     }
1867     *pp_outline = outline;
1868
1869     return VLC_SUCCESS;
1870 }
1871
1872 static void FixGlyph( FT_Glyph glyph, FT_BBox *p_bbox, FT_Face face, const FT_Vector *p_pen )
1873 {
1874     FT_BitmapGlyph glyph_bmp = (FT_BitmapGlyph)glyph;
1875     if( p_bbox->xMin >= p_bbox->xMax )
1876     {
1877         p_bbox->xMin = FT_CEIL(p_pen->x);
1878         p_bbox->xMax = FT_CEIL(p_pen->x + face->glyph->advance.x);
1879         glyph_bmp->left = p_bbox->xMin;
1880     }
1881     if( p_bbox->yMin >= p_bbox->yMax )
1882     {
1883         p_bbox->yMax = FT_CEIL(p_pen->y);
1884         p_bbox->yMin = FT_CEIL(p_pen->y + face->glyph->advance.y);
1885         glyph_bmp->top  = p_bbox->yMax;
1886     }
1887 }
1888
1889 static void BBoxEnlarge( FT_BBox *p_max, const FT_BBox *p )
1890 {
1891     p_max->xMin = __MIN(p_max->xMin, p->xMin);
1892     p_max->yMin = __MIN(p_max->yMin, p->yMin);
1893     p_max->xMax = __MAX(p_max->xMax, p->xMax);
1894     p_max->yMax = __MAX(p_max->yMax, p->yMax);
1895 }
1896
1897 static int ProcessLines( filter_t *p_filter,
1898                          line_desc_t **pp_lines,
1899                          FT_BBox     *p_bbox,
1900                          int         *pi_max_face_height,
1901
1902                          uni_char_t *psz_text,
1903                          text_style_t **pp_styles,
1904                          uint32_t *pi_k_dates,
1905                          int i_len )
1906 {
1907     filter_sys_t   *p_sys = p_filter->p_sys;
1908     uni_char_t     *p_fribidi_string = NULL;
1909     text_style_t   **pp_fribidi_styles = NULL;
1910     int            *p_new_positions = NULL;
1911
1912 #if defined(HAVE_FRIBIDI)
1913     {
1914         int    *p_old_positions;
1915         int start_pos, pos = 0;
1916
1917         pp_fribidi_styles = calloc( i_len, sizeof(*pp_fribidi_styles) );
1918
1919         p_fribidi_string  = malloc( (i_len + 1) * sizeof(*p_fribidi_string) );
1920         p_old_positions   = malloc( (i_len + 1) * sizeof(*p_old_positions) );
1921         p_new_positions   = malloc( (i_len + 1) * sizeof(*p_new_positions) );
1922
1923         if( ! pp_fribidi_styles ||
1924             ! p_fribidi_string ||
1925             ! p_old_positions ||
1926             ! p_new_positions )
1927         {
1928             free( p_old_positions );
1929             free( p_new_positions );
1930             free( p_fribidi_string );
1931             free( pp_fribidi_styles );
1932             return VLC_ENOMEM;
1933         }
1934
1935         /* Do bidi conversion line-by-line */
1936         while(pos < i_len)
1937         {
1938             while(pos < i_len) {
1939                 if (psz_text[pos] != '\n')
1940                     break;
1941                 p_fribidi_string[pos] = psz_text[pos];
1942                 pp_fribidi_styles[pos] = pp_styles[pos];
1943                 p_new_positions[pos] = pos;
1944                 ++pos;
1945             }
1946             start_pos = pos;
1947             while(pos < i_len) {
1948                 if (psz_text[pos] == '\n')
1949                     break;
1950                 ++pos;
1951             }
1952             if (pos > start_pos)
1953             {
1954 #if (FRIBIDI_MINOR_VERSION < 19) && (FRIBIDI_MAJOR_VERSION == 0)
1955                 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1956 #else
1957                 FriBidiParType base_dir = FRIBIDI_PAR_LTR;
1958 #endif
1959                 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1960                         pos - start_pos, &base_dir,
1961                         (FriBidiChar*)p_fribidi_string + start_pos,
1962                         p_new_positions + start_pos,
1963                         p_old_positions,
1964                         NULL );
1965                 for( int j = start_pos; j < pos; j++ )
1966                 {
1967                     pp_fribidi_styles[ j ] = pp_styles[ start_pos + p_old_positions[j - start_pos] ];
1968                     p_new_positions[ j ] += start_pos;
1969                 }
1970             }
1971         }
1972         p_fribidi_string[ i_len ] = 0;
1973         free( p_old_positions );
1974
1975         pp_styles = pp_fribidi_styles;
1976         psz_text = p_fribidi_string;
1977     }
1978 #endif
1979     /* Work out the karaoke */
1980     uint8_t *pi_karaoke_bar = NULL;
1981     if( pi_k_dates )
1982     {
1983         pi_karaoke_bar = malloc( i_len * sizeof(*pi_karaoke_bar));
1984         if( pi_karaoke_bar )
1985         {
1986             int64_t i_elapsed  = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1987             for( int i = 0; i < i_len; i++ )
1988             {
1989                 unsigned i_bar = p_new_positions ? p_new_positions[i] : i;
1990                 pi_karaoke_bar[i_bar] = pi_k_dates[i] >= i_elapsed;
1991             }
1992         }
1993     }
1994     free( p_new_positions );
1995
1996     *pi_max_face_height = 0;
1997     *pp_lines = NULL;
1998     line_desc_t **pp_line_next = pp_lines;
1999
2000     FT_BBox bbox = {
2001         .xMin = INT_MAX,
2002         .yMin = INT_MAX,
2003         .xMax = INT_MIN,
2004         .yMax = INT_MIN,
2005     };
2006     int i_face_height_previous = 0;
2007     int i_base_line = 0;
2008     const text_style_t *p_previous_style = NULL;
2009     FT_Face p_face = NULL;
2010     for( int i_start = 0; i_start < i_len; )
2011     {
2012         /* Compute the length of the current text line */
2013         int i_length = 0;
2014         while( i_start + i_length < i_len && psz_text[i_start + i_length] != '\n' )
2015             i_length++;
2016
2017         /* Render the text line (or the begining if too long) into 0 or 1 glyph line */
2018         line_desc_t *p_line = i_length > 0 ? NewLine( i_length ) : NULL;
2019         int i_index = i_start;
2020         FT_Vector pen = {
2021             .x = 0,
2022             .y = 0,
2023         };
2024         int i_face_height = 0;
2025         FT_BBox line_bbox = {
2026             .xMin = INT_MAX,
2027             .yMin = INT_MAX,
2028             .xMax = INT_MIN,
2029             .yMax = INT_MIN,
2030         };
2031         int i_ul_offset = 0;
2032         int i_ul_thickness = 0;
2033         typedef struct {
2034             int       i_index;
2035             FT_Vector pen;
2036             FT_BBox   line_bbox;
2037             int i_face_height;
2038             int i_ul_offset;
2039             int i_ul_thickness;
2040         } break_point_t;
2041         break_point_t break_point;
2042         break_point_t break_point_fallback;
2043
2044 #define SAVE_BP(dst) do { \
2045         dst.i_index = i_index; \
2046         dst.pen = pen; \
2047         dst.line_bbox = line_bbox; \
2048         dst.i_face_height = i_face_height; \
2049         dst.i_ul_offset = i_ul_offset; \
2050         dst.i_ul_thickness = i_ul_thickness; \
2051     } while(0)
2052
2053         SAVE_BP( break_point );
2054         SAVE_BP( break_point_fallback );
2055
2056         while( i_index < i_start + i_length )
2057         {
2058             /* Split by common FT_Face + Size */
2059             const text_style_t *p_current_style = pp_styles[i_index];
2060             int i_part_length = 0;
2061             while( i_index + i_part_length < i_start + i_length )
2062             {
2063                 const text_style_t *p_style = pp_styles[i_index + i_part_length];
2064                 if( !FaceStyleEquals( p_style, p_current_style ) ||
2065                     p_style->i_font_size != p_current_style->i_font_size )
2066                     break;
2067                 i_part_length++;
2068             }
2069
2070             /* (Re)load/reconfigure the face if needed */
2071             if( !FaceStyleEquals( p_current_style, p_previous_style ) )
2072             {
2073                 if( p_face )
2074                     FT_Done_Face( p_face );
2075                 p_previous_style = NULL;
2076
2077                 p_face = LoadFace( p_filter, p_current_style );
2078             }
2079             FT_Face p_current_face = p_face ? p_face : p_sys->p_face;
2080             if( !p_previous_style || p_previous_style->i_font_size != p_current_style->i_font_size )
2081             {
2082                 if( FT_Set_Pixel_Sizes( p_current_face, 0, p_current_style->i_font_size ) )
2083                     msg_Err( p_filter, "Failed to set font size to %d", p_current_style->i_font_size );
2084                 if( p_sys->p_stroker )
2085                 {
2086                     int i_radius = (p_current_style->i_font_size << 6) * p_sys->f_outline_thickness;
2087                     FT_Stroker_Set( p_sys->p_stroker,
2088                                     i_radius,
2089                                     FT_STROKER_LINECAP_ROUND,
2090                                     FT_STROKER_LINEJOIN_ROUND, 0 );
2091                 }
2092             }
2093             p_previous_style = p_current_style;
2094
2095             i_face_height = __MAX(i_face_height, FT_CEIL(FT_MulFix(p_current_face->height,
2096                                                                    p_current_face->size->metrics.y_scale)));
2097
2098             /* Render the part */
2099             bool b_break_line = false;
2100             int i_glyph_last = 0;
2101             while( i_part_length > 0 )
2102             {
2103                 const text_style_t *p_glyph_style = pp_styles[i_index];
2104                 uni_char_t character = psz_text[i_index];
2105                 int i_glyph_index = FT_Get_Char_Index( p_current_face, character );
2106
2107                 /* Get kerning vector */
2108                 FT_Vector kerning = { .x = 0, .y = 0 };
2109                 if( FT_HAS_KERNING( p_current_face ) && i_glyph_last != 0 && i_glyph_index != 0 )
2110                     FT_Get_Kerning( p_current_face, i_glyph_last, i_glyph_index, ft_kerning_default, &kerning );
2111
2112                 /* Get the glyph bitmap and its bounding box and all the associated properties */
2113                 FT_Vector pen_new = {
2114                     .x = pen.x + kerning.x,
2115                     .y = pen.y + kerning.y,
2116                 };
2117                 FT_Vector pen_shadow_new = {
2118                     .x = pen_new.x + p_sys->f_shadow_vector_x * (p_current_style->i_font_size << 6),
2119                     .y = pen_new.y + p_sys->f_shadow_vector_y * (p_current_style->i_font_size << 6),
2120                 };
2121                 FT_Glyph glyph;
2122                 FT_BBox  glyph_bbox;
2123                 FT_Glyph outline;
2124                 FT_BBox  outline_bbox;
2125                 FT_Glyph shadow;
2126                 FT_BBox  shadow_bbox;
2127
2128                 if( GetGlyph( p_filter,
2129                               &glyph, &glyph_bbox,
2130                               &outline, &outline_bbox,
2131                               &shadow, &shadow_bbox,
2132                               p_current_face, i_glyph_index, p_glyph_style->i_style_flags,
2133                               &pen_new, &pen_shadow_new ) )
2134                     goto next;
2135
2136                 FixGlyph( glyph, &glyph_bbox, p_current_face, &pen_new );
2137                 if( outline )
2138                     FixGlyph( outline, &outline_bbox, p_current_face, &pen_new );
2139                 if( shadow )
2140                     FixGlyph( shadow, &shadow_bbox, p_current_face, &pen_shadow_new );
2141
2142                 /* FIXME and what about outline */
2143
2144                 bool     b_karaoke = pi_karaoke_bar && pi_karaoke_bar[i_index] != 0;
2145                 uint32_t i_color = b_karaoke ? (p_glyph_style->i_karaoke_background_color |
2146                                                 (p_glyph_style->i_karaoke_background_alpha << 24))
2147                                              : (p_glyph_style->i_font_color |
2148                                                 (p_glyph_style->i_font_alpha << 24));
2149                 int i_line_offset    = 0;
2150                 int i_line_thickness = 0;
2151                 if( p_glyph_style->i_style_flags & (STYLE_UNDERLINE | STYLE_STRIKEOUT) )
2152                 {
2153                     i_line_offset = abs( FT_FLOOR(FT_MulFix(p_current_face->underline_position,
2154                                                             p_current_face->size->metrics.y_scale)) );
2155
2156                     i_line_thickness = abs( FT_CEIL(FT_MulFix(p_current_face->underline_thickness,
2157                                                               p_current_face->size->metrics.y_scale)) );
2158
2159                     if( p_glyph_style->i_style_flags & STYLE_STRIKEOUT )
2160                     {
2161                         /* Move the baseline to make it strikethrough instead of
2162                          * underline. That means that strikethrough takes precedence
2163                          */
2164                         i_line_offset -= abs( FT_FLOOR(FT_MulFix(p_current_face->descender*2,
2165                                                                  p_current_face->size->metrics.y_scale)) );
2166                     }
2167                     else if( i_line_thickness > 0 )
2168                     {
2169                         glyph_bbox.yMin = __MIN( glyph_bbox.yMin, - i_line_offset - i_line_thickness );
2170
2171                         /* The real underline thickness and position are
2172                          * updated once the whole line has been parsed */
2173                         i_ul_offset = __MAX( i_ul_offset, i_line_offset );
2174                         i_ul_thickness = __MAX( i_ul_thickness, i_line_thickness );
2175                         i_line_thickness = -1;
2176                     }
2177                 }
2178                 FT_BBox line_bbox_new = line_bbox;
2179                 BBoxEnlarge( &line_bbox_new, &glyph_bbox );
2180                 if( outline )
2181                     BBoxEnlarge( &line_bbox_new, &outline_bbox );
2182                 if( shadow )
2183                     BBoxEnlarge( &line_bbox_new, &shadow_bbox );
2184
2185                 b_break_line = i_index > i_start &&
2186                                line_bbox_new.xMax - line_bbox_new.xMin >= (int)p_filter->fmt_out.video.i_visible_width;
2187                 if( b_break_line )
2188                 {
2189                     FT_Done_Glyph( glyph );
2190                     if( outline )
2191                         FT_Done_Glyph( outline );
2192                     if( shadow )
2193                         FT_Done_Glyph( shadow );
2194
2195                     break_point_t *p_bp = NULL;
2196                     if( break_point.i_index > i_start )
2197                         p_bp = &break_point;
2198                     else if( break_point_fallback.i_index > i_start )
2199                         p_bp = &break_point_fallback;
2200
2201                     if( p_bp )
2202                     {
2203                         msg_Dbg( p_filter, "Breaking line");
2204                         for( int i = p_bp->i_index; i < i_index; i++ )
2205                         {
2206                             line_character_t *ch = &p_line->p_character[i - i_start];
2207                             FT_Done_Glyph( (FT_Glyph)ch->p_glyph );
2208                             if( ch->p_outline )
2209                                 FT_Done_Glyph( (FT_Glyph)ch->p_outline );
2210                             if( ch->p_shadow )
2211                                 FT_Done_Glyph( (FT_Glyph)ch->p_shadow );
2212                         }
2213                         p_line->i_character_count = p_bp->i_index - i_start;
2214
2215                         i_index = p_bp->i_index;
2216                         pen = p_bp->pen;
2217                         line_bbox = p_bp->line_bbox;
2218                         i_face_height = p_bp->i_face_height;
2219                         i_ul_offset = p_bp->i_ul_offset;
2220                         i_ul_thickness = p_bp->i_ul_thickness;
2221                     }
2222                     else
2223                     {
2224                         msg_Err( p_filter, "Breaking unbreakable line");
2225                     }
2226                     break;
2227                 }
2228
2229                 assert( p_line->i_character_count == i_index - i_start);
2230                 p_line->p_character[p_line->i_character_count++] = (line_character_t){
2231                     .p_glyph = (FT_BitmapGlyph)glyph,
2232                     .p_outline = (FT_BitmapGlyph)outline,
2233                     .p_shadow = (FT_BitmapGlyph)shadow,
2234                     .i_color = i_color,
2235                     .i_line_offset = i_line_offset,
2236                     .i_line_thickness = i_line_thickness,
2237                 };
2238
2239                 pen.x = pen_new.x + p_current_face->glyph->advance.x;
2240                 pen.y = pen_new.y + p_current_face->glyph->advance.y;
2241                 line_bbox = line_bbox_new;
2242             next:
2243                 i_glyph_last = i_glyph_index;
2244                 i_part_length--;
2245                 i_index++;
2246
2247                 if( character == ' ' || character == '\t' )
2248                     SAVE_BP( break_point );
2249                 else if( character == 160 )
2250                     SAVE_BP( break_point_fallback );
2251             }
2252             if( b_break_line )
2253                 break;
2254         }
2255 #undef SAVE_BP
2256         /* Update our baseline */
2257         if( i_face_height_previous > 0 )
2258             i_base_line += __MAX(i_face_height, i_face_height_previous);
2259         if( i_face_height > 0 )
2260             i_face_height_previous = i_face_height;
2261
2262         /* Update the line bbox with the actual base line */
2263         if (line_bbox.yMax > line_bbox.yMin) {
2264             line_bbox.yMin -= i_base_line;
2265             line_bbox.yMax -= i_base_line;
2266         }
2267         BBoxEnlarge( &bbox, &line_bbox );
2268
2269         /* Terminate and append the line */
2270         if( p_line )
2271         {
2272             p_line->i_width  = __MAX(line_bbox.xMax - line_bbox.xMin, 0);
2273             p_line->i_base_line = i_base_line;
2274             if( i_ul_thickness > 0 )
2275             {
2276                 for( int i = 0; i < p_line->i_character_count; i++ )
2277                 {
2278                     line_character_t *ch = &p_line->p_character[i];
2279                     if( ch->i_line_thickness < 0 )
2280                     {
2281                         ch->i_line_offset    = i_ul_offset;
2282                         ch->i_line_thickness = i_ul_thickness;
2283                     }
2284                 }
2285             }
2286
2287             *pp_line_next = p_line;
2288             pp_line_next = &p_line->p_next;
2289         }
2290
2291         *pi_max_face_height = __MAX( *pi_max_face_height, i_face_height );
2292
2293         /* Skip what we have rendered and the line delimitor if present */
2294         i_start = i_index;
2295         if( i_start < i_len && psz_text[i_start] == '\n' )
2296             i_start++;
2297
2298         if( bbox.yMax - bbox.yMin >= (int)p_filter->fmt_out.video.i_visible_height )
2299         {
2300             msg_Err( p_filter, "Truncated too high subtitle" );
2301             break;
2302         }
2303     }
2304     if( p_face )
2305         FT_Done_Face( p_face );
2306
2307     free( pp_fribidi_styles );
2308     free( p_fribidi_string );
2309     free( pi_karaoke_bar );
2310
2311     *p_bbox = bbox;
2312     return VLC_SUCCESS;
2313 }
2314
2315 /**
2316  * This function renders a text subpicture region into another one.
2317  * It also calculates the size needed for this string, and renders the
2318  * needed glyphs into memory. It is used as pf_add_string callback in
2319  * the vout method by this module
2320  */
2321 static int RenderCommon( filter_t *p_filter, subpicture_region_t *p_region_out,
2322                          subpicture_region_t *p_region_in, bool b_html,
2323                          const vlc_fourcc_t *p_chroma_list )
2324 {
2325     filter_sys_t *p_sys = p_filter->p_sys;
2326
2327     if( !p_region_in )
2328         return VLC_EGENERIC;
2329     if( b_html && !p_region_in->psz_html )
2330         return VLC_EGENERIC;
2331     if( !b_html && !p_region_in->psz_text )
2332         return VLC_EGENERIC;
2333
2334     const size_t i_text_max = strlen( b_html ? p_region_in->psz_html
2335                                              : p_region_in->psz_text );
2336
2337     uni_char_t *psz_text = calloc( i_text_max, sizeof( *psz_text ) );
2338     text_style_t **pp_styles = calloc( i_text_max, sizeof( *pp_styles ) );
2339     if( !psz_text || !pp_styles )
2340     {
2341         free( psz_text );
2342         free( pp_styles );
2343         return VLC_EGENERIC;
2344     }
2345
2346     /* Reset the default fontsize in case screen metrics have changed */
2347     p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2348
2349     /* */
2350     int rv = VLC_SUCCESS;
2351     int i_text_length = 0;
2352     FT_BBox bbox;
2353     int i_max_face_height;
2354     line_desc_t *p_lines = NULL;
2355
2356     uint32_t *pi_k_durations   = NULL;
2357
2358 #ifdef HAVE_STYLES
2359     if( b_html )
2360     {
2361         stream_t *p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2362                                             (uint8_t *) p_region_in->psz_html,
2363                                             strlen( p_region_in->psz_html ),
2364                                             true );
2365         if( unlikely(p_sub == NULL) )
2366             return VLC_SUCCESS;
2367
2368         xml_reader_t *p_xml_reader = p_filter->p_sys->p_xml;
2369         if( !p_xml_reader )
2370             p_xml_reader = xml_ReaderCreate( p_filter, p_sub );
2371         else
2372             p_xml_reader = xml_ReaderReset( p_xml_reader, p_sub );
2373         p_filter->p_sys->p_xml = p_xml_reader;
2374
2375         if( !p_xml_reader )
2376             rv = VLC_EGENERIC;
2377
2378         if( !rv )
2379         {
2380             /* Look for Root Node */
2381             const char *node;
2382
2383             if( xml_ReaderNextNode( p_xml_reader, &node ) == XML_READER_STARTELEM )
2384             {
2385                 if( strcasecmp( "karaoke", node ) == 0 )
2386                 {
2387                     pi_k_durations = calloc( i_text_max, sizeof( *pi_k_durations ) );
2388                 }
2389                 else if( strcasecmp( "text", node ) != 0 )
2390                 {
2391                     /* Only text and karaoke tags are supported */
2392                     msg_Dbg( p_filter, "Unsupported top-level tag <%s> ignored.",
2393                              node );
2394                     rv = VLC_EGENERIC;
2395                 }
2396             }
2397             else
2398             {
2399                 msg_Err( p_filter, "Malformed HTML subtitle" );
2400                 rv = VLC_EGENERIC;
2401             }
2402         }
2403         if( !rv )
2404         {
2405             rv = ProcessNodes( p_filter,
2406                                psz_text, pp_styles, pi_k_durations, &i_text_length,
2407                                p_xml_reader, p_region_in->p_style );
2408         }
2409
2410         if( p_xml_reader )
2411             p_filter->p_sys->p_xml = xml_ReaderReset( p_xml_reader, NULL );
2412
2413         stream_Delete( p_sub );
2414     }
2415     else
2416 #endif
2417     {
2418         text_style_t *p_style;
2419         if( p_region_in->p_style )
2420             p_style = CreateStyle( p_region_in->p_style->psz_fontname,
2421                                    p_region_in->p_style->i_font_size > 0 ? p_region_in->p_style->i_font_size
2422                                                                          : p_sys->i_font_size,
2423                                    (p_region_in->p_style->i_font_color & 0xffffff) |
2424                                    ((p_region_in->p_style->i_font_alpha & 0xff) << 24),
2425                                    0x00ffffff,
2426                                    p_region_in->p_style->i_style_flags & (STYLE_BOLD |
2427                                                                           STYLE_ITALIC |
2428                                                                           STYLE_UNDERLINE |
2429                                                                           STYLE_STRIKEOUT) );
2430         else
2431             p_style = CreateStyle( p_sys->psz_fontfamily,
2432                                    p_sys->i_font_size,
2433                                    (p_sys->i_font_color & 0xffffff) |
2434                                    ((p_sys->i_font_opacity & 0xff) << 24),
2435                                    0x00ffffff, 0);
2436         if( p_sys->b_font_bold )
2437             p_style->i_style_flags |= STYLE_BOLD;
2438
2439         i_text_length = SetupText( p_filter,
2440                                    psz_text,
2441                                    pp_styles,
2442                                    NULL,
2443                                    p_region_in->psz_text, p_style, 0 );
2444     }
2445
2446     if( !rv && i_text_length > 0 )
2447     {
2448         rv = ProcessLines( p_filter,
2449                            &p_lines, &bbox, &i_max_face_height,
2450                            psz_text, pp_styles, pi_k_durations, i_text_length );
2451     }
2452
2453     p_region_out->i_x = p_region_in->i_x;
2454     p_region_out->i_y = p_region_in->i_y;
2455
2456     /* Don't attempt to render text that couldn't be layed out
2457      * properly. */
2458     if( !rv && i_text_length > 0 && bbox.xMin < bbox.xMax && bbox.yMin < bbox.yMax )
2459     {
2460         const vlc_fourcc_t p_chroma_list_yuvp[] = { VLC_CODEC_YUVP, 0 };
2461         const vlc_fourcc_t p_chroma_list_rgba[] = { VLC_CODEC_RGBA, 0 };
2462
2463         if( var_InheritBool( p_filter, "freetype-yuvp" ) )
2464             p_chroma_list = p_chroma_list_yuvp;
2465         else if( !p_chroma_list || *p_chroma_list == 0 )
2466             p_chroma_list = p_chroma_list_rgba;
2467
2468         const int i_margin = p_sys->i_background_opacity > 0 ? i_max_face_height / 4 : 0;
2469         for( const vlc_fourcc_t *p_chroma = p_chroma_list; *p_chroma != 0; p_chroma++ )
2470         {
2471             rv = VLC_EGENERIC;
2472             if( *p_chroma == VLC_CODEC_YUVP )
2473                 rv = RenderYUVP( p_filter, p_region_out, p_lines, &bbox );
2474             else if( *p_chroma == VLC_CODEC_YUVA )
2475                 rv = RenderAXYZ( p_filter, p_region_out, p_lines, &bbox, i_margin,
2476                                  VLC_CODEC_YUVA,
2477                                  YUVFromRGB,
2478                                  FillYUVAPicture,
2479                                  BlendYUVAPixel );
2480             else if( *p_chroma == VLC_CODEC_RGBA )
2481                 rv = RenderAXYZ( p_filter, p_region_out, p_lines, &bbox, i_margin,
2482                                  VLC_CODEC_RGBA,
2483                                  RGBFromRGB,
2484                                  FillRGBAPicture,
2485                                  BlendRGBAPixel );
2486             if( !rv )
2487                 break;
2488         }
2489
2490         /* With karaoke, we're going to have to render the text a number
2491          * of times to show the progress marker on the text.
2492          */
2493         if( pi_k_durations )
2494             var_SetBool( p_filter, "text-rerender", true );
2495     }
2496
2497     FreeLines( p_lines );
2498
2499     free( psz_text );
2500     for( int i = 0; i < i_text_length; i++ )
2501     {
2502         if( pp_styles[i] && ( i + 1 == i_text_length || pp_styles[i] != pp_styles[i + 1] ) )
2503             text_style_Delete( pp_styles[i] );
2504     }
2505     free( pp_styles );
2506     free( pi_k_durations );
2507
2508     return rv;
2509 }
2510
2511 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
2512                        subpicture_region_t *p_region_in,
2513                        const vlc_fourcc_t *p_chroma_list )
2514 {
2515     return RenderCommon( p_filter, p_region_out, p_region_in, false, p_chroma_list );
2516 }
2517
2518 #ifdef HAVE_STYLES
2519
2520 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2521                        subpicture_region_t *p_region_in,
2522                        const vlc_fourcc_t *p_chroma_list )
2523 {
2524     return RenderCommon( p_filter, p_region_out, p_region_in, true, p_chroma_list );
2525 }
2526
2527 #endif
2528
2529 /*****************************************************************************
2530  * Create: allocates osd-text video thread output method
2531  *****************************************************************************
2532  * This function allocates and initializes a Clone vout method.
2533  *****************************************************************************/
2534 static int Create( vlc_object_t *p_this )
2535 {
2536     filter_t      *p_filter = (filter_t *)p_this;
2537     filter_sys_t  *p_sys;
2538     char          *psz_fontfile   = NULL;
2539     char          *psz_fontfamily = NULL;
2540     int            i_error = 0, fontindex = 0;
2541
2542     /* Allocate structure */
2543     p_filter->p_sys = p_sys = malloc( sizeof(*p_sys) );
2544     if( !p_sys )
2545         return VLC_ENOMEM;
2546
2547     p_sys->psz_fontfamily   = NULL;
2548 #ifdef HAVE_STYLES
2549     p_sys->p_xml            = NULL;
2550 #endif
2551     p_sys->p_face           = 0;
2552     p_sys->p_library        = 0;
2553     p_sys->i_font_size      = 0;
2554     p_sys->i_display_height = 0;
2555
2556     var_Create( p_filter, "freetype-rel-fontsize",
2557                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
2558
2559     psz_fontfamily = var_InheritString( p_filter, "freetype-font" );
2560     p_sys->i_default_font_size = var_InheritInteger( p_filter, "freetype-fontsize" );
2561     p_sys->i_font_opacity = var_InheritInteger( p_filter,"freetype-opacity" );
2562     p_sys->i_font_opacity = VLC_CLIP( p_sys->i_font_opacity, 0, 255 );
2563     p_sys->i_font_color = var_InheritInteger( p_filter, "freetype-color" );
2564     p_sys->i_font_color = VLC_CLIP( p_sys->i_font_color, 0, 0xFFFFFF );
2565     p_sys->b_font_bold = var_InheritBool( p_filter, "freetype-bold" );
2566
2567     p_sys->i_background_opacity = var_InheritInteger( p_filter,"freetype-background-opacity" );;
2568     p_sys->i_background_opacity = VLC_CLIP( p_sys->i_background_opacity, 0, 255 );
2569     p_sys->i_background_color = var_InheritInteger( p_filter, "freetype-background-color" );
2570     p_sys->i_background_color = VLC_CLIP( p_sys->i_background_color, 0, 0xFFFFFF );
2571
2572     p_sys->f_outline_thickness = var_InheritInteger( p_filter, "freetype-outline-thickness" ) / 100.0;
2573     p_sys->f_outline_thickness = VLC_CLIP( p_sys->f_outline_thickness, 0.0, 0.5 );
2574     p_sys->i_outline_opacity = var_InheritInteger( p_filter, "freetype-outline-opacity" );
2575     p_sys->i_outline_opacity = VLC_CLIP( p_sys->i_outline_opacity, 0, 255 );
2576     p_sys->i_outline_color = var_InheritInteger( p_filter, "freetype-outline-color" );
2577     p_sys->i_outline_color = VLC_CLIP( p_sys->i_outline_color, 0, 0xFFFFFF );
2578
2579     p_sys->i_shadow_opacity = var_InheritInteger( p_filter, "freetype-shadow-opacity" );
2580     p_sys->i_shadow_opacity = VLC_CLIP( p_sys->i_shadow_opacity, 0, 255 );
2581     p_sys->i_shadow_color = var_InheritInteger( p_filter, "freetype-shadow-color" );
2582     p_sys->i_shadow_color = VLC_CLIP( p_sys->i_shadow_color, 0, 0xFFFFFF );
2583     float f_shadow_angle = var_InheritFloat( p_filter, "freetype-shadow-angle" );
2584     float f_shadow_distance = var_InheritFloat( p_filter, "freetype-shadow-distance" );
2585     f_shadow_distance = VLC_CLIP( f_shadow_distance, 0, 1 );
2586     p_sys->f_shadow_vector_x = f_shadow_distance * cos(2 * M_PI * f_shadow_angle / 360);
2587     p_sys->f_shadow_vector_y = f_shadow_distance * sin(2 * M_PI * f_shadow_angle / 360);
2588
2589 #ifdef WIN32
2590     /* Get Windows Font folder */
2591     wchar_t wdir[MAX_PATH];
2592     if( S_OK != SHGetFolderPathW( NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, wdir ) )
2593     {
2594         GetWindowsDirectoryW( wdir, MAX_PATH );
2595         wcscat( wdir, L"\\fonts" );
2596     }
2597     p_sys->psz_win_fonts_path = FromWide( wdir );
2598 #endif
2599
2600     /* Set default psz_fontfamily */
2601     if( !psz_fontfamily || !*psz_fontfamily )
2602     {
2603         free( psz_fontfamily );
2604 #ifdef HAVE_STYLES
2605         psz_fontfamily = strdup( DEFAULT_FAMILY );
2606 #else
2607 # ifdef WIN32
2608         if( asprintf( &psz_fontfamily, "%s"DEFAULT_FONT_FILE, p_sys->psz_win_fonts_path ) == -1 )
2609             goto error;
2610 # else
2611         psz_fontfamily = strdup( DEFAULT_FONT_FILE );
2612 # endif
2613         msg_Err( p_filter,"User specified an empty fontfile, using %s", psz_fontfamily );
2614 #endif
2615     }
2616
2617     /* Set the current font file */
2618     p_sys->psz_fontfamily = psz_fontfamily;
2619 #ifdef HAVE_STYLES
2620 #ifdef HAVE_FONTCONFIG
2621     FontConfig_BuildCache( p_filter );
2622
2623     /* */
2624     psz_fontfile = FontConfig_Select( NULL, psz_fontfamily, false, false,
2625                                       p_sys->i_default_font_size, &fontindex );
2626 #elif defined(WIN32)
2627     psz_fontfile = Win32_Select( p_filter, psz_fontfamily, false, false,
2628                                  p_sys->i_default_font_size, &fontindex );
2629
2630 #endif
2631     msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily, psz_fontfile );
2632
2633     /* If nothing is found, use the default family */
2634     if( !psz_fontfile )
2635         psz_fontfile = strdup( psz_fontfamily );
2636
2637 #else /* !HAVE_STYLES */
2638     /* Use the default file */
2639     psz_fontfile = psz_fontfamily;
2640 #endif
2641
2642     /* */
2643     i_error = FT_Init_FreeType( &p_sys->p_library );
2644     if( i_error )
2645     {
2646         msg_Err( p_filter, "couldn't initialize freetype" );
2647         goto error;
2648     }
2649
2650     i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
2651                            fontindex, &p_sys->p_face );
2652
2653     if( i_error == FT_Err_Unknown_File_Format )
2654     {
2655         msg_Err( p_filter, "file %s have unknown format",
2656                  psz_fontfile ? psz_fontfile : "(null)" );
2657         goto error;
2658     }
2659     else if( i_error )
2660     {
2661         msg_Err( p_filter, "failed to load font file %s",
2662                  psz_fontfile ? psz_fontfile : "(null)" );
2663         goto error;
2664     }
2665
2666     i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
2667     if( i_error )
2668     {
2669         msg_Err( p_filter, "font has no unicode translation table" );
2670         goto error;
2671     }
2672
2673     if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
2674
2675     p_sys->p_stroker = NULL;
2676     if( p_sys->f_outline_thickness > 0.001 )
2677     {
2678         i_error = FT_Stroker_New( p_sys->p_library, &p_sys->p_stroker );
2679         if( i_error )
2680             msg_Err( p_filter, "Failed to create stroker for outlining" );
2681     }
2682
2683     p_sys->pp_font_attachments = NULL;
2684     p_sys->i_font_attachments = 0;
2685
2686     p_filter->pf_render_text = RenderText;
2687 #ifdef HAVE_STYLES
2688     p_filter->pf_render_html = RenderHtml;
2689 #else
2690     p_filter->pf_render_html = NULL;
2691 #endif
2692
2693     LoadFontsFromAttachments( p_filter );
2694
2695 #ifdef HAVE_STYLES
2696     free( psz_fontfile );
2697 #endif
2698
2699     return VLC_SUCCESS;
2700
2701 error:
2702     if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
2703     if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
2704 #ifdef HAVE_STYLES
2705     free( psz_fontfile );
2706 #endif
2707     free( psz_fontfamily );
2708     free( p_sys );
2709     return VLC_EGENERIC;
2710 }
2711
2712 /*****************************************************************************
2713  * Destroy: destroy Clone video thread output method
2714  *****************************************************************************
2715  * Clean up all data and library connections
2716  *****************************************************************************/
2717 static void Destroy( vlc_object_t *p_this )
2718 {
2719     filter_t *p_filter = (filter_t *)p_this;
2720     filter_sys_t *p_sys = p_filter->p_sys;
2721
2722     if( p_sys->pp_font_attachments )
2723     {
2724         for( int k = 0; k < p_sys->i_font_attachments; k++ )
2725             vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
2726
2727         free( p_sys->pp_font_attachments );
2728     }
2729
2730 #ifdef HAVE_STYLES
2731     if( p_sys->p_xml ) xml_ReaderDelete( p_sys->p_xml );
2732 #endif
2733     free( p_sys->psz_fontfamily );
2734
2735     /* FcFini asserts calling the subfunction FcCacheFini()
2736      * even if no other library functions have been made since FcInit(),
2737      * so don't call it. */
2738
2739     if( p_sys->p_stroker )
2740         FT_Stroker_Done( p_sys->p_stroker );
2741     FT_Done_Face( p_sys->p_face );
2742     FT_Done_FreeType( p_sys->p_library );
2743     free( p_sys );
2744 }
2745
2746