freetype: fix extra squares in Arabic subtitles (fixes #6152 #8117)
[vlc.git] / modules / text_renderer / text_renderer.c
1 /*****************************************************************************
2  * freetype.c : Put text on the video, using freetype2
3  *****************************************************************************
4  * Copyright (C) 2002 - 2012 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8  *          Gildas Bazin <gbazin@videolan.org>
9  *          Bernie Purcell <bitmap@videolan.org>
10  *          Jean-Baptiste Kempf <jb@videolan.org>
11  *          Felix Paul K├╝hne <fkuehne@videolan.org>
12  *
13  * This program is free software; you can redistribute it and/or modify it
14  * under the terms of the GNU Lesser General Public License as published by
15  * the Free Software Foundation; either version 2.1 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU Lesser General Public License for more details.
22  *
23  * You should have received a copy of the GNU Lesser General Public License
24  * along with this program; if not, write to the Free Software Foundation, Inc.,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <errno.h>
33 #include <vlc_common.h>
34 #include <vlc_variables.h>
35 #include <vlc_filter.h>                                      /* filter_sys_t */
36 #include <vlc_xml.h>                                         /* xml_reader */
37 #include <vlc_charset.h>                                     /* ToCharset */
38 #include <vlc_strings.h>                         /* resolve_xml_special_chars */
39
40 #include "text_renderer.h"
41
42 text_style_t *CreateStyle( char *psz_fontname, int i_font_size,
43                                   uint32_t i_font_color, uint32_t i_karaoke_bg_color,
44                                   int i_style_flags )
45 {
46     text_style_t *p_style = text_style_New();
47     if( !p_style )
48         return NULL;
49
50     p_style->psz_fontname = psz_fontname ? strdup( psz_fontname ) : NULL;
51     p_style->i_font_size  = i_font_size;
52     p_style->i_font_color = (i_font_color & 0x00ffffff) >>  0;
53     p_style->i_font_alpha = (i_font_color & 0xff000000) >> 24;
54     p_style->i_karaoke_background_color = (i_karaoke_bg_color & 0x00ffffff) >>  0;
55     p_style->i_karaoke_background_alpha = (i_karaoke_bg_color & 0xff000000) >> 24;
56     p_style->i_style_flags |= i_style_flags;
57     return p_style;
58 }
59
60 int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
61                      uint32_t i_color, uint32_t i_karaoke_bg_color )
62 {
63     if( !p_font )
64         return VLC_EGENERIC;
65
66     font_stack_t *p_new = malloc( sizeof(*p_new) );
67     if( !p_new )
68         return VLC_ENOMEM;
69
70     p_new->p_next = NULL;
71
72     if( psz_name )
73         p_new->psz_name = strdup( psz_name );
74     else
75         p_new->psz_name = NULL;
76
77     p_new->i_size              = i_size;
78     p_new->i_color             = i_color;
79     p_new->i_karaoke_bg_color  = i_karaoke_bg_color;
80
81     if( !*p_font )
82     {
83         *p_font = p_new;
84     }
85     else
86     {
87         font_stack_t *p_last;
88
89         for( p_last = *p_font;
90              p_last->p_next;
91              p_last = p_last->p_next )
92         ;
93
94         p_last->p_next = p_new;
95     }
96     return VLC_SUCCESS;
97 }
98
99 int PopFont( font_stack_t **p_font )
100 {
101     font_stack_t *p_last, *p_next_to_last;
102
103     if( !p_font || !*p_font )
104         return VLC_EGENERIC;
105
106     p_next_to_last = NULL;
107     for( p_last = *p_font;
108          p_last->p_next;
109          p_last = p_last->p_next )
110     {
111         p_next_to_last = p_last;
112     }
113
114     if( p_next_to_last )
115         p_next_to_last->p_next = NULL;
116     else
117         *p_font = NULL;
118
119     free( p_last->psz_name );
120     free( p_last );
121
122     return VLC_SUCCESS;
123 }
124
125 int PeekFont( font_stack_t **p_font, char **psz_name, int *i_size,
126                      uint32_t *i_color, uint32_t *i_karaoke_bg_color )
127 {
128     font_stack_t *p_last;
129
130     if( !p_font || !*p_font )
131         return VLC_EGENERIC;
132
133     for( p_last=*p_font;
134          p_last->p_next;
135          p_last=p_last->p_next )
136     ;
137
138     *psz_name            = p_last->psz_name;
139     *i_size              = p_last->i_size;
140     *i_color             = p_last->i_color;
141     *i_karaoke_bg_color  = p_last->i_karaoke_bg_color;
142
143     return VLC_SUCCESS;
144 }
145
146 static const struct {
147     const char *psz_name;
148     uint32_t   i_value;
149 } p_html_colors[] = {
150     /* Official html colors */
151     { "Aqua",    0x00FFFF },
152     { "Black",   0x000000 },
153     { "Blue",    0x0000FF },
154     { "Fuchsia", 0xFF00FF },
155     { "Gray",    0x808080 },
156     { "Green",   0x008000 },
157     { "Lime",    0x00FF00 },
158     { "Maroon",  0x800000 },
159     { "Navy",    0x000080 },
160     { "Olive",   0x808000 },
161     { "Purple",  0x800080 },
162     { "Red",     0xFF0000 },
163     { "Silver",  0xC0C0C0 },
164     { "Teal",    0x008080 },
165     { "White",   0xFFFFFF },
166     { "Yellow",  0xFFFF00 },
167
168     /* Common ones */
169     { "AliceBlue", 0xF0F8FF },
170     { "AntiqueWhite", 0xFAEBD7 },
171     { "Aqua", 0x00FFFF },
172     { "Aquamarine", 0x7FFFD4 },
173     { "Azure", 0xF0FFFF },
174     { "Beige", 0xF5F5DC },
175     { "Bisque", 0xFFE4C4 },
176     { "Black", 0x000000 },
177     { "BlanchedAlmond", 0xFFEBCD },
178     { "Blue", 0x0000FF },
179     { "BlueViolet", 0x8A2BE2 },
180     { "Brown", 0xA52A2A },
181     { "BurlyWood", 0xDEB887 },
182     { "CadetBlue", 0x5F9EA0 },
183     { "Chartreuse", 0x7FFF00 },
184     { "Chocolate", 0xD2691E },
185     { "Coral", 0xFF7F50 },
186     { "CornflowerBlue", 0x6495ED },
187     { "Cornsilk", 0xFFF8DC },
188     { "Crimson", 0xDC143C },
189     { "Cyan", 0x00FFFF },
190     { "DarkBlue", 0x00008B },
191     { "DarkCyan", 0x008B8B },
192     { "DarkGoldenRod", 0xB8860B },
193     { "DarkGray", 0xA9A9A9 },
194     { "DarkGrey", 0xA9A9A9 },
195     { "DarkGreen", 0x006400 },
196     { "DarkKhaki", 0xBDB76B },
197     { "DarkMagenta", 0x8B008B },
198     { "DarkOliveGreen", 0x556B2F },
199     { "Darkorange", 0xFF8C00 },
200     { "DarkOrchid", 0x9932CC },
201     { "DarkRed", 0x8B0000 },
202     { "DarkSalmon", 0xE9967A },
203     { "DarkSeaGreen", 0x8FBC8F },
204     { "DarkSlateBlue", 0x483D8B },
205     { "DarkSlateGray", 0x2F4F4F },
206     { "DarkSlateGrey", 0x2F4F4F },
207     { "DarkTurquoise", 0x00CED1 },
208     { "DarkViolet", 0x9400D3 },
209     { "DeepPink", 0xFF1493 },
210     { "DeepSkyBlue", 0x00BFFF },
211     { "DimGray", 0x696969 },
212     { "DimGrey", 0x696969 },
213     { "DodgerBlue", 0x1E90FF },
214     { "FireBrick", 0xB22222 },
215     { "FloralWhite", 0xFFFAF0 },
216     { "ForestGreen", 0x228B22 },
217     { "Fuchsia", 0xFF00FF },
218     { "Gainsboro", 0xDCDCDC },
219     { "GhostWhite", 0xF8F8FF },
220     { "Gold", 0xFFD700 },
221     { "GoldenRod", 0xDAA520 },
222     { "Gray", 0x808080 },
223     { "Grey", 0x808080 },
224     { "Green", 0x008000 },
225     { "GreenYellow", 0xADFF2F },
226     { "HoneyDew", 0xF0FFF0 },
227     { "HotPink", 0xFF69B4 },
228     { "IndianRed", 0xCD5C5C },
229     { "Indigo", 0x4B0082 },
230     { "Ivory", 0xFFFFF0 },
231     { "Khaki", 0xF0E68C },
232     { "Lavender", 0xE6E6FA },
233     { "LavenderBlush", 0xFFF0F5 },
234     { "LawnGreen", 0x7CFC00 },
235     { "LemonChiffon", 0xFFFACD },
236     { "LightBlue", 0xADD8E6 },
237     { "LightCoral", 0xF08080 },
238     { "LightCyan", 0xE0FFFF },
239     { "LightGoldenRodYellow", 0xFAFAD2 },
240     { "LightGray", 0xD3D3D3 },
241     { "LightGrey", 0xD3D3D3 },
242     { "LightGreen", 0x90EE90 },
243     { "LightPink", 0xFFB6C1 },
244     { "LightSalmon", 0xFFA07A },
245     { "LightSeaGreen", 0x20B2AA },
246     { "LightSkyBlue", 0x87CEFA },
247     { "LightSlateGray", 0x778899 },
248     { "LightSlateGrey", 0x778899 },
249     { "LightSteelBlue", 0xB0C4DE },
250     { "LightYellow", 0xFFFFE0 },
251     { "Lime", 0x00FF00 },
252     { "LimeGreen", 0x32CD32 },
253     { "Linen", 0xFAF0E6 },
254     { "Magenta", 0xFF00FF },
255     { "Maroon", 0x800000 },
256     { "MediumAquaMarine", 0x66CDAA },
257     { "MediumBlue", 0x0000CD },
258     { "MediumOrchid", 0xBA55D3 },
259     { "MediumPurple", 0x9370D8 },
260     { "MediumSeaGreen", 0x3CB371 },
261     { "MediumSlateBlue", 0x7B68EE },
262     { "MediumSpringGreen", 0x00FA9A },
263     { "MediumTurquoise", 0x48D1CC },
264     { "MediumVioletRed", 0xC71585 },
265     { "MidnightBlue", 0x191970 },
266     { "MintCream", 0xF5FFFA },
267     { "MistyRose", 0xFFE4E1 },
268     { "Moccasin", 0xFFE4B5 },
269     { "NavajoWhite", 0xFFDEAD },
270     { "Navy", 0x000080 },
271     { "OldLace", 0xFDF5E6 },
272     { "Olive", 0x808000 },
273     { "OliveDrab", 0x6B8E23 },
274     { "Orange", 0xFFA500 },
275     { "OrangeRed", 0xFF4500 },
276     { "Orchid", 0xDA70D6 },
277     { "PaleGoldenRod", 0xEEE8AA },
278     { "PaleGreen", 0x98FB98 },
279     { "PaleTurquoise", 0xAFEEEE },
280     { "PaleVioletRed", 0xD87093 },
281     { "PapayaWhip", 0xFFEFD5 },
282     { "PeachPuff", 0xFFDAB9 },
283     { "Peru", 0xCD853F },
284     { "Pink", 0xFFC0CB },
285     { "Plum", 0xDDA0DD },
286     { "PowderBlue", 0xB0E0E6 },
287     { "Purple", 0x800080 },
288     { "Red", 0xFF0000 },
289     { "RosyBrown", 0xBC8F8F },
290     { "RoyalBlue", 0x4169E1 },
291     { "SaddleBrown", 0x8B4513 },
292     { "Salmon", 0xFA8072 },
293     { "SandyBrown", 0xF4A460 },
294     { "SeaGreen", 0x2E8B57 },
295     { "SeaShell", 0xFFF5EE },
296     { "Sienna", 0xA0522D },
297     { "Silver", 0xC0C0C0 },
298     { "SkyBlue", 0x87CEEB },
299     { "SlateBlue", 0x6A5ACD },
300     { "SlateGray", 0x708090 },
301     { "SlateGrey", 0x708090 },
302     { "Snow", 0xFFFAFA },
303     { "SpringGreen", 0x00FF7F },
304     { "SteelBlue", 0x4682B4 },
305     { "Tan", 0xD2B48C },
306     { "Teal", 0x008080 },
307     { "Thistle", 0xD8BFD8 },
308     { "Tomato", 0xFF6347 },
309     { "Turquoise", 0x40E0D0 },
310     { "Violet", 0xEE82EE },
311     { "Wheat", 0xF5DEB3 },
312     { "White", 0xFFFFFF },
313     { "WhiteSmoke", 0xF5F5F5 },
314     { "Yellow", 0xFFFF00 },
315     { "YellowGreen", 0x9ACD32 },
316
317     { NULL, 0 }
318 };
319
320 int HandleFontAttributes( xml_reader_t *p_xml_reader,
321                                  font_stack_t **p_fonts )
322 {
323     int        rv;
324     char      *psz_fontname = NULL;
325     uint32_t   i_font_color = 0xffffff;
326     int        i_font_alpha = 255;
327     uint32_t   i_karaoke_bg_color = 0x00ffffff;
328     int        i_font_size  = STYLE_DEFAULT_FONT_SIZE;
329
330     /* Default all attributes to the top font in the stack -- in case not
331      * all attributes are specified in the sub-font
332      */
333     if( VLC_SUCCESS == PeekFont( p_fonts,
334                                  &psz_fontname,
335                                  &i_font_size,
336                                  &i_font_color,
337                                  &i_karaoke_bg_color ))
338     {
339         psz_fontname = strdup( psz_fontname );
340     }
341     i_font_alpha = (i_font_color >> 24) & 0xff;
342     i_font_color &= 0x00ffffff;
343
344     const char *name, *value;
345     while( (name = xml_ReaderNextAttr( p_xml_reader, &value )) != NULL )
346     {
347         if( !strcasecmp( "face", name ) )
348         {
349             free( psz_fontname );
350             psz_fontname = strdup( value );
351         }
352         else if( !strcasecmp( "size", name ) )
353         {
354             if( ( *value == '+' ) || ( *value == '-' ) )
355             {
356                 int i_value = atoi( value );
357
358                 if( ( i_value >= -5 ) && ( i_value <= 5 ) )
359                     i_font_size += ( i_value * i_font_size ) / 10;
360                 else if( i_value < -5 )
361                     i_font_size = - i_value;
362                 else if( i_value > 5 )
363                     i_font_size = i_value;
364             }
365             else
366                 i_font_size = atoi( value );
367         }
368         else if( !strcasecmp( "color", name ) )
369         {
370             if( value[0] == '#' )
371             {
372                 i_font_color = strtol( value + 1, NULL, 16 );
373                 i_font_color &= 0x00ffffff;
374             }
375             else
376             {
377                 char *end;
378                 uint32_t i_value = strtol( value, &end, 16 );
379                 if( *end == '\0' || *end == ' ' )
380                     i_font_color = i_value & 0x00ffffff;
381
382                 for( int i = 0; p_html_colors[i].psz_name != NULL; i++ )
383                 {
384                     if( !strncasecmp( value, p_html_colors[i].psz_name, strlen(p_html_colors[i].psz_name) ) )
385                     {
386                         i_font_color = p_html_colors[i].i_value;
387                         break;
388                     }
389                 }
390             }
391         }
392         else if( !strcasecmp( "alpha", name ) && ( value[0] == '#' ) )
393         {
394             i_font_alpha = strtol( value + 1, NULL, 16 );
395             i_font_alpha &= 0xff;
396         }
397     }
398     rv = PushFont( p_fonts,
399                    psz_fontname,
400                    i_font_size,
401                    (i_font_color & 0xffffff) | ((uint32_t)(i_font_alpha & 0xff) << 24),
402                    i_karaoke_bg_color );
403
404     free( psz_fontname );
405
406     return rv;
407 }
408
409 int HandleTT(font_stack_t **p_fonts, const char *p_fontfamily )
410 {
411     char      *psz_unused_fontname = NULL;
412     uint32_t   i_font_color = 0xffffff;
413     uint32_t   i_karaoke_bg_color = 0x00ffffff;
414     int        i_font_size  = STYLE_DEFAULT_FONT_SIZE;
415
416     /* Default all attributes to the top font in the stack -- in case not
417      * all attributes are specified in the sub-font
418      */
419     PeekFont( p_fonts,
420              &psz_unused_fontname,
421              &i_font_size,
422              &i_font_color,
423              &i_karaoke_bg_color );
424
425     /* Keep all the parent's font attributes, but change to a monospace font */
426     return PushFont( p_fonts,
427                    p_fontfamily,
428                    i_font_size,
429                    i_font_color,
430                    i_karaoke_bg_color );
431 }
432
433 /* Turn any multiple-whitespaces into single spaces */
434 void HandleWhiteSpace( char *psz_node )
435 {
436     char *s = strpbrk( psz_node, "\t\r\n " );
437     while( s )
438     {
439         int i_whitespace = strspn( s, "\t\r\n " );
440
441         if( i_whitespace > 1 )
442             memmove( &s[1],
443                      &s[i_whitespace],
444                      strlen( s ) - i_whitespace + 1 );
445         *s++ = ' ';
446
447         s = strpbrk( s, "\t\r\n " );
448     }
449 }
450
451 unsigned SetupText( filter_t *p_filter,
452                            uni_char_t *psz_text_out,
453                            text_style_t **pp_styles,
454                            uint32_t *pi_k_dates,
455
456                            const char *psz_text_in,
457                            text_style_t *p_style,
458                            uint32_t i_k_date )
459 {
460     size_t i_string_length;
461
462     size_t i_string_bytes;
463     uni_char_t *psz_tmp = ToCharset( FREETYPE_TO_UCS, psz_text_in, &i_string_bytes );
464     if( psz_tmp )
465     {
466         memcpy( psz_text_out, psz_tmp, i_string_bytes );
467         i_string_length = i_string_bytes / sizeof( *psz_tmp );
468         free( psz_tmp );
469     }
470     else
471     {
472         msg_Warn( p_filter, "failed to convert string to unicode (%s)",
473                   vlc_strerror_c(errno) );
474         i_string_length = 0;
475     }
476
477     if( i_string_length > 0 )
478     {
479         for( unsigned i = 0; i < i_string_length; i++ )
480             pp_styles[i] = p_style;
481     }
482     else
483     {
484         text_style_Delete( p_style );
485     }
486     if( i_string_length > 0 && pi_k_dates )
487     {
488         for( unsigned i = 0; i < i_string_length; i++ )
489             pi_k_dates[i] = i_k_date;
490     }
491     return i_string_length;
492 }
493
494 bool FaceStyleEquals( const text_style_t *p_style1,
495                              const text_style_t *p_style2 )
496 {
497     if( !p_style1 || !p_style2 )
498         return false;
499     if( p_style1 == p_style2 )
500         return true;
501
502     const int i_style_mask = STYLE_BOLD | STYLE_ITALIC;
503     return (p_style1->i_style_flags & i_style_mask) == (p_style2->i_style_flags & i_style_mask) &&
504            !strcmp( p_style1->psz_fontname, p_style2->psz_fontname );
505 }
506
507 text_style_t *GetStyleFromFontStack( filter_t *p_filter,
508                                      font_stack_t **p_fonts,
509                                      text_style_t *style,
510                                      int i_style_flags )
511 {
512     char       *psz_fontname = NULL;
513     uint32_t    i_font_color = var_InheritInteger( p_filter, "freetype-color" );
514     i_font_color = VLC_CLIP( i_font_color, 0, 0xFFFFFF );
515     i_font_color = i_font_color & 0x00ffffff;
516
517     int         i_font_size  = style->i_font_size;
518     uint32_t    i_karaoke_bg_color = i_font_color;
519
520     if( PeekFont( p_fonts, &psz_fontname, &i_font_size,
521                   &i_font_color, &i_karaoke_bg_color ) )
522         return NULL;
523
524     return CreateStyle( psz_fontname, i_font_size, i_font_color,
525                         i_karaoke_bg_color,
526                         i_style_flags );
527 }
528
529 int ProcessNodes( filter_t *p_filter,
530                          uni_char_t *psz_text,
531                          text_style_t **pp_styles,
532                          uint32_t *pi_k_dates,
533                          int *pi_len,
534                          xml_reader_t *p_xml_reader,
535                          text_style_t *p_font_style,
536                          text_style_t *p_default_style )
537 {
538     int           rv      = VLC_SUCCESS;
539     int i_text_length     = 0;
540     font_stack_t *p_fonts = NULL;
541     uint32_t i_k_date     = 0;
542
543     int i_style_flags = 0;
544
545     if( p_font_style )
546     {
547         /* If the font is not specified in the style, assume the system font */
548         if(!p_font_style->psz_fontname)
549              p_font_style->psz_fontname = strdup(p_default_style->psz_fontname);
550
551         rv = PushFont( &p_fonts,
552                p_font_style->psz_fontname,
553                p_font_style->i_font_size > 0 ? p_font_style->i_font_size
554                                              : p_default_style->i_font_size,
555                (p_font_style->i_font_color & 0xffffff) |
556                    ((p_font_style->i_font_alpha & 0xff) << 24),
557                (p_font_style->i_karaoke_background_color & 0xffffff) |
558                    ((p_font_style->i_karaoke_background_alpha & 0xff) << 24));
559
560         i_style_flags = p_font_style->i_style_flags & (STYLE_BOLD |
561                                                        STYLE_ITALIC |
562                                                        STYLE_UNDERLINE |
563                                                        STYLE_STRIKEOUT);
564     }
565     else
566     {
567         uint32_t i_font_size = p_default_style->i_font_size;
568         uint32_t i_font_color = var_InheritInteger( p_filter, "freetype-color" );
569         i_font_color = VLC_CLIP( i_font_color, 0, 0xFFFFFF );
570         int i_font_alpha = p_default_style->i_font_alpha;
571         rv = PushFont( &p_fonts,
572                        p_default_style->psz_fontname,
573                        i_font_size,
574                        (i_font_color & 0xffffff) |
575                        ((uint32_t)(i_font_alpha & 0xff) << 24),
576                        0x00ffffff );
577     }
578     if( p_default_style->i_style_flags & STYLE_BOLD )
579         i_style_flags |= STYLE_BOLD;
580
581     if( rv != VLC_SUCCESS )
582         return rv;
583
584     const char *node;
585     int type;
586
587     while ( (type = xml_ReaderNextNode( p_xml_reader, &node )) > 0 )
588     {
589         switch ( type )
590         {
591             case XML_READER_ENDELEM:
592                 if( !strcasecmp( "font", node ) )
593                     PopFont( &p_fonts );
594                 else if( !strcasecmp( "tt", node ) )
595                     PopFont( &p_fonts );
596                 else if( !strcasecmp( "b", node ) )
597                     i_style_flags &= ~STYLE_BOLD;
598                else if( !strcasecmp( "i", node ) )
599                     i_style_flags &= ~STYLE_ITALIC;
600                 else if( !strcasecmp( "u", node ) )
601                     i_style_flags &= ~STYLE_UNDERLINE;
602                 else if( !strcasecmp( "s", node ) )
603                     i_style_flags &= ~STYLE_STRIKEOUT;
604                 break;
605
606             case XML_READER_STARTELEM:
607                 if( !strcasecmp( "font", node ) )
608                     HandleFontAttributes( p_xml_reader, &p_fonts );
609                 else if( !strcasecmp( "tt", node ) )
610                     HandleTT( &p_fonts, p_default_style->psz_monofontname );
611                 else if( !strcasecmp( "b", node ) )
612                     i_style_flags |= STYLE_BOLD;
613                 else if( !strcasecmp( "i", node ) )
614                     i_style_flags |= STYLE_ITALIC;
615                 else if( !strcasecmp( "u", node ) )
616                     i_style_flags |= STYLE_UNDERLINE;
617                 else if( !strcasecmp( "s", node ) )
618                     i_style_flags |= STYLE_STRIKEOUT;
619                 else if( !strcasecmp( "br", node ) )
620                 {
621                     i_text_length += SetupText( p_filter,
622                                                 &psz_text[i_text_length],
623                                                 &pp_styles[i_text_length],
624                                                 pi_k_dates ? &pi_k_dates[i_text_length] : NULL,
625                                                 "\n",
626                                                 GetStyleFromFontStack( p_filter,
627                                                                        &p_fonts,
628                                                                        p_default_style,
629                                                                        i_style_flags ),
630                                                 i_k_date );
631                 }
632                 else if( !strcasecmp( "k", node ) )
633                 {
634                     /* Karaoke tags */
635                     const char *name, *value;
636                     while( (name = xml_ReaderNextAttr( p_xml_reader, &value )) != NULL )
637                     {
638                         if( !strcasecmp( "t", name ) && value )
639                             i_k_date += atoi( value );
640                     }
641                 }
642                 break;
643
644             case XML_READER_TEXT:
645             {
646                 char *psz_node = strdup( node );
647                 if( unlikely(!psz_node) )
648                     break;
649
650                 HandleWhiteSpace( psz_node );
651                 resolve_xml_special_chars( psz_node );
652
653                 i_text_length += SetupText( p_filter,
654                                             &psz_text[i_text_length],
655                                             &pp_styles[i_text_length],
656                                             pi_k_dates ? &pi_k_dates[i_text_length] : NULL,
657                                             psz_node,
658                                             GetStyleFromFontStack( p_filter,
659                                                                    &p_fonts,
660                                                                    p_default_style,
661                                                                    i_style_flags ),
662                                             i_k_date );
663                 free( psz_node );
664                 break;
665             }
666         }
667     }
668
669     *pi_len = i_text_length;
670
671     while( VLC_SUCCESS == PopFont( &p_fonts ) );
672
673     return VLC_SUCCESS;
674 }
675
676