[vlc-devel] Re: [PATCH] USF subtitles enhancements

Jean-Paul Saman jean-paul.saman at planet.nl
Mon May 28 20:28:09 CEST 2007


Bernie Purcell wrote:
> On Wed, 2007-05-23 at 16:20 +1000, I wrote:
> 
>> I am aware of a problem that is occurring at the moment, when the video
>> area is resized to be less than 1:1 - everything swaps to a centre
>> alignment (see sample mkv file). I haven't managed to work the cause
>> yet,
> Now I have. I'm glad it was something simple. The patch below is
> identical to before except it fixes this issue, which was not
> surprisingly to do with the scaling.
>> Bernie

Please revert the rename of i_flags to i_align. I believe it is better 
to have that named as flags iso i_align.

Check the return values of malloc, calloc, strdup, etc. If the pointers 
are NULL then handle the error. There are way too many of these cases in 
this patch.

Provide more rational on certain kind of changes like:
 > -static line_desc_t *NewLine( byte_t *psz_string )
 > +static line_desc_t *NewLine( int i_count )
Why is it needed what was wrong with the old way?

What has attachement_t todo with the demuxer? I feel it is in the wrong 
headerfile.

The patch is not mergable in its current state, there are simply too 
many error paths unchecked and it handles more then one topic. Please 
split the patch up in at least a USF-patch, freetype (or font)-patch and 
subtitle alignment patch.

It is in your and our best interest to chop this big patch up in smaller 
patches. Cause developers review a patch quicker if it is small and 
can't easily oversee such a big patch. Further more then one topic is 
touched in this big patch, most of the smaller topic's can be merged 
sooner if provided in separate patches and it will make the USF subtitle 
part of the patch easier to review in the process.

See my other comments inline with your code.

Kind greetings,
Jean-Paul Saman.

> ------------------------------------------------------------------------
> 
> Index: src/control/mediacontrol_audio_video.c
> ===================================================================
> --- src/control/mediacontrol_audio_video.c	(revision 20287)
> +++ src/control/mediacontrol_audio_video.c	(working copy)
> @@ -120,7 +120,7 @@
>  static
>  int mediacontrol_showtext( vout_thread_t *p_vout, int i_channel,
>                             char *psz_string, text_style_t *p_style,
> -                           int i_flags, int i_hmargin, int i_vmargin,
> +                           int i_align, int i_hmargin, int i_vmargin,
>                             mtime_t i_start, mtime_t i_stop )
>  {
>      subpicture_t *p_spu;
> @@ -146,6 +146,7 @@
>      }
>  
>      p_spu->p_region->psz_text = strdup( psz_string );
> +    p_spu->p_region->i_align = i_align;
>      p_spu->i_start = i_start;
>      p_spu->i_stop = i_stop;
>      p_spu->b_ephemer = VLC_FALSE;
> @@ -153,7 +154,6 @@
>  
>      p_spu->i_x = i_hmargin;
>      p_spu->i_y = i_vmargin;
> -    p_spu->i_flags = i_flags;
>      p_spu->i_channel = i_channel;
>  
>      spu_DisplaySubpicture( p_vout->p_spu, p_spu );
> Index: src/osd/osd_text.c
> ===================================================================
> --- src/osd/osd_text.c	(revision 20287)
> +++ src/osd/osd_text.c	(working copy)
> @@ -32,20 +32,20 @@
>   * \param i_channel Subpicture channel
>   * \param psz_string The text to be shown
>   * \param p_style Pointer to a struct with text style info
> - * \param i_flags flags for alignment and such
> + * \param i_align alignment information
>   * \param i_hmargin horizontal margin in pixels
>   * \param i_vmargin vertical margin in pixels
>   * \param i_duration Amount of time the text is to be shown.
>   */
>  int osd_ShowTextRelative( spu_t *p_spu, int i_channel,
>                             char *psz_string, text_style_t *p_style,
> -                           int i_flags, int i_hmargin, int i_vmargin,
> +                           int i_align, int i_hmargin, int i_vmargin,
>                             mtime_t i_duration )
>  {
>      mtime_t i_now = mdate();
>  
>      return osd_ShowTextAbsolute( p_spu, i_channel, psz_string,
> -                                  p_style, i_flags, i_hmargin, i_vmargin,
> +                                  p_style, i_align, i_hmargin, i_vmargin,
>                                    i_now, i_now + i_duration );
>  }
>  
> @@ -55,7 +55,7 @@
>   * \param i_channel Subpicture channel
>   * \param psz_string The text to be shown
>   * \param p_style Pointer to a struct with text style info
> - * \param i_flags flags for alignment and such
> + * \param i_align alignment information
>   * \param i_hmargin horizontal margin in pixels
>   * \param i_vmargin vertical margin in pixels
>   * \param i_start the time when this string is to appear on the video
> @@ -65,7 +65,7 @@
>   */
>  int osd_ShowTextAbsolute( spu_t *p_spu_channel, int i_channel,
>                             char *psz_string, text_style_t *p_style,
> -                           int i_flags, int i_hmargin, int i_vmargin,
> +                           int i_align, int i_hmargin, int i_vmargin,
>                             mtime_t i_start, mtime_t i_stop )
>  {
>      subpicture_t *p_spu;
> @@ -92,6 +92,7 @@
>      }
>  
>      p_spu->p_region->psz_text = strdup( psz_string );
> +    p_spu->p_region->i_align = i_align;
>      p_spu->i_start = i_start;
>      p_spu->i_stop = i_stop;
>      p_spu->b_ephemer = VLC_TRUE;
> @@ -99,7 +100,6 @@
>  
>      p_spu->i_x = i_hmargin;
>      p_spu->i_y = i_vmargin;
> -    p_spu->i_flags = i_flags;
>      p_spu->i_channel = i_channel;
>  
>      spu_DisplaySubpicture( p_spu_channel, p_spu );
> Index: src/video_output/vout_subpictures.c
> ===================================================================
> --- src/video_output/vout_subpictures.c	(revision 20287)
> +++ src/video_output/vout_subpictures.c	(working copy)
> @@ -652,15 +652,21 @@
>          while( p_region && p_spu->p_blend && p_spu->p_blend->pf_video_blend )
>          {
>              int i_fade_alpha = 255;
> -            int i_x_offset = p_region->i_x + i_subpic_x;
> -            int i_y_offset = p_region->i_y + p_subpic->i_y;
> +            int i_x_offset;
> +            int i_y_offset;
>  
> +            if( p_region->i_percent_x != 0 )
> +                p_region->i_x = ( p_fmt->i_width * p_region->i_percent_x ) / 100;
> +            if( p_region->i_percent_y != 0 )
> +                p_region->i_y = ( p_fmt->i_height * p_region->i_percent_y ) / 100;
> +
> +            i_x_offset = p_region->i_x + i_subpic_x;
> +            i_y_offset = p_region->i_y + p_subpic->i_y;
> +
>              if( p_region->fmt.i_chroma == VLC_FOURCC('T','E','X','T') )
>              {
>                  if( p_spu->p_text && p_spu->p_text->p_module )
>                  {
> -                    p_region->i_align = p_subpic->i_flags;
> -
>                      if( p_spu->p_text->pf_render_html && p_region->psz_html )
>                      {
>                          p_spu->p_text->pf_render_html( p_spu->p_text,
> @@ -726,6 +732,7 @@
>                  p_region->p_cache->fmt = p_spu->p_scale->fmt_out.video;
>                  p_region->p_cache->i_x = p_region->i_x * i_scale_width / 1000;
>                  p_region->p_cache->i_y = p_region->i_y * i_scale_height / 1000;
> +                p_region->p_cache->i_align = p_region->i_align;
>  
>                  p_pic = p_spu->p_scale->pf_video_filter(
>                                   p_spu->p_scale, &p_region->p_cache->picture );
> @@ -743,22 +750,22 @@
>                  p_region = p_region->p_cache;
>              }
>  
> -            if( p_subpic->i_flags & SUBPICTURE_ALIGN_BOTTOM )
> +            if( p_region->i_align & SUBPICTURE_ALIGN_BOTTOM )
>              {
>                  i_y_offset = p_fmt->i_height - p_region->fmt.i_height -
> -                    p_subpic->i_y;
> +                    p_subpic->i_y - p_region->i_y;
>              }
> -            else if ( !(p_subpic->i_flags & SUBPICTURE_ALIGN_TOP) )
> +            else if ( !(p_region->i_align & SUBPICTURE_ALIGN_TOP) )
>              {
>                  i_y_offset = p_fmt->i_height / 2 - p_region->fmt.i_height / 2;
>              }
>  
> -            if( p_subpic->i_flags & SUBPICTURE_ALIGN_RIGHT )
> +            if( p_region->i_align & SUBPICTURE_ALIGN_RIGHT )
>              {
>                  i_x_offset = p_fmt->i_width - p_region->fmt.i_width -
> -                    i_subpic_x;
> +                    i_subpic_x - p_region->i_x;
>              }
> -            else if ( !(p_subpic->i_flags & SUBPICTURE_ALIGN_LEFT) )
> +            else if ( !(p_region->i_align & SUBPICTURE_ALIGN_LEFT) )
>              {
>                  i_x_offset = p_fmt->i_width / 2 - p_region->fmt.i_width / 2;
>              }
> Index: src/video_output/video_text.c
> ===================================================================
> --- src/video_output/video_text.c	(revision 20287)
> +++ src/video_output/video_text.c	(working copy)
> @@ -33,20 +33,20 @@
>   * \param i_channel Subpicture channel
>   * \param psz_string The text to be shown
>   * \param p_style Pointer to a struct with text style info
> - * \param i_flags flags for alignment and such
> + * \param i_align alignment information
>   * \param i_hmargin horizontal margin in pixels
>   * \param i_vmargin vertical margin in pixels
>   * \param i_duration Amount of time the text is to be shown.
>   */
>  int vout_ShowTextRelative( vout_thread_t *p_vout, int i_channel,
>                             char *psz_string, text_style_t *p_style,
> -                           int i_flags, int i_hmargin, int i_vmargin,
> +                           int i_align, int i_hmargin, int i_vmargin,
>                             mtime_t i_duration )
>  {
>      mtime_t i_now = mdate();
>  
>      return vout_ShowTextAbsolute( p_vout, i_channel, psz_string,
> -                                  p_style, i_flags, i_hmargin, i_vmargin,
> +                                  p_style, i_align, i_hmargin, i_vmargin,
>                                    i_now, i_now + i_duration );
>  }
>  
> @@ -56,7 +56,7 @@
>   * \param i_channel Subpicture channel
>   * \param psz_string The text to be shown
>   * \param p_style Pointer to a struct with text style info
> - * \param i_flags flags for alignment and such
> + * \param i_align alignment information
>   * \param i_hmargin horizontal margin in pixels
>   * \param i_vmargin vertical margin in pixels
>   * \param i_start the time when this string is to appear on the video
> @@ -66,7 +66,7 @@
>   */
>  int vout_ShowTextAbsolute( vout_thread_t *p_vout, int i_channel,
>                             const char *psz_string, text_style_t *p_style,
> -                           int i_flags, int i_hmargin, int i_vmargin,
> +                           int i_align, int i_hmargin, int i_vmargin,
>                             mtime_t i_start, mtime_t i_stop )
>  {
>      subpicture_t *p_spu;
> @@ -93,6 +93,7 @@
>      }
>  
>      p_spu->p_region->psz_text = strdup( psz_string );
> +    p_spu->p_region->i_align = i_align;
>      p_spu->i_start = i_start;
>      p_spu->i_stop = i_stop;
>      p_spu->b_ephemer = VLC_TRUE;
> @@ -100,7 +101,6 @@
>  
>      p_spu->i_x = i_hmargin;
>      p_spu->i_y = i_vmargin;
> -    p_spu->i_flags = i_flags;
>      p_spu->i_channel = i_channel;
>  
>      spu_DisplaySubpicture( p_vout->p_spu, p_spu );
> Index: modules/demux/mkv.cpp
> ===================================================================
> --- modules/demux/mkv.cpp	(revision 20287)
> +++ modules/demux/mkv.cpp	(working copy)
> @@ -62,6 +62,7 @@
>  #include "ebml/StdIOCallback.h"
>  
>  #include "matroska/KaxAttachments.h"
> +#include "matroska/KaxAttached.h"
>  #include "matroska/KaxBlock.h"
>  #include "matroska/KaxBlockData.h"
>  #include "matroska/KaxChapters.h"
> @@ -1154,6 +1155,7 @@
>      bool Preload( );
>      bool PreloadFamily( const matroska_segment_c & segment );
>      void ParseInfo( KaxInfo *info );
> +    void ParseAttachments( KaxAttachments *attachments );
>      void ParseChapters( KaxChapters *chapters );
>      void ParseSeekHead( KaxSeekHead *seekhead );
>      void ParseTracks( KaxTracks *tracks );
> @@ -1298,6 +1300,24 @@
>  
>  } event_thread_t;
>  
> +class attachment_c
> +{
> +public:
> +    attachment_c()
> +        :p_data(NULL)
> +        ,i_size(0)
> +    {}
> +    virtual ~attachment_c()
> +    {
> +        if( p_data ) free( p_data );
> +    }
> +
> +    std::string    psz_file_name;
> +    std::string    psz_mime_type;
> +    void          *p_data;
> +    int            i_size;
> +};
> +
>  class demux_sys_t
>  {
>  public:
> @@ -1329,6 +1349,8 @@
>              delete opened_segments[i];
>          for ( i=0; i<used_segments.size(); i++ )
>              delete used_segments[i];
> +        for ( i=0; i<stored_attachments.size(); i++ )
> +            delete stored_attachments[i];
>          if( meta ) vlc_meta_Delete( meta );
>  
>          while( titles.size() )
> @@ -1350,6 +1372,7 @@
>      size_t                           i_current_title;
>  
>      std::vector<matroska_stream_c*>  streams;
> +    std::vector<attachment_c*>       stored_attachments;
>      std::vector<matroska_segment_c*> opened_segments;
>      std::vector<virtual_segment_c*>  used_segments;
>      virtual_segment_c                *p_current_segment;
> @@ -1607,9 +1630,25 @@
>      size_t      i_idx;
>  
>      vlc_meta_t *p_meta;
> +    attachment_t *p_attach;
>  
>      switch( i_query )
>      {
> +        case DEMUX_GET_ATTACHMENT:
> +            i_idx = (int)va_arg( args, int );
> +            p_attach = (attachment_t*)va_arg( args, attachment_t * );
> +
> +            if( i_idx < p_sys->stored_attachments.size() )
> +            {
> +                p_attach->psz_file_name = p_sys->stored_attachments[i_idx]->psz_file_name.c_str();
> +                p_attach->psz_mime_type = p_sys->stored_attachments[i_idx]->psz_mime_type.c_str();
> +                p_attach->p_data = p_sys->stored_attachments[i_idx]->p_data;
> +                p_attach->i_size = p_sys->stored_attachments[i_idx]->i_size;
> +
> +                return VLC_SUCCESS;
> +            }
> +            return VLC_EGENERIC;
> +
>          case DEMUX_GET_META:
>              p_meta = (vlc_meta_t*)va_arg( args, vlc_meta_t* );
>              vlc_meta_Merge( p_meta, p_sys->meta );
> @@ -4911,6 +4950,39 @@
>  }
>  
>  /*****************************************************************************
> + * ParseAttachments:
> + *****************************************************************************/
> +void matroska_segment_c::ParseAttachments( KaxAttachments *attachments )
> +{
> +    EbmlElement *el;
> +    int i_upper_level = 0;
> +
> +    attachments->Read( es, attachments->Generic().Context, i_upper_level, el, true );
> +
> +    KaxAttached *attachedFile = FindChild<KaxAttached>( *attachments );
> +    
> +    while( attachedFile && ( attachedFile->GetSize() > 0 ) )
> +    {
> +        std::string psz_mime_type  = GetChild<KaxMimeType>( *attachedFile );
> +        KaxFileName  &file_name    = GetChild<KaxFileName>( *attachedFile );
> +        KaxFileData  &img_data     = GetChild<KaxFileData>( *attachedFile );
> +
> +        attachment_c *new_attachment = new attachment_c();
> +
> +        new_attachment->psz_file_name  = ToUTF8( UTFstring( file_name ) );
> +        new_attachment->psz_mime_type  = psz_mime_type;
> +        new_attachment->i_size         = img_data.GetSize();
> +        new_attachment->p_data         = malloc( img_data.GetSize() );

Test for NULL pointer here!
   if( !new_attachement->p_data )
      /* Handle error here */

> +
> +        memcpy( new_attachment->p_data, img_data.GetBuffer(), img_data.GetSize() );
> +
> +        sys.stored_attachments.push_back( new_attachment );
> +        
> +        attachedFile = &GetNextChild<KaxAttached>( *attachments, *attachedFile );
> +    }
> +}
> +
> +/*****************************************************************************
>   * ParseChapters:
>   *****************************************************************************/
>  void matroska_segment_c::ParseChapters( KaxChapters *chapters )
> @@ -5441,7 +5513,8 @@
>          }
>          else if( MKV_IS_ID( el, KaxAttachments ) )
>          {
> -            msg_Dbg( &sys.demuxer, "|   + Attachments FIXME (but probably never supported)" );
> +            msg_Dbg( &sys.demuxer, "|   + Attachments" );
> +            ParseAttachments( static_cast<KaxAttachments*>( el ) );
>          }
>          else if( MKV_IS_ID( el, KaxChapters ) )
>          {
> Index: modules/misc/freetype.c
> ===================================================================
> --- modules/misc/freetype.c	(revision 20287)
> +++ modules/misc/freetype.c	(working copy)
> @@ -93,7 +93,7 @@
>  static char *FontConfig_Select( FcConfig *, const char *,
>                                  vlc_bool_t, vlc_bool_t, int * );
>  #endif
> -static line_desc_t *NewLine( byte_t * );
> +static line_desc_t *NewLine( int );
>  
>  static int SetFontSize( filter_t *, int );
>  
> @@ -218,6 +218,16 @@
>      font_stack_t  *p_next;
>  };
>  
> +typedef struct
> +{
> +    int         i_font_size;
> +    uint32_t    i_font_color; /* ARGB */
> +    vlc_bool_t  b_italic;
> +    vlc_bool_t  b_bold;
> +    vlc_bool_t  b_underline;
> +    char       *psz_fontname;
> +} ft_style_t;
> +
>  static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
>  static void FreeLines( line_desc_t * );
>  static void FreeLine( line_desc_t * );
> @@ -318,7 +328,7 @@
>          msg_Err( p_filter, "couldn't initialize freetype" );
>          goto error;
>      }
> -
> +retry:
>      i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
>                             0, &p_sys->p_face );
>      if( i_error == FT_Err_Unknown_File_Format )
> @@ -328,6 +338,19 @@
>      }
>      else if( i_error )
>      {
> +#ifdef HAVE_FONTCONFIG
> +        if( strcmp( psz_fontfile, DEFAULT_FONT ) == 0 )
> +        {
> +            int   i_idx = 0;
> +            if( psz_fontfile ) free( psz_fontfile );
> +            psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
> +                                              FC_DEFAULT_FONT,
> +                                              VLC_FALSE,
> +                                              VLC_FALSE,
> +                                              &i_idx );
> +            goto retry;
> +        }
> +#endif
>          msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
>          goto error;
>      }
> @@ -472,13 +495,11 @@
>  
>          if( p_line->i_width < i_width )
>          {
> -            if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
> -                ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
> +            if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
>              {
>                  i_align_offset = i_width - p_line->i_width;
>              }
> -            else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
> -                ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
> +            else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
>              {
>                  i_align_offset = ( i_width - p_line->i_width ) / 2;
>              }
> @@ -623,13 +644,11 @@
>  
>          if( p_line->i_width < i_width )
>          {
> -            if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
> -                ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
> +            if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
>              {
>                  i_align_offset = i_width - p_line->i_width;
>              }
> -            else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
> -                ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
> +            else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
>              {
>                  i_align_offset = ( i_width - p_line->i_width ) / 2;
>              }
> @@ -775,13 +794,11 @@
>  
>          if( p_line->i_width < i_width )
>          {
> -            if( ( p_region->p_style && p_region->p_style->i_text_align == SUBPICTURE_ALIGN_RIGHT ) ||
> -                ( !p_region->p_style && (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT ) )
> +            if( (p_region->i_align & 0x3) == SUBPICTURE_ALIGN_RIGHT )
>              {
>                  i_align_offset = i_width - p_line->i_width;
>              }
> -            else if( ( p_region->p_style && p_region->p_style->i_text_align != SUBPICTURE_ALIGN_LEFT ) ||
> -                ( !p_region->p_style && (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT ) )
> +            else if( (p_region->i_align & 0x3) != SUBPICTURE_ALIGN_LEFT )
>              {
>                  i_align_offset = ( i_width - p_line->i_width ) / 2;
>              }
> @@ -984,7 +1001,7 @@
>  
>      /* Calculate relative glyph positions and a bounding box for the
>       * entire string */
> -    if( !(p_line = NewLine( (byte_t *)psz_string )) )
> +    if( !(p_line = NewLine( strlen((char *)psz_string) )) )
>      {
>          msg_Err( p_filter, "out of memory" );
>          goto error;
> @@ -1008,7 +1025,7 @@
>          if( i_char == '\n' )
>          {
>              psz_line_start = psz_unicode;
> -            if( !(p_next = NewLine( (byte_t *)psz_string )) )
> +            if( !(p_next = NewLine( strlen((char *)psz_string) )) )
>              {
>                  msg_Err( p_filter, "out of memory" );
>                  goto error;
> @@ -1077,7 +1094,7 @@
>          {
>              p_line->pp_glyphs[ i ] = NULL;
>              FreeLine( p_line );
> -            p_line = NewLine( (byte_t *)psz_string );
> +            p_line = NewLine( strlen((char *)psz_string) );
>              if( p_prev ) p_prev->p_next = p_line;
>              else p_lines = p_line;
>  
> @@ -1123,6 +1140,8 @@
>  
>      p_region_out->i_x = p_region_in->i_x;
>      p_region_out->i_y = p_region_in->i_y;
> +    p_region_out->i_percent_x = p_region_in->i_percent_x;
> +    p_region_out->i_percent_y = p_region_in->i_percent_y;
>  
>      if( config_GetInt( p_filter, "freetype-yuvp" ) )
>          Render( p_filter, p_region_out, p_lines, result.x, result.y );
> @@ -1140,6 +1159,52 @@
>  }
>  
>  #ifdef HAVE_FONTCONFIG
> +static ft_style_t *CreateStyle( char *psz_fontname, int i_font_size, int i_font_color, int i_font_alpha,
> +                               vlc_bool_t b_bold, vlc_bool_t b_italic, vlc_bool_t b_uline )
> +{
> +    ft_style_t  *p_style = malloc( sizeof( ft_style_t ));
> +
> +    if( p_style )
> +    {
> +        p_style->i_font_size  = i_font_size;
> +        p_style->i_font_color = ( i_font_color & 0x00ffffff ) | (( i_font_alpha & 0xff ) << 24 );
> +        p_style->b_italic     = b_italic;
> +        p_style->b_bold       = b_bold;
> +        p_style->b_underline  = b_uline;
/* handle the case here when p_style->psz_fontname is already assigmed */
if( p_style->psz_fontname ) free( p_style->psz_fontname );
> +        p_style->psz_fontname = strdup( psz_fontname );
> +    }
> +    return p_style;
> +}
> +
> +static void DeleteStyle( ft_style_t *p_style )
> +{
> +    if( p_style )
> +    {
> +        if( p_style->psz_fontname )
> +            free( p_style->psz_fontname );
> +        free( p_style );
> +    }
> +}
> +
> +static vlc_bool_t StyleEquals( ft_style_t *s1, ft_style_t *s2 )
> +{
> +    if( !s1 || !s2 )
> +        return VLC_FALSE;
> +    if( s1 == s2 )
> +        return VLC_TRUE;
> +
> +    if(( s1->i_font_size  == s2->i_font_size ) &&
> +       ( s1->i_font_color == s2->i_font_color ) &&
> +       ( s1->b_italic     == s2->b_italic ) &&
> +       ( s1->b_bold       == s2->b_bold ) &&
> +       ( s1->b_underline  == s2->b_underline ) &&
> +       ( !strcmp( s1->psz_fontname, s2->psz_fontname )))
> +    {
> +        return VLC_TRUE;
> +    }
> +    return VLC_FALSE;
> +}
> +
>  static int PushFont( font_stack_t **p_font, const char *psz_name, int i_size,
>                       int i_color, int i_alpha )
>  {
> @@ -1225,72 +1290,68 @@
>      return VLC_SUCCESS;
>  }
>  
> -static uint32_t *IconvText( filter_t *p_filter, char *psz_string )
> +static void IconvText( filter_t *p_filter, const char *psz_string, uint32_t *i_string_length, uint32_t **ppsz_unicode )
>  {
>      vlc_iconv_t iconv_handle = (vlc_iconv_t)(-1);
> -    uint32_t *psz_unicode;
> -    int i_string_length;
>  
> -    psz_unicode =
> -        malloc( ( strlen( psz_string ) + 1 ) * sizeof( uint32_t ) );
> -    if( psz_unicode == NULL )
> +    if( !*ppsz_unicode )
> +        *ppsz_unicode = (uint32_t *) calloc( strlen( psz_string ) + 1, sizeof( uint32_t ));
> +
/* handle *ppsz_unicode == NULL here */

> +    if( *ppsz_unicode )
>      {
> -        msg_Err( p_filter, "out of memory" );
> -        return NULL;;
> -    }
>  #if defined(WORDS_BIGENDIAN)
> -    iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
> +        iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
>  #else
> -    iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
> +        iconv_handle = vlc_iconv_open( "UCS-4LE", "UTF-8" );
>  #endif
> -    if( iconv_handle == (vlc_iconv_t)-1 )
> -    {
> -        msg_Warn( p_filter, "unable to do conversion" );
> -        free( psz_unicode );
> -        return NULL;;
> -    }
> +        if( iconv_handle != (vlc_iconv_t)-1 )
> +        {
> +            char *p_in_buffer, *p_out_buffer;
> +            size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
> +            i_in_bytes = strlen( psz_string );
> +            i_out_bytes = i_in_bytes * sizeof( uint32_t );
> +            i_out_bytes_left = i_out_bytes;
> +            p_in_buffer = (char *) psz_string;
> +            p_out_buffer = (char *) *ppsz_unicode;
> +            i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer, &i_in_bytes,
> +                               &p_out_buffer, &i_out_bytes_left );
>  
> -    {
> -        char *p_in_buffer, *p_out_buffer;
> -        size_t i_in_bytes, i_out_bytes, i_out_bytes_left, i_ret;
> -        i_in_bytes = strlen( psz_string );
> -        i_out_bytes = i_in_bytes * sizeof( uint32_t );
> -        i_out_bytes_left = i_out_bytes;
> -        p_in_buffer = psz_string;
> -        p_out_buffer = (char *)psz_unicode;
> -        i_ret = vlc_iconv( iconv_handle, (const char**)&p_in_buffer, &i_in_bytes,
> -                           &p_out_buffer, &i_out_bytes_left );
> +            vlc_iconv_close( iconv_handle );
>  
> -        vlc_iconv_close( iconv_handle );
> -
> -        if( i_in_bytes )
> +            if( i_in_bytes )
> +            {
> +                msg_Warn( p_filter, "failed to convert string to unicode (%s), "
> +                          "bytes left %d", strerror(errno), (int)i_in_bytes );
> +            }
> +            else
> +            {
> +                *(uint32_t*)p_out_buffer = 0;
> +                *i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
> +            }
> +        }
> +        else
>          {
> -            msg_Warn( p_filter, "failed to convert string to unicode (%s), "
> -                      "bytes left %d", strerror(errno), (int)i_in_bytes );
> -            free( psz_unicode );
> -            return NULL;;
> +            msg_Warn( p_filter, "unable to do conversion" );
>          }
> -        *(uint32_t*)p_out_buffer = 0;
> -        i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
>      }
> +}
>  
> -#if defined(HAVE_FRIBIDI)
> -    {
> -        uint32_t *p_fribidi_string;
> +static ft_style_t *GetStyleFromFontStack( filter_sys_t *p_sys, font_stack_t **p_fonts,
> +                              vlc_bool_t b_bold, vlc_bool_t b_italic, vlc_bool_t b_uline )
> +{
> +    ft_style_t   *p_style = NULL;
>  
> -        p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
> +    char  *psz_fontname = NULL;
> +    int    i_font_color = p_sys->i_font_color;
> +    int    i_font_alpha = 0;
> +    int    i_font_size  = p_sys->i_font_size;
>  
> -        /* Do bidi conversion line-by-line */
> -        FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
> -        fribidi_log2vis((FriBidiChar*)psz_unicode, i_string_length,
> -                        &base_dir, (FriBidiChar*)p_fribidi_string, 0, 0, 0);
> -
> -        free( psz_unicode );
> -        psz_unicode = p_fribidi_string;
> -        p_fribidi_string[ i_string_length ] = 0;
> +    if( VLC_SUCCESS == PeekFont( p_fonts, &psz_fontname, &i_font_size, &i_font_color, &i_font_alpha ) )
> +    {
> +        p_style = CreateStyle( psz_fontname, i_font_size, i_font_color, i_font_alpha,
> +                               b_bold, b_italic, b_uline );
>      }
> -#endif
> -    return psz_unicode;
> +    return p_style;
>  }
>  
>  static int RenderTag( filter_t *p_filter, FT_Face p_face, int i_font_color,
> @@ -1301,6 +1362,7 @@
>      FT_BBox      line;
>      int          i_yMin, i_yMax;
>      int          i;
> +    vlc_bool_t   b_first_on_line = VLC_TRUE;
>  
>      int          i_previous = 0;
>      int          i_pen_x_start = *pi_pen_x;
> @@ -1324,7 +1386,10 @@
>      i_yMin = line.yMin;
>      i_yMax = line.yMax;
>  
> -    while( *psz_unicode && ( *psz_unicode != 0xffff ) )
> +    if( line.xMax > 0 )
> +        b_first_on_line = VLC_FALSE;
> +
> +    while( *psz_unicode && ( *psz_unicode != '\n' ) )
>      {
>          FT_BBox glyph_size;
>          FT_Glyph tmp_glyph;
> @@ -1389,13 +1454,27 @@
>              }
>              if( psz_unicode == psz_unicode_start )
>              {
> -                msg_Warn( p_filter, "unbreakable string" );
> +                if( b_first_on_line )
> +                {
> +                    msg_Warn( p_filter, "unbreakable string" );
> +                    p_line->pp_glyphs[ i ] = NULL;
> +                    return VLC_EGENERIC;
> +                }
> +                *pi_pen_x = i_pen_x_start;
> +
> +                p_line->i_width = line.xMax;
> +                p_line->i_height = __MAX( p_line->i_height, p_face->size->metrics.height >> 6 );
>                  p_line->pp_glyphs[ i ] = NULL;
> -                return VLC_EGENERIC;
> +
> +                p_result->x = __MAX( p_result->x, line.xMax );
> +                p_result->y = __MAX( p_result->y, __MAX( p_line->i_height, i_yMax - i_yMin ) );
> +
> +                *pi_start = i;
> +                return VLC_SUCCESS;
>              }
>              else
>              {
> -                *psz_unicode = 0xffff;
> +                *psz_unicode = '\n';
>              }
>              psz_unicode = psz_unicode_start;
>              *pi_pen_x = i_pen_x_start;
> @@ -1440,31 +1519,21 @@
>      return VLC_SUCCESS;
>  }
>  
> -static int ProcessNodes( filter_t *p_filter, xml_reader_t *p_xml_reader, char *psz_html, text_style_t *p_font_style, line_desc_t  **p_lines, FT_Vector *p_result )
> +static void ProcessNodes( filter_t *p_filter, xml_reader_t *p_xml_reader,
> +                          text_style_t *p_font_style, uint32_t *psz_text, int *pi_len,
> +                          uint32_t *pi_runs, uint32_t **ppi_run_lengths,
> +                          ft_style_t ***ppp_styles)
>  {
> -    filter_sys_t *p_sys = p_filter->p_sys;
> +    filter_sys_t *p_sys          = p_filter->p_sys;
> +    uint32_t     *psz_text_orig  = psz_text;
> +    font_stack_t *p_fonts        = NULL;
>  
> -    FT_Vector tmp_result;
> +    char *psz_node  = NULL;
>  
> -    font_stack_t *p_fonts = NULL;
>      vlc_bool_t b_italic = VLC_FALSE;
>      vlc_bool_t b_bold   = VLC_FALSE;
>      vlc_bool_t b_uline  = VLC_FALSE;
>  
> -    line_desc_t *p_line = NULL;
> -    line_desc_t *p_prev = NULL;
> -
> -    char *psz_node  = NULL;
> -
> -    int i_pen_x = 0;
> -    int i_pen_y = 0;
> -    int i_posn  = 0;
> -
> -    int rv = VLC_SUCCESS;
> -
> -    p_result->x = p_result->y = 0;
> -    tmp_result.x = tmp_result.y = 0;
> -
>      if( p_font_style )
>      {
>          PushFont( &p_fonts,
> @@ -1485,7 +1554,7 @@
>          PushFont( &p_fonts, FC_DEFAULT_FONT, p_sys->i_font_size, 0xffffff, 0 );
>      }
>  
> -    while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) && ( rv == VLC_SUCCESS ) )
> +    while ( ( xml_ReaderRead( p_xml_reader ) == 1 ) )
>      {
>          switch ( xml_ReaderNodeType( p_xml_reader ) )
>          {
> @@ -1515,9 +1584,9 @@
>                      if( !strcasecmp( "font", psz_node ) )
>                      {
>                          char *psz_fontname = NULL;
> -                        int  i_font_color = 0xffffff;
> -                        int  i_font_alpha = 0;
> -                        int  i_font_size  = 24;
> +                        int   i_font_color = 0xffffff;
> +                        int   i_font_alpha = 0;
> +                        int   i_font_size  = 24;
>  
>                          /* Default all attributes to the top font in the stack -- in case not
>                           * all attributes are specified in the sub-font
> @@ -1541,7 +1610,10 @@
>                                  }
>                                  else if( !strcasecmp( "size", psz_name ) )
>                                  {
> -                                    i_font_size = atoi( psz_value );
> +                                    if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
> +                                        i_font_size += atoi( psz_value );
> +                                    else
> +                                        i_font_size = atoi( psz_value );
>                                  }
>                                  else if( !strcasecmp( "color", psz_name )  &&
>                                           ( psz_value[0] == '#' ) )
> @@ -1576,28 +1648,26 @@
>                      }
>                      else if( !strcasecmp( "br", psz_node ) )
>                      {
> -                        if( p_line )
> -                        {
> -                            p_prev = p_line;
> -                            if( !(p_line = NewLine( (byte_t *)psz_html )) )
> -                            {
> -                                msg_Err( p_filter, "out of memory" );
> -                                free( psz_node );
> -                                rv = VLC_EGENERIC;
> -                                break;
> -                            }
> -                            p_line->b_new_color_mode = VLC_TRUE;
> -                            p_result->x = __MAX( p_result->x, tmp_result.x );
> -                            p_result->y += tmp_result.y;
> +                        uint32_t i_string_length;
>  
> -                            p_line->p_next = NULL;
> -                            i_pen_x = 0;
> -                            i_pen_y += tmp_result.y;
> -                            i_posn = 0;
> -                            p_prev->p_next = p_line;
> -                            tmp_result.x = 0;
> -                            tmp_result.y = 0;
> -                        }
> +                        IconvText( p_filter, "\n", &i_string_length, &psz_text );
> +                        psz_text += i_string_length;
> +
> +                        (*pi_runs)++;
> +
> +                        if( *ppp_styles )
> +                            *ppp_styles = (ft_style_t **) realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t * ) );
> +                        else
> +                            *ppp_styles = (ft_style_t **) malloc( *pi_runs * sizeof( ft_style_t * ) );
> +
Check for *pp_styles being NULL and handle the error if it does.

> +                        (*ppp_styles)[ *pi_runs - 1 ] = GetStyleFromFontStack( p_sys, &p_fonts, b_bold, b_italic, b_uline );
> +
Looks dangerous to me. Check if pi_runs is within the memory area of 
pps_styles.

> +                        if( *ppi_run_lengths )
> +                            *ppi_run_lengths = (uint32_t *) realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
> +                        else
> +                            *ppi_run_lengths = (uint32_t *) malloc( *pi_runs * sizeof( uint32_t ) );
> +
> +                        (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
same remarks here.

>                      }
>                      free( psz_node );
>                  }
> @@ -1606,119 +1676,246 @@
>                  psz_node = xml_ReaderValue( p_xml_reader );
>                  if( psz_node )
>                  {
> -                    char *psz_fontname = NULL;
> -                    int  i_font_color = 0xffffff;
> -                    int  i_font_alpha = 0;
> -                    int  i_font_size  = 24;
> -                    FT_Face p_face = NULL;
> +                    uint32_t i_string_length;
>  
> -                    if( VLC_SUCCESS == PeekFont( &p_fonts, &psz_fontname, &i_font_size, &i_font_color, &i_font_alpha ) )
> +                    /* Turn any multiple-whitespaces into single spaces */
> +                    char *s = strpbrk( psz_node, "\t\r\n " );
> +                    while( s )
>                      {
> -                        int            i_idx = 0;
> -                        char *psz_fontfile = FontConfig_Select( p_sys->p_fontconfig, psz_fontname, b_bold, b_italic, &i_idx );
> +                        int i_whitespace = strspn( s, "\t\r\n " );
>  
> -                        if( psz_fontfile )
> -                        {
> -                            if( FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
> -                                             i_idx, &p_face ) )
> -                            {
> -                                free( psz_fontfile );
> -                                free( psz_node );
> -                                rv = VLC_EGENERIC;
> -                                break;
> -                            }
> -                            free( psz_fontfile );
> -                        }
> -                    }
> +                        if( i_whitespace > 1 )
> +                            memcpy( s + 1,
> +                                    s + i_whitespace,
> +                                    strlen( s ) - i_whitespace + 1 );
> +                        *s = ' ';
>  
> -                    if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face, ft_encoding_unicode ) ||
> -                        FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0, i_font_size ) )
> -                    {
> -                        free( psz_node );
> -                        rv = VLC_EGENERIC;
> -                        break;
> +                        s = strpbrk( s+1, "\t\r\n " );
>                      }
> -                    p_sys->i_use_kerning = FT_HAS_KERNING( ( p_face  ? p_face : p_sys->p_face ) );
>  
> -                    uint32_t *psz_unicode = IconvText( p_filter, psz_node );
> +                    IconvText( p_filter, psz_node, &i_string_length, &psz_text );
> +                    psz_text += i_string_length;
>  
> -                    if( !psz_unicode )
> -                    {
> -                        free( psz_node );
> -                        if( p_face ) FT_Done_Face( p_face );
> -                        rv = VLC_EGENERIC;
> -                        break;
> -                    }
> +                    (*pi_runs)++;
>  
> -                    while( *psz_unicode )
> -                    {
> -                        if( !p_line )
> -                        {
> -                            if( !(p_line = NewLine( (byte_t *)psz_html )) )
> -                            {
> -                                msg_Err( p_filter, "out of memory" );
> -                                free( psz_node );
> -                                if( p_face ) FT_Done_Face( p_face );
> -                                rv = VLC_EGENERIC;
> -                                break;
> -                            }
> -                            /* New Color mode only works in YUVA rendering mode --
> -                             * (RGB mode has palette constraints on it). We therefore
> -                             * need to populate the legacy colour fields also.
> -                             */
> -                            p_line->b_new_color_mode = VLC_TRUE;
> -                            p_line->i_alpha = i_font_alpha;
> -                            p_line->i_red   = ( i_font_color & 0xff0000 ) >> 16;
> -                            p_line->i_green = ( i_font_color & 0x00ff00 ) >>  8;
> -                            p_line->i_blue  = ( i_font_color & 0x0000ff );
> -                            p_line->p_next = NULL;
> -                            i_pen_x = 0;
> -                            i_pen_y += tmp_result.y;
> -                            tmp_result.x = 0;
> -                            tmp_result.y = 0;
> -                            i_posn = 0;
> -                            if( p_prev ) p_prev->p_next = p_line;
> -                            else *p_lines = p_line;
> -                        }
> +                    if( *ppp_styles )
> +                        *ppp_styles = (ft_style_t **) realloc( *ppp_styles, *pi_runs * sizeof( ft_style_t *) );
> +                    else
> +                        *ppp_styles = (ft_style_t **) malloc( *pi_runs * sizeof( ft_style_t  *) );
handle the error case here, if (*ppp_styles == NULL) handle error;
>  
> -                        if( RenderTag( p_filter, p_face, i_font_color, b_uline, p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn, &tmp_result ) != VLC_SUCCESS )
> -                        {
> -                            free( psz_node );
> -                            if( p_face ) FT_Done_Face( p_face );
> -                            rv = VLC_EGENERIC;
> -                            break;
> -                        }
> -                        if( *psz_unicode )
> -                        {
> -                            p_result->x = __MAX( p_result->x, tmp_result.x );
> -                            p_result->y += tmp_result.y;
> +                    (*ppp_styles)[ *pi_runs - 1 ] = GetStyleFromFontStack( p_sys, &p_fonts, b_bold, b_italic, b_uline );
is pi_runs < length_of(ppp_styles)???
>  
> -                            p_prev = p_line;
> -                            p_line = NULL;
> -                        }
> -                    }
> -                    if( rv != VLC_SUCCESS ) break;
> +                    if( *ppi_run_lengths )
> +                        *ppi_run_lengths = (uint32_t *) realloc( *ppi_run_lengths, *pi_runs * sizeof( uint32_t ) );
> +                    else
> +                        *ppi_run_lengths = (uint32_t *) malloc( *pi_runs * sizeof( uint32_t ) );
>  
handle error if pointer is NULL
> -                    if( p_face ) FT_Done_Face( p_face );
> -                    free( psz_unicode );
> +                    (*ppi_run_lengths)[ *pi_runs - 1 ] = i_string_length;
> +
check your memory area is pi_runs inside of it?
>                      free( psz_node );
>                  }
>                  break;
>          }
>      }
> +
> +    *pi_len = psz_text - psz_text_orig;
> +
> +    while( VLC_SUCCESS == PopFont( &p_fonts ) );
> +}
> +
> +static int ProcessLines( filter_t *p_filter, uint32_t *psz_text,
> +                         int i_len, uint32_t i_runs,
> +                         uint32_t *pi_run_lengths, ft_style_t **pp_styles,
> +                         line_desc_t **pp_lines, FT_Vector *p_result )
> +{
> +    filter_sys_t   *p_sys = p_filter->p_sys;
> +    ft_style_t     **pp_char_styles;
> +    uint32_t        i, j, k;
> +    int             i_prev;
> +
> +    /* Assign each character in the text string its style explicitly, so that
> +     * after the characters have been shuffled around by Fribidi, we can re-apply
> +     * the styles, and to simplify the calculation of runs within a line.
> +     */
> +    pp_char_styles = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
what if pp_char_styles is NULL ??

> +    i = 0;
> +    for( j = 0; j < i_runs; j++ )
> +        for( k = 0; k < pi_run_lengths[ j ]; k++ )
> +            pp_char_styles[ i++ ] = pp_styles[ j ];
> +
> +#if defined(HAVE_FRIBIDI)
> +    {
> +        ft_style_t  **pp_char_styles_new;
> +        int         *p_positions;
> +        uint32_t    *p_fribidi_string;
> +        int start_pos, pos = 0;
> +
> +        p_fribidi_string = malloc( (i_len + 1) * sizeof(uint32_t) );
> +        pp_char_styles_new  = (ft_style_t **) malloc( i_len * sizeof( ft_style_t * ));
> +        p_positions = (int *) malloc( (i_len + 1) * sizeof( int ));

what if malloc returns NULL??
> +
> +        /* Do bidi conversion line-by-line */
> +        while(pos < i_len)
> +        {
> +            while(pos < i_len) {
> +                if (psz_text[pos] != '\n')
> +                    break;
> +                p_fribidi_string[pos] = psz_text[pos];
> +                pp_char_styles_new[pos] = pp_char_styles[pos];
> +                ++pos;
> +            }
> +            start_pos = pos;
> +            while(pos < i_len) {
> +                if (psz_text[pos] == '\n')
> +                    break;
> +                ++pos;
> +            }
> +            if (pos > start_pos)
> +            {
> +                FriBidiCharType base_dir = FRIBIDI_TYPE_LTR;
> +                fribidi_log2vis((FriBidiChar*)psz_text + start_pos, pos - start_pos,
> +                                &base_dir, (FriBidiChar*)p_fribidi_string + start_pos, 0, p_positions, 0);
> +
> +                for( j = start_pos; j < pos; j++ )
> +                {
> +                    pp_char_styles_new[ j ] = pp_char_styles[ start_pos + p_positions[ j - start_pos ] ];
> +                }
> +            }
> +        }
> +        free( p_positions );
> +        free( pp_char_styles );
> +        pp_char_styles = pp_char_styles_new;
> +        psz_text = p_fribidi_string;
> +        p_fribidi_string[ i_len ] = 0;
> +    }
> +#endif
> +    FT_Vector tmp_result;
> +
> +    line_desc_t *p_line = NULL;
> +    line_desc_t *p_prev = NULL;
> +
> +    int i_pen_x = 0;
> +    int i_pen_y = 0;
> +    int i_posn  = 0;
> +
> +    p_result->x = p_result->y = 0;
> +    tmp_result.x = tmp_result.y = 0;
> +
> +    i_prev = 0;
> +    for( k = 0; k <= i_len; k++ )
> +    {
> +        if(( k == i_len ) ||
> +          (( k > 0 ) && !StyleEquals( pp_char_styles[ k ], pp_char_styles[ k - 1] )))
> +        {
> +            ft_style_t *p_style = pp_char_styles[ k - 1 ];
> +
> +            /* End of the current style run */
> +            FT_Face p_face = NULL;
> +            int      i_idx = 0;
> +            char *psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
> +                                                    p_style->psz_fontname,
> +                                                    p_style->b_bold,
> +                                                    p_style->b_italic,
> +                                                    &i_idx );
> +            if( psz_fontfile )
> +            {
> +                if( FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "", i_idx, &p_face ) )
> +                {
> +                    free( psz_fontfile );
> +                    free( pp_char_styles );
> +#if defined(HAVE_FRIBIDI)
> +                    free( psz_text );
> +#endif
> +                    return VLC_EGENERIC;
> +                }
> +                free( psz_fontfile );
> +            }
> +
> +            if( FT_Select_Charmap( p_face ? p_face : p_sys->p_face, ft_encoding_unicode ) ||
> +                FT_Set_Pixel_Sizes( p_face ? p_face : p_sys->p_face, 0, p_style->i_font_size ) )
> +            {
> +                if( p_face ) FT_Done_Face( p_face );
> +                free( pp_char_styles );
> +#if defined(HAVE_FRIBIDI)
> +                free( psz_text );
> +#endif
> +                return VLC_EGENERIC;
> +            }
> +            p_sys->i_use_kerning = FT_HAS_KERNING( ( p_face  ? p_face : p_sys->p_face ) );
> +
> +
> +            uint32_t *psz_unicode = (uint32_t *) calloc( k - i_prev + 1, sizeof( uint32_t ));
> +            memcpy( psz_unicode, psz_text + i_prev, sizeof( uint32_t ) * ( k - i_prev ) );
> +            psz_unicode[ k - i_prev ] = 0;
> +            while( *psz_unicode )
> +            {
> +                if( !p_line )
> +                {
> +                    if( !(p_line = NewLine( i_len - i_prev)) )
> +                    {
> +                        msg_Err( p_filter, "out of memory" );
> +                        if( p_face ) FT_Done_Face( p_face );
> +                        free( pp_char_styles );
> +                        free( psz_unicode );
> +#if defined(HAVE_FRIBIDI)
> +                        free( psz_text );
> +#endif
> +                        return VLC_EGENERIC;
> +                    }
> +                    /* New Color mode only works in YUVA rendering mode --
> +                     * (RGB mode has palette constraints on it). We therefore
> +                     * need to populate the legacy colour fields also.
> +                     */
> +                    p_line->b_new_color_mode = VLC_TRUE;
> +                    p_line->i_alpha = ( p_style->i_font_color & 0xff000000 ) >> 24;
> +                    p_line->i_red   = ( p_style->i_font_color & 0x00ff0000 ) >> 16;
> +                    p_line->i_green = ( p_style->i_font_color & 0x0000ff00 ) >>  8;
> +                    p_line->i_blue  = ( p_style->i_font_color & 0x000000ff );
> +                    p_line->p_next = NULL;
> +                    i_pen_x = 0;
> +                    i_pen_y += tmp_result.y;
> +                    tmp_result.x = 0;
> +                    tmp_result.y = 0;
> +                    i_posn = 0;
> +                    if( p_prev ) p_prev->p_next = p_line;
> +                    else *pp_lines = p_line;
> +                }
> +                if( RenderTag( p_filter, p_face ? p_face : p_sys->p_face, p_style->i_font_color, p_style->b_underline, p_line, psz_unicode, &i_pen_x, i_pen_y, &i_posn, &tmp_result ) != VLC_SUCCESS )
> +                {
> +                    if( p_face ) FT_Done_Face( p_face );
> +                    free( pp_char_styles );
> +                    free( psz_unicode );
> +#if defined(HAVE_FRIBIDI)
> +                    free( psz_text );
> +#endif
> +                    return VLC_EGENERIC;
> +                }
> +                if( *psz_unicode )
> +                {
> +                    p_result->x = __MAX( p_result->x, tmp_result.x );
> +                    p_result->y += tmp_result.y;
> +
> +                    p_prev = p_line;
> +                    p_line = NULL;
> +                }
> +            }
> +            free( psz_unicode );
> +            if( p_face ) FT_Done_Face( p_face );
> +            i_prev = k;
> +        }
> +    }
> +    free( pp_char_styles );
> +#if defined(HAVE_FRIBIDI)
> +    free( psz_text );
> +#endif
> +
>      if( p_line )
>      {
>          p_result->x = __MAX( p_result->x, tmp_result.x );
>          p_result->y += tmp_result.y;
>      }
> -
> -
> -    while( VLC_SUCCESS == PopFont( &p_fonts ) );
> -
> -    return rv;
>  }
>  
> -
>  static int RenderHtml( filter_t *p_filter, subpicture_region_t *p_region_out,
>                         subpicture_region_t *p_region_in )
>  {
> @@ -1742,16 +1939,35 @@
>              p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
>              if( p_xml_reader )
>              {
> +                uint32_t   *psz_text;
> +                int         i_len;
> +                uint32_t    i_runs = 0;
> +                uint32_t   *pi_run_lengths = NULL;
> +                ft_style_t  **pp_styles = NULL;
>                  FT_Vector    result;
>                  line_desc_t  *p_lines = NULL;
>  
> -                rv = ProcessNodes( p_filter, p_xml_reader, p_region_in->psz_html, p_region_in->p_style, &p_lines, &result );
> +                psz_text = (uint32_t *) calloc( strlen( p_region_in->psz_html ), sizeof( uint32_t ) );
> +                if( psz_text )
> +                {
> +                    uint32_t k;
>  
> -                if( rv == VLC_SUCCESS )
> -                {
> +                    ProcessNodes( p_filter, p_xml_reader, p_region_in->p_style, psz_text,
> +                                  &i_len, &i_runs, &pi_run_lengths, &pp_styles );
> +
>                      p_region_out->i_x = p_region_in->i_x;
>                      p_region_out->i_y = p_region_in->i_y;
> +                    p_region_out->i_percent_x = p_region_in->i_percent_x;
> +                    p_region_out->i_percent_y = p_region_in->i_percent_y;
>  
> +                    ProcessLines( p_filter, psz_text, i_len, i_runs, pi_run_lengths, pp_styles, &p_lines, &result );
> +
> +                    for( k=0; k<i_runs; k++)
> +                        DeleteStyle( pp_styles[k] );
> +                    free( pp_styles );
> +                    free( pi_run_lengths );
> +                    free( psz_text );
> +
>                      if( config_GetInt( p_filter, "freetype-yuvp" ) )
>                          Render( p_filter, p_region_out, p_lines, result.x, result.y );
>                      else
> @@ -1858,9 +2074,8 @@
>      }
>  }
>  
> -static line_desc_t *NewLine( byte_t *psz_string )
> +static line_desc_t *NewLine( int i_count )
>  {
> -    int i_count;
>      line_desc_t *p_line = malloc( sizeof(line_desc_t) );
>  
>      if( !p_line ) return NULL;
> @@ -1868,15 +2083,11 @@
>      p_line->i_width = 0;
>      p_line->p_next = NULL;
>  
> -    /* We don't use CountUtf8Characters() here because we are not acutally
> -     * sure the string is utf8. Better be safe than sorry. */
> -    i_count = strlen( (char *)psz_string );
> -
>      p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph) * ( i_count + 1 ) );
> -    p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * i_count + 1 );
> -    p_line->p_rgb = malloc( sizeof( uint32_t ) * i_count + 1 );
> -    p_line->pi_underline_offset = calloc( i_count+ + 1, sizeof( uint16_t ) );
> -    p_line->pi_underline_thickness = calloc( i_count+ + 1, sizeof( uint16_t ) );
> +    p_line->p_glyph_pos = malloc( sizeof( FT_Vector ) * ( i_count + 1 ) );
> +    p_line->p_rgb = malloc( sizeof( uint32_t ) * ( i_count + 1 ) );
> +    p_line->pi_underline_offset = calloc( i_count + 1, sizeof( uint16_t ) );
> +    p_line->pi_underline_thickness = calloc( i_count + 1, sizeof( uint16_t ) );
>      if( ( p_line->pp_glyphs == NULL ) ||
>          ( p_line->p_glyph_pos == NULL ) ||
>          ( p_line->p_rgb == NULL ) ||
> Index: modules/misc/quartztext.c
> ===================================================================
> --- modules/misc/quartztext.c	(revision 20287)
> +++ modules/misc/quartztext.c	(working copy)
> @@ -294,6 +294,8 @@
>  
>      p_region_out->i_x = p_region_in->i_x;
>      p_region_out->i_y = p_region_in->i_y;
> +    p_region_out->i_percent_x = p_region_in->i_percent_x;
> +    p_region_out->i_percent_y = p_region_in->i_percent_y;
>  
>      if( psz_utf16_str != NULL )
>      {
> @@ -559,7 +561,10 @@
>                                  }
>                                  else if( !strcasecmp( "size", psz_name ) )
>                                  {
> -                                    i_font_size = atoi( psz_value );
> +                                    if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
> +                                        i_font_size += atoi( psz_value );
> +                                    else
> +                                        i_font_size = atoi( psz_value );
>                                  }
>                                  else if( !strcasecmp( "color", psz_name )  &&
>                                           ( psz_value[0] == '#' ) )
> @@ -624,6 +629,21 @@
>                  {
>                      uint32_t i_string_length;
>  
> +                    // Turn any multiple-whitespaces into single spaces
> +                    char *s = strpbrk( psz_node, "\t\r\n " );
> +                    while( s )
> +                    {
> +                        int i_whitespace = strspn( s, "\t\r\n " );
> +
> +                        if( i_whitespace > 1 )
> +                            memcpy( s + 1,
> +                                    s + i_whitespace,
> +                                    strlen( s ) - i_whitespace + 1 );
> +                        *s = ' ';
> +
> +                        s = strpbrk( s+1, "\t\r\n " );
> +                    }
> +
>                      ConvertToUTF16( psz_node, &i_string_length, &psz_text );
>                      psz_text += i_string_length;
>  
> @@ -693,6 +713,8 @@
>  
>                      p_region_out->i_x = p_region_in->i_x;
>                      p_region_out->i_y = p_region_in->i_y;
> +                    p_region_out->i_percent_x = p_region_in->i_percent_x;
> +                    p_region_out->i_percent_y = p_region_in->i_percent_y;
>  
>                      RenderYUVA( p_filter, p_region_out, psz_text, i_len, i_runs, pi_run_lengths, pp_styles);
>  
> @@ -881,13 +903,8 @@
>  
>      if( psz_utf16_str != NULL )
>      {
> -        int i_text_align = 0;
> +        int i_text_align = p_region->i_align & 0x3;
>  
> -        if( p_region->p_style )
> -            i_text_align = p_region->p_style->i_text_align;
> -        else
> -            i_text_align = p_region->i_align & 0x3;
> -
>          p_offScreen = Compose( i_text_align, psz_utf16_str, i_text_len,
>                                 i_runs, pi_run_lengths, pp_styles,
>                                 i_width, i_height, &i_textblock_height );
> Index: modules/video_filter/rss.c
> ===================================================================
> --- modules/video_filter/rss.c	(revision 20287)
> +++ modules/video_filter/rss.c	(working copy)
> @@ -482,14 +482,14 @@
>      /*  where to locate the string: */
>      if( p_sys->i_xoff < 0 || p_sys->i_yoff < 0 )
>      {   /* set to one of the 9 relative locations */
> -        p_spu->i_flags = p_sys->i_pos;
> +        p_spu->p_region->i_align = p_sys->i_pos;
>          p_spu->i_x = 0;
>          p_spu->i_y = 0;
>          p_spu->b_absolute = VLC_FALSE;
>      }
>      else
>      {   /*  set to an absolute xy, referenced to upper left corner */
> -        p_spu->i_flags = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
> +        p_spu->p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
>          p_spu->i_x = p_sys->i_xoff;
>          p_spu->i_y = p_sys->i_yoff;
>          p_spu->b_absolute = VLC_TRUE;
> Index: modules/video_filter/osdmenu.c
> ===================================================================
> --- modules/video_filter/osdmenu.c	(revision 20287)
> +++ modules/video_filter/osdmenu.c	(working copy)
> @@ -406,7 +406,6 @@
>      p_spu->b_ephemer = VLC_TRUE;
>      p_spu->b_fade = VLC_TRUE;    
>      p_spu->b_absolute = p_sys->b_absolute;
> -    p_spu->i_flags = p_sys->position;
>  
>      /* Determine the duration of the subpicture */
>      if( p_sys->i_end_date > 0 )
> @@ -439,6 +438,7 @@
>              NULL );
>  
>          /* proper positioning of OSD menu image */
> +        p_region->i_align = p_sys->position;
>          p_spu->i_x = p_filter->p_sys->p_menu->p_state->i_x;
>          p_spu->i_y = p_filter->p_sys->p_menu->p_state->i_y;
>          p_spu->p_region = p_region;
> @@ -458,6 +458,7 @@
>  #endif
>  
>      /* proper positioning of OSD menu image */
> +    p_region->i_align = p_sys->position;
>      p_spu->i_x = p_filter->p_sys->p_menu->p_state->i_x;
>      p_spu->i_y = p_filter->p_sys->p_menu->p_state->i_y;
>      p_spu->p_region = p_region;
> Index: modules/video_filter/logo.c
> ===================================================================
> --- modules/video_filter/logo.c	(revision 20287)
> +++ modules/video_filter/logo.c	(working copy)
> @@ -882,14 +882,14 @@
>      /*  where to locate the logo: */
>      if( p_sys->posx < 0 || p_sys->posy < 0 )
>      {   /* set to one of the 9 relative locations */
> -        p_spu->i_flags = p_sys->pos;
> +        p_spu->p_region->i_align = p_sys->pos;
>          p_spu->i_x = 0;
>          p_spu->i_y = 0;
>          p_spu->b_absolute = VLC_FALSE;
>      }
>      else
>      {   /*  set to an absolute xy, referenced to upper left corner */
> -        p_spu->i_flags = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
> +        p_spu->p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
>          p_spu->i_x = p_sys->posx;
>          p_spu->i_y = p_sys->posy;
>          p_spu->b_absolute = VLC_TRUE;
> Index: modules/video_filter/marq.c
> ===================================================================
> --- modules/video_filter/marq.c	(revision 20287)
> +++ modules/video_filter/marq.c	(working copy)
> @@ -296,14 +296,14 @@
>      /*  where to locate the string: */
>      if( p_sys->i_xoff < 0 || p_sys->i_yoff < 0 )
>      {   /* set to one of the 9 relative locations */
> -        p_spu->i_flags = p_sys->i_pos;
> +        p_spu->p_region->i_align = p_sys->i_pos;
>          p_spu->i_x = 0;
>          p_spu->i_y = 0;
>          p_spu->b_absolute = VLC_FALSE;
>      }
>      else
>      {   /*  set to an absolute xy, referenced to upper left corner */
> -        p_spu->i_flags = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
> +        p_spu->p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
>          p_spu->i_x = p_sys->i_xoff;
>          p_spu->i_y = p_sys->i_yoff;
>          p_spu->b_absolute = VLC_TRUE;
> Index: modules/video_filter/mosaic.c
> ===================================================================
> --- modules/video_filter/mosaic.c	(revision 20287)
> +++ modules/video_filter/mosaic.c	(working copy)
> @@ -710,6 +710,7 @@
>                      + ( i_row * p_sys->i_borderh ) / p_sys->i_rows
>                      + ( row_inner_height - fmt_out.i_height ) / 2;
>          }
> +        p_region->i_align = p_sys->i_align;
>  
>          if( p_region_prev == NULL )
>          {
> Index: modules/codec/telx.c
> ===================================================================
> --- modules/codec/telx.c	(revision 20287)
> +++ modules/codec/telx.c	(working copy)
> @@ -711,7 +711,7 @@
>      }
>  
>      /* Normal text subs, easy markup */
> -    p_spu->i_flags = SUBPICTURE_ALIGN_BOTTOM | p_sys->i_align;
> +    p_spu->p_region->i_align = SUBPICTURE_ALIGN_BOTTOM | p_sys->i_align;
>      p_spu->i_x = p_sys->i_align ? 20 : 0;
>      p_spu->i_y = 10;
>  
> Index: modules/codec/sdl_image.c
> ===================================================================
> --- modules/codec/sdl_image.c	(revision 20287)
> +++ modules/codec/sdl_image.c	(working copy)
> @@ -52,6 +52,7 @@
>  vlc_module_begin();
>      set_category( CAT_INPUT );
>      set_subcategory( SUBCAT_INPUT_VCODEC );
> +    set_shortname( _("SDL Image decoder"));
>      set_description( _("SDL_image video decoder") );
>      set_capability( "decoder", 60 );
>      set_callbacks( OpenDecoder, CloseDecoder );
> Index: modules/codec/subsdec.c
> ===================================================================
> --- modules/codec/subsdec.c	(revision 20287)
> +++ modules/codec/subsdec.c	(working copy)
> @@ -30,17 +30,33 @@
>  #include <vlc/vlc.h>
>  #include <vlc_vout.h>
>  #include <vlc_codec.h>
> +#include <vlc_demux.h>
>  
>  #include <vlc_osd.h>
>  #include <vlc_filter.h>
> +#include <vlc_image.h>
>  #include <vlc_charset.h>
>  #include <vlc_stream.h>
>  #include <vlc_xml.h>
>  #include <errno.h>
>  #include <string.h>
>  
> +#define F_MOD( X, Y ) { \
> +        if(( (X) >= (Y) ) || ( (X) < 0.0 )) \
> +        { \
> +            int i = (int) (X) / (int)(Y) - (((X) < 0.0) ? 1 : 0 ); \
> +            (X) -= (float)(i * (int)(Y)); \
> +        } \
> +}
> +
>  typedef struct
>  {
> +    const char *psz_filename;
> +    picture_t  *p_pic;
> +} image_attach_t;
> +
> +typedef struct
> +{
>      char *          psz_stylename; /* The name of the style, no comma's allowed */
>      text_style_t    font_style;
>      int             i_align;
> @@ -62,6 +78,9 @@
>  
>      ssa_style_t         **pp_ssa_styles;
>      int                 i_ssa_styles;
> +
> +    image_attach_t      **pp_images;
> +    int                 i_images;
>  };
>  
>  /*****************************************************************************
> @@ -76,10 +95,11 @@
>  static void         ParseUSFHeader ( decoder_t * );
>  static void         ParseUSFHeaderTags( decoder_sys_t *, xml_reader_t * );
>  static void         ParseSSAString ( decoder_t *, char *, subpicture_t * );
> -static void         ParseUSFString ( decoder_t *, char *, subpicture_t * );
> +static subpicture_region_t *ParseUSFString ( decoder_t *, char *, subpicture_t * );
>  static void         ParseColor     ( decoder_t *, char *, int *, int * );
>  static char        *StripTags      ( char * );
>  static char        *CreateHtmlSubtitle ( char * );
> +static subpicture_region_t *LoadEmbeddedImage( decoder_t *p_dec, subpicture_t *p_spu_in, const char *psz_filename );
>  
>  #define DEFAULT_NAME "Default"
>  #define MAX_LINE 8192
> @@ -207,6 +227,8 @@
>      p_sys->i_original_width = -1;
>      p_sys->pp_ssa_styles = NULL;
>      p_sys->i_ssa_styles = 0;
> +    p_sys->pp_images = NULL;
> +    p_sys->i_images = 0;
>  
>      char *psz_charset = NULL;
>      /* First try demux-specified encoding */
> @@ -330,7 +352,23 @@
>          }
>          free( p_sys->pp_ssa_styles ); p_sys->pp_ssa_styles = NULL;
>      }
> +    if( p_sys->pp_images )
> +    {
> +        int i;
> +        for( i = 0; i < p_sys->i_images; i++ )
> +        {
> +            if( p_sys->pp_images[i] )
> +            {
> +                if( p_sys->pp_images[i]->p_pic )
> +                    p_sys->pp_images[i]->p_pic->pf_release( p_sys->pp_images[i]->p_pic );
>  
> +                free( p_sys->pp_images[i] );
> +                       p_sys->pp_images[i] = NULL;
> +            }
> +        }
> +        free( p_sys->pp_images ); p_sys->pp_images = NULL;
> +    }
> +
>      free( p_sys );
>  }
>  
> @@ -448,7 +486,7 @@
>          p_dec->fmt_in.i_codec != VLC_FOURCC('u','s','f',' ') )
>      {
>          /* Normal text subs, easy markup */
> -        p_spu->i_flags = SUBPICTURE_ALIGN_BOTTOM | p_sys->i_align;
> +        p_spu->p_region->i_align = SUBPICTURE_ALIGN_BOTTOM | p_sys->i_align;
>          p_spu->i_x = p_sys->i_align ? 20 : 0;
>          p_spu->i_y = 10;
>  
> @@ -468,7 +506,10 @@
>          if( p_dec->fmt_in.i_codec == VLC_FOURCC('s','s','a',' ') )
>              ParseSSAString( p_dec, psz_subtitle, p_spu );
>          else
> -            ParseUSFString( p_dec, psz_subtitle, p_spu );
> +        {
> +            p_spu->pf_destroy_region( VLC_OBJECT(p_dec), p_spu->p_region );
> +            p_spu->p_region = ParseUSFString( p_dec, psz_subtitle, p_spu );
> +        }
>  
>          p_spu->i_start = p_block->i_pts;
>          p_spu->i_stop = p_block->i_pts + p_block->i_length;
> @@ -482,31 +523,271 @@
>      return p_spu;
>  }
>  
> -static void ParseUSFString( decoder_t *p_dec, char *psz_subtitle, subpicture_t *p_spu_in )
> +static ssa_style_t *ParseStyle( decoder_sys_t *p_sys, char *psz_subtitle )
>  {
> -    decoder_sys_t   *p_sys = p_dec->p_sys;
> -    subpicture_t    *p_spu = p_spu_in;
> -    char            *psz_text;
> -    char            *psz_text_start;
> -    ssa_style_t     *p_style = NULL;
> -    int              i;
> +    ssa_style_t *p_style = NULL;
> +    char *psz_style = strcasestr( psz_subtitle, "style=\"" );
>  
> -    /* Create a text only copy of the subtitle (for legacy implementations) and copy
> -     * the rich html version across as is - for parsing by a rendering engine capable
> -     * of understanding it.
> -     */
> -    p_spu->p_region->psz_text = NULL;
> -    p_spu->p_region->psz_html = strdup( psz_subtitle );
> +    if( psz_style && ( psz_style < strchr( psz_subtitle, '>' ) ))
> +    {
> +        int i;
> +        int i_len;
>  
> -    for( i = 0; i < p_sys->i_ssa_styles; i++ )
> +        psz_style += strcspn( psz_style, "\"" ) + 1;
> +        i_len = strcspn( psz_style, "\"" );
> +
> +        psz_style[ i_len ] = '\0';
> +
> +        for( i = 0; i < p_sys->i_ssa_styles; i++ )
> +        {
> +            if( !strcmp( p_sys->pp_ssa_styles[i]->psz_stylename, psz_style ) )
> +                p_style = p_sys->pp_ssa_styles[i];
> +        }
> +
> +        psz_style[ i_len ] = '\"';
> +    }
> +    return p_style;
> +}
> +
> +static char *GrabAttributeValue( char *psz_in, char *psz_tag_end )
> +{
> +    if( psz_in && ( psz_in < psz_tag_end ))
>      {
> -        if( !strcasecmp( p_sys->pp_ssa_styles[i]->psz_stylename, "Default" ) )
> -            p_style = p_sys->pp_ssa_styles[i];
> +        int   i_len;
> +        char *psz_tmp;
> +
> +        psz_in += strcspn( psz_in, "\"" ) + 1;
> +        i_len = strcspn( psz_in, "\"" );
> +
> +        psz_tmp = malloc( i_len + 1 );
> +        if( psz_tmp )
> +        {
> +            strncpy( psz_tmp, psz_in, i_len );
> +            psz_tmp[ i_len ] = '\0';
> +        }
> +
> +        return psz_tmp;
>      }
> +    return NULL;
> +}
>  
> -    /* The StripTags() function doesn't do everything we need (eg. <br/> tag )
> -     * so do it here ourselves.
> +static int ParsePositionAttributeList( char *psz_subtitle, int *i_align, int *i_x, int *i_y,
> +               float *f_rot_x, float *f_rot_y, float *f_rot_z )
> +{
> +    int   i_mask = 0;
> +    char *psz_tag_end  = strchr( psz_subtitle, '>' );
> +
> +    char *psz_align    = GrabAttributeValue( strcasestr( psz_subtitle, "alignment=\"" ), psz_tag_end );
> +    char *psz_margin_x = GrabAttributeValue( strcasestr( psz_subtitle, "horizontal-margin=\"" ), psz_tag_end );
> +    char *psz_margin_y = GrabAttributeValue( strcasestr( psz_subtitle, "vertical-margin=\"" ), psz_tag_end );
> +    char *psz_relative = GrabAttributeValue( strcasestr( psz_subtitle, "relative-to=\"" ), psz_tag_end );    /* UNSUPPORTED */
> +    char *psz_rotate_x = GrabAttributeValue( strcasestr( psz_subtitle, "rotate-x=\"" ), psz_tag_end );
> +    char *psz_rotate_y = GrabAttributeValue( strcasestr( psz_subtitle, "rotate-y=\"" ), psz_tag_end );
> +    char *psz_rotate_z = GrabAttributeValue( strcasestr( psz_subtitle, "rotate-z=\"" ), psz_tag_end );
> +
> +    *i_align = SUBPICTURE_ALIGN_BOTTOM;
> +    *i_x = 0;
> +    *i_y = 0;
> +    *f_rot_x = 0.0;
> +    *f_rot_y = 0.0;
> +    *f_rot_z = 0.0;
> +
> +    if( psz_align )
> +    {
> +        if( !strcasecmp( "TopLeft", psz_align ) )
> +            *i_align = SUBPICTURE_ALIGN_TOP | SUBPICTURE_ALIGN_LEFT;
> +        else if( !strcasecmp( "TopCenter", psz_align ) )
> +            *i_align = SUBPICTURE_ALIGN_TOP;
> +        else if( !strcasecmp( "TopRight", psz_align ) )
> +            *i_align = SUBPICTURE_ALIGN_TOP | SUBPICTURE_ALIGN_RIGHT;
> +        else if( !strcasecmp( "MiddleLeft", psz_align ) )
> +            *i_align = SUBPICTURE_ALIGN_LEFT;
> +        else if( !strcasecmp( "MiddleCenter", psz_align ) )
> +            *i_align = 0;
> +        else if( !strcasecmp( "MiddleRight", psz_align ) )
> +            *i_align = SUBPICTURE_ALIGN_RIGHT;
> +        else if( !strcasecmp( "BottomLeft", psz_align ) )
> +            *i_align = SUBPICTURE_ALIGN_BOTTOM | SUBPICTURE_ALIGN_LEFT;
> +        else if( !strcasecmp( "BottomCenter", psz_align ) )
> +            *i_align = SUBPICTURE_ALIGN_BOTTOM;
> +        else if( !strcasecmp( "BottomRight", psz_align ) )
> +            *i_align = SUBPICTURE_ALIGN_BOTTOM | SUBPICTURE_ALIGN_RIGHT;
> +
> +        i_mask |= 1;
> +        free( psz_align );
> +    }
> +    if( psz_margin_x )
> +    {
> +        *i_x = atoi( psz_margin_x );
> +        if( strchr( psz_margin_x, '%' ) ) *i_x = 0 - *i_x;  /* Indicate %'s as -ves */
> +
> +        i_mask |= 2;
> +        free( psz_margin_x );
> +    }
> +    if( psz_margin_y )
> +    {
> +        *i_y = atoi( psz_margin_y );
> +        if( strchr( psz_margin_y, '%' ) ) *i_y = 0 - *i_y;  /* Indicate %'s as -ves */
> +
> +        i_mask |= 4;
> +        free( psz_margin_y );
> +    }
> +    if( psz_relative )
> +    {
> +        /* VLC needs significant changes made to much of the code in order
> +         * to support subpicture planes / regions aligned to the window instead of
> +         * the video area - hence this option is presently unavailable.
> +         */
> +        i_mask |= 8;
> +        free( psz_relative );
> +    }
> +    if( psz_rotate_x )
> +    {
> +        *f_rot_x = atof( psz_rotate_x );
> +        F_MOD( *f_rot_x, 360.0 );
> +
> +        i_mask |= 16;
> +        free( psz_rotate_x );
> +    }
> +    if( psz_rotate_y )
> +    {
> +        *f_rot_y = atof( psz_rotate_y );
> +        F_MOD( *f_rot_y, 360.0 );
> +
> +        i_mask |= 32;
> +        free( psz_rotate_y );
> +    }
> +    if( psz_rotate_z )
> +    {
> +        *f_rot_z = atof( psz_rotate_z );
> +        F_MOD( *f_rot_z, 360.0 );
> +
> +        i_mask |= 64;
> +        free( psz_rotate_z );
> +    }
> +    return i_mask;
> +}
> +
> +static subpicture_region_t *CreateTextRegion( decoder_t *p_dec, subpicture_t *p_spu, char *psz_subtitle, int i_len, int i_sys_align )
> +{
> +    decoder_sys_t        *p_sys = p_dec->p_sys;
> +    subpicture_region_t  *p_text_region;
> +    video_format_t fmt;
> +
> +    /* Create a new subpicture region */
> +    memset( &fmt, 0, sizeof(video_format_t) );
> +    fmt.i_chroma = VLC_FOURCC('T','E','X','T');
> +    fmt.i_aspect = 0;
> +    fmt.i_width = fmt.i_height = 0;
> +    fmt.i_x_offset = fmt.i_y_offset = 0;
> +    p_text_region = p_spu->pf_create_region( VLC_OBJECT(p_dec), &fmt );
> +
> +    if( p_text_region != NULL )
> +    {
> +        ssa_style_t  *p_style = NULL;
> +        int           i_mask = 0;
> +        int           i_align;
> +        int           i_x, i_y;
> +        float         f_rot_x, f_rot_y, f_rot_z;
> +
> +        p_text_region->psz_text = NULL;
> +        p_text_region->psz_html = malloc( i_len + 1 );
> +        strncpy( p_text_region->psz_html, psz_subtitle, i_len );
> +        p_text_region->psz_html[ i_len ] = '\0';
> +
> +        p_style = ParseStyle( p_sys, p_text_region->psz_html );
> +        if( !p_style )
> +        {
> +            int i;
> +
> +            for( i = 0; i < p_sys->i_ssa_styles; i++ )
> +            {
> +                if( !strcasecmp( p_sys->pp_ssa_styles[i]->psz_stylename, "Default" ) )
> +                    p_style = p_sys->pp_ssa_styles[i];
> +            }
> +        }
> +
> +        if( p_style )
> +        {
> +            msg_Dbg( p_dec, "style is: %s", p_style->psz_stylename );
> +
> +            p_text_region->p_style = &p_style->font_style;
> +            p_text_region->i_align = p_style->i_align;
> +
> +            if( p_style->i_margin_h < 0 ) /* Percentage of width */
> +            {
> +                p_text_region->i_percent_x = - p_style->i_margin_h;
> +                p_text_region->i_x = 0;
> +            }
> +            else
> +            {
> +                p_text_region->i_x = p_style->i_margin_h;
> +            }
> +            if( p_style->i_margin_v < 0 ) /* Percentage of height */
> +            {
> +                p_text_region->i_percent_y = - p_style->i_margin_v;
> +                p_text_region->i_y = 0;
> +            }
> +            else
> +            {
> +                p_text_region->i_y = p_style->i_margin_v;
> +            }
> +        }
> +        else
> +        {
> +            p_text_region->i_align = SUBPICTURE_ALIGN_BOTTOM | i_sys_align;
> +            p_text_region->i_x = i_sys_align ? 20 : 0;
> +            p_text_region->i_y = 10;
> +        }
> +        i_mask = ParsePositionAttributeList( psz_subtitle, &i_align, &i_x, &i_y, &f_rot_x, &f_rot_y, &f_rot_z );
> +        if( i_mask & 1 ) /* Alignment specified */
> +        {
> +            p_text_region->i_align = i_align;
> +        }
> +        if( i_mask & 2 )
> +        {
> +            if( i_x < 0 ) /* Percentage of width */
> +            {
> +                p_text_region->i_percent_x = - i_x;
> +                p_text_region->i_x = 0;
> +            }
> +            else
> +            {
> +                p_text_region->i_x = i_x;
> +            }
> +        }
> +        if( i_mask & 4 )
> +        {
> +            if( i_y < 0 ) /* Percentage of height */
> +            {
> +                p_text_region->i_percent_y = - i_y;
> +                p_text_region->i_y = 0;
> +            }
> +            else
> +            {
> +                p_text_region->i_y = i_y;
> +            }
> +        }
> +
> +        p_text_region->p_next = NULL;
> +    }
> +    return p_text_region;
> +}
> +
> +static subpicture_region_t *ParseUSFString( decoder_t *p_dec, char *psz_subtitle, subpicture_t *p_spu_in )
> +{
> +    decoder_sys_t        *p_sys = p_dec->p_sys;
> +    subpicture_t         *p_spu = p_spu_in;
> +    char                 *psz_text;
> +    char                 *psz_text_start;
> +    subpicture_region_t  *p_region_first = NULL;
> +    subpicture_region_t  *p_region_upto  = p_region_first;
> +
> +    /* Create a text only copy of the subtitle (for legacy implementations) and split
> +     * the rich html version into multiple regions based on tag - for parsing by a
> +     * rendering engine capable of understanding them.
>       */
> +
>      psz_text_start = malloc( strlen( psz_subtitle ));
>  
>      psz_text = psz_text_start;
> @@ -516,28 +797,123 @@
>          {
>              if( !strncasecmp( psz_subtitle, "<br/>", 5 ))
>                  *psz_text++ = '\n';
> -            else if( !strncasecmp( psz_subtitle, "<text ", 6 ))
> +            else if(( !strncasecmp( psz_subtitle, "<text ", 6 )) ||
> +                    ( !strncasecmp( psz_subtitle, "<text>", 6 )))
>              {
> -                char *psz_style = strcasestr( psz_subtitle, "style=\"" );
> +                char *psz_end = strcasestr( psz_subtitle, "</text>" );
> +                
> +                if( psz_end )
> +                {
> +                    subpicture_region_t  *p_text_region;
>  
> -                if( psz_style && ( psz_style < strchr( psz_subtitle, '>' ) ))
> +                    psz_end += 7; /* strlen("</text>") */
> +
> +                    p_text_region = CreateTextRegion( p_dec, p_spu, psz_subtitle, psz_end - psz_subtitle, p_sys->i_align );
> +                
> +                    if( !p_region_first )
> +                    {
> +                        p_region_first = p_region_upto = p_text_region;
> +                    }
> +                    else if( p_text_region )
> +                    {
> +                        p_region_upto->p_next = p_text_region;
> +                        p_region_upto = p_region_upto->p_next;
> +                    }
> +                }
> +            }
> +            else if(( !strncasecmp( psz_subtitle, "<karaoke ", 9 )) ||
> +                    ( !strncasecmp( psz_subtitle, "<karaoke>", 9 )))
> +            {
> +                char *psz_end = strcasestr( psz_subtitle, "</karaoke>" );
> +                
> +                if( psz_end )
>                  {
> -                    int i_len;
> +                    subpicture_region_t  *p_text_region;
>  
> -                    psz_style += strspn( psz_style, "\"" ) + 1;
> -                    i_len = strcspn( psz_style, "\"" );
> +                    psz_end += 10; /* strlen("</karaoke>") */
>  
> -                    psz_style[ i_len ] = '\0';
> +                    p_text_region = CreateTextRegion( p_dec, p_spu, psz_subtitle, psz_end - psz_subtitle, p_sys->i_align );
> +                
> +                    if( !p_region_first )
> +                    {
> +                        p_region_first = p_region_upto = p_text_region;
> +                    }
> +                    else if( p_text_region )
> +                    {
> +                        p_region_upto->p_next = p_text_region;
> +                        p_region_upto = p_region_upto->p_next;
> +                    }
> +                    /* Karaoke tags shouldn't be put towards the plain-text version
> +                     * of the subtitle
> +                     */
> +                    psz_subtitle = psz_end - 1 ;
> +                }
> +            }
> +            else if(( !strncasecmp( psz_subtitle, "<image ", 7 )) ||
> +                    ( !strncasecmp( psz_subtitle, "<image>", 7 )))
> +            {
> +                subpicture_region_t *p_image_region = NULL;
>  
> -                    for( i = 0; i < p_sys->i_ssa_styles; i++ )
> +                char *psz_end = strcasestr( psz_subtitle, "</image>" );
> +                char *psz_content = strchr( psz_subtitle, '>' );
> +                
> +                if( psz_content && ( psz_content < psz_end ) )
> +                {
> +                    char *psz_filename = (char *) malloc( psz_end - psz_content );
> +
> +                    strncpy( psz_filename, psz_content + 1, psz_end - psz_content - 1 );
> +                    psz_filename[ psz_end - psz_content - 1 ] = '\0';
> +
> +                    p_image_region = LoadEmbeddedImage( p_dec, p_spu, psz_filename );
> +                }
> +
> +                if( psz_end ) psz_end += 8; /* strlen("</image>") */
> +
> +                if( p_image_region )
> +                {
> +                    float f_rot_x, f_rot_y, f_rot_z;
> +
> +                    ParsePositionAttributeList( psz_subtitle,
> +                            &(p_image_region->i_align),
> +                            &(p_image_region->i_x), &(p_image_region->i_y),
> +                            &f_rot_x, &f_rot_y, &f_rot_z );
> +
> +                    if( p_image_region->i_x < 0 ) /* Percentage of width */
>                      {
> -                        if( !strcmp( p_sys->pp_ssa_styles[i]->psz_stylename, psz_style ) )
> -                            p_style = p_sys->pp_ssa_styles[i];
> +                        p_image_region->i_percent_x = - p_image_region->i_x;
> +                        p_image_region->i_x = 0;
>                      }
> +                    if( p_image_region->i_y < 0 ) /* Percentage of height */
> +                    {
> +                        p_image_region->i_percent_y = - p_image_region->i_y;
> +                        p_image_region->i_y = 0;
> +                    }
> +                    /* FIXME: Rotations around X, Y and Z axes */
>  
> -                    psz_style[ i_len ] = '\"';
> +                    p_image_region->p_next   = NULL;
> +                    p_image_region->psz_text = NULL;
> +                    p_image_region->psz_html = NULL;
> +
> +                    if( !p_region_first )
> +                    {
> +                        p_region_first = p_region_upto = p_image_region;
> +                    }
> +                    else
> +                    {
> +                        p_region_upto->p_next = p_image_region;
> +                        p_region_upto = p_region_upto->p_next;
> +                    }
>                  }
> +                /* Image tags shouldn't be put towards the plain-text version
> +                 * of the subtitle
> +                 */
> +                psz_subtitle = psz_end - 1 ;
>              }
> +            else if(( !strncasecmp( psz_subtitle, "<shape ", 7 )) ||
> +                    ( !strncasecmp( psz_subtitle, "<shape>", 7 )))
> +            {
> +                /* TODO */
> +            }
>              
>              psz_subtitle += strcspn( psz_subtitle, ">" );
>          }
> @@ -569,21 +945,19 @@
>          psz_subtitle++;
>      }
>      *psz_text = '\0';
> -    p_spu->p_region->psz_text = strdup( psz_text_start );
> -    free( psz_text_start );
>  
> -    if( p_style == NULL )
> +    for( p_region_upto = p_region_first; p_region_upto; p_region_upto = p_region_upto->p_next )
>      {
> -        p_spu->i_flags = SUBPICTURE_ALIGN_BOTTOM | p_sys->i_align;
> -        p_spu->i_x = p_sys->i_align ? 20 : 0;
> -        p_spu->i_y = 10;
> +        if( p_region_upto->psz_html )
> +        {
> +            p_region_upto->psz_text = strdup( psz_text_start );
> +            break;
> +        }
>      }
> -    else
> -    {
> -        msg_Dbg( p_dec, "style is: %s", p_style->psz_stylename);
> -        p_spu->p_region->p_style = &p_style->font_style;
> -        p_spu->i_flags = p_style->i_align;
> -    }
> +
> +    free( psz_text_start );
> +
> +    return p_region_first;
>  }
>  
>  static void ParseSSAString( decoder_t *p_dec, char *psz_subtitle, subpicture_t *p_spu_in )
> @@ -678,7 +1052,7 @@
>      p_spu->p_region->psz_text = psz_new_subtitle;
>      if( p_style == NULL )
>      {
> -        p_spu->i_flags = SUBPICTURE_ALIGN_BOTTOM | p_sys->i_align;
> +        p_spu->p_region->i_align = SUBPICTURE_ALIGN_BOTTOM | p_sys->i_align;
>          p_spu->i_x = p_sys->i_align ? 20 : 0;
>          p_spu->i_y = 10;
>      }
> @@ -686,7 +1060,7 @@
>      {
>          msg_Dbg( p_dec, "style is: %s", p_style->psz_stylename);
>          p_spu->p_region->p_style = &p_style->font_style;
> -        p_spu->i_flags = p_style->i_align;
> +        p_spu->p_region->i_align = p_style->i_align;
>          if( p_style->i_align & SUBPICTURE_ALIGN_LEFT )
>          {
>              p_spu->i_x = (i_margin_l) ? i_margin_l : p_style->i_margin_h;
> @@ -740,6 +1114,118 @@
>          *pi_alpha = ( i_color & 0xFF000000 ) >> 24;
>  }
>  
> +static inline int demux2_Control( demux_t *p_demux, int i_query, ... )
> +{
> +    va_list args;
> +    int     i_result;
> +
> +    va_start( args, i_query );
> +    i_result = p_demux->pf_control( p_demux, i_query, args );
> +    va_end( args );
> +    return i_result;
> +}
> +
> +static void ParseMKVAttachments( decoder_t *p_dec )
> +{
> +    decoder_sys_t *p_sys = p_dec->p_sys;
> +    demux_t       *p_demux;
> +    attachment_t   attachment;
> +    int            k = 0;
> +
> +    p_demux = (demux_t *)vlc_object_find( p_dec, VLC_OBJECT_DEMUX, FIND_ANYWHERE );
> +
> +    while( VLC_EGENERIC != demux2_Control( p_demux, DEMUX_GET_ATTACHMENT, k++, &attachment ) )
> +    {
> +        vlc_fourcc_t  type  = 0;
> +
> +        if( ( !strcmp( attachment.psz_mime_type, "image/bmp" ) )      || /* BMP */
> +            ( !strcmp( attachment.psz_mime_type, "image/x-bmp" ) )    ||
> +            ( !strcmp( attachment.psz_mime_type, "image/x-bitmap" ) ) ||
> +            ( !strcmp( attachment.psz_mime_type, "image/x-ms-bmp" ) ) )
> +        {
> +             type = VLC_FOURCC('b','m','p',' ');
> +        }
> +        else if( ( !strcmp( attachment.psz_mime_type, "image/x-portable-anymap" ) )  || /* PNM */
> +                 ( !strcmp( attachment.psz_mime_type, "image/x-portable-bitmap" ) )  || /* PBM */
> +                 ( !strcmp( attachment.psz_mime_type, "image/x-portable-graymap" ) ) || /* PGM */
> +                 ( !strcmp( attachment.psz_mime_type, "image/x-portable-pixmap" ) ) )   /* PPM */
> +        {
> +            type = VLC_FOURCC('p','n','m',' ');
> +        }
> +        else if ( !strcmp( attachment.psz_mime_type, "image/gif" ) )         /* GIF */
> +            type = VLC_FOURCC('g','i','f',' ');
> +        else if ( !strcmp( attachment.psz_mime_type, "image/jpeg" ) )        /* JPG, JPEG */
> +            type = VLC_FOURCC('j','p','e','g');
> +        else if ( !strcmp( attachment.psz_mime_type, "image/pcx" ) )         /* PCX */
> +            type = VLC_FOURCC('p','c','x',' ');
> +        else if ( !strcmp( attachment.psz_mime_type, "image/png" ) )         /* PNG */
> +            type = VLC_FOURCC('p','n','g',' ');
> +        else if ( !strcmp( attachment.psz_mime_type, "image/tiff" ) )        /* TIF, TIFF */
> +            type = VLC_FOURCC('t','i','f','f');
> +        else if ( !strcmp( attachment.psz_mime_type, "image/x-tga" ) )       /* TGA */
> +            type = VLC_FOURCC('t','g','a',' ');
> +        else if ( !strcmp( attachment.psz_mime_type, "image/x-xpixmap") )    /* XPM */
> +            type = VLC_FOURCC('x','p','m',' ');
> +
> +        if( ( type != 0 ) &&
> +            ( attachment.i_size > 0 ) &&
> +            ( attachment.p_data != NULL ) )
> +        {
> +            picture_t   *p_pic   = NULL;
> +            image_handler_t   *p_image;
> +
> +            p_image = image_HandlerCreate( p_dec );
> +            if( p_image != NULL )
> +            {
> +                block_t   *p_block;
> +               
> +                p_block = block_New( p_image->p_parent, attachment.i_size );
> +
> +                if( p_block != NULL )
> +                {
> +                    video_format_t     fmt_in;
> +                    video_format_t     fmt_out;
> +
> +                    memcpy( p_block->p_buffer, attachment.p_data, attachment.i_size );
> +
> +                    memset( &fmt_in,  0, sizeof( video_format_t));
> +                    memset( &fmt_out, 0, sizeof( video_format_t));
> +
> +                    fmt_in.i_chroma  = type;
> +                    fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
> +
> +                    /* Find a suitable decoder module */
> +                    if( module_Exists( p_dec, "SDL Image decoder" ) )
> +                    {
> +                        /* ffmpeg thinks it can handle bmp properly but it can't (at least
> +                         * not all of them), so use sdl_image if it is available */
> +
> +                        vlc_value_t val;
> +
> +                        var_Create( p_dec, "codec", VLC_VAR_MODULE | VLC_VAR_DOINHERIT );
> +                        val.psz_string = (char*) "sdl_image";
> +                        var_Set( p_dec, "codec", val );
> +                    }
> +
> +                    p_pic = image_Read( p_image, p_block, &fmt_in, &fmt_out );
> +                    var_Destroy( p_dec, "codec" );
> +                }
> +
> +                image_HandlerDelete( p_image );
> +            }
> +            if( p_pic )
> +            {
> +                image_attach_t *p_attach = malloc( sizeof(image_attach_t) );
> +
> +                p_attach->psz_filename = attachment.psz_file_name;
> +                p_attach->p_pic = p_pic;
> +
> +                TAB_APPEND( p_sys->i_images, p_sys->pp_images, p_attach );
> +            }
> +        }
> +    }
> +}
> +
>  /*****************************************************************************
>   * ParseUSFHeader: Retrieve global formatting information etc
>   *****************************************************************************/
> @@ -750,6 +1236,8 @@
>      xml_t         *p_xml = NULL;
>      xml_reader_t  *p_xml_reader = NULL;
>  
> +    ParseMKVAttachments( p_dec );
> +
>      p_sub = stream_MemoryNew( VLC_OBJECT(p_dec),
>                                p_dec->fmt_in.p_extra,
>                                p_dec->fmt_in.i_extra,
> @@ -817,8 +1305,6 @@
>                          case 2:
>                              if( !strcasecmp( "style", psz_node ) )
>                              {
> -                                p_style->font_style.i_text_align = p_style->i_align;
> -
>                                  TAB_APPEND( p_sys->i_ssa_styles, p_sys->pp_ssa_styles, p_style );
>  
>                                  p_style = NULL;
> @@ -866,7 +1352,24 @@
>                          i_style_level++;
>  
>                          p_style = calloc( 1, sizeof(ssa_style_t) );
> +                        /* All styles are supposed to default to Default, and then
> +                         * one or more settings are over-ridden. 
> +                         * At the moment this only effects styles defined AFTER
> +                         * Default in the XML
> +                         */
> +                        int i;
> +                        for( i = 0; i < p_sys->i_ssa_styles; i++ )
> +                        {
> +                            if( !strcasecmp( p_sys->pp_ssa_styles[i]->psz_stylename, "Default" ) )
> +                            {
> +                                ssa_style_t *p_default_style = p_sys->pp_ssa_styles[i];
>  
> +                                memcpy( p_style, p_default_style, sizeof( ssa_style_t ) );
> +                                p_style->font_style.psz_fontname = strdup( p_style->font_style.psz_fontname );
> +                                p_style->psz_stylename = NULL;
> +                            }
> +                        }
> +
>                          while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
>                          {
>                              char *psz_name = xml_ReaderName ( p_xml_reader );
> @@ -891,23 +1394,37 @@
>                              if( psz_name && psz_value )
>                              {
>                                  if( !strcasecmp( "face", psz_name ) )
> -                                    p_style->font_style.psz_fontname = strdup( psz_value);
> +                                {
> +                                    if( p_style->font_style.psz_fontname ) free( p_style->font_style.psz_fontname );
> +                                    p_style->font_style.psz_fontname = strdup( psz_value );
> +                                }
>                                  else if( !strcasecmp( "size", psz_name ) )
> -                                    p_style->font_style.i_font_size = atoi( psz_value);
> +                                {
> +                                    if(( *psz_value == '+' ) || ( *psz_value == '-' ))
> +                                        p_style->font_style.i_font_size += atoi( psz_value );
> +                                    else
> +                                        p_style->font_style.i_font_size = atoi( psz_value );
> +                                }
>                                  else if( !strcasecmp( "italic", psz_name ) )
>                                  {
>                                      if( !strcasecmp( "yes", psz_value ))
>                                          p_style->font_style.i_style_flags |= STYLE_ITALIC;
> +                                    else
> +                                        p_style->font_style.i_style_flags &= ~STYLE_ITALIC;
>                                  }
>                                  else if( !strcasecmp( "weight", psz_name ) )
>                                  {
>                                      if( !strcasecmp( "bold", psz_value ))
>                                          p_style->font_style.i_style_flags |= STYLE_BOLD;
> +                                    else
> +                                        p_style->font_style.i_style_flags &= ~STYLE_BOLD;
>                                  }
>                                  else if( !strcasecmp( "underline", psz_name ) )
>                                  {
>                                      if( !strcasecmp( "yes", psz_value ))
>                                          p_style->font_style.i_style_flags |= STYLE_UNDERLINE;
> +                                    else
> +                                        p_style->font_style.i_style_flags &= ~STYLE_UNDERLINE;
>                                  }
>                                  else if( !strcasecmp( "color", psz_name ) )
>                                  {
> @@ -956,46 +1473,34 @@
>                                  if( !strcasecmp( "alignment", psz_name ) )
>                                  {
>                                      if( !strcasecmp( "TopLeft", psz_value ) )
> -                                    {
> -                                        p_style->i_align |= SUBPICTURE_ALIGN_TOP;
> -                                        p_style->i_align |= SUBPICTURE_ALIGN_LEFT;
> -                                    }
> +                                        p_style->i_align = SUBPICTURE_ALIGN_TOP | SUBPICTURE_ALIGN_LEFT;
>                                      else if( !strcasecmp( "TopCenter", psz_value ) )
> -                                    {
> -                                        p_style->i_align |= SUBPICTURE_ALIGN_TOP;
> -                                    }
> +                                        p_style->i_align = SUBPICTURE_ALIGN_TOP;
>                                      else if( !strcasecmp( "TopRight", psz_value ) )
> -                                    {
> -                                        p_style->i_align |= SUBPICTURE_ALIGN_TOP;
> -                                        p_style->i_align |= SUBPICTURE_ALIGN_RIGHT;
> -                                    }
> +                                        p_style->i_align = SUBPICTURE_ALIGN_TOP | SUBPICTURE_ALIGN_RIGHT;
>                                      else if( !strcasecmp( "MiddleLeft", psz_value ) )
> -                                    {
> -                                        p_style->i_align |= SUBPICTURE_ALIGN_LEFT;
> -                                    }
> +                                        p_style->i_align = SUBPICTURE_ALIGN_LEFT;
>                                      else if( !strcasecmp( "MiddleCenter", psz_value ) )
> -                                    {
>                                          p_style->i_align = 0;
> -                                    }
>                                      else if( !strcasecmp( "MiddleRight", psz_value ) )
> -                                    {
> -                                        p_style->i_align |= SUBPICTURE_ALIGN_RIGHT;
> -                                    }
> +                                        p_style->i_align = SUBPICTURE_ALIGN_RIGHT;
>                                      else if( !strcasecmp( "BottomLeft", psz_value ) )
> -                                    {
> -                                        p_style->i_align |= SUBPICTURE_ALIGN_BOTTOM;
> -                                        p_style->i_align |= SUBPICTURE_ALIGN_LEFT;
> -                                    }
> +                                        p_style->i_align = SUBPICTURE_ALIGN_BOTTOM | SUBPICTURE_ALIGN_LEFT;
>                                      else if( !strcasecmp( "BottomCenter", psz_value ) )
> -                                    {
> -                                        p_style->i_align |= SUBPICTURE_ALIGN_BOTTOM;
> -                                    }
> +                                        p_style->i_align = SUBPICTURE_ALIGN_BOTTOM;
>                                      else if( !strcasecmp( "BottomRight", psz_value ) )
> -                                    {
> -                                        p_style->i_align |= SUBPICTURE_ALIGN_BOTTOM;
> -                                        p_style->i_align |= SUBPICTURE_ALIGN_RIGHT;
> -                                    }
> +                                        p_style->i_align = SUBPICTURE_ALIGN_BOTTOM | SUBPICTURE_ALIGN_RIGHT;
>                                  }
> +                                else if( !strcasecmp( "horizontal-margin", psz_name ) )
> +                                {
> +                                    p_style->i_margin_h = atoi( psz_value );
> +                                    if( strchr( psz_value, '%' ) ) p_style->i_margin_h = 0 - p_style->i_margin_h;  /* Indicate %'s as -ves */
> +                                }
> +                                else if( !strcasecmp( "vertical-margin", psz_name ) )
> +                                {
> +                                    p_style->i_margin_v = atoi( psz_value );
> +                                    if( strchr( psz_value, '%' ) ) p_style->i_margin_v = 0 - p_style->i_margin_v;  /* Indicate %'s as -ves */
> +                                }
>                              }
>                              if( psz_name )  free( psz_name );
>                              if( psz_value ) free( psz_value );
> @@ -1147,7 +1652,7 @@
>                      //p_style->font_style.f_angle = f_angle;
>  
>                      p_style->i_align = 0;
> -                    if( i_align == 0x1 || i_align == 0x4 || i_align == 0x1 ) p_style->i_align |= SUBPICTURE_ALIGN_LEFT;
> +                    if( i_align == 0x1 || i_align == 0x4 || i_align == 0x7 ) p_style->i_align |= SUBPICTURE_ALIGN_LEFT;
>                      if( i_align == 0x3 || i_align == 0x6 || i_align == 0x9 ) p_style->i_align |= SUBPICTURE_ALIGN_RIGHT;
>                      if( i_align == 0x7 || i_align == 0x8 || i_align == 0x9 ) p_style->i_align |= SUBPICTURE_ALIGN_TOP;
>                      if( i_align == 0x1 || i_align == 0x2 || i_align == 0x3 ) p_style->i_align |= SUBPICTURE_ALIGN_BOTTOM;
> @@ -1458,3 +1963,55 @@
>      }
>      return psz_html_start;
>  }
> +
> +/****************************************************************************
> + * download and resize image located at psz_url
> + ***************************************************************************/
> +static subpicture_region_t *LoadEmbeddedImage( decoder_t *p_dec, subpicture_t *p_spu, const char *psz_filename )
> +{
> +    decoder_sys_t         *p_sys = p_dec->p_sys;
> +    subpicture_region_t   *p_region = NULL;
> +    video_format_t         fmt_out;
> +    int                    k;
> +    picture_t             *p_pic = NULL;
> +
> +    for( k = 0; k < p_sys->i_images; k++ )
> +    {
> +        if( p_sys->pp_images &&
> +            !strcmp( p_sys->pp_images[k]->psz_filename, psz_filename ) )
> +        {
> +            p_pic = p_sys->pp_images[k]->p_pic;
> +            break;
> +        }
> +    }
> +
> +    if( !p_pic )
> +    {
> +        msg_Err( p_dec, "Unable to read image %s", psz_filename );
> +        return p_region;
> +    }
> +    
> +    /* Display the feed's image */
> +    memset( &fmt_out, 0, sizeof( video_format_t));
> +
> +    fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
> +    fmt_out.i_aspect = VOUT_ASPECT_FACTOR;
> +    fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
> +    fmt_out.i_width =
> +        fmt_out.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
> +    fmt_out.i_height =
> +        fmt_out.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
> +
> +    p_region = p_spu->pf_create_region( VLC_OBJECT(p_dec), &fmt_out );
> +    if( !p_region )
> +    {
> +        msg_Err( p_dec, "cannot allocate SPU region" );
> +    }
> +    else
> +    {
> +        vout_CopyPicture( p_dec, &p_region->picture, p_pic );
> +        /* Offset text to display right next to the image */
> +    }
> +
> +    return p_region;
> +}
> Index: modules/codec/dvbsub.c
> ===================================================================
> --- modules/codec/dvbsub.c	(revision 20287)
> +++ modules/codec/dvbsub.c	(working copy)
> @@ -1492,6 +1492,7 @@
>          }
>          p_spu_region->i_x = p_regiondef->i_x;
>          p_spu_region->i_y = p_regiondef->i_y;
> +        p_spu_region->i_align = p_sys->i_spu_position;
>          *pp_spu_region = p_spu_region;
>          pp_spu_region = &p_spu_region->p_next;
>  
> @@ -1546,6 +1547,7 @@
>              p_spu_region->psz_text = strdup( p_object_def->psz_text );
>              p_spu_region->i_x = p_regiondef->i_x + p_object_def->i_x;
>              p_spu_region->i_y = p_regiondef->i_y + p_object_def->i_y;
> +            p_spu_region->i_align = p_sys->i_spu_position;
>              *pp_spu_region = p_spu_region;
>              pp_spu_region = &p_spu_region->p_next;
>          }
> Index: include/vlc_osd.h
> ===================================================================
> --- include/vlc_osd.h	(revision 20287)
> +++ include/vlc_osd.h	(working copy)
> @@ -245,7 +245,6 @@
>      int        i_outline_width;   /**< The width of the outline in pixels */
>      int        i_shadow_width;    /**< The width of the shadow in pixels */
>      int        i_spacing;         /**< The spaceing between glyphs in pixels */
> -    int        i_text_align;      /**< An alignment hint for the text */
>  };
>  
>  /* Style flags for \ref text_style_t */
> @@ -258,7 +257,7 @@
>  #define STYLE_STRIKEOUT   64
>  
>  static const text_style_t default_text_style = { NULL, 22, 0xffffff, 0xff, STYLE_OUTLINE,
> -                0x000000, 0xff, 0x000000, 0xff, 0xffffff, 0x80, 1, 0, -1, 0 };
> +                0x000000, 0xff, 0x000000, 0xff, 0xffffff, 0x80, 1, 0, -1 };
>  
>  
>  
> Index: include/vlc_common.h
> ===================================================================
> --- include/vlc_common.h	(revision 20287)
> +++ include/vlc_common.h	(working copy)
> @@ -429,6 +429,7 @@
>  /* divers */
>  typedef struct vlc_meta_t    vlc_meta_t;
>  typedef struct meta_export_t meta_export_t;
> +typedef struct attachment_t  attachment_t;
>  
>  /* Stats */
>  typedef struct counter_t     counter_t;
> Index: include/vlc_vout.h
> ===================================================================
> --- include/vlc_vout.h	(revision 20287)
> +++ include/vlc_vout.h	(working copy)
> @@ -210,6 +210,8 @@
>  
>      int             i_x;                             /**< position of region */
>      int             i_y;                             /**< position of region */
> +    int             i_percent_x;                     /**< position of region */
> +    int             i_percent_y;                     /**< position of region */
>      int             i_align;                  /**< alignment within a region */
>  
>      char            *psz_text;       /**< text string comprising this region */
> Index: include/vlc_demux.h
> ===================================================================
> --- include/vlc_demux.h	(revision 20287)
> +++ include/vlc_demux.h	(working copy)
> @@ -72,6 +72,13 @@
>      demux_sys_t *p_sys;
>  };
>  
> +struct attachment_t
> +{
> +    const char  *psz_file_name;
> +    const char  *psz_mime_type;
> +    void        *p_data;
> +    int          i_size;
> +};
What has this struct todo with the demuxer??

>  enum demux_query_e
>  {
>      /* I. Common queries to access_demux and demux */
> @@ -113,7 +120,8 @@
>      DEMUX_CAN_PAUSE,            /* arg1= vlc_bool_t*    cannot fail */
>      DEMUX_CAN_CONTROL_PACE,     /* arg1= vlc_bool_t*    cannot fail */
>      DEMUX_GET_PTS_DELAY,        /* arg1= int64_t*       cannot fail */
> -    DEMUX_SET_PAUSE_STATE       /* arg1= vlc_bool_t     can fail */
> +    DEMUX_SET_PAUSE_STATE,      /* arg1= vlc_bool_t     can fail */
> +    DEMUX_GET_ATTACHMENT        /* arg1= int, arg2= attachment_t*   can fail */
>  };
>  
>  VLC_EXPORT( int,       demux2_vaControlHelper, ( stream_t *, int64_t i_start, int64_t i_end, int i_bitrate, int i_align, int i_query, va_list args ) );

-- 
This is the vlc-devel mailing-list, see http://www.videolan.org/vlc/
To unsubscribe, please read http://developers.videolan.org/lists.html



More information about the vlc-devel mailing list