X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Fstl_binary_writer.cc;h=7ae75094d9bc19cc89171046265d20796b57d631;hb=3d236df4bbe1e7e8a8c8c4c467c9b9178ace00f1;hp=cb6c71a67c547c6bd73d8abbb8d23d17831bb085;hpb=44a5e000d62230451cc35a239c7c3ba83d830bd7;p=libsub.git diff --git a/src/stl_binary_writer.cc b/src/stl_binary_writer.cc index cb6c71a..7ae7509 100644 --- a/src/stl_binary_writer.cc +++ b/src/stl_binary_writer.cc @@ -17,10 +17,16 @@ */ +/** @file src/stl_binary_writer.cc + * @brief Writer for STL binary files. + */ + #include "stl_binary_writer.h" #include "subtitle.h" #include "iso6937.h" +#include "stl_util.h" #include "compose.hpp" +#include "sub_assert.h" #include #include #include @@ -37,8 +43,18 @@ using std::setfill; using std::max; using std::cout; using boost::locale::conv::utf_to_utf; +using boost::optional; using namespace sub; +/** Arbitrary number which to divide the screen into rows; e.g. + * 64 here would mean that there are 64 addressable vertical positions + * on the screen, each 1/64th of the screen height tall. + * + * The magic 23 makes our output agree more closely with + * AnnotationEdit, which makes life easier when testing. + */ +static int const ROWS = 23; + static void put_string (char* p, string s) { @@ -48,8 +64,8 @@ put_string (char* p, string s) static void put_string (char* p, unsigned int n, string s) { - assert (s.length() <= n); - + SUB_ASSERT (s.length() <= n); + memcpy (p, s.c_str (), s.length ()); memset (p + s.length(), ' ', n - s.length ()); } @@ -57,12 +73,12 @@ put_string (char* p, unsigned int n, string s) static void put_int_as_string (char* p, int v, unsigned int n) { - std::stringstream s; + locked_stringstream s; /* Be careful to ensure we get no thousands separators */ s.imbue (std::locale::classic ()); s << setw (n) << setfill ('0'); s << v; - assert (s.str().length() == n); + SUB_ASSERT (s.str().length() == n); put_string (p, s.str ()); } @@ -74,6 +90,44 @@ put_int_as_int (char* p, int v, unsigned int n) } } +static int +vertical_position (sub::Line const & line) +{ + int vp = 0; + if (line.vertical_position.proportional) { + switch (line.vertical_position.reference.get_value_or (TOP_OF_SCREEN)) { + case TOP_OF_SCREEN: + vp = rint (line.vertical_position.proportional.get() * ROWS); + break; + case CENTRE_OF_SCREEN: + vp = rint (line.vertical_position.proportional.get() * ROWS + (ROWS / 2.0)); + break; + case BOTTOM_OF_SCREEN: + vp = rint (ROWS - (line.vertical_position.proportional.get() * ROWS)); + break; + default: + break; + } + } else if (line.vertical_position.line) { + float const prop = float (line.vertical_position.line.get()) / line.vertical_position.lines.get (); + switch (line.vertical_position.reference.get_value_or (TOP_OF_SCREEN)) { + case TOP_OF_SCREEN: + vp = prop * ROWS; + break; + case CENTRE_OF_SCREEN: + vp = (prop + 0.5) * ROWS; + break; + case BOTTOM_OF_SCREEN: + vp = (1 - prop) * ROWS; + break; + default: + break; + } + } + + return vp; +} + /** @param language ISO 3-character country code for the language of the subtitles */ void sub::write_stl_binary ( @@ -96,34 +150,29 @@ sub::write_stl_binary ( boost::filesystem::path file_name ) { - assert (original_programme_title.size() <= 32); - assert (original_episode_title.size() <= 32); - assert (translated_programme_title.size() <= 32); - assert (translated_episode_title.size() <= 32); - assert (translator_name.size() <= 32); - assert (translator_contact_details.size() <= 32); - assert (creation_date.size() == 6); - assert (revision_date.size() == 6); - assert (revision_number <= 99); - assert (country_of_origin.size() == 3); - assert (publisher.size() <= 32); - assert (editor_name.size() <= 32); - assert (editor_contact_details.size() <= 32); - + SUB_ASSERT (original_programme_title.size() <= 32); + SUB_ASSERT (original_episode_title.size() <= 32); + SUB_ASSERT (translated_programme_title.size() <= 32); + SUB_ASSERT (translated_episode_title.size() <= 32); + SUB_ASSERT (translator_name.size() <= 32); + SUB_ASSERT (translator_contact_details.size() <= 32); + SUB_ASSERT (creation_date.size() == 6); + SUB_ASSERT (revision_date.size() == 6); + SUB_ASSERT (revision_number <= 99); + SUB_ASSERT (country_of_origin.size() == 3); + SUB_ASSERT (publisher.size() <= 32); + SUB_ASSERT (editor_name.size() <= 32); + SUB_ASSERT (editor_contact_details.size() <= 32); + char* buffer = new char[1024]; memset (buffer, 0, 1024); ofstream output (file_name.string().c_str ()); STLBinaryTables tables; - /* Find the longest subtitle in characters and the number of rows */ + /* Find the longest subtitle in characters */ int longest = 0; - set check_top; - set check_centre; - set check_bottom; - set check_rows; - for (list::const_iterator i = subtitles.begin(); i != subtitles.end(); ++i) { for (list::const_iterator j = i->lines.begin(); j != i->lines.end(); ++j) { int t = 0; @@ -131,31 +180,13 @@ sub::write_stl_binary ( t += k->text.size (); } longest = max (longest, t); - - if (j->vertical_position.proportional) { - switch (j->vertical_position.reference.get ()) { - case TOP: - check_top.insert (j->vertical_position.proportional.get ()); - break; - case CENTRE: - check_centre.insert (j->vertical_position.proportional.get ()); - break; - case BOTTOM: - check_bottom.insert (j->vertical_position.proportional.get ()); - break; - } - } else { - check_rows.insert (j->vertical_position.line.get ()); - } } } - int const rows = check_top.size() + check_centre.size() + check_bottom.size() + check_rows.size(); - /* Code page: 850 */ put_string (buffer + 0, "850"); /* Disk format code */ - put_string (buffer + 3, String::compose ("STL%1.01", rint (frames_per_second))); + put_string (buffer + 3, stl_frame_rate_to_dfc (frames_per_second)); /* Display standard code: open subtitling */ put_string (buffer + 11, "0"); /* Character code table: Latin (ISO 6937) */ @@ -173,15 +204,15 @@ sub::write_stl_binary ( put_string (buffer + 230, revision_date); put_int_as_string (buffer + 236, revision_number, 2); /* TTI blocks */ - put_int_as_string (buffer + 238, subtitles.size (), 5); + put_int_as_string (buffer + 238, subtitles.size(), 5); /* Total number of subtitles */ - put_int_as_string (buffer + 243, subtitles.size (), 5); + put_int_as_string (buffer + 243, subtitles.size(), 5); /* Total number of subtitle groups */ put_string (buffer + 248, "001"); /* Maximum number of displayable characters in any text row */ put_int_as_string (buffer + 251, longest, 2); /* Maximum number of displayable rows */ - put_int_as_string (buffer + 253, rows, 2); + put_int_as_string (buffer + 253, ROWS, 2); /* Time code status */ put_string (buffer + 255, "1"); /* Start-of-programme time code */ @@ -199,15 +230,23 @@ sub::write_stl_binary ( output.write (buffer, 1024); - int N = 0; for (list::const_iterator i = subtitles.begin(); i != subtitles.end(); ++i) { + /* Find the top vertical position of this subtitle */ + optional top; + for (list::const_iterator j = i->lines.begin(); j != i->lines.end(); ++j) { + int const vp = vertical_position (*j); + if (!top || vp < top.get ()) { + top = vp; + } + } + memset (buffer, 0, 1024); /* Subtitle group number */ put_int_as_int (buffer + 0, 1, 1); /* Subtitle number */ - put_int_as_int (buffer + 1, N, 2); + put_int_as_int (buffer + 1, 0, 2); /* Extension block number. Use 0xff here to indicate that it is the last TTI block in this subtitle "set", as we only ever use one. */ @@ -215,21 +254,32 @@ sub::write_stl_binary ( /* 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); + put_int_as_int (buffer + 5, i->from.hours(), 1); + put_int_as_int (buffer + 6, i->from.minutes(), 1); + put_int_as_int (buffer + 7, i->from.seconds(), 1); + put_int_as_int (buffer + 8, i->from.frames_at(sub::Rational (frames_per_second * 1000, 1000)), 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); + put_int_as_int (buffer + 9, i->to.hours(), 1); + put_int_as_int (buffer + 10, i->to.minutes(), 1); + put_int_as_int (buffer + 11, i->to.seconds(), 1); + put_int_as_int (buffer + 12, i->to.frames_at(sub::Rational (frames_per_second * 1000, 1000)), 1); /* Vertical position */ - /* XXX */ - put_int_as_int (buffer + 13, 0, 1); + put_int_as_int (buffer + 13, top.get(), 1); + /* Justification code */ - /* XXX */ - put_int_as_int (buffer + 14, tables.justification_enum_to_file (JUSTIFICATION_NONE), 1); + /* XXX: this assumes the first line has the right value */ + switch (i->lines.front().horizontal_position) { + case LEFT: + put_int_as_int (buffer + 14, tables.justification_enum_to_file (JUSTIFICATION_LEFT), 1); + break; + case CENTRE: + put_int_as_int (buffer + 14, tables.justification_enum_to_file (JUSTIFICATION_CENTRE), 1); + break; + case RIGHT: + put_int_as_int (buffer + 14, tables.justification_enum_to_file (JUSTIFICATION_RIGHT), 1); + break; + } + /* Comment flag */ put_int_as_int (buffer + 15, tables.comment_enum_to_file (COMMENT_NO), 1); @@ -237,8 +287,21 @@ sub::write_stl_binary ( string text; bool italic = false; bool underline = false; - + optional last_vp; + for (list::const_iterator j = i->lines.begin(); j != i->lines.end(); ++j) { + + /* CR/LF down to this line */ + int const vp = vertical_position (*j); + + if (last_vp) { + for (int i = last_vp.get(); i < vp; ++i) { + text += "\x8A"; + } + } + + last_vp = vp; + for (list::const_iterator k = j->blocks.begin(); k != j->blocks.end(); ++k) { if (k->underline && !underline) { text += "\x82"; @@ -257,8 +320,16 @@ sub::write_stl_binary ( text += utf16_to_iso6937 (utf_to_utf (k->text)); } + } + + /* Turn italic/underline off before the end of this subtitle */ + + if (underline) { + text += "\x83"; + } - text += "\x8A"; + if (italic) { + text += "\x81"; } if (text.length() > 111) {