First version.
authorCarl Hetherington <cth@carlh.net>
Tue, 28 Jan 2014 21:12:10 +0000 (21:12 +0000)
committerCarl Hetherington <cth@carlh.net>
Tue, 28 Jan 2014 21:12:10 +0000 (21:12 +0000)
18 files changed:
.gitignore [new file with mode: 0644]
libsub.pc.in [new file with mode: 0644]
run/tests [new file with mode: 0755]
src/compose.hpp [new file with mode: 0644]
src/reader.cc [new file with mode: 0644]
src/reader.h [new file with mode: 0644]
src/stl_reader.cc [new file with mode: 0644]
src/stl_reader.h [new file with mode: 0644]
src/sub_time.cc [new file with mode: 0644]
src/sub_time.h [new file with mode: 0644]
src/subtitle.h [new file with mode: 0644]
src/wscript [new file with mode: 0644]
test/data/test.stl [new file with mode: 0644]
test/stl_reader_test.cc [new file with mode: 0644]
test/test.cc [new file with mode: 0644]
test/wscript [new file with mode: 0644]
waf [new file with mode: 0755]
wscript [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..bdeda98
--- /dev/null
@@ -0,0 +1,5 @@
+*~
+build
+.waf-*
+.lock-waf*
+src/version.cc
\ No newline at end of file
diff --git a/libsub.pc.in b/libsub.pc.in
new file mode 100644 (file)
index 0000000..ff7e351
--- /dev/null
@@ -0,0 +1,10 @@
+prefix=@prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libsub
+Description: Subtitle reading and writing library
+Version: @version@
+Requires: 
+Libs: @libs@
+Cflags: -I${includedir}
diff --git a/run/tests b/run/tests
new file mode 100755 (executable)
index 0000000..8529f63
--- /dev/null
+++ b/run/tests
@@ -0,0 +1,4 @@
+#!/bin/bash -e
+
+export LD_LIBRARY_PATH=build/src
+build/test/tests
diff --git a/src/compose.hpp b/src/compose.hpp
new file mode 100644 (file)
index 0000000..b3f410c
--- /dev/null
@@ -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 (file)
index 0000000..f83745a
--- /dev/null
@@ -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 (file)
index 0000000..e5f142f
--- /dev/null
@@ -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 (file)
index 0000000..c86d607
--- /dev/null
@@ -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 (file)
index 0000000..307992a
--- /dev/null
@@ -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 (file)
index 0000000..6de2456
--- /dev/null
@@ -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 (file)
index 0000000..ee14745
--- /dev/null
@@ -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 (file)
index 0000000..5cc1284
--- /dev/null
@@ -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 (file)
index 0000000..b7b53b0
--- /dev/null
@@ -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')
diff --git a/test/data/test.stl b/test/data/test.stl
new file mode 100644 (file)
index 0000000..abb755a
--- /dev/null
@@ -0,0 +1,9 @@
+$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.
diff --git a/test/stl_reader_test.cc b/test/stl_reader_test.cc
new file mode 100644 (file)
index 0000000..14a6ada
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+    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 ());
+}
+
+
diff --git a/test/test.cc b/test/test.cc
new file mode 100644 (file)
index 0000000..eb6eecf
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+    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>
diff --git a/test/wscript b/test/wscript
new file mode 100644 (file)
index 0000000..5d16abb
--- /dev/null
@@ -0,0 +1,23 @@
+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 = ''
diff --git a/waf b/waf
new file mode 100755 (executable)
index 0000000..178461f
Binary files /dev/null and b/waf differ
diff --git a/wscript b/wscript
new file mode 100644 (file)
index 0000000..06b8c68
--- /dev/null
+++ b/wscript
@@ -0,0 +1,84 @@
+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')