Fix handling of timing in SMPTE subtitles.
[libdcp.git] / src / interop_subtitle_content.cc
index 85e52da733a79a87a31abfc8175df6f173abdbd1..1b6ee1a97a0fd8fda9adc7727e0d3666b1db9818 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
 
     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
 #include "xml.h"
 #include "raw_convert.h"
 #include "font.h"
+#include <boost/foreach.hpp>
 
 using std::list;
 using std::string;
+using std::cout;
 using boost::shared_ptr;
+using boost::optional;
+using boost::dynamic_pointer_cast;
 using namespace dcp;
 
 InteropSubtitleContent::InteropSubtitleContent (boost::filesystem::path file)
@@ -36,9 +40,13 @@ InteropSubtitleContent::InteropSubtitleContent (boost::filesystem::path file)
        _id = xml->string_child ("SubtitleID");
 
        _movie_title = xml->string_child ("MovieTitle");
-
        _load_font_nodes = type_children<dcp::InteropLoadFont> (xml, "LoadFont");
-       list<shared_ptr<dcp::Font> > font_nodes = type_children<dcp::Font> (xml, "Font");
+
+       list<cxml::NodePtr> f = xml->node_children ("Font");
+       list<shared_ptr<dcp::Font> > font_nodes;
+       BOOST_FOREACH (cxml::NodePtr& i, f) {
+               font_nodes.push_back (shared_ptr<Font> (new Font (i, 250)));
+       }
 
        parse_common (xml, font_nodes);
 }
@@ -70,35 +78,31 @@ InteropSubtitleContent::xml_as_string () const
        root->add_child("ReelNumber")->add_child_text (raw_convert<string> (_reel_number));
        root->add_child("Language")->add_child_text (_language);
 
-       if (_load_font_nodes.size() > 1) {
-               boost::throw_exception (MiscError ("multiple LoadFont nodes not supported"));
-       }
-
-       if (!_load_font_nodes.empty ()) {
+       for (list<shared_ptr<InteropLoadFont> >::const_iterator i = _load_font_nodes.begin(); i != _load_font_nodes.end(); ++i) {
                xmlpp::Element* load_font = root->add_child("LoadFont");
-               load_font->set_attribute ("Id", _load_font_nodes.front()->id);
-               load_font->set_attribute ("URI", _load_font_nodes.front()->uri);
+               load_font->set_attribute ("Id", (*i)->id);
+               load_font->set_attribute ("URI", (*i)->uri);
        }
 
        list<SubtitleString> sorted = _subtitles;
        sorted.sort (SubtitleSorter ());
 
-       /* XXX: multiple fonts not supported */
        /* XXX: script, underlined, weight not supported */
 
+       optional<string> font;
        bool italic = false;
-       Color color;
+       Colour colour;
        int size = 0;
        Effect effect = NONE;
-       Color effect_color;
+       Colour effect_colour;
        int spot_number = 1;
        Time last_in;
        Time last_out;
        Time last_fade_up_time;
        Time last_fade_down_time;
 
-       xmlpp::Element* font = 0;
-       xmlpp::Element* subtitle = 0;
+       xmlpp::Element* font_element = 0;
+       xmlpp::Element* subtitle_element = 0;
 
        for (list<SubtitleString>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
 
@@ -108,50 +112,50 @@ InteropSubtitleContent::xml_as_string () const
                */
 
                bool const font_changed =
+                       font         != i->font()         ||
                        italic       != i->italic()       ||
-                       color        != i->color()        ||
+                       colour        != i->colour()        ||
                        size         != i->size()         ||
                        effect       != i->effect()       ||
-                       effect_color != i->effect_color();
+                       effect_colour != i->effect_colour();
 
                if (font_changed) {
+                       font = i->font ();
                        italic = i->italic ();
-                       color = i->color ();
+                       colour = i->colour ();
                        size = i->size ();
                        effect = i->effect ();
-                       effect_color = i->effect_color ();
+                       effect_colour = i->effect_colour ();
                }
 
-               if (!font || font_changed) {
-                       font = root->add_child ("Font");
-                       string id = "theFontId";
-                       if (!_load_font_nodes.empty()) {
-                               id = _load_font_nodes.front()->id;
+               if (!font_element || font_changed) {
+                       font_element = root->add_child ("Font");
+                       if (font) {
+                               font_element->set_attribute ("Id", font.get ());
                        }
-                       font->set_attribute ("Id", id);
-                       font->set_attribute ("Italic", italic ? "yes" : "no");
-                       font->set_attribute ("Color", color.to_argb_string());
-                       font->set_attribute ("Size", raw_convert<string> (size));
-                       font->set_attribute ("Effect", effect_to_string (effect));
-                       font->set_attribute ("EffectColor", effect_color.to_argb_string());
-                       font->set_attribute ("Script", "normal");
-                       font->set_attribute ("Underlined", "no");
-                       font->set_attribute ("Weight", "normal");
+                       font_element->set_attribute ("Italic", italic ? "yes" : "no");
+                       font_element->set_attribute ("Color", colour.to_argb_string());
+                       font_element->set_attribute ("Size", raw_convert<string> (size));
+                       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 (!subtitle || font_changed ||
+               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()
                            )) {
 
-                       subtitle = font->add_child ("Subtitle");
-                       subtitle->set_attribute ("SpotNumber", raw_convert<string> (spot_number++));
-                       subtitle->set_attribute ("TimeIn", i->in().to_string());
-                       subtitle->set_attribute ("TimeOut", i->out().to_string());
-                       subtitle->set_attribute ("FadeUpTime", raw_convert<string> (i->fade_up_time().to_ticks()));
-                       subtitle->set_attribute ("FadeDownTime", raw_convert<string> (i->fade_down_time().to_ticks()));
+                       subtitle_element = font_element->add_child ("Subtitle");
+                       subtitle_element->set_attribute ("SpotNumber", raw_convert<string> (spot_number++));
+                       subtitle_element->set_attribute ("TimeIn", i->in().to_string());
+                       subtitle_element->set_attribute ("TimeOut", i->out().to_string());
+                       subtitle_element->set_attribute ("FadeUpTime", raw_convert<string> (i->fade_up_time().to_editable_units(250)));
+                       subtitle_element->set_attribute ("FadeDownTime", raw_convert<string> (i->fade_down_time().to_editable_units(250)));
 
                        last_in = i->in ();
                        last_out = i->out ();
@@ -159,7 +163,7 @@ InteropSubtitleContent::xml_as_string () const
                        last_fade_down_time = i->fade_down_time ();
                }
 
-               xmlpp::Element* text = subtitle->add_child ("Text");
+               xmlpp::Element* text = subtitle_element->add_child ("Text");
                text->set_attribute ("VAlign", valign_to_string (i->v_align()));                
                text->set_attribute ("VPosition", raw_convert<string> (i->v_position()));
                text->add_child_text (i->text());
@@ -168,3 +172,54 @@ InteropSubtitleContent::xml_as_string () const
        return doc.write_to_string_formatted ("UTF-8");
 }
 
+void
+InteropSubtitleContent::add_font (string id, string uri)
+{
+       _load_font_nodes.push_back (shared_ptr<InteropLoadFont> (new InteropLoadFont (id, uri)));
+}
+
+bool
+InteropSubtitleContent::equals (shared_ptr<const Asset> other_asset, EqualityOptions options, NoteHandler note) const
+{
+       if (!SubtitleContent::equals (other_asset, options, note)) {
+               return false;
+       }
+       
+       shared_ptr<const InteropSubtitleContent> other = dynamic_pointer_cast<const InteropSubtitleContent> (other_asset);
+       if (!other) {
+               return false;
+       }
+
+       list<shared_ptr<InteropLoadFont> >::const_iterator i = _load_font_nodes.begin ();
+       list<shared_ptr<InteropLoadFont> >::const_iterator j = other->_load_font_nodes.begin ();
+
+       while (i != _load_font_nodes.end ()) {
+               if (j == _load_font_nodes.end ()) {
+                       note (DCP_ERROR, "<LoadFont> nodes differ");
+                       return false;
+               }
+
+               if (**i != **j) {
+                       note (DCP_ERROR, "<LoadFont> nodes differ");
+                       return false;
+               }
+
+               ++i;
+               ++j;
+       }
+
+       if (_movie_title != other->_movie_title) {
+               note (DCP_ERROR, "Subtitle movie titles differ");
+               return false;
+       }
+
+       return true;
+}
+
+list<shared_ptr<LoadFont> >
+InteropSubtitleContent::load_font_nodes () const
+{
+       list<shared_ptr<LoadFont> > lf;
+       copy (_load_font_nodes.begin(), _load_font_nodes.end(), back_inserter (lf));
+       return lf;
+}