1 /*****************************************************************************
2 * freetype.c : Put text on the video, using freetype2
3 *****************************************************************************
4 * Copyright (C) 2002 - 2012 VLC authors and VideoLAN
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>
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.
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.
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 *****************************************************************************/
28 /*****************************************************************************
30 *****************************************************************************/
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_strings.h> /* resolve_xml_special_chars */
43 #include <vlc_charset.h> /* ToCharset */
44 #include <vlc_dialog.h> /* FcCache dialog */
45 #include <vlc_filter.h> /* filter_sys_t */
46 #include <vlc_text_style.h> /* text_style_t*/
50 # define SYSTEM_DEFAULT_FONT_FILE "/Library/Fonts/Arial Unicode.ttf"
51 # define SYSTEM_DEFAULT_FAMILY "Arial Unicode MS"
52 # define SYSTEM_DEFAULT_MONOSPACE_FONT_FILE "/System/Library/Fonts/Monaco.dfont"
53 # define SYSTEM_DEFAULT_MONOSPACE_FAMILY "Monaco"
54 #elif defined( WIN32 )
55 # define SYSTEM_DEFAULT_FONT_FILE "arial.ttf" /* Default path font found at run-time */
56 # define SYSTEM_DEFAULT_FAMILY "Arial"
57 # define SYSTEM_DEFAULT_MONOSPACE_FONT_FILE "cour.ttf"
58 # define SYSTEM_DEFAULT_MONOSPACE_FAMILY "Courier New"
59 #elif defined( __OS2__ )
60 # define SYSTEM_DEFAULT_FONT_FILE "/psfonts/tnrwt_k.ttf"
61 # define SYSTEM_DEFAULT_FAMILY "Times New Roman WT K"
62 # define SYSTEM_DEFAULT_MONOSPACE_FONT_FILE "/psfonts/mtsansdk.ttf"
63 # define SYSTEM_DEFAULT_MONOSPACE_FAMILY "Monotype Sans Duospace WT K"
64 #elif defined( __ANDROID__ )
65 # define SYSTEM_DEFAULT_FONT_FILE "/system/fonts/DroidSans-Bold.ttf"
66 # define SYSTEM_DEFAULT_FAMILY "Droid Sans Bold"
67 # define SYSTEM_DEFAULT_MONOSPACE_FONT_FILE "/system/fonts/DroidSansMono.ttf"
68 # define SYSTEM_DEFAULT_MONOSPACE_FAMILY "Droid Sans Mono"
70 # define SYSTEM_DEFAULT_FONT_FILE "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
71 # define SYSTEM_DEFAULT_FAMILY "Serif Bold"
72 # define SYSTEM_DEFAULT_MONOSPACE_FONT_FILE "/usr/share/fonts/truetype/freefont/FreeMono.ttf"
73 # define SYSTEM_DEFAULT_MONOSPACE_FAMILY "Monospace"
76 #ifndef DEFAULT_FONT_FILE
77 #define DEFAULT_FONT_FILE SYSTEM_DEFAULT_FONT_FILE
80 #ifndef DEFAULT_FAMILY
81 #define DEFAULT_FAMILY SYSTEM_DEFAULT_FAMILY
84 #ifndef DEFAULT_MONOSPACE_FONT_FILE
85 #define DEFAULT_MONOSPACE_FONT_FILE SYSTEM_DEFAULT_MONOSPACE_FONT_FILE
88 #ifndef DEFAULT_MONOSPACE_FAMILY
89 #define DEFAULT_MONOSPACE_FAMILY SYSTEM_DEFAULT_MONOSPACE_FAMILY
93 #include <freetype/ftsynth.h>
94 #include FT_FREETYPE_H
98 #define FT_FLOOR(X) ((X & -64) >> 6)
99 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
101 #define FT_MulFix(v, s) (((v)*(s))>>16)
106 #include <Carbon/Carbon.h>
107 #include <sys/param.h> /* for MAXPATHLEN */
108 #undef HAVE_FONTCONFIG
113 #if defined(HAVE_FRIBIDI)
114 # include <fribidi/fribidi.h>
119 # include <windows.h>
122 # undef HAVE_FONTCONFIG
126 #ifdef HAVE_FONTCONFIG
127 # include <fontconfig/fontconfig.h>
134 typedef uint16_t uni_char_t;
135 # define FREETYPE_TO_UCS "UCS-2LE"
137 typedef uint32_t uni_char_t;
138 # if defined(WORDS_BIGENDIAN)
139 # define FREETYPE_TO_UCS "UCS-4BE"
141 # define FREETYPE_TO_UCS "UCS-4LE"
145 /*****************************************************************************
147 *****************************************************************************/
148 static int Create ( vlc_object_t * );
149 static void Destroy( vlc_object_t * );
151 #define FONT_TEXT N_("Font")
152 #define MONOSPACE_FONT_TEXT N_("Monospace Font")
154 #define FAMILY_LONGTEXT N_("Font family for the font you want to use")
155 #define FONT_LONGTEXT N_("Font file for the font you want to use")
157 #define FONTSIZE_TEXT N_("Font size in pixels")
158 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
159 "that will be rendered on the video. " \
160 "If set to something different than 0 this option will override the " \
161 "relative font size." )
162 #define OPACITY_TEXT N_("Text opacity")
163 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
164 "text that will be rendered on the video. 0 = transparent, " \
165 "255 = totally opaque. " )
166 #define COLOR_TEXT N_("Text default color")
167 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
168 "the video. This must be an hexadecimal (like HTML colors). The first two "\
169 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
170 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
171 #define FONTSIZER_TEXT N_("Relative font size")
172 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
173 "fonts that will be rendered on the video. If absolute font size is set, "\
174 "relative size will be overridden." )
175 #define BOLD_TEXT N_("Force bold")
177 #define BG_OPACITY_TEXT N_("Background opacity")
178 #define BG_COLOR_TEXT N_("Background color")
180 #define OUTLINE_OPACITY_TEXT N_("Outline opacity")
181 #define OUTLINE_COLOR_TEXT N_("Outline color")
182 #define OUTLINE_THICKNESS_TEXT N_("Outline thickness")
184 #define SHADOW_OPACITY_TEXT N_("Shadow opacity")
185 #define SHADOW_COLOR_TEXT N_("Shadow color")
186 #define SHADOW_ANGLE_TEXT N_("Shadow angle")
187 #define SHADOW_DISTANCE_TEXT N_("Shadow distance")
190 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
191 static const char *const ppsz_sizes_text[] = {
192 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
193 #define YUVP_TEXT N_("Use YUVP renderer")
194 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
195 "This option is only needed if you want to encode into DVB subtitles" )
197 static const int pi_color_values[] = {
198 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
199 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
200 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
202 static const char *const ppsz_color_descriptions[] = {
203 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
204 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
205 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
207 static const int pi_outline_thickness[] = {
210 static const char *const ppsz_outline_thickness[] = {
211 N_("None"), N_("Thin"), N_("Normal"), N_("Thick"),
215 set_shortname( N_("Text renderer"))
216 set_description( N_("Freetype2 font renderer") )
217 set_category( CAT_VIDEO )
218 set_subcategory( SUBCAT_VIDEO_SUBPIC )
221 add_font( "freetype-font", DEFAULT_FAMILY, FONT_TEXT, FAMILY_LONGTEXT, false )
222 add_font( "freetype-monofont", DEFAULT_MONOSPACE_FAMILY, MONOSPACE_FONT_TEXT, FAMILY_LONGTEXT, false )
224 add_loadfile( "freetype-font", DEFAULT_FONT_FILE, FONT_TEXT, FONT_LONGTEXT, false )
225 add_loadfile( "freetype-monofont", DEFAULT_MONOSPACE_FONT_FILE, MONOSPACE_FONT_TEXT, FONT_LONGTEXT, false )
228 add_integer( "freetype-fontsize", 0, FONTSIZE_TEXT,
229 FONTSIZE_LONGTEXT, true )
230 change_integer_range( 0, 4096)
233 add_integer( "freetype-rel-fontsize", 16, FONTSIZER_TEXT,
234 FONTSIZER_LONGTEXT, false )
235 change_integer_list( pi_sizes, ppsz_sizes_text )
238 /* opacity valid on 0..255, with default 255 = fully opaque */
239 add_integer_with_range( "freetype-opacity", 255, 0, 255,
240 OPACITY_TEXT, OPACITY_LONGTEXT, false )
243 /* hook to the color values list, with default 0x00ffffff = white */
244 add_rgb( "freetype-color", 0x00FFFFFF, COLOR_TEXT,
245 COLOR_LONGTEXT, false )
246 change_integer_list( pi_color_values, ppsz_color_descriptions )
249 add_bool( "freetype-bold", false, BOLD_TEXT, NULL, false )
252 add_integer_with_range( "freetype-background-opacity", 0, 0, 255,
253 BG_OPACITY_TEXT, NULL, false )
255 add_rgb( "freetype-background-color", 0x00000000, BG_COLOR_TEXT,
257 change_integer_list( pi_color_values, ppsz_color_descriptions )
260 add_integer_with_range( "freetype-outline-opacity", 255, 0, 255,
261 OUTLINE_OPACITY_TEXT, NULL, false )
263 add_rgb( "freetype-outline-color", 0x00000000, OUTLINE_COLOR_TEXT,
265 change_integer_list( pi_color_values, ppsz_color_descriptions )
267 add_integer_with_range( "freetype-outline-thickness", 4, 0, 50, OUTLINE_THICKNESS_TEXT,
269 change_integer_list( pi_outline_thickness, ppsz_outline_thickness )
272 add_integer_with_range( "freetype-shadow-opacity", 128, 0, 255,
273 SHADOW_OPACITY_TEXT, NULL, false )
275 add_rgb( "freetype-shadow-color", 0x00000000, SHADOW_COLOR_TEXT,
277 change_integer_list( pi_color_values, ppsz_color_descriptions )
279 add_float_with_range( "freetype-shadow-angle", -45, -360, 360,
280 SHADOW_ANGLE_TEXT, NULL, false )
282 add_float_with_range( "freetype-shadow-distance", 0.06, 0.0, 1.0,
283 SHADOW_DISTANCE_TEXT, NULL, false )
286 add_obsolete_integer( "freetype-effect" );
288 add_bool( "freetype-yuvp", false, YUVP_TEXT,
289 YUVP_LONGTEXT, true )
290 set_capability( "text renderer", 100 )
291 add_shortcut( "text" )
292 set_callbacks( Create, Destroy )
296 /*****************************************************************************
298 *****************************************************************************/
302 FT_BitmapGlyph p_glyph;
303 FT_BitmapGlyph p_outline;
304 FT_BitmapGlyph p_shadow;
305 uint32_t i_color; /* ARGB color */
306 int i_line_offset; /* underline/strikethrough offset */
307 int i_line_thickness; /* underline/strikethrough thickness */
310 typedef struct line_desc_t line_desc_t;
318 int i_character_count;
319 line_character_t *p_character;
322 typedef struct font_stack_t font_stack_t;
327 uint32_t i_color; /* ARGB */
328 uint32_t i_karaoke_bg_color; /* ARGB */
330 font_stack_t *p_next;
333 /*****************************************************************************
334 * filter_sys_t: freetype local data
335 *****************************************************************************
336 * This structure is part of the video output thread descriptor.
337 * It describes the freetype specific properties of an output thread.
338 *****************************************************************************/
341 FT_Library p_library; /* handle to library */
342 FT_Face p_face; /* handle to face object */
343 FT_Stroker p_stroker;
344 uint8_t i_font_opacity;
349 uint8_t i_background_opacity;
350 int i_background_color;
352 double f_outline_thickness;
353 uint8_t i_outline_opacity;
356 float f_shadow_vector_x;
357 float f_shadow_vector_y;
358 uint8_t i_shadow_opacity;
361 int i_default_font_size;
362 int i_display_height;
363 char* psz_fontfamily;
364 char* psz_monofontfamily;
367 char* psz_win_fonts_path;
370 input_attachment_t **pp_font_attachments;
371 int i_font_attachments;
375 static void YUVFromRGB( uint32_t i_argb,
376 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
378 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
379 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
380 int i_blue = ( i_argb & 0x000000ff );
382 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
383 802 * i_blue + 4096 + 131072 ) >> 13, 235);
384 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
385 3598 * i_blue + 4096 + 1048576) >> 13, 240);
386 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
387 -585 * i_blue + 4096 + 1048576) >> 13, 240);
389 static void RGBFromRGB( uint32_t i_argb,
390 uint8_t *pi_r, uint8_t *pi_g, uint8_t *pi_b )
392 *pi_r = ( i_argb & 0x00ff0000 ) >> 16;
393 *pi_g = ( i_argb & 0x0000ff00 ) >> 8;
394 *pi_b = ( i_argb & 0x000000ff );
396 /*****************************************************************************
397 * Make any TTF/OTF fonts present in the attachments of the media file
398 * and store them for later use by the FreeType Engine
399 *****************************************************************************/
400 static int LoadFontsFromAttachments( filter_t *p_filter )
402 filter_sys_t *p_sys = p_filter->p_sys;
403 input_attachment_t **pp_attachments;
404 int i_attachments_cnt;
406 if( filter_GetInputAttachments( p_filter, &pp_attachments, &i_attachments_cnt ) )
409 p_sys->i_font_attachments = 0;
410 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof(*p_sys->pp_font_attachments));
411 if( !p_sys->pp_font_attachments )
414 for( int k = 0; k < i_attachments_cnt; k++ )
416 input_attachment_t *p_attach = pp_attachments[k];
418 if( ( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
419 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
420 p_attach->i_data > 0 && p_attach->p_data )
422 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
426 vlc_input_attachment_Delete( p_attach );
429 free( pp_attachments );
434 static int GetFontSize( filter_t *p_filter )
436 filter_sys_t *p_sys = p_filter->p_sys;
439 if( p_sys->i_default_font_size )
441 i_size = p_sys->i_default_font_size;
445 int i_ratio = var_GetInteger( p_filter, "freetype-rel-fontsize" );
448 i_size = (int)p_filter->fmt_out.video.i_height / i_ratio;
449 p_filter->p_sys->i_display_height = p_filter->fmt_out.video.i_height;
454 msg_Warn( p_filter, "invalid fontsize, using 12" );
460 static int SetFontSize( filter_t *p_filter, int i_size )
462 filter_sys_t *p_sys = p_filter->p_sys;
466 i_size = GetFontSize( p_filter );
468 msg_Dbg( p_filter, "using fontsize: %i", i_size );
471 p_sys->i_font_size = i_size;
473 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
475 msg_Err( p_filter, "couldn't set font size to %d", i_size );
483 #ifdef HAVE_FONTCONFIG
484 static void FontConfig_BuildCache( filter_t *p_filter )
487 msg_Dbg( p_filter, "Building font databases.");
495 #if defined( WIN32 ) || defined( __APPLE__ )
496 dialog_progress_bar_t *p_dialog = NULL;
497 FcConfig *fcConfig = FcInitLoadConfig();
499 p_dialog = dialog_ProgressCreate( p_filter,
500 _("Building font cache"),
501 _("Please wait while your font cache is rebuilt.\n"
502 "This should take less than a few minutes."), NULL );
505 dialog_ProgressSet( p_dialog, NULL, 0.5 ); */
507 FcConfigBuildFonts( fcConfig );
508 #if defined( __APPLE__ )
509 // By default, scan only the directory /System/Library/Fonts.
510 // So build the set of available fonts under another directories,
511 // and add the set to the current configuration.
512 FcConfigAppFontAddDir( NULL, "~/Library/Fonts" );
513 FcConfigAppFontAddDir( NULL, "/Library/Fonts" );
514 FcConfigAppFontAddDir( NULL, "/Network/Library/Fonts" );
515 //FcConfigAppFontAddDir( NULL, "/System/Library/Fonts" );
519 // dialog_ProgressSet( p_dialog, NULL, 1.0 );
520 dialog_ProgressDestroy( p_dialog );
525 msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
529 * \brief Selects a font matching family, bold, italic provided
531 static char* FontConfig_Select( FcConfig* config, const char* family,
532 bool b_bold, bool b_italic, int i_size, int *i_idx )
534 FcResult result = FcResultMatch;
535 FcPattern *pat, *p_pat;
540 /* Create a pattern and fills it */
541 pat = FcPatternCreate();
542 if (!pat) return NULL;
545 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
546 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
547 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
548 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
552 if( asprintf( &psz_fontsize, "%d", i_size ) != -1 )
554 FcPatternAddString( pat, FC_SIZE, (const FcChar8 *)psz_fontsize );
555 free( psz_fontsize );
560 FcDefaultSubstitute( pat );
561 if( !FcConfigSubstitute( config, pat, FcMatchPattern ) )
563 FcPatternDestroy( pat );
567 /* Find the best font for the pattern, destroy the pattern */
568 p_pat = FcFontMatch( config, pat, &result );
569 FcPatternDestroy( pat );
570 if( !p_pat || result == FcResultNoMatch ) return NULL;
572 /* Check the new pattern */
573 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
574 || ( val_b != FcTrue ) )
576 FcPatternDestroy( p_pat );
579 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
584 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
586 FcPatternDestroy( p_pat );
590 /* if( strcasecmp((const char*)val_s, family ) != 0 )
591 msg_Warn( p_filter, "fontconfig: selected font family is not"
592 "the requested one: '%s' != '%s'\n",
593 (const char*)val_s, family ); */
595 if( FcResultMatch == FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
596 ret = strdup( (const char*)val_s );
598 FcPatternDestroy( p_pat );
604 #define FONT_DIR_NT _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts")
606 static int GetFileFontByName( LPCTSTR font_name, char **psz_filename )
609 TCHAR vbuffer[MAX_PATH];
612 if( RegOpenKeyEx(HKEY_LOCAL_MACHINE, FONT_DIR_NT, 0, KEY_READ, &hKey)
616 char *font_name_temp = FromT( font_name );
617 size_t fontname_len = strlen( font_name_temp );
619 for( int index = 0;; index++ )
621 DWORD vbuflen = MAX_PATH - 1;
624 LONG i_result = RegEnumValue( hKey, index, vbuffer, &vbuflen,
625 NULL, NULL, (LPBYTE)dbuffer, &dbuflen);
626 if( i_result != ERROR_SUCCESS )
632 char *psz_value = FromT( vbuffer );
634 char *s = strchr( psz_value,'(' );
635 if( s != NULL && s != psz_value ) s[-1] = '\0';
637 /* Manage concatenated font names */
638 if( strchr( psz_value, '&') ) {
639 if( strcasestr( psz_value, font_name_temp ) != NULL )
646 if( strncasecmp( psz_value, font_name_temp, fontname_len ) == 0 )
656 *psz_filename = FromT( dbuffer );
657 free( font_name_temp );
663 static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *lpelfe, const NEWTEXTMETRICEX *metric,
664 DWORD type, LPARAM lParam)
666 VLC_UNUSED( metric );
667 if( (type & RASTER_FONTTYPE) ) return 1;
668 // if( lpelfe->elfScript ) FIXME
670 return GetFileFontByName( (LPCTSTR)lpelfe->elfFullName, (char **)lParam );
673 static char* Win32_Select( filter_t *p_filter, const char* family,
674 bool b_bold, bool b_italic, int i_size, int *i_idx )
676 VLC_UNUSED( i_size );
678 if( !family || strlen( family ) < 1 )
683 lf.lfCharSet = DEFAULT_CHARSET;
687 lf.lfWeight = FW_BOLD;
689 LPTSTR psz_fbuffer = ToT( family );
690 _tcsncpy( (LPTSTR)&lf.lfFaceName, psz_fbuffer, LF_FACESIZE );
694 char *psz_filename = NULL;
695 HDC hDC = GetDC( NULL );
696 EnumFontFamiliesEx(hDC, &lf, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&psz_filename, 0);
697 ReleaseDC(NULL, hDC);
700 if( psz_filename != NULL )
702 /* FIXME: increase i_idx, when concatenated strings */
705 /* Prepend the Windows Font path, when only a filename was provided */
706 if( strchr( psz_filename, DIR_SEP_CHAR ) )
711 if( asprintf( &psz_tmp, "%s\\%s", p_filter->p_sys->psz_win_fonts_path, psz_filename ) == -1 )
713 free( psz_filename );
716 free( psz_filename );
720 else /* Let's take any font we can */
724 if( asprintf( &psz_tmp, "%s\\%s", p_filter->p_sys->psz_win_fonts_path, "arial.ttf" ) == -1 )
730 #endif /* HAVE_WIN32 */
733 static char* MacLegacy_Select( filter_t *p_filter, const char* psz_fontname,
734 bool b_bold, bool b_italic, int i_size, int *i_idx )
736 VLC_UNUSED( b_bold );
737 VLC_UNUSED( b_italic );
738 VLC_UNUSED( i_size );
740 unsigned char path[MAXPATHLEN];
743 CFStringRef cf_fontName;
744 ATSFontRef ats_font_id;
748 if( psz_fontname == NULL )
751 msg_Dbg( p_filter, "looking for %s", psz_fontname );
752 cf_fontName = CFStringCreateWithCString( kCFAllocatorDefault, psz_fontname, kCFStringEncodingUTF8 );
754 ats_font_id = ATSFontFindFromName( cf_fontName, kATSOptionFlagsIncludeDisabledMask );
756 if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL )
758 msg_Dbg( p_filter, "ATS couldn't find %s by name, checking family", psz_fontname );
759 ats_font_id = ATSFontFamilyFindFromName( cf_fontName, kATSOptionFlagsDefault );
761 if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL )
763 msg_Dbg( p_filter, "ATS couldn't find either %s nor its family, checking PS name", psz_fontname );
764 ats_font_id = ATSFontFindFromPostScriptName( cf_fontName, kATSOptionFlagsDefault );
766 if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL )
768 msg_Err( p_filter, "ATS couldn't find %s (no font name, family or PS name)", psz_fontname );
769 CFRelease( cf_fontName );
774 CFRelease( cf_fontName );
776 if ( noErr != ATSFontGetFileReference( ats_font_id, &ref ) )
778 msg_Err( p_filter, "ATS couldn't get file ref for %s", psz_fontname );
782 /* i_idx calculation by searching preceding fontIDs */
783 /* with same FSRef */
785 ATSFontRef id2 = ats_font_id - 1;
790 if ( noErr != ATSFontGetFileReference( id2, &ref2 ) )
792 if ( noErr != FSCompareFSRefs( &ref, &ref2 ) )
797 *i_idx = ats_font_id - ( id2 + 1 );
800 if ( noErr != FSRefMakePath( &ref, path, sizeof(path) ) )
802 msg_Err( p_filter, "failure when getting path from FSRef" );
805 msg_Dbg( p_filter, "found %s", path );
807 psz_path = strdup( (char *)path );
813 #endif /* HAVE_STYLES */
816 /*****************************************************************************
817 * RenderYUVP: place string in picture
818 *****************************************************************************
819 * This function merges the previously rendered freetype glyphs into a picture
820 *****************************************************************************/
821 static int RenderYUVP( filter_t *p_filter, subpicture_region_t *p_region,
825 VLC_UNUSED(p_filter);
826 static const uint8_t pi_gamma[16] =
827 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
828 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
832 int i, x, y, i_pitch;
833 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
835 /* Create a new subpicture region */
836 video_format_Init( &fmt, VLC_CODEC_YUVP );
838 fmt.i_visible_width = p_bbox->xMax - p_bbox->xMin + 4;
840 fmt.i_visible_height = p_bbox->yMax - p_bbox->yMin + 4;
842 assert( !p_region->p_picture );
843 p_region->p_picture = picture_NewFromFormat( &fmt );
844 if( !p_region->p_picture )
846 fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
849 /* Calculate text color components
850 * Only use the first color */
851 int i_alpha = (p_line->p_character[0].i_color >> 24) & 0xff;
852 YUVFromRGB( p_line->p_character[0].i_color, &i_y, &i_u, &i_v );
855 fmt.p_palette->i_entries = 16;
856 for( i = 0; i < 8; i++ )
858 fmt.p_palette->palette[i][0] = 0;
859 fmt.p_palette->palette[i][1] = 0x80;
860 fmt.p_palette->palette[i][2] = 0x80;
861 fmt.p_palette->palette[i][3] = pi_gamma[i];
862 fmt.p_palette->palette[i][3] =
863 (int)fmt.p_palette->palette[i][3] * i_alpha / 255;
865 for( i = 8; i < fmt.p_palette->i_entries; i++ )
867 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
868 fmt.p_palette->palette[i][1] = i_u;
869 fmt.p_palette->palette[i][2] = i_v;
870 fmt.p_palette->palette[i][3] = pi_gamma[i];
871 fmt.p_palette->palette[i][3] =
872 (int)fmt.p_palette->palette[i][3] * i_alpha / 255;
875 p_dst = p_region->p_picture->Y_PIXELS;
876 i_pitch = p_region->p_picture->Y_PITCH;
878 /* Initialize the region pixels */
879 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
881 for( ; p_line != NULL; p_line = p_line->p_next )
883 int i_align_left = 0;
884 if( p_line->i_width < (int)fmt.i_visible_width )
886 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
887 i_align_left = ( fmt.i_visible_width - p_line->i_width );
888 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
889 i_align_left = ( fmt.i_visible_width - p_line->i_width ) / 2;
893 for( i = 0; i < p_line->i_character_count; i++ )
895 const line_character_t *ch = &p_line->p_character[i];
896 FT_BitmapGlyph p_glyph = ch->p_glyph;
898 int i_glyph_y = i_align_top - p_glyph->top + p_bbox->yMax + p_line->i_base_line;
899 int i_glyph_x = i_align_left + p_glyph->left - p_bbox->xMin;
901 for( y = 0; y < p_glyph->bitmap.rows; y++ )
903 for( x = 0; x < p_glyph->bitmap.width; x++ )
905 if( p_glyph->bitmap.buffer[y * p_glyph->bitmap.width + x] )
906 p_dst[(i_glyph_y + y) * i_pitch + (i_glyph_x + x)] =
907 (p_glyph->bitmap.buffer[y * p_glyph->bitmap.width + x] + 8)/16;
913 /* Outlining (find something better than nearest neighbour filtering ?) */
916 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
917 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
918 uint8_t left, current;
920 for( y = 1; y < (int)fmt.i_height - 1; y++ )
922 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
923 p_dst += p_region->p_picture->Y_PITCH;
926 for( x = 1; x < (int)fmt.i_width - 1; x++ )
929 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
930 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;
934 memset( p_top, 0, fmt.i_width );
940 /*****************************************************************************
941 * RenderYUVA: place string in picture
942 *****************************************************************************
943 * This function merges the previously rendered freetype glyphs into a picture
944 *****************************************************************************/
945 static void FillYUVAPicture( picture_t *p_picture,
946 int i_a, int i_y, int i_u, int i_v )
948 memset( p_picture->p[0].p_pixels, i_y,
949 p_picture->p[0].i_pitch * p_picture->p[0].i_lines );
950 memset( p_picture->p[1].p_pixels, i_u,
951 p_picture->p[1].i_pitch * p_picture->p[1].i_lines );
952 memset( p_picture->p[2].p_pixels, i_v,
953 p_picture->p[2].i_pitch * p_picture->p[2].i_lines );
954 memset( p_picture->p[3].p_pixels, i_a,
955 p_picture->p[3].i_pitch * p_picture->p[3].i_lines );
958 static inline void BlendYUVAPixel( picture_t *p_picture,
959 int i_picture_x, int i_picture_y,
960 int i_a, int i_y, int i_u, int i_v,
963 int i_an = i_a * i_alpha / 255;
965 uint8_t *p_y = &p_picture->p[0].p_pixels[i_picture_y * p_picture->p[0].i_pitch + i_picture_x];
966 uint8_t *p_u = &p_picture->p[1].p_pixels[i_picture_y * p_picture->p[1].i_pitch + i_picture_x];
967 uint8_t *p_v = &p_picture->p[2].p_pixels[i_picture_y * p_picture->p[2].i_pitch + i_picture_x];
968 uint8_t *p_a = &p_picture->p[3].p_pixels[i_picture_y * p_picture->p[3].i_pitch + i_picture_x];
980 *p_a = 255 - (255 - *p_a) * (255 - i_an) / 255;
983 *p_y = ( *p_y * i_ao * (255 - i_an) / 255 + i_y * i_an ) / *p_a;
984 *p_u = ( *p_u * i_ao * (255 - i_an) / 255 + i_u * i_an ) / *p_a;
985 *p_v = ( *p_v * i_ao * (255 - i_an) / 255 + i_v * i_an ) / *p_a;
990 static void FillRGBAPicture( picture_t *p_picture,
991 int i_a, int i_r, int i_g, int i_b )
993 for( int dy = 0; dy < p_picture->p[0].i_visible_lines; dy++ )
995 for( int dx = 0; dx < p_picture->p[0].i_visible_pitch; dx += 4 )
997 uint8_t *p_rgba = &p_picture->p->p_pixels[dy * p_picture->p->i_pitch + dx];
1006 static inline void BlendRGBAPixel( picture_t *p_picture,
1007 int i_picture_x, int i_picture_y,
1008 int i_a, int i_r, int i_g, int i_b,
1011 int i_an = i_a * i_alpha / 255;
1013 uint8_t *p_rgba = &p_picture->p->p_pixels[i_picture_y * p_picture->p->i_pitch + 4 * i_picture_x];
1015 int i_ao = p_rgba[3];
1025 p_rgba[3] = 255 - (255 - p_rgba[3]) * (255 - i_an) / 255;
1026 if( p_rgba[3] != 0 )
1028 p_rgba[0] = ( p_rgba[0] * i_ao * (255 - i_an) / 255 + i_r * i_an ) / p_rgba[3];
1029 p_rgba[1] = ( p_rgba[1] * i_ao * (255 - i_an) / 255 + i_g * i_an ) / p_rgba[3];
1030 p_rgba[2] = ( p_rgba[2] * i_ao * (255 - i_an) / 255 + i_b * i_an ) / p_rgba[3];
1035 static inline void BlendAXYZGlyph( picture_t *p_picture,
1036 int i_picture_x, int i_picture_y,
1037 int i_a, int i_x, int i_y, int i_z,
1038 FT_BitmapGlyph p_glyph,
1039 void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
1042 for( int dy = 0; dy < p_glyph->bitmap.rows; dy++ )
1044 for( int dx = 0; dx < p_glyph->bitmap.width; dx++ )
1045 BlendPixel( p_picture, i_picture_x + dx, i_picture_y + dy,
1047 p_glyph->bitmap.buffer[dy * p_glyph->bitmap.width + dx] );
1051 static inline void BlendAXYZLine( picture_t *p_picture,
1052 int i_picture_x, int i_picture_y,
1053 int i_a, int i_x, int i_y, int i_z,
1054 const line_character_t *p_current,
1055 const line_character_t *p_next,
1056 void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
1058 int i_line_width = p_current->p_glyph->bitmap.width;
1060 i_line_width = p_next->p_glyph->left - p_current->p_glyph->left;
1062 for( int dx = 0; dx < i_line_width; dx++ )
1064 for( int dy = 0; dy < p_current->i_line_thickness; dy++ )
1065 BlendPixel( p_picture,
1067 i_picture_y + p_current->i_line_offset + dy,
1068 i_a, i_x, i_y, i_z, 0xff );
1072 static inline void RenderBackground( subpicture_region_t *p_region,
1073 line_desc_t *p_line_head,
1076 picture_t *p_picture,
1078 void (*ExtractComponents)( uint32_t, uint8_t *, uint8_t *, uint8_t * ),
1079 void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
1081 for( line_desc_t *p_line = p_line_head; p_line != NULL; p_line = p_line->p_next )
1083 int i_align_left = i_margin;
1084 int i_align_top = i_margin;
1087 unsigned line_top = 0;
1088 int line_bottom = 0;
1091 if( p_line->i_width < i_text_width )
1093 /* Left offset to take into account alignment */
1094 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1095 i_align_left += ( i_text_width - p_line->i_width );
1096 else if( (p_region->i_align & 0x10) == SUBPICTURE_ALIGN_LEAVETEXT)
1097 i_align_left = i_margin; /* Keep it the way it is */
1098 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1099 i_align_left += ( i_text_width - p_line->i_width ) / 2;
1102 /* Find the tallest character in the line */
1103 for( int i = 0; i < p_line->i_character_count; i++ ) {
1104 const line_character_t *ch = &p_line->p_character[i];
1105 FT_BitmapGlyph p_glyph = ch->p_outline ? ch->p_outline : ch->p_glyph;
1106 if (p_glyph->top > max_height)
1107 max_height = p_glyph->top;
1110 /* Compute the background for the line (identify leading/trailing space) */
1111 for( int i = 0; i < p_line->i_character_count; i++ ) {
1112 const line_character_t *ch = &p_line->p_character[i];
1113 FT_BitmapGlyph p_glyph = ch->p_outline ? ch->p_outline : ch->p_glyph;
1114 if (p_glyph && p_glyph->bitmap.rows > 0) {
1115 // Found a non-whitespace character
1116 line_start = i_align_left + p_glyph->left - p_bbox->xMin;
1121 /* Fudge factor to make sure caption background edges are left aligned
1122 despite variable font width */
1123 if (line_start < 12)
1126 /* Find right boundary for bounding box for background */
1127 for( int i = p_line->i_character_count; i > 0; i-- ) {
1128 const line_character_t *ch = &p_line->p_character[i - 1];
1129 FT_BitmapGlyph p_glyph = ch->p_shadow ? ch->p_shadow : ch->p_glyph;
1130 if (p_glyph && p_glyph->bitmap.rows > 0) {
1131 // Found a non-whitespace character
1132 line_end = i_align_left + p_glyph->left - p_bbox->xMin + p_glyph->bitmap.width;
1137 /* Setup color for the background */
1138 uint8_t i_x, i_y, i_z;
1139 ExtractComponents( 0x000000, &i_x, &i_y, &i_z );
1141 /* Compute the upper boundary for the background */
1142 if ((i_align_top + p_line->i_base_line - max_height) < 0)
1143 line_top = i_align_top + p_line->i_base_line;
1145 line_top = i_align_top + p_line->i_base_line - max_height;
1147 /* Compute lower boundary for the background */
1148 line_bottom = __MIN(line_top + p_line->i_height, p_region->fmt.i_visible_height);
1150 /* Render the actual background */
1151 for( int dy = line_top; dy < line_bottom; dy++ )
1153 for( int dx = line_start; dx < line_end; dx++ )
1154 BlendPixel( p_picture, dx, dy, 0xff, i_x, i_y, i_z, 0xff );
1159 static inline int RenderAXYZ( filter_t *p_filter,
1160 subpicture_region_t *p_region,
1161 line_desc_t *p_line_head,
1164 vlc_fourcc_t i_chroma,
1165 void (*ExtractComponents)( uint32_t, uint8_t *, uint8_t *, uint8_t * ),
1166 void (*FillPicture)( picture_t *p_picture, int, int, int, int ),
1167 void (*BlendPixel)(picture_t *, int, int, int, int, int, int, int) )
1169 filter_sys_t *p_sys = p_filter->p_sys;
1171 /* Create a new subpicture region */
1172 const int i_text_width = p_bbox->xMax - p_bbox->xMin;
1173 const int i_text_height = p_bbox->yMax - p_bbox->yMin;
1175 video_format_Init( &fmt, i_chroma );
1177 fmt.i_visible_width = i_text_width + 2 * i_margin;
1179 fmt.i_visible_height = i_text_height + 2 * i_margin;
1181 picture_t *p_picture = p_region->p_picture = picture_NewFromFormat( &fmt );
1182 if( !p_region->p_picture )
1183 return VLC_EGENERIC;
1184 p_region->fmt = fmt;
1186 /* Initialize the picture background */
1187 uint8_t i_a = p_sys->i_background_opacity;
1188 uint8_t i_x, i_y, i_z;
1190 if (p_region->b_renderbg) {
1191 /* Render the background just under the text */
1192 FillPicture( p_picture, 0x00, 0x00, 0x00, 0x00 );
1193 RenderBackground(p_region, p_line_head, p_bbox, i_margin, p_picture, i_text_width,
1194 ExtractComponents, BlendPixel);
1196 /* Render background under entire subpicture block */
1197 ExtractComponents( p_sys->i_background_color, &i_x, &i_y, &i_z );
1198 FillPicture( p_picture, i_a, i_x, i_y, i_z );
1201 /* Render shadow then outline and then normal glyphs */
1202 for( int g = 0; g < 3; g++ )
1204 /* Render all lines */
1205 for( line_desc_t *p_line = p_line_head; p_line != NULL; p_line = p_line->p_next )
1207 int i_align_left = i_margin;
1208 if( p_line->i_width < i_text_width )
1210 /* Left offset to take into account alignment */
1211 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
1212 i_align_left += ( i_text_width - p_line->i_width );
1213 else if( (p_region->i_align & 0x10) == SUBPICTURE_ALIGN_LEAVETEXT)
1214 i_align_left = i_margin; /* Keep it the way it is */
1215 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
1216 i_align_left += ( i_text_width - p_line->i_width ) / 2;
1218 int i_align_top = i_margin;
1220 /* Render all glyphs and underline/strikethrough */
1221 for( int i = 0; i < p_line->i_character_count; i++ )
1223 const line_character_t *ch = &p_line->p_character[i];
1224 FT_BitmapGlyph p_glyph = g == 0 ? ch->p_shadow : g == 1 ? ch->p_outline : ch->p_glyph;
1228 i_a = (ch->i_color >> 24) & 0xff;
1232 i_a = i_a * p_sys->i_shadow_opacity / 255;
1233 i_color = p_sys->i_shadow_color;
1236 i_a = i_a * p_sys->i_outline_opacity / 255;
1237 i_color = p_sys->i_outline_color;
1240 i_color = ch->i_color;
1243 ExtractComponents( i_color, &i_x, &i_y, &i_z );
1245 int i_glyph_y = i_align_top - p_glyph->top + p_bbox->yMax + p_line->i_base_line;
1246 int i_glyph_x = i_align_left + p_glyph->left - p_bbox->xMin;
1248 BlendAXYZGlyph( p_picture,
1249 i_glyph_x, i_glyph_y,
1254 /* underline/strikethrough are only rendered for the normal glyph */
1255 if( g == 2 && ch->i_line_thickness > 0 )
1256 BlendAXYZLine( p_picture,
1257 i_glyph_x, i_glyph_y + p_glyph->top,
1260 i + 1 < p_line->i_character_count ? &ch[1] : NULL,
1269 static text_style_t *CreateStyle( char *psz_fontname, int i_font_size,
1270 uint32_t i_font_color, uint32_t i_karaoke_bg_color,
1273 text_style_t *p_style = text_style_New();
1277 p_style->psz_fontname = psz_fontname ? strdup( psz_fontname ) : NULL;
1278 p_style->i_font_size = i_font_size;
1279 p_style->i_font_color = (i_font_color & 0x00ffffff) >> 0;
1280 p_style->i_font_alpha = (i_font_color & 0xff000000) >> 24;
1281 p_style->i_karaoke_background_color = (i_karaoke_bg_color & 0x00ffffff) >> 0;
1282 p_style->i_karaoke_background_alpha = (i_karaoke_bg_color & 0xff000000) >> 24;
1283 p_style->i_style_flags |= i_style_flags;
1287 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
1288 uint32_t i_color, uint32_t i_karaoke_bg_color )
1291 return VLC_EGENERIC;
1293 font_stack_t *p_new = malloc( sizeof(*p_new) );
1297 p_new->p_next = NULL;
1300 p_new->psz_name = strdup( psz_name );
1302 p_new->psz_name = NULL;
1304 p_new->i_size = i_size;
1305 p_new->i_color = i_color;
1306 p_new->i_karaoke_bg_color = i_karaoke_bg_color;
1314 font_stack_t *p_last;
1316 for( p_last = *p_font;
1318 p_last = p_last->p_next )
1321 p_last->p_next = p_new;
1326 static int PopFont( font_stack_t **p_font )
1328 font_stack_t *p_last, *p_next_to_last;
1330 if( !p_font || !*p_font )
1331 return VLC_EGENERIC;
1333 p_next_to_last = NULL;
1334 for( p_last = *p_font;
1336 p_last = p_last->p_next )
1338 p_next_to_last = p_last;
1341 if( p_next_to_last )
1342 p_next_to_last->p_next = NULL;
1346 free( p_last->psz_name );
1352 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
1353 uint32_t *i_color, uint32_t *i_karaoke_bg_color )
1355 font_stack_t *p_last;
1357 if( !p_font || !*p_font )
1358 return VLC_EGENERIC;
1360 for( p_last=*p_font;
1362 p_last=p_last->p_next )
1365 *psz_name = p_last->psz_name;
1366 *i_size = p_last->i_size;
1367 *i_color = p_last->i_color;
1368 *i_karaoke_bg_color = p_last->i_karaoke_bg_color;
1373 static const struct {
1374 const char *psz_name;
1376 } p_html_colors[] = {
1377 /* Official html colors */
1378 { "Aqua", 0x00FFFF },
1379 { "Black", 0x000000 },
1380 { "Blue", 0x0000FF },
1381 { "Fuchsia", 0xFF00FF },
1382 { "Gray", 0x808080 },
1383 { "Green", 0x008000 },
1384 { "Lime", 0x00FF00 },
1385 { "Maroon", 0x800000 },
1386 { "Navy", 0x000080 },
1387 { "Olive", 0x808000 },
1388 { "Purple", 0x800080 },
1389 { "Red", 0xFF0000 },
1390 { "Silver", 0xC0C0C0 },
1391 { "Teal", 0x008080 },
1392 { "White", 0xFFFFFF },
1393 { "Yellow", 0xFFFF00 },
1396 { "AliceBlue", 0xF0F8FF },
1397 { "AntiqueWhite", 0xFAEBD7 },
1398 { "Aqua", 0x00FFFF },
1399 { "Aquamarine", 0x7FFFD4 },
1400 { "Azure", 0xF0FFFF },
1401 { "Beige", 0xF5F5DC },
1402 { "Bisque", 0xFFE4C4 },
1403 { "Black", 0x000000 },
1404 { "BlanchedAlmond", 0xFFEBCD },
1405 { "Blue", 0x0000FF },
1406 { "BlueViolet", 0x8A2BE2 },
1407 { "Brown", 0xA52A2A },
1408 { "BurlyWood", 0xDEB887 },
1409 { "CadetBlue", 0x5F9EA0 },
1410 { "Chartreuse", 0x7FFF00 },
1411 { "Chocolate", 0xD2691E },
1412 { "Coral", 0xFF7F50 },
1413 { "CornflowerBlue", 0x6495ED },
1414 { "Cornsilk", 0xFFF8DC },
1415 { "Crimson", 0xDC143C },
1416 { "Cyan", 0x00FFFF },
1417 { "DarkBlue", 0x00008B },
1418 { "DarkCyan", 0x008B8B },
1419 { "DarkGoldenRod", 0xB8860B },
1420 { "DarkGray", 0xA9A9A9 },
1421 { "DarkGrey", 0xA9A9A9 },
1422 { "DarkGreen", 0x006400 },
1423 { "DarkKhaki", 0xBDB76B },
1424 { "DarkMagenta", 0x8B008B },
1425 { "DarkOliveGreen", 0x556B2F },
1426 { "Darkorange", 0xFF8C00 },
1427 { "DarkOrchid", 0x9932CC },
1428 { "DarkRed", 0x8B0000 },
1429 { "DarkSalmon", 0xE9967A },
1430 { "DarkSeaGreen", 0x8FBC8F },
1431 { "DarkSlateBlue", 0x483D8B },
1432 { "DarkSlateGray", 0x2F4F4F },
1433 { "DarkSlateGrey", 0x2F4F4F },
1434 { "DarkTurquoise", 0x00CED1 },
1435 { "DarkViolet", 0x9400D3 },
1436 { "DeepPink", 0xFF1493 },
1437 { "DeepSkyBlue", 0x00BFFF },
1438 { "DimGray", 0x696969 },
1439 { "DimGrey", 0x696969 },
1440 { "DodgerBlue", 0x1E90FF },
1441 { "FireBrick", 0xB22222 },
1442 { "FloralWhite", 0xFFFAF0 },
1443 { "ForestGreen", 0x228B22 },
1444 { "Fuchsia", 0xFF00FF },
1445 { "Gainsboro", 0xDCDCDC },
1446 { "GhostWhite", 0xF8F8FF },
1447 { "Gold", 0xFFD700 },
1448 { "GoldenRod", 0xDAA520 },
1449 { "Gray", 0x808080 },
1450 { "Grey", 0x808080 },
1451 { "Green", 0x008000 },
1452 { "GreenYellow", 0xADFF2F },
1453 { "HoneyDew", 0xF0FFF0 },
1454 { "HotPink", 0xFF69B4 },
1455 { "IndianRed", 0xCD5C5C },
1456 { "Indigo", 0x4B0082 },
1457 { "Ivory", 0xFFFFF0 },
1458 { "Khaki", 0xF0E68C },
1459 { "Lavender", 0xE6E6FA },
1460 { "LavenderBlush", 0xFFF0F5 },
1461 { "LawnGreen", 0x7CFC00 },
1462 { "LemonChiffon", 0xFFFACD },
1463 { "LightBlue", 0xADD8E6 },
1464 { "LightCoral", 0xF08080 },
1465 { "LightCyan", 0xE0FFFF },
1466 { "LightGoldenRodYellow", 0xFAFAD2 },
1467 { "LightGray", 0xD3D3D3 },
1468 { "LightGrey", 0xD3D3D3 },
1469 { "LightGreen", 0x90EE90 },
1470 { "LightPink", 0xFFB6C1 },
1471 { "LightSalmon", 0xFFA07A },
1472 { "LightSeaGreen", 0x20B2AA },
1473 { "LightSkyBlue", 0x87CEFA },
1474 { "LightSlateGray", 0x778899 },
1475 { "LightSlateGrey", 0x778899 },
1476 { "LightSteelBlue", 0xB0C4DE },
1477 { "LightYellow", 0xFFFFE0 },
1478 { "Lime", 0x00FF00 },
1479 { "LimeGreen", 0x32CD32 },
1480 { "Linen", 0xFAF0E6 },
1481 { "Magenta", 0xFF00FF },
1482 { "Maroon", 0x800000 },
1483 { "MediumAquaMarine", 0x66CDAA },
1484 { "MediumBlue", 0x0000CD },
1485 { "MediumOrchid", 0xBA55D3 },
1486 { "MediumPurple", 0x9370D8 },
1487 { "MediumSeaGreen", 0x3CB371 },
1488 { "MediumSlateBlue", 0x7B68EE },
1489 { "MediumSpringGreen", 0x00FA9A },
1490 { "MediumTurquoise", 0x48D1CC },
1491 { "MediumVioletRed", 0xC71585 },
1492 { "MidnightBlue", 0x191970 },
1493 { "MintCream", 0xF5FFFA },
1494 { "MistyRose", 0xFFE4E1 },
1495 { "Moccasin", 0xFFE4B5 },
1496 { "NavajoWhite", 0xFFDEAD },
1497 { "Navy", 0x000080 },
1498 { "OldLace", 0xFDF5E6 },
1499 { "Olive", 0x808000 },
1500 { "OliveDrab", 0x6B8E23 },
1501 { "Orange", 0xFFA500 },
1502 { "OrangeRed", 0xFF4500 },
1503 { "Orchid", 0xDA70D6 },
1504 { "PaleGoldenRod", 0xEEE8AA },
1505 { "PaleGreen", 0x98FB98 },
1506 { "PaleTurquoise", 0xAFEEEE },
1507 { "PaleVioletRed", 0xD87093 },
1508 { "PapayaWhip", 0xFFEFD5 },
1509 { "PeachPuff", 0xFFDAB9 },
1510 { "Peru", 0xCD853F },
1511 { "Pink", 0xFFC0CB },
1512 { "Plum", 0xDDA0DD },
1513 { "PowderBlue", 0xB0E0E6 },
1514 { "Purple", 0x800080 },
1515 { "Red", 0xFF0000 },
1516 { "RosyBrown", 0xBC8F8F },
1517 { "RoyalBlue", 0x4169E1 },
1518 { "SaddleBrown", 0x8B4513 },
1519 { "Salmon", 0xFA8072 },
1520 { "SandyBrown", 0xF4A460 },
1521 { "SeaGreen", 0x2E8B57 },
1522 { "SeaShell", 0xFFF5EE },
1523 { "Sienna", 0xA0522D },
1524 { "Silver", 0xC0C0C0 },
1525 { "SkyBlue", 0x87CEEB },
1526 { "SlateBlue", 0x6A5ACD },
1527 { "SlateGray", 0x708090 },
1528 { "SlateGrey", 0x708090 },
1529 { "Snow", 0xFFFAFA },
1530 { "SpringGreen", 0x00FF7F },
1531 { "SteelBlue", 0x4682B4 },
1532 { "Tan", 0xD2B48C },
1533 { "Teal", 0x008080 },
1534 { "Thistle", 0xD8BFD8 },
1535 { "Tomato", 0xFF6347 },
1536 { "Turquoise", 0x40E0D0 },
1537 { "Violet", 0xEE82EE },
1538 { "Wheat", 0xF5DEB3 },
1539 { "White", 0xFFFFFF },
1540 { "WhiteSmoke", 0xF5F5F5 },
1541 { "Yellow", 0xFFFF00 },
1542 { "YellowGreen", 0x9ACD32 },
1547 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1548 font_stack_t **p_fonts )
1551 char *psz_fontname = NULL;
1552 uint32_t i_font_color = 0xffffff;
1553 int i_font_alpha = 255;
1554 uint32_t i_karaoke_bg_color = 0x00ffffff;
1555 int i_font_size = 24;
1557 /* Default all attributes to the top font in the stack -- in case not
1558 * all attributes are specified in the sub-font
1560 if( VLC_SUCCESS == PeekFont( p_fonts,
1564 &i_karaoke_bg_color ))
1566 psz_fontname = strdup( psz_fontname );
1568 i_font_alpha = (i_font_color >> 24) & 0xff;
1569 i_font_color &= 0x00ffffff;
1571 const char *name, *value;
1572 while( (name = xml_ReaderNextAttr( p_xml_reader, &value )) != NULL )
1574 if( !strcasecmp( "face", name ) )
1576 free( psz_fontname );
1577 psz_fontname = strdup( value );
1579 else if( !strcasecmp( "size", name ) )
1581 if( ( *value == '+' ) || ( *value == '-' ) )
1583 int i_value = atoi( value );
1585 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
1586 i_font_size += ( i_value * i_font_size ) / 10;
1587 else if( i_value < -5 )
1588 i_font_size = - i_value;
1589 else if( i_value > 5 )
1590 i_font_size = i_value;
1593 i_font_size = atoi( value );
1595 else if( !strcasecmp( "color", name ) )
1597 if( value[0] == '#' )
1599 i_font_color = strtol( value + 1, NULL, 16 );
1600 i_font_color &= 0x00ffffff;
1605 uint32_t i_value = strtol( value, &end, 16 );
1606 if( *end == '\0' || *end == ' ' )
1607 i_font_color = i_value & 0x00ffffff;
1609 for( int i = 0; p_html_colors[i].psz_name != NULL; i++ )
1611 if( !strncasecmp( value, p_html_colors[i].psz_name, strlen(p_html_colors[i].psz_name) ) )
1613 i_font_color = p_html_colors[i].i_value;
1619 else if( !strcasecmp( "alpha", name ) && ( value[0] == '#' ) )
1621 i_font_alpha = strtol( value + 1, NULL, 16 );
1622 i_font_alpha &= 0xff;
1625 rv = PushFont( p_fonts,
1628 (i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24),
1629 i_karaoke_bg_color );
1631 free( psz_fontname );
1636 static int HandleTT(font_stack_t **p_fonts, const char *p_fontfamily )
1638 char *psz_unused_fontname = NULL;
1639 uint32_t i_font_color = 0xffffff;
1640 uint32_t i_karaoke_bg_color = 0x00ffffff;
1641 int i_font_size = 24;
1643 /* Default all attributes to the top font in the stack -- in case not
1644 * all attributes are specified in the sub-font
1647 &psz_unused_fontname,
1650 &i_karaoke_bg_color );
1652 /* Keep all the parent's font attributes, but change to a monospace font */
1653 return PushFont( p_fonts,
1657 i_karaoke_bg_color );
1660 /* Turn any multiple-whitespaces into single spaces */
1661 static void HandleWhiteSpace( char *psz_node )
1663 char *s = strpbrk( psz_node, "\t\r\n " );
1666 int i_whitespace = strspn( s, "\t\r\n " );
1668 if( i_whitespace > 1 )
1671 strlen( s ) - i_whitespace + 1 );
1674 s = strpbrk( s, "\t\r\n " );
1679 static text_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1680 font_stack_t **p_fonts,
1683 char *psz_fontname = NULL;
1684 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1685 uint32_t i_karaoke_bg_color = i_font_color;
1686 int i_font_size = p_sys->i_font_size;
1688 if( PeekFont( p_fonts, &psz_fontname, &i_font_size,
1689 &i_font_color, &i_karaoke_bg_color ) )
1692 return CreateStyle( psz_fontname, i_font_size, i_font_color,
1697 static unsigned SetupText( filter_t *p_filter,
1698 uni_char_t *psz_text_out,
1699 text_style_t **pp_styles,
1700 uint32_t *pi_k_dates,
1702 const char *psz_text_in,
1703 text_style_t *p_style,
1706 size_t i_string_length;
1708 size_t i_string_bytes;
1709 uni_char_t *psz_tmp = ToCharset( FREETYPE_TO_UCS, psz_text_in, &i_string_bytes );
1712 memcpy( psz_text_out, psz_tmp, i_string_bytes );
1713 i_string_length = i_string_bytes / sizeof( *psz_tmp );
1718 msg_Warn( p_filter, "failed to convert string to unicode (%m)" );
1719 i_string_length = 0;
1722 if( i_string_length > 0 )
1724 for( unsigned i = 0; i < i_string_length; i++ )
1725 pp_styles[i] = p_style;
1729 text_style_Delete( p_style );
1731 if( i_string_length > 0 && pi_k_dates )
1733 for( unsigned i = 0; i < i_string_length; i++ )
1734 pi_k_dates[i] = i_k_date;
1736 return i_string_length;
1739 static int ProcessNodes( filter_t *p_filter,
1740 uni_char_t *psz_text,
1741 text_style_t **pp_styles,
1742 uint32_t *pi_k_dates,
1744 xml_reader_t *p_xml_reader,
1745 text_style_t *p_font_style )
1747 int rv = VLC_SUCCESS;
1748 filter_sys_t *p_sys = p_filter->p_sys;
1749 int i_text_length = 0;
1750 font_stack_t *p_fonts = NULL;
1751 uint32_t i_k_date = 0;
1753 int i_style_flags = 0;
1757 /* If the font is not specified in the style, assume the system font */
1758 if(!p_font_style->psz_fontname)
1759 p_font_style->psz_fontname = strdup(p_sys->psz_fontfamily);
1761 rv = PushFont( &p_fonts,
1762 p_font_style->psz_fontname,
1763 p_font_style->i_font_size > 0 ? p_font_style->i_font_size
1764 : p_sys->i_font_size,
1765 (p_font_style->i_font_color & 0xffffff) |
1766 ((p_font_style->i_font_alpha & 0xff) << 24),
1767 (p_font_style->i_karaoke_background_color & 0xffffff) |
1768 ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
1770 i_style_flags = p_font_style->i_style_flags & (STYLE_BOLD |
1777 rv = PushFont( &p_fonts,
1778 p_sys->psz_fontfamily,
1780 (p_sys->i_font_color & 0xffffff) |
1781 ((p_sys->i_font_opacity & 0xff) << 24),
1784 if( p_sys->b_font_bold )
1785 i_style_flags |= STYLE_BOLD;
1787 if( rv != VLC_SUCCESS )
1793 while ( (type = xml_ReaderNextNode( p_xml_reader, &node )) > 0 )
1797 case XML_READER_ENDELEM:
1798 if( !strcasecmp( "font", node ) )
1799 PopFont( &p_fonts );
1800 else if( !strcasecmp( "tt", node ) )
1801 PopFont( &p_fonts );
1802 else if( !strcasecmp( "b", node ) )
1803 i_style_flags &= ~STYLE_BOLD;
1804 else if( !strcasecmp( "i", node ) )
1805 i_style_flags &= ~STYLE_ITALIC;
1806 else if( !strcasecmp( "u", node ) )
1807 i_style_flags &= ~STYLE_UNDERLINE;
1808 else if( !strcasecmp( "s", node ) )
1809 i_style_flags &= ~STYLE_STRIKEOUT;
1812 case XML_READER_STARTELEM:
1813 if( !strcasecmp( "font", node ) )
1814 HandleFontAttributes( p_xml_reader, &p_fonts );
1815 else if( !strcasecmp( "tt", node ) )
1816 HandleTT( &p_fonts, p_sys->psz_monofontfamily );
1817 else if( !strcasecmp( "b", node ) )
1818 i_style_flags |= STYLE_BOLD;
1819 else if( !strcasecmp( "i", node ) )
1820 i_style_flags |= STYLE_ITALIC;
1821 else if( !strcasecmp( "u", node ) )
1822 i_style_flags |= STYLE_UNDERLINE;
1823 else if( !strcasecmp( "s", node ) )
1824 i_style_flags |= STYLE_STRIKEOUT;
1825 else if( !strcasecmp( "br", node ) )
1827 i_text_length += SetupText( p_filter,
1828 &psz_text[i_text_length],
1829 &pp_styles[i_text_length],
1830 pi_k_dates ? &pi_k_dates[i_text_length] : NULL,
1832 GetStyleFromFontStack( p_sys,
1837 else if( !strcasecmp( "k", node ) )
1840 const char *name, *value;
1841 while( (name = xml_ReaderNextAttr( p_xml_reader, &value )) != NULL )
1843 if( !strcasecmp( "t", name ) && value )
1844 i_k_date += atoi( value );
1849 case XML_READER_TEXT:
1851 char *psz_node = strdup( node );
1852 if( unlikely(!psz_node) )
1855 HandleWhiteSpace( psz_node );
1856 resolve_xml_special_chars( psz_node );
1858 i_text_length += SetupText( p_filter,
1859 &psz_text[i_text_length],
1860 &pp_styles[i_text_length],
1861 pi_k_dates ? &pi_k_dates[i_text_length] : NULL,
1863 GetStyleFromFontStack( p_sys,
1873 *pi_len = i_text_length;
1875 while( VLC_SUCCESS == PopFont( &p_fonts ) );
1880 static void FreeLine( line_desc_t *p_line )
1882 for( int i = 0; i < p_line->i_character_count; i++ )
1884 line_character_t *ch = &p_line->p_character[i];
1885 FT_Done_Glyph( (FT_Glyph)ch->p_glyph );
1887 FT_Done_Glyph( (FT_Glyph)ch->p_outline );
1889 FT_Done_Glyph( (FT_Glyph)ch->p_shadow );
1892 free( p_line->p_character );
1896 static void FreeLines( line_desc_t *p_lines )
1898 for( line_desc_t *p_line = p_lines; p_line != NULL; )
1900 line_desc_t *p_next = p_line->p_next;
1906 static line_desc_t *NewLine( int i_count )
1908 line_desc_t *p_line = malloc( sizeof(*p_line) );
1913 p_line->p_next = NULL;
1914 p_line->i_width = 0;
1915 p_line->i_base_line = 0;
1916 p_line->i_character_count = 0;
1918 p_line->p_character = calloc( i_count, sizeof(*p_line->p_character) );
1919 if( !p_line->p_character )
1927 static FT_Face LoadEmbeddedFace( filter_sys_t *p_sys, const text_style_t *p_style )
1929 for( int k = 0; k < p_sys->i_font_attachments; k++ )
1931 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1933 FT_Face p_face = NULL;
1935 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1943 int i_style_received = ((p_face->style_flags & FT_STYLE_FLAG_BOLD) ? STYLE_BOLD : 0) |
1944 ((p_face->style_flags & FT_STYLE_FLAG_ITALIC ) ? STYLE_ITALIC : 0);
1945 if( p_face->family_name != NULL
1946 && !strcasecmp( p_face->family_name, p_style->psz_fontname )
1947 && (p_style->i_style_flags & (STYLE_BOLD | STYLE_ITALIC))
1948 == i_style_received )
1951 FT_Done_Face( p_face );
1959 static FT_Face LoadFace( filter_t *p_filter,
1960 const text_style_t *p_style )
1962 filter_sys_t *p_sys = p_filter->p_sys;
1964 /* Look for a match amongst our attachments first */
1965 FT_Face p_face = LoadEmbeddedFace( p_sys, p_style );
1967 /* Load system wide font otheriwse */
1971 char *psz_fontfile = NULL;
1972 #ifdef HAVE_FONTCONFIG
1973 psz_fontfile = FontConfig_Select( NULL,
1974 p_style->psz_fontname,
1975 (p_style->i_style_flags & STYLE_BOLD) != 0,
1976 (p_style->i_style_flags & STYLE_ITALIC) != 0,
1979 #elif defined( __APPLE__ )
1980 psz_fontfile = MacLegacy_Select( p_filter, p_style->psz_fontname, false, false, -1, &i_idx );
1981 #elif defined( WIN32 )
1982 psz_fontfile = Win32_Select( p_filter,
1983 p_style->psz_fontname,
1984 (p_style->i_style_flags & STYLE_BOLD) != 0,
1985 (p_style->i_style_flags & STYLE_ITALIC) != 0,
1989 psz_fontfile = NULL;
1994 if( *psz_fontfile == '\0' )
1997 "We were not able to find a matching font: \"%s\" (%s %s),"
1998 " so using default font",
1999 p_style->psz_fontname,
2000 (p_style->i_style_flags & STYLE_BOLD) ? "Bold" : "",
2001 (p_style->i_style_flags & STYLE_ITALIC) ? "Italic" : "" );
2006 if( FT_New_Face( p_sys->p_library, psz_fontfile, i_idx, &p_face ) )
2009 free( psz_fontfile );
2014 if( FT_Select_Charmap( p_face, ft_encoding_unicode ) )
2016 /* We've loaded a font face which is unhelpful for actually
2017 * rendering text - fallback to the default one.
2019 FT_Done_Face( p_face );
2025 static bool FaceStyleEquals( const text_style_t *p_style1,
2026 const text_style_t *p_style2 )
2028 if( !p_style1 || !p_style2 )
2030 if( p_style1 == p_style2 )
2033 const int i_style_mask = STYLE_BOLD | STYLE_ITALIC;
2034 return (p_style1->i_style_flags & i_style_mask) == (p_style2->i_style_flags & i_style_mask) &&
2035 !strcmp( p_style1->psz_fontname, p_style2->psz_fontname );
2038 static int GetGlyph( filter_t *p_filter,
2039 FT_Glyph *pp_glyph, FT_BBox *p_glyph_bbox,
2040 FT_Glyph *pp_outline, FT_BBox *p_outline_bbox,
2041 FT_Glyph *pp_shadow, FT_BBox *p_shadow_bbox,
2047 FT_Vector *p_pen_shadow )
2049 if( FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_DEFAULT ) &&
2050 FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT ) )
2052 msg_Err( p_filter, "unable to render text FT_Load_Glyph failed" );
2053 return VLC_EGENERIC;
2056 /* Do synthetic styling now that Freetype supports it;
2057 * ie. if the font we have loaded is NOT already in the
2058 * style that the tags want, then switch it on; if they
2059 * are then don't. */
2060 if ((i_style_flags & STYLE_BOLD) && !(p_face->style_flags & FT_STYLE_FLAG_BOLD))
2061 FT_GlyphSlot_Embolden( p_face->glyph );
2062 if ((i_style_flags & STYLE_ITALIC) && !(p_face->style_flags & FT_STYLE_FLAG_ITALIC))
2063 FT_GlyphSlot_Oblique( p_face->glyph );
2066 if( FT_Get_Glyph( p_face->glyph, &glyph ) )
2068 msg_Err( p_filter, "unable to render text FT_Get_Glyph failed" );
2069 return VLC_EGENERIC;
2072 FT_Glyph outline = NULL;
2073 if( p_filter->p_sys->p_stroker )
2076 if( FT_Glyph_StrokeBorder( &outline, p_filter->p_sys->p_stroker, 0, 0 ) )
2080 FT_Glyph shadow = NULL;
2081 if( p_filter->p_sys->i_shadow_opacity > 0 )
2083 shadow = outline ? outline : glyph;
2084 if( FT_Glyph_To_Bitmap( &shadow, FT_RENDER_MODE_NORMAL, p_pen_shadow, 0 ) )
2090 FT_Glyph_Get_CBox( shadow, ft_glyph_bbox_pixels, p_shadow_bbox );
2093 *pp_shadow = shadow;
2095 if( FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, p_pen, 1) )
2097 FT_Done_Glyph( glyph );
2099 FT_Done_Glyph( outline );
2101 FT_Done_Glyph( shadow );
2102 return VLC_EGENERIC;
2104 FT_Glyph_Get_CBox( glyph, ft_glyph_bbox_pixels, p_glyph_bbox );
2109 FT_Glyph_To_Bitmap( &outline, FT_RENDER_MODE_NORMAL, p_pen, 1 );
2110 FT_Glyph_Get_CBox( outline, ft_glyph_bbox_pixels, p_outline_bbox );
2112 *pp_outline = outline;
2117 static void FixGlyph( FT_Glyph glyph, FT_BBox *p_bbox, FT_Face face, const FT_Vector *p_pen )
2119 FT_BitmapGlyph glyph_bmp = (FT_BitmapGlyph)glyph;
2120 if( p_bbox->xMin >= p_bbox->xMax )
2122 p_bbox->xMin = FT_CEIL(p_pen->x);
2123 p_bbox->xMax = FT_CEIL(p_pen->x + face->glyph->advance.x);
2124 glyph_bmp->left = p_bbox->xMin;
2126 if( p_bbox->yMin >= p_bbox->yMax )
2128 p_bbox->yMax = FT_CEIL(p_pen->y);
2129 p_bbox->yMin = FT_CEIL(p_pen->y + face->glyph->advance.y);
2130 glyph_bmp->top = p_bbox->yMax;
2134 static void BBoxEnlarge( FT_BBox *p_max, const FT_BBox *p )
2136 p_max->xMin = __MIN(p_max->xMin, p->xMin);
2137 p_max->yMin = __MIN(p_max->yMin, p->yMin);
2138 p_max->xMax = __MAX(p_max->xMax, p->xMax);
2139 p_max->yMax = __MAX(p_max->yMax, p->yMax);
2142 static int ProcessLines( filter_t *p_filter,
2143 line_desc_t **pp_lines,
2145 int *pi_max_face_height,
2147 uni_char_t *psz_text,
2148 text_style_t **pp_styles,
2149 uint32_t *pi_k_dates,
2152 filter_sys_t *p_sys = p_filter->p_sys;
2153 uni_char_t *p_fribidi_string = NULL;
2154 text_style_t **pp_fribidi_styles = NULL;
2155 int *p_new_positions = NULL;
2157 #if defined(HAVE_FRIBIDI)
2159 int *p_old_positions;
2160 int start_pos, pos = 0;
2162 pp_fribidi_styles = calloc( i_len, sizeof(*pp_fribidi_styles) );
2164 p_fribidi_string = malloc( (i_len + 1) * sizeof(*p_fribidi_string) );
2165 p_old_positions = malloc( (i_len + 1) * sizeof(*p_old_positions) );
2166 p_new_positions = malloc( (i_len + 1) * sizeof(*p_new_positions) );
2168 if( ! pp_fribidi_styles ||
2169 ! p_fribidi_string ||
2170 ! p_old_positions ||
2173 free( p_old_positions );
2174 free( p_new_positions );
2175 free( p_fribidi_string );
2176 free( pp_fribidi_styles );
2180 /* Do bidi conversion line-by-line */
2183 while(pos < i_len) {
2184 if (psz_text[pos] != '\n')
2186 p_fribidi_string[pos] = psz_text[pos];
2187 pp_fribidi_styles[pos] = pp_styles[pos];
2188 p_new_positions[pos] = pos;
2192 while(pos < i_len) {
2193 if (psz_text[pos] == '\n')
2197 if (pos > start_pos)
2199 #if (FRIBIDI_MINOR_VERSION < 19) && (FRIBIDI_MAJOR_VERSION == 0)
2200 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
2202 FriBidiParType base_dir = FRIBIDI_PAR_LTR;
2204 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
2205 pos - start_pos, &base_dir,
2206 (FriBidiChar*)p_fribidi_string + start_pos,
2207 p_new_positions + start_pos,
2210 for( int j = start_pos; j < pos; j++ )
2212 pp_fribidi_styles[ j ] = pp_styles[ start_pos + p_old_positions[j - start_pos] ];
2213 p_new_positions[ j ] += start_pos;
2217 p_fribidi_string[ i_len ] = 0;
2218 free( p_old_positions );
2220 pp_styles = pp_fribidi_styles;
2221 psz_text = p_fribidi_string;
2224 /* Work out the karaoke */
2225 uint8_t *pi_karaoke_bar = NULL;
2228 pi_karaoke_bar = malloc( i_len * sizeof(*pi_karaoke_bar));
2229 if( pi_karaoke_bar )
2231 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
2232 for( int i = 0; i < i_len; i++ )
2234 unsigned i_bar = p_new_positions ? p_new_positions[i] : i;
2235 pi_karaoke_bar[i_bar] = pi_k_dates[i] >= i_elapsed;
2239 free( p_new_positions );
2241 *pi_max_face_height = 0;
2243 line_desc_t **pp_line_next = pp_lines;
2251 int i_face_height_previous = 0;
2252 int i_base_line = 0;
2253 const text_style_t *p_previous_style = NULL;
2254 FT_Face p_face = NULL;
2255 for( int i_start = 0; i_start < i_len; )
2257 /* Compute the length of the current text line */
2259 while( i_start + i_length < i_len && psz_text[i_start + i_length] != '\n' )
2262 /* Render the text line (or the begining if too long) into 0 or 1 glyph line */
2263 line_desc_t *p_line = i_length > 0 ? NewLine( i_length ) : NULL;
2264 int i_index = i_start;
2269 int i_face_height = 0;
2270 FT_BBox line_bbox = {
2276 int i_ul_offset = 0;
2277 int i_ul_thickness = 0;
2286 break_point_t break_point;
2287 break_point_t break_point_fallback;
2289 #define SAVE_BP(dst) do { \
2290 dst.i_index = i_index; \
2292 dst.line_bbox = line_bbox; \
2293 dst.i_face_height = i_face_height; \
2294 dst.i_ul_offset = i_ul_offset; \
2295 dst.i_ul_thickness = i_ul_thickness; \
2298 SAVE_BP( break_point );
2299 SAVE_BP( break_point_fallback );
2301 while( i_index < i_start + i_length )
2303 /* Split by common FT_Face + Size */
2304 const text_style_t *p_current_style = pp_styles[i_index];
2305 int i_part_length = 0;
2306 while( i_index + i_part_length < i_start + i_length )
2308 const text_style_t *p_style = pp_styles[i_index + i_part_length];
2309 if( !FaceStyleEquals( p_style, p_current_style ) ||
2310 p_style->i_font_size != p_current_style->i_font_size )
2315 /* (Re)load/reconfigure the face if needed */
2316 if( !FaceStyleEquals( p_current_style, p_previous_style ) )
2319 FT_Done_Face( p_face );
2320 p_previous_style = NULL;
2322 p_face = LoadFace( p_filter, p_current_style );
2324 FT_Face p_current_face = p_face ? p_face : p_sys->p_face;
2325 if( !p_previous_style || p_previous_style->i_font_size != p_current_style->i_font_size )
2327 if( FT_Set_Pixel_Sizes( p_current_face, 0, p_current_style->i_font_size ) )
2328 msg_Err( p_filter, "Failed to set font size to %d", p_current_style->i_font_size );
2329 if( p_sys->p_stroker )
2331 int i_radius = (p_current_style->i_font_size << 6) * p_sys->f_outline_thickness;
2332 FT_Stroker_Set( p_sys->p_stroker,
2334 FT_STROKER_LINECAP_ROUND,
2335 FT_STROKER_LINEJOIN_ROUND, 0 );
2338 p_previous_style = p_current_style;
2340 i_face_height = __MAX(i_face_height, FT_CEIL(FT_MulFix(p_current_face->height,
2341 p_current_face->size->metrics.y_scale)));
2343 /* Render the part */
2344 bool b_break_line = false;
2345 int i_glyph_last = 0;
2346 while( i_part_length > 0 )
2348 const text_style_t *p_glyph_style = pp_styles[i_index];
2349 uni_char_t character = psz_text[i_index];
2350 int i_glyph_index = FT_Get_Char_Index( p_current_face, character );
2352 /* Get kerning vector */
2353 FT_Vector kerning = { .x = 0, .y = 0 };
2354 if( FT_HAS_KERNING( p_current_face ) && i_glyph_last != 0 && i_glyph_index != 0 )
2355 FT_Get_Kerning( p_current_face, i_glyph_last, i_glyph_index, ft_kerning_default, &kerning );
2357 /* Get the glyph bitmap and its bounding box and all the associated properties */
2358 FT_Vector pen_new = {
2359 .x = pen.x + kerning.x,
2360 .y = pen.y + kerning.y,
2362 FT_Vector pen_shadow_new = {
2363 .x = pen_new.x + p_sys->f_shadow_vector_x * (p_current_style->i_font_size << 6),
2364 .y = pen_new.y + p_sys->f_shadow_vector_y * (p_current_style->i_font_size << 6),
2369 FT_BBox outline_bbox;
2371 FT_BBox shadow_bbox;
2373 if( GetGlyph( p_filter,
2374 &glyph, &glyph_bbox,
2375 &outline, &outline_bbox,
2376 &shadow, &shadow_bbox,
2377 p_current_face, i_glyph_index, p_glyph_style->i_style_flags,
2378 &pen_new, &pen_shadow_new ) )
2381 FixGlyph( glyph, &glyph_bbox, p_current_face, &pen_new );
2383 FixGlyph( outline, &outline_bbox, p_current_face, &pen_new );
2385 FixGlyph( shadow, &shadow_bbox, p_current_face, &pen_shadow_new );
2387 /* FIXME and what about outline */
2389 bool b_karaoke = pi_karaoke_bar && pi_karaoke_bar[i_index] != 0;
2390 uint32_t i_color = b_karaoke ? (p_glyph_style->i_karaoke_background_color |
2391 (p_glyph_style->i_karaoke_background_alpha << 24))
2392 : (p_glyph_style->i_font_color |
2393 (p_glyph_style->i_font_alpha << 24));
2394 int i_line_offset = 0;
2395 int i_line_thickness = 0;
2396 if( p_glyph_style->i_style_flags & (STYLE_UNDERLINE | STYLE_STRIKEOUT) )
2398 i_line_offset = abs( FT_FLOOR(FT_MulFix(p_current_face->underline_position,
2399 p_current_face->size->metrics.y_scale)) );
2401 i_line_thickness = abs( FT_CEIL(FT_MulFix(p_current_face->underline_thickness,
2402 p_current_face->size->metrics.y_scale)) );
2404 if( p_glyph_style->i_style_flags & STYLE_STRIKEOUT )
2406 /* Move the baseline to make it strikethrough instead of
2407 * underline. That means that strikethrough takes precedence
2409 i_line_offset -= abs( FT_FLOOR(FT_MulFix(p_current_face->descender*2,
2410 p_current_face->size->metrics.y_scale)) );
2412 else if( i_line_thickness > 0 )
2414 glyph_bbox.yMin = __MIN( glyph_bbox.yMin, - i_line_offset - i_line_thickness );
2416 /* The real underline thickness and position are
2417 * updated once the whole line has been parsed */
2418 i_ul_offset = __MAX( i_ul_offset, i_line_offset );
2419 i_ul_thickness = __MAX( i_ul_thickness, i_line_thickness );
2420 i_line_thickness = -1;
2423 FT_BBox line_bbox_new = line_bbox;
2424 BBoxEnlarge( &line_bbox_new, &glyph_bbox );
2426 BBoxEnlarge( &line_bbox_new, &outline_bbox );
2428 BBoxEnlarge( &line_bbox_new, &shadow_bbox );
2430 b_break_line = i_index > i_start &&
2431 line_bbox_new.xMax - line_bbox_new.xMin >= (int)p_filter->fmt_out.video.i_visible_width;
2434 FT_Done_Glyph( glyph );
2436 FT_Done_Glyph( outline );
2438 FT_Done_Glyph( shadow );
2440 break_point_t *p_bp = NULL;
2441 if( break_point.i_index > i_start )
2442 p_bp = &break_point;
2443 else if( break_point_fallback.i_index > i_start )
2444 p_bp = &break_point_fallback;
2448 msg_Dbg( p_filter, "Breaking line");
2449 for( int i = p_bp->i_index; i < i_index; i++ )
2451 line_character_t *ch = &p_line->p_character[i - i_start];
2452 FT_Done_Glyph( (FT_Glyph)ch->p_glyph );
2454 FT_Done_Glyph( (FT_Glyph)ch->p_outline );
2456 FT_Done_Glyph( (FT_Glyph)ch->p_shadow );
2458 p_line->i_character_count = p_bp->i_index - i_start;
2460 i_index = p_bp->i_index;
2462 line_bbox = p_bp->line_bbox;
2463 i_face_height = p_bp->i_face_height;
2464 i_ul_offset = p_bp->i_ul_offset;
2465 i_ul_thickness = p_bp->i_ul_thickness;
2469 msg_Err( p_filter, "Breaking unbreakable line");
2474 assert( p_line->i_character_count == i_index - i_start);
2475 p_line->p_character[p_line->i_character_count++] = (line_character_t){
2476 .p_glyph = (FT_BitmapGlyph)glyph,
2477 .p_outline = (FT_BitmapGlyph)outline,
2478 .p_shadow = (FT_BitmapGlyph)shadow,
2480 .i_line_offset = i_line_offset,
2481 .i_line_thickness = i_line_thickness,
2484 pen.x = pen_new.x + p_current_face->glyph->advance.x;
2485 pen.y = pen_new.y + p_current_face->glyph->advance.y;
2486 line_bbox = line_bbox_new;
2488 i_glyph_last = i_glyph_index;
2492 if( character == ' ' || character == '\t' )
2493 SAVE_BP( break_point );
2494 else if( character == 160 )
2495 SAVE_BP( break_point_fallback );
2501 /* Update our baseline */
2502 if( i_face_height_previous > 0 )
2503 i_base_line += __MAX(i_face_height, i_face_height_previous);
2504 if( i_face_height > 0 )
2505 i_face_height_previous = i_face_height;
2507 /* Update the line bbox with the actual base line */
2508 if (line_bbox.yMax > line_bbox.yMin) {
2509 line_bbox.yMin -= i_base_line;
2510 line_bbox.yMax -= i_base_line;
2512 BBoxEnlarge( &bbox, &line_bbox );
2514 /* Terminate and append the line */
2517 p_line->i_width = __MAX(line_bbox.xMax - line_bbox.xMin, 0);
2518 p_line->i_base_line = i_base_line;
2519 p_line->i_height = __MAX(i_face_height, i_face_height_previous);
2520 if( i_ul_thickness > 0 )
2522 for( int i = 0; i < p_line->i_character_count; i++ )
2524 line_character_t *ch = &p_line->p_character[i];
2525 if( ch->i_line_thickness < 0 )
2527 ch->i_line_offset = i_ul_offset;
2528 ch->i_line_thickness = i_ul_thickness;
2533 *pp_line_next = p_line;
2534 pp_line_next = &p_line->p_next;
2537 *pi_max_face_height = __MAX( *pi_max_face_height, i_face_height );
2539 /* Skip what we have rendered and the line delimitor if present */
2541 if( i_start < i_len && psz_text[i_start] == '\n' )
2544 if( bbox.yMax - bbox.yMin >= (int)p_filter->fmt_out.video.i_visible_height )
2546 msg_Err( p_filter, "Truncated too high subtitle" );
2551 FT_Done_Face( p_face );
2553 free( pp_fribidi_styles );
2554 free( p_fribidi_string );
2555 free( pi_karaoke_bar );
2562 * This function renders a text subpicture region into another one.
2563 * It also calculates the size needed for this string, and renders the
2564 * needed glyphs into memory. It is used as pf_add_string callback in
2565 * the vout method by this module
2567 static int RenderCommon( filter_t *p_filter, subpicture_region_t *p_region_out,
2568 subpicture_region_t *p_region_in, bool b_html,
2569 const vlc_fourcc_t *p_chroma_list )
2571 filter_sys_t *p_sys = p_filter->p_sys;
2574 return VLC_EGENERIC;
2575 if( b_html && !p_region_in->psz_html )
2576 return VLC_EGENERIC;
2577 if( !b_html && !p_region_in->psz_text )
2578 return VLC_EGENERIC;
2580 const size_t i_text_max = strlen( b_html ? p_region_in->psz_html
2581 : p_region_in->psz_text );
2583 uni_char_t *psz_text = calloc( i_text_max, sizeof( *psz_text ) );
2584 text_style_t **pp_styles = calloc( i_text_max, sizeof( *pp_styles ) );
2585 if( !psz_text || !pp_styles )
2589 return VLC_EGENERIC;
2592 /* Reset the default fontsize in case screen metrics have changed */
2593 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2596 int rv = VLC_SUCCESS;
2597 int i_text_length = 0;
2599 int i_max_face_height;
2600 line_desc_t *p_lines = NULL;
2602 uint32_t *pi_k_durations = NULL;
2606 stream_t *p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2607 (uint8_t *) p_region_in->psz_html,
2608 strlen( p_region_in->psz_html ),
2610 if( unlikely(p_sub == NULL) )
2613 xml_reader_t *p_xml_reader = p_filter->p_sys->p_xml;
2615 p_xml_reader = xml_ReaderCreate( p_filter, p_sub );
2617 p_xml_reader = xml_ReaderReset( p_xml_reader, p_sub );
2618 p_filter->p_sys->p_xml = p_xml_reader;
2625 /* Look for Root Node */
2628 if( xml_ReaderNextNode( p_xml_reader, &node ) == XML_READER_STARTELEM )
2630 if( strcasecmp( "karaoke", node ) == 0 )
2632 pi_k_durations = calloc( i_text_max, sizeof( *pi_k_durations ) );
2634 else if( strcasecmp( "text", node ) != 0 )
2636 /* Only text and karaoke tags are supported */
2637 msg_Dbg( p_filter, "Unsupported top-level tag <%s> ignored.",
2644 msg_Err( p_filter, "Malformed HTML subtitle" );
2650 rv = ProcessNodes( p_filter,
2651 psz_text, pp_styles, pi_k_durations, &i_text_length,
2652 p_xml_reader, p_region_in->p_style );
2656 p_filter->p_sys->p_xml = xml_ReaderReset( p_xml_reader, NULL );
2658 stream_Delete( p_sub );
2662 text_style_t *p_style;
2663 if( p_region_in->p_style )
2664 p_style = CreateStyle( p_region_in->p_style->psz_fontname,
2665 p_region_in->p_style->i_font_size > 0 ? p_region_in->p_style->i_font_size
2666 : p_sys->i_font_size,
2667 (p_region_in->p_style->i_font_color & 0xffffff) |
2668 ((p_region_in->p_style->i_font_alpha & 0xff) << 24),
2670 p_region_in->p_style->i_style_flags & (STYLE_BOLD |
2675 p_style = CreateStyle( p_sys->psz_fontfamily,
2677 (p_sys->i_font_color & 0xffffff) |
2678 ((p_sys->i_font_opacity & 0xff) << 24),
2680 if( p_sys->b_font_bold )
2681 p_style->i_style_flags |= STYLE_BOLD;
2683 i_text_length = SetupText( p_filter,
2687 p_region_in->psz_text, p_style, 0 );
2690 if( !rv && i_text_length > 0 )
2692 rv = ProcessLines( p_filter,
2693 &p_lines, &bbox, &i_max_face_height,
2694 psz_text, pp_styles, pi_k_durations, i_text_length );
2697 p_region_out->i_x = p_region_in->i_x;
2698 p_region_out->i_y = p_region_in->i_y;
2700 /* Don't attempt to render text that couldn't be layed out
2702 if( !rv && i_text_length > 0 && bbox.xMin < bbox.xMax && bbox.yMin < bbox.yMax )
2704 const vlc_fourcc_t p_chroma_list_yuvp[] = { VLC_CODEC_YUVP, 0 };
2705 const vlc_fourcc_t p_chroma_list_rgba[] = { VLC_CODEC_RGBA, 0 };
2707 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
2708 p_chroma_list = p_chroma_list_yuvp;
2709 else if( !p_chroma_list || *p_chroma_list == 0 )
2710 p_chroma_list = p_chroma_list_rgba;
2712 const int i_margin = p_sys->i_background_opacity > 0 ? i_max_face_height / 4 : 0;
2713 for( const vlc_fourcc_t *p_chroma = p_chroma_list; *p_chroma != 0; p_chroma++ )
2716 if( *p_chroma == VLC_CODEC_YUVP )
2717 rv = RenderYUVP( p_filter, p_region_out, p_lines, &bbox );
2718 else if( *p_chroma == VLC_CODEC_YUVA )
2719 rv = RenderAXYZ( p_filter, p_region_out, p_lines, &bbox, i_margin,
2724 else if( *p_chroma == VLC_CODEC_RGBA )
2725 rv = RenderAXYZ( p_filter, p_region_out, p_lines, &bbox, i_margin,
2734 /* With karaoke, we're going to have to render the text a number
2735 * of times to show the progress marker on the text.
2737 if( pi_k_durations )
2738 var_SetBool( p_filter, "text-rerender", true );
2741 FreeLines( p_lines );
2744 for( int i = 0; i < i_text_length; i++ )
2746 if( pp_styles[i] && ( i + 1 == i_text_length || pp_styles[i] != pp_styles[i + 1] ) )
2747 text_style_Delete( pp_styles[i] );
2750 free( pi_k_durations );
2755 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
2756 subpicture_region_t *p_region_in,
2757 const vlc_fourcc_t *p_chroma_list )
2759 return RenderCommon( p_filter, p_region_out, p_region_in, false, p_chroma_list );
2762 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2763 subpicture_region_t *p_region_in,
2764 const vlc_fourcc_t *p_chroma_list )
2766 return RenderCommon( p_filter, p_region_out, p_region_in, true, p_chroma_list );
2769 /*****************************************************************************
2770 * Create: allocates osd-text video thread output method
2771 *****************************************************************************
2772 * This function allocates and initializes a Clone vout method.
2773 *****************************************************************************/
2774 static int Create( vlc_object_t *p_this )
2776 filter_t *p_filter = (filter_t *)p_this;
2777 filter_sys_t *p_sys;
2778 char *psz_fontfile = NULL;
2779 char *psz_fontfamily = NULL;
2780 char *psz_monofontfile = NULL;
2781 char *psz_monofontfamily = NULL;
2782 int i_error = 0, fontindex = 0, monofontindex = 0;
2784 /* Allocate structure */
2785 p_filter->p_sys = p_sys = malloc( sizeof(*p_sys) );
2789 p_sys->psz_fontfamily = NULL;
2790 p_sys->p_xml = NULL;
2792 p_sys->p_library = 0;
2793 p_sys->i_font_size = 0;
2794 p_sys->i_display_height = 0;
2796 var_Create( p_filter, "freetype-rel-fontsize",
2797 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
2799 psz_fontfamily = var_InheritString( p_filter, "freetype-font" );
2800 psz_monofontfamily = var_InheritString( p_filter, "freetype-monofont" );
2801 p_sys->i_default_font_size = var_InheritInteger( p_filter, "freetype-fontsize" );
2802 p_sys->i_font_opacity = var_InheritInteger( p_filter,"freetype-opacity" );
2803 p_sys->i_font_opacity = VLC_CLIP( p_sys->i_font_opacity, 0, 255 );
2804 p_sys->i_font_color = var_InheritInteger( p_filter, "freetype-color" );
2805 p_sys->i_font_color = VLC_CLIP( p_sys->i_font_color, 0, 0xFFFFFF );
2806 p_sys->b_font_bold = var_InheritBool( p_filter, "freetype-bold" );
2808 p_sys->i_background_opacity = var_InheritInteger( p_filter,"freetype-background-opacity" );;
2809 p_sys->i_background_opacity = VLC_CLIP( p_sys->i_background_opacity, 0, 255 );
2810 p_sys->i_background_color = var_InheritInteger( p_filter, "freetype-background-color" );
2811 p_sys->i_background_color = VLC_CLIP( p_sys->i_background_color, 0, 0xFFFFFF );
2813 p_sys->f_outline_thickness = var_InheritInteger( p_filter, "freetype-outline-thickness" ) / 100.0;
2814 p_sys->f_outline_thickness = VLC_CLIP( p_sys->f_outline_thickness, 0.0, 0.5 );
2815 p_sys->i_outline_opacity = var_InheritInteger( p_filter, "freetype-outline-opacity" );
2816 p_sys->i_outline_opacity = VLC_CLIP( p_sys->i_outline_opacity, 0, 255 );
2817 p_sys->i_outline_color = var_InheritInteger( p_filter, "freetype-outline-color" );
2818 p_sys->i_outline_color = VLC_CLIP( p_sys->i_outline_color, 0, 0xFFFFFF );
2820 p_sys->i_shadow_opacity = var_InheritInteger( p_filter, "freetype-shadow-opacity" );
2821 p_sys->i_shadow_opacity = VLC_CLIP( p_sys->i_shadow_opacity, 0, 255 );
2822 p_sys->i_shadow_color = var_InheritInteger( p_filter, "freetype-shadow-color" );
2823 p_sys->i_shadow_color = VLC_CLIP( p_sys->i_shadow_color, 0, 0xFFFFFF );
2824 float f_shadow_angle = var_InheritFloat( p_filter, "freetype-shadow-angle" );
2825 float f_shadow_distance = var_InheritFloat( p_filter, "freetype-shadow-distance" );
2826 f_shadow_distance = VLC_CLIP( f_shadow_distance, 0, 1 );
2827 p_sys->f_shadow_vector_x = f_shadow_distance * cos(2 * M_PI * f_shadow_angle / 360);
2828 p_sys->f_shadow_vector_y = f_shadow_distance * sin(2 * M_PI * f_shadow_angle / 360);
2831 /* Get Windows Font folder */
2832 wchar_t wdir[MAX_PATH];
2833 if( S_OK != SHGetFolderPathW( NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, wdir ) )
2835 GetWindowsDirectoryW( wdir, MAX_PATH );
2836 wcscat( wdir, L"\\fonts" );
2838 p_sys->psz_win_fonts_path = FromWide( wdir );
2841 /* Set default psz_fontfamily */
2842 if( !psz_fontfamily || !*psz_fontfamily )
2844 free( psz_fontfamily );
2846 psz_fontfamily = strdup( DEFAULT_FAMILY );
2849 if( asprintf( &psz_fontfamily, "%s"DEFAULT_FONT_FILE, p_sys->psz_win_fonts_path ) == -1 )
2852 psz_fontfamily = strdup( DEFAULT_FONT_FILE );
2854 msg_Err( p_filter,"User specified an empty fontfile, using %s", psz_fontfamily );
2858 /* Set the current font file */
2859 p_sys->psz_fontfamily = psz_fontfamily;
2861 #ifdef HAVE_FONTCONFIG
2862 FontConfig_BuildCache( p_filter );
2865 psz_fontfile = FontConfig_Select( NULL, psz_fontfamily, false, false,
2866 p_sys->i_default_font_size, &fontindex );
2867 psz_monofontfile = FontConfig_Select( NULL, psz_monofontfamily, false,
2868 false, p_sys->i_default_font_size,
2870 #elif defined(__APPLE__)
2871 psz_fontfile = MacLegacy_Select( p_filter, psz_fontfamily, false, false, 0, &fontindex );
2872 #elif defined(WIN32)
2873 psz_fontfile = Win32_Select( p_filter, psz_fontfamily, false, false,
2874 p_sys->i_default_font_size, &fontindex );
2877 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily, psz_fontfile );
2879 /* If nothing is found, use the default family */
2881 psz_fontfile = strdup( psz_fontfamily );
2882 if( !psz_monofontfile )
2883 psz_monofontfile = strdup( psz_monofontfamily );
2885 #else /* !HAVE_STYLES */
2886 /* Use the default file */
2887 psz_fontfile = psz_fontfamily;
2888 psz_monofontfile = psz_monofontfamily;
2890 p_sys->psz_monofontfamily = psz_monofontfamily;
2893 i_error = FT_Init_FreeType( &p_sys->p_library );
2896 msg_Err( p_filter, "couldn't initialize freetype" );
2900 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
2901 fontindex, &p_sys->p_face );
2903 if( i_error == FT_Err_Unknown_File_Format )
2905 msg_Err( p_filter, "file %s have unknown format",
2906 psz_fontfile ? psz_fontfile : "(null)" );
2911 msg_Err( p_filter, "failed to load font file %s",
2912 psz_fontfile ? psz_fontfile : "(null)" );
2916 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
2919 msg_Err( p_filter, "font has no unicode translation table" );
2923 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
2925 p_sys->p_stroker = NULL;
2926 if( p_sys->f_outline_thickness > 0.001 )
2928 i_error = FT_Stroker_New( p_sys->p_library, &p_sys->p_stroker );
2930 msg_Err( p_filter, "Failed to create stroker for outlining" );
2933 p_sys->pp_font_attachments = NULL;
2934 p_sys->i_font_attachments = 0;
2936 p_filter->pf_render_text = RenderText;
2937 p_filter->pf_render_html = RenderHtml;
2939 LoadFontsFromAttachments( p_filter );
2942 free( psz_fontfile );
2943 free( psz_monofontfile );
2949 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
2950 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
2952 free( psz_fontfile );
2953 free( psz_monofontfile );
2955 free( psz_fontfamily );
2956 free( psz_monofontfamily );
2958 return VLC_EGENERIC;
2961 /*****************************************************************************
2962 * Destroy: destroy Clone video thread output method
2963 *****************************************************************************
2964 * Clean up all data and library connections
2965 *****************************************************************************/
2966 static void Destroy( vlc_object_t *p_this )
2968 filter_t *p_filter = (filter_t *)p_this;
2969 filter_sys_t *p_sys = p_filter->p_sys;
2971 if( p_sys->pp_font_attachments )
2973 for( int k = 0; k < p_sys->i_font_attachments; k++ )
2974 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
2976 free( p_sys->pp_font_attachments );
2979 if( p_sys->p_xml ) xml_ReaderDelete( p_sys->p_xml );
2980 free( p_sys->psz_fontfamily );
2983 free( p_sys->psz_win_fonts_path );
2986 /* FcFini asserts calling the subfunction FcCacheFini()
2987 * even if no other library functions have been made since FcInit(),
2988 * so don't call it. */
2990 if( p_sys->p_stroker )
2991 FT_Stroker_Done( p_sys->p_stroker );
2992 FT_Done_Face( p_sys->p_face );
2993 FT_Done_FreeType( p_sys->p_library );