[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [krita/kazakov/svg-loading] /: Implemented correct parsing of 'transform' attribute
From:       Dmitry Kazakov <dimula73 () gmail ! com>
Date:       2016-10-07 13:14:05
Message-ID: E1bsUyT-0001DF-WC () code ! kde ! org
[Download RAW message or body]

Git commit 821a11671c39b88e0f71adda2c065e86bd5836fa by Dmitry Kazakov.
Committed on 07/10/2016 at 13:13.
Pushed by dkazakov into branch 'kazakov/svg-loading'.

Implemented correct parsing of 'transform' attribute

This patch introduces the dependency from boost::spirit. Implementing
the same thing in regexp were too tedious and complicated.

M  +1    -0    libs/flake/CMakeLists.txt
M  +7    -2    libs/flake/svg/SvgLoadingContext.cpp
M  +15   -4    libs/flake/svg/SvgParser.cpp
M  +2    -62   libs/flake/svg/SvgUtil.cpp
M  +0    -7    libs/flake/svg/SvgUtil.h
A  +278  -0    libs/flake/svg/parsers/SvgTransformParser.cpp     [License: GPL (v2+)]
C  +14   -22   libs/flake/svg/parsers/SvgTransformParser.h [from: \
libs/flake/tests/TestSvgParser.h - 054% similarity] M  +171  -0    \
libs/flake/tests/TestSvgParser.cpp M  +6    -0    libs/flake/tests/TestSvgParser.h
M  +1    -1    plugins/flake/artistictextshape/ArtisticTextShape.cpp

http://commits.kde.org/krita/821a11671c39b88e0f71adda2c065e86bd5836fa

diff --git a/libs/flake/CMakeLists.txt b/libs/flake/CMakeLists.txt
index aef106e..b6543fd 100644
--- a/libs/flake/CMakeLists.txt
+++ b/libs/flake/CMakeLists.txt
@@ -196,6 +196,7 @@ set(kritaflake_SRCS
     svg/SvgClipPathHelper.cpp
     svg/SvgLoadingContext.cpp
     svg/SvgShapeFactory.cpp
+    svg/parsers/SvgTransformParser.cpp
 
     FlakeDebug.cpp
 )
diff --git a/libs/flake/svg/SvgLoadingContext.cpp \
b/libs/flake/svg/SvgLoadingContext.cpp index ca944f7..94789fa 100644
--- a/libs/flake/svg/SvgLoadingContext.cpp
+++ b/libs/flake/svg/SvgLoadingContext.cpp
@@ -82,6 +82,8 @@ SvgGraphicsContext *SvgLoadingContext::currentGC() const
     return d->gcStack.top();
 }
 
+#include "parsers/SvgTransformParser.h"
+
 SvgGraphicsContext *SvgLoadingContext::pushGraphicsContext(const KoXmlElement \
&element, bool inherit)  {
     SvgGraphicsContext *gc = new SvgGraphicsContext;
@@ -98,8 +100,11 @@ SvgGraphicsContext *SvgLoadingContext::pushGraphicsContext(const \
KoXmlElement &e  
     if (!element.isNull()) {
         if (element.hasAttribute("transform")) {
-            QTransform mat = \
                SvgUtil::parseTransform(element.attribute("transform"));
-            gc->matrix = mat * gc->matrix;
+            SvgTransformParser p(element.attribute("transform"));
+            if (p.isValid()) {
+                QTransform mat = p.transform();
+                gc->matrix = mat * gc->matrix;
+            }
         }
         if (element.hasAttribute("xml:base"))
             gc->xmlBaseDir = element.attribute("xml:base");
diff --git a/libs/flake/svg/SvgParser.cpp b/libs/flake/svg/SvgParser.cpp
index c1720b6..92e66af 100644
--- a/libs/flake/svg/SvgParser.cpp
+++ b/libs/flake/svg/SvgParser.cpp
@@ -58,6 +58,7 @@
 #include "SvgFilterHelper.h"
 #include "SvgGradientHelper.h"
 #include "SvgClipPathHelper.h"
+#include "parsers/SvgTransformParser.h"
 
 #include "kis_debug.h"
 
@@ -384,7 +385,14 @@ bool SvgParser::parseGradient(const KoXmlElement &e, const \
                KoXmlElement &referen
     // Parse the color stops. The referencing gradient does not have colorstops,
     // so use the stops from the gradient it references to (e in this case and not \
b)  m_context.styleParser().parseColorStops(gradhelper.gradient(), e);
-    gradhelper.setTransform(SvgUtil::parseTransform(b.attribute("gradientTransform")));
 +
+    if (b.hasAttribute("gradientTransform")) {
+        SvgTransformParser p(e.attribute("gradientTransform"));
+        if (p.isValid()) {
+            gradhelper.setTransform(p.transform());
+        }
+    }
+
     m_gradients.insert(gradientId, gradhelper);
 
     return true;
@@ -404,9 +412,12 @@ void SvgParser::parsePattern(SvgPatternHelper &pattern, const \
KoXmlElement &e)  
         //pattern.setPatternContentViewbox(SvgUtil::parseViewBox(viewBox));
     }
-    const QString transform = e.attribute("patternTransform");
-    if (!transform.isEmpty()) {
-        pattern.setTransform(SvgUtil::parseTransform(transform));
+
+    if (e.hasAttribute("patternTransform")) {
+        SvgTransformParser p(e.attribute("patternTransform"));
+        if (p.isValid()) {
+            pattern.setTransform(p.transform());
+        }
     }
 
     const QString x = e.attribute("x");
diff --git a/libs/flake/svg/SvgUtil.cpp b/libs/flake/svg/SvgUtil.cpp
index ac38e20..b70870b 100644
--- a/libs/flake/svg/SvgUtil.cpp
+++ b/libs/flake/svg/SvgUtil.cpp
@@ -114,68 +114,6 @@ QSizeF SvgUtil::userSpaceToObject(const QSizeF &size, const \
QRectF &objectBound)  return QSizeF(w, h);
 }
 
-QTransform SvgUtil::parseTransform(const QString &transform)
-{
-    QTransform result;
-
-    // Split string for handling 1 transform statement at a time
-    QStringList subtransforms = transform.split(')', QString::SkipEmptyParts);
-    QStringList::ConstIterator it = subtransforms.constBegin();
-    QStringList::ConstIterator end = subtransforms.constEnd();
-    for (; it != end; ++it) {
-        QStringList subtransform = (*it).simplified().split('(', \
                QString::SkipEmptyParts);
-        if (subtransform.count() < 2)
-            continue;
-
-        subtransform[0] = subtransform[0].trimmed().toLower();
-        subtransform[1] = subtransform[1].simplified();
-        QRegExp reg("[,( ]");
-        QStringList params = subtransform[1].split(reg, QString::SkipEmptyParts);
-
-        if (subtransform[0].startsWith(';') || subtransform[0].startsWith(','))
-            subtransform[0] = subtransform[0].right(subtransform[0].length() - 1);
-
-        if (subtransform[0] == "rotate") {
-            if (params.count() == 3) {
-                double x = params[1].toDouble();
-                double y = params[2].toDouble();
-
-                result.translate(x, y);
-                result.rotate(params[0].toDouble());
-                result.translate(-x, -y);
-            } else {
-                result.rotate(params[0].toDouble());
-            }
-        } else if (subtransform[0] == "translate") {
-            if (params.count() == 2) {
-                result.translate(SvgUtil::fromUserSpace(params[0].toDouble()),
-                                 SvgUtil::fromUserSpace(params[1].toDouble()));
-            } else {   // Spec : if only one param given, assume 2nd param to be 0
-                result.translate(SvgUtil::fromUserSpace(params[0].toDouble()) , 0);
-            }
-        } else if (subtransform[0] == "scale") {
-            if (params.count() == 2) {
-                result.scale(params[0].toDouble(), params[1].toDouble());
-            } else {   // Spec : if only one param given, assume uniform scaling
-                result.scale(params[0].toDouble(), params[0].toDouble());
-            }
-        } else if (subtransform[0].toLower() == "skewx") {
-            result.shear(tan(DEG2RAD(params[0].toDouble())), 0.0F);
-        } else if (subtransform[0].toLower() == "skewy") {
-            result.shear(0.0F, tan(DEG2RAD(params[0].toDouble())));
-        } else if (subtransform[0] == "matrix") {
-            if (params.count() >= 6) {
-                result.setMatrix(params[0].toDouble(), params[1].toDouble(), 0,
-                                 params[2].toDouble(), params[3].toDouble(), 0,
-                                 SvgUtil::fromUserSpace(params[4].toDouble()),
-                                 SvgUtil::fromUserSpace(params[5].toDouble()), 1);
-            }
-        }
-    }
-
-    return result;
-}
-
 QString SvgUtil::transformToString(const QTransform &transform)
 {
     if (transform.isIdentity())
@@ -198,6 +136,8 @@ bool SvgUtil::parseViewBox(SvgGraphicsContext *gc, const \
KoXmlElement &e,  const QRectF &elementBounds,
                            QRectF *_viewRect, QTransform *_viewTransform)
 {
+    Q_UNUSED(gc)
+
     KIS_ASSERT(_viewRect);
     KIS_ASSERT(_viewTransform);
 
diff --git a/libs/flake/svg/SvgUtil.h b/libs/flake/svg/SvgUtil.h
index d59f909..6540854 100644
--- a/libs/flake/svg/SvgUtil.h
+++ b/libs/flake/svg/SvgUtil.h
@@ -81,13 +81,6 @@ public:
      */
     static QSizeF userSpaceToObject(const QSizeF &size, const QRectF &objectBound);
 
-    /**
-     * Parses transform attribute value into a matrix.
-     * @param transform the transform attribute value
-     * @return the resulting transformation matrix
-     */
-    static QTransform parseTransform(const QString &transform);
-
     /// Converts specified transformation to a string
     static QString transformToString(const QTransform &transform);
 
diff --git a/libs/flake/svg/parsers/SvgTransformParser.cpp \
b/libs/flake/svg/parsers/SvgTransformParser.cpp new file mode 100644
index 0000000..f49e2f4
--- /dev/null
+++ b/libs/flake/svg/parsers/SvgTransformParser.cpp
@@ -0,0 +1,278 @@
+/*
+ *  Copyright (c) 2016 Dmitry Kazakov <dimula73@gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "SvgTransformParser.h"
+
+#include <QtGlobal>
+
+//#include "kis_debug.h"
+
+#include <boost/config/warning_disable.hpp>
+#include <boost/spirit/include/qi.hpp>
+#include <boost/spirit/include/phoenix_stl.hpp>
+#include <boost/spirit/include/phoenix_operator.hpp>
+#include <boost/spirit/include/phoenix_fusion.hpp>
+
+
+namespace Private
+{
+
+struct matrix
+{
+    qreal a = 0;
+    qreal b = 0;
+    qreal c = 0;
+    qreal d = 0;
+    qreal e = 0;
+    qreal f = 0;
+};
+
+struct translate
+{
+    qreal tx = 0.0;
+    qreal ty = 0.0;
+};
+
+struct scale
+{
+    qreal sx = 0;
+    qreal sy = 0;
+    bool syPresent = false;
+};
+
+struct rotate
+{
+    qreal angle = 0;
+    qreal cx = 0;
+    qreal cy = 0;
+};
+
+struct skewX
+{
+    qreal angle = 0;
+};
+
+struct skewY
+{
+    qreal angle = 0;
+};
+
+struct transform_unit
+{
+    transform_unit() {}
+
+    transform_unit(const matrix &m) {
+        transform = QTransform(m.a, m.b, m.c, m.d, m.e, m.f);
+    }
+
+    transform_unit(const translate &t) {
+        transform = QTransform::fromTranslate(t.tx, t.ty);
+    }
+
+    transform_unit(const scale &sc) {
+        transform =
+            QTransform::fromScale(sc.sx,
+                                  sc.syPresent ? sc.sy : sc.sx);
+    }
+
+    transform_unit(const rotate &r) {
+        transform.rotate(r.angle);
+        if (r.cx != 0.0 || r.cy != 0.0) {
+            transform =
+                QTransform::fromTranslate(-r.cx, -r.cy) *
+                transform *
+                QTransform::fromTranslate(r.cx, r.cy);
+        }
+    }
+
+    transform_unit(const skewX &sx) {
+        const qreal deg2rad = qreal(0.017453292519943295769);
+        const qreal value = tan(deg2rad * sx.angle);
+        transform.shear(value, 0);
+    }
+
+    transform_unit(const skewY &sy) {
+        const qreal deg2rad = qreal(0.017453292519943295769);
+        const qreal value = tan(deg2rad * sy.angle);
+        transform.shear(0, value);
+    }
+
+    QTransform transform;
+};
+}
+
+// We need to tell fusion about our transform_unit struct
+// to make it a first-class fusion citizen. This has to
+// be in global scope.
+
+BOOST_FUSION_ADAPT_STRUCT(
+        Private::matrix,
+        (qreal, a)
+        (qreal, b)
+        (qreal, c)
+        (qreal, d)
+        (qreal, e)
+        (qreal, f)
+        )
+
+BOOST_FUSION_ADAPT_STRUCT(
+        Private::translate,
+        (qreal, tx)
+        (qreal, ty)
+        )
+
+BOOST_FUSION_ADAPT_STRUCT(
+        Private::scale,
+        (qreal, sx)
+        (qreal, sy)
+        (bool, syPresent)
+        )
+
+BOOST_FUSION_ADAPT_STRUCT(
+        Private::rotate,
+        (qreal, angle)
+        (qreal, cx)
+        (qreal, cy)
+        )
+
+BOOST_FUSION_ADAPT_STRUCT(
+        Private::skewX,
+        (qreal, angle)
+        )
+
+BOOST_FUSION_ADAPT_STRUCT(
+        Private::skewY,
+        (qreal, angle)
+        )
+
+#define BOOST_SPIRIT_DEBUG 1
+
+namespace Private
+{
+    // Define our grammar
+
+    namespace qi = boost::spirit::qi;
+    namespace ascii = boost::spirit::ascii;
+
+    template <typename Iterator>
+    struct transform_unit_parser : qi::grammar<Iterator, \
std::vector<transform_unit>(), ascii::space_type> +    {
+        transform_unit_parser() : transform_unit_parser::base_type(start)
+        {
+            namespace phoenix = boost::phoenix;
+            using qi::lit;
+            using qi::double_;
+            using ascii::char_;
+            using qi::cntrl;
+            using phoenix::at_c;
+            using phoenix::push_back;
+            using namespace qi::labels;
+
+
+            comma %= -char_(',');
+
+            matrix_rule %=
+                lit("matrix")
+                >> '('
+                >>  double_ >> comma
+                >>  double_ >> comma
+                >>  double_ >> comma
+                >>  double_ >> comma
+                >>  double_ >> comma
+                >>  double_ >> comma
+                >>  ')';
+
+            translate_rule %=
+                lit("translate")
+                >> '(' >>  double_ >> comma >>  -double_ >>  ')';
+
+            scale_rule %=
+                lit("scale")
+                >> '('
+                >>  double_ >> comma
+                >>  -double_ [at_c<2>(_val) = true]
+                >>  ')';
+
+
+            // due to braces "-(...)" we cannot use automated
+            // semantic actions without relayouting the structure
+            rotate_rule =
+                lit("rotate")
+                >> '('
+                >>  double_ [at_c<0>(_val) = _1]
+                >> comma
+                >>  -(double_ [at_c<1>(_val) = _1]
+                      >> comma
+                      >> double_ [at_c<2>(_val) = _1])
+                >>  ')';
+
+            skewX_rule %= lit("skewX") >> '(' >>  double_ >>  ')';
+            skewY_rule %= lit("skewY") >> '(' >>  double_ >>  ')';
+
+            start %=
+                (matrix_rule | translate_rule | scale_rule |
+                 rotate_rule | skewX_rule | skewY_rule) %
+                (cntrl | comma);
+        }
+
+        qi::rule<Iterator, std::vector<transform_unit>(), ascii::space_type> start;
+        qi::rule<Iterator, translate(), ascii::space_type> translate_rule;
+        qi::rule<Iterator, matrix(), ascii::space_type> matrix_rule;
+        qi::rule<Iterator, scale(), ascii::space_type> scale_rule;
+        qi::rule<Iterator, rotate(), ascii::space_type> rotate_rule;
+        qi::rule<Iterator, skewX(), ascii::space_type> skewX_rule;
+        qi::rule<Iterator, skewY(), ascii::space_type> skewY_rule;
+        qi::rule<Iterator> comma;
+    };
+}
+
+
+SvgTransformParser::SvgTransformParser(const QString &_str)
+    : m_isValid(false)
+{
+    using boost::spirit::ascii::space;
+    typedef std::string::const_iterator iterator_type;
+    typedef Private::transform_unit_parser<iterator_type> transform_unit_parser;
+
+    transform_unit_parser g; // Our grammar
+    const std::string str = _str.toStdString();
+
+    std::vector<Private::transform_unit> transforms;
+    iterator_type iter = str.begin();
+    iterator_type end = str.end();
+    bool r = phrase_parse(iter, end, g, space, transforms);
+
+    if (r && iter == end) {
+        m_isValid = true;
+
+        for (const Private::transform_unit &t : transforms) {
+             m_transform = t.transform * m_transform;
+         }
+    }
+}
+bool SvgTransformParser::isValid() const
+{
+    return m_isValid;
+}
+
+QTransform SvgTransformParser::transform() const
+{
+    return m_transform;
+}
+
+
diff --git a/libs/flake/tests/TestSvgParser.h \
b/libs/flake/svg/parsers/SvgTransformParser.h similarity index 54%
copy from libs/flake/tests/TestSvgParser.h
copy to libs/flake/svg/parsers/SvgTransformParser.h
index c2eacb7..84bf56f 100644
--- a/libs/flake/tests/TestSvgParser.h
+++ b/libs/flake/svg/parsers/SvgTransformParser.h
@@ -16,31 +16,23 @@
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
-#ifndef TESTSVGPARSER_H
-#define TESTSVGPARSER_H
+#ifndef SVGTRANSFORMPARSER_H
+#define SVGTRANSFORMPARSER_H
 
-#include <QtTest>
+#include <QTransform>
+#include "kritaflake_export.h"
 
-class TestSvgParser : public QObject
-{
-    Q_OBJECT
-private Q_SLOTS:
 
-    void testUnitPx();
-    void testUnitPxResolution();
-    void testUnitPt();
-    void testUnitIn();
-    void testUnitPercentInitial();
-    void testScalingViewport();
-    void testScalingViewportKeepMeet1();
-    void testScalingViewportKeepMeet2();
-    void testScalingViewportKeepMeetAlign();
-    void testScalingViewportKeepSlice1();
-    void testScalingViewportKeepSlice2();
-    void testScalingViewportResolution();
-    void testScalingViewportPercentInternal();
-    void testParsePreserveAspectRatio();
+class KRITAFLAKE_EXPORT SvgTransformParser
+{
+public:
+    SvgTransformParser(const QString &str);
+    bool isValid() const;
+    QTransform transform() const;
 
+private:
+    bool m_isValid;
+    QTransform m_transform;
 };
 
-#endif // TESTSVGPARSER_H
+#endif // SVGTRANSFORMPARSER_H
diff --git a/libs/flake/tests/TestSvgParser.cpp b/libs/flake/tests/TestSvgParser.cpp
index 7eec035..89c3ab8 100644
--- a/libs/flake/tests/TestSvgParser.cpp
+++ b/libs/flake/tests/TestSvgParser.cpp
@@ -482,4 +482,175 @@ void TestSvgParser::testParsePreserveAspectRatio()
     }
 }
 
+#include "parsers/SvgTransformParser.h"
+
+void TestSvgParser::testParseTransform()
+{
+    {
+        QString str("translate(-111.0, 33) translate(-111.0, 33) matrix (1 1 0 0 1, \
3), translate(1)" +                    "scale(0.5) rotate(10) rotate(10, 3 3) \
skewX(1) skewY(2)"); +
+        SvgTransformParser p(str);
+        QCOMPARE(p.isValid(), true);
+    }
+
+    {
+        // forget about one brace
+        QString str("translate(-111.0, 33) translate(-111.0, 33 matrix (1 1 0 0 1, \
3), translate(1)" +                    "scale(0.5) rotate(10) rotate(10, 3 3) \
skewX(1) skewY(2)"); +
+        SvgTransformParser p(str);
+        QCOMPARE(p.isValid(), false);
+    }
+
+    {
+        SvgTransformParser p("translate(100, 50)");
+        QCOMPARE(p.isValid(), true);
+        QCOMPARE(p.transform(), QTransform::fromTranslate(100, 50));
+    }
+
+    {
+        SvgTransformParser p("translate(100 50)");
+        QCOMPARE(p.isValid(), true);
+        QCOMPARE(p.transform(), QTransform::fromTranslate(100, 50));
+    }
+
+    {
+        SvgTransformParser p("translate(100)");
+        QCOMPARE(p.isValid(), true);
+        QCOMPARE(p.transform(), QTransform::fromTranslate(100, 0));
+    }
+
+    {
+        SvgTransformParser p("scale(100, 50)");
+        QCOMPARE(p.isValid(), true);
+        QCOMPARE(p.transform(), QTransform::fromScale(100, 50));
+    }
+
+    {
+        SvgTransformParser p("scale(100)");
+        QCOMPARE(p.isValid(), true);
+        QCOMPARE(p.transform(), QTransform::fromScale(100, 100));
+    }
+
+    {
+        SvgTransformParser p("rotate(90 70 74.0)");
+        QCOMPARE(p.isValid(), true);
+        QTransform t;
+        t.rotate(90);
+        t = QTransform::fromTranslate(-70, -74) * t * QTransform::fromTranslate(70, \
74); +        qDebug() << ppVar(p.transform());
+        QCOMPARE(p.transform(), t);
+    }
+}
+
+void TestSvgParser::testScalingViewportTransform()
+{
+    /**
+     * Note: 'transform' affects all the attributes of the *current*
+     * element, while 'vewBox' affects only the decendants!
+     */
+
+    const QString data =
+            "<svg width=\"5px\" height=\"10px\" viewBox=\"60 70 20 40\""
+            "    transform=\"scale(2)\""
+            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"
+
+            "<rect id=\"testRect\" x=\"64\" y=\"74\" width=\"12\" height=\"32\""
+            "    transform=\"translate(6)\""
+            "    fill=\"none\" stroke=\"none\" stroke-width=\"10\"/>"
+
+            "</svg>";
+
+    SvgTester t (data);
+    t.parser.setResolution(QRectF(0, 0, 600, 400) /* px */, 72 /* ppi */);
+    t.run();
+
+    KoShape *shape = t.findShape("testRect");
+    QVERIFY(shape);
+
+    QCOMPARE(shape->absoluteTransformation(0), QTransform::fromTranslate(10, 4) * \
QTransform::fromScale(0.5, 0.5)); +    QCOMPARE(shape->outlineRect(), \
QRectF(0,0,12,32)); +    QCOMPARE(shape->absolutePosition(KoFlake::TopLeftCorner), \
QPointF(5,2)); +    QCOMPARE(shape->absolutePosition(KoFlake::TopRightCorner), \
QPointF(11,2)); +    QCOMPARE(shape->absolutePosition(KoFlake::BottomLeftCorner), \
QPointF(5,18)); +    QCOMPARE(shape->absolutePosition(KoFlake::BottomRightCorner), \
QPointF(11,18)); +}
+
+void TestSvgParser::testTransformNesting()
+{
+    const QString data =
+            "<svg width=\"10px\" height=\"20px\" viewBox=\"0 0 10 20\""
+            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"
+
+            "<rect id=\"testRect\" x=\"0\" y=\"0\" width=\"10\" height=\"20\""
+            "    transform=\"translate(10,10), scale(2, 1)\""
+            "    fill=\"none\" stroke=\"none\" stroke-width=\"10\"/>"
+
+            "</svg>";
+
+    SvgTester t (data);
+    t.parser.setResolution(QRectF(0, 0, 600, 400) /* px */, 72 /* ppi */);
+    t.run();
+
+    KoShape *shape = t.findShape("testRect");
+    QVERIFY(shape);
+
+    QCOMPARE(shape->boundingRect(), QRectF(10 - 1,10 - 0.5, 20 + 2, 20 + 1));
+    QCOMPARE(shape->outlineRect(), QRectF(0,0,10,20));
+    QCOMPARE(shape->absolutePosition(KoFlake::TopLeftCorner), QPointF(10,10));
+    QCOMPARE(shape->absolutePosition(KoFlake::BottomRightCorner), QPointF(30,30));
+}
+
+void TestSvgParser::testTransformRotation1()
+{
+    const QString data =
+            "<svg width=\"10px\" height=\"20px\" viewBox=\"0 0 10 20\""
+            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"
+
+            "<rect id=\"testRect\" x=\"0\" y=\"0\" width=\"10\" height=\"20\""
+            "    transform=\"rotate(90)\""
+            "    fill=\"none\" stroke=\"none\" stroke-width=\"10\"/>"
+
+            "</svg>";
+
+    SvgTester t (data);
+    t.parser.setResolution(QRectF(0, 0, 600, 400) /* px */, 72 /* ppi */);
+    t.run();
+
+    KoShape *shape = t.findShape("testRect");
+    QVERIFY(shape);
+
+    QCOMPARE(shape->boundingRect(), kisGrowRect(QRectF(-20,0,20,10), 0.5));
+    QCOMPARE(shape->outlineRect(), QRectF(0,0,10,20));
+    QCOMPARE(shape->absolutePosition(KoFlake::TopLeftCorner), QPointF(0,0));
+    QCOMPARE(shape->absolutePosition(KoFlake::BottomRightCorner), QPointF(-20,10));
+}
+
+void TestSvgParser::testTransformRotation2()
+{
+    const QString data =
+            "<svg width=\"10px\" height=\"20px\" viewBox=\"0 0 10 20\""
+            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"
+
+            "<rect id=\"testRect\" x=\"0\" y=\"0\" width=\"10\" height=\"20\""
+            "    transform=\"rotate(-90 10 5)\""
+            "    fill=\"none\" stroke=\"none\" stroke-width=\"10\"/>"
+
+            "</svg>";
+
+    SvgTester t (data);
+    t.parser.setResolution(QRectF(0, 0, 600, 400) /* px */, 72 /* ppi */);
+    t.run();
+
+    KoShape *shape = t.findShape("testRect");
+    QVERIFY(shape);
+
+    QCOMPARE(shape->boundingRect(), kisGrowRect(QRectF(5,5,20,10), 0.5));
+    QCOMPARE(shape->outlineRect(), QRectF(0,0,10,20));
+    QCOMPARE(shape->absolutePosition(KoFlake::TopLeftCorner), QPointF(5,15));
+    QCOMPARE(shape->absolutePosition(KoFlake::BottomRightCorner), QPointF(25,5));
+}
+
+
 QTEST_GUILESS_MAIN(TestSvgParser)
diff --git a/libs/flake/tests/TestSvgParser.h b/libs/flake/tests/TestSvgParser.h
index c2eacb7..150892c 100644
--- a/libs/flake/tests/TestSvgParser.h
+++ b/libs/flake/tests/TestSvgParser.h
@@ -40,6 +40,12 @@ private Q_SLOTS:
     void testScalingViewportResolution();
     void testScalingViewportPercentInternal();
     void testParsePreserveAspectRatio();
+    void testParseTransform();
+
+    void testScalingViewportTransform();
+    void testTransformNesting();
+    void testTransformRotation1();
+    void testTransformRotation2();
 
 };
 
diff --git a/plugins/flake/artistictextshape/ArtisticTextShape.cpp \
b/plugins/flake/artistictextshape/ArtisticTextShape.cpp index 0cd1f73..52441b5 100644
--- a/plugins/flake/artistictextshape/ArtisticTextShape.cpp
+++ b/plugins/flake/artistictextshape/ArtisticTextShape.cpp
@@ -1219,7 +1219,7 @@ bool ArtisticTextShape::loadSvg(const KoXmlElement \
&textElement, SvgLoadingConte  
                 path->setSize(newSize);
                 path->setPosition(newPosition);
-                path->applyAbsoluteTransformation(SvgUtil::parseTransform(p.attribute("transform")));
 +                path->applyAbsoluteTransformation(context.currentGC()->matrix);
             }
         } else {
             path = dynamic_cast<KoPathShape *>(context.shapeById(href));


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic