~ubuntu-branches/ubuntu/wily/aegisub/wily-proposed

« back to all changes in this revision

Viewing changes to src/ass_parser.cpp

  • Committer: Package Import Robot
  • Author(s): Sebastian Reichel, Pascal De Vuyst, Juan Picca, Sebastian Reichel
  • Date: 2015-08-04 21:40:50 UTC
  • mfrom: (5.1.1 sid)
  • Revision ID: package-import@ubuntu.com-20150804214050-y2aghm9vdksoc8t7
Tags: 3.2.2+dfsg-1
[ Pascal De Vuyst ]
* Fix Typo in package description (Closes: #739219)

[ Juan Picca ]
* Add patch to fix reproducible build (Closes: #789728)

[ Sebastian Reichel ]
* New upstream release
 - remove vendor directory from orig tarball
* Update Debian Standards Version to 3.9.6

Show diffs side-by-side

added added

removed removed

Lines of Context:
12
12
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13
13
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
14
 
15
 
#include "config.h"
16
 
 
17
15
#include "ass_parser.h"
18
16
 
19
17
#include "ass_attachment.h"
21
19
#include "ass_file.h"
22
20
#include "ass_info.h"
23
21
#include "ass_style.h"
 
22
#include "string_codec.h"
24
23
#include "subtitle_format.h"
25
24
 
 
25
#include <libaegisub/ass/uuencode.h>
 
26
#include <libaegisub/make_unique.h>
 
27
#include <libaegisub/util.h>
 
28
 
26
29
#include <algorithm>
27
30
#include <boost/algorithm/string/case_conv.hpp>
28
31
#include <boost/algorithm/string/predicate.hpp>
29
32
#include <boost/algorithm/string/trim.hpp>
 
33
#include <boost/lexical_cast.hpp>
 
34
#include <boost/regex.hpp>
 
35
#include <boost/variant.hpp>
 
36
#include <unordered_map>
 
37
 
 
38
class AssParser::HeaderToProperty {
 
39
        using field = boost::variant<
 
40
                std::string ProjectProperties::*,
 
41
                int ProjectProperties::*,
 
42
                double ProjectProperties::*
 
43
        >;
 
44
        std::unordered_map<std::string, field> fields;
 
45
 
 
46
public:
 
47
        HeaderToProperty()
 
48
        : fields({
 
49
                {"Automation Scripts", &ProjectProperties::automation_scripts},
 
50
                {"Export Filters", &ProjectProperties::export_filters},
 
51
                {"Export Encoding", &ProjectProperties::export_encoding},
 
52
                {"Last Style Storage", &ProjectProperties::style_storage},
 
53
                {"Audio URI", &ProjectProperties::audio_file},
 
54
                {"Audio File", &ProjectProperties::audio_file},
 
55
                {"Video File", &ProjectProperties::video_file},
 
56
                {"Timecodes File", &ProjectProperties::timecodes_file},
 
57
                {"Keyframes File", &ProjectProperties::keyframes_file},
 
58
                {"Video Zoom Percent", &ProjectProperties::video_zoom},
 
59
                {"Scroll Position", &ProjectProperties::scroll_position},
 
60
                {"Active Line", &ProjectProperties::active_row},
 
61
                {"Video Position", &ProjectProperties::video_position},
 
62
                {"Video AR Mode", &ProjectProperties::ar_mode},
 
63
                {"Video AR Value", &ProjectProperties::ar_value},
 
64
                {"Aegisub Video Zoom Percent", &ProjectProperties::video_zoom},
 
65
                {"Aegisub Scroll Position", &ProjectProperties::scroll_position},
 
66
                {"Aegisub Active Line", &ProjectProperties::active_row},
 
67
                {"Aegisub Video Position", &ProjectProperties::video_position}
 
68
        })
 
69
        {
 
70
        }
 
71
 
 
72
        bool ProcessProperty(AssFile *target, std::string const& key, std::string const& value) {
 
73
                auto it = fields.find(key);
 
74
                if (it != end(fields)) {
 
75
                        using namespace agi::util;
 
76
                        struct {
 
77
                                using result_type = void;
 
78
                                ProjectProperties &obj;
 
79
                                std::string const& value;
 
80
                                void operator()(std::string ProjectProperties::*f) const { obj.*f = value; }
 
81
                                void operator()(int ProjectProperties::*f)         const { try_parse(value, &(obj.*f)); }
 
82
                                void operator()(double ProjectProperties::*f)      const { try_parse(value, &(obj.*f)); }
 
83
                        } visitor {target->Properties, value};
 
84
                        boost::apply_visitor(visitor, it->second);
 
85
                        return true;
 
86
                }
 
87
 
 
88
                if (boost::starts_with(key, "Automation Settings ")) {
 
89
                        target->Properties.automation_settings[key.substr(strlen("Automation Settings"))] = value;
 
90
                        return true;
 
91
                }
 
92
 
 
93
                return false;
 
94
        }
 
95
};
30
96
 
31
97
AssParser::AssParser(AssFile *target, int version)
32
 
: target(target)
 
98
: property_handler(new HeaderToProperty)
 
99
, target(target)
33
100
, version(version)
34
101
, state(&AssParser::ParseScriptInfoLine)
35
102
{
36
 
        std::fill(begin(insertion_positions), end(insertion_positions), nullptr);
37
103
}
38
104
 
39
105
AssParser::~AssParser() {
52
118
 
53
119
        // Data is over, add attachment to the file
54
120
        if (!valid_data || is_filename) {
55
 
                InsertLine(attach.release());
 
121
                target->Attachments.push_back(*attach.release());
56
122
                AddLine(data);
57
123
        }
58
124
        else {
60
126
 
61
127
                // Done building
62
128
                if (data.size() < 80)
63
 
                        InsertLine(attach.release());
 
129
                        target->Attachments.push_back(*attach.release());
64
130
        }
65
131
}
66
132
 
80
146
                else if (version_str == "v4.00+")
81
147
                        version = 1;
82
148
                else
83
 
                        throw SubtitleFormatParseError("Unknown SSA file format version", nullptr);
 
149
                        throw SubtitleFormatParseError("Unknown SSA file format version");
84
150
        }
85
151
 
86
152
        // Nothing actually supports the Collisions property and malformed values
91
157
        size_t pos = data.find(':');
92
158
        if (pos == data.npos) return;
93
159
 
94
 
        InsertLine(new AssInfo(data.substr(0, pos), boost::trim_left_copy(data.substr(pos + 1))));
 
160
        auto key = data.substr(0, pos);
 
161
        auto value = data.substr(pos + 1);
 
162
        boost::trim_left(value);
 
163
 
 
164
        if (!property_handler->ProcessProperty(target, key, value))
 
165
                target->Info.push_back(*new AssInfo(std::move(key), std::move(value)));
 
166
}
 
167
 
 
168
void AssParser::ParseMetadataLine(std::string const& data) {
 
169
        size_t pos = data.find(':');
 
170
        if (pos == data.npos) return;
 
171
 
 
172
        auto key = data.substr(0, pos);
 
173
        auto value = data.substr(pos + 1);
 
174
        boost::trim_left(value);
 
175
 
 
176
        property_handler->ProcessProperty(target, key, value);
95
177
}
96
178
 
97
179
void AssParser::ParseEventLine(std::string const& data) {
98
180
        if (boost::starts_with(data, "Dialogue:") || boost::starts_with(data, "Comment:"))
99
 
                InsertLine(new AssDialogue(data));
 
181
                target->Events.push_back(*new AssDialogue(data));
100
182
}
101
183
 
102
184
void AssParser::ParseStyleLine(std::string const& data) {
103
185
        if (boost::starts_with(data, "Style:"))
104
 
                InsertLine(new AssStyle(data, version));
 
186
                target->Styles.push_back(*new AssStyle(data, version));
105
187
}
106
188
 
107
189
void AssParser::ParseFontLine(std::string const& data) {
108
190
        if (boost::starts_with(data, "fontname: "))
109
 
                attach.reset(new AssAttachment(data, AssEntryGroup::FONT));
 
191
                attach = agi::make_unique<AssAttachment>(data, AssEntryGroup::FONT);
110
192
}
111
193
 
112
194
void AssParser::ParseGraphicsLine(std::string const& data) {
113
195
        if (boost::starts_with(data, "filename: "))
114
 
                attach.reset(new AssAttachment(data, AssEntryGroup::GRAPHIC));
 
196
                attach = agi::make_unique<AssAttachment>(data, AssEntryGroup::GRAPHIC);
 
197
}
 
198
 
 
199
void AssParser::ParseExtradataLine(std::string const &data) {
 
200
        static const boost::regex matcher("Data:[[:space:]]*(\\d+),([^,]+),(.)(.*)");
 
201
        boost::match_results<std::string::const_iterator> mr;
 
202
 
 
203
        if (boost::regex_match(data, mr, matcher)) {
 
204
                auto id = boost::lexical_cast<uint32_t>(mr.str(1));
 
205
                auto key = inline_string_decode(mr.str(2));
 
206
                auto valuetype = mr.str(3);
 
207
                auto value = mr.str(4);
 
208
                if (valuetype == "e") {
 
209
                        // escaped/inline_string encoded
 
210
                        value = inline_string_decode(value);
 
211
                } else if (valuetype == "u") {
 
212
                        // ass uuencoded
 
213
                        auto valuedata = agi::ass::UUDecode(value);
 
214
                        value = std::string(valuedata.begin(), valuedata.end());
 
215
                } else {
 
216
                        // unknown, error?
 
217
                        value = "";
 
218
                }
 
219
 
 
220
                // ensure next_extradata_id is always at least 1 more than the largest existing id
 
221
                target->next_extradata_id = std::max(id+1, target->next_extradata_id);
 
222
                target->Extradata.push_back(ExtradataEntry{id, std::move(key), std::move(value)});
 
223
        }
115
224
}
116
225
 
117
226
void AssParser::AddLine(std::string const& data) {
129
238
        if (data[0] == '[' && data.back() == ']') {
130
239
                // Ugly hacks to allow intermixed v4 and v4+ style sections
131
240
                const std::string low = boost::to_lower_copy(data);
132
 
                std::string header = data;
133
241
                if (low == "[v4 styles]") {
134
 
                        header = "[V4+ Styles]";
135
242
                        version = 0;
136
243
                        state = &AssParser::ParseStyleLine;
137
244
                }
138
245
                else if (low == "[v4+ styles]") {
139
 
                        header = "[V4+ Styles]";
140
246
                        version = 1;
141
247
                        state = &AssParser::ParseStyleLine;
142
248
                }
144
250
                        state = &AssParser::ParseEventLine;
145
251
                else if (low == "[script info]")
146
252
                        state = &AssParser::ParseScriptInfoLine;
 
253
                else if (low == "[aegisub project garbage]")
 
254
                        state = &AssParser::ParseMetadataLine;
 
255
                else if (low == "[aegisub extradata]")
 
256
                        state = &AssParser::ParseExtradataLine;
147
257
                else if (low == "[graphics]")
148
258
                        state = &AssParser::ParseGraphicsLine;
149
259
                else if (low == "[fonts]")
155
265
 
156
266
        (this->*state)(data);
157
267
}
158
 
 
159
 
void AssParser::InsertLine(AssEntry *entry) {
160
 
        AssEntry *position = insertion_positions[(size_t)entry->Group()];
161
 
        if (position)
162
 
                target->Line.insert(++target->Line.iterator_to(*position), *entry);
163
 
        else
164
 
                target->Line.push_back(*entry);
165
 
        insertion_positions[(size_t)entry->Group()] = entry;
166
 
}