--- /dev/null
+*~
+build
+.waf-*
+.lock-waf*
+src/version.cc
\ No newline at end of file
--- /dev/null
+prefix=@prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libsub
+Description: Subtitle reading and writing library
+Version: @version@
+Requires:
+Libs: @libs@
+Cflags: -I${includedir}
--- /dev/null
+#!/bin/bash -e
+
+export LD_LIBRARY_PATH=build/src
+build/test/tests
--- /dev/null
+/* 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
--- /dev/null
+/*
+ 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";
+}
--- /dev/null
+/*
+ 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;
+};
+
+}
--- /dev/null
+/*
+ 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 ();
+ }
+}
+
--- /dev/null
+/*
+ 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;
+};
+
+}
--- /dev/null
+/*
+ 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;
+}
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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
--- /dev/null
+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')
--- /dev/null
+$FontName = Arial
+$Bold = False
+$Italic = False
+$Underlined = False
+$FontSize = 42
+// This is a comment which we should ignore
+// These timecodes are H:M:S:F
+00:00:41:09 , 00:00:42:21 , This is a subtitle | and that's a line break
+00:01:01:01 , 00:01:02:10 , This is some ^Bbold^B and some ^B^Ibold italic^B^I and some ^Uunderlined^U.
--- /dev/null
+/*
+ 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/test/unit_test.hpp>
+#include <fstream>
+#include "stl_reader.h"
+#include "subtitle.h"
+
+using std::list;
+using std::ifstream;
+
+/* Test reading of an STL file */
+BOOST_AUTO_TEST_CASE (stl_reader_test)
+{
+ ifstream file ("test/data/test.stl");
+ sub::STLReader reader (file);
+ list<sub::Subtitle> subs = reader.subtitles ();
+
+ list<sub::Subtitle>::iterator i = subs.begin ();
+
+ BOOST_CHECK (i != subs.end ());
+ BOOST_CHECK_EQUAL (i->from, sub::Time (0, 0, 41, 9));
+ BOOST_CHECK_EQUAL (i->to, sub::Time (0, 0, 42, 21));
+ BOOST_CHECK_EQUAL (i->bold, false);
+ BOOST_CHECK_EQUAL (i->italic, false);
+ BOOST_CHECK_EQUAL (i->underline, false);
+ BOOST_CHECK_EQUAL (i->text, " This is a subtitle ");
+ ++i;
+
+ BOOST_CHECK (i != subs.end ());
+ BOOST_CHECK_EQUAL (i->from, sub::Time (0, 0, 41, 9));
+ BOOST_CHECK_EQUAL (i->to, sub::Time (0, 0, 42, 21));
+ BOOST_CHECK_EQUAL (i->bold, false);
+ BOOST_CHECK_EQUAL (i->italic, false);
+ BOOST_CHECK_EQUAL (i->underline, false);
+ BOOST_CHECK_EQUAL (i->text, " and that's a line break");
+ ++i;
+
+ BOOST_CHECK (i != subs.end ());
+ BOOST_CHECK_EQUAL (i->from, sub::Time (0, 1, 1, 1));
+ BOOST_CHECK_EQUAL (i->to, sub::Time (0, 1, 2, 10));
+ BOOST_CHECK_EQUAL (i->bold, false);
+ BOOST_CHECK_EQUAL (i->italic, false);
+ BOOST_CHECK_EQUAL (i->underline, false);
+ BOOST_CHECK_EQUAL (i->text, " This is some ");
+ ++i;
+
+ BOOST_CHECK (i != subs.end ());
+ BOOST_CHECK_EQUAL (i->from, sub::Time (0, 1, 1, 1));
+ BOOST_CHECK_EQUAL (i->to, sub::Time (0, 1, 2, 10));
+ BOOST_CHECK_EQUAL (i->bold, true);
+ BOOST_CHECK_EQUAL (i->italic, false);
+ BOOST_CHECK_EQUAL (i->underline, false);
+ BOOST_CHECK_EQUAL (i->text, "bold");
+ ++i;
+
+ BOOST_CHECK (i != subs.end ());
+ BOOST_CHECK_EQUAL (i->from, sub::Time (0, 1, 1, 1));
+ BOOST_CHECK_EQUAL (i->to, sub::Time (0, 1, 2, 10));
+ BOOST_CHECK_EQUAL (i->bold, false);
+ BOOST_CHECK_EQUAL (i->italic, false);
+ BOOST_CHECK_EQUAL (i->underline, false);
+ BOOST_CHECK_EQUAL (i->text, " and some ");
+ ++i;
+
+ BOOST_CHECK (i != subs.end ());
+ BOOST_CHECK_EQUAL (i->from, sub::Time (0, 1, 1, 1));
+ BOOST_CHECK_EQUAL (i->to, sub::Time (0, 1, 2, 10));
+ BOOST_CHECK_EQUAL (i->bold, true);
+ BOOST_CHECK_EQUAL (i->italic, true);
+ BOOST_CHECK_EQUAL (i->underline, false);
+ BOOST_CHECK_EQUAL (i->text, "bold italic");
+ ++i;
+
+ BOOST_CHECK (i != subs.end ());
+ BOOST_CHECK_EQUAL (i->from, sub::Time (0, 1, 1, 1));
+ BOOST_CHECK_EQUAL (i->to, sub::Time (0, 1, 2, 10));
+ BOOST_CHECK_EQUAL (i->bold, false);
+ BOOST_CHECK_EQUAL (i->italic, false);
+ BOOST_CHECK_EQUAL (i->underline, false);
+ BOOST_CHECK_EQUAL (i->text, " and some ");
+ ++i;
+
+ BOOST_CHECK (i != subs.end ());
+ BOOST_CHECK_EQUAL (i->from, sub::Time (0, 1, 1, 1));
+ BOOST_CHECK_EQUAL (i->to, sub::Time (0, 1, 2, 10));
+ BOOST_CHECK_EQUAL (i->bold, false);
+ BOOST_CHECK_EQUAL (i->italic, false);
+ BOOST_CHECK_EQUAL (i->underline, true);
+ BOOST_CHECK_EQUAL (i->text, "underlined");
+ ++i;
+
+ BOOST_CHECK_EQUAL (i->from, sub::Time (0, 1, 1, 1));
+ BOOST_CHECK_EQUAL (i->to, sub::Time (0, 1, 2, 10));
+ BOOST_CHECK_EQUAL (i->bold, false);
+ BOOST_CHECK_EQUAL (i->italic, false);
+ BOOST_CHECK_EQUAL (i->underline, false);
+ BOOST_CHECK_EQUAL (i->text, ".");
+ ++i;
+
+ BOOST_CHECK (i == subs.end ());
+}
+
+
--- /dev/null
+/*
+ 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.
+
+*/
+
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_MODULE libsub_test
+#include <boost/test/unit_test.hpp>
--- /dev/null
+def configure(conf):
+ conf.check_cxx(fragment="""
+ #define BOOST_TEST_MODULE Config test\n
+ #include <boost/test/unit_test.hpp>\n
+ int main() {}
+ """,
+ msg='Checking for boost unit testing library',
+ lib='boost_unit_test_framework',
+ uselib_store='BOOST_TEST')
+
+ conf.env.prepend_value('LINKFLAGS', '-Lsrc')
+
+def build(bld):
+ obj = bld(features='cxx cxxprogram')
+ obj.name = 'tests'
+ obj.uselib = 'BOOST_TEST'
+ obj.use = 'libsub'
+ obj.source = """
+ stl_reader_test.cc
+ test.cc
+ """
+ obj.target = 'tests'
+ obj.install_path = ''
--- /dev/null
+import subprocess
+import os
+
+APPNAME = 'libsub'
+VERSION = '0.01.0devel'
+
+def options(opt):
+ opt.load('compiler_cxx')
+ opt.add_option('--enable-debug', action='store_true', default=False, help='build with debugging information and without optimisation')
+ opt.add_option('--static', action='store_true', default=False, help='build libsub statically')
+
+def configure(conf):
+ conf.load('compiler_cxx')
+ conf.env.append_value('CXXFLAGS', ['-Wall', '-Wextra', '-D_FILE_OFFSET_BITS=64', '-D__STDC_FORMAT_MACROS'])
+ conf.env.append_value('CXXFLAGS', ['-DLIBSUB_VERSION="%s"' % VERSION])
+
+ conf.env.ENABLE_DEBUG = conf.options.enable_debug
+ conf.env.STATIC = conf.options.static
+
+ if conf.options.enable_debug:
+ conf.env.append_value('CXXFLAGS', '-g')
+ else:
+ conf.env.append_value('CXXFLAGS', '-O3')
+
+ conf.check_cxx(fragment="""
+ #include <boost/version.hpp>\n
+ #if BOOST_VERSION < 104500\n
+ #error boost too old\n
+ #endif\n
+ int main(void) { return 0; }\n
+ """,
+ mandatory=True,
+ msg='Checking for boost library >= 1.45',
+ okmsg='yes',
+ errmsg='too old\nPlease install boost version 1.45 or higher.')
+
+ conf.recurse('test')
+
+def build(bld):
+ create_version_cc(bld, VERSION)
+
+ bld(source='libsub.pc.in',
+ version=VERSION,
+ includedir='%s/include' % bld.env.PREFIX,
+ libs="-L${libdir} -lsub -lboost_system",
+ install_path='${LIBDIR}/pkgconfig')
+
+ bld.recurse('src')
+ bld.recurse('test')
+
+ bld.add_post_fun(post)
+
+def dist(ctx):
+ ctx.excl = 'TODO core *~ .git build .waf* .lock* doc/*~ src/*~ test/ref/*~ __pycache__ GPATH GRTAGS GSYMS GTAGS'
+
+def create_version_cc(bld, version):
+ if os.path.exists('.git'):
+ cmd = "LANG= git log --abbrev HEAD^..HEAD ."
+ output = subprocess.Popen(cmd, shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE).communicate()[0].splitlines()
+ o = output[0].decode('utf-8')
+ commit = o.replace ("commit ", "")[0:10]
+ else:
+ commit = "release"
+
+ try:
+ text = '#include "version.h"\n'
+ text += 'char const * sub::git_commit = \"%s\";\n' % commit
+ text += 'char const * sub::version = \"%s\";\n' % version
+ if bld.env.ENABLE_DEBUG:
+ debug_string = 'true'
+ else:
+ debug_string = 'false'
+ text += 'bool const built_with_debug = %s;\n' % debug_string
+ print('Writing version information to src/version.cc')
+ o = open('src/version.cc', 'w')
+ o.write(text)
+ o.close()
+ except IOError:
+ print('Could not open src/version.cc for writing\n')
+ sys.exit(-1)
+
+def post(ctx):
+ if ctx.cmd == 'install':
+ ctx.exec_command('/sbin/ldconfig')