Move some tests from write_subtitle_test to smpte_subtitle_test.
[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::make_shared;
46 using std::string;
47 using std::shared_ptr;
48 using std::dynamic_pointer_cast;
49 using std::vector;
50 using boost::optional;
51
52
53 BOOST_AUTO_TEST_CASE (smpte_subtitle_id_test)
54 {
55         dcp::SMPTESubtitleAsset subs;
56         subs.add(
57                 make_shared<dcp::SubtitleString>(
58                         optional<string>(),
59                         false, false, false,
60                         dcp::Colour(),
61                         64,
62                         1,
63                         dcp::Time(0, 1, 2, 3, 24),
64                         dcp::Time(0, 2, 2, 3, 24),
65                         0.5,
66                         dcp::HAlign::CENTER,
67                         0.5,
68                         dcp::VAlign::CENTER,
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                         )
76                 );
77         subs.write("build/test/smpte_subtitle_id_test.mxf");
78
79         dcp::SMPTESubtitleAsset check("build/test/smpte_subtitle_id_test.mxf");
80         BOOST_CHECK(check.id() != check.xml_id());
81 }
82
83
84 /** Check reading of a SMPTE subtitle file */
85 BOOST_AUTO_TEST_CASE (read_smpte_subtitle_test)
86 {
87         dcp::SMPTESubtitleAsset sc (
88                 private_test /
89                 "data" /
90                 "JourneyToJah_TLR-1_F_EN-DE-FR_CH_51_2K_LOK_20140225_DGL_SMPTE_OV" /
91                 "8b48f6ae-c74b-4b80-b994-a8236bbbad74_sub.mxf"
92                 );
93
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));
121 }
122
123
124 /** And another one featuring <Font> within <Text> */
125 BOOST_AUTO_TEST_CASE (read_smpte_subtitle_test2)
126 {
127         dcp::SMPTESubtitleAsset sc (private_test / "olsson.xml");
128
129         auto subs = sc.subtitles();
130         BOOST_REQUIRE_EQUAL (subs.size(), 6);
131         auto i = 0;
132         auto is = dynamic_pointer_cast<const dcp::SubtitleString>(subs[i]);
133         BOOST_REQUIRE (is);
134         BOOST_CHECK_EQUAL (is->text(), "Testing is ");
135         BOOST_CHECK (!is->italic());
136         ++i;
137         is = dynamic_pointer_cast<const dcp::SubtitleString>(subs[i]);
138         BOOST_REQUIRE (is);
139         BOOST_CHECK_EQUAL (is->text(), "really");
140         BOOST_CHECK (is->italic());
141         ++i;
142         is = dynamic_pointer_cast<const dcp::SubtitleString>(subs[i]);
143         BOOST_REQUIRE (is);
144         BOOST_CHECK_EQUAL (is->text(), " fun!");
145         BOOST_CHECK (!is->italic());
146         ++i;
147         is = dynamic_pointer_cast<const dcp::SubtitleString>(subs[i]);
148         BOOST_REQUIRE (is);
149         BOOST_CHECK_EQUAL (is->text(), "This is the ");
150         BOOST_CHECK (!is->italic());
151         ++i;
152         is = dynamic_pointer_cast<const dcp::SubtitleString>(subs[i]);
153         BOOST_REQUIRE (is);
154         BOOST_CHECK_EQUAL (is->text(), "second");
155         BOOST_CHECK (is->italic());
156         ++i;
157         is = dynamic_pointer_cast<const dcp::SubtitleString>(subs[i]);
158         BOOST_REQUIRE (is);
159         BOOST_CHECK_EQUAL (is->text(), " line!");
160         BOOST_CHECK (!is->italic());
161 }
162
163
164 /** And another one featuring image subtitles */
165 BOOST_AUTO_TEST_CASE (read_smpte_subtitle_test3)
166 {
167         dcp::SMPTESubtitleAsset subs ("test/data/subs.mxf");
168
169         BOOST_REQUIRE_EQUAL (subs.subtitles().size(), 1);
170         auto si = dynamic_pointer_cast<const dcp::SubtitleImage>(subs.subtitles()[0]);
171         BOOST_REQUIRE (si);
172         BOOST_CHECK (si->png_image() == dcp::ArrayData("test/data/sub.png"));
173 }
174
175
176 /* Write some subtitle content as SMPTE XML and check that it is right */
177 BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test)
178 {
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"));
184
185         c.add (
186                 make_shared<dcp::SubtitleString> (
187                         string ("Frutiger"),
188                         false,
189                         false,
190                         false,
191                         dcp::Colour (255, 255, 255),
192                         48,
193                         1.0,
194                         dcp::Time (0, 4,  9, 22, 24),
195                         dcp::Time (0, 4, 11, 22, 24),
196                         0,
197                         dcp::HAlign::CENTER,
198                         0.8,
199                         dcp::VAlign::TOP,
200                         dcp::Direction::LTR,
201                         "Hello world",
202                         dcp::Effect::NONE,
203                         dcp::Colour (0, 0, 0),
204                         dcp::Time (0, 0, 0, 0, 24),
205                         dcp::Time (0, 0, 0, 0, 24)
206                         )
207                 );
208
209         c.add (
210                 make_shared<dcp::SubtitleString>(
211                         boost::optional<string> (),
212                         true,
213                         true,
214                         true,
215                         dcp::Colour (128, 0, 64),
216                         91,
217                         1.0,
218                         dcp::Time (5, 41,  0, 21, 24),
219                         dcp::Time (6, 12, 15, 21, 24),
220                         0,
221                         dcp::HAlign::CENTER,
222                         0.4,
223                         dcp::VAlign::BOTTOM,
224                         dcp::Direction::RTL,
225                         "What's going on",
226                         dcp::Effect::BORDER,
227                         dcp::Colour (1, 2, 3),
228                         dcp::Time (1, 2, 3, 4, 24),
229                         dcp::Time (5, 6, 7, 8, 24)
230                         )
231                 );
232
233         c._xml_id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
234
235         check_xml (
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>"
249                       "</dcst:Subtitle>"
250                     "</dcst:Font>"
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>"
254                       "</dcst:Subtitle>"
255                     "</dcst:Font>"
256                   "</dcst:SubtitleList>"
257                 "</dcst:SubtitleReel>",
258                 c.xml_as_string (),
259                 vector<string>()
260                 );
261 }
262
263 /* Write some subtitle content as SMPTE XML and check that it is right.
264    This includes in-line font changes.
265 */
266 BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test2)
267 {
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"));
273
274         c.add (
275                 make_shared<dcp::SubtitleString>(
276                         string ("Arial"),
277                         false,
278                         false,
279                         false,
280                         dcp::Colour (255, 255, 255),
281                         48,
282                         1.0,
283                         dcp::Time (0, 0, 1, 0, 24),
284                         dcp::Time (0, 0, 9, 0, 24),
285                         0,
286                         dcp::HAlign::CENTER,
287                         0.8,
288                         dcp::VAlign::TOP,
289                         dcp::Direction::LTR,
290                         "Testing is ",
291                         dcp::Effect::NONE,
292                         dcp::Colour (0, 0, 0),
293                         dcp::Time (0, 0, 0, 0, 24),
294                         dcp::Time (0, 0, 0, 0, 24)
295                         )
296                 );
297
298         c.add (
299                 make_shared<dcp::SubtitleString>(
300                         string ("Arial"),
301                         true,
302                         false,
303                         false,
304                         dcp::Colour (255, 255, 255),
305                         48,
306                         1.0,
307                         dcp::Time (0, 0, 1, 0, 24),
308                         dcp::Time (0, 0, 9, 0, 24),
309                         0,
310                         dcp::HAlign::CENTER,
311                         0.8,
312                         dcp::VAlign::TOP,
313                         dcp::Direction::LTR,
314                         "really",
315                         dcp::Effect::NONE,
316                         dcp::Colour (0, 0, 0),
317                         dcp::Time (0, 0, 0, 0, 24),
318                         dcp::Time (0, 0, 0, 0, 24)
319                         )
320                 );
321
322         c.add (
323                 make_shared<dcp::SubtitleString>(
324                         string ("Arial"),
325                         false,
326                         false,
327                         false,
328                         dcp::Colour (255, 255, 255),
329                         48,
330                         1.0,
331                         dcp::Time (0, 0, 1, 0, 24),
332                         dcp::Time (0, 0, 9, 0, 24),
333                         0,
334                         dcp::HAlign::CENTER,
335                         0.8,
336                         dcp::VAlign::TOP,
337                         dcp::Direction::LTR,
338                         " fun",
339                         dcp::Effect::NONE,
340                         dcp::Colour (0, 0, 0),
341                         dcp::Time (0, 0, 0, 0, 24),
342                         dcp::Time (0, 0, 0, 0, 24)
343                         )
344                 );
345
346         c.add (
347                 make_shared<dcp::SubtitleString>(
348                         string ("Arial"),
349                         false,
350                         false,
351                         false,
352                         dcp::Colour (255, 255, 255),
353                         48,
354                         1.0,
355                         dcp::Time (0, 0, 1, 0, 24),
356                         dcp::Time (0, 0, 9, 0, 24),
357                         0,
358                         dcp::HAlign::CENTER,
359                         0.9,
360                         dcp::VAlign::TOP,
361                         dcp::Direction::LTR,
362                         "This is the ",
363                         dcp::Effect::NONE,
364                         dcp::Colour (0, 0, 0),
365                         dcp::Time (0, 0, 0, 0, 24),
366                         dcp::Time (0, 0, 0, 0, 24)
367                         )
368                 );
369
370         c.add (
371                 make_shared<dcp::SubtitleString>(
372                         string ("Arial"),
373                         true,
374                         false,
375                         false,
376                         dcp::Colour (255, 255, 255),
377                         48,
378                         1.0,
379                         dcp::Time (0, 0, 1, 0, 24),
380                         dcp::Time (0, 0, 9, 0, 24),
381                         0,
382                         dcp::HAlign::CENTER,
383                         0.9,
384                         dcp::VAlign::TOP,
385                         dcp::Direction::LTR,
386                         "second",
387                         dcp::Effect::NONE,
388                         dcp::Colour (0, 0, 0),
389                         dcp::Time (0, 0, 0, 0, 24),
390                         dcp::Time (0, 0, 0, 0, 24)
391                         )
392                 );
393
394         c.add (
395                 make_shared<dcp::SubtitleString>(
396                         string ("Arial"),
397                         false,
398                         false,
399                         false,
400                         dcp::Colour (255, 255, 255),
401                         48,
402                         1.0,
403                         dcp::Time (0, 0, 1, 0, 24),
404                         dcp::Time (0, 0, 9, 0, 24),
405                         0,
406                         dcp::HAlign::CENTER,
407                         0.9,
408                         dcp::VAlign::TOP,
409                         dcp::Direction::LTR,
410                         " line",
411                         dcp::Effect::NONE,
412                         dcp::Colour (0, 0, 0),
413                         dcp::Time (0, 0, 0, 0, 24),
414                         dcp::Time (0, 0, 0, 0, 24)
415                         )
416                 );
417
418         c._xml_id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
419
420         check_xml (
421                 c.xml_as_string(),
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>"
438                         "</dcst:Text>"
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>"
443                         "</dcst:Text>"
444                       "</dcst:Subtitle>"
445                     "</dcst:Font>"
446                   "</dcst:SubtitleList>"
447                 "</dcst:SubtitleReel>",
448                 vector<string>()
449                 );
450 }
451
452 /* Write some subtitle content as SMPTE using bitmaps and check that it is right */
453 BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test3)
454 {
455         dcp::SMPTESubtitleAsset c;
456         c.set_reel_number (1);
457         c.set_language (dcp::LanguageTag("en"));
458         c.set_content_title_text ("Test");
459
460         c.add (
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),
465                         0,
466                         dcp::HAlign::CENTER,
467                         0.8,
468                         dcp::VAlign::TOP,
469                         dcp::Time (0, 0, 0, 0, 24),
470                         dcp::Time (0, 0, 0, 0, 24)
471                         )
472               );
473
474         c._id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
475
476         boost::filesystem::create_directories ("build/test/write_smpte_subtitle_test3");
477         c.write ("build/test/write_smpte_subtitle_test3/subs.mxf");
478
479         /* XXX: check this result when we can read them back in again */
480 }