]> git.sesse.net Git - vlc/blob - modules/codec/subsdec.c
MKV USF subtitles support and other Subtitles improvements.
[vlc] / modules / codec / subsdec.c
1 /*****************************************************************************
2  * subsdec.c : text subtitles decoder
3  *****************************************************************************
4  * Copyright (C) 2000-2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Gildas Bazin <gbazin@videolan.org>
8  *          Samuel Hocevar <sam@zoy.org>
9  *          Derk-Jan Hartman <hartman at videolan dot org>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <vlc/vlc.h>
30 #include <vlc_vout.h>
31 #include <vlc_codec.h>
32
33 #include <vlc_osd.h>
34 #include <vlc_filter.h>
35 #include <vlc_charset.h>
36 #include <vlc_stream.h>
37 #include <vlc_xml.h>
38 #include <errno.h>
39 #include <string.h>
40
41 typedef struct
42 {
43     char *          psz_stylename; /* The name of the style, no comma's allowed */
44     text_style_t    font_style;
45     int             i_align;
46     int             i_margin_h;
47     int             i_margin_v;
48 }  ssa_style_t;
49
50 /*****************************************************************************
51  * decoder_sys_t : decoder descriptor
52  *****************************************************************************/
53 struct decoder_sys_t
54 {
55     vlc_bool_t          b_ass;                           /* The subs are ASS */
56     int                 i_original_height;
57     int                 i_original_width;
58     int                 i_align;          /* Subtitles alignment on the vout */
59     vlc_iconv_t         iconv_handle;            /* handle to iconv instance */
60     vlc_bool_t          b_autodetect_utf8;
61
62     ssa_style_t         **pp_ssa_styles;
63     int                 i_ssa_styles;
64 };
65
66 /*****************************************************************************
67  * Local prototypes
68  *****************************************************************************/
69 static int  OpenDecoder   ( vlc_object_t * );
70 static void CloseDecoder  ( vlc_object_t * );
71
72 static subpicture_t *DecodeBlock   ( decoder_t *, block_t ** );
73 static subpicture_t *ParseText     ( decoder_t *, block_t * );
74 static void         ParseSSAHeader ( decoder_t * );
75 static void         ParseUSFHeader ( decoder_t * );
76 static void         ParseUSFHeaderTags( decoder_sys_t *, xml_reader_t * );
77 static void         ParseSSAString ( decoder_t *, char *, subpicture_t * );
78 static void         ParseUSFString ( decoder_t *, char *, subpicture_t * );
79 static void         ParseColor     ( decoder_t *, char *, int *, int * );
80 static void         StripTags      ( char * );
81
82 #define DEFAULT_NAME "Default"
83 #define MAX_LINE 8192
84
85 /*****************************************************************************
86  * Module descriptor.
87  *****************************************************************************/
88 static const char *ppsz_encodings[] = { DEFAULT_NAME, "ASCII", "UTF-8", "",
89     "ISO-8859-1", "CP1252", "MacRoman", "MacIceland","ISO-8859-15", "",
90     "ISO-8859-2", "CP1250", "MacCentralEurope", "MacCroatian", "MacRomania", "",
91     "ISO-8859-5", "CP1251", "MacCyrillic", "MacUkraine", "KOI8-R", "KOI8-U", "KOI8-RU", "",
92     "ISO-8859-6", "CP1256", "MacArabic", "",
93     "ISO-8859-7", "CP1253", "MacGreek", "",
94     "ISO-8859-8", "CP1255", "MacHebrew", "",
95     "ISO-8859-9", "CP1254", "MacTurkish", "",
96     "ISO-8859-13", "CP1257", "",
97     "ISO-2022-JP", "ISO-2022-JP-1", "ISO-2022-JP-2", "EUC-JP", "SHIFT_JIS", "",
98     "ISO-2022-CN", "ISO-2022-CN-EXT", "EUC-CN", "EUC-TW", "BIG5", "BIG5-HKSCS", "",
99     "ISO-2022-KR", "EUC-KR", "",
100     "MacThai", "KOI8-T", "",
101     "ISO-8859-3", "ISO-8859-4", "ISO-8859-10", "ISO-8859-14", "ISO-8859-16", "",
102     "CP850", "CP862", "CP866", "CP874", "CP932", "CP949", "CP950", "CP1133", "CP1258", "",
103     "Macintosh", "",
104     "UTF-7", "UTF-16", "UTF-16BE", "UTF-16LE", "UTF-32", "UTF-32BE", "UTF-32LE",
105     "C99", "JAVA", "UCS-2", "UCS-2BE", "UCS-2LE", "UCS-4", "UCS-4BE", "UCS-4LE", "",
106     "HZ", "GBK", "GB18030", "JOHAB", "ARMSCII-8",
107     "Georgian-Academy", "Georgian-PS", "TIS-620", "MuleLao-1", "VISCII", "TCVN",
108     "HPROMAN8", "NEXTSTEP" };
109 /*
110 SSA supports charset selection.
111 The following known charsets are used:
112
113 0 = Ansi - Western European
114 1 = default
115 2 = symbol
116 3 = invalid
117 77 = Mac
118 128 = Japanese (Shift JIS)
119 129 = Hangul
120 130 = Johab
121 134 = GB2312 Simplified Chinese
122 136 = Big5 Traditional Chinese
123 161 = Greek
124 162 = Turkish
125 163 = Vietnamese
126 177 = Hebrew
127 178 = Arabic
128 186 = Baltic
129 204 = Russian (Cyrillic)
130 222 = Thai
131 238 = Eastern European
132 254 = PC 437
133 */
134
135 static int  pi_justification[] = { 0, 1, 2 };
136 static const char *ppsz_justification_text[] = {N_("Center"),N_("Left"),N_("Right")};
137
138 #define ENCODING_TEXT N_("Subtitles text encoding")
139 #define ENCODING_LONGTEXT N_("Set the encoding used in text subtitles")
140 #define ALIGN_TEXT N_("Subtitles justification")
141 #define ALIGN_LONGTEXT N_("Set the justification of subtitles")
142 #define AUTODETECT_UTF8_TEXT N_("UTF-8 subtitles autodetection")
143 #define AUTODETECT_UTF8_LONGTEXT N_("This enables automatic detection of " \
144             "UTF-8 encoding within subtitles files.")
145 #define FORMAT_TEXT N_("Formatted Subtitles")
146 #define FORMAT_LONGTEXT N_("Some subtitle formats allow for text formatting. " \
147  "VLC partly implements this, but you can choose to disable all formatting.")
148
149
150 vlc_module_begin();
151     set_shortname( _("Subtitles"));
152     set_description( _("Text subtitles decoder") );
153     set_capability( "decoder", 50 );
154     set_callbacks( OpenDecoder, CloseDecoder );
155     set_category( CAT_INPUT );
156     set_subcategory( SUBCAT_INPUT_SCODEC );
157
158     add_integer( "subsdec-align", 0, NULL, ALIGN_TEXT, ALIGN_LONGTEXT,
159                  VLC_FALSE );
160         change_integer_list( pi_justification, ppsz_justification_text, 0 );
161     add_string( "subsdec-encoding", DEFAULT_NAME, NULL,
162                 ENCODING_TEXT, ENCODING_LONGTEXT, VLC_FALSE );
163         change_string_list( ppsz_encodings, 0, 0 );
164     add_bool( "subsdec-autodetect-utf8", VLC_TRUE, NULL,
165               AUTODETECT_UTF8_TEXT, AUTODETECT_UTF8_LONGTEXT, VLC_FALSE );
166     add_bool( "subsdec-formatted", VLC_TRUE, NULL, FORMAT_TEXT, FORMAT_LONGTEXT,
167                  VLC_FALSE );
168 vlc_module_end();
169
170 /*****************************************************************************
171  * OpenDecoder: probe the decoder and return score
172  *****************************************************************************
173  * Tries to launch a decoder and return score so that the interface is able
174  * to chose.
175  *****************************************************************************/
176 static int OpenDecoder( vlc_object_t *p_this )
177 {
178     decoder_t     *p_dec = (decoder_t*)p_this;
179     decoder_sys_t *p_sys;
180     vlc_value_t    val;
181
182     if( p_dec->fmt_in.i_codec != VLC_FOURCC('s','u','b','t') &&
183         p_dec->fmt_in.i_codec != VLC_FOURCC('u','s','f',' ') &&
184         p_dec->fmt_in.i_codec != VLC_FOURCC('s','s','a',' ') )
185     {
186         return VLC_EGENERIC;
187     }
188
189     p_dec->pf_decode_sub = DecodeBlock;
190
191     /* Allocate the memory needed to store the decoder's structure */
192     if( ( p_dec->p_sys = p_sys =
193           (decoder_sys_t *)calloc(1, sizeof(decoder_sys_t)) ) == NULL )
194     {
195         msg_Err( p_dec, "out of memory" );
196         return VLC_ENOMEM;
197     }
198
199     /* init of p_sys */
200     p_sys->i_align = 0;
201     p_sys->iconv_handle = (vlc_iconv_t)-1;
202     p_sys->b_autodetect_utf8 = VLC_FALSE;
203     p_sys->b_ass = VLC_FALSE;
204     p_sys->i_original_height = -1;
205     p_sys->i_original_width = -1;
206     p_sys->pp_ssa_styles = NULL;
207     p_sys->i_ssa_styles = 0;
208
209     char *psz_charset = NULL;
210     /* First try demux-specified encoding */
211     if( p_dec->fmt_in.subs.psz_encoding && *p_dec->fmt_in.subs.psz_encoding )
212     {
213         psz_charset = strdup (p_dec->fmt_in.subs.psz_encoding);
214         msg_Dbg (p_dec, "trying demuxer-specified character encoding: %s",
215                  p_dec->fmt_in.subs.psz_encoding ?: "not specified");
216     }
217
218     /* Second, try configured encoding */
219     if (psz_charset == NULL)
220     {
221         psz_charset = var_CreateGetNonEmptyString (p_dec, "subsdec-encoding");
222         if ((psz_charset != NULL) && !strcasecmp (psz_charset, DEFAULT_NAME))
223         {
224             free (psz_charset);
225             psz_charset = NULL;
226         }
227
228         msg_Dbg (p_dec, "trying configured character encoding: %s",
229                  psz_charset ?: "not specified");
230     }
231
232     /* Third, try "local" encoding with optional UTF-8 autodetection */
233     if (psz_charset == NULL)
234     {
235         psz_charset = strdup (GetFallbackEncoding ());
236         msg_Dbg (p_dec, "trying default character encoding: %s",
237                  psz_charset ?: "not specified");
238
239         if (var_CreateGetBool (p_dec, "subsdec-autodetect-utf8"))
240         {
241             msg_Dbg (p_dec, "using automatic UTF-8 detection");
242             p_sys->b_autodetect_utf8 = VLC_TRUE;
243         }
244     }
245
246     if (psz_charset == NULL)
247     {
248         psz_charset = strdup ("UTF-8");
249         msg_Dbg (p_dec, "trying hard-coded character encoding: %s",
250                  psz_charset ?: "error");
251     }
252
253     if (psz_charset == NULL)
254     {
255         free (p_sys);
256         return VLC_ENOMEM;
257     }
258
259     if (strcasecmp (psz_charset, "UTF-8") && strcasecmp (psz_charset, "utf8"))
260     {
261         p_sys->iconv_handle = vlc_iconv_open ("UTF-8", psz_charset);
262         if (p_sys->iconv_handle == (vlc_iconv_t)(-1))
263             msg_Err (p_dec, "cannot convert from %s: %s", psz_charset,
264                      strerror (errno));
265     }
266     free (psz_charset);
267
268     var_Create( p_dec, "subsdec-align", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
269     var_Get( p_dec, "subsdec-align", &val );
270     p_sys->i_align = val.i_int;
271
272     if( p_dec->fmt_in.i_codec == VLC_FOURCC('s','s','a',' ') && var_CreateGetBool( p_dec, "subsdec-formatted" ) )
273     {
274         if( p_dec->fmt_in.i_extra > 0 )
275             ParseSSAHeader( p_dec );
276     }
277     else if( p_dec->fmt_in.i_codec == VLC_FOURCC('u','s','f',' ') && var_CreateGetBool( p_dec, "subsdec-formatted" ) )
278     {
279         if( p_dec->fmt_in.i_extra > 0 )
280             ParseUSFHeader( p_dec );
281     }
282
283     return VLC_SUCCESS;
284 }
285
286 /****************************************************************************
287  * DecodeBlock: the whole thing
288  ****************************************************************************
289  * This function must be fed with complete subtitles units.
290  ****************************************************************************/
291 static subpicture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
292 {
293     subpicture_t *p_spu = NULL;
294
295     if( !pp_block || *pp_block == NULL ) return NULL;
296
297     p_spu = ParseText( p_dec, *pp_block );
298
299     block_Release( *pp_block );
300     *pp_block = NULL;
301
302     return p_spu;
303 }
304
305 /*****************************************************************************
306  * CloseDecoder: clean up the decoder
307  *****************************************************************************/
308 static void CloseDecoder( vlc_object_t *p_this )
309 {
310     decoder_t *p_dec = (decoder_t *)p_this;
311     decoder_sys_t *p_sys = p_dec->p_sys;
312
313     if( p_sys->iconv_handle != (vlc_iconv_t)-1 )
314     {
315         vlc_iconv_close( p_sys->iconv_handle );
316     }
317
318     if( p_sys->pp_ssa_styles )
319     {
320         int i;
321         for( i = 0; i < p_sys->i_ssa_styles; i++ )
322         {
323             if( p_sys->pp_ssa_styles[i]->psz_stylename ) free( p_sys->pp_ssa_styles[i]->psz_stylename );
324             p_sys->pp_ssa_styles[i]->psz_stylename = NULL;
325             if( p_sys->pp_ssa_styles[i]->font_style.psz_fontname ) free( p_sys->pp_ssa_styles[i]->font_style.psz_fontname );
326             p_sys->pp_ssa_styles[i]->font_style.psz_fontname = NULL;
327             if( p_sys->pp_ssa_styles[i] ) free( p_sys->pp_ssa_styles[i] ); p_sys->pp_ssa_styles[i] = NULL;
328         }
329         free( p_sys->pp_ssa_styles ); p_sys->pp_ssa_styles = NULL;
330     }
331
332     free( p_sys );
333 }
334
335 /*****************************************************************************
336  * ParseText: parse an text subtitle packet and send it to the video output
337  *****************************************************************************/
338 static subpicture_t *ParseText( decoder_t *p_dec, block_t *p_block )
339 {
340     decoder_sys_t *p_sys = p_dec->p_sys;
341     subpicture_t *p_spu = NULL;
342     char *psz_subtitle = NULL;
343     video_format_t fmt;
344
345     /* We cannot display a subpicture with no date */
346     if( p_block->i_pts == 0 )
347     {
348         msg_Warn( p_dec, "subtitle without a date" );
349         return NULL;
350     }
351
352     /* Check validity of packet data */
353     /* An "empty" line containing only \0 can be used to force
354        and ephemer picture from the screen */
355     if( p_block->i_buffer < 1 )
356     {
357         msg_Warn( p_dec, "no subtitle data" );
358         return NULL;
359     }
360
361     /* Should be resiliant against bad subtitles */
362     psz_subtitle = strndup( (const char *)p_block->p_buffer,
363                             p_block->i_buffer );
364     if( psz_subtitle == NULL )
365         return NULL;
366
367     if( p_sys->iconv_handle == (vlc_iconv_t)-1 )
368     {
369         if (EnsureUTF8( psz_subtitle ) == NULL)
370         {
371             msg_Err( p_dec, _("failed to convert subtitle encoding.\n"
372                      "Try manually setting a character-encoding "
373                      "before you open the file.") );
374         }
375     }
376     else
377     {
378
379         if( p_sys->b_autodetect_utf8 )
380         {
381             if( IsUTF8( psz_subtitle ) == NULL )
382             {
383                 msg_Dbg( p_dec, "invalid UTF-8 sequence: "
384                          "disabling UTF-8 subtitles autodetection" );
385                 p_sys->b_autodetect_utf8 = VLC_FALSE;
386             }
387         }
388
389         if( !p_sys->b_autodetect_utf8 )
390         {
391             size_t inbytes_left = strlen( psz_subtitle );
392             size_t outbytes_left = 6 * inbytes_left;
393             char *psz_new_subtitle = malloc( outbytes_left + 1 );
394             char *psz_convert_buffer_out = psz_new_subtitle;
395             const char *psz_convert_buffer_in = psz_subtitle;
396
397             size_t ret = vlc_iconv( p_sys->iconv_handle,
398                                     &psz_convert_buffer_in, &inbytes_left,
399                                     &psz_convert_buffer_out, &outbytes_left );
400
401             *psz_convert_buffer_out++ = '\0';
402             free( psz_subtitle );
403
404             if( ( ret == (size_t)(-1) ) || inbytes_left )
405             {
406                 free( psz_new_subtitle );
407                 msg_Err( p_dec, _("failed to convert subtitle encoding.\n"
408                         "Try manually setting a character-encoding "
409                                 "before you open the file.") );
410                 return NULL;
411             }
412
413             psz_subtitle = realloc( psz_new_subtitle,
414                                     psz_convert_buffer_out - psz_new_subtitle );
415         }
416     }
417
418     /* Create the subpicture unit */
419     p_spu = p_dec->pf_spu_buffer_new( p_dec );
420     if( !p_spu )
421     {
422         msg_Warn( p_dec, "can't get spu buffer" );
423         if( psz_subtitle ) free( psz_subtitle );
424         return NULL;
425     }
426
427     p_spu->b_pausable = VLC_TRUE;
428
429     /* Create a new subpicture region */
430     memset( &fmt, 0, sizeof(video_format_t) );
431     fmt.i_chroma = VLC_FOURCC('T','E','X','T');
432     fmt.i_aspect = 0;
433     fmt.i_width = fmt.i_height = 0;
434     fmt.i_x_offset = fmt.i_y_offset = 0;
435     p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_dec), &fmt );
436     if( !p_spu->p_region )
437     {
438         msg_Err( p_dec, "cannot allocate SPU region" );
439         if( psz_subtitle ) free( psz_subtitle );
440         p_dec->pf_spu_buffer_del( p_dec, p_spu );
441         return NULL;
442     }
443
444     /* Decode and format the subpicture unit */
445     if( p_dec->fmt_in.i_codec != VLC_FOURCC('s','s','a',' ') &&
446         p_dec->fmt_in.i_codec != VLC_FOURCC('u','s','f',' ') )
447     {
448         /* Normal text subs, easy markup */
449         p_spu->i_flags = SUBPICTURE_ALIGN_BOTTOM | p_sys->i_align;
450         p_spu->i_x = p_sys->i_align ? 20 : 0;
451         p_spu->i_y = 10;
452
453         /* Remove formatting from string */
454         StripTags( psz_subtitle );
455
456         p_spu->p_region->psz_text = psz_subtitle;
457         p_spu->p_region->psz_html = NULL;
458         p_spu->i_start = p_block->i_pts;
459         p_spu->i_stop = p_block->i_pts + p_block->i_length;
460         p_spu->b_ephemer = (p_block->i_length == 0);
461         p_spu->b_absolute = VLC_FALSE;
462     }
463     else
464     {
465         /* Decode SSA/USF strings */
466         if( p_dec->fmt_in.i_codec == VLC_FOURCC('s','s','a',' ') )
467             ParseSSAString( p_dec, psz_subtitle, p_spu );
468         else
469             ParseUSFString( p_dec, psz_subtitle, p_spu );
470
471         p_spu->i_start = p_block->i_pts;
472         p_spu->i_stop = p_block->i_pts + p_block->i_length;
473         p_spu->b_ephemer = (p_block->i_length == 0);
474         p_spu->b_absolute = VLC_FALSE;
475         p_spu->i_original_picture_width = p_sys->i_original_width;
476         p_spu->i_original_picture_height = p_sys->i_original_height;
477         if( psz_subtitle ) free( psz_subtitle );
478     }
479     return p_spu;
480 }
481
482 static void ParseUSFString( decoder_t *p_dec, char *psz_subtitle, subpicture_t *p_spu_in )
483 {
484     decoder_sys_t   *p_sys = p_dec->p_sys;
485     subpicture_t    *p_spu = p_spu_in;
486     char            *psz_text;
487     char            *psz_text_start;
488     ssa_style_t     *p_style = NULL;
489     int              i;
490
491     /* Create a text only copy of the subtitle (for legacy implementations) and copy
492      * the rich html version across as is - for parsing by a rendering engine capable
493      * of understanding it.
494      */
495     p_spu->p_region->psz_text = NULL;
496     p_spu->p_region->psz_html = strdup( psz_subtitle );
497
498     for( i = 0; i < p_sys->i_ssa_styles; i++ )
499     {
500         if( !strcasecmp( p_sys->pp_ssa_styles[i]->psz_stylename, "Default" ) )
501             p_style = p_sys->pp_ssa_styles[i];
502     }
503
504     /* The StripTags() function doesn't handle HTML tags that have attribute/values with
505      * them, or properly translate <br/> sequences into newlines, or handle &' sequences
506      * so do it here ourselves.
507      */
508     psz_text_start = malloc( strlen( psz_subtitle ));
509
510     psz_text = psz_text_start;
511     while( *psz_subtitle )
512     {
513         if( *psz_subtitle == '<' )
514         {
515             if( !strncasecmp( psz_subtitle, "<br/>", 5 ))
516                 *psz_text++ = '\n';
517             else if( strncasecmp( psz_subtitle, "<text ", 6 ))
518             {
519                 char *psz_style = strcasestr( psz_subtitle, "style=\"" );
520
521                 if( psz_style && ( psz_style < strchr( psz_subtitle, '>' ) ))
522                 {
523                     int i_len;
524
525                     psz_style += strspn( psz_style, "\"" ) + 1;
526                     i_len = strspn( psz_style, "\"" );
527
528                     psz_style[ i_len ] = '\0';
529
530                     for( i = 0; i < p_sys->i_ssa_styles; i++ )
531                     {
532                         if( !strcmp( p_sys->pp_ssa_styles[i]->psz_stylename, psz_style ) )
533                             p_style = p_sys->pp_ssa_styles[i];
534                     }
535
536                     psz_style[ i_len ] = '\"';
537                 }
538             }
539             
540             psz_subtitle += strcspn( psz_subtitle, ">" );
541         }
542         else if( *psz_subtitle == '&' )
543         {
544             if( !strncasecmp( psz_subtitle, "&lt;", 4 ))
545                 *psz_text++ = '<';
546             else if( !strncasecmp( psz_subtitle, "&gt;", 4 ))
547                 *psz_text++ = '>';
548             else if( !strncasecmp( psz_subtitle, "&amp;", 5 ))
549                 *psz_text++ = '&';
550
551             psz_subtitle += strcspn( psz_subtitle, ";" );
552         }
553         else if( ( *psz_subtitle == '\t' ) ||
554                  ( *psz_subtitle == '\r' ) ||
555                  ( *psz_subtitle == '\n' ) ||
556                  ( *psz_subtitle == ' ' ) )
557         {
558             if( ( psz_text_start < psz_text ) &&
559                 ( *(psz_text-1) != ' ' ) )
560             {
561                 *psz_text++ = ' ';
562             }
563         }
564         else
565             *psz_text++ = *psz_subtitle;
566
567         psz_subtitle++;
568     }
569     *psz_text = '\0';
570     p_spu->p_region->psz_text = strdup( psz_text_start );
571     free( psz_text_start );
572
573     if( p_style == NULL )
574     {
575         p_spu->i_flags = SUBPICTURE_ALIGN_BOTTOM | p_sys->i_align;
576         p_spu->i_x = p_sys->i_align ? 20 : 0;
577         p_spu->i_y = 10;
578     }
579     else
580     {
581         msg_Dbg( p_dec, "style is: %s", p_style->psz_stylename);
582         p_spu->p_region->p_style = &p_style->font_style;
583         p_spu->i_flags = p_style->i_align;
584     }
585 }
586
587 static void ParseSSAString( decoder_t *p_dec, char *psz_subtitle, subpicture_t *p_spu_in )
588 {
589     /* We expect MKV formatted SSA:
590      * ReadOrder, Layer, Style, CharacterName, MarginL, MarginR,
591      * MarginV, Effect, Text */
592     decoder_sys_t   *p_sys = p_dec->p_sys;
593     subpicture_t    *p_spu = p_spu_in;
594     ssa_style_t     *p_style = NULL;
595     char            *psz_new_subtitle = NULL;
596     char            *psz_buffer_sub = NULL;
597     char            *psz_style = NULL;
598     char            *psz_style_start = NULL;
599     char            *psz_style_end = NULL;
600     int             i_text = 0, i_comma = 0, i_strlen = 0, i;
601     int             i_margin_l = 0, i_margin_r = 0, i_margin_v = 0;
602
603     psz_buffer_sub = psz_subtitle;
604
605     p_spu->p_region->psz_html = NULL;
606
607     i_comma = 0;
608     while( i_comma < 8 && *psz_buffer_sub != '\0' )
609     {
610         if( *psz_buffer_sub == ',' )
611         {
612             i_comma++;
613             if( i_comma == 2 ) psz_style_start = &psz_buffer_sub[1];
614             if( i_comma == 3 ) psz_style_end = &psz_buffer_sub[0];
615             if( i_comma == 4 ) i_margin_l = (int)strtol( psz_buffer_sub+1, NULL, 10 );
616             if( i_comma == 5 ) i_margin_r = (int)strtol( psz_buffer_sub+1, NULL, 10 );
617             if( i_comma == 6 ) i_margin_v = (int)strtol( psz_buffer_sub+1, NULL, 10 );
618         }
619         psz_buffer_sub++;
620     }
621
622     if( *psz_buffer_sub == '\0' && i_comma == 8 )
623     {
624         msg_Dbg( p_dec, "couldn't find all fields in this SSA line" );
625         return;
626     }
627
628     psz_new_subtitle = malloc( strlen( psz_buffer_sub ) + 1);
629     i_text = 0;
630     while( psz_buffer_sub[0] != '\0' )
631     {
632         if( psz_buffer_sub[0] == '\\' && psz_buffer_sub[1] == 'n' )
633         {
634             psz_new_subtitle[i_text] = ' ';
635             i_text++;
636             psz_buffer_sub += 2;
637         }
638         else if( psz_buffer_sub[0] == '\\' && psz_buffer_sub[1] == 'N' )
639         {
640             psz_new_subtitle[i_text] = '\n';
641             i_text++;
642             psz_buffer_sub += 2;
643         }
644         else if( psz_buffer_sub[0] == '{' &&
645                  psz_buffer_sub[1] == '\\' )
646         {
647             /* SSA control code */
648             while( psz_buffer_sub[0] != '\0' &&
649                    psz_buffer_sub[0] != '}' )
650             {
651                 psz_buffer_sub++;
652             }
653             psz_buffer_sub++;
654         }
655         else
656         {
657             psz_new_subtitle[i_text] = psz_buffer_sub[0];
658             i_text++;
659             psz_buffer_sub++;
660         }
661     }
662     psz_new_subtitle[i_text] = '\0';
663
664     i_strlen = __MAX( psz_style_end - psz_style_start, 0);
665     psz_style = (char *)malloc( i_strlen + 1);
666     psz_style = memcpy( psz_style, psz_style_start, i_strlen );
667     psz_style[i_strlen] = '\0';
668
669     for( i = 0; i < p_sys->i_ssa_styles; i++ )
670     {
671         if( !strcmp( p_sys->pp_ssa_styles[i]->psz_stylename, psz_style ) )
672             p_style = p_sys->pp_ssa_styles[i];
673     }
674     if( psz_style ) free( psz_style );
675
676     p_spu->p_region->psz_text = psz_new_subtitle;
677     if( p_style == NULL )
678     {
679         p_spu->i_flags = SUBPICTURE_ALIGN_BOTTOM | p_sys->i_align;
680         p_spu->i_x = p_sys->i_align ? 20 : 0;
681         p_spu->i_y = 10;
682     }
683     else
684     {
685         msg_Dbg( p_dec, "style is: %s", p_style->psz_stylename);
686         p_spu->p_region->p_style = &p_style->font_style;
687         p_spu->i_flags = p_style->i_align;
688         if( p_style->i_align & SUBPICTURE_ALIGN_LEFT )
689         {
690             p_spu->i_x = (i_margin_l) ? i_margin_l : p_style->i_margin_h;
691         }
692         else if( p_style->i_align & SUBPICTURE_ALIGN_RIGHT ) 
693         {
694             p_spu->i_x = (i_margin_r) ? i_margin_r : p_style->i_margin_h;
695         }
696         p_spu->i_y = (i_margin_v) ? i_margin_v : p_style->i_margin_v;
697     }
698 }
699
700 static char* GotoNextLine( char *psz_text )
701 {
702     char *p_newline = psz_text;
703
704     while( p_newline[0] != '\0' )
705     {
706         if( p_newline[0] == '\n' || p_newline[0] == '\r' )
707         {
708             p_newline++;
709             while( p_newline[0] == '\n' || p_newline[0] == '\r' )
710                 p_newline++;
711             break;
712         }
713         else p_newline++;
714     }
715     return p_newline;
716 }
717
718 /*****************************************************************************
719  * ParseColor: SSA stores color in BBGGRR, in ASS it uses AABBGGRR
720  * The string value in the string can be a pure integer, or hexadecimal &HBBGGRR
721  *****************************************************************************/
722 static void ParseColor( decoder_t *p_dec, char *psz_color, int *pi_color, int *pi_alpha )
723 {
724     int i_color = 0;
725     if( !strncasecmp( psz_color, "&H", 2 ) )
726     {
727         /* textual HEX representation */
728         i_color = (int) strtol( psz_color+2, NULL, 16 );
729     }
730     else i_color = (int) strtol( psz_color, NULL, 0 );
731
732     *pi_color = 0;
733     *pi_color |= ( ( i_color & 0x000000FF ) << 16 ); /* Red */
734     *pi_color |= ( ( i_color & 0x0000FF00 ) );       /* Green */
735     *pi_color |= ( ( i_color & 0x00FF0000 ) >> 16 ); /* Blue */
736
737     if( pi_alpha != NULL )
738         *pi_alpha = ( i_color & 0xFF000000 ) >> 24;
739 }
740
741 /*****************************************************************************
742  * ParseUSFHeader: Retrieve global formatting information etc
743  *****************************************************************************/
744 static void ParseUSFHeader( decoder_t *p_dec )
745 {
746     decoder_sys_t *p_sys = p_dec->p_sys;
747     stream_t      *p_sub = NULL;
748     xml_t         *p_xml = NULL;
749     xml_reader_t  *p_xml_reader = NULL;
750
751     p_sub = stream_MemoryNew( VLC_OBJECT(p_dec),
752                               p_dec->fmt_in.p_extra,
753                               p_dec->fmt_in.i_extra,
754                               VLC_FALSE );
755     if( p_sub )
756     {
757         p_xml = xml_Create( p_dec );
758         if( p_xml )
759         {
760             p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
761             if( p_xml_reader )
762             {
763                 /* Look for Root Node */
764                 if( xml_ReaderRead( p_xml_reader ) == 1 )
765                 {
766                     char *psz_node = xml_ReaderName( p_xml_reader );
767
768                     if( !strcasecmp( "usfsubtitles", psz_node ) )
769                         ParseUSFHeaderTags( p_sys, p_xml_reader );
770
771                     free( psz_node );
772                 }
773
774                 xml_ReaderDelete( p_xml, p_xml_reader );
775             }
776             xml_Delete( p_xml );
777         }
778         stream_Delete( p_sub );
779     }
780 }
781
782 static void ParseUSFHeaderTags( decoder_sys_t *p_sys, xml_reader_t *p_xml_reader )
783 {
784     char *psz_node;
785     ssa_style_t *p_style = NULL;
786     int i_style_level = 0;
787     int i_metadata_level = 0;
788
789     while ( xml_ReaderRead( p_xml_reader ) == 1 )
790     {
791         switch ( xml_ReaderNodeType( p_xml_reader ) )
792         {
793             case XML_READER_TEXT:
794             case XML_READER_NONE:
795                 break;
796             case XML_READER_ENDELEM:
797                 psz_node = xml_ReaderName( p_xml_reader );
798                 
799                 if( psz_node )
800                 {
801                     switch (i_style_level)
802                     {
803                         case 0:
804                             if( !strcasecmp( "metadata", psz_node ) && (i_metadata_level == 1) )
805                             {
806                                 i_metadata_level--;
807                             }
808                             break;
809                         case 1:
810                             if( !strcasecmp( "styles", psz_node ) )
811                             {
812                                 i_style_level--;
813                             }
814                             break;
815                         case 2:
816                             if( !strcasecmp( "style", psz_node ) )
817                             {
818                                 p_style->font_style.i_text_align = p_style->i_align;
819
820                                 TAB_APPEND( p_sys->i_ssa_styles, p_sys->pp_ssa_styles, p_style );
821
822                                 p_style = NULL;
823                                 i_style_level--;
824                             }
825                             break;
826                     }
827                     
828                     free( psz_node );
829                 }
830                 break;
831             case XML_READER_STARTELEM:
832                 psz_node = xml_ReaderName( p_xml_reader );
833
834                 if( psz_node )
835                 {
836                     if( !strcasecmp( "metadata", psz_node ) && (i_style_level == 0) )
837                     {
838                         i_metadata_level++;
839                     }
840                     else if( !strcasecmp( "resolution", psz_node ) && (i_metadata_level == 1) )
841                     {
842                         while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
843                         {
844                             char *psz_name = xml_ReaderName ( p_xml_reader );
845                             char *psz_value = xml_ReaderValue ( p_xml_reader );
846
847                             if( psz_name && psz_value )
848                             {
849                                 if( !strcasecmp( "x", psz_name ) )
850                                     p_sys->i_original_width = atoi( psz_value );
851                                 else if( !strcasecmp( "y", psz_name ) )
852                                     p_sys->i_original_height = atoi( psz_value );
853                             }
854                             if( psz_name )  free( psz_name );
855                             if( psz_value ) free( psz_value );
856                         }
857                     }
858                     else if( !strcasecmp( "styles", psz_node ) && (i_style_level == 0) )
859                     {
860                         i_style_level++;
861                     }
862                     else if( !strcasecmp( "style", psz_node ) && (i_style_level == 1) )
863                     {
864                         i_style_level++;
865
866                         p_style = calloc( 1, sizeof(ssa_style_t) );
867
868                         while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
869                         {
870                             char *psz_name = xml_ReaderName ( p_xml_reader );
871                             char *psz_value = xml_ReaderValue ( p_xml_reader );
872
873                             if( psz_name && psz_value )
874                             {
875                                 if( !strcasecmp( "name", psz_name ) )
876                                     p_style->psz_stylename = strdup( psz_value);
877                             }
878                             if( psz_name )  free( psz_name );
879                             if( psz_value ) free( psz_value );
880                         }
881                     }
882                     else if( !strcasecmp( "fontstyle", psz_node ) && (i_style_level == 2) )
883                     {
884                         while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
885                         {
886                             char *psz_name = xml_ReaderName ( p_xml_reader );
887                             char *psz_value = xml_ReaderValue ( p_xml_reader );
888
889                             if( psz_name && psz_value )
890                             {
891                                 if( !strcasecmp( "face", psz_name ) )
892                                     p_style->font_style.psz_fontname = strdup( psz_value);
893                                 else if( !strcasecmp( "size", psz_name ) )
894                                     p_style->font_style.i_font_size = atoi( psz_value);
895                                 else if( !strcasecmp( "italic", psz_name ) )
896                                 {
897                                     if( !strcasecmp( "yes", psz_value ))
898                                         p_style->font_style.i_style_flags |= STYLE_ITALIC;
899                                 }
900                                 else if( !strcasecmp( "weight", psz_name ) )
901                                 {
902                                     if( !strcasecmp( "bold", psz_value ))
903                                         p_style->font_style.i_style_flags |= STYLE_BOLD;
904                                 }
905                                 else if( !strcasecmp( "underline", psz_name ) )
906                                 {
907                                     if( !strcasecmp( "yes", psz_value ))
908                                         p_style->font_style.i_style_flags |= STYLE_UNDERLINE;
909                                 }
910                                 else if( !strcasecmp( "color", psz_name ) )
911                                 {
912                                     if( *psz_value == '#' )
913                                     {
914                                         unsigned long col = strtol(psz_value+1, NULL, 16);
915                                         p_style->font_style.i_font_color = (col & 0x00ffffff);
916                                         // From DTD: <!-- alpha range = 0..100 -->
917                                         p_style->font_style.i_font_alpha = ((col >> 24) & 0xff) * 255 / 100;
918                                     }
919                                 }
920                                 else if( !strcasecmp( "outline-color", psz_name ) )
921                                 {
922                                     if( *psz_value == '#' )
923                                     {
924                                         unsigned long col = strtol(psz_value+1, NULL, 16);
925                                         p_style->font_style.i_outline_color = (col & 0x00ffffff);
926                                         // From DTD: <!-- alpha range = 0..100 -->
927                                         p_style->font_style.i_outline_alpha = ((col >> 24) & 0xff) * 255 / 100;
928                                     }
929                                 } 
930                                 else if( !strcasecmp( "shadow-color", psz_name ) )
931                                 {
932                                     if( *psz_value == '#' )
933                                     {
934                                         unsigned long col = strtol(psz_value+1, NULL, 16);
935                                         p_style->font_style.i_shadow_color = (col & 0x00ffffff);
936                                         // From DTD: <!-- alpha range = 0..100 -->
937                                         p_style->font_style.i_shadow_alpha = ((col >> 24) & 0xff) * 255 / 100;
938                                     }
939                                 }
940                             }
941                             if( psz_name )  free( psz_name );
942                             if( psz_value ) free( psz_value );
943                         }
944                     }
945                     else if( !strcasecmp( "position", psz_node ) && (i_style_level == 2) )
946                     {
947                         while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
948                         {
949                             char *psz_name = xml_ReaderName ( p_xml_reader );
950                             char *psz_value = xml_ReaderValue ( p_xml_reader );
951
952                             if( psz_name && psz_value )
953                             {
954                                 if( !strcasecmp( "alignment", psz_name ) )
955                                 {
956                                     if( !strcasecmp( "TopLeft", psz_value ) )
957                                     {
958                                         p_style->i_align |= SUBPICTURE_ALIGN_TOP;
959                                         p_style->i_align |= SUBPICTURE_ALIGN_LEFT;
960                                     }
961                                     else if( !strcasecmp( "TopCenter", psz_value ) )
962                                     {
963                                         p_style->i_align |= SUBPICTURE_ALIGN_TOP;
964                                     }
965                                     else if( !strcasecmp( "TopRight", psz_value ) )
966                                     {
967                                         p_style->i_align |= SUBPICTURE_ALIGN_TOP;
968                                         p_style->i_align |= SUBPICTURE_ALIGN_RIGHT;
969                                     }
970                                     else if( !strcasecmp( "MiddleLeft", psz_value ) )
971                                     {
972                                         p_style->i_align |= SUBPICTURE_ALIGN_LEFT;
973                                     }
974                                     else if( !strcasecmp( "MiddleCenter", psz_value ) )
975                                     {
976                                         p_style->i_align = 0;
977                                     }
978                                     else if( !strcasecmp( "MiddleRight", psz_value ) )
979                                     {
980                                         p_style->i_align |= SUBPICTURE_ALIGN_RIGHT;
981                                     }
982                                     else if( !strcasecmp( "BottomLeft", psz_value ) )
983                                     {
984                                         p_style->i_align |= SUBPICTURE_ALIGN_BOTTOM;
985                                         p_style->i_align |= SUBPICTURE_ALIGN_LEFT;
986                                     }
987                                     else if( !strcasecmp( "BottomCenter", psz_value ) )
988                                     {
989                                         p_style->i_align |= SUBPICTURE_ALIGN_BOTTOM;
990                                     }
991                                     else if( !strcasecmp( "BottomRight", psz_value ) )
992                                     {
993                                         p_style->i_align |= SUBPICTURE_ALIGN_BOTTOM;
994                                         p_style->i_align |= SUBPICTURE_ALIGN_RIGHT;
995                                     }
996                                 }
997                             }
998                             if( psz_name )  free( psz_name );
999                             if( psz_value ) free( psz_value );
1000                         }
1001                     }
1002                     
1003                     free( psz_node );
1004                 }
1005                 break;
1006         }
1007     }
1008     if( p_style ) free( p_style );
1009 }
1010 /*****************************************************************************
1011  * ParseSSAHeader: Retrieve global formatting information etc
1012  *****************************************************************************/
1013 static void ParseSSAHeader( decoder_t *p_dec )
1014 {
1015     decoder_sys_t *p_sys = p_dec->p_sys;
1016     char *psz_parser = NULL;
1017     char *psz_header = malloc( p_dec->fmt_in.i_extra+1 );
1018     int i_section_type = 1;
1019
1020     memcpy( psz_header, p_dec->fmt_in.p_extra, p_dec->fmt_in.i_extra );
1021     psz_header[ p_dec->fmt_in.i_extra] = '\0';
1022
1023     /* Handle [Script Info] section */
1024     psz_parser = strcasestr( psz_header, "[Script Info]" );
1025     if( psz_parser == NULL ) goto eof;
1026
1027     psz_parser = GotoNextLine( psz_parser );
1028
1029     while( psz_parser[0] != '\0' )
1030     {
1031         int temp;
1032         char buffer_text[MAX_LINE + 1];
1033
1034         if( psz_parser[0] == '!' || psz_parser[0] == ';' ) /* comment */;
1035         else if( sscanf( psz_parser, "PlayResX: %d", &temp ) == 1 )
1036             p_sys->i_original_width = ( temp > 0 ) ? temp : -1;
1037         else if( sscanf( psz_parser, "PlayResY: %d", &temp ) == 1 )
1038             p_sys->i_original_height = ( temp > 0 ) ? temp : -1;
1039         else if( sscanf( psz_parser, "Script Type: %8192s", buffer_text ) == 1 )
1040         {
1041             if( !strcasecmp( buffer_text, "V4.00+" ) ) p_sys->b_ass = VLC_TRUE;
1042         }
1043         else if( !strncasecmp( psz_parser, "[V4 Styles]", 11 ) )
1044             i_section_type = 1;
1045         else if( !strncasecmp( psz_parser, "[V4+ Styles]", 12) )
1046         {
1047             i_section_type = 2;
1048             p_sys->b_ass = VLC_TRUE;
1049         }
1050         else if( !strncasecmp( psz_parser, "[Events]", 8 ) )
1051             i_section_type = 4;
1052         else if( !strncasecmp( psz_parser, "Style:", 6 ) )
1053         {
1054             int i_font_size, i_bold, i_italic, i_border, i_outline, i_shadow, i_underline,
1055                 i_strikeout, i_scale_x, i_scale_y, i_spacing, i_align, i_margin_l, i_margin_r, i_margin_v;
1056
1057             char psz_temp_stylename[MAX_LINE+1];
1058             char psz_temp_fontname[MAX_LINE+1];
1059             char psz_temp_color1[MAX_LINE+1];
1060             char psz_temp_color2[MAX_LINE+1];
1061             char psz_temp_color3[MAX_LINE+1];
1062             char psz_temp_color4[MAX_LINE+1];
1063
1064             if( i_section_type == 1 ) /* V4 */
1065             {
1066                 if( sscanf( psz_parser, "Style: %8192[^,],%8192[^,],%d,%8192[^,],%8192[^,],%8192[^,],%8192[^,],%d,%d,%d,%d,%d,%d,%d,%d,%d%*[^\r\n]",
1067                     psz_temp_stylename, psz_temp_fontname, &i_font_size,
1068                     psz_temp_color1, psz_temp_color2, psz_temp_color3, psz_temp_color4, &i_bold, &i_italic,
1069                     &i_border, &i_outline, &i_shadow, &i_align, &i_margin_l, &i_margin_r, &i_margin_v ) == 16 )
1070                 {
1071                     ssa_style_t *p_style = malloc( sizeof(ssa_style_t) );
1072
1073                     p_style->psz_stylename = strdup( psz_temp_stylename );
1074                     p_style->font_style.psz_fontname = strdup( psz_temp_fontname );
1075                     p_style->font_style.i_font_size = i_font_size;
1076
1077                     ParseColor( p_dec, psz_temp_color1, &p_style->font_style.i_font_color, NULL );
1078                     ParseColor( p_dec, psz_temp_color4, &p_style->font_style.i_shadow_color, NULL );
1079                     p_style->font_style.i_outline_color = p_style->font_style.i_shadow_color;
1080                     p_style->font_style.i_font_alpha = p_style->font_style.i_outline_alpha = p_style->font_style.i_shadow_alpha = 0x00;
1081                     p_style->font_style.i_style_flags = 0;
1082                     if( i_bold ) p_style->font_style.i_style_flags |= STYLE_BOLD;
1083                     if( i_italic ) p_style->font_style.i_style_flags |= STYLE_ITALIC;
1084
1085                     if( i_border == 1 ) p_style->font_style.i_style_flags |= (STYLE_ITALIC | STYLE_OUTLINE);
1086                     else if( i_border == 3 )
1087                     {
1088                         p_style->font_style.i_style_flags |= STYLE_BACKGROUND;
1089                         p_style->font_style.i_background_color = p_style->font_style.i_shadow_color;
1090                         p_style->font_style.i_background_alpha = p_style->font_style.i_shadow_alpha;
1091                     }
1092                     p_style->font_style.i_shadow_width = i_shadow;
1093                     p_style->font_style.i_outline_width = i_outline;
1094
1095                     p_style->i_align = 0;
1096                     if( i_align == 1 || i_align == 5 || i_align == 9 ) p_style->i_align |= SUBPICTURE_ALIGN_LEFT;
1097                     if( i_align == 3 || i_align == 7 || i_align == 11 ) p_style->i_align |= SUBPICTURE_ALIGN_RIGHT;
1098                     if( i_align < 4 ) p_style->i_align |= SUBPICTURE_ALIGN_BOTTOM;
1099                     else if( i_align < 8 ) p_style->i_align |= SUBPICTURE_ALIGN_TOP; 
1100
1101                     p_style->i_margin_h = ( p_style->i_align & SUBPICTURE_ALIGN_RIGHT ) ? i_margin_r : i_margin_l;
1102                     p_style->i_margin_v = i_margin_v;
1103
1104                     TAB_APPEND( p_sys->i_ssa_styles, p_sys->pp_ssa_styles, p_style );
1105                 }
1106                 else msg_Warn( p_dec, "SSA v4 styleline parsing failed" );
1107             }
1108             else if( i_section_type == 2 ) /* V4+ */
1109             {
1110                 /* Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour,
1111                    Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline,
1112                    Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
1113                 */
1114                 if( sscanf( psz_parser, "Style: %8192[^,],%8192[^,],%d,%8192[^,],%8192[^,],%8192[^,],%8192[^,],%d,%d,%d,%d,%d,%d,%d,%*f,%d,%d,%d,%d,%d,%d,%d%*[^\r\n]",
1115                     psz_temp_stylename, psz_temp_fontname, &i_font_size,
1116                     psz_temp_color1, psz_temp_color2, psz_temp_color3, psz_temp_color4, &i_bold, &i_italic,
1117                     &i_underline, &i_strikeout, &i_scale_x, &i_scale_y, &i_spacing, &i_border, &i_outline,
1118                     &i_shadow, &i_align, &i_margin_l, &i_margin_r, &i_margin_v ) == 21 )
1119                 {
1120                     ssa_style_t *p_style = malloc( sizeof(ssa_style_t) );
1121
1122                     p_style->psz_stylename = strdup( psz_temp_stylename );
1123                     p_style->font_style.psz_fontname = strdup( psz_temp_fontname );
1124                     p_style->font_style.i_font_size = i_font_size;
1125                     msg_Dbg( p_dec, psz_temp_color1 );
1126                     ParseColor( p_dec, psz_temp_color1, &p_style->font_style.i_font_color, &p_style->font_style.i_font_alpha );
1127                     ParseColor( p_dec, psz_temp_color3, &p_style->font_style.i_outline_color, &p_style->font_style.i_outline_alpha );
1128                     ParseColor( p_dec, psz_temp_color4, &p_style->font_style.i_shadow_color, &p_style->font_style.i_shadow_alpha );
1129
1130                     p_style->font_style.i_style_flags = 0;
1131                     if( i_bold ) p_style->font_style.i_style_flags |= STYLE_BOLD;
1132                     if( i_italic ) p_style->font_style.i_style_flags |= STYLE_ITALIC;
1133                     if( i_underline ) p_style->font_style.i_style_flags |= STYLE_UNDERLINE;
1134                     if( i_strikeout ) p_style->font_style.i_style_flags |= STYLE_STRIKEOUT;
1135                     if( i_border == 1 ) p_style->font_style.i_style_flags |= (STYLE_ITALIC | STYLE_OUTLINE);
1136                     else if( i_border == 3 )
1137                     {
1138                         p_style->font_style.i_style_flags |= STYLE_BACKGROUND;
1139                         p_style->font_style.i_background_color = p_style->font_style.i_shadow_color;
1140                         p_style->font_style.i_background_alpha = p_style->font_style.i_shadow_alpha;
1141                     }
1142                     p_style->font_style.i_shadow_width  = ( i_border == 1 ) ? i_shadow : 0;
1143                     p_style->font_style.i_outline_width = ( i_border == 1 ) ? i_outline : 0;
1144                     p_style->font_style.i_spacing = i_spacing;
1145                     //p_style->font_style.f_angle = f_angle;
1146
1147                     p_style->i_align = 0;
1148                     if( i_align == 0x1 || i_align == 0x4 || i_align == 0x1 ) p_style->i_align |= SUBPICTURE_ALIGN_LEFT;
1149                     if( i_align == 0x3 || i_align == 0x6 || i_align == 0x9 ) p_style->i_align |= SUBPICTURE_ALIGN_RIGHT;
1150                     if( i_align == 0x7 || i_align == 0x8 || i_align == 0x9 ) p_style->i_align |= SUBPICTURE_ALIGN_TOP;
1151                     if( i_align == 0x1 || i_align == 0x2 || i_align == 0x3 ) p_style->i_align |= SUBPICTURE_ALIGN_BOTTOM;
1152                     p_style->i_margin_h = ( p_style->i_align & SUBPICTURE_ALIGN_RIGHT ) ? i_margin_r : i_margin_l;
1153                     p_style->i_margin_v = i_margin_v;
1154
1155                     /*TODO: Ignored: angle i_scale_x|y (fontscaling), i_encoding */
1156                     TAB_APPEND( p_sys->i_ssa_styles, p_sys->pp_ssa_styles, p_style );
1157                 }
1158                 else msg_Dbg( p_dec, "SSA V4+ styleline parsing failed" );
1159             }
1160         }
1161         psz_parser = GotoNextLine( psz_parser );
1162     }
1163
1164 eof:
1165     if( psz_header ) free( psz_header );
1166     return;
1167 }
1168
1169 static void StripTags( char *psz_text )
1170 {
1171     int i_left_moves = 0;
1172     vlc_bool_t b_inside_tag = VLC_FALSE;
1173     int i = 0;
1174     int i_tag_start = -1;
1175     while( psz_text[ i ] )
1176     {
1177         if( !b_inside_tag )
1178         {
1179             if( psz_text[ i ] == '<' )
1180             {
1181                 b_inside_tag = VLC_TRUE;
1182                 i_tag_start = i;
1183             }
1184             psz_text[ i - i_left_moves ] = psz_text[ i ];
1185         }
1186         else
1187         {
1188             if( ( psz_text[ i ] == ' ' ) ||
1189                 ( psz_text[ i ] == '\t' ) ||
1190                 ( psz_text[ i ] == '\n' ) ||
1191                 ( psz_text[ i ] == '\r' ) )
1192             {
1193                 b_inside_tag = VLC_FALSE;
1194                 i_tag_start = -1;
1195             }
1196             else if( psz_text[ i ] == '>' )
1197             {
1198                 i_left_moves += i - i_tag_start + 1;
1199                 i_tag_start = -1;
1200                 b_inside_tag = VLC_FALSE;
1201             }
1202             else
1203             {
1204                 psz_text[ i - i_left_moves ] = psz_text[ i ];
1205             }
1206         }
1207         i++;
1208     }
1209     psz_text[ i - i_left_moves ] = '\0';
1210 }