Initial work on SMPTE subtitles.
[libdcp.git] / src / subtitle_asset.cc
index f1ecc9392c45778a62036c30f13a24e0bc8c1149..1c341de49d309369d0ac89a85e6d2c27dfcccd12 100644 (file)
@@ -43,14 +43,12 @@ using boost::dynamic_pointer_cast;
 using namespace dcp;
 
 SubtitleAsset::SubtitleAsset ()
-       : _reel_number ("1")
 {
 
 }
 
 SubtitleAsset::SubtitleAsset (boost::filesystem::path file)
        : Asset (file)
-       , _reel_number ("1")
 {
 
 }
@@ -58,13 +56,7 @@ SubtitleAsset::SubtitleAsset (boost::filesystem::path file)
 void
 SubtitleAsset::parse_common (shared_ptr<cxml::Document> xml, list<shared_ptr<dcp::FontNode> > font_nodes)
 {
-       _reel_number = xml->string_child ("ReelNumber");
-       _language = xml->string_child ("Language");
-
-       /* Now make Subtitle objects to represent the raw XML nodes
-          in a sane way.
-       */
-
+       /* Make Subtitle objects to represent the raw XML nodes in a sane way */
        ParseState parse_state;
        examine_font_nodes (xml, font_nodes, parse_state);
 }
@@ -169,21 +161,6 @@ SubtitleAsset::add (SubtitleString s)
        _subtitles.push_back (s);
 }
 
-void
-SubtitleAsset::write_xml (boost::filesystem::path p) const
-{
-       FILE* f = fopen_boost (p, "w");
-       if (!f) {
-               throw FileError ("Could not open file for writing", p, -1);
-       }
-       
-       Glib::ustring const s = xml_as_string ();
-       fwrite (s.c_str(), 1, s.bytes(), f);
-       fclose (f);
-
-       _file = p;
-}
-
 Time
 SubtitleAsset::latest_subtitle_out () const
 {
@@ -209,16 +186,6 @@ SubtitleAsset::equals (shared_ptr<const Asset> other_asset, EqualityOptions opti
                return false;
        }
 
-       if (_reel_number != other->_reel_number) {
-               note (DCP_ERROR, "subtitle reel numbers differ");
-               return false;
-       }
-
-       if (_language != other->_language) {
-               note (DCP_ERROR, "subtitle languages differ");
-               return false;
-       }
-
        if (_subtitles != other->_subtitles) {
                note (DCP_ERROR, "subtitles differ");
                return false;
@@ -226,3 +193,115 @@ SubtitleAsset::equals (shared_ptr<const Asset> other_asset, EqualityOptions opti
 
        return true;
 }
+
+struct SubtitleSorter {
+       bool operator() (SubtitleString const & a, SubtitleString const & b) {
+               if (a.in() != b.in()) {
+                       return a.in() < b.in();
+               }
+               return a.v_position() < b.v_position();
+       }
+};
+
+void
+SubtitleAsset::subtitles_as_xml (xmlpp::Element* root, int time_code_rate, string xmlns) const
+{
+       list<SubtitleString> sorted = _subtitles;
+       sorted.sort (SubtitleSorter ());
+
+       /* XXX: script, underlined, weight not supported */
+
+       optional<string> font;
+       bool italic = false;
+       Colour colour;
+       int size = 0;
+       float aspect_adjust = 1.0;
+       Effect effect = NONE;
+       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_element = 0;
+       xmlpp::Element* subtitle_element = 0;
+
+       for (list<SubtitleString>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
+
+               /* We will start a new <Font>...</Font> whenever some font property changes.
+                  I suppose we should really make an optimal hierarchy of <Font> tags, but
+                  that seems hard.
+               */
+
+               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();
+
+               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 ();
+               }
+
+               if (!font_element || font_changed) {
+                       font_element = root->add_child ("Font", xmlns);
+                       if (font) {
+                               font_element->set_attribute ("Id", font.get ());
+                       }
+                       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));
+                       if (fabs (aspect_adjust - 1.0) > ASPECT_ADJUST_EPSILON) {
+                               font_element->set_attribute ("AspectAdjust", raw_convert<string> (aspect_adjust));
+                       }
+                       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_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_element = font_element->add_child ("Subtitle", xmlns);
+                       subtitle_element->set_attribute ("SpotNumber", raw_convert<string> (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<string> (i->fade_up_time().as_editable_units(time_code_rate)));
+                       subtitle_element->set_attribute ("FadeDownTime", raw_convert<string> (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_position() > ALIGN_EPSILON) {
+                       text->set_attribute ("HPosition", raw_convert<string> (i->h_position() * 100, 6));
+               }
+               text->set_attribute ("VAlign", valign_to_string (i->v_align()));                
+               text->set_attribute ("VPosition", raw_convert<string> (i->v_position() * 100, 6));
+               text->add_child_text (i->text());
+       }
+}
+
+