File indexing completed on 2024-06-16 04:11:12
0001 /* 0002 * SPDX-FileCopyrightText: 2016 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "SvgTransformParser.h" 0008 0009 #include <QtGlobal> 0010 0011 //#include "kis_debug.h" 0012 0013 #include <boost/config/warning_disable.hpp> 0014 #include <boost/phoenix/fusion.hpp> 0015 #include <boost/phoenix/operator.hpp> 0016 #include <boost/phoenix/stl.hpp> 0017 #include <boost/spirit/include/qi.hpp> 0018 0019 namespace Private 0020 { 0021 0022 struct matrix 0023 { 0024 qreal a = 0; 0025 qreal b = 0; 0026 qreal c = 0; 0027 qreal d = 0; 0028 qreal e = 0; 0029 qreal f = 0; 0030 }; 0031 0032 struct translate 0033 { 0034 qreal tx = 0.0; 0035 qreal ty = 0.0; 0036 }; 0037 0038 struct scale 0039 { 0040 qreal sx = 0; 0041 qreal sy = 0; 0042 bool syPresent = false; 0043 }; 0044 0045 struct rotate 0046 { 0047 qreal angle = 0; 0048 qreal cx = 0; 0049 qreal cy = 0; 0050 }; 0051 0052 struct skewX 0053 { 0054 qreal angle = 0; 0055 }; 0056 0057 struct skewY 0058 { 0059 qreal angle = 0; 0060 }; 0061 0062 struct transform_unit 0063 { 0064 transform_unit() {} 0065 0066 transform_unit(const matrix &m) 0067 : transform(QTransform(m.a, m.b, m.c, m.d, m.e, m.f)) 0068 { 0069 } 0070 0071 transform_unit(const translate &t) 0072 : transform(QTransform::fromTranslate(t.tx, t.ty)) 0073 { 0074 } 0075 0076 transform_unit(const scale &sc) 0077 : transform(QTransform::fromScale(sc.sx, sc.syPresent ? sc.sy : sc.sx)) 0078 { 0079 } 0080 0081 transform_unit(const rotate &r) { 0082 transform.rotate(r.angle); 0083 if (r.cx != 0.0 || r.cy != 0.0) { 0084 transform = 0085 QTransform::fromTranslate(-r.cx, -r.cy) * 0086 transform * 0087 QTransform::fromTranslate(r.cx, r.cy); 0088 } 0089 } 0090 0091 transform_unit(const skewX &sx) { 0092 const qreal deg2rad = qreal(0.017453292519943295769); 0093 const qreal value = tan(deg2rad * sx.angle); 0094 transform.shear(value, 0); 0095 } 0096 0097 transform_unit(const skewY &sy) { 0098 const qreal deg2rad = qreal(0.017453292519943295769); 0099 const qreal value = tan(deg2rad * sy.angle); 0100 transform.shear(0, value); 0101 } 0102 0103 QTransform transform; 0104 }; 0105 } 0106 0107 // We need to tell fusion about our transform_unit struct 0108 // to make it a first-class fusion citizen. This has to 0109 // be in global scope. 0110 0111 BOOST_FUSION_ADAPT_STRUCT( 0112 Private::matrix, 0113 (qreal, a) 0114 (qreal, b) 0115 (qreal, c) 0116 (qreal, d) 0117 (qreal, e) 0118 (qreal, f) 0119 ) 0120 0121 BOOST_FUSION_ADAPT_STRUCT( 0122 Private::translate, 0123 (qreal, tx) 0124 (qreal, ty) 0125 ) 0126 0127 BOOST_FUSION_ADAPT_STRUCT( 0128 Private::scale, 0129 (qreal, sx) 0130 (qreal, sy) 0131 (bool, syPresent) 0132 ) 0133 0134 BOOST_FUSION_ADAPT_STRUCT( 0135 Private::rotate, 0136 (qreal, angle) 0137 (qreal, cx) 0138 (qreal, cy) 0139 ) 0140 0141 BOOST_FUSION_ADAPT_STRUCT( 0142 Private::skewX, 0143 (qreal, angle) 0144 ) 0145 0146 BOOST_FUSION_ADAPT_STRUCT( 0147 Private::skewY, 0148 (qreal, angle) 0149 ) 0150 0151 #define BOOST_SPIRIT_DEBUG 1 0152 0153 namespace Private 0154 { 0155 // Define our grammar 0156 0157 namespace qi = boost::spirit::qi; 0158 namespace ascii = boost::spirit::ascii; 0159 0160 template <typename Iterator> 0161 struct transform_unit_parser : qi::grammar<Iterator, std::vector<transform_unit>(), ascii::space_type> 0162 { 0163 transform_unit_parser() : transform_unit_parser::base_type(start) 0164 { 0165 namespace phoenix = boost::phoenix; 0166 using qi::lit; 0167 using qi::double_; 0168 using ascii::char_; 0169 using qi::cntrl; 0170 using phoenix::at_c; 0171 using phoenix::push_back; 0172 using namespace qi::labels; 0173 0174 0175 comma %= -char_(','); 0176 0177 matrix_rule %= 0178 lit("matrix") 0179 >> '(' 0180 >> double_ >> comma 0181 >> double_ >> comma 0182 >> double_ >> comma 0183 >> double_ >> comma 0184 >> double_ >> comma 0185 >> double_ >> comma 0186 >> ')'; 0187 0188 translate_rule %= 0189 lit("translate") 0190 >> '(' >> double_ >> comma >> -double_ >> ')'; 0191 0192 scale_rule %= 0193 lit("scale") 0194 >> '(' 0195 >> double_ >> comma 0196 >> -double_ [at_c<2>(_val) = true] 0197 >> ')'; 0198 0199 0200 // due to braces "-(...)" we cannot use automated 0201 // semantic actions without relayouting the structure 0202 rotate_rule = 0203 lit("rotate") 0204 >> '(' 0205 >> double_ [at_c<0>(_val) = _1] 0206 >> comma 0207 >> -(double_ [at_c<1>(_val) = _1] 0208 >> comma 0209 >> double_ [at_c<2>(_val) = _1]) 0210 >> ')'; 0211 0212 skewX_rule %= lit("skewX") >> '(' >> double_ >> ')'; 0213 skewY_rule %= lit("skewY") >> '(' >> double_ >> ')'; 0214 0215 start %= 0216 (matrix_rule | translate_rule | scale_rule | 0217 rotate_rule | skewX_rule | skewY_rule) % 0218 (cntrl | comma); 0219 } 0220 0221 qi::rule<Iterator, std::vector<transform_unit>(), ascii::space_type> start; 0222 qi::rule<Iterator, translate(), ascii::space_type> translate_rule; 0223 qi::rule<Iterator, matrix(), ascii::space_type> matrix_rule; 0224 qi::rule<Iterator, scale(), ascii::space_type> scale_rule; 0225 qi::rule<Iterator, rotate(), ascii::space_type> rotate_rule; 0226 qi::rule<Iterator, skewX(), ascii::space_type> skewX_rule; 0227 qi::rule<Iterator, skewY(), ascii::space_type> skewY_rule; 0228 qi::rule<Iterator> comma; 0229 }; 0230 } 0231 0232 0233 SvgTransformParser::SvgTransformParser(const QString &_str) 0234 : m_isValid(false) 0235 { 0236 using boost::spirit::ascii::space; 0237 typedef std::string::const_iterator iterator_type; 0238 typedef Private::transform_unit_parser<iterator_type> transform_unit_parser; 0239 0240 transform_unit_parser g; // Our grammar 0241 const std::string str = _str.toStdString(); 0242 0243 std::vector<Private::transform_unit> transforms; 0244 iterator_type iter = str.begin(); 0245 iterator_type end = str.end(); 0246 bool r = phrase_parse(iter, end, g, space, transforms); 0247 0248 if (r && iter == end) { 0249 m_isValid = true; 0250 0251 for (const Private::transform_unit &t : transforms) { 0252 m_transform = t.transform * m_transform; 0253 } 0254 } 0255 } 0256 bool SvgTransformParser::isValid() const 0257 { 0258 return m_isValid; 0259 } 0260 0261 QTransform SvgTransformParser::transform() const 0262 { 0263 return m_transform; 0264 } 0265 0266