void subpicture_region_Delete( subpicture_region_t *p_region ) { if( !p_region ) return; if( p_region->p_private ) subpicture_region_private_Delete( p_region->p_private ); if( p_region->p_picture ) picture_Release( p_region->p_picture ); text_segment_ChainDelete( p_region->p_text ); video_format_Clean( &p_region->fmt ); free( p_region ); }
static text_segment_t* ParseSubtitles( int *pi_align, const char *psz_subtitle ) { text_segment_t* p_segment; text_segment_t* p_first_segment; style_stack_t* p_stack = NULL; tag_stack_t* p_tag_stack = NULL; //FIXME: Remove initial allocation? Might make the below code more complicated p_first_segment = p_segment = text_segment_New( "" ); bool b_has_align = false; /* */ while( *psz_subtitle ) { /* HTML extensions */ if( *psz_subtitle == '<' ) { char *psz_tagname = GetTag( &psz_subtitle, false ); if ( psz_tagname != NULL ) { if( !strcasecmp( psz_tagname, "br" ) ) { if ( !AppendCharacter( p_segment, '\n' ) ) { free( psz_tagname ); goto fail; } } else if( !strcasecmp( psz_tagname, "b" ) ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->i_style_flags |= STYLE_BOLD; p_segment->style->i_features |= STYLE_HAS_FLAGS; } else if( !strcasecmp( psz_tagname, "i" ) ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->i_style_flags |= STYLE_ITALIC; p_segment->style->i_features |= STYLE_HAS_FLAGS; } else if( !strcasecmp( psz_tagname, "u" ) ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->i_style_flags |= STYLE_UNDERLINE; p_segment->style->i_features |= STYLE_HAS_FLAGS; } else if( !strcasecmp( psz_tagname, "s" ) ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->i_style_flags |= STYLE_STRIKEOUT; p_segment->style->i_features |= STYLE_HAS_FLAGS; } else if( !strcasecmp( psz_tagname, "font" ) ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); char* psz_attribute_name; char* psz_attribute_value; while( ( psz_attribute_name = ConsumeAttribute( &psz_subtitle, &psz_attribute_value ) ) ) { if ( !strcasecmp( psz_attribute_name, "face" ) ) { p_segment->style->psz_fontname = psz_attribute_value; // We don't want to free the attribute value since it has become our fontname psz_attribute_value = NULL; } else if ( !strcasecmp( psz_attribute_name, "family" ) ) { p_segment->style->psz_monofontname = psz_attribute_value; psz_attribute_value = NULL; } else if ( !strcasecmp( psz_attribute_name, "size" ) ) { int size = atoi( psz_attribute_value ); if( size ) { p_segment->style->i_font_size = size; p_segment->style->f_font_relsize = STYLE_DEFAULT_REL_FONT_SIZE * STYLE_DEFAULT_FONT_SIZE / p_segment->style->i_font_size; } } else if ( !strcasecmp( psz_attribute_name, "color" ) ) { p_segment->style->i_font_color = vlc_html_color( psz_attribute_value, NULL ); p_segment->style->i_features |= STYLE_HAS_FONT_COLOR; } else if ( !strcasecmp( psz_attribute_name, "outline-color" ) ) { p_segment->style->i_outline_color = vlc_html_color( psz_attribute_value, NULL ); p_segment->style->i_features |= STYLE_HAS_OUTLINE_COLOR; } else if ( !strcasecmp( psz_attribute_name, "shadow-color" ) ) { p_segment->style->i_shadow_color = vlc_html_color( psz_attribute_value, NULL ); p_segment->style->i_features |= STYLE_HAS_SHADOW_COLOR; } else if ( !strcasecmp( psz_attribute_name, "outline-level" ) ) { p_segment->style->i_outline_width = atoi( psz_attribute_value ); } else if ( !strcasecmp( psz_attribute_name, "shadow-level" ) ) { p_segment->style->i_shadow_width = atoi( psz_attribute_value ); } else if ( !strcasecmp( psz_attribute_name, "back-color" ) ) { p_segment->style->i_background_color = vlc_html_color( psz_attribute_value, NULL ); p_segment->style->i_features |= STYLE_HAS_BACKGROUND_COLOR; } else if ( !strcasecmp( psz_attribute_name, "alpha" ) ) { p_segment->style->i_font_alpha = atoi( psz_attribute_value ); p_segment->style->i_features |= STYLE_HAS_FONT_ALPHA; } free( psz_attribute_name ); free( psz_attribute_value ); } } else { // This is an unknown tag. We need to hide it if it's properly closed, and display it otherwise if ( !IsClosed( psz_subtitle, psz_tagname ) ) { AppendCharacter( p_segment, '<' ); AppendString( p_segment, psz_tagname ); AppendCharacter( p_segment, '>' ); } else { AppendTag( &p_tag_stack, psz_tagname ); // We don't want to free the tagname now, it will be freed when the tag // gets poped from the stack. psz_tagname = NULL; } // In any case, fall through and skip to the closing tag. } // Skip potential spaces & end tag while ( *psz_subtitle && *psz_subtitle != '>' ) psz_subtitle++; if ( *psz_subtitle == '>' ) psz_subtitle++; free( psz_tagname ); } else if( !strncmp( psz_subtitle, "</", 2 )) { char* psz_tagname = GetTag( &psz_subtitle, true ); if ( psz_tagname != NULL ) { if ( !strcasecmp( psz_tagname, "b" ) || !strcasecmp( psz_tagname, "i" ) || !strcasecmp( psz_tagname, "u" ) || !strcasecmp( psz_tagname, "s" ) || !strcasecmp( psz_tagname, "font" ) ) { // A closing tag for one of the tags we handle, meaning // we pushed a style onto the stack earlier p_segment = NewTextSegmentPopStyle( p_segment, &p_stack ); } else { // Unknown closing tag. If it is closing an unknown tag, ignore it. Otherwise, display it if ( !HasTag( &p_tag_stack, psz_tagname ) ) { AppendString( p_segment, "</" ); AppendString( p_segment, psz_tagname ); AppendCharacter( p_segment, '>' ); } } while ( *psz_subtitle == ' ' ) psz_subtitle++; if ( *psz_subtitle == '>' ) psz_subtitle++; free( psz_tagname ); } } else { /* We have an unknown tag, just append it, and move on. * The rest of the string won't be recognized as a tag, and * we will ignore unknown closing tag */ AppendCharacter( p_segment, '<' ); psz_subtitle++; } } /* SSA extensions */ else if( psz_subtitle[0] == '{' && psz_subtitle[1] == '\\' && strchr( psz_subtitle, '}' ) ) { /* Check for forced alignment */ if( !b_has_align && !strncmp( psz_subtitle, "{\\an", 4 ) && psz_subtitle[4] >= '1' && psz_subtitle[4] <= '9' && psz_subtitle[5] == '}' ) { static const int pi_vertical[3] = { SUBPICTURE_ALIGN_BOTTOM, 0, SUBPICTURE_ALIGN_TOP }; static const int pi_horizontal[3] = { SUBPICTURE_ALIGN_LEFT, 0, SUBPICTURE_ALIGN_RIGHT }; const int i_id = psz_subtitle[4] - '1'; b_has_align = true; *pi_align = pi_vertical[i_id/3] | pi_horizontal[i_id%3]; } /* TODO fr -> rotation */ /* Hide {\stupidity} */ psz_subtitle = strchr( psz_subtitle, '}' ) + 1; } /* MicroDVD extensions */ /* FIXME: * - Currently, we don't do difference between X and x, and we should: * Capital Letters applies to the whole text and not one line * - We don't support Position and Coordinates * - We don't support the DEFAULT flag (HEADER) */ else if( psz_subtitle[0] == '{' && psz_subtitle[2] == ':' && strchr( &psz_subtitle[2], '}' ) ) { const char *psz_tag_end = strchr( &psz_subtitle[2], '}' ); size_t i_len = psz_tag_end - &psz_subtitle[3]; if( psz_subtitle[1] == 'Y' || psz_subtitle[1] == 'y' ) { if( psz_subtitle[3] == 'i' ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->i_style_flags |= STYLE_ITALIC; p_segment->style->i_features |= STYLE_HAS_FLAGS; psz_subtitle++; } if( psz_subtitle[3] == 'b' ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->i_style_flags |= STYLE_BOLD; p_segment->style->i_features |= STYLE_HAS_FLAGS; psz_subtitle++; } if( psz_subtitle[3] == 'u' ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->i_style_flags |= STYLE_UNDERLINE; p_segment->style->i_features |= STYLE_HAS_FLAGS; psz_subtitle++; } } else if( (psz_subtitle[1] == 'C' || psz_subtitle[1] == 'c' ) && psz_subtitle[3] == '$' && i_len >= 7 ) { /* Yes, they use BBGGRR, instead of RRGGBB */ char psz_color[7]; psz_color[0] = psz_subtitle[8]; psz_color[1] = psz_subtitle[9]; psz_color[2] = psz_subtitle[6]; psz_color[3] = psz_subtitle[7]; psz_color[4] = psz_subtitle[4]; psz_color[5] = psz_subtitle[5]; psz_color[6] = '\0'; p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->i_font_color = vlc_html_color( psz_color, NULL ); p_segment->style->i_features |= STYLE_HAS_FONT_COLOR; } else if( psz_subtitle[1] == 'F' || psz_subtitle[1] == 'f' ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->psz_fontname = strndup( &psz_subtitle[3], i_len ); } else if( psz_subtitle[1] == 'S' || psz_subtitle[1] == 's' ) { int size = atoi( &psz_subtitle[3] ); if( size ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->i_font_size = size; p_segment->style->f_font_relsize = STYLE_DEFAULT_REL_FONT_SIZE * STYLE_DEFAULT_FONT_SIZE / p_segment->style->i_font_size; } } /* Currently unsupported since we don't have access to the i_align flag here else if( psz_subtitle[1] == 'P' ) { if( psz_subtitle[3] == "1" ) i_align = SUBPICTURE_ALIGN_TOP; else if( psz_subtitle[3] == "0" ) i_align = SUBPICTURE_ALIGN_BOTTOM; } */ // Hide other {x:y} atrocities, notably {o:x} psz_subtitle = psz_tag_end + 1; } else { if( *psz_subtitle == '\n' || !strncasecmp( psz_subtitle, "\\n", 2 ) ) { if ( !AppendCharacter( p_segment, '\n' ) ) goto fail; if ( *psz_subtitle == '\n' ) psz_subtitle++; else psz_subtitle += 2; } else if( !strncasecmp( psz_subtitle, "\\h", 2 ) ) { if ( !AppendString( p_segment, "\xC2\xA0" ) ) goto fail; psz_subtitle += 2; } else { //FIXME: Highly inneficient AppendCharacter( p_segment, *psz_subtitle ); psz_subtitle++; } } } while ( p_stack ) PopStyle( &p_stack ); while ( p_tag_stack ) { tag_stack_t *p_tag = p_tag_stack; p_tag_stack = p_tag_stack->p_next; free( p_tag->psz_tagname ); free( p_tag ); } return p_first_segment; fail: text_segment_ChainDelete( p_first_segment ); return NULL; }