summaryrefslogtreecommitdiff
path: root/src/svg/qsvghandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/svg/qsvghandler.cpp')
-rw-r--r--src/svg/qsvghandler.cpp581
1 files changed, 294 insertions, 287 deletions
diff --git a/src/svg/qsvghandler.cpp b/src/svg/qsvghandler.cpp
index 523efa6..a891848 100644
--- a/src/svg/qsvghandler.cpp
+++ b/src/svg/qsvghandler.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt SVG module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** 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 Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** 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-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qplatformdefs.h"
@@ -65,6 +29,7 @@
#include "private/qmath_p.h"
#include "float.h"
+#include <cmath>
QT_BEGIN_NAMESPACE
@@ -134,7 +99,7 @@ bool qsvg_get_hex_rgb(const char *name, QRgb *rgb)
if(name[0] != '#')
return false;
name++;
- int len = qstrlen(name);
+ const size_t len = qstrlen(name);
int r, g, b;
bool ok = true;
if (len == 12) {
@@ -221,6 +186,7 @@ struct QSvgAttributes
QStringView offset;
QStringView stopColor;
QStringView stopOpacity;
+ QStringView imageRendering;
#ifndef QT_NO_CSSPARSER
QList<QSvgCssAttribute> m_cssAttributes;
@@ -229,109 +195,7 @@ struct QSvgAttributes
QSvgAttributes::QSvgAttributes(const QXmlStreamAttributes &xmlAttributes, QSvgHandler *handler)
{
-#ifndef QT_NO_CSSPARSER
- QStringView style = xmlAttributes.value(QLatin1String("style"));
- if (!style.isEmpty()) {
- handler->parseCSStoXMLAttrs(style.toString(), &m_cssAttributes);
- for (int j = 0; j < m_cssAttributes.count(); ++j) {
- const QSvgCssAttribute &attribute = m_cssAttributes.at(j);
- QStringView name = attribute.name;
- QStringView value = attribute.value;
- if (name.isEmpty())
- continue;
-
- switch (name.at(0).unicode()) {
-
- case 'c':
- if (name == QLatin1String("color"))
- color = value;
- else if (name == QLatin1String("color-opacity"))
- colorOpacity = value;
- else if (name == QLatin1String("comp-op"))
- compOp = value;
- break;
-
- case 'd':
- if (name == QLatin1String("display"))
- display = value;
- break;
-
- case 'f':
- if (name == QLatin1String("fill"))
- fill = value;
- else if (name == QLatin1String("fill-rule"))
- fillRule = value;
- else if (name == QLatin1String("fill-opacity"))
- fillOpacity = value;
- else if (name == QLatin1String("font-family"))
- fontFamily = value;
- else if (name == QLatin1String("font-size"))
- fontSize = value;
- else if (name == QLatin1String("font-style"))
- fontStyle = value;
- else if (name == QLatin1String("font-weight"))
- fontWeight = value;
- else if (name == QLatin1String("font-variant"))
- fontVariant = value;
- break;
-
- case 'o':
- if (name == QLatin1String("opacity"))
- opacity = value;
- else if (name == QLatin1String("offset"))
- offset = value;
- break;
-
- case 's':
- if (name.length() > 5 && name.mid(1, 5) == QLatin1String("troke")) {
- QStringView strokeRef = name.mid(6, name.length() - 6);
- if (strokeRef.isEmpty())
- stroke = value;
- else if (strokeRef == QLatin1String("-dasharray"))
- strokeDashArray = value;
- else if (strokeRef == QLatin1String("-dashoffset"))
- strokeDashOffset = value;
- else if (strokeRef == QLatin1String("-linecap"))
- strokeLineCap = value;
- else if (strokeRef == QLatin1String("-linejoin"))
- strokeLineJoin = value;
- else if (strokeRef == QLatin1String("-miterlimit"))
- strokeMiterLimit = value;
- else if (strokeRef == QLatin1String("-opacity"))
- strokeOpacity = value;
- else if (strokeRef == QLatin1String("-width"))
- strokeWidth = value;
- }
- else if (name == QLatin1String("stop-color"))
- stopColor = value;
- else if (name == QLatin1String("stop-opacity"))
- stopOpacity = value;
- break;
-
- case 't':
- if (name == QLatin1String("text-anchor"))
- textAnchor = value;
- else if (name == QLatin1String("transform"))
- transform = value;
- break;
-
- case 'v':
- if (name == QLatin1String("vector-effect"))
- vectorEffect = value;
- else if (name == QLatin1String("visibility"))
- visibility = value;
- break;
-
- default:
- break;
- }
- }
- }
-#else
- Q_UNUSED(handler);
-#endif // QT_NO_CSSPARSER
-
- for (int i = 0; i < xmlAttributes.count(); ++i) {
+ for (int i = 0; i < xmlAttributes.size(); ++i) {
const QXmlStreamAttribute &attribute = xmlAttributes.at(i);
QStringView name = attribute.qualifiedName();
if (name.isEmpty())
@@ -376,6 +240,8 @@ QSvgAttributes::QSvgAttributes(const QXmlStreamAttributes &xmlAttributes, QSvgHa
case 'i':
if (name == QLatin1String("id"))
id = value.toString();
+ else if (name == QLatin1String("image-rendering"))
+ imageRendering = value;
break;
case 'o':
@@ -386,8 +252,8 @@ QSvgAttributes::QSvgAttributes(const QXmlStreamAttributes &xmlAttributes, QSvgHa
break;
case 's':
- if (name.length() > 5 && name.mid(1, 5) == QLatin1String("troke")) {
- QStringView strokeRef = name.mid(6, name.length() - 6);
+ if (name.size() > 5 && name.mid(1, 5) == QLatin1String("troke")) {
+ QStringView strokeRef = name.mid(6, name.size() - 6);
if (strokeRef.isEmpty())
stroke = value;
else if (strokeRef == QLatin1String("-dasharray"))
@@ -404,8 +270,7 @@ QSvgAttributes::QSvgAttributes(const QXmlStreamAttributes &xmlAttributes, QSvgHa
strokeOpacity = value;
else if (strokeRef == QLatin1String("-width"))
strokeWidth = value;
- }
- else if (name == QLatin1String("stop-color"))
+ } else if (name == QLatin1String("stop-color"))
stopColor = value;
else if (name == QLatin1String("stop-opacity"))
stopOpacity = value;
@@ -435,6 +300,112 @@ QSvgAttributes::QSvgAttributes(const QXmlStreamAttributes &xmlAttributes, QSvgHa
}
}
+ // If a style attribute is present, let its attribute settings override the plain attribute
+ // values. The spec seems to indicate that, and it is common behavior in svg renderers.
+#ifndef QT_NO_CSSPARSER
+ QStringView style = xmlAttributes.value(QLatin1String("style"));
+ if (!style.isEmpty()) {
+ handler->parseCSStoXMLAttrs(style.toString(), &m_cssAttributes);
+ for (int j = 0; j < m_cssAttributes.size(); ++j) {
+ const QSvgCssAttribute &attribute = m_cssAttributes.at(j);
+ QStringView name = attribute.name;
+ QStringView value = attribute.value;
+ if (name.isEmpty())
+ continue;
+
+ switch (name.at(0).unicode()) {
+
+ case 'c':
+ if (name == QLatin1String("color"))
+ color = value;
+ else if (name == QLatin1String("color-opacity"))
+ colorOpacity = value;
+ else if (name == QLatin1String("comp-op"))
+ compOp = value;
+ break;
+
+ case 'd':
+ if (name == QLatin1String("display"))
+ display = value;
+ break;
+
+ case 'f':
+ if (name == QLatin1String("fill"))
+ fill = value;
+ else if (name == QLatin1String("fill-rule"))
+ fillRule = value;
+ else if (name == QLatin1String("fill-opacity"))
+ fillOpacity = value;
+ else if (name == QLatin1String("font-family"))
+ fontFamily = value;
+ else if (name == QLatin1String("font-size"))
+ fontSize = value;
+ else if (name == QLatin1String("font-style"))
+ fontStyle = value;
+ else if (name == QLatin1String("font-weight"))
+ fontWeight = value;
+ else if (name == QLatin1String("font-variant"))
+ fontVariant = value;
+ break;
+ case 'i':
+ if (name == QLatin1String("image-rendering"))
+ imageRendering = value;
+ break;
+
+ case 'o':
+ if (name == QLatin1String("opacity"))
+ opacity = value;
+ else if (name == QLatin1String("offset"))
+ offset = value;
+ break;
+
+ case 's':
+ if (name.size() > 5 && name.mid(1, 5) == QLatin1String("troke")) {
+ QStringView strokeRef = name.mid(6, name.size() - 6);
+ if (strokeRef.isEmpty())
+ stroke = value;
+ else if (strokeRef == QLatin1String("-dasharray"))
+ strokeDashArray = value;
+ else if (strokeRef == QLatin1String("-dashoffset"))
+ strokeDashOffset = value;
+ else if (strokeRef == QLatin1String("-linecap"))
+ strokeLineCap = value;
+ else if (strokeRef == QLatin1String("-linejoin"))
+ strokeLineJoin = value;
+ else if (strokeRef == QLatin1String("-miterlimit"))
+ strokeMiterLimit = value;
+ else if (strokeRef == QLatin1String("-opacity"))
+ strokeOpacity = value;
+ else if (strokeRef == QLatin1String("-width"))
+ strokeWidth = value;
+ } else if (name == QLatin1String("stop-color"))
+ stopColor = value;
+ else if (name == QLatin1String("stop-opacity"))
+ stopOpacity = value;
+ break;
+
+ case 't':
+ if (name == QLatin1String("text-anchor"))
+ textAnchor = value;
+ else if (name == QLatin1String("transform"))
+ transform = value;
+ break;
+
+ case 'v':
+ if (name == QLatin1String("vector-effect"))
+ vectorEffect = value;
+ else if (name == QLatin1String("visibility"))
+ visibility = value;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+#else
+ Q_UNUSED(handler);
+#endif // QT_NO_CSSPARSER
}
#ifndef QT_NO_CSSPARSER
@@ -508,8 +479,9 @@ public:
QString name = nodeToName(n);
return QString::compare(name, nodeName, Qt::CaseInsensitive) == 0;
}
- QString attribute(NodePtr node, const QString &name) const override
+ QString attributeValue(NodePtr node, const QCss::AttributeSelector &asel) const override
{
+ const QString &name = asel.name;
QSvgNode *n = svgNode(node);
if ((!n->nodeId().isEmpty() && (name == QLatin1String("id") ||
name == QLatin1String("xml:id"))))
@@ -672,6 +644,9 @@ static qreal toDouble(const QChar *&str)
val = -val;
} else {
val = QByteArray::fromRawData(temp, pos).toDouble();
+ // Do not tolerate values too wild to be represented normally by floats
+ if (qFpClassify(float(val)) != FP_NORMAL)
+ val = 0;
}
return val;
@@ -682,7 +657,7 @@ static qreal toDouble(QStringView str, bool *ok = NULL)
const QChar *c = str.constData();
qreal res = (c == nullptr ? qreal{} : toDouble(c));
if (ok)
- *ok = (c == (str.constData() + str.length()));
+ *ok = (c == (str.constData() + str.size()));
return res;
}
@@ -714,15 +689,25 @@ static QList<qreal> parseNumbersList(const QChar *&str)
return points;
}
-static inline void parseNumbersArray(const QChar *&str, QVarLengthArray<qreal, 8> &points)
+static inline void parseNumbersArray(const QChar *&str, QVarLengthArray<qreal, 8> &points,
+ const char *pattern = nullptr)
{
+ const size_t patternLen = qstrlen(pattern);
while (str->isSpace())
++str;
while (isDigit(str->unicode()) ||
*str == QLatin1Char('-') || *str == QLatin1Char('+') ||
*str == QLatin1Char('.')) {
- points.append(toDouble(str));
+ if (patternLen && pattern[points.size() % patternLen] == 'f') {
+ // flag expected, may only be 0 or 1
+ if (*str != QLatin1Char('0') && *str != QLatin1Char('1'))
+ return;
+ points.append(*str == QLatin1Char('0') ? 0.0 : 1.0);
+ ++str;
+ } else {
+ points.append(toDouble(str));
+ }
while (str->isSpace())
++str;
@@ -813,7 +798,7 @@ static bool resolveColor(QStringView colorStr, QColor &color, QSvgHandler *handl
// #rrggbb is very very common, so let's tackle it here
// rather than falling back to QColor
QRgb rgb;
- bool ok = qsvg_get_hex_rgb(colorStrTr.constData(), colorStrTr.length(), &rgb);
+ bool ok = qsvg_get_hex_rgb(colorStrTr.constData(), colorStrTr.size(), &rgb);
if (ok)
color.setRgb(rgb);
return ok;
@@ -823,7 +808,7 @@ static bool resolveColor(QStringView colorStr, QColor &color, QSvgHandler *handl
case 'r':
{
// starts with "rgb(", ends with ")" and consists of at least 7 characters "rgb(,,)"
- if (colorStrTr.length() >= 7 && colorStrTr.at(colorStrTr.length() - 1) == QLatin1Char(')')
+ if (colorStrTr.size() >= 7 && colorStrTr.at(colorStrTr.size() - 1) == QLatin1Char(')')
&& colorStrTr.mid(0, 4) == QLatin1String("rgb(")) {
const QChar *s = colorStrTr.constData() + 4;
QList<qreal> compo = parseNumbersList(s);
@@ -861,7 +846,7 @@ static bool resolveColor(QStringView colorStr, QColor &color, QSvgHandler *handl
break;
}
- color = QColor(colorStrTr.toString());
+ color = QColor::fromString(colorStrTr.toString());
return color.isValid();
}
@@ -931,7 +916,7 @@ static bool createSvgGlyph(QSvgFont *font, const QXmlStreamAttributes &attribute
QStringView havStr = attributes.value(QLatin1String("horiz-adv-x"));
QStringView pathStr = attributes.value(QLatin1String("d"));
- QChar unicode = (uncStr.isEmpty()) ? 0 : uncStr.at(0);
+ QChar unicode = (uncStr.isEmpty()) ? u'\0' : uncStr.at(0);
qreal havx = (havStr.isEmpty()) ? -1 : toDouble(havStr);
QPainterPath path;
path.setFillRule(Qt::WindingFill);
@@ -1012,8 +997,8 @@ static void parseBrush(QSvgNode *node,
//fill attribute handling
if ((!attributes.fill.isEmpty()) && (attributes.fill != QT_INHERIT) ) {
- if (attributes.fill.length() > 3 && attributes.fill.mid(0, 3) == QLatin1String("url")) {
- QString value = attributes.fill.mid(3, attributes.fill.length() - 3).toString();
+ if (attributes.fill.size() > 3 && attributes.fill.mid(0, 3) == QLatin1String("url")) {
+ QString value = attributes.fill.mid(3, attributes.fill.size() - 3).toString();
QSvgStyleProperty *style = styleFromUrl(node, value);
if (style) {
if (style->type() == QSvgStyleProperty::SOLID_COLOR || style->type() == QSvgStyleProperty::GRADIENT)
@@ -1044,7 +1029,7 @@ static QTransform parseTransformationMatrix(QStringView value)
QTransform matrix;
const QChar *str = value.constData();
- const QChar *end = str + value.length();
+ const QChar *end = str + value.size();
while (str < end) {
if (str->isSpace() || *str == QLatin1Char(',')) {
@@ -1123,22 +1108,22 @@ static QTransform parseTransformationMatrix(QStringView value)
++str;
if(state == Matrix) {
- if(points.count() != 6)
+ if(points.size() != 6)
goto error;
matrix = QTransform(points[0], points[1],
points[2], points[3],
points[4], points[5]) * matrix;
} else if (state == Translate) {
- if (points.count() == 1)
+ if (points.size() == 1)
matrix.translate(points[0], 0);
- else if (points.count() == 2)
+ else if (points.size() == 2)
matrix.translate(points[0], points[1]);
else
goto error;
} else if (state == Rotate) {
- if(points.count() == 1) {
+ if(points.size() == 1) {
matrix.rotate(points[0]);
- } else if (points.count() == 3) {
+ } else if (points.size() == 3) {
matrix.translate(points[1], points[2]);
matrix.rotate(points[0]);
matrix.translate(-points[1], -points[2]);
@@ -1146,23 +1131,21 @@ static QTransform parseTransformationMatrix(QStringView value)
goto error;
}
} else if (state == Scale) {
- if (points.count() < 1 || points.count() > 2)
+ if (points.size() < 1 || points.size() > 2)
goto error;
qreal sx = points[0];
qreal sy = sx;
- if(points.count() == 2)
+ if(points.size() == 2)
sy = points[1];
matrix.scale(sx, sy);
} else if (state == SkewX) {
- if (points.count() != 1)
+ if (points.size() != 1)
goto error;
- const qreal deg2rad = qreal(0.017453292519943295769);
- matrix.shear(qTan(points[0]*deg2rad), 0);
+ matrix.shear(qTan(qDegreesToRadians(points[0])), 0);
} else if (state == SkewY) {
- if (points.count() != 1)
+ if (points.size() != 1)
goto error;
- const qreal deg2rad = qreal(0.017453292519943295769);
- matrix.shear(0, qTan(points[0]*deg2rad));
+ matrix.shear(0, qTan(qDegreesToRadians(points[0])));
}
}
error:
@@ -1183,8 +1166,8 @@ static void parsePen(QSvgNode *node,
//stroke attribute handling
if ((!attributes.stroke.isEmpty()) && (attributes.stroke != QT_INHERIT) ) {
- if (attributes.stroke.length() > 3 && attributes.stroke.mid(0, 3) == QLatin1String("url")) {
- QString value = attributes.stroke.mid(3, attributes.stroke.length() - 3).toString();
+ if (attributes.stroke.size() > 3 && attributes.stroke.mid(0, 3) == QLatin1String("url")) {
+ QString value = attributes.stroke.mid(3, attributes.stroke.size() - 3).toString();
QSvgStyleProperty *style = styleFromUrl(node, value);
if (style) {
if (style->type() == QSvgStyleProperty::SOLID_COLOR || style->type() == QSvgStyleProperty::GRADIENT)
@@ -1320,7 +1303,7 @@ static void parseFont(QSvgNode *node,
return;
QSvgTinyDocument *doc = node->document();
- QSvgFontStyle *fontStyle = 0;
+ QSvgFontStyle *fontStyle = nullptr;
if (!attributes.fontFamily.isEmpty()) {
QSvgFont *svgFont = doc->svgFont(attributes.fontFamily.toString());
if (svgFont)
@@ -1331,7 +1314,7 @@ static void parseFont(QSvgNode *node,
if (!attributes.fontFamily.isEmpty() && attributes.fontFamily != QT_INHERIT) {
QString family = attributes.fontFamily.toString().trimmed();
if (family.at(0) == QLatin1Char('\'') || family.at(0) == QLatin1Char('\"'))
- family = family.mid(1, family.length() - 2);
+ family = family.mid(1, family.size() - 2);
fontStyle->setFamily(family);
}
@@ -1342,8 +1325,10 @@ static void parseFont(QSvgNode *node,
case FontSizeNone:
break;
case FontSizeValue: {
- QSvgHandler::LengthType dummy; // should always be pixel size
- fontStyle->setSize(parseLength(attributes.fontSize, dummy, handler));
+ QSvgHandler::LengthType type;
+ qreal fs = parseLength(attributes.fontSize, type, handler);
+ fs = convertToPixels(fs, true, type);
+ fontStyle->setSize(qMin(fs, qreal(0xffff)));
}
break;
default:
@@ -1574,6 +1559,7 @@ static void pathArc(QPainterPath &path,
static bool parsePathDataFast(QStringView dataStr, QPainterPath &path)
{
+ const int maxElementCount = 0x7fff; // Assume file corruption if more path elements than this
qreal x0 = 0, y0 = 0; // starting point
qreal x = 0, y = 0; // current point
char lastMode = 0;
@@ -1581,28 +1567,31 @@ static bool parsePathDataFast(QStringView dataStr, QPainterPath &path)
const QChar *str = dataStr.constData();
const QChar *end = str + dataStr.size();
- while (str != end) {
+ bool ok = true;
+ while (ok && str != end) {
while (str->isSpace() && (str + 1) != end)
++str;
QChar pathElem = *str;
++str;
QChar endc = *end;
- *const_cast<QChar *>(end) = 0; // parseNumbersArray requires 0-termination that QStringView cannot guarantee
+ *const_cast<QChar *>(end) = u'\0'; // parseNumbersArray requires 0-termination that QStringView cannot guarantee
+ const char *pattern = nullptr;
+ if (pathElem == QLatin1Char('a') || pathElem == QLatin1Char('A'))
+ pattern = "rrrffrr";
QVarLengthArray<qreal, 8> arg;
- parseNumbersArray(str, arg);
+ parseNumbersArray(str, arg, pattern);
*const_cast<QChar *>(end) = endc;
if (pathElem == QLatin1Char('z') || pathElem == QLatin1Char('Z'))
arg.append(0);//dummy
const qreal *num = arg.constData();
- int count = arg.count();
- while (count > 0) {
+ int count = arg.size();
+ while (ok && count > 0) {
qreal offsetX = x; // correction offsets
qreal offsetY = y; // for relative commands
switch (pathElem.unicode()) {
case 'm': {
if (count < 2) {
- num++;
- count--;
+ ok = false;
break;
}
x = x0 = num[0] + offsetX;
@@ -1619,8 +1608,7 @@ static bool parsePathDataFast(QStringView dataStr, QPainterPath &path)
break;
case 'M': {
if (count < 2) {
- num++;
- count--;
+ ok = false;
break;
}
x = x0 = num[0];
@@ -1646,8 +1634,7 @@ static bool parsePathDataFast(QStringView dataStr, QPainterPath &path)
break;
case 'l': {
if (count < 2) {
- num++;
- count--;
+ ok = false;
break;
}
x = num[0] + offsetX;
@@ -1660,8 +1647,7 @@ static bool parsePathDataFast(QStringView dataStr, QPainterPath &path)
break;
case 'L': {
if (count < 2) {
- num++;
- count--;
+ ok = false;
break;
}
x = num[0];
@@ -1701,8 +1687,7 @@ static bool parsePathDataFast(QStringView dataStr, QPainterPath &path)
break;
case 'c': {
if (count < 6) {
- num += count;
- count = 0;
+ ok = false;
break;
}
QPointF c1(num[0] + offsetX, num[1] + offsetY);
@@ -1718,8 +1703,7 @@ static bool parsePathDataFast(QStringView dataStr, QPainterPath &path)
}
case 'C': {
if (count < 6) {
- num += count;
- count = 0;
+ ok = false;
break;
}
QPointF c1(num[0], num[1]);
@@ -1735,8 +1719,7 @@ static bool parsePathDataFast(QStringView dataStr, QPainterPath &path)
}
case 's': {
if (count < 4) {
- num += count;
- count = 0;
+ ok = false;
break;
}
QPointF c1;
@@ -1757,8 +1740,7 @@ static bool parsePathDataFast(QStringView dataStr, QPainterPath &path)
}
case 'S': {
if (count < 4) {
- num += count;
- count = 0;
+ ok = false;
break;
}
QPointF c1;
@@ -1779,8 +1761,7 @@ static bool parsePathDataFast(QStringView dataStr, QPainterPath &path)
}
case 'q': {
if (count < 4) {
- num += count;
- count = 0;
+ ok = false;
break;
}
QPointF c(num[0] + offsetX, num[1] + offsetY);
@@ -1795,8 +1776,7 @@ static bool parsePathDataFast(QStringView dataStr, QPainterPath &path)
}
case 'Q': {
if (count < 4) {
- num += count;
- count = 0;
+ ok = false;
break;
}
QPointF c(num[0], num[1]);
@@ -1811,8 +1791,7 @@ static bool parsePathDataFast(QStringView dataStr, QPainterPath &path)
}
case 't': {
if (count < 2) {
- num += count;
- count = 0;
+ ok = false;
break;
}
QPointF e(num[0] + offsetX, num[1] + offsetY);
@@ -1832,8 +1811,7 @@ static bool parsePathDataFast(QStringView dataStr, QPainterPath &path)
}
case 'T': {
if (count < 2) {
- num += count;
- count = 0;
+ ok = false;
break;
}
QPointF e(num[0], num[1]);
@@ -1853,8 +1831,7 @@ static bool parsePathDataFast(QStringView dataStr, QPainterPath &path)
}
case 'a': {
if (count < 7) {
- num += count;
- count = 0;
+ ok = false;
break;
}
qreal rx = (*num++);
@@ -1876,8 +1853,7 @@ static bool parsePathDataFast(QStringView dataStr, QPainterPath &path)
break;
case 'A': {
if (count < 7) {
- num += count;
- count = 0;
+ ok = false;
break;
}
qreal rx = (*num++);
@@ -1898,12 +1874,15 @@ static bool parsePathDataFast(QStringView dataStr, QPainterPath &path)
}
break;
default:
- return false;
+ ok = false;
+ break;
}
lastMode = pathElem.toLatin1();
+ if (path.elementCount() > maxElementCount)
+ ok = false;
}
}
- return true;
+ return ok;
}
static bool parseStyle(QSvgNode *node,
@@ -1919,13 +1898,13 @@ static bool parseStyle(QSvgNode *node,
static void parseCSStoXMLAttrs(const QList<QCss::Declaration> &declarations,
QXmlStreamAttributes &attributes)
{
- for (int i = 0; i < declarations.count(); ++i) {
+ for (int i = 0; i < declarations.size(); ++i) {
const QCss::Declaration &decl = declarations.at(i);
if (decl.d->property.isEmpty())
continue;
QCss::Value val = decl.d->values.first();
QString valueStr;
- const int valCount = decl.d->values.count();
+ const int valCount = decl.d->values.size();
if (valCount != 1) {
for (int i = 0; i < valCount; ++i) {
valueStr += decl.d->values[i].toString();
@@ -1942,9 +1921,9 @@ static void parseCSStoXMLAttrs(const QList<QCss::Declaration> &declarations,
QStringList lst = val.variant.toStringList();
valueStr.append(lst.at(0));
valueStr.append(QLatin1Char('('));
- for (int i = 1; i < lst.count(); ++i) {
+ for (int i = 1; i < lst.size(); ++i) {
valueStr.append(lst.at(i));
- if ((i +1) < lst.count())
+ if ((i +1) < lst.size())
valueStr.append(QLatin1Char(','));
}
valueStr.append(QLatin1Char(')'));
@@ -2065,7 +2044,7 @@ static bool parseCoreNode(QSvgNode *node,
QStringList fonts;
QString xmlClassStr;
- for (int i = 0; i < attributes.count(); ++i) {
+ for (int i = 0; i < attributes.size(); ++i) {
const QXmlStreamAttribute &attribute = attributes.at(i);
QStringView name = attribute.qualifiedName();
if (name.isEmpty())
@@ -2251,6 +2230,25 @@ static void parseOthers(QSvgNode *node,
}
}
+static void parseRenderingHints(QSvgNode *node,
+ const QSvgAttributes &attributes,
+ QSvgHandler *)
+{
+ if (attributes.imageRendering.isEmpty())
+ return;
+
+ QString ir = attributes.imageRendering.toString().trimmed();
+ QSvgQualityStyle *p = new QSvgQualityStyle(0);
+ if (ir == QLatin1String("auto"))
+ p->setImageRendering(QSvgQualityStyle::ImageRenderingAuto);
+ else if (ir == QLatin1String("optimizeSpeed"))
+ p->setImageRendering(QSvgQualityStyle::ImageRenderingOptimizeSpeed);
+ else if (ir == QLatin1String("optimizeQuality"))
+ p->setImageRendering(QSvgQualityStyle::ImageRenderingOptimizeQuality);
+ node->appendStyleProperty(p, attributes.id);
+}
+
+
static bool parseStyle(QSvgNode *node,
const QSvgAttributes &attributes,
QSvgHandler *handler)
@@ -2263,7 +2261,9 @@ static bool parseStyle(QSvgNode *node,
parseVisibility(node, attributes, handler);
parseOpacity(node, attributes, handler);
parseCompOp(node, attributes, handler);
+ parseRenderingHints(node, attributes, handler);
parseOthers(node, attributes, handler);
+
#if 0
value = attributes.value("audio-level");
@@ -2317,6 +2317,27 @@ static bool parseAnimateNode(QSvgNode *parent,
return true;
}
+static int parseClockValue(QStringView str, bool *ok)
+{
+ int res = 0;
+ int ms = 1000;
+ str = str.trimmed();
+ if (str.endsWith(QLatin1String("ms"))) {
+ str.chop(2);
+ ms = 1;
+ } else if (str.endsWith(QLatin1String("s"))) {
+ str.chop(1);
+ }
+ double val = ms * toDouble(str, ok);
+ if (ok) {
+ if (val > std::numeric_limits<int>::min() && val < std::numeric_limits<int>::max())
+ res = static_cast<int>(val);
+ else
+ *ok = false;
+ }
+ return res;
+}
+
static bool parseAnimateColorNode(QSvgNode *parent,
const QXmlStreamAttributes &attributes,
QSvgHandler *handler)
@@ -2340,7 +2361,7 @@ static bool parseAnimateColorNode(QSvgNode *parent,
colors.append(endColor);
} else {
QStringList str = valuesStr.split(QLatin1Char(';'));
- colors.reserve(str.count());
+ colors.reserve(str.size());
QStringList::const_iterator itr;
for (itr = str.constBegin(); itr != str.constEnd(); ++itr) {
QColor color;
@@ -2349,23 +2370,13 @@ static bool parseAnimateColorNode(QSvgNode *parent,
}
}
- int ms = 1000;
- beginStr = beginStr.trimmed();
- if (beginStr.endsWith(QLatin1String("ms"))) {
- beginStr.chop(2);
- ms = 1;
- } else if (beginStr.endsWith(QLatin1String("s"))) {
- beginStr.chop(1);
- }
- durStr = durStr.trimmed();
- if (durStr.endsWith(QLatin1String("ms"))) {
- durStr.chop(2);
- ms = 1;
- } else if (durStr.endsWith(QLatin1String("s"))) {
- durStr.chop(1);
- }
- int begin = static_cast<int>(toDouble(beginStr) * ms);
- int end = static_cast<int>((toDouble(durStr) + begin) * ms);
+ bool ok = true;
+ int begin = parseClockValue(beginStr, &ok);
+ if (!ok)
+ return false;
+ int end = begin + parseClockValue(durStr, &ok);
+ if (!ok || end <= begin)
+ return false;
QSvgAnimateColor *anim = new QSvgAnimateColor(begin, end, 0);
anim->setArgs((targetStr == QLatin1String("fill")), colors);
@@ -2454,25 +2465,16 @@ static bool parseAnimateTransformNode(QSvgNode *parent,
++s;
}
}
+ if (vals.size() % 3 != 0)
+ return false;
- int ms = 1000;
- beginStr = beginStr.trimmed();
- if (beginStr.endsWith(QLatin1String("ms"))) {
- beginStr.chop(2);
- ms = 1;
- } else if (beginStr.endsWith(QLatin1String("s"))) {
- beginStr.chop(1);
- }
- int begin = static_cast<int>(toDouble(beginStr) * ms);
- durStr = durStr.trimmed();
- if (durStr.endsWith(QLatin1String("ms"))) {
- durStr.chop(2);
- ms = 1;
- } else if (durStr.endsWith(QLatin1String("s"))) {
- durStr.chop(1);
- ms = 1000;
- }
- int end = static_cast<int>(toDouble(durStr)*ms) + begin;
+ bool ok = true;
+ int begin = parseClockValue(beginStr, &ok);
+ if (!ok)
+ return false;
+ int end = begin + parseClockValue(durStr, &ok);
+ if (!ok || end <= begin)
+ return false;
QSvgAnimateTransform::TransformType type = QSvgAnimateTransform::Empty;
if (typeStr == QLatin1String("translate")) {
@@ -2528,6 +2530,8 @@ static QSvgNode *createCircleNode(QSvgNode *parent,
qreal ncx = toDouble(cx);
qreal ncy = toDouble(cy);
qreal nr = toDouble(r);
+ if (nr < 0.0)
+ return nullptr;
QRectF rect(ncx-nr, ncy-nr, nr*2, nr*2);
QSvgNode *circle = new QSvgCircle(parent, rect);
@@ -2618,7 +2622,7 @@ static bool parseFontFaceNode(QSvgStyleProperty *parent,
qreal unitsPerEm = toDouble(unitsPerEmStr);
if (!unitsPerEm)
- unitsPerEm = 1000;
+ unitsPerEm = QSvgFont::DEFAULT_UNITS_PER_EM;
if (!name.isEmpty())
font->setFamilyName(name);
@@ -2823,7 +2827,7 @@ static void parseBaseGradient(QSvgNode *node,
QTransform matrix;
QGradient *grad = gradProp->qgradient();
- if (!link.isEmpty()) {
+ if (node && !link.isEmpty()) {
QSvgStyleProperty *prop = node->styleProperty(link);
//qDebug()<<"inherited "<<prop<<" ("<<link<<")";
if (prop && prop->type() == QSvgStyleProperty::GRADIENT) {
@@ -2938,8 +2942,8 @@ static QSvgNode *createPathNode(QSvgNode *parent,
QPainterPath qpath;
qpath.setFillRule(Qt::WindingFill);
- //XXX do error handling
- parsePathDataFast(data, qpath);
+ if (!parsePathDataFast(data, qpath))
+ qCWarning(lcSvgHandler, "Invalid path data; path truncated.");
QSvgNode *path = new QSvgPath(parent, qpath);
return path;
@@ -2954,7 +2958,7 @@ static QSvgNode *createPolygonNode(QSvgNode *parent,
//same QPolygon parsing is in createPolylineNode
const QChar *s = pointsStr.constData();
QList<qreal> points = parseNumbersList(s);
- QPolygonF poly(points.count()/2);
+ QPolygonF poly(points.size()/2);
for (int i = 0; i < poly.size(); ++i)
poly[i] = QPointF(points.at(2 * i), points.at(2 * i + 1));
QSvgNode *polygon = new QSvgPolygon(parent, poly);
@@ -2970,7 +2974,7 @@ static QSvgNode *createPolylineNode(QSvgNode *parent,
//same QPolygon parsing is in createPolygonNode
const QChar *s = pointsStr.constData();
QList<qreal> points = parseNumbersList(s);
- QPolygonF poly(points.count()/2);
+ QPolygonF poly(points.size()/2);
for (int i = 0; i < poly.size(); ++i)
poly[i] = QPointF(points.at(2 * i), points.at(2 * i + 1));
@@ -2998,13 +3002,16 @@ static QSvgStyleProperty *createRadialGradientNode(QSvgNode *node,
qreal ncx = 0.5;
qreal ncy = 0.5;
- qreal nr = 0.5;
if (!cx.isEmpty())
ncx = toDouble(cx);
if (!cy.isEmpty())
ncy = toDouble(cy);
+
+ qreal nr = 0.0;
if (!r.isEmpty())
nr = toDouble(r);
+ if (nr <= 0.0)
+ return nullptr;
qreal nfx = ncx;
if (!fx.isEmpty())
@@ -3133,12 +3140,12 @@ static bool parseStopNode(QSvgStyleProperty *parent,
cssNode.ptr = &anim;
QList<QCss::Declaration> decls = handler->selector()->declarationsForNode(cssNode);
- for (int i = 0; i < decls.count(); ++i) {
+ for (int i = 0; i < decls.size(); ++i) {
const QCss::Declaration &decl = decls.at(i);
if (decl.d->property.isEmpty())
continue;
- if (decl.d->values.count() != 1)
+ if (decl.d->values.size() != 1)
continue;
QCss::Value val = decl.d->values.first();
QString valueStr = val.toString();
@@ -3246,7 +3253,7 @@ static QSvgNode *createSvgNode(QSvgNode *parent,
viewBoxStr = viewBoxStr.replace(QLatin1Char('\t'), QLatin1Char(','));
viewBoxValues = viewBoxStr.split(QLatin1Char(','), Qt::SkipEmptyParts);
}
- if (viewBoxValues.count() == 4) {
+ if (viewBoxValues.size() == 4) {
QString xStr = viewBoxValues.at(0).trimmed();
QString yStr = viewBoxValues.at(1).trimmed();
QString widthStr = viewBoxValues.at(2).trimmed();
@@ -3300,7 +3307,9 @@ static QSvgNode *createTextNode(QSvgNode *parent,
//### editable and rotate not handled
QSvgHandler::LengthType type;
qreal nx = parseLength(x.toString(), type, handler);
+ nx = convertToPixels(nx, true, type);
qreal ny = parseLength(y.toString(), type, handler);
+ ny = convertToPixels(ny, true, type);
QSvgNode *text = new QSvgText(parent, QPointF(nx, ny));
return text;
@@ -3342,7 +3351,7 @@ static QSvgNode *createUseNode(QSvgNode *parent,
QString linkId = attributes.value(QLatin1String("xlink:href")).toString().remove(0, 1);
const QStringView xStr = attributes.value(QLatin1String("x"));
const QStringView yStr = attributes.value(QLatin1String("y"));
- QSvgStructureNode *group = 0;
+ QSvgStructureNode *group = nullptr;
if (linkId.isEmpty())
linkId = attributes.value(QLatin1String("href")).toString().remove(0, 1);
@@ -3400,7 +3409,7 @@ static FactoryMethod findGroupFactory(const QString &name)
if (name.isEmpty())
return 0;
- QStringView ref = QStringView{name}.mid(1, name.length() - 1);
+ QStringView ref = QStringView{name}.mid(1, name.size() - 1);
switch (name.at(0).unicode()) {
case 'd':
if (ref == QLatin1String("efs")) return createDefsNode;
@@ -3423,7 +3432,7 @@ static FactoryMethod findGraphicsFactory(const QString &name)
if (name.isEmpty())
return 0;
- QStringView ref = QStringView{name}.mid(1, name.length() - 1);
+ QStringView ref = QStringView{name}.mid(1, name.size() - 1);
switch (name.at(0).unicode()) {
case 'a':
if (ref == QLatin1String("nimation")) return createAnimationNode;
@@ -3472,7 +3481,7 @@ static ParseMethod findUtilFactory(const QString &name)
if (name.isEmpty())
return 0;
- QStringView ref = QStringView{name}.mid(1, name.length() - 1);
+ QStringView ref = QStringView{name}.mid(1, name.size() - 1);
switch (name.at(0).unicode()) {
case 'a':
if (ref.isEmpty()) return parseAnchorNode;
@@ -3524,7 +3533,7 @@ static StyleFactoryMethod findStyleFactoryMethod(const QString &name)
if (name.isEmpty())
return 0;
- QStringView ref = QStringView{name}.mid(1, name.length() - 1);
+ QStringView ref = QStringView{name}.mid(1, name.size() - 1);
switch (name.at(0).unicode()) {
case 'f':
if (ref == QLatin1String("ont")) return createFontNode;
@@ -3553,7 +3562,7 @@ static StyleParseMethod findStyleUtilFactoryMethod(const QString &name)
if (name.isEmpty())
return 0;
- QStringView ref = QStringView{name}.mid(1, name.length() - 1);
+ QStringView ref = QStringView{name}.mid(1, name.size() - 1);
switch (name.at(0).unicode()) {
case 'f':
if (ref == QLatin1String("ont-face")) return parseFontFaceNode;
@@ -3634,16 +3643,14 @@ void QSvgHandler::parse()
--remainingUnfinishedElements;
} else {
delete m_doc;
- m_doc = 0;
+ m_doc = nullptr;
return;
}
break;
case QXmlStreamReader::EndElement:
endElement(xml->name());
++remainingUnfinishedElements;
- // if we are using somebody else's qxmlstreamreader
- // we should not read until the end of the stream
- done = !m_ownsReader && (xml->name() == QLatin1String("svg"));
+ done = (xml->name() == QLatin1String("svg"));
break;
case QXmlStreamReader::Characters:
characters(xml->text());
@@ -3662,7 +3669,7 @@ void QSvgHandler::parse()
bool QSvgHandler::startElement(const QString &localName,
const QXmlStreamAttributes &attributes)
{
- QSvgNode *node = 0;
+ QSvgNode *node = nullptr;
pushColorCopy();
@@ -3775,8 +3782,9 @@ bool QSvgHandler::startElement(const QString &localName,
} else if (node->type() == QSvgNode::TSPAN) {
static_cast<QSvgTspan *>(node)->setWhitespaceMode(m_whitespaceMode.top());
} else if (node->type() == QSvgNode::USE) {
- if (!static_cast<QSvgUse *>(node)->isResolved())
- m_resolveNodes.append(node);
+ auto useNode = static_cast<QSvgUse *>(node);
+ if (!useNode->isResolved())
+ m_toBeResolved.append(useNode);
}
}
}
@@ -3883,17 +3891,16 @@ void QSvgHandler::resolveGradients(QSvgNode *node, int nestedDepth)
void QSvgHandler::resolveNodes()
{
- for (QSvgNode *node : qAsConst(m_resolveNodes)) {
- if (!node || !node->parent() || node->type() != QSvgNode::USE)
- continue;
- QSvgUse *useNode = static_cast<QSvgUse *>(node);
- if (useNode->isResolved())
+ for (QSvgUse *useNode : std::as_const(m_toBeResolved)) {
+ const auto parent = useNode->parent();
+ if (!parent)
continue;
- QSvgNode::Type t = useNode->parent()->type();
- if (!(t == QSvgNode::DOC || t == QSvgNode::DEFS || t == QSvgNode::G || t == QSvgNode::SWITCH))
+
+ QSvgNode::Type t = parent->type();
+ if (t != QSvgNode::DOC && t != QSvgNode::DEFS && t != QSvgNode::G && t != QSvgNode::SWITCH)
continue;
- QSvgStructureNode *group = static_cast<QSvgStructureNode *>(useNode->parent());
+ QSvgStructureNode *group = static_cast<QSvgStructureNode *>(parent);
QSvgNode *link = group->scopeNode(useNode->linkId());
if (!link) {
qCWarning(lcSvgHandler, "link #%s is undefined!", qPrintable(useNode->linkId()));
@@ -3905,7 +3912,7 @@ void QSvgHandler::resolveNodes()
useNode->setLink(link);
}
- m_resolveNodes.clear();
+ m_toBeResolved.clear();
}
bool QSvgHandler::characters(const QStringView str)
@@ -3936,7 +3943,7 @@ QIODevice *QSvgHandler::device() const
return xml->device();
}
-QSvgTinyDocument * QSvgHandler::document() const
+QSvgTinyDocument *QSvgHandler::document() const
{
return m_doc;
}
@@ -3959,7 +3966,7 @@ void QSvgHandler::pushColor(const QColor &color)
void QSvgHandler::pushColorCopy()
{
- if (m_colorTagCount.count())
+ if (m_colorTagCount.size())
++m_colorTagCount.top();
else
pushColor(Qt::black);
@@ -3967,7 +3974,7 @@ void QSvgHandler::pushColorCopy()
void QSvgHandler::popColor()
{
- if (m_colorTagCount.count()) {
+ if (m_colorTagCount.size()) {
if (!--m_colorTagCount.top()) {
m_colorStack.pop();
m_colorTagCount.pop();