diff options
Diffstat (limited to 'src/svg/qsvghandler.cpp')
-rw-r--r-- | src/svg/qsvghandler.cpp | 581 |
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(); |