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