2 Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include "stl_binary_writer.h"
24 #include "compose.hpp"
25 #include <boost/locale.hpp>
40 using boost::locale::conv::utf_to_utf;
44 put_string (char* p, string s)
46 memcpy (p, s.c_str (), s.length ());
50 put_string (char* p, unsigned int n, string s)
52 assert (s.length() <= n);
54 memcpy (p, s.c_str (), s.length ());
55 memset (p + s.length(), ' ', n - s.length ());
59 put_int_as_string (char* p, int v, unsigned int n)
62 /* Be careful to ensure we get no thousands separators */
63 s.imbue (std::locale::classic ());
64 s << setw (n) << setfill ('0');
66 assert (s.str().length() == n);
67 put_string (p, s.str ());
71 put_int_as_int (char* p, int v, unsigned int n)
73 for (unsigned int i = 0; i < n; ++i) {
74 *p++ = (v & ((1 << ((i + 1) * 8)) - 1)) >> (i * 8);
78 /** @param language ISO 3-character country code for the language of the subtitles */
80 sub::write_stl_binary (
81 list<Subtitle> subtitles,
82 float frames_per_second,
84 string original_programme_title,
85 string original_episode_title,
86 string translated_programme_title,
87 string translated_episode_title,
88 string translator_name,
89 string translator_contact_details,
93 string country_of_origin,
96 string editor_contact_details,
97 boost::filesystem::path file_name
100 assert (original_programme_title.size() <= 32);
101 assert (original_episode_title.size() <= 32);
102 assert (translated_programme_title.size() <= 32);
103 assert (translated_episode_title.size() <= 32);
104 assert (translator_name.size() <= 32);
105 assert (translator_contact_details.size() <= 32);
106 assert (creation_date.size() == 6);
107 assert (revision_date.size() == 6);
108 assert (revision_number <= 99);
109 assert (country_of_origin.size() == 3);
110 assert (publisher.size() <= 32);
111 assert (editor_name.size() <= 32);
112 assert (editor_contact_details.size() <= 32);
114 char* buffer = new char[1024];
115 memset (buffer, 0, 1024);
116 ofstream output (file_name.string().c_str ());
117 STLBinaryTables tables;
119 /* Find the longest subtitle in characters and the number of rows */
123 set<float> check_top;
124 set<float> check_centre;
125 set<float> check_bottom;
128 for (list<Subtitle>::const_iterator i = subtitles.begin(); i != subtitles.end(); ++i) {
129 for (list<Line>::const_iterator j = i->lines.begin(); j != i->lines.end(); ++j) {
131 for (list<Block>::const_iterator k = j->blocks.begin(); k != j->blocks.end(); ++k) {
132 t += k->text.size ();
134 longest = max (longest, t);
136 if (j->vertical_position.proportional) {
137 switch (j->vertical_position.reference.get ()) {
139 check_top.insert (j->vertical_position.proportional.get ());
141 case CENTRE_OF_SCREEN:
142 check_centre.insert (j->vertical_position.proportional.get ());
144 case BOTTOM_OF_SCREEN:
145 check_bottom.insert (j->vertical_position.proportional.get ());
147 case TOP_OF_SUBTITLE:
148 /* XXX: something needs to be done here */
152 check_rows.insert (j->vertical_position.line.get ());
153 /* XXX: this needs to take vertical_position.reference into account */
158 int const rows = check_top.size() + check_centre.size() + check_bottom.size() + check_rows.size();
161 put_string (buffer + 0, "850");
162 /* Disk format code */
163 put_string (buffer + 3, stl_frame_rate_to_dfc (frames_per_second));
164 /* Display standard code: open subtitling */
165 put_string (buffer + 11, "0");
166 /* Character code table: Latin (ISO 6937) */
167 put_string (buffer + 12, "00");
168 put_string (buffer + 14, tables.language_enum_to_file (language));
169 put_string (buffer + 16, 32, original_programme_title);
170 put_string (buffer + 48, 32, original_episode_title);
171 put_string (buffer + 80, 32, translated_programme_title);
172 put_string (buffer + 112, 32, translated_episode_title);
173 put_string (buffer + 144, 32, translator_name);
174 put_string (buffer + 176, 32, translator_contact_details);
175 /* Subtitle list reference code */
176 put_string (buffer + 208, "0000000000000000");
177 put_string (buffer + 224, creation_date);
178 put_string (buffer + 230, revision_date);
179 put_int_as_string (buffer + 236, revision_number, 2);
181 put_int_as_string (buffer + 238, subtitles.size (), 5);
182 /* Total number of subtitles */
183 put_int_as_string (buffer + 243, subtitles.size (), 5);
184 /* Total number of subtitle groups */
185 put_string (buffer + 248, "001");
186 /* Maximum number of displayable characters in any text row */
187 put_int_as_string (buffer + 251, longest, 2);
188 /* Maximum number of displayable rows */
189 put_int_as_string (buffer + 253, rows, 2);
190 /* Time code status */
191 put_string (buffer + 255, "1");
192 /* Start-of-programme time code */
193 put_string (buffer + 256, "00000000");
194 /* First-in-cue time code */
195 put_string (buffer + 264, "00000000");
196 /* Total number of disks */
197 put_string (buffer + 272, "1");
198 /* Disk sequence number */
199 put_string (buffer + 273, "1");
200 put_string (buffer + 274, 3, country_of_origin);
201 put_string (buffer + 277, 32, publisher);
202 put_string (buffer + 309, 32, editor_name);
203 put_string (buffer + 341, 32, editor_contact_details);
205 output.write (buffer, 1024);
208 for (list<Subtitle>::const_iterator i = subtitles.begin(); i != subtitles.end(); ++i) {
210 memset (buffer, 0, 1024);
212 /* Subtitle group number */
213 put_int_as_int (buffer + 0, 1, 1);
214 /* Subtitle number */
215 put_int_as_int (buffer + 1, N, 2);
216 /* Extension block number. Use 0xff here to indicate that it is the last TTI
217 block in this subtitle "set", as we only ever use one.
219 put_int_as_int (buffer + 3, 255, 1);
220 /* Cumulative status */
221 put_int_as_int (buffer + 4, tables.cumulative_status_enum_to_file (CUMULATIVE_STATUS_NOT_CUMULATIVE), 1);
223 put_int_as_int (buffer + 5, i->from.frame(frames_per_second).hours (), 1);
224 put_int_as_int (buffer + 6, i->from.frame(frames_per_second).minutes (), 1);
225 put_int_as_int (buffer + 7, i->from.frame(frames_per_second).seconds (), 1);
226 put_int_as_int (buffer + 8, i->from.frame(frames_per_second).frames (), 1);
228 put_int_as_int (buffer + 9, i->to.frame(frames_per_second).hours (), 1);
229 put_int_as_int (buffer + 10, i->to.frame(frames_per_second).minutes (), 1);
230 put_int_as_int (buffer + 11, i->to.frame(frames_per_second).seconds (), 1);
231 put_int_as_int (buffer + 12, i->to.frame(frames_per_second).frames (), 1);
232 /* Vertical position */
234 put_int_as_int (buffer + 13, 0, 1);
235 /* Justification code */
237 put_int_as_int (buffer + 14, tables.justification_enum_to_file (JUSTIFICATION_NONE), 1);
239 put_int_as_int (buffer + 15, tables.comment_enum_to_file (COMMENT_NO), 1);
244 bool underline = false;
246 for (list<Line>::const_iterator j = i->lines.begin(); j != i->lines.end(); ++j) {
247 for (list<Block>::const_iterator k = j->blocks.begin(); k != j->blocks.end(); ++k) {
248 if (k->underline && !underline) {
251 } else if (underline && !k->underline) {
255 if (k->italic && !italic) {
258 } else if (italic && !k->italic) {
263 text += utf16_to_iso6937 (utf_to_utf<wchar_t> (k->text));
269 if (text.length() > 111) {
270 text = text.substr (111);
273 while (text.length() < 112) {
277 put_string (buffer + 16, text);
278 output.write (buffer, 128);