Make SSA reader more tolerant of missing styles (DoM #2685).
[libsub.git] / test / ssa_reader_test.cc
1 /*
2     Copyright (C) 2016-2021 Carl Hetherington <cth@carlh.net>
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20
21 #include "collect.h"
22 #include "compose.hpp"
23 #include "exceptions.h"
24 #include "ssa_reader.h"
25 #include "subtitle.h"
26 #include "test.h"
27 #include <boost/test/unit_test.hpp>
28 #include <boost/filesystem.hpp>
29 #include <cstdio>
30 #include <cmath>
31 #include <iostream>
32
33
34 using std::fabs;
35 using std::string;
36 using std::vector;
37
38
39 BOOST_AUTO_TEST_CASE (ssa_reader_test)
40 {
41         boost::filesystem::path p = private_test / "example.ssa";
42         FILE* f = fopen (p.string().c_str(), "r");
43         sub::SSAReader reader (f);
44         fclose (f);
45         auto subs = sub::collect<vector<sub::Subtitle>> (reader.subtitles());
46
47         auto i = subs.begin ();
48
49         /* Convert a font size in points to a proportional size for this file */
50         auto fs = [](int x) {
51                 return static_cast<float>(x) / 1024;
52         };
53
54         BOOST_REQUIRE (i != subs.end ());
55         BOOST_CHECK_EQUAL (i->from, sub::Time::from_hms (0, 2, 40, 650));
56         BOOST_CHECK_EQUAL (i->to, sub::Time::from_hms (0, 2, 41, 790));
57         auto j = i->lines.begin();
58         BOOST_REQUIRE (j != i->lines.end ());
59         BOOST_REQUIRE_EQUAL (j->blocks.size(), 1);
60         sub::Block b = j->blocks.front ();
61         BOOST_CHECK_EQUAL (b.text, "Et les enregistrements de ses ondes delta ?");
62         BOOST_CHECK_EQUAL (b.font.get(), "Wolf_Rain");
63         BOOST_CHECK_CLOSE(b.font_size.proportional().get(), fs(56), 0.1);
64         BOOST_CHECK_EQUAL (b.bold, false);
65         BOOST_CHECK_EQUAL (b.italic, false);
66         BOOST_CHECK_EQUAL (b.underline, false);
67         ++i;
68
69         BOOST_REQUIRE (i != subs.end ());
70         BOOST_CHECK_EQUAL (i->from, sub::Time::from_hms (0, 2, 42, 420));
71         BOOST_CHECK_EQUAL (i->to, sub::Time::from_hms (0, 2, 44, 150));
72         j = i->lines.begin();
73         BOOST_REQUIRE (j != i->lines.end ());
74         BOOST_REQUIRE_EQUAL (j->blocks.size(), 1);
75         b = j->blocks.front ();
76         BOOST_CHECK_EQUAL (b.text, "Toujours rien.");
77         BOOST_CHECK_EQUAL (b.font.get(), "Wolf_Rain");
78         BOOST_CHECK_CLOSE(b.font_size.proportional().get(), fs(56), 0.1);
79         BOOST_CHECK_EQUAL (b.bold, false);
80         BOOST_CHECK_EQUAL (b.italic, false);
81         BOOST_CHECK_EQUAL (b.underline, false);
82         ++i;
83
84         BOOST_CHECK (i == subs.end());
85 }
86
87
88 BOOST_AUTO_TEST_CASE (ssa_reader_line_test1)
89 {
90         sub::RawSubtitle base;
91         auto r = sub::SSAReader::parse_line (
92                 base,
93                 "This is a line with some {\\i1}italics{\\i0} and then\\nthere is a new line.",
94                 1920, 1080,
95                 sub::Colour(1, 1, 1)
96                 );
97
98         auto i = r.begin();
99         BOOST_CHECK_EQUAL (i->text, "This is a line with some ");
100         BOOST_CHECK_EQUAL (i->italic, false);
101         ++i;
102         BOOST_REQUIRE (i != r.end ());
103
104         BOOST_CHECK_EQUAL (i->text, "italics");
105         BOOST_CHECK_EQUAL (i->italic, true);
106         ++i;
107         BOOST_REQUIRE (i != r.end ());
108
109         BOOST_CHECK_EQUAL (i->text, " and then");
110         BOOST_CHECK_EQUAL (i->italic, false);
111         ++i;
112         BOOST_REQUIRE (i != r.end ());
113
114         BOOST_CHECK_EQUAL (i->text, "there is a new line.");
115         ++i;
116         BOOST_REQUIRE (i == r.end ());
117 }
118
119
120 BOOST_AUTO_TEST_CASE (ssa_reader_line_test2)
121 {
122         sub::RawSubtitle base;
123         auto r = sub::SSAReader::parse_line (
124                 base,
125                 "{\\i1}It's all just italics{\\i0}",
126                 1920, 1080,
127                 sub::Colour(1, 1, 1)
128                 );
129
130         /* Convert a font size in points to a vertical position for this file */
131         auto vp = [](int x) {
132                 return x * 1.2 / 1080;
133         };
134
135         auto i = r.begin ();
136         BOOST_CHECK_EQUAL (i->text, "It's all just italics");
137         BOOST_CHECK_EQUAL (i->italic, true);
138         ++i;
139         BOOST_REQUIRE (i == r.end ());
140
141         r = sub::SSAReader::parse_line (
142                 base,
143                 "{\\i1}Italic{\\i0}\\Nand new line",
144                 1920, 1080,
145                 sub::Colour(1, 1, 1)
146                 );
147
148         i = r.begin ();
149         BOOST_CHECK_EQUAL (i->text, "Italic");
150         BOOST_CHECK_EQUAL (i->italic, true);
151         BOOST_CHECK(fabs(vp(72) - i->vertical_position.proportional.get()) < 1e-5);
152         ++i;
153         BOOST_CHECK_EQUAL (i->text, "and new line");
154         BOOST_CHECK_EQUAL (i->italic, false);
155         BOOST_CHECK (i->vertical_position.proportional.get() < 1e-5);
156 }
157
158
159 static void
160 test (boost::filesystem::path p)
161 {
162         p = private_test / p;
163         FILE* f = fopen (p.string().c_str(), "r");
164         BOOST_REQUIRE (f);
165         sub::SSAReader r (f);
166         fclose (f);
167 }
168
169 /** Test of reading some typical .ssa files */
170 BOOST_AUTO_TEST_CASE (ssa_reader_test2)
171 {
172         test ("DKH_UT_EN20160601def.ssa");
173         test ("dcpsubtest-en.ssa");
174         test ("dcpsubtest-en.ssa");
175         test ("W_GERMAN_SUBS_grey.ass");
176         test ("XxxHolic (2022) ITA 071223.ass");
177 }
178
179 #define SUB_START(f, t) \
180         BOOST_REQUIRE (i != subs.end ()); \
181         BOOST_CHECK_EQUAL (i->from, f); \
182         BOOST_CHECK_EQUAL (i->to, t); \
183         j = i->lines.begin ();
184
185 #define LINE(vp, vr, hp, hr)                  \
186         BOOST_REQUIRE (j != i->lines.end ()); \
187         BOOST_CHECK (j->vertical_position.proportional); \
188         BOOST_CHECK (fabs (j->vertical_position.proportional.get() - vp) < 1e-5); \
189         BOOST_CHECK (j->vertical_position.reference); \
190         BOOST_CHECK_EQUAL (j->vertical_position.reference.get(), vr); \
191         BOOST_CHECK (fabs (j->horizontal_position.proportional - hp) < 1e-5); \
192         BOOST_CHECK_EQUAL (j->horizontal_position.reference, hr); \
193         k = j->blocks.begin (); \
194         ++j;
195
196 #define BLOCK(t, f, s, b, i, u) \
197         BOOST_REQUIRE (k != j->blocks.end ()); \
198         BOOST_CHECK_EQUAL (k->text, t); \
199         BOOST_CHECK_EQUAL (k->font.get(), f); \
200         BOOST_CHECK_CLOSE(k->font_size.proportional().get(), s, 0.1); \
201         BOOST_CHECK_EQUAL (k->bold, b); \
202         BOOST_CHECK_EQUAL (k->italic, i); \
203         BOOST_CHECK_EQUAL (k->underline, u); \
204         ++k;
205
206 #define SUB_END() \
207         ++i;
208
209 /** Test reading of a file within the libsub tree which exercises the parser */
210 BOOST_AUTO_TEST_CASE (ssa_reader_test3)
211 {
212         boost::filesystem::path p = "test/data/test.ssa";
213         FILE* f = fopen (p.string().c_str(), "r");
214         sub::SSAReader reader (f);
215         fclose (f);
216         auto subs = sub::collect<vector<sub::Subtitle>> (reader.subtitles());
217
218         /* Convert a font size in points to a proportional size for this file */
219         auto fs = [](int x) {
220                 return static_cast<float>(x) / 1080;
221         };
222
223         /* Convert a font size in points to a vertical position for this file */
224         auto vp = [&fs](int x) {
225                 return fs(x) * 1.2;
226         };
227
228         auto i = subs.begin();
229         vector<sub::Line>::iterator j;
230         vector<sub::Block>::iterator k;
231
232         /* Hello world */
233         SUB_START (sub::Time::from_hms (0, 0, 1, 230), sub::Time::from_hms (0, 0, 4, 550));
234         LINE((10.0 / 1080), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
235         BLOCK("Hello world", "Arial", fs(20), false, false, false);
236         SUB_END();
237
238         /* This is vertically moved\nand has two lines. */
239         SUB_START (sub::Time::from_hms (0, 0, 5, 740), sub::Time::from_hms (0, 0, 11, 0));
240         /* The first line should be 900 pixels and one line (20
241            points, 1.2 times spaced, as a proportion of the total
242            screen height 729 points) up.
243         */
244         LINE((900.0 / 1080) - vp(20), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
245         BLOCK("This is vertically moved", "Arial", fs(20), false, false, false);
246         LINE((900.0 / 1080), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
247         BLOCK("and has two lines.", "Arial", fs(20), false, false, false);
248         SUB_END();
249
250         /* Some {\i1}italics{\i} are here. */
251         SUB_START (sub::Time::from_hms (0, 0, 7, 740), sub::Time::from_hms (0, 0, 9, 0));
252         LINE((10.0 / 1080), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
253         BLOCK("Some ", "Arial", fs(20), false, false, false);
254         BLOCK("italics", "Arial", fs(20), false, true, false);
255         BLOCK(" are here.", "Arial", fs(20), false, false, false);
256         SUB_END();
257
258         /* Alignments */
259
260         SUB_START (sub::Time::from_hms (0, 0, 9, 230), sub::Time::from_hms (0, 0, 11, 560));
261         LINE ((10.0 / 1080), sub::BOTTOM_OF_SCREEN, 0, sub::LEFT_OF_SCREEN);
262         BLOCK("bottom left", "Arial", fs(20), false, false, false);
263         SUB_END ();
264
265         SUB_START (sub::Time::from_hms (0, 0, 9, 240), sub::Time::from_hms (0, 0, 11, 560));
266         LINE ((10.0 / 1080), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
267         BLOCK("bottom centre", "Arial", fs(20), false, false, false);
268         SUB_END ();
269
270         SUB_START (sub::Time::from_hms (0, 0, 9, 250), sub::Time::from_hms (0, 0, 11, 560));
271         LINE ((10.0 / 1080), sub::BOTTOM_OF_SCREEN, 0, sub::RIGHT_OF_SCREEN);
272         BLOCK("bottom right", "Arial", fs(20), false, false, false);
273         SUB_END ();
274
275         SUB_START (sub::Time::from_hms (0, 0, 9, 260), sub::Time::from_hms (0, 0, 11, 560));
276         /* Position is half of a 20pt line (with line spacing) above vertical centre */
277         LINE (-vp(10), sub::VERTICAL_CENTRE_OF_SCREEN, 0, sub::LEFT_OF_SCREEN);
278         BLOCK("middle left", "Arial", fs(20), false, false, false);
279         SUB_END ();
280
281         SUB_START (sub::Time::from_hms (0, 0, 9, 270), sub::Time::from_hms (0, 0, 11, 560));
282         LINE (-vp(10), sub::VERTICAL_CENTRE_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
283         BLOCK("middle centre", "Arial", fs(20), false, false, false);
284         SUB_END ();
285
286         SUB_START (sub::Time::from_hms (0, 0, 9, 280), sub::Time::from_hms (0, 0, 11, 560));
287         LINE (-vp(10), sub::VERTICAL_CENTRE_OF_SCREEN, 0, sub::RIGHT_OF_SCREEN);
288         BLOCK("middle right", "Arial", fs(20), false, false, false);
289         SUB_END ();
290
291         SUB_START (sub::Time::from_hms (0, 0, 9, 290), sub::Time::from_hms (0, 0, 11, 560));
292         LINE ((10.0 / 1080), sub::TOP_OF_SCREEN, 0, sub::LEFT_OF_SCREEN);
293         BLOCK("top left", "Arial", fs(20), false, false, false);
294         SUB_END ();
295
296         SUB_START (sub::Time::from_hms (0, 0, 9, 300), sub::Time::from_hms (0, 0, 11, 560));
297         LINE ((10.0 / 1080), sub::TOP_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
298         BLOCK("top centre", "Arial", fs(20), false, false, false);
299         SUB_END ();
300
301         SUB_START (sub::Time::from_hms (0, 0, 9, 310), sub::Time::from_hms (0, 0, 11, 560));
302         LINE ((10.0 / 1080), sub::TOP_OF_SCREEN, 0, sub::RIGHT_OF_SCREEN);
303         BLOCK("top right", "Arial", fs(20), false, false, false);
304         SUB_END ();
305
306         BOOST_REQUIRE (i == subs.end ());
307 }
308
309
310 /** Test reading of a file within the libsub-test-private tree which exercises the parser */
311 BOOST_AUTO_TEST_CASE (ssa_reader_test4)
312 {
313         boost::filesystem::path p = private_test / "dcpsubtest2-en.ssa";
314         FILE* f = fopen (p.string().c_str(), "r");
315         sub::SSAReader reader (f);
316         fclose (f);
317         auto subs = sub::collect<vector<sub::Subtitle>> (reader.subtitles());
318
319         auto i = subs.begin();
320         vector<sub::Line>::iterator j;
321         vector<sub::Block>::iterator k;
322
323         BOOST_REQUIRE (i != subs.end ());
324
325         /* Convert a font size in points to a proportional size for this file */
326         auto fs = [](int x) {
327                 return static_cast<float>(x) / 288;
328         };
329
330         /* Convert a font size in points to a vertical position for this file */
331         auto vp = [&fs](int x) {
332                 return fs(x) * 1.2;
333         };
334
335         SUB_START (sub::Time::from_hms (0, 0, 1, 0), sub::Time::from_hms (0, 0, 3, 0));
336         /* The first line should be one line (50 points, 1.2 times
337            spaced, as a proportion of the total screen height 729
338            points) up.
339         */
340         LINE(vp(50), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
341         BLOCK("1st line: This is normal", "Verdana", fs(50), false, false, false);
342         LINE(0, sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
343         BLOCK("2d line: this is bold", "Verdana", fs(50), true, false, false);
344         SUB_END ();
345
346         SUB_START (sub::Time::from_hms (0, 0, 3, 100), sub::Time::from_hms (0, 0, 5, 100));
347         LINE(vp(50), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
348         BLOCK("1st line: this is bold", "Verdana", fs(50), true, false, false);
349         LINE(0, sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
350         BLOCK("2nd line: This is normal", "Verdana", fs(50), false, false, false);
351         SUB_END ();
352
353         SUB_START (sub::Time::from_hms (0, 0, 5, 200), sub::Time::from_hms (0, 0, 7, 200));
354         LINE(vp(50), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
355         BLOCK("1st line: this is bold", "Verdana", fs(50), true, false, false);
356         LINE(0, sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
357         BLOCK("2nd line: this is italics", "Verdana", fs(50), false, true, false);
358         SUB_END ();
359
360         SUB_START (sub::Time::from_hms (0, 0, 7, 300), sub::Time::from_hms (0, 0, 9, 300));
361         LINE(vp(50), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
362         BLOCK("1st line: this is italics", "Verdana", fs(50), false, true, false);
363         LINE(0, sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
364         BLOCK("2nd line: this is bold", "Verdana", fs(50), true, false, false);
365         SUB_END ();
366 }
367
368
369 /** Test reading of a .ass file */
370 BOOST_AUTO_TEST_CASE (ssa_reader_test5)
371 {
372         boost::filesystem::path p = private_test / "dcpsubtest-en.ass";
373         FILE* f = fopen (p.string().c_str(), "r");
374         sub::SSAReader reader (f);
375         fclose (f);
376         auto subs = sub::collect<vector<sub::Subtitle>> (reader.subtitles());
377
378         /* Convert a font size in points to a proportional size for this file */
379         auto fs = [](int x) {
380                 return static_cast<float>(x) / 288;
381         };
382
383         /* Convert a font size in points to a vertical position for this file */
384         auto vp = [&fs](int x) {
385                 return fs(x) * 1.2;
386         };
387
388         auto i = subs.begin ();
389         vector<sub::Line>::iterator j;
390         vector<sub::Block>::iterator k;
391
392         BOOST_REQUIRE (i != subs.end ());
393
394         SUB_START (sub::Time::from_hms (0, 0, 1, 0), sub::Time::from_hms (0, 0, 3, 0));
395         /* The first line should be one line (26 points, 1.2 times
396            spaced, as a proportion of the total screen height 729
397            points) up.
398         */
399         LINE(vp(26), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
400         BLOCK("1st subtitle, 1st line", "arial", fs(26), true, false, false);
401         LINE(0, sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
402         BLOCK("2nd subtitle, 2nd line", "arial", fs(26), true, false, false);
403         SUB_END ();
404
405         SUB_START (sub::Time::from_hms (0, 0, 3, 100), sub::Time::from_hms (0, 0, 5, 100));
406         LINE(vp(26), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
407         BLOCK("2nd subtitle, 1st line", "arial", fs(26), true, false, false);
408         LINE(0, sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
409         BLOCK("2nd subtitle, 2nd line", "arial", fs(26), true, false, false);
410         SUB_END ();
411
412         SUB_START (sub::Time::from_hms (0, 0, 5, 200), sub::Time::from_hms (0, 0, 7, 200));
413         LINE(vp(26), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
414         BLOCK("3rd subtitle, 1st line", "arial", fs(26), true, false, false);
415         LINE(0, sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
416         BLOCK("3rd subtitle, 2nd line", "arial", fs(26), true, false, false);
417         SUB_END ();
418
419         SUB_START (sub::Time::from_hms (0, 0, 7, 300), sub::Time::from_hms (0, 0, 9, 300));
420         LINE(vp(26), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
421         BLOCK("4th subtitle, 1st line", "arial", fs(26), true, false, false);
422         LINE(0, sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
423         BLOCK("4th subtitle, 2nd line", "arial", fs(26), true, false, false);
424         SUB_END ();
425 }
426
427
428 /** Test reading of another .ass file */
429 BOOST_AUTO_TEST_CASE (ssa_reader_test6)
430 {
431         boost::filesystem::path p = private_test / "DCP-o-matic_test_subs_1.ass";
432         auto f = fopen (p.string().c_str(), "r");
433         BOOST_REQUIRE (f);
434         sub::SSAReader reader (f);
435         fclose (f);
436         auto subs = sub::collect<vector<sub::Subtitle>> (reader.subtitles());
437
438         /* Convert a font size in points to a proportional size for this file */
439         auto fs = [](int x) {
440                 return static_cast<float>(x) / 288;
441         };
442
443         /* Convert a font size in points to a vertical position for this file */
444         auto vp = [&fs](int x) {
445                 return fs(x) * 1.2;
446         };
447
448         auto i = subs.begin ();
449         vector<sub::Line>::iterator j;
450         vector<sub::Block>::iterator k;
451
452         BOOST_REQUIRE (i != subs.end ());
453
454         SUB_START (sub::Time::from_hms (0, 0, 0, 70), sub::Time::from_hms (0, 0, 1, 110));
455         /* The first line should be one line (30 points, 1.2 times
456            spaced, as a proportion of the total screen height 792
457            points) up.  There's also a 10 pixel (with respect to a
458            288-pixel-high screen) margin.
459         */
460         LINE((vp(30) + (10.0 / 288.0)), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
461         BLOCK("This line is normal", "Arial", fs(30), false, false, false);
462         LINE ((10.0 / 288.0), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
463         BLOCK("This line is bold", "Arial", fs(30), true, false, false);
464         SUB_END ();
465
466         SUB_START (sub::Time::from_hms (0, 0, 1, 200), sub::Time::from_hms (0, 0, 2, 240));
467         LINE((vp(30) + (10.0 / 288.0)), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
468         BLOCK("This line is bold", "Arial", fs(30), true, false, false);
469         LINE ((10.0 / 288.0), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
470         BLOCK("This line is normal", "Arial", fs(30), false, false, false);
471         SUB_END ();
472
473         SUB_START (sub::Time::from_hms (0, 0, 2, 300), sub::Time::from_hms (0, 0, 3, 380));
474         LINE ((vp(30) + (10.0 / 288.0)), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
475         BLOCK("This line is bold", "Arial", fs(30), true, false, false);
476         LINE ((10.0 / 288.0), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
477         BLOCK("This line is italic", "Arial", fs(30), false, true, false);
478         SUB_END ();
479
480         SUB_START (sub::Time::from_hms (0, 0, 3, 400), sub::Time::from_hms (0, 0, 4, 480));
481         LINE ((vp(30) + (10.0 / 288.0)), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
482         BLOCK("This line is italic", "Arial", fs(30), false, true, false);
483         LINE ((10.0 / 288.0), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
484         BLOCK("This line is bold", "Arial", fs(30), true, false, false);
485         SUB_END ();
486
487         SUB_START (sub::Time::from_hms (0, 0, 4, 510), sub::Time::from_hms (0, 0, 5, 600));
488         LINE ((vp(30) + (10.0 / 288.0)), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
489         BLOCK("Last three words are ", "Arial", fs(30), false, false, false);
490         BLOCK("bold AND italic", "Arial", fs(30), true, true, false);
491         LINE ((10.0 / 288.0), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
492         BLOCK("Last three words are ", "Arial", fs(30), false, false, false);
493         BLOCK("italic AND bold", "Arial", fs(30), true, true, false);
494         SUB_END ();
495
496         SUB_START (sub::Time::from_hms (0, 0, 5, 620), sub::Time::from_hms (0, 0, 6, 710));
497         LINE((vp(30) + (10.0 / 288.0)), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
498         BLOCK("Last three words are ", "Arial", fs(30), false, false, false);
499         BLOCK("bold AND italic", "Arial", fs(30), true, true, false);
500         LINE ((10.0 / 288.0), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
501         BLOCK("First three words", "Arial", fs(30), true, true, false);
502         BLOCK(" are italic AND bold", "Arial", fs(30), false, false, false);
503         SUB_END ();
504
505         SUB_START (sub::Time::from_hms (0, 0, 6, 730), sub::Time::from_hms (0, 0, 8, 30));
506         LINE ((vp(30) + (10.0 / 288.0)), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
507         BLOCK("Last three words are ", "Arial", fs(30), false, false, false);
508         BLOCK("bold AND italic", "Arial", fs(30), true, true, false);
509         LINE ((10.0 / 288.0), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
510         BLOCK("This line is normal", "Arial", fs(30), false, false, false);
511         SUB_END ();
512
513         SUB_START (sub::Time::from_hms (0, 0, 8, 90), sub::Time::from_hms (0, 0, 9, 210));
514         LINE((vp(30) + (10.0 / 288.0)), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
515         BLOCK("Both lines are bold AND italic", "Arial", fs(30), true, true, false);
516         LINE((10.0 / 288.0), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
517         BLOCK("Both lines are bold AND italic", "Arial", fs(30), true, true, false);
518         SUB_END ();
519 }
520
521
522 BOOST_AUTO_TEST_CASE (ssa_reader_test7)
523 {
524         auto p = boost::filesystem::path("test") / "data" / "test3.ssa";
525         auto f = fopen(p.string().c_str(), "r");
526         BOOST_REQUIRE(f);
527         sub::SSAReader reader(f);
528         fclose(f);
529         auto subs = sub::collect<vector<sub::Subtitle>>(reader.subtitles());
530
531         /* Convert a font size in points to a proportional size for this file */
532         auto fs = [](int x) {
533                 return static_cast<float>(x) / 1080;
534         };
535
536         /* Convert a font size in points to a vertical position for this file */
537         auto vp = [&fs](int x) {
538                 return fs(x) * 1.2;
539         };
540
541         auto i = subs.begin();
542         vector<sub::Line>::iterator j;
543         vector<sub::Block>::iterator k;
544
545         BOOST_REQUIRE (i != subs.end());
546
547         SUB_START(sub::Time::from_hms(0, 0, 1, 0), sub::Time::from_hms(0, 0, 3, 0));
548         LINE((vp(60) + (100.0 / 1080)), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
549         BLOCK("Helvetica Neue 60pt - Default", "Helvetica Neue", fs(60), false, false, false);
550         LINE((100.0 / 1080), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
551         BLOCK("Bottom 100 pt off edge", "Helvetica Neue", fs(60), false, false, false);
552         SUB_END();
553
554         SUB_START(sub::Time::from_hms(0, 0, 4, 0), sub::Time::from_hms(0, 0, 6, 0));
555         LINE((vp(30) + (100.0 / 1080)), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
556         BLOCK("Helvetica Neue 30pt", "Helvetica Neue", fs(30), false, false, false);
557         LINE((100.0 / 1080), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
558         BLOCK("Bottom 100pt off edge", "Helvetica Neue", fs(30), false, false, false);
559         SUB_END();
560
561         SUB_START(sub::Time::from_hms(0, 0, 7, 0), sub::Time::from_hms(0, 0, 9, 0));
562         LINE((vp(120) + (100.0 / 1080)), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
563         BLOCK("Helvetica Neue 120pt", "Helvetica Neue", fs(120), false, false, false);
564         LINE((100.0 / 1080), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
565         BLOCK("Bottom 100pt off edge", "Helvetica Neue", fs(120), false, false, false);
566         SUB_END();
567
568         SUB_START(sub::Time::from_hms(0, 0, 10, 0), sub::Time::from_hms(0, 0, 12, 0));
569         LINE((100.0 / 1080), sub::TOP_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
570         BLOCK("Helvetica Neue 60pt", "Helvetica Neue", fs(60), false, false, false);
571         LINE((vp(60) + (100.0 / 1080)), sub::TOP_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
572         BLOCK("Top Alignment 100pt off edge", "Helvetica Neue", fs(60), false, false, false);
573         SUB_END();
574
575         SUB_START(sub::Time::from_hms(0, 0, 13, 0), sub::Time::from_hms(0, 0, 15, 0));
576         LINE(vp(-60), sub::VERTICAL_CENTRE_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
577         BLOCK("Helvetica Neue 60pt", "Helvetica Neue 60 Center", fs(60), false, false, false);
578         LINE(0, sub::VERTICAL_CENTRE_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
579         BLOCK(" Vertical Center Alignment", "Helvetica Neue 60 Center", fs(60), false, false, false);
580         SUB_END();
581 }
582
583
584 /** Test \pos */
585 BOOST_AUTO_TEST_CASE (ssa_reader_pos)
586 {
587         boost::filesystem::path p = "test/data/test2.ssa";
588         FILE* f = fopen (p.string().c_str(), "r");
589         sub::SSAReader reader (f);
590         fclose (f);
591         auto subs = sub::collect<vector<sub::Subtitle>> (reader.subtitles());
592
593         /* Convert a font size in points to a proportional size for this file */
594         auto fs = [](int x) {
595                 return static_cast<float>(x) / 1080;
596         };
597
598         auto i = subs.begin ();
599         vector<sub::Line>::iterator j;
600         vector<sub::Block>::iterator k;
601
602         /* Hello world */
603         SUB_START (sub::Time::from_hms (0, 0, 1, 230), sub::Time::from_hms (0, 0, 4, 550));
604         LINE ((10.0 / 1080), sub::BOTTOM_OF_SCREEN, 0, sub::HORIZONTAL_CENTRE_OF_SCREEN);
605         BLOCK ("Hello world this is ", "Arial", fs(20), false, false, false);
606         LINE ((310.0 / 1080), sub::TOP_OF_SCREEN, 400.0 / 1920, sub::LEFT_OF_SCREEN);
607         BLOCK ("positioning.", "Arial", fs(20), false, false, false);
608         SUB_END();
609 }
610
611
612 /** Test \fs */
613 BOOST_AUTO_TEST_CASE (ssa_reader_fs)
614 {
615         sub::RawSubtitle base;
616         auto r = sub::SSAReader::parse_line (
617                 base,
618                 "This is a line with some {\\fs64}font sizing.",
619                 1920, 1080,
620                 sub::Colour(1, 1, 1)
621                 );
622
623         auto i = r.begin ();
624         BOOST_CHECK_EQUAL (i->text, "This is a line with some ");
625         ++i;
626         BOOST_REQUIRE (i != r.end ());
627
628         BOOST_CHECK_EQUAL (i->text, "font sizing.");
629         BOOST_REQUIRE(i->font_size.proportional());
630         BOOST_CHECK_CLOSE(i->font_size.proportional().get(), 64.0 / 1080, 0.1);
631         ++i;
632         BOOST_REQUIRE (i == r.end ());
633 }
634
635
636 static void
637 test_c(string command, string colour)
638 {
639         sub::RawSubtitle base;
640         auto r = sub::SSAReader::parse_line (
641                 base,
642                 String::compose("{\\c%1}Hello world", command),
643                 1920, 1080,
644                 sub::Colour(1, 0, 1)
645                 );
646
647         auto i = r.begin ();
648         BOOST_CHECK_EQUAL (i->text, "Hello world");
649         BOOST_CHECK (i->colour == sub::Colour::from_rgb_hex(colour));
650         BOOST_REQUIRE(std::next(i) == r.end());
651 }
652
653
654 /** Test a valid \c */
655 BOOST_AUTO_TEST_CASE (ssa_reader_c)
656 {
657         test_c("&H00FFFF&", "ffff00");
658         test_c("&H123456&", "563412");
659         test_c("&H0&", "000000");
660         test_c("&HFF&", "ff0000");
661         test_c("&HFF00&", "00ff00");
662         test_c("&HFF0000&", "0000ff");
663         test_c("&HFFFFFF&", "ffffff");
664         /* \c with no parameter seems to be parsed as "return to primary colour" */
665         test_c("", "ff00ff");
666 }
667