Support MarginL and MarginR in SSA subtitles (DoM #2811). main v1.6.49
authorCarl Hetherington <cth@carlh.net>
Wed, 22 May 2024 21:29:58 +0000 (23:29 +0200)
committerCarl Hetherington <cth@carlh.net>
Wed, 22 May 2024 21:30:01 +0000 (23:30 +0200)
src/ssa_reader.cc
src/ssa_reader.h
test/data/horizontal_margin.ssa [new file with mode: 0644]
test/ssa_reader_test.cc

index 262bba74aab487f32ab9f9f65839b4610756d494..1302b6becaddf141f87c6b862501a04cfcd04865 100644 (file)
@@ -149,6 +149,10 @@ public:
                                }
                        } else if (keys[i] == "MarginV") {
                                vertical_margin = raw_convert<int> (style[i]);
+                       } else if (keys[i] == "MarginL") {
+                               left_margin = raw_convert<int>(style[i]);
+                       } else if (keys[i] == "MarginR") {
+                               right_margin = raw_convert<int>(style[i]);
                        }
                }
        }
@@ -166,6 +170,8 @@ public:
        HorizontalReference horizontal_reference;
        VerticalReference vertical_reference;
        int vertical_margin;
+       int left_margin = 0;
+       int right_margin = 0;
 
 private:
        Colour colour (string c) const
@@ -185,6 +191,24 @@ private:
        }
 };
 
+
+void
+SSAReader::Context::update_horizontal_position(RawSubtitle& sub) const
+{
+       switch (sub.horizontal_position.reference) {
+       case LEFT_OF_SCREEN:
+               sub.horizontal_position.proportional = static_cast<float>(left_margin) / play_res_x;
+               break;
+       case HORIZONTAL_CENTRE_OF_SCREEN:
+               sub.horizontal_position.proportional = static_cast<float>(left_margin - right_margin) / (2 * play_res_x);
+               break;
+       case RIGHT_OF_SCREEN:
+               sub.horizontal_position.proportional = static_cast<float>(right_margin) / play_res_x;
+               break;
+       }
+}
+
+
 Time
 SSAReader::parse_time (string t) const
 {
@@ -218,30 +242,39 @@ SSAReader::parse_tag(RawSubtitle& sub, string tag, Context const& context)
        } else if (tag == "\\an1") {
                sub.horizontal_position.reference = sub::LEFT_OF_SCREEN;
                sub.vertical_position.reference = sub::BOTTOM_OF_SCREEN;
+               context.update_horizontal_position(sub);
        } else if (tag == "\\an2") {
                sub.horizontal_position.reference = sub::HORIZONTAL_CENTRE_OF_SCREEN;
                sub.vertical_position.reference = sub::BOTTOM_OF_SCREEN;
+               context.update_horizontal_position(sub);
        } else if (tag == "\\an3") {
                sub.horizontal_position.reference = sub::RIGHT_OF_SCREEN;
                sub.vertical_position.reference = sub::BOTTOM_OF_SCREEN;
+               context.update_horizontal_position(sub);
        } else if (tag == "\\an4") {
                sub.horizontal_position.reference = sub::LEFT_OF_SCREEN;
                sub.vertical_position.reference = sub::VERTICAL_CENTRE_OF_SCREEN;
+               context.update_horizontal_position(sub);
        } else if (tag == "\\an5") {
                sub.horizontal_position.reference = sub::HORIZONTAL_CENTRE_OF_SCREEN;
                sub.vertical_position.reference = sub::VERTICAL_CENTRE_OF_SCREEN;
+               context.update_horizontal_position(sub);
        } else if (tag == "\\an6") {
                sub.horizontal_position.reference = sub::RIGHT_OF_SCREEN;
                sub.vertical_position.reference = sub::VERTICAL_CENTRE_OF_SCREEN;
+               context.update_horizontal_position(sub);
        } else if (tag == "\\an7") {
                sub.horizontal_position.reference = sub::LEFT_OF_SCREEN;
                sub.vertical_position.reference = sub::TOP_OF_SCREEN;
+               context.update_horizontal_position(sub);
        } else if (tag == "\\an8") {
                sub.horizontal_position.reference = sub::HORIZONTAL_CENTRE_OF_SCREEN;
                sub.vertical_position.reference = sub::TOP_OF_SCREEN;
+               context.update_horizontal_position(sub);
        } else if (tag == "\\an9") {
                sub.horizontal_position.reference = sub::RIGHT_OF_SCREEN;
                sub.vertical_position.reference = sub::TOP_OF_SCREEN;
+               context.update_horizontal_position(sub);
        } else if (boost::starts_with(tag, "\\pos")) {
                vector<string> bits;
                boost::algorithm::split (bits, tag, boost::is_any_of("(,"));
@@ -293,6 +326,8 @@ SSAReader::parse_line(RawSubtitle base, string line, Context const& context)
         */
        current.vertical_position.proportional = 0;
 
+       context.update_horizontal_position(current);
+
        /* We must have a font size, as there could be a margin specified
           in pixels and in that case we must know how big the subtitle
           lines are to work out the position on screen.
@@ -477,6 +512,8 @@ SSAReader::read (function<optional<string> ()> get_line)
 
                                RawSubtitle sub;
                                optional<Style> style;
+                               int left_margin = 0;
+                               int right_margin = 0;
 
                                for (size_t i = 0; i < event.size(); ++i) {
                                        trim (event[i]);
@@ -512,13 +549,24 @@ SSAReader::read (function<optional<string> ()> get_line)
                                                if (sub.vertical_position.reference != sub::VERTICAL_CENTRE_OF_SCREEN) {
                                                        sub.vertical_position.proportional = float(style->vertical_margin) / play_res_y;
                                                }
+                                               left_margin = style->left_margin;
+                                               right_margin = style->right_margin;
                                        } else if (event_format[i] == "MarginV") {
                                                if (event[i] != "0" && sub.vertical_position.reference != sub::VERTICAL_CENTRE_OF_SCREEN) {
                                                        /* Override the style if its non-zero */
                                                        sub.vertical_position.proportional = raw_convert<float>(event[i]) / play_res_y;
                                                }
+                                       } else if (event_format[i] == "MarginL") {
+                                               if (event[i] != "0") {
+                                                       left_margin = raw_convert<int>(event[i]);
+                                               }
+                                       } else if (event_format[i] == "MarginR") {
+                                               if (event[i] != "0") {
+                                                       right_margin = raw_convert<int>(event[i]);
+                                               }
                                        } else if (event_format[i] == "Text") {
-                                               for (auto j: parse_line(sub, event[i], Context(play_res_x, play_res_y, style ? style->primary_colour : Colour(1, 1, 1)))) {
+                                               auto context = Context(play_res_x, play_res_y, style ? style->primary_colour : Colour(1, 1, 1), left_margin, right_margin);
+                                               for (auto j: parse_line(sub, event[i], context)) {
                                                        _subs.push_back (j);
                                                }
                                        }
index 5de660c073c4676bd28f2e0e0cfc1916527ffa30..e8c2a521f7e6e7c51cd23cc4e6cf6c893e12a4e6 100644 (file)
@@ -44,15 +44,21 @@ public:
        class Context
        {
        public:
-               Context(int play_res_x_, int play_res_y_, Colour primary_colour_)
+               Context(int play_res_x_, int play_res_y_, Colour primary_colour_, int left_margin_ = 0, int right_margin_ = 0)
                        : play_res_x(play_res_x_)
                        , play_res_y(play_res_y_)
                        , primary_colour(primary_colour_)
+                       , left_margin(left_margin_)
+                       , right_margin(right_margin_)
                {}
 
                int play_res_x;
                int play_res_y;
                Colour primary_colour;
+               int left_margin;
+               int right_margin;
+
+               void update_horizontal_position(RawSubtitle& sub) const;
        };
 
        static std::vector<RawSubtitle> parse_line(RawSubtitle base, std::string line, Context const& context);
diff --git a/test/data/horizontal_margin.ssa b/test/data/horizontal_margin.ssa
new file mode 100644 (file)
index 0000000..c5a9f9e
--- /dev/null
@@ -0,0 +1,23 @@
+[Script Info]
+Title: libsub test
+ScriptType: v4.00
+WrapStyle: 0
+ScaledBorderAndShadow: yes
+PlayResX: 1920
+PlayResY: 1080
+
+[V4 Styles]
+Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding
+Style: Default,Arial,20,16777215,255,0,0,0,0,1,2,2,2,200,100,10,0,1
+
+[Fonts]
+
+[Graphics]
+
+[Events]
+Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
+Dialogue: Marked=0,0:00:01.23,0:00:04.55,Default,,100,0,0,,Hello world
+Dialogue: Marked=0,0:01:01.23,0:01:04.55,Default,,10,0,0,,Left of centre
+Dialogue: Marked=0,0:02:01.23,0:02:04.55,Default,,10,0,0,,{\an4}Left
+Dialogue: Marked=0,0:03:01.23,0:03:04.55,Default,,10,0,0,,{\an6}Right
+Dialogue: Marked=0,0:04:01.23,0:04:04.55,Default,,0,100,0,,{\an4}Style left
index 89a543c93205297260c71570cabf8946f9258118..8820bf41df1ccab2bd0f727f88d1eb1f236cf5ad 100644 (file)
@@ -254,7 +254,7 @@ BOOST_AUTO_TEST_CASE (ssa_reader_test3)
        /* Alignments */
 
        SUB_START (sub::Time::from_hms (0, 0, 9, 230), sub::Time::from_hms (0, 0, 11, 560));
-       LINE ((10.0 / 1080), sub::BOTTOM_OF_SCREEN, 0, sub::LEFT_OF_SCREEN);
+       LINE ((10.0 / 1080), sub::BOTTOM_OF_SCREEN, (10.0 / 1920), sub::LEFT_OF_SCREEN);
        BLOCK("bottom left", "Arial", fs(20), false, false, false);
        SUB_END ();
 
@@ -264,13 +264,13 @@ BOOST_AUTO_TEST_CASE (ssa_reader_test3)
        SUB_END ();
 
        SUB_START (sub::Time::from_hms (0, 0, 9, 250), sub::Time::from_hms (0, 0, 11, 560));
-       LINE ((10.0 / 1080), sub::BOTTOM_OF_SCREEN, 0, sub::RIGHT_OF_SCREEN);
+       LINE ((10.0 / 1080), sub::BOTTOM_OF_SCREEN, (10.0 / 1920), sub::RIGHT_OF_SCREEN);
        BLOCK("bottom right", "Arial", fs(20), false, false, false);
        SUB_END ();
 
        SUB_START (sub::Time::from_hms (0, 0, 9, 260), sub::Time::from_hms (0, 0, 11, 560));
        /* Position is half of a 20pt line (with line spacing) above vertical centre */
-       LINE (-vp(10), sub::VERTICAL_CENTRE_OF_SCREEN, 0, sub::LEFT_OF_SCREEN);
+       LINE (-vp(10), sub::VERTICAL_CENTRE_OF_SCREEN, (10.0 / 1920), sub::LEFT_OF_SCREEN);
        BLOCK("middle left", "Arial", fs(20), false, false, false);
        SUB_END ();
 
@@ -280,12 +280,12 @@ BOOST_AUTO_TEST_CASE (ssa_reader_test3)
        SUB_END ();
 
        SUB_START (sub::Time::from_hms (0, 0, 9, 280), sub::Time::from_hms (0, 0, 11, 560));
-       LINE (-vp(10), sub::VERTICAL_CENTRE_OF_SCREEN, 0, sub::RIGHT_OF_SCREEN);
+       LINE (-vp(10), sub::VERTICAL_CENTRE_OF_SCREEN, (10.0 / 1920), sub::RIGHT_OF_SCREEN);
        BLOCK("middle right", "Arial", fs(20), false, false, false);
        SUB_END ();
 
        SUB_START (sub::Time::from_hms (0, 0, 9, 290), sub::Time::from_hms (0, 0, 11, 560));
-       LINE ((10.0 / 1080), sub::TOP_OF_SCREEN, 0, sub::LEFT_OF_SCREEN);
+       LINE ((10.0 / 1080), sub::TOP_OF_SCREEN, (10.0 / 1920), sub::LEFT_OF_SCREEN);
        BLOCK("top left", "Arial", fs(20), false, false, false);
        SUB_END ();
 
@@ -295,7 +295,7 @@ BOOST_AUTO_TEST_CASE (ssa_reader_test3)
        SUB_END ();
 
        SUB_START (sub::Time::from_hms (0, 0, 9, 310), sub::Time::from_hms (0, 0, 11, 560));
-       LINE ((10.0 / 1080), sub::TOP_OF_SCREEN, 0, sub::RIGHT_OF_SCREEN);
+       LINE ((10.0 / 1080), sub::TOP_OF_SCREEN, (10.0 / 1920), sub::RIGHT_OF_SCREEN);
        BLOCK("top right", "Arial", fs(20), false, false, false);
        SUB_END ();
 
@@ -637,3 +637,37 @@ BOOST_AUTO_TEST_CASE (ssa_reader_c)
        test_c("", "ff00ff");
 }
 
+
+BOOST_AUTO_TEST_CASE(ssa_reader_horizontal_margin)
+{
+       auto subs = read_file("test/data/horizontal_margin.ssa");
+       BOOST_REQUIRE_EQUAL(subs.size(), 5U);
+
+       int n = 0;
+
+       BOOST_REQUIRE_EQUAL(subs[n].lines.size(), 1U);
+       BOOST_CHECK(subs[n].lines[0].horizontal_position.reference == sub::HORIZONTAL_CENTRE_OF_SCREEN);
+       BOOST_CHECK_CLOSE(subs[n].lines[0].horizontal_position.proportional, 0, 1);
+       ++n;
+
+       BOOST_REQUIRE_EQUAL(subs[n].lines.size(), 1U);
+       BOOST_CHECK(subs[n].lines[0].horizontal_position.reference == sub::HORIZONTAL_CENTRE_OF_SCREEN);
+       BOOST_CHECK_CLOSE(subs[n].lines[0].horizontal_position.proportional, -90.0f / (2 * 1920.0f), 1);
+       ++n;
+
+       BOOST_REQUIRE_EQUAL(subs[n].lines.size(), 1U);
+       BOOST_CHECK(subs[n].lines[0].horizontal_position.reference == sub::LEFT_OF_SCREEN);
+       BOOST_CHECK_CLOSE(subs[n].lines[0].horizontal_position.proportional, 10.0f / 1920.f, 1);
+       ++n;
+
+       BOOST_REQUIRE_EQUAL(subs[n].lines.size(), 1U);
+       BOOST_CHECK(subs[n].lines[0].horizontal_position.reference == sub::RIGHT_OF_SCREEN);
+       BOOST_CHECK_CLOSE(subs[n].lines[0].horizontal_position.proportional, 100.0f / 1920.f, 1);
+       ++n;
+
+       BOOST_REQUIRE_EQUAL(subs[n].lines.size(), 1U);
+       BOOST_CHECK(subs[n].lines[0].horizontal_position.reference == sub::LEFT_OF_SCREEN);
+       BOOST_CHECK_CLOSE(subs[n].lines[0].horizontal_position.proportional, 200.0f / 1920.f, 1);
+       ++n;
+}
+