Add EPISODE and PROMO content types.
[libdcp.git] / src / types.cc
1 /*
2     Copyright (C) 2012-2019 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 #include "raw_convert.h"
35 #include "types.h"
36 #include "exceptions.h"
37 #include "compose.hpp"
38 #include "dcp_assert.h"
39 #include <libxml++/libxml++.h>
40 #include <boost/algorithm/string.hpp>
41 #include <vector>
42 #include <cstdio>
43 #include <iomanip>
44
45 using namespace std;
46 using namespace dcp;
47 using namespace boost;
48
49 bool dcp::operator== (dcp::Size const & a, dcp::Size const & b)
50 {
51         return (a.width == b.width && a.height == b.height);
52 }
53
54 bool dcp::operator!= (dcp::Size const & a, dcp::Size const & b)
55 {
56         return !(a == b);
57 }
58
59 ostream& dcp::operator<< (ostream& s, dcp::Size const & a)
60 {
61         s << a.width << "x" << a.height;
62         return s;
63 }
64
65 /** Construct a Fraction from a string of the form <numerator> <denominator>
66  *  e.g. "1 3".
67  */
68 Fraction::Fraction (string s)
69 {
70         vector<string> b;
71         split (b, s, is_any_of (" "));
72         if (b.size() != 2) {
73                 boost::throw_exception (XMLError ("malformed fraction " + s + " in XML node"));
74         }
75         numerator = raw_convert<int> (b[0]);
76         denominator = raw_convert<int> (b[1]);
77 }
78
79 string
80 Fraction::as_string () const
81 {
82         return String::compose ("%1 %2", numerator, denominator);
83 }
84
85 bool
86 dcp::operator== (Fraction const & a, Fraction const & b)
87 {
88         return (a.numerator == b.numerator && a.denominator == b.denominator);
89 }
90
91 bool
92 dcp::operator!= (Fraction const & a, Fraction const & b)
93 {
94         return (a.numerator != b.numerator || a.denominator != b.denominator);
95 }
96
97 ostream&
98 dcp::operator<< (ostream& s, Fraction const & f)
99 {
100         s << f.numerator << "/" << f.denominator;
101         return s;
102 }
103
104 /** Construct a Colour, initialising it to black. */
105 Colour::Colour ()
106         : r (0)
107         , g (0)
108         , b (0)
109 {
110
111 }
112
113 /** Construct a Colour from R, G and B.  The values run between
114  *  0 and 255.
115  */
116 Colour::Colour (int r_, int g_, int b_)
117         : r (r_)
118         , g (g_)
119         , b (b_)
120 {
121
122 }
123
124 /** Construct a Colour from an ARGB hex string; the alpha value is ignored.
125  *  @param argb_hex A string of the form AARRGGBB, where e.g. RR is a two-character
126  *  hex value.
127  */
128 Colour::Colour (string argb_hex)
129 {
130         int alpha;
131         if (sscanf (argb_hex.c_str(), "%2x%2x%2x%2x", &alpha, &r, &g, &b) != 4) {
132                 boost::throw_exception (XMLError ("could not parse colour string"));
133         }
134 }
135
136 /** @return An ARGB string of the form AARRGGBB, where e.g. RR is a two-character
137  *  hex value.  The alpha value will always be FF (ie 255; maximum alpha).
138  */
139 string
140 Colour::to_argb_string () const
141 {
142         char buffer[9];
143         snprintf (buffer, sizeof(buffer), "FF%02X%02X%02X", r, g, b);
144         return buffer;
145 }
146
147 /** @return An RGB string of the form RRGGBB, where e.g. RR is a two-character
148  *  hex value.
149  */
150 string
151 Colour::to_rgb_string () const
152 {
153         char buffer[7];
154         snprintf (buffer, sizeof(buffer), "%02X%02X%02X", r, g, b);
155         return buffer;
156 }
157
158 /** operator== for Colours.
159  *  @param a First colour to compare.
160  *  @param b Second colour to compare.
161  */
162 bool
163 dcp::operator== (Colour const & a, Colour const & b)
164 {
165         return (a.r == b.r && a.g == b.g && a.b == b.b);
166 }
167
168 /** operator!= for Colours.
169  *  @param a First colour to compare.
170  *  @param b Second colour to compare.
171  */
172 bool
173 dcp::operator!= (Colour const & a, Colour const & b)
174 {
175         return !(a == b);
176 }
177
178 ostream &
179 dcp::operator<< (ostream& s, Colour const & c)
180 {
181         s << "(" << c.r << ", " << c.g << ", " << c.b << ")";
182         return s;
183 }
184
185 string
186 dcp::effect_to_string (Effect e)
187 {
188         switch (e) {
189         case NONE:
190                 return "none";
191         case BORDER:
192                 return "border";
193         case SHADOW:
194                 return "shadow";
195         }
196
197         boost::throw_exception (MiscError ("unknown effect type"));
198 }
199
200 Effect
201 dcp::string_to_effect (string s)
202 {
203         if (s == "none") {
204                 return NONE;
205         } else if (s == "border") {
206                 return BORDER;
207         } else if (s == "shadow") {
208                 return SHADOW;
209         }
210
211         boost::throw_exception (DCPReadError ("unknown subtitle effect type"));
212 }
213
214 string
215 dcp::halign_to_string (HAlign h)
216 {
217         switch (h) {
218         case HALIGN_LEFT:
219                 return "left";
220         case HALIGN_CENTER:
221                 return "center";
222         case HALIGN_RIGHT:
223                 return "right";
224         }
225
226         boost::throw_exception (MiscError ("unknown subtitle halign type"));
227 }
228
229 HAlign
230 dcp::string_to_halign (string s)
231 {
232         if (s == "left") {
233                 return HALIGN_LEFT;
234         } else if (s == "center") {
235                 return HALIGN_CENTER;
236         } else if (s == "right") {
237                 return HALIGN_RIGHT;
238         }
239
240         boost::throw_exception (DCPReadError ("unknown subtitle halign type"));
241 }
242
243 string
244 dcp::valign_to_string (VAlign v)
245 {
246         switch (v) {
247         case VALIGN_TOP:
248                 return "top";
249         case VALIGN_CENTER:
250                 return "center";
251         case VALIGN_BOTTOM:
252                 return "bottom";
253         }
254
255         boost::throw_exception (MiscError ("unknown subtitle valign type"));
256 }
257
258 VAlign
259 dcp::string_to_valign (string s)
260 {
261         if (s == "top") {
262                 return VALIGN_TOP;
263         } else if (s == "center") {
264                 return VALIGN_CENTER;
265         } else if (s == "bottom") {
266                 return VALIGN_BOTTOM;
267         }
268
269         boost::throw_exception (DCPReadError ("unknown subtitle valign type"));
270 }
271
272 string
273 dcp::direction_to_string (Direction v)
274 {
275         switch (v) {
276         case DIRECTION_LTR:
277                 return "ltr";
278         case DIRECTION_RTL:
279                 return "rtl";
280         case DIRECTION_TTB:
281                 return "ttb";
282         case DIRECTION_BTT:
283                 return "btt";
284         }
285
286         boost::throw_exception (MiscError ("unknown subtitle direction type"));
287 }
288
289 Direction
290 dcp::string_to_direction (string s)
291 {
292         if (s == "ltr" || s == "horizontal") {
293                 return DIRECTION_LTR;
294         } else if (s == "rtl") {
295                 return DIRECTION_RTL;
296         } else if (s == "ttb" || s == "vertical") {
297                 return DIRECTION_TTB;
298         } else if (s == "btt") {
299                 return DIRECTION_BTT;
300         }
301
302         boost::throw_exception (DCPReadError ("unknown subtitle direction type"));
303 }
304
305 /** Convert a content kind to a string which can be used in a
306  *  &lt;ContentKind&gt; node.
307  *  @param kind ContentKind.
308  *  @return string.
309  */
310 string
311 dcp::content_kind_to_string (ContentKind kind)
312 {
313         switch (kind) {
314         case FEATURE:
315                 return "feature";
316         case SHORT:
317                 return "short";
318         case TRAILER:
319                 return "trailer";
320         case TEST:
321                 return "test";
322         case TRANSITIONAL:
323                 return "transitional";
324         case RATING:
325                 return "rating";
326         case TEASER:
327                 return "teaser";
328         case POLICY:
329                 return "policy";
330         case PUBLIC_SERVICE_ANNOUNCEMENT:
331                 return "psa";
332         case ADVERTISEMENT:
333                 return "advertisement";
334         case EPISODE:
335                 return "episode";
336         case PROMO:
337                 return "promo";
338         }
339
340         DCP_ASSERT (false);
341 }
342
343 /** Convert a string from a &lt;ContentKind&gt; node to a libdcp ContentKind.
344  *  Reasonably tolerant about varying case.
345  *  @param kind Content kind string.
346  *  @return libdcp ContentKind.
347  */
348 dcp::ContentKind
349 dcp::content_kind_from_string (string kind)
350 {
351         transform (kind.begin(), kind.end(), kind.begin(), ::tolower);
352
353         if (kind == "feature") {
354                 return FEATURE;
355         } else if (kind == "short") {
356                 return SHORT;
357         } else if (kind == "trailer") {
358                 return TRAILER;
359         } else if (kind == "test") {
360                 return TEST;
361         } else if (kind == "transitional") {
362                 return TRANSITIONAL;
363         } else if (kind == "rating") {
364                 return RATING;
365         } else if (kind == "teaser") {
366                 return TEASER;
367         } else if (kind == "policy") {
368                 return POLICY;
369         } else if (kind == "psa") {
370                 return PUBLIC_SERVICE_ANNOUNCEMENT;
371         } else if (kind == "advertisement") {
372                 return ADVERTISEMENT;
373         } else if (kind == "episode") {
374                 return EPISODE;
375         } else if (kind == "promo") {
376                 return PROMO;
377         }
378
379         throw BadContentKindError (kind);
380 }
381
382 string
383 dcp::marker_to_string (dcp::Marker m)
384 {
385         switch (m) {
386         case FFOC:
387                 return "FFOC";
388         case LFOC:
389                 return "LFOC";
390         case FFTC:
391                 return "FFTC";
392         case LFTC:
393                 return "LFTC";
394         case FFOI:
395                 return "FFOI";
396         case LFOI:
397                 return "LFOI";
398         case FFEC:
399                 return "FFEC";
400         case LFEC:
401                 return "LFEC";
402         case FFMC:
403                 return "FFMC";
404         case LFMC:
405                 return "LFMC";
406         }
407
408         DCP_ASSERT (false);
409 }
410
411 dcp::Marker
412 dcp::marker_from_string (string s)
413 {
414         if (s == "FFOC") {
415                 return FFOC;
416         } else if (s == "LFOC") {
417                 return LFOC;
418         } else if (s == "FFTC") {
419                 return FFTC;
420         } else if (s == "LFTC") {
421                 return LFTC;
422         } else if (s == "FFOI") {
423                 return FFOI;
424         } else if (s == "LFOI") {
425                 return LFOI;
426         } else if (s == "FFEC") {
427                 return FFEC;
428         } else if (s == "LFEC") {
429                 return LFEC;
430         } else if (s == "FFMC") {
431                 return FFMC;
432         } else if (s == "LFMC") {
433                 return LFMC;
434         }
435
436         DCP_ASSERT (false);
437 }
438
439 Rating::Rating (cxml::ConstNodePtr node)
440 {
441         agency = node->string_child("Agency");
442         label = node->string_child("Label");
443         node->done ();
444 }
445
446 void
447 Rating::as_xml (xmlpp::Element* parent) const
448 {
449         parent->add_child("Agency")->add_child_text(agency);
450         parent->add_child("Label")->add_child_text(label);
451 }
452
453 bool
454 dcp::operator== (Rating const & a, Rating const & b)
455 {
456         return a.agency == b.agency && a.label == b.label;
457 }
458
459 ostream &
460 dcp::operator<< (ostream& s, Rating const & r)
461 {
462         s << r.agency << " " << r.label;
463         return s;
464 }