summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2014-01-29 16:42:55 +0000
committerCarl Hetherington <cth@carlh.net>2014-01-29 16:42:55 +0000
commitd0869653a0bdfa010da0b1b00f83ea89f3abbdb1 (patch)
tree45ba336b5396875731c1acbe9be97d80e84a7958 /src
parentc8c3db36a4593e396681b4acd5e9d318a28b1648 (diff)
Various developments.
Diffstat (limited to 'src')
-rw-r--r--src/colour.cc45
-rw-r--r--src/colour.h52
-rw-r--r--src/dcp_reader.cc343
-rw-r--r--src/dcp_reader.h66
-rw-r--r--src/effect.cc40
-rw-r--r--src/effect.h33
-rw-r--r--src/exceptions.h41
-rw-r--r--src/frame_time.cc18
-rw-r--r--src/frame_time.h2
-rw-r--r--src/metric_time.cc65
-rw-r--r--src/metric_time.h53
-rw-r--r--src/stl_reader.cc6
-rw-r--r--src/stl_writer.cc14
-rw-r--r--src/subtitle.cc36
-rw-r--r--src/subtitle.h75
-rw-r--r--src/vertical_reference.cc39
-rw-r--r--src/vertical_reference.h38
-rw-r--r--src/wscript10
-rw-r--r--src/xml.h91
19 files changed, 1028 insertions, 39 deletions
diff --git a/src/colour.cc b/src/colour.cc
new file mode 100644
index 0000000..f163424
--- /dev/null
+++ b/src/colour.cc
@@ -0,0 +1,45 @@
+/*
+ Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "colour.h"
+#include "exceptions.h"
+#include <string>
+#include <cstdio>
+
+using std::string;
+using namespace sub;
+
+Colour::Colour (string argb_hex)
+{
+ int alpha, ir, ig, ib;
+ if (sscanf (argb_hex.c_str(), "%2x%2x%2x%2x", &alpha, &ir, &ig, &ib) < 4) {
+ throw XMLError ("could not parse colour string");
+ }
+
+ r = float (ir) / 255;
+ g = float (ig) / 255;
+ b = float (ib) / 255;
+}
+
+bool
+sub::operator== (Colour const & a, Colour const & b)
+{
+ return a.r == b.r && a.g == b.g && a.b == b.b;
+}
+
diff --git a/src/colour.h b/src/colour.h
new file mode 100644
index 0000000..d6ca267
--- /dev/null
+++ b/src/colour.h
@@ -0,0 +1,52 @@
+/*
+ Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <string>
+
+namespace sub {
+
+class Colour
+{
+public:
+ Colour ()
+ : r (0)
+ , g (0)
+ , b (0)
+ {}
+
+ Colour (float r, float g, float b)
+ : r (r)
+ , g (g)
+ , b (b)
+ {}
+
+ Colour (std::string);
+
+ /** red component (from 0 to 1) */
+ float r;
+ /** green component (from 0 to 1) */
+ float g;
+ /** blue component (from 0 to 1) */
+ float b;
+};
+
+bool
+operator== (Colour const & a, Colour const & b);
+
+}
diff --git a/src/dcp_reader.cc b/src/dcp_reader.cc
new file mode 100644
index 0000000..dd40c97
--- /dev/null
+++ b/src/dcp_reader.cc
@@ -0,0 +1,343 @@
+/*
+ Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "dcp_reader.h"
+#include "vertical_reference.h"
+#include "xml.h"
+#include <libcxml/cxml.h>
+#include <boost/algorithm/string.hpp>
+#include <boost/lexical_cast.hpp>
+
+using std::string;
+using std::list;
+using std::vector;
+using std::istream;
+using std::cout;
+using boost::shared_ptr;
+using boost::optional;
+using boost::lexical_cast;
+using boost::is_any_of;
+using namespace sub;
+
+namespace sub {
+
+class DCPFont;
+
+class DCPText
+{
+public:
+ DCPText ()
+ : v_position (0)
+ , v_align (TOP)
+ {}
+
+ DCPText (shared_ptr<const cxml::Node> node)
+ : v_align (CENTRE)
+ {
+ text = node->content ();
+ v_position = node->number_attribute<float> ("VPosition");
+ optional<string> v = node->optional_string_attribute ("VAlign");
+ if (v) {
+ v_align = string_to_vertical_reference (v.get ());
+ }
+
+ font_nodes = type_children<DCPFont> (node, "Font");
+ }
+
+ float v_position;
+ VerticalReference v_align;
+ string text;
+ shared_ptr<DCPFont> foo;
+ list<shared_ptr<DCPFont> > font_nodes;
+};
+
+class DCPSubtitle
+{
+public:
+ DCPSubtitle () {}
+ DCPSubtitle (shared_ptr<const cxml::Node> node)
+ {
+ in = MetricTime (time (node->string_attribute ("TimeIn")));
+ out = MetricTime (time (node->string_attribute ("TimeOut")));
+ font_nodes = type_children<DCPFont> (node, "Font");
+ text_nodes = type_children<DCPText> (node, "Text");
+ fade_up_time = fade_time (node, "FadeUpTime");
+ fade_down_time = fade_time (node, "FadeDownTime");
+ }
+
+ MetricTime in;
+ MetricTime out;
+ MetricTime fade_up_time;
+ MetricTime fade_down_time;
+ list<shared_ptr<DCPFont> > font_nodes;
+ list<shared_ptr<DCPText> > text_nodes;
+
+private:
+ static MetricTime time (std::string time)
+ {
+ vector<string> b;
+ split (b, time, is_any_of (":"));
+ if (b.size() != 4) {
+ boost::throw_exception (XMLError ("unrecognised time specification"));
+ }
+
+ return MetricTime (lexical_cast<int>(b[0]), lexical_cast<int> (b[1]), lexical_cast<int> (b[2]), lexical_cast<int> (b[3]) * 4);
+ }
+
+ MetricTime fade_time (shared_ptr<const cxml::Node> node, string name)
+ {
+ string const u = node->optional_string_attribute (name).get_value_or ("");
+ MetricTime t;
+
+ if (u.empty ()) {
+ t = MetricTime (0, 0, 0, 80);
+ } else if (u.find (":") != string::npos) {
+ t = time (u);
+ } else {
+ t = MetricTime (0, 0, 0, lexical_cast<int>(u) * 4);
+ }
+
+ if (t > MetricTime (0, 0, 8, 0)) {
+ t = MetricTime (0, 0, 8, 0);
+ }
+
+ return t;
+ }
+};
+
+class DCPFont
+{
+public:
+ DCPFont ()
+ : size (0)
+ {}
+
+ DCPFont (shared_ptr<const cxml::Node> node)
+ {
+ text = node->content ();
+
+ id = node->optional_string_attribute ("Id").get_value_or ("");
+ size = node->optional_number_attribute<int64_t> ("Size").get_value_or (0);
+ italic = node->optional_bool_attribute ("Italic");
+ optional<string> c = node->optional_string_attribute ("Color");
+ if (c) {
+ colour = Colour (c.get ());
+ }
+ optional<string> const e = node->optional_string_attribute ("Effect");
+ if (e) {
+ effect = string_to_effect (e.get ());
+ }
+ c = node->optional_string_attribute ( "EffectColor");
+ if (c) {
+ effect_colour = Colour (c.get ());
+ }
+ subtitle_nodes = type_children<DCPSubtitle> (node, "Subtitle");
+ font_nodes = type_children<DCPFont> (node, "Font");
+ text_nodes = type_children<DCPText> (node, "Text");
+ }
+
+ DCPFont (list<shared_ptr<DCPFont> > const & font_nodes)
+ : size (0)
+ , italic (false)
+ , colour ("FFFFFFFF")
+ , effect_colour ("FFFFFFFF")
+ {
+ for (list<shared_ptr<DCPFont> >::const_iterator i = font_nodes.begin(); i != font_nodes.end(); ++i) {
+ if (!(*i)->id.empty ()) {
+ id = (*i)->id;
+ }
+ if ((*i)->size != 0) {
+ size = (*i)->size;
+ }
+ if ((*i)->italic) {
+ italic = (*i)->italic.get ();
+ }
+ if ((*i)->colour) {
+ colour = (*i)->colour.get ();
+ }
+ if ((*i)->effect) {
+ effect = (*i)->effect.get ();
+ }
+ if ((*i)->effect_colour) {
+ effect_colour = (*i)->effect_colour.get ();
+ }
+ }
+ }
+
+ string text;
+ string id;
+ int size;
+ optional<bool> italic;
+ optional<Colour> colour;
+ optional<Effect> effect;
+ optional<Colour> effect_colour;
+
+ list<shared_ptr<DCPSubtitle> > subtitle_nodes;
+ list<shared_ptr<DCPFont> > font_nodes;
+ list<shared_ptr<DCPText> > text_nodes;
+};
+
+class DCPLoadFont
+{
+public:
+ DCPLoadFont () {}
+ DCPLoadFont (shared_ptr<const cxml::Node> node)
+ {
+ id = node->string_attribute ("Id");
+ uri = node->string_attribute ("URI");
+ }
+
+ string id;
+ string uri;
+};
+
+}
+
+/** @param s A string.
+ * @return true if the string contains only space, newline or tab characters, or is empty.
+ */
+static bool
+empty_or_white_space (string s)
+{
+ for (size_t i = 0; i < s.length(); ++i) {
+ if (s[i] != ' ' && s[i] != '\n' && s[i] != '\t') {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+string
+DCPReader::font_id_to_name (string id) const
+{
+ list<shared_ptr<DCPLoadFont> >::const_iterator i = _load_font_nodes.begin();
+ while (i != _load_font_nodes.end() && (*i)->id != id) {
+ ++i;
+ }
+
+ if (i == _load_font_nodes.end ()) {
+ return "";
+ }
+
+ if ((*i)->uri == "arial.ttf") {
+ return "Arial";
+ }
+
+ return "";
+}
+
+DCPReader::DCPReader (istream& in)
+{
+ shared_ptr<cxml::Document> xml (new cxml::Document ("DCSubtitle"));
+ xml->read_stream (in);
+
+ xml->ignore_child ("SubtitleID");
+ xml->ignore_child ("MovieTitle");
+ xml->ignore_child ("ReelNumber");
+ xml->ignore_child ("Language");
+
+ list<shared_ptr<DCPFont> > font_nodes = type_children<DCPFont> (xml, "Font");
+ _load_font_nodes = type_children<DCPLoadFont> (xml, "LoadFont");
+
+ /* Now make Subtitle objects to represent the raw XML nodes
+ in a sane way.
+ */
+
+ ParseState parse_state;
+ examine_font_nodes (xml, font_nodes, parse_state);
+
+ _subs.sort ();
+}
+
+void
+DCPReader::examine_font_nodes (
+ shared_ptr<const cxml::Node> xml,
+ list<shared_ptr<DCPFont> > const & font_nodes,
+ ParseState& parse_state
+ )
+{
+ for (list<shared_ptr<DCPFont> >::const_iterator i = font_nodes.begin(); i != font_nodes.end(); ++i) {
+
+ parse_state.font_nodes.push_back (*i);
+ maybe_add_subtitle ((*i)->text, parse_state);
+
+ for (list<shared_ptr<DCPSubtitle> >::iterator j = (*i)->subtitle_nodes.begin(); j != (*i)->subtitle_nodes.end(); ++j) {
+ parse_state.subtitle_nodes.push_back (*j);
+ examine_text_nodes (xml, (*j)->text_nodes, parse_state);
+ examine_font_nodes (xml, (*j)->font_nodes, parse_state);
+ parse_state.subtitle_nodes.pop_back ();
+ }
+
+ examine_font_nodes (xml, (*i)->font_nodes, parse_state);
+ examine_text_nodes (xml, (*i)->text_nodes, parse_state);
+
+ parse_state.font_nodes.pop_back ();
+ }
+}
+
+void
+DCPReader::examine_text_nodes (
+ shared_ptr<const cxml::Node> xml,
+ list<shared_ptr<DCPText> > const & text_nodes,
+ ParseState& parse_state
+ )
+{
+ for (list<shared_ptr<DCPText> >::const_iterator i = text_nodes.begin(); i != text_nodes.end(); ++i) {
+ parse_state.text_nodes.push_back (*i);
+ maybe_add_subtitle ((*i)->text, parse_state);
+ examine_font_nodes (xml, (*i)->font_nodes, parse_state);
+ parse_state.text_nodes.pop_back ();
+ }
+}
+
+void
+DCPReader::maybe_add_subtitle (string text, ParseState const & parse_state)
+{
+ if (empty_or_white_space (text)) {
+ return;
+ }
+
+ if (parse_state.text_nodes.empty() || parse_state.subtitle_nodes.empty ()) {
+ return;
+ }
+
+ assert (!parse_state.text_nodes.empty ());
+ assert (!parse_state.subtitle_nodes.empty ());
+
+ DCPFont effective_font (parse_state.font_nodes);
+ DCPText effective_text (*parse_state.text_nodes.back ());
+ DCPSubtitle effective_subtitle (*parse_state.subtitle_nodes.back ());
+
+ Subtitle s;
+ s.text = text;
+ s.font = font_id_to_name (effective_font.id);
+ s.font_size.proportional = float (effective_font.size) / (72 * 11);
+ s.vertical_position.proportional = float (effective_text.v_position) / 100;
+ s.vertical_position.reference = effective_text.v_align;
+ s.effect = effective_font.effect;
+ s.effect_colour = effective_font.effect_colour;
+ s.colour = effective_font.colour.get ();
+ s.italic = effective_font.italic.get ();
+ s.from.metric = effective_subtitle.in;
+ s.to.metric = effective_subtitle.out;
+ s.fade_up = effective_subtitle.fade_up_time;
+ s.fade_down = effective_subtitle.fade_down_time;
+ _subs.push_back (s);
+}
diff --git a/src/dcp_reader.h b/src/dcp_reader.h
new file mode 100644
index 0000000..51ada1a
--- /dev/null
+++ b/src/dcp_reader.h
@@ -0,0 +1,66 @@
+/*
+ Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "reader.h"
+#include <boost/shared_ptr.hpp>
+
+namespace cxml {
+ class Node;
+}
+
+namespace sub {
+
+class DCPFont;
+class DCPText;
+class DCPSubtitle;
+class DCPLoadFont;
+
+class DCPReader : public Reader
+{
+public:
+ DCPReader (std::istream &);
+
+private:
+
+ struct ParseState {
+ std::list<boost::shared_ptr<DCPFont> > font_nodes;
+ std::list<boost::shared_ptr<DCPText> > text_nodes;
+ std::list<boost::shared_ptr<DCPSubtitle> > subtitle_nodes;
+ };
+
+ void maybe_add_subtitle (std::string text, ParseState const & parse_state);
+
+ void examine_font_nodes (
+ boost::shared_ptr<const cxml::Node> xml,
+ std::list<boost::shared_ptr<DCPFont> > const & font_nodes,
+ ParseState& parse_state
+ );
+
+ void examine_text_nodes (
+ boost::shared_ptr<const cxml::Node> xml,
+ std::list<boost::shared_ptr<DCPText> > const & text_nodes,
+ ParseState& parse_state
+ );
+
+ std::string font_id_to_name (std::string id) const;
+
+ std::list<boost::shared_ptr<DCPLoadFont> > _load_font_nodes;
+};
+
+}
diff --git a/src/effect.cc b/src/effect.cc
new file mode 100644
index 0000000..57eb863
--- /dev/null
+++ b/src/effect.cc
@@ -0,0 +1,40 @@
+/*
+ Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "effect.h"
+#include "exceptions.h"
+#include <boost/optional.hpp>
+
+using std::string;
+using boost::optional;
+using namespace sub;
+
+optional<Effect>
+sub::string_to_effect (string s)
+{
+ if (s == "none") {
+ return optional<Effect> ();
+ } else if (s == "border") {
+ return BORDER;
+ } else if (s == "shadow") {
+ return SHADOW;
+ }
+
+ throw XMLError ("unknown subtitle effect type");
+}
diff --git a/src/effect.h b/src/effect.h
new file mode 100644
index 0000000..41534a5
--- /dev/null
+++ b/src/effect.h
@@ -0,0 +1,33 @@
+/*
+ Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <boost/optional.hpp>
+#include <string>
+
+namespace sub {
+
+enum Effect
+{
+ BORDER,
+ SHADOW
+};
+
+boost::optional<Effect> string_to_effect (std::string s);
+
+}
diff --git a/src/exceptions.h b/src/exceptions.h
new file mode 100644
index 0000000..9bab16c
--- /dev/null
+++ b/src/exceptions.h
@@ -0,0 +1,41 @@
+/*
+ Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <stdexcept>
+#include <string>
+
+namespace sub {
+
+class XMLError : public std::exception
+{
+public:
+ XMLError (std::string const & message) : _message (message) {}
+ ~XMLError () throw () {}
+
+ /** @return error message */
+ char const * what () const throw () {
+ return _message.c_str ();
+ }
+
+private:
+ /** error message */
+ std::string _message;
+};
+
+}
diff --git a/src/frame_time.cc b/src/frame_time.cc
index 0bf63ad..726c552 100644
--- a/src/frame_time.cc
+++ b/src/frame_time.cc
@@ -31,6 +31,24 @@ sub::operator== (FrameTime const & a, FrameTime const & b)
return a._hours == b._hours && a._minutes == b._minutes && a._seconds == b._seconds && a._frames == b._frames;
}
+bool
+sub::operator< (FrameTime const & a, FrameTime const & b)
+{
+ if (a._hours != b._hours) {
+ return a._hours < b._hours;
+ }
+
+ if (a._minutes != b._minutes) {
+ return a._minutes < b._minutes;
+ }
+
+ if (a._seconds != b._seconds) {
+ return a._seconds < b._seconds;
+ }
+
+ return a._frames < b._frames;
+}
+
ostream&
sub::operator<< (ostream& s, FrameTime const & t)
{
diff --git a/src/frame_time.h b/src/frame_time.h
index 891fbe1..045b833 100644
--- a/src/frame_time.h
+++ b/src/frame_time.h
@@ -45,6 +45,7 @@ public:
private:
friend bool operator== (FrameTime const & a, FrameTime const & b);
+ friend bool operator< (FrameTime const & a, FrameTime const & b);
friend std::ostream& operator<< (std::ostream& s, FrameTime const & t);
int _hours;
@@ -54,6 +55,7 @@ private:
};
bool operator== (FrameTime const & a, FrameTime const & b);
+bool operator< (FrameTime const & a, FrameTime const & b);
std::ostream& operator<< (std::ostream&, FrameTime const & t);
}
diff --git a/src/metric_time.cc b/src/metric_time.cc
new file mode 100644
index 0000000..3fc7874
--- /dev/null
+++ b/src/metric_time.cc
@@ -0,0 +1,65 @@
+/*
+ Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "metric_time.h"
+#include "compose.hpp"
+#include <iostream>
+
+using std::ostream;
+using std::string;
+using namespace sub;
+
+MetricTime::MetricTime (int h, int m, int s, int ms)
+ : _milliseconds ((h * 3600 + m * 60 + s) * 1000 + ms)
+{
+
+}
+
+bool
+sub::operator== (MetricTime const & a, MetricTime const & b)
+{
+ return a._milliseconds == b._milliseconds;
+}
+
+bool
+sub::operator> (MetricTime const & a, MetricTime const & b)
+{
+ return a._milliseconds > b._milliseconds;
+}
+
+bool
+sub::operator< (MetricTime const & a, MetricTime const & b)
+{
+ return a._milliseconds < b._milliseconds;
+}
+
+ostream&
+sub::operator<< (ostream& st, MetricTime const & t)
+{
+ int64_t ms = t._milliseconds;
+ int const h = ms / (3600 * 1000);
+ ms -= h * 3600 * 1000;
+ int const m = ms / (60 * 1000);
+ ms -= m * 60 * 1000;
+ int const s = ms / 1000;
+ ms -= s * 1000;
+
+ st << h << ":" << m << ":" << s << ":" << ms;
+ return st;
+}
diff --git a/src/metric_time.h b/src/metric_time.h
new file mode 100644
index 0000000..5a35176
--- /dev/null
+++ b/src/metric_time.h
@@ -0,0 +1,53 @@
+/*
+ Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef LIBSUB_METRIC_TIME_H
+#define LIBSUB_METRIC_TIME_H
+
+#include <stdint.h>
+#include <iostream>
+
+namespace sub {
+
+class MetricTime
+{
+public:
+ MetricTime ()
+ : _milliseconds (0)
+ {}
+
+ MetricTime (int h, int m, int s, int ms);
+
+private:
+ friend bool operator== (MetricTime const & a, MetricTime const & b);
+ friend bool operator> (MetricTime const & a, MetricTime const & b);
+ friend bool operator< (MetricTime const & a, MetricTime const & b);
+ friend std::ostream& operator<< (std::ostream&, MetricTime const & t);
+
+ int64_t _milliseconds;
+};
+
+bool operator== (MetricTime const & a, MetricTime const & b);
+bool operator> (MetricTime const & a, MetricTime const & b);
+bool operator< (MetricTime const & a, MetricTime const & b);
+std::ostream& operator<< (std::ostream&, MetricTime const & t);
+
+}
+
+#endif
diff --git a/src/stl_reader.cc b/src/stl_reader.cc
index dcc9070..0ed66c0 100644
--- a/src/stl_reader.cc
+++ b/src/stl_reader.cc
@@ -91,8 +91,8 @@ STLReader::STLReader (istream& in)
continue;
}
- _current.frame_from = from.get ();
- _current.frame_to = to.get ();
+ _current.from.frame = from.get ();
+ _current.to.frame = to.get ();
/* Parse ^B/^I/^U */
string text = line.substr (divider[1] + 1);
@@ -152,7 +152,7 @@ STLReader::set (string name, string value)
} else if (name == "$Underlined") {
_current.underline = value == "True";
} else if (name == "$FontSize") {
- _current.font_size = lexical_cast<int> (value);
+ _current.font_size.points = lexical_cast<int> (value);
}
}
diff --git a/src/stl_writer.cc b/src/stl_writer.cc
index 977f861..2266732 100644
--- a/src/stl_writer.cc
+++ b/src/stl_writer.cc
@@ -45,9 +45,9 @@ STLWriter::STLWriter (list<Subtitle> subtitles, ostream& out)
font = i->font;
started_new = true;
}
- if (!font_size || font_size.get() != i->font_size) {
- out << "\n$FontSize = " << i->font_size;
- font_size = i->font_size;
+ if (!font_size || font_size.get() != i->font_size.points.get()) {
+ out << "\n$FontSize = " << i->font_size.points.get();
+ font_size = i->font_size.points.get();
started_new = true;
}
string text;
@@ -66,16 +66,16 @@ STLWriter::STLWriter (list<Subtitle> subtitles, ostream& out)
text += i->text;
- if (from && from.get() == i->frame_from && to && to.get() == i->frame_to && !started_new) {
+ if (from && from.get() == i->from.frame.get() && to && to.get() == i->to.frame.get() && !started_new) {
for (int j = line; j < i->line; ++j) {
out << "|";
}
out << text;
line = i->line;
} else {
- out << "\n" << i->frame_from.get().timecode() << "," << i->frame_to.get().timecode() << "," << text;
- from = i->frame_from;
- to = i->frame_to;
+ out << "\n" << i->from.frame.get().timecode() << "," << i->to.frame.get().timecode() << "," << text;
+ from = i->from.frame.get();
+ to = i->to.frame.get();
line = 0;
}
}
diff --git a/src/subtitle.cc b/src/subtitle.cc
new file mode 100644
index 0000000..d637ed9
--- /dev/null
+++ b/src/subtitle.cc
@@ -0,0 +1,36 @@
+/*
+ Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "subtitle.h"
+
+using namespace sub;
+
+bool
+sub::operator< (Subtitle const & a, Subtitle const & b)
+{
+ if (a.from.frame && b.from.frame) {
+ return a.from.frame.get() < b.from.frame.get();
+ }
+
+ if (a.from.metric && b.from.metric) {
+ return a.from.metric.get() < b.from.metric.get();
+ }
+
+ assert (false);
+}
diff --git a/src/subtitle.h b/src/subtitle.h
index 3afd0f3..b60b7f7 100644
--- a/src/subtitle.h
+++ b/src/subtitle.h
@@ -21,6 +21,10 @@
#define LIBSUB_SUBTITLE_H
#include "frame_time.h"
+#include "metric_time.h"
+#include "colour.h"
+#include "vertical_reference.h"
+#include "effect.h"
#include <boost/optional.hpp>
#include <string>
@@ -30,46 +34,59 @@ class Subtitle
{
public:
Subtitle ()
- : font_size (0)
+ : colour (1, 1, 1)
, bold (false)
, italic (false)
, underline (false)
, line (0)
{}
- Subtitle (
- std::string text,
- std::string font,
- int font_size,
- bool bold,
- bool italic,
- bool underline,
- int line,
- FrameTime from,
- FrameTime to
- )
- : text (text)
- , font (font)
- , font_size (font_size)
- , bold (bold)
- , italic (italic)
- , underline (underline)
- , line (line)
- , frame_from (from)
- , frame_to (to)
- {}
-
std::string text;
std::string font;
- int font_size;
- bool bold;
- bool italic;
- bool underline;
+
+ /** font size */
+ struct {
+ /** as a proportion of screen height */
+ boost::optional<float> proportional;
+ /** in points */
+ boost::optional<int> points;
+ } font_size;
+
+ /** vertical position of the baseline of the text */
+ struct {
+ /** as a proportion of screen height offset from some reference point */
+ boost::optional<float> proportional;
+ /** reference position for proportional */
+ boost::optional<VerticalReference> reference;
+ } vertical_position;
+
+ boost::optional<Effect> effect;
+ boost::optional<Colour> effect_colour;
+
+ Colour colour;
+ bool bold; ///< true to use a bold version of font
+ bool italic; ///< true to use an italic version of font
+ bool underline; ///< true to underline
int line;
- boost::optional<FrameTime> frame_from;
- boost::optional<FrameTime> frame_to;
+
+ /** from time */
+ struct {
+ boost::optional<FrameTime> frame;
+ boost::optional<MetricTime> metric;
+ } from;
+
+ /** to time */
+ struct {
+ boost::optional<FrameTime> frame;
+ boost::optional<MetricTime> metric;
+ } to;
+
+ boost::optional<MetricTime> fade_up;
+ boost::optional<MetricTime> fade_down;
};
+bool operator< (Subtitle const & a, Subtitle const & b);
+
}
#endif
diff --git a/src/vertical_reference.cc b/src/vertical_reference.cc
new file mode 100644
index 0000000..69f9d84
--- /dev/null
+++ b/src/vertical_reference.cc
@@ -0,0 +1,39 @@
+/*
+ Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "vertical_reference.h"
+#include "exceptions.h"
+#include <string>
+
+using std::string;
+using namespace sub;
+
+VerticalReference
+sub::string_to_vertical_reference (string s)
+{
+ if (s == "top") {
+ return TOP;
+ } else if (s == "center") {
+ return CENTRE;
+ } else if (s == "bottom") {
+ return BOTTOM;
+ }
+
+ throw XMLError ("unknown subtitle valign type");
+}
diff --git a/src/vertical_reference.h b/src/vertical_reference.h
new file mode 100644
index 0000000..52cf840
--- /dev/null
+++ b/src/vertical_reference.h
@@ -0,0 +1,38 @@
+/*
+ Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef LIBSUB_VERTICAL_REFERENCE_H
+#define LIBSUB_VERTICAL_REFERENCE_H
+
+#include <string>
+
+namespace sub {
+
+enum VerticalReference
+{
+ TOP,
+ CENTRE,
+ BOTTOM
+};
+
+VerticalReference string_to_vertical_reference (std::string s);
+
+}
+
+#endif
diff --git a/src/wscript b/src/wscript
index 400eb54..3aa1054 100644
--- a/src/wscript
+++ b/src/wscript
@@ -8,16 +8,26 @@ def build(bld):
obj.name = 'libsub'
obj.target = 'sub'
+ obj.uselib = 'CXML'
obj.export_includes = ['.']
obj.source = """
+ colour.cc
+ dcp_reader.cc
+ effect.cc
frame_time.cc
+ metric_time.cc
reader.cc
stl_reader.cc
stl_writer.cc
+ subtitle.cc
+ vertical_reference.cc
"""
headers = """
+ colour.h
+ dcp_reader.h
frame_time.h
+ metric_time.h
reader.h
stl_reader.h
stl_writer.h
diff --git a/src/xml.h b/src/xml.h
new file mode 100644
index 0000000..568eba5
--- /dev/null
+++ b/src/xml.h
@@ -0,0 +1,91 @@
+/*
+ Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef LIBSUB_XML_H
+#define LIBSUB_XML_H
+
+#include "exceptions.h"
+#include <libcxml/cxml.h>
+
+namespace sub
+{
+
+template <class T>
+boost::shared_ptr<T>
+optional_type_child (cxml::Node const & node, std::string name)
+{
+ std::list<boost::shared_ptr<cxml::Node> > n = node.node_children (name);
+ if (n.size() > 1) {
+ throw XMLError ("duplicate XML tag");
+ } else if (n.empty ()) {
+ return boost::shared_ptr<T> ();
+ }
+
+ return boost::shared_ptr<T> (new T (n.front ()));
+}
+
+template <class T>
+boost::shared_ptr<T> type_child (boost::shared_ptr<const cxml::Node> node, std::string name) {
+ return boost::shared_ptr<T> (new T (node->node_child (name)));
+}
+
+template <class T>
+boost::shared_ptr<T>
+optional_type_child (boost::shared_ptr<const cxml::Node> node, std::string name)
+{
+ return optional_type_child<T> (*node.get(), name);
+}
+
+template <class T>
+std::list<boost::shared_ptr<T> >
+type_children (cxml::Node const & node, std::string name)
+{
+ std::list<boost::shared_ptr<cxml::Node> > n = node.node_children (name);
+ std::list<boost::shared_ptr<T> > r;
+ for (typename std::list<boost::shared_ptr<cxml::Node> >::iterator i = n.begin(); i != n.end(); ++i) {
+ r.push_back (boost::shared_ptr<T> (new T (*i)));
+ }
+ return r;
+}
+
+template <class T>
+std::list<boost::shared_ptr<T> >
+type_children (boost::shared_ptr<const cxml::Node> node, std::string name)
+{
+ return type_children<T> (*node.get(), name);
+}
+
+template <class T>
+std::list<boost::shared_ptr<T> >
+type_grand_children (cxml::Node const & node, std::string name, std::string sub)
+{
+ boost::shared_ptr<const cxml::Node> p = node.node_child (name);
+ return type_children<T> (p, sub);
+}
+
+template <class T>
+std::list<boost::shared_ptr<T> >
+type_grand_children (boost::shared_ptr<const cxml::Node> node, std::string name, std::string sub)
+{
+ return type_grand_children<T> (*node.get(), name, sub);
+}
+
+}
+
+#endif