Fix warning.
[libsub.git] / src / subrip_reader.cc
1 /*
2     Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
3
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.
8
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.
13
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.
17
18 */
19
20 #include "subrip_reader.h"
21 #include "exceptions.h"
22 #include <boost/algorithm/string.hpp>
23 #include <boost/lexical_cast.hpp>
24 #include <cstdio>
25 #include <vector>
26
27 using std::string;
28 using std::vector;
29 using boost::lexical_cast;
30 using namespace sub;
31
32 SubripReader::SubripReader (FILE* f)
33 {
34         enum {
35                 COUNTER,
36                 METADATA,
37                 CONTENT
38         } state = COUNTER;
39
40         char buffer[256];
41
42         TimePair from;
43         TimePair to;
44
45         string line;
46         int line_number = 0;
47
48         while (!feof (f)) {
49                 char* r = fgets (buffer, sizeof (buffer), f);
50                 if (r == 0 || feof (f)) {
51                         break;
52                 }
53
54                 line = string (buffer);
55                 trim_right_if (line, boost::is_any_of ("\n\r"));
56
57                 switch (state) {
58                 case COUNTER:
59                 {
60                         if (line.empty ()) {
61                                 /* a blank line at the start is ok */
62                                 break;
63                         }
64
65                         state = METADATA;
66                 }
67                 break;
68                 case METADATA:
69                 {
70                         vector<string> p;
71                         boost::algorithm::split (p, line, boost::algorithm::is_any_of (" "));
72                         if (p.size() != 3 && p.size() != 7) {
73                                 throw SubripError (line, "a time/position line");
74                         }
75
76                         from = convert_time (p[0]);
77                         to = convert_time (p[2]);
78
79                         /* XXX: should not ignore coordinate specifications */
80                         
81                         state = CONTENT;
82                         break;
83                 }
84                 case CONTENT:
85                         if (line.empty ()) {
86                                 state = COUNTER;
87                                 line_number = 0;
88                         } else {
89                                 convert_line (line, line_number, from, to);
90                                 line_number++;
91                         }
92                         break;
93                 }
94         }
95 }
96
97 TimePair
98 SubripReader::convert_time (string t)
99 {
100         vector<string> a;
101         boost::algorithm::split (a, t, boost::is_any_of (":"));
102         if (a.size() != 3) {
103                 throw SubripError (t, "time in the format h:m:s,ms");
104         }
105
106         vector<string> b;
107         boost::algorithm::split (b, a[2], boost::is_any_of (","));
108
109         return TimePair (
110                 MetricTime (
111                         lexical_cast<int> (a[0]),
112                         lexical_cast<int> (a[1]),
113                         lexical_cast<int> (b[0]),
114                         lexical_cast<int> (b[1])
115                         )
116                 );
117 }
118
119 void
120 SubripReader::convert_line (string t, int line_number, TimePair from, TimePair to)
121 {
122         enum {
123                 TEXT,
124                 TAG
125         } state = TEXT;
126         
127         string tag;
128
129         RawSubtitle p;
130         p.font = "Arial";
131         p.font_size.set_points (48);
132         p.from = from;
133         p.to = to;
134         p.vertical_position.proportional = 0.7 + line_number * 0.1;
135         p.vertical_position.reference = TOP;
136         
137         /* XXX: missing <font> support */
138         /* XXX: nesting of tags e.g. <b>foo<i>bar<b>baz</b>fred</i>jim</b> might
139            not work, I think.
140         */
141
142         for (size_t i = 0; i < t.size(); ++i) {
143                 switch (state) {
144                 case TEXT:
145                         if (t[i] == '<' || t[i] == '{') {
146                                 state = TAG;
147                         } else {
148                                 p.text += t[i];
149                         }
150                         break;
151                 case TAG:
152                         if (t[i] == '>' || t[i] == '}') {
153                                 if (tag == "b") {
154                                         maybe_content (p);
155                                         p.bold = true;
156                                 } else if (tag == "/b") {
157                                         maybe_content (p);
158                                         p.bold = false;
159                                 } else if (tag == "i") {
160                                         maybe_content (p);
161                                         p.italic = true;
162                                 } else if (tag == "/i") {
163                                         maybe_content (p);
164                                         p.italic = false;
165                                 } else if (tag == "u") {
166                                         maybe_content (p);
167                                         p.underline = true;
168                                 } else if (tag == "/u") {
169                                         maybe_content (p);
170                                         p.underline = false;
171                                 }
172                                 tag.clear ();
173                                 state = TEXT;
174                         } else {
175                                 tag += t[i];
176                         }
177                         break;
178                 }
179         }
180
181         maybe_content (p);
182 }
183
184 void
185 SubripReader::maybe_content (RawSubtitle& p)
186 {
187         if (!p.text.empty ()) {
188                 _subs.push_back (p);
189                 p.text.clear ();
190         }
191 }