+ *psz_text++ = *psz_subtitle;
+ }
+
+ /* Security fix: Account for the case where input ends early */
+ if( *psz_subtitle == '\0' ) break;
+
+ psz_subtitle++;
+ }
+ *psz_text = '\0';
+ char *psz = realloc( psz_text_start, strlen( psz_text_start ) + 1 );
+ if( psz ) psz_text_start = psz;
+
+ return psz_text_start;
+}
+
+/* Try to respect any style tags present in the subtitle string. The main
+ * problem here is a lack of adequate specs for the subtitle formats.
+ * SSA/ASS and USF are both detail spec'ed -- but they are handled elsewhere.
+ * SAMI has a detailed spec, but extensive rework is needed in the demux
+ * code to prevent all this style information being excised, as it presently
+ * does.
+ * That leaves the others - none of which were (I guess) originally intended
+ * to be carrying style information. Over time people have used them that way.
+ * In the absence of specifications from which to work, the tags supported
+ * have been restricted to the simple set permitted by the USF DTD, ie. :
+ * Basic: <br>, <i>, <b>, <u>, <s>
+ * Extended: <font>
+ * Attributes: face
+ * family
+ * size
+ * color
+ * outline-color
+ * shadow-color
+ * outline-level
+ * shadow-level
+ * back-color
+ * alpha
+ * There is also the further restriction that the subtitle be well-formed
+ * as an XML entity, ie. the HTML sentence:
+ * <b><i>Bold and Italics</b></i>
+ * doesn't qualify because the tags aren't nested one inside the other.
+ * <text> tags are automatically added to the output to ensure
+ * well-formedness.
+ * If the text doesn't qualify for any reason, a NULL string is
+ * returned, and the rendering engine will fall back to the
+ * plain text version of the subtitle.
+ */
+static void HtmlNPut( char **ppsz_html, const char *psz_text, int i_max )
+{
+ const int i_len = strlen(psz_text);
+
+ strncpy( *ppsz_html, psz_text, i_max );
+ *ppsz_html += __MIN(i_max,i_len);
+}
+
+static void HtmlPut( char **ppsz_html, const char *psz_text )
+{
+ strcpy( *ppsz_html, psz_text );
+ *ppsz_html += strlen(psz_text);
+}
+static void HtmlCopy( char **ppsz_html, char **ppsz_subtitle, const char *psz_text )
+{
+ HtmlPut( ppsz_html, psz_text );
+ *ppsz_subtitle += strlen(psz_text);
+}
+
+static char *CreateHtmlSubtitle( int *pi_align, char *psz_subtitle )
+{
+ /* */
+ char *psz_tag = malloc( ( strlen( psz_subtitle ) / 3 ) + 1 );
+ if( !psz_tag )
+ return NULL;
+ psz_tag[ 0 ] = '\0';
+
+ /* */
+ //Oo + 100 ???
+ size_t i_buf_size = strlen( psz_subtitle ) + 100;
+ char *psz_html_start = malloc( i_buf_size );
+ char *psz_html = psz_html_start;
+ if( psz_html_start == NULL )
+ {
+ free( psz_tag );
+ return NULL;
+ }
+ psz_html[0] = '\0';
+
+ bool b_has_align = false;
+
+ HtmlPut( &psz_html, "<text>" );
+
+ /* */
+ while( *psz_subtitle )
+ {
+ if( *psz_subtitle == '\n' )
+ {
+ HtmlPut( &psz_html, "<br/>" );
+ psz_subtitle++;
+ }
+ else if( *psz_subtitle == '<' )
+ {
+ if( !strncasecmp( psz_subtitle, "<br/>", 5 ))
+ {
+ HtmlCopy( &psz_html, &psz_subtitle, "<br/>" );
+ }
+ else if( !strncasecmp( psz_subtitle, "<b>", 3 ) )
+ {
+ HtmlCopy( &psz_html, &psz_subtitle, "<b>" );
+ strcat( psz_tag, "b" );
+ }
+ else if( !strncasecmp( psz_subtitle, "<i>", 3 ) )
+ {
+ HtmlCopy( &psz_html, &psz_subtitle, "<i>" );
+ strcat( psz_tag, "i" );
+ }
+ else if( !strncasecmp( psz_subtitle, "<u>", 3 ) )
+ {
+ HtmlCopy( &psz_html, &psz_subtitle, "<u>" );
+ strcat( psz_tag, "u" );
+ }
+ else if( !strncasecmp( psz_subtitle, "<s>", 3 ) )
+ {
+ HtmlCopy( &psz_html, &psz_subtitle, "<s>" );
+ strcat( psz_tag, "s" );
+ }
+ else if( !strncasecmp( psz_subtitle, "<font ", 6 ))
+ {
+ const char *psz_attribs[] = { "face=", "family=", "size=",
+ "color=", "outline-color=", "shadow-color=",
+ "outline-level=", "shadow-level=", "back-color=",
+ "alpha=", NULL };
+
+ HtmlCopy( &psz_html, &psz_subtitle, "<font " );
+ strcat( psz_tag, "f" );
+
+ while( *psz_subtitle != '>' )
+ {
+ int k;
+
+ for( k=0; psz_attribs[ k ]; k++ )
+ {
+ int i_len = strlen( psz_attribs[ k ] );
+
+ if( !strncasecmp( psz_subtitle, psz_attribs[k], i_len ) )
+ {
+ /* */
+ HtmlPut( &psz_html, psz_attribs[k] );
+ psz_subtitle += i_len;
+
+ /* */
+ if( *psz_subtitle == '"' )
+ {
+ psz_subtitle++;
+ i_len = strcspn( psz_subtitle, "\"" );
+ }
+ else
+ {
+ i_len = strcspn( psz_subtitle, " \t>" );
+ }
+ HtmlPut( &psz_html, "\"" );
+ if( !strcmp( psz_attribs[ k ], "color=" ) && *psz_subtitle >= '0' && *psz_subtitle <= '9' )
+ HtmlPut( &psz_html, "#" );
+ HtmlNPut( &psz_html, psz_subtitle, i_len );
+ HtmlPut( &psz_html, "\"" );
+
+ psz_subtitle += i_len;
+ if( *psz_subtitle == '\"' )
+ psz_subtitle++;
+ break;
+ }
+ }
+ if( psz_attribs[ k ] == NULL )
+ {
+ /* Jump over unrecognised tag */
+ int i_len = strcspn( psz_subtitle, "\"" );
+ if( psz_subtitle[i_len] == '\"' )
+ {
+ i_len += 1 + strcspn( &psz_subtitle[i_len + 1], "\"" );
+ if( psz_subtitle[i_len] == '\"' )
+ i_len++;
+ }
+ psz_subtitle += i_len;
+ }
+ while (*psz_subtitle == ' ')
+ *psz_html++ = *psz_subtitle++;
+ }
+ *psz_html++ = *psz_subtitle++;
+ }
+ else if( !strncmp( psz_subtitle, "</", 2 ))
+ {
+ bool b_match = false;
+ bool b_ignore = false;
+ int i_len = strlen( psz_tag ) - 1;
+ char *psz_lastTag = NULL;
+
+ if( i_len >= 0 )
+ {
+ psz_lastTag = psz_tag + i_len;
+ i_len = 0;
+
+ switch( *psz_lastTag )
+ {
+ case 'b':
+ b_match = !strncasecmp( psz_subtitle, "</b>", 4 );
+ i_len = 4;
+ break;
+ case 'i':
+ b_match = !strncasecmp( psz_subtitle, "</i>", 4 );
+ i_len = 4;
+ break;
+ case 'u':
+ b_match = !strncasecmp( psz_subtitle, "</u>", 4 );
+ i_len = 4;
+ break;
+ case 's':
+ b_match = !strncasecmp( psz_subtitle, "</s>", 4 );
+ i_len = 4;
+ break;
+ case 'f':
+ b_match = !strncasecmp( psz_subtitle, "</font>", 7 );
+ i_len = 7;
+ break;
+ case 'I':
+ i_len = strcspn( psz_subtitle, ">" );
+ b_match = psz_subtitle[i_len] == '>';
+ b_ignore = true;
+ if( b_match )
+ i_len++;
+ break;
+ }
+ }
+ if( !b_match )
+ {
+ /* Not well formed -- kill everything */
+ free( psz_html_start );
+ psz_html_start = NULL;
+ break;
+ }
+ *psz_lastTag = '\0';
+ if( !b_ignore )
+ HtmlNPut( &psz_html, psz_subtitle, i_len );
+
+ psz_subtitle += i_len;
+ }
+ else if( ( psz_subtitle[1] < 'a' || psz_subtitle[1] > 'z' ) &&
+ ( psz_subtitle[1] < 'A' || psz_subtitle[1] > 'Z' ) )
+ {
+ /* We have a single < */
+ HtmlPut( &psz_html, "<" );
+ psz_subtitle++;
+ }
+ else
+ {
+ /* We have an unknown tag or a single < */
+
+ /* Search for the next tag or end of tag or end of string */
+ char *psz_stop = psz_subtitle + 1 + strcspn( &psz_subtitle[1], "<>" );
+ char *psz_closing = strstr( psz_subtitle, "/>" );
+
+ if( psz_closing && psz_closing < psz_stop )
+ {
+ /* We have a self closed tag, remove it */
+ psz_subtitle = &psz_closing[2];
+ }
+ else if( *psz_stop == '>' )
+ {
+ char psz_match[256];
+
+ snprintf( psz_match, sizeof(psz_match), "</%s", &psz_subtitle[1] );
+ psz_match[strcspn( psz_match, " \t>" )] = '\0';
+
+ if( strstr( psz_subtitle, psz_match ) )
+ {
+ /* We have the closing tag, ignore it TODO */
+ psz_subtitle = &psz_stop[1];
+ strcat( psz_tag, "I" );
+ }
+ else
+ {
+ int i_len = psz_stop + 1 - psz_subtitle;
+
+ /* Copy the whole data */
+ for( ; i_len > 0; i_len--, psz_subtitle++ )
+ {
+ if( *psz_subtitle == '<' )
+ HtmlPut( &psz_html, "<" );
+ else if( *psz_subtitle == '>' )
+ HtmlPut( &psz_html, ">" );
+ else
+ *psz_html++ = *psz_subtitle;
+ }
+ }
+ }
+ else
+ {
+ /* We have a single < */
+ HtmlPut( &psz_html, "<" );
+ psz_subtitle++;
+ }
+ }
+ }
+ else if( *psz_subtitle == '&' )
+ {
+ if( !strncasecmp( psz_subtitle, "<", 4 ))
+ {
+ HtmlCopy( &psz_html, &psz_subtitle, "<" );
+ }
+ else if( !strncasecmp( psz_subtitle, ">", 4 ))
+ {
+ HtmlCopy( &psz_html, &psz_subtitle, ">" );
+ }
+ else if( !strncasecmp( psz_subtitle, "&", 5 ))
+ {
+ HtmlCopy( &psz_html, &psz_subtitle, "&" );
+ }
+ else
+ {
+ HtmlPut( &psz_html, "&" );
+ psz_subtitle++;
+ }
+ }
+ else if( *psz_subtitle == '>' )
+ {
+ HtmlPut( &psz_html, ">" );
+ psz_subtitle++;
+ }
+ 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;
+ }
+ else if( psz_subtitle[0] == '{' &&
+ ( psz_subtitle[1] == 'Y' || psz_subtitle[1] == 'y' )
+ && psz_subtitle[2] == ':' && strchr( psz_subtitle, '}' ) )
+ {
+ // FIXME: We don't do difference between Y and y, and we should.
+ if( psz_subtitle[3] == 'i' )
+ {
+ HtmlPut( &psz_html, "<i>" );
+ strcat( psz_tag, "i" );
+ }
+ if( psz_subtitle[3] == 'b' )
+ {
+ HtmlPut( &psz_html, "<b>" );
+ strcat( psz_tag, "b" );
+ }
+ if( psz_subtitle[3] == 'u' )
+ {
+ HtmlPut( &psz_html, "<u>" );
+ strcat( psz_tag, "u" );
+ }
+ psz_subtitle = strchr( psz_subtitle, '}' ) + 1;
+ }
+ else if( psz_subtitle[0] == '{' && psz_subtitle[2] == ':' && strchr( psz_subtitle, '}' ) )
+ {
+ // Hide other {x:y} atrocities, like {c:$bbggrr} or {P:x}
+ psz_subtitle = strchr( psz_subtitle, '}' ) + 1;
+ }
+ else if( psz_subtitle[0] == '\\' && psz_subtitle[1] )
+ {
+ if( psz_subtitle[1] == 'N' || psz_subtitle[1] == 'n' )