2 Copyright (C) 2018-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.
35 #include "smpte_load_font_node.h"
36 #include "smpte_subtitle_asset.h"
37 #include "stream_operators.h"
38 #include "subtitle_image.h"
41 #include <boost/optional/optional_io.hpp>
42 #include <boost/test/unit_test.hpp>
45 using std::make_shared;
47 using std::shared_ptr;
48 using std::dynamic_pointer_cast;
50 using boost::optional;
53 BOOST_AUTO_TEST_CASE (smpte_subtitle_id_test)
55 dcp::SMPTESubtitleAsset subs;
57 make_shared<dcp::SubtitleString>(
63 dcp::Time(0, 1, 2, 3, 24),
64 dcp::Time(0, 2, 2, 3, 24),
73 dcp::Time(0, 0, 0, 0, 24),
74 dcp::Time(0, 0, 0, 0, 24)
77 subs.write("build/test/smpte_subtitle_id_test.mxf");
79 dcp::SMPTESubtitleAsset check("build/test/smpte_subtitle_id_test.mxf");
80 BOOST_CHECK(check.id() != check.xml_id());
84 /** Check reading of a SMPTE subtitle file */
85 BOOST_AUTO_TEST_CASE (read_smpte_subtitle_test)
87 dcp::SMPTESubtitleAsset sc (
90 "JourneyToJah_TLR-1_F_EN-DE-FR_CH_51_2K_LOK_20140225_DGL_SMPTE_OV" /
91 "8b48f6ae-c74b-4b80-b994-a8236bbbad74_sub.mxf"
94 BOOST_CHECK_EQUAL (sc.id(), "8b48f6ae-c74b-4b80-b994-a8236bbbad74");
95 BOOST_CHECK_EQUAL (sc.content_title_text(), "Journey to Jah");
96 BOOST_REQUIRE (sc.annotation_text());
97 BOOST_CHECK_EQUAL (sc.annotation_text().get(), "Journey to Jah");
98 BOOST_CHECK_EQUAL (sc.issue_date(), dcp::LocalTime ("2014-02-25T11:22:48.000-00:00"));
99 BOOST_REQUIRE (sc.reel_number());
100 BOOST_CHECK_EQUAL (sc.reel_number().get(), 1);
101 BOOST_REQUIRE (sc.language ());
102 BOOST_CHECK_EQUAL (sc.language().get (), "de");
103 BOOST_CHECK_EQUAL (sc.edit_rate(), dcp::Fraction (25, 1));
104 BOOST_CHECK_EQUAL (sc.time_code_rate(), 25);
105 BOOST_CHECK_EQUAL (sc.start_time(), dcp::Time (0, 0, 0, 0, 25));
106 auto lfn = sc.load_font_nodes ();
107 BOOST_REQUIRE_EQUAL (lfn.size(), 1);
108 shared_ptr<dcp::SMPTELoadFontNode> smpte_lfn = dynamic_pointer_cast<dcp::SMPTELoadFontNode> (lfn.front ());
109 BOOST_REQUIRE (smpte_lfn);
110 BOOST_CHECK_EQUAL (smpte_lfn->id, "theFontId");
111 BOOST_CHECK_EQUAL (smpte_lfn->urn, "9118bbce-4105-4a05-b37c-a5a6f75e1fea");
112 BOOST_REQUIRE_EQUAL (sc.subtitles().size(), 63);
113 BOOST_REQUIRE (dynamic_pointer_cast<const dcp::SubtitleString>(sc.subtitles().front()));
114 BOOST_CHECK_EQUAL (dynamic_pointer_cast<const dcp::SubtitleString>(sc.subtitles().front())->text(), "Noch mal.");
115 BOOST_CHECK_EQUAL (sc.subtitles().front()->in(), dcp::Time (0, 0, 25, 12, 25));
116 BOOST_CHECK_EQUAL (sc.subtitles().front()->out(), dcp::Time (0, 0, 26, 4, 25));
117 BOOST_REQUIRE (dynamic_pointer_cast<const dcp::SubtitleString>(sc.subtitles().back()));
118 BOOST_CHECK_EQUAL (dynamic_pointer_cast<const dcp::SubtitleString>(sc.subtitles().back())->text(), "Prochainement");
119 BOOST_CHECK_EQUAL (sc.subtitles().back()->in(), dcp::Time (0, 1, 57, 17, 25));
120 BOOST_CHECK_EQUAL (sc.subtitles().back()->out(), dcp::Time (0, 1, 58, 12, 25));
124 /** And another one featuring <Font> within <Text> */
125 BOOST_AUTO_TEST_CASE (read_smpte_subtitle_test2)
127 dcp::SMPTESubtitleAsset sc (private_test / "olsson.xml");
129 auto subs = sc.subtitles();
130 BOOST_REQUIRE_EQUAL (subs.size(), 6);
132 auto is = dynamic_pointer_cast<const dcp::SubtitleString>(subs[i]);
134 BOOST_CHECK_EQUAL (is->text(), "Testing is ");
135 BOOST_CHECK (!is->italic());
137 is = dynamic_pointer_cast<const dcp::SubtitleString>(subs[i]);
139 BOOST_CHECK_EQUAL (is->text(), "really");
140 BOOST_CHECK (is->italic());
142 is = dynamic_pointer_cast<const dcp::SubtitleString>(subs[i]);
144 BOOST_CHECK_EQUAL (is->text(), " fun!");
145 BOOST_CHECK (!is->italic());
147 is = dynamic_pointer_cast<const dcp::SubtitleString>(subs[i]);
149 BOOST_CHECK_EQUAL (is->text(), "This is the ");
150 BOOST_CHECK (!is->italic());
152 is = dynamic_pointer_cast<const dcp::SubtitleString>(subs[i]);
154 BOOST_CHECK_EQUAL (is->text(), "second");
155 BOOST_CHECK (is->italic());
157 is = dynamic_pointer_cast<const dcp::SubtitleString>(subs[i]);
159 BOOST_CHECK_EQUAL (is->text(), " line!");
160 BOOST_CHECK (!is->italic());
164 /** And another one featuring image subtitles */
165 BOOST_AUTO_TEST_CASE (read_smpte_subtitle_test3)
167 dcp::SMPTESubtitleAsset subs ("test/data/subs.mxf");
169 BOOST_REQUIRE_EQUAL (subs.subtitles().size(), 1);
170 auto si = dynamic_pointer_cast<const dcp::SubtitleImage>(subs.subtitles()[0]);
172 BOOST_CHECK (si->png_image() == dcp::ArrayData("test/data/sub.png"));
176 /* Write some subtitle content as SMPTE XML and check that it is right */
177 BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test)
179 dcp::SMPTESubtitleAsset c;
180 c.set_reel_number (1);
181 c.set_language (dcp::LanguageTag("en"));
182 c.set_content_title_text ("Test");
183 c.set_issue_date (dcp::LocalTime ("2016-04-01T03:52:00+00:00"));
186 make_shared<dcp::SubtitleString> (
191 dcp::Colour (255, 255, 255),
194 dcp::Time (0, 4, 9, 22, 24),
195 dcp::Time (0, 4, 11, 22, 24),
203 dcp::Colour (0, 0, 0),
204 dcp::Time (0, 0, 0, 0, 24),
205 dcp::Time (0, 0, 0, 0, 24)
210 make_shared<dcp::SubtitleString>(
211 boost::optional<string> (),
215 dcp::Colour (128, 0, 64),
218 dcp::Time (5, 41, 0, 21, 24),
219 dcp::Time (6, 12, 15, 21, 24),
227 dcp::Colour (1, 2, 3),
228 dcp::Time (1, 2, 3, 4, 24),
229 dcp::Time (5, 6, 7, 8, 24)
233 c._xml_id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
236 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
237 "<dcst:SubtitleReel xmlns:dcst=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
238 "<dcst:Id>urn:uuid:a6c58cff-3e1e-4b38-acec-a42224475ef6</dcst:Id>"
239 "<dcst:ContentTitleText>Test</dcst:ContentTitleText>"
240 "<dcst:IssueDate>2016-04-01T03:52:00.000+00:00</dcst:IssueDate>"
241 "<dcst:ReelNumber>1</dcst:ReelNumber>"
242 "<dcst:Language>en</dcst:Language>"
243 "<dcst:EditRate>24 1</dcst:EditRate>"
244 "<dcst:TimeCodeRate>24</dcst:TimeCodeRate>"
245 "<dcst:SubtitleList>"
246 "<dcst:Font AspectAdjust=\"1.0\" Color=\"FFFFFFFF\" Effect=\"none\" EffectColor=\"FF000000\" ID=\"Frutiger\" Italic=\"no\" Script=\"normal\" Size=\"48\" Underline=\"no\" Weight=\"normal\">"
247 "<dcst:Subtitle SpotNumber=\"1\" TimeIn=\"00:04:09:22\" TimeOut=\"00:04:11:22\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
248 "<dcst:Text Valign=\"top\" Vposition=\"80\">Hello world</dcst:Text>"
251 "<dcst:Font AspectAdjust=\"1.0\" Color=\"FF800040\" Effect=\"border\" EffectColor=\"FF010203\" Italic=\"yes\" Script=\"normal\" Size=\"91\" Underline=\"yes\" Weight=\"bold\">"
252 "<dcst:Subtitle SpotNumber=\"2\" TimeIn=\"05:41:00:21\" TimeOut=\"06:12:15:21\" FadeUpTime=\"01:02:03:04\" FadeDownTime=\"05:06:07:08\">"
253 "<dcst:Text Valign=\"bottom\" Vposition=\"40\" Direction=\"rtl\">What's going on</dcst:Text>"
256 "</dcst:SubtitleList>"
257 "</dcst:SubtitleReel>",
263 /* Write some subtitle content as SMPTE XML and check that it is right.
264 This includes in-line font changes.
266 BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test2)
268 dcp::SMPTESubtitleAsset c;
269 c.set_reel_number (1);
270 c.set_language (dcp::LanguageTag("en"));
271 c.set_content_title_text ("Test");
272 c.set_issue_date (dcp::LocalTime ("2016-04-01T03:52:00+00:00"));
275 make_shared<dcp::SubtitleString>(
280 dcp::Colour (255, 255, 255),
283 dcp::Time (0, 0, 1, 0, 24),
284 dcp::Time (0, 0, 9, 0, 24),
292 dcp::Colour (0, 0, 0),
293 dcp::Time (0, 0, 0, 0, 24),
294 dcp::Time (0, 0, 0, 0, 24)
299 make_shared<dcp::SubtitleString>(
304 dcp::Colour (255, 255, 255),
307 dcp::Time (0, 0, 1, 0, 24),
308 dcp::Time (0, 0, 9, 0, 24),
316 dcp::Colour (0, 0, 0),
317 dcp::Time (0, 0, 0, 0, 24),
318 dcp::Time (0, 0, 0, 0, 24)
323 make_shared<dcp::SubtitleString>(
328 dcp::Colour (255, 255, 255),
331 dcp::Time (0, 0, 1, 0, 24),
332 dcp::Time (0, 0, 9, 0, 24),
340 dcp::Colour (0, 0, 0),
341 dcp::Time (0, 0, 0, 0, 24),
342 dcp::Time (0, 0, 0, 0, 24)
347 make_shared<dcp::SubtitleString>(
352 dcp::Colour (255, 255, 255),
355 dcp::Time (0, 0, 1, 0, 24),
356 dcp::Time (0, 0, 9, 0, 24),
364 dcp::Colour (0, 0, 0),
365 dcp::Time (0, 0, 0, 0, 24),
366 dcp::Time (0, 0, 0, 0, 24)
371 make_shared<dcp::SubtitleString>(
376 dcp::Colour (255, 255, 255),
379 dcp::Time (0, 0, 1, 0, 24),
380 dcp::Time (0, 0, 9, 0, 24),
388 dcp::Colour (0, 0, 0),
389 dcp::Time (0, 0, 0, 0, 24),
390 dcp::Time (0, 0, 0, 0, 24)
395 make_shared<dcp::SubtitleString>(
400 dcp::Colour (255, 255, 255),
403 dcp::Time (0, 0, 1, 0, 24),
404 dcp::Time (0, 0, 9, 0, 24),
412 dcp::Colour (0, 0, 0),
413 dcp::Time (0, 0, 0, 0, 24),
414 dcp::Time (0, 0, 0, 0, 24)
418 c._xml_id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
422 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
423 "<dcst:SubtitleReel xmlns:dcst=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
424 "<dcst:Id>urn:uuid:a6c58cff-3e1e-4b38-acec-a42224475ef6</dcst:Id>"
425 "<dcst:ContentTitleText>Test</dcst:ContentTitleText>"
426 "<dcst:IssueDate>2016-04-01T03:52:00.000+00:00</dcst:IssueDate>"
427 "<dcst:ReelNumber>1</dcst:ReelNumber>"
428 "<dcst:Language>en</dcst:Language>"
429 "<dcst:EditRate>24 1</dcst:EditRate>"
430 "<dcst:TimeCodeRate>24</dcst:TimeCodeRate>"
431 "<dcst:SubtitleList>"
432 "<dcst:Font AspectAdjust=\"1.0\" Color=\"FFFFFFFF\" Effect=\"none\" EffectColor=\"FF000000\" ID=\"Arial\" Script=\"normal\" Size=\"48\" Underline=\"no\" Weight=\"normal\">"
433 "<dcst:Subtitle SpotNumber=\"1\" TimeIn=\"00:00:01:00\" TimeOut=\"00:00:09:00\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
434 "<dcst:Text Valign=\"top\" Vposition=\"80\">"
435 "<dcst:Font Italic=\"no\">Testing is </dcst:Font>"
436 "<dcst:Font Italic=\"yes\">really</dcst:Font>"
437 "<dcst:Font Italic=\"no\"> fun</dcst:Font>"
439 "<dcst:Text Valign=\"top\" Vposition=\"90\">"
440 "<dcst:Font Italic=\"no\">This is the </dcst:Font>"
441 "<dcst:Font Italic=\"yes\">second</dcst:Font>"
442 "<dcst:Font Italic=\"no\"> line</dcst:Font>"
446 "</dcst:SubtitleList>"
447 "</dcst:SubtitleReel>",
452 /* Write some subtitle content as SMPTE using bitmaps and check that it is right */
453 BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test3)
455 dcp::SMPTESubtitleAsset c;
456 c.set_reel_number (1);
457 c.set_language (dcp::LanguageTag("en"));
458 c.set_content_title_text ("Test");
461 make_shared<dcp::SubtitleImage>(
462 dcp::ArrayData ("test/data/sub.png"),
463 dcp::Time (0, 4, 9, 22, 24),
464 dcp::Time (0, 4, 11, 22, 24),
469 dcp::Time (0, 0, 0, 0, 24),
470 dcp::Time (0, 0, 0, 0, 24)
474 c._id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
476 boost::filesystem::create_directories ("build/test/write_smpte_subtitle_test3");
477 c.write ("build/test/write_smpte_subtitle_test3/subs.mxf");
479 /* XXX: check this result when we can read them back in again */