X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Fsubtitle_asset.cc;h=b9f6336c594928d09c6990aeaf035e2f37c936dd;hb=9486066c29b91a8d9ac25be1c596cad62387208f;hp=c0ffb75a5d4952637776f802b26922c7f01d56f5;hpb=81312913ad26aaee12eeabd3f27a537806c97049;p=libdcp.git diff --git a/src/subtitle_asset.cc b/src/subtitle_asset.cc index c0ffb75a..b9f6336c 100644 --- a/src/subtitle_asset.cc +++ b/src/subtitle_asset.cc @@ -1,20 +1,34 @@ /* Copyright (C) 2012-2015 Carl Hetherington - This program is free software; you can redistribute it and/or modify + This file is part of libdcp. + + libdcp is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - This program is distributed in the hope that it will be useful, + libdcp is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - + along with libdcp. If not, see . + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. */ #include "raw_convert.h" @@ -25,19 +39,15 @@ #include "text_node.h" #include "subtitle_string.h" #include "dcp_assert.h" -#include "AS_DCP.h" -#include "KM_util.h" +#include +#include #include #include #include #include -#include using std::string; using std::list; -using std::ostream; -using std::ofstream; -using std::stringstream; using std::cout; using std::cerr; using std::map; @@ -59,50 +69,64 @@ SubtitleAsset::SubtitleAsset (boost::filesystem::path file) } void -SubtitleAsset::parse_subtitles (shared_ptr xml, list > font_nodes) +SubtitleAsset::parse_subtitles ( + shared_ptr xml, + list > font_nodes, + list > subtitle_nodes + ) { /* Make Subtitle objects to represent the raw XML nodes in a sane way */ ParseState parse_state; - examine_font_nodes (xml, font_nodes, parse_state); + examine_nodes (xml, font_nodes, parse_state); + examine_nodes (xml, subtitle_nodes, parse_state); } void -SubtitleAsset::examine_font_nodes ( +SubtitleAsset::examine_nodes ( shared_ptr xml, - list > const & font_nodes, + list > const & subtitle_nodes, ParseState& parse_state ) { - for (list >::const_iterator i = font_nodes.begin(); i != font_nodes.end(); ++i) { + BOOST_FOREACH (shared_ptr i, subtitle_nodes) { + parse_state.subtitle_nodes.push_back (i); + examine_nodes (xml, i->text_nodes, parse_state); + examine_nodes (xml, i->font_nodes, parse_state); + parse_state.subtitle_nodes.pop_back (); + } +} - parse_state.font_nodes.push_back (*i); - maybe_add_subtitle ((*i)->text, parse_state); +void +SubtitleAsset::examine_nodes ( + shared_ptr xml, + list > const & font_nodes, + ParseState& parse_state + ) +{ + BOOST_FOREACH (shared_ptr i, font_nodes) { - 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 (); - } + parse_state.font_nodes.push_back (i); + maybe_add_subtitle (i->text, parse_state); - examine_font_nodes (xml, (*i)->font_nodes, parse_state); - examine_text_nodes (xml, (*i)->text_nodes, parse_state); + examine_nodes (xml, i->subtitle_nodes, parse_state); + examine_nodes (xml, i->font_nodes, parse_state); + examine_nodes (xml, i->text_nodes, parse_state); parse_state.font_nodes.pop_back (); } } void -SubtitleAsset::examine_text_nodes ( +SubtitleAsset::examine_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); + BOOST_FOREACH (shared_ptr i, text_nodes) { + parse_state.text_nodes.push_back (i); + maybe_add_subtitle (i->text, parse_state); + examine_nodes (xml, i->font_nodes, parse_state); parse_state.text_nodes.pop_back (); } } @@ -129,6 +153,8 @@ SubtitleAsset::maybe_add_subtitle (string text, ParseState const & parse_state) SubtitleString ( effective_font.id, effective_font.italic.get_value_or (false), + effective_font.bold.get_value_or (false), + effective_font.underline.get_value_or (false), effective_font.colour.get_value_or (dcp::Colour (255, 255, 255)), effective_font.size, effective_font.aspect_adjust.get_value_or (1.0), @@ -138,6 +164,7 @@ SubtitleAsset::maybe_add_subtitle (string text, ParseState const & parse_state) effective_text.h_align, effective_text.v_position, effective_text.v_align, + effective_text.direction, text, effective_font.effect.get_value_or (NONE), effective_font.effect_colour.get_value_or (dcp::Colour (0, 0, 0)), @@ -148,12 +175,12 @@ SubtitleAsset::maybe_add_subtitle (string text, ParseState const & parse_state) } list -SubtitleAsset::subtitles_during (Time from, Time to) const +SubtitleAsset::subtitles_during (Time from, Time to, bool starting) const { list s; - for (list::const_iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) { - if (i->out() >= from && i->in() <= to) { - s.push_back (*i); + BOOST_FOREACH (SubtitleString const & i, _subtitles) { + if ((starting && from <= i.in() && i.in() < to) || (!starting && i.out() >= from && i.in() <= to)) { + s.push_back (i); } } @@ -170,9 +197,9 @@ Time SubtitleAsset::latest_subtitle_out () const { Time t; - for (list::const_iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) { - if (i->out() > t) { - t = i->out (); + BOOST_FOREACH (SubtitleString const & i, _subtitles) { + if (i.out() > t) { + t = i.out (); } } @@ -208,16 +235,23 @@ struct SubtitleSorter { } }; +/** @param standard Standard (INTEROP or SMPTE); this is used rather than putting things in the child + * class because the differences between the two are fairly subtle. + */ void -SubtitleAsset::subtitles_as_xml (xmlpp::Element* root, int time_code_rate, string xmlns) const +SubtitleAsset::subtitles_as_xml (xmlpp::Element* root, int time_code_rate, Standard standard) const { list sorted = _subtitles; sorted.sort (SubtitleSorter ()); - /* XXX: script, underlined, weight not supported */ + string const xmlns = standard == SMPTE ? "dcst" : ""; + + /* XXX: script not supported */ optional font; bool italic = false; + bool bold = false; + bool underline = false; Colour colour; int size = 0; float aspect_adjust = 1.0; @@ -232,7 +266,7 @@ SubtitleAsset::subtitles_as_xml (xmlpp::Element* root, int time_code_rate, strin xmlpp::Element* font_element = 0; xmlpp::Element* subtitle_element = 0; - for (list::iterator i = sorted.begin(); i != sorted.end(); ++i) { + BOOST_FOREACH (SubtitleString const & i, sorted) { /* We will start a new ... whenever some font property changes. I suppose we should really make an optimal hierarchy of tags, but @@ -240,28 +274,36 @@ SubtitleAsset::subtitles_as_xml (xmlpp::Element* root, int time_code_rate, strin */ bool const font_changed = - font != i->font() || - italic != i->italic() || - colour != i->colour() || - size != i->size() || - fabs (aspect_adjust - i->aspect_adjust()) > ASPECT_ADJUST_EPSILON || - effect != i->effect() || - effect_colour != i->effect_colour(); + font != i.font() || + italic != i.italic() || + bold != i.bold() || + underline != i.underline() || + colour != i.colour() || + size != i.size() || + fabs (aspect_adjust - i.aspect_adjust()) > ASPECT_ADJUST_EPSILON || + effect != i.effect() || + effect_colour != i.effect_colour(); if (font_changed) { - font = i->font (); - italic = i->italic (); - colour = i->colour (); - size = i->size (); - aspect_adjust = i->aspect_adjust (); - effect = i->effect (); - effect_colour = i->effect_colour (); + font = i.font (); + italic = i.italic (); + bold = i.bold (); + underline = i.underline (); + colour = i.colour (); + size = i.size (); + aspect_adjust = i.aspect_adjust (); + effect = i.effect (); + effect_colour = i.effect_colour (); } if (!font_element || font_changed) { font_element = root->add_child ("Font", xmlns); if (font) { - font_element->set_attribute ("Id", font.get ()); + if (standard == SMPTE) { + font_element->set_attribute ("ID", font.get ()); + } else { + font_element->set_attribute ("Id", font.get ()); + } } font_element->set_attribute ("Italic", italic ? "yes" : "no"); font_element->set_attribute ("Color", colour.to_argb_string()); @@ -272,40 +314,85 @@ SubtitleAsset::subtitles_as_xml (xmlpp::Element* root, int time_code_rate, strin font_element->set_attribute ("Effect", effect_to_string (effect)); font_element->set_attribute ("EffectColor", effect_colour.to_argb_string()); font_element->set_attribute ("Script", "normal"); - font_element->set_attribute ("Underlined", "no"); - font_element->set_attribute ("Weight", "normal"); + if (standard == SMPTE) { + font_element->set_attribute ("Underline", underline ? "yes" : "no"); + } else { + font_element->set_attribute ("Underlined", underline ? "yes" : "no"); + } + font_element->set_attribute ("Weight", bold ? "bold" : "normal"); } if (!subtitle_element || font_changed || - (last_in != i->in() || - last_out != i->out() || - last_fade_up_time != i->fade_up_time() || - last_fade_down_time != i->fade_down_time() + (last_in != i.in() || + last_out != i.out() || + last_fade_up_time != i.fade_up_time() || + last_fade_down_time != i.fade_down_time() )) { subtitle_element = font_element->add_child ("Subtitle", xmlns); subtitle_element->set_attribute ("SpotNumber", raw_convert (spot_number++)); - subtitle_element->set_attribute ("TimeIn", i->in().rebase(time_code_rate).as_string()); - subtitle_element->set_attribute ("TimeOut", i->out().rebase(time_code_rate).as_string()); - subtitle_element->set_attribute ("FadeUpTime", raw_convert (i->fade_up_time().as_editable_units(time_code_rate))); - subtitle_element->set_attribute ("FadeDownTime", raw_convert (i->fade_down_time().as_editable_units(time_code_rate))); - - last_in = i->in (); - last_out = i->out (); - last_fade_up_time = i->fade_up_time (); - last_fade_down_time = i->fade_down_time (); + subtitle_element->set_attribute ("TimeIn", i.in().rebase(time_code_rate).as_string(standard)); + subtitle_element->set_attribute ("TimeOut", i.out().rebase(time_code_rate).as_string(standard)); + if (standard == SMPTE) { + subtitle_element->set_attribute ("FadeUpTime", i.fade_up_time().rebase(time_code_rate).as_string(standard)); + subtitle_element->set_attribute ("FadeDownTime", i.fade_down_time().rebase(time_code_rate).as_string(standard)); + } else { + subtitle_element->set_attribute ("FadeUpTime", raw_convert (i.fade_up_time().as_editable_units(time_code_rate))); + subtitle_element->set_attribute ("FadeDownTime", raw_convert (i.fade_down_time().as_editable_units(time_code_rate))); + } + + last_in = i.in (); + last_out = i.out (); + last_fade_up_time = i.fade_up_time (); + last_fade_down_time = i.fade_down_time (); } xmlpp::Element* text = subtitle_element->add_child ("Text", xmlns); - if (i->h_align() != HALIGN_CENTER) { - text->set_attribute ("HAlign", halign_to_string (i->h_align ())); + + if (i.h_align() != HALIGN_CENTER) { + if (standard == SMPTE) { + text->set_attribute ("Halign", halign_to_string (i.h_align ())); + } else { + text->set_attribute ("HAlign", halign_to_string (i.h_align ())); + } + } + + if (i.h_position() > ALIGN_EPSILON) { + if (standard == SMPTE) { + text->set_attribute ("Hposition", raw_convert (i.h_position() * 100, 6)); + } else { + text->set_attribute ("HPosition", raw_convert (i.h_position() * 100, 6)); + } } - if (i->h_position() > ALIGN_EPSILON) { - text->set_attribute ("HPosition", raw_convert (i->h_position() * 100, 6)); + + if (standard == SMPTE) { + text->set_attribute ("Valign", valign_to_string (i.v_align())); + } else { + text->set_attribute ("VAlign", valign_to_string (i.v_align())); } - text->set_attribute ("VAlign", valign_to_string (i->v_align())); - text->set_attribute ("VPosition", raw_convert (i->v_position() * 100, 6)); - text->add_child_text (i->text()); + + if (i.v_position() > ALIGN_EPSILON) { + if (standard == SMPTE) { + text->set_attribute ("Vposition", raw_convert (i.v_position() * 100, 6)); + } else { + text->set_attribute ("VPosition", raw_convert (i.v_position() * 100, 6)); + } + } else { + if (standard == SMPTE) { + text->set_attribute ("Vposition", "0"); + } else { + text->set_attribute ("VPosition", "0"); + } + } + + /* Interop only supports "horizontal" or "vertical" for direction, so only write this + for SMPTE. + */ + if (i.direction() != DIRECTION_LTR && standard == SMPTE) { + text->set_attribute ("Direction", direction_to_string (i.direction ())); + } + + text->add_child_text (i.text()); } }