Order subtitles in the XML according to their vertical position (DoM bug #2106).
[libdcp.git] / src / subtitle_asset.cc
index 99673da991c5c57cb64e283d97857265587a74f2..22781196ac5c485ca042ba8813895e06a85db7f1 100644 (file)
@@ -64,7 +64,6 @@ using std::map;
 using std::shared_ptr;
 using std::vector;
 using std::make_shared;
-using boost::shared_array;
 using boost::optional;
 using boost::lexical_cast;
 using namespace dcp;
@@ -287,14 +286,28 @@ SubtitleAsset::parse_subtitles (xmlpp::Element const * node, vector<ParseState>&
                throw XMLError ("unexpected node " + node->get_name());
        }
 
+       float space_before = 0;
+
        for (auto i: node->get_children()) {
                auto const v = dynamic_cast<xmlpp::ContentNode const *>(i);
                if (v) {
-                       maybe_add_subtitle (v->get_content(), state, standard);
+                       maybe_add_subtitle (v->get_content(), state, space_before, standard);
+                       space_before = 0;
                }
                auto const e = dynamic_cast<xmlpp::Element const *>(i);
                if (e) {
-                       parse_subtitles (e, state, tcr, standard);
+                       if (e->get_name() == "Space") {
+                               if (node->get_name() != "Text") {
+                                       throw XMLError ("Space node found outside Text");
+                               }
+                               auto size = optional_string_attribute(e, "Size").get_value_or("0.5");
+                               if (standard == dcp::Standard::INTEROP) {
+                                       boost::replace_all(size, "em", "");
+                               }
+                               space_before += raw_convert<float>(size);
+                       } else {
+                               parse_subtitles (e, state, tcr, standard);
+                       }
                }
        }
 
@@ -303,7 +316,7 @@ SubtitleAsset::parse_subtitles (xmlpp::Element const * node, vector<ParseState>&
 
 
 void
-SubtitleAsset::maybe_add_subtitle (string text, vector<ParseState> const & parse_state, Standard standard)
+SubtitleAsset::maybe_add_subtitle (string text, vector<ParseState> const & parse_state, float space_before, Standard standard)
 {
        if (empty_or_white_space (text)) {
                return;
@@ -399,16 +412,37 @@ SubtitleAsset::maybe_add_subtitle (string text, vector<ParseState> const & parse
                                ps.effect.get_value_or (Effect::NONE),
                                ps.effect_colour.get_value_or (dcp::Colour (0, 0, 0)),
                                ps.fade_up_time.get_value_or(Time()),
-                               ps.fade_down_time.get_value_or(Time())
+                               ps.fade_down_time.get_value_or(Time()),
+                               space_before
                                )
                        );
                break;
        case ParseState::Type::IMAGE:
+       {
+               switch (standard) {
+               case Standard::INTEROP:
+                       if (text.size() >= 4) {
+                               /* Remove file extension */
+                               text = text.substr(0, text.size() - 4);
+                       }
+                       break;
+               case Standard::SMPTE:
+                       /* It looks like this urn:uuid: is required, but DoM wasn't expecting it (and not writing it)
+                        * until around 2.15.140 so I guess either:
+                        *   a) it is not (always) used in the field, or
+                        *   b) nobody noticed / complained.
+                        */
+                       if (text.substr(0, 9) == "urn:uuid:") {
+                               text = text.substr(9);
+                       }
+                       break;
+               }
+
                /* Add a subtitle with no image data and we'll fill that in later */
                _subtitles.push_back (
                        make_shared<SubtitleImage>(
                                ArrayData(),
-                               standard == Standard::INTEROP ? text.substr(0, text.size() - 4) : text,
+                               text,
                                ps.in.get(),
                                ps.out.get(),
                                ps.h_position.get_value_or(0),
@@ -421,6 +455,7 @@ SubtitleAsset::maybe_add_subtitle (string text, vector<ParseState> const & parse
                        );
                break;
        }
+       }
 }
 
 
@@ -544,6 +579,9 @@ struct SubtitleSorter
                if (a->in() != b->in()) {
                        return a->in() < b->in();
                }
+               if (a->v_align() == VAlign::BOTTOM) {
+                       return a->v_position() > b->v_position();
+               }
                return a->v_position() < b->v_position();
        }
 };
@@ -664,7 +702,7 @@ SubtitleAsset::subtitles_as_xml (xmlpp::Element* xml_root, int time_code_rate, S
                            fabs(last_v_position - is->v_position()) > ALIGN_EPSILON ||
                            last_direction != is->direction()
                                ) {
-                               text.reset (new order::Text (subtitle, is->h_align(), is->h_position(), is->v_align(), is->v_position(), is->direction()));
+                               text = make_shared<order::Text>(subtitle, is->h_align(), is->h_position(), is->v_align(), is->v_position(), is->direction());
                                subtitle->children.push_back (text);
 
                                last_h_align = is->h_align ();
@@ -674,14 +712,14 @@ SubtitleAsset::subtitles_as_xml (xmlpp::Element* xml_root, int time_code_rate, S
                                last_direction = is->direction ();
                        }
 
-                       text->children.push_back (shared_ptr<order::String> (new order::String (text, order::Font (is, standard), is->text())));
+                       text->children.push_back (make_shared<order::String>(text, order::Font (is, standard), is->text(), is->space_before()));
                }
 
                auto ii = dynamic_pointer_cast<SubtitleImage>(i);
                if (ii) {
                        text.reset ();
                        subtitle->children.push_back (
-                               shared_ptr<order::Image> (new order::Image (subtitle, ii->id(), ii->png_image(), ii->h_align(), ii->h_position(), ii->v_align(), ii->v_position()))
+                               make_shared<order::Image>(subtitle, ii->id(), ii->png_image(), ii->h_align(), ii->h_position(), ii->v_align(), ii->v_position())
                                );
                }
        }