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