/*
 *  SPDX-FileCopyrightText: 2016 Dmitry Kazakov <dimula73@gmail.com>
 *
 *  SPDX-License-Identifier: GPL-2.0-or-later
 */

#include "TestSvgParser.h"


#include <QPainterPath>
#include <simpletest.h>
#include <svg/SvgUtil.h>
#include <KoColorBackground.h>
#include <KoColorProfile.h>


#include "SvgParserTestingUtils.h"
#include <sdk/tests/testflake.h>
#include "../../sdk/tests/qimage_test_util.h"

#ifdef USE_ROUND_TRIP
#include "SvgWriter.h"
#include <QBuffer>
#include <QDomDocument>
#endif


void TestSvgParser::testUnitPx()
{
    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\""
            "    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(0,0,10,20));
    QCOMPARE(shape->absoluteTransformation(), QTransform());
    QCOMPARE(shape->outlineRect(), QRectF(0,0,10,20));
    QCOMPARE(shape->absolutePosition(KoFlake::TopLeft), QPointF(0,0));
    QCOMPARE(shape->absolutePosition(KoFlake::BottomRight), QPointF(10,20));
}

void TestSvgParser::testUnitPxResolution()
{
    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\""
            "    fill=\"none\" stroke=\"none\" stroke-width=\"10\"/>"

            "</svg>";

    SvgTester t (data);
    t.parser.setResolution(QRectF(0, 0, 600, 400) /* px */, 144 /* ppi */);
    t.run();

    KoShape *shape = t.findShape("testRect");
    QVERIFY(shape);

    QCOMPARE(shape->boundingRect(), QRectF(0,0,5,10));
    QCOMPARE(shape->absoluteTransformation(), QTransform::fromScale(0.5, 0.5));
    QCOMPARE(shape->outlineRect(), QRectF(0,0,10,20));
    QCOMPARE(shape->absolutePosition(KoFlake::TopLeft), QPointF(0,0));
    QCOMPARE(shape->absolutePosition(KoFlake::BottomRight), QPointF(5,10));
}


void TestSvgParser::testUnitPt()
{
    const QString data =
            "<svg width=\"10pt\" height=\"20pt\" 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\""
            "    fill=\"none\" stroke=\"none\" stroke-width=\"10\"/>"

            "</svg>";

    SvgTester t (data);
    t.parser.setResolution(QRectF(0, 0, 600, 400) /* px */, 666 /* ppi */);
    t.run();

    KoShape *shape = t.findShape("testRect");
    QVERIFY(shape);

    QCOMPARE(shape->boundingRect(), kisGrowRect(QRectF(0,0,10,20), 0));
    QCOMPARE(shape->absoluteTransformation(), QTransform());
    QCOMPARE(shape->outlineRect(), QRectF(0,0,10,20));
    QCOMPARE(shape->absolutePosition(KoFlake::TopLeft), QPointF(0,0));
    QCOMPARE(shape->absolutePosition(KoFlake::BottomRight), QPointF(10,20));
}

void TestSvgParser::testUnitIn()
{
    const QString data =
            "<svg width=\"10in\" height=\"20in\" 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\""
            "    fill=\"none\" stroke=\"none\" stroke-width=\"10\"/>"

            "</svg>";

    SvgTester t (data);
    t.parser.setResolution(QRectF(0, 0, 600, 400) /* px */, 666 /* ppi */);
    t.run();

    KoShape *shape = t.findShape("testRect");
    QVERIFY(shape);

    QCOMPARE(shape->boundingRect(), QRectF(0,0,720,1440));
    QCOMPARE(shape->absoluteTransformation(), QTransform::fromScale(72, 72));
    QCOMPARE(shape->outlineRect(), QRectF(0,0,10,20));
    QCOMPARE(shape->absolutePosition(KoFlake::TopLeft), QPointF(0,0));
    QCOMPARE(shape->absolutePosition(KoFlake::BottomRight), QPointF(720,1440));
}

void TestSvgParser::testUnitPercentInitial()
{
    const QString data =
            "<svg width=\"12.5%\" height=\"25%\" 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\""
            "    fill=\"none\" stroke=\"none\" stroke-width=\"10\"/>"

            "</svg>";

    SvgTester t (data);
    t.parser.setResolution(QRectF(0, 0, 80, 80) /* px */, 144 /* ppi */);
    t.run();

    KoShape *shape = t.findShape("testRect");
    QVERIFY(shape);

    QCOMPARE(shape->boundingRect(), QRectF(0,0,5,10));
    QCOMPARE(shape->absoluteTransformation(), QTransform::fromScale(0.5, 0.5));
    QCOMPARE(shape->outlineRect(), QRectF(0,0,10,20));
    QCOMPARE(shape->absolutePosition(KoFlake::TopLeft), QPointF(0,0));
    QCOMPARE(shape->absolutePosition(KoFlake::BottomRight), QPointF(5,10));
}

void TestSvgParser::testScalingViewport()
{
    const QString data =
            "<svg width=\"10px\" height=\"20px\" viewBox=\"60 70 20 40\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<rect id=\"testRect\" x=\"64\" y=\"74\" width=\"12\" height=\"32\""
            "    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(), QTransform::fromTranslate(4, 4) * QTransform::fromScale(0.5, 0.5));
    QCOMPARE(shape->outlineRect(), QRectF(0,0,12,32));
    QCOMPARE(shape->absolutePosition(KoFlake::TopLeft), QPointF(2,2));
    QCOMPARE(shape->absolutePosition(KoFlake::TopRight), QPointF(8,2));
    QCOMPARE(shape->absolutePosition(KoFlake::BottomLeft), QPointF(2,18));
    QCOMPARE(shape->absolutePosition(KoFlake::BottomRight), QPointF(8,18));
}

void TestSvgParser::testScalingViewportKeepMeet1()
{
    const QString data =
            "<svg width=\"10px\" height=\"30px\" viewBox=\"60 70 20 40\""
            "    preserveAspectRatio=\" xMinYMin meet\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<rect id=\"testRect\" x=\"64\" y=\"74\" width=\"12\" height=\"32\""
            "    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(), QTransform::fromTranslate(4, 4) * QTransform::fromScale(0.5, 0.5));
    QCOMPARE(shape->outlineRect(), QRectF(0,0,12,32));
    QCOMPARE(shape->absolutePosition(KoFlake::TopLeft), QPointF(2,2));
    QCOMPARE(shape->absolutePosition(KoFlake::TopRight), QPointF(8,2));
    QCOMPARE(shape->absolutePosition(KoFlake::BottomLeft), QPointF(2,18));
    QCOMPARE(shape->absolutePosition(KoFlake::BottomRight), QPointF(8,18));
}

void TestSvgParser::testScalingViewportKeepMeet2()
{
    const QString data =
            "<svg width=\"15px\" height=\"20px\" viewBox=\"60 70 20 40\""
            "    preserveAspectRatio=\" xMinYMin meet\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<rect id=\"testRect\" x=\"64\" y=\"74\" width=\"12\" height=\"32\""
            "    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(), QTransform::fromTranslate(4, 4) * QTransform::fromScale(0.5, 0.5));
    QCOMPARE(shape->outlineRect(), QRectF(0,0,12,32));
    QCOMPARE(shape->absolutePosition(KoFlake::TopLeft), QPointF(2,2));
    QCOMPARE(shape->absolutePosition(KoFlake::TopRight), QPointF(8,2));
    QCOMPARE(shape->absolutePosition(KoFlake::BottomLeft), QPointF(2,18));
    QCOMPARE(shape->absolutePosition(KoFlake::BottomRight), QPointF(8,18));
}

void TestSvgParser::testScalingViewportKeepMeetAlign()
{
    const QString data =
            "<svg width=\"10px\" height=\"30px\" viewBox=\"60 70 20 40\""
            "    preserveAspectRatio=\" xMaxYMax meet\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<rect id=\"testRect\" x=\"64\" y=\"74\" width=\"12\" height=\"32\""
            "    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(), QTransform::fromTranslate(4, 24) * QTransform::fromScale(0.5, 0.5));
    QCOMPARE(shape->outlineRect(), QRectF(0,0,12,32));
    QCOMPARE(shape->absolutePosition(KoFlake::TopLeft), QPointF(2,12));
    QCOMPARE(shape->absolutePosition(KoFlake::TopRight), QPointF(8,12));
    QCOMPARE(shape->absolutePosition(KoFlake::BottomLeft), QPointF(2,28));
    QCOMPARE(shape->absolutePosition(KoFlake::BottomRight), QPointF(8,28));
}

void TestSvgParser::testScalingViewportKeepSlice1()
{
    const QString data =
            "<svg width=\"5px\" height=\"20px\" viewBox=\"60 70 20 40\""
            "    preserveAspectRatio=\" xMinYMin slice\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<rect id=\"testRect\" x=\"64\" y=\"74\" width=\"12\" height=\"32\""
            "    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(), QTransform::fromTranslate(4, 4) * QTransform::fromScale(0.5, 0.5));
    QCOMPARE(shape->outlineRect(), QRectF(0,0,12,32));
    QCOMPARE(shape->absolutePosition(KoFlake::TopLeft), QPointF(2,2));
    QCOMPARE(shape->absolutePosition(KoFlake::TopRight), QPointF(8,2));
    QCOMPARE(shape->absolutePosition(KoFlake::BottomLeft), QPointF(2,18));
    QCOMPARE(shape->absolutePosition(KoFlake::BottomRight), QPointF(8,18));
}

void TestSvgParser::testScalingViewportKeepSlice2()
{
    const QString data =
            "<svg width=\"10px\" height=\"15px\" viewBox=\"60 70 20 40\""
            "    preserveAspectRatio=\" xMinYMin slice\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<rect id=\"testRect\" x=\"64\" y=\"74\" width=\"12\" height=\"32\""
            "    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(), QTransform::fromTranslate(4, 4) * QTransform::fromScale(0.5, 0.5));
    QCOMPARE(shape->outlineRect(), QRectF(0,0,12,32));
    QCOMPARE(shape->absolutePosition(KoFlake::TopLeft), QPointF(2,2));
    QCOMPARE(shape->absolutePosition(KoFlake::TopRight), QPointF(8,2));
    QCOMPARE(shape->absolutePosition(KoFlake::BottomLeft), QPointF(2,18));
    QCOMPARE(shape->absolutePosition(KoFlake::BottomRight), QPointF(8,18));
}

void TestSvgParser::testScalingViewportResolution()
{
    const QString data =
            "<svg width=\"10px\" height=\"20px\" viewBox=\"60 70 20 40\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<rect id=\"testRect\" x=\"64\" y=\"74\" width=\"12\" height=\"32\""
            "    fill=\"none\" stroke=\"none\" stroke-width=\"10\"/>"

            "</svg>";

    SvgTester t (data);
    t.parser.setResolution(QRectF(0, 0, 600, 400) /* px */, 144 /* ppi */);
    t.run();

    KoShape *shape = t.findShape("testRect");
    QVERIFY(shape);

    QCOMPARE(shape->absoluteTransformation(), QTransform::fromTranslate(4, 4) * QTransform::fromScale(0.25, 0.25));
    QCOMPARE(shape->outlineRect(), QRectF(0,0,12,32));
    QCOMPARE(shape->absolutePosition(KoFlake::TopLeft), QPointF(1,1));
    QCOMPARE(shape->absolutePosition(KoFlake::TopRight), QPointF(4,1));
    QCOMPARE(shape->absolutePosition(KoFlake::BottomLeft), QPointF(1,9));
    QCOMPARE(shape->absolutePosition(KoFlake::BottomRight), QPointF(4,9));
}

void TestSvgParser::testScalingViewportPercentInternal()
{
    const QString data =
            "<svg width=\"10px\" height=\"20px\" viewBox=\"60 70 20 40\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<rect id=\"testRect\" x=\"320%\" y=\"185%\" width=\"60%\" height=\"80%\""
            "    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(), QTransform::fromTranslate(4, 4) * QTransform::fromScale(0.5, 0.5));
    QCOMPARE(shape->outlineRect(), QRectF(0,0,12,32));
    QCOMPARE(shape->absolutePosition(KoFlake::TopLeft), QPointF(2,2));
    QCOMPARE(shape->absolutePosition(KoFlake::TopRight), QPointF(8,2));
    QCOMPARE(shape->absolutePosition(KoFlake::BottomLeft), QPointF(2,18));
    QCOMPARE(shape->absolutePosition(KoFlake::BottomRight), QPointF(8,18));
}


void TestSvgParser::testParsePreserveAspectRatio()
{
    {
        SvgUtil::PreserveAspectRatioParser p(" defer  xMinYMax meet");
        QCOMPARE(p.defer, true);
        QCOMPARE(p.mode, Qt::KeepAspectRatio);
        QCOMPARE(p.xAlignment, SvgUtil::PreserveAspectRatioParser::Min);
        QCOMPARE(p.yAlignment, SvgUtil::PreserveAspectRatioParser::Max);
    }

    {
        SvgUtil::PreserveAspectRatioParser p(" xMinYMid slice");
        QCOMPARE(p.defer, false);
        QCOMPARE(p.mode, Qt::KeepAspectRatioByExpanding);
        QCOMPARE(p.xAlignment, SvgUtil::PreserveAspectRatioParser::Min);
        QCOMPARE(p.yAlignment, SvgUtil::PreserveAspectRatioParser::Middle);
    }

    {
        SvgUtil::PreserveAspectRatioParser p(" xmidYMid ");
        QCOMPARE(p.defer, false);
        QCOMPARE(p.mode, Qt::KeepAspectRatio);
        QCOMPARE(p.xAlignment, SvgUtil::PreserveAspectRatioParser::Middle);
        QCOMPARE(p.yAlignment, SvgUtil::PreserveAspectRatioParser::Middle);
    }

    {
        SvgUtil::PreserveAspectRatioParser p(" NoNe ");
        QCOMPARE(p.defer, false);
        QCOMPARE(p.mode, Qt::IgnoreAspectRatio);
        QCOMPARE(p.xAlignment, SvgUtil::PreserveAspectRatioParser::Min);
        QCOMPARE(p.yAlignment, SvgUtil::PreserveAspectRatioParser::Min);
    }

    {
        SvgUtil::PreserveAspectRatioParser p("defer NoNe ");
        QCOMPARE(p.defer, true);
        QCOMPARE(p.mode, Qt::IgnoreAspectRatio);
        QCOMPARE(p.xAlignment, SvgUtil::PreserveAspectRatioParser::Min);
        QCOMPARE(p.yAlignment, SvgUtil::PreserveAspectRatioParser::Min);
    }

    {
        SvgUtil::PreserveAspectRatioParser p("sweet brown fox jumps over a nice svg file");
        QCOMPARE(p.defer, false);
        QCOMPARE(p.mode, Qt::IgnoreAspectRatio);
        QCOMPARE(p.xAlignment, SvgUtil::PreserveAspectRatioParser::Min);
        QCOMPARE(p.yAlignment, SvgUtil::PreserveAspectRatioParser::Min);
    }
}

#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 'viewBox' affects only the descendants!
     */

    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(), QTransform::fromTranslate(10, 4) * QTransform::fromScale(0.5, 0.5));
    QCOMPARE(shape->outlineRect(), QRectF(0,0,12,32));
    QCOMPARE(shape->absolutePosition(KoFlake::TopLeft), QPointF(5,2));
    QCOMPARE(shape->absolutePosition(KoFlake::TopRight), QPointF(11,2));
    QCOMPARE(shape->absolutePosition(KoFlake::BottomLeft), QPointF(5,18));
    QCOMPARE(shape->absolutePosition(KoFlake::BottomRight), 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,10,20,20));
    QCOMPARE(shape->outlineRect(), QRectF(0,0,10,20));
    QCOMPARE(shape->absolutePosition(KoFlake::TopLeft), QPointF(10,10));
    QCOMPARE(shape->absolutePosition(KoFlake::BottomRight), QPointF(30,30));
}

void TestSvgParser::testTransformNestingGroups()
{
    const QString data =
            "<svg width=\"10px\" height=\"20px\" viewBox=\"0 0 10 20\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<g transform=\"translate(10,10)\">"
            "    <rect id=\"testRect\" x=\"0\" y=\"0\" width=\"10\" height=\"20\""
            "        transform=\"scale(2, 1)\""
            "        fill=\"none\" stroke=\"none\" stroke-width=\"10\"/>"
            "</g>"

            "</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,10, 20, 20));
    QCOMPARE(shape->outlineRect(), QRectF(0,0,10,20));
    QCOMPARE(shape->absolutePosition(KoFlake::TopLeft), QPointF(10,10));
    QCOMPARE(shape->absolutePosition(KoFlake::BottomRight), 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(), QRectF(-20,0,20,10));
    QCOMPARE(shape->outlineRect(), QRectF(0,0,10,20));
    QCOMPARE(shape->absolutePosition(KoFlake::TopLeft), QPointF(0,0));
    QCOMPARE(shape->absolutePosition(KoFlake::BottomRight), 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(), QRectF(5,5,20,10));
    QCOMPARE(shape->outlineRect(), QRectF(0,0,10,20));
    QCOMPARE(shape->absolutePosition(KoFlake::TopLeft), QPointF(5,15));
    QCOMPARE(shape->absolutePosition(KoFlake::BottomRight), QPointF(25,5));
}



void TestSvgParser::testRenderStrokeNone()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<rect id=\"testRect\" x=\"5\" y=\"5\" width=\"10\" height=\"20\""
            "    fill=\"cyan\" stroke=\"none\" stroke-width=\"1\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("stroke_none");
}

void TestSvgParser::testRenderStrokeColorName()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<rect id=\"testRect\" x=\"5\" y=\"5\" width=\"10\" height=\"20\""
            "    fill=\"cyan\" stroke=\"blue\" stroke-width=\"1\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("stroke_blue");
}

void TestSvgParser::testRenderStrokeColorHex3()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<rect id=\"testRect\" x=\"5\" y=\"5\" width=\"10\" height=\"20\""
            "    fill=\"cyan\" stroke=\"#00f\" stroke-width=\"1\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("stroke_blue");
}

void TestSvgParser::testRenderStrokeColorHex6()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<rect id=\"testRect\" x=\"5\" y=\"5\" width=\"10\" height=\"20\""
            "    fill=\"cyan\" stroke=\"#0000ff\" stroke-width=\"1\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("stroke_blue");
}

void TestSvgParser::testRenderStrokeColorRgbValues()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<rect id=\"testRect\" x=\"5\" y=\"5\" width=\"10\" height=\"20\""
            "    fill=\"cyan\" stroke=\"rgb(0, 0 ,255)\" stroke-width=\"1\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("stroke_blue");
}

void TestSvgParser::testRenderStrokeColorRgbPercent()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<rect id=\"testRect\" x=\"5\" y=\"5\" width=\"10\" height=\"20\""
            "    fill=\"cyan\" stroke=\"rgb(0, 0 ,100%)\" stroke-width=\"1\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("stroke_blue");
}

void TestSvgParser::testRenderStrokeColorCurrent()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<g color=\"blue\">"
            "    <rect id=\"testRect\" x=\"5\" y=\"5\" width=\"10\" height=\"20\""
            "        fill=\"cyan\" stroke=\"currentColor\" stroke-width=\"1\"/>"
            "</g>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("stroke_blue");
}

void TestSvgParser::testRenderStrokeColorNonexistentIri()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<rect id=\"testRect\" x=\"5\" y=\"5\" width=\"10\" height=\"20\""
            "    fill=\"cyan\" stroke=\"url(notexists) blue\" stroke-width=\"1\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("stroke_blue");
}

void TestSvgParser::testRenderStrokeWidth()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<rect id=\"testRect\" x=\"5\" y=\"5\" width=\"10\" height=\"20\""
            "    fill=\"cyan\" stroke=\"blue\" stroke-width=\"2\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("stroke_blue_width_2");
}

void TestSvgParser::testRenderStrokeZeroWidth()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<rect id=\"testRect\" x=\"5\" y=\"5\" width=\"10\" height=\"20\""
            "    fill=\"cyan\" stroke=\"blue\" stroke-width=\"0\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("stroke_none");
}

void TestSvgParser::testRenderStrokeOpacity()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<rect id=\"testRect\" x=\"5\" y=\"5\" width=\"10\" height=\"20\""
            "    fill=\"cyan\" stroke=\"blue\" stroke-width=\"4\" stroke-opacity=\"0.3\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.setFuzzyThreshold(1);
    t.test_standard_30px_72ppi("stroke_blue_0_3_opacity");
}

void TestSvgParser::testRenderStrokeJointRound()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<rect id=\"testRect\" x=\"5\" y=\"5\" width=\"10\" height=\"20\""
            "    fill=\"cyan\" stroke=\"blue\" stroke-width=\"4\" stroke-linejoin=\"round\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("stroke_blue_join_round");
}

void TestSvgParser::testRenderStrokeLinecap()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<polyline id=\"testRect\" points=\"5,5 10,25 15,5\""
            "    fill=\"none\" stroke=\"blue\" stroke-width=\"5\" stroke-linecap=\"round\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("stroke_blue_linecap_round");
}

void TestSvgParser::testRenderStrokeMiterLimit()
{
    // TODO:seems like doesn't work!!
    qWarning() << "WARNING: Miter limit test is skipped!!!";
    return;

    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<polyline id=\"testRect\" points=\"5,5 10,25 15,5\""
            "    fill=\"none\" stroke=\"blue\" stroke-width=\"5\" stroke-miterlimit=\"1.114\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("stroke_blue_miter_limit");
}

void TestSvgParser::testRenderStrokeDashArrayEven()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<rect id=\"testRect\" x=\"5\" y=\"5\" width=\"10\" height=\"20\""
            "    fill=\"cyan\" stroke=\"blue\" stroke-width=\"2\" stroke-dasharray=\"3 2, 5 2\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("stroke_blue_dasharray_even");
}

void TestSvgParser::testRenderStrokeDashArrayEvenOffset()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<rect id=\"testRect\" x=\"5\" y=\"5\" width=\"10\" height=\"20\""
            "    fill=\"cyan\" stroke=\"blue\" stroke-width=\"2\" stroke-dasharray=\"3 2, 5 2\""
            "    stroke-dashoffset=\"1\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("stroke_blue_dasharray_even_offset");
}

void TestSvgParser::testRenderStrokeDashArrayOdd()
{
    // SVG 1.1: if the dasharray is odd, repeat it

    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<rect id=\"testRect\" x=\"5\" y=\"5\" width=\"10\" height=\"20\""
            "    fill=\"cyan\" stroke=\"blue\" stroke-width=\"2\" stroke-dasharray=\"3 2, 5\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("stroke_blue_dasharray_odd");
}

void TestSvgParser::testRenderStrokeDashArrayRelative()
{
    // SVG 1.1: relative to view box
    // (40 x 50) * sqrt(2) => dash length = 5 px

    const QString data =
            "<svg width=\"42.4264px\" height=\"56.56854px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<rect id=\"testRect\" x=\"5\" y=\"5\" width=\"10\" height=\"20\""
            "    fill=\"cyan\" stroke=\"blue\" stroke-width=\"2\" stroke-dasharray=\"10% 10%\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("stroke_blue_dasharray_relative");
}

void TestSvgParser::testRenderFillDefault()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<rect id=\"testRect\" x=\"5\" y=\"5\" width=\"10\" height=\"20\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("fill_black");
}

void TestSvgParser::testRenderFillRuleNonZero()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<polyline id=\"testRect\" points=\"5,5 15,11 15,19 5,25 5,19 15,5 15,25 5,11 5,5\""
            "    fill=\"black\" fill-rule=\"nonzero\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("fill_non_zero");
}

void TestSvgParser::testRenderFillRuleEvenOdd()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<polyline id=\"testRect\" points=\"5,5 15,11 15,19 5,25 5,19 15,5 15,25 5,11 5,5\""
            "    fill=\"black\" fill-rule=\"evenodd\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("fill_even_odd");
}

void TestSvgParser::testRenderFillOpacity()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<rect id=\"testRect\" x=\"5\" y=\"5\" width=\"10\" height=\"20\""
            "    fill=\"cyan\" fill-opacity=\"0.3\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.setFuzzyThreshold(1);
    t.test_standard_30px_72ppi("fill_opacity_0_3");
}

void TestSvgParser::testRenderDisplayAttribute()
{
    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\""
            "    fill=\"black\" display=\"none\"/>"

            "</svg>";

    SvgTester t (data);
    t.parser.setResolution(QRectF(0, 0, 600, 400) /* px */, 144 /* ppi */);
    t.run();

    KoShape *shape = t.findShape("testRect");
    QVERIFY(shape);

    QCOMPARE(shape->isVisible(), false);
}

void TestSvgParser::testRenderVisibilityAttribute()
{
    {
        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\""
                "    fill=\"black\" visibility=\"visible\"/>"

                "</svg>";

        SvgTester t (data);
        t.parser.setResolution(QRectF(0, 0, 600, 400) /* px */, 144 /* ppi */);
        t.run();

        KoShape *shape = t.findShape("testRect");
        QVERIFY(shape);

        QCOMPARE(shape->isVisible(), true);
    }

    {
        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\""
                "    fill=\"black\" visibility=\"hidden\"/>"

                "</svg>";

        SvgTester t (data);
        t.parser.setResolution(QRectF(0, 0, 600, 400) /* px */, 144 /* ppi */);
        t.run();

        KoShape *shape = t.findShape("testRect");
        QVERIFY(shape);

        QCOMPARE(shape->isVisible(), false);
    }

    {
        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\""
                "    fill=\"black\" visibility=\"collapse\"/>"

                "</svg>";

        SvgTester t (data);
        t.parser.setResolution(QRectF(0, 0, 600, 400) /* px */, 144 /* ppi */);
        t.run();

        KoShape *shape = t.findShape("testRect");
        QVERIFY(shape);

        QCOMPARE(shape->isVisible(), false);
    }
}

void TestSvgParser::testRenderVisibilityInheritance()
{
    const QString data =
            "<svg width=\"10px\" height=\"20px\" viewBox=\"0 0 10 20\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<g visibility=\"none\">"
            "    <rect id=\"testRect\" x=\"0\" y=\"0\" width=\"10\" height=\"20\""
            "        fill=\"black\" visibility=\"visible\"/>"
            "</g>"

            "</svg>";

    SvgTester t (data);
    t.parser.setResolution(QRectF(0, 0, 600, 400) /* px */, 144 /* ppi */);
    t.run();

    KoShape *shape = t.findShape("testRect");
    QVERIFY(shape);

    QCOMPARE(shape->isVisible(false), true);
    QCOMPARE(shape->isVisible(true), false);
}

void TestSvgParser::testRenderDisplayInheritance()
{
    const QString data =
            "<svg width=\"10px\" height=\"20px\" viewBox=\"0 0 10 20\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<g display=\"none\">"
            "    <rect id=\"testRect\" x=\"0\" y=\"0\" width=\"10\" height=\"20\""
            "        fill=\"black\" visibility=\"visible\"/>"
            "</g>"

            "</svg>";

    SvgTester t (data);
    t.parser.setResolution(QRectF(0, 0, 600, 400) /* px */, 144 /* ppi */);
    t.run();

    KoShape *shape = t.findShape("testRect");
    QVERIFY(shape);

    QCOMPARE(shape->isVisible(false), true);
    QEXPECT_FAIL("", "TODO: Fix 'display' attribute not to be inherited in shapes hierarchy!", Continue);
    QCOMPARE(shape->isVisible(true), true);
}

void TestSvgParser::testRenderStrokeWithInlineStyle()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<rect id=\"testRect\" x=\"5\" y=\"5\" width=\"10\" height=\"20\""
            "    style = \"fill: cyan; stroke :blue; stroke-width:2;\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("stroke_blue_width_2");
}

void TestSvgParser::testIccColor()
{
    struct ScopedProfileRemover
    {
        ScopedProfileRemover()
            : m_profile(KoColorSpaceRegistry::instance()->profileByUniqueId(QByteArray::fromHex("133a66607cffeebdd64dd433ada9bf4e")))
        {
            if (m_profile) {
                qWarning() << "Profile already loaded, removing profile before test";
                KoColorProfile *clone = m_profile->clone();
                KoColorSpaceRegistry::instance()->removeProfile(clone);
                m_profile = clone;
            }
        }
        ~ScopedProfileRemover() {
            if (m_profile) {
                qWarning() << "Restoring removed profile after test";
                KoColorSpaceRegistry::instance()->addProfile(m_profile);
                delete m_profile;
            }
        }
        const KoColorProfile *m_profile;
    };
    ScopedProfileRemover _profileRemover;

    // This test works because the icc-color won't be loaded unless there's a profile for it,
    // and the fill will be red if the icc-color is not loaded (it should be cyan).
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<g xml:base=\"icc\">"
            "    <color-profile xlink:href=\"sRGB-elle-V4-srgbtrc.icc\""
            "        local=\"133a66607cffeebdd64dd433ada9bf4e\" name=\"default-profile\"/>"

            "    <color-profile xlink:href=\"sRGB-elle-V4-srgbtrc.icc\""
            "        local=\"133a66607cffeebdd64dd433ada9bf4e\" name=\"some-other-name\"/>"

            "    <rect id=\"testRect\" x=\"5\" y=\"5\" width=\"10\" height=\"20\""
            "        style = \"fill: red icc-color(default-profile, 0, 1, 1); stroke :blue; stroke-width:2;\"/>"
            "</g>"

            "</svg>";

    SvgRenderTester t (data);

    int numFetches = 0;

    t.parser.setFileFetcher(
        [&numFetches](const QString &name) {
            numFetches++;
            QString fileName = TestUtil::fetchDataFileLazy(name);
            if (fileName.isEmpty()) {
                fileName = TestUtil::fetchDataFileLazy("icc/" + name);
            }
            QFile file(fileName);
            KIS_ASSERT(file.exists());
            file.open(QIODevice::ReadOnly);
            return file.readAll();
        });

    t.test_standard_30px_72ppi("stroke_blue_width_2");

    KoShape *shape = t.findShape("testRect");
    if (shape) {
        QSharedPointer<KoColorBackground>  bg = qSharedPointerDynamicCast<KoColorBackground>(shape->background());
        QVERIFY2(bg->color() == QColor("#00FFFF"), "icc-color is not being loaded during parsing");
    }
    QCOMPARE(numFetches, 1);
}

void TestSvgParser::testRenderFillLinearGradientRelativePercent()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<defs>"
            "    <linearGradient id=\"testGrad\" x1=\"0%\" y1=\"50%\" x2=\"100%\" y2=\"50%\">"
            "        <stop offset=\"20%\" stop-color=\"#F60\" />"
            "        <stop offset=\"80%\" stop-color=\"#FF6\" />"
            "    </linearGradient>"
            "</defs>"

            "<rect id=\"testRect\" x=\"5\" y=\"5\" width=\"10\" height=\"20\""
            "    style = \"fill:url(#testGrad) magenta; stroke:none; stroke-width:2;\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("fill_gradient");
}

void TestSvgParser::testRenderFillLinearGradientRelativePortion()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<linearGradient id=\"testGrad\" x1=\"0\" y1=\"0.5\" x2=\"1.0\" y2=\"0.5\">"
            "    <stop offset=\"20%\" stop-color=\"#F60\" />"
            "    <stop offset=\"80%\" stop-color=\"#FF6\" />"
            "</linearGradient>"

            "<rect id=\"testRect\" x=\"5\" y=\"5\" width=\"10\" height=\"20\""
            "    style = \"fill:url(#testGrad) magenta; stroke:none; stroke-width:2;\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("fill_gradient");
}

void TestSvgParser::testRenderFillLinearGradientUserCoord()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\" viewBox=\"60 70 60 90\""
            "        preserveAspectRatio=\"none meet\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<linearGradient id=\"testGrad\" x1=\"70\" y1=\"115\" x2=\"90\" y2=\"115\""
            "    gradientUnits=\"userSpaceOnUse\">"
            "    <stop offset=\"20%\" stop-color=\"#F60\" />"
            "    <stop offset=\"80%\" stop-color=\"#FF6\" />"
            "</linearGradient>"

            "<rect id=\"testRect\" x=\"70\" y=\"85\" width=\"20\" height=\"60\""
            "    fill=\"url(#testGrad) magenta\" stroke=\"none\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("fill_gradient");
}

void TestSvgParser::testRenderFillLinearGradientStopPortion()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<linearGradient id=\"testGrad\" x1=\"0\" y1=\"0.5\" x2=\"1.0\" y2=\"0.5\">"
            "    <stop offset=\"0.2\" stop-color=\"#F60\" />"
            "    <stop offset=\"0.8\" stop-color=\"#FF6\" />"
            "</linearGradient>"

            "<rect id=\"testRect\" x=\"5\" y=\"5\" width=\"10\" height=\"20\""
            "    style = \"fill:url(#testGrad) magenta; stroke:none; stroke-width:2;\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("fill_gradient");
}

void TestSvgParser::testRenderFillLinearGradientTransform()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<linearGradient id=\"testGrad\" x1=\"0\" y1=\"0.5\" x2=\"1.0\" y2=\"0.5\""
            "    gradientTransform=\"rotate(90, 0.5, 0.5)\">"

            "    <stop offset=\"0.2\" stop-color=\"#F60\" />"
            "    <stop offset=\"0.8\" stop-color=\"#FF6\" />"
            "</linearGradient>"

            "<rect id=\"testRect\" x=\"5\" y=\"5\" width=\"10\" height=\"20\""
            "    style = \"fill:url(#testGrad) magenta; stroke:none; stroke-width:2;\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("fill_gradient_vertical");
}

void TestSvgParser::testRenderFillLinearGradientTransformUserCoord()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\" viewBox=\"60 70 60 90\""
            "        preserveAspectRatio=\"none meet\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<linearGradient id=\"testGrad\" x1=\"70\" y1=\"115\" x2=\"90\" y2=\"115\""
            "    gradientUnits=\"userSpaceOnUse\""
            "    gradientTransform=\"rotate(90, 80, 115)\">"

            "    <stop offset=\"20%\" stop-color=\"#F60\" />"
            "    <stop offset=\"80%\" stop-color=\"#FF6\" />"
            "</linearGradient>"

            "<rect id=\"testRect\" x=\"70\" y=\"85\" width=\"20\" height=\"60\""
            "    fill=\"url(#testGrad) magenta\" stroke=\"none\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("fill_gradient_vertical_in_user");
}

void TestSvgParser::testRenderFillLinearGradientRotatedShape()
{
    // DK: I'm not sure I fully understand if it is a correct transformation,
    //     but inkscape opens the file in the same way...

    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<linearGradient id=\"testGrad\" x1=\"0\" y1=\"0.5\" x2=\"1.0\" y2=\"0.5\""
            "    gradientTransform=\"rotate(90, 0.5, 0.5)\">"

            "    <stop offset=\"0.2\" stop-color=\"#F60\" />"
            "    <stop offset=\"0.8\" stop-color=\"#FF6\" />"
            "</linearGradient>"

            "<rect id=\"testRect\" x=\"5\" y=\"5\" width=\"10\" height=\"20\""
            "    style = \"fill:url(#testGrad) magenta; stroke:none; stroke-width:2;\""
            "    transform=\"rotate(90, 10, 12.5)\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("fill_gradient_shape_rotated", false);
}

void TestSvgParser::testRenderFillLinearGradientRotatedShapeUserCoord()
{
    // DK: I'm not sure I fully understand if it is a correct transformation,
    //     but inkscape opens the file in the same way...

    const QString data =
            "<svg width=\"30px\" height=\"30px\" viewBox=\"60 70 60 90\""
            "        preserveAspectRatio=\"none meet\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<linearGradient id=\"testGrad\" x1=\"70\" y1=\"115\" x2=\"90\" y2=\"115\""
            "    gradientUnits=\"userSpaceOnUse\">"

            "    <stop offset=\"20%\" stop-color=\"#F60\" />"
            "    <stop offset=\"80%\" stop-color=\"#FF6\" />"
            "</linearGradient>"

            "<rect id=\"testRect\" x=\"70\" y=\"85\" width=\"20\" height=\"60\""
            "    fill=\"url(#testGrad) magenta\" stroke=\"none\""
            "    transform=\"rotate(90, 80, 115)\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("fill_gradient_shape_rotated_in_user", false);
}

void TestSvgParser::testRenderFillLinearGradientTransparent()
{
    const QString data =
            "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"30px\" height=\"30px\" "
            "    version=\"1.1\">"
            ""
            "  <linearGradient x1=\"0\" y1=\"15\" x2=\"30\" y2=\"15\""
            "      gradientUnits=\"userSpaceOnUse\" id=\"linearGradient5574\" >"
            ""
            "      <stop style=\"stop-color:#0000ff;stop-opacity:1\" offset=\"0\" />"
            "      <stop style=\"stop-color:#ff0000;stop-opacity:0\" offset=\"1\" />"
            ""
            "  </linearGradient>"
            ""
            "  <rect id=\"testRect\" style=\"fill:url(#linearGradient5574);fill-opacity:1\""
            "        width=\"30\" height=\"30\" x=\"0\" y=\"0\"/>"
            "</svg>";

    SvgRenderTester t(data);
    t.setCheckQImagePremultiplied(true);
    t.test_standard_30px_72ppi("fill_gradient_transparent", false);
}

void TestSvgParser::testRenderFillRadialGradient()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<radialGradient id=\"testGrad\" cx=\"0.5\" cy=\"0.5\" fx=\"0.2\" fy=\"0.2\" r=\"0.5\">"
            "    <stop offset=\"0.2\" stop-color=\"#F60\" />"
            "    <stop offset=\"0.8\" stop-color=\"#FF6\" />"
            "</radialGradient>"

            "<rect id=\"testRect\" x=\"5\" y=\"5\" width=\"10\" height=\"20\""
            "    style = \"fill:url(#testGrad) magenta; stroke:none; stroke-width:2;\"/>"
            "</svg>";

    SvgRenderTester t (data);
    t.setFuzzyThreshold(1);
    t.test_standard_30px_72ppi("fill_gradient_radial");
}

void TestSvgParser::testRenderFillRadialGradientUserCoord()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<radialGradient id=\"testGrad\" cx=\"10\" cy=\"12.5\" fx=\"7\" fy=\"9\" r=\"5\""
            "    gradientUnits=\"userSpaceOnUse\">"

            "    <stop offset=\"0.2\" stop-color=\"#F60\" />"
            "    <stop offset=\"0.8\" stop-color=\"#FF6\" />"
            "</radialGradient>"

            "<rect id=\"testRect\" x=\"5\" y=\"5\" width=\"10\" height=\"20\""
            "    style = \"fill:url(#testGrad) magenta; stroke:none; stroke-width:2;\"/>"
            "</svg>";

    SvgRenderTester t (data);
    t.setFuzzyThreshold(1);
    t.test_standard_30px_72ppi("fill_gradient_radial_in_user");
}

void TestSvgParser::testRenderFillRadialGradientTransparent()
{
    const QString data =
            "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"30px\" height=\"30px\""
            "     version=\"1.1\" >"
            ""
            "    <radialGradient id=\"radialGradient10189\""
            "      cx=\"15\" cy=\"15\" fx=\"15\" fy=\"15\" r=\"15\""
            "      gradientUnits=\"userSpaceOnUse\" >"
            ""
            "      <stop style=\"stop-color:#0000ff;stop-opacity:1\" offset=\"0\" />"
            "      <stop style=\"stop-color:#ff0000;stop-opacity:0\" offset=\"1\" />"
            "    </radialGradient>"
            ""
            "  <rect"
            "    id=\"testRect\" style=\"fill:url(#radialGradient10189);fill-opacity:1\""
            "    width=\"30\" height=\"30\" x=\"0\" y=\"0\" />"
            "</svg>";

    SvgRenderTester t(data);
    t.setFuzzyThreshold(1);
    t.setCheckQImagePremultiplied(true);
    t.test_standard_30px_72ppi("fill_gradient_radial_transparent", false);
}

void TestSvgParser::testRenderFillLinearGradientUserCoordPercent()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\" viewBox=\"60 70 60 90\""
            "        preserveAspectRatio=\"none meet\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<linearGradient id=\"testGrad\" x1=\"116.667%\" y1=\"127.778%\" x2=\"150%\" y2=\"127.778%\""
            "    gradientUnits=\"userSpaceOnUse\">"
            "    <stop offset=\"20%\" stop-color=\"#F60\" />"
            "    <stop offset=\"80%\" stop-color=\"#FF6\" />"
            "</linearGradient>"

            "<rect id=\"testRect\" x=\"70\" y=\"85\" width=\"20\" height=\"60\""
            "    fill=\"url(#testGrad) magenta\" stroke=\"none\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("fill_gradient");
}

void TestSvgParser::testRenderStrokeLinearGradient()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<defs>"
            "    <linearGradient id=\"testGrad\" x1=\"0%\" y1=\"50%\" x2=\"100%\" y2=\"50%\">"
            "        <stop offset=\"20%\" stop-color=\"#F60\" />"
            "        <stop offset=\"80%\" stop-color=\"#FF6\" />"
            "    </linearGradient>"
            "</defs>"

            "<rect id=\"testRect\" x=\"5\" y=\"5\" width=\"10\" height=\"20\""
            "    style = \"grey; stroke:url(#testGrad); stroke-width:3; stroke-dasharray:3,1\"/>"

            "</svg>";

    SvgRenderTester t (data);
    t.test_standard_30px_72ppi("stroke_gradient_dashed");
}

QTransform rotateTransform(qreal degree, const QPointF &center) {
    QTransform rotate;
    rotate.rotate(degree);
    return
        QTransform::fromTranslate(-center.x(), -center.y()) *
        rotate *
        QTransform::fromTranslate(center.x(), center.y());
}

QTransform viewTransform(const QRectF &src, const QRectF &dst) {
    return QTransform::fromTranslate(-src.x(), -src.y()) *
            QTransform::fromScale(dst.width() / src.width(),
                                  dst.height() / src.height()) *
            QTransform::fromTranslate(dst.x(), dst.y());

}


void TestSvgParser::testRenderMeshGradient_bilinear_1by1_UserCoord()
{
    QString data =
        "<svg"
        "   width=\"50px\""
        "   height=\"50px\""
        "   version=\"1.1\""
        "   id=\"svg8\">"
        "  <defs"
        "     id=\"defs2\">"
        "    <meshgradient"
        "       id=\"meshgradient901\""
        "       gradientUnits=\"userSpaceOnUse\""
        "       x=\"0\""
        "       y=\"0\">"
        "      <meshrow"
        "         id=\"meshrow903\">"
        "        <meshpatch"
        "           id=\"meshpatch905\">"
        "          <stop"
        "             path=\"c 15,0  30,0  45,0\""
        "             style=\"stop-color:#ffffff;stop-opacity:1\""
        "             id=\"stop907\" />"
        "          <stop"
        "             path=\"c 0,15  0,30  0,45\""
        "             style=\"stop-color:#ff0000;stop-opacity:1\""
        "             id=\"stop909\" />"
        "          <stop"
        "             path=\"c -15,0  -30,0  -45,0\""
        "             style=\"stop-color:#ffffff;stop-opacity:1\""
        "             id=\"stop911\" />"
        "          <stop"
        "             path=\"c 0,-15  0,-30  0,-45\""
        "             style=\"stop-color:#ff0000;stop-opacity:1\""
        "             id=\"stop913\" />"
        "        </meshpatch>"
        "      </meshrow>"
        "    </meshgradient>"
        "  </defs>"
        "  <rect"
        "     style=\"fill:url(#meshgradient901);fill-opacity:1;filter:url(#filter859)\""
        "     id=\"testRect\""
        "     width=\"45\""
        "     height=\"45\""
        "     x=\"0\""
        "     y=\"0\" />"
        "</svg>";


    SvgRenderTester t(data);
    t.setFuzzyThreshold(5);
    t.test_standard("meshgradient_bilinear_1by1_in_user", QSize(50, 50), 72);
}

void TestSvgParser::testRenderMeshGradient_bicubic_1by1_UserCoord()
{
    QString data =
        "<svg id=\"svg8\""
        "   version=\"1.1\""
        "   height=\"100px\""
        "   width=\"100px\">"
        "  <defs"
        "     id=\"defs2\">"
        "    <meshgradient"
        "       type=\"bicubic\""
        "       y=\"-10\""
        "       x=\"-10\""
        "       gradientUnits=\"userSpaceOnUse\""
        "       id=\"meshgradient1989\">"
        "      <meshrow"
        "         id=\"meshrow1991\">"
        "        <meshpatch"
        "           id=\"meshpatch1993\">"
        "          <stop"
        "             id=\"stop1995\""
        "             style=\"stop-color:#ffffff;stop-opacity:1\""
        "             path=\"c 30,0  60,0  90,0\" />"
        "          <stop"
        "             id=\"stop1997\""
        "             style=\"stop-color:#ff0000;stop-opacity:1\""
        "             path=\"c 0,30  0,60  0,90\" />"
        "          <stop"
        "             id=\"stop1999\""
        "             style=\"stop-color:#ffffff;stop-opacity:1\""
        "             path=\"c -30,0  -60,0  -90,0\" />"
        "          <stop"
        "             id=\"stop2001\""
        "             style=\"stop-color:#ff0000;stop-opacity:1\""
        "             path=\"c 0,-30  0,-60  0,-90\" />"
        "        </meshpatch>"
        "      </meshrow>"
        "    </meshgradient>"
        "  </defs>"
        "  <rect"
        "     y=\"-10\""
        "     x=\"-10\""
        "     height=\"90\""
        "     width=\"90\""
        "     id=\"testRect\""
        "     style=\"fill:url(#meshgradient1989);fill-opacity:1\" />"
        "</svg>";


    SvgRenderTester t(data);
    t.setFuzzyThreshold(5);
    t.test_standard("meshgradient_bicubic_1by1_in_user", QSize(100, 100), 72);
}

void TestSvgParser::testRenderMeshGradient_bilinear_2by2_UserCoord()
{
    QString data =
        "<svg"
        "   version=\"1.1\""
        "   width=\"100px\""
        "   height=\"100px\""
        "   id=\"svg856\">"
        "  <defs"
        "     id=\"defs850\">"
        "    <meshgradient"
        "       y=\"0\""
        "       x=\"0\""
        "       gradientUnits=\"userSpaceOnUse\""
        "       id=\"meshgradient832\">"
        "      <meshrow"
        "         id=\"meshrow834\">"
        "        <meshpatch"
        "           id=\"meshpatch836\">"
        "          <stop"
        "             id=\"stop838\""
        "             style=\"stop-color:#ffffff;stop-opacity:1\""
        "             path=\"c 13.3333,0  26.6667,0  40,0\" />"
        "          <stop"
        "             id=\"stop840\""
        "             style=\"stop-color:#000000;stop-opacity:1\""
        "             path=\"c 0,13.3333  0,26.6667  0,40\" />"
        "          <stop"
        "             id=\"stop842\""
        "             style=\"stop-color:#ffffff;stop-opacity:1\""
        "             path=\"c -13.3333,0  -26.6667,0  -40,0\" />"
        "          <stop"
        "             id=\"stop844\""
        "             style=\"stop-color:#000000;stop-opacity:1\""
        "             path=\"c 0,-13.3333  0,-26.6667  0,-40\" />"
        "        </meshpatch>"
        "        <meshpatch"
        "           id=\"meshpatch846\">"
        "          <stop"
        "             id=\"stop848\""
        "             path=\"c 13.3333,0  26.6667,0  40,0\" />"
        "          <stop"
        "             id=\"stop850\""
        "             style=\"stop-color:#ffffff;stop-opacity:1\""
        "             path=\"c 0,13.3333  0,26.6667  0,40\" />"
        "          <stop"
        "             id=\"stop852\""
        "             style=\"stop-color:#000000;stop-opacity:1\""
        "             path=\"c -13.3333,0  -26.6667,0  -40,0\" />"
        "        </meshpatch>"
        "      </meshrow>"
        "      <meshrow"
        "         id=\"meshrow854\">"
        "        <meshpatch"
        "           id=\"meshpatch856\">"
        "          <stop"
        "             id=\"stop858\""
        "             path=\"c 0,13.3333  0,26.6667  0,40\" />"
        "          <stop"
        "             id=\"stop860\""
        "             style=\"stop-color:#000000;stop-opacity:1\""
        "             path=\"c -13.3333,0  -26.6667,0  -40,0\" />"
        "          <stop"
        "             id=\"stop862\""
        "             style=\"stop-color:#ffffff;stop-opacity:1\""
        "             path=\"c 0,-13.3333  0,-26.6667  0,-40\" />"
        "        </meshpatch>"
        "        <meshpatch"
        "           id=\"meshpatch864\">"
        "          <stop"
        "             id=\"stop866\""
        "             path=\"c 0,13.3333  0,26.6667  0,40\" />"
        "          <stop"
        "             id=\"stop868\""
        "             style=\"stop-color:#ffffff;stop-opacity:1\""
        "             path=\"c -13.3333,0  -26.6667,0  -40,0\" />"
        "        </meshpatch>"
        "      </meshrow>"
        "    </meshgradient>"
        "  </defs>"
        ""
        "  <rect"
        "     style=\"fill:url(#meshgradient832);fill-opacity:1\""
        "     id=\"testRect\""
        "     width=\"80\""
        "     height=\"80\""
        "     x=\"0\""
        "     y=\"0\" />"
        "</svg>";

    SvgRenderTester t(data);
    t.setFuzzyThreshold(5);
    t.test_standard("meshgradient_bilinear_2by2_in_user", QSize(100, 100), 72);
}

void TestSvgParser::testRenderMeshGradient_bicubic_2by2_UserCoord()
{
    QString data =
        "<svg width=\"100px\""
        "    height=\"100px\">"
        "<defs>"
        "  <meshgradient id=\"meshgradient0\""
        "    gradientUnits=\"userSpaceOnUse\" "
        "    x=\"0\" "
        "    y=\"0\" "
        "    type=\"bicubic\">"
        "   <meshrow "
        "     id=\"meshrow0\">"
        "    <meshpatch "
        "      id=\"meshpatch0\">"
        "     <stop path=\"C 13.33328,0 26.66664,0 40,0\" "
        "       stop-color=\"#ffffff\" stop-opacity=\"1\"/>"
        "     <stop path=\"C 40,13.33328 40,26.66664 40,40\" "
        "       stop-color=\"#000000\" stop-opacity=\"1\"/>"
        "     <stop path=\"C 26.66672,40 13.33336,40 0,40\" "
        "       stop-color=\"#ffffff\" stop-opacity=\"1\"/>"
        "     <stop path=\"C 0,26.66672 0,13.33336 0,0\" "
        "       stop-color=\"#000000\" stop-opacity=\"1\"/>"
        "    </meshpatch>"
        "    <meshpatch "
        "      id=\"meshpatch1\">"
        "     <stop path=\"C 53.33328,0 66.666640,0 80,0\"/>"
        "     <stop path=\"C 80,13.33328 80,26.66664 80,40\" "
        "       stop-color=\"#ffffff\" stop-opacity=\"1\"/>"
        "     <stop path=\"C 66.66672,40 53.33336,40 40,40\" "
        "       stop-color=\"#000000\" stop-opacity=\"1\"/>"
        "    </meshpatch>"
        "   </meshrow>"
        "   <meshrow "
        "     id=\"meshrow1\">"
        "    <meshpatch "
        "      id=\"meshpatch2\">"
        "     <stop path=\"C 40,53.33328 40,66.66664 40,80\"/>"
        "     <stop path=\"C 26.66672,80 13.33336,80 0,80\" "
        "       stop-color=\"#000000\" stop-opacity=\"1\"/>"
        "     <stop path=\"C 0,66.66672 0,53.33336 0,40\" "
        "       stop-color=\"#ffffff\" stop-opacity=\"1\"/>"
        "    </meshpatch>"
        "    <meshpatch "
        "      id=\"meshpatch3\">"
        "     <stop path=\"C 80,53.33328 80,66.66664 80,80\"/>"
        "     <stop path=\"C 66.66672,80 53.33336,80 40,80\" "
        "       stop-color=\"#ffffff\" stop-opacity=\"1\"/>"
        "    </meshpatch>"
        "   </meshrow>"
        "  </meshgradient>"
        " </defs>"
        ""
        "<rect id=\"testRect\" fill=\"url(#meshgradient0)\" width=\"80\" height=\"80\"/>"
        "</svg>";

    SvgRenderTester t(data);
    t.setFuzzyThreshold(5);
    t.test_standard("meshgradient_bicubic_2by2", QSize(100, 100), 72);
}

void TestSvgParser::testRenderMeshGradient_bilinear_1by1_Obb()
{
    // inkscape adds 1px border (unreliably) when the bbox doesn't the meshpatch's curves
    qWarning() << "WARNING: skipped, the edge couldn't be reliably verified";
    return;
    // inkscape is _very_ weird with meshgradients in OBB coordinate system
    QString data =
        "<svg width=\"100px\" height=\"100px\""
        "   version=\"1.1\" id=\"svg8\" >"
        "  <defs>"
        "    <meshgradient id=\"meshgradient1635\" gradientUnits=\"objectBoundingBox\" x=\"0\" y=\"0\">"
        "      <meshrow"
        "         id=\"meshrow2306\">"
        "        <meshpatch"
        "           id=\"meshpatch2296\">"
        "          <stop"
        "             id=\"stop2288\""
        "             stop-color=\"#000000\""
        "             path=\"c  0.25,-0.25  0.75, 0.25  1,0\" />"
        "          <stop"
        "             id=\"stop2290\""
        "             stop-color=\"#0000ff\""
        "             path=\"c  0.25, 0.25 -0.25, 0.75  0,1\" />"
        "          <stop"
        "             id=\"stop2292\""
        "             stop-color=\"#00ff00\""
        "             path=\"c -0.25, 0.25 -0.75,-0.25 -1,0\" />"
        "          <stop"
        "             id=\"stop2294\""
        "             stop-color=\"ff0000\""
        "             path=\"c -0.25,-0.25, 0.25,-0.75\" />"
        "        </meshpatch>"
        "      </meshrow>"
        "    </meshgradient>"
        "  </defs>"
        "  <rect"
        "     style=\"fill:url(#meshgradient1635);fill-opacity:1\""
        "     id=\"testRect\""
        "     width=\"70\""
        "     height=\"70\""
        "     x=\"0\""
        "     y=\"0\" />"
        "</svg>";

    SvgRenderTester t(data);
    t.setFuzzyThreshold(5);
    t.test_standard("meshgradient_bilinear_1by1_in_obb", QSize(100, 100), 72);
}

void TestSvgParser::testRenderMeshGradient_bicubic_2by2_Obb()
{
    // inkscape is _very_ weird with meshgradients in OBB coordinate system
    QString data =
        "<svg"
        "   version=\"1.1\""
        "   width=\"100px\""
        "   height=\"100px\""
        "   id=\"svg856\">"
        "  <defs"
        "     id=\"defs850\">"
        "    <meshgradient"
        "       y=\"0\""
        "       x=\"0\""
        "       gradientUnits=\"objectBoundingBox\""
        "       id=\"meshgradient832\""
        "       type=\"bicubic\" >"
        "      <meshrow"
        "         id=\"meshrow834\">"
        "        <meshpatch"
        "           id=\"meshpatch836\">"
        "          <stop"
        "             id=\"stop838\""
        "             style=\"stop-color:#ffffff;stop-opacity:1\""
        "             path=\"c 0.166666,0  0.333333,0  0.5,0\" />"
        "          <stop"
        "             id=\"stop840\""
        "             style=\"stop-color:#000000;stop-opacity:1\""
        "             path=\"c 0,0.166666  0,0.333333  0,0.5\" />"
        "          <stop"
        "             id=\"stop842\""
        "             style=\"stop-color:#ffffff;stop-opacity:1\""
        "             path=\"c -0.166666,0  -0.333333,0  -0.5,0\" />"
        "          <stop"
        "             id=\"stop844\""
        "             style=\"stop-color:#000000;stop-opacity:1\""
        "             path=\"c 0,-0.166666  0,-0.333333  0,-0.5\" />"
        "        </meshpatch>"
        "        <meshpatch"
        "           id=\"meshpatch846\">"
        "          <stop"
        "             id=\"stop848\""
        "             path=\"c 0.166666,0  0.333333,0  0.5,0\" />"
        "          <stop"
        "             id=\"stop850\""
        "             style=\"stop-color:#ffffff;stop-opacity:1\""
        "             path=\"c 0,0.166666  0,0.333333  0,0.5\" />"
        "          <stop"
        "             id=\"stop852\""
        "             style=\"stop-color:#000000;stop-opacity:1\""
        "             path=\"c -0.166666,0  -0.333333,0  -0.5,0\" />"
        "        </meshpatch>"
        "      </meshrow>"
        "      <meshrow"
        "         id=\"meshrow854\">"
        "        <meshpatch"
        "           id=\"meshpatch856\">"
        "          <stop"
        "             id=\"stop858\""
        "             path=\"c 0,0.166666  0,0.333333  0,0.5\" />"
        "          <stop"
        "             id=\"stop860\""
        "             style=\"stop-color:#000000;stop-opacity:1\""
        "             path=\"c -0.166666,0  -0.333333,0  -0.5,0\" />"
        "          <stop"
        "             id=\"stop862\""
        "             style=\"stop-color:#ffffff;stop-opacity:1\""
        "             path=\"c 0,-0.166666  0,-0.333333  0,-0.5\" />"
        "        </meshpatch>"
        "        <meshpatch"
        "           id=\"meshpatch864\">"
        "          <stop"
        "             id=\"stop866\""
        "             path=\"c 0,0.166666  0,0.333333  0,0.5\" />"
        "          <stop"
        "             id=\"stop868\""
        "             style=\"stop-color:#ffffff;stop-opacity:1\""
        "             path=\"c -0.166666,0  -0.333333,0  -0.5,0\" />"
        "        </meshpatch>"
        "      </meshrow>"
        "    </meshgradient>"
        "  </defs>"
        ""
        "  <rect"
        "     style=\"fill:url(#meshgradient832);fill-opacity:1\""
        "     id=\"testRect\""
        "     width=\"80\""
        "     height=\"80\""
        "     x=\"0\""
        "     y=\"0\" />"
        "</svg>";


    SvgRenderTester t(data);
    t.setFuzzyThreshold(5);
    t.test_standard("meshgradient_bicubic_2by2", QSize(100, 100), 72);
}

void TestSvgParser::testRenderMeshGradient_MeshTransform_UserCoord()
{
    QString data =
        "<svg"
        "   id=\"svg8\""
        "   version=\"1.1\""
        "   height=\"100px\""
        "   width=\"100px\">"
        "  <defs"
        "     id=\"defs2\">"
        "    <meshgradient"
        "       y=\"0\""
        "       x=\"0\""
        "       gradientUnits=\"userSpaceOnUse\""
        "       id=\"meshgradient1989\""
        "       transform=\"translate(90, 0) rotate(90)\">"
        "      <meshrow"
        "         id=\"meshrow1991\">"
        "        <meshpatch"
        "           id=\"meshpatch1993\">"
        "          <stop"
        "             id=\"stop1995\""
        "             style=\"stop-color:#ffffff;stop-opacity:1\""
        "             path=\"c 30,0  60,0  90,0\" />"
        "          <stop"
        "             id=\"stop1997\""
        "             style=\"stop-color:#ff0000;stop-opacity:1\""
        "             path=\"c 0,30  0,60  0,90\" />"
        "          <stop"
        "             id=\"stop1999\""
        "             style=\"stop-color:#ffffff;stop-opacity:1\""
        "             path=\"c -30,0  -60,0  -90,0\" />"
        "          <stop"
        "             id=\"stop2001\""
        "             style=\"stop-color:#ff0000;stop-opacity:1\""
        "             path=\"c 0,-30  0,-60  0,-90\" />"
        "        </meshpatch>"
        "      </meshrow>"
        "    </meshgradient>"
        "  </defs>"
        "  <rect"
        "     y=\"0\""
        "     x=\"0\""
        "     height=\"100\""
        "     width=\"100\""
        "     id=\"testRect\""
        "     style=\"fill:url(#meshgradient1989);fill-opacity:1\" />"
        "</svg>";

    SvgRenderTester t(data);
    t.setFuzzyThreshold(5);
    t.test_standard("meshgradient_meshtransform_in_user", QSize(100, 100), 72);
}

void TestSvgParser::testRenderMeshGradient_ShapeTransform_UserCoord()
{
    // QString data = fetchSvgFile("svg_render", "meshgradient_shapetransform_in_user");
    QString data =
        "<svg"
        "   width=\"100px\""
        "   height=\"100px\""
        "   version=\"1.1\""
        "   id=\"svg8\">"
        "  <defs"
        "     id=\"defs2\">"
        "    <meshgradient"
        "       y=\"-79.791451\""
        "       x=\"-154.33087\""
        "       gradientUnits=\"userSpaceOnUse\""
        "       id=\"meshgradient839\">"
        "      <meshrow"
        "         id=\"meshrow841\">"
        "        <meshpatch"
        "           id=\"meshpatch843\">"
        "          <stop"
        "             id=\"stop845\""
        "             style=\"stop-color:#ffffff;stop-opacity:1\""
        "             path=\"c 51.0352,0  102.07,0  153.105,0\" />"
        "          <stop"
        "             id=\"stop847\""
        "             style=\"stop-color:#000000;stop-opacity:1\""
        "             path=\"c 0,52.6788  0,105.358  0,158.036\" />"
        "          <stop"
        "             id=\"stop849\""
        "             style=\"stop-color:#ffffff;stop-opacity:1\""
        "             path=\"c -51.0352,0  -102.07,0  -153.105,0\" />"
        "          <stop"
        "             id=\"stop851\""
        "             style=\"stop-color:#000000;stop-opacity:1\""
        "             path=\"c 0,-52.6788  0,-105.358  0,-158.036\" />"
        "        </meshpatch>"
        "      </meshrow>"
        "    </meshgradient>"
        "  </defs>"
        "  <rect"
        "     style=\"fill:url(#meshgradient839);fill-opacity:1\""
        "     id=\"testRect\""
        "     width=\"153.10547\""
        "     height=\"158.03641\""
        "     x=\"-154.33087\""
        "     y=\"-79.791451\""
        "     transform=\"matrix(-0.71410131,-0.70004237,0.70026385,-0.71388413,0,0)\" />"
        "</svg>";


    SvgRenderTester t(data);
    t.setFuzzyThreshold(5);
    t.test_standard("meshgradient_shapetransform_in_user", QSize(100, 100), 72);
}

void TestSvgParser::testRenderMeshGradient_MeshTransform_Obb()
{
    // inkscape is _very_ weird with meshgradients in OBB coordinate system
    QString data =
        "<svg"
        "   version=\"1.1\""
        "   height=\"100px\""
        "   width=\"100px\">"
        "  <defs"
        "     id=\"defs2\">"
        "    <meshgradient"
        "       y=\"0\""
        "       x=\"0\""
        "       transform=\"translate(0.7, 0) rotate(90)\""
        "       gradientUnits=\"objectBoundingBox\""
        "       id=\"meshgradient901\">"
        "      <meshrow"
        "         id=\"meshrow903\">"
        "        <meshpatch"
        "           id=\"meshpatch905\">"
        "          <stop"
        "             id=\"stop907\""
        "             style=\"stop-color:#ffffff;stop-opacity:1\""
        "             path=\"c 0.166666,0  0.333333,0  0.5,0\" />"
        "          <stop"
        "             id=\"stop909\""
        "             style=\"stop-color:#ff0000;stop-opacity:1\""
        "             path=\"c 0,0.166666  0,0.333333  0,0.5\" />"
        "          <stop"
        "             id=\"stop911\""
        "             style=\"stop-color:#ffffff;stop-opacity:1\""
        "             path=\"c -0.166666,0  -0.333333,0  -0.5,0\" />"
        "          <stop"
        "             id=\"stop913\""
        "             style=\"stop-color:#ff0000;stop-opacity:1\""
        "             path=\"c 0,-0.166666 0,-0.333333  0,-0.5\" />"
        "        </meshpatch>"
        "        <meshpatch"
        "           id=\"meshpatch915\">"
        "          <stop"
        "             id=\"stop917\""
        "             path=\"c 0.166666,0  0.333333,0  0.5,0\" />"
        "          <stop"
        "             id=\"stop919\""
        "             style=\"stop-color:#ffffff;stop-opacity:1\""
        "             path=\"c 0,0.166666 0,0.333333  0,0.5\" />"
        "          <stop"
        "             id=\"stop921\""
        "             style=\"stop-color:#ff0000;stop-opacity:1\""
        "             path=\"c -0.166666,0  -0.333333,0  -0.5,0\" />"
        "        </meshpatch>"
        "      </meshrow>"
        "    </meshgradient>"
        "  </defs>"
        "  <rect"
        "     y=\"0\""
        "     x=\"0\""
        "     height=\"90\""
        "     width=\"90\""
        "     id=\"testRect\""
        "     style=\"fill:url(#meshgradient901);fill-opacity:1;filter:url(#filter859)\" />"
        "</svg>";


    SvgRenderTester t(data);
    t.setFuzzyThreshold(5);
    t.test_standard("meshgradient_meshtransform_in_obb", QSize(100, 100), 72);
}

void TestSvgParser::testRenderMeshGradient_ShapeTransform_Obb()
{
    // inkscape is _very_ weird with meshgradients in OBB coordinate system
    QString data =
        "<svg"
        "   width=\"100px\""
        "   height=\"100px\""
        "   version=\"1.1\""
        "   id=\"svg8\">"
        "  <defs"
        "     id=\"defs2\">"
        "    <meshgradient"
        "       y=\"0\""
        "       x=\"0\""
        "       gradientUnits=\"objectBoundingBox\""
        "       id=\"meshgradient839\">"
        "      <meshrow"
        "         id=\"meshrow841\">"
        "        <meshpatch"
        "           id=\"meshpatch843\">"
        "          <stop"
        "             id=\"stop845\""
        "             style=\"stop-color:#ffffff;stop-opacity:1\""
        "             path=\"c  0.33333,0  0.666666,0  1,0\" />"
        "          <stop"
        "             id=\"stop847\""
        "             style=\"stop-color:#000000;stop-opacity:1\""
        "             path=\"c 0,0.33333  0,0.66667  0,1\" />"
        "          <stop"
        "             id=\"stop849\""
        "             style=\"stop-color:#ffffff;stop-opacity:1\""
        "             path=\"c -0.33333,0  -0.666666,0  -1,0\" />"
        "          <stop"
        "             id=\"stop851\""
        "             style=\"stop-color:#000000;stop-opacity:1\""
        "             path=\"c 0,-0.33333  0,-0.66667  0,-1\" />"
        "        </meshpatch>"
        "      </meshrow>"
        "    </meshgradient>"
        "  </defs>"
        "  <rect"
        "     style=\"fill:url(#meshgradient839);fill-opacity:1\""
        "     id=\"testRect\""
        "     width=\"153.10547\""
        "     height=\"158.03641\""
        "     x=\"-154.33087\""
        "     y=\"-79.791451\""
        "     transform=\"matrix(-0.71410131,-0.70004237,0.70026385,-0.71388413,0,0)\" />"
        "</svg>";

    SvgRenderTester t(data);
    t.setFuzzyThreshold(3);
    t.test_standard("meshgradient_shapetransform_in_obb", QSize(100, 100), 72);
}

void TestSvgParser::testRenderMeshGradient_transparent()
{
    // here I use Krita's output, due to my lack of knowledge about Inkscape's RGBA
    // output format. But the results, have been verified - SZ
    QString data =
        "<svg"
        "   width=\"100px\""
        "   height=\"100px\""
        "   version=\"1.1\""
        "   id=\"svg8\">"
        "  <defs"
        "     id=\"defs2\">"
        "    <meshgradient"
        "       id=\"meshgradient1989\""
        "       gradientUnits=\"userSpaceOnUse\""
        "       x=\"-10\""
        "       y=\"-10\""
        "       type=\"bilinear\">"
        "      <meshrow"
        "         id=\"meshrow1991\">"
        "        <meshpatch"
        "           id=\"meshpatch1993\">"
        "          <stop"
        "             path=\"c 30,0  60,0  90,0\""
        "             style=\"stop-color:#ffffff;stop-opacity:1\""
        "             id=\"stop1995\" />"
        "          <stop"
        "             path=\"c 0,30  0,60  0,90\""
        "             style=\"stop-color:#ff0000;stop-opacity:1\""
        "             id=\"stop1997\" />"
        "          <stop"
        "             path=\"c -30,0  -60,0  -90,0\""
        "             style=\"stop-color:#ffffff;stop-opacity:0\""
        "             id=\"stop1999\" />"
        "          <stop"
        "             path=\"c 0,-30  0,-60  0,-90\""
        "             style=\"stop-color:#ff0000;stop-opacity:1\""
        "             id=\"stop2001\" />"
        "        </meshpatch>"
        "      </meshrow>"
        "    </meshgradient>"
        "  </defs>"
        "  <rect"
        "     style=\"fill:url(#meshgradient1989);fill-opacity:1\""
        "     id=\"testRect\""
        "     width=\"90\""
        "     height=\"90\""
        "     x=\"-10\""
        "     y=\"-10\" />"
        "</svg>";

    SvgRenderTester t(data);
    t.setFuzzyThreshold(5);
    t.setCheckQImagePremultiplied(true);
    t.test_standard("meshgradient_transparent", QSize(100, 100), 72);
}

void TestSvgParser::testRenderMeshGradient_reversed()
{
    // Not sure if this is part of specs, but this brings up an underlying problem:
    // bounds in QPainterPath are inclusive on right and bottom side, so an extra 1px
    // border is drawn.
    QString data =
        "<svg"
        "   id=\"svg856\""
        "   height=\"100px\""
        "   width=\"100px\""
        ">"
        "  <defs"
        "     id=\"defs850\">"
        "    <meshgradient"
        "       y=\"0\""
        "       x=\"80\""
        "       gradientUnits=\"userSpaceOnUse\""
        "       id=\"meshgradient903\">"
        "      <meshrow"
        "         id=\"meshrow905\">"
        "        <meshpatch"
        "           id=\"meshpatch907\">"
        "          <stop"
        "             id=\"stop909\""
        "             style=\"stop-color:#ffffff;stop-opacity:1\""
        "             path=\"c -13.3333,0  -26.6667,0  -40,0\" />"
        "          <stop"
        "             id=\"stop911\""
        "             style=\"stop-color:#000000;stop-opacity:1\""
        "             path=\"c 0,26.6667  0,53.3333  0,80\" />"
        "          <stop"
        "             id=\"stop913\""
        "             style=\"stop-color:#ffffff;stop-opacity:1\""
        "             path=\"c  13.3333,0   26.6667,0   40,0\" />"
        "          <stop"
        "             id=\"stop915\""
        "             style=\"stop-color:#000000;stop-opacity:1\""
        "             path=\"c 0,-26.6667  0,-53.3333  0,-80\" />"
        "        </meshpatch>"
        "        <meshpatch"
        "           id=\"meshpatch917\">"
        "          <stop"
        "             id=\"stop919\""
        "             path=\"c -13.3333,0  -26.6667,0  -40,0\" />"
        "          <stop"
        "             id=\"stop921\""
        "             style=\"stop-color:#ffffff;stop-opacity:1\""
        "             path=\"c 0,26.6667  0,53.3333  0,80\" />"
        "          <stop"
        "             id=\"stop923\""
        "             style=\"stop-color:#000000;stop-opacity:1\""
        "             path=\"c  13.3333,0   26.6667,0   40,0\" />"
        "        </meshpatch>"
        "      </meshrow>"
        "    </meshgradient>"
        "  </defs>"
        "  <rect"
        "     y=\"0\""
        "     x=\"0\""
        "     height=\"90\""
        "     width=\"90\""
        "     id=\"testRect\""
        "     style=\"fill:url(#meshgradient903);fill-opacity:1\" />"
        "</svg>";

    SvgRenderTester t(data);
    t.setFuzzyThreshold(5);
    t.test_standard("meshgradient_reversed", QSize(100, 100), 72);
}

QPainterPath bakeShape(const QPainterPath &path,
                       const QTransform &bakeTransform,
                       bool contentIsObb = false, const QRectF &shapeBoundingRect = QRectF(),
                       bool contentIsViewBox = false, const QRectF &viewBoxRect= QRectF(), const QRectF &refRect = QRectF())
{
    const QTransform relativeToShape(shapeBoundingRect.width(), 0, 0, shapeBoundingRect.height(),
                                     shapeBoundingRect.x(), shapeBoundingRect.y());

    QTransform newTransform = bakeTransform;

    if (contentIsObb) {
        newTransform = relativeToShape * newTransform;
    }

    if (contentIsViewBox) {
        newTransform = viewTransform(viewBoxRect, refRect) * newTransform;
    }

    return newTransform.map(path);
}

#include <KoBakedShapeRenderer.h>

void renderBakedPath(QPainter &painter,
                     const QPainterPath &bakedFillPath, const QTransform &bakedTransform,
                     const QRect &shapeOutline, const QTransform &shapeTransform,
                     const QRectF &referenceRect,
                     bool contentIsObb, const QRectF &bakedShapeBoundingRect,
                     bool referenceIsObb,
                     const QTransform &patternTransform,
                     QImage *stampResult)
{
    painter.setTransform(QTransform());
    painter.setPen(Qt::NoPen);

    QPainterPath shapeOutlinePath;
    shapeOutlinePath.addRect(shapeOutline);

    KoBakedShapeRenderer renderer(
                shapeOutlinePath,
                shapeTransform,
                bakedTransform,
                referenceRect,
                contentIsObb, bakedShapeBoundingRect,
                referenceIsObb,
                patternTransform);

    QPainter *patchPainter = renderer.bakeShapePainter();
    patchPainter->fillPath(bakedFillPath, Qt::blue);
    patchPainter->end();

    renderer.renderShape(painter);

    if (stampResult) {
        *stampResult = renderer.patchImage();
    }
}

void TestSvgParser::testManualRenderPattern_ContentUser_RefObb()
{
    const QRectF referenceRect(0, 0, 1.0, 0.5);

    QPainterPath fillPath;
    fillPath.addRect(QRect(2, 2, 6, 6));
    fillPath.addRect(QRect(8, 4, 3, 2));

    QTransform bakedTransform = QTransform::fromTranslate(10, 10) * QTransform::fromScale(2, 2);
    QPainterPath bakedFillPath = bakeShape(fillPath, bakedTransform);

    QRect shape1OutlineRect(0,0,10,20);

    QImage stampResult;
    QImage fillResult(QSize(60,60), QImage::Format_ARGB32);
    QPainter gc(&fillResult);

    fillResult.fill(0);
    renderBakedPath(gc,
                    bakedFillPath, bakedTransform,
                    shape1OutlineRect, bakedTransform,
                    referenceRect,
                    false, QRectF(),
                    true,
                    QTransform(),
                    &stampResult);

    QVERIFY(TestUtil::checkQImage(stampResult, "svg_render", "render", "pattern_c_user_r_obb_patch1"));
    QVERIFY(TestUtil::checkQImage(fillResult, "svg_render", "render", "pattern_c_user_r_obb_fill1"));

    QRect shape2OutlineRect(5,5,20,10);
    QTransform shape2Transform = QTransform::fromScale(2, 2) * QTransform::fromTranslate(5, 5);

    fillResult.fill(0);
    renderBakedPath(gc,
                    bakedFillPath, bakedTransform,
                    shape2OutlineRect, shape2Transform,
                    referenceRect,
                    false, QRectF(),
                    true,
                    QTransform(),
                    &stampResult);

    QVERIFY(TestUtil::checkQImage(stampResult, "svg_render", "render", "pattern_c_user_r_obb_patch2"));
    QVERIFY(TestUtil::checkQImage(fillResult, "svg_render", "render", "pattern_c_user_r_obb_fill2"));
}

void TestSvgParser::testManualRenderPattern_ContentObb_RefObb()
{
    const QRectF referenceRect(0.3, 0.3, 0.4, 0.4);

    QPainterPath fillPath;
    fillPath.addRect(QRectF(0.4, 0.4, 0.2, 0.2));
    fillPath.addRect(QRectF(0.6, 0.5, 0.1, 0.1));
    fillPath.addRect(QRectF(0.3, 0.4, 0.1, 0.1));


    const QRect bakedShapeRect(2,2,10,10);
    QTransform bakedTransform = QTransform::fromTranslate(10, 10) * QTransform::fromScale(2, 2);

    QPainterPath bakedFillPath = bakeShape(fillPath, bakedTransform, true, bakedShapeRect);

    QImage stampResult;
    QImage fillResult(QSize(60,60), QImage::Format_ARGB32);
    QPainter gc(&fillResult);

    // Round trip to the same shape

    fillResult.fill(0);
    renderBakedPath(gc,
                    bakedFillPath, bakedTransform,
                    bakedShapeRect, bakedTransform,
                    referenceRect,
                    true, bakedShapeRect,
                    true,
                    QTransform(),
                    &stampResult);

    QVERIFY(TestUtil::checkQImage(stampResult, "svg_render", "render", "pattern_c_obb_r_obb_patch1"));
    QVERIFY(TestUtil::checkQImage(fillResult, "svg_render", "render", "pattern_c_obb_r_obb_fill1"));

    // Move to a different shape

    QRect shape2OutlineRect(5,5,20,10);
    QTransform shape2Transform = QTransform::fromScale(2, 2) * QTransform::fromTranslate(5, 5);

    fillResult.fill(0);
    renderBakedPath(gc,
                    bakedFillPath, bakedTransform,
                    shape2OutlineRect, shape2Transform,
                    referenceRect,
                    true, bakedShapeRect,
                    true,
                    QTransform(),
                    &stampResult);

    QVERIFY(TestUtil::checkQImage(stampResult, "svg_render", "render", "pattern_c_obb_r_obb_patch2"));
    QVERIFY(TestUtil::checkQImage(fillResult, "svg_render", "render", "pattern_c_obb_r_obb_fill2"));
}

void TestSvgParser::testManualRenderPattern_ContentUser_RefUser()
{
    const QRectF referenceRect(5, 2, 8, 8);

    QPainterPath fillPath;
    fillPath.addRect(QRect(2, 2, 6, 6));
    fillPath.addRect(QRect(8, 4, 3, 2));


    QTransform bakedTransform = QTransform::fromTranslate(10, 10) * QTransform::fromScale(2, 2);
    QPainterPath bakedFillPath = bakeShape(fillPath, bakedTransform);

    QRect shape1OutlineRect(0,0,10,20);

    QImage stampResult;
    QImage fillResult(QSize(60,60), QImage::Format_ARGB32);
    QPainter gc(&fillResult);

    fillResult.fill(0);
    renderBakedPath(gc,
                    bakedFillPath, bakedTransform,
                    shape1OutlineRect, bakedTransform,
                    referenceRect,
                    false, QRectF(),
                    false,
                    QTransform(),
                    &stampResult);

    QVERIFY(TestUtil::checkQImage(stampResult, "svg_render", "render", "pattern_c_user_r_user_patch1"));
    QVERIFY(TestUtil::checkQImage(fillResult, "svg_render", "render", "pattern_c_user_r_user_fill1"));

    QRect shape2OutlineRect(5,5,20,10);
    QTransform shape2Transform = QTransform::fromScale(2, 2) * QTransform::fromTranslate(5, 5);

    fillResult.fill(0);
    renderBakedPath(gc,
                    bakedFillPath, bakedTransform,
                    shape2OutlineRect, shape2Transform,
                    referenceRect,
                    false, QRectF(),
                    false,
                    QTransform(),
                    &stampResult);

    QVERIFY(TestUtil::checkQImage(stampResult, "svg_render", "render", "pattern_c_user_r_user_patch2"));
    QVERIFY(TestUtil::checkQImage(fillResult, "svg_render", "render", "pattern_c_user_r_user_fill2"));
}

void TestSvgParser::testManualRenderPattern_ContentObb_RefObb_Transform_Rotate()
{
    const QRectF referenceRect(0.0, 0.0, 0.4, 0.2);

    QPainterPath fillPath;
    fillPath.addRect(QRectF(0.0, 0.0, 0.5, 0.1));
    fillPath.addRect(QRectF(0.0, 0.1, 0.1, 0.1));

    const QRect bakedShapeRect(2,1,10,10);
    QTransform bakedTransform = QTransform::fromScale(2, 2) * QTransform::fromTranslate(10,10);

    QPainterPath bakedFillPath = bakeShape(fillPath, bakedTransform, true, bakedShapeRect);

    QImage stampResult;
    QImage fillResult(QSize(60,60), QImage::Format_ARGB32);
    QPainter gc(&fillResult);

    QTransform patternTransform;
    patternTransform.rotate(90);
    patternTransform = patternTransform * QTransform::fromTranslate(0.5, 0.0);

    // Round trip to the same shape

    fillResult.fill(0);
    renderBakedPath(gc,
                    bakedFillPath, bakedTransform,
                    bakedShapeRect, bakedTransform,
                    referenceRect,
                    true, bakedShapeRect,
                    true,
                    patternTransform,
                    &stampResult);

    QVERIFY(TestUtil::checkQImage(stampResult, "svg_render", "render", "pattern_c_obb_r_obb_rotate_patch1"));
    QVERIFY(TestUtil::checkQImage(fillResult, "svg_render", "render", "pattern_c_obb_r_obb_rotate_fill1"));

    QRect shape2OutlineRect(5,5,20,10);
    QTransform shape2Transform = QTransform::fromScale(2, 2) * QTransform::fromTranslate(5, 5);

    fillResult.fill(0);
    renderBakedPath(gc,
                    bakedFillPath, bakedTransform,
                    shape2OutlineRect, shape2Transform,
                    referenceRect,
                    true, bakedShapeRect,
                    true,
                    patternTransform,
                    &stampResult);

    QVERIFY(TestUtil::checkQImage(stampResult, "svg_render", "render", "pattern_c_obb_r_obb_rotate_patch2"));
    QVERIFY(TestUtil::checkQImage(fillResult, "svg_render", "render", "pattern_c_obb_r_obb_rotate_fill2"));
}


void TestSvgParser::testManualRenderPattern_ContentView_RefObb()
{
    const QRectF referenceRect(0, 0, 0.5, 1.0/3.0);
    const QRectF viewRect(10,10,60,90);

    QPainterPath fillPath;
    fillPath.addRect(QRect(30, 10, 20, 60));
    fillPath.addRect(QRect(50, 40, 20, 30));


    QRect shape1OutlineRect(10,20,40,120);

    QTransform bakedTransform = QTransform::fromScale(2, 0.5) * QTransform::fromTranslate(40,30);
    QPainterPath bakedFillPath = bakeShape(fillPath, bakedTransform,
                                           true, shape1OutlineRect,
                                           true, viewRect, referenceRect);

    QImage stampResult;
    QImage fillResult(QSize(220,160), QImage::Format_ARGB32);
    QPainter gc(&fillResult);

    fillResult.fill(0);
    renderBakedPath(gc,
                    bakedFillPath, bakedTransform,
                    shape1OutlineRect, bakedTransform,
                    referenceRect,
                    true, shape1OutlineRect,
                    true,
                    QTransform(),
                    &stampResult);

    QVERIFY(TestUtil::checkQImage(stampResult, "svg_render", "render", "pattern_c_view_r_obb_patch1"));
    QVERIFY(TestUtil::checkQImage(fillResult, "svg_render", "render", "pattern_c_view_r_obb_fill1"));

    QRect shape2OutlineRect(20,10,60,90);
    QTransform shape2Transform = QTransform::fromScale(2, 1) * QTransform::fromTranslate(50, 50);

    fillResult.fill(0);
    renderBakedPath(gc,
                    bakedFillPath, bakedTransform,
                    shape2OutlineRect, shape2Transform,
                    referenceRect,
                    true, shape1OutlineRect,
                    true,
                    rotateTransform(90, QPointF(0, 1.0 / 3.0)),
                    &stampResult);

    QVERIFY(TestUtil::checkQImage(stampResult, "svg_render", "render", "pattern_c_view_r_obb_patch2"));
    QVERIFY(TestUtil::checkQImage(fillResult, "svg_render", "render", "pattern_c_view_r_obb_fill2"));
}

void TestSvgParser::testManualRenderPattern_ContentView_RefUser()
{
    const QRectF referenceRect(60, 0, 30, 20);
    const QRectF viewRect(10,10,60,90);

    QPainterPath fillPath;
    fillPath.addRect(QRect(30, 10, 20, 60));
    fillPath.addRect(QRect(50, 40, 20, 30));


    QRect shape1OutlineRect(10,20,40,120);

    QTransform bakedTransform = QTransform::fromScale(2, 0.5) * QTransform::fromTranslate(40,30);
    QPainterPath bakedFillPath = bakeShape(fillPath, bakedTransform,
                                           false, shape1OutlineRect,
                                           true, viewRect, referenceRect);

    QImage stampResult;
    QImage fillResult(QSize(220,160), QImage::Format_ARGB32);
    QPainter gc(&fillResult);

    fillResult.fill(0);
    renderBakedPath(gc,
                    bakedFillPath, bakedTransform,
                    shape1OutlineRect, bakedTransform,
                    referenceRect,
                    false, shape1OutlineRect,
                    false,
                    QTransform(),
                    &stampResult);

    QVERIFY(TestUtil::checkQImage(stampResult, "svg_render", "render", "pattern_c_view_r_user_patch1"));
    QVERIFY(TestUtil::checkQImage(fillResult, "svg_render", "render", "pattern_c_view_r_user_fill1"));

    QRect shape2OutlineRect(20,10,60,90);
    QTransform shape2Transform = QTransform::fromScale(2, 1) * QTransform::fromTranslate(50, 50);

    QTransform patternTransform2 = rotateTransform(90, QPointF()) * QTransform::fromTranslate(40, 10);

    fillResult.fill(0);
    renderBakedPath(gc,
                    bakedFillPath, bakedTransform,
                    shape2OutlineRect, shape2Transform,
                    referenceRect,
                    false, shape1OutlineRect,
                    false,
                    patternTransform2,
                    &stampResult);

    QVERIFY(TestUtil::checkQImage(stampResult, "svg_render", "render", "pattern_c_view_r_user_patch2"));
    QVERIFY(TestUtil::checkQImage(fillResult, "svg_render", "render", "pattern_c_view_r_user_fill2"));
}

void TestSvgParser::testRenderPattern_r_User_c_User()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<defs>"
            "    <pattern id=\"TestPattern\" patternUnits=\"userSpaceOnUse\""
            "        patternContentUnits=\"userSpaceOnUse\""
            "        x=\"60\" y=\"0\" width=\"30\" height=\"20\">"

            "        <g id=\"patternRect\">"
            "            <rect id=\"patternRect1\" x=\"70\" y=\"0\" width=\"10\" height=\"13.3333\""
            "                fill=\"red\" stroke=\"none\" />"

            "            <rect id=\"patternRect2\" x=\"80\" y=\"6.3333\" width=\"10\" height=\"6.6666\""
            "                fill=\"red\" stroke=\"none\" />"
            "        </g>"

            "    </pattern>"
            "</defs>"

            "<g>"
            "    <rect id=\"testRect\" x=\"10\" y=\"20\" width=\"40\" height=\"120\""
            "        transform=\"translate(40 30) scale(2 0.5)\""
            "        fill=\"url(#TestPattern)blue\" stroke=\"none\"/>"
            "</g>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("fill_pattern_base", false, QSize(160, 160));
}

void TestSvgParser::testRenderPattern_InfiniteRecursionWhenInherited()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<defs>"
            "    <pattern id=\"TestPattern\" patternUnits=\"userSpaceOnUse\""
            "        patternContentUnits=\"userSpaceOnUse\""
            "        x=\"60\" y=\"0\" width=\"30\" height=\"20\">"

            "        <g id=\"patternRect\">"
            "            <rect id=\"patternRect1\" x=\"70\" y=\"0\" width=\"10\" height=\"13.3333\""
            "                stroke=\"none\" />"

            "            <rect id=\"patternRect2\" x=\"80\" y=\"6.3333\" width=\"10\" height=\"6.6666\""
            "                stroke=\"none\" />"
            "        </g>"

            "    </pattern>"
            "</defs>"

            "<g>"
            "    <rect id=\"testRect\" x=\"10\" y=\"20\" width=\"40\" height=\"120\""
            "        transform=\"translate(40 30) scale(2 0.5)\""
            "        fill=\"url(#TestPattern)blue\" stroke=\"none\"/>"
            "</g>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("fill_pattern_base_black", false, QSize(160, 160));
}

void TestSvgParser::testRenderPattern_r_User_c_View()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<defs>"
            "    <pattern id=\"TestPattern\" patternUnits=\"userSpaceOnUse\""
            "        viewBox=\"10 10 60 90\""
            "        preserveAspectRatio=\"none meet\""
            "        x=\"60\" y=\"0\" width=\"30\" height=\"20\">"

            "        <g id=\"patternRect\">"
            "            <rect id=\"patternRect1\" x=\"30\" y=\"10\" width=\"20\" height=\"60\""
            "                fill=\"red\" stroke=\"none\" />"

            // y is changed to 39 from 40 to fix a rounding issue!
            "            <rect id=\"patternRect2\" x=\"50\" y=\"39\" width=\"20\" height=\"30\""
            "                fill=\"red\" stroke=\"none\" />"
            "        </g>"

            "    </pattern>"
            "</defs>"

            "<g>"
            "    <rect id=\"testRect\" x=\"10\" y=\"20\" width=\"40\" height=\"120\""
            "        transform=\"translate(40 30) scale(2 0.5)\""
            "        fill=\"url(#TestPattern)blue\" stroke=\"none\"/>"
            "</g>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("fill_pattern_base", false, QSize(160, 160));
}

void TestSvgParser::testRenderPattern_r_User_c_Obb()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<defs>"
            "    <pattern id=\"TestPattern\" patternUnits=\"userSpaceOnUse\""
            "        patternContentUnits=\"objectBoundingBox\""
            "        x=\"60\" y=\"0\" width=\"30\" height=\"20\">"

            "        <g id=\"patternRect\">"
            "            <rect id=\"patternRect1\" x=\"1.5\" y=\"-0.1666\" width=\"0.25\" height=\"0.111\""
            "                fill=\"red\" stroke=\"none\" />"

            "            <rect id=\"patternRect2\" x=\"1.75\" y=\"-0.12\" width=\"0.25\" height=\"0.07\""
            "                fill=\"red\" stroke=\"none\" />"
            "        </g>"

            "    </pattern>"
            "</defs>"

            "<g>"
            "    <rect id=\"testRect\" x=\"10\" y=\"20\" width=\"40\" height=\"120\""
            "        transform=\"translate(40 30) scale(2 0.5)\""
            "        fill=\"url(#TestPattern)blue\" stroke=\"none\"/>"
            "</g>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("fill_pattern_base", false, QSize(160, 160));
}

void TestSvgParser::testRenderPattern_r_User_c_View_Rotated()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<defs>"
            "    <pattern id=\"TestPattern\" patternUnits=\"userSpaceOnUse\""
            "        viewBox=\"10 10 60 90\""
            "        preserveAspectRatio=\"none meet\""
            "        x=\"60\" y=\"0\" width=\"30\" height=\"20\""
            "        patternTransform=\"translate(40 10) rotate(90)\">"

            "        <g id=\"patternRect\">"
            "            <rect id=\"patternRect1\" x=\"30\" y=\"10\" width=\"20\" height=\"60\""
            "                fill=\"red\" stroke=\"none\" />"
            "            <rect id=\"patternRect2\" x=\"50\" y=\"40\" width=\"20\" height=\"30\""
            "                fill=\"red\" stroke=\"none\" />"
            "        </g>"

            "    </pattern>"
            "</defs>"

            "<g>"
            "    <rect id=\"testRect\" x=\"20\" y=\"10\" width=\"60\" height=\"90\""
            "        transform=\"translate(50 50) scale(2 1)\""
            "        fill=\"url(#TestPattern)blue\" stroke=\"none\"/>"
            "</g>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("fill_pattern_rotated", false, QSize(220, 160));
}

void TestSvgParser::testRenderPattern_r_Obb_c_View_Rotated()
{
    /**
     * This test case differs from any application existent in the world :(
     *
     * Chrome and Firefox premultiply the patternTransform instead of doing post-
     * multiplication. Photoshop forgets to multiply the reference rect on it.
     *
     * So...
     */

    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<defs>"
            "    <pattern id=\"TestPattern\" patternUnits=\"objectBoundingBox\""
            "        viewBox=\"10 10 60 90\""
            "        preserveAspectRatio=\"none meet\""
            "        x=\"0\" y=\"0\" width=\"0.5\" height=\"0.333\""
            "        patternTransform=\"translate(0 0) rotate(90)\">"

            "        <g id=\"patternRect\">"
            "            <rect id=\"patternRect1\" x=\"30\" y=\"10\" width=\"20\" height=\"60\""
            "                fill=\"red\" stroke=\"none\" />"
            "            <rect id=\"patternRect2\" x=\"50\" y=\"40\" width=\"20\" height=\"30\""
            "                fill=\"red\" stroke=\"none\" />"
            "        </g>"

            "    </pattern>"
            "</defs>"

            "<g>"
            "    <rect id=\"testRect\" x=\"20\" y=\"10\" width=\"60\" height=\"90\""
            "        transform=\"translate(50 50) scale(1 1)\""
            "        fill=\"url(#TestPattern)blue\" stroke=\"none\"/>"
            "</g>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("fill_pattern_rotated_odd", false, QSize(220, 160));
}

#include <KoPathShape.h>
#include <KoColorBackground.h>
#include <KoClipPath.h>
#include <commands/KoShapeGroupCommand.h>

void TestSvgParser::testKoClipPathRendering()
{
    QPainterPath path1;
    path1.addRect(QRect(5,5,15,15));

    QPainterPath path2;
    path2.addRect(QRect(10,10,15,15));

    QPainterPath clipPath1;
    clipPath1.addRect(QRect(10, 0, 10, 30));

    QPainterPath clipPath2;
    clipPath2.moveTo(0,7);
    clipPath2.lineTo(30,7);
    clipPath2.lineTo(15,30);
    clipPath2.lineTo(0,7);

    QScopedPointer<KoPathShape> shape1(KoPathShape::createShapeFromPainterPath(path1));
    shape1->setBackground(QSharedPointer<KoColorBackground>(new KoColorBackground(Qt::blue)));

    QScopedPointer<KoPathShape> shape2(KoPathShape::createShapeFromPainterPath(path2));
    shape2->setBackground(QSharedPointer<KoColorBackground>(new KoColorBackground(Qt::red)));

    QScopedPointer<KoPathShape> clipShape1(KoPathShape::createShapeFromPainterPath(clipPath1));
    KoClipPath *koClipPath1 = new KoClipPath({clipShape1.take()}, KoFlake::UserSpaceOnUse);
    koClipPath1->setClipRule(Qt::WindingFill);
    shape1->setClipPath(koClipPath1);

    QScopedPointer<KoShapeGroup> group(new KoShapeGroup());
    {
        QList<KoShape*> shapes({shape1.take(), shape2.take()});

        KoShapeGroupCommand cmd(group.data(), shapes, false);
        cmd.redo();
    }

    QScopedPointer<KoPathShape> clipShape2(KoPathShape::createShapeFromPainterPath(clipPath2));
    KoClipPath *koClipPath2 = new KoClipPath({clipShape2.take()}, KoFlake::UserSpaceOnUse);
    koClipPath2->setClipRule(Qt::WindingFill);
    group->setClipPath(koClipPath2);

    SvgRenderTester::testRender(group.take(), "load", "clip_render_test", QSize(30,30), 72.0);
}

void TestSvgParser::testKoClipPathRelativeRendering()
{
    QPainterPath path1;
    path1.addRect(QRect(5,5,15,15));

    QPainterPath path2;
    path2.addRect(QRect(10,10,15,15));

    QPainterPath clipPath1;
    clipPath1.addRect(QRect(10, 0, 10, 30));

    QPainterPath clipPath2;
    clipPath2.moveTo(0,0);
    clipPath2.lineTo(1,0);
    clipPath2.lineTo(0.5,1);
    clipPath2.lineTo(0,0);

    QScopedPointer<KoPathShape> shape1(KoPathShape::createShapeFromPainterPath(path1));
    shape1->setBackground(QSharedPointer<KoColorBackground>(new KoColorBackground(Qt::blue)));

    QScopedPointer<KoPathShape> shape2(KoPathShape::createShapeFromPainterPath(path2));
    shape2->setBackground(QSharedPointer<KoColorBackground>(new KoColorBackground(Qt::red)));

    QScopedPointer<KoPathShape> clipShape1(KoPathShape::createShapeFromPainterPath(clipPath1));
    KoClipPath *koClipPath1 = new KoClipPath({clipShape1.take()}, KoFlake::UserSpaceOnUse);
    koClipPath1->setClipRule(Qt::WindingFill);
    shape1->setClipPath(koClipPath1);

    QScopedPointer<KoShapeGroup> group(new KoShapeGroup());
    {
        QList<KoShape*> shapes({shape1.take(), shape2.take()});

        KoShapeGroupCommand cmd(group.data(), shapes, false);
        cmd.redo();
    }

    QScopedPointer<KoPathShape> clipShape2(KoPathShape::createShapeFromPainterPath(clipPath2));
    KoClipPath *koClipPath2 = new KoClipPath({clipShape2.take()}, KoFlake::ObjectBoundingBox);
    koClipPath2->setClipRule(Qt::WindingFill);
    group->setClipPath(koClipPath2);

    SvgRenderTester::testRender(group.take(), "load", "relative_clip_render_test", QSize(30,30), 72.0);
}

void TestSvgParser::testRenderClipPath_User()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<clipPath id=\"clip1\" clipPathUnits=\"userSpaceOnUse\">"
            "    <rect id=\"clipRect1\" x=\"10\" y=\"0\" width=\"10\" height=\"30\""
            "            fill=\"red\" stroke=\"none\" />"
            "    <rect id=\"clipRect1\" x=\"10\" y=\"0\" width=\"10\" height=\"30\""
            "            fill=\"yellow\" stroke=\"none\" />"
            "</clipPath>"

            "<clipPath id=\"clip2\" clipPathUnits=\"userSpaceOnUse\">"
            "    <path id=\"clipRect1\" d=\"M 0 7 L 30 7 15 30 0 7 z\""
            "            fill=\"red\" stroke=\"none\" />"
            "</clipPath>"

            "<g id=\"testRect\" clip-path=\"url(#clip2)\">"
            "    <rect id=\"testRect1\" x=\"5\" y=\"5\" width=\"15\" height=\"15\""
            "        fill=\"blue\" stroke=\"none\" clip-path=\"url(#clip1)\"/>"

            "    <rect id=\"testRect2\" x=\"10\" y=\"10\" width=\"15\" height=\"15\""
            "        fill=\"red\" stroke=\"none\"/>"
            "</g>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("clip_render_test", false);
}

void TestSvgParser::testRenderClipPath_Obb()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<clipPath id=\"clip1\" clipPathUnits=\"userSpaceOnUse\">"
            "    <rect id=\"clipRect1\" x=\"10\" y=\"0\" width=\"10\" height=\"30\""
            "            fill=\"red\" stroke=\"none\" />"
            "    <rect id=\"clipRect1\" x=\"10\" y=\"0\" width=\"10\" height=\"30\""
            "            fill=\"yellow\" stroke=\"none\" />"
            "</clipPath>"

            "<clipPath id=\"clip2\" clipPathUnits=\"objectBoundingBox\">"
            "    <path id=\"clipRect1\" d=\"M 0 0 L 1 0 0.5 1 0 0 z\""
            "            fill=\"red\" stroke=\"none\" />"
            "</clipPath>"

            "<g id=\"testRect\" clip-path=\"url(#clip2)\">"
            "    <rect id=\"testRect1\" x=\"5\" y=\"5\" width=\"15\" height=\"15\""
            "        fill=\"blue\" stroke=\"none\" clip-path=\"url(#clip1)\"/>"

            "    <rect id=\"testRect2\" x=\"10\" y=\"10\" width=\"15\" height=\"15\""
            "        fill=\"red\" stroke=\"none\"/>"
            "</g>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("relative_clip_render_test", false);
}

void TestSvgParser::testRenderClipPath_Obb_Transform()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<clipPath id=\"clip1\" clipPathUnits=\"userSpaceOnUse\""
            "    transform=\"rotate(90,15,15)\">"
            "    <rect id=\"clipRect1\" x=\"10\" y=\"0\" width=\"10\" height=\"30\""
            "            fill=\"red\" stroke=\"none\" />"
            "    <rect id=\"clipRect1\" x=\"10\" y=\"0\" width=\"10\" height=\"30\""
            "            fill=\"yellow\" stroke=\"none\" />"
            "</clipPath>"

            "<clipPath id=\"clip2\" clipPathUnits=\"objectBoundingBox\""
            "    transform=\"rotate(90 0.5 0.5)\">"
            "    <path id=\"clipRect1\" d=\"M 0 0 L 1 0 0.5 1 0 0 z\""
            "            fill=\"red\" stroke=\"none\" />"
            "</clipPath>"

            "<g id=\"testRect\" clip-path=\"url(#clip2)\">"
            "    <rect id=\"testRect1\" x=\"5\" y=\"5\" width=\"15\" height=\"15\""
            "        fill=\"blue\" stroke=\"none\" clip-path=\"url(#clip1)\"/>"

            "    <rect id=\"testRect2\" x=\"10\" y=\"10\" width=\"15\" height=\"15\""
            "        fill=\"red\" stroke=\"none\"/>"
            "</g>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("clip_render_test_rotated", false);
}

void TestSvgParser::testRenderClipMask_Obb()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<defs>"

            "    <linearGradient id=\"Gradient\" gradientUnits=\"objectBoundingBox\""
            "        x1=\"0\" y1=\"0\" x2=\"1\" y2=\"0\">"

            "        <stop offset=\"0\" stop-color=\"white\" stop-opacity=\"0\" />"
            "        <stop offset=\"1\" stop-color=\"white\" stop-opacity=\"1\" />"

            "    </linearGradient>"

            "    <mask id=\"Mask\" maskUnits=\"objectBoundingBox\""
            "        maskContentUnits=\"objectBoundingBox\""
            "        x=\"0.2\" y=\"0.2\" width=\"0.6\" height=\"0.6\">"

            "        <rect x=\"0\" y=\"0\" width=\"1\" height=\"1\" fill=\"url(#Gradient)\" />"

            "    </mask>"

            "</defs>"


            "<g id=\"testRect\">"
            "    <rect id=\"testRect1\" x=\"5\" y=\"5\" width=\"15\" height=\"15\""
            "        fill=\"blue\" stroke=\"none\"/>"

            "    <rect id=\"testRect2\" x=\"10\" y=\"10\" width=\"15\" height=\"15\""
            "        fill=\"red\" stroke=\"none\" mask=\"url(#Mask)\" />"
            "</g>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("clip_mask_obb", false);
}

void TestSvgParser::testRenderClipMaskOnGroup_Obb()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<defs>"

            "    <linearGradient id=\"Gradient\" gradientUnits=\"objectBoundingBox\""
            "        x1=\"0\" y1=\"0\" x2=\"1\" y2=\"0\">"

            "        <stop offset=\"0\" stop-color=\"white\" stop-opacity=\"0\" />"
            "        <stop offset=\"1\" stop-color=\"white\" stop-opacity=\"1\" />"

            "    </linearGradient>"

            "    <mask id=\"Mask\" maskUnits=\"objectBoundingBox\""
            "        maskContentUnits=\"objectBoundingBox\""
            "        x=\"0.2\" y=\"0.2\" width=\"0.6\" height=\"0.6\">"

            "        <rect x=\"0\" y=\"0\" width=\"1\" height=\"1\" fill=\"url(#Gradient)\" />"

            "    </mask>"

            "</defs>"


            "<g id=\"testRect\" mask=\"url(#Mask)\">"
            "    <rect id=\"testRect1\" x=\"5\" y=\"5\" width=\"15\" height=\"15\""
            "        fill=\"blue\" stroke=\"none\"/>"

            "    <rect id=\"testRect2\" x=\"10\" y=\"10\" width=\"15\" height=\"15\""
            "        fill=\"red\" stroke=\"none\" />"
            "</g>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("clip_mask_on_group_obb", false);
}

QByteArray fileFetcherFunc(const QString &name)
{
    const QString fileName = TestUtil::fetchDataFileLazy(name);
    QFile file(fileName);
    KIS_ASSERT(file.exists());
    file.open(QIODevice::ReadOnly);
    return file.readAll();
}

void TestSvgParser::testRenderClipMask_User_Clip_Obb()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            //"<defs>"

            "    <linearGradient id=\"Gradient\" gradientUnits=\"objectBoundingBox\""
            "        x1=\"0\" y1=\"0\" x2=\"1\" y2=\"0\">"

            "        <stop offset=\"0\" stop-color=\"white\" stop-opacity=\"0\" />"
            "        <stop offset=\"1\" stop-color=\"white\" stop-opacity=\"1\" />"

            "    </linearGradient>"

            "    <mask id=\"Mask\" maskUnits=\"objectBoundingBox\""
            "        maskContentUnits=\"userSpaceOnUse\""
            "        x=\"0.2\" y=\"0.2\" width=\"0.6\" height=\"0.6\">"

            "        <rect x=\"10\" y=\"10\" width=\"15\" height=\"15\" fill=\"url(#Gradient)\" />"

            "    </mask>"

            //"</defs>"


            "<g id=\"testRect\">"
            "    <rect id=\"testRect1\" x=\"5\" y=\"5\" width=\"15\" height=\"15\""
            "        fill=\"blue\" stroke=\"none\"/>"

            "    <rect id=\"testRect2\" x=\"10\" y=\"10\" width=\"15\" height=\"15\""
            "        fill=\"red\" stroke=\"none\" mask=\"url(#Mask)\" />"
            "</g>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("clip_mask_obb", false);
}

void TestSvgParser::testRenderClipMask_User_Clip_User()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            //"<defs>"

            "    <linearGradient id=\"Gradient\" gradientUnits=\"objectBoundingBox\""
            "        x1=\"0\" y1=\"0\" x2=\"1\" y2=\"0\">"

            "        <stop offset=\"0\" stop-color=\"white\" stop-opacity=\"0\" />"
            "        <stop offset=\"1\" stop-color=\"white\" stop-opacity=\"1\" />"

            "    </linearGradient>"

            "    <mask id=\"Mask\" maskUnits=\"userSpaceOnUse\""
            "        maskContentUnits=\"userSpaceOnUse\""
            "        x=\"13\" y=\"13\" width=\"9\" height=\"9\">"

            "        <rect x=\"10\" y=\"10\" width=\"15\" height=\"15\" fill=\"url(#Gradient)\" />"

            "    </mask>"

            //"</defs>"


            "<g id=\"testRect\">"
            "    <rect id=\"testRect1\" x=\"5\" y=\"5\" width=\"15\" height=\"15\""
            "        fill=\"blue\" stroke=\"none\"/>"

            "    <rect id=\"testRect2\" x=\"10\" y=\"10\" width=\"15\" height=\"15\""
            "        fill=\"red\" stroke=\"none\" mask=\"url(#Mask)\" />"
            "</g>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("clip_mask_obb", false);
}

void TestSvgParser::testRenderImage_AspectDefault()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<g id=\"testRect\">"
            "    <rect id=\"testRect1\" x=\"2\" y=\"2\" width=\"26\" height=\"26\""
            "        fill=\"green\" stroke=\"none\"/>"

            "    <rect id=\"testRect1\" x=\"5\" y=\"5\" width=\"15\" height=\"10\""
            "        fill=\"white\" stroke=\"none\"/>"


            "    <image x=\"5\" y=\"5\" width=\"15\" height=\"10\""
            "        xlink:href=\"svg_render/testing_ref_image.png\">"

            "        <title>My image</title>"

            "    </image>"

            "</g>"

            "</svg>";

    SvgRenderTester t (data);
    t.parser.setFileFetcher(fileFetcherFunc);

    t.test_standard_30px_72ppi("image_aspect_default", false);
}

void TestSvgParser::testRenderImage_AspectNone()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<g id=\"testRect\">"
            "    <rect id=\"testRect1\" x=\"2\" y=\"2\" width=\"26\" height=\"26\""
            "        fill=\"green\" stroke=\"none\"/>"

            "    <rect id=\"testRect1\" x=\"5\" y=\"5\" width=\"15\" height=\"10\""
            "        fill=\"white\" stroke=\"none\"/>"


            "    <image x=\"5\" y=\"5\" width=\"15\" height=\"10\""
            "        preserveAspectRatio=\"none\""
            "        xlink:href=\"svg_render/testing_ref_image.png\">"

            "        <title>My image</title>"

            "    </image>"

            "</g>"

            "</svg>";

    SvgRenderTester t (data);
    t.setFuzzyThreshold(5);
    t.parser.setFileFetcher(fileFetcherFunc);

    t.test_standard_30px_72ppi("image_aspect_none", false);
}

void TestSvgParser::testRenderImage_AspectMeet()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<g id=\"testRect\">"
            "    <rect id=\"testRect1\" x=\"2\" y=\"2\" width=\"26\" height=\"26\""
            "        fill=\"green\" stroke=\"none\"/>"

            "    <rect id=\"testRect1\" x=\"5\" y=\"5\" width=\"15\" height=\"10\""
            "        fill=\"white\" stroke=\"none\"/>"


            "    <image x=\"5\" y=\"5\" width=\"15\" height=\"10\""
            "        preserveAspectRatio=\"xMinYMin meet\""
            "        xlink:href=\"svg_render/testing_ref_image.png\">"

            "        <title>My image</title>"

            "    </image>"

            "</g>"

            "</svg>";

    SvgRenderTester t (data);
    t.parser.setFileFetcher(fileFetcherFunc);

    t.test_standard_30px_72ppi("image_aspect_meet", false);
}

void TestSvgParser::testRectShapeRoundUniformX()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "    <rect id=\"testRect\" x=\"5\" y=\"5\" width=\"20\" height=\"20\""
            "        rx=\"5\""
            "        fill=\"blue\" stroke=\"none\"/>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("rect_5_5", false);
}

void TestSvgParser::testRectShapeRoundUniformY()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "    <rect id=\"testRect\" x=\"5\" y=\"5\" width=\"20\" height=\"20\""
            "        rx=\"5\""
            "        fill=\"blue\" stroke=\"none\"/>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("rect_5_5", false);
}

void TestSvgParser::testRectShapeRoundXY()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "    <rect id=\"testRect\" x=\"5\" y=\"5\" width=\"20\" height=\"20\""
            "        rx=\"5\" ry=\"10\""
            "        fill=\"blue\" stroke=\"none\"/>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("rect_5_10", false);
}

void TestSvgParser::testRectShapeRoundXYOverflow()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "    <rect id=\"testRect\" x=\"5\" y=\"5\" width=\"20\" height=\"20\""
            "        rx=\"5\" ry=\"25\""
            "        fill=\"blue\" stroke=\"none\"/>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("rect_5_10", false);
}

void TestSvgParser::testCircleShape()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "    <circle id=\"testRect\" cx=\"15\" cy=\"15\" r=\"10\""
            "        fill=\"blue\" stroke=\"white\"/>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("circle", false);
}

void TestSvgParser::testEllipseShape()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "    <ellipse id=\"testRect\" cx=\"15\" cy=\"15\" rx=\"10\" ry=\"5\""
            "        fill=\"blue\" stroke=\"white\"/>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("ellipse", false);
}

void TestSvgParser::testLineShape()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "    <line id=\"testRect\" x1=\"5\" y1=\"5\" x2=\"25\" y2=\"15\""
            "        fill=\"blue\" stroke=\"white\"/>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("line", false);
}

void TestSvgParser::testPolylineShape()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "    <polyline id=\"testRect\" points=\"5,5 10, 5 10,10 5,10 \n"
            "        5 ,15 15 , 15 15,20 20,20 20,10 25,10 25,25 5,25\""
            "        fill=\"red\" stroke=\"white\"/>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("polyline", false);
}

void TestSvgParser::testPolygonShape()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "    <polygon id=\"testRect\" points=\"5,5 10, 5 10,10 5,10 \n"
            "        5 ,15 15 , 15 15,20 20,20 20,10 25,10 25,25 5,25\""
            "        fill=\"red\" stroke=\"white\"/>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("polygon", false);
}

void TestSvgParser::testPathShape()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "    <path id=\"testRect\""
            "        d=\"M 5,5 h 5 l 0,5 L5,10 l 0,5 l10,0 v5 l 5, 0 L20, 10 l 5,0 L 25,25 L 5,25 z\""
            "        fill=\"red\" stroke=\"white\"/>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("polygon", false);
}

void TestSvgParser::testPathData()
{
    const QString fileName = TestUtil::fetchDataFileLazy("paths-data-03-f.svg");
    QVERIFY(!fileName.isEmpty());

    QFile file(fileName);
    file.open(QIODevice::ReadOnly);
    QByteArray pathData = file.readAll();

    SvgRenderTester t(pathData);
    t.test_standard("paths_data", {480, 360}, 72);
}

void TestSvgParser::testPathShapeEllipticalArc()
{
    const QString data =
        "<svg width=\"100px\" height=\"100px\""
        "   xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"
        ""
        "  <path"
        "     d=\"M 0.12808856,29.836374 3.4329535,27.237679"
        "        a 1.8474812,2.9054325 0 013.3048758,-2.598695 l 3.3048637,-2.598687"
        "        a 5.4242101,1.9738467 73.492263 0,1,3.304876,-2.598694 l 3.304865,-2.598696"
        "        A 8.0718175,1.9886167 71.359615,0119.9573,14.244222 l 3.304873,-2.598695"
        "        a 10.73523,1.9933048 70.675722   0  1 3.304865,-2.5986956 l 3.304876,-2.598694"
        "        a 0,0,0 00, 0.12808856,29.836374 a 10.73523,1.9933048,0 00, 0,0\""
        "     fill=\"none\" stroke=\"#ff0000\" stroke-width=\"0.414447\""
        "     transform=\"scale(3, 3)\""
        "     id=\"testRect\" />"
        "</svg>";

    SvgRenderTester t (data);
    t.test_standard("elliptical_arc", QSize(100, 100), 72);
}

void TestSvgParser::testDefsHidden()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<g id=\"testRect\">"
            "    <defs>"
            "        <rect id=\"testRect1\" x=\"5\" y=\"5\" width=\"15\" height=\"15\""
            "            fill=\"blue\" stroke=\"none\"/>"
            "    </defs>"

            "    <rect id=\"testRect2\" x=\"10\" y=\"10\" width=\"15\" height=\"15\""
            "        fill=\"red\" stroke=\"none\"/>"
            "</g>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("test_defs_hidden", false);
}

void TestSvgParser::testDefsUseInheritance()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"



            "<g id=\"unrenderedRect\" fill=\"green\" >"
            "    <defs>"
            "        <rect id=\"testRect1\" x=\"1\" y=\"1\" width=\"15\" height=\"15\""
            "            stroke=\"none\"/>"
            "    </defs>"
            "</g>"


            /**
             * NOTES:
             * 1) width/height attributes for <use> are not implemented yet
             * 2) x and y are summed up
             * 3) stroke="white" is overridden by the original templated object
             * 4) fill="green" attribute from <defs> is not inherited
             */

            "<g id=\"testRect\" fill=\"blue\" >"
            "    <use x=\"4\" y=\"4\" xlink:href=\"#testRect1\""
            "        stroke=\"white\" stroke-width=\"1\" />"

            "    <rect id=\"testRect2\" x=\"10\" y=\"10\" width=\"15\" height=\"15\""
            "        fill=\"red\" stroke=\"none\"/>"
            "</g>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("defs_use_inheritance", false);
}

void TestSvgParser::testUseWithoutDefs()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            // technical rect for rendering
            "<g id=\"testRect\">"

            "<g id=\"renderedRect1\" fill=\"green\" >"
            "    <rect id=\"testRect1\" x=\"1\" y=\"1\" width=\"15\" height=\"15\""
            "        stroke=\"none\"/>"
            "</g>"


            /**
             * NOTES:
             * 1) width/height attributes for <use> are not implemented yet
             * 2) x and y are summed up
             * 3) stroke="white" is overridden by the original templated object
             * 4) fill="green" attribute from <defs> is not inherited
             */

            "<g id=\"renderedRect2\" fill=\"blue\" >"
            "    <use x=\"4\" y=\"4\" xlink:href=\"#testRect1\""
            "        stroke=\"white\" stroke-width=\"1\" />"

            "    <rect id=\"testRect2\" x=\"10\" y=\"10\" width=\"15\" height=\"15\""
            "        fill=\"red\" stroke=\"none\"/>"
            "</g>"

            "</g>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("use_without_defs", false);
}

void TestSvgParser::testMarkersAutoOrientation()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<marker id=\"SimpleRectMarker\""
            "    orient=\"auto\" refY=\"12.5\" refX=\"12.5\" >"

            "    <rect id=\"markerRect\" x=\"10\" y=\"10\" width=\"5\" height=\"5\""
            "        fill=\"red\" stroke=\"none\"/>"
            "    <rect id=\"markerRect\" x=\"14\" y=\"12\" width=\"1\" height=\"1\""
            "        fill=\"yellow\" stroke=\"none\"/>"
            "    <rect id=\"markerRect\" x=\"12\" y=\"12\" width=\"1\" height=\"1\""
            "        fill=\"white\" stroke=\"none\"/>"
            "</marker>"

            "<path id=\"testRect\""
            "    style=\"fill:none;stroke:#000000;stroke-width:1px;marker-start:url(#SimpleRectMarker);marker-end:url(#SimpleRectMarker);marker-mid:url(#SimpleRectMarker)\""
            "    d=\"M5,15 C5,5 25,5 25,15 L15,25\"/>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("markers", false);
}

void TestSvgParser::testMarkersAutoOrientationScaled()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<marker id=\"SimpleRectMarker\""
            "    orient=\"auto\" refY=\"12.5\" refX=\"12.5\" >"

            "    <rect id=\"markerRect\" x=\"10\" y=\"10\" width=\"5\" height=\"5\""
            "        fill=\"red\" stroke=\"none\"/>"
            "    <rect id=\"markerRect\" x=\"14\" y=\"12\" width=\"1\" height=\"1\""
            "        fill=\"yellow\" stroke=\"none\"/>"
            "    <rect id=\"markerRect\" x=\"12\" y=\"12\" width=\"1\" height=\"1\""
            "        fill=\"white\" stroke=\"none\"/>"
            "</marker>"

            "<path id=\"testRect\""
            "    style=\"fill:none;stroke:#000000;stroke-width:2px;marker-start:url(#SimpleRectMarker);marker-end:url(#SimpleRectMarker);marker-mid:url(#SimpleRectMarker)\""
            "    d=\"M5,15 C5,5 25,5 25,15 L15,25\"/>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("markers_scaled", false);
}

void TestSvgParser::testMarkersAutoOrientationScaledUserCoordinates()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<marker id=\"SimpleRectMarker\""
            "    markerUnits = \"userSpaceOnUse\""
            "    orient=\"auto\" refY=\"12.5\" refX=\"12.5\" >"

            "    <rect id=\"markerRect\" x=\"10\" y=\"10\" width=\"5\" height=\"5\""
            "        fill=\"red\" stroke=\"none\"/>"
            "    <rect id=\"markerRect\" x=\"14\" y=\"12\" width=\"1\" height=\"1\""
            "        fill=\"yellow\" stroke=\"none\"/>"
            "    <rect id=\"markerRect\" x=\"12\" y=\"12\" width=\"1\" height=\"1\""
            "        fill=\"white\" stroke=\"none\"/>"
            "</marker>"

            "<path id=\"testRect\""
            "    style=\"fill:none;stroke:#000000;stroke-width:2px;marker-start:url(#SimpleRectMarker);marker-end:url(#SimpleRectMarker);marker-mid:url(#SimpleRectMarker)\""
            "    d=\"M5,15 C5,5 25,5 25,15 L15,25\"/>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("markers_user_coordinates", false);
}

void TestSvgParser::testMarkersCustomOrientation()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<marker id=\"SimpleRectMarker\""
            "    orient=\"45\" refY=\"12.5\" refX=\"12.5\" >"

            "    <rect id=\"markerRect\" x=\"10\" y=\"10\" width=\"5\" height=\"5\""
            "        fill=\"red\" stroke=\"none\"/>"
            "    <rect id=\"markerRect\" x=\"14\" y=\"12\" width=\"1\" height=\"1\""
            "        fill=\"yellow\" stroke=\"none\"/>"
            "    <rect id=\"markerRect\" x=\"12\" y=\"12\" width=\"1\" height=\"1\""
            "        fill=\"white\" stroke=\"none\"/>"
            "</marker>"

            "<path id=\"testRect\""
            "    style=\"fill:none;stroke:#000000;stroke-width:1px;marker-start:url(#SimpleRectMarker);marker-end:url(#SimpleRectMarker);marker-mid:url(#SimpleRectMarker)\""
            "    d=\"M5,15 C5,5 25,5 25,15 L15,25\"/>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("markers_custom_orientation", false);
}

void TestSvgParser::testMarkersDifferent()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<marker id=\"SimpleRectMarker1\""
            "    orient=\"auto\" refY=\"12.5\" refX=\"12.5\" >"

            "    <rect id=\"markerRect\" x=\"10\" y=\"10\" width=\"5\" height=\"5\""
            "        fill=\"red\" stroke=\"none\"/>"
            "    <rect id=\"markerRect\" x=\"14\" y=\"12\" width=\"1\" height=\"1\""
            "        fill=\"yellow\" stroke=\"none\"/>"
            "    <rect id=\"markerRect\" x=\"12\" y=\"12\" width=\"1\" height=\"1\""
            "        fill=\"white\" stroke=\"none\"/>"
            "</marker>"

            "<marker id=\"SimpleRectMarker2\""
            "    orient=\"auto\" refY=\"12.5\" refX=\"12.5\" >"

            "    <rect id=\"markerRect\" x=\"10\" y=\"10\" width=\"5\" height=\"5\""
            "        fill=\"green\" stroke=\"none\"/>"
            "    <rect id=\"markerRect\" x=\"14\" y=\"12\" width=\"1\" height=\"1\""
            "        fill=\"yellow\" stroke=\"none\"/>"
            "    <rect id=\"markerRect\" x=\"12\" y=\"12\" width=\"1\" height=\"1\""
            "        fill=\"white\" stroke=\"none\"/>"
            "</marker>"

            "<marker id=\"SimpleRectMarker3\""
            "    orient=\"auto\" refY=\"12.5\" refX=\"12.5\" >"

            "    <rect id=\"markerRect\" x=\"10\" y=\"10\" width=\"5\" height=\"5\""
            "        fill=\"blue\" stroke=\"none\"/>"
            "    <rect id=\"markerRect\" x=\"14\" y=\"12\" width=\"1\" height=\"1\""
            "        fill=\"yellow\" stroke=\"none\"/>"
            "    <rect id=\"markerRect\" x=\"12\" y=\"12\" width=\"1\" height=\"1\""
            "        fill=\"white\" stroke=\"none\"/>"
            "</marker>"

            "<path id=\"testRect\""
            "    style=\"fill:none;stroke:#000000;stroke-width:1px;marker-start:url(#SimpleRectMarker1);marker-end:url(#SimpleRectMarker2);marker-mid:url(#SimpleRectMarker3)\""
            "    d=\"M5,15 C5,5 25,5 25,15 L15,25\"/>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("markers_different", false);
}

void TestSvgParser::testMarkersFillAsShape()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<defs>"
            "    <linearGradient id=\"testGrad\" x1=\"0%\" y1=\"50%\" x2=\"100%\" y2=\"50%\">"
            "        <stop offset=\"5%\" stop-color=\"#F60\" />"
            "        <stop offset=\"80%\" stop-color=\"#FF6\" />"
            "    </linearGradient>"

            "    <marker id=\"SimpleRectMarker\""
            "        orient=\"auto\" refY=\"12.5\" refX=\"12.5\" >"

            "        <rect id=\"markerRect\" x=\"10\" y=\"10\" width=\"5\" height=\"5\""
            "            fill=\"red\" stroke=\"none\"/>"
            "        <rect id=\"markerRect\" x=\"14\" y=\"12\" width=\"1\" height=\"1\""
            "            fill=\"yellow\" stroke=\"none\"/>"
            "        <rect id=\"markerRect\" x=\"12\" y=\"12\" width=\"1\" height=\"1\""
            "            fill=\"white\" stroke=\"none\"/>"
            "    </marker>"

            "</defs>"

            "<path id=\"testRect\""
            "    style=\"fill:none;stroke:url(#testGrad) magenta;stroke-width:2px;marker-start:url(#SimpleRectMarker);marker-end:url(#SimpleRectMarker);marker-mid:url(#SimpleRectMarker);krita|marker-fill-method:auto\""
            "    d=\"M5,15 C5,5 25,5 25,15 L15,25\"/>"
            //"    d=\"M5,15 L25,15\"/>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("markers_scaled_fill_as_shape", false);
}

void TestSvgParser::testMarkersOnClosedPath()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            "<marker id=\"SimpleRectMarker1\""
            "    orient=\"auto\" refY=\"12.5\" refX=\"12.5\" >"

            "    <rect id=\"markerRect\" x=\"9\" y=\"9\" width=\"7\" height=\"7\""
            "        fill=\"red\" stroke=\"none\"/>"
            "    <rect id=\"markerRect\" x=\"14\" y=\"12\" width=\"1\" height=\"1\""
            "        fill=\"yellow\" stroke=\"none\"/>"
            "    <rect id=\"markerRect\" x=\"12\" y=\"12\" width=\"1\" height=\"1\""
            "        fill=\"white\" stroke=\"none\"/>"
            "</marker>"

            "<marker id=\"SimpleRectMarker2\""
            "    orient=\"auto\" refY=\"12.5\" refX=\"12.5\" >"

            "    <rect id=\"markerRect\" x=\"10\" y=\"10\" width=\"5\" height=\"5\""
            "        fill=\"green\" stroke=\"none\"/>"
            "    <rect id=\"markerRect\" x=\"14\" y=\"12\" width=\"1\" height=\"1\""
            "        fill=\"yellow\" stroke=\"none\"/>"
            "    <rect id=\"markerRect\" x=\"12\" y=\"12\" width=\"1\" height=\"1\""
            "        fill=\"white\" stroke=\"none\"/>"
            "</marker>"

            "<marker id=\"SimpleRectMarker3\""
            "    orient=\"auto\" refY=\"12.5\" refX=\"12.5\" >"

            "    <rect id=\"markerRect\" x=\"10\" y=\"10\" width=\"5\" height=\"5\""
            "        fill=\"blue\" stroke=\"none\"/>"
            "    <rect id=\"markerRect\" x=\"14\" y=\"12\" width=\"1\" height=\"1\""
            "        fill=\"yellow\" stroke=\"none\"/>"
            "    <rect id=\"markerRect\" x=\"12\" y=\"12\" width=\"1\" height=\"1\""
            "        fill=\"white\" stroke=\"none\"/>"
            "</marker>"

            "<path id=\"testRect\""
            "    style=\"fill:none;stroke:#000000;stroke-width:1px;marker-start:url(#SimpleRectMarker1);marker-end:url(#SimpleRectMarker2);marker-mid:url(#SimpleRectMarker3)\""
            "    d=\"M5,15 C5,5 25,5 25,15 L15,25 z\"/>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("markers_on_closed_path", false);
}


void TestSvgParser::testGradientRecoveringTransform()
{
    // used for experimenting purposes only!

    QImage image(100,100,QImage::Format_ARGB32);
    image.fill(0);
    QPainter painter(&image);

    painter.setPen(QPen(Qt::black, 0));


    QLinearGradient gradient(0, 0.5, 1, 0.5);
    gradient.setCoordinateMode(QGradient::ObjectBoundingMode);

    //QLinearGradient gradient(0, 50, 100, 50);
    //gradient.setCoordinateMode(QGradient::LogicalMode);

    gradient.setColorAt(0.0, Qt::red);
    gradient.setColorAt(1.0, Qt::blue);

    QTransform gradientTransform;
    gradientTransform.shear(0.2, 0);

    {
        QBrush brush(gradient);
        brush.setTransform(gradientTransform);
        painter.setBrush(brush);
    }

    QRect mainShape(3,3,94,94);
    painter.drawRect(mainShape);

    QTransform gradientToUser(mainShape.width(), 0, 0, mainShape.height(),
                              mainShape.x(), mainShape.y());

    QRect smallShape(0,0,20,20);
    QTransform smallShapeTransform;

    {
        smallShapeTransform =
            QTransform::fromTranslate(-smallShape.center().x(), -smallShape.center().y());

        QTransform r; r.rotate(90);
        smallShapeTransform *= r;

        smallShapeTransform *=
            QTransform::fromTranslate(mainShape.center().x(), mainShape.center().y());
    }


    {
        gradient.setCoordinateMode(QGradient::LogicalMode);
        QBrush brush(gradient);
        brush.setTransform(gradientTransform * gradientToUser * smallShapeTransform.inverted());
        painter.setBrush(brush);
        painter.setPen(Qt::NoPen);
    }

    painter.setTransform(smallShapeTransform);
    painter.drawRect(smallShape);

    //image.save("gradient_recovering_transform.png");
}

void TestSvgParser::testMarkersAngularUnits()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"

            // start
            "<marker id=\"SimpleRectMarker1\""
            "    orient=\"45deg\" refY=\"12.5\" refX=\"12.5\" >"

            "    <rect id=\"markerRect\" x=\"10\" y=\"10\" width=\"5\" height=\"5\""
            "        fill=\"red\" stroke=\"none\"/>"
            "    <rect id=\"markerRect\" x=\"14\" y=\"12\" width=\"1\" height=\"1\""
            "        fill=\"yellow\" stroke=\"none\"/>"
            "    <rect id=\"markerRect\" x=\"12\" y=\"12\" width=\"1\" height=\"1\""
            "        fill=\"white\" stroke=\"none\"/>"
            "</marker>"

            // end
            "<marker id=\"SimpleRectMarker2\""
            "    orient=\"200grad\" refY=\"12.5\" refX=\"12.5\" >"

            "    <rect id=\"markerRect\" x=\"10\" y=\"10\" width=\"5\" height=\"5\""
            "        fill=\"green\" stroke=\"none\"/>"
            "    <rect id=\"markerRect\" x=\"14\" y=\"12\" width=\"1\" height=\"1\""
            "        fill=\"yellow\" stroke=\"none\"/>"
            "    <rect id=\"markerRect\" x=\"12\" y=\"12\" width=\"1\" height=\"1\""
            "        fill=\"white\" stroke=\"none\"/>"
            "</marker>"

            // mid
            "<marker id=\"SimpleRectMarker3\""
            "    orient=\"-1.57rad\" refY=\"12.5\" refX=\"12.5\" >"

            "    <rect id=\"markerRect\" x=\"10\" y=\"10\" width=\"5\" height=\"5\""
            "        fill=\"blue\" stroke=\"none\"/>"
            "    <rect id=\"markerRect\" x=\"14\" y=\"12\" width=\"1\" height=\"1\""
            "        fill=\"yellow\" stroke=\"none\"/>"
            "    <rect id=\"markerRect\" x=\"12\" y=\"12\" width=\"1\" height=\"1\""
            "        fill=\"white\" stroke=\"none\"/>"
            "</marker>"

            "<path id=\"testRect\""
            "    style=\"fill:none;stroke:#000000;stroke-width:1px;marker-start:url(#SimpleRectMarker1);marker-end:url(#SimpleRectMarker2);marker-mid:url(#SimpleRectMarker3)\""
            "    d=\"M5,15 C5,5 25,5 25,15 L15,25\"/>"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("markers_angular_units", false);
}

#include "KoParameterShape.h"

void TestSvgParser::testSodipodiArcShape()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\""
            "    xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\""
            ">"

            "<path"
            "    fill=\"red\" stroke=\"black\""
            "    id=\"testRect\""

            "    sodipodi:type=\"arc\""
            "    sodipodi:cx=\"15.464287\""
            "    sodipodi:cy=\"14.517863\""
            "    sodipodi:rx=\"6.25\""
            "    sodipodi:ry=\"8.5\""
            "    sodipodi:start=\"5.5346039\""
            "    sodipodi:end=\"4.0381334\""
            //"    d=\"m 20.043381,8.7327624 a 6.25,8.5 0 0 1 1.053863,9.4677386 6.25,8.5 0 0 1 -6.094908,4.794113 6.25,8.5 0 0 1 -5.5086474,-5.963709 6.25,8.5 0 0 1 2.0686234,-9.1530031 l 3.901975,6.6399611 z\" />"
            "    d=\" some weird unparsable text \" />"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("sodipodi_closed_arc", false);

    KoShape *shape = t.findShape("testRect");
    QVERIFY(dynamic_cast<KoParameterShape*>(shape));
}

void TestSvgParser::testSodipodiArcShapeOpen()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\""
            "    xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\""
            ">"

            "<path"
            "    fill=\"red\" stroke=\"black\""
            "    id=\"testRect\""

            "    sodipodi:type=\"arc\""
            "    sodipodi:open=\"true\""
            "    sodipodi:cx=\"15.464287\""
            "    sodipodi:cy=\"14.517863\""
            "    sodipodi:rx=\"6.25\""
            "    sodipodi:ry=\"8.5\""
            "    sodipodi:start=\"5.5346039\""
            "    sodipodi:end=\"4.0381334\""
            //"    d=\"m 20.043381,8.7327624 a 6.25,8.5 0 0 1 1.053863,9.4677386 6.25,8.5 0 0 1 -6.094908,4.794113 6.25,8.5 0 0 1 -5.5086474,-5.963709 6.25,8.5 0 0 1 2.0686234,-9.1530031 l 3.901975,6.6399611 z\" />"
            "    d=\" some weird unparsable text \" />"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("sodipodi_open_arc", false);

    KoShape *shape = t.findShape("testRect");
    QVERIFY(dynamic_cast<KoParameterShape*>(shape));
}

void TestSvgParser::testKritaChordShape()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\""
            "    xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\""
            ">"

            "<path"
            "    fill=\"red\" stroke=\"black\""
            "    id=\"testRect\""

            "    krita:type=\"arc\""
            "    krita:arcType=\"chord\""
            "    krita:cx=\"15.464287\""
            "    krita:cy=\"14.517863\""
            "    krita:rx=\"6.25\""
            "    krita:ry=\"8.5\""
            "    krita:start=\"5.5346039\""
            "    krita:end=\"4.0381334\""
            //"    d=\"m 20.043381,8.7327624 a 6.25,8.5 0 0 1 1.053863,9.4677386 6.25,8.5 0 0 1 -6.094908,4.794113 6.25,8.5 0 0 1 -5.5086474,-5.963709 6.25,8.5 0 0 1 2.0686234,-9.1530031 l 3.901975,6.6399611 z\" />"
            "    d=\" some weird unparsable text \" />"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("sodipodi_chord_arc", false);

    KoShape *shape = t.findShape("testRect");
    QVERIFY(dynamic_cast<KoParameterShape*>(shape));
}

void TestSvgParser::testSodipodiChordShape()
{
    const QString data =
            "<svg width=\"30px\" height=\"30px\""
            "    xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\""
            "    xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\""
            ">"

            "<path"
            "    fill=\"red\" stroke=\"black\""
            "    id=\"testRect\""

            "    sodipodi:type=\"arc\""
            "    sodipodi:arc-type=\"chord\""
            "    sodipodi:open=\"true\""
            "    sodipodi:cx=\"15.464287\""
            "    sodipodi:cy=\"14.517863\""
            "    sodipodi:rx=\"6.25\""
            "    sodipodi:ry=\"8.5\""
            "    sodipodi:start=\"5.5346039\""
            "    sodipodi:end=\"4.0381334\""
            //"    d=\"m 20.043381,8.7327624 a 6.25,8.5 0 0 1 1.053863,9.4677386 6.25,8.5 0 0 1 -6.094908,4.794113 6.25,8.5 0 0 1 -5.5086474,-5.963709 6.25,8.5 0 0 1 2.0686234,-9.1530031 l 3.901975,6.6399611 z\" />"
            "    d=\" some weird unparsable text \" />"

            "</svg>";

    SvgRenderTester t (data);

    t.test_standard_30px_72ppi("sodipodi_chord_arc", false);

    KoShape *shape = t.findShape("testRect");
    QVERIFY(dynamic_cast<KoParameterShape*>(shape));
}


KISTEST_MAIN(TestSvgParser)
