Simple pass-through of <Ruby> tags in subtitles.
authorCarl Hetherington <cth@carlh.net>
Wed, 15 Nov 2023 19:14:55 +0000 (20:14 +0100)
committerCarl Hetherington <cth@carlh.net>
Wed, 15 Nov 2023 19:14:55 +0000 (20:14 +0100)
17 files changed:
src/ruby.cc [new file with mode: 0644]
src/ruby.h [new file with mode: 0644]
src/subtitle_asset.cc
src/subtitle_asset.h
src/subtitle_asset_internal.cc
src/subtitle_asset_internal.h
src/subtitle_string.cc
src/subtitle_string.h
src/types.h
src/wscript
test/data/ruby1.xml [new file with mode: 0644]
test/decryption_test.cc
test/interop_subtitle_test.cc
test/shared_subtitle_test.cc
test/smpte_subtitle_test.cc
test/test.cc
test/verify_test.cc

diff --git a/src/ruby.cc b/src/ruby.cc
new file mode 100644 (file)
index 0000000..6adfbcb
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+    Copyright (C) 2023 Carl Hetherington <cth@carlh.net>
+
+    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.
+
+    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 libdcp.  If not, see <http://www.gnu.org/licenses/>.
+
+    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 "ruby.h"
+#include "types.h"
+
+
+using namespace dcp;
+
+
+bool dcp::operator==(Ruby const& a, Ruby const& b)
+{
+       return a.base == b.base &&
+               a.annotation == b.annotation &&
+               fabs(a.size - b.size) < SIZE_EPSILON &&
+               a. position == b.position &&
+               fabs(a.offset - b.offset) < OFFSET_EPSILON &&
+               fabs(a.spacing - b.spacing) < SPACING_EPSILON &&
+               fabs(a.aspect_adjust - b.aspect_adjust) < ASPECT_ADJUST_EPSILON;
+}
+
+
+bool dcp::operator!=(Ruby const& a, Ruby const& b)
+{
+       return !(a == b);
+}
diff --git a/src/ruby.h b/src/ruby.h
new file mode 100644 (file)
index 0000000..343583d
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+    Copyright (C) 2023 Carl Hetherington <cth@carlh.net>
+
+    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.
+
+    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 libdcp.  If not, see <http://www.gnu.org/licenses/>.
+
+    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 <string>
+
+
+namespace dcp {
+
+
+enum class RubyPosition
+{
+       BEFORE,
+       AFTER
+};
+
+
+class Ruby
+{
+public:
+       Ruby(std::string base_, std::string annotation_)
+               : base(base_)
+               , annotation(annotation_)
+       {}
+
+       std::string base;
+       std::string annotation;
+       float size = 0.5;
+       RubyPosition position = RubyPosition::BEFORE;
+       float offset = 0;
+       float spacing = 0;
+       float aspect_adjust = 1;
+};
+
+
+bool operator==(Ruby const& a, Ruby const& b);
+bool operator!=(Ruby const& a, Ruby const& b);
+
+
+}
+
index 4deb366aa4921cf2bbfdb8c9b2a6f5d8e2287b0d..1cd4fc07db7d2ecba9f577d7043614574e128298 100644 (file)
@@ -294,12 +294,83 @@ SubtitleAsset::parse_subtitles (xmlpp::Element const * node, vector<ParseState>&
 
        float space_before = 0;
 
+       /* Collect <Ruby>s first */
+       auto get_text_content = [](xmlpp::Element const* element) {
+               string all_content;
+               for (auto child: element->get_children()) {
+                       auto content = dynamic_cast<xmlpp::ContentNode const*>(child);
+                       if (content) {
+                               all_content += content->get_content();
+                       }
+               }
+               return all_content;
+       };
+
+       vector<Ruby> rubies;
+       for (auto child: node->get_children()) {
+               auto element = dynamic_cast<xmlpp::Element const*>(child);
+               if (element && element->get_name() == "Ruby") {
+                       optional<string> base;
+                       optional<string> annotation;
+                       optional<float> size;
+                       optional<RubyPosition> position;
+                       optional<float> offset;
+                       optional<float> spacing;
+                       optional<float> aspect_adjust;
+                       for (auto ruby_child: element->get_children()) {
+                               if (auto ruby_element = dynamic_cast<xmlpp::Element const*>(ruby_child)) {
+                                       if (ruby_element->get_name() == "Rb") {
+                                               base = get_text_content(ruby_element);
+                                       } else if (ruby_element->get_name() == "Rt") {
+                                               annotation = get_text_content(ruby_element);
+                                               size = optional_number_attribute<float>(ruby_element, "Size");
+                                               if (auto position_string = optional_string_attribute(ruby_element, "Position")) {
+                                                       if (*position_string == "before") {
+                                                               position = RubyPosition::BEFORE;
+                                                       } else if (*position_string == "after") {
+                                                               position = RubyPosition::AFTER;
+                                                       } else {
+                                                               DCP_ASSERT(false);
+                                                       }
+                                               }
+                                               offset = optional_number_attribute<float>(ruby_element, "Offset");
+                                               spacing = optional_number_attribute<float>(ruby_element, "Spacing");
+                                               aspect_adjust = optional_number_attribute<float>(ruby_element, "AspectAdjust");
+                                       }
+                               }
+                       }
+                       DCP_ASSERT(base);
+                       DCP_ASSERT(annotation);
+                       auto ruby = Ruby{*base, *annotation};
+                       if (size) {
+                               ruby.size = *size;
+                       }
+                       if (position) {
+                               ruby.position = *position;
+                       }
+                       if (offset) {
+                               ruby.offset = *offset;
+                       }
+                       if (spacing) {
+                               ruby.spacing = *spacing;
+                       }
+                       if (aspect_adjust) {
+                               ruby.aspect_adjust = *aspect_adjust;
+                       }
+                       rubies.push_back(ruby);
+               }
+       }
+
        for (auto i: node->get_children()) {
+
+               /* Handle actual content e.g. text */
                auto const v = dynamic_cast<xmlpp::ContentNode const *>(i);
                if (v) {
-                       maybe_add_subtitle (v->get_content(), state, space_before, standard);
+                       maybe_add_subtitle (v->get_content(), state, space_before, standard, rubies);
                        space_before = 0;
                }
+
+               /* Handle other nodes */
                auto const e = dynamic_cast<xmlpp::Element const *>(i);
                if (e) {
                        if (e->get_name() == "Space") {
@@ -311,7 +382,7 @@ SubtitleAsset::parse_subtitles (xmlpp::Element const * node, vector<ParseState>&
                                        boost::replace_all(size, "em", "");
                                }
                                space_before += raw_convert<float>(size);
-                       } else {
+                       } else if (e->get_name() != "Ruby") {
                                parse_subtitles (e, state, tcr, standard);
                        }
                }
@@ -322,7 +393,13 @@ SubtitleAsset::parse_subtitles (xmlpp::Element const * node, vector<ParseState>&
 
 
 void
-SubtitleAsset::maybe_add_subtitle (string text, vector<ParseState> const & parse_state, float space_before, Standard standard)
+SubtitleAsset::maybe_add_subtitle(
+       string text,
+       vector<ParseState> const & parse_state,
+       float space_before,
+       Standard standard,
+       vector<Ruby> const& rubies
+       )
 {
        auto wanted = [](ParseState const& ps) {
                return ps.type && (ps.type.get() == ParseState::Type::TEXT || ps.type.get() == ParseState::Type::IMAGE);
@@ -427,7 +504,8 @@ SubtitleAsset::maybe_add_subtitle (string text, vector<ParseState> const & parse
                                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()),
-                               space_before
+                               space_before,
+                               rubies
                                )
                        );
                break;
@@ -698,7 +776,16 @@ SubtitleAsset::subtitles_as_xml (xmlpp::Element* xml_root, int time_code_rate, S
                            fabs(last_z_position - is->z_position()) > ALIGN_EPSILON ||
                            last_direction != is->direction()
                                ) {
-                               text = make_shared<order::Text>(subtitle, is->h_align(), is->h_position(), is->v_align(), is->v_position(), is->z_position(), is->direction());
+                               text = make_shared<order::Text>(
+                                       subtitle,
+                                       is->h_align(),
+                                       is->h_position(),
+                                       is->v_align(),
+                                       is->v_position(),
+                                       is->z_position(),
+                                       is->direction(),
+                                       is->rubies()
+                                       );
                                subtitle->children.push_back (text);
 
                                last_h_align = is->h_align ();
index 42030288a38d225f4d6454c4eb4de253f9af9fd0..25758c2e6477841ba69a00c15450b24feb1cfe4a 100644 (file)
@@ -216,7 +216,13 @@ private:
        friend struct ::pull_fonts_test2;
        friend struct ::pull_fonts_test3;
 
-       void maybe_add_subtitle (std::string text, std::vector<ParseState> const & parse_state, float space_before, Standard standard);
+       void maybe_add_subtitle(
+               std::string text,
+               std::vector<ParseState> const & parse_state,
+               float space_before,
+               Standard standard,
+               std::vector<Ruby> const& rubies
+               );
 
        static void pull_fonts (std::shared_ptr<order::Part> part);
 };
index bb67b45a83f333de0d2a9715c760535570631be8..99d8411b3614789d2812b26137d5a2c89c552626 100644 (file)
@@ -223,6 +223,18 @@ order::Text::as_xml (xmlpp::Element* parent, Context& context) const
                e->set_attribute ("Direction", direction_to_string (_direction));
        }
 
+       for (auto const& ruby: _rubies) {
+               auto xml = e->add_child("Ruby");
+               xml->add_child("Rb")->add_child_text(ruby.base);
+               auto rt = xml->add_child("Rt");
+               rt->add_child_text(ruby.annotation);
+               rt->set_attribute("Size", dcp::raw_convert<string>(ruby.size, 6));
+               rt->set_attribute("Position", ruby.position == RubyPosition::BEFORE ? "before" : "after");
+               rt->set_attribute("Offset", dcp::raw_convert<string>(ruby.offset, 6));
+               rt->set_attribute("Spacing", dcp::raw_convert<string>(ruby.spacing, 6));
+               rt->set_attribute("AspectAdjust", dcp::raw_convert<string>(ruby.aspect_adjust, 6));
+       }
+
        return e;
 }
 
index adf2c994788f037bbc8d0e52311d70c6fb3401d6..557db2e495f2c48087f40c2b9b22b725e0fb95aa 100644 (file)
@@ -62,6 +62,7 @@ struct pull_fonts_test3;
 namespace dcp {
 
 
+class Ruby;
 class SubtitleString;
 
 
@@ -145,7 +146,7 @@ private:
 class Text : public Part
 {
 public:
-       Text (std::shared_ptr<Part> parent, HAlign h_align, float h_position, VAlign v_align, float v_position, float z_position, Direction direction)
+       Text(std::shared_ptr<Part> parent, HAlign h_align, float h_position, VAlign v_align, float v_position, float z_position, Direction direction, std::vector<Ruby> rubies)
                : Part (parent)
                , _h_align (h_align)
                , _h_position (h_position)
@@ -153,6 +154,7 @@ public:
                , _v_position (v_position)
                , _z_position(z_position)
                , _direction (direction)
+               , _rubies(rubies)
        {}
 
        xmlpp::Element* as_xml (xmlpp::Element* parent, Context& context) const override;
@@ -164,6 +166,7 @@ private:
        float _v_position;
        float _z_position;
        Direction _direction;
+       std::vector<Ruby> _rubies;
 };
 
 
index b627240b60c487563198f612bb47eeab0c99a021..af61d9286f07cda2351b3ff58589e2d8773cd0b6 100644 (file)
@@ -49,6 +49,7 @@ using std::min;
 using std::ostream;
 using std::shared_ptr;
 using std::string;
+using std::vector;
 using boost::optional;
 using namespace dcp;
 
@@ -74,7 +75,8 @@ SubtitleString::SubtitleString (
        Colour effect_colour,
        Time fade_up_time,
        Time fade_down_time,
-       float space_before
+       float space_before,
+       vector<Ruby> rubies
        )
        : Subtitle(in, out, h_position, h_align, v_position, v_align, z_position, fade_up_time, fade_down_time)
        , _font (font)
@@ -89,6 +91,7 @@ SubtitleString::SubtitleString (
        , _effect (effect)
        , _effect_colour (effect_colour)
        , _space_before (space_before)
+       , _rubies(rubies)
 {
        _aspect_adjust = max(min(_aspect_adjust, 4.0f), 0.25f);
 }
@@ -130,7 +133,8 @@ dcp::operator== (SubtitleString const & a, SubtitleString const & b)
                a.effect_colour() == b.effect_colour() &&
                a.fade_up_time() == b.fade_up_time() &&
                a.fade_down_time() == b.fade_down_time() &&
-               fabs (a.space_before() - b.space_before()) < SPACE_BEFORE_EPSILON
+               fabs (a.space_before() - b.space_before()) < SPACE_BEFORE_EPSILON &&
+               a.rubies() == b.rubies()
                );
 }
 
@@ -175,6 +179,10 @@ dcp::operator<< (ostream& s, SubtitleString const & sub)
          << ", effect colour (" << sub.effect_colour().r << ", " << sub.effect_colour().g << ", " << sub.effect_colour().b << ")"
          << ", space before " << sub.space_before();
 
+       for (auto ruby: sub.rubies()) {
+               s << ", ruby " << ruby.base << " " << ruby.annotation;
+       }
+
        return s;
 }
 
@@ -254,6 +262,11 @@ SubtitleString::equals(shared_ptr<const Subtitle> other_sub, EqualityOptions con
                same = false;
        }
 
+       if (_rubies != other->_rubies) {
+               note(NoteType::ERROR, "rubies differ");
+               same = false;
+       }
+
        return same;
 }
 
index 4eb64a68a1e6ef0e9c95d6dcd28a4ec3dc7e3c39..1ef57ff22bf8d80ae693c4c727213a56e671657b 100644 (file)
@@ -42,6 +42,7 @@
 
 
 #include "dcp_time.h"
+#include "ruby.h"
 #include "subtitle.h"
 #include <boost/optional.hpp>
 #include <string>
@@ -100,7 +101,8 @@ public:
                Colour effect_colour,
                Time fade_up_time,
                Time fade_down_time,
-               float space_before
+               float space_before,
+               std::vector<Ruby> rubies
                );
 
        /** @return font ID */
@@ -158,6 +160,10 @@ public:
                return _aspect_adjust;
        }
 
+       std::vector<Ruby> const& rubies() const {
+               return _rubies;
+       }
+
        void set_font (std::string id) {
                _font = id;
        }
@@ -190,6 +196,10 @@ public:
                _effect_colour = c;
        }
 
+       void set_rubies(std::vector<Ruby> rubies) {
+               _rubies = std::move(rubies);
+       }
+
        bool equals(std::shared_ptr<const dcp::Subtitle> other_sub, EqualityOptions const& options, NoteHandler node) const override;
 
 private:
@@ -213,6 +223,7 @@ private:
        Effect _effect;
        Colour _effect_colour;
        float _space_before;
+       std::vector<Ruby> _rubies;
 };
 
 bool operator== (SubtitleString const & a, SubtitleString const & b);
@@ -224,3 +235,4 @@ std::ostream& operator<< (std::ostream& s, SubtitleString const & sub);
 
 
 #endif
+
index bcb6da5b91cbd567f94e61c4290f95d08678487d..183ce6383e6cc0f440977bf8e61e180c2a369b99 100644 (file)
@@ -277,6 +277,11 @@ constexpr float ALIGN_EPSILON = 1e-3;
 constexpr float SPACE_BEFORE_EPSILON = 1e-3;
 
 
+constexpr float SIZE_EPSILON = 1e-3;
+constexpr float OFFSET_EPSILON = 1e-3;
+constexpr float SPACING_EPSILON = 1e-3;
+
+
 enum class Marker {
        FFOC, ///< first frame of composition
        LFOC, ///< last frame of composition
index e6bee6f2d2a344869decb452efc91c9843e4275d..3bc8537b3724e0f5d68c60649bbe87c0b74b3777 100644 (file)
@@ -101,6 +101,7 @@ def build(bld):
              reel_subtitle_asset.cc
              ref.cc
              rgb_xyz.cc
+             ruby.cc
              s_gamut3_transfer_function.cc
              search.cc
              smpte_load_font_node.cc
@@ -205,6 +206,7 @@ def build(bld):
               reel_stereo_picture_asset.h
               reel_subtitle_asset.h
               ref.h
+              ruby.h
               s_gamut3_transfer_function.h
               search.h
               smpte_load_font_node.h
diff --git a/test/data/ruby1.xml b/test/data/ruby1.xml
new file mode 100644 (file)
index 0000000..fe1d8ad
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<DCSubtitle Version="1.0">
+  <SubtitleID>cab5c268-222b-41d2-88ae-6d6999441b17</SubtitleID>
+  <MovieTitle>Movie Title</MovieTitle>
+  <ReelNumber>1</ReelNumber>
+  <Language>French</Language>
+  <LoadFont Id="theFontId" URI="arial.ttf"/>
+  <Font AspectAdjust="1.0" Color="FFFFFFFF" Effect="border" EffectColor="FF000000" Id="theFontId" Italic="no" Script="normal" Size="39" Underlined="no" Weight="normal">
+    <Subtitle SpotNumber="1" TimeIn="00:00:05:198" TimeOut="00:00:07:115" FadeUpTime="1" FadeDownTime="1">
+      <Text VAlign="center" VPosition="0"><Ruby><Rb>Base</Rb><Rt Size="0.7" Position="after" Offset="0.1" Spacing="0.4" AspectAdjust="0.9">Annotate</Rt></Ruby>Hello world</Text>
+    </Subtitle>
+  </Font>
+</DCSubtitle>
+
index 1e2ca94beedb4a10e2373d05449ad20ce6fd20af..8f3dbff711295205cbe7c407f5d73ba137aef108 100644 (file)
@@ -196,7 +196,7 @@ BOOST_AUTO_TEST_CASE (decryption_test2)
                "Hello world",
                dcp::Effect::NONE,
                dcp::Colour(0, 0, 0),
-               dcp::Time(), dcp::Time(), 0
+               dcp::Time(), dcp::Time(), 0, std::vector<dcp::Ruby>()
                ));
        subs_asset->write (dir / "subs.mxf");
 
index b477346c1576305df03b424689c94d9492a8f138..0efca007e28e3df7cfe885fc28c3dd92cd8d6ea7 100644 (file)
@@ -89,7 +89,8 @@ BOOST_AUTO_TEST_CASE (read_interop_subtitle_test1)
                                   dcp::Colour (0, 0, 0),
                                   dcp::Time (0, 0, 0, 1, 250),
                                   dcp::Time (0, 0, 0, 1, 250),
-                                  0
+                                  0,
+                                  {}
                                   ));
        BOOST_REQUIRE (dynamic_pointer_cast<const dcp::SubtitleString>(s.back()));
        BOOST_CHECK_EQUAL (*dynamic_pointer_cast<const dcp::SubtitleString>(s.back()), dcp::SubtitleString (
@@ -113,7 +114,8 @@ BOOST_AUTO_TEST_CASE (read_interop_subtitle_test1)
                                   dcp::Colour (0, 0, 0),
                                   dcp::Time (0, 0, 0, 1, 250),
                                   dcp::Time (0, 0, 0, 1, 250),
-                                  6
+                                  6,
+                                  {}
                                   ));
 
        s = subs.subtitles_during (dcp::Time (0, 0, 7, 190, 250), dcp::Time (0, 0, 7, 191, 250), false);
@@ -140,7 +142,8 @@ BOOST_AUTO_TEST_CASE (read_interop_subtitle_test1)
                                   dcp::Colour (0, 0, 0),
                                   dcp::Time (0, 0, 0, 1, 250),
                                   dcp::Time (0, 0, 0, 1, 250),
-                                  0
+                                  0,
+                                  {}
                                   ));
        BOOST_REQUIRE (dynamic_pointer_cast<const dcp::SubtitleString>(s.back()));
        BOOST_CHECK_EQUAL (*dynamic_pointer_cast<const dcp::SubtitleString>(s.back()), dcp::SubtitleString (
@@ -164,7 +167,8 @@ BOOST_AUTO_TEST_CASE (read_interop_subtitle_test1)
                                   dcp::Colour (0, 0, 0),
                                   dcp::Time (0, 0, 0, 1, 250),
                                   dcp::Time (0, 0, 0, 1, 250),
-                                  0
+                                  0,
+                                  {}
                                   ));
 
        s = subs.subtitles_during (dcp::Time (0, 0, 11, 95, 250), dcp::Time (0, 0, 11, 96, 250), false);
@@ -191,7 +195,8 @@ BOOST_AUTO_TEST_CASE (read_interop_subtitle_test1)
                                   dcp::Colour (0, 0, 0),
                                   dcp::Time (0, 0, 0, 1, 250),
                                   dcp::Time (0, 0, 0, 1, 250),
-                                  0
+                                  0,
+                                  {}
                                   ));
 
        s = subs.subtitles_during (dcp::Time (0, 0, 14, 42, 250), dcp::Time (0, 0, 14, 43, 250), false);
@@ -218,7 +223,8 @@ BOOST_AUTO_TEST_CASE (read_interop_subtitle_test1)
                                   dcp::Colour (0, 0, 0),
                                   dcp::Time (0, 0, 0, 1, 250),
                                   dcp::Time (0, 0, 0, 1, 250),
-                                  0
+                                  0,
+                                  {}
                                   ));
 }
 
@@ -251,7 +257,8 @@ BOOST_AUTO_TEST_CASE (read_interop_subtitle_test2)
                                   dcp::Colour (0, 0, 0),
                                   dcp::Time (0, 0, 0, 0, 250),
                                   dcp::Time (0, 0, 0, 0, 250),
-                                  0
+                                  0,
+                                  {}
                                   ));
        BOOST_REQUIRE (dynamic_pointer_cast<const dcp::SubtitleString>(s.back()));
        BOOST_CHECK_EQUAL (*dynamic_pointer_cast<const dcp::SubtitleString>(s.back()), dcp::SubtitleString (
@@ -275,7 +282,8 @@ BOOST_AUTO_TEST_CASE (read_interop_subtitle_test2)
                                   dcp::Colour (0, 0, 0),
                                   dcp::Time (0, 0, 0, 0, 250),
                                   dcp::Time (0, 0, 0, 0, 250),
-                                  0
+                                  0,
+                                  {}
                                   ));
 
        s = subs.subtitles_during (dcp::Time (0, 0, 50, 50, 250), dcp::Time (0, 0, 50, 51, 250), false);
@@ -302,7 +310,8 @@ BOOST_AUTO_TEST_CASE (read_interop_subtitle_test2)
                                   dcp::Colour (0, 0, 0),
                                   dcp::Time (0, 0, 0, 0, 250),
                                   dcp::Time (0, 0, 0, 0, 250),
-                                  0
+                                  0,
+                                  {}
                                   ));
        BOOST_REQUIRE (dynamic_pointer_cast<const dcp::SubtitleString>(s.back()));
        BOOST_CHECK_EQUAL (*dynamic_pointer_cast<const dcp::SubtitleString>(s.back()), dcp::SubtitleString (
@@ -326,7 +335,8 @@ BOOST_AUTO_TEST_CASE (read_interop_subtitle_test2)
                                   dcp::Colour (0, 0, 0),
                                   dcp::Time (0, 0, 0, 0, 250),
                                   dcp::Time (0, 0, 0, 0, 250),
-                                  0
+                                  0,
+                                  {}
                                   ));
 
        s = subs.subtitles_during (dcp::Time (0, 1, 2, 300, 250), dcp::Time (0, 1, 2, 301, 250), false);
@@ -353,7 +363,8 @@ BOOST_AUTO_TEST_CASE (read_interop_subtitle_test2)
                                   dcp::Colour(0, 0, 0),
                                   dcp::Time(0, 0, 0, 0, 250),
                                   dcp::Time(0, 0, 0, 0, 250),
-                                  0
+                                  0,
+                                  {}
                                   ));
        BOOST_REQUIRE (dynamic_pointer_cast<const dcp::SubtitleString>(s.back()));
        BOOST_CHECK_EQUAL (*dynamic_pointer_cast<const dcp::SubtitleString>(s.back()), dcp::SubtitleString (
@@ -377,7 +388,8 @@ BOOST_AUTO_TEST_CASE (read_interop_subtitle_test2)
                                   dcp::Colour(0, 0, 0),
                                   dcp::Time(0, 0, 0, 0, 250),
                                   dcp::Time(0, 0, 0, 0, 250),
-                                  0
+                                  0,
+                                  {}
                                   ));
 
        s = subs.subtitles_during (dcp::Time (0, 1, 15, 50, 250), dcp::Time (0, 1, 15, 51, 250), false);
@@ -404,7 +416,8 @@ BOOST_AUTO_TEST_CASE (read_interop_subtitle_test2)
                                   dcp::Colour(0, 0, 0),
                                   dcp::Time(0, 0, 0, 0, 250),
                                   dcp::Time(0, 0, 0, 0, 250),
-                                  0
+                                  0,
+                                  {}
                                   ));
        BOOST_REQUIRE (dynamic_pointer_cast<const dcp::SubtitleString>(s.back()));
        BOOST_CHECK_EQUAL (*dynamic_pointer_cast<const dcp::SubtitleString>(s.back()), dcp::SubtitleString (
@@ -428,7 +441,8 @@ BOOST_AUTO_TEST_CASE (read_interop_subtitle_test2)
                                   dcp::Colour(0, 0, 0),
                                   dcp::Time(0, 0, 0, 0, 250),
                                   dcp::Time(0, 0, 0, 0, 250),
-                                  0
+                                  0,
+                                  {}
                                   ));
 
        s = subs.subtitles_during (dcp::Time (0, 1, 27, 200, 250), dcp::Time (0, 1, 27, 201, 250), false);
@@ -455,7 +469,8 @@ BOOST_AUTO_TEST_CASE (read_interop_subtitle_test2)
                                   dcp::Colour(0, 0, 0),
                                   dcp::Time(0, 0, 0, 0, 250),
                                   dcp::Time(0, 0, 0, 0, 250),
-                                  0
+                                  0,
+                                  {}
                                   ));
        BOOST_REQUIRE (dynamic_pointer_cast<const dcp::SubtitleString>(s.back()));
        BOOST_CHECK_EQUAL (*dynamic_pointer_cast<const dcp::SubtitleString>(s.back()), dcp::SubtitleString (
@@ -479,7 +494,8 @@ BOOST_AUTO_TEST_CASE (read_interop_subtitle_test2)
                                   dcp::Colour(0, 0, 0),
                                   dcp::Time(0, 0, 0, 0, 250),
                                   dcp::Time(0, 0, 0, 0, 250),
-                                  0
+                                  0,
+                                  {}
                                   ));
 
        s = subs.subtitles_during (dcp::Time (0, 1, 42, 300, 250), dcp::Time (0, 1, 42, 301, 250), false);
@@ -506,7 +522,8 @@ BOOST_AUTO_TEST_CASE (read_interop_subtitle_test2)
                                   dcp::Colour(0, 0, 0),
                                   dcp::Time(0, 0, 0, 0, 250),
                                   dcp::Time(0, 0, 0, 0, 250),
-                                  0
+                                  0,
+                                  {}
                                   ));
        BOOST_REQUIRE (dynamic_pointer_cast<const dcp::SubtitleString>(s.back()));
        BOOST_CHECK_EQUAL (*dynamic_pointer_cast<const dcp::SubtitleString>(s.back()), dcp::SubtitleString (
@@ -530,7 +547,8 @@ BOOST_AUTO_TEST_CASE (read_interop_subtitle_test2)
                                   dcp::Colour(0, 0, 0),
                                   dcp::Time(0, 0, 0, 0, 250),
                                   dcp::Time(0, 0, 0, 0, 250),
-                                  0
+                                  0,
+                                  {}
                                   ));
 
        s = subs.subtitles_during (dcp::Time (0, 1, 45, 200, 250), dcp::Time (0, 1, 45, 201, 250), false);
@@ -557,7 +575,8 @@ BOOST_AUTO_TEST_CASE (read_interop_subtitle_test2)
                                   dcp::Colour(0, 0, 0),
                                   dcp::Time(0, 0, 0, 0, 250),
                                   dcp::Time(0, 0, 0, 0, 250),
-                                  0
+                                  0,
+                                  {}
                                   ));
        BOOST_REQUIRE (dynamic_pointer_cast<const dcp::SubtitleString>(s.back()));
        BOOST_CHECK_EQUAL (*dynamic_pointer_cast<const dcp::SubtitleString>(s.back()), dcp::SubtitleString (
@@ -581,7 +600,8 @@ BOOST_AUTO_TEST_CASE (read_interop_subtitle_test2)
                                   dcp::Colour(0, 0, 0),
                                   dcp::Time(0, 0, 0, 0, 250),
                                   dcp::Time(0, 0, 0, 0, 250),
-                                  0
+                                  0,
+                                  {}
                                   ));
 
        s = subs.subtitles_during (dcp::Time (0, 1, 47, 249, 250), dcp::Time (0, 1, 47, 250, 250), false);
@@ -608,7 +628,8 @@ BOOST_AUTO_TEST_CASE (read_interop_subtitle_test2)
                                   dcp::Colour(0, 0, 0),
                                   dcp::Time(0, 0, 0, 0, 250),
                                   dcp::Time(0, 0, 0, 0, 250),
-                                  0
+                                  0,
+                                  {}
                                   ));
        BOOST_REQUIRE (dynamic_pointer_cast<const dcp::SubtitleString>(s.back()));
        BOOST_CHECK_EQUAL (*dynamic_pointer_cast<const dcp::SubtitleString>(s.back()), dcp::SubtitleString (
@@ -632,7 +653,8 @@ BOOST_AUTO_TEST_CASE (read_interop_subtitle_test2)
                                   dcp::Colour(0, 0, 0),
                                   dcp::Time(0, 0, 0, 0, 250),
                                   dcp::Time(0, 0, 0, 0, 250),
-                                  0
+                                  0,
+                                  {}
                                   ));
 
        s = subs.subtitles_during (dcp::Time (0, 2, 6, 210, 250), dcp::Time (0, 2, 6, 211, 250), false);
@@ -659,7 +681,8 @@ BOOST_AUTO_TEST_CASE (read_interop_subtitle_test2)
                                   dcp::Colour(0, 0, 0),
                                   dcp::Time(0, 0, 0, 0, 250),
                                   dcp::Time(0, 0, 0, 0, 250),
-                                  0
+                                  0,
+                                  {}
                                   ));
        BOOST_REQUIRE (dynamic_pointer_cast<const dcp::SubtitleString>(s.back()));
        BOOST_CHECK_EQUAL (*dynamic_pointer_cast<const dcp::SubtitleString>(s.back()), dcp::SubtitleString (
@@ -683,7 +706,8 @@ BOOST_AUTO_TEST_CASE (read_interop_subtitle_test2)
                                   dcp::Colour(0, 0, 0),
                                   dcp::Time(0, 0, 0, 0, 250),
                                   dcp::Time(0, 0, 0, 0, 250),
-                                  0
+                                  0,
+                                  {}
                                   ));
 }
 
@@ -729,7 +753,8 @@ BOOST_AUTO_TEST_CASE (write_interop_subtitle_test)
                        dcp::Colour (0, 0, 0),
                        dcp::Time (0, 0, 0, 0, 24),
                        dcp::Time (0, 0, 0, 0, 24),
-                       0
+                       0,
+                       std::vector<dcp::Ruby>()
                        )
                );
 
@@ -755,7 +780,8 @@ BOOST_AUTO_TEST_CASE (write_interop_subtitle_test)
                        dcp::Colour (1, 2, 3),
                        dcp::Time (1, 2, 3, 4, 24),
                        dcp::Time (5, 6, 7, 8, 24),
-                       0
+                       0,
+                       std::vector<dcp::Ruby>()
                        )
                );
 
@@ -781,7 +807,8 @@ BOOST_AUTO_TEST_CASE (write_interop_subtitle_test)
                        dcp::Colour (1, 2, 3),
                        dcp::Time (1, 2, 3, 4, 24),
                        dcp::Time (5, 6, 7, 8, 24),
-                       9
+                       9,
+                       std::vector<dcp::Ruby>()
                        )
                );
 
@@ -841,7 +868,8 @@ BOOST_AUTO_TEST_CASE (write_interop_subtitle_test2)
                        dcp::Colour (0, 0, 0),
                        dcp::Time (0, 0, 0, 0, 24),
                        dcp::Time (0, 0, 0, 0, 24),
-                       0
+                       0,
+                       std::vector<dcp::Ruby>()
                        )
                );
 
@@ -867,7 +895,8 @@ BOOST_AUTO_TEST_CASE (write_interop_subtitle_test2)
                        dcp::Colour (1, 2, 3),
                        dcp::Time (1, 2, 3, 4, 24),
                        dcp::Time (5, 6, 7, 8, 24),
-                       0
+                       0,
+                       std::vector<dcp::Ruby>()
                        )
                );
 
index 69ae2be4e315334c2dcd5463e4ea67ab0c5dfee9..7ac20e1071bf04f9e74b438db15b90e8a85f7769 100644 (file)
@@ -108,7 +108,7 @@ BOOST_AUTO_TEST_CASE (pull_fonts_test1)
        auto root = make_shared<dcp::order::Part>(shared_ptr<dcp::order::Part>());
        auto sub1 = make_shared<dcp::order::Subtitle>(root, dcp::Time(), dcp::Time(), dcp::Time(), dcp::Time());
        root->children.push_back (sub1);
-       auto text1 = make_shared<dcp::order::Text>(sub1, dcp::HAlign::CENTER, 0, dcp::VAlign::TOP, 0, 0, dcp::Direction::LTR);
+       auto text1 = make_shared<dcp::order::Text>(sub1, dcp::HAlign::CENTER, 0, dcp::VAlign::TOP, 0, 0, dcp::Direction::LTR, std::vector<dcp::Ruby>());
        sub1->children.push_back (text1);
        text1->font._values["font"] = "Inconsolata";
        text1->font._values["size"] = "42";
@@ -127,11 +127,11 @@ BOOST_AUTO_TEST_CASE (pull_fonts_test2)
        auto root = make_shared<dcp::order::Part>(shared_ptr<dcp::order::Part> ());
        auto sub1 = make_shared<dcp::order::Subtitle>(root, dcp::Time(), dcp::Time(), dcp::Time(), dcp::Time());
        root->children.push_back (sub1);
-       auto text1 = make_shared<dcp::order::Text>(sub1, dcp::HAlign::CENTER, 0, dcp::VAlign::TOP, 0, 0, dcp::Direction::LTR);
+       auto text1 = make_shared<dcp::order::Text>(sub1, dcp::HAlign::CENTER, 0, dcp::VAlign::TOP, 0, 0, dcp::Direction::LTR, std::vector<dcp::Ruby>());
        sub1->children.push_back (text1);
        text1->font._values["font"] = "Inconsolata";
        text1->font._values["size"] = "42";
-       auto text2 = make_shared<dcp::order::Text>(sub1, dcp::HAlign::CENTER, 0, dcp::VAlign::TOP, 0, 0, dcp::Direction::LTR);
+       auto text2 = make_shared<dcp::order::Text>(sub1, dcp::HAlign::CENTER, 0, dcp::VAlign::TOP, 0, 0, dcp::Direction::LTR, std::vector<dcp::Ruby>());
        sub1->children.push_back (text2);
        text2->font._values["font"] = "Inconsolata";
        text2->font._values["size"] = "48";
@@ -152,7 +152,7 @@ BOOST_AUTO_TEST_CASE (pull_fonts_test3)
        auto root = make_shared<dcp::order::Part>(shared_ptr<dcp::order::Part> ());
        auto sub1 = make_shared<dcp::order::Subtitle>(root, dcp::Time(), dcp::Time(), dcp::Time(), dcp::Time());
        root->children.push_back (sub1);
-       auto text1 = make_shared<dcp::order::Text>(sub1, dcp::HAlign::CENTER, 0, dcp::VAlign::TOP, 0, 0, dcp::Direction::LTR);
+       auto text1 = make_shared<dcp::order::Text>(sub1, dcp::HAlign::CENTER, 0, dcp::VAlign::TOP, 0, 0, dcp::Direction::LTR, std::vector<dcp::Ruby>());
        sub1->children.push_back (text1);
        dcp::order::Font font;
        font._values["font"] = "Inconsolata";
@@ -217,3 +217,12 @@ BOOST_AUTO_TEST_CASE (format_xml_entities_test)
 "  <Bar>Don't panic &amp;amp; xml \"is\" 'great' &amp; &lt; &gt; —</Bar>\n"
 "</Foo>\n");
 }
+
+
+BOOST_AUTO_TEST_CASE(ruby_round_trip_test)
+{
+       dcp::InteropSubtitleAsset asset("test/data/ruby1.xml");
+       check_xml(dcp::file_to_string("test/data/ruby1.xml"), asset.xml_as_string(), {}, false);
+}
+
+
index 414d392dc4f50d25b890e9ddc6b25d8cb74c62d8..eca5cd1f033602d41556a7b4322cad23f50eab03 100644 (file)
@@ -72,7 +72,8 @@ BOOST_AUTO_TEST_CASE (smpte_subtitle_id_test)
                        dcp::Colour(),
                        dcp::Time(0, 0, 0, 0, 24),
                        dcp::Time(0, 0, 0, 0, 24),
-                       0
+                       0,
+                       std::vector<dcp::Ruby>()
                        )
                );
        subs.write("build/test/smpte_subtitle_id_test.mxf");
@@ -201,7 +202,8 @@ BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test)
                        dcp::Colour (0, 0, 0),
                        dcp::Time (0, 0, 0, 0, 24),
                        dcp::Time (0, 0, 0, 0, 24),
-                       0
+                       0,
+                       std::vector<dcp::Ruby>()
                        )
                );
 
@@ -227,7 +229,8 @@ BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test)
                        dcp::Colour (1, 2, 3),
                        dcp::Time (1, 2, 3, 4, 24),
                        dcp::Time (5, 6, 7, 8, 24),
-                       0
+                       0,
+                       std::vector<dcp::Ruby>()
                        )
                );
 
@@ -253,7 +256,8 @@ BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test)
                        dcp::Colour (1, 2, 3),
                        dcp::Time (1, 2, 3, 4, 24),
                        dcp::Time (5, 6, 7, 8, 24),
-                       4.2
+                       4.2,
+                       std::vector<dcp::Ruby>()
                        )
                );
 
@@ -320,7 +324,8 @@ BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test2)
                        dcp::Colour (0, 0, 0),
                        dcp::Time (0, 0, 0, 0, 24),
                        dcp::Time (0, 0, 0, 0, 24),
-                       0
+                       0,
+                       std::vector<dcp::Ruby>()
                        )
                );
 
@@ -346,7 +351,8 @@ BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test2)
                        dcp::Colour (0, 0, 0),
                        dcp::Time (0, 0, 0, 0, 24),
                        dcp::Time (0, 0, 0, 0, 24),
-                       0
+                       0,
+                       std::vector<dcp::Ruby>()
                        )
                );
 
@@ -372,7 +378,8 @@ BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test2)
                        dcp::Colour (0, 0, 0),
                        dcp::Time (0, 0, 0, 0, 24),
                        dcp::Time (0, 0, 0, 0, 24),
-                       0
+                       0,
+                       std::vector<dcp::Ruby>()
                        )
                );
 
@@ -398,7 +405,8 @@ BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test2)
                        dcp::Colour (0, 0, 0),
                        dcp::Time (0, 0, 0, 0, 24),
                        dcp::Time (0, 0, 0, 0, 24),
-                       0
+                       0,
+                       std::vector<dcp::Ruby>()
                        )
                );
 
@@ -424,7 +432,8 @@ BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test2)
                        dcp::Colour (0, 0, 0),
                        dcp::Time (0, 0, 0, 0, 24),
                        dcp::Time (0, 0, 0, 0, 24),
-                       0
+                       0,
+                       std::vector<dcp::Ruby>()
                        )
                );
 
@@ -450,7 +459,8 @@ BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test2)
                        dcp::Colour (0, 0, 0),
                        dcp::Time (0, 0, 0, 0, 24),
                        dcp::Time (0, 0, 0, 0, 24),
-                       0
+                       0,
+                       std::vector<dcp::Ruby>()
                        )
                );
 
@@ -564,7 +574,8 @@ BOOST_AUTO_TEST_CASE (write_subtitles_in_vertical_order_with_top_alignment)
                        dcp::Colour (0, 0, 0),
                        dcp::Time (0, 0, 0, 0, 24),
                        dcp::Time (0, 0, 0, 0, 24),
-                       0
+                       0,
+                       std::vector<dcp::Ruby>()
                        )
                );
 
@@ -590,7 +601,8 @@ BOOST_AUTO_TEST_CASE (write_subtitles_in_vertical_order_with_top_alignment)
                        dcp::Colour (0, 0, 0),
                        dcp::Time (0, 0, 0, 0, 24),
                        dcp::Time (0, 0, 0, 0, 24),
-                       0
+                       0,
+                       std::vector<dcp::Ruby>()
                        )
                );
 
@@ -652,7 +664,8 @@ BOOST_AUTO_TEST_CASE (write_subtitles_in_vertical_order_with_bottom_alignment)
                        dcp::Colour (0, 0, 0),
                        dcp::Time (0, 0, 0, 0, 24),
                        dcp::Time (0, 0, 0, 0, 24),
-                       0
+                       0,
+                       std::vector<dcp::Ruby>()
                        )
                );
 
@@ -678,7 +691,8 @@ BOOST_AUTO_TEST_CASE (write_subtitles_in_vertical_order_with_bottom_alignment)
                        dcp::Colour (0, 0, 0),
                        dcp::Time (0, 0, 0, 0, 24),
                        dcp::Time (0, 0, 0, 0, 24),
-                       0
+                       0,
+                       std::vector<dcp::Ruby>()
                        )
                );
 
index bff647d7a3675ea741fe64867a1cb5e479fdc15f..d384bdfa4fe8593616def05c305a15a0752119f6 100644 (file)
@@ -417,7 +417,8 @@ simple_subtitle ()
                dcp::Colour(255, 255, 255),
                dcp::Time(),
                dcp::Time(),
-               0
+               0,
+               std::vector<dcp::Ruby>()
                );
 }
 
index 2de443e3a92a719112ca75a71db80280f862beda..a1a2d280245e5267190655994d5595220831a53a 100644 (file)
@@ -1335,7 +1335,8 @@ add_test_subtitle (shared_ptr<dcp::SubtitleAsset> asset, int start_frame, int en
                        dcp::Colour(),
                        dcp::Time(),
                        dcp::Time(),
-                       0
+                       0,
+                       std::vector<dcp::Ruby>()
                )
        );
 }