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