diff --git a/src/lib_ccx/ccx_encoders_helpers.c b/src/lib_ccx/ccx_encoders_helpers.c index de9b32997..64c8d9b64 100644 --- a/src/lib_ccx/ccx_encoders_helpers.c +++ b/src/lib_ccx/ccx_encoders_helpers.c @@ -19,6 +19,11 @@ struct word_list capitalization_list = { .capacity = 0, }; +static const char *ssa_cols[] = { + "", "{\\c&H00FF00&}", "{\\c&HFF0000&}", "{\\c&HFFFF00&}", + "{\\c&H0000FF&}", "{\\c&H00FFFF&}", "{\\c&HFF00FF&}", + "{\\c&H", "", ""}; + struct word_list profane = { .words = NULL, .len = 0, @@ -263,15 +268,24 @@ unsigned char *close_tag(struct encoder_ctx *ctx, unsigned char *buffer, char *t switch (cur) { case 'F': - buffer += encode_line(ctx, buffer, (unsigned char *)""); + if (ctx->write_format == CCX_OF_SSA) + buffer += encode_line(ctx, buffer, (unsigned char *)"{\\c}"); + else + buffer += encode_line(ctx, buffer, (unsigned char *)""); (*pchanged_font)--; break; case 'U': - buffer += encode_line(ctx, buffer, (unsigned char *)""); + if (ctx->write_format == CCX_OF_SSA) + buffer += encode_line(ctx, buffer, (unsigned char *)"{\\u0}"); + else + buffer += encode_line(ctx, buffer, (unsigned char *)""); (*punderlined)--; break; case 'I': - buffer += encode_line(ctx, buffer, (unsigned char *)""); + if (ctx->write_format == CCX_OF_SSA) + buffer += encode_line(ctx, buffer, (unsigned char *)"{\\i0}"); + else + buffer += encode_line(ctx, buffer, (unsigned char *)""); (*pitalics)--; break; } @@ -310,7 +324,16 @@ unsigned get_decoder_line_encoded(struct encoder_ctx *ctx, unsigned char *buffer // Add new font tag if (MAX_COLOR > its_color) - buffer += encode_line(ctx, buffer, (unsigned char *)color_text[its_color][1]); + { + if (ctx->write_format == CCX_OF_SSA) + { + buffer += encode_line(ctx, buffer, (unsigned char *)ssa_cols[its_color]); + } + else + { + buffer += encode_line(ctx, buffer, (unsigned char *)color_text[its_color][1]); + } + } else { ccx_common_logging.log_ftn("WARNING:get_decoder_line_encoded:Invalid Color index Selected %d\n", its_color); @@ -319,12 +342,45 @@ unsigned get_decoder_line_encoded(struct encoder_ctx *ctx, unsigned char *buffer if (its_color == COL_USERDEFINED) { - // The previous sentence doesn't copy the whole - // tag, just up to the quote before the color - buffer += encode_line(ctx, buffer, (unsigned char *)usercolor_rgb); - buffer += encode_line(ctx, buffer, (unsigned char *)"\">"); + if (ctx->write_format == CCX_OF_SSA) + { + if (strlen((char *)usercolor_rgb) == 7 && usercolor_rgb[0] == '#') + { + char bgr[7]; + bgr[0] = usercolor_rgb[5]; + bgr[1] = usercolor_rgb[6]; + bgr[2] = usercolor_rgb[3]; + bgr[3] = usercolor_rgb[4]; + bgr[4] = usercolor_rgb[1]; + bgr[5] = usercolor_rgb[2]; + bgr[6] = '\0'; + buffer += encode_line(ctx, buffer, (unsigned char *)bgr); + } + buffer += encode_line(ctx, buffer, (unsigned char *)"&}"); + } + else + { + buffer += encode_line(ctx, buffer, (unsigned char *)usercolor_rgb); + buffer += encode_line(ctx, buffer, (unsigned char *)"\">"); + } } - if (color_text[its_color][1][0]) // That means a was added to the buffer + int added_font = 0; + if (ctx->write_format == CCX_OF_SSA) + { + if (MAX_COLOR > its_color && ssa_cols[its_color][0]) + added_font = 1; + else if (its_color == COL_USERDEFINED) + added_font = 1; + } + else + { + if (MAX_COLOR > its_color && color_text[its_color][1][0]) + added_font = 1; + else if (its_color == COL_USERDEFINED) + added_font = 1; + } + + if (added_font) // That means a or {\c} was added to the buffer { strncat(tagstack, "F", sizeof(tagstack) - strlen(tagstack) - 1); changed_font++; @@ -335,7 +391,10 @@ unsigned get_decoder_line_encoded(struct encoder_ctx *ctx, unsigned char *buffer int is_underlined = data->fonts[line_num][i] & FONT_UNDERLINED; if (is_underlined && underlined == 0 && !ctx->no_type_setting) // Open underline { - buffer += encode_line(ctx, buffer, (unsigned char *)""); + if (ctx->write_format == CCX_OF_SSA) + buffer += encode_line(ctx, buffer, (unsigned char *)"{\\u1}"); + else + buffer += encode_line(ctx, buffer, (unsigned char *)""); strncat(tagstack, "U", sizeof(tagstack) - strlen(tagstack) - 1); underlined++; } @@ -347,7 +406,10 @@ unsigned get_decoder_line_encoded(struct encoder_ctx *ctx, unsigned char *buffer int has_ita = data->fonts[line_num][i] & FONT_ITALICS; if (has_ita && italics == 0 && !ctx->no_type_setting) // Open italics { - buffer += encode_line(ctx, buffer, (unsigned char *)""); + if (ctx->write_format == CCX_OF_SSA) + buffer += encode_line(ctx, buffer, (unsigned char *)"{\\i1}"); + else + buffer += encode_line(ctx, buffer, (unsigned char *)""); strncat(tagstack, "I", sizeof(tagstack) - strlen(tagstack) - 1); italics++; } diff --git a/src/lib_ccx/ccx_encoders_ssa.c b/src/lib_ccx/ccx_encoders_ssa.c index 321c82eb6..278763f52 100644 --- a/src/lib_ccx/ccx_encoders_ssa.c +++ b/src/lib_ccx/ccx_encoders_ssa.c @@ -4,6 +4,39 @@ #include "utility.h" #include "ccx_encoders_helpers.h" #include "ocr.h" +#include + +static int ccx_strncasecmp(const char *s1, const char *s2, size_t n) +{ + for (size_t i = 0; i < n; i++) + { + int c1 = tolower((unsigned char)s1[i]); + int c2 = tolower((unsigned char)s2[i]); + if (c1 != c2) + return c1 - c2; + if (c1 == 0) + return 0; + } + return 0; +} + +typedef struct +{ + const char *from; + size_t from_len; + const char *to; + size_t to_len; +} tag_map_t; + +static const tag_map_t html_to_ass[] = { + {"", 3, "{\\i1}", 5}, + {"", 4, "{\\i0}", 5}, + {"", 3, "{\\u1}", 5}, + {"", 4, "{\\u0}", 5}, + {"", 3, "{\\b1}", 5}, + {"", 4, "{\\b0}", 5}, +}; +#define NUM_TAG_MAPS (sizeof(html_to_ass) / sizeof(html_to_ass[0])) static void ass_position_from_row_col( int row, @@ -37,7 +70,7 @@ int write_stringz_as_ssa(char *string, struct encoder_ctx *context, LLONG ms_sta millis_to_time(ms_start, &h1, &m1, &s1, &ms1); millis_to_time(ms_end - 1, &h2, &m2, &s2, &ms2); // -1 To prevent overlapping with next line. - snprintf(timeline, sizeof(timeline), "Dialogue: 0,%02u:%02u:%02u.%01u,%02u:%02u:%02u.%02u,Default,,0000,0000,0000,,", + snprintf(timeline, sizeof(timeline), "Dialogue: 0,%u:%02u:%02u.%02u,%u:%02u:%02u.%02u,Default,,0000,0000,0000,,", h1, m1, s1, ms1 / 10, h2, m2, s2, ms2 / 10); used = encode_line(context, context->buffer, (unsigned char *)timeline); dbg_print(CCX_DMT_DECODER_608, "\n- - - ASS/SSA caption - - -\n"); @@ -45,7 +78,7 @@ int write_stringz_as_ssa(char *string, struct encoder_ctx *context, LLONG ms_sta write_wrapped(context->out->fh, context->buffer, used); int len = strlen(string); - unsigned char *unescaped = (unsigned char *)malloc(len + 1); + unsigned char *unescaped = (unsigned char *)malloc(len * 2 + 1); if (!unescaped) fatal(EXIT_NOT_ENOUGH_MEMORY, "In write_stringz_as_ssa() - not enough memory for unescaped buffer.\n"); unsigned char *el = (unsigned char *)malloc(len * 3 + 1); // Be generous @@ -61,15 +94,67 @@ int write_stringz_as_ssa(char *string, struct encoder_ctx *context, LLONG ms_sta { if (string[pos_r] == '\\' && string[pos_r + 1] == 'n') { - unescaped[pos_w] = 0; + unescaped[pos_w++] = 0; pos_r += 2; + continue; + } + + int matched = 0; + for (size_t i = 0; i < NUM_TAG_MAPS; i++) + { + const tag_map_t *m = &html_to_ass[i]; + if (ccx_strncasecmp(string + pos_r, m->from, m->from_len) == 0) + { + memcpy(unescaped + pos_w, m->to, m->to_len); + pos_w += m->to_len; + pos_r += m->from_len; + matched = 1; + break; + } + } + if (matched) + continue; + + if (ccx_strncasecmp(string + pos_r, "') + { + char r1 = string[pos_r + 14], r2 = string[pos_r + 15]; + char g1 = string[pos_r + 16], g2 = string[pos_r + 17]; + char b1 = string[pos_r + 18], b2 = string[pos_r + 19]; + char ssa_col[14]; + snprintf(ssa_col, sizeof(ssa_col), "{\\c&H%c%c%c%c%c%c&}", b1, b2, g1, g2, r1, r2); + memcpy(unescaped + pos_w, ssa_col, 13); + pos_w += 13; + pos_r += 22; // Skip up to and including '>' + } + else + { + while (pos_r < len && string[pos_r] != '>') + pos_r++; + if (pos_r < len) + pos_r++; // Skip '>' + } + continue; + } + + if (ccx_strncasecmp(string + pos_r, "", 7) == 0) { - unescaped[pos_w] = string[pos_r]; - pos_r++; + memcpy(unescaped + pos_w, "{\\c}", 4); + pos_w += 4; + pos_r += 7; + continue; } - pos_w++; + + unescaped[pos_w++] = string[pos_r++]; } unescaped[pos_w] = 0; // Now read the unescaped string (now several string'z and write them) @@ -134,7 +219,7 @@ int write_cc_bitmap_as_ssa(struct cc_subtitle *sub, struct encoder_ctx *context) millis_to_time(sub->start_time, &h1, &m1, &s1, &ms1); millis_to_time(sub->end_time - 1, &h2, &m2, &s2, &ms2); // -1 To prevent overlapping with next line. - snprintf(timeline, sizeof(timeline), "Dialogue: 0,%02u:%02u:%02u.%01u,%02u:%02u:%02u.%02u,Default,,0000,0000,0000,,", + snprintf(timeline, sizeof(timeline), "Dialogue: 0,%u:%02u:%02u.%02u,%u:%02u:%02u.%02u,Default,,0000,0000,0000,,", h1, m1, s1, ms1 / 10, h2, m2, s2, ms2 / 10); used = encode_line(context, context->buffer, (unsigned char *)timeline); write_wrapped(context->out->fh, context->buffer, used); @@ -220,7 +305,7 @@ int write_cc_buffer_as_ssa(struct eia608_screen *data, struct encoder_ctx *conte millis_to_time(data->start_time, &h1, &m1, &s1, &ms1); millis_to_time(data->end_time - 1, &h2, &m2, &s2, &ms2); // -1 To prevent overlapping with next line. char timeline[128]; - snprintf(timeline, sizeof(timeline), "Dialogue: 0,%02u:%02u:%02u.%01u,%02u:%02u:%02u.%02u,Default,,0000,0000,0000,,", + snprintf(timeline, sizeof(timeline), "Dialogue: 0,%u:%02u:%02u.%02u,%u:%02u:%02u.%02u,Default,,0000,0000,0000,,", h1, m1, s1, ms1 / 10, h2, m2, s2, ms2 / 10); used = encode_line(context, context->buffer, (unsigned char *)timeline);