X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Fssa_reader.cc;h=72ba13186ee8c6545f518a39a2ed712267c0b1c9;hb=49da5bc0d2548418f363ceaca148e29f7929682b;hp=93e96f28af7cf4dd7fe6e8dcc0cdd7acf32461f8;hpb=23a1a0395b31c4dc36d65531d02b7df240a25dce;p=libsub.git diff --git a/src/ssa_reader.cc b/src/ssa_reader.cc index 93e96f2..72ba131 100644 --- a/src/ssa_reader.cc +++ b/src/ssa_reader.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2016 Carl Hetherington + Copyright (C) 2016-2019 Carl Hetherington This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,15 +22,14 @@ #include "sub_assert.h" #include "raw_convert.h" #include "subtitle.h" +#include "compose.hpp" #include #include #include -#include #include #include using std::string; -using std::stringstream; using std::vector; using std::map; using std::cout; @@ -41,10 +40,9 @@ using namespace boost::algorithm; using namespace sub; /** @param s Subtitle string encoded in UTF-8 */ -SSAReader::SSAReader (string const & s) +SSAReader::SSAReader (string s) { - stringstream str (s); - this->read (boost::bind (&get_line_stringstream, &str)); + this->read (boost::bind(&get_line_string, &s)); } /** @param f Subtitle file encoded in UTF-8 */ @@ -53,6 +51,22 @@ SSAReader::SSAReader (FILE* f) this->read (boost::bind (&get_line_file, f)); } +Colour +h_colour (string s) +{ + /* There are both BGR and ABGR versions of these colours */ + if ((s.length() != 8 && s.length() != 10) || s[0] != '&' || s[1] != 'H') { + throw SSAError(String::compose("Badly formatted colour tag %1", s)); + } + int ir, ig, ib; + /* XXX: ignoring alpha channel here; note that 00 is opaque and FF is transparent */ + int const off = s.length() == 10 ? 4 : 2; + if (sscanf(s.c_str() + off, "%2x%2x%2x", &ib, &ig, &ir) < 3) { + throw SSAError(String::compose("Badly formatted colour tag %1", s)); + } + return sub::Colour(ir / 255.0, ig / 255.0, ib / 255.0); +} + class Style { public: @@ -61,6 +75,8 @@ public: , primary_colour (255, 255, 255) , bold (false) , italic (false) + , underline (false) + , horizontal_reference (HORIZONTAL_CENTRE_OF_SCREEN) , vertical_reference (BOTTOM_OF_SCREEN) , vertical_margin (0) {} @@ -70,13 +86,15 @@ public: , primary_colour (255, 255, 255) , bold (false) , italic (false) + , underline (false) + , horizontal_reference (HORIZONTAL_CENTRE_OF_SCREEN) , vertical_reference (BOTTOM_OF_SCREEN) , vertical_margin (0) { vector keys; - split (keys, format_line, is_any_of (",")); + split (keys, format_line, boost::is_any_of (",")); vector style; - split (style, style_line, is_any_of (",")); + split (style, style_line, boost::is_any_of (",")); SUB_ASSERT (!keys.empty()); SUB_ASSERT (!style.empty()); @@ -92,25 +110,38 @@ public: } else if (keys[i] == "Fontsize") { font_size = raw_convert (style[i]); } else if (keys[i] == "PrimaryColour") { - primary_colour = colour (raw_convert (style[i])); + primary_colour = colour (style[i]); } else if (keys[i] == "BackColour") { - back_colour = colour (raw_convert (style[i])); + back_colour = colour (style[i]); } else if (keys[i] == "Bold") { 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]) - 1) % 3) { + case 0: + horizontal_reference = LEFT_OF_SCREEN; + break; + case 1: + horizontal_reference = HORIZONTAL_CENTRE_OF_SCREEN; + break; + case 2: + horizontal_reference = RIGHT_OF_SCREEN; + break; + } switch (raw_convert (style[i]) & 12) { case 4: vertical_reference = TOP_OF_SCREEN; break; case 8: - vertical_reference = CENTRE_OF_SCREEN; + vertical_reference = VERTICAL_CENTRE_OF_SCREEN; break; case 0: vertical_reference = BOTTOM_OF_SCREEN; @@ -130,18 +161,27 @@ public: optional back_colour; bool bold; bool italic; + bool underline; optional effect; + HorizontalReference horizontal_reference; VerticalReference vertical_reference; int vertical_margin; private: - Colour colour (int c) const + Colour colour (string c) const { - return Colour ( - ((c & 0x0000ff) >> 0) / 255.0, - ((c & 0x00ff00) >> 8) / 255.0, - ((c & 0xff0000) >> 16) / 255.0 - ); + if (c.length() > 0 && c[0] == '&') { + /* &Hbbggrr or &Haabbggrr */ + return h_colour (c); + } else { + /* integer */ + int i = raw_convert(c); + return Colour ( + ((i & 0x0000ff) >> 0) / 255.0, + ((i & 0x00ff00) >> 8) / 255.0, + ((i & 0xff0000) >> 16) / 255.0 + ); + } } }; @@ -164,7 +204,7 @@ SSAReader::parse_time (string t) const * @return List of RawSubtitles to represent line with vertical reference TOP_OF_SUBTITLE. */ list -SSAReader::parse_line (RawSubtitle base, string line) +SSAReader::parse_line (RawSubtitle base, string line, int play_res_x, int play_res_y) { enum { TEXT, @@ -194,9 +234,11 @@ SSAReader::parse_line (RawSubtitle base, string line) /* 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; + if (line.length() > 0) { + for (size_t i = 0; i < line.length() - 1; ++i) { + if (line[i] == '\\' && (line[i+1] == 'n' || line[i+1] == 'N')) { + ++line_breaks; + } } } @@ -209,7 +251,7 @@ SSAReader::parse_line (RawSubtitle base, string line) case TOP_OF_SUBTITLE: /* Nothing to do */ break; - case CENTRE_OF_SCREEN: + case VERTICAL_CENTRE_OF_SCREEN: current.vertical_position.proportional = current.vertical_position.proportional.get() - ((line_breaks + 1) * line_size) / 2; break; case BOTTOM_OF_SCREEN: @@ -230,7 +272,7 @@ 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 = ""; @@ -243,14 +285,38 @@ SSAReader::parse_line (RawSubtitle base, string line) current.bold = true; } else if (style == "\\b0") { current.bold = false; + } else if (style == "\\u1") { + current.underline = true; + } else if (style == "\\u0") { + current.underline = 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; + current.vertical_position.reference = sub::VERTICAL_CENTRE_OF_SCREEN; } else if (style == "\\an7" || style == "\\an8" || style == "\\an9") { current.vertical_position.reference = sub::TOP_OF_SCREEN; + } else if (boost::starts_with(style, "\\pos")) { + vector bits; + boost::algorithm::split (bits, style, boost::is_any_of("(,")); + SUB_ASSERT (bits.size() == 3); + current.horizontal_position.reference = sub::LEFT_OF_SCREEN; + current.horizontal_position.proportional = raw_convert(bits[1]) / play_res_x; + current.vertical_position.reference = sub::TOP_OF_SCREEN; + current.vertical_position.proportional = raw_convert(bits[2]) / play_res_y; + } else if (boost::starts_with(style, "\\fs")) { + SUB_ASSERT (style.length() > 3); + current.font_size.set_points (raw_convert(style.substr(3))); + } else if (boost::starts_with(style, "\\c")) { + /* \c&Hbbggrr& */ + if (style.length() <= 2) { + throw SSAError(String::compose("Badly formatted colour tag %1", style)); + } + current.colour = h_colour (style.substr(2, style.length() - 3)); } style = ""; + } + + if (c == '}') { state = TEXT; } else { style += c; @@ -290,6 +356,7 @@ SSAReader::read (function ()> get_line) EVENTS } part = INFO; + int play_res_x = 288; int play_res_y = 288; map styles; string style_format_line; @@ -328,7 +395,9 @@ SSAReader::read (function ()> get_line) switch (part) { case INFO: - if (type == "PlayResY") { + if (type == "PlayResX") { + play_res_x = raw_convert (body); + } else if (type == "PlayResY") { play_res_y = raw_convert (body); } break; @@ -385,13 +454,15 @@ 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; + sub.horizontal_position.reference = style.horizontal_reference; 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])) { + BOOST_FOREACH (sub::RawSubtitle j, parse_line (sub, event[i], play_res_x, play_res_y)) { _subs.push_back (j); } }