X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Fssa_reader.cc;h=630eb29a47ff8442cd6a4a5ed13c4d93087990ad;hb=0a1b4c69c23d6e4838707fe74f3a55aa7b6c8649;hp=859a00ecba18cbb36ab79ba2a51ba01151ae3658;hpb=c24798d1ab4bb75c8ee0c8b8663b11e599df1c25;p=libsub.git diff --git a/src/ssa_reader.cc b/src/ssa_reader.cc index 859a00e..630eb29 100644 --- a/src/ssa_reader.cc +++ b/src/ssa_reader.cc @@ -57,17 +57,23 @@ class Style { public: Style () - : font_size (24) + : font_size (72) , primary_colour (255, 255, 255) , bold (false) , italic (false) + , underline (false) + , vertical_reference (BOTTOM_OF_SCREEN) + , vertical_margin (0) {} Style (string format_line, string style_line) - : font_size (24) + : font_size (72) , primary_colour (255, 255, 255) , bold (false) , italic (false) + , underline (false) + , vertical_reference (BOTTOM_OF_SCREEN) + , vertical_margin (0) { vector keys; split (keys, format_line, is_any_of (",")); @@ -95,10 +101,27 @@ public: bold = style[i] == "-1"; } else if (keys[i] == "Italic") { italic = style[i] == "-1"; + } else if (keys[i] == "Underline") { + underline = style[i] == "-1"; } else if (keys[i] == "BorderStyle") { if (style[i] == "1") { effect = SHADOW; } + } else if (keys[i] == "Alignment") { + /* These values from libass' source code */ + switch (raw_convert (style[i]) & 12) { + case 4: + vertical_reference = TOP_OF_SCREEN; + break; + case 8: + vertical_reference = CENTRE_OF_SCREEN; + break; + case 0: + vertical_reference = BOTTOM_OF_SCREEN; + break; + } + } else if (keys[i] == "MarginV") { + vertical_margin = raw_convert (style[i]); } } } @@ -111,7 +134,10 @@ public: optional back_colour; bool bold; bool italic; + bool underline; optional effect; + VerticalReference vertical_reference; + int vertical_margin; private: Colour colour (int c) const @@ -139,7 +165,7 @@ SSAReader::parse_time (string t) const } /** @param base RawSubtitle filled in with any required common values. - * @param line SSA line string. + * @param line SSA line string (i.e. just the subtitle, possibly with embedded stuff) * @return List of RawSubtitles to represent line with vertical reference TOP_OF_SUBTITLE. */ list @@ -155,10 +181,46 @@ SSAReader::parse_line (RawSubtitle base, string line) RawSubtitle current = base; string style; - current.vertical_position.line = 0; - /* XXX: arbitrary */ - current.vertical_position.lines = 32; - current.vertical_position.reference = TOP_OF_SUBTITLE; + if (!current.vertical_position.reference) { + current.vertical_position.reference = BOTTOM_OF_SCREEN; + } + + if (!current.vertical_position.proportional) { + current.vertical_position.proportional = 0; + } + + /* We must have a font size, as there could be a margin specified + in pixels and in that case we must know how big the subtitle + lines are to work out the position on screen. + */ + if (!current.font_size.points()) { + current.font_size.set_points (72); + } + + /* Count the number of line breaks */ + int line_breaks = 0; + for (size_t i = 0; i < line.length() - 1; ++i) { + if (line[i] == '\\' && (line[i+1] == 'n' || line[i+1] == 'N')) { + ++line_breaks; + } + } + + /* Imagine that the screen is 792 points (i.e. 11 inches) high (as with DCP) */ + double const line_size = current.font_size.proportional(792) * 1.2; + + /* Tweak vertical_position accordingly */ + switch (current.vertical_position.reference.get()) { + case TOP_OF_SCREEN: + case TOP_OF_SUBTITLE: + /* Nothing to do */ + break; + case CENTRE_OF_SCREEN: + current.vertical_position.proportional = current.vertical_position.proportional.get() - ((line_breaks + 1) * line_size) / 2; + break; + case BOTTOM_OF_SCREEN: + current.vertical_position.proportional = current.vertical_position.proportional.get() + line_breaks * line_size; + break; + } for (size_t i = 0; i < line.length(); ++i) { char const c = line[i]; @@ -173,27 +235,48 @@ SSAReader::parse_line (RawSubtitle base, string line) } break; case STYLE: - if (c == '}') { + if (c == '}' || c == '\\') { if (!current.text.empty ()) { subs.push_back (current); current.text = ""; } - if (style == "i1") { + if (style == "\\i1") { current.italic = true; - } else if (style == "i0") { + } else if (style == "\\i0" || style == "\\i") { current.italic = false; + } else if (style == "\\b1") { + current.bold = true; + } else if (style == "\\b0") { + current.bold = false; + } else if (style == "\\an1" || style == "\\an2" || style == "\\an3") { + current.vertical_position.reference = sub::BOTTOM_OF_SCREEN; + } else if (style == "\\an4" || style == "\\an5" || style == "\\an6") { + current.vertical_position.reference = sub::CENTRE_OF_SCREEN; + } else if (style == "\\an7" || style == "\\an8" || style == "\\an9") { + current.vertical_position.reference = sub::TOP_OF_SCREEN; } + style = ""; + } + + if (c == '}') { state = TEXT; } else { style += c; } break; case BACKSLASH: - if ((c == 'n' || c == 'N') && !current.text.empty ()) { - subs.push_back (current); - current.text = ""; - current.vertical_position.line = current.vertical_position.line.get() + 1; + if (c == 'n' || c == 'N') { + if (!current.text.empty ()) { + subs.push_back (current); + current.text = ""; + } + /* Move down one line (1.2 times the font size) */ + if (current.vertical_position.reference.get() == BOTTOM_OF_SCREEN) { + current.vertical_position.proportional = current.vertical_position.proportional.get() - line_size; + } else { + current.vertical_position.proportional = current.vertical_position.proportional.get() + line_size; + } } state = TEXT; break; @@ -216,6 +299,7 @@ SSAReader::read (function ()> get_line) EVENTS } part = INFO; + int play_res_y = 288; map styles; string style_format_line; vector event_format; @@ -237,7 +321,7 @@ SSAReader::read (function ()> get_line) /* Section heading */ if (line.get() == "[Script Info]") { part = INFO; - } else if (line.get() == "[V4 Styles]") { + } else if (line.get() == "[V4 Styles]" || line.get() == "[V4+ Styles]") { part = STYLES; } else if (line.get() == "[Events]") { part = EVENTS; @@ -247,12 +331,15 @@ SSAReader::read (function ()> get_line) size_t const colon = line->find (":"); SUB_ASSERT (colon != string::npos); - SUB_ASSERT (line->length() > colon + 1); string const type = line->substr (0, colon); - string const body = line->substr (colon + 2); + string body = line->substr (colon + 1); + trim (body); switch (part) { case INFO: + if (type == "PlayResY") { + play_res_y = raw_convert (body); + } break; case STYLES: if (type == "Format") { @@ -274,6 +361,15 @@ SSAReader::read (function ()> get_line) vector event; split (event, body, is_any_of (",")); + /* There may be commas in the subtitle part; reassemble any extra parts + from when we just split it. + */ + while (event.size() > event_format.size()) { + string const ex = event.back (); + event.pop_back (); + event.back() += "," + ex; + } + SUB_ASSERT (!event.empty()); SUB_ASSERT (event_format.size() == event.size()); @@ -286,6 +382,10 @@ SSAReader::read (function ()> get_line) } else if (event_format[i] == "End") { sub.to = parse_time (event[i]); } else if (event_format[i] == "Style") { + /* libass trims leading '*'s from style names, commenting that + "they seem to mean literally nothing". Go figure... + */ + trim_left_if (event[i], boost::is_any_of ("*")); SUB_ASSERT (styles.find(event[i]) != styles.end()); Style style = styles[event[i]]; sub.font = style.font_name; @@ -294,13 +394,12 @@ SSAReader::read (function ()> get_line) sub.effect_colour = style.back_colour; sub.bold = style.bold; sub.italic = style.italic; + sub.underline = style.underline; sub.effect = style.effect; - - /* XXX: arbitrary */ - sub.vertical_position.lines = 32; - sub.vertical_position.reference = TOP_OF_SUBTITLE; - sub.vertical_position.line = 0; - + sub.vertical_position.reference = style.vertical_reference; + sub.vertical_position.proportional = float(style.vertical_margin) / play_res_y; + } else if (event_format[i] == "MarginV") { + sub.vertical_position.proportional = raw_convert(event[i]) / play_res_y; } else if (event_format[i] == "Text") { BOOST_FOREACH (sub::RawSubtitle j, parse_line (sub, event[i])) { _subs.push_back (j);