1 /*****************************************************************************
2 * freetype.c : Put text on the video, using freetype2
3 *****************************************************************************
4 * Copyright (C) 2002 - 2011 the VideoLAN team
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>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 *****************************************************************************/
27 /*****************************************************************************
29 *****************************************************************************/
35 #include <vlc_common.h>
36 #include <vlc_plugin.h>
37 #include <vlc_stream.h> /* stream_MemoryNew */
38 #include <vlc_input.h> /* vlc_input_attachment_* */
39 #include <vlc_xml.h> /* xml_reader */
40 #include <vlc_strings.h> /* resolve_xml_special_chars */
41 #include <vlc_charset.h> /* ToCharset */
42 #include <vlc_dialog.h> /* FcCache dialog */
43 #include <vlc_filter.h> /* filter_sys_t */
44 #include <vlc_text_style.h> /* text_style_t*/
48 # define DEFAULT_FONT_FILE "/Library/Fonts/Arial Black.ttf"
49 # define DEFAULT_FAMILY "Arial Black"
50 #elif defined( WIN32 )
51 # define DEFAULT_FONT_FILE "arial.ttf" /* Default path font found at run-time */
52 # define DEFAULT_FAMILY "Arial"
53 #elif defined( HAVE_MAEMO )
54 # define DEFAULT_FONT_FILE "/usr/share/fonts/nokia/nosnb.ttf"
55 # define DEFAULT_FAMILY "Nokia Sans Bold"
57 # define DEFAULT_FONT_FILE "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf"
58 # define DEFAULT_FAMILY "Serif Bold"
62 #include <freetype/ftsynth.h>
63 #include FT_FREETYPE_H
65 #define FT_FLOOR(X) ((X & -64) >> 6)
66 #define FT_CEIL(X) (((X + 63) & -64) >> 6)
68 #define FT_MulFix(v, s) (((v)*(s))>>16)
72 #if defined(HAVE_FRIBIDI)
73 # include <fribidi/fribidi.h>
81 # undef HAVE_FONTCONFIG
85 #ifdef HAVE_FONTCONFIG
86 # include <fontconfig/fontconfig.h>
92 /*****************************************************************************
94 *****************************************************************************/
95 static int Create ( vlc_object_t * );
96 static void Destroy( vlc_object_t * );
98 #define FONT_TEXT N_("Font")
100 #define FAMILY_LONGTEXT N_("Font family for the font you want to use")
101 #define FONT_LONGTEXT N_("Font file for the font you want to use")
103 #define FONTSIZE_TEXT N_("Font size in pixels")
104 #define FONTSIZE_LONGTEXT N_("This is the default size of the fonts " \
105 "that will be rendered on the video. " \
106 "If set to something different than 0 this option will override the " \
107 "relative font size." )
108 #define OPACITY_TEXT N_("Opacity")
109 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of the " \
110 "text that will be rendered on the video. 0 = transparent, " \
111 "255 = totally opaque. " )
112 #define COLOR_TEXT N_("Text default color")
113 #define COLOR_LONGTEXT N_("The color of the text that will be rendered on "\
114 "the video. This must be an hexadecimal (like HTML colors). The first two "\
115 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
116 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
117 #define FONTSIZER_TEXT N_("Relative font size")
118 #define FONTSIZER_LONGTEXT N_("This is the relative default size of the " \
119 "fonts that will be rendered on the video. If absolute font size is set, "\
120 "relative size will be overridden." )
122 static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
123 static const char *const ppsz_sizes_text[] = {
124 N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
125 #define YUVP_TEXT N_("Use YUVP renderer")
126 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
127 "This option is only needed if you want to encode into DVB subtitles" )
129 static const int pi_color_values[] = {
130 0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
131 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
132 0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
134 static const char *const ppsz_color_descriptions[] = {
135 N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
136 N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
137 N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
140 set_shortname( N_("Text renderer"))
141 set_description( N_("Freetype2 font renderer") )
142 set_category( CAT_VIDEO )
143 set_subcategory( SUBCAT_VIDEO_SUBPIC )
146 add_font( "freetype-font", DEFAULT_FAMILY, FONT_TEXT, FAMILY_LONGTEXT, false )
148 add_loadfile( "freetype-font", DEFAULT_FONT_FILE, FONT_TEXT, FONT_LONGTEXT, false )
151 add_integer( "freetype-fontsize", 0, FONTSIZE_TEXT,
152 FONTSIZE_LONGTEXT, true )
155 /* opacity valid on 0..255, with default 255 = fully opaque */
156 add_integer_with_range( "freetype-opacity", 255, 0, 255,
157 OPACITY_TEXT, OPACITY_LONGTEXT, false )
160 /* hook to the color values list, with default 0x00ffffff = white */
161 add_integer( "freetype-color", 0x00FFFFFF, COLOR_TEXT,
162 COLOR_LONGTEXT, false )
163 change_integer_list( pi_color_values, ppsz_color_descriptions )
166 add_integer( "freetype-rel-fontsize", 16, FONTSIZER_TEXT,
167 FONTSIZER_LONGTEXT, false )
168 change_integer_list( pi_sizes, ppsz_sizes_text )
171 add_obsolete_integer( "freetype-effect" );
173 add_bool( "freetype-yuvp", false, YUVP_TEXT,
174 YUVP_LONGTEXT, true )
175 set_capability( "text renderer", 100 )
176 add_shortcut( "text" )
177 set_callbacks( Create, Destroy )
181 /*****************************************************************************
183 *****************************************************************************/
185 typedef struct line_desc_t line_desc_t;
188 /** NULL-terminated list of glyphs making the string */
189 FT_BitmapGlyph *pp_glyphs;
190 /** list of relative positions for the glyphs */
191 FT_Vector *p_glyph_pos;
192 /** list of ARGB information for styled text */
194 /** underline/strikethrough information */
196 uint16_t *pi_line_thickness;
203 typedef struct font_stack_t font_stack_t;
208 uint32_t i_color; /* ARGB */
209 uint32_t i_karaoke_bg_color; /* ARGB */
211 font_stack_t *p_next;
214 /*****************************************************************************
215 * filter_sys_t: freetype local data
216 *****************************************************************************
217 * This structure is part of the video output thread descriptor.
218 * It describes the freetype specific properties of an output thread.
219 *****************************************************************************/
222 FT_Library p_library; /* handle to library */
223 FT_Face p_face; /* handle to face object */
224 uint8_t i_font_opacity;
228 int i_default_font_size;
229 int i_display_height;
230 char* psz_fontfamily;
234 char* psz_win_fonts_path;
238 input_attachment_t **pp_font_attachments;
239 int i_font_attachments;
243 static void YUVFromRGB( uint32_t i_argb,
244 uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v )
246 int i_red = ( i_argb & 0x00ff0000 ) >> 16;
247 int i_green = ( i_argb & 0x0000ff00 ) >> 8;
248 int i_blue = ( i_argb & 0x000000ff );
250 *pi_y = (uint8_t)__MIN(abs( 2104 * i_red + 4130 * i_green +
251 802 * i_blue + 4096 + 131072 ) >> 13, 235);
252 *pi_u = (uint8_t)__MIN(abs( -1214 * i_red + -2384 * i_green +
253 3598 * i_blue + 4096 + 1048576) >> 13, 240);
254 *pi_v = (uint8_t)__MIN(abs( 3598 * i_red + -3013 * i_green +
255 -585 * i_blue + 4096 + 1048576) >> 13, 240);
258 /*****************************************************************************
259 * Make any TTF/OTF fonts present in the attachments of the media file
260 * and store them for later use by the FreeType Engine
261 *****************************************************************************/
262 static int LoadFontsFromAttachments( filter_t *p_filter )
264 filter_sys_t *p_sys = p_filter->p_sys;
265 input_attachment_t **pp_attachments;
266 int i_attachments_cnt;
268 if( filter_GetInputAttachments( p_filter, &pp_attachments, &i_attachments_cnt ) )
271 p_sys->i_font_attachments = 0;
272 p_sys->pp_font_attachments = malloc( i_attachments_cnt * sizeof(*p_sys->pp_font_attachments));
273 if( !p_sys->pp_font_attachments )
276 for( int k = 0; k < i_attachments_cnt; k++ )
278 input_attachment_t *p_attach = pp_attachments[k];
280 if( ( !strcmp( p_attach->psz_mime, "application/x-truetype-font" ) || // TTF
281 !strcmp( p_attach->psz_mime, "application/x-font-otf" ) ) && // OTF
282 p_attach->i_data > 0 && p_attach->p_data )
284 p_sys->pp_font_attachments[ p_sys->i_font_attachments++ ] = p_attach;
288 vlc_input_attachment_Delete( p_attach );
291 free( pp_attachments );
296 static int GetFontSize( filter_t *p_filter )
298 filter_sys_t *p_sys = p_filter->p_sys;
301 if( p_sys->i_default_font_size )
303 i_size = p_sys->i_default_font_size;
307 int i_ratio = var_GetInteger( p_filter, "freetype-rel-fontsize" );
310 i_size = (int)p_filter->fmt_out.video.i_height / i_ratio;
311 p_filter->p_sys->i_display_height = p_filter->fmt_out.video.i_height;
316 msg_Warn( p_filter, "invalid fontsize, using 12" );
322 static int SetFontSize( filter_t *p_filter, int i_size )
324 filter_sys_t *p_sys = p_filter->p_sys;
328 i_size = GetFontSize( p_filter );
330 msg_Dbg( p_filter, "using fontsize: %i", i_size );
333 p_sys->i_font_size = i_size;
335 if( FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_size ) )
337 msg_Err( p_filter, "couldn't set font size to %d", i_size );
345 #ifdef HAVE_FONTCONFIG
346 static void FontConfig_BuildCache( filter_t *p_filter )
349 msg_Dbg( p_filter, "Building font databases.");
354 dialog_progress_bar_t *p_dialog = NULL;
355 FcConfig *fcConfig = FcInitLoadConfig();
357 p_dialog = dialog_ProgressCreate( p_filter,
358 _("Building font cache"),
359 _("Please wait while your font cache is rebuilt.\n"
360 "This should take less than a few minutes."), NULL );
363 dialog_ProgressSet( p_dialog, NULL, 0.5 ); */
365 FcConfigBuildFonts( fcConfig );
368 // dialog_ProgressSet( p_dialog, NULL, 1.0 );
369 dialog_ProgressDestroy( p_dialog );
374 msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
378 * \brief Selects a font matching family, bold, italic provided
380 static char* FontConfig_Select( FcConfig* config, const char* family,
381 bool b_bold, bool b_italic, int i_size, int *i_idx )
383 FcResult result = FcResultMatch;
384 FcPattern *pat, *p_pat;
388 /* Create a pattern and fills it */
389 pat = FcPatternCreate();
390 if (!pat) return NULL;
393 FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
394 FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
395 FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
396 FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
400 if( asprintf( &psz_fontsize, "%d", i_size ) != -1 )
402 FcPatternAddString( pat, FC_SIZE, (const FcChar8 *)psz_fontsize );
403 free( psz_fontsize );
408 FcDefaultSubstitute( pat );
409 if( !FcConfigSubstitute( config, pat, FcMatchPattern ) )
411 FcPatternDestroy( pat );
415 /* Find the best font for the pattern, destroy the pattern */
416 p_pat = FcFontMatch( config, pat, &result );
417 FcPatternDestroy( pat );
418 if( !p_pat || result == FcResultNoMatch ) return NULL;
420 /* Check the new pattern */
421 if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
422 || ( val_b != FcTrue ) )
424 FcPatternDestroy( p_pat );
427 if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
432 if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
434 FcPatternDestroy( p_pat );
438 /* if( strcasecmp((const char*)val_s, family ) != 0 )
439 msg_Warn( p_filter, "fontconfig: selected font family is not"
440 "the requested one: '%s' != '%s'\n",
441 (const char*)val_s, family ); */
443 if( FcResultMatch != FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
445 FcPatternDestroy( p_pat );
449 FcPatternDestroy( p_pat );
450 return strdup( (const char*)val_s );
456 #define FONT_DIR_NT "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"
458 static int GetFileFontByName( const char *font_name, char **psz_filename )
461 wchar_t vbuffer[MAX_PATH];
462 wchar_t dbuffer[256];
464 if( RegOpenKeyEx(HKEY_LOCAL_MACHINE, FONT_DIR_NT, 0, KEY_READ, &hKey) != ERROR_SUCCESS )
467 for( int index = 0;; index++ )
469 DWORD vbuflen = MAX_PATH - 1;
472 if( RegEnumValueW( hKey, index, vbuffer, &vbuflen,
473 NULL, NULL, (LPBYTE)dbuffer, &dbuflen) != ERROR_SUCCESS )
476 char *psz_value = FromWide( vbuffer );
478 char *s = strchr( psz_value,'(' );
479 if( s != NULL && s != psz_value ) s[-1] = '\0';
481 /* Manage concatenated font names */
482 if( strchr( psz_value, '&') ) {
483 if( strcasestr( psz_value, font_name ) != NULL )
487 if( strcasecmp( psz_value, font_name ) == 0 )
492 *psz_filename = FromWide( dbuffer );
497 static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *lpelfe, const NEWTEXTMETRICEX *metric,
498 DWORD type, LPARAM lParam)
500 VLC_UNUSED( metric );
501 if( (type & RASTER_FONTTYPE) ) return 1;
502 // if( lpelfe->elfScript ) FIXME
504 return GetFileFontByName( (const char *)lpelfe->elfFullName, (char **)lParam );
507 static char* Win32_Select( filter_t *p_filter, const char* family,
508 bool b_bold, bool b_italic, int i_size, int *i_idx )
510 VLC_UNUSED( i_size );
511 // msg_Dbg( p_filter, "Here in Win32_Select, asking for %s", family );
515 lf.lfCharSet = DEFAULT_CHARSET;
519 lf.lfWeight = FW_BOLD;
520 strncpy( (LPSTR)&lf.lfFaceName, family, 32);
523 char *psz_filename = NULL;
524 HDC hDC = GetDC( NULL );
525 EnumFontFamiliesEx(hDC, &lf, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&psz_filename, 0);
526 ReleaseDC(NULL, hDC);
528 if( psz_filename == NULL )
531 /* FIXME: increase i_idx, when concatenated strings */
536 if( asprintf( &psz_tmp, "%s\\%s", p_filter->p_sys->psz_win_fonts_path, psz_filename ) == -1 )
545 /*****************************************************************************
546 * RenderYUVP: place string in picture
547 *****************************************************************************
548 * This function merges the previously rendered freetype glyphs into a picture
549 *****************************************************************************/
550 static int RenderYUVP( filter_t *p_filter, subpicture_region_t *p_region,
551 line_desc_t *p_line, int i_width, int i_height )
553 VLC_UNUSED(p_filter);
554 static const uint8_t pi_gamma[16] =
555 {0x00, 0x52, 0x84, 0x96, 0xb8, 0xca, 0xdc, 0xee, 0xff,
556 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
560 int i, x, y, i_pitch;
561 uint8_t i_y, i_u, i_v; /* YUV values, derived from incoming RGB */
563 /* Create a new subpicture region */
564 memset( &fmt, 0, sizeof(video_format_t) );
565 fmt.i_chroma = VLC_CODEC_YUVP;
566 fmt.i_width = fmt.i_visible_width = i_width + 4;
567 fmt.i_height = fmt.i_visible_height = i_height + 4;
568 if( p_region->fmt.i_visible_width > 0 )
569 fmt.i_visible_width = p_region->fmt.i_visible_width;
570 if( p_region->fmt.i_visible_height > 0 )
571 fmt.i_visible_height = p_region->fmt.i_visible_height;
572 fmt.i_x_offset = fmt.i_y_offset = 0;
576 assert( !p_region->p_picture );
577 p_region->p_picture = picture_NewFromFormat( &fmt );
578 if( !p_region->p_picture )
580 fmt.p_palette = p_region->fmt.p_palette ? p_region->fmt.p_palette : malloc(sizeof(*fmt.p_palette));
583 /* Calculate text color components
584 * Only use the first color */
585 int i_alpha = 0xff - ((p_line->pi_color[ 0 ] >> 24) & 0xff);
586 YUVFromRGB( p_line->pi_color[ 0 ], &i_y, &i_u, &i_v );
589 fmt.p_palette->i_entries = 16;
590 for( i = 0; i < 8; i++ )
592 fmt.p_palette->palette[i][0] = 0;
593 fmt.p_palette->palette[i][1] = 0x80;
594 fmt.p_palette->palette[i][2] = 0x80;
595 fmt.p_palette->palette[i][3] = pi_gamma[i];
596 fmt.p_palette->palette[i][3] =
597 (int)fmt.p_palette->palette[i][3] * i_alpha / 255;
599 for( i = 8; i < fmt.p_palette->i_entries; i++ )
601 fmt.p_palette->palette[i][0] = i * 16 * i_y / 256;
602 fmt.p_palette->palette[i][1] = i_u;
603 fmt.p_palette->palette[i][2] = i_v;
604 fmt.p_palette->palette[i][3] = pi_gamma[i];
605 fmt.p_palette->palette[i][3] =
606 (int)fmt.p_palette->palette[i][3] * i_alpha / 255;
609 p_dst = p_region->p_picture->Y_PIXELS;
610 i_pitch = p_region->p_picture->Y_PITCH;
612 /* Initialize the region pixels */
613 memset( p_dst, 0, i_pitch * p_region->fmt.i_height );
615 for( ; p_line != NULL; p_line = p_line->p_next )
617 int i_glyph_tmax = 0;
618 int i_bitmap_offset, i_offset, i_align_offset = 0;
619 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
621 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
622 i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
625 if( p_line->i_width < i_width )
627 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
629 i_align_offset = i_width - p_line->i_width;
631 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
633 i_align_offset = ( i_width - p_line->i_width ) / 2;
637 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
639 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
641 i_offset = ( p_line->p_glyph_pos[ i ].y +
642 i_glyph_tmax - p_glyph->top + 2 ) *
643 i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 2 +
646 for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
648 for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
650 if( p_glyph->bitmap.buffer[i_bitmap_offset] )
652 ((int)p_glyph->bitmap.buffer[i_bitmap_offset] + 8)/16;
659 /* Outlining (find something better than nearest neighbour filtering ?) */
662 uint8_t *p_dst = p_region->p_picture->Y_PIXELS;
663 uint8_t *p_top = p_dst; /* Use 1st line as a cache */
664 uint8_t left, current;
666 for( y = 1; y < (int)fmt.i_height - 1; y++ )
668 if( y > 1 ) memcpy( p_top, p_dst, fmt.i_width );
669 p_dst += p_region->p_picture->Y_PITCH;
672 for( x = 1; x < (int)fmt.i_width - 1; x++ )
675 p_dst[x] = ( 8 * (int)p_dst[x] + left + p_dst[x+1] + p_top[x -1]+ p_top[x] + p_top[x+1] +
676 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;
680 memset( p_top, 0, fmt.i_width );
686 /*****************************************************************************
687 * RenderYUVA: place string in picture
688 *****************************************************************************
689 * This function merges the previously rendered freetype glyphs into a picture
690 *****************************************************************************/
691 static inline void BlendYUVAPixel( picture_t *p_picture,
692 int i_picture_x, int i_picture_y,
693 int i_a, int i_y, int i_u, int i_v,
696 int i_an = i_a * i_alpha / 255;
698 uint8_t *p_y = &p_picture->p[0].p_pixels[i_picture_y * p_picture->p[0].i_pitch + i_picture_x];
699 uint8_t *p_u = &p_picture->p[1].p_pixels[i_picture_y * p_picture->p[1].i_pitch + i_picture_x];
700 uint8_t *p_v = &p_picture->p[2].p_pixels[i_picture_y * p_picture->p[2].i_pitch + i_picture_x];
701 uint8_t *p_a = &p_picture->p[3].p_pixels[i_picture_y * p_picture->p[3].i_pitch + i_picture_x];
713 *p_a = 255 - (255 - *p_a) * (255 - i_an) / 255;
716 *p_y = ( *p_y * i_ao * (255 - i_an) / 255 + i_y * i_an ) / *p_a;
717 *p_u = ( *p_u * i_ao * (255 - i_an) / 255 + i_u * i_an ) / *p_a;
718 *p_v = ( *p_v * i_ao * (255 - i_an) / 255 + i_v * i_an ) / *p_a;
723 static inline void BlendYUVAGlyph( picture_t *p_picture,
724 int i_picture_x, int i_picture_y,
725 int i_a, int i_y, int i_u, int i_v,
726 FT_BitmapGlyph p_glyph )
728 for( int dy = 0; dy < p_glyph->bitmap.rows; dy++ )
730 for( int dx = 0; dx < p_glyph->bitmap.width; dx++ )
731 BlendYUVAPixel( p_picture, i_picture_x + dx, i_picture_y + dy,
733 p_glyph->bitmap.buffer[dy * p_glyph->bitmap.width + dx] );
737 static inline void BlendYUVALine( picture_t *p_picture,
738 int i_picture_x, int i_picture_y,
739 int i_a, int i_y, int i_u, int i_v,
740 FT_BitmapGlyph p_glyph_current,
741 FT_BitmapGlyph p_glyph_next,
742 FT_Vector *p_pos_current,
743 FT_Vector *p_pos_next,
744 int i_line_thickness,
748 int i_line_width = p_glyph_current->bitmap.width;
750 i_line_width = (p_pos_next->x + p_glyph_next->left) -
751 (p_pos_current->x + p_glyph_current->left);
753 for( int dx = 0; dx < i_line_width; dx++ )
755 /* break the underline around the tails of any glyphs which cross it
756 Strikethrough doesn't get broken */
758 for( int z = dx - i_line_thickness; z < dx + i_line_thickness && b_ok && i_line_offset >= 0; z++ )
760 FT_BitmapGlyph p_glyph_check = NULL;
762 if( p_glyph_next && z >= i_line_width )
764 i_column = z - i_line_width;
765 p_glyph_check = p_glyph_next;
767 else if( z >= 0 && z < p_glyph_current->bitmap.width )
770 p_glyph_check = p_glyph_current;
774 const FT_Bitmap *p_bitmap = &p_glyph_check->bitmap;
775 for( int dy = 0; dy < i_line_thickness && b_ok; dy++ )
777 int i_row = i_line_offset + p_glyph_check->top + dy;
778 b_ok = i_row >= p_bitmap->rows ||
779 p_bitmap->buffer[p_bitmap->width * i_row + i_column] == 0;
784 for( int dy = 0; dy < i_line_thickness && b_ok; dy++ )
785 BlendYUVAPixel( p_picture, i_picture_x + dx, i_picture_y + i_line_offset + dy,
786 i_a, i_y, i_u, i_v, 0xff );
790 static int RenderYUVA( filter_t *p_filter,
791 subpicture_region_t *p_region,
792 line_desc_t *p_line_head,
793 int i_width, int i_height )
795 filter_sys_t *p_sys = p_filter->p_sys;
797 /* Create a new subpicture region */
799 video_format_Init( &fmt, VLC_CODEC_YUVA );
801 fmt.i_visible_width = i_width;
803 fmt.i_visible_height = i_height;
805 picture_t *p_picture = p_region->p_picture = picture_NewFromFormat( &fmt );
806 if( !p_region->p_picture )
810 /* Initialize the picture background */
811 uint32_t i_background = 0 ? 0x80000000 : 0xff000000;
812 uint8_t i_a = 0xff - ((i_background >> 24) & 0xff);
813 uint8_t i_y, i_u, i_v;
814 YUVFromRGB( i_background, &i_y, &i_u, &i_v );
816 memset( p_picture->p[0].p_pixels, i_y,
817 p_picture->p[0].i_pitch * p_picture->p[0].i_lines );
818 memset( p_picture->p[1].p_pixels, i_u,
819 p_picture->p[1].i_pitch * p_picture->p[1].i_lines );
820 memset( p_picture->p[2].p_pixels, i_v,
821 p_picture->p[2].i_pitch * p_picture->p[2].i_lines );
822 memset( p_picture->p[3].p_pixels, i_a,
823 p_picture->p[3].i_pitch * p_picture->p[3].i_lines );
825 /* Render all lines */
826 for( line_desc_t *p_line = p_line_head; p_line != NULL; p_line = p_line->p_next )
828 /* Left offset to take into account alignment */
829 int i_align_left = 0;
830 if( p_line->i_width < i_width )
832 if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
833 i_align_left = i_width - p_line->i_width;
834 else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
835 i_align_left = ( i_width - p_line->i_width ) / 2;
838 /* Compute the top alignment
839 * FIXME seems bad (it seems that the glyphs are aligned too high) */
841 for( int i = 0; p_line->pp_glyphs[i]; i++ )
842 i_align_top = __MAX( i_align_top, p_line->pp_glyphs[i]->top );
844 /* Render all glyphs and underline/strikethrough */
845 for( int i = 0; p_line->pp_glyphs[i]; i++ )
847 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[i];
849 uint32_t i_color = p_line->pi_color[i];
850 i_a = 0xff - ((i_color >> 24) & 0xff);
851 YUVFromRGB( i_color, &i_y, &i_u, &i_v );
853 int i_picture_y = p_line->p_glyph_pos[i].y + i_align_top;
854 int i_picture_x = p_line->p_glyph_pos[i].x + i_align_left + p_glyph->left;
856 BlendYUVAGlyph( p_picture, i_picture_x, i_picture_y - p_glyph->top,
860 const int i_line_thickness = p_line->pi_line_thickness[i];
861 const int i_line_offset = p_line->pi_line_offset[i];
862 if( i_line_thickness > 0 )
863 BlendYUVALine( p_picture, i_picture_x, i_picture_y,
865 p_glyph, p_line->pp_glyphs[i + 1],
866 &p_line->p_glyph_pos[i], &p_line->p_glyph_pos[i + 1],
867 i_line_thickness, i_line_offset,
868 p_line->pp_glyphs[i + 1] && p_line->pi_line_thickness[i + 1] > 0 );
875 static text_style_t *CreateStyle( char *psz_fontname, int i_font_size,
876 uint32_t i_font_color, uint32_t i_karaoke_bg_color,
879 text_style_t *p_style = text_style_New();
883 p_style->psz_fontname = psz_fontname ? strdup( psz_fontname ) : NULL;
884 p_style->i_font_size = i_font_size;
885 p_style->i_font_color = (i_font_color & 0x00ffffff) >> 0;
886 p_style->i_font_alpha = (i_font_color & 0xff000000) >> 24;
887 p_style->i_karaoke_background_color = (i_karaoke_bg_color & 0x00ffffff) >> 0;
888 p_style->i_karaoke_background_alpha = (i_karaoke_bg_color & 0xff000000) >> 24;
889 p_style->i_style_flags |= i_style_flags;
893 static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
894 uint32_t i_color, uint32_t i_karaoke_bg_color )
899 font_stack_t *p_new = malloc( sizeof(*p_new) );
903 p_new->p_next = NULL;
906 p_new->psz_name = strdup( psz_name );
908 p_new->psz_name = NULL;
910 p_new->i_size = i_size;
911 p_new->i_color = i_color;
912 p_new->i_karaoke_bg_color = i_karaoke_bg_color;
920 font_stack_t *p_last;
922 for( p_last = *p_font;
924 p_last = p_last->p_next )
927 p_last->p_next = p_new;
932 static int PopFont( font_stack_t **p_font )
934 font_stack_t *p_last, *p_next_to_last;
936 if( !p_font || !*p_font )
939 p_next_to_last = NULL;
940 for( p_last = *p_font;
942 p_last = p_last->p_next )
944 p_next_to_last = p_last;
948 p_next_to_last->p_next = NULL;
952 free( p_last->psz_name );
958 static int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
959 uint32_t *i_color, uint32_t *i_karaoke_bg_color )
961 font_stack_t *p_last;
963 if( !p_font || !*p_font )
968 p_last=p_last->p_next )
971 *psz_name = p_last->psz_name;
972 *i_size = p_last->i_size;
973 *i_color = p_last->i_color;
974 *i_karaoke_bg_color = p_last->i_karaoke_bg_color;
979 static const struct {
980 const char *psz_name;
982 } p_html_colors[] = {
983 /* Official html colors */
984 { "Aqua", 0x00FFFF },
985 { "Black", 0x000000 },
986 { "Blue", 0x0000FF },
987 { "Fuchsia", 0xFF00FF },
988 { "Gray", 0x808080 },
989 { "Green", 0x008000 },
990 { "Lime", 0x00FF00 },
991 { "Maroon", 0x800000 },
992 { "Navy", 0x000080 },
993 { "Olive", 0x808000 },
994 { "Purple", 0x800080 },
996 { "Silver", 0xC0C0C0 },
997 { "Teal", 0x008080 },
998 { "White", 0xFFFFFF },
999 { "Yellow", 0xFFFF00 },
1002 { "AliceBlue", 0xF0F8FF },
1003 { "AntiqueWhite", 0xFAEBD7 },
1004 { "Aqua", 0x00FFFF },
1005 { "Aquamarine", 0x7FFFD4 },
1006 { "Azure", 0xF0FFFF },
1007 { "Beige", 0xF5F5DC },
1008 { "Bisque", 0xFFE4C4 },
1009 { "Black", 0x000000 },
1010 { "BlanchedAlmond", 0xFFEBCD },
1011 { "Blue", 0x0000FF },
1012 { "BlueViolet", 0x8A2BE2 },
1013 { "Brown", 0xA52A2A },
1014 { "BurlyWood", 0xDEB887 },
1015 { "CadetBlue", 0x5F9EA0 },
1016 { "Chartreuse", 0x7FFF00 },
1017 { "Chocolate", 0xD2691E },
1018 { "Coral", 0xFF7F50 },
1019 { "CornflowerBlue", 0x6495ED },
1020 { "Cornsilk", 0xFFF8DC },
1021 { "Crimson", 0xDC143C },
1022 { "Cyan", 0x00FFFF },
1023 { "DarkBlue", 0x00008B },
1024 { "DarkCyan", 0x008B8B },
1025 { "DarkGoldenRod", 0xB8860B },
1026 { "DarkGray", 0xA9A9A9 },
1027 { "DarkGrey", 0xA9A9A9 },
1028 { "DarkGreen", 0x006400 },
1029 { "DarkKhaki", 0xBDB76B },
1030 { "DarkMagenta", 0x8B008B },
1031 { "DarkOliveGreen", 0x556B2F },
1032 { "Darkorange", 0xFF8C00 },
1033 { "DarkOrchid", 0x9932CC },
1034 { "DarkRed", 0x8B0000 },
1035 { "DarkSalmon", 0xE9967A },
1036 { "DarkSeaGreen", 0x8FBC8F },
1037 { "DarkSlateBlue", 0x483D8B },
1038 { "DarkSlateGray", 0x2F4F4F },
1039 { "DarkSlateGrey", 0x2F4F4F },
1040 { "DarkTurquoise", 0x00CED1 },
1041 { "DarkViolet", 0x9400D3 },
1042 { "DeepPink", 0xFF1493 },
1043 { "DeepSkyBlue", 0x00BFFF },
1044 { "DimGray", 0x696969 },
1045 { "DimGrey", 0x696969 },
1046 { "DodgerBlue", 0x1E90FF },
1047 { "FireBrick", 0xB22222 },
1048 { "FloralWhite", 0xFFFAF0 },
1049 { "ForestGreen", 0x228B22 },
1050 { "Fuchsia", 0xFF00FF },
1051 { "Gainsboro", 0xDCDCDC },
1052 { "GhostWhite", 0xF8F8FF },
1053 { "Gold", 0xFFD700 },
1054 { "GoldenRod", 0xDAA520 },
1055 { "Gray", 0x808080 },
1056 { "Grey", 0x808080 },
1057 { "Green", 0x008000 },
1058 { "GreenYellow", 0xADFF2F },
1059 { "HoneyDew", 0xF0FFF0 },
1060 { "HotPink", 0xFF69B4 },
1061 { "IndianRed", 0xCD5C5C },
1062 { "Indigo", 0x4B0082 },
1063 { "Ivory", 0xFFFFF0 },
1064 { "Khaki", 0xF0E68C },
1065 { "Lavender", 0xE6E6FA },
1066 { "LavenderBlush", 0xFFF0F5 },
1067 { "LawnGreen", 0x7CFC00 },
1068 { "LemonChiffon", 0xFFFACD },
1069 { "LightBlue", 0xADD8E6 },
1070 { "LightCoral", 0xF08080 },
1071 { "LightCyan", 0xE0FFFF },
1072 { "LightGoldenRodYellow", 0xFAFAD2 },
1073 { "LightGray", 0xD3D3D3 },
1074 { "LightGrey", 0xD3D3D3 },
1075 { "LightGreen", 0x90EE90 },
1076 { "LightPink", 0xFFB6C1 },
1077 { "LightSalmon", 0xFFA07A },
1078 { "LightSeaGreen", 0x20B2AA },
1079 { "LightSkyBlue", 0x87CEFA },
1080 { "LightSlateGray", 0x778899 },
1081 { "LightSlateGrey", 0x778899 },
1082 { "LightSteelBlue", 0xB0C4DE },
1083 { "LightYellow", 0xFFFFE0 },
1084 { "Lime", 0x00FF00 },
1085 { "LimeGreen", 0x32CD32 },
1086 { "Linen", 0xFAF0E6 },
1087 { "Magenta", 0xFF00FF },
1088 { "Maroon", 0x800000 },
1089 { "MediumAquaMarine", 0x66CDAA },
1090 { "MediumBlue", 0x0000CD },
1091 { "MediumOrchid", 0xBA55D3 },
1092 { "MediumPurple", 0x9370D8 },
1093 { "MediumSeaGreen", 0x3CB371 },
1094 { "MediumSlateBlue", 0x7B68EE },
1095 { "MediumSpringGreen", 0x00FA9A },
1096 { "MediumTurquoise", 0x48D1CC },
1097 { "MediumVioletRed", 0xC71585 },
1098 { "MidnightBlue", 0x191970 },
1099 { "MintCream", 0xF5FFFA },
1100 { "MistyRose", 0xFFE4E1 },
1101 { "Moccasin", 0xFFE4B5 },
1102 { "NavajoWhite", 0xFFDEAD },
1103 { "Navy", 0x000080 },
1104 { "OldLace", 0xFDF5E6 },
1105 { "Olive", 0x808000 },
1106 { "OliveDrab", 0x6B8E23 },
1107 { "Orange", 0xFFA500 },
1108 { "OrangeRed", 0xFF4500 },
1109 { "Orchid", 0xDA70D6 },
1110 { "PaleGoldenRod", 0xEEE8AA },
1111 { "PaleGreen", 0x98FB98 },
1112 { "PaleTurquoise", 0xAFEEEE },
1113 { "PaleVioletRed", 0xD87093 },
1114 { "PapayaWhip", 0xFFEFD5 },
1115 { "PeachPuff", 0xFFDAB9 },
1116 { "Peru", 0xCD853F },
1117 { "Pink", 0xFFC0CB },
1118 { "Plum", 0xDDA0DD },
1119 { "PowderBlue", 0xB0E0E6 },
1120 { "Purple", 0x800080 },
1121 { "Red", 0xFF0000 },
1122 { "RosyBrown", 0xBC8F8F },
1123 { "RoyalBlue", 0x4169E1 },
1124 { "SaddleBrown", 0x8B4513 },
1125 { "Salmon", 0xFA8072 },
1126 { "SandyBrown", 0xF4A460 },
1127 { "SeaGreen", 0x2E8B57 },
1128 { "SeaShell", 0xFFF5EE },
1129 { "Sienna", 0xA0522D },
1130 { "Silver", 0xC0C0C0 },
1131 { "SkyBlue", 0x87CEEB },
1132 { "SlateBlue", 0x6A5ACD },
1133 { "SlateGray", 0x708090 },
1134 { "SlateGrey", 0x708090 },
1135 { "Snow", 0xFFFAFA },
1136 { "SpringGreen", 0x00FF7F },
1137 { "SteelBlue", 0x4682B4 },
1138 { "Tan", 0xD2B48C },
1139 { "Teal", 0x008080 },
1140 { "Thistle", 0xD8BFD8 },
1141 { "Tomato", 0xFF6347 },
1142 { "Turquoise", 0x40E0D0 },
1143 { "Violet", 0xEE82EE },
1144 { "Wheat", 0xF5DEB3 },
1145 { "White", 0xFFFFFF },
1146 { "WhiteSmoke", 0xF5F5F5 },
1147 { "Yellow", 0xFFFF00 },
1148 { "YellowGreen", 0x9ACD32 },
1153 static int HandleFontAttributes( xml_reader_t *p_xml_reader,
1154 font_stack_t **p_fonts )
1157 char *psz_fontname = NULL;
1158 uint32_t i_font_color = 0xffffff;
1159 int i_font_alpha = 0;
1160 uint32_t i_karaoke_bg_color = 0x00ffffff;
1161 int i_font_size = 24;
1163 /* Default all attributes to the top font in the stack -- in case not
1164 * all attributes are specified in the sub-font
1166 if( VLC_SUCCESS == PeekFont( p_fonts,
1170 &i_karaoke_bg_color ))
1172 psz_fontname = strdup( psz_fontname );
1173 i_font_size = i_font_size;
1175 i_font_alpha = (i_font_color >> 24) & 0xff;
1176 i_font_color &= 0x00ffffff;
1178 const char *name, *value;
1179 while( (name = xml_ReaderNextAttr( p_xml_reader, &value )) != NULL )
1181 if( !strcasecmp( "face", name ) )
1183 free( psz_fontname );
1184 psz_fontname = strdup( value );
1186 else if( !strcasecmp( "size", name ) )
1188 if( ( *value == '+' ) || ( *value == '-' ) )
1190 int i_value = atoi( value );
1192 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
1193 i_font_size += ( i_value * i_font_size ) / 10;
1194 else if( i_value < -5 )
1195 i_font_size = - i_value;
1196 else if( i_value > 5 )
1197 i_font_size = i_value;
1200 i_font_size = atoi( value );
1202 else if( !strcasecmp( "color", name ) )
1204 if( value[0] == '#' )
1206 i_font_color = strtol( value + 1, NULL, 16 );
1207 i_font_color &= 0x00ffffff;
1211 for( int i = 0; p_html_colors[i].psz_name != NULL; i++ )
1213 if( !strncasecmp( value, p_html_colors[i].psz_name, strlen(p_html_colors[i].psz_name) ) )
1215 i_font_color = p_html_colors[i].i_value;
1221 else if( !strcasecmp( "alpha", name ) && ( value[0] == '#' ) )
1223 i_font_alpha = strtol( value + 1, NULL, 16 );
1224 i_font_alpha &= 0xff;
1227 rv = PushFont( p_fonts,
1230 (i_font_color & 0xffffff) | ((i_font_alpha & 0xff) << 24),
1231 i_karaoke_bg_color );
1233 free( psz_fontname );
1238 /* Turn any multiple-whitespaces into single spaces */
1239 static void HandleWhiteSpace( char *psz_node )
1241 char *s = strpbrk( psz_node, "\t\r\n " );
1244 int i_whitespace = strspn( s, "\t\r\n " );
1246 if( i_whitespace > 1 )
1249 strlen( s ) - i_whitespace + 1 );
1252 s = strpbrk( s, "\t\r\n " );
1257 static text_style_t *GetStyleFromFontStack( filter_sys_t *p_sys,
1258 font_stack_t **p_fonts,
1261 char *psz_fontname = NULL;
1262 uint32_t i_font_color = p_sys->i_font_color & 0x00ffffff;
1263 uint32_t i_karaoke_bg_color = i_font_color;
1264 int i_font_size = p_sys->i_font_size;
1266 if( PeekFont( p_fonts, &psz_fontname, &i_font_size,
1267 &i_font_color, &i_karaoke_bg_color ) )
1270 return CreateStyle( psz_fontname, i_font_size, i_font_color,
1275 static unsigned SetupText( filter_t *p_filter,
1276 uint32_t *psz_text_out,
1277 text_style_t **pp_styles,
1278 uint32_t *pi_k_dates,
1280 const char *psz_text_in,
1281 text_style_t *p_style,
1284 size_t i_string_length;
1286 size_t i_string_bytes;
1287 #if defined(WORDS_BIGENDIAN)
1288 uint32_t *psz_tmp = ToCharset( "UCS-4BE", psz_text_in, &i_string_bytes );
1290 uint32_t *psz_tmp = ToCharset( "UCS-4LE", psz_text_in, &i_string_bytes );
1294 memcpy( psz_text_out, psz_tmp, i_string_bytes );
1295 i_string_length = i_string_bytes / 4;
1300 msg_Warn( p_filter, "failed to convert string to unicode (%m)" );
1301 i_string_length = 0;
1304 if( i_string_length > 0 )
1306 for( unsigned i = 0; i < i_string_length; i++ )
1307 pp_styles[i] = p_style;
1311 text_style_Delete( p_style );
1313 if( i_string_length > 0 && pi_k_dates )
1315 for( unsigned i = 0; i < i_string_length; i++ )
1316 pi_k_dates[i] = i_k_date;
1318 return i_string_length;
1321 static int ProcessNodes( filter_t *p_filter,
1323 text_style_t **pp_styles,
1324 uint32_t *pi_k_dates,
1326 xml_reader_t *p_xml_reader,
1327 text_style_t *p_font_style )
1329 int rv = VLC_SUCCESS;
1330 filter_sys_t *p_sys = p_filter->p_sys;
1331 int i_text_length = 0;
1332 font_stack_t *p_fonts = NULL;
1333 uint32_t i_k_date = 0;
1335 int i_style_flags = 0;
1339 rv = PushFont( &p_fonts,
1340 p_font_style->psz_fontname,
1341 p_font_style->i_font_size,
1342 (p_font_style->i_font_color & 0xffffff) |
1343 ((p_font_style->i_font_alpha & 0xff) << 24),
1344 (p_font_style->i_karaoke_background_color & 0xffffff) |
1345 ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
1347 i_style_flags = p_font_style->i_style_flags & (STYLE_BOLD |
1355 rv = PushFont( &p_fonts,
1356 p_sys->psz_fontfamily,
1358 (p_sys->i_font_color & 0xffffff) |
1359 (((255-p_sys->i_font_opacity) & 0xff) << 24),
1364 if( rv != VLC_SUCCESS )
1370 while ( (type = xml_ReaderNextNode( p_xml_reader, &node )) > 0 )
1374 case XML_READER_ENDELEM:
1375 if( !strcasecmp( "font", node ) )
1376 PopFont( &p_fonts );
1377 else if( !strcasecmp( "b", node ) )
1378 i_style_flags &= ~STYLE_BOLD;
1379 else if( !strcasecmp( "i", node ) )
1380 i_style_flags &= ~STYLE_ITALIC;
1381 else if( !strcasecmp( "u", node ) )
1382 i_style_flags &= ~STYLE_UNDERLINE;
1383 else if( !strcasecmp( "s", node ) )
1384 i_style_flags &= ~STYLE_STRIKEOUT;
1387 case XML_READER_STARTELEM:
1388 if( !strcasecmp( "font", node ) )
1389 HandleFontAttributes( p_xml_reader, &p_fonts );
1390 else if( !strcasecmp( "b", node ) )
1391 i_style_flags |= STYLE_BOLD;
1392 else if( !strcasecmp( "i", node ) )
1393 i_style_flags |= STYLE_ITALIC;
1394 else if( !strcasecmp( "u", node ) )
1395 i_style_flags |= STYLE_UNDERLINE;
1396 else if( !strcasecmp( "s", node ) )
1397 i_style_flags |= STYLE_STRIKEOUT;
1398 else if( !strcasecmp( "br", node ) )
1400 i_text_length += SetupText( p_filter,
1401 &psz_text[i_text_length],
1402 &pp_styles[i_text_length],
1403 pi_k_dates ? &pi_k_dates[i_text_length] : NULL,
1405 GetStyleFromFontStack( p_sys,
1410 else if( !strcasecmp( "k", node ) )
1413 const char *name, *value;
1414 while( (name = xml_ReaderNextAttr( p_xml_reader, &value )) != NULL )
1416 if( !strcasecmp( "t", name ) && value )
1417 i_k_date += atoi( value );
1422 case XML_READER_TEXT:
1424 char *psz_node = strdup( node );
1425 if( unlikely(!psz_node) )
1428 HandleWhiteSpace( psz_node );
1429 resolve_xml_special_chars( psz_node );
1431 i_text_length += SetupText( p_filter,
1432 &psz_text[i_text_length],
1433 &pp_styles[i_text_length],
1434 pi_k_dates ? &pi_k_dates[i_text_length] : NULL,
1436 GetStyleFromFontStack( p_sys,
1446 *pi_len = i_text_length;
1448 while( VLC_SUCCESS == PopFont( &p_fonts ) );
1453 static void FreeLine( line_desc_t *p_line )
1455 for( int i = 0; p_line->pp_glyphs && p_line->pp_glyphs[i] != NULL; i++ )
1456 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[i] );
1458 free( p_line->pp_glyphs );
1459 free( p_line->p_glyph_pos );
1460 free( p_line->pi_color );
1461 free( p_line->pi_line_offset );
1462 free( p_line->pi_line_thickness );
1466 static void FreeLines( line_desc_t *p_lines )
1468 for( line_desc_t *p_line = p_lines; p_line != NULL; )
1470 line_desc_t *p_next = p_line->p_next;
1476 static line_desc_t *NewLine( int i_count )
1478 line_desc_t *p_line = malloc( sizeof(*p_line) );
1483 p_line->i_width = 0;
1485 p_line->p_next = NULL;
1487 p_line->pp_glyphs = calloc( i_count + 1, sizeof(*p_line->pp_glyphs) );
1488 p_line->p_glyph_pos = calloc( i_count + 1, sizeof(*p_line->p_glyph_pos) );
1489 p_line->pi_color = calloc( i_count + 1, sizeof(*p_line->pi_color) );
1490 p_line->pi_line_offset = calloc( i_count + 1, sizeof(*p_line->pi_line_offset) );
1491 p_line->pi_line_thickness = calloc( i_count + 1, sizeof(*p_line->pi_line_thickness) );
1493 if( !p_line->pp_glyphs || !p_line->p_glyph_pos ||
1494 !p_line->pi_color ||
1495 !p_line->pi_line_offset || !p_line->pi_line_thickness )
1500 p_line->pp_glyphs[0] = NULL;
1504 static FT_Face LoadEmbeddedFace( filter_sys_t *p_sys, const text_style_t *p_style )
1506 for( int k = 0; k < p_sys->i_font_attachments; k++ )
1508 input_attachment_t *p_attach = p_sys->pp_font_attachments[k];
1510 FT_Face p_face = NULL;
1512 while( 0 == FT_New_Memory_Face( p_sys->p_library,
1520 int i_style_received = ((p_face->style_flags & FT_STYLE_FLAG_BOLD) ? STYLE_BOLD : 0) |
1521 ((p_face->style_flags & FT_STYLE_FLAG_ITALIC ) ? STYLE_ITALIC : 0);
1522 if( !strcasecmp( p_face->family_name, p_style->psz_fontname ) &&
1523 (p_style->i_style_flags & (STYLE_BOLD | STYLE_BOLD)) == i_style_received )
1526 FT_Done_Face( p_face );
1534 static FT_Face LoadFace( filter_t *p_filter,
1535 const text_style_t *p_style )
1537 filter_sys_t *p_sys = p_filter->p_sys;
1539 /* Look for a match amongst our attachments first */
1540 FT_Face p_face = LoadEmbeddedFace( p_sys, p_style );
1542 /* Load system wide font otheriwse */
1547 #ifdef HAVE_FONTCONFIG
1548 psz_fontfile = FontConfig_Select( NULL,
1549 p_style->psz_fontname,
1550 (p_style->i_style_flags & STYLE_BOLD) != 0,
1551 (p_style->i_style_flags & STYLE_ITALIC) != 0,
1554 #elif defined( WIN32 )
1555 psz_fontfile = Win32_Select( p_filter,
1556 p_style->psz_fontname,
1557 (p_style->i_style_flags & STYLE_BOLD) != 0,
1558 (p_style->i_style_flags & STYLE_ITALIC) != 0,
1562 psz_fontfile = NULL;
1567 if( *psz_fontfile == '\0' )
1570 "We were not able to find a matching font: \"%s\" (%s %s),"
1571 " so using default font",
1572 p_style->psz_fontname,
1573 (p_style->i_style_flags & STYLE_BOLD) ? "Bold" : "",
1574 (p_style->i_style_flags & STYLE_ITALIC) ? "Italic" : "" );
1579 if( FT_New_Face( p_sys->p_library, psz_fontfile, i_idx, &p_face ) )
1582 free( psz_fontfile );
1587 if( FT_Select_Charmap( p_face, ft_encoding_unicode ) )
1589 /* We've loaded a font face which is unhelpful for actually
1590 * rendering text - fallback to the default one.
1592 FT_Done_Face( p_face );
1598 static bool FaceStyleEquals( const text_style_t *p_style1,
1599 const text_style_t *p_style2 )
1601 if( !p_style1 || !p_style2 )
1603 if( p_style1 == p_style2 )
1606 const int i_style_mask = STYLE_BOLD | STYLE_ITALIC;
1607 return (p_style1->i_style_flags & i_style_mask) == (p_style2->i_style_flags & i_style_mask) &&
1608 !strcmp( p_style1->psz_fontname, p_style2->psz_fontname );
1611 static int GetGlyph( filter_t *p_filter,
1619 if( FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_DEFAULT ) &&
1620 FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT ) )
1622 msg_Err( p_filter, "unable to render text FT_Load_Glyph failed" );
1623 return VLC_EGENERIC;
1626 /* Do synthetic styling now that Freetype supports it;
1627 * ie. if the font we have loaded is NOT already in the
1628 * style that the tags want, then switch it on; if they
1629 * are then don't. */
1630 if ((i_style_flags & STYLE_BOLD) && !(p_face->style_flags & FT_STYLE_FLAG_BOLD))
1631 FT_GlyphSlot_Embolden( p_face->glyph );
1632 if ((i_style_flags & STYLE_ITALIC) && !(p_face->style_flags & FT_STYLE_FLAG_ITALIC))
1633 FT_GlyphSlot_Oblique( p_face->glyph );
1636 if( FT_Get_Glyph( p_face->glyph, &glyph ) )
1638 msg_Err( p_filter, "unable to render text FT_Get_Glyph failed" );
1639 return VLC_EGENERIC;
1643 FT_Glyph_Get_CBox( glyph, ft_glyph_bbox_pixels, &bbox );
1645 if( FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, 0, 1) )
1647 FT_Done_Glyph( glyph );
1648 return VLC_EGENERIC;
1656 static int ProcessLines( filter_t *p_filter,
1657 line_desc_t **pp_lines,
1661 text_style_t **pp_styles,
1662 uint32_t *pi_k_dates,
1665 filter_sys_t *p_sys = p_filter->p_sys;
1666 uint32_t *p_fribidi_string = NULL;
1667 text_style_t **pp_fribidi_styles = NULL;
1668 int *p_new_positions = NULL;
1670 #if defined(HAVE_FRIBIDI)
1672 int *p_old_positions;
1673 int start_pos, pos = 0;
1675 pp_fribidi_styles = calloc( i_len, sizeof(*pp_fribidi_styles) );
1677 p_fribidi_string = malloc( (i_len + 1) * sizeof(*p_fribidi_string) );
1678 p_old_positions = malloc( (i_len + 1) * sizeof(*p_old_positions) );
1679 p_new_positions = malloc( (i_len + 1) * sizeof(*p_new_positions) );
1681 if( ! pp_fribidi_styles ||
1682 ! p_fribidi_string ||
1683 ! p_old_positions ||
1686 free( p_old_positions );
1687 free( p_new_positions );
1688 free( p_fribidi_string );
1689 free( pp_fribidi_styles );
1693 /* Do bidi conversion line-by-line */
1696 while(pos < i_len) {
1697 if (psz_text[pos] != '\n')
1699 p_fribidi_string[pos] = psz_text[pos];
1700 pp_fribidi_styles[pos] = pp_styles[pos];
1701 p_new_positions[pos] = pos;
1705 while(pos < i_len) {
1706 if (psz_text[pos] == '\n')
1710 if (pos > start_pos)
1712 #if (FRIBIDI_MINOR_VERSION < 19) && (FRIBIDI_MAJOR_VERSION == 0)
1713 FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
1715 FriBidiParType base_dir = FRIBIDI_PAR_LTR;
1717 fribidi_log2vis((FriBidiChar*)psz_text + start_pos,
1718 pos - start_pos, &base_dir,
1719 (FriBidiChar*)p_fribidi_string + start_pos,
1720 p_new_positions + start_pos,
1723 for( int j = start_pos; j < pos; j++ )
1725 pp_fribidi_styles[ j ] = pp_styles[ start_pos + p_old_positions[j - start_pos] ];
1726 p_new_positions[ j ] += start_pos;
1730 p_fribidi_string[ i_len ] = 0;
1731 free( p_old_positions );
1733 pp_styles = pp_fribidi_styles;
1734 psz_text = p_fribidi_string;
1737 /* Work out the karaoke */
1738 uint8_t *pi_karaoke_bar = NULL;
1741 pi_karaoke_bar = malloc( i_len * sizeof(*pi_karaoke_bar));
1742 if( pi_karaoke_bar )
1744 int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
1745 for( int i = 0; i < i_len; i++ )
1747 unsigned i_bar = p_new_positions ? p_new_positions[i] : i;
1748 pi_karaoke_bar[i_bar] = pi_k_dates[i] >= i_elapsed;
1752 free( p_new_positions );
1755 line_desc_t **pp_line_next = pp_lines;
1763 FT_Vector pen = { .x = 0, .y = 0 };
1764 const text_style_t *p_previous_style = NULL;
1765 FT_Face p_face = NULL;
1766 for( int i_start = 0; i_start < i_len; )
1768 /* Compute the length of the current text line */
1770 while( i_start + i_length < i_len && psz_text[i_start + i_length] != '\n' )
1773 /* Render the text line (or the begining if too long) into 0 or 1 glyph line */
1774 line_desc_t *p_line = i_length > 0 ? NewLine( i_length ) : NULL;
1775 int i_index = i_start;
1777 int i_face_height = 0;
1778 FT_BBox line_bbox = {
1790 break_point_t break_point;
1791 break_point_t break_point_fallback;
1793 #define SAVE_BP(dst) do { \
1794 dst.i_index = i_index; \
1796 dst.line_bbox = line_bbox; \
1797 dst.i_face_height = i_face_height; \
1800 SAVE_BP( break_point );
1801 SAVE_BP( break_point_fallback );
1803 while( i_index < i_start + i_length )
1805 /* Split by common FT_Face + Size */
1806 const text_style_t *p_current_style = pp_styles[i_index];
1807 int i_part_length = 0;
1808 while( i_index + i_part_length < i_start + i_length )
1810 const text_style_t *p_style = pp_styles[i_index + i_part_length];
1811 if( !FaceStyleEquals( p_style, p_current_style ) ||
1812 p_style->i_font_size != p_current_style->i_font_size )
1817 /* (Re)load/reconfigure the face if needed */
1818 if( !FaceStyleEquals( p_current_style, p_previous_style ) )
1821 FT_Done_Face( p_face );
1822 p_previous_style = NULL;
1824 p_face = LoadFace( p_filter, p_current_style );
1826 FT_Face p_current_face = p_face ? p_face : p_sys->p_face;
1827 if( !p_previous_style || p_previous_style->i_font_size != p_current_style->i_font_size )
1829 if( FT_Set_Pixel_Sizes( p_current_face, 0, p_current_style->i_font_size ) )
1830 msg_Err( p_filter, "Failed to set font size to %d", p_current_style->i_font_size );
1832 p_previous_style = p_current_style;
1834 i_face_height = __MAX(i_face_height, FT_CEIL(p_current_face->size->metrics.height));
1836 /* Render the part */
1837 bool b_break_line = false;
1838 int i_glyph_last = 0;
1839 while( i_part_length > 0 )
1841 const text_style_t *p_glyph_style = pp_styles[i_index];
1842 uint32_t character = psz_text[i_index];
1843 int i_glyph_index = FT_Get_Char_Index( p_current_face, character );
1845 /* Get kerning vector */
1846 FT_Vector kerning = { .x = 0, .y = 0 };
1847 if( FT_HAS_KERNING( p_current_face ) && i_glyph_last != 0 && i_glyph_index != 0 )
1848 FT_Get_Kerning( p_current_face, i_glyph_last, i_glyph_index, ft_kerning_default, &kerning );
1850 /* Get the glyph bitmap and its bounding box and all the associated properties */
1853 if( GetGlyph( p_filter, &glyph, &glyph_bbox,
1854 p_current_face, i_glyph_index, p_glyph_style->i_style_flags ) )
1857 FT_Vector glyph_pos = {
1858 .x = pen.x + FT_CEIL(kerning.x),
1861 bool b_karaoke = pi_karaoke_bar && pi_karaoke_bar[i_index] != 0;
1862 uint32_t i_color = b_karaoke ? (p_glyph_style->i_karaoke_background_color |
1863 (p_glyph_style->i_karaoke_background_alpha << 24))
1864 : (p_glyph_style->i_font_color |
1865 (p_glyph_style->i_font_alpha << 24));
1866 int i_ul_offset = 0;
1867 int i_ul_thickness = 0;
1868 if( p_glyph_style->i_style_flags & (STYLE_UNDERLINE | STYLE_STRIKEOUT) )
1870 i_ul_offset = abs( FT_FLOOR(FT_MulFix(p_current_face->underline_position,
1871 p_current_face->size->metrics.y_scale)) );
1873 i_ul_thickness = abs( FT_CEIL(FT_MulFix(p_current_face->underline_thickness,
1874 p_current_face->size->metrics.y_scale)) );
1876 if( p_glyph_style->i_style_flags & STYLE_STRIKEOUT )
1878 /* Move the baseline to make it strikethrough instead of
1879 * underline. That means that strikethrough takes precedence
1881 i_ul_offset -= abs( FT_FLOOR(FT_MulFix(p_current_face->descender*2,
1882 p_current_face->size->metrics.y_scale)) );
1885 FT_BitmapGlyph glyph_bmp = (FT_BitmapGlyph)glyph;
1886 FT_BBox line_bbox_new = {
1888 .xMax = __MAX( line_bbox.xMax,
1889 glyph_pos.x + glyph_bbox.xMax - glyph_bbox.xMin + glyph_bmp->left ),
1891 .yMax = __MAX( line_bbox.yMax,
1892 glyph_pos.y + glyph_bbox.yMax - glyph_bbox.yMin + glyph_bmp->top ),
1895 b_break_line = i_index > i_start &&
1896 line_bbox_new.xMax >= p_filter->fmt_out.video.i_visible_width;
1899 FT_Done_Glyph( glyph );
1901 break_point_t *p_bp = NULL;
1902 if( break_point.i_index > i_start )
1903 p_bp = &break_point;
1904 else if( break_point_fallback.i_index > i_start )
1905 p_bp = &break_point_fallback;
1909 msg_Dbg( p_filter, "Breaking line");
1910 for( int i = p_bp->i_index; i < i_index; i++ )
1912 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[i - i_start] );
1913 p_line->pp_glyphs[i - i_start] = NULL;
1915 i_index = p_bp->i_index;
1917 line_bbox = p_bp->line_bbox;
1918 i_face_height = p_bp->i_face_height;
1922 msg_Err( p_filter, "Breaking unbreakable line");
1927 int i_line_index = i_index - i_start;
1928 p_line->pp_glyphs[i_line_index] = (FT_BitmapGlyph)glyph;
1929 p_line->p_glyph_pos[i_line_index] = glyph_pos;
1930 p_line->pi_color[i_line_index] = i_color;
1931 p_line->pi_line_offset[i_line_index] = i_ul_offset;
1932 p_line->pi_line_thickness[i_line_index] = i_ul_thickness;
1934 pen.x += FT_CEIL(kerning.x) + FT_CEIL(p_current_face->glyph->advance.x);
1935 line_bbox = line_bbox_new;
1937 i_glyph_last = i_glyph_index;
1941 if( character == ' ' || character == '\t' )
1942 SAVE_BP( break_point );
1943 else if( character == 160 )
1944 SAVE_BP( break_point_fallback );
1950 bbox.xMax = __MAX(bbox.xMax, line_bbox.xMax);
1951 bbox.yMax = __MAX(bbox.yMax, line_bbox.yMax);
1953 pen.y += i_face_height;
1955 /* Terminate and append the line */
1958 p_line->i_width = line_bbox.xMax - line_bbox.xMin;
1959 *pp_line_next = p_line;
1960 pp_line_next = &p_line->p_next;
1963 /* Skip what we have rendered and the line delimitor if present */
1965 if( i_start < i_len && psz_text[i_start] == '\n' )
1968 if( bbox.yMax >= p_filter->fmt_out.video.i_visible_height )
1970 msg_Err( p_filter, "Truncated too high subtitle" );
1975 FT_Done_Face( p_face );
1977 free( pp_fribidi_styles );
1978 free( p_fribidi_string );
1979 free( pi_karaoke_bar );
1981 p_size->x = bbox.xMax - bbox.xMin;
1982 p_size->y = bbox.yMax - bbox.yMin;
1987 * This function renders a text subpicture region into another one.
1988 * It also calculates the size needed for this string, and renders the
1989 * needed glyphs into memory. It is used as pf_add_string callback in
1990 * the vout method by this module
1992 static int RenderCommon( filter_t *p_filter, subpicture_region_t *p_region_out,
1993 subpicture_region_t *p_region_in, bool b_html )
1995 filter_sys_t *p_sys = p_filter->p_sys;
1998 return VLC_EGENERIC;
1999 if( b_html && !p_region_in->psz_html )
2000 return VLC_EGENERIC;
2001 if( !b_html && !p_region_in->psz_text )
2002 return VLC_EGENERIC;
2004 const size_t i_text_max = strlen( b_html ? p_region_in->psz_html
2005 : p_region_in->psz_text );
2007 uint32_t *psz_text = calloc( i_text_max, sizeof( *psz_text ) );
2008 text_style_t **pp_styles = calloc( i_text_max, sizeof( *pp_styles ) );
2009 if( !psz_text || !pp_styles )
2013 return VLC_EGENERIC;
2016 /* Reset the default fontsize in case screen metrics have changed */
2017 p_filter->p_sys->i_font_size = GetFontSize( p_filter );
2020 int rv = VLC_SUCCESS;
2021 int i_text_length = 0;
2022 FT_Vector result = {0, 0};
2023 line_desc_t *p_lines = NULL;
2025 uint32_t *pi_k_durations = NULL;
2030 stream_t *p_sub = stream_MemoryNew( VLC_OBJECT(p_filter),
2031 (uint8_t *) p_region_in->psz_html,
2032 strlen( p_region_in->psz_html ),
2034 if( unlikely(p_sub == NULL) )
2037 xml_reader_t *p_xml_reader = p_filter->p_sys->p_xml;
2039 p_xml_reader = xml_ReaderCreate( p_filter, p_sub );
2041 p_xml_reader = xml_ReaderReset( p_xml_reader, p_sub );
2042 p_filter->p_sys->p_xml = p_xml_reader;
2049 /* Look for Root Node */
2052 if( xml_ReaderNextNode( p_xml_reader, &node ) == XML_READER_STARTELEM )
2054 if( strcasecmp( "karaoke", node ) == 0 )
2056 pi_k_durations = calloc( i_text_max, sizeof( *pi_k_durations ) );
2058 else if( strcasecmp( "text", node ) != 0 )
2060 /* Only text and karaoke tags are supported */
2061 msg_Dbg( p_filter, "Unsupported top-level tag <%s> ignored.",
2068 msg_Err( p_filter, "Malformed HTML subtitle" );
2074 rv = ProcessNodes( p_filter,
2075 psz_text, pp_styles, pi_k_durations, &i_text_length,
2076 p_xml_reader, p_region_in->p_style );
2080 p_filter->p_sys->p_xml = xml_ReaderReset( p_xml_reader, NULL );
2082 stream_Delete( p_sub );
2087 text_style_t *p_style;
2088 if( p_region_in->p_style )
2089 p_style = CreateStyle( p_region_in->p_style->psz_fontname,
2090 p_region_in->p_style->i_font_size,
2091 (p_region_in->p_style->i_font_color & 0xffffff) |
2092 ((p_region_in->p_style->i_font_alpha & 0xff) << 24),
2094 p_region_in->p_style->i_style_flags & (STYLE_BOLD |
2099 p_style = CreateStyle( p_sys->psz_fontfamily,
2101 (p_sys->i_font_color & 0xffffff) |
2102 (((255-p_sys->i_font_opacity) & 0xff) << 24),
2105 i_text_length = SetupText( p_filter,
2109 p_region_in->psz_text, p_style, 0 );
2112 if( !rv && i_text_length > 0 )
2114 rv = ProcessLines( p_filter,
2116 psz_text, pp_styles, pi_k_durations, i_text_length );
2119 p_region_out->i_x = p_region_in->i_x;
2120 p_region_out->i_y = p_region_in->i_y;
2122 /* Don't attempt to render text that couldn't be layed out
2124 if( !rv && i_text_length > 0 && result.x > 0 && result.y > 0)
2126 if( var_InheritBool( p_filter, "freetype-yuvp" ) )
2127 RenderYUVP( p_filter, p_region_out, p_lines,
2128 result.x, result.y );
2130 RenderYUVA( p_filter, p_region_out, p_lines,
2131 result.x, result.y );
2134 /* With karaoke, we're going to have to render the text a number
2135 * of times to show the progress marker on the text.
2137 if( pi_k_durations )
2138 var_SetBool( p_filter, "text-rerender", true );
2141 FreeLines( p_lines );
2144 for( int i = 0; i < i_text_length; i++ )
2146 if( pp_styles[i] && ( i + 1 == i_text_length || pp_styles[i] != pp_styles[i + 1] ) )
2147 text_style_Delete( pp_styles[i] );
2150 free( pi_k_durations );
2155 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
2156 subpicture_region_t *p_region_in )
2158 return RenderCommon( p_filter, p_region_out, p_region_in, false );
2163 static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
2164 subpicture_region_t *p_region_in )
2166 return RenderCommon( p_filter, p_region_out, p_region_in, true );
2171 /*****************************************************************************
2172 * Create: allocates osd-text video thread output method
2173 *****************************************************************************
2174 * This function allocates and initializes a Clone vout method.
2175 *****************************************************************************/
2176 static int Create( vlc_object_t *p_this )
2178 filter_t *p_filter = (filter_t *)p_this;
2179 filter_sys_t *p_sys;
2180 char *psz_fontfile = NULL;
2181 char *psz_fontfamily = NULL;
2182 int i_error = 0, fontindex = 0;
2184 /* Allocate structure */
2185 p_filter->p_sys = p_sys = malloc( sizeof(*p_sys) );
2189 p_sys->psz_fontfamily = NULL;
2191 p_sys->p_xml = NULL;
2194 p_sys->p_library = 0;
2195 p_sys->i_font_size = 0;
2196 p_sys->i_display_height = 0;
2198 var_Create( p_filter, "freetype-rel-fontsize",
2199 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
2201 psz_fontfamily = var_InheritString( p_filter, "freetype-font" );
2202 p_sys->i_default_font_size = var_InheritInteger( p_filter, "freetype-fontsize" );
2203 p_sys->i_font_opacity = var_InheritInteger( p_filter,"freetype-opacity" );
2204 p_sys->i_font_opacity = __MAX( __MIN( p_sys->i_font_opacity, 255 ), 0 );
2205 p_sys->i_font_color = var_InheritInteger( p_filter, "freetype-color" );
2206 p_sys->i_font_color = __MAX( __MIN( p_sys->i_font_color , 0xFFFFFF ), 0 );
2209 /* Get Windows Font folder */
2210 wchar_t wdir[MAX_PATH];
2211 if( S_OK != SHGetFolderPathW( NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, wdir ) )
2213 GetWindowsDirectoryW( wdir, MAX_PATH );
2214 wcscat( wdir, L"\\fonts" );
2216 p_sys->psz_win_fonts_path = FromWide( wdir );
2219 /* Set default psz_fontfamily */
2220 if( !psz_fontfamily || !*psz_fontfamily )
2222 free( psz_fontfamily );
2224 psz_fontfamily = strdup( DEFAULT_FAMILY );
2227 if( asprintf( &psz_fontfamily, "%s"DEFAULT_FONT_FILE, p_sys->psz_win_fonts_path ) == -1 )
2230 psz_fontfamily = strdup( DEFAULT_FONT_FILE );
2232 msg_Err( p_filter,"User specified an empty fontfile, using %s", psz_fontfamily );
2236 /* Set the current font file */
2237 p_sys->psz_fontfamily = psz_fontfamily;
2239 #ifdef HAVE_FONTCONFIG
2240 FontConfig_BuildCache( p_filter );
2243 psz_fontfile = FontConfig_Select( NULL, psz_fontfamily, false, false,
2244 p_sys->i_default_font_size, &fontindex );
2245 #elif defined(WIN32)
2246 psz_fontfile = Win32_Select( p_filter, psz_fontfamily, false, false,
2247 p_sys->i_default_font_size, &fontindex );
2250 msg_Dbg( p_filter, "Using %s as font from file %s", psz_fontfamily, psz_fontfile );
2252 /* If nothing is found, use the default family */
2254 psz_fontfile = strdup( psz_fontfamily );
2256 #else /* !HAVE_STYLES */
2257 /* Use the default file */
2258 psz_fontfile = psz_fontfamily;
2262 i_error = FT_Init_FreeType( &p_sys->p_library );
2265 msg_Err( p_filter, "couldn't initialize freetype" );
2269 i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
2270 fontindex, &p_sys->p_face );
2272 if( i_error == FT_Err_Unknown_File_Format )
2274 msg_Err( p_filter, "file %s have unknown format",
2275 psz_fontfile ? psz_fontfile : "(null)" );
2280 msg_Err( p_filter, "failed to load font file %s",
2281 psz_fontfile ? psz_fontfile : "(null)" );
2285 free( psz_fontfile );
2288 i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
2291 msg_Err( p_filter, "font has no unicode translation table" );
2295 if( SetFontSize( p_filter, 0 ) != VLC_SUCCESS ) goto error;
2298 p_sys->pp_font_attachments = NULL;
2299 p_sys->i_font_attachments = 0;
2301 p_filter->pf_render_text = RenderText;
2303 p_filter->pf_render_html = RenderHtml;
2305 p_filter->pf_render_html = NULL;
2308 LoadFontsFromAttachments( p_filter );
2313 if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
2314 if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
2316 free( psz_fontfile );
2318 free( psz_fontfamily );
2320 return VLC_EGENERIC;
2323 /*****************************************************************************
2324 * Destroy: destroy Clone video thread output method
2325 *****************************************************************************
2326 * Clean up all data and library connections
2327 *****************************************************************************/
2328 static void Destroy( vlc_object_t *p_this )
2330 filter_t *p_filter = (filter_t *)p_this;
2331 filter_sys_t *p_sys = p_filter->p_sys;
2333 if( p_sys->pp_font_attachments )
2335 for( int k = 0; k < p_sys->i_font_attachments; k++ )
2336 vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
2338 free( p_sys->pp_font_attachments );
2342 if( p_sys->p_xml ) xml_ReaderDelete( p_sys->p_xml );
2344 free( p_sys->psz_fontfamily );
2346 /* FcFini asserts calling the subfunction FcCacheFini()
2347 * even if no other library functions have been made since FcInit(),
2348 * so don't call it. */
2350 FT_Done_Face( p_sys->p_face );
2351 FT_Done_FreeType( p_sys->p_library );