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