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 /* Write some subtitle content as SMPTE XML and check that it is right */
165 BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test)
167 dcp::SMPTESubtitleAsset c;
168 c.set_reel_number (1);
169 c.set_language (dcp::LanguageTag("en"));
170 c.set_content_title_text ("Test");
171 c.set_issue_date (dcp::LocalTime ("2016-04-01T03:52:00+00:00"));
174 make_shared<dcp::SubtitleString> (
179 dcp::Colour (255, 255, 255),
182 dcp::Time (0, 4, 9, 22, 24),
183 dcp::Time (0, 4, 11, 22, 24),
191 dcp::Colour (0, 0, 0),
192 dcp::Time (0, 0, 0, 0, 24),
193 dcp::Time (0, 0, 0, 0, 24)
198 make_shared<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)
221 c._xml_id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
224 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
225 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
226 "<Id>urn:uuid:a6c58cff-3e1e-4b38-acec-a42224475ef6</Id>"
227 "<ContentTitleText>Test</ContentTitleText>"
228 "<IssueDate>2016-04-01T03:52:00.000+00:00</IssueDate>"
229 "<ReelNumber>1</ReelNumber>"
230 "<Language>en</Language>"
231 "<EditRate>24 1</EditRate>"
232 "<TimeCodeRate>24</TimeCodeRate>"
234 "<Font AspectAdjust=\"1.0\" Color=\"FFFFFFFF\" Effect=\"none\" EffectColor=\"FF000000\" ID=\"Frutiger\" Italic=\"no\" Script=\"normal\" Size=\"48\" Underline=\"no\" Weight=\"normal\">"
235 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:04:09:22\" TimeOut=\"00:04:11:22\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
236 "<Text Valign=\"top\" Vposition=\"80\">Hello world</Text>"
239 "<Font AspectAdjust=\"1.0\" Color=\"FF800040\" Effect=\"border\" EffectColor=\"FF010203\" Italic=\"yes\" Script=\"normal\" Size=\"91\" Underline=\"yes\" Weight=\"bold\">"
240 "<Subtitle SpotNumber=\"2\" TimeIn=\"05:41:00:21\" TimeOut=\"06:12:15:21\" FadeUpTime=\"01:02:03:04\" FadeDownTime=\"05:06:07:08\">"
241 "<Text Valign=\"bottom\" Vposition=\"40\" Direction=\"rtl\">What's going on</Text>"
251 /* Write some subtitle content as SMPTE XML and check that it is right.
252 This includes in-line font changes.
254 BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test2)
256 dcp::SMPTESubtitleAsset c;
257 c.set_reel_number (1);
258 c.set_language (dcp::LanguageTag("en"));
259 c.set_content_title_text ("Test");
260 c.set_issue_date (dcp::LocalTime ("2016-04-01T03:52:00+00:00"));
263 make_shared<dcp::SubtitleString>(
268 dcp::Colour (255, 255, 255),
271 dcp::Time (0, 0, 1, 0, 24),
272 dcp::Time (0, 0, 9, 0, 24),
280 dcp::Colour (0, 0, 0),
281 dcp::Time (0, 0, 0, 0, 24),
282 dcp::Time (0, 0, 0, 0, 24)
287 make_shared<dcp::SubtitleString>(
292 dcp::Colour (255, 255, 255),
295 dcp::Time (0, 0, 1, 0, 24),
296 dcp::Time (0, 0, 9, 0, 24),
304 dcp::Colour (0, 0, 0),
305 dcp::Time (0, 0, 0, 0, 24),
306 dcp::Time (0, 0, 0, 0, 24)
311 make_shared<dcp::SubtitleString>(
316 dcp::Colour (255, 255, 255),
319 dcp::Time (0, 0, 1, 0, 24),
320 dcp::Time (0, 0, 9, 0, 24),
328 dcp::Colour (0, 0, 0),
329 dcp::Time (0, 0, 0, 0, 24),
330 dcp::Time (0, 0, 0, 0, 24)
335 make_shared<dcp::SubtitleString>(
340 dcp::Colour (255, 255, 255),
343 dcp::Time (0, 0, 1, 0, 24),
344 dcp::Time (0, 0, 9, 0, 24),
352 dcp::Colour (0, 0, 0),
353 dcp::Time (0, 0, 0, 0, 24),
354 dcp::Time (0, 0, 0, 0, 24)
359 make_shared<dcp::SubtitleString>(
364 dcp::Colour (255, 255, 255),
367 dcp::Time (0, 0, 1, 0, 24),
368 dcp::Time (0, 0, 9, 0, 24),
376 dcp::Colour (0, 0, 0),
377 dcp::Time (0, 0, 0, 0, 24),
378 dcp::Time (0, 0, 0, 0, 24)
383 make_shared<dcp::SubtitleString>(
388 dcp::Colour (255, 255, 255),
391 dcp::Time (0, 0, 1, 0, 24),
392 dcp::Time (0, 0, 9, 0, 24),
400 dcp::Colour (0, 0, 0),
401 dcp::Time (0, 0, 0, 0, 24),
402 dcp::Time (0, 0, 0, 0, 24)
406 c._xml_id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
410 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
411 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
412 "<Id>urn:uuid:a6c58cff-3e1e-4b38-acec-a42224475ef6</Id>"
413 "<ContentTitleText>Test</ContentTitleText>"
414 "<IssueDate>2016-04-01T03:52:00.000+00:00</IssueDate>"
415 "<ReelNumber>1</ReelNumber>"
416 "<Language>en</Language>"
417 "<EditRate>24 1</EditRate>"
418 "<TimeCodeRate>24</TimeCodeRate>"
420 "<Font AspectAdjust=\"1.0\" Color=\"FFFFFFFF\" Effect=\"none\" EffectColor=\"FF000000\" ID=\"Arial\" Script=\"normal\" Size=\"48\" Underline=\"no\" Weight=\"normal\">"
421 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:01:00\" TimeOut=\"00:00:09:00\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
422 "<Text Valign=\"top\" Vposition=\"80\">"
423 "<Font Italic=\"no\">Testing is </Font>"
424 "<Font Italic=\"yes\">really</Font>"
425 "<Font Italic=\"no\"> fun</Font>"
427 "<Text Valign=\"top\" Vposition=\"90\">"
428 "<Font Italic=\"no\">This is the </Font>"
429 "<Font Italic=\"yes\">second</Font>"
430 "<Font Italic=\"no\"> line</Font>"
440 /* Write some subtitle content as SMPTE using bitmaps and check that it is right */
441 BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test3)
443 dcp::SMPTESubtitleAsset c;
444 c.set_reel_number (1);
445 c.set_language (dcp::LanguageTag("en"));
446 c.set_content_title_text ("Test");
447 c.set_start_time (dcp::Time());
449 boost::filesystem::path const sub_image = "test/data/sub.png";
452 make_shared<dcp::SubtitleImage>(
453 dcp::ArrayData(sub_image),
454 dcp::Time (0, 4, 9, 22, 24),
455 dcp::Time (0, 4, 11, 22, 24),
460 dcp::Time (0, 0, 0, 0, 24),
461 dcp::Time (0, 0, 0, 0, 24)
465 c._id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
467 boost::filesystem::path path = "build/test/write_smpte_subtitle_test3";
468 boost::filesystem::create_directories (path);
469 c.write (path / "subs.mxf");
471 dcp::SMPTESubtitleAsset read_back (path / "subs.mxf");
472 auto subs = read_back.subtitles ();
473 BOOST_REQUIRE_EQUAL (subs.size(), 1U);
474 auto image = dynamic_pointer_cast<const dcp::SubtitleImage>(subs[0]);
475 BOOST_REQUIRE (image);
477 BOOST_CHECK (image->png_image() == dcp::ArrayData(sub_image));
478 BOOST_CHECK (image->in() == dcp::Time(0, 4, 9, 22, 24));
479 BOOST_CHECK (image->out() == dcp::Time(0, 4, 11, 22, 24));
480 BOOST_CHECK_CLOSE (image->h_position(), 0.0, 1);
481 BOOST_CHECK (image->h_align() == dcp::HAlign::CENTER);
482 BOOST_CHECK_CLOSE (image->v_position(), 0.8, 1);
483 BOOST_CHECK (image->v_align() == dcp::VAlign::TOP);
484 BOOST_CHECK (image->fade_up_time() == dcp::Time(0, 0, 0, 0, 24));
485 BOOST_CHECK (image->fade_down_time() == dcp::Time(0, 0, 0, 0, 24));