Mercurial > mplayer.hg
annotate sub/subassconvert.c @ 36384:e79bac50b920
Use correct types for constants.
Otherwise the compiler might insert a conversion from float
to double and back.
author | reimar |
---|---|
date | Sun, 20 Oct 2013 12:51:35 +0000 |
parents | 389d43c448b3 |
children |
rev | line source |
---|---|
32462 | 1 /* |
2 * Subtitles converter to SSA/ASS in order to allow special formatting | |
3 * | |
4 * This file is part of MPlayer. | |
5 * | |
6 * MPlayer is free software; you can redistribute it and/or modify | |
7 * it under the terms of the GNU General Public License as published by | |
8 * the Free Software Foundation; either version 2 of the License, or | |
9 * (at your option) any later version. | |
10 * | |
11 * MPlayer is distributed in the hope that it will be useful, | |
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 * GNU General Public License for more details. | |
15 * | |
16 * You should have received a copy of the GNU General Public License along | |
17 * with MPlayer; if not, write to the Free Software Foundation, Inc., | |
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
19 */ | |
20 | |
21 #include <string.h> | |
35903 | 22 #include <strings.h> |
32462 | 23 #include <stdint.h> |
24 #include <stdlib.h> | |
25 #include <stdio.h> | |
26 #include <stdarg.h> | |
27 | |
28 #include "mp_msg.h" | |
29 #include "help_mp.h" | |
30 #include "bstr.h" | |
32464
22888a8cb312
Do not use a path for including files in the same directory.
reimar
parents:
32462
diff
changeset
|
31 #include "subassconvert.h" |
32462 | 32 #include "libavutil/common.h" |
33 | |
34 struct line { | |
35 char *buf; | |
36 size_t bufsize; | |
37 size_t len; | |
38 }; | |
39 | |
40 #ifdef __GNUC__ | |
41 static void append_text(struct line *dst, char *fmt, ...) __attribute__ ((format(printf, 2, 3))); | |
42 #endif | |
43 | |
44 static void append_text(struct line *dst, char *fmt, ...) | |
45 { | |
46 va_list va; | |
47 int ret; | |
48 | |
49 va_start(va, fmt); | |
50 ret = vsnprintf(dst->buf + dst->len, dst->bufsize - dst->len, fmt, va); | |
51 if (ret >= 0) { | |
52 dst->len += ret; | |
53 if (dst->len > dst->bufsize) | |
54 dst->len = dst->bufsize; | |
55 } | |
56 va_end(va); | |
57 } | |
58 | |
59 static int indexof(const char *s, int c) | |
60 { | |
61 char *f = strchr(s, c); | |
62 return f ? (f - s) : -1; | |
63 } | |
64 | |
65 | |
66 | |
67 /* | |
68 * SubRip | |
69 * | |
70 * Support basic tags (italic, bold, underline, strike-through) | |
71 * and font tag with size, color and face attributes. | |
72 * | |
73 */ | |
74 | |
75 struct font_tag { | |
76 struct bstr face; | |
77 int size; | |
78 uint32_t color; | |
79 }; | |
80 | |
81 static const struct tag_conv { | |
82 const char *from; | |
83 const char *to; | |
84 } subrip_basic_tags[] = { | |
85 {"<i>", "{\\i1}"}, {"</i>", "{\\i0}"}, | |
86 {"<b>", "{\\b1}"}, {"</b>", "{\\b0}"}, | |
87 {"<u>", "{\\u1}"}, {"</u>", "{\\u0}"}, | |
88 {"<s>", "{\\s1}"}, {"</s>", "{\\s0}"}, | |
89 {"{", "\\{"}, {"}", "\\}"}, | |
34757 | 90 {"\r\n", "\\N"}, {"\n", "\\N"}, {"\r", "\\N"}, |
32462 | 91 }; |
92 | |
93 static const struct { | |
94 const char *s; | |
95 uint32_t v; | |
96 } subrip_web_colors[] = { | |
34912
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
97 /* Named CSS3 colors in BGR format; a subset of those |
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
98 at http://www.w3.org/TR/css3-color/#svg-color */ |
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
99 {"aqua", 0xffff00}, |
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
100 {"black", 0x000000}, |
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
101 {"blue", 0xff0000}, |
34913 | 102 {"cyan", 0xffff00}, |
34912
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
103 {"fuchsia", 0xff00ff}, |
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
104 {"gray", 0x808080}, |
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
105 {"green", 0x008000}, |
34913 | 106 {"grey", 0x808080}, |
34912
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
107 {"lime", 0x00ff00}, |
34913 | 108 {"magenta", 0xff00ff}, |
34912
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
109 {"maroon", 0x000080}, |
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
110 {"navy", 0x800000}, |
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
111 {"olive", 0x008080}, |
34913 | 112 {"orange", 0x00a5ff}, |
113 {"pink", 0xcbc0ff}, | |
34912
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
114 {"purple", 0x800080}, |
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
115 {"red", 0x0000ff}, |
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
116 {"silver", 0xc0c0c0}, |
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
117 {"teal", 0x808000}, |
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
118 {"white", 0xffffff}, |
3f8886645ebd
Order colour names alphabetically / better match W3 list.
cehoyos
parents:
34758
diff
changeset
|
119 {"yellow", 0x00ffff}, |
32462 | 120 }; |
121 | |
122 #define SUBRIP_MAX_STACKED_FONT_TAGS 16 | |
123 #define SUBRIP_FLAG_COLOR 0x01000000 | |
124 | |
125 /** | |
126 * \brief Convert SubRip lines into ASS markup | |
127 * \param orig original SubRip lines. The content will remain untouched. | |
128 * \param dest ASS markup destination buffer. | |
129 * \param dest_buffer_size maximum size for the destination buffer. | |
130 */ | |
131 void subassconvert_subrip(const char *orig, char *dest, size_t dest_buffer_size) | |
132 { | |
133 /* line is not const to avoid warnings with strtol, etc. | |
134 * orig content won't be changed */ | |
135 char *line = (char *)orig; | |
136 struct line new_line = { | |
137 .buf = dest, | |
138 .bufsize = dest_buffer_size, | |
139 }; | |
140 struct font_tag font_stack[SUBRIP_MAX_STACKED_FONT_TAGS]; | |
141 int sp = 0; | |
142 | |
143 font_stack[0] = (struct font_tag){}; // type with all defaults | |
144 while (*line && new_line.len < new_line.bufsize - 1) { | |
145 char *orig_line = line; | |
146 int i; | |
147 | |
148 for (i = 0; i < FF_ARRAY_ELEMS(subrip_basic_tags); i++) { | |
149 const struct tag_conv *tag = &subrip_basic_tags[i]; | |
150 int from_len = strlen(tag->from); | |
151 if (strncmp(line, tag->from, from_len) == 0) { | |
152 append_text(&new_line, "%s", tag->to); | |
153 line += from_len; | |
154 } | |
155 } | |
156 | |
157 if (strncmp(line, "</font>", 7) == 0) { | |
158 /* Closing font tag */ | |
159 line += 7; | |
160 | |
161 if (sp > 0) { | |
162 struct font_tag *tag = &font_stack[sp]; | |
163 struct font_tag *last_tag = &tag[-1]; | |
164 sp--; | |
165 | |
166 if (tag->size) { | |
167 if (!last_tag->size) | |
168 append_text(&new_line, "{\\fs}"); | |
169 else if (last_tag->size != tag->size) | |
170 append_text(&new_line, "{\\fs%d}", last_tag->size); | |
171 } | |
172 | |
173 if (tag->color & SUBRIP_FLAG_COLOR) { | |
174 if (!(last_tag->color & SUBRIP_FLAG_COLOR)) | |
175 append_text(&new_line, "{\\c}"); | |
176 else if (last_tag->color != tag->color) | |
177 append_text(&new_line, "{\\c&H%06X&}", | |
178 last_tag->color & 0xffffff); | |
179 } | |
180 | |
181 if (tag->face.len) { | |
182 if (!last_tag->face.len) | |
183 append_text(&new_line, "{\\fn}"); | |
184 else if (bstrcmp(last_tag->face, tag->face) != 0) | |
185 append_text(&new_line, "{\\fn%.*s}", | |
186 BSTR_P(last_tag->face)); | |
187 } | |
188 } | |
189 } else if (strncmp(line, "<font ", 6) == 0 | |
190 && sp + 1 < FF_ARRAY_ELEMS(font_stack)) { | |
191 /* Opening font tag */ | |
192 char *potential_font_tag_start = line; | |
193 int len_backup = new_line.len; | |
194 struct font_tag *tag = &font_stack[sp + 1]; | |
195 int has_valid_attr = 0; | |
196 | |
197 *tag = tag[-1]; // keep values from previous tag | |
198 line += 6; | |
199 | |
200 while (*line && *line != '>') { | |
34976
482418e9e922
subassconvert: better handling of invalid SubRip tag attributes.
cboesch
parents:
34913
diff
changeset
|
201 if (strncmp(line, "size=", 5) == 0) { |
482418e9e922
subassconvert: better handling of invalid SubRip tag attributes.
cboesch
parents:
34913
diff
changeset
|
202 line += 5; |
482418e9e922
subassconvert: better handling of invalid SubRip tag attributes.
cboesch
parents:
34913
diff
changeset
|
203 if (*line == '"') line++; |
32462 | 204 tag->size = strtol(line, &line, 10); |
34976
482418e9e922
subassconvert: better handling of invalid SubRip tag attributes.
cboesch
parents:
34913
diff
changeset
|
205 if (!tag->size) |
32462 | 206 break; |
207 append_text(&new_line, "{\\fs%d}", tag->size); | |
208 has_valid_attr = 1; | |
34976
482418e9e922
subassconvert: better handling of invalid SubRip tag attributes.
cboesch
parents:
34913
diff
changeset
|
209 } else if (strncmp(line, "color=", 6) == 0) { |
482418e9e922
subassconvert: better handling of invalid SubRip tag attributes.
cboesch
parents:
34913
diff
changeset
|
210 line += 6; |
482418e9e922
subassconvert: better handling of invalid SubRip tag attributes.
cboesch
parents:
34913
diff
changeset
|
211 if (*line == '"') line++; |
32462 | 212 if (*line == '#') { |
213 // #RRGGBB format | |
214 line++; | |
215 tag->color = strtol(line, &line, 16) & 0x00ffffff; | |
216 tag->color = ((tag->color & 0xff) << 16) | | |
217 (tag->color & 0xff00) | | |
218 ((tag->color & 0xff0000) >> 16) | | |
219 SUBRIP_FLAG_COLOR; | |
220 } else { | |
221 // Standard web colors | |
34976
482418e9e922
subassconvert: better handling of invalid SubRip tag attributes.
cboesch
parents:
34913
diff
changeset
|
222 int i; |
32462 | 223 for (i = 0; i < FF_ARRAY_ELEMS(subrip_web_colors); i++) { |
224 const char *color = subrip_web_colors[i].s; | |
34976
482418e9e922
subassconvert: better handling of invalid SubRip tag attributes.
cboesch
parents:
34913
diff
changeset
|
225 const int len = strlen(color); |
482418e9e922
subassconvert: better handling of invalid SubRip tag attributes.
cboesch
parents:
34913
diff
changeset
|
226 if (strncasecmp(line, color, len) == 0) { |
32462 | 227 tag->color = SUBRIP_FLAG_COLOR | subrip_web_colors[i].v; |
34976
482418e9e922
subassconvert: better handling of invalid SubRip tag attributes.
cboesch
parents:
34913
diff
changeset
|
228 line += len; |
32462 | 229 break; |
230 } | |
231 } | |
232 | |
233 if (i == FF_ARRAY_ELEMS(subrip_web_colors)) { | |
234 /* We didn't find any matching color */ | |
34976
482418e9e922
subassconvert: better handling of invalid SubRip tag attributes.
cboesch
parents:
34913
diff
changeset
|
235 line += strcspn(line, "\" >"); |
32462 | 236 mp_msg(MSGT_SUBREADER, MSGL_WARN, |
237 MSGTR_SUBTITLES_SubRip_UnknownFontColor, orig); | |
238 append_text(&new_line, "{\\c}"); | |
239 continue; | |
240 } | |
241 } | |
242 append_text(&new_line, "{\\c&H%06X&}", tag->color & 0xffffff); | |
243 has_valid_attr = 1; | |
244 } else if (strncmp(line, "face=\"", 6) == 0) { | |
245 /* Font face attribute */ | |
246 int len; | |
247 line += 6; | |
248 len = indexof(line, '"'); | |
249 if (len <= 0) | |
250 break; | |
251 tag->face.start = line; | |
252 tag->face.len = len; | |
253 line += len; | |
254 append_text(&new_line, "{\\fn%.*s}", BSTR_P(tag->face)); | |
255 has_valid_attr = 1; | |
256 } | |
34976
482418e9e922
subassconvert: better handling of invalid SubRip tag attributes.
cboesch
parents:
34913
diff
changeset
|
257 if (*line != '>') |
482418e9e922
subassconvert: better handling of invalid SubRip tag attributes.
cboesch
parents:
34913
diff
changeset
|
258 line++; |
32462 | 259 } |
260 | |
261 if (!has_valid_attr || *line != '>') { /* Not valid font tag */ | |
262 line = potential_font_tag_start; | |
263 new_line.len = len_backup; | |
264 } else { | |
265 sp++; | |
266 line++; | |
267 } | |
268 } | |
269 | |
270 /* Tag conversion code didn't match */ | |
271 if (line == orig_line) | |
272 new_line.buf[new_line.len++] = *line++; | |
273 } | |
274 new_line.buf[new_line.len] = 0; | |
275 } | |
276 | |
277 | |
278 /* | |
279 * MicroDVD | |
280 * | |
281 * Based on the specifications found here: | |
282 * https://trac.videolan.org/vlc/ticket/1825#comment:6 | |
283 */ | |
284 | |
285 struct microdvd_tag { | |
286 char key; | |
287 int persistent; | |
288 uint32_t data1; | |
289 uint32_t data2; | |
290 struct bstr data_string; | |
291 }; | |
292 | |
293 #define MICRODVD_PERSISTENT_OFF 0 | |
294 #define MICRODVD_PERSISTENT_ON 1 | |
295 #define MICRODVD_PERSISTENT_OPENED 2 | |
296 | |
297 // Color, Font, Size, cHarset, stYle, Position, cOordinate | |
298 #define MICRODVD_TAGS "cfshyYpo" | |
299 | |
300 static void microdvd_set_tag(struct microdvd_tag *tags, struct microdvd_tag tag) | |
301 { | |
302 int tag_index = indexof(MICRODVD_TAGS, tag.key); | |
303 | |
304 if (tag_index < 0) | |
305 return; | |
306 memcpy(&tags[tag_index], &tag, sizeof(tag)); | |
307 } | |
308 | |
309 // italic, bold, underline, strike-through | |
310 #define MICRODVD_STYLES "ibus" | |
311 | |
312 static char *microdvd_load_tags(struct microdvd_tag *tags, char *s) | |
313 { | |
314 while (*s == '{') { | |
315 char *start = s; | |
316 char tag_char = *(s + 1); | |
34758 | 317 struct microdvd_tag tag = {0}; |
32462 | 318 |
319 if (!tag_char || *(s + 2) != ':') | |
320 break; | |
321 s += 3; | |
322 | |
323 switch (tag_char) { | |
324 | |
325 /* Style */ | |
326 case 'Y': | |
327 tag.persistent = MICRODVD_PERSISTENT_ON; | |
328 case 'y': | |
329 while (*s && *s != '}') { | |
330 int style_index = indexof(MICRODVD_STYLES, *s); | |
331 | |
332 if (style_index >= 0) | |
333 tag.data1 |= (1 << style_index); | |
334 s++; | |
335 } | |
336 if (*s != '}') | |
337 break; | |
338 /* We must distinguish persistent and non-persistent styles | |
339 * to handle this kind of style tags: {y:ib}{Y:us} */ | |
340 tag.key = tag_char; | |
341 break; | |
342 | |
343 /* Color */ | |
344 case 'C': | |
345 tag.persistent = MICRODVD_PERSISTENT_ON; | |
346 case 'c': | |
347 tag.data1 = strtol(s, &s, 16) & 0x00ffffff; | |
348 if (*s != '}') | |
349 break; | |
350 tag.key = 'c'; | |
351 break; | |
352 | |
353 /* Font name */ | |
354 case 'F': | |
355 tag.persistent = MICRODVD_PERSISTENT_ON; | |
356 case 'f': | |
357 { | |
358 int len = indexof(s, '}'); | |
359 if (len < 0) | |
360 break; | |
361 tag.data_string.start = s; | |
362 tag.data_string.len = len; | |
363 s += len; | |
364 tag.key = 'f'; | |
365 break; | |
366 } | |
367 | |
368 /* Font size */ | |
369 case 'S': | |
370 tag.persistent = MICRODVD_PERSISTENT_ON; | |
371 case 's': | |
372 tag.data1 = strtol(s, &s, 10); | |
373 if (*s != '}') | |
374 break; | |
375 tag.key = 's'; | |
376 break; | |
377 | |
378 /* Charset */ | |
379 case 'H': | |
380 { | |
381 //TODO: not yet handled, just parsed. | |
382 int len = indexof(s, '}'); | |
383 if (len < 0) | |
384 break; | |
385 tag.data_string.start = s; | |
386 tag.data_string.len = len; | |
387 s += len; | |
388 tag.key = 'h'; | |
389 break; | |
390 } | |
391 | |
392 /* Position */ | |
393 case 'P': | |
394 tag.persistent = MICRODVD_PERSISTENT_ON; | |
395 tag.data1 = (*s++ == '1'); | |
396 if (*s != '}') | |
397 break; | |
398 tag.key = 'p'; | |
399 break; | |
400 | |
401 /* Coordinates */ | |
402 case 'o': | |
403 tag.persistent = MICRODVD_PERSISTENT_ON; | |
404 tag.data1 = strtol(s, &s, 10); | |
405 if (*s != ',') | |
406 break; | |
407 s++; | |
408 tag.data2 = strtol(s, &s, 10); | |
409 if (*s != '}') | |
410 break; | |
411 tag.key = 'o'; | |
412 break; | |
413 | |
414 default: /* Unknown tag, we consider it's text */ | |
415 break; | |
416 } | |
417 | |
418 if (tag.key == 0) | |
419 return start; | |
420 | |
421 microdvd_set_tag(tags, tag); | |
422 s++; | |
423 } | |
424 return s; | |
425 } | |
426 | |
427 static void microdvd_open_tags(struct line *new_line, struct microdvd_tag *tags) | |
428 { | |
429 int i, sidx; | |
430 for (i = 0; i < sizeof(MICRODVD_TAGS) - 1; i++) { | |
431 if (tags[i].persistent == MICRODVD_PERSISTENT_OPENED) | |
432 continue; | |
433 switch (tags[i].key) { | |
434 case 'Y': | |
435 case 'y': | |
436 for (sidx = 0; sidx < sizeof(MICRODVD_STYLES) - 1; sidx++) | |
437 if (tags[i].data1 & (1 << sidx)) | |
438 append_text(new_line, "{\\%c1}", MICRODVD_STYLES[sidx]); | |
439 break; | |
440 | |
441 case 'c': | |
442 append_text(new_line, "{\\c&H%06X&}", tags[i].data1); | |
443 break; | |
444 | |
445 case 'f': | |
446 append_text(new_line, "{\\fn%.*s}", BSTR_P(tags[i].data_string)); | |
447 break; | |
448 | |
449 case 's': | |
450 append_text(new_line, "{\\fs%d}", tags[i].data1); | |
451 break; | |
452 | |
453 case 'p': | |
454 if (tags[i].data1 == 0) | |
455 append_text(new_line, "{\\an8}"); | |
456 break; | |
457 | |
458 case 'o': | |
459 append_text(new_line, "{\\pos(%d,%d)}", | |
460 tags[i].data1, tags[i].data2); | |
461 break; | |
462 } | |
463 if (tags[i].persistent == MICRODVD_PERSISTENT_ON) | |
464 tags[i].persistent = MICRODVD_PERSISTENT_OPENED; | |
465 } | |
466 } | |
467 | |
468 static void microdvd_close_no_persistent_tags(struct line *new_line, | |
469 struct microdvd_tag *tags) | |
470 { | |
471 int i, sidx; | |
472 | |
473 for (i = sizeof(MICRODVD_TAGS) - 2; i; i--) { | |
474 if (tags[i].persistent != MICRODVD_PERSISTENT_OFF) | |
475 continue; | |
476 switch (tags[i].key) { | |
477 | |
478 case 'y': | |
479 for (sidx = sizeof(MICRODVD_STYLES) - 2; sidx >= 0; sidx--) | |
480 if (tags[i].data1 & (1 << sidx)) | |
481 append_text(new_line, "{\\%c0}", MICRODVD_STYLES[sidx]); | |
482 break; | |
483 | |
484 case 'c': | |
485 append_text(new_line, "{\\c}"); | |
486 break; | |
487 | |
488 case 'f': | |
489 append_text(new_line, "{\\fn}"); | |
490 break; | |
491 | |
492 case 's': | |
493 append_text(new_line, "{\\fs}"); | |
494 break; | |
495 } | |
496 tags[i].key = 0; | |
497 } | |
498 } | |
499 | |
500 /** | |
501 * \brief Convert MicroDVD lines into ASS markup | |
502 * \param orig original MicroDVD line. The content will remain untouched. | |
503 * \param dest ASS markup destination buffer. | |
504 * \param dest_buffer_size maximum size for the destination buffer. | |
505 */ | |
506 void subassconvert_microdvd(const char *orig, char *dest, size_t dest_buffer_size) | |
507 { | |
508 /* line is not const to avoid warnings with strtol, etc. | |
509 * orig content won't be changed */ | |
510 char *line = (char *)orig; | |
511 struct line new_line = { | |
512 .buf = dest, | |
513 .bufsize = dest_buffer_size, | |
514 }; | |
34758 | 515 struct microdvd_tag tags[sizeof(MICRODVD_TAGS) - 1] = {{0}}; |
32462 | 516 |
517 while (*line) { | |
518 line = microdvd_load_tags(tags, line); | |
519 microdvd_open_tags(&new_line, tags); | |
520 | |
521 while (*line && *line != '|') | |
522 new_line.buf[new_line.len++] = *line++; | |
523 | |
524 if (*line == '|') { | |
525 microdvd_close_no_persistent_tags(&new_line, tags); | |
526 append_text(&new_line, "\\N"); | |
527 line++; | |
528 } | |
529 } | |
530 new_line.buf[new_line.len] = 0; | |
531 } |