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