|
48 | 48 | #include <boost/algorithm/string/predicate.hpp> |
49 | 49 | #include <boost/algorithm/string/replace.hpp> |
50 | 50 | #include <boost/algorithm/string/trim.hpp> |
51 | | -#include <boost/regex.hpp> |
52 | 51 |
|
53 | 52 | DEFINE_EXCEPTION(SRTParseError, SubtitleFormatParseError); |
54 | 53 |
|
@@ -119,169 +118,154 @@ struct ToggleTag { |
119 | 118 | } |
120 | 119 | }; |
121 | 120 |
|
122 | | -class SrtTagParser { |
123 | | - struct FontAttribs { |
124 | | - std::string face; |
125 | | - std::string size; |
126 | | - std::string color; |
127 | | - }; |
| 121 | +std::string WriteSRTTime(agi::Time const& ts) |
| 122 | +{ |
| 123 | + return ts.GetSrtFormatted(); |
| 124 | +} |
128 | 125 |
|
129 | | - const boost::regex tag_matcher; |
130 | | - const boost::regex attrib_matcher; |
131 | | - const boost::regex is_quoted; |
| 126 | +} |
132 | 127 |
|
133 | | -public: |
134 | | - SrtTagParser() |
135 | | - : tag_matcher("^(.*?)<(/?b|/?i|/?u|/?s|/?font)([^>]*)>(.*)$", boost::regex::icase) |
136 | | - , attrib_matcher(R"(^[[:space:]]+(face|size|color)=('[^']*'|"[^"]*"|[^[:space:]]+))", boost::regex::icase) |
137 | | - , is_quoted(R"(^(['"]).*\1$)") |
138 | | - { |
139 | | - } |
| 128 | +SrtTagParser::SrtTagParser() |
| 129 | +: tag_matcher("^(.*?)<(/?b|/?i|/?u|/?s|/?font)([^>]*)>(.*)$", boost::regex::icase) |
| 130 | +, attrib_matcher(R"(^[[:space:]]+(face|size|color)=('[^']*'|"[^"]*"|[^[:space:]]+))", boost::regex::icase) |
| 131 | +, is_quoted(R"(^(['"]).*\1$)") |
| 132 | +{} |
| 133 | + |
| 134 | +std::string SrtTagParser::ToAss(std::string srt) { |
| 135 | + ToggleTag bold('b'); |
| 136 | + ToggleTag italic('i'); |
| 137 | + ToggleTag underline('u'); |
| 138 | + ToggleTag strikeout('s'); |
| 139 | + std::vector<FontAttribs> font_stack; |
| 140 | + |
| 141 | + std::string ass; // result to be built |
140 | 142 |
|
141 | | - std::string ToAss(std::string srt) |
| 143 | + while (!srt.empty()) |
142 | 144 | { |
143 | | - ToggleTag bold('b'); |
144 | | - ToggleTag italic('i'); |
145 | | - ToggleTag underline('u'); |
146 | | - ToggleTag strikeout('s'); |
147 | | - std::vector<FontAttribs> font_stack; |
| 145 | + boost::smatch result; |
| 146 | + if (!regex_match(srt, result, tag_matcher)) |
| 147 | + { |
| 148 | + // no more tags could be matched, end of string |
| 149 | + ass.append(srt); |
| 150 | + break; |
| 151 | + } |
148 | 152 |
|
149 | | - std::string ass; // result to be built |
| 153 | + // we found a tag, translate it |
| 154 | + std::string pre_text = result.str(1); |
| 155 | + std::string tag_name = result.str(2); |
| 156 | + std::string tag_attrs = result.str(3); |
| 157 | + std::string post_text = result.str(4); |
150 | 158 |
|
151 | | - while (!srt.empty()) |
| 159 | + // the text before the tag goes through unchanged |
| 160 | + ass.append(pre_text); |
| 161 | + // the text after the tag is the input for next iteration |
| 162 | + srt = post_text; |
| 163 | + |
| 164 | + boost::to_lower(tag_name); |
| 165 | + switch (type_from_name(tag_name)) |
152 | 166 | { |
153 | | - boost::smatch result; |
154 | | - if (!regex_match(srt, result, tag_matcher)) |
| 167 | + case TagType::BOLD_OPEN: bold.Open(ass); break; |
| 168 | + case TagType::BOLD_CLOSE: bold.Close(ass); break; |
| 169 | + case TagType::ITALICS_OPEN: italic.Open(ass); break; |
| 170 | + case TagType::ITALICS_CLOSE: italic.Close(ass); break; |
| 171 | + case TagType::UNDERLINE_OPEN: underline.Open(ass); break; |
| 172 | + case TagType::UNDERLINE_CLOSE: underline.Close(ass); break; |
| 173 | + case TagType::STRIKEOUT_OPEN: strikeout.Open(ass); break; |
| 174 | + case TagType::STRIKEOUT_CLOSE: strikeout.Close(ass); break; |
| 175 | + case TagType::FONT_OPEN: |
155 | 176 | { |
156 | | - // no more tags could be matched, end of string |
157 | | - ass.append(srt); |
158 | | - break; |
159 | | - } |
160 | | - |
161 | | - // we found a tag, translate it |
162 | | - std::string pre_text = result.str(1); |
163 | | - std::string tag_name = result.str(2); |
164 | | - std::string tag_attrs = result.str(3); |
165 | | - std::string post_text = result.str(4); |
| 177 | + // new attributes to fill in |
| 178 | + FontAttribs new_attribs; |
| 179 | + FontAttribs old_attribs; |
| 180 | + // start out with any previous ones on stack |
| 181 | + if (font_stack.size() > 0) |
| 182 | + old_attribs = font_stack.back(); |
| 183 | + new_attribs = old_attribs; |
| 184 | + // now find all attributes on this font tag |
| 185 | + boost::smatch result; |
| 186 | + while (regex_search(tag_attrs, result, attrib_matcher)) |
| 187 | + { |
| 188 | + // get attribute name and values |
| 189 | + std::string attr_name = result.str(1); |
| 190 | + std::string attr_value = result.str(2); |
| 191 | + |
| 192 | + // clean them |
| 193 | + boost::to_lower(attr_name); |
| 194 | + if (regex_match(attr_value, is_quoted)) |
| 195 | + attr_value = attr_value.substr(1, attr_value.size() - 2); |
| 196 | + |
| 197 | + // handle the attributes |
| 198 | + if (attr_name == "face") |
| 199 | + new_attribs.face = agi::format("{\\fn%s}", attr_value); |
| 200 | + else if (attr_name == "size") |
| 201 | + new_attribs.size = agi::format("{\\fs%s}", attr_value); |
| 202 | + else if (attr_name == "color") |
| 203 | + new_attribs.color = agi::format("{\\c%s}", agi::Color(attr_value).GetAssOverrideFormatted()); |
| 204 | + |
| 205 | + // remove this attribute to prepare for the next |
| 206 | + tag_attrs = result.suffix().str(); |
| 207 | + } |
166 | 208 |
|
167 | | - // the text before the tag goes through unchanged |
168 | | - ass.append(pre_text); |
169 | | - // the text after the tag is the input for next iteration |
170 | | - srt = post_text; |
| 209 | + // the attributes changed from old are then written out |
| 210 | + if (new_attribs.face != old_attribs.face) |
| 211 | + ass.append(new_attribs.face); |
| 212 | + if (new_attribs.size != old_attribs.size) |
| 213 | + ass.append(new_attribs.size); |
| 214 | + if (new_attribs.color != old_attribs.color) |
| 215 | + ass.append(new_attribs.color); |
171 | 216 |
|
172 | | - boost::to_lower(tag_name); |
173 | | - switch (type_from_name(tag_name)) |
| 217 | + // lastly dump the new attributes state onto the stack |
| 218 | + font_stack.push_back(new_attribs); |
| 219 | + } |
| 220 | + break; |
| 221 | + case TagType::FONT_CLOSE: |
174 | 222 | { |
175 | | - case TagType::BOLD_OPEN: bold.Open(ass); break; |
176 | | - case TagType::BOLD_CLOSE: bold.Close(ass); break; |
177 | | - case TagType::ITALICS_OPEN: italic.Open(ass); break; |
178 | | - case TagType::ITALICS_CLOSE: italic.Close(ass); break; |
179 | | - case TagType::UNDERLINE_OPEN: underline.Open(ass); break; |
180 | | - case TagType::UNDERLINE_CLOSE: underline.Close(ass); break; |
181 | | - case TagType::STRIKEOUT_OPEN: strikeout.Open(ass); break; |
182 | | - case TagType::STRIKEOUT_CLOSE: strikeout.Close(ass); break; |
183 | | - case TagType::FONT_OPEN: |
| 223 | + // this requires a font stack entry |
| 224 | + if (font_stack.empty()) |
| 225 | + break; |
| 226 | + // get the current attribs |
| 227 | + FontAttribs cur_attribs = font_stack.back(); |
| 228 | + // remove them from the stack |
| 229 | + font_stack.pop_back(); |
| 230 | + // grab the old attributes if there are any |
| 231 | + FontAttribs old_attribs; |
| 232 | + if (font_stack.size() > 0) |
| 233 | + old_attribs = font_stack.back(); |
| 234 | + // then restore the attributes to previous settings |
| 235 | + if (cur_attribs.face != old_attribs.face) |
184 | 236 | { |
185 | | - // new attributes to fill in |
186 | | - FontAttribs new_attribs; |
187 | | - FontAttribs old_attribs; |
188 | | - // start out with any previous ones on stack |
189 | | - if (font_stack.size() > 0) |
190 | | - old_attribs = font_stack.back(); |
191 | | - new_attribs = old_attribs; |
192 | | - // now find all attributes on this font tag |
193 | | - boost::smatch result; |
194 | | - while (regex_search(tag_attrs, result, attrib_matcher)) |
195 | | - { |
196 | | - // get attribute name and values |
197 | | - std::string attr_name = result.str(1); |
198 | | - std::string attr_value = result.str(2); |
199 | | - |
200 | | - // clean them |
201 | | - boost::to_lower(attr_name); |
202 | | - if (regex_match(attr_value, is_quoted)) |
203 | | - attr_value = attr_value.substr(1, attr_value.size() - 2); |
204 | | - |
205 | | - // handle the attributes |
206 | | - if (attr_name == "face") |
207 | | - new_attribs.face = agi::format("{\\fn%s}", attr_value); |
208 | | - else if (attr_name == "size") |
209 | | - new_attribs.size = agi::format("{\\fs%s}", attr_value); |
210 | | - else if (attr_name == "color") |
211 | | - new_attribs.color = agi::format("{\\c%s}", agi::Color(attr_value).GetAssOverrideFormatted()); |
212 | | - |
213 | | - // remove this attribute to prepare for the next |
214 | | - tag_attrs = result.suffix().str(); |
215 | | - } |
216 | | - |
217 | | - // the attributes changed from old are then written out |
218 | | - if (new_attribs.face != old_attribs.face) |
219 | | - ass.append(new_attribs.face); |
220 | | - if (new_attribs.size != old_attribs.size) |
221 | | - ass.append(new_attribs.size); |
222 | | - if (new_attribs.color != old_attribs.color) |
223 | | - ass.append(new_attribs.color); |
224 | | - |
225 | | - // lastly dump the new attributes state onto the stack |
226 | | - font_stack.push_back(new_attribs); |
| 237 | + if (old_attribs.face.empty()) |
| 238 | + ass.append("{\\fn}"); |
| 239 | + else |
| 240 | + ass.append(old_attribs.face); |
227 | 241 | } |
228 | | - break; |
229 | | - case TagType::FONT_CLOSE: |
| 242 | + if (cur_attribs.size != old_attribs.size) |
230 | 243 | { |
231 | | - // this requires a font stack entry |
232 | | - if (font_stack.empty()) |
233 | | - break; |
234 | | - // get the current attribs |
235 | | - FontAttribs cur_attribs = font_stack.back(); |
236 | | - // remove them from the stack |
237 | | - font_stack.pop_back(); |
238 | | - // grab the old attributes if there are any |
239 | | - FontAttribs old_attribs; |
240 | | - if (font_stack.size() > 0) |
241 | | - old_attribs = font_stack.back(); |
242 | | - // then restore the attributes to previous settings |
243 | | - if (cur_attribs.face != old_attribs.face) |
244 | | - { |
245 | | - if (old_attribs.face.empty()) |
246 | | - ass.append("{\\fn}"); |
247 | | - else |
248 | | - ass.append(old_attribs.face); |
249 | | - } |
250 | | - if (cur_attribs.size != old_attribs.size) |
251 | | - { |
252 | | - if (old_attribs.size.empty()) |
253 | | - ass.append("{\\fs}"); |
254 | | - else |
255 | | - ass.append(old_attribs.size); |
256 | | - } |
257 | | - if (cur_attribs.color != old_attribs.color) |
258 | | - { |
259 | | - if (old_attribs.color.empty()) |
260 | | - ass.append("{\\c}"); |
261 | | - else |
262 | | - ass.append(old_attribs.color); |
263 | | - } |
| 244 | + if (old_attribs.size.empty()) |
| 245 | + ass.append("{\\fs}"); |
| 246 | + else |
| 247 | + ass.append(old_attribs.size); |
| 248 | + } |
| 249 | + if (cur_attribs.color != old_attribs.color) |
| 250 | + { |
| 251 | + if (old_attribs.color.empty()) |
| 252 | + ass.append("{\\c}"); |
| 253 | + else |
| 254 | + ass.append(old_attribs.color); |
264 | 255 | } |
265 | | - break; |
266 | | - default: |
267 | | - // unknown tag, replicate it in the output |
268 | | - ass.append("<").append(tag_name).append(tag_attrs).append(">"); |
269 | | - break; |
270 | 256 | } |
| 257 | + break; |
| 258 | + default: |
| 259 | + // unknown tag, replicate it in the output |
| 260 | + ass.append("<").append(tag_name).append(tag_attrs).append(">"); |
| 261 | + break; |
271 | 262 | } |
272 | | - |
273 | | - // make it a little prettier, join tag groups |
274 | | - boost::replace_all(ass, "}{", ""); |
275 | | - |
276 | | - return ass; |
277 | 263 | } |
278 | | -}; |
279 | 264 |
|
280 | | -std::string WriteSRTTime(agi::Time const& ts) |
281 | | -{ |
282 | | - return ts.GetSrtFormatted(); |
283 | | -} |
| 265 | + // make it a little prettier, join tag groups |
| 266 | + boost::replace_all(ass, "}{", ""); |
284 | 267 |
|
| 268 | + return ass; |
285 | 269 | } |
286 | 270 |
|
287 | 271 | SRTSubtitleFormat::SRTSubtitleFormat() |
|
0 commit comments