2 Copyright (C) 2015-2021 Carl Hetherington <cth@carlh.net>
4 This file is part of libdcp.
6 libdcp is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 libdcp is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with libdcp. If not, see <http://www.gnu.org/licenses/>.
19 In addition, as a special exception, the copyright holders give
20 permission to link the code of portions of this program with the
21 OpenSSL library under certain conditions as described in each
22 individual source file, and distribute linked combinations
25 You must obey the GNU General Public License in all respects
26 for all of the code used other than OpenSSL. If you modify
27 file(s) with this exception, you may extend this exception to your
28 version of the file(s), but you are not obligated to do so. If you
29 do not wish to do so, delete this exception statement from your
30 version. If you delete this exception statement from all source
31 files in the program, then also delete it here.
34 #include "interop_subtitle_asset.h"
35 #include "smpte_subtitle_asset.h"
36 #include "subtitle_string.h"
37 #include "subtitle_image.h"
38 #include "subtitle_asset_internal.h"
39 #include "reel_interop_subtitle_asset.h"
45 #include <boost/test/unit_test.hpp>
48 using std::shared_ptr;
50 using std::make_shared;
51 using boost::optional;
53 /** Test dcp::order::Font::take_intersection */
54 BOOST_AUTO_TEST_CASE (take_intersection_test)
57 A._values["foo"] = "bar";
58 A._values["fred"] = "jim";
61 B._values["foo"] = "bar";
62 B._values["sheila"] = "baz";
64 A.take_intersection (B);
65 BOOST_REQUIRE_EQUAL (A._values.size(), 1);
66 BOOST_CHECK_EQUAL (A._values["foo"], "bar");
71 A._values["foo"] = "bar";
72 A._values["fred"] = "jim";
74 B._values["foo"] = "hello";
75 B._values["sheila"] = "baz";
77 A.take_intersection (B);
78 BOOST_CHECK_EQUAL (A._values.size(), 0);
81 /** Test dcp::order::Font::take_difference */
82 BOOST_AUTO_TEST_CASE (take_difference_test)
85 A._values["foo"] = "bar";
86 A._values["fred"] = "jim";
89 B._values["foo"] = "bar";
90 B._values["sheila"] = "baz";
92 A.take_difference (B);
93 BOOST_REQUIRE_EQUAL (A._values.size(), 1);
94 BOOST_CHECK_EQUAL (A._values["fred"], "jim");
97 /** Test dcp::order::Subtitle::pull_fonts */
98 BOOST_AUTO_TEST_CASE (pull_fonts_test1)
100 auto root = make_shared<dcp::order::Part>(shared_ptr<dcp::order::Part>());
101 auto sub1 = make_shared<dcp::order::Subtitle>(root, dcp::Time(), dcp::Time(), dcp::Time(), dcp::Time());
102 root->children.push_back (sub1);
103 auto text1 = make_shared<dcp::order::Text>(sub1, dcp::HAlign::CENTER, 0, dcp::VAlign::TOP, 0, dcp::Direction::LTR);
104 sub1->children.push_back (text1);
105 text1->font._values["font"] = "Inconsolata";
106 text1->font._values["size"] = "42";
108 dcp::SubtitleAsset::pull_fonts (root);
110 BOOST_REQUIRE_EQUAL (sub1->font._values.size(), 2);
111 BOOST_CHECK_EQUAL (sub1->font._values["font"], "Inconsolata");
112 BOOST_CHECK_EQUAL (sub1->font._values["size"], "42");
113 BOOST_CHECK_EQUAL (text1->font._values.size(), 0);
116 /** Test dcp::order::Subtitle::pull_fonts */
117 BOOST_AUTO_TEST_CASE (pull_fonts_test2)
119 shared_ptr<dcp::order::Part> root (new dcp::order::Part (shared_ptr<dcp::order::Part> ()));
120 shared_ptr<dcp::order::Subtitle> sub1 (new dcp::order::Subtitle (root, dcp::Time(), dcp::Time(), dcp::Time(), dcp::Time()));
121 root->children.push_back (sub1);
122 shared_ptr<dcp::order::Text> text1 (new dcp::order::Text (sub1, dcp::HAlign::CENTER, 0, dcp::VAlign::TOP, 0, dcp::Direction::LTR));
123 sub1->children.push_back (text1);
124 text1->font._values["font"] = "Inconsolata";
125 text1->font._values["size"] = "42";
126 shared_ptr<dcp::order::Text> text2 (new dcp::order::Text (sub1, dcp::HAlign::CENTER, 0, dcp::VAlign::TOP, 0, dcp::Direction::LTR));
127 sub1->children.push_back (text2);
128 text2->font._values["font"] = "Inconsolata";
129 text2->font._values["size"] = "48";
131 dcp::SubtitleAsset::pull_fonts (root);
133 BOOST_REQUIRE_EQUAL (sub1->font._values.size(), 1);
134 BOOST_CHECK_EQUAL (sub1->font._values["font"], "Inconsolata");
135 BOOST_REQUIRE_EQUAL (text1->font._values.size(), 1);
136 BOOST_CHECK_EQUAL (text1->font._values["size"], "42");
137 BOOST_REQUIRE_EQUAL (text2->font._values.size(), 1);
138 BOOST_CHECK_EQUAL (text2->font._values["size"], "48");
141 /** Test dcp::order::Subtitle::pull_fonts */
142 BOOST_AUTO_TEST_CASE (pull_fonts_test3)
144 shared_ptr<dcp::order::Part> root (new dcp::order::Part (shared_ptr<dcp::order::Part> ()));
145 shared_ptr<dcp::order::Subtitle> sub1 (new dcp::order::Subtitle (root, dcp::Time(), dcp::Time(), dcp::Time(), dcp::Time()));
146 root->children.push_back (sub1);
147 shared_ptr<dcp::order::Text> text1 (new dcp::order::Text (sub1, dcp::HAlign::CENTER, 0, dcp::VAlign::TOP, 0, dcp::Direction::LTR));
148 sub1->children.push_back (text1);
149 dcp::order::Font font;
150 font._values["font"] = "Inconsolata";
151 font._values["size"] = "42";
152 shared_ptr<dcp::order::String> string1 (new dcp::order::String (text1, font, "Hello world"));
153 text1->children.push_back (string1);
155 dcp::SubtitleAsset::pull_fonts (root);
157 BOOST_REQUIRE_EQUAL (sub1->font._values.size(), 2);
158 BOOST_CHECK_EQUAL (sub1->font._values["font"], "Inconsolata");
159 BOOST_CHECK_EQUAL (sub1->font._values["size"], "42");
162 /** Write some subtitle content as Interop XML and check that it is right */
163 BOOST_AUTO_TEST_CASE (write_interop_subtitle_test)
165 dcp::InteropSubtitleAsset c;
166 c.set_reel_number ("1");
167 c.set_language ("EN");
168 c.set_movie_title ("Test");
171 shared_ptr<dcp::Subtitle> (
172 new dcp::SubtitleString (
177 dcp::Colour (255, 255, 255),
180 dcp::Time (0, 4, 9, 22, 24),
181 dcp::Time (0, 4, 11, 22, 24),
189 dcp::Colour (0, 0, 0),
190 dcp::Time (0, 0, 0, 0, 24),
191 dcp::Time (0, 0, 0, 0, 24)
197 shared_ptr<dcp::Subtitle> (
198 new dcp::SubtitleString (
199 boost::optional<string> (),
203 dcp::Colour (128, 0, 64),
206 dcp::Time (5, 41, 0, 21, 24),
207 dcp::Time (6, 12, 15, 21, 24),
215 dcp::Colour (1, 2, 3),
216 dcp::Time (1, 2, 3, 4, 24),
217 dcp::Time (5, 6, 7, 8, 24)
222 c._id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
225 "<DCSubtitle Version=\"1.0\">"
226 "<SubtitleID>a6c58cff-3e1e-4b38-acec-a42224475ef6</SubtitleID>"
227 "<MovieTitle>Test</MovieTitle>"
228 "<ReelNumber>1</ReelNumber>"
229 "<Language>EN</Language>"
230 "<Font AspectAdjust=\"1.0\" Color=\"FFFFFFFF\" Effect=\"none\" EffectColor=\"FF000000\" Id=\"Frutiger\" Italic=\"no\" Script=\"normal\" Size=\"48\" Underlined=\"no\" Weight=\"normal\">"
231 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:04:09:229\" TimeOut=\"00:04:11:229\" FadeUpTime=\"0\" FadeDownTime=\"0\">"
232 "<Text VAlign=\"top\" VPosition=\"80\">Hello world</Text>"
235 "<Font AspectAdjust=\"1.0\" Color=\"FF800040\" Effect=\"border\" EffectColor=\"FF010203\" Italic=\"yes\" Script=\"normal\" Size=\"91\" Underlined=\"yes\" Weight=\"bold\">"
236 "<Subtitle SpotNumber=\"2\" TimeIn=\"05:41:00:219\" TimeOut=\"06:12:15:219\" FadeUpTime=\"930792\" FadeDownTime=\"4591834\">"
237 "<Text VAlign=\"bottom\" VPosition=\"40\">What's going on</Text>"
246 /** Write some subtitle content as Interop XML and check that it is right.
247 * This test includes some horizontal alignment.
249 BOOST_AUTO_TEST_CASE (write_interop_subtitle_test2)
251 dcp::InteropSubtitleAsset c;
252 c.set_reel_number ("1");
253 c.set_language ("EN");
254 c.set_movie_title ("Test");
257 shared_ptr<dcp::Subtitle> (
258 new dcp::SubtitleString (
263 dcp::Colour (255, 255, 255),
266 dcp::Time (0, 4, 9, 22, 24),
267 dcp::Time (0, 4, 11, 22, 24),
275 dcp::Colour (0, 0, 0),
276 dcp::Time (0, 0, 0, 0, 24),
277 dcp::Time (0, 0, 0, 0, 24)
283 shared_ptr<dcp::Subtitle> (
284 new dcp::SubtitleString (
285 boost::optional<string> (),
289 dcp::Colour (128, 0, 64),
292 dcp::Time (5, 41, 0, 21, 24),
293 dcp::Time (6, 12, 15, 21, 24),
301 dcp::Colour (1, 2, 3),
302 dcp::Time (1, 2, 3, 4, 24),
303 dcp::Time (5, 6, 7, 8, 24)
308 c._id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
311 "<DCSubtitle Version=\"1.0\">"
312 "<SubtitleID>a6c58cff-3e1e-4b38-acec-a42224475ef6</SubtitleID>"
313 "<MovieTitle>Test</MovieTitle>"
314 "<ReelNumber>1</ReelNumber>"
315 "<Language>EN</Language>"
316 "<Font AspectAdjust=\"1.0\" Color=\"FFFFFFFF\" Effect=\"none\" EffectColor=\"FF000000\" Id=\"Frutiger\" Italic=\"no\" Script=\"normal\" Size=\"48\" Underlined=\"no\" Weight=\"normal\">"
317 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:04:09:229\" TimeOut=\"00:04:11:229\" FadeUpTime=\"0\" FadeDownTime=\"0\">"
318 "<Text HPosition=\"-20\" VAlign=\"top\" VPosition=\"80\">Hello world</Text>"
321 "<Font AspectAdjust=\"1.0\" Color=\"FF800040\" Effect=\"border\" EffectColor=\"FF010203\" Italic=\"yes\" Script=\"normal\" Size=\"91\" Underlined=\"yes\" Weight=\"bold\">"
322 "<Subtitle SpotNumber=\"2\" TimeIn=\"05:41:00:219\" TimeOut=\"06:12:15:219\" FadeUpTime=\"930792\" FadeDownTime=\"4591834\">"
323 "<Text HPosition=\"-20\" VAlign=\"bottom\" VPosition=\"40\">What's going on</Text>"
332 /* Write some subtitle content as Interop XML using bitmaps and check that it is right */
333 BOOST_AUTO_TEST_CASE (write_interop_subtitle_test3)
337 auto c = make_shared<dcp::InteropSubtitleAsset>();
338 c->set_reel_number ("1");
339 c->set_language ("EN");
340 c->set_movie_title ("Test");
343 make_shared<dcp::SubtitleImage>(
344 dcp::ArrayData ("test/data/sub.png"),
345 dcp::Time (0, 4, 9, 22, 24),
346 dcp::Time (0, 4, 11, 22, 24),
351 dcp::Time (0, 0, 0, 0, 24),
352 dcp::Time (0, 0, 0, 0, 24)
356 c->_id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
357 boost::filesystem::remove_all ("build/test/write_interop_subtitle_test3");
358 boost::filesystem::create_directories ("build/test/write_interop_subtitle_test3");
359 c->write ("build/test/write_interop_subtitle_test3/subs.xml");
361 auto reel = make_shared<dcp::Reel>();
362 reel->add(make_shared<dcp::ReelInteropSubtitleAsset>(c, dcp::Fraction(24, 1), 6046, 0));
364 string const issue_date = "2018-09-02T04:45:18+00:00";
365 string const issuer = "libdcp";
366 string const creator = "libdcp";
367 string const annotation_text = "Created by libdcp";
369 auto cpl = make_shared<dcp::CPL>("My film", dcp::ContentKind::FEATURE, dcp::Standard::INTEROP);
371 cpl->set_issuer (issuer);
372 cpl->set_creator (creator);
373 cpl->set_issue_date (issue_date);
374 cpl->set_annotation_text (annotation_text);
375 auto cv = cpl->content_version();
377 cv->label_text = "foo";
378 cpl->set_content_version (*cv);
380 dcp::DCP dcp ("build/test/write_interop_subtitle_test3");
382 dcp.write_xml (issuer, creator, issue_date, annotation_text);
385 dcp::file_to_string("test/ref/write_interop_subtitle_test3/subs.xml"),
386 dcp::file_to_string("build/test/write_interop_subtitle_test3/subs.xml"),
389 check_file ("build/test/write_interop_subtitle_test3/d36f4bb3-c4fa-4a95-9915-6fec3110cd71.png", "test/data/sub.png");
392 dcp::file_to_string("test/ref/write_interop_subtitle_test3/ASSETMAP"),
393 dcp::file_to_string("build/test/write_interop_subtitle_test3/ASSETMAP"),
398 dcp::file_to_string("test/ref/write_interop_subtitle_test3/pkl.xml"),
399 dcp::file_to_string("build/test/write_interop_subtitle_test3/pkl_6a9e31a6-50a4-4ecb-8683-fa667848470a.xml"),
404 /* Write some subtitle content as SMPTE XML and check that it is right */
405 BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test)
407 dcp::SMPTESubtitleAsset c;
408 c.set_reel_number (1);
409 c.set_language (dcp::LanguageTag("en"));
410 c.set_content_title_text ("Test");
411 c.set_issue_date (dcp::LocalTime ("2016-04-01T03:52:00+00:00"));
414 make_shared<dcp::SubtitleString> (
419 dcp::Colour (255, 255, 255),
422 dcp::Time (0, 4, 9, 22, 24),
423 dcp::Time (0, 4, 11, 22, 24),
431 dcp::Colour (0, 0, 0),
432 dcp::Time (0, 0, 0, 0, 24),
433 dcp::Time (0, 0, 0, 0, 24)
438 make_shared<dcp::SubtitleString>(
439 boost::optional<string> (),
443 dcp::Colour (128, 0, 64),
446 dcp::Time (5, 41, 0, 21, 24),
447 dcp::Time (6, 12, 15, 21, 24),
455 dcp::Colour (1, 2, 3),
456 dcp::Time (1, 2, 3, 4, 24),
457 dcp::Time (5, 6, 7, 8, 24)
461 c._xml_id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
464 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
465 "<dcst:SubtitleReel xmlns:dcst=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
466 "<dcst:Id>urn:uuid:a6c58cff-3e1e-4b38-acec-a42224475ef6</dcst:Id>"
467 "<dcst:ContentTitleText>Test</dcst:ContentTitleText>"
468 "<dcst:IssueDate>2016-04-01T03:52:00.000+00:00</dcst:IssueDate>"
469 "<dcst:ReelNumber>1</dcst:ReelNumber>"
470 "<dcst:Language>en</dcst:Language>"
471 "<dcst:EditRate>24 1</dcst:EditRate>"
472 "<dcst:TimeCodeRate>24</dcst:TimeCodeRate>"
473 "<dcst:SubtitleList>"
474 "<dcst:Font AspectAdjust=\"1.0\" Color=\"FFFFFFFF\" Effect=\"none\" EffectColor=\"FF000000\" ID=\"Frutiger\" Italic=\"no\" Script=\"normal\" Size=\"48\" Underline=\"no\" Weight=\"normal\">"
475 "<dcst:Subtitle SpotNumber=\"1\" TimeIn=\"00:04:09:22\" TimeOut=\"00:04:11:22\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
476 "<dcst:Text Valign=\"top\" Vposition=\"80\">Hello world</dcst:Text>"
479 "<dcst:Font AspectAdjust=\"1.0\" Color=\"FF800040\" Effect=\"border\" EffectColor=\"FF010203\" Italic=\"yes\" Script=\"normal\" Size=\"91\" Underline=\"yes\" Weight=\"bold\">"
480 "<dcst:Subtitle SpotNumber=\"2\" TimeIn=\"05:41:00:21\" TimeOut=\"06:12:15:21\" FadeUpTime=\"01:02:03:04\" FadeDownTime=\"05:06:07:08\">"
481 "<dcst:Text Valign=\"bottom\" Vposition=\"40\" Direction=\"rtl\">What's going on</dcst:Text>"
484 "</dcst:SubtitleList>"
485 "</dcst:SubtitleReel>",
491 /* Write some subtitle content as SMPTE XML and check that it is right.
492 This includes in-line font changes.
494 BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test2)
496 dcp::SMPTESubtitleAsset c;
497 c.set_reel_number (1);
498 c.set_language (dcp::LanguageTag("en"));
499 c.set_content_title_text ("Test");
500 c.set_issue_date (dcp::LocalTime ("2016-04-01T03:52:00+00:00"));
503 make_shared<dcp::SubtitleString>(
508 dcp::Colour (255, 255, 255),
511 dcp::Time (0, 0, 1, 0, 24),
512 dcp::Time (0, 0, 9, 0, 24),
520 dcp::Colour (0, 0, 0),
521 dcp::Time (0, 0, 0, 0, 24),
522 dcp::Time (0, 0, 0, 0, 24)
527 make_shared<dcp::SubtitleString>(
532 dcp::Colour (255, 255, 255),
535 dcp::Time (0, 0, 1, 0, 24),
536 dcp::Time (0, 0, 9, 0, 24),
544 dcp::Colour (0, 0, 0),
545 dcp::Time (0, 0, 0, 0, 24),
546 dcp::Time (0, 0, 0, 0, 24)
551 make_shared<dcp::SubtitleString>(
556 dcp::Colour (255, 255, 255),
559 dcp::Time (0, 0, 1, 0, 24),
560 dcp::Time (0, 0, 9, 0, 24),
568 dcp::Colour (0, 0, 0),
569 dcp::Time (0, 0, 0, 0, 24),
570 dcp::Time (0, 0, 0, 0, 24)
575 make_shared<dcp::SubtitleString>(
580 dcp::Colour (255, 255, 255),
583 dcp::Time (0, 0, 1, 0, 24),
584 dcp::Time (0, 0, 9, 0, 24),
592 dcp::Colour (0, 0, 0),
593 dcp::Time (0, 0, 0, 0, 24),
594 dcp::Time (0, 0, 0, 0, 24)
599 make_shared<dcp::SubtitleString>(
604 dcp::Colour (255, 255, 255),
607 dcp::Time (0, 0, 1, 0, 24),
608 dcp::Time (0, 0, 9, 0, 24),
616 dcp::Colour (0, 0, 0),
617 dcp::Time (0, 0, 0, 0, 24),
618 dcp::Time (0, 0, 0, 0, 24)
623 make_shared<dcp::SubtitleString>(
628 dcp::Colour (255, 255, 255),
631 dcp::Time (0, 0, 1, 0, 24),
632 dcp::Time (0, 0, 9, 0, 24),
640 dcp::Colour (0, 0, 0),
641 dcp::Time (0, 0, 0, 0, 24),
642 dcp::Time (0, 0, 0, 0, 24)
646 c._xml_id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
650 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
651 "<dcst:SubtitleReel xmlns:dcst=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
652 "<dcst:Id>urn:uuid:a6c58cff-3e1e-4b38-acec-a42224475ef6</dcst:Id>"
653 "<dcst:ContentTitleText>Test</dcst:ContentTitleText>"
654 "<dcst:IssueDate>2016-04-01T03:52:00.000+00:00</dcst:IssueDate>"
655 "<dcst:ReelNumber>1</dcst:ReelNumber>"
656 "<dcst:Language>en</dcst:Language>"
657 "<dcst:EditRate>24 1</dcst:EditRate>"
658 "<dcst:TimeCodeRate>24</dcst:TimeCodeRate>"
659 "<dcst:SubtitleList>"
660 "<dcst:Font AspectAdjust=\"1.0\" Color=\"FFFFFFFF\" Effect=\"none\" EffectColor=\"FF000000\" ID=\"Arial\" Script=\"normal\" Size=\"48\" Underline=\"no\" Weight=\"normal\">"
661 "<dcst:Subtitle SpotNumber=\"1\" TimeIn=\"00:00:01:00\" TimeOut=\"00:00:09:00\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
662 "<dcst:Text Valign=\"top\" Vposition=\"80\">"
663 "<dcst:Font Italic=\"no\">Testing is </dcst:Font>"
664 "<dcst:Font Italic=\"yes\">really</dcst:Font>"
665 "<dcst:Font Italic=\"no\"> fun</dcst:Font>"
667 "<dcst:Text Valign=\"top\" Vposition=\"90\">"
668 "<dcst:Font Italic=\"no\">This is the </dcst:Font>"
669 "<dcst:Font Italic=\"yes\">second</dcst:Font>"
670 "<dcst:Font Italic=\"no\"> line</dcst:Font>"
674 "</dcst:SubtitleList>"
675 "</dcst:SubtitleReel>",
680 /* Write some subtitle content as SMPTE using bitmaps and check that it is right */
681 BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test3)
683 dcp::SMPTESubtitleAsset c;
684 c.set_reel_number (1);
685 c.set_language (dcp::LanguageTag("en"));
686 c.set_content_title_text ("Test");
689 make_shared<dcp::SubtitleImage>(
690 dcp::ArrayData ("test/data/sub.png"),
691 dcp::Time (0, 4, 9, 22, 24),
692 dcp::Time (0, 4, 11, 22, 24),
697 dcp::Time (0, 0, 0, 0, 24),
698 dcp::Time (0, 0, 0, 0, 24)
702 c._id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
704 boost::filesystem::create_directories ("build/test/write_smpte_subtitle_test3");
705 c.write ("build/test/write_smpte_subtitle_test3/subs.mxf");
707 /* XXX: check this result when we can read them back in again */