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