Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 73 additions & 11 deletions src/lib_ccx/ccx_encoders_helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 *)"</font>");
if (ctx->write_format == CCX_OF_SSA)
buffer += encode_line(ctx, buffer, (unsigned char *)"{\\c}");
else
buffer += encode_line(ctx, buffer, (unsigned char *)"</font>");
(*pchanged_font)--;
break;
case 'U':
buffer += encode_line(ctx, buffer, (unsigned char *)"</u>");
if (ctx->write_format == CCX_OF_SSA)
buffer += encode_line(ctx, buffer, (unsigned char *)"{\\u0}");
else
buffer += encode_line(ctx, buffer, (unsigned char *)"</u>");
(*punderlined)--;
break;
case 'I':
buffer += encode_line(ctx, buffer, (unsigned char *)"</i>");
if (ctx->write_format == CCX_OF_SSA)
buffer += encode_line(ctx, buffer, (unsigned char *)"{\\i0}");
else
buffer += encode_line(ctx, buffer, (unsigned char *)"</i>");
(*pitalics)--;
break;
}
Expand Down Expand Up @@ -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);
Expand All @@ -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
// <font> 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 <font> 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 <font> or {\c} was added to the buffer
{
strncat(tagstack, "F", sizeof(tagstack) - strlen(tagstack) - 1);
changed_font++;
Expand All @@ -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 *)"<u>");
if (ctx->write_format == CCX_OF_SSA)
buffer += encode_line(ctx, buffer, (unsigned char *)"{\\u1}");
else
buffer += encode_line(ctx, buffer, (unsigned char *)"<u>");
strncat(tagstack, "U", sizeof(tagstack) - strlen(tagstack) - 1);
underlined++;
}
Expand All @@ -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 *)"<i>");
if (ctx->write_format == CCX_OF_SSA)
buffer += encode_line(ctx, buffer, (unsigned char *)"{\\i1}");
else
buffer += encode_line(ctx, buffer, (unsigned char *)"<i>");
strncat(tagstack, "I", sizeof(tagstack) - strlen(tagstack) - 1);
italics++;
}
Expand Down
103 changes: 94 additions & 9 deletions src/lib_ccx/ccx_encoders_ssa.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,39 @@
#include "utility.h"
#include "ccx_encoders_helpers.h"
#include "ocr.h"
#include <ctype.h>

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[] = {
{"<i>", 3, "{\\i1}", 5},
{"</i>", 4, "{\\i0}", 5},
{"<u>", 3, "{\\u1}", 5},
{"</u>", 4, "{\\u0}", 5},
{"<b>", 3, "{\\b1}", 5},
{"</b>", 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,
Expand Down Expand Up @@ -37,15 +70,15 @@ 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");
dbg_print(CCX_DMT_DECODER_608, "%s", timeline);

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
Expand All @@ -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, "<font color=\"#", 14) == 0)
{
if (pos_r + 21 < len && string[pos_r + 20] == '"' && string[pos_r + 21] == '>')
{
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, "<font", 5) == 0)
{
while (pos_r < len && string[pos_r] != '>')
pos_r++;
if (pos_r < len)
pos_r++;
continue;
}
else
if (ccx_strncasecmp(string + pos_r, "</font>", 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)
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down
Loading