Various fixes to STL read/write.
authorCarl Hetherington <cth@carlh.net>
Fri, 30 May 2014 12:02:29 +0000 (13:02 +0100)
committerCarl Hetherington <cth@carlh.net>
Fri, 30 May 2014 12:02:29 +0000 (13:02 +0100)
src/stl_binary_reader.cc
src/stl_binary_tables.cc
src/stl_binary_tables.h
src/stl_binary_writer.cc
src/time_pair.cc
src/time_pair.h
test/stl_binary_writer_test.cc
tools/dumpsubs.cc

index 6c0252665b84c564f821a2c1b42a912bdb94c830..d0fc2667f7f00c1dfb0066fdca89a6df0901dbbc 100644 (file)
@@ -108,17 +108,49 @@ STLBinaryReader::STLBinaryReader (istream& in)
                        sub.from.set_frame (get_timecode (5));
                        sub.to.set_frame (get_timecode (9));
                        sub.vertical_position.line = get_int (13, 1) + i;
-                       
-                       /* XXX: justification, effects */
 
-                       /* 8Fh is unused space, so trim the string to the first instance of that */
-                       size_t unused = lines[i].find_first_of ('\x8f');
-                       if (unused != string::npos) {
-                               lines[i] = lines[i].substr (0, unused);
+                       string text;
+                       for (size_t j = 0; j < lines[i].size(); ++j) {
+
+                               unsigned char const c = static_cast<unsigned char> (lines[i][j]);
+
+                               if (c == 0x8f) {
+                                       /* Unused space i.e. end of line */
+                                       break;
+                               }
+
+                               if (c >= 0x80 && c <= 0x83) {
+                                       /* Italic or underline control code */
+                                       sub.text = utf_to_utf<char> (iso6937_to_utf16 (text.c_str()));
+                                       _subs.push_back (sub);
+                                       text.clear ();
+                               }
+
+                               switch (c) {
+                               case 0x80:
+                                       sub.italic = true;
+                                       break;
+                               case 0x81:
+                                       sub.italic = false;
+                                       break;
+                               case 0x82:
+                                       sub.underline = true;
+                                       break;
+                               case 0x83:
+                                       sub.underline = false;
+                                       break;
+                               default:
+                                       text += lines[i][j];
+                                       break;
+                               }
                        }
 
-                       sub.text = utf_to_utf<char> (iso6937_to_utf16 (lines[i].c_str()));
-                       _subs.push_back (sub);
+                       if (!text.empty ()) {
+                               sub.text = utf_to_utf<char> (iso6937_to_utf16 (text.c_str()));
+                               _subs.push_back (sub);
+                       }
+                               
+                       /* XXX: justification */
                }
        }
 }
index 664921dec40ccdd27e4a21cd538796ed3b0bcd96..7fdad7b6121852acd55a8c3226e8ba098680466f 100644 (file)
@@ -121,6 +121,24 @@ STLBinaryTables::language_enum_to_file (Language e) const
        return enum_to_file (e, _language_map);
 }
 
+int
+STLBinaryTables::cumulative_status_enum_to_file (CumulativeStatus v) const
+{
+       return enum_to_file (v, _cumulative_status_map);
+}
+
+int
+STLBinaryTables::justification_enum_to_file (Justification v) const
+{
+       return enum_to_file (v, _justification_map);
+}
+
+int
+STLBinaryTables::comment_enum_to_file (Comment v) const
+{
+       return enum_to_file (v, _comment_map);
+}
+             
 string
 STLBinaryTables::display_standard_enum_to_description (DisplayStandard v) const
 {
@@ -162,7 +180,7 @@ STLBinaryTables::comment_enum_to_description (Comment v) const
 {
        return enum_to_description (v, _comment_map);
 }
-             
+
 STLBinaryTables::STLBinaryTables ()
 {
        code<DisplayStandard, string> (_display_standard_map, " ", DISPLAY_STANDARD_UNDEFINED, "Undefined");
index 0a85dd8a4833e40e27cbdd788e5624c93e6dcf7c..3118b024b8caa11a2918362264443dcbad6256ca 100644 (file)
@@ -198,6 +198,9 @@ public:
        Comment comment_file_to_enum (int) const;
 
        std::string language_enum_to_file (Language) const;
+       int cumulative_status_enum_to_file (CumulativeStatus) const;
+       int justification_enum_to_file (Justification) const;
+       int comment_enum_to_file (Comment) const;
 
        std::string display_standard_enum_to_description (DisplayStandard) const;
        std::string language_group_enum_to_description (LanguageGroup) const;
index e662764b2c6673343494da4cff324b156ffe47fa..334a5bb69076a6d55a7998ef26beb747cab62d43 100644 (file)
@@ -33,6 +33,7 @@ using std::string;
 using std::setw;
 using std::setfill;
 using std::max;
+using std::cout;
 using namespace sub;
 
 static void
@@ -51,7 +52,7 @@ put_string (char* p, unsigned int n, string s)
 }
 
 static void
-put_int (char* p, int v, unsigned int n)
+put_int_as_string (char* p, int v, unsigned int n)
 {
        std::stringstream s;
        /* Be careful to ensure we get no thousands separators */
@@ -62,6 +63,14 @@ put_int (char* p, int v, unsigned int n)
        put_string (p, s.str ());
 }
 
+static void
+put_int_as_int (char* p, int v, unsigned int n)
+{
+       for (unsigned int i = 0; i < n; ++i) {
+               *p++ = (v & ((1 << ((i + 1) * 8)) - 1)) >> (i * 8);
+       }
+}
+
 /** @param language ISO 3-character country code for the language of the subtitles */
 void
 sub::write_stl_binary (
@@ -158,17 +167,17 @@ sub::write_stl_binary (
        put_string (buffer + 208, "0000000000000000");
        put_string (buffer + 224, creation_date);
        put_string (buffer + 230, revision_date);
-       put_int (buffer + 236, revision_number, 2);
+       put_int_as_string (buffer + 236, revision_number, 2);
        /* TTI blocks */
-       put_int (buffer + 238, subtitles.size (), 5);
+       put_int_as_string (buffer + 238, subtitles.size (), 5);
        /* Total number of subtitles */
-       put_int (buffer + 243, subtitles.size (), 5);
+       put_int_as_string (buffer + 243, subtitles.size (), 5);
        /* Total number of subtitle groups */
        put_string (buffer + 248, "000");
        /* Maximum number of displayable characters in any text row */
-       put_int (buffer + 251, 2, longest);
+       put_int_as_string (buffer + 251, 2, longest);
        /* Maximum number of displayable rows */
-       put_int (buffer + 253, 2, rows);
+       put_int_as_string (buffer + 253, 2, rows);
        /* Time code status */
        put_string (buffer + 255, "1");
        /* Start-of-programme time code */
@@ -186,8 +195,74 @@ sub::write_stl_binary (
 
        output.write (buffer, 1024);
 
+       int N = 0;
        for (list<Subtitle>::const_iterator i = subtitles.begin(); i != subtitles.end(); ++i) {
+
+               /* Subtitle group number */
+               put_int_as_int (buffer + 0, 0, 1);
+               /* Subtitle number */
+               put_int_as_int (buffer + 1, N, 2);
+               /* Extension block number */
+               put_int_as_int (buffer + 3, 0, 1);
+               /* Cumulative status */
+               put_int_as_int (buffer + 4, tables.cumulative_status_enum_to_file (CUMULATIVE_STATUS_NOT_CUMULATIVE), 1);
+               /* Time code in */
+               put_int_as_int (buffer + 5, i->from.frame(frames_per_second).hours (), 1);
+               put_int_as_int (buffer + 6, i->from.frame(frames_per_second).minutes (), 1);
+               put_int_as_int (buffer + 7, i->from.frame(frames_per_second).seconds (), 1);
+               put_int_as_int (buffer + 8, i->from.frame(frames_per_second).frames (), 1);
+               /* Time code out */
+               put_int_as_int (buffer + 9, i->to.frame(frames_per_second).hours (), 1);
+               put_int_as_int (buffer + 10, i->to.frame(frames_per_second).minutes (), 1);
+               put_int_as_int (buffer + 11, i->to.frame(frames_per_second).seconds (), 1);
+               put_int_as_int (buffer + 12, i->to.frame(frames_per_second).frames (), 1);
+               /* Vertical position */
+               /* XXX */
+               put_int_as_int (buffer + 13, 0, 1);
+               /* Justification code */
+               /* XXX */
+               put_int_as_int (buffer + 14, tables.justification_enum_to_file (JUSTIFICATION_NONE), 1);
+               /* Comment flag */
+               put_int_as_int (buffer + 15, tables.comment_enum_to_file (COMMENT_NO), 1);
+
+               /* Text */
+               string text;
+               bool italic = false;
+               bool underline = false;
                
+               for (list<Line>::const_iterator j = i->lines.begin(); j != i->lines.end(); ++j) {
+                       for (list<Block>::const_iterator k = j->blocks.begin(); k != j->blocks.end(); ++k) {
+                               if (k->underline && !underline) {
+                                       text += "\x82";
+                                       underline = true;
+                               } else if (underline && !k->underline) {
+                                       text += "\x83";
+                                       underline = false;
+                               }
+                               if (k->italic && !italic) {
+                                       text += "\x80";
+                                       italic = true;
+                               } else if (italic && !k->italic) {
+                                       text += "\x81";
+                                       italic = false;
+                               }
+
+                               text += k->text;
+                       }
+
+                       text += "\x8A";
+               }
+
+               if (text.length() > 111) {
+                       text = text.substr (111);
+               }
+
+               while (text.length() < 112) {
+                       text += "\x8F";
+               }
+
+               put_string (buffer + 16, text);
+               output.write (buffer, 128);
        }
 
        delete[] buffer;
index 186d54caaffb675af4fa2d1dafe7339fadd2b884..3ef2429c96e143dfe52c7ae1922f05e955a1c6d0 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "time_pair.h"
 
+using std::ostream;
 using namespace sub;
 
 FrameTime
@@ -54,3 +55,15 @@ TimePair::operator== (TimePair const & other) const
 
        return false;
 }
+
+ostream &
+sub::operator<< (ostream& s, TimePair const & t)
+{
+       if (t.frame ()) {
+               s << "[FRAME] " << t.frame().get();
+       } else {
+               s << "[METRIC]" << t.metric().get();
+       }
+
+       return s;
+}
index 404c143e8076f4eec6e2c71a8a1b6d169263019f..9b9c3572901c1ee3a539afa8533afcee5a223d16 100644 (file)
@@ -57,6 +57,8 @@ private:
        boost::optional<MetricTime> _metric;
 };
 
+std::ostream& operator<< (std::ostream & s, TimePair const &); 
+
 }
 
 #endif
index c29bc9b5a6f3418a9ac2163bf2d2abd4a593e72c..05b2cac58cbf93c2f2dc3e34ec897cb1325b45ca 100644 (file)
@@ -33,21 +33,28 @@ BOOST_AUTO_TEST_CASE (stl_binary_writer_test)
                s.from.set_frame (sub::FrameTime (0, 0, 41, 9));
                s.to.set_frame (sub::FrameTime (0, 0, 42, 21));
 
-               sub::Line l;
-               l.vertical_position.line = 0;
-               
-               sub::Block b;
-               b.text = "This is a subtitle ";
-               b.font = "Arial";
-               b.font_size.set_points (42);
-               l.blocks.push_back (b);
-
-               b.text = " and that's a line break";
-               b.font = "Arial";
-               b.font_size.set_points (42);
-               l.blocks.push_back (b);
+               {
+                       sub::Block b;
+                       b.text = "This is a subtitle";
+                       b.font = "Arial";
+                       b.font_size.set_points (42);
+                       sub::Line l;
+                       l.vertical_position.line = 0;
+                       l.blocks.push_back (b);
+                       s.lines.push_back (l);
+               }
+
+               {
+                       sub::Block b;
+                       b.text = "and that's a line break";
+                       b.font = "Arial";
+                       b.font_size.set_points (42);
+                       sub::Line l;
+                       l.vertical_position.line = 1;
+                       l.blocks.push_back (b);
+                       s.lines.push_back (l);
+               }
 
-               s.lines.push_back (l);
                subs.push_back (s);
        }
 
@@ -65,23 +72,17 @@ BOOST_AUTO_TEST_CASE (stl_binary_writer_test)
                b.font_size.set_points (42);
                l.blocks.push_back (b);
 
-               b.text = "bold";
-               b.bold = true;
+               b.text = "underline";
+               b.underline = true;
                l.blocks.push_back (b);
 
                b.text = " and some ";
-               b.bold = false;
+               b.underline = false;
                l.blocks.push_back (b);
 
-               b.text = "bold italic";
-               b.bold = true;
-               b.italic = true;
-               l.blocks.push_back (b);
-
-               b.text = " and some ";
-               b.bold = false;
-               b.italic = false;
+               b.text = "underlined italic";
                b.underline = true;
+               b.italic = true;
                l.blocks.push_back (b);
 
                s.lines.push_back (l);
index d98375c7f5e24ee494a2bb63c63e4a82119c185e..ac582e687c1404cf45d5516488fc200de71929d3 100644 (file)
@@ -84,10 +84,36 @@ main (int argc, char* argv[])
 
        list<sub::Subtitle> subs = collect (reader->subtitles ());
        for (list<sub::Subtitle>::const_iterator i = subs.begin(); i != subs.end(); ++i) {
+               cout << "Subtitle at " << i->from << " -> " << i->to << "\n";
                for (list<sub::Line>::const_iterator j = i->lines.begin(); j != i->lines.end(); ++j) {
+                       cout << "\t";
+                       bool italic = false;
+                       bool underline = false;
                        for (list<sub::Block>::const_iterator k = j->blocks.begin(); k != j->blocks.end(); ++k) {
-                               cout << k->text << "\n";
+                               if (k->italic && !italic) {
+                                       cout << "<i>";
+                               } else if (italic && !k->italic) {
+                                       cout << "</i>";
+                               }
+                               if (k->underline && !underline) {
+                                       cout << "<u>";
+                               } else if (underline && !k->underline) {
+                                       cout << "</u>";
+                               }
+
+                               italic = k->italic;
+                               underline = k->underline;
+                                       
+                               cout << k->text;
                        }
+
+                       if (italic) {
+                               cout << "</i>";
+                       }
+                       if (underline) {
+                               cout << "</u>";
+                       }
+                       cout << "\n";
                }
        }