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