X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Fdcp_reader.cc;h=a3ee8e54753e568d87a2c47343de1680abd7e1a7;hb=1c82568fa688351444b56dd190de33df29f688f5;hp=3634d934941a0951a8777ac87c55a91a10cfd2b1;hpb=4ada3e7583dfdc658dbebca3c3603be1e3477c12;p=libsub.git diff --git a/src/dcp_reader.cc b/src/dcp_reader.cc index 3634d93..a3ee8e5 100644 --- a/src/dcp_reader.cc +++ b/src/dcp_reader.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2014 Carl Hetherington + Copyright (C) 2014-2018 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 @@ -18,348 +18,122 @@ */ #include "dcp_reader.h" -#include "vertical_reference.h" -#include "xml.h" -#include -#include -#include +#include "compose.hpp" +#include "exceptions.h" +#include +#include +#include +#include +#include -using std::string; using std::list; -using std::vector; -using std::istream; using std::cout; +using std::string; +using std::exception; using boost::shared_ptr; using boost::optional; -using boost::lexical_cast; -using boost::is_any_of; +using boost::dynamic_pointer_cast; using namespace sub; -namespace sub { - -class DCPFont; - -/** @class DCPText - * @brief A DCP subtitle <Text> node. - */ -class DCPText -{ -public: - DCPText () - : v_position (0) - , v_align (TOP_OF_SCREEN) - {} - - DCPText (shared_ptr node) - : v_align (CENTRE_OF_SCREEN) - { - text = node->content (); - v_position = node->number_attribute ("VPosition"); - optional v = node->optional_string_attribute ("VAlign"); - if (v) { - v_align = string_to_vertical_reference (v.get ()); - } - - font_nodes = type_children (node, "Font"); - } - - float v_position; - VerticalReference v_align; - string text; - shared_ptr foo; - list > font_nodes; -}; - -/** @class DCPSubtitle - * @brief A DCP subtitle <Subtitle> node. - */ -class DCPSubtitle -{ -public: - DCPSubtitle () {} - DCPSubtitle (shared_ptr node) - { - in = MetricTime (time (node->string_attribute ("TimeIn"))); - out = MetricTime (time (node->string_attribute ("TimeOut"))); - font_nodes = type_children (node, "Font"); - text_nodes = type_children (node, "Text"); - fade_up_time = fade_time (node, "FadeUpTime"); - fade_down_time = fade_time (node, "FadeDownTime"); - } - - MetricTime in; - MetricTime out; - MetricTime fade_up_time; - MetricTime fade_down_time; - list > font_nodes; - list > text_nodes; - -private: - static MetricTime time (std::string time) - { - vector b; - split (b, time, is_any_of (":")); - if (b.size() != 4) { - boost::throw_exception (XMLError ("unrecognised time specification")); - } - - return MetricTime (lexical_cast(b[0]), lexical_cast (b[1]), lexical_cast (b[2]), lexical_cast (b[3]) * 4); - } - - MetricTime fade_time (shared_ptr node, string name) - { - string const u = node->optional_string_attribute (name).get_value_or (""); - MetricTime t; - - if (u.empty ()) { - t = MetricTime (0, 0, 0, 80); - } else if (u.find (":") != string::npos) { - t = time (u); - } else { - t = MetricTime (0, 0, 0, lexical_cast(u) * 4); - } - - if (t > MetricTime (0, 0, 8, 0)) { - t = MetricTime (0, 0, 8, 0); - } - - return t; - } -}; - -/** @class DCPFont - * @brief A DCP subtitle <Font> node. - */ -class DCPFont -{ -public: - DCPFont () - : size (0) - {} - - DCPFont (shared_ptr node) - { - text = node->content (); - - id = node->optional_string_attribute ("Id").get_value_or (""); - size = node->optional_number_attribute ("Size").get_value_or (0); - italic = node->optional_bool_attribute ("Italic"); - optional c = node->optional_string_attribute ("Color"); - if (c) { - colour = Colour (c.get ()); - } - optional const e = node->optional_string_attribute ("Effect"); - if (e) { - effect = string_to_effect (e.get ()); - } - c = node->optional_string_attribute ( "EffectColor"); - if (c) { - effect_colour = Colour (c.get ()); - } - subtitle_nodes = type_children (node, "Subtitle"); - font_nodes = type_children (node, "Font"); - text_nodes = type_children (node, "Text"); - } - - DCPFont (list > const & font_nodes) - : size (0) - , italic (false) - , colour ("FFFFFFFF") - , effect_colour ("FFFFFFFF") - { - for (list >::const_iterator i = font_nodes.begin(); i != font_nodes.end(); ++i) { - if (!(*i)->id.empty ()) { - id = (*i)->id; - } - if ((*i)->size != 0) { - size = (*i)->size; - } - if ((*i)->italic) { - italic = (*i)->italic.get (); - } - if ((*i)->colour) { - colour = (*i)->colour.get (); - } - if ((*i)->effect) { - effect = (*i)->effect.get (); - } - if ((*i)->effect_colour) { - effect_colour = (*i)->effect_colour.get (); - } - } - } - - string text; - string id; - int size; - optional italic; - optional colour; - optional effect; - optional effect_colour; - - list > subtitle_nodes; - list > font_nodes; - list > text_nodes; -}; - -/** @class DCPLoadFont - * @brief A DCP subtitle <LoadFont> node. - */ -class DCPLoadFont +static Time +dcp_to_sub_time (dcp::Time t) { -public: - DCPLoadFont () {} - DCPLoadFont (shared_ptr node) - { - id = node->string_attribute ("Id"); - uri = node->string_attribute ("URI"); - } - - string id; - string uri; -}; - -/** @class DCPReader::ParseState - * @brief Holder of state for use while reading DCP subtitles. - */ -struct DCPReader::ParseState { - list > font_nodes; - list > text_nodes; - list > subtitle_nodes; -}; - + return Time::from_hms (t.h, t.m, t.s, t.e * 1000.0 / t.tcr); } -/** @param s A string. - * @return true if the string contains only space, newline or tab characters, or is empty. - */ -static bool -empty_or_white_space (string s) +static Colour +dcp_to_sub_colour (dcp::Colour c) { - for (size_t i = 0; i < s.length(); ++i) { - if (s[i] != ' ' && s[i] != '\n' && s[i] != '\t') { - return false; - } - } - - return true; + return Colour (c.r / 255.0, c.g / 255.0, c.b / 255.0); } -string -DCPReader::font_id_to_name (string id) const +DCPReader::DCPReader (boost::filesystem::path file) { - list >::const_iterator i = _load_font_nodes.begin(); - while (i != _load_font_nodes.end() && (*i)->id != id) { - ++i; + shared_ptr sc; + string interop_error; + string smpte_error; + + try { + sc.reset (new dcp::InteropSubtitleAsset (file)); + } catch (exception& e) { + interop_error = e.what (); } - if (i == _load_font_nodes.end ()) { - return ""; + if (!sc) { + try { + sc.reset (new dcp::SMPTESubtitleAsset (file)); + } catch (exception& e) { + smpte_error = e.what(); + } } - if ((*i)->uri == "arial.ttf" || (*i)->uri == "Arial.ttf") { - return "Arial"; + if (!sc) { + throw DCPError (String::compose ("Could not read subtitles (%1 / %2)", interop_error, smpte_error)); } - return (*i)->uri; -} - -/** @class DCPReader - * @brief A class to read DCP subtitles. - */ -DCPReader::DCPReader (istream& in) -{ - shared_ptr xml (new cxml::Document ("DCSubtitle")); - xml->read_stream (in); - - xml->ignore_child ("SubtitleID"); - xml->ignore_child ("MovieTitle"); - xml->ignore_child ("ReelNumber"); - xml->ignore_child ("Language"); - list > font_nodes = type_children (xml, "Font"); - _load_font_nodes = type_children (xml, "LoadFont"); - - /* Now make Subtitle objects to represent the raw XML nodes - in a sane way. - */ + BOOST_FOREACH (shared_ptr i, sc->subtitles ()) { - ParseState parse_state; - examine_font_nodes (xml, font_nodes, parse_state); -} - -void -DCPReader::examine_font_nodes ( - shared_ptr xml, - list > const & font_nodes, - ParseState& parse_state - ) -{ - for (list >::const_iterator i = font_nodes.begin(); i != font_nodes.end(); ++i) { - - parse_state.font_nodes.push_back (*i); - maybe_add_subtitle ((*i)->text, parse_state); - - for (list >::iterator j = (*i)->subtitle_nodes.begin(); j != (*i)->subtitle_nodes.end(); ++j) { - parse_state.subtitle_nodes.push_back (*j); - examine_text_nodes (xml, (*j)->text_nodes, parse_state); - examine_font_nodes (xml, (*j)->font_nodes, parse_state); - parse_state.subtitle_nodes.pop_back (); + /* We don't deal with image subs */ + shared_ptr is = dynamic_pointer_cast(i); + if (!is) { + continue; } - - examine_font_nodes (xml, (*i)->font_nodes, parse_state); - examine_text_nodes (xml, (*i)->text_nodes, parse_state); - - parse_state.font_nodes.pop_back (); - } -} -void -DCPReader::examine_text_nodes ( - shared_ptr xml, - list > const & text_nodes, - ParseState& parse_state - ) -{ - for (list >::const_iterator i = text_nodes.begin(); i != text_nodes.end(); ++i) { - parse_state.text_nodes.push_back (*i); - maybe_add_subtitle ((*i)->text, parse_state); - examine_font_nodes (xml, (*i)->font_nodes, parse_state); - parse_state.text_nodes.pop_back (); - } -} + RawSubtitle rs; + rs.text = is->text (); + rs.font = is->font (); + rs.font_size = FontSize::from_proportional (is->size() / (72.0 * 11.0)); + + switch (is->effect ()) { + case dcp::BORDER: + rs.effect = BORDER; + break; + case dcp::SHADOW: + rs.effect = SHADOW; + break; + default: + break; + } -void -DCPReader::maybe_add_subtitle (string text, ParseState& parse_state) -{ - if (empty_or_white_space (text)) { - return; - } - - if (parse_state.text_nodes.empty() || parse_state.subtitle_nodes.empty ()) { - return; - } + rs.effect_colour = dcp_to_sub_colour (is->effect_colour()); + + rs.colour = dcp_to_sub_colour (is->colour()); + rs.bold = is->bold (); + rs.italic = is->italic (); + rs.underline = is->underline (); + + switch (is->h_align()) { + case dcp::HALIGN_LEFT: + rs.horizontal_position.reference = LEFT_OF_SCREEN; + break; + case dcp::HALIGN_CENTER: + rs.horizontal_position.reference = HORIZONTAL_CENTRE_OF_SCREEN; + break; + case dcp::HALIGN_RIGHT: + rs.horizontal_position.reference = RIGHT_OF_SCREEN; + break; + } - DCPFont effective_font (parse_state.font_nodes); - DCPText effective_text (*parse_state.text_nodes.back ()); - DCPSubtitle effective_subtitle (*parse_state.subtitle_nodes.back ()); + rs.vertical_position.proportional = is->v_position(); + switch (is->v_align()) { + case dcp::VALIGN_TOP: + rs.vertical_position.reference = TOP_OF_SCREEN; + break; + case dcp::VALIGN_CENTER: + rs.vertical_position.reference = VERTICAL_CENTRE_OF_SCREEN; + break; + case dcp::VALIGN_BOTTOM: + rs.vertical_position.reference = BOTTOM_OF_SCREEN; + break; + } - RawSubtitle sub; + rs.from = dcp_to_sub_time (is->in ()); + rs.to = dcp_to_sub_time (is->out ()); - sub.vertical_position.proportional = float (effective_text.v_position) / 100; - sub.vertical_position.reference = effective_text.v_align; - sub.from.set_metric (effective_subtitle.in); - sub.to.set_metric (effective_subtitle.out); - sub.fade_up = effective_subtitle.fade_up_time; - sub.fade_down = effective_subtitle.fade_down_time; - - sub.text = text; - sub.font = font_id_to_name (effective_font.id); - sub.font_size.set_proportional (float (effective_font.size) / (72 * 11)); - sub.effect = effective_font.effect; - sub.effect_colour = effective_font.effect_colour; - sub.colour = effective_font.colour.get (); - sub.italic = effective_font.italic.get (); + rs.fade_up = dcp_to_sub_time (is->fade_up_time ()); + rs.fade_down = dcp_to_sub_time (is->fade_down_time ()); - _subs.push_back (sub); + _subs.push_back (rs); + } }