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