summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2014-01-28 21:12:10 +0000
committerCarl Hetherington <cth@carlh.net>2014-01-28 21:12:10 +0000
commit7f20aa518356f188946eb508239caf7c113da819 (patch)
tree8647f9b6e6f87415cfd38e47365b14cf3e6df30f /src
First version.
Diffstat (limited to 'src')
-rw-r--r--src/compose.hpp393
-rw-r--r--src/reader.cc33
-rw-r--r--src/reader.h38
-rw-r--r--src/stl_reader.cc165
-rw-r--r--src/stl_reader.h38
-rw-r--r--src/sub_time.cc37
-rw-r--r--src/sub_time.h59
-rw-r--r--src/subtitle.h50
-rw-r--r--src/wscript26
9 files changed, 839 insertions, 0 deletions
diff --git a/src/compose.hpp b/src/compose.hpp
new file mode 100644
index 0000000..b3f410c
--- /dev/null
+++ b/src/compose.hpp
@@ -0,0 +1,393 @@
+/* Defines String::compose(fmt, arg...) for easy, i18n-friendly
+ * composition of strings.
+ *
+ * Version 1.0.
+ *
+ * Copyright (c) 2002 Ole Laursen <olau@hardworking.dk>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA.
+ */
+
+//
+// Basic usage is like
+//
+// std::cout << String::compose("This is a %1x%2 matrix.", rows, cols);
+//
+// See http://www.cs.aau.dk/~olau/compose/ or the included README.compose for
+// more details.
+//
+
+#ifndef STRING_COMPOSE_H
+#define STRING_COMPOSE_H
+
+#include <sstream>
+#include <string>
+#include <list>
+#include <map> // for multimap
+
+namespace StringPrivate
+{
+ // the actual composition class - using string::compose is cleaner, so we
+ // hide it here
+ class Composition
+ {
+ public:
+ // initialize and prepare format string on the form "text %1 text %2 etc."
+ explicit Composition(std::string fmt);
+
+ // supply an replacement argument starting from %1
+ template <typename T>
+ Composition &arg(const T &obj);
+
+ // compose and return string
+ std::string str() const;
+
+ private:
+ std::ostringstream os;
+ int arg_no;
+
+ // we store the output as a list - when the output string is requested, the
+ // list is concatenated to a string; this way we can keep iterators into
+ // the list instead of into a string where they're possibly invalidated on
+ // inserting a specification string
+ typedef std::list<std::string> output_list;
+ output_list output;
+
+ // the initial parse of the format string fills in the specification map
+ // with positions for each of the various %?s
+ typedef std::multimap<int, output_list::iterator> specification_map;
+ specification_map specs;
+ };
+
+ // helper for converting spec string numbers
+ inline int char_to_int(char c)
+ {
+ switch (c) {
+ case '0': return 0;
+ case '1': return 1;
+ case '2': return 2;
+ case '3': return 3;
+ case '4': return 4;
+ case '5': return 5;
+ case '6': return 6;
+ case '7': return 7;
+ case '8': return 8;
+ case '9': return 9;
+ default: return -1000;
+ }
+ }
+
+ inline bool is_number(int n)
+ {
+ switch (n) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+
+ // implementation of class Composition
+ template <typename T>
+ inline Composition &Composition::arg(const T &obj)
+ {
+ os << obj;
+
+ std::string rep = os.str();
+
+ if (!rep.empty()) { // manipulators don't produce output
+ for (specification_map::const_iterator i = specs.lower_bound(arg_no),
+ end = specs.upper_bound(arg_no); i != end; ++i) {
+ output_list::iterator pos = i->second;
+ ++pos;
+
+ output.insert(pos, rep);
+ }
+
+ os.str(std::string());
+ //os.clear();
+ ++arg_no;
+ }
+
+ return *this;
+ }
+
+ inline Composition::Composition(std::string fmt)
+ : arg_no(1)
+ {
+ std::string::size_type b = 0, i = 0;
+
+ // fill in output with the strings between the %1 %2 %3 etc. and
+ // fill in specs with the positions
+ while (i < fmt.length()) {
+ if (fmt[i] == '%' && i + 1 < fmt.length()) {
+ if (fmt[i + 1] == '%') { // catch %%
+ fmt.replace(i, 2, "%");
+ ++i;
+ }
+ else if (is_number(fmt[i + 1])) { // aha! a spec!
+ // save string
+ output.push_back(fmt.substr(b, i - b));
+
+ int n = 1; // number of digits
+ int spec_no = 0;
+
+ do {
+ spec_no += char_to_int(fmt[i + n]);
+ spec_no *= 10;
+ ++n;
+ } while (i + n < fmt.length() && is_number(fmt[i + n]));
+
+ spec_no /= 10;
+ output_list::iterator pos = output.end();
+ --pos; // safe since we have just inserted a string>
+
+ specs.insert(specification_map::value_type(spec_no, pos));
+
+ // jump over spec string
+ i += n;
+ b = i;
+ }
+ else
+ ++i;
+ }
+ else
+ ++i;
+ }
+
+ if (i - b > 0) // add the rest of the string
+ output.push_back(fmt.substr(b, i - b));
+ }
+
+ inline std::string Composition::str() const
+ {
+ // assemble string
+ std::string str;
+
+ for (output_list::const_iterator i = output.begin(), end = output.end();
+ i != end; ++i)
+ str += *i;
+
+ return str;
+ }
+}
+
+// now for the real thing(s)
+namespace String
+{
+ // a series of functions which accept a format string on the form "text %1
+ // more %2 less %3" and a number of templated parameters and spits out the
+ // composited string
+ template <typename T1>
+ inline std::string compose(const std::string &fmt, const T1 &o1)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1);
+ return c.str();
+ }
+
+ template <typename T1, typename T2>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8, const T9 &o9)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8, const T9 &o9,
+ const T10 &o10)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
+ .arg(o10);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8, const T9 &o9,
+ const T10 &o10, const T11 &o11)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
+ .arg(o10).arg(o11);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8, const T9 &o9,
+ const T10 &o10, const T11 &o11, const T12 &o12)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
+ .arg(o10).arg(o11).arg(o12);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8, const T9 &o9,
+ const T10 &o10, const T11 &o11, const T12 &o12,
+ const T13 &o13)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
+ .arg(o10).arg(o11).arg(o12).arg(o13);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8, const T9 &o9,
+ const T10 &o10, const T11 &o11, const T12 &o12,
+ const T13 &o13, const T14 &o14)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
+ .arg(o10).arg(o11).arg(o12).arg(o13).arg(o14);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14,
+ typename T15>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8, const T9 &o9,
+ const T10 &o10, const T11 &o11, const T12 &o12,
+ const T13 &o13, const T14 &o14, const T15 &o15)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
+ .arg(o10).arg(o11).arg(o12).arg(o13).arg(o14).arg(o15);
+ return c.str();
+ }
+}
+
+
+#endif // STRING_COMPOSE_H
diff --git a/src/reader.cc b/src/reader.cc
new file mode 100644
index 0000000..f83745a
--- /dev/null
+++ b/src/reader.cc
@@ -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 "reader.h"
+#include <string>
+#include <iostream>
+
+using std::string;
+using std::cout;
+using namespace sub;
+
+void
+Reader::warn (string m) const
+{
+ /* XXX */
+ cout << m << "\n";
+}
diff --git a/src/reader.h b/src/reader.h
new file mode 100644
index 0000000..e5f142f
--- /dev/null
+++ b/src/reader.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.
+
+*/
+
+#include "subtitle.h"
+#include <list>
+
+namespace sub {
+
+class Reader
+{
+public:
+ std::list<Subtitle> subtitles () const {
+ return _subs;
+ }
+
+protected:
+ void warn (std::string) const;
+
+ std::list<Subtitle> _subs;
+};
+
+}
diff --git a/src/stl_reader.cc b/src/stl_reader.cc
new file mode 100644
index 0000000..c86d607
--- /dev/null
+++ b/src/stl_reader.cc
@@ -0,0 +1,165 @@
+/*
+ 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 "stl_reader.h"
+#include "compose.hpp"
+#include <boost/algorithm/string.hpp>
+#include <boost/lexical_cast.hpp>
+#include <vector>
+
+using std::list;
+using std::ostream;
+using std::istream;
+using std::string;
+using std::vector;
+using std::cout;
+using boost::algorithm::trim;
+using boost::algorithm::starts_with;
+using boost::is_any_of;
+using boost::optional;
+using boost::lexical_cast;
+using namespace sub;
+
+STLReader::STLReader (istream& in)
+{
+ while (in.good ()) {
+ string line;
+ getline (in, line);
+ if (!in.good ()) {
+ return;
+ }
+
+ trim (line);
+
+ if (starts_with (line, "//")) {
+ continue;
+ }
+
+ if (line.size() > 0 && line[0] == '$') {
+ /* $ variables */
+ vector<string> bits;
+ split (bits, line, is_any_of ("="));
+ if (bits.size() == 2) {
+ string name = bits[0];
+ trim (name);
+ string value = bits[1];
+ trim (value);
+
+ set (name, value);
+ } else {
+ warn (String::compose ("Unrecognised line %1", line));
+ }
+ } else {
+ /* "Normal" lines */
+ size_t divider[2];
+ divider[0] = line.find_first_of (",");
+ if (divider[0] != string::npos) {
+ divider[1] = line.find_first_of (",", divider[0] + 1);
+ }
+
+ if (divider[0] == string::npos || divider[1] == string::npos || divider[0] <= 1 || divider[1] >= line.length() - 1) {
+ warn (String::compose ("Unrecognised line %1", line));
+ continue;
+ }
+
+ string from_string = line.substr (0, divider[0] - 1);
+ trim (from_string);
+ string to_string = line.substr (divider[0] + 1, divider[1] - divider[0] - 1);
+ trim (to_string);
+
+ optional<Time> from = time (from_string);
+ optional<Time> to = time (to_string);
+
+ if (!from || !to) {
+ warn (String::compose ("Unrecognised line %1", line));
+ continue;
+ }
+
+ _current.from = from.get ();
+ _current.to = to.get ();
+
+ /* Parse ^B/^I/^U */
+ string text = line.substr (divider[1] + 1);
+ for (size_t i = 0; i < text.length(); ++i) {
+ if (text[i] == '|') {
+ maybe_push ();
+ } else if (text[i] == '^') {
+ maybe_push ();
+ if ((i + 1) < text.length()) {
+ switch (text[i + 1]) {
+ case 'B':
+ _current.bold = !_current.bold;
+ break;
+ case 'I':
+ _current.italic = !_current.italic;
+ break;
+ case 'U':
+ _current.underline = !_current.underline;
+ break;
+ }
+ }
+ ++i;
+ } else {
+ _current.text += text[i];
+ }
+ }
+
+ maybe_push ();
+ }
+ }
+}
+
+optional<Time>
+STLReader::time (string t) const
+{
+ vector<string> b;
+ split (b, t, is_any_of (":"));
+ if (b.size() != 4) {
+ warn (String::compose ("Unrecognised time %1", t));
+ return optional<Time> ();
+ }
+
+ return Time (lexical_cast<int> (b[0]), lexical_cast<int> (b[1]), lexical_cast<int> (b[2]), lexical_cast<int> (b[3]));
+}
+
+void
+STLReader::set (string name, string value)
+{
+ if (name == "FontName") {
+ _current.font = value;
+ } else if (name == "Bold") {
+ _current.bold = value == "True";
+ } else if (name == "Italic") {
+ _current.italic = value == "True";
+ } else if (name == "Underlined") {
+ _current.underline = value == "True";
+ } else if (name == "FontSize") {
+ _current.font_size = lexical_cast<int> (value);
+ }
+}
+
+void
+STLReader::maybe_push ()
+{
+ if (!_current.text.empty ()) {
+ _subs.push_back (_current);
+ _current.text.clear ();
+ }
+}
+
diff --git a/src/stl_reader.h b/src/stl_reader.h
new file mode 100644
index 0000000..307992a
--- /dev/null
+++ b/src/stl_reader.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.
+
+*/
+
+#include "reader.h"
+#include <boost/optional.hpp>
+
+namespace sub {
+
+class STLReader : public Reader
+{
+public:
+ STLReader (std::istream &);
+
+private:
+ void set (std::string name, std::string value);
+ void maybe_push ();
+ boost::optional<Time> time (std::string t) const;
+
+ Subtitle _current;
+};
+
+}
diff --git a/src/sub_time.cc b/src/sub_time.cc
new file mode 100644
index 0000000..6de2456
--- /dev/null
+++ b/src/sub_time.cc
@@ -0,0 +1,37 @@
+/*
+ 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 "sub_time.h"
+#include <iostream>
+
+using std::ostream;
+using namespace sub;
+
+bool
+sub::operator== (Time const & a, Time const & b)
+{
+ return a._hours == b._hours && a._minutes == b._minutes && a._seconds == b._seconds && a._frames == b._frames;
+}
+
+ostream&
+sub::operator<< (ostream& s, Time const & t)
+{
+ s << t._hours << ":" << t._minutes << ":" << t._seconds << ":" << t._frames;
+ return s;
+}
diff --git a/src/sub_time.h b/src/sub_time.h
new file mode 100644
index 0000000..ee14745
--- /dev/null
+++ b/src/sub_time.h
@@ -0,0 +1,59 @@
+/*
+ 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_TIME_H
+#define LIBSUB_TIME_H
+
+#include <iostream>
+
+namespace sub {
+
+class Time
+{
+public:
+ Time ()
+ : _hours (0)
+ , _minutes (0)
+ , _seconds (0)
+ , _frames (0)
+ {}
+
+ Time (int h, int m, int s, int f)
+ : _hours (h)
+ , _minutes (m)
+ , _seconds (s)
+ , _frames (f)
+ {}
+
+private:
+ friend bool operator== (Time const & a, Time const & b);
+ friend std::ostream& operator<< (std::ostream& s, Time const & t);
+
+ int _hours;
+ int _minutes;
+ int _seconds;
+ int _frames;
+};
+
+bool operator== (Time const & a, Time const & b);
+std::ostream& operator<< (std::ostream&, Time const & t);
+
+}
+
+#endif
diff --git a/src/subtitle.h b/src/subtitle.h
new file mode 100644
index 0000000..5cc1284
--- /dev/null
+++ b/src/subtitle.h
@@ -0,0 +1,50 @@
+/*
+ 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_SUBTITLE_H
+#define LIBSUB_SUBTITLE_H
+
+#include "sub_time.h"
+#include <string>
+
+namespace sub {
+
+class Subtitle
+{
+public:
+ Subtitle ()
+ : font_size (0)
+ , bold (false)
+ , italic (false)
+ , underline (false)
+ {}
+
+ std::string text;
+ std::string font;
+ int font_size;
+ bool bold;
+ bool italic;
+ bool underline;
+ Time from;
+ Time to;
+};
+
+}
+
+#endif
diff --git a/src/wscript b/src/wscript
new file mode 100644
index 0000000..b7b53b0
--- /dev/null
+++ b/src/wscript
@@ -0,0 +1,26 @@
+from waflib import TaskGen
+
+def build(bld):
+ if bld.env.STATIC:
+ obj = bld(features='cxx cxxstlib')
+ else:
+ obj = bld(features='cxx cxxshlib')
+
+ obj.name = 'libsub'
+ obj.target = 'sub'
+ obj.export_includes = ['.']
+ obj.source = """
+ reader.cc
+ stl_reader.cc
+ sub_time.cc
+ """
+
+ headers = """
+ reader.h
+ stl_reader.h
+ sub_time.h
+ """
+
+ bld.install_files('${PREFIX}/include/libsub', headers)
+ if bld.env.STATIC:
+ bld.install_files('${PREFIX}/lib', 'libsub.a')