Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package qt6-svg for openSUSE:Factory checked in at 2025-11-25 15:48:22 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/qt6-svg (Old) and /work/SRC/openSUSE:Factory/.qt6-svg.new.14147 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "qt6-svg" Tue Nov 25 15:48:22 2025 rev:42 rq:1319506 version:6.10.1 Changes: -------- --- /work/SRC/openSUSE:Factory/qt6-svg/qt6-svg.changes 2025-10-13 17:27:11.462931072 +0200 +++ /work/SRC/openSUSE:Factory/.qt6-svg.new.14147/qt6-svg.changes 2025-11-25 15:49:34.438201857 +0100 @@ -1,0 +2,6 @@ +Thu Nov 20 15:15:05 UTC 2025 - Christophe Marin <[email protected]> + +- Update to 6.10.1 + https://www.qt.io/blog/qt-6.10.1-released + +------------------------------------------------------------------- Old: ---- qtsvg-everywhere-src-6.10.0.tar.xz New: ---- qtsvg-everywhere-src-6.10.1.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ qt6-svg.spec ++++++ --- /var/tmp/diff_new_pack.tvfNCn/_old 2025-11-25 15:49:35.294238025 +0100 +++ /var/tmp/diff_new_pack.tvfNCn/_new 2025-11-25 15:49:35.294238025 +0100 @@ -16,7 +16,7 @@ # -%define real_version 6.10.0 +%define real_version 6.10.1 %define short_version 6.10 %define tar_name qtsvg-everywhere-src %define tar_suffix %{nil} @@ -27,7 +27,7 @@ %endif # Name: qt6-svg%{?pkg_suffix} -Version: 6.10.0 +Version: 6.10.1 Release: 0 Summary: Classes for rendering and displaying SVG drawings License: LGPL-3.0-only OR (GPL-2.0-only OR GPL-3.0-or-later) ++++++ qtsvg-everywhere-src-6.10.0.tar.xz -> qtsvg-everywhere-src-6.10.1.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtsvg-everywhere-src-6.10.0/.cmake.conf new/qtsvg-everywhere-src-6.10.1/.cmake.conf --- old/qtsvg-everywhere-src-6.10.0/.cmake.conf 2025-10-01 20:34:24.000000000 +0200 +++ new/qtsvg-everywhere-src-6.10.1/.cmake.conf 2025-11-13 17:35:39.000000000 +0100 @@ -1,4 +1,4 @@ -set(QT_REPO_MODULE_VERSION "6.10.0") +set(QT_REPO_MODULE_VERSION "6.10.1") set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "alpha1") set(QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_NO_AS_CONST=1") list(APPEND QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_NO_FOREACH=1") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtsvg-everywhere-src-6.10.0/.tag new/qtsvg-everywhere-src-6.10.1/.tag --- old/qtsvg-everywhere-src-6.10.0/.tag 2025-10-01 20:34:24.000000000 +0200 +++ new/qtsvg-everywhere-src-6.10.1/.tag 2025-11-13 17:35:39.000000000 +0100 @@ -1 +1 @@ -ad1cee1d8cf7ab05dc779306668ef16fa600faed +4b1a183d3592f497bc3fd3a2c7130f228175b899 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtsvg-everywhere-src-6.10.0/dependencies.yaml new/qtsvg-everywhere-src-6.10.1/dependencies.yaml --- old/qtsvg-everywhere-src-6.10.0/dependencies.yaml 2025-10-01 20:34:24.000000000 +0200 +++ new/qtsvg-everywhere-src-6.10.1/dependencies.yaml 2025-11-13 17:35:39.000000000 +0100 @@ -1,4 +1,4 @@ dependencies: ../qtbase: - ref: 5a8637e4516bc48a0b3f4b5ec3b18618b92e7222 + ref: 90b845d15ffb97693dba527385db83510ebd121a required: true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtsvg-everywhere-src-6.10.0/src/svg/css/qsvgcsshandler.cpp new/qtsvg-everywhere-src-6.10.1/src/svg/css/qsvgcsshandler.cpp --- old/qtsvg-everywhere-src-6.10.0/src/svg/css/qsvgcsshandler.cpp 2025-10-01 20:34:24.000000000 +0200 +++ new/qtsvg-everywhere-src-6.10.1/src/svg/css/qsvgcsshandler.cpp 2025-11-13 17:35:39.000000000 +0100 @@ -327,12 +327,20 @@ const int valCount = decl.d->values.size(); for (int i = 0; i < valCount; ++i) { QCss::Value val = decl.d->values.at(i); - if (val.type == QCss::Value::TermOperatorComma) { + switch (val.type) { + case QCss::Value::TermOperatorComma: valueStr += QLatin1Char(';'); - } else if (val.type == QCss::Value::Uri) { - valueStr.prepend(QLatin1String("url(")); - valueStr.append(QLatin1Char(')')); - } else if (val.type == QCss::Value::Function) { + break; + case QCss::Value::Uri: + { + QString temp = val.toString(); + temp.prepend(QLatin1String("url(")); + temp.append(QLatin1Char(')')); + valueStr += temp; + break; + } + case QCss::Value::Function: + { QStringList lst = val.variant.toStringList(); valueStr.append(lst.at(0)); valueStr.append(QLatin1Char('(')); @@ -342,16 +350,22 @@ valueStr.append(QLatin1Char(',')); } valueStr.append(QLatin1Char(')')); - } else if (val.type == QCss::Value::KnownIdentifier) { + break; + } + case QCss::Value::KnownIdentifier: switch (val.variant.toInt()) { case QCss::Value_None: - valueStr = QLatin1String("none"); + valueStr += QLatin1String("none"); break; default: + valueStr += val.toString(); break; } - } else + break; + default: valueStr += val.toString(); + break; + } if (i + 1 < valCount) valueStr += QLatin1Char(' '); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtsvg-everywhere-src-6.10.0/src/svg/css/qsvgcsshandler_p.h new/qtsvg-everywhere-src-6.10.1/src/svg/css/qsvgcsshandler_p.h --- old/qtsvg-everywhere-src-6.10.0/src/svg/css/qsvgcsshandler_p.h 2025-10-01 20:34:24.000000000 +0200 +++ new/qtsvg-everywhere-src-6.10.1/src/svg/css/qsvgcsshandler_p.h 2025-11-13 17:35:39.000000000 +0100 @@ -25,7 +25,7 @@ class QSvgStyleSelector; class QSvgNode; -class QSvgCssHandler { +class Q_SVG_EXPORT QSvgCssHandler { public: QSvgCssHandler(); ~QSvgCssHandler(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtsvg-everywhere-src-6.10.0/src/svg/doc/src/svgrendering.qdoc new/qtsvg-everywhere-src-6.10.1/src/svg/doc/src/svgrendering.qdoc --- old/qtsvg-everywhere-src-6.10.0/src/svg/doc/src/svgrendering.qdoc 2025-10-01 20:34:24.000000000 +0200 +++ new/qtsvg-everywhere-src-6.10.1/src/svg/doc/src/svgrendering.qdoc 2025-11-13 17:35:39.000000000 +0100 @@ -6,54 +6,116 @@ \title Rendering SVG Files \brief Rendering SVG files with the Qt SVG module - Qt SVG provides classes for rendering SVG files. To include the definitions - of the module's classes, use the following directive: + SVG drawings can be rendered onto any \l QPaintDevice subclass, such as + \l QWidget or \l QImage. The easiest way to render SVG files is to use + \l QSvgWidget or \l QSvgRenderer. + + \section1 Using QSvgWidget + + \l QSvgWidget provides a convenient widget for displaying SVG files. + + \code + #include <QApplication> + #include <QSvgWidget> + + int main(int argc, char *argv[]) + { + QApplication app(argc, argv); + QSvgWidget svgWidget(QStringLiteral(":/images/example.svg")); + svgWidget.setGeometry(100, 100, 400, 400); + svgWidget.show(); + return app.exec(); + } + \endcode + + You can also load an SVG file after construction: + + \code + #include <QApplication> + #include <QSvgWidget> + + int main(int argc, char *argv[]) + { + QApplication app(argc, argv); + QSvgWidget *svgWidget = new QSvgWidget; + svgWidget->load(QStringLiteral(":/images/example.svg")); + svgWidget->show(); + return app.exec(); + } + \endcode + + \section1 Using QSvgRenderer + + Use \l QSvgRenderer to render SVG content onto any \l QPaintDevice. + + \code + #include <QApplication> + #include <QSvgRenderer> + #include <QImage> + #include <QPainter> + #include <QLabel> + #include <QPixmap> + + int main(int argc, char *argv[]) + { + QApplication app(argc, argv); + QSvgRenderer renderer(QStringLiteral(":/images/example.svg")); + if (renderer.isValid()) { + QImage image(400, 400, QImage::Format_ARGB32_Premultiplied); + image.fill(Qt::transparent); + QPainter painter(&image); + renderer.render(&painter); + + QLabel label; + label.setPixmap(QPixmap::fromImage(image)); + label.resize(400, 400); + label.show(); + return app.exec(); + } + return 1; + } + \endcode + + You can also use \l QSvgRenderer to render SVG directly in a custom widget's + paint event: + + \code + // In your widget's header file: + #include <QSvgRenderer> + + class MyWidget : public QWidget + { + Q_OBJECT + public: + MyWidget(QWidget *parent = nullptr) + : QWidget(parent), renderer(QStringLiteral(":/images/example.svg")) {} + + protected: + void paintEvent(QPaintEvent *) override { + QPainter painter(this); + renderer.render(&painter); + } + + private: + QSvgRenderer renderer; + }; + \endcode + + These approaches allow you to render SVG files on all paint devices + supported by Qt, including \l QWidget and \l QImage. + + + \section2 Rendering options + + \l QSvgRenderer provides rendering \l {QSvgRenderer::}{options} via the + \l {QtSvg::Option} enum. These options let you control how SVG files are + parsed and rendered. + + \section2 Rendering animated SVG files + + The Qt SVG module supports rendering animated SVG files. Refer to the + \l QSvgRenderer class documentation for details on how to work with animated + SVGs. - \snippet doc_src_qtsvg.cpp 0 - - To link against the module, add this line to your \l qmake \c - .pro file: - - \snippet doc_src_qtsvg.pro 1 - - \section1 Rendering SVG Files - - Scalable Vector Graphics (SVG) is a language for describing two-dimensional - graphics and graphical applications in XML. SVG 1.1 is a W3C Recommendation - and forms the core of the current SVG developments in Qt. SVG 1.2 is the - specification currently being developed by the \l{SVG Working Group}, and it - is \l{http://www.w3.org/TR/SVG12/}{available in draft form}. The \l{Mobile - SVG Profiles} (SVG Basic and SVG Tiny) are aimed at resource-limited devices - and are part of the 3GPP platform for third generation mobile phones. You - can read more about SVG at \l{About SVG}. - - Qt supports the \l{SVG 1.2 Tiny Static Features}{static features} as well as - color and transform animations of \l{http://www.w3.org/TR/SVGMobile12}{SVG 1.2 Tiny}. - ECMA scripts and DOM manipulation are currently not supported. - - Since Qt 6.7, some \l{Extended Features}{additional features} of the - \l{https://www.w3.org/TR/SVG11/}{SVG 1.1} standard are supported. - - Since Qt 6.9, CSS animations for fill, stroke and transform were added to - match the currently supported SMIL animations by the module. - - SVG drawings can be rendered onto any QPaintDevice subclass. This - approach gives developers the flexibility to experiment, in order - to find the best solution for each application. - - The easiest way to render SVG files is to construct a QSvgWidget and - load an SVG file using one of the QSvgWidget::load() functions. - - QSvgRenderer is the class responsible for rendering SVG files for - QSvgWidget, and it can be used directly to provide SVG support for - custom widgets. - To load an SVG file, construct a QSvgRenderer with a file name or the - contents of a file, or call QSvgRenderer::load() on an existing - renderer. If the SVG file has been loaded successfully the - QSvgRenderer::isValid() will return true. - - Once you have loaded the SVG file successfully, you can render it - with the QSvgRenderer::render() function. Note that this scheme allows - you to render SVG files on all paint devices supported by Qt, including - QWidget, QGLWidget, and QImage. + \sa {Vector Image Formats in Qt}, {Extended Features} */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtsvg-everywhere-src-6.10.0/src/svg/qsvgfilter.cpp new/qtsvg-everywhere-src-6.10.1/src/svg/qsvgfilter.cpp --- old/qtsvg-everywhere-src-6.10.0/src/svg/qsvgfilter.cpp 2025-10-01 20:34:24.000000000 +0200 +++ new/qtsvg-everywhere-src-6.10.1/src/svg/qsvgfilter.cpp 2025-11-13 17:35:39.000000000 +0100 @@ -107,7 +107,7 @@ m_matrix.fill(0); m_matrix.data()[0+0*5] = 0.213f + 0.787f * s; - m_matrix.data()[1+0*5] = 0.715f - 0.717f * s; + m_matrix.data()[1+0*5] = 0.715f - 0.715f * s; m_matrix.data()[2+0*5] = 0.072f - 0.072f * s; m_matrix.data()[0+1*5] = 0.213f - 0.213f * s; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtsvg-everywhere-src-6.10.0/src/svg/qsvgfont.cpp new/qtsvg-everywhere-src-6.10.1/src/svg/qsvgfont.cpp --- old/qtsvg-everywhere-src-6.10.0/src/svg/qsvgfont.cpp 2025-10-01 20:34:24.000000000 +0200 +++ new/qtsvg-everywhere-src-6.10.1/src/svg/qsvgfont.cpp 2025-11-13 17:35:39.000000000 +0100 @@ -35,6 +35,16 @@ (horizAdvX==-1)?m_horizAdvX:horizAdvX)); } +bool QSvgFont::addMissingGlyph(const QPainterPath &path, qreal horizAdvX) +{ + if (m_missingGlyph) { + qWarning("The font already has a 'missing-glyph' element."); + return false; + } + m_missingGlyph.reset(new QSvgGlyph(QChar(), path, (horizAdvX == -1) ? m_horizAdvX : horizAdvX)); + return true; +} + void QSvgFont::draw(QPainter *p, const QPointF &point, const QString &str, qreal pixelSize, Qt::Alignment alignment) const @@ -65,9 +75,9 @@ for ( ; itr != str.constEnd(); ++itr) { QChar unicode = *itr; if (!m_glyphs.contains(*itr)) { - unicode = u'\0'; - if (!m_glyphs.contains(unicode)) - continue; + if (m_missingGlyph) + textWidth += static_cast<int>(m_missingGlyph->m_horizAdvX); + continue; } textWidth += static_cast<int>(m_glyphs[unicode].m_horizAdvX); } @@ -91,26 +101,29 @@ itr = str.constBegin(); for ( ; itr != str.constEnd(); ++itr) { + QSvgGlyph foundGlyph; QChar unicode = *itr; - if (!m_glyphs.contains(*itr)) { - unicode = u'\0'; - if (!m_glyphs.contains(unicode)) + if (m_glyphs.contains(*itr)) { + foundGlyph = m_glyphs[unicode]; + } else { + if (!m_missingGlyph) continue; + foundGlyph = *m_missingGlyph; } if (isPainting) - p->drawPath(m_glyphs[unicode].m_path); + p->drawPath(foundGlyph.m_path); if (boundingRect) { QPainterPathStroker stroker; stroker.setWidth(penWidth); stroker.setJoinStyle(p->pen().joinStyle()); stroker.setMiterLimit(p->pen().miterLimit()); - QPainterPath stroke = stroker.createStroke(m_glyphs[unicode].m_path); + QPainterPath stroke = stroker.createStroke(foundGlyph.m_path); *boundingRect |= p->transform().map(stroke).boundingRect(); } - p->translate(m_glyphs[unicode].m_horizAdvX, 0); + p->translate(foundGlyph.m_horizAdvX, 0); } p->restore(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtsvg-everywhere-src-6.10.0/src/svg/qsvgfont_p.h new/qtsvg-everywhere-src-6.10.1/src/svg/qsvgfont_p.h --- old/qtsvg-everywhere-src-6.10.0/src/svg/qsvgfont_p.h 2025-10-01 20:34:24.000000000 +0200 +++ new/qtsvg-everywhere-src-6.10.1/src/svg/qsvgfont_p.h 2025-11-13 17:35:39.000000000 +0100 @@ -21,6 +21,8 @@ #include "qsvgstyle_p.h" #include "qtsvgglobal_p.h" +#include <memory> + QT_BEGIN_NAMESPACE class Q_SVG_EXPORT QSvgGlyph @@ -47,6 +49,7 @@ void setUnitsPerEm(qreal upem); void addGlyph(QChar unicode, const QPainterPath &path, qreal horizAdvX = -1); + bool addMissingGlyph(const QPainterPath &path, qreal horizAdvX); void draw(QPainter *p, const QPointF &point, const QString &str, qreal pixelSize, Qt::Alignment alignment) const; @@ -57,6 +60,8 @@ QString m_familyName; qreal m_unitsPerEm = DEFAULT_UNITS_PER_EM; qreal m_horizAdvX; + // not about a missing <glyph> element, but the font's <missing-glyph> element: + std::unique_ptr<const QSvgGlyph> m_missingGlyph; QHash<QChar, QSvgGlyph> m_glyphs; private: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtsvg-everywhere-src-6.10.0/src/svg/qsvggraphics.cpp new/qtsvg-everywhere-src-6.10.1/src/svg/qsvggraphics.cpp --- old/qtsvg-everywhere-src-6.10.0/src/svg/qsvggraphics.cpp 2025-10-01 20:34:24.000000000 +0200 +++ new/qtsvg-everywhere-src-6.10.1/src/svg/qsvggraphics.cpp 2025-11-13 17:35:39.000000000 +0100 @@ -123,7 +123,8 @@ p->drawPoint(m_path.boundingRect().topLeft()); else p->drawPath(m_path); - QSvgMarker::drawMarkersForNode(this, p, states); + if (!path().isEmpty()) + QSvgMarker::drawMarkersForNode(this, p, states); } bool QSvgPath::separateFillStroke() const diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtsvg-everywhere-src-6.10.0/src/svg/qsvghandler.cpp new/qtsvg-everywhere-src-6.10.1/src/svg/qsvghandler.cpp --- old/qtsvg-everywhere-src-6.10.0/src/svg/qsvghandler.cpp 2025-10-01 20:34:24.000000000 +0200 +++ new/qtsvg-everywhere-src-6.10.1/src/svg/qsvghandler.cpp 2025-11-13 17:35:39.000000000 +0100 @@ -538,20 +538,29 @@ return num; } -static bool createSvgGlyph(QSvgFont *font, const QXmlStreamAttributes &attributes) +static bool createSvgGlyph(QSvgFont *font, const QXmlStreamAttributes &attributes, + bool isMissingGlyph) { QStringView uncStr = attributes.value(QLatin1String("unicode")); QStringView havStr = attributes.value(QLatin1String("horiz-adv-x")); QStringView pathStr = attributes.value(QLatin1String("d")); - QChar unicode = (uncStr.isEmpty()) ? u'\0' : uncStr.at(0); qreal havx = (havStr.isEmpty()) ? -1 : QSvgUtils::toDouble(havStr); QPainterPath path; path.setFillRule(Qt::WindingFill); parsePathDataFast(pathStr, path); - font->addGlyph(unicode, path, havx); + if (isMissingGlyph) { + if (!uncStr.isEmpty()) + qWarning("Ignoring missing-glyph's 'unicode' attribute"); + return font->addMissingGlyph(path, havx); + } + if (uncStr.isEmpty()) { + qWarning("glyph does not define a non-empty 'unicode' attribute and will be ignored"); + return false; + } + font->addGlyph(uncStr.at(0), path, havx); return true; } @@ -2272,8 +2281,7 @@ QSvgFontStyle *style = static_cast<QSvgFontStyle*>(parent); QSvgFont *font = style->svgFont(); - createSvgGlyph(font, attributes); - return true; + return createSvgGlyph(font, attributes, false); } static bool parseHandlerNode(QSvgNode *parent, @@ -2505,8 +2513,7 @@ QSvgFontStyle *style = static_cast<QSvgFontStyle*>(parent); QSvgFont *font = style->svgFont(); - createSvgGlyph(font, attributes); - return true; + return createSvgGlyph(font, attributes, true); } static bool parseMpathNode(QSvgNode *parent, @@ -3413,6 +3420,9 @@ attrs.setAttributes(styleCssAttributes, handler); #endif + //TODO: Handle style parsing for gradients stop like the rest of the nodes. + parseColor(&dummy, attrs, handler); + QSvgGradientStyle *gradientStyle = static_cast<QSvgGradientStyle*>(parent); QStringView colorStr = attrs.stopColor; @@ -4223,79 +4233,74 @@ } else if (FactoryMethod method = findGraphicsFactory(localName, options())) { //rendering element Q_ASSERT(!m_nodes.isEmpty()); - node = method(m_nodes.top(), attributes, this); - if (node) { - switch (m_nodes.top()->type()) { - case QSvgNode::Doc: - case QSvgNode::Group: - case QSvgNode::Defs: - case QSvgNode::Switch: - case QSvgNode::Mask: - case QSvgNode::Symbol: - case QSvgNode::Marker: - case QSvgNode::Pattern: - { - if (node->type() == QSvgNode::Tspan) { - const QByteArray msg = QByteArrayLiteral("\'tspan\' element in wrong context."); - qCWarning(lcSvgHandler, "%s", prefixMessage(msg, xml).constData()); - delete node; - node = 0; - break; - } + switch (m_nodes.top()->type()) { + case QSvgNode::Doc: + case QSvgNode::Group: + case QSvgNode::Defs: + case QSvgNode::Switch: + case QSvgNode::Mask: + case QSvgNode::Symbol: + case QSvgNode::Marker: + case QSvgNode::Pattern: + { + if (localName == QLatin1String("tspan")) { + const QByteArray msg = QByteArrayLiteral("\'tspan\' element in wrong context."); + qCWarning(lcSvgHandler, "%s", prefixMessage(msg, xml).constData()); + break; + } + node = method(m_nodes.top(), attributes, this); + if (node) { QSvgStructureNode *group = static_cast<QSvgStructureNode*>(m_nodes.top()); group->addChild(node, someId(attributes)); } - break; - case QSvgNode::Text: - case QSvgNode::Textarea: - if (node->type() == QSvgNode::Tspan) { + } + break; + case QSvgNode::Text: + case QSvgNode::Textarea: + if (localName == QLatin1String("tspan")) { + node = method(m_nodes.top(), attributes, this); + if (node) { static_cast<QSvgText *>(m_nodes.top())->addTspan(static_cast<QSvgTspan *>(node)); - } else { - const QByteArray msg = QByteArrayLiteral("\'text\' or \'textArea\' element contains invalid element type."); - qCWarning(lcSvgHandler, "%s", prefixMessage(msg, xml).constData()); - delete node; - node = 0; } - break; - default: - const QByteArray msg = QByteArrayLiteral("Could not add child element to parent element because the types are incorrect."); + } else { + const QByteArray msg = QByteArrayLiteral("\'text\' or \'textArea\' element contains invalid element type."); qCWarning(lcSvgHandler, "%s", prefixMessage(msg, xml).constData()); - delete node; - node = 0; - break; } + break; + default: + const QByteArray msg = QByteArrayLiteral("Could not add child element to parent element because the types are incorrect."); + qCWarning(lcSvgHandler, "%s", prefixMessage(msg, xml).constData()); + break; + } - if (node) { - parseCoreNode(node, attributes); - parseStyle(node, attributes, this); - if (node->type() == QSvgNode::Text || node->type() == QSvgNode::Textarea) { - static_cast<QSvgText *>(node)->setWhitespaceMode(m_whitespaceMode.top()); - } else if (node->type() == QSvgNode::Tspan) { - static_cast<QSvgTspan *>(node)->setWhitespaceMode(m_whitespaceMode.top()); - } else if (node->type() == QSvgNode::Use) { - auto useNode = static_cast<QSvgUse *>(node); - if (!useNode->isResolved()) - m_toBeResolved.append(useNode); - } + if (node) { + parseCoreNode(node, attributes); + parseStyle(node, attributes, this); + if (node->type() == QSvgNode::Text || node->type() == QSvgNode::Textarea) { + static_cast<QSvgText *>(node)->setWhitespaceMode(m_whitespaceMode.top()); + } else if (node->type() == QSvgNode::Tspan) { + static_cast<QSvgTspan *>(node)->setWhitespaceMode(m_whitespaceMode.top()); + } else if (node->type() == QSvgNode::Use) { + auto useNode = static_cast<QSvgUse *>(node); + if (!useNode->isResolved()) + m_toBeResolved.append(useNode); } } } else if (FactoryMethod method = findFilterFactory(localName, options())) { //filter nodes to be aded to be filtercontainer Q_ASSERT(!m_nodes.isEmpty()); - node = method(m_nodes.top(), attributes, this); - if (node) { - if (m_nodes.top()->type() == QSvgNode::Filter || - (m_nodes.top()->type() == QSvgNode::FeMerge && node->type() == QSvgNode::FeMergenode)) { + if (m_nodes.top()->type() == QSvgNode::Filter || + (m_nodes.top()->type() == QSvgNode::FeMerge && localName == QLatin1String("feMergeNode"))) { + node = method(m_nodes.top(), attributes, this); + if (node) { QSvgStructureNode *container = static_cast<QSvgStructureNode*>(m_nodes.top()); container->addChild(node, someId(attributes)); - } else { - const QByteArray msg = QByteArrayLiteral("Could not add child element to parent element because the types are incorrect."); - qCWarning(lcSvgHandler, "%s", prefixMessage(msg, xml).constData()); - delete node; - node = 0; } + } else { + const QByteArray msg = QByteArrayLiteral("Could not add child element to parent element because the types are incorrect."); + qCWarning(lcSvgHandler, "%s", prefixMessage(msg, xml).constData()); } } else if (AnimationMethod method = findAnimationFactory(localName, options())) { Q_ASSERT(!m_nodes.isEmpty()); @@ -4449,8 +4454,13 @@ } else if (node->type() == QSvgNode::AnimateTransform || node->type() == QSvgNode::AnimateColor) { QSvgAnimateNode *anim = static_cast<QSvgAnimateNode *>(node); QSvgNode *targetNode = m_doc->namedNode(anim->linkId()); - if (targetNode) + if (targetNode) { m_doc->animator()->appendAnimation(targetNode, anim); + } else { + qCWarning(lcSvgHandler, "Cannot find target for link #%s!", + qPrintable(anim->linkId())); + delete anim; + } } } m_toBeResolved.clear(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtsvg-everywhere-src-6.10.0/src/svg/qsvgstructure.cpp new/qtsvg-everywhere-src-6.10.1/src/svg/qsvgstructure.cpp --- old/qtsvg-everywhere-src-6.10.0/src/svg/qsvgstructure.cpp 2025-10-01 20:34:24.000000000 +0200 +++ new/qtsvg-everywhere-src-6.10.1/src/svg/qsvgstructure.cpp 2025-11-13 17:35:39.000000000 +0100 @@ -860,6 +860,8 @@ QSize imageSize; imageSize.setWidth(qCeil(patternBoundingBox.width() * t.m11() * m_transform.m11())); imageSize.setHeight(qCeil(patternBoundingBox.height() * t.m22() * m_transform.m22())); + if (imageSize.isEmpty()) + return QImage(); // Avoid division by zero in calculateAppliedTransform() calculateAppliedTransform(t, peBoundingBox, imageSize); return renderPattern(imageSize, contentScaleFactorX, contentScaleFactorY); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtsvg-everywhere-src-6.10.0/src/svg/qsvgtinydocument.cpp new/qtsvg-everywhere-src-6.10.1/src/svg/qsvgtinydocument.cpp --- old/qtsvg-everywhere-src-6.10.0/src/svg/qsvgtinydocument.cpp 2025-10-01 20:34:24.000000000 +0200 +++ new/qtsvg-everywhere-src-6.10.1/src/svg/qsvgtinydocument.cpp 2025-11-13 17:35:39.000000000 +0100 @@ -147,7 +147,9 @@ if (doCheckContent) { // Quick format check, equivalent to QSvgIOHandler::canRead() - if (!hasSvgHeader(destination)) { + const qsizetype destinationContents = std::min(destination.size(), static_cast<qsizetype>(zlibStream.total_out)); + Q_ASSERT(destinationContents == static_cast<qsizetype>(zlibStream.total_out)); + if (!hasSvgHeader(QByteArray::fromRawData(destination.constData(), destinationContents))) { inflateEnd(&zlibStream); qCWarning(lcSvgHandler, "Error while inflating gzip file: SVG format check failed"); return QByteArray(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtsvg-everywhere-src-6.10.0/tests/auto/CMakeLists.txt new/qtsvg-everywhere-src-6.10.1/tests/auto/CMakeLists.txt --- old/qtsvg-everywhere-src-6.10.0/tests/auto/CMakeLists.txt 2025-10-01 20:34:24.000000000 +0200 +++ new/qtsvg-everywhere-src-6.10.1/tests/auto/CMakeLists.txt 2025-11-13 17:35:39.000000000 +0100 @@ -22,3 +22,6 @@ if(NOT ANDROID) add_subdirectory(qsvgrenderer) endif() +if (QT_FEATURE_cssparser) + add_subdirectory(qsvgcss) +endif() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtsvg-everywhere-src-6.10.0/tests/auto/qsvgcss/CMakeLists.txt new/qtsvg-everywhere-src-6.10.1/tests/auto/qsvgcss/CMakeLists.txt --- old/qtsvg-everywhere-src-6.10.0/tests/auto/qsvgcss/CMakeLists.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/qtsvg-everywhere-src-6.10.1/tests/auto/qsvgcss/CMakeLists.txt 2025-11-13 17:35:39.000000000 +0100 @@ -0,0 +1,21 @@ +# Copyright (C) 2025 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qsvgcss Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qsvgcss LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qsvgcss + SOURCES + tst_qsvgcss.cpp + LIBRARIES + Qt::Gui + Qt::GuiPrivate + Qt::SvgPrivate +) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtsvg-everywhere-src-6.10.0/tests/auto/qsvgcss/tst_qsvgcss.cpp new/qtsvg-everywhere-src-6.10.1/tests/auto/qsvgcss/tst_qsvgcss.cpp --- old/qtsvg-everywhere-src-6.10.0/tests/auto/qsvgcss/tst_qsvgcss.cpp 1970-01-01 01:00:00.000000000 +0100 +++ new/qtsvg-everywhere-src-6.10.1/tests/auto/qsvgcss/tst_qsvgcss.cpp 2025-11-13 17:35:39.000000000 +0100 @@ -0,0 +1,77 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + + +#include <QtTest/QTest> + +#include <QtSvg/private/qsvgcsshandler_p.h> +#include <QtCore/QXmlStreamAttributes> +#include <QtGui/private/qcssparser_p.h> + +using namespace Qt::Literals::StringLiterals; + +class tst_QSvgCss : public QObject +{ +Q_OBJECT + +public: + tst_QSvgCss(); + virtual ~tst_QSvgCss(); + +private slots: + void testParsedDeclarations_data(); + void testParsedDeclarations(); +}; + +tst_QSvgCss::tst_QSvgCss() +{ +} + +tst_QSvgCss::~tst_QSvgCss() +{ +} + +void tst_QSvgCss::testParsedDeclarations_data() +{ + QTest::addColumn<QString>("css"); + QTest::addColumn<QString>("expectedProperty"); + QTest::addColumn<QString>("expectedValue"); + + QTest::newRow("value") << u"fill: red;"_s << u"fill"_s << u"red"_s; + QTest::newRow("url") << u"fill: url(#id1);"_s << u"fill"_s << u"url(#id1)"_s; + QTest::newRow("function") << u"transform: translate(50px, 50px)"_s << u"transform"_s << u"translate(50px, 50px)"_s; + QTest::newRow("list of numbers") << u"stroke-dasharray: 2, 5, 10, 3;"_s << u"stroke-dasharray"_s << u"2 ; 5 ; 10 ; 3"_s; + QTest::newRow("known identifier none") << u"display: none"_s << u"display"_s << u"none"_s; + QTest::newRow("multiple values 1") << u"animation: transformAnim 2000ms infinite"_s << u"animation"_s << u"transformAnim 2000ms infinite"_s; + QTest::newRow("multiple urls") << u"fill: url(#id1) url(#id2)"_s << u"fill"_s << u"url(#id1) url(#id2)"_s; +} + +void tst_QSvgCss::testParsedDeclarations() +{ + QFETCH(QString, css); + QFETCH(QString, expectedProperty); + QFETCH(QString, expectedValue); + + css.prepend("dummy {"); + css.append("}"); + QCss::Parser parser(css); + + QCss::StyleSheet sheet; + parser.parse(&sheet); + + QCss::StyleRule rule = (!sheet.styleRules.isEmpty()) ? + sheet.styleRules.at(0) : *sheet.nameIndex.begin(); + QCOMPARE(rule.declarations.size(), 1); + + QList<QCss::Declaration> decls = rule.declarations; + QSvgCssHandler handler; + QXmlStreamAttributes attributes; + handler.parseCSStoXMLAttrs(decls, attributes); + + QCOMPARE(attributes.size(), 1); + QCOMPARE(attributes.first().name(), expectedProperty); + QCOMPARE(attributes.first().value(), expectedValue); +} + +QTEST_MAIN(tst_QSvgCss) +#include "tst_qsvgcss.moc" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtsvg-everywhere-src-6.10.0/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp new/qtsvg-everywhere-src-6.10.1/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp --- old/qtsvg-everywhere-src-6.10.0/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp 2025-10-01 20:34:24.000000000 +0200 +++ new/qtsvg-everywhere-src-6.10.1/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp 2025-11-13 17:35:39.000000000 +0100 @@ -58,12 +58,16 @@ void opacity(); void paths(); void paths2(); + void emptyPath_data(); + void emptyPath(); void displayMode(); void strokeInherit(); void testFillInheritance(); void testStopOffsetOpacity(); void testUseElement(); void smallFont(); + void glyphWarnings_data(); + void glyphWarnings(); void styleSheet(); void duplicateStyleId(); void ossFuzzRender_data(); @@ -870,6 +874,18 @@ <linearGradient id="2" xlink:href="#0"/> <rect x="0" y="0" width="20" height="20" fill="url(#0) "/> </svg>)"); + + QTest::newRow("pattern") << QByteArray(R"(<svg><pattern id="pattern" width="4" height="4" + fill="url(#pattern) "> + <rect width="2" height="2" fill=" "/></pattern> + <rect width="2" height="2" fill="url(#pattern) "/> + </svg>)"); + + // lead to division by zero in QSvgPattern::patternImage while loading document + QTest::newRow("pattern-no-elements") << QByteArray(R"(<svg> + <pattern id="pattern" width="4" height="4" + fill="url(#pattern) "/> + </svg>)"); } void tst_QSvgRenderer::recursiveRefs() @@ -1200,6 +1216,26 @@ QCOMPARE(renderer.boundsOnElement("path1"_L1).toRect(), QRect(3, 8, 10, 5)); } +void tst_QSvgRenderer::emptyPath_data() +{ + QTest::addColumn<QByteArray>("svg"); + + // This caused a division by zero in in QPainterPath::angleAtPercent, reported by UBSAN. + QTest::newRow("empty") + << (R"(<svg><path marker-end="url(#w) "/></svg>)"_ba); + QTest::newRow("zero-movements") + << (R"(<svg><path d="M1 2L1 2l0 0H1h0V2v0Z" marker-end="url(#w) "/></svg>)"_ba); +} + +void tst_QSvgRenderer::emptyPath() +{ + QFETCH(QByteArray, svg); + QImage image(377, 233, QImage::Format_RGB32); + QPainter painter(&image); + QSvgRenderer renderer(svg); + renderer.render(&painter); +} + void tst_QSvgRenderer::displayMode() { static const char *svgs[] = { @@ -1729,6 +1765,52 @@ QVERIFY(images[0] != images[1]); } +void tst_QSvgRenderer::glyphWarnings_data() +{ + QTest::addColumn<QString>("missingGlyphAttributes"); + QTest::addColumn<QString>("secondElement"); + QTest::addColumn<QByteArrayList>("warnings"); + + const QString glyph = R"(<glyph %1 d="M0 0L9 9z"/>)"; + QTest::newRow("glyph-without-unicode") + << "" << glyph.arg("") + << QByteArrayList{"glyph does not define a non-empty 'unicode' attribute and will be ignored", + "<input>:1:120: Problem parsing glyph"}; + QTest::newRow("glyph-empty-unicode") + << "" << glyph.arg(R"(unicode="")") + << QByteArrayList{"glyph does not define a non-empty 'unicode' attribute and will be ignored", + "<input>:1:130: Problem parsing glyph"}; + QTest::newRow("missing-glyph-with-unicode") + << R"(unicode="a")" << glyph.arg(R"(unicode="a")") + << QByteArrayList{"Ignoring missing-glyph's 'unicode' attribute"}; + QTest::newRow("double-missing-glyph") + << "" << R"(<missing-glyph d="M0 0L9 9z"/>)" + << QByteArrayList{"The font already has a 'missing-glyph' element.", + "<input>:1:127: Problem parsing missing-glyph"}; +} + +void tst_QSvgRenderer::glyphWarnings() +{ + // This test validates that questionable attributes in a font's glyph and missing-glyph elements + // will trigger suitable warnings while parsing of the entire document finishes sucessfully. + + const QString svgTemplate = R"(<svg><defs>)" + R"(<font xml:id="tstFnt"><font-face font-family="tstFnt"/>)" + R"(<missing-glyph %1 d="M0 0L9 9z"/>)" + R"(%2)" + R"(</font></defs>)" + R"(<text font-family="tstFnt">a</text></svg>)"; + + QFETCH(QString, missingGlyphAttributes); + QFETCH(QString, secondElement); + QFETCH(QByteArrayList, warnings); + + for (const auto &w: warnings) + QTest::ignoreMessage(QtWarningMsg, w); + QSvgRenderer renderer(svgTemplate.arg(missingGlyphAttributes).arg(secondElement).toUtf8()); + QVERIFY(renderer.isValid()); +} + void tst_QSvgRenderer::styleSheet() { static const char *svgs[] = { R"(<svg><style type="text/css">.cls {fill:#ff0000;}</style><rect class="cls" x = "10" y = "10" width = "30" height = "30"/></svg>)", @@ -1813,6 +1895,9 @@ // resulted in stack overflow QTest::newRow("cyclic-reference-from-parent") // id=390467765 << R"-(<svg stroke="url(#c)"><pattern height="2" width="4" id="c"/><path stroke="#F00" d="v2"/></svg>)-"_ba; + // resulted in memory leak, reported when configured with "-sanitize address" + QTest::newRow("animation-without-target") // id=456050169 + << R"-(<svg><animateTransform type="rotate" from=" " to=" " href="#X">)-"_ba; } void tst_QSvgRenderer::ossFuzzLoad() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qtsvg-everywhere-src-6.10.0/tests/baseline/data/bugs/missing-glyph.svg new/qtsvg-everywhere-src-6.10.1/tests/baseline/data/bugs/missing-glyph.svg --- old/qtsvg-everywhere-src-6.10.0/tests/baseline/data/bugs/missing-glyph.svg 1970-01-01 01:00:00.000000000 +0100 +++ new/qtsvg-everywhere-src-6.10.1/tests/baseline/data/bugs/missing-glyph.svg 2025-11-13 17:35:39.000000000 +0100 @@ -0,0 +1,93 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="1000" height="390" font-size="25"> + <!-- SVG allows defining glyph elements with an empty unicode attribute and missing-glyph --> + <!-- elements with a non-empty unicode attribute. This file is meant to test for the --> + <!-- confusion that may cause. --> + <!-- If glyph handling works at all, the table will display arrows either facing up or --> + <!-- down. An arrow facing up stands for a passed test, an arrow facing down for a fail. --> + <defs> + <font xml:id="missing-glyph-with-unicode-mgf"> + <font-face font-family="missing-glyph-with-unicode-mgf"/> + <missing-glyph unicode="a" d="M0 1000L500 0L1000 1000z"/> + <glyph unicode="a" d="M0 0L1000 0L500 1000z"/> + </font> + + <font xml:id="missing-glyph-with-unicode-mgl"> + <font-face font-family="missing-glyph-with-unicode-mgl"/> + <glyph unicode="a" d="M0 0L1000 0L500 1000z"/> + <missing-glyph unicode="a" d="M0 1000L500 0L1000 1000z"/> + </font> + + <font xml:id="glyph-without-unicode-mgf"> + <font-face font-family="glyph-without-unicode-mgf"/> + <missing-glyph d="M0 0L1000 0L500 1000z"/> + <glyph d="M0 1000L500 0L1000 1000z"/> + </font> + + <font xml:id="glyph-without-unicode-mgl"> + <font-face font-family="glyph-without-unicode-mgl"/> + <glyph d="M0 1000L500 0L1000 1000z"/> + <missing-glyph d="M0 0L1000 0L500 1000z"/> + </font> + + <font xml:id="glyph-empty-unicode-mgf"> + <font-face font-family="glyph-empty-unicode-mgf"/> + <missing-glyph d="M0 0L1000 0L500 1000z"/> + <glyph unicode="" d="M0 1000L500 0L1000 1000z"/> + </font> + + <font xml:id="glyph-empty-unicode-mgl"> + <font-face font-family="glyph-empty-unicode-mgl"/> + <glyph unicode="" d="M0 1000L500 0L1000 1000z"/> + <missing-glyph d="M0 0L1000 0L500 1000z"/> + </font> + + <font xml:id="multiple-missing-glyph-mgf"> + <font-face font-family="multiple-missing-glyph-mgf"/> + <missing-glyph d="M0 0L1000 0L500 1000z"/> + <missing-glyph d="M0 1000L500 0L1000 1000z"/> + <glyph unicode="a" d="M0 1000L500 0L1000 1000z"/> + </font> + + <font xml:id="multiple-missing-glyph-mgl"> + <font-face font-family="multiple-missing-glyph-mgl"/> + <glyph unicode="a" d="M0 1000L500 0L1000 1000z"/> + <missing-glyph d="M0 0L1000 0L500 1000z"/> + <missing-glyph d="M0 1000L500 0L1000 1000z"/> + </font> + + <font xml:id="no-missing-glyph"> + <font-face font-family="no-missing-glyph"/> + <glyph unicode="a" d="M0 1000L500 0L1000 1000z"/> + </font> + </defs> + + <rect x="0" y="0" width="1000" height="390" fill="white"/> + <text x="10" y="30" font-family="Arial">(All arrows should face up)</text> + <text x="450" y="30" font-family="Arial"><missing-glyph> first</text> + <text x="750" y="30" font-family="Arial"><missing-glyph> last</text> + + <text x="10" y="80" font-family="Arial"><missing-glyph> with unicode attribute:</text> + <!-- Should show the <glyph>, not the <missing glyph> with the same unicode --> + <text x="550" y="80" font-family="missing-glyph-with-unicode-mgf">a</text> + <text x="850" y="80" font-family="missing-glyph-with-unicode-mgl">a</text> + + <text x="10" y="130" font-family="Arial"><glyph> without unicode attribute:</text> + <!-- Should show the <missing-glyph>, not the <glyph> without unicode --> + <text x="550" y="130" font-family="glyph-without-unicode-mgf">a</text> + <text x="850" y="130" font-family="glyph-without-unicode-mgl">a</text> + + <text x="10" y="180" font-family="Arial"><glyph> with empty unicode attribute:</text> + <!-- Should show the <missing-glyph>, not the <glyph> with empty unicode --> + <text x="550" y="180" font-family="glyph-empty-unicode-mgf">a</text> + <text x="850" y="180" font-family="glyph-empty-unicode-mgl">a</text> + + <text x="10" y="230" font-family="Arial">multiple <missing-glyph> elements:</text> + <!-- Should show the first <missing-glyph> --> + <text x="550" y="230" font-family="multiple-missing-glyph-mgf">b</text> + <text x="850" y="230" font-family="multiple-missing-glyph-mgl">b</text> + + <text x="10" y="330" font-family="Arial">(No arrow must be drawn)</text> + <text x="10" y="380" font-family="Arial">no <missing-glyph> element:</text> + <!-- Should show nothing --> + <text x="550" y="380" font-family="no-missing-glyph">b</text> +</svg>
