]> git.sesse.net Git - vlc/blob - modules/text_renderer/freetype.c
x265: initialize picture and set picture type
[vlc] / modules / text_renderer / freetype.c
1 /*****************************************************************************
2  * freetype.c : Put text on the video, using freetype2
3  *****************************************************************************
4  * Copyright (C) 2002 - 2012 VLC authors and VideoLAN
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  *          Felix Paul Kühne <fkuehne@videolan.org>
12  *
13  * This program is free software; you can redistribute it and/or modify it
14  * under the terms of the GNU Lesser General Public License as published by
15  * the Free Software Foundation; either version 2.1 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU Lesser General Public License for more details.
22  *
23  * You should have received a copy of the GNU Lesser General Public License
24  * along with this program; if not, write to the Free Software Foundation, Inc.,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26  *****************************************************************************/
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31
32 #ifdef HAVE_CONFIG_H
33 # include "config.h"
34 #endif
35 #include <math.h>
36
37 #include <vlc_common.h>
38 #include <vlc_plugin.h>
39 #include <vlc_stream.h>                        /* stream_MemoryNew */
40 #include <vlc_input.h>                         /* vlc_input_attachment_* */
41 #include <vlc_xml.h>                           /* xml_reader */
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 SYSTEM_DEFAULT_FONT_FILE "/Library/Fonts/Arial Unicode.ttf"
49 # define SYSTEM_DEFAULT_FAMILY "Arial Unicode MS"
50 # define SYSTEM_DEFAULT_MONOSPACE_FONT_FILE "/System/Library/Fonts/Monaco.dfont"
51 # define SYSTEM_DEFAULT_MONOSPACE_FAMILY "Monaco"
52 #elif defined( _WIN32 )
53 # define SYSTEM_DEFAULT_FONT_FILE "arial.ttf" /* Default path font found at run-time */
54 # define SYSTEM_DEFAULT_FAMILY "Arial"
55 # define SYSTEM_DEFAULT_MONOSPACE_FONT_FILE "cour.ttf"
56 # define SYSTEM_DEFAULT_MONOSPACE_FAMILY "Courier New"
57 #elif defined( __OS2__ )
58 # define SYSTEM_DEFAULT_FONT_FILE "/psfonts/tnrwt_k.ttf"
59 # define SYSTEM_DEFAULT_FAMILY "Times New Roman WT K"
60 # define SYSTEM_DEFAULT_MONOSPACE_FONT_FILE "/psfonts/mtsansdk.ttf"
61 # define SYSTEM_DEFAULT_MONOSPACE_FAMILY "Monotype Sans Duospace WT K"
62 #elif defined( __ANDROID__ )
63 # define SYSTEM_DEFAULT_FONT_FILE "/system/fonts/DroidSans-Bold.ttf"
64 # define SYSTEM_DEFAULT_FAMILY "Droid Sans Bold"
65 # define SYSTEM_DEFAULT_MONOSPACE_FONT_FILE "/system/fonts/DroidSansMono.ttf"
66 # define SYSTEM_DEFAULT_MONOSPACE_FAMILY "Droid Sans Mono"
67 #else
68 # define SYSTEM_DEFAULT_FONT_FILE "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
69 # define SYSTEM_DEFAULT_FAMILY "Serif Bold"
70 # define SYSTEM_DEFAULT_MONOSPACE_FONT_FILE "/usr/share/fonts/truetype/freefont/FreeMono.ttf"
71 # define SYSTEM_DEFAULT_MONOSPACE_FAMILY "Monospace"
72 #endif
73
74 #ifndef DEFAULT_FONT_FILE
75 #define DEFAULT_FONT_FILE SYSTEM_DEFAULT_FONT_FILE
76 #endif
77
78 #ifndef DEFAULT_FAMILY
79 #define DEFAULT_FAMILY SYSTEM_DEFAULT_FAMILY
80 #endif
81
82 #ifndef DEFAULT_MONOSPACE_FONT_FILE
83 #define DEFAULT_MONOSPACE_FONT_FILE SYSTEM_DEFAULT_MONOSPACE_FONT_FILE
84 #endif
85
86 #ifndef DEFAULT_MONOSPACE_FAMILY
87 #define DEFAULT_MONOSPACE_FAMILY SYSTEM_DEFAULT_MONOSPACE_FAMILY
88 #endif
89
90 /* Freetype */
91 #include <freetype/ftsynth.h>
92 #include FT_FREETYPE_H
93 #include FT_GLYPH_H
94 #include FT_STROKER_H
95
96 #define FT_FLOOR(X)     ((X & -64) >> 6)
97 #define FT_CEIL(X)      (((X + 63) & -64) >> 6)
98 #ifndef FT_MulFix
99  #define FT_MulFix(v, s) (((v)*(s))>>16)
100 #endif
101
102 /* apple stuff */
103 #ifdef __APPLE__
104 #include <TargetConditionals.h>
105 #if !TARGET_OS_IPHONE
106 #include <Carbon/Carbon.h>
107 #endif
108 #include <sys/param.h>                         /* for MAXPATHLEN */
109 #undef HAVE_FONTCONFIG
110 #define HAVE_STYLES
111 #endif
112
113 /* RTL */
114 #if defined(HAVE_FRIBIDI)
115 # include <fribidi/fribidi.h>
116 #endif
117
118 /* Win32 GDI */
119 #ifdef _WIN32
120 # include <windows.h>
121 # include <shlobj.h>
122 # define HAVE_STYLES
123 # undef HAVE_FONTCONFIG
124 # include <vlc_charset.h>                                     /* FromT */
125 #endif
126
127 /* FontConfig */
128 #ifdef HAVE_FONTCONFIG
129 # include <fontconfig/fontconfig.h>
130 # define HAVE_STYLES
131 #endif
132
133 #include <assert.h>
134
135 #include "text_renderer.h"
136
137 /*****************************************************************************
138  * Module descriptor
139  *****************************************************************************/
140 static int  Create ( vlc_object_t * );
141 static void Destroy( vlc_object_t * );
142
143 #define FONT_TEXT N_("Font")
144 #define MONOSPACE_FONT_TEXT N_("Monospace Font")
145
146 #define FAMILY_LONGTEXT N_("Font family for the font you want to use")
147 #define FONT_LONGTEXT N_("Font file for the font you want to use")
148
149 #define FONTSIZE_TEXT N_("Font size in pixels")
150 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
151     "that will be rendered on the video. " \
152     "If set to something different than 0 this option will override the " \
153     "relative font size." )
154 #define OPACITY_TEXT N_("Text opacity")
155 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
156     "text that will be rendered on the video. 0 = transparent, " \
157     "255 = totally opaque. " )
158 #define COLOR_TEXT N_("Text default color")
159 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
160     "the video. This must be an hexadecimal (like HTML colors). The first two "\
161     "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
162     " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
163 #define FONTSIZER_TEXT N_("Relative font size")
164 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
165     "fonts that will be rendered on the video. If absolute font size is set, "\
166     "relative size will be overridden." )
167 #define BOLD_TEXT N_("Force bold")
168
169 #define BG_OPACITY_TEXT N_("Background opacity")
170 #define BG_COLOR_TEXT N_("Background color")
171
172 #define OUTLINE_OPACITY_TEXT N_("Outline opacity")
173 #define OUTLINE_COLOR_TEXT N_("Outline color")
174 #define OUTLINE_THICKNESS_TEXT N_("Outline thickness")
175
176 #define SHADOW_OPACITY_TEXT N_("Shadow opacity")
177 #define SHADOW_COLOR_TEXT N_("Shadow color")
178 #define SHADOW_ANGLE_TEXT N_("Shadow angle")
179 #define SHADOW_DISTANCE_TEXT N_("Shadow distance")
180
181
182 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
183 static const char *const ppsz_sizes_text[] = {
184     N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
185 #define YUVP_TEXT N_("Use YUVP renderer")
186 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
187   "This option is only needed if you want to encode into DVB subtitles" )
188
189 static const int pi_color_values[] = {
190   0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
191   0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
192   0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
193
194 static const char *const ppsz_color_descriptions[] = {
195   N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
196   N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
197   N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
198
199 static const int pi_outline_thickness[] = {
200     0, 2, 4, 6,
201 };
202 static const char *const ppsz_outline_thickness[] = {
203     N_("None"), N_("Thin"), N_("Normal"), N_("Thick"),
204 };
205
206 vlc_module_begin ()
207     set_shortname( N_("Text renderer"))
208     set_description( N_("Freetype2 font renderer") )
209     set_category( CAT_VIDEO )
210     set_subcategory( SUBCAT_VIDEO_SUBPIC )
211
212 #ifdef HAVE_STYLES
213     add_font( "freetype-font", DEFAULT_FAMILY, FONT_TEXT, FAMILY_LONGTEXT, false )
214     add_font( "freetype-monofont", DEFAULT_MONOSPACE_FAMILY, MONOSPACE_FONT_TEXT, FAMILY_LONGTEXT, false )
215 #else
216     add_loadfile( "freetype-font", DEFAULT_FONT_FILE, FONT_TEXT, FONT_LONGTEXT, false )
217     add_loadfile( "freetype-monofont", DEFAULT_MONOSPACE_FONT_FILE, MONOSPACE_FONT_TEXT, FONT_LONGTEXT, false )
218 #endif
219
220     add_integer( "freetype-fontsize", 0, FONTSIZE_TEXT,
221                  FONTSIZE_LONGTEXT, true )
222         change_integer_range( 0, 4096)
223         change_safe()
224
225     add_integer( "freetype-rel-fontsize", 16, FONTSIZER_TEXT,
226                  FONTSIZER_LONGTEXT, false )
227         change_integer_list( pi_sizes, ppsz_sizes_text )
228         change_safe()
229
230     /* opacity valid on 0..255, with default 255 = fully opaque */
231     add_integer_with_range( "freetype-opacity", 255, 0, 255,
232         OPACITY_TEXT, OPACITY_LONGTEXT, false )
233         change_safe()
234
235     /* hook to the color values list, with default 0x00ffffff = white */
236     add_rgb( "freetype-color", 0x00FFFFFF, COLOR_TEXT,
237                  COLOR_LONGTEXT, false )
238         change_integer_list( pi_color_values, ppsz_color_descriptions )
239         change_safe()
240
241     add_bool( "freetype-bold", false, BOLD_TEXT, NULL, false )
242         change_safe()
243
244     add_integer_with_range( "freetype-background-opacity", 0, 0, 255,
245                             BG_OPACITY_TEXT, NULL, false )
246         change_safe()
247     add_rgb( "freetype-background-color", 0x00000000, BG_COLOR_TEXT,
248              NULL, false )
249         change_integer_list( pi_color_values, ppsz_color_descriptions )
250         change_safe()
251
252     add_integer_with_range( "freetype-outline-opacity", 255, 0, 255,
253                             OUTLINE_OPACITY_TEXT, NULL, false )
254         change_safe()
255     add_rgb( "freetype-outline-color", 0x00000000, OUTLINE_COLOR_TEXT,
256              NULL, false )
257         change_integer_list( pi_color_values, ppsz_color_descriptions )
258         change_safe()
259     add_integer_with_range( "freetype-outline-thickness", 4, 0, 50, OUTLINE_THICKNESS_TEXT,
260              NULL, false )
261         change_integer_list( pi_outline_thickness, ppsz_outline_thickness )
262         change_safe()
263
264     add_integer_with_range( "freetype-shadow-opacity", 128, 0, 255,
265                             SHADOW_OPACITY_TEXT, NULL, false )
266         change_safe()
267     add_rgb( "freetype-shadow-color", 0x00000000, SHADOW_COLOR_TEXT,
268              NULL, false )
269         change_integer_list( pi_color_values, ppsz_color_descriptions )
270         change_safe()
271     add_float_with_range( "freetype-shadow-angle", -45, -360, 360,
272                           SHADOW_ANGLE_TEXT, NULL, false )
273         change_safe()
274     add_float_with_range( "freetype-shadow-distance", 0.06, 0.0, 1.0,
275                           SHADOW_DISTANCE_TEXT, NULL, false )
276         change_safe()
277
278     add_obsolete_integer( "freetype-effect" );
279
280     add_bool( "freetype-yuvp", false, YUVP_TEXT,
281               YUVP_LONGTEXT, true )
282     set_capability( "text renderer", 100 )
283     add_shortcut( "text" )
284     set_callbacks( Create, Destroy )
285 vlc_module_end ()
286
287
288 /*****************************************************************************
289  * Local prototypes
290  *****************************************************************************/
291
292 typedef struct
293 {
294     FT_BitmapGlyph p_glyph;
295     FT_BitmapGlyph p_outline;
296     FT_BitmapGlyph p_shadow;
297     uint32_t       i_color;             /* ARGB color */
298     int            i_line_offset;       /* underline/strikethrough offset */
299     int            i_line_thickness;    /* underline/strikethrough thickness */
300 } line_character_t;
301
302 typedef struct line_desc_t line_desc_t;
303 struct line_desc_t
304 {
305     line_desc_t      *p_next;
306
307     int              i_width;
308     int              i_height;
309     int              i_base_line;
310     int              i_character_count;
311     line_character_t *p_character;
312 };
313
314 /*****************************************************************************
315  * filter_sys_t: freetype local data
316  *****************************************************************************
317  * This structure is part of the video output thread descriptor.
318  * It describes the freetype specific properties of an output thread.
319  *****************************************************************************/
320 struct filter_sys_t
321 {
322     FT_Library     p_library;   /* handle to library     */
323     FT_Face        p_face;      /* handle to face object */
324     FT_Stroker     p_stroker;   /* handle to path stroker object */
325
326     xml_reader_t  *p_xml;       /* vlc xml parser */
327
328     text_style_t   style;       /* Current Style */
329
330     /* More styles... */
331     float          f_shadow_vector_x;
332     float          f_shadow_vector_y;
333     int            i_default_font_size;
334
335     /* Attachments */
336     input_attachment_t **pp_font_attachments;
337     int                  i_font_attachments;
338
339     /* Cache the Win32 font folder */
340 #ifdef _WIN32
341     char*          psz_win_fonts_path;
342 #endif
343
344 };
345
346 /* */
347 static void YUVFromRGB( uint32_t i_argb,
348                     uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
349 {
350     int i_red   = ( i_argb & 0x00ff0000 ) >> 16;
351     int i_green = ( i_argb & 0x0000ff00 ) >>  8;
352     int i_blue  = ( i_argb & 0x000000ff );
353
354     *pi_y = (uint8_t)__MIN(abs( 2104 * i_red  + 4130 * i_green +
355                       802 * i_blue + 4096 + 131072 ) >> 13, 235);
356     *pi_u = (uint8_t)__MIN(abs( -1214 * i_red  + -2384 * i_green +
357                      3598 * i_blue + 4096 + 1048576) >> 13, 240);
358     *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
359                       -585 * i_blue + 4096 + 1048576) >> 13, 240);
360 }
361 static void RGBFromRGB( uint32_t i_argb,
362                         uint8_t *pi_r, uint8_t *pi_g, uint8_t *pi_b )
363 {
364     *pi_r = ( i_argb & 0x00ff0000 ) >> 16;
365     *pi_g = ( i_argb & 0x0000ff00 ) >>  8;
366     *pi_b = ( i_argb & 0x000000ff );
367 }
368 /*****************************************************************************
369  * Make any TTF/OTF fonts present in the attachments of the media file
370  * and store them for later use by the FreeType Engine
371  *****************************************************************************/
372 static int LoadFontsFromAttachments( filter_t *p_filter )
373 {
374     filter_sys_t         *p_sys = p_filter->p_sys;
375     input_attachment_t  **pp_attachments;
376     int                   i_attachments_cnt;
377
378     if( filter_GetInputAttachments( p_filter, &pp_attachments, &i_attachments_cnt ) )
379         return VLC_EGENERIC;
380
381     p_sys->i_font_attachments = 0;
382     p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof(*p_sys->pp_font_attachments));
383     if( !p_sys->pp_font_attachments )
384         return VLC_ENOMEM;
385
386     for( int k = 0; k < i_attachments_cnt; k++ )
387     {
388         input_attachment_t *p_attach = pp_attachments[k];
389
390         if( ( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
391               !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) &&    // OTF
392             p_attach->i_data > 0 && p_attach->p_data )
393         {
394             p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
395         }
396         else
397         {
398             vlc_input_attachment_Delete( p_attach );
399         }
400     }
401     free( pp_attachments );
402
403     return VLC_SUCCESS;
404 }
405
406 static int GetFontSize( filter_t *p_filter )
407 {
408     filter_sys_t *p_sys = p_filter->p_sys;
409     int           i_size = 0;
410
411     if( p_sys->i_default_font_size )
412     {
413         i_size = p_sys->i_default_font_size;
414     }
415     else
416     {
417         int i_ratio = var_InheritInteger( p_filter, "freetype-rel-fontsize" );
418         if( i_ratio > 0 )
419         {
420             i_size = (int)p_filter->fmt_out.video.i_height / i_ratio;
421         }
422     }
423     if( i_size <= 0 )
424     {
425         msg_Warn( p_filter, "invalid fontsize, using 12" );
426         i_size = 12;
427     }
428     return i_size;
429 }
430
431 static int SetFontSize( filter_t *p_filter, int i_size )
432 {
433     filter_sys_t *p_sys = p_filter->p_sys;
434
435     if( !i_size )
436     {
437         i_size = GetFontSize( p_filter );
438
439         msg_Dbg( p_filter, "using fontsize: %i", i_size );
440     }
441
442     p_sys->style.i_font_size = i_size;
443
444     if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
445     {
446         msg_Err( p_filter, "couldn't set font size to %d", i_size );
447         return VLC_EGENERIC;
448     }
449
450     return VLC_SUCCESS;
451 }
452
453 #ifdef HAVE_STYLES
454 #ifdef HAVE_FONTCONFIG
455 static void FontConfig_BuildCache( filter_t *p_filter )
456 {
457     /* */
458     msg_Dbg( p_filter, "Building font databases.");
459     mtime_t t1, t2;
460     t1 = mdate();
461
462 #ifdef __OS2__
463     FcInit();
464 #endif
465
466 #if defined( _WIN32 ) || defined( __APPLE__ )
467     dialog_progress_bar_t *p_dialog = NULL;
468     FcConfig *fcConfig = FcInitLoadConfig();
469
470     p_dialog = dialog_ProgressCreate( p_filter,
471             _("Building font cache"),
472             _("Please wait while your font cache is rebuilt.\n"
473                 "This should take less than a few minutes."), NULL );
474
475 /*    if( p_dialog )
476         dialog_ProgressSet( p_dialog, NULL, 0.5 ); */
477
478     FcConfigBuildFonts( fcConfig );
479 #if defined( __APPLE__ )
480     // By default, scan only the directory /System/Library/Fonts.
481     // So build the set of available fonts under another directories,
482     // and add the set to the current configuration.
483     FcConfigAppFontAddDir( NULL, "~/Library/Fonts" );
484     FcConfigAppFontAddDir( NULL, "/Library/Fonts" );
485     FcConfigAppFontAddDir( NULL, "/Network/Library/Fonts" );
486     //FcConfigAppFontAddDir( NULL, "/System/Library/Fonts" );
487 #endif
488     if( p_dialog )
489     {
490 //        dialog_ProgressSet( p_dialog, NULL, 1.0 );
491         dialog_ProgressDestroy( p_dialog );
492         p_dialog = NULL;
493     }
494 #endif
495     t2 = mdate();
496     msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
497 }
498
499 /***
500  * \brief Selects a font matching family, bold, italic provided
501  ***/
502 static char* FontConfig_Select( FcConfig* config, const char* family,
503                           bool b_bold, bool b_italic, int i_size, int *i_idx )
504 {
505     FcResult result = FcResultMatch;
506     FcPattern *pat, *p_pat;
507     FcChar8* val_s;
508     FcBool val_b;
509     char *ret = NULL;
510
511     /* Create a pattern and fills it */
512     pat = FcPatternCreate();
513     if (!pat) return NULL;
514
515     /* */
516     FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
517     FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
518     FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
519     FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
520     if( i_size != -1 )
521     {
522         char *psz_fontsize;
523         if( asprintf( &psz_fontsize, "%d", i_size ) != -1 )
524         {
525             FcPatternAddString( pat, FC_SIZE, (const FcChar8 *)psz_fontsize );
526             free( psz_fontsize );
527         }
528     }
529
530     /* */
531     FcDefaultSubstitute( pat );
532     if( !FcConfigSubstitute( config, pat, FcMatchPattern ) )
533     {
534         FcPatternDestroy( pat );
535         return NULL;
536     }
537
538     /* Find the best font for the pattern, destroy the pattern */
539     p_pat = FcFontMatch( config, pat, &result );
540     FcPatternDestroy( pat );
541     if( !p_pat || result == FcResultNoMatch ) return NULL;
542
543     /* Check the new pattern */
544     if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
545         || ( val_b != FcTrue ) )
546     {
547         FcPatternDestroy( p_pat );
548         return NULL;
549     }
550     if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
551     {
552         *i_idx = 0;
553     }
554
555     if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
556     {
557         FcPatternDestroy( p_pat );
558         return NULL;
559     }
560
561     /* if( strcasecmp((const char*)val_s, family ) != 0 )
562         msg_Warn( p_filter, "fontconfig: selected font family is not"
563                             "the requested one: '%s' != '%s'\n",
564                             (const char*)val_s, family );   */
565
566     if( FcResultMatch == FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
567         ret = strdup( (const char*)val_s );
568
569     FcPatternDestroy( p_pat );
570     return ret;
571 }
572 #endif
573
574 #ifdef _WIN32
575 #define FONT_DIR_NT _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts")
576
577 static int GetFileFontByName( LPCTSTR font_name, char **psz_filename )
578 {
579     HKEY hKey;
580     TCHAR vbuffer[MAX_PATH];
581     TCHAR dbuffer[256];
582
583     if( RegOpenKeyEx(HKEY_LOCAL_MACHINE, FONT_DIR_NT, 0, KEY_READ, &hKey)
584             != ERROR_SUCCESS )
585         return 1;
586
587     char *font_name_temp = FromT( font_name );
588     size_t fontname_len = strlen( font_name_temp );
589
590     for( int index = 0;; index++ )
591     {
592         DWORD vbuflen = MAX_PATH - 1;
593         DWORD dbuflen = 255;
594
595         LONG i_result = RegEnumValue( hKey, index, vbuffer, &vbuflen,
596                                       NULL, NULL, (LPBYTE)dbuffer, &dbuflen);
597         if( i_result != ERROR_SUCCESS )
598         {
599             RegCloseKey( hKey );
600             return i_result;
601         }
602
603         char *psz_value = FromT( vbuffer );
604
605         char *s = strchr( psz_value,'(' );
606         if( s != NULL && s != psz_value ) s[-1] = '\0';
607
608         /* Manage concatenated font names */
609         if( strchr( psz_value, '&') ) {
610             if( strcasestr( psz_value, font_name_temp ) != NULL )
611             {
612                 free( psz_value );
613                 break;
614             }
615         }
616         else {
617             if( strncasecmp( psz_value, font_name_temp, fontname_len ) == 0 )
618             {
619                 free( psz_value );
620                 break;
621             }
622         }
623
624         free( psz_value );
625     }
626
627     *psz_filename = FromT( dbuffer );
628     free( font_name_temp );
629     RegCloseKey( hKey );
630     return 0;
631 }
632
633
634 static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *lpelfe, const NEWTEXTMETRICEX *metric,
635                                      DWORD type, LPARAM lParam)
636 {
637     VLC_UNUSED( metric );
638     if( (type & RASTER_FONTTYPE) ) return 1;
639     // if( lpelfe->elfScript ) FIXME
640
641     return GetFileFontByName( (LPCTSTR)lpelfe->elfFullName, (char **)lParam );
642 }
643
644 static char* Win32_Select( filter_t *p_filter, const char* family,
645                            bool b_bold, bool b_italic, int i_size, int *i_idx )
646 {
647     VLC_UNUSED( i_size );
648
649     if( !family || strlen( family ) < 1 )
650         goto fail;
651
652     /* */
653     LOGFONT lf;
654     lf.lfCharSet = DEFAULT_CHARSET;
655     if( b_italic )
656         lf.lfItalic = true;
657     if( b_bold )
658         lf.lfWeight = FW_BOLD;
659
660     LPTSTR psz_fbuffer = ToT( family );
661     _tcsncpy( (LPTSTR)&lf.lfFaceName, psz_fbuffer, LF_FACESIZE );
662     free( psz_fbuffer );
663
664     /* */
665     char *psz_filename = NULL;
666     HDC hDC = GetDC( NULL );
667     EnumFontFamiliesEx(hDC, &lf, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&psz_filename, 0);
668     ReleaseDC(NULL, hDC);
669
670     /* */
671     if( psz_filename != NULL )
672     {
673         /* FIXME: increase i_idx, when concatenated strings  */
674         i_idx = 0;
675
676         /* Prepend the Windows Font path, when only a filename was provided */
677         if( strchr( psz_filename, DIR_SEP_CHAR ) )
678             return psz_filename;
679         else
680         {
681             char *psz_tmp;
682             if( asprintf( &psz_tmp, "%s\\%s", p_filter->p_sys->psz_win_fonts_path, psz_filename ) == -1 )
683             {
684                 free( psz_filename );
685                 return NULL;
686             }
687             free( psz_filename );
688             return psz_tmp;
689         }
690     }
691     else /* Let's take any font we can */
692 fail:
693     {
694         char *psz_tmp;
695         if( asprintf( &psz_tmp, "%s\\%s", p_filter->p_sys->psz_win_fonts_path, "arial.ttf" ) == -1 )
696             return NULL;
697         else
698             return psz_tmp;
699     }
700 }
701 #endif /* _WIN32 */
702
703 #ifdef __APPLE__
704 #if !TARGET_OS_IPHONE
705 static char* MacLegacy_Select( filter_t *p_filter, const char* psz_fontname,
706                           bool b_bold, bool b_italic, int i_size, int *i_idx )
707 {
708     VLC_UNUSED( b_bold );
709     VLC_UNUSED( b_italic );
710     VLC_UNUSED( i_size );
711     FSRef ref;
712     unsigned char path[MAXPATHLEN];
713     char * psz_path;
714
715     CFStringRef  cf_fontName;
716     ATSFontRef   ats_font_id;
717
718     *i_idx = 0;
719
720     if( psz_fontname == NULL )
721         return NULL;
722
723     msg_Dbg( p_filter, "looking for %s", psz_fontname );
724     cf_fontName = CFStringCreateWithCString( kCFAllocatorDefault, psz_fontname, kCFStringEncodingUTF8 );
725
726     ats_font_id = ATSFontFindFromName( cf_fontName, kATSOptionFlagsIncludeDisabledMask );
727
728     if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL )
729     {
730         msg_Dbg( p_filter, "ATS couldn't find %s by name, checking family", psz_fontname );
731         ats_font_id = ATSFontFamilyFindFromName( cf_fontName, kATSOptionFlagsDefault );
732
733         if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL )
734         {
735             msg_Dbg( p_filter, "ATS couldn't find either %s nor its family, checking PS name", psz_fontname );
736             ats_font_id = ATSFontFindFromPostScriptName( cf_fontName, kATSOptionFlagsDefault );
737
738             if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL )
739             {
740                 msg_Err( p_filter, "ATS couldn't find %s (no font name, family or PS name)", psz_fontname );
741                 CFRelease( cf_fontName );
742                 return NULL;
743             }
744         }
745     }
746     CFRelease( cf_fontName );
747
748     if ( noErr != ATSFontGetFileReference( ats_font_id, &ref ) )
749     {
750         msg_Err( p_filter, "ATS couldn't get file ref for %s", psz_fontname );
751         return NULL;
752     }
753
754     /* i_idx calculation by searching preceding fontIDs */
755     /* with same FSRef                                       */
756     {
757         ATSFontRef  id2 = ats_font_id - 1;
758         FSRef       ref2;
759
760         while ( id2 > 0 )
761         {
762             if ( noErr != ATSFontGetFileReference( id2, &ref2 ) )
763                 break;
764             if ( noErr != FSCompareFSRefs( &ref, &ref2 ) )
765                 break;
766
767             id2 --;
768         }
769         *i_idx = ats_font_id - ( id2 + 1 );
770     }
771
772     if ( noErr != FSRefMakePath( &ref, path, sizeof(path) ) )
773     {
774         msg_Err( p_filter, "failure when getting path from FSRef" );
775         return NULL;
776     }
777     msg_Dbg( p_filter, "found %s", path );
778
779     psz_path = strdup( (char *)path );
780
781     return psz_path;
782 }
783 #endif
784 #endif
785
786 #endif /* HAVE_STYLES */
787
788
789 /*****************************************************************************
790  * RenderYUVP: place string in picture
791  *****************************************************************************
792  * This function merges the previously rendered freetype glyphs into a picture
793  *****************************************************************************/
794 static int RenderYUVP( filter_t *p_filter, subpicture_region_t *p_region,
795                        line_desc_t *p_line,
796                        FT_BBox *p_bbox )
797 {
798     VLC_UNUSED(p_filter);
799     static const uint8_t pi_gamma[16] =
800         {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
801           0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
802
803     uint8_t *p_dst;
804     video_format_t fmt;
805     int i, x, y, i_pitch;
806     uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
807
808     /* Create a new subpicture region */
809     video_format_Init( &fmt, VLC_CODEC_YUVP );
810     fmt.i_width          =
811     fmt.i_visible_width  = p_bbox->xMax - p_bbox->xMin + 4;
812     fmt.i_height         =
813     fmt.i_visible_height = p_bbox->yMax - p_bbox->yMin + 4;
814
815     assert( !p_region->p_picture );
816     p_region->p_picture = picture_NewFromFormat( &fmt );
817     if( !p_region->p_picture )
818         return VLC_EGENERIC;
819     fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
820     p_region->fmt = fmt;
821
822     /* Calculate text color components
823      * Only use the first color */
824     int i_alpha = (p_line->p_character[0].i_color >> 24) & 0xff;
825     YUVFromRGB( p_line->p_character[0].i_color, &i_y, &i_u, &i_v );
826
827     /* Build palette */
828     fmt.p_palette->i_entries = 16;
829     for( i = 0; i < 8; i++ )
830     {
831         fmt.p_palette->palette[i][0] = 0;
832         fmt.p_palette->palette[i][1] = 0x80;
833         fmt.p_palette->palette[i][2] = 0x80;
834         fmt.p_palette->palette[i][3] = pi_gamma[i];
835         fmt.p_palette->palette[i][3] =
836             (int)fmt.p_palette->palette[i][3] * i_alpha / 255;
837     }
838     for( i = 8; i < fmt.p_palette->i_entries; i++ )
839     {
840         fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
841         fmt.p_palette->palette[i][1] = i_u;
842         fmt.p_palette->palette[i][2] = i_v;
843         fmt.p_palette->palette[i][3] = pi_gamma[i];
844         fmt.p_palette->palette[i][3] =
845             (int)fmt.p_palette->palette[i][3] * i_alpha / 255;
846     }
847
848     p_dst = p_region->p_picture->Y_PIXELS;
849     i_pitch = p_region->p_picture->Y_PITCH;
850
851     /* Initialize the region pixels */
852     memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
853
854     for( ; p_line != NULL; p_line = p_line->p_next )
855     {
856         int i_align_left = 0;
857         if( p_line->i_width < (int)fmt.i_visible_width )
858         {
859             if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
860                 i_align_left = ( fmt.i_visible_width - p_line->i_width );
861             else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
862                 i_align_left = ( fmt.i_visible_width - p_line->i_width ) / 2;
863         }
864         int i_align_top = 0;
865
866         for( i = 0; i < p_line->i_character_count; i++ )
867         {
868             const line_character_t *ch = &p_line->p_character[i];
869             FT_BitmapGlyph p_glyph = ch->p_glyph;
870
871             int i_glyph_y = i_align_top  - p_glyph->top  + p_bbox->yMax + p_line->i_base_line;
872             int i_glyph_x = i_align_left + p_glyph->left - p_bbox->xMin;
873
874             for( y = 0; y < p_glyph->bitmap.rows; y++ )
875             {
876                 for( x = 0; x < p_glyph->bitmap.width; x++ )
877                 {
878                     if( p_glyph->bitmap.buffer[y * p_glyph->bitmap.width + x] )
879                         p_dst[(i_glyph_y + y) * i_pitch + (i_glyph_x + x)] =
880                             (p_glyph->bitmap.buffer[y * p_glyph->bitmap.width + x] + 8)/16;
881                 }
882             }
883         }
884     }
885
886     /* Outlining (find something better than nearest neighbour filtering ?) */
887     if( 1 )
888     {
889         uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
890         uint8_t *p_top = p_dst; /* Use 1st line as a cache */
891         uint8_t left, current;
892
893         for( y = 1; y < (int)fmt.i_height - 1; y++ )
894         {
895             if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
896             p_dst += p_region->p_picture->Y_PITCH;
897             left = 0;
898
899             for( x = 1; x < (int)fmt.i_width - 1; x++ )
900             {
901                 current = p_dst[x];
902                 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
903                              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;
904                 left = current;
905             }
906         }
907         memset( p_top, 0, fmt.i_width );
908     }
909
910     return VLC_SUCCESS;
911 }
912
913 /*****************************************************************************
914  * RenderYUVA: place string in picture
915  *****************************************************************************
916  * This function merges the previously rendered freetype glyphs into a picture
917  *****************************************************************************/
918 static void FillYUVAPicture( picture_t *p_picture,
919                              int i_a, int i_y, int i_u, int i_v )
920 {
921     memset( p_picture->p[0].p_pixels, i_y,
922             p_picture->p[0].i_pitch * p_picture->p[0].i_lines );
923     memset( p_picture->p[1].p_pixels, i_u,
924             p_picture->p[1].i_pitch * p_picture->p[1].i_lines );
925     memset( p_picture->p[2].p_pixels, i_v,
926             p_picture->p[2].i_pitch * p_picture->p[2].i_lines );
927     memset( p_picture->p[3].p_pixels, i_a,
928             p_picture->p[3].i_pitch * p_picture->p[3].i_lines );
929 }
930
931 static inline void BlendYUVAPixel( picture_t *p_picture,
932                                    int i_picture_x, int i_picture_y,
933                                    int i_a, int i_y, int i_u, int i_v,
934                                    int i_alpha )
935 {
936     int i_an = i_a * i_alpha / 255;
937
938     uint8_t *p_y = &p_picture->p[0].p_pixels[i_picture_y * p_picture->p[0].i_pitch + i_picture_x];
939     uint8_t *p_u = &p_picture->p[1].p_pixels[i_picture_y * p_picture->p[1].i_pitch + i_picture_x];
940     uint8_t *p_v = &p_picture->p[2].p_pixels[i_picture_y * p_picture->p[2].i_pitch + i_picture_x];
941     uint8_t *p_a = &p_picture->p[3].p_pixels[i_picture_y * p_picture->p[3].i_pitch + i_picture_x];
942
943     int i_ao = *p_a;
944     if( i_ao == 0 )
945     {
946         *p_y = i_y;
947         *p_u = i_u;
948         *p_v = i_v;
949         *p_a = i_an;
950     }
951     else
952     {
953         *p_a = 255 - (255 - *p_a) * (255 - i_an) / 255;
954         if( *p_a != 0 )
955         {
956             *p_y = ( *p_y * i_ao * (255 - i_an) / 255 + i_y * i_an ) / *p_a;
957             *p_u = ( *p_u * i_ao * (255 - i_an) / 255 + i_u * i_an ) / *p_a;
958             *p_v = ( *p_v * i_ao * (255 - i_an) / 255 + i_v * i_an ) / *p_a;
959         }
960     }
961 }
962
963 static void FillRGBAPicture( picture_t *p_picture,
964                              int i_a, int i_r, int i_g, int i_b )
965 {
966     for( int dy = 0; dy < p_picture->p[0].i_visible_lines; dy++ )
967     {
968         for( int dx = 0; dx < p_picture->p[0].i_visible_pitch; dx += 4 )
969         {
970             uint8_t *p_rgba = &p_picture->p->p_pixels[dy * p_picture->p->i_pitch + dx];
971             p_rgba[0] = i_r;
972             p_rgba[1] = i_g;
973             p_rgba[2] = i_b;
974             p_rgba[3] = i_a;
975         }
976     }
977 }
978
979 static inline void BlendRGBAPixel( picture_t *p_picture,
980                                    int i_picture_x, int i_picture_y,
981                                    int i_a, int i_r, int i_g, int i_b,
982                                    int i_alpha )
983 {
984     int i_an = i_a * i_alpha / 255;
985
986     uint8_t *p_rgba = &p_picture->p->p_pixels[i_picture_y * p_picture->p->i_pitch + 4 * i_picture_x];
987
988     int i_ao = p_rgba[3];
989     if( i_ao == 0 )
990     {
991         p_rgba[0] = i_r;
992         p_rgba[1] = i_g;
993         p_rgba[2] = i_b;
994         p_rgba[3] = i_an;
995     }
996     else
997     {
998         p_rgba[3] = 255 - (255 - p_rgba[3]) * (255 - i_an) / 255;
999         if( p_rgba[3] != 0 )
1000         {
1001             p_rgba[0] = ( p_rgba[0] * i_ao * (255 - i_an) / 255 + i_r * i_an ) / p_rgba[3];
1002             p_rgba[1] = ( p_rgba[1] * i_ao * (255 - i_an) / 255 + i_g * i_an ) / p_rgba[3];
1003             p_rgba[2] = ( p_rgba[2] * i_ao * (255 - i_an) / 255 + i_b * i_an ) / p_rgba[3];
1004         }
1005     }
1006 }
1007
1008 static inline void BlendAXYZGlyph( picture_t *p_picture,
1009                                    int i_picture_x, int i_picture_y,
1010                                    int i_a, int i_x, int i_y, int i_z,
1011                                    FT_BitmapGlyph p_glyph,
1012                                    void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
1013
1014 {
1015     for( int dy = 0; dy < p_glyph->bitmap.rows; dy++ )
1016     {
1017         for( int dx = 0; dx < p_glyph->bitmap.width; dx++ )
1018             BlendPixel( p_picture, i_picture_x + dx, i_picture_y + dy,
1019                         i_a, i_x, i_y, i_z,
1020                         p_glyph->bitmap.buffer[dy * p_glyph->bitmap.width + dx] );
1021     }
1022 }
1023
1024 static inline void BlendAXYZLine( picture_t *p_picture,
1025                                   int i_picture_x, int i_picture_y,
1026                                   int i_a, int i_x, int i_y, int i_z,
1027                                   const line_character_t *p_current,
1028                                   const line_character_t *p_next,
1029                                   void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
1030 {
1031     int i_line_width = p_current->p_glyph->bitmap.width;
1032     if( p_next )
1033         i_line_width = p_next->p_glyph->left - p_current->p_glyph->left;
1034
1035     for( int dx = 0; dx < i_line_width; dx++ )
1036     {
1037         for( int dy = 0; dy < p_current->i_line_thickness; dy++ )
1038             BlendPixel( p_picture,
1039                         i_picture_x + dx,
1040                         i_picture_y + p_current->i_line_offset + dy,
1041                         i_a, i_x, i_y, i_z, 0xff );
1042     }
1043 }
1044
1045 static inline void RenderBackground( subpicture_region_t *p_region,
1046                                      line_desc_t *p_line_head,
1047                                      FT_BBox *p_bbox,
1048                                      int i_margin,
1049                                      picture_t *p_picture,
1050                                      int i_text_width,
1051                                      void (*ExtractComponents)( uint32_t, uint8_t *, uint8_t *, uint8_t * ),
1052                                      void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
1053 {
1054     for( line_desc_t *p_line = p_line_head; p_line != NULL; p_line = p_line->p_next )
1055     {
1056         int i_align_left = i_margin;
1057         int i_align_top = i_margin;
1058         int line_start = 0;
1059         int line_end = 0;
1060         unsigned line_top = 0;
1061         int line_bottom = 0;
1062         int max_height = 0;
1063
1064         if( p_line->i_width < i_text_width )
1065         {
1066             /* Left offset to take into account alignment */
1067             if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1068                 i_align_left += ( i_text_width - p_line->i_width );
1069             else if( (p_region->i_align & 0x10) == SUBPICTURE_ALIGN_LEAVETEXT)
1070                 i_align_left = i_margin; /* Keep it the way it is */
1071             else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1072                 i_align_left += ( i_text_width - p_line->i_width ) / 2;
1073         }
1074
1075         /* Find the tallest character in the line */
1076         for( int i = 0; i < p_line->i_character_count; i++ ) {
1077             const line_character_t *ch = &p_line->p_character[i];
1078             FT_BitmapGlyph p_glyph = ch->p_outline ? ch->p_outline : ch->p_glyph;
1079             if (p_glyph->top > max_height)
1080                 max_height = p_glyph->top;
1081         }
1082
1083         /* Compute the background for the line (identify leading/trailing space) */
1084         for( int i = 0; i < p_line->i_character_count; i++ ) {
1085             const line_character_t *ch = &p_line->p_character[i];
1086             FT_BitmapGlyph p_glyph = ch->p_outline ? ch->p_outline : ch->p_glyph;
1087             if (p_glyph && p_glyph->bitmap.rows > 0) {
1088                 // Found a non-whitespace character
1089                 line_start = i_align_left + p_glyph->left - p_bbox->xMin;
1090                 break;
1091             }
1092         }
1093
1094         /* Fudge factor to make sure caption background edges are left aligned
1095            despite variable font width */
1096         if (line_start < 12)
1097             line_start = 0;
1098
1099         /* Find right boundary for bounding box for background */
1100         for( int i = p_line->i_character_count; i > 0; i-- ) {
1101             const line_character_t *ch = &p_line->p_character[i - 1];
1102             FT_BitmapGlyph p_glyph = ch->p_shadow ? ch->p_shadow : ch->p_glyph;
1103             if (p_glyph && p_glyph->bitmap.rows > 0) {
1104                 // Found a non-whitespace character
1105                 line_end = i_align_left + p_glyph->left - p_bbox->xMin + p_glyph->bitmap.width;
1106                 break;
1107             }
1108         }
1109
1110         /* Setup color for the background */
1111         uint8_t i_x, i_y, i_z;
1112         ExtractComponents( 0x000000, &i_x, &i_y, &i_z );
1113
1114         /* Compute the upper boundary for the background */
1115         if ((i_align_top + p_line->i_base_line - max_height) < 0)
1116             line_top = i_align_top + p_line->i_base_line;
1117         else
1118             line_top = i_align_top + p_line->i_base_line - max_height;
1119
1120         /* Compute lower boundary for the background */
1121         line_bottom =  __MIN(line_top + p_line->i_height, p_region->fmt.i_visible_height);
1122
1123         /* Render the actual background */
1124         for( int dy = line_top; dy < line_bottom; dy++ )
1125         {
1126             for( int dx = line_start; dx < line_end; dx++ )
1127                 BlendPixel( p_picture, dx, dy, 0xff, i_x, i_y, i_z, 0xff );
1128         }
1129     }
1130 }
1131
1132 static inline int RenderAXYZ( filter_t *p_filter,
1133                               subpicture_region_t *p_region,
1134                               line_desc_t *p_line_head,
1135                               FT_BBox *p_bbox,
1136                               int i_margin,
1137                               vlc_fourcc_t i_chroma,
1138                               void (*ExtractComponents)( uint32_t, uint8_t *, uint8_t *, uint8_t * ),
1139                               void (*FillPicture)( picture_t *p_picture, int, int, int, int ),
1140                               void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
1141 {
1142     filter_sys_t *p_sys = p_filter->p_sys;
1143
1144     /* Create a new subpicture region */
1145     const int i_text_width  = p_bbox->xMax - p_bbox->xMin;
1146     const int i_text_height = p_bbox->yMax - p_bbox->yMin;
1147     video_format_t fmt;
1148     video_format_Init( &fmt, i_chroma );
1149     fmt.i_width          =
1150     fmt.i_visible_width  = i_text_width  + 2 * i_margin;
1151     fmt.i_height         =
1152     fmt.i_visible_height = i_text_height + 2 * i_margin;
1153
1154     picture_t *p_picture = p_region->p_picture = picture_NewFromFormat( &fmt );
1155     if( !p_region->p_picture )
1156         return VLC_EGENERIC;
1157     p_region->fmt = fmt;
1158
1159     /* Initialize the picture background */
1160     uint8_t i_a = var_InheritInteger( p_filter, "freetype-background-opacity" );
1161     i_a = VLC_CLIP( i_a, 0, 255 );
1162     uint8_t i_x, i_y, i_z;
1163
1164     if (p_region->b_renderbg) {
1165         /* Render the background just under the text */
1166         FillPicture( p_picture, 0x00, 0x00, 0x00, 0x00 );
1167         RenderBackground(p_region, p_line_head, p_bbox, i_margin, p_picture, i_text_width,
1168                          ExtractComponents, BlendPixel);
1169     } else {
1170         /* Render background under entire subpicture block */
1171         int i_background_color = var_InheritInteger( p_filter, "freetype-background-color" );
1172         i_background_color = VLC_CLIP( i_background_color, 0, 0xFFFFFF );
1173         ExtractComponents( i_background_color, &i_x, &i_y, &i_z );
1174         FillPicture( p_picture, i_a, i_x, i_y, i_z );
1175     }
1176
1177     /* Render shadow then outline and then normal glyphs */
1178     for( int g = 0; g < 3; g++ )
1179     {
1180         /* Render all lines */
1181         for( line_desc_t *p_line = p_line_head; p_line != NULL; p_line = p_line->p_next )
1182         {
1183             int i_align_left = i_margin;
1184             if( p_line->i_width < i_text_width )
1185             {
1186                 /* Left offset to take into account alignment */
1187                 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1188                     i_align_left += ( i_text_width - p_line->i_width );
1189                 else if( (p_region->i_align & 0x10) == SUBPICTURE_ALIGN_LEAVETEXT)
1190                     i_align_left = i_margin; /* Keep it the way it is */
1191                 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1192                     i_align_left += ( i_text_width - p_line->i_width ) / 2;
1193             }
1194             int i_align_top = i_margin;
1195
1196             /* Render all glyphs and underline/strikethrough */
1197             for( int i = 0; i < p_line->i_character_count; i++ )
1198             {
1199                 const line_character_t *ch = &p_line->p_character[i];
1200                 FT_BitmapGlyph p_glyph = g == 0 ? ch->p_shadow : g == 1 ? ch->p_outline : ch->p_glyph;
1201                 if( !p_glyph )
1202                     continue;
1203
1204                 i_a = (ch->i_color >> 24) & 0xff;
1205                 uint32_t i_color;
1206                 switch (g) {
1207                 case 0:
1208                     i_a     = i_a * p_sys->style.i_shadow_alpha / 255;
1209                     i_color = p_sys->style.i_shadow_color;
1210                     break;
1211                 case 1:
1212                     i_a     = i_a * p_sys->style.i_outline_alpha / 255;
1213                     i_color = p_sys->style.i_outline_color;
1214                     break;
1215                 default:
1216                     i_color = ch->i_color;
1217                     break;
1218                 }
1219                 ExtractComponents( i_color, &i_x, &i_y, &i_z );
1220
1221                 int i_glyph_y = i_align_top  - p_glyph->top  + p_bbox->yMax + p_line->i_base_line;
1222                 int i_glyph_x = i_align_left + p_glyph->left - p_bbox->xMin;
1223
1224                 BlendAXYZGlyph( p_picture,
1225                                 i_glyph_x, i_glyph_y,
1226                                 i_a, i_x, i_y, i_z,
1227                                 p_glyph,
1228                                 BlendPixel );
1229
1230                 /* underline/strikethrough are only rendered for the normal glyph */
1231                 if( g == 2 && ch->i_line_thickness > 0 )
1232                     BlendAXYZLine( p_picture,
1233                                    i_glyph_x, i_glyph_y + p_glyph->top,
1234                                    i_a, i_x, i_y, i_z,
1235                                    &ch[0],
1236                                    i + 1 < p_line->i_character_count ? &ch[1] : NULL,
1237                                    BlendPixel );
1238             }
1239         }
1240     }
1241
1242     return VLC_SUCCESS;
1243 }
1244
1245
1246
1247 static void FreeLine( line_desc_t *p_line )
1248 {
1249     for( int i = 0; i < p_line->i_character_count; i++ )
1250     {
1251         line_character_t *ch = &p_line->p_character[i];
1252         FT_Done_Glyph( (FT_Glyph)ch->p_glyph );
1253         if( ch->p_outline )
1254             FT_Done_Glyph( (FT_Glyph)ch->p_outline );
1255         if( ch->p_shadow )
1256             FT_Done_Glyph( (FT_Glyph)ch->p_shadow );
1257     }
1258
1259     free( p_line->p_character );
1260     free( p_line );
1261 }
1262
1263 static void FreeLines( line_desc_t *p_lines )
1264 {
1265     for( line_desc_t *p_line = p_lines; p_line != NULL; )
1266     {
1267         line_desc_t *p_next = p_line->p_next;
1268         FreeLine( p_line );
1269         p_line = p_next;
1270     }
1271 }
1272
1273 static line_desc_t *NewLine( int i_count )
1274 {
1275     line_desc_t *p_line = malloc( sizeof(*p_line) );
1276
1277     if( !p_line )
1278         return NULL;
1279
1280     p_line->p_next = NULL;
1281     p_line->i_width = 0;
1282     p_line->i_base_line = 0;
1283     p_line->i_character_count = 0;
1284
1285     p_line->p_character = calloc( i_count, sizeof(*p_line->p_character) );
1286     if( !p_line->p_character )
1287     {
1288         free( p_line );
1289         return NULL;
1290     }
1291     return p_line;
1292 }
1293
1294 static FT_Face LoadEmbeddedFace( filter_sys_t *p_sys, const text_style_t *p_style )
1295 {
1296     for( int k = 0; k < p_sys->i_font_attachments; k++ )
1297     {
1298         input_attachment_t *p_attach   = p_sys->pp_font_attachments[k];
1299         int                 i_font_idx = 0;
1300         FT_Face             p_face = NULL;
1301
1302         while( 0 == FT_New_Memory_Face( p_sys->p_library,
1303                                         p_attach->p_data,
1304                                         p_attach->i_data,
1305                                         i_font_idx,
1306                                         &p_face ))
1307         {
1308             if( p_face )
1309             {
1310                 int i_style_received = ((p_face->style_flags & FT_STYLE_FLAG_BOLD)    ? STYLE_BOLD   : 0) |
1311                                        ((p_face->style_flags & FT_STYLE_FLAG_ITALIC ) ? STYLE_ITALIC : 0);
1312                 if( p_face->family_name != NULL
1313                  && !strcasecmp( p_face->family_name, p_style->psz_fontname )
1314                  && (p_style->i_style_flags & (STYLE_BOLD | STYLE_ITALIC))
1315                                                           == i_style_received )
1316                     return p_face;
1317
1318                 FT_Done_Face( p_face );
1319             }
1320             i_font_idx++;
1321         }
1322     }
1323     return NULL;
1324 }
1325
1326 static FT_Face LoadFace( filter_t *p_filter,
1327                          const text_style_t *p_style )
1328 {
1329     filter_sys_t *p_sys = p_filter->p_sys;
1330
1331     /* Look for a match amongst our attachments first */
1332     FT_Face p_face = LoadEmbeddedFace( p_sys, p_style );
1333
1334     /* Load system wide font otheriwse */
1335     if( !p_face )
1336     {
1337         int  i_idx = 0;
1338         char *psz_fontfile = NULL;
1339 #ifdef HAVE_FONTCONFIG
1340         psz_fontfile = FontConfig_Select( NULL,
1341                                           p_style->psz_fontname,
1342                                           (p_style->i_style_flags & STYLE_BOLD) != 0,
1343                                           (p_style->i_style_flags & STYLE_ITALIC) != 0,
1344                                           -1,
1345                                           &i_idx );
1346 #elif defined( __APPLE__ )
1347 #if !TARGET_OS_IPHONE
1348         psz_fontfile = MacLegacy_Select( p_filter, p_style->psz_fontname, false, false, -1, &i_idx );
1349 #endif
1350 #elif defined( _WIN32 )
1351         psz_fontfile = Win32_Select( p_filter,
1352                                     p_style->psz_fontname,
1353                                     (p_style->i_style_flags & STYLE_BOLD) != 0,
1354                                     (p_style->i_style_flags & STYLE_ITALIC) != 0,
1355                                     -1,
1356                                     &i_idx );
1357 #else
1358         psz_fontfile = NULL;
1359 #endif
1360         if( !psz_fontfile )
1361             return NULL;
1362
1363         if( *psz_fontfile == '\0' )
1364         {
1365             msg_Warn( p_filter,
1366                       "We were not able to find a matching font: \"%s\" (%s %s),"
1367                       " so using default font",
1368                       p_style->psz_fontname,
1369                       (p_style->i_style_flags & STYLE_BOLD)   ? "Bold" : "",
1370                       (p_style->i_style_flags & STYLE_ITALIC) ? "Italic" : "" );
1371             p_face = NULL;
1372         }
1373         else
1374         {
1375             if( FT_New_Face( p_sys->p_library, psz_fontfile, i_idx, &p_face ) )
1376                 p_face = NULL;
1377         }
1378         free( psz_fontfile );
1379     }
1380     if( !p_face )
1381         return NULL;
1382
1383     if( FT_Select_Charmap( p_face, ft_encoding_unicode ) )
1384     {
1385         /* We've loaded a font face which is unhelpful for actually
1386          * rendering text - fallback to the default one.
1387          */
1388         FT_Done_Face( p_face );
1389         return NULL;
1390     }
1391     return p_face;
1392 }
1393
1394 static int GetGlyph( filter_t *p_filter,
1395                      FT_Glyph *pp_glyph,   FT_BBox *p_glyph_bbox,
1396                      FT_Glyph *pp_outline, FT_BBox *p_outline_bbox,
1397                      FT_Glyph *pp_shadow,  FT_BBox *p_shadow_bbox,
1398
1399                      FT_Face  p_face,
1400                      int i_glyph_index,
1401                      int i_style_flags,
1402                      FT_Vector *p_pen,
1403                      FT_Vector *p_pen_shadow )
1404 {
1405     if( FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_DEFAULT ) &&
1406         FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT ) )
1407     {
1408         msg_Err( p_filter, "unable to render text FT_Load_Glyph failed" );
1409         return VLC_EGENERIC;
1410     }
1411
1412     /* Do synthetic styling now that Freetype supports it;
1413      * ie. if the font we have loaded is NOT already in the
1414      * style that the tags want, then switch it on; if they
1415      * are then don't. */
1416     if ((i_style_flags & STYLE_BOLD) && !(p_face->style_flags & FT_STYLE_FLAG_BOLD))
1417         FT_GlyphSlot_Embolden( p_face->glyph );
1418     if ((i_style_flags & STYLE_ITALIC) && !(p_face->style_flags & FT_STYLE_FLAG_ITALIC))
1419         FT_GlyphSlot_Oblique( p_face->glyph );
1420
1421     FT_Glyph glyph;
1422     if( FT_Get_Glyph( p_face->glyph, &glyph ) )
1423     {
1424         msg_Err( p_filter, "unable to render text FT_Get_Glyph failed" );
1425         return VLC_EGENERIC;
1426     }
1427
1428     FT_Glyph outline = NULL;
1429     if( p_filter->p_sys->p_stroker )
1430     {
1431         outline = glyph;
1432         if( FT_Glyph_StrokeBorder( &outline, p_filter->p_sys->p_stroker, 0, 0 ) )
1433             outline = NULL;
1434     }
1435
1436     FT_Glyph shadow = NULL;
1437     if( p_filter->p_sys->style.i_shadow_alpha > 0 )
1438     {
1439         shadow = outline ? outline : glyph;
1440         if( FT_Glyph_To_Bitmap( &shadow, FT_RENDER_MODE_NORMAL, p_pen_shadow, 0  ) )
1441         {
1442             shadow = NULL;
1443         }
1444         else
1445         {
1446             FT_Glyph_Get_CBox( shadow, ft_glyph_bbox_pixels, p_shadow_bbox );
1447         }
1448     }
1449     *pp_shadow = shadow;
1450
1451     if( FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, p_pen, 1) )
1452     {
1453         FT_Done_Glyph( glyph );
1454         if( outline )
1455             FT_Done_Glyph( outline );
1456         if( shadow )
1457             FT_Done_Glyph( shadow );
1458         return VLC_EGENERIC;
1459     }
1460     FT_Glyph_Get_CBox( glyph, ft_glyph_bbox_pixels, p_glyph_bbox );
1461     *pp_glyph = glyph;
1462
1463     if( outline )
1464     {
1465         FT_Glyph_To_Bitmap( &outline, FT_RENDER_MODE_NORMAL, p_pen, 1 );
1466         FT_Glyph_Get_CBox( outline, ft_glyph_bbox_pixels, p_outline_bbox );
1467     }
1468     *pp_outline = outline;
1469
1470     return VLC_SUCCESS;
1471 }
1472
1473 static void FixGlyph( FT_Glyph glyph, FT_BBox *p_bbox, FT_Face face, const FT_Vector *p_pen )
1474 {
1475     FT_BitmapGlyph glyph_bmp = (FT_BitmapGlyph)glyph;
1476     if( p_bbox->xMin >= p_bbox->xMax )
1477     {
1478         p_bbox->xMin = FT_CEIL(p_pen->x);
1479         p_bbox->xMax = FT_CEIL(p_pen->x + face->glyph->advance.x);
1480         glyph_bmp->left = p_bbox->xMin;
1481     }
1482     if( p_bbox->yMin >= p_bbox->yMax )
1483     {
1484         p_bbox->yMax = FT_CEIL(p_pen->y);
1485         p_bbox->yMin = FT_CEIL(p_pen->y + face->glyph->advance.y);
1486         glyph_bmp->top  = p_bbox->yMax;
1487     }
1488 }
1489
1490 static void BBoxEnlarge( FT_BBox *p_max, const FT_BBox *p )
1491 {
1492     p_max->xMin = __MIN(p_max->xMin, p->xMin);
1493     p_max->yMin = __MIN(p_max->yMin, p->yMin);
1494     p_max->xMax = __MAX(p_max->xMax, p->xMax);
1495     p_max->yMax = __MAX(p_max->yMax, p->yMax);
1496 }
1497
1498 static int ProcessLines( filter_t *p_filter,
1499                          line_desc_t **pp_lines,
1500                          FT_BBox     *p_bbox,
1501                          int         *pi_max_face_height,
1502
1503                          uni_char_t *psz_text,
1504                          text_style_t **pp_styles,
1505                          uint32_t *pi_k_dates,
1506                          int i_len )
1507 {
1508     filter_sys_t   *p_sys = p_filter->p_sys;
1509     uni_char_t     *p_fribidi_string = NULL;
1510     text_style_t   **pp_fribidi_styles = NULL;
1511     int            *p_new_positions = NULL;
1512
1513 #if defined(HAVE_FRIBIDI)
1514     {
1515         int    *p_old_positions;
1516         int start_pos, pos = 0;
1517
1518         pp_fribidi_styles = calloc( i_len, sizeof(*pp_fribidi_styles) );
1519
1520         p_fribidi_string  = malloc( (i_len + 1) * sizeof(*p_fribidi_string) );
1521         p_old_positions   = malloc( (i_len + 1) * sizeof(*p_old_positions) );
1522         p_new_positions   = malloc( (i_len + 1) * sizeof(*p_new_positions) );
1523
1524         if( ! pp_fribidi_styles ||
1525             ! p_fribidi_string ||
1526             ! p_old_positions ||
1527             ! p_new_positions )
1528         {
1529             free( p_old_positions );
1530             free( p_new_positions );
1531             free( p_fribidi_string );
1532             free( pp_fribidi_styles );
1533             return VLC_ENOMEM;
1534         }
1535
1536         /* Do bidi conversion line-by-line */
1537         while(pos < i_len)
1538         {
1539             while(pos < i_len) {
1540                 if (psz_text[pos] != '\n')
1541                     break;
1542                 p_fribidi_string[pos] = psz_text[pos];
1543                 pp_fribidi_styles[pos] = pp_styles[pos];
1544                 p_new_positions[pos] = pos;
1545                 ++pos;
1546             }
1547             start_pos = pos;
1548             while(pos < i_len) {
1549                 if (psz_text[pos] == '\n')
1550                     break;
1551                 ++pos;
1552             }
1553             if (pos > start_pos)
1554             {
1555 #if (FRIBIDI_MINOR_VERSION < 19) && (FRIBIDI_MAJOR_VERSION == 0)
1556                 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1557 #else
1558                 FriBidiParType base_dir = FRIBIDI_PAR_LTR;
1559 #endif
1560                 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1561                         pos - start_pos, &base_dir,
1562                         (FriBidiChar*)p_fribidi_string + start_pos,
1563                         p_new_positions + start_pos,
1564                         p_old_positions,
1565                         NULL );
1566                 for( int j = start_pos; j < pos; j++ )
1567                 {
1568                     pp_fribidi_styles[ j ] = pp_styles[ start_pos + p_old_positions[j - start_pos] ];
1569                     p_new_positions[ j ] += start_pos;
1570                 }
1571             }
1572         }
1573         p_fribidi_string[ i_len ] = 0;
1574         free( p_old_positions );
1575
1576         pp_styles = pp_fribidi_styles;
1577         psz_text = p_fribidi_string;
1578     }
1579 #endif
1580     /* Work out the karaoke */
1581     uint8_t *pi_karaoke_bar = NULL;
1582     if( pi_k_dates )
1583     {
1584         pi_karaoke_bar = malloc( i_len * sizeof(*pi_karaoke_bar));
1585         if( pi_karaoke_bar )
1586         {
1587             int64_t i_elapsed  = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1588             for( int i = 0; i < i_len; i++ )
1589             {
1590                 unsigned i_bar = p_new_positions ? p_new_positions[i] : i;
1591                 pi_karaoke_bar[i_bar] = pi_k_dates[i] >= i_elapsed;
1592             }
1593         }
1594     }
1595     free( p_new_positions );
1596
1597     *pi_max_face_height = 0;
1598     *pp_lines = NULL;
1599     line_desc_t **pp_line_next = pp_lines;
1600
1601     FT_BBox bbox = {
1602         .xMin = INT_MAX,
1603         .yMin = INT_MAX,
1604         .xMax = INT_MIN,
1605         .yMax = INT_MIN,
1606     };
1607     int i_face_height_previous = 0;
1608     int i_base_line = 0;
1609     const text_style_t *p_previous_style = NULL;
1610     FT_Face p_face = NULL;
1611     for( int i_start = 0; i_start < i_len; )
1612     {
1613         /* Compute the length of the current text line */
1614         int i_length = 0;
1615         while( i_start + i_length < i_len && psz_text[i_start + i_length] != '\n' )
1616             i_length++;
1617
1618         /* Render the text line (or the begining if too long) into 0 or 1 glyph line */
1619         line_desc_t *p_line = i_length > 0 ? NewLine( i_length ) : NULL;
1620         int i_index = i_start;
1621         FT_Vector pen = {
1622             .x = 0,
1623             .y = 0,
1624         };
1625         int i_face_height = 0;
1626         FT_BBox line_bbox = {
1627             .xMin = INT_MAX,
1628             .yMin = INT_MAX,
1629             .xMax = INT_MIN,
1630             .yMax = INT_MIN,
1631         };
1632         int i_ul_offset = 0;
1633         int i_ul_thickness = 0;
1634         typedef struct {
1635             int       i_index;
1636             FT_Vector pen;
1637             FT_BBox   line_bbox;
1638             int i_face_height;
1639             int i_ul_offset;
1640             int i_ul_thickness;
1641         } break_point_t;
1642         break_point_t break_point;
1643         break_point_t break_point_fallback;
1644
1645 #define SAVE_BP(dst) do { \
1646         dst.i_index = i_index; \
1647         dst.pen = pen; \
1648         dst.line_bbox = line_bbox; \
1649         dst.i_face_height = i_face_height; \
1650         dst.i_ul_offset = i_ul_offset; \
1651         dst.i_ul_thickness = i_ul_thickness; \
1652     } while(0)
1653
1654         SAVE_BP( break_point );
1655         SAVE_BP( break_point_fallback );
1656
1657         while( i_index < i_start + i_length )
1658         {
1659             /* Split by common FT_Face + Size */
1660             const text_style_t *p_current_style = pp_styles[i_index];
1661             int i_part_length = 0;
1662             while( i_index + i_part_length < i_start + i_length )
1663             {
1664                 const text_style_t *p_style = pp_styles[i_index + i_part_length];
1665                 if( !FaceStyleEquals( p_style, p_current_style ) ||
1666                     p_style->i_font_size != p_current_style->i_font_size )
1667                     break;
1668                 i_part_length++;
1669             }
1670
1671             /* (Re)load/reconfigure the face if needed */
1672             if( !FaceStyleEquals( p_current_style, p_previous_style ) )
1673             {
1674                 if( p_face )
1675                     FT_Done_Face( p_face );
1676                 p_previous_style = NULL;
1677
1678                 p_face = LoadFace( p_filter, p_current_style );
1679             }
1680             FT_Face p_current_face = p_face ? p_face : p_sys->p_face;
1681             if( !p_previous_style || p_previous_style->i_font_size != p_current_style->i_font_size )
1682             {
1683                 if( FT_Set_Pixel_Sizes( p_current_face, 0, p_current_style->i_font_size ) )
1684                     msg_Err( p_filter, "Failed to set font size to %d", p_current_style->i_font_size );
1685                 if( p_sys->p_stroker )
1686                 {
1687                     double f_outline_thickness = var_InheritInteger( p_filter, "freetype-outline-thickness" ) / 100.0;
1688                     f_outline_thickness = VLC_CLIP( f_outline_thickness, 0.0, 0.5 );
1689                     int i_radius = (p_current_style->i_font_size << 6) * f_outline_thickness;
1690                     FT_Stroker_Set( p_sys->p_stroker,
1691                                     i_radius,
1692                                     FT_STROKER_LINECAP_ROUND,
1693                                     FT_STROKER_LINEJOIN_ROUND, 0 );
1694                 }
1695             }
1696             p_previous_style = p_current_style;
1697
1698             i_face_height = __MAX(i_face_height, FT_CEIL(FT_MulFix(p_current_face->height,
1699                                                                    p_current_face->size->metrics.y_scale)));
1700
1701             /* Render the part */
1702             bool b_break_line = false;
1703             int i_glyph_last = 0;
1704             while( i_part_length > 0 )
1705             {
1706                 const text_style_t *p_glyph_style = pp_styles[i_index];
1707                 uni_char_t character = psz_text[i_index];
1708                 int i_glyph_index = FT_Get_Char_Index( p_current_face, character );
1709
1710                 /* Get kerning vector */
1711                 FT_Vector kerning = { .x = 0, .y = 0 };
1712                 if( FT_HAS_KERNING( p_current_face ) && i_glyph_last != 0 && i_glyph_index != 0 )
1713                     FT_Get_Kerning( p_current_face, i_glyph_last, i_glyph_index, ft_kerning_default, &kerning );
1714
1715                 /* Get the glyph bitmap and its bounding box and all the associated properties */
1716                 FT_Vector pen_new = {
1717                     .x = pen.x + kerning.x,
1718                     .y = pen.y + kerning.y,
1719                 };
1720                 FT_Vector pen_shadow_new = {
1721                     .x = pen_new.x + p_sys->f_shadow_vector_x * (p_current_style->i_font_size << 6),
1722                     .y = pen_new.y + p_sys->f_shadow_vector_y * (p_current_style->i_font_size << 6),
1723                 };
1724                 FT_Glyph glyph;
1725                 FT_BBox  glyph_bbox;
1726                 FT_Glyph outline;
1727                 FT_BBox  outline_bbox;
1728                 FT_Glyph shadow;
1729                 FT_BBox  shadow_bbox;
1730
1731                 if( GetGlyph( p_filter,
1732                               &glyph, &glyph_bbox,
1733                               &outline, &outline_bbox,
1734                               &shadow, &shadow_bbox,
1735                               p_current_face, i_glyph_index, p_glyph_style->i_style_flags,
1736                               &pen_new, &pen_shadow_new ) )
1737                     goto next;
1738
1739                 FixGlyph( glyph, &glyph_bbox, p_current_face, &pen_new );
1740                 if( outline )
1741                     FixGlyph( outline, &outline_bbox, p_current_face, &pen_new );
1742                 if( shadow )
1743                     FixGlyph( shadow, &shadow_bbox, p_current_face, &pen_shadow_new );
1744
1745                 /* FIXME and what about outline */
1746
1747                 bool     b_karaoke = pi_karaoke_bar && pi_karaoke_bar[i_index] != 0;
1748                 uint32_t i_color = b_karaoke ? (p_glyph_style->i_karaoke_background_color |
1749                                                 (p_glyph_style->i_karaoke_background_alpha << 24))
1750                                              : (p_glyph_style->i_font_color |
1751                                                 (p_glyph_style->i_font_alpha << 24));
1752                 int i_line_offset    = 0;
1753                 int i_line_thickness = 0;
1754                 if( p_glyph_style->i_style_flags & (STYLE_UNDERLINE | STYLE_STRIKEOUT) )
1755                 {
1756                     i_line_offset = abs( FT_FLOOR(FT_MulFix(p_current_face->underline_position,
1757                                                             p_current_face->size->metrics.y_scale)) );
1758
1759                     i_line_thickness = abs( FT_CEIL(FT_MulFix(p_current_face->underline_thickness,
1760                                                               p_current_face->size->metrics.y_scale)) );
1761
1762                     if( p_glyph_style->i_style_flags & STYLE_STRIKEOUT )
1763                     {
1764                         /* Move the baseline to make it strikethrough instead of
1765                          * underline. That means that strikethrough takes precedence
1766                          */
1767                         i_line_offset -= abs( FT_FLOOR(FT_MulFix(p_current_face->descender*2,
1768                                                                  p_current_face->size->metrics.y_scale)) );
1769                     }
1770                     else if( i_line_thickness > 0 )
1771                     {
1772                         glyph_bbox.yMin = __MIN( glyph_bbox.yMin, - i_line_offset - i_line_thickness );
1773
1774                         /* The real underline thickness and position are
1775                          * updated once the whole line has been parsed */
1776                         i_ul_offset = __MAX( i_ul_offset, i_line_offset );
1777                         i_ul_thickness = __MAX( i_ul_thickness, i_line_thickness );
1778                         i_line_thickness = -1;
1779                     }
1780                 }
1781                 FT_BBox line_bbox_new = line_bbox;
1782                 BBoxEnlarge( &line_bbox_new, &glyph_bbox );
1783                 if( outline )
1784                     BBoxEnlarge( &line_bbox_new, &outline_bbox );
1785                 if( shadow )
1786                     BBoxEnlarge( &line_bbox_new, &shadow_bbox );
1787
1788                 b_break_line = i_index > i_start &&
1789                                line_bbox_new.xMax - line_bbox_new.xMin >= (int)p_filter->fmt_out.video.i_visible_width;
1790                 if( b_break_line )
1791                 {
1792                     FT_Done_Glyph( glyph );
1793                     if( outline )
1794                         FT_Done_Glyph( outline );
1795                     if( shadow )
1796                         FT_Done_Glyph( shadow );
1797
1798                     break_point_t *p_bp = NULL;
1799                     if( break_point.i_index > i_start )
1800                         p_bp = &break_point;
1801                     else if( break_point_fallback.i_index > i_start )
1802                         p_bp = &break_point_fallback;
1803
1804                     if( p_bp )
1805                     {
1806                         msg_Dbg( p_filter, "Breaking line");
1807                         for( int i = p_bp->i_index; i < i_index; i++ )
1808                         {
1809                             line_character_t *ch = &p_line->p_character[i - i_start];
1810                             FT_Done_Glyph( (FT_Glyph)ch->p_glyph );
1811                             if( ch->p_outline )
1812                                 FT_Done_Glyph( (FT_Glyph)ch->p_outline );
1813                             if( ch->p_shadow )
1814                                 FT_Done_Glyph( (FT_Glyph)ch->p_shadow );
1815                         }
1816                         p_line->i_character_count = p_bp->i_index - i_start;
1817
1818                         i_index = p_bp->i_index;
1819                         pen = p_bp->pen;
1820                         line_bbox = p_bp->line_bbox;
1821                         i_face_height = p_bp->i_face_height;
1822                         i_ul_offset = p_bp->i_ul_offset;
1823                         i_ul_thickness = p_bp->i_ul_thickness;
1824                     }
1825                     else
1826                     {
1827                         msg_Err( p_filter, "Breaking unbreakable line");
1828                     }
1829                     break;
1830                 }
1831
1832                 assert( p_line->i_character_count == i_index - i_start);
1833                 p_line->p_character[p_line->i_character_count++] = (line_character_t){
1834                     .p_glyph = (FT_BitmapGlyph)glyph,
1835                     .p_outline = (FT_BitmapGlyph)outline,
1836                     .p_shadow = (FT_BitmapGlyph)shadow,
1837                     .i_color = i_color,
1838                     .i_line_offset = i_line_offset,
1839                     .i_line_thickness = i_line_thickness,
1840                 };
1841
1842                 pen.x = pen_new.x + p_current_face->glyph->advance.x;
1843                 pen.y = pen_new.y + p_current_face->glyph->advance.y;
1844                 line_bbox = line_bbox_new;
1845             next:
1846                 i_glyph_last = i_glyph_index;
1847                 i_part_length--;
1848                 i_index++;
1849
1850                 if( character == ' ' || character == '\t' )
1851                     SAVE_BP( break_point );
1852                 else if( character == 160 )
1853                     SAVE_BP( break_point_fallback );
1854             }
1855             if( b_break_line )
1856                 break;
1857         }
1858 #undef SAVE_BP
1859         /* Update our baseline */
1860         if( i_face_height_previous > 0 )
1861             i_base_line += __MAX(i_face_height, i_face_height_previous);
1862         if( i_face_height > 0 )
1863             i_face_height_previous = i_face_height;
1864
1865         /* Update the line bbox with the actual base line */
1866         if (line_bbox.yMax > line_bbox.yMin) {
1867             line_bbox.yMin -= i_base_line;
1868             line_bbox.yMax -= i_base_line;
1869         }
1870         BBoxEnlarge( &bbox, &line_bbox );
1871
1872         /* Terminate and append the line */
1873         if( p_line )
1874         {
1875             p_line->i_width  = __MAX(line_bbox.xMax - line_bbox.xMin, 0);
1876             p_line->i_base_line = i_base_line;
1877             p_line->i_height = __MAX(i_face_height, i_face_height_previous);
1878             if( i_ul_thickness > 0 )
1879             {
1880                 for( int i = 0; i < p_line->i_character_count; i++ )
1881                 {
1882                     line_character_t *ch = &p_line->p_character[i];
1883                     if( ch->i_line_thickness < 0 )
1884                     {
1885                         ch->i_line_offset    = i_ul_offset;
1886                         ch->i_line_thickness = i_ul_thickness;
1887                     }
1888                 }
1889             }
1890
1891             *pp_line_next = p_line;
1892             pp_line_next = &p_line->p_next;
1893         }
1894
1895         *pi_max_face_height = __MAX( *pi_max_face_height, i_face_height );
1896
1897         /* Skip what we have rendered and the line delimitor if present */
1898         i_start = i_index;
1899         if( i_start < i_len && psz_text[i_start] == '\n' )
1900             i_start++;
1901
1902         if( bbox.yMax - bbox.yMin >= (int)p_filter->fmt_out.video.i_visible_height )
1903         {
1904             msg_Err( p_filter, "Truncated too high subtitle" );
1905             break;
1906         }
1907     }
1908     if( p_face )
1909         FT_Done_Face( p_face );
1910
1911     free( pp_fribidi_styles );
1912     free( p_fribidi_string );
1913     free( pi_karaoke_bar );
1914
1915     *p_bbox = bbox;
1916     return VLC_SUCCESS;
1917 }
1918
1919 /**
1920  * This function renders a text subpicture region into another one.
1921  * It also calculates the size needed for this string, and renders the
1922  * needed glyphs into memory. It is used as pf_add_string callback in
1923  * the vout method by this module
1924  */
1925 static int RenderCommon( filter_t *p_filter, subpicture_region_t *p_region_out,
1926                          subpicture_region_t *p_region_in, bool b_html,
1927                          const vlc_fourcc_t *p_chroma_list )
1928 {
1929     filter_sys_t *p_sys = p_filter->p_sys;
1930
1931     if( !p_region_in )
1932         return VLC_EGENERIC;
1933     if( b_html && !p_region_in->psz_html )
1934         return VLC_EGENERIC;
1935     if( !b_html && !p_region_in->psz_text )
1936         return VLC_EGENERIC;
1937
1938     const size_t i_text_max = strlen( b_html ? p_region_in->psz_html
1939                                              : p_region_in->psz_text );
1940
1941     uni_char_t *psz_text = calloc( i_text_max, sizeof( *psz_text ) );
1942     text_style_t **pp_styles = calloc( i_text_max, sizeof( *pp_styles ) );
1943     if( !psz_text || !pp_styles )
1944     {
1945         free( psz_text );
1946         free( pp_styles );
1947         return VLC_EGENERIC;
1948     }
1949
1950     /* Reset the default fontsize in case screen metrics have changed */
1951     p_filter->p_sys->style.i_font_size = GetFontSize( p_filter );
1952
1953     /* */
1954     int rv = VLC_SUCCESS;
1955     int i_text_length = 0;
1956     FT_BBox bbox;
1957     int i_max_face_height;
1958     line_desc_t *p_lines = NULL;
1959
1960     uint32_t *pi_k_durations   = NULL;
1961
1962     if( b_html )
1963     {
1964         stream_t *p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
1965                                             (uint8_t *) p_region_in->psz_html,
1966                                             strlen( p_region_in->psz_html ),
1967                                             true );
1968         if( unlikely(p_sub == NULL) )
1969             return VLC_SUCCESS;
1970
1971         xml_reader_t *p_xml_reader = p_filter->p_sys->p_xml;
1972         if( !p_xml_reader )
1973             p_xml_reader = xml_ReaderCreate( p_filter, p_sub );
1974         else
1975             p_xml_reader = xml_ReaderReset( p_xml_reader, p_sub );
1976         p_filter->p_sys->p_xml = p_xml_reader;
1977
1978         if( !p_xml_reader )
1979             rv = VLC_EGENERIC;
1980
1981         if( !rv )
1982         {
1983             /* Look for Root Node */
1984             const char *node;
1985
1986             if( xml_ReaderNextNode( p_xml_reader, &node ) == XML_READER_STARTELEM )
1987             {
1988                 if( strcasecmp( "karaoke", node ) == 0 )
1989                 {
1990                     pi_k_durations = calloc( i_text_max, sizeof( *pi_k_durations ) );
1991                 }
1992                 else if( strcasecmp( "text", node ) != 0 )
1993                 {
1994                     /* Only text and karaoke tags are supported */
1995                     msg_Dbg( p_filter, "Unsupported top-level tag <%s> ignored.",
1996                              node );
1997                     rv = VLC_EGENERIC;
1998                 }
1999             }
2000             else
2001             {
2002                 msg_Err( p_filter, "Malformed HTML subtitle" );
2003                 rv = VLC_EGENERIC;
2004             }
2005         }
2006         if( !rv )
2007         {
2008             rv = ProcessNodes( p_filter,
2009                                psz_text, pp_styles, pi_k_durations, &i_text_length,
2010                                p_xml_reader, p_region_in->p_style, &p_filter->p_sys->style );
2011         }
2012
2013         if( p_xml_reader )
2014             p_filter->p_sys->p_xml = xml_ReaderReset( p_xml_reader, NULL );
2015
2016         stream_Delete( p_sub );
2017     }
2018     else
2019     {
2020         text_style_t *p_style;
2021         if( p_region_in->p_style )
2022             p_style = CreateStyle( p_region_in->p_style->psz_fontname ? p_region_in->p_style->psz_fontname
2023                                                                       : p_sys->style.psz_fontname,
2024                                    p_region_in->p_style->i_font_size > 0 ? p_region_in->p_style->i_font_size
2025                                                                          : p_sys->style.i_font_size,
2026                                    (p_region_in->p_style->i_font_color & 0xffffff) |
2027                                    ((p_region_in->p_style->i_font_alpha & 0xff) << 24),
2028                                    0x00ffffff,
2029                                    p_region_in->p_style->i_style_flags & (STYLE_BOLD |
2030                                                                           STYLE_ITALIC |
2031                                                                           STYLE_UNDERLINE |
2032                                                                           STYLE_STRIKEOUT) );
2033         else
2034         {
2035             uint32_t i_font_color = var_InheritInteger( p_filter, "freetype-color" );
2036             i_font_color = VLC_CLIP( i_font_color, 0, 0xFFFFFF );
2037             p_style = CreateStyle( p_sys->style.psz_fontname,
2038                                    p_sys->style.i_font_size,
2039                                    (i_font_color & 0xffffff) |
2040                                    ((p_sys->style.i_font_alpha & 0xff) << 24),
2041                                    0x00ffffff, 0);
2042         }
2043         if( p_sys->style.i_style_flags & STYLE_BOLD )
2044             p_style->i_style_flags |= STYLE_BOLD;
2045
2046         i_text_length = SetupText( p_filter,
2047                                    psz_text,
2048                                    pp_styles,
2049                                    NULL,
2050                                    p_region_in->psz_text, p_style, 0 );
2051     }
2052
2053     if( !rv && i_text_length > 0 )
2054     {
2055         rv = ProcessLines( p_filter,
2056                            &p_lines, &bbox, &i_max_face_height,
2057                            psz_text, pp_styles, pi_k_durations, i_text_length );
2058     }
2059
2060     p_region_out->i_x = p_region_in->i_x;
2061     p_region_out->i_y = p_region_in->i_y;
2062
2063     /* Don't attempt to render text that couldn't be layed out
2064      * properly. */
2065     if( !rv && i_text_length > 0 && bbox.xMin < bbox.xMax && bbox.yMin < bbox.yMax )
2066     {
2067         const vlc_fourcc_t p_chroma_list_yuvp[] = { VLC_CODEC_YUVP, 0 };
2068         const vlc_fourcc_t p_chroma_list_rgba[] = { VLC_CODEC_RGBA, 0 };
2069
2070         if( var_InheritBool( p_filter, "freetype-yuvp" ) )
2071             p_chroma_list = p_chroma_list_yuvp;
2072         else if( !p_chroma_list || *p_chroma_list == 0 )
2073             p_chroma_list = p_chroma_list_rgba;
2074
2075         uint8_t i_background_opacity = var_InheritInteger( p_filter, "freetype-background-opacity" );
2076         i_background_opacity = VLC_CLIP( i_background_opacity, 0, 255 );
2077         const int i_margin = i_background_opacity > 0 ? i_max_face_height / 4 : 0;
2078         for( const vlc_fourcc_t *p_chroma = p_chroma_list; *p_chroma != 0; p_chroma++ )
2079         {
2080             rv = VLC_EGENERIC;
2081             if( *p_chroma == VLC_CODEC_YUVP )
2082                 rv = RenderYUVP( p_filter, p_region_out, p_lines, &bbox );
2083             else if( *p_chroma == VLC_CODEC_YUVA )
2084                 rv = RenderAXYZ( p_filter, p_region_out, p_lines, &bbox, i_margin,
2085                                  VLC_CODEC_YUVA,
2086                                  YUVFromRGB,
2087                                  FillYUVAPicture,
2088                                  BlendYUVAPixel );
2089             else if( *p_chroma == VLC_CODEC_RGBA )
2090                 rv = RenderAXYZ( p_filter, p_region_out, p_lines, &bbox, i_margin,
2091                                  VLC_CODEC_RGBA,
2092                                  RGBFromRGB,
2093                                  FillRGBAPicture,
2094                                  BlendRGBAPixel );
2095             if( !rv )
2096                 break;
2097         }
2098
2099         /* With karaoke, we're going to have to render the text a number
2100          * of times to show the progress marker on the text.
2101          */
2102         if( pi_k_durations )
2103             var_SetBool( p_filter, "text-rerender", true );
2104     }
2105
2106     FreeLines( p_lines );
2107
2108     free( psz_text );
2109     for( int i = 0; i < i_text_length; i++ )
2110     {
2111         if( pp_styles[i] && ( i + 1 == i_text_length || pp_styles[i] != pp_styles[i + 1] ) )
2112             text_style_Delete( pp_styles[i] );
2113     }
2114     free( pp_styles );
2115     free( pi_k_durations );
2116
2117     return rv;
2118 }
2119
2120 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
2121                        subpicture_region_t *p_region_in,
2122                        const vlc_fourcc_t *p_chroma_list )
2123 {
2124     return RenderCommon( p_filter, p_region_out, p_region_in, false, p_chroma_list );
2125 }
2126
2127 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2128                        subpicture_region_t *p_region_in,
2129                        const vlc_fourcc_t *p_chroma_list )
2130 {
2131     return RenderCommon( p_filter, p_region_out, p_region_in, true, p_chroma_list );
2132 }
2133
2134 /*****************************************************************************
2135  * Create: allocates osd-text video thread output method
2136  *****************************************************************************
2137  * This function allocates and initializes a Clone vout method.
2138  *****************************************************************************/
2139 static int Create( vlc_object_t *p_this )
2140 {
2141     filter_t      *p_filter = (filter_t *)p_this;
2142     filter_sys_t  *p_sys;
2143     char          *psz_fontfile   = NULL;
2144     char          *psz_fontname = NULL;
2145     char          *psz_monofontfile   = NULL;
2146     char          *psz_monofontfamily = NULL;
2147     int            i_error = 0, fontindex = 0, monofontindex = 0;
2148
2149     /* Allocate structure */
2150     p_filter->p_sys = p_sys = malloc( sizeof(*p_sys) );
2151     if( !p_sys )
2152         return VLC_ENOMEM;
2153
2154     p_sys->style.psz_fontname   = NULL;
2155     p_sys->p_xml            = NULL;
2156     p_sys->p_face           = 0;
2157     p_sys->p_library        = 0;
2158     p_sys->style.i_font_size      = 0;
2159
2160     /*
2161      * The following variables should not be cached, as they might be changed on-the-fly:
2162      * freetype-rel-fontsize, freetype-background-opacity, freetype-background-color,
2163      * freetype-outline-thickness, freetype-color
2164      *
2165      */
2166
2167     psz_fontname = var_InheritString( p_filter, "freetype-font" );
2168     psz_monofontfamily = var_InheritString( p_filter, "freetype-monofont" );
2169     p_sys->i_default_font_size = var_InheritInteger( p_filter, "freetype-fontsize" );
2170     p_sys->style.i_font_alpha = var_InheritInteger( p_filter,"freetype-opacity" );
2171     p_sys->style.i_font_alpha = VLC_CLIP( p_sys->style.i_font_alpha, 0, 255 );
2172     if( var_InheritBool( p_filter, "freetype-bold" ) )
2173         p_sys->style.i_style_flags |= STYLE_BOLD;
2174
2175     double f_outline_thickness = var_InheritInteger( p_filter, "freetype-outline-thickness" ) / 100.0;
2176     f_outline_thickness = VLC_CLIP( f_outline_thickness, 0.0, 0.5 );
2177     p_sys->style.i_outline_alpha = var_InheritInteger( p_filter, "freetype-outline-opacity" );
2178     p_sys->style.i_outline_alpha = VLC_CLIP( p_sys->style.i_outline_alpha, 0, 255 );
2179     p_sys->style.i_outline_color = var_InheritInteger( p_filter, "freetype-outline-color" );
2180     p_sys->style.i_outline_color = VLC_CLIP( p_sys->style.i_outline_color, 0, 0xFFFFFF );
2181
2182     p_sys->style.i_shadow_alpha = var_InheritInteger( p_filter, "freetype-shadow-opacity" );
2183     p_sys->style.i_shadow_alpha = VLC_CLIP( p_sys->style.i_shadow_alpha, 0, 255 );
2184     p_sys->style.i_shadow_color = var_InheritInteger( p_filter, "freetype-shadow-color" );
2185     p_sys->style.i_shadow_color = VLC_CLIP( p_sys->style.i_shadow_color, 0, 0xFFFFFF );
2186     float f_shadow_angle = var_InheritFloat( p_filter, "freetype-shadow-angle" );
2187     float f_shadow_distance = var_InheritFloat( p_filter, "freetype-shadow-distance" );
2188     f_shadow_distance = VLC_CLIP( f_shadow_distance, 0, 1 );
2189     p_sys->f_shadow_vector_x = f_shadow_distance * cos(2 * M_PI * f_shadow_angle / 360);
2190     p_sys->f_shadow_vector_y = f_shadow_distance * sin(2 * M_PI * f_shadow_angle / 360);
2191
2192 #ifdef _WIN32
2193     /* Get Windows Font folder */
2194     wchar_t wdir[MAX_PATH];
2195     if( S_OK != SHGetFolderPathW( NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, wdir ) )
2196     {
2197         GetWindowsDirectoryW( wdir, MAX_PATH );
2198         wcscat( wdir, L"\\fonts" );
2199     }
2200     p_sys->psz_win_fonts_path = FromWide( wdir );
2201 #endif
2202
2203     /* Set default psz_fontname */
2204     if( !psz_fontname || !*psz_fontname )
2205     {
2206         free( psz_fontname );
2207 #ifdef HAVE_STYLES
2208         psz_fontname = strdup( DEFAULT_FAMILY );
2209 #else
2210 # ifdef _WIN32
2211         if( asprintf( &psz_fontname, "%s"DEFAULT_FONT_FILE, p_sys->psz_win_fonts_path ) == -1 )
2212         {
2213             psz_fontname = NULL;
2214             goto error;
2215         }
2216 # else
2217         psz_fontname = strdup( DEFAULT_FONT_FILE );
2218 # endif
2219         msg_Err( p_filter,"User specified an empty fontfile, using %s", psz_fontname );
2220 #endif
2221     }
2222
2223     /* Set the current font file */
2224     p_sys->style.psz_fontname = psz_fontname;
2225 #ifdef HAVE_STYLES
2226 #ifdef HAVE_FONTCONFIG
2227     FontConfig_BuildCache( p_filter );
2228
2229     /* */
2230     psz_fontfile = FontConfig_Select( NULL, psz_fontname, false, false,
2231                                       p_sys->i_default_font_size, &fontindex );
2232     psz_monofontfile = FontConfig_Select( NULL, psz_monofontfamily, false,
2233                                           false, p_sys->i_default_font_size,
2234                                           &monofontindex );
2235 #elif defined(__APPLE__)
2236 #if !TARGET_OS_IPHONE
2237     psz_fontfile = MacLegacy_Select( p_filter, psz_fontname, false, false, -1, &fontindex );
2238 #endif
2239 #elif defined(_WIN32)
2240     psz_fontfile = Win32_Select( p_filter, psz_fontname, false, false, -1, &fontindex );
2241
2242 #endif
2243     msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontname, psz_fontfile );
2244
2245     /* If nothing is found, use the default family */
2246     if( !psz_fontfile )
2247         psz_fontfile = strdup( psz_fontname );
2248     if( !psz_monofontfile )
2249         psz_monofontfile = strdup( psz_monofontfamily );
2250
2251 #else /* !HAVE_STYLES */
2252     /* Use the default file */
2253     psz_fontfile = psz_fontname;
2254     psz_monofontfile = psz_monofontfamily;
2255 #endif
2256     p_sys->style.psz_monofontname = psz_monofontfamily;
2257
2258     /* */
2259     i_error = FT_Init_FreeType( &p_sys->p_library );
2260     if( i_error )
2261     {
2262         msg_Err( p_filter, "couldn't initialize freetype" );
2263         goto error;
2264     }
2265
2266     i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
2267                            fontindex, &p_sys->p_face );
2268
2269     if( i_error == FT_Err_Unknown_File_Format )
2270     {
2271         msg_Err( p_filter, "file %s have unknown format",
2272                  psz_fontfile ? psz_fontfile : "(null)" );
2273         goto error;
2274     }
2275     else if( i_error )
2276     {
2277         msg_Err( p_filter, "failed to load font file %s",
2278                  psz_fontfile ? psz_fontfile : "(null)" );
2279         goto error;
2280     }
2281
2282     i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
2283     if( i_error )
2284     {
2285         msg_Err( p_filter, "font has no unicode translation table" );
2286         goto error;
2287     }
2288
2289     if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
2290
2291     p_sys->p_stroker = NULL;
2292     if( f_outline_thickness > 0.001 )
2293     {
2294         i_error = FT_Stroker_New( p_sys->p_library, &p_sys->p_stroker );
2295         if( i_error )
2296             msg_Err( p_filter, "Failed to create stroker for outlining" );
2297     }
2298
2299     p_sys->pp_font_attachments = NULL;
2300     p_sys->i_font_attachments = 0;
2301
2302     p_filter->pf_render_text = RenderText;
2303     p_filter->pf_render_html = RenderHtml;
2304
2305     LoadFontsFromAttachments( p_filter );
2306
2307 #ifdef HAVE_STYLES
2308     free( psz_fontfile );
2309     free( psz_monofontfile );
2310 #endif
2311
2312     return VLC_SUCCESS;
2313
2314 error:
2315     if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
2316     if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
2317 #ifdef HAVE_STYLES
2318     free( psz_fontfile );
2319     free( psz_monofontfile );
2320 #endif
2321     free( psz_fontname );
2322     free( psz_monofontfamily );
2323     free( p_sys );
2324     return VLC_EGENERIC;
2325 }
2326
2327 /*****************************************************************************
2328  * Destroy: destroy Clone video thread output method
2329  *****************************************************************************
2330  * Clean up all data and library connections
2331  *****************************************************************************/
2332 static void Destroy( vlc_object_t *p_this )
2333 {
2334     filter_t *p_filter = (filter_t *)p_this;
2335     filter_sys_t *p_sys = p_filter->p_sys;
2336
2337     if( p_sys->pp_font_attachments )
2338     {
2339         for( int k = 0; k < p_sys->i_font_attachments; k++ )
2340             vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
2341
2342         free( p_sys->pp_font_attachments );
2343     }
2344
2345     if( p_sys->p_xml ) xml_ReaderDelete( p_sys->p_xml );
2346     free( p_sys->style.psz_fontname );
2347     free( p_sys->style.psz_monofontname );
2348
2349 #ifdef _WIN32
2350     free( p_sys->psz_win_fonts_path );
2351 #endif
2352
2353     /* FcFini asserts calling the subfunction FcCacheFini()
2354      * even if no other library functions have been made since FcInit(),
2355      * so don't call it. */
2356
2357     if( p_sys->p_stroker )
2358         FT_Stroker_Done( p_sys->p_stroker );
2359     FT_Done_Face( p_sys->p_face );
2360     FT_Done_FreeType( p_sys->p_library );
2361     free( p_sys );
2362 }