Merge branch 'master' of ssh://carlh.dyndns.org/home/carl/git/libdcp
[libdcp.git] / src / subtitle_asset.cc
index 2a244a27cff6b75067b78e8362c5d32480ee7924..4eb1a9cdf54fe95257d5784f22cf01bf8474a8ba 100644 (file)
 */
 
 #include <fstream>
+#include <cerrno>
 #include <boost/lexical_cast.hpp>
 #include <boost/algorithm/string.hpp>
+#include <libxml++/nodes/element.h>
+#include "AS_DCP.h"
+#include "KM_util.h"
 #include "subtitle_asset.h"
 #include "parse/subtitle.h"
 #include "util.h"
@@ -30,16 +34,34 @@ using std::list;
 using std::ostream;
 using std::ofstream;
 using std::stringstream;
+using std::cout;
 using boost::shared_ptr;
 using boost::lexical_cast;
 using boost::optional;
 using namespace libdcp;
 
-SubtitleAsset::SubtitleAsset (string directory, string xml_file)
-       : Asset (directory, xml_file)
+SubtitleAsset::SubtitleAsset (string directory, string file)
+       : Asset (directory, file)
        , _need_sort (false)
 {
-       read_xml (path().string());
+       /* Grotesque hack: we should look in the PKL to see what type this file is;
+          instead we'll look at the first character to decide what to do.
+          I think this is easily fixable (properly) in 1.0.
+       */
+
+       FILE* f = fopen_boost (path(), "r");
+       if (!f) {
+               throw FileError ("Could not open file for reading", file, errno);
+       }
+       unsigned char test[1];
+       fread (test, 1, 1, f);
+       fclose (f);
+
+       if (test[0] == '<' || test[0] == 0xef) {
+               read_xml (path().string());
+       } else {
+               read_mxf (path().string());
+       }
 }
 
 SubtitleAsset::SubtitleAsset (string directory, string movie_title, string language)
@@ -52,13 +74,46 @@ SubtitleAsset::SubtitleAsset (string directory, string movie_title, string langu
 
 }
 
+void
+SubtitleAsset::read_mxf (string mxf_file)
+{
+       ASDCP::TimedText::MXFReader reader;
+       Kumu::Result_t r = reader.OpenRead (mxf_file.c_str ());
+       if (ASDCP_FAILURE (r)) {
+               boost::throw_exception (MXFFileError ("could not open MXF file for reading", mxf_file, r));
+       }
+
+       string s;
+       reader.ReadTimedTextResource (s, 0, 0);
+       shared_ptr<cxml::Document> xml (new cxml::Document ("SubtitleReel"));
+       stringstream t;
+       t << s;
+       xml->read_stream (t);
+       read_xml (xml);
+}
+
 void
 SubtitleAsset::read_xml (string xml_file)
 {
-       shared_ptr<cxml::File> xml (new cxml::File (xml_file, "DCSubtitle"));
+       shared_ptr<cxml::Document> xml (new cxml::Document ("DCSubtitle"));
+       xml->read_file (xml_file);
+       read_xml (xml);
+}
+
+void
+SubtitleAsset::read_xml (shared_ptr<cxml::Document> xml)
+{
+       /* XXX: hacks aplenty in here; need separate parsers for DCSubtitle and SubtitleReel */
        
-       _uuid = xml->string_child ("SubtitleID");
-       _movie_title = xml->string_child ("MovieTitle");
+       /* DCSubtitle */
+       optional<string> x = xml->optional_string_child ("SubtitleID");
+       if (!x) {
+               /* SubtitleReel */
+               x = xml->optional_string_child ("Id");
+       }
+       _uuid = x.get_value_or ("");
+
+       _movie_title = xml->optional_string_child ("MovieTitle");
        _reel_number = xml->string_child ("ReelNumber");
        _language = xml->string_child ("Language");
 
@@ -71,6 +126,12 @@ SubtitleAsset::read_xml (string xml_file)
           in a sane way.
        */
 
+       shared_ptr<cxml::Node> subtitle_list = xml->optional_node_child ("SubtitleList");
+       if (subtitle_list) {
+               list<shared_ptr<libdcp::parse::Font> > font = type_children<libdcp::parse::Font> (subtitle_list, "Font");
+               copy (font.begin(), font.end(), back_inserter (font_nodes));
+       }
+       
        ParseState parse_state;
        examine_font_nodes (xml, font_nodes, parse_state);
 }
@@ -180,7 +241,7 @@ SubtitleAsset::font_id_to_name (string id) const
                return "";
        }
 
-       if ((*i)->uri == "arial.ttf") {
+       if ((*i)->uri && (*i)->uri.get() == "arial.ttf") {
                return "Arial";
        }
 
@@ -277,13 +338,13 @@ SubtitleAsset::add (shared_ptr<Subtitle> s)
 }
 
 void
-SubtitleAsset::write_to_cpl (xmlpp::Node* node) const
+SubtitleAsset::write_to_cpl (xmlpp::Element* node) const
 {
        /* XXX: should EditRate, Duration and IntrinsicDuration be in here? */
 
        xmlpp::Node* ms = node->add_child ("MainSubtitle");
        ms->add_child("Id")->add_child_text("urn:uuid:" + _uuid);
-       ms->add_child("AnnotationText")->add_child_text (_file_name);
+       ms->add_child("AnnotationText")->add_child_text (_file_name.string ());
        /* XXX */
        ms->add_child("EntryPoint")->add_child_text ("0");
 }
@@ -300,19 +361,23 @@ struct SubtitleSorter {
 void
 SubtitleAsset::write_xml () const
 {
-       ofstream s (path().string().c_str());
-       write_xml (s);
+       FILE* f = fopen_boost (path (), "r");
+       Glib::ustring const s = xml_as_string ();
+       fwrite (s.c_str(), 1, s.length(), f);
+       fclose (f);
 }
 
-void
-SubtitleAsset::write_xml (ostream& s) const
+Glib::ustring
+SubtitleAsset::xml_as_string () const
 {
        xmlpp::Document doc;
        xmlpp::Element* root = doc.create_root_node ("DCSubtitle");
        root->set_attribute ("Version", "1.0");
 
        root->add_child("SubtitleID")->add_child_text (_uuid);
-       root->add_child("MovieTitle")->add_child_text (_movie_title);
+       if (_movie_title) {
+               root->add_child("MovieTitle")->add_child_text (_movie_title.get ());
+       }
        root->add_child("ReelNumber")->add_child_text (lexical_cast<string> (_reel_number));
        root->add_child("Language")->add_child_text (_language);
 
@@ -323,7 +388,9 @@ SubtitleAsset::write_xml (ostream& s) const
        if (!_load_font_nodes.empty ()) {
                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);
+               if (_load_font_nodes.front()->uri) {
+                       load_font->set_attribute("URI",  _load_font_nodes.front()->uri.get ());
+               }
        }
 
        list<shared_ptr<Subtitle> > sorted = _subtitles;
@@ -413,6 +480,6 @@ SubtitleAsset::write_xml (ostream& s) const
                text->add_child_text ((*i)->text());
        }
 
-       doc.write_to_stream_formatted (s, "UTF-8");
+       return doc.write_to_string_formatted ("UTF-8");
 }