/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include #include #include #ifndef SRCDIR #define SRCDIR #endif class tst_QSvgRenderer : public QObject { Q_OBJECT public: tst_QSvgRenderer(); virtual ~tst_QSvgRenderer(); private slots: void getSetCheck(); void inexistentUrl(); void emptyUrl(); void invalidUrl_data(); void invalidUrl(); void testStrokeWidth(); void testMapViewBoxToTarget(); void testRenderElement(); void testRenderElementToBounds(); void testRenderDocumentWithSizeToBounds(); void constructorQXmlStreamReader() const; void loadQXmlStreamReader() const; void nestedQXmlStreamReader() const; void stylePropagation() const; void transformForElement() const; void boundsOnElement() const; void gradientStops() const; void gradientRefs(); void recursiveRefs_data(); void recursiveRefs(); void fillRule(); void opacity(); void paths(); void displayMode(); void strokeInherit(); void testFillInheritance(); void testStopOffsetOpacity(); void testUseElement(); void smallFont(); void styleSheet(); void duplicateStyleId(); void oss_fuzz_23731(); #ifndef QT_NO_COMPRESS void testGzLoading(); void testGzHelper_data(); void testGzHelper(); #endif private: static const char *const src; }; const char *const tst_QSvgRenderer::src = "" ""; tst_QSvgRenderer::tst_QSvgRenderer() { } tst_QSvgRenderer::~tst_QSvgRenderer() { } // Testing get/set functions void tst_QSvgRenderer::getSetCheck() { QSvgRenderer obj1; // int QSvgRenderer::framesPerSecond() // void QSvgRenderer::setFramesPerSecond(int) obj1.setFramesPerSecond(20); QCOMPARE(20, obj1.framesPerSecond()); obj1.setFramesPerSecond(0); QCOMPARE(0, obj1.framesPerSecond()); obj1.setFramesPerSecond(INT_MIN); QCOMPARE(0, obj1.framesPerSecond()); // Can't have a negative framerate obj1.setFramesPerSecond(INT_MAX); QCOMPARE(INT_MAX, obj1.framesPerSecond()); } void tst_QSvgRenderer::inexistentUrl() { const char *src = ""; QByteArray data(src); QSvgRenderer renderer(data); QVERIFY(renderer.isValid()); } void tst_QSvgRenderer::emptyUrl() { const char *src = ""; QByteArray data(src); QSvgRenderer renderer(data); QVERIFY(renderer.isValid()); } void tst_QSvgRenderer::invalidUrl_data() { QTest::addColumn("svg"); QTest::newRow("01") << QByteArray(""); QTest::newRow("02") << QByteArray(""); QTest::newRow("03") << QByteArray(""); QTest::newRow("04") << QByteArray(""); QTest::newRow("05") << QByteArray(""); QTest::newRow("06") << QByteArray(""); QTest::newRow("07") << QByteArray(""); QTest::newRow("08") << QByteArray(""); QTest::newRow("09") << QByteArray(""); QTest::newRow("10") << QByteArray(""); QTest::newRow("11") << QByteArray(""); QTest::newRow("12") << QByteArray(""); QTest::newRow("13") << QByteArray(""); } void tst_QSvgRenderer::invalidUrl() { QFETCH(QByteArray, svg); #if QT_CONFIG(regularexpression) QTest::ignoreMessage(QtWarningMsg, QRegularExpression("Could not resolve property")); #endif QSvgRenderer renderer(svg); QVERIFY(renderer.isValid()); } void tst_QSvgRenderer::testStrokeWidth() { qreal squareSize = 30.0; qreal strokeWidth = 1.0; qreal topLeft = 100.0; QSvgGenerator generator; QBuffer buffer; QByteArray byteArray; buffer.setBuffer(&byteArray); generator.setOutputDevice(&buffer); QPainter painter(&generator); painter.setBrush(Qt::blue); // Draw a rect with stroke painter.setPen(QPen(Qt::black, strokeWidth)); painter.drawRect(topLeft, topLeft, squareSize, squareSize); // Draw a rect without stroke painter.setPen(Qt::NoPen); painter.drawRect(topLeft, topLeft, squareSize, squareSize); painter.end(); // Insert ID tags into the document byteArray.insert(byteArray.indexOf("stroke=\"#000000\""), "id=\"SquareStroke\" "); byteArray.insert(byteArray.indexOf("stroke=\"none\""), "id=\"SquareNoStroke\" "); QSvgRenderer renderer(byteArray); QRectF noStrokeRect = renderer.boundsOnElement("SquareNoStroke"); QCOMPARE(noStrokeRect.width(), squareSize); QCOMPARE(noStrokeRect.height(), squareSize); QCOMPARE(noStrokeRect.x(), topLeft); QCOMPARE(noStrokeRect.y(), topLeft); QRectF strokeRect = renderer.boundsOnElement("SquareStroke"); QCOMPARE(strokeRect.width(), squareSize + strokeWidth); QCOMPARE(strokeRect.height(), squareSize + strokeWidth); QCOMPARE(strokeRect.x(), topLeft - (strokeWidth / 2)); QCOMPARE(strokeRect.y(), topLeft - (strokeWidth / 2)); } void tst_QSvgRenderer::testMapViewBoxToTarget() { const char *src = ""; QByteArray data(src); { // No viewport, viewBox, targetRect, or deviceRect -> boundingRect QPicture picture; QPainter painter(&picture); QSvgRenderer rend(data); rend.render(&painter); painter.end(); QCOMPARE(picture.boundingRect(), QRect(0, 0, 500, 500)); } { // No viewport, viewBox, targetRect -> deviceRect QPicture picture; picture.setBoundingRect(QRect(100, 100, 200, 200)); QPainter painter(&picture); QSvgRenderer rend(data); rend.render(&painter); painter.end(); QCOMPARE(picture.boundingRect(), QRect(100, 100, 200, 200)); } { // No viewport, viewBox -> targetRect QPicture picture; QPainter painter(&picture); QSvgRenderer rend(data); rend.render(&painter, QRectF(50, 50, 250, 250)); painter.end(); QCOMPARE(picture.boundingRect(), QRect(50, 50, 250, 250)); } data.replace("", ""); { // No viewport, no targetRect -> viewBox QPicture picture; QPainter painter(&picture); QSvgRenderer rend(data); rend.render(&painter); painter.end(); QCOMPARE(picture.boundingRect(), QRect(250, 250, 500, 500)); } data.replace(" scale 500x500 square to 1000x750 while preserving aspect ratio gives 750x750 data = ""; QPicture picture; QPainter painter(&picture); QSvgRenderer rend(data); rend.setAspectRatioMode(Qt::KeepAspectRatio); rend.render(&painter); painter.end(); QCOMPARE(picture.boundingRect(), QRect(500, 375, 750, 750)); } } void tst_QSvgRenderer::testRenderElement() { QByteArray data(src); { // No viewport, viewBox, targetRect, or deviceRect -> boundingRect QPicture picture; QPainter painter(&picture); QSvgRenderer rend(data); rend.render(&painter, QLatin1String("foo")); painter.end(); QCOMPARE(picture.boundingRect(), QRect(0, 0, 100, 100)); } { // No viewport, viewBox, targetRect -> deviceRect QPicture picture; picture.setBoundingRect(QRect(100, 100, 200, 200)); QPainter painter(&picture); QSvgRenderer rend(data); rend.render(&painter, QLatin1String("foo")); painter.end(); QCOMPARE(picture.boundingRect(), QRect(100, 100, 200, 200)); } { // No viewport, viewBox -> targetRect QPicture picture; QPainter painter(&picture); QSvgRenderer rend(data); rend.render(&painter, QLatin1String("foo"), QRectF(50, 50, 250, 250)); painter.end(); QCOMPARE(picture.boundingRect(), QRect(50, 50, 250, 250)); } data.replace("", ""); { // No viewport, no targetRect -> view box size QPicture picture; QPainter painter(&picture); QSvgRenderer rend(data); rend.render(&painter, QLatin1String("foo")); painter.end(); QCOMPARE(picture.boundingRect(), QRect(0, 0, 100, 100)); } data.replace("" // Presence of a viewBox triggered QTBUG-79933 "" "" ""; const QByteArray data(src); QSvgRenderer rend(data); rendering.fill(Qt::transparent); QPainter p(&rendering); rend.render(&p, "el1", QRectF( 0, 0, 200, 100)); rend.render(&p, "el2", QRectF( 0, 0, 200, 100)); rend.render(&p, "el1", QRectF(200, 100, 200, 100)); rend.render(&p, "el2", QRectF(200, 100, 200, 100)); } QCOMPARE(reference, rendering); } void tst_QSvgRenderer::testRenderDocumentWithSizeToBounds() { // QTBUG-80888 QImage reference(400, 200, QImage::Format_ARGB32); { reference.fill(Qt::transparent); QPainter p(&reference); p.fillRect(100, 100, 100, 50, Qt::blue); p.fillRect(200, 50, 100, 50, Qt::blue); } QImage rendering(400, 200, QImage::Format_ARGB32); { const char *const src = R"src( )src"; const QByteArray data(src); QSvgRenderer rend(data); rendering.fill(Qt::transparent); QPainter p(&rendering); rend.render(&p, QRectF(100, 50, 200, 100)); } QCOMPARE(reference, rendering); } void tst_QSvgRenderer::constructorQXmlStreamReader() const { const QByteArray data(src); QXmlStreamReader reader(data); QPicture picture; QPainter painter(&picture); QSvgRenderer rend(&reader); rend.render(&painter, QLatin1String("foo")); painter.end(); QCOMPARE(picture.boundingRect(), QRect(0, 0, 100, 100)); } void tst_QSvgRenderer::loadQXmlStreamReader() const { const QByteArray data(src); QXmlStreamReader reader(data); QPicture picture; QPainter painter(&picture); QSvgRenderer rend; rend.load(&reader); rend.render(&painter, QLatin1String("foo")); painter.end(); QCOMPARE(picture.boundingRect(), QRect(0, 0, 100, 100)); } void tst_QSvgRenderer::nestedQXmlStreamReader() const { const QByteArray data(QByteArray("") + QByteArray(src) + QByteArray("")); QXmlStreamReader reader(data); QCOMPARE(reader.readNext(), QXmlStreamReader::StartDocument); QCOMPARE(reader.readNext(), QXmlStreamReader::StartElement); QCOMPARE(reader.name().toString(), QLatin1String("bar")); QPicture picture; QPainter painter(&picture); QSvgRenderer renderer(&reader); renderer.render(&painter, QLatin1String("foo")); painter.end(); QCOMPARE(picture.boundingRect(), QRect(0, 0, 100, 100)); QCOMPARE(reader.readNext(), QXmlStreamReader::EndElement); QCOMPARE(reader.name().toString(), QLatin1String("bar")); QCOMPARE(reader.readNext(), QXmlStreamReader::EndDocument); QVERIFY(reader.atEnd()); QVERIFY(!reader.hasError()); } void tst_QSvgRenderer::stylePropagation() const { QByteArray data("" "" "" "" "" "" "" "" "" "" "" ""); // alpha=cyan, beta=magenta, gamma=yellow, delta=black QImage image1(200, 200, QImage::Format_RGB32); QImage image2(200, 200, QImage::Format_RGB32); QImage image3(200, 200, QImage::Format_RGB32); QPainter painter; QSvgRenderer renderer(data); QLatin1String parts[4] = {QLatin1String("alpha"), QLatin1String("beta"), QLatin1String("gamma"), QLatin1String("delta")}; QVERIFY(painter.begin(&image1)); for (int i = 0; i < 4; ++i) renderer.render(&painter, parts[i], QRectF(renderer.boundsOnElement(parts[i]))); painter.end(); QVERIFY(painter.begin(&image2)); renderer.render(&painter, renderer.viewBoxF()); painter.end(); QVERIFY(painter.begin(&image3)); painter.setPen(Qt::NoPen); painter.setBrush(QBrush(Qt::cyan)); painter.drawRect(0, 0, 100, 100); painter.setBrush(QBrush(Qt::magenta)); painter.drawRect(100, 0, 100, 100); painter.setBrush(QBrush(Qt::yellow)); painter.drawRect(0, 100, 100, 100); painter.setBrush(QBrush(Qt::black)); painter.drawRect(100, 100, 100, 100); painter.end(); QCOMPARE(image1, image2); QCOMPARE(image1, image3); } static qreal transformNorm(const QTransform &m) { return qSqrt(m.m11() * m.m11() + m.m12() * m.m12() + m.m13() * m.m13() + m.m21() * m.m21() + m.m22() * m.m22() + m.m23() * m.m23() + m.m31() * m.m31() + m.m32() * m.m32() + m.m33() * m.m33()); } static bool diffIsSmallEnough(double diff, double norm) { return diff <= 1e-12 * norm; } static inline bool diffIsSmallEnough(float diff, float norm) { return diff <= 1e-5 * norm; } static void compareTransforms(const QTransform &m1, const QTransform &m2) { qreal norm1 = transformNorm(m1); qreal norm2 = transformNorm(m2); qreal diffNorm = transformNorm(QTransform(m1.m11() - m2.m11(), m1.m12() - m2.m12(), m1.m13() - m2.m13(), m1.m21() - m2.m21(), m1.m22() - m2.m22(), m1.m23() - m2.m23(), m1.m31() - m2.m31(), m1.m32() - m2.m32(), m1.m33() - m2.m33())); QVERIFY(diffIsSmallEnough(diffNorm, qMin(norm1, norm2))); } void tst_QSvgRenderer::transformForElement() const { QByteArray data("" "" "" "" "" "" "" "" "" "" ""); QImage image(13, 37, QImage::Format_RGB32); QPainter painter(&image); QSvgRenderer renderer(data); compareTransforms(painter.worldTransform(), renderer.transformForElement(QLatin1String("ichi"))); painter.translate(-3, 1); compareTransforms(painter.worldTransform(), renderer.transformForElement(QLatin1String("ni"))); painter.rotate(45); compareTransforms(painter.worldTransform(), renderer.transformForElement(QLatin1String("san"))); painter.scale(4, 2); compareTransforms(painter.worldTransform(), renderer.transformForElement(QLatin1String("yon"))); painter.setWorldTransform(QTransform(1, 2, 3, 4, 5, 6), true); compareTransforms(painter.worldTransform(), renderer.transformForElement(QLatin1String("firkant"))); } void tst_QSvgRenderer::boundsOnElement() const { QByteArray data("" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""); qreal sqrt2 = qSqrt(2); QSvgRenderer renderer(data); QCOMPARE(renderer.boundsOnElement(QLatin1String("sjokade")), QRectF(-10 * sqrt2, -10 * sqrt2, 20 * sqrt2, 20 * sqrt2)); QCOMPARE(renderer.boundsOnElement(QLatin1String("kaviar")), QRectF(-10 * sqrt2, -10 * sqrt2, 20 * sqrt2, 20 * sqrt2)); QCOMPARE(renderer.boundsOnElement(QLatin1String("nugatti")), QRectF(-11, -11, 22, 22)); QCOMPARE(renderer.boundsOnElement(QLatin1String("nutella")), QRectF(-10 * sqrt2, -10 * sqrt2, 20 * sqrt2, 20 * sqrt2)); QCOMPARE(renderer.boundsOnElement(QLatin1String("baconost")), QRectF(-10 * sqrt2, -10 * sqrt2, 20 * sqrt2, 20 * sqrt2)); QCOMPARE(renderer.boundsOnElement(QLatin1String("hapaa")), QRectF(-13, -9, 22, 22)); QCOMPARE(renderer.boundsOnElement(QLatin1String("prim")), QRectF(-10 * sqrt2 - 3, -10 * sqrt2 + 1, 20 * sqrt2, 20 * sqrt2)); } void tst_QSvgRenderer::gradientStops() const { { QByteArray data("" "" "" "" "" "" ""); QSvgRenderer renderer(data); QImage image(64, 64, QImage::Format_ARGB32_Premultiplied), refImage(64, 64, QImage::Format_ARGB32_Premultiplied); image.fill(0x87654321); refImage.fill(0x87654321); QPainter painter(&image); renderer.render(&painter); QCOMPARE(image, refImage); } { QByteArray data("" "" "" "" "" "" "" ""); QSvgRenderer renderer(data); QImage image(64, 64, QImage::Format_ARGB32_Premultiplied), refImage(64, 64, QImage::Format_ARGB32_Premultiplied); refImage.fill(0xff00ffff); QPainter painter(&image); renderer.render(&painter); QCOMPARE(image, refImage); } { QByteArray data("" "" "" "" "" "" "" "" "" "" "" "" "" ""); QSvgRenderer renderer(data); QImage image(64, 64, QImage::Format_ARGB32_Premultiplied), refImage(64, 64, QImage::Format_ARGB32_Premultiplied); QPainter painter; painter.begin(&refImage); painter.fillRect(QRectF(0, 0, 32, 64), Qt::cyan); painter.fillRect(QRectF(32, 0, 32, 64), Qt::yellow); painter.end(); painter.begin(&image); renderer.render(&painter); painter.end(); QCOMPARE(image, refImage); } } void tst_QSvgRenderer::gradientRefs() { const char *svgs[] = { "" "" "" "" "" "" "" "" "", "" "" "" "" "" "" "" "" "" "" "", "" "" "" "" "" "" "" "" "" "" "", "" "" "" "" "" "" "" "" "" "" "", "" "" "" "" "" "" "" "" "" }; for (size_t i = 0 ; i < sizeof(svgs) / sizeof(svgs[0]) ; ++i) { QByteArray data = svgs[i]; QSvgRenderer renderer(data); QImage image(256, 8, QImage::Format_ARGB32_Premultiplied); image.fill(0); QPainter painter(&image); renderer.render(&painter); const QRgb *line = reinterpret_cast(image.scanLine(3)); QRgb left = line[0]; // transparent black QRgb mid = line[127]; // semi transparent magenta QRgb right = line[255]; // opaque blue QVERIFY((qAlpha(left) < 3) && (qRed(left) < 3) && (qGreen(left) == 0) && (qBlue(left) < 3)); QVERIFY((qAbs(qAlpha(mid) - 127) < 3) && (qAbs(qRed(mid) - 63) < 4) && (qGreen(mid) == 0) && (qAbs(qBlue(mid) - 63) < 4)); QVERIFY((qAlpha(right) > 253) && (qRed(right) < 3) && (qGreen(right) == 0) && (qBlue(right) > 251)); } } void tst_QSvgRenderer::recursiveRefs_data() { QTest::addColumn("svg"); QTest::newRow("single") << QByteArray("" "" "" ""); QTest::newRow("double") << QByteArray("" "" "" "" ""); QTest::newRow("triple") << QByteArray("" "" "" "" "" ""); } void tst_QSvgRenderer::recursiveRefs() { QFETCH(QByteArray, svg); QImage image(20, 20, QImage::Format_ARGB32_Premultiplied); image.fill(Qt::green); QImage refImage = image.copy(); QSvgRenderer renderer(svg); QPainter painter(&image); renderer.render(&painter); QCOMPARE(image, refImage); } #ifndef QT_NO_COMPRESS void tst_QSvgRenderer::testGzLoading() { QSvgRenderer renderer(QLatin1String(SRCDIR "heart.svgz")); QVERIFY(renderer.isValid()); QSvgRenderer resourceRenderer(QLatin1String(":/heart.svgz")); QVERIFY(resourceRenderer.isValid()); QFile largeFileGz(SRCDIR "large.svgz"); largeFileGz.open(QIODevice::ReadOnly); QByteArray data = largeFileGz.readAll(); QSvgRenderer autoDetectGzData(data); QVERIFY(autoDetectGzData.isValid()); } #ifdef QT_BUILD_INTERNAL QT_BEGIN_NAMESPACE QByteArray qt_inflateGZipDataFrom(QIODevice *device); QT_END_NAMESPACE #endif void tst_QSvgRenderer::testGzHelper_data() { QTest::addColumn("in"); QTest::addColumn("out"); QTest::newRow("empty") << QByteArray() << QByteArray(); QTest::newRow("small") << QByteArray::fromHex(QByteArray("1f8b08005819934800034b" "cbcfe70200a865327e04000000")) << QByteArray("foo\n"); QFile largeFileGz("large.svgz"); largeFileGz.open(QIODevice::ReadOnly); QFile largeFile("large.svg"); largeFile.open(QIODevice::ReadOnly); QTest::newRow("large") << largeFileGz.readAll() << largeFile.readAll(); QTest::newRow("zeroes") << QByteArray::fromHex(QByteArray("1f8b0800131f9348000333" "301805a360148c54c00500d266708601040000")) << QByteArray(1024, '0').append('\n'); QTest::newRow("twoMembers") << QByteArray::fromHex(QByteArray("1f8b08001c2a934800034b" "cbcfe70200a865327e040000001f8b08001c2a934800034b4a2ce20200e9b3a20404000000")) << QByteArray("foo\nbar\n"); QTest::newRow("corruptedSecondMember") << QByteArray::fromHex(QByteArray("1f8b08001c2a934800034b" "cbcfe70200a865327e040000001f8c08001c2a934800034b4a2ce20200e9b3a20404000000")) << QByteArray(); } void tst_QSvgRenderer::testGzHelper() { #ifdef QT_BUILD_INTERNAL QFETCH(QByteArray, in); QFETCH(QByteArray, out); QBuffer buffer(&in); buffer.open(QIODevice::ReadOnly); QVERIFY(buffer.isReadable()); QByteArray result = qt_inflateGZipDataFrom(&buffer); QCOMPARE(result, out); #endif } #endif void tst_QSvgRenderer::fillRule() { static const char *svgs[] = { // Paths // Default fill-rule (nonzero) "" " " " " "", // nonzero "" " " " " "", // evenodd "" " " " " "", // Polygons // Default fill-rule (nonzero) "" " " " " "", // nonzero "" " " " " "", // evenodd "" " " " " "" }; const int COUNT = sizeof(svgs) / sizeof(svgs[0]); QImage refImageNonZero(30, 30, QImage::Format_ARGB32_Premultiplied); QImage refImageEvenOdd(30, 30, QImage::Format_ARGB32_Premultiplied); refImageNonZero.fill(0xffff0000); refImageEvenOdd.fill(0xffff0000); QPainter p; p.begin(&refImageNonZero); p.fillRect(QRectF(0, 0, 10, 10), Qt::blue); p.end(); p.begin(&refImageEvenOdd); p.fillRect(QRectF(0, 0, 10, 10), Qt::blue); p.fillRect(QRectF(10, 10, 10, 10), Qt::blue); p.end(); for (int i = 0; i < COUNT; ++i) { QByteArray data(svgs[i]); QSvgRenderer renderer(data); QImage image(30, 30, QImage::Format_ARGB32_Premultiplied); image.fill(0); p.begin(&image); renderer.render(&p); p.end(); QCOMPARE(image, i % 3 == 2 ? refImageEvenOdd : refImageNonZero); } } static void opacity_drawSvgAndVerify(const QByteArray &data) { QSvgRenderer renderer(data); QVERIFY(renderer.isValid()); QImage image(10, 10, QImage::Format_ARGB32_Premultiplied); image.fill(0xffff00ff); QPainter painter(&image); renderer.render(&painter); painter.end(); QCOMPARE(image.pixel(5, 5), 0xff7f7f7f); } void tst_QSvgRenderer::opacity() { static const char *opacities[] = {"-1.4641", "0", "0.5", "1", "1.337"}; static const char *firstColors[] = {"#7f7f7f", "#7f7f7f", "#402051", "blue", "#123456"}; static const char *secondColors[] = {"red", "#bad", "#bedead", "#7f7f7f", "#7f7f7f"}; // Fill-opacity for (int i = 0; i < 5; ++i) { QByteArray data(""); opacity_drawSvgAndVerify(data); } // Stroke-opacity for (int i = 0; i < 5; ++i) { QByteArray data(""); opacity_drawSvgAndVerify(data); } // As gradients: // Fill-opacity for (int i = 0; i < 5; ++i) { QByteArray data(""); opacity_drawSvgAndVerify(data); } // Stroke-opacity for (int i = 0; i < 5; ++i) { QByteArray data(""); opacity_drawSvgAndVerify(data); } } void tst_QSvgRenderer::paths() { static const char *svgs[] = { // Absolute coordinates, explicit commands. "" " " " " "", // Absolute coordinates, implicit commands. "" " " " " "", // Relative coordinates, explicit commands. "" " " " " "", // Relative coordinates, implicit commands. "" " " " " "", // Absolute coordinates, explicit commands, minimal whitespace. "" " " " " "", // Absolute coordinates, explicit commands, extra whitespace. "" " " " " "" }; const int COUNT = sizeof(svgs) / sizeof(svgs[0]); QImage images[COUNT]; QPainter p; for (int i = 0; i < COUNT; ++i) { QByteArray data(svgs[i]); QSvgRenderer renderer(data); QVERIFY(renderer.isValid()); images[i] = QImage(50, 50, QImage::Format_ARGB32_Premultiplied); images[i].fill(0); p.begin(&images[i]); renderer.render(&p); p.end(); if (i != 0) { QCOMPARE(images[i], images[0]); } } } void tst_QSvgRenderer::displayMode() { static const char *svgs[] = { // All visible. "" " " " " " " " " "", // Don't display svg element. "" " " " " " " " " "", // Don't display g element. "" " " " " " " " " "", // Don't display first rect element. "" " " " " " " " " "", // Don't display second rect element. "" " " " " " " " " "", // Don't display svg element, but set display mode to "inline" for other elements. "" " " " " " " " " "" }; QRgb expectedColors[] = {0xff0000ff, 0xff00ff00, 0xff00ff00, 0xff0000ff, 0xffff0000, 0xff00ff00}; const int COUNT = sizeof(svgs) / sizeof(svgs[0]); QPainter p; for (int i = 0; i < COUNT; ++i) { QByteArray data(svgs[i]); QSvgRenderer renderer(data); QVERIFY(renderer.isValid()); QImage image(10, 10, QImage::Format_ARGB32_Premultiplied); image.fill(0xff00ff00); p.begin(&image); renderer.render(&p); p.end(); QCOMPARE(image.pixel(5, 5), expectedColors[i]); } } void tst_QSvgRenderer::strokeInherit() { static const char *svgs[] = { // Reference. "" " " " " " " " " " " " " "", // stroke "" " " " " " " " " " " " " "", // stroke-width "" " " " " " " " " " " " " "", // stroke-linecap "" " " " " " " " " " " " " "", // stroke-linejoin "" " " " " " " " " " " " " "", // stroke-miterlimit "" " " " " " " " " " " " " "", // stroke-dasharray "" " " " " " " " " " " " " "", // stroke-dashoffset "" " " " " " " " " " " " " "", // stroke-opacity "" " " " " " " " " " " " " "" }; const int COUNT = sizeof(svgs) / sizeof(svgs[0]); QImage images[COUNT]; QPainter p; for (int i = 0; i < COUNT; ++i) { QByteArray data(svgs[i]); QSvgRenderer renderer(data); QVERIFY(renderer.isValid()); images[i] = QImage(200, 30, QImage::Format_ARGB32_Premultiplied); images[i].fill(-1); p.begin(&images[i]); renderer.render(&p); p.end(); if (i != 0) { QCOMPARE(images[0], images[i]); } } } void tst_QSvgRenderer::testFillInheritance() { static const char *svgs[] = { //reference "" " " "", "" " " " " "", "" " " " " " " " " "", "" " " " " " " " " " " " " "", "" " " " " " " " " " " " " " " " " " " " " " " " " "", "" " " " " " " " " " " " " "" }; const int COUNT = sizeof(svgs) / sizeof(svgs[0]); QImage images[COUNT]; QPainter p; for (int i = 0; i < COUNT; ++i) { QByteArray data(svgs[i]); QSvgRenderer renderer(data); QVERIFY(renderer.isValid()); images[i] = QImage(200, 200, QImage::Format_ARGB32_Premultiplied); images[i].fill(-1); p.begin(&images[i]); renderer.render(&p); p.end(); if (i != 0) { QCOMPARE(images[0], images[i]); } } } void tst_QSvgRenderer::testStopOffsetOpacity() { static const char *svgs[] = { //reference "" "" "" "" "" "" "" "" "" "" "" "" "" "", //Stop Offset "" "" "" "" "" "" "" "" "" "" "" "" "" "", //Stop Opacity "" "" "" "" "" "" "" "" "" "" "" "" "" "", //Stop offset and Stop opacity "" "" "" "" "" "" "" "" "" "" "" "" "" "" }; QImage images[4]; QPainter p; for (int i = 0; i < 4; ++i) { QByteArray data(svgs[i]); QSvgRenderer renderer(data); QVERIFY(renderer.isValid()); images[i] = QImage(64, 64, QImage::Format_ARGB32_Premultiplied); images[i].fill(-1); p.begin(&images[i]); renderer.render(&p); p.end(); } QCOMPARE(images[0], images[1]); QCOMPARE(images[0], images[2]); QCOMPARE(images[0], images[3]); } void tst_QSvgRenderer::testUseElement() { static const char *svgs[] = { // 0 - Use referring to non group node (1) "" " " " " "", // 1 "" " " " " "", // 2 "" " " " " "" "" "", // 3 "" " " " " " " " " "", // 4 - Use referring to non group node (2) "" " " " " "", // 5 "" " " " " " " " " " " " " "", // 6 "" " " " " " " " " " " " " "", // 7 - Use referring to group node "" " " " " " " " " " " "", // 8 "" " " " " " " " " " " " " " " " " "", // 9 "" " " " " " " " " " " " " " " " " " " " " " " " " "", // 10 - Self referral, should be ignored "", // 11 "" " " "", // 12 "" " " "", // 13 "" " " "", // 14 (undefined) "" " " " " "", // 15 - Forward references "" " " " " "", // 16 "" " " " " " " " " " " " " " " " " "", // 17 - Indirect self referral "" " " " " " " " " " " " " " " "" }; const int COUNT = sizeof(svgs) / sizeof(svgs[0]); QImage images[COUNT]; QPainter p; for (int i = 0; i < COUNT; ++i) { QByteArray data(svgs[i]); QSvgRenderer renderer(data); images[i] = QImage(200, 200, QImage::Format_ARGB32_Premultiplied); images[i].fill(-1); p.begin(&images[i]); renderer.render(&p); p.end(); if (i < 4 && i != 0) { QCOMPARE(images[0], images[i]); } else if (i > 4 && i < 7) { if (sizeof(qreal) != sizeof(float)) { // These images use blending functions which due to numerical // issues on Windows CE and likes differ in very few pixels. // For this reason an exact comparison will fail. QCOMPARE(images[4], images[i]); } } else if (i > 7 && i < 10) { QCOMPARE(images[8], images[i]); } else if (i > 11 && i < 15) { QCOMPARE(images[11], images[i]); } else if (i == 15) { QCOMPARE(images[0], images[i]); } else if (i == 16) { QCOMPARE(images[8], images[i]); } } } void tst_QSvgRenderer::smallFont() { static const char *svgs[] = { "Hello world", "Hello world" }; const int COUNT = sizeof(svgs) / sizeof(svgs[0]); QImage images[COUNT]; QPainter p; for (int i = 0; i < COUNT; ++i) { QByteArray data(svgs[i]); if (i == 0) QTest::ignoreMessage(QtWarningMsg, "QFont::setPointSizeF: Point size <= 0 (0.000000), must be greater than 0"); QSvgRenderer renderer(data); images[i] = QImage(50, 50, QImage::Format_ARGB32_Premultiplied); images[i].fill(-1); p.begin(&images[i]); renderer.render(&p); p.end(); } QVERIFY(images[0] != images[1]); } void tst_QSvgRenderer::styleSheet() { static const char *svgs[] = { "", "", }; const int COUNT = sizeof(svgs) / sizeof(svgs[0]); QImage images[COUNT]; QPainter p; for (int i = 0; i < COUNT; ++i) { QByteArray data(svgs[i]); QSvgRenderer renderer(data); images[i] = QImage(50, 50, QImage::Format_ARGB32_Premultiplied); images[i].fill(-1); p.begin(&images[i]); renderer.render(&p); p.end(); } QCOMPARE(images[0], images[1]); } void tst_QSvgRenderer::duplicateStyleId() { QByteArray svg = QByteArrayLiteral("" "" ""); QTest::ignoreMessage(QtWarningMsg, "Duplicate unique style id: \"a\""); QImage image(200, 200, QImage::Format_RGB32); QPainter painter(&image); QSvgRenderer renderer(svg); renderer.render(&painter); } void tst_QSvgRenderer::oss_fuzz_23731() { // when configured with "-sanitize undefined", this resulted in: // "runtime error: division by zero" QSvgRenderer().load(QByteArray("")); } QTEST_MAIN(tst_QSvgRenderer) #include "tst_qsvgrenderer.moc"