1 /*
2 * Copyright (c) 2016 Dmitry Kazakov <dimula73@gmail.com>
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 #include "SvgTransformParser.h"
20
21 #include <QtGlobal>
22
23 //#include "kis_debug.h"
24
25 #include <boost/config/warning_disable.hpp>
26 #include <boost/spirit/include/qi.hpp>
27 #include <boost/spirit/include/phoenix_stl.hpp>
28 #include <boost/spirit/include/phoenix_operator.hpp>
29 #include <boost/spirit/include/phoenix_fusion.hpp>
30
31
32 namespace Private
33 {
34
35 struct matrix
36 {
37 qreal a = 0;
38 qreal b = 0;
39 qreal c = 0;
40 qreal d = 0;
41 qreal e = 0;
42 qreal f = 0;
43 };
44
45 struct translate
46 {
47 qreal tx = 0.0;
48 qreal ty = 0.0;
49 };
50
51 struct scale
52 {
53 qreal sx = 0;
54 qreal sy = 0;
55 bool syPresent = false;
56 };
57
58 struct rotate
59 {
60 qreal angle = 0;
61 qreal cx = 0;
62 qreal cy = 0;
63 };
64
65 struct skewX
66 {
67 qreal angle = 0;
68 };
69
70 struct skewY
71 {
72 qreal angle = 0;
73 };
74
75 struct transform_unit
76 {
transform_unitPrivate::transform_unit77 transform_unit() {}
78
transform_unitPrivate::transform_unit79 transform_unit(const matrix &m)
80 : transform(QTransform(m.a, m.b, m.c, m.d, m.e, m.f))
81 {
82 }
83
transform_unitPrivate::transform_unit84 transform_unit(const translate &t)
85 : transform(QTransform::fromTranslate(t.tx, t.ty))
86 {
87 }
88
transform_unitPrivate::transform_unit89 transform_unit(const scale &sc)
90 : transform(QTransform::fromScale(sc.sx, sc.syPresent ? sc.sy : sc.sx))
91 {
92 }
93
transform_unitPrivate::transform_unit94 transform_unit(const rotate &r) {
95 transform.rotate(r.angle);
96 if (r.cx != 0.0 || r.cy != 0.0) {
97 transform =
98 QTransform::fromTranslate(-r.cx, -r.cy) *
99 transform *
100 QTransform::fromTranslate(r.cx, r.cy);
101 }
102 }
103
transform_unitPrivate::transform_unit104 transform_unit(const skewX &sx) {
105 const qreal deg2rad = qreal(0.017453292519943295769);
106 const qreal value = tan(deg2rad * sx.angle);
107 transform.shear(value, 0);
108 }
109
transform_unitPrivate::transform_unit110 transform_unit(const skewY &sy) {
111 const qreal deg2rad = qreal(0.017453292519943295769);
112 const qreal value = tan(deg2rad * sy.angle);
113 transform.shear(0, value);
114 }
115
116 QTransform transform;
117 };
118 }
119
120 // We need to tell fusion about our transform_unit struct
121 // to make it a first-class fusion citizen. This has to
122 // be in global scope.
123
124 BOOST_FUSION_ADAPT_STRUCT(
125 Private::matrix,
126 (qreal, a)
127 (qreal, b)
128 (qreal, c)
129 (qreal, d)
130 (qreal, e)
131 (qreal, f)
132 )
133
134 BOOST_FUSION_ADAPT_STRUCT(
135 Private::translate,
136 (qreal, tx)
137 (qreal, ty)
138 )
139
140 BOOST_FUSION_ADAPT_STRUCT(
141 Private::scale,
142 (qreal, sx)
143 (qreal, sy)
144 (bool, syPresent)
145 )
146
147 BOOST_FUSION_ADAPT_STRUCT(
148 Private::rotate,
149 (qreal, angle)
150 (qreal, cx)
151 (qreal, cy)
152 )
153
154 BOOST_FUSION_ADAPT_STRUCT(
155 Private::skewX,
156 (qreal, angle)
157 )
158
159 BOOST_FUSION_ADAPT_STRUCT(
160 Private::skewY,
161 (qreal, angle)
162 )
163
164 #define BOOST_SPIRIT_DEBUG 1
165
166 namespace Private
167 {
168 // Define our grammar
169
170 namespace qi = boost::spirit::qi;
171 namespace ascii = boost::spirit::ascii;
172
173 template <typename Iterator>
174 struct transform_unit_parser : qi::grammar<Iterator, std::vector<transform_unit>(), ascii::space_type>
175 {
transform_unit_parserPrivate::transform_unit_parser176 transform_unit_parser() : transform_unit_parser::base_type(start)
177 {
178 namespace phoenix = boost::phoenix;
179 using qi::lit;
180 using qi::double_;
181 using ascii::char_;
182 using qi::cntrl;
183 using phoenix::at_c;
184 using phoenix::push_back;
185 using namespace qi::labels;
186
187
188 comma %= -char_(',');
189
190 matrix_rule %=
191 lit("matrix")
192 >> '('
193 >> double_ >> comma
194 >> double_ >> comma
195 >> double_ >> comma
196 >> double_ >> comma
197 >> double_ >> comma
198 >> double_ >> comma
199 >> ')';
200
201 translate_rule %=
202 lit("translate")
203 >> '(' >> double_ >> comma >> -double_ >> ')';
204
205 scale_rule %=
206 lit("scale")
207 >> '('
208 >> double_ >> comma
209 >> -double_ [at_c<2>(_val) = true]
210 >> ')';
211
212
213 // due to braces "-(...)" we cannot use automated
214 // semantic actions without relayouting the structure
215 rotate_rule =
216 lit("rotate")
217 >> '('
218 >> double_ [at_c<0>(_val) = _1]
219 >> comma
220 >> -(double_ [at_c<1>(_val) = _1]
221 >> comma
222 >> double_ [at_c<2>(_val) = _1])
223 >> ')';
224
225 skewX_rule %= lit("skewX") >> '(' >> double_ >> ')';
226 skewY_rule %= lit("skewY") >> '(' >> double_ >> ')';
227
228 start %=
229 (matrix_rule | translate_rule | scale_rule |
230 rotate_rule | skewX_rule | skewY_rule) %
231 (cntrl | comma);
232 }
233
234 qi::rule<Iterator, std::vector<transform_unit>(), ascii::space_type> start;
235 qi::rule<Iterator, translate(), ascii::space_type> translate_rule;
236 qi::rule<Iterator, matrix(), ascii::space_type> matrix_rule;
237 qi::rule<Iterator, scale(), ascii::space_type> scale_rule;
238 qi::rule<Iterator, rotate(), ascii::space_type> rotate_rule;
239 qi::rule<Iterator, skewX(), ascii::space_type> skewX_rule;
240 qi::rule<Iterator, skewY(), ascii::space_type> skewY_rule;
241 qi::rule<Iterator> comma;
242 };
243 }
244
245
SvgTransformParser(const QString & _str)246 SvgTransformParser::SvgTransformParser(const QString &_str)
247 : m_isValid(false)
248 {
249 using boost::spirit::ascii::space;
250 typedef std::string::const_iterator iterator_type;
251 typedef Private::transform_unit_parser<iterator_type> transform_unit_parser;
252
253 transform_unit_parser g; // Our grammar
254 const std::string str = _str.toStdString();
255
256 std::vector<Private::transform_unit> transforms;
257 iterator_type iter = str.begin();
258 iterator_type end = str.end();
259 bool r = phrase_parse(iter, end, g, space, transforms);
260
261 if (r && iter == end) {
262 m_isValid = true;
263
264 for (const Private::transform_unit &t : transforms) {
265 m_transform = t.transform * m_transform;
266 }
267 }
268 }
isValid() const269 bool SvgTransformParser::isValid() const
270 {
271 return m_isValid;
272 }
273
transform() const274 QTransform SvgTransformParser::transform() const
275 {
276 return m_transform;
277 }
278
279
280