diff options
| author | Carl Hetherington <cth@carlh.net> | 2014-01-28 21:12:10 +0000 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2014-01-28 21:12:10 +0000 |
| commit | 7f20aa518356f188946eb508239caf7c113da819 (patch) | |
| tree | 8647f9b6e6f87415cfd38e47365b14cf3e6df30f | |
First version.
| -rw-r--r-- | .gitignore | 5 | ||||
| -rw-r--r-- | libsub.pc.in | 10 | ||||
| -rwxr-xr-x | run/tests | 4 | ||||
| -rw-r--r-- | src/compose.hpp | 393 | ||||
| -rw-r--r-- | src/reader.cc | 33 | ||||
| -rw-r--r-- | src/reader.h | 38 | ||||
| -rw-r--r-- | src/stl_reader.cc | 165 | ||||
| -rw-r--r-- | src/stl_reader.h | 38 | ||||
| -rw-r--r-- | src/sub_time.cc | 37 | ||||
| -rw-r--r-- | src/sub_time.h | 59 | ||||
| -rw-r--r-- | src/subtitle.h | 50 | ||||
| -rw-r--r-- | src/wscript | 26 | ||||
| -rw-r--r-- | test/data/test.stl | 9 | ||||
| -rw-r--r-- | test/stl_reader_test.cc | 120 | ||||
| -rw-r--r-- | test/test.cc | 22 | ||||
| -rw-r--r-- | test/wscript | 23 | ||||
| -rwxr-xr-x | waf | bin | 0 -> 87683 bytes | |||
| -rw-r--r-- | wscript | 84 |
18 files changed, 1116 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bdeda98 --- /dev/null +++ b/.gitignore @@ -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 index 0000000..ff7e351 --- /dev/null +++ b/libsub.pc.in @@ -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 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 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') diff --git a/test/data/test.stl b/test/data/test.stl new file mode 100644 index 0000000..abb755a --- /dev/null +++ b/test/data/test.stl @@ -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 index 0000000..14a6ada --- /dev/null +++ b/test/stl_reader_test.cc @@ -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 index 0000000..eb6eecf --- /dev/null +++ b/test/test.cc @@ -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 index 0000000..5d16abb --- /dev/null +++ b/test/wscript @@ -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 = '' Binary files differ@@ -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') |
