In-line run of subs_in_out so that it gets the environment more easily.
[libdcp.git] / test / smpte_subtitle_test.cc
1 /*
2     Copyright (C) 2018-2021 Carl Hetherington <cth@carlh.net>
3
4     This file is part of libdcp.
5
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.
10
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.
15
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/>.
18
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
23     including the two.
24
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.
32 */
33
34
35 #include "smpte_load_font_node.h"
36 #include "smpte_subtitle_asset.h"
37 #include "stream_operators.h"
38 #include "subtitle_image.h"
39 #include "test.h"
40 #include "types.h"
41 #include <boost/optional/optional_io.hpp>
42 #include <boost/test/unit_test.hpp>
43
44
45 using std::dynamic_pointer_cast;
46 using std::shared_ptr;
47 using std::string;
48 using std::vector;
49 using boost::optional;
50
51
52 BOOST_AUTO_TEST_CASE (smpte_subtitle_id_test)
53 {
54         dcp::SMPTESubtitleAsset subs;
55         subs.add(
56                 std::make_shared<dcp::SubtitleString>(
57                         optional<string>(),
58                         false, false, false,
59                         dcp::Colour(),
60                         64,
61                         1,
62                         dcp::Time(0, 1, 2, 3, 24),
63                         dcp::Time(0, 2, 2, 3, 24),
64                         0.5,
65                         dcp::HAlign::CENTER,
66                         0.5,
67                         dcp::VAlign::CENTER,
68                         0,
69                         dcp::Direction::LTR,
70                         "Hello",
71                         dcp::Effect::NONE,
72                         dcp::Colour(),
73                         dcp::Time(0, 0, 0, 0, 24),
74                         dcp::Time(0, 0, 0, 0, 24),
75                         0,
76                         std::vector<dcp::Ruby>()
77                         )
78                 );
79         subs.write("build/test/smpte_subtitle_id_test.mxf");
80
81         dcp::SMPTESubtitleAsset check("build/test/smpte_subtitle_id_test.mxf");
82         BOOST_CHECK(check.id() != check.xml_id());
83 }
84
85
86 /** Check reading of a SMPTE subtitle file */
87 BOOST_AUTO_TEST_CASE (read_smpte_subtitle_test)
88 {
89         dcp::SMPTESubtitleAsset sc (
90                 private_test /
91                 "data" /
92                 "JourneyToJah_TLR-1_F_EN-DE-FR_CH_51_2K_LOK_20140225_DGL_SMPTE_OV" /
93                 "8b48f6ae-c74b-4b80-b994-a8236bbbad74_sub.mxf"
94                 );
95
96         BOOST_CHECK_EQUAL (sc.id(), "8b48f6ae-c74b-4b80-b994-a8236bbbad74");
97         BOOST_CHECK_EQUAL (sc.content_title_text(), "Journey to Jah");
98         BOOST_REQUIRE (sc.annotation_text());
99         BOOST_CHECK_EQUAL (sc.annotation_text().get(), "Journey to Jah");
100         BOOST_CHECK_EQUAL (sc.issue_date(), dcp::LocalTime ("2014-02-25T11:22:48.000-00:00"));
101         BOOST_REQUIRE (sc.reel_number());
102         BOOST_CHECK_EQUAL (sc.reel_number().get(), 1);
103         BOOST_REQUIRE (sc.language ());
104         BOOST_CHECK_EQUAL (sc.language().get (), "de");
105         BOOST_CHECK_EQUAL (sc.edit_rate(), dcp::Fraction (25, 1));
106         BOOST_CHECK_EQUAL (sc.time_code_rate(), 25);
107         BOOST_CHECK_EQUAL (sc.start_time(), dcp::Time (0, 0, 0, 0, 25));
108         auto lfn = sc.load_font_nodes ();
109         BOOST_REQUIRE_EQUAL (lfn.size(), 1U);
110         shared_ptr<dcp::SMPTELoadFontNode> smpte_lfn = dynamic_pointer_cast<dcp::SMPTELoadFontNode> (lfn.front ());
111         BOOST_REQUIRE (smpte_lfn);
112         BOOST_CHECK_EQUAL (smpte_lfn->id, "theFontId");
113         BOOST_CHECK_EQUAL (smpte_lfn->urn, "9118bbce-4105-4a05-b37c-a5a6f75e1fea");
114         BOOST_REQUIRE_EQUAL (sc.subtitles().size(), 63U);
115         BOOST_REQUIRE (dynamic_pointer_cast<const dcp::SubtitleString>(sc.subtitles().front()));
116         BOOST_CHECK_EQUAL (dynamic_pointer_cast<const dcp::SubtitleString>(sc.subtitles().front())->text(), "Noch mal.");
117         BOOST_CHECK_EQUAL (dynamic_pointer_cast<const dcp::SubtitleString>(sc.subtitles().front())->space_before(), 0.0f);
118         BOOST_CHECK_EQUAL (sc.subtitles().front()->in(), dcp::Time (0, 0, 25, 12, 25));
119         BOOST_CHECK_EQUAL (sc.subtitles().front()->out(), dcp::Time (0, 0, 26, 4, 25));
120         BOOST_REQUIRE (dynamic_pointer_cast<const dcp::SubtitleString>(sc.subtitles().back()));
121         BOOST_CHECK_EQUAL (dynamic_pointer_cast<const dcp::SubtitleString>(sc.subtitles().back())->text(), "Prochainement");
122         BOOST_CHECK_EQUAL (dynamic_pointer_cast<const dcp::SubtitleString>(sc.subtitles().back())->space_before(), 0.0f);
123         BOOST_CHECK_EQUAL (sc.subtitles().back()->in(), dcp::Time (0, 1, 57, 17, 25));
124         BOOST_CHECK_EQUAL (sc.subtitles().back()->out(), dcp::Time (0, 1, 58, 12, 25));
125 }
126
127
128 /** And another one featuring <Font> within <Text> and some <Space> */
129 BOOST_AUTO_TEST_CASE (read_smpte_subtitle_test2)
130 {
131         dcp::SMPTESubtitleAsset sc (private_test / "olsson.xml");
132
133         auto subs = sc.subtitles();
134         BOOST_REQUIRE_EQUAL (subs.size(), 6U);
135         auto i = 0;
136         auto is = dynamic_pointer_cast<const dcp::SubtitleString>(subs[i]);
137         BOOST_REQUIRE (is);
138         BOOST_CHECK_EQUAL (is->text(), "Testing is ");
139         BOOST_CHECK (!is->italic());
140         BOOST_CHECK_CLOSE (is->space_before(), 0, 0.1);
141         ++i;
142         is = dynamic_pointer_cast<const dcp::SubtitleString>(subs[i]);
143         BOOST_REQUIRE (is);
144         BOOST_CHECK_EQUAL (is->text(), "really");
145         BOOST_CHECK (is->italic());
146         BOOST_CHECK_CLOSE (is->space_before(), 0, 0.1);
147         ++i;
148         is = dynamic_pointer_cast<const dcp::SubtitleString>(subs[i]);
149         BOOST_REQUIRE (is);
150         BOOST_CHECK_EQUAL (is->text(), " fun!");
151         BOOST_CHECK (!is->italic());
152         BOOST_CHECK_CLOSE (is->space_before(), 5, 0.1);
153         ++i;
154         is = dynamic_pointer_cast<const dcp::SubtitleString>(subs[i]);
155         BOOST_REQUIRE (is);
156         BOOST_CHECK_EQUAL (is->text(), "This is the ");
157         BOOST_CHECK (!is->italic());
158         BOOST_CHECK_CLOSE (is->space_before(), 0, 0.1);
159         ++i;
160         is = dynamic_pointer_cast<const dcp::SubtitleString>(subs[i]);
161         BOOST_REQUIRE (is);
162         BOOST_CHECK_EQUAL (is->text(), "second");
163         BOOST_CHECK (is->italic());
164         BOOST_CHECK_CLOSE (is->space_before(), 0, 0.1);
165         ++i;
166         is = dynamic_pointer_cast<const dcp::SubtitleString>(subs[i]);
167         BOOST_REQUIRE (is);
168         BOOST_CHECK_EQUAL (is->text(), " line!");
169         BOOST_CHECK (!is->italic());
170         BOOST_CHECK_CLOSE (is->space_before(), 0, 0.1);
171 }
172
173
174 /* Write some subtitle content as SMPTE XML and check that it is right */
175 BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test)
176 {
177         dcp::SMPTESubtitleAsset c;
178         c.set_reel_number (1);
179         c.set_language (dcp::LanguageTag("en"));
180         c.set_content_title_text ("Test");
181         c.set_issue_date (dcp::LocalTime ("2016-04-01T03:52:00+00:00"));
182
183         c.add (
184                 std::make_shared<dcp::SubtitleString> (
185                         string ("Frutiger"),
186                         false,
187                         false,
188                         false,
189                         dcp::Colour (255, 255, 255),
190                         48,
191                         1.0,
192                         dcp::Time (0, 4,  9, 22, 24),
193                         dcp::Time (0, 4, 11, 22, 24),
194                         0,
195                         dcp::HAlign::CENTER,
196                         0.8,
197                         dcp::VAlign::TOP,
198                         0.3,
199                         dcp::Direction::LTR,
200                         "Hello world",
201                         dcp::Effect::NONE,
202                         dcp::Colour (0, 0, 0),
203                         dcp::Time (0, 0, 0, 0, 24),
204                         dcp::Time (0, 0, 0, 0, 24),
205                         0,
206                         std::vector<dcp::Ruby>()
207                         )
208                 );
209
210         c.add (
211                 std::make_shared<dcp::SubtitleString>(
212                         boost::optional<string> (),
213                         true,
214                         true,
215                         true,
216                         dcp::Colour (128, 0, 64),
217                         91,
218                         1.0,
219                         dcp::Time (5, 41,  0, 21, 24),
220                         dcp::Time (6, 12, 15, 21, 24),
221                         0,
222                         dcp::HAlign::CENTER,
223                         0.4,
224                         dcp::VAlign::BOTTOM,
225                         0,
226                         dcp::Direction::RTL,
227                         "What's going ",
228                         dcp::Effect::BORDER,
229                         dcp::Colour (1, 2, 3),
230                         dcp::Time (1, 2, 3, 4, 24),
231                         dcp::Time (5, 6, 7, 8, 24),
232                         0,
233                         std::vector<dcp::Ruby>()
234                         )
235                 );
236
237         c.add (
238                 std::make_shared<dcp::SubtitleString>(
239                         boost::optional<string> (),
240                         true,
241                         true,
242                         true,
243                         dcp::Colour (128, 0, 64),
244                         91,
245                         1.0,
246                         dcp::Time (5, 41,  0, 21, 24),
247                         dcp::Time (6, 12, 15, 21, 24),
248                         0,
249                         dcp::HAlign::CENTER,
250                         0.4,
251                         dcp::VAlign::BOTTOM,
252                         0,
253                         dcp::Direction::RTL,
254                         "on",
255                         dcp::Effect::BORDER,
256                         dcp::Colour (1, 2, 3),
257                         dcp::Time (1, 2, 3, 4, 24),
258                         dcp::Time (5, 6, 7, 8, 24),
259                         4.2,
260                         std::vector<dcp::Ruby>()
261                         )
262                 );
263
264         c._xml_id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
265
266         check_xml (
267                 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
268                 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">\n"
269                 "  <Id>urn:uuid:a6c58cff-3e1e-4b38-acec-a42224475ef6</Id>\n"
270                 "  <ContentTitleText>Test</ContentTitleText>\n"
271                 "  <IssueDate>2016-04-01T03:52:00</IssueDate>\n"
272                 "  <ReelNumber>1</ReelNumber>\n"
273                 "  <Language>en</Language>\n"
274                 "  <EditRate>24 1</EditRate>\n"
275                 "  <TimeCodeRate>24</TimeCodeRate>\n"
276                 "  <SubtitleList>\n"
277                 "    <Font AspectAdjust=\"1.0\" Color=\"FFFFFFFF\" Effect=\"none\" EffectColor=\"FF000000\" ID=\"Frutiger\" Italic=\"no\" Script=\"normal\" Size=\"48\" Underline=\"no\" Weight=\"normal\">\n"
278                 "      <Subtitle SpotNumber=\"1\" TimeIn=\"00:04:09:22\" TimeOut=\"00:04:11:22\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">\n"
279                 "        <Text Valign=\"top\" Vposition=\"80\" Zposition=\"30\">Hello world</Text>\n"
280                 "      </Subtitle>\n"
281                 "    </Font>\n"
282                 "    <Font AspectAdjust=\"1.0\" Color=\"FF800040\" Effect=\"border\" EffectColor=\"FF010203\" Italic=\"yes\" Script=\"normal\" Size=\"91\" Underline=\"yes\" Weight=\"bold\">\n"
283                 "      <Subtitle SpotNumber=\"2\" TimeIn=\"05:41:00:21\" TimeOut=\"06:12:15:21\" FadeUpTime=\"01:02:03:04\" FadeDownTime=\"05:06:07:08\">\n"
284                 "        <Text Valign=\"bottom\" Vposition=\"40\" Direction=\"rtl\">What's going <Space Size=\"4.2\"/>on</Text>\n"
285                 "      </Subtitle>\n"
286                 "    </Font>\n"
287                 "  </SubtitleList>\n"
288                 "</SubtitleReel>",
289                 c.xml_as_string (),
290                 vector<string>()
291                 );
292 }
293
294 /* Write some subtitle content as SMPTE XML and check that it is right.
295    This includes in-line font changes.
296 */
297 BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test2)
298 {
299         dcp::SMPTESubtitleAsset c;
300         c.set_reel_number (1);
301         c.set_language (dcp::LanguageTag("en"));
302         c.set_content_title_text ("Test");
303         c.set_issue_date (dcp::LocalTime ("2016-04-01T03:52:00+00:00"));
304
305         c.add (
306                 std::make_shared<dcp::SubtitleString>(
307                         string ("Arial"),
308                         false,
309                         false,
310                         false,
311                         dcp::Colour (255, 255, 255),
312                         48,
313                         1.0,
314                         dcp::Time (0, 0, 1, 0, 24),
315                         dcp::Time (0, 0, 9, 0, 24),
316                         0,
317                         dcp::HAlign::CENTER,
318                         0.8,
319                         dcp::VAlign::TOP,
320                         0,
321                         dcp::Direction::LTR,
322                         "Testing is ",
323                         dcp::Effect::NONE,
324                         dcp::Colour (0, 0, 0),
325                         dcp::Time (0, 0, 0, 0, 24),
326                         dcp::Time (0, 0, 0, 0, 24),
327                         0,
328                         std::vector<dcp::Ruby>()
329                         )
330                 );
331
332         c.add (
333                 std::make_shared<dcp::SubtitleString>(
334                         string ("Arial"),
335                         true,
336                         false,
337                         false,
338                         dcp::Colour (255, 255, 255),
339                         48,
340                         1.0,
341                         dcp::Time (0, 0, 1, 0, 24),
342                         dcp::Time (0, 0, 9, 0, 24),
343                         0,
344                         dcp::HAlign::CENTER,
345                         0.8,
346                         dcp::VAlign::TOP,
347                         0,
348                         dcp::Direction::LTR,
349                         "really",
350                         dcp::Effect::NONE,
351                         dcp::Colour (0, 0, 0),
352                         dcp::Time (0, 0, 0, 0, 24),
353                         dcp::Time (0, 0, 0, 0, 24),
354                         0,
355                         std::vector<dcp::Ruby>()
356                         )
357                 );
358
359         c.add (
360                 std::make_shared<dcp::SubtitleString>(
361                         string ("Arial"),
362                         false,
363                         false,
364                         false,
365                         dcp::Colour (255, 255, 255),
366                         48,
367                         1.0,
368                         dcp::Time (0, 0, 1, 0, 24),
369                         dcp::Time (0, 0, 9, 0, 24),
370                         0,
371                         dcp::HAlign::CENTER,
372                         0.8,
373                         dcp::VAlign::TOP,
374                         0,
375                         dcp::Direction::LTR,
376                         " fun",
377                         dcp::Effect::NONE,
378                         dcp::Colour (0, 0, 0),
379                         dcp::Time (0, 0, 0, 0, 24),
380                         dcp::Time (0, 0, 0, 0, 24),
381                         0,
382                         std::vector<dcp::Ruby>()
383                         )
384                 );
385
386         c.add (
387                 std::make_shared<dcp::SubtitleString>(
388                         string ("Arial"),
389                         false,
390                         false,
391                         false,
392                         dcp::Colour (255, 255, 255),
393                         48,
394                         1.0,
395                         dcp::Time (0, 0, 1, 0, 24),
396                         dcp::Time (0, 0, 9, 0, 24),
397                         0,
398                         dcp::HAlign::CENTER,
399                         0.9,
400                         dcp::VAlign::TOP,
401                         0,
402                         dcp::Direction::LTR,
403                         "This is the ",
404                         dcp::Effect::NONE,
405                         dcp::Colour (0, 0, 0),
406                         dcp::Time (0, 0, 0, 0, 24),
407                         dcp::Time (0, 0, 0, 0, 24),
408                         0,
409                         std::vector<dcp::Ruby>()
410                         )
411                 );
412
413         c.add (
414                 std::make_shared<dcp::SubtitleString>(
415                         string ("Arial"),
416                         true,
417                         false,
418                         false,
419                         dcp::Colour (255, 255, 255),
420                         48,
421                         1.0,
422                         dcp::Time (0, 0, 1, 0, 24),
423                         dcp::Time (0, 0, 9, 0, 24),
424                         0,
425                         dcp::HAlign::CENTER,
426                         0.9,
427                         dcp::VAlign::TOP,
428                         0,
429                         dcp::Direction::LTR,
430                         "second",
431                         dcp::Effect::NONE,
432                         dcp::Colour (0, 0, 0),
433                         dcp::Time (0, 0, 0, 0, 24),
434                         dcp::Time (0, 0, 0, 0, 24),
435                         0,
436                         std::vector<dcp::Ruby>()
437                         )
438                 );
439
440         c.add (
441                 std::make_shared<dcp::SubtitleString>(
442                         string ("Arial"),
443                         false,
444                         false,
445                         false,
446                         dcp::Colour (255, 255, 255),
447                         48,
448                         1.0,
449                         dcp::Time (0, 0, 1, 0, 24),
450                         dcp::Time (0, 0, 9, 0, 24),
451                         0,
452                         dcp::HAlign::CENTER,
453                         0.9,
454                         dcp::VAlign::TOP,
455                         0,
456                         dcp::Direction::LTR,
457                         " line",
458                         dcp::Effect::NONE,
459                         dcp::Colour (0, 0, 0),
460                         dcp::Time (0, 0, 0, 0, 24),
461                         dcp::Time (0, 0, 0, 0, 24),
462                         0,
463                         std::vector<dcp::Ruby>()
464                         )
465                 );
466
467         c._xml_id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
468
469         check_xml (
470                 c.xml_as_string(),
471                 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
472                 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">\n"
473                 "  <Id>urn:uuid:a6c58cff-3e1e-4b38-acec-a42224475ef6</Id>\n"
474                 "  <ContentTitleText>Test</ContentTitleText>\n"
475                 "  <IssueDate>2016-04-01T03:52:00</IssueDate>\n"
476                 "  <ReelNumber>1</ReelNumber>\n"
477                 "  <Language>en</Language>\n"
478                 "  <EditRate>24 1</EditRate>\n"
479                 "  <TimeCodeRate>24</TimeCodeRate>\n"
480                 "  <SubtitleList>\n"
481                 "    <Font AspectAdjust=\"1.0\" Color=\"FFFFFFFF\" Effect=\"none\" EffectColor=\"FF000000\" ID=\"Arial\" Script=\"normal\" Size=\"48\" Underline=\"no\" Weight=\"normal\">\n"
482                 "      <Subtitle SpotNumber=\"1\" TimeIn=\"00:00:01:00\" TimeOut=\"00:00:09:00\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">\n"
483                 "        <Text Valign=\"top\" Vposition=\"80\"><Font Italic=\"no\">Testing is </Font><Font Italic=\"yes\">really</Font><Font Italic=\"no\"> fun</Font></Text>\n"
484                 "        <Text Valign=\"top\" Vposition=\"90\"><Font Italic=\"no\">This is the </Font><Font Italic=\"yes\">second</Font><Font Italic=\"no\"> line</Font></Text>\n"
485                 "      </Subtitle>\n"
486                 "    </Font>\n"
487                 "  </SubtitleList>\n"
488                 "</SubtitleReel>",
489                 vector<string>()
490                 );
491 }
492
493 /* Write some subtitle content as SMPTE using bitmaps and check that it is right */
494 BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test3)
495 {
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_start_time (dcp::Time());
501
502         boost::filesystem::path const sub_image = "test/data/sub.png";
503
504         c.add (
505                 std::make_shared<dcp::SubtitleImage>(
506                         dcp::ArrayData(sub_image),
507                         dcp::Time (0, 4,  9, 22, 24),
508                         dcp::Time (0, 4, 11, 22, 24),
509                         0,
510                         dcp::HAlign::CENTER,
511                         0.8,
512                         dcp::VAlign::TOP,
513                         -88,
514                         dcp::Time (0, 0, 0, 0, 24),
515                         dcp::Time (0, 0, 0, 0, 24)
516                         )
517               );
518
519         c._id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
520
521         boost::filesystem::path path = "build/test/write_smpte_subtitle_test3";
522         boost::filesystem::create_directories (path);
523         c.write (path / "subs.mxf");
524
525         dcp::SMPTESubtitleAsset read_back (path / "subs.mxf");
526         auto subs = read_back.subtitles ();
527         BOOST_REQUIRE_EQUAL (subs.size(), 1U);
528         auto image = dynamic_pointer_cast<const dcp::SubtitleImage>(subs[0]);
529         BOOST_REQUIRE (image);
530
531         BOOST_CHECK (image->png_image() == dcp::ArrayData(sub_image));
532         BOOST_CHECK (image->in() == dcp::Time(0, 4, 9, 22, 24));
533         BOOST_CHECK (image->out() == dcp::Time(0, 4, 11, 22, 24));
534         BOOST_CHECK_CLOSE (image->h_position(), 0.0, 1);
535         BOOST_CHECK (image->h_align() == dcp::HAlign::CENTER);
536         BOOST_CHECK_CLOSE (image->v_position(), 0.8, 1);
537         BOOST_CHECK (image->v_align() == dcp::VAlign::TOP);
538         BOOST_CHECK_EQUAL(image->z_position(), -88);
539         BOOST_CHECK (image->fade_up_time() == dcp::Time(0, 0, 0, 0, 24));
540         BOOST_CHECK (image->fade_down_time() == dcp::Time(0, 0, 0, 0, 24));
541 }
542
543
544 /* Some closed caption systems require the <Text> elements to be written in order of their
545  * vertical position (see DoM bug #2106).
546  */
547 BOOST_AUTO_TEST_CASE (write_subtitles_in_vertical_order_with_top_alignment)
548 {
549         dcp::SMPTESubtitleAsset c;
550         c.set_reel_number (1);
551         c.set_language (dcp::LanguageTag("en"));
552         c.set_content_title_text ("Test");
553         c.set_issue_date (dcp::LocalTime ("2016-04-01T03:52:00+00:00"));
554
555         c.add (
556                 std::make_shared<dcp::SubtitleString>(
557                         string ("Arial"),
558                         false,
559                         false,
560                         false,
561                         dcp::Colour (255, 255, 255),
562                         48,
563                         1.0,
564                         dcp::Time (0, 0, 1, 0, 24),
565                         dcp::Time (0, 0, 9, 0, 24),
566                         0,
567                         dcp::HAlign::CENTER,
568                         0.8,
569                         dcp::VAlign::TOP,
570                         0,
571                         dcp::Direction::LTR,
572                         "Top line",
573                         dcp::Effect::NONE,
574                         dcp::Colour (0, 0, 0),
575                         dcp::Time (0, 0, 0, 0, 24),
576                         dcp::Time (0, 0, 0, 0, 24),
577                         0,
578                         std::vector<dcp::Ruby>()
579                         )
580                 );
581
582         c.add (
583                 std::make_shared<dcp::SubtitleString>(
584                         string ("Arial"),
585                         false,
586                         false,
587                         false,
588                         dcp::Colour (255, 255, 255),
589                         48,
590                         1.0,
591                         dcp::Time (0, 0, 1, 0, 24),
592                         dcp::Time (0, 0, 9, 0, 24),
593                         0,
594                         dcp::HAlign::CENTER,
595                         0.9,
596                         dcp::VAlign::TOP,
597                         0,
598                         dcp::Direction::LTR,
599                         "Bottom line",
600                         dcp::Effect::NONE,
601                         dcp::Colour (0, 0, 0),
602                         dcp::Time (0, 0, 0, 0, 24),
603                         dcp::Time (0, 0, 0, 0, 24),
604                         0,
605                         std::vector<dcp::Ruby>()
606                         )
607                 );
608
609         c._xml_id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
610
611         check_xml (
612                 c.xml_as_string(),
613                 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
614                 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">\n"
615                 "  <Id>urn:uuid:a6c58cff-3e1e-4b38-acec-a42224475ef6</Id>\n"
616                 "  <ContentTitleText>Test</ContentTitleText>\n"
617                 "  <IssueDate>2016-04-01T03:52:00</IssueDate>\n"
618                 "  <ReelNumber>1</ReelNumber>\n"
619                 "  <Language>en</Language>\n"
620                 "  <EditRate>24 1</EditRate>\n"
621                 "  <TimeCodeRate>24</TimeCodeRate>\n"
622                 "  <SubtitleList>\n"
623                 "    <Font AspectAdjust=\"1.0\" Color=\"FFFFFFFF\" Effect=\"none\" EffectColor=\"FF000000\" ID=\"Arial\" Italic=\"no\" Script=\"normal\" Size=\"48\" Underline=\"no\" Weight=\"normal\">\n"
624                 "      <Subtitle SpotNumber=\"1\" TimeIn=\"00:00:01:00\" TimeOut=\"00:00:09:00\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">\n"
625                 "        <Text Valign=\"top\" Vposition=\"80\">Top line</Text>\n"
626                 "        <Text Valign=\"top\" Vposition=\"90\">Bottom line</Text>\n"
627                 "      </Subtitle>\n"
628                 "    </Font>\n"
629                 "  </SubtitleList>\n"
630                 "</SubtitleReel>",
631                 {}
632                 );
633 }
634
635
636 /* See the test above */
637 BOOST_AUTO_TEST_CASE (write_subtitles_in_vertical_order_with_bottom_alignment)
638 {
639         dcp::SMPTESubtitleAsset c;
640         c.set_reel_number (1);
641         c.set_language (dcp::LanguageTag("en"));
642         c.set_content_title_text ("Test");
643         c.set_issue_date (dcp::LocalTime ("2016-04-01T03:52:00+00:00"));
644
645         c.add (
646                 std::make_shared<dcp::SubtitleString>(
647                         string ("Arial"),
648                         false,
649                         false,
650                         false,
651                         dcp::Colour (255, 255, 255),
652                         48,
653                         1.0,
654                         dcp::Time (0, 0, 1, 0, 24),
655                         dcp::Time (0, 0, 9, 0, 24),
656                         0,
657                         dcp::HAlign::CENTER,
658                         0.8,
659                         dcp::VAlign::BOTTOM,
660                         0,
661                         dcp::Direction::LTR,
662                         "Top line",
663                         dcp::Effect::NONE,
664                         dcp::Colour (0, 0, 0),
665                         dcp::Time (0, 0, 0, 0, 24),
666                         dcp::Time (0, 0, 0, 0, 24),
667                         0,
668                         std::vector<dcp::Ruby>()
669                         )
670                 );
671
672         c.add (
673                 std::make_shared<dcp::SubtitleString>(
674                         string ("Arial"),
675                         false,
676                         false,
677                         false,
678                         dcp::Colour (255, 255, 255),
679                         48,
680                         1.0,
681                         dcp::Time (0, 0, 1, 0, 24),
682                         dcp::Time (0, 0, 9, 0, 24),
683                         0,
684                         dcp::HAlign::CENTER,
685                         0.7,
686                         dcp::VAlign::BOTTOM,
687                         0,
688                         dcp::Direction::LTR,
689                         "Bottom line",
690                         dcp::Effect::NONE,
691                         dcp::Colour (0, 0, 0),
692                         dcp::Time (0, 0, 0, 0, 24),
693                         dcp::Time (0, 0, 0, 0, 24),
694                         0,
695                         std::vector<dcp::Ruby>()
696                         )
697                 );
698
699         c._xml_id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
700
701         check_xml (
702                 c.xml_as_string(),
703                 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
704                 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">\n"
705                 "  <Id>urn:uuid:a6c58cff-3e1e-4b38-acec-a42224475ef6</Id>\n"
706                 "  <ContentTitleText>Test</ContentTitleText>\n"
707                 "  <IssueDate>2016-04-01T03:52:00</IssueDate>\n"
708                 "  <ReelNumber>1</ReelNumber>\n"
709                 "  <Language>en</Language>\n"
710                 "  <EditRate>24 1</EditRate>\n"
711                 "  <TimeCodeRate>24</TimeCodeRate>\n"
712                 "  <SubtitleList>\n"
713                 "    <Font AspectAdjust=\"1.0\" Color=\"FFFFFFFF\" Effect=\"none\" EffectColor=\"FF000000\" ID=\"Arial\" Italic=\"no\" Script=\"normal\" Size=\"48\" Underline=\"no\" Weight=\"normal\">\n"
714                 "      <Subtitle SpotNumber=\"1\" TimeIn=\"00:00:01:00\" TimeOut=\"00:00:09:00\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">\n"
715                 "        <Text Valign=\"bottom\" Vposition=\"80\">Top line</Text>\n"
716                 "        <Text Valign=\"bottom\" Vposition=\"70\">Bottom line</Text>\n"
717                 "      </Subtitle>\n"
718                 "    </Font>\n"
719                 "  </SubtitleList>\n"
720                 "</SubtitleReel>",
721                 {}
722                 );
723 }
724
725
726 BOOST_AUTO_TEST_CASE(smpte_subtitle_standard_written_correctly)
727 {
728         RNGFixer fixer;
729
730         boost::filesystem::path const ref = "test/data";
731         boost::filesystem::path const out = "build/test/smpte_subtitle_standard_written_correctly";
732
733         boost::filesystem::remove_all(out);
734         boost::filesystem::create_directories(out);
735
736         dcp::SMPTESubtitleAsset test_2014;
737         test_2014.set_issue_date(dcp::LocalTime("2020-01-01T14:00:00"));
738         test_2014.write(out / "2014.mxf");
739         BOOST_CHECK_EQUAL(dcp::SMPTESubtitleAsset(ref / "2014.mxf").raw_xml(), dcp::SMPTESubtitleAsset(out / "2014.mxf").raw_xml());
740
741         dcp::SMPTESubtitleAsset test_2010(dcp::SubtitleStandard::SMPTE_2010);
742         test_2010.set_issue_date(dcp::LocalTime("2020-01-01T14:00:00"));
743         test_2010.write(out / "2010.mxf");
744         BOOST_CHECK_EQUAL(dcp::SMPTESubtitleAsset(ref / "2010.mxf").raw_xml(), dcp::SMPTESubtitleAsset(out / "2010.mxf").raw_xml());
745
746         dcp::SMPTESubtitleAsset test_2007(dcp::SubtitleStandard::SMPTE_2007);
747         test_2007.set_issue_date(dcp::LocalTime("2020-01-01T14:00:00"));
748         test_2007.write(out / "2007.mxf");
749         BOOST_CHECK_EQUAL(dcp::SMPTESubtitleAsset(ref / "2007.mxf").raw_xml(), dcp::SMPTESubtitleAsset(out / "2007.mxf").raw_xml());
750 }
751
752
753 BOOST_AUTO_TEST_CASE(smpte_subtitle_standard_read_correctly)
754 {
755         dcp::SMPTESubtitleAsset test_2007("test/data/2007.mxf");
756         BOOST_CHECK(test_2007.subtitle_standard() == dcp::SubtitleStandard::SMPTE_2007);
757
758         dcp::SMPTESubtitleAsset test_2010("test/data/2010.mxf");
759         BOOST_CHECK(test_2010.subtitle_standard() == dcp::SubtitleStandard::SMPTE_2010);
760
761         dcp::SMPTESubtitleAsset test_2014("test/data/2014.mxf");
762         BOOST_CHECK(test_2014.subtitle_standard() == dcp::SubtitleStandard::SMPTE_2014);
763 }
764
765
766 BOOST_AUTO_TEST_CASE(smpte_subtitle_intrinsic_duration_read_correctly)
767 {
768         dcp::SMPTESubtitleAsset ref("test/data/verify_incorrect_closed_caption_ordering3.xml");
769
770         dcp::Key key;
771         ref.set_key(key);
772
773         auto constexpr duration = 480U;
774
775         ref.set_intrinsic_duration(duration);
776
777         auto const path = boost::filesystem::path("build/test/smpte_subtitle_instrinsic_duration_read_correctly.mxf");
778         ref.write(path);
779
780         auto check = dcp::SMPTESubtitleAsset(path);
781         check.set_key(key);
782         BOOST_CHECK_EQUAL(check.intrinsic_duration(), duration);
783 }
784