5#include "qplatformdefs.h"
23#include <QtCore/private/qdataurl_p.h>
31#include <qregularexpression.h>
34#include "private/qmath_p.h"
41Q_LOGGING_CATEGORY(lcSvgHandler,
"qt.svg")
43static const char *qt_inherit_text =
"inherit";
44#define QT_INHERIT QLatin1String(qt_inherit_text)
50 if (
const QFile *file = qobject_cast<
const QFile *>(r->device()))
51 result.append(QFile::encodeName(QDir::toNativeSeparators(file->fileName())));
53 result.append(QByteArrayLiteral(
"<input>"));
55 result.append(QByteArray::number(r->lineNumber()));
56 if (
const qint64 column = r->columnNumber()) {
58 result.append(QByteArray::number(column));
60 result.append(QByteArrayLiteral(
": "));
68 return prefixMessage(
"Problem parsing " + localName.toLocal8Bit(), r);
73 return prefixMessage(
"Could not resolve property: " + id.toLocal8Bit(), r);
78 static const QRegularExpression delimiterRE(
QStringLiteral(
"[,\\s]+"));
79 return delimitedList.split(delimiterRE, Qt::SkipEmptyParts);
84static inline int qsvg_h2i(
char hex,
bool *ok =
nullptr)
86 if (hex >=
'0' && hex <=
'9')
88 if (hex >=
'a' && hex <=
'f')
89 return hex -
'a' + 10;
90 if (hex >=
'A' && hex <=
'F')
91 return hex -
'A' + 10;
113 const size_t len = qstrlen(name);
120 }
else if (len == 9) {
124 }
else if (len == 6) {
128 }
else if (len == 3) {
135 if ((uint)r > 255 || (uint)g > 255 || (uint)b > 255 || !ok) {
139 *rgb = qRgb(r, g ,b);
148 for(
int i = 0; i < len; ++i)
149 tmp[i] = str[i].toLatin1();
151 return qsvg_get_hex_rgb(tmp, rgb);
156static bool parsePathDataFast(QStringView data, QPainterPath &path,
bool limitLength =
true);
158static inline QString
someId(
const QXmlStreamAttributes &attributes)
160 QStringView id = attributes.value(QLatin1String(
"id"));
162 id = attributes.value(QLatin1String(
"xml:id"));
163 return id.toString();
169 void setAttributes(
const QXmlStreamAttributes &attributes, QSvgHandler *handler);
211 setAttributes(xmlAttributes, handler);
216 for (
const QXmlStreamAttribute &attribute : attributes) {
217 QStringView name = attribute.qualifiedName();
220 QStringView value = attribute.value();
222 switch (name.at(0).unicode()) {
225 if (name == QLatin1String(
"color"))
227 else if (name == QLatin1String(
"color-opacity"))
228 colorOpacity = value;
229 else if (name == QLatin1String(
"comp-op"))
234 if (name == QLatin1String(
"display"))
239 if (name == QLatin1String(
"fill"))
241 else if (name == QLatin1String(
"fill-rule"))
243 else if (name == QLatin1String(
"fill-opacity"))
245 else if (name == QLatin1String(
"font-family"))
247 else if (name == QLatin1String(
"font-size"))
249 else if (name == QLatin1String(
"font-style"))
251 else if (name == QLatin1String(
"font-weight"))
253 else if (name == QLatin1String(
"font-variant"))
255 else if (name == QLatin1String(
"filter") &&
256 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
261 if (name == QLatin1String(
"id"))
262 id = value.toString();
263 else if (name == QLatin1String(
"image-rendering"))
264 imageRendering = value;
268 if (name == QLatin1String(
"mask") &&
269 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
271 if (name == QLatin1String(
"marker-start") &&
272 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
274 if (name == QLatin1String(
"marker-mid") &&
275 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
277 if (name == QLatin1String(
"marker-end") &&
278 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
283 if (name == QLatin1String(
"opacity"))
285 if (name == QLatin1String(
"offset"))
290 if (name.size() > 5 && name.mid(1, 5) == QLatin1String(
"troke")) {
291 QStringView strokeRef = name.mid(6, name.size() - 6);
292 if (strokeRef.isEmpty())
294 else if (strokeRef == QLatin1String(
"-dasharray"))
295 strokeDashArray = value;
296 else if (strokeRef == QLatin1String(
"-dashoffset"))
297 strokeDashOffset = value;
298 else if (strokeRef == QLatin1String(
"-linecap"))
299 strokeLineCap = value;
300 else if (strokeRef == QLatin1String(
"-linejoin"))
301 strokeLineJoin = value;
302 else if (strokeRef == QLatin1String(
"-miterlimit"))
303 strokeMiterLimit = value;
304 else if (strokeRef == QLatin1String(
"-opacity"))
305 strokeOpacity = value;
306 else if (strokeRef == QLatin1String(
"-width"))
308 }
else if (name == QLatin1String(
"stop-color"))
310 else if (name == QLatin1String(
"stop-opacity"))
315 if (name == QLatin1String(
"text-anchor"))
317 else if (name == QLatin1String(
"transform"))
322 if (name == QLatin1String(
"vector-effect"))
323 vectorEffect = value;
324 else if (name == QLatin1String(
"visibility"))
329 if (name == QLatin1String(
"xml:id") && id.isEmpty())
330 id = value.toString();
346 while (str->isSpace())
349 *str == QLatin1Char(
'-') || *str == QLatin1Char(
'+') ||
350 *str == QLatin1Char(
'.')) {
352 points.append(QSvgUtils::toDouble(str));
354 while (str->isSpace())
356 if (*str == QLatin1Char(
','))
360 while (str->isSpace())
368 const char *pattern =
nullptr)
370 const size_t patternLen = qstrlen(pattern);
371 while (str->isSpace())
374 *str == QLatin1Char(
'-') || *str == QLatin1Char(
'+') ||
375 *str == QLatin1Char(
'.')) {
377 if (patternLen && pattern[points.size() % patternLen] ==
'f') {
379 if (*str != QLatin1Char(
'0') && *str != QLatin1Char(
'1'))
381 points.append(*str == QLatin1Char(
'0') ? 0.0 : 1.0);
384 points.append(QSvgUtils::toDouble(str));
387 while (str->isSpace())
389 if (*str == QLatin1Char(
','))
393 while (str->isSpace())
404 while (str->isSpace())
406 while ((*str >= QLatin1Char(
'0') && *str <= QLatin1Char(
'9')) ||
407 *str == QLatin1Char(
'-') || *str == QLatin1Char(
'+') ||
408 *str == QLatin1Char(
'.')) {
410 points.append(QSvgUtils::toDouble(str));
412 while (str->isSpace())
414 if (*str == QLatin1Char(
'%'))
416 while (str->isSpace())
418 if (*str == QLatin1Char(
','))
422 while (str->isSpace())
434 if (url.startsWith(QLatin1Char(
'(')))
437 return QStringView();
439 if (!url.startsWith(QLatin1Char(
'#')))
440 return QStringView();
441 const qsizetype closingBracePos = url.indexOf(QLatin1Char(
')'));
442 if (closingBracePos == -1)
443 return QStringView();
444 return url.first(closingBracePos).trimmed();
448
449
450
451static bool resolveColor(QStringView colorStr, QColor &color, QSvgHandler *handler)
453 QStringView colorStrTr = colorStr.trimmed();
454 if (colorStrTr.isEmpty())
457 switch(colorStrTr.at(0).unicode()) {
464 bool ok = qsvg_get_hex_rgb(colorStrTr.constData(), colorStrTr.size(), &rgb);
474 if (colorStrTr.size() >= 7 && colorStrTr.at(colorStrTr.size() - 1) == QLatin1Char(
')')
475 && colorStrTr.mid(0, 4) == QLatin1String(
"rgb(")) {
476 const QChar *s = colorStrTr.constData() + 4;
477 QList<qreal> compo = parseNumbersList(s);
480 if (compo.size() == 1) {
481 s = colorStrTr.constData() + 4;
482 compo = parsePercentageList(s);
483 for (
int i = 0; i < compo.size(); ++i)
484 compo[i] *= (qreal)2.55;
487 if (compo.size() == 3) {
488 color = QColor(
int(compo[0]),
499 if (colorStrTr == QLatin1String(
"currentColor")) {
500 color = handler->currentColor();
512 color = QColor::fromString(colorStrTr);
513 return color.isValid();
517 QColor &color, QSvgHandler *handler)
519 if (!resolveColor(colorStr, color, handler))
521 if (!opacity.isEmpty()) {
523 qreal op = qMin(qreal(1.0), qMax(qreal(0.0), QSvgUtils::toDouble(opacity, &ok)));
534 qreal num = QSvgUtils::parseLength(str.toString(), &type, ok);
541static bool createSvgGlyph(QSvgFont *font,
const QXmlStreamAttributes &attributes,
544 QStringView uncStr = attributes.value(QLatin1String(
"unicode"));
545 QStringView havStr = attributes.value(QLatin1String(
"horiz-adv-x"));
546 QStringView pathStr = attributes.value(QLatin1String(
"d"));
548 qreal havx = (havStr.isEmpty()) ? -1 : QSvgUtils::toDouble(havStr);
550 path.setFillRule(Qt::WindingFill);
551 parsePathDataFast(pathStr, path);
553 if (isMissingGlyph) {
554 if (!uncStr.isEmpty())
555 qWarning(
"Ignoring missing-glyph's 'unicode' attribute");
556 return font->addMissingGlyph(path, havx);
559 if (uncStr.isEmpty()) {
560 qWarning(
"glyph does not define a non-empty 'unicode' attribute and will be ignored");
563 font->addGlyph(uncStr.at(0), path, havx);
569 QSvgHandler *handler)
572 if (constructColor(attributes.color, attributes.colorOpacity, color, handler)) {
574 handler->pushColor(color);
580 return node ? node->styleProperty(idFromUrl(url)) : 0;
585 QSvgHandler *handler)
587 if (!attributes.fill.isEmpty() || !attributes.fillRule.isEmpty() || !attributes.fillOpacity.isEmpty()) {
588 QSvgFillStyle *prop =
new QSvgFillStyle;
591 if (!attributes.fillRule.isEmpty() && attributes.fillRule !=
QT_INHERIT) {
592 if (attributes.fillRule == QLatin1String(
"evenodd"))
593 prop->setFillRule(Qt::OddEvenFill);
594 else if (attributes.fillRule == QLatin1String(
"nonzero"))
595 prop->setFillRule(Qt::WindingFill);
599 if (!attributes.fillOpacity.isEmpty() && attributes.fillOpacity !=
QT_INHERIT) {
600 prop->setFillOpacity(qMin(qreal(1.0), qMax(qreal(0.0), QSvgUtils::toDouble(attributes.fillOpacity))));
604 if ((!attributes.fill.isEmpty()) && (attributes.fill !=
QT_INHERIT) ) {
605 if (attributes.fill.startsWith(QLatin1String(
"url"))) {
606 QStringView value = attributes.fill.sliced(3);
607 QSvgStyleProperty *style = styleFromUrl(node, value);
609 if (style->type() == QSvgStyleProperty::SOLID_COLOR || style->type() == QSvgStyleProperty::GRADIENT
610 || style->type() == QSvgStyleProperty::PATTERN)
611 prop->setFillStyle(
reinterpret_cast<QSvgPaintStyleProperty *>(style));
613 QString id = idFromUrl(value).toString();
614 prop->setPaintStyleId(id);
615 prop->setPaintStyleResolved(
false);
617 }
else if (attributes.fill != QLatin1String(
"none")) {
619 if (resolveColor(attributes.fill, color, handler))
620 prop->setBrush(QBrush(color));
622 prop->setBrush(QBrush(Qt::NoBrush));
625 node->appendStyleProperty(prop, attributes.id);
637 const QChar *str = value.constData();
638 const QChar *end = str + value.size();
641 if (str->isSpace() || *str == QLatin1Char(
',')) {
653 State state = Matrix;
654 if (*str == QLatin1Char(
'm')) {
655 const char *ident =
"atrix";
656 for (
int i = 0; i < 5; ++i)
657 if (*(++str) != QLatin1Char(ident[i]))
661 }
else if (*str == QLatin1Char(
't')) {
662 const char *ident =
"ranslate";
663 for (
int i = 0; i < 8; ++i)
664 if (*(++str) != QLatin1Char(ident[i]))
668 }
else if (*str == QLatin1Char(
'r')) {
669 const char *ident =
"otate";
670 for (
int i = 0; i < 5; ++i)
671 if (*(++str) != QLatin1Char(ident[i]))
675 }
else if (*str == QLatin1Char(
's')) {
677 if (*str == QLatin1Char(
'c')) {
678 const char *ident =
"ale";
679 for (
int i = 0; i < 3; ++i)
680 if (*(++str) != QLatin1Char(ident[i]))
684 }
else if (*str == QLatin1Char(
'k')) {
685 if (*(++str) != QLatin1Char(
'e'))
687 if (*(++str) != QLatin1Char(
'w'))
690 if (*str == QLatin1Char(
'X'))
692 else if (*str == QLatin1Char(
'Y'))
705 while (str < end && str->isSpace())
707 if (*str != QLatin1Char(
'('))
710 QVarLengthArray<qreal, 8> points;
711 parseNumbersArray(str, points);
712 if (*str != QLatin1Char(
')'))
716 if(state == Matrix) {
717 if(points.size() != 6)
719 matrix = QTransform(points[0], points[1],
720 points[2], points[3],
721 points[4], points[5]) * matrix;
722 }
else if (state == Translate) {
723 if (points.size() == 1)
724 matrix.translate(points[0], 0);
725 else if (points.size() == 2)
726 matrix.translate(points[0], points[1]);
729 }
else if (state == Rotate) {
730 if(points.size() == 1) {
731 matrix.rotate(points[0]);
732 }
else if (points.size() == 3) {
733 matrix.translate(points[1], points[2]);
734 matrix.rotate(points[0]);
735 matrix.translate(-points[1], -points[2]);
739 }
else if (state == Scale) {
740 if (points.size() < 1 || points.size() > 2)
742 qreal sx = points[0];
744 if(points.size() == 2)
746 matrix.scale(sx, sy);
747 }
else if (state == SkewX) {
748 if (points.size() != 1)
750 matrix.shear(qTan(qDegreesToRadians(points[0])), 0);
751 }
else if (state == SkewY) {
752 if (points.size() != 1)
754 matrix.shear(0, qTan(qDegreesToRadians(points[0])));
763 QSvgHandler *handler)
765 if (!attributes.stroke.isEmpty() || !attributes.strokeDashArray.isEmpty() || !attributes.strokeDashOffset.isEmpty() || !attributes.strokeLineCap.isEmpty()
766 || !attributes.strokeLineJoin.isEmpty() || !attributes.strokeMiterLimit.isEmpty() || !attributes.strokeOpacity.isEmpty() || !attributes.strokeWidth.isEmpty()
767 || !attributes.vectorEffect.isEmpty()) {
769 QSvgStrokeStyle *prop =
new QSvgStrokeStyle;
772 if ((!attributes.stroke.isEmpty()) && (attributes.stroke !=
QT_INHERIT) ) {
773 if (attributes.stroke.startsWith(QLatin1String(
"url"))) {
774 QStringView value = attributes.stroke.sliced(3);
775 QSvgStyleProperty *style = styleFromUrl(node, value);
777 if (style->type() == QSvgStyleProperty::SOLID_COLOR || style->type() == QSvgStyleProperty::GRADIENT
778 || style->type() == QSvgStyleProperty::PATTERN)
779 prop->setStyle(
reinterpret_cast<QSvgPaintStyleProperty *>(style));
781 QString id = idFromUrl(value).toString();
782 prop->setPaintStyleId(id);
783 prop->setPaintStyleResolved(
false);
785 }
else if (attributes.stroke != QLatin1String(
"none")) {
787 if (resolveColor(attributes.stroke, color, handler))
788 prop->setStroke(QBrush(color));
790 prop->setStroke(QBrush(Qt::NoBrush));
795 if (!attributes.strokeWidth.isEmpty() && attributes.strokeWidth !=
QT_INHERIT) {
797 prop->setWidth(QSvgUtils::parseLength(attributes.strokeWidth, <));
801 if (!attributes.strokeDashArray.isEmpty() && attributes.strokeDashArray !=
QT_INHERIT) {
802 if (attributes.strokeDashArray == QLatin1String(
"none")) {
803 prop->setDashArrayNone();
805 QString dashArray = attributes.strokeDashArray.toString();
806 const QChar *s = dashArray.constData();
807 QList<qreal> dashes = parseNumbersList(s);
808 bool allZeroes =
true;
809 for (qreal dash : dashes) {
818 if (allZeroes ==
false) {
820 if ((dashes.size() & 1) != 0)
821 dashes << QList<qreal>(dashes);
822 prop->setDashArray(dashes);
824 prop->setDashArrayNone();
830 if (!attributes.strokeLineJoin.isEmpty()) {
831 if (attributes.strokeLineJoin == QLatin1String(
"miter"))
832 prop->setLineJoin(Qt::SvgMiterJoin);
833 else if (attributes.strokeLineJoin == QLatin1String(
"round"))
834 prop->setLineJoin(Qt::RoundJoin);
835 else if (attributes.strokeLineJoin == QLatin1String(
"bevel"))
836 prop->setLineJoin(Qt::BevelJoin);
840 if (!attributes.strokeLineCap.isEmpty()) {
841 if (attributes.strokeLineCap == QLatin1String(
"butt"))
842 prop->setLineCap(Qt::FlatCap);
843 else if (attributes.strokeLineCap == QLatin1String(
"round"))
844 prop->setLineCap(Qt::RoundCap);
845 else if (attributes.strokeLineCap == QLatin1String(
"square"))
846 prop->setLineCap(Qt::SquareCap);
850 if (!attributes.strokeDashOffset.isEmpty() && attributes.strokeDashOffset !=
QT_INHERIT)
851 prop->setDashOffset(QSvgUtils::toDouble(attributes.strokeDashOffset));
854 if (!attributes.vectorEffect.isEmpty()) {
855 if (attributes.vectorEffect == QLatin1String(
"non-scaling-stroke"))
856 prop->setVectorEffect(
true);
857 else if (attributes.vectorEffect == QLatin1String(
"none"))
858 prop->setVectorEffect(
false);
862 if (!attributes.strokeMiterLimit.isEmpty() && attributes.strokeMiterLimit !=
QT_INHERIT)
863 prop->setMiterLimit(QSvgUtils::toDouble(attributes.strokeMiterLimit));
866 if (!attributes.strokeOpacity.isEmpty() && attributes.strokeOpacity !=
QT_INHERIT)
867 prop->setOpacity(qMin(qreal(1.0), qMax(qreal(0.0), QSvgUtils::toDouble(attributes.strokeOpacity))));
869 node->appendStyleProperty(prop, attributes.id);
877{ qreal(6.9), qreal(8.3), qreal(10.0), qreal(12.0), qreal(14.4), qreal(17.3), qreal(20.7) };
883 switch (spec.at(0).unicode()) {
885 if (spec == QLatin1String(
"xx-small"))
887 if (spec == QLatin1String(
"x-small"))
889 if (spec == QLatin1String(
"x-large"))
891 if (spec == QLatin1String(
"xx-large"))
895 if (spec == QLatin1String(
"small"))
899 if (spec == QLatin1String(
"medium"))
903 if (spec == QLatin1String(
"large"))
907 if (spec == QLatin1String(
"none"))
920 if (attributes.fontFamily.isEmpty() && attributes.fontSize.isEmpty() && attributes.fontStyle.isEmpty() &&
921 attributes.fontWeight.isEmpty() && attributes.fontVariant.isEmpty() && attributes.textAnchor.isEmpty())
924 QSvgFontStyle *fontStyle =
nullptr;
925 if (!attributes.fontFamily.isEmpty()) {
926 QSvgTinyDocument *doc = node->document();
928 QSvgFont *svgFont = doc->svgFont(attributes.fontFamily.toString());
930 fontStyle =
new QSvgFontStyle(svgFont, doc);
934 fontStyle =
new QSvgFontStyle;
935 if (!attributes.fontFamily.isEmpty() && attributes.fontFamily !=
QT_INHERIT) {
936 QStringView family = attributes.fontFamily.trimmed();
937 if (!family.isEmpty() && (family.at(0) == QLatin1Char(
'\'') || family.at(0) == QLatin1Char(
'\"')))
938 family = family.mid(1, family.size() - 2);
939 fontStyle->setFamily(family.toString());
942 if (!attributes.fontSize.isEmpty() && attributes.fontSize !=
QT_INHERIT) {
944 const FontSizeSpec spec = fontSizeSpec(attributes.fontSize);
950 qreal fs = QSvgUtils::parseLength(attributes.fontSize, &type);
951 fs = QSvgUtils::convertToPixels(fs,
true, type);
952 fontStyle->setSize(qMin(fs, qreal(0xffff)));
956 fontStyle->setSize(sizeTable[spec]);
961 if (!attributes.fontStyle.isEmpty() && attributes.fontStyle !=
QT_INHERIT) {
962 if (attributes.fontStyle == QLatin1String(
"normal")) {
963 fontStyle->setStyle(QFont::StyleNormal);
964 }
else if (attributes.fontStyle == QLatin1String(
"italic")) {
965 fontStyle->setStyle(QFont::StyleItalic);
966 }
else if (attributes.fontStyle == QLatin1String(
"oblique")) {
967 fontStyle->setStyle(QFont::StyleOblique);
971 if (!attributes.fontWeight.isEmpty() && attributes.fontWeight !=
QT_INHERIT) {
973 const int weightNum = attributes.fontWeight.toInt(&ok);
975 fontStyle->setWeight(weightNum);
977 if (attributes.fontWeight == QLatin1String(
"normal")) {
978 fontStyle->setWeight(QFont::Normal);
979 }
else if (attributes.fontWeight == QLatin1String(
"bold")) {
980 fontStyle->setWeight(QFont::Bold);
981 }
else if (attributes.fontWeight == QLatin1String(
"bolder")) {
982 fontStyle->setWeight(QSvgFontStyle::BOLDER);
983 }
else if (attributes.fontWeight == QLatin1String(
"lighter")) {
984 fontStyle->setWeight(QSvgFontStyle::LIGHTER);
989 if (!attributes.fontVariant.isEmpty() && attributes.fontVariant !=
QT_INHERIT) {
990 if (attributes.fontVariant == QLatin1String(
"normal"))
991 fontStyle->setVariant(QFont::MixedCase);
992 else if (attributes.fontVariant == QLatin1String(
"small-caps"))
993 fontStyle->setVariant(QFont::SmallCaps);
996 if (!attributes.textAnchor.isEmpty() && attributes.textAnchor !=
QT_INHERIT) {
997 if (attributes.textAnchor == QLatin1String(
"start"))
998 fontStyle->setTextAnchor(Qt::AlignLeft);
999 if (attributes.textAnchor == QLatin1String(
"middle"))
1000 fontStyle->setTextAnchor(Qt::AlignHCenter);
1001 else if (attributes.textAnchor == QLatin1String(
"end"))
1002 fontStyle->setTextAnchor(Qt::AlignRight);
1005 node->appendStyleProperty(fontStyle, attributes.id);
1012 if (attributes.transform.isEmpty())
1014 QTransform matrix = parseTransformationMatrix(attributes.transform.trimmed());
1016 if (!matrix.isIdentity()) {
1017 node->appendStyleProperty(
new QSvgTransformStyle(QTransform(matrix)), attributes.id);
1026 QSvgNode *parent = node->parent();
1028 if (parent && (attributes.visibility.isEmpty() || attributes.visibility ==
QT_INHERIT))
1029 node->setVisible(parent->isVisible());
1030 else if (attributes.visibility == QLatin1String(
"hidden") || attributes.visibility == QLatin1String(
"collapse")) {
1031 node->setVisible(
false);
1033 node->setVisible(
true);
1038 qreal th0, qreal th1,
1039 qreal rx, qreal ry, qreal xAxisRotation)
1042 qreal a00, a01, a10, a11;
1043 qreal x1, y1, x2, y2, x3, y3;
1047 sinTh = qSin(xAxisRotation * (Q_PI / 180.0));
1048 cosTh = qCos(xAxisRotation * (Q_PI / 180.0));
1055 thHalf = 0.5 * (th1 - th0);
1056 t = (8.0 / 3.0) * qSin(thHalf * 0.5) * qSin(thHalf * 0.5) / qSin(thHalf);
1057 x1 = xc + qCos(th0) - t * qSin(th0);
1058 y1 = yc + qSin(th0) + t * qCos(th0);
1059 x3 = xc + qCos(th1);
1060 y3 = yc + qSin(th1);
1061 x2 = x3 + t * qSin(th1);
1062 y2 = y3 - t * qCos(th1);
1064 path.cubicTo(a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
1065 a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
1066 a00 * x3 + a01 * y3, a10 * x3 + a11 * y3);
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1098 qreal x_axis_rotation,
1103 qreal curx, qreal cury)
1105 const qreal Pr1 = rx * rx;
1106 const qreal Pr2 = ry * ry;
1111 qreal sin_th, cos_th;
1112 qreal a00, a01, a10, a11;
1113 qreal x0, y0, x1, y1, xc, yc;
1114 qreal d, sfactor, sfactor_sq;
1115 qreal th0, th1, th_arc;
1117 qreal dx, dy, dx1, dy1, Px, Py, check;
1122 sin_th = qSin(x_axis_rotation * (Q_PI / 180.0));
1123 cos_th = qCos(x_axis_rotation * (Q_PI / 180.0));
1125 dx = (curx - x) / 2.0;
1126 dy = (cury - y) / 2.0;
1127 dx1 = cos_th * dx + sin_th * dy;
1128 dy1 = -sin_th * dx + cos_th * dy;
1132 check = Px / Pr1 + Py / Pr2;
1134 rx = rx * qSqrt(check);
1135 ry = ry * qSqrt(check);
1142 x0 = a00 * curx + a01 * cury;
1143 y0 = a10 * curx + a11 * cury;
1144 x1 = a00 * x + a01 * y;
1145 y1 = a10 * x + a11 * y;
1147
1148
1149
1150
1151 d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
1154 sfactor_sq = 1.0 / d - 0.25;
1155 if (sfactor_sq < 0) sfactor_sq = 0;
1156 sfactor = qSqrt(sfactor_sq);
1157 if (sweep_flag == large_arc_flag) sfactor = -sfactor;
1158 xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
1159 yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
1162 th0 = qAtan2(y0 - yc, x0 - xc);
1163 th1 = qAtan2(y1 - yc, x1 - xc);
1166 if (th_arc < 0 && sweep_flag)
1168 else if (th_arc > 0 && !sweep_flag)
1171 n_segs = qCeil(qAbs(th_arc / (Q_PI * 0.5 + 0.001)));
1173 for (i = 0; i < n_segs; i++) {
1174 pathArcSegment(path, xc, yc,
1175 th0 + i * th_arc / n_segs,
1176 th0 + (i + 1) * th_arc / n_segs,
1177 rx, ry, x_axis_rotation);
1183 const int maxElementCount = 0x7fff;
1184 qreal x0 = 0, y0 = 0;
1188 const QChar *str = dataStr.constData();
1189 const QChar *end = str + dataStr.size();
1192 while (ok && str != end) {
1193 while (str->isSpace() && (str + 1) != end)
1195 QChar pathElem = *str;
1198 *
const_cast<QChar *>(end) = u'\0';
1199 const char *pattern =
nullptr;
1200 if (pathElem == QLatin1Char(
'a') || pathElem == QLatin1Char(
'A'))
1201 pattern =
"rrrffrr";
1202 QVarLengthArray<qreal, 8> arg;
1203 parseNumbersArray(str, arg, pattern);
1204 *
const_cast<QChar *>(end) = endc;
1205 if (pathElem == QLatin1Char(
'z') || pathElem == QLatin1Char(
'Z'))
1207 const qreal *num = arg.constData();
1208 int count = arg.size();
1209 while (ok && count > 0) {
1212 switch (pathElem.unicode()) {
1218 x = x0 = num[0] + offsetX;
1219 y = y0 = num[1] + offsetY;
1222 path.moveTo(x0, y0);
1227 pathElem = QLatin1Char(
'l');
1239 path.moveTo(x0, y0);
1244 pathElem = QLatin1Char(
'L');
1253 path.closeSubpath();
1261 x = num[0] + offsetX;
1262 y = num[1] + offsetY;
1282 x = num[0] + offsetX;
1296 y = num[0] + offsetY;
1314 QPointF c1(num[0] + offsetX, num[1] + offsetY);
1315 QPointF c2(num[2] + offsetX, num[3] + offsetY);
1316 QPointF e(num[4] + offsetX, num[5] + offsetY);
1319 path.cubicTo(c1, c2, e);
1330 QPointF c1(num[0], num[1]);
1331 QPointF c2(num[2], num[3]);
1332 QPointF e(num[4], num[5]);
1335 path.cubicTo(c1, c2, e);
1347 if (lastMode ==
'c' || lastMode ==
'C' ||
1348 lastMode ==
's' || lastMode ==
'S')
1349 c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
1352 QPointF c2(num[0] + offsetX, num[1] + offsetY);
1353 QPointF e(num[2] + offsetX, num[3] + offsetY);
1356 path.cubicTo(c1, c2, e);
1368 if (lastMode ==
'c' || lastMode ==
'C' ||
1369 lastMode ==
's' || lastMode ==
'S')
1370 c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
1373 QPointF c2(num[0], num[1]);
1374 QPointF e(num[2], num[3]);
1377 path.cubicTo(c1, c2, e);
1388 QPointF c(num[0] + offsetX, num[1] + offsetY);
1389 QPointF e(num[2] + offsetX, num[3] + offsetY);
1403 QPointF c(num[0], num[1]);
1404 QPointF e(num[2], num[3]);
1418 QPointF e(num[0] + offsetX, num[1] + offsetY);
1422 if (lastMode ==
'q' || lastMode ==
'Q' ||
1423 lastMode ==
't' || lastMode ==
'T')
1424 c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
1438 QPointF e(num[0], num[1]);
1442 if (lastMode ==
'q' || lastMode ==
'Q' ||
1443 lastMode ==
't' || lastMode ==
'T')
1444 c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
1458 qreal rx = (*num++);
1459 qreal ry = (*num++);
1460 qreal xAxisRotation = (*num++);
1461 qreal largeArcFlag = (*num++);
1462 qreal sweepFlag = (*num++);
1463 qreal ex = (*num++) + offsetX;
1464 qreal ey = (*num++) + offsetY;
1468 pathArc(path, rx, ry, xAxisRotation,
int(largeArcFlag),
1469 int(sweepFlag), ex, ey, curx, cury);
1480 qreal rx = (*num++);
1481 qreal ry = (*num++);
1482 qreal xAxisRotation = (*num++);
1483 qreal largeArcFlag = (*num++);
1484 qreal sweepFlag = (*num++);
1485 qreal ex = (*num++);
1486 qreal ey = (*num++);
1490 pathArc(path, rx, ry, xAxisRotation,
int(largeArcFlag),
1491 int(sweepFlag), ex, ey, curx, cury);
1501 lastMode = pathElem.toLatin1();
1502 if (limitLength && path.elementCount() > maxElementCount)
1510 const QXmlStreamAttributes &attributes,
1511 QSvgHandler *handler);
1517 str = str.trimmed();
1518 if (str.endsWith(QLatin1String(
"ms"))) {
1521 }
else if (str.endsWith(QLatin1String(
"s"))) {
1524 double val = ms * QSvgUtils::toDouble(str, ok);
1526 if (val >
std::numeric_limits<
int>::min() && val <
std::numeric_limits<
int>::max())
1527 res =
static_cast<
int>(val);
1534#ifndef QT_NO_CSSPARSER
1537 const QXmlStreamAttributes &attributes,
1538 QSvgHandler *handler)
1540 QSvgCssAnimationProperties cssAnimProps(attributes);
1541 QList<QSvgAnimationProperty> parsedProperties = cssAnimProps.parse();
1543 for (QSvgAnimationProperty property : parsedProperties) {
1544 QSvgCssAnimation *anim = handler->cssHandler().createAnimation(property.name);
1548 anim->setRunningTime(property.delay, property.duration);
1549 anim->setIterationCount(property.iteration);
1550 handler->setAnimPeriod(property.delay, property.delay + property.duration);
1551 handler->document()->animator()->appendAnimation(node, anim);
1552 handler->document()->setAnimated(
true);
1558QtSvg::Options QSvgHandler::options()
const
1563QtSvg::AnimatorType QSvgHandler::animatorType()
const
1565 return m_animatorType;
1568bool QSvgHandler::trustedSourceMode()
const
1570 return m_options.testFlag(QtSvg::AssumeTrustedSource);
1575 QStringList lst = str.split(QLatin1Char(
','), Qt::SkipEmptyParts);
1580 const QXmlStreamAttributes &attributes)
1582 QStringList features;
1583 QStringList extensions;
1584 QStringList languages;
1585 QStringList formats;
1587 QStringView xmlClassStr;
1589 for (
const QXmlStreamAttribute &attribute : attributes) {
1590 QStringView name = attribute.qualifiedName();
1593 QStringView value = attribute.value();
1594 switch (name.at(0).unicode()) {
1596 if (name == QLatin1String(
"class"))
1597 xmlClassStr = value;
1600 if (name == QLatin1String(
"requiredFeatures"))
1601 features = stringToList(value.toString());
1602 else if (name == QLatin1String(
"requiredExtensions"))
1603 extensions = stringToList(value.toString());
1604 else if (name == QLatin1String(
"requiredFormats"))
1605 formats = stringToList(value.toString());
1606 else if (name == QLatin1String(
"requiredFonts"))
1607 fonts = stringToList(value.toString());
1610 if (name == QLatin1String(
"systemLanguage"))
1611 languages = stringToList(value.toString());
1618 node->setRequiredFeatures(features);
1619 node->setRequiredExtensions(extensions);
1620 node->setRequiredLanguages(languages);
1621 node->setRequiredFormats(formats);
1622 node->setRequiredFonts(fonts);
1623 node->setNodeId(someId(attributes));
1624 node->setXmlClass(xmlClassStr.toString());
1633 if (attributes.opacity.isEmpty())
1636 const QStringView value = attributes.opacity.trimmed();
1639 qreal op = value.toDouble(&ok);
1642 QSvgOpacityStyle *opacity =
new QSvgOpacityStyle(qBound(qreal(0.0), op, qreal(1.0)));
1643 node->appendStyleProperty(opacity, attributes.id);
1649#define NOOP qDebug()<<"Operation: "<<op<<" is not implemented"
1650 if (op == QLatin1String(
"clear")) {
1651 return QPainter::CompositionMode_Clear;
1652 }
else if (op == QLatin1String(
"src")) {
1653 return QPainter::CompositionMode_Source;
1654 }
else if (op == QLatin1String(
"dst")) {
1655 return QPainter::CompositionMode_Destination;
1656 }
else if (op == QLatin1String(
"src-over")) {
1657 return QPainter::CompositionMode_SourceOver;
1658 }
else if (op == QLatin1String(
"dst-over")) {
1659 return QPainter::CompositionMode_DestinationOver;
1660 }
else if (op == QLatin1String(
"src-in")) {
1661 return QPainter::CompositionMode_SourceIn;
1662 }
else if (op == QLatin1String(
"dst-in")) {
1663 return QPainter::CompositionMode_DestinationIn;
1664 }
else if (op == QLatin1String(
"src-out")) {
1665 return QPainter::CompositionMode_SourceOut;
1666 }
else if (op == QLatin1String(
"dst-out")) {
1667 return QPainter::CompositionMode_DestinationOut;
1668 }
else if (op == QLatin1String(
"src-atop")) {
1669 return QPainter::CompositionMode_SourceAtop;
1670 }
else if (op == QLatin1String(
"dst-atop")) {
1671 return QPainter::CompositionMode_DestinationAtop;
1672 }
else if (op == QLatin1String(
"xor")) {
1673 return QPainter::CompositionMode_Xor;
1674 }
else if (op == QLatin1String(
"plus")) {
1675 return QPainter::CompositionMode_Plus;
1676 }
else if (op == QLatin1String(
"multiply")) {
1677 return QPainter::CompositionMode_Multiply;
1678 }
else if (op == QLatin1String(
"screen")) {
1679 return QPainter::CompositionMode_Screen;
1680 }
else if (op == QLatin1String(
"overlay")) {
1681 return QPainter::CompositionMode_Overlay;
1682 }
else if (op == QLatin1String(
"darken")) {
1683 return QPainter::CompositionMode_Darken;
1684 }
else if (op == QLatin1String(
"lighten")) {
1685 return QPainter::CompositionMode_Lighten;
1686 }
else if (op == QLatin1String(
"color-dodge")) {
1687 return QPainter::CompositionMode_ColorDodge;
1688 }
else if (op == QLatin1String(
"color-burn")) {
1689 return QPainter::CompositionMode_ColorBurn;
1690 }
else if (op == QLatin1String(
"hard-light")) {
1691 return QPainter::CompositionMode_HardLight;
1692 }
else if (op == QLatin1String(
"soft-light")) {
1693 return QPainter::CompositionMode_SoftLight;
1694 }
else if (op == QLatin1String(
"difference")) {
1695 return QPainter::CompositionMode_Difference;
1696 }
else if (op == QLatin1String(
"exclusion")) {
1697 return QPainter::CompositionMode_Exclusion;
1702 return QPainter::CompositionMode_SourceOver;
1709 if (attributes.compOp.isEmpty())
1711 QStringView value = attributes.compOp.trimmed();
1713 if (!value.isEmpty()) {
1714 QSvgCompOpStyle *compop =
new QSvgCompOpStyle(svgToQtCompositionMode(value));
1715 node->appendStyleProperty(compop, attributes.id);
1721 if (str == QLatin1String(
"inline")) {
1722 return QSvgNode::InlineMode;
1723 }
else if (str == QLatin1String(
"block")) {
1724 return QSvgNode::BlockMode;
1725 }
else if (str == QLatin1String(
"list-item")) {
1726 return QSvgNode::ListItemMode;
1727 }
else if (str == QLatin1String(
"run-in")) {
1728 return QSvgNode::RunInMode;
1729 }
else if (str == QLatin1String(
"compact")) {
1730 return QSvgNode::CompactMode;
1731 }
else if (str == QLatin1String(
"marker")) {
1732 return QSvgNode::MarkerMode;
1733 }
else if (str == QLatin1String(
"table")) {
1734 return QSvgNode::TableMode;
1735 }
else if (str == QLatin1String(
"inline-table")) {
1736 return QSvgNode::InlineTableMode;
1737 }
else if (str == QLatin1String(
"table-row-group")) {
1738 return QSvgNode::TableRowGroupMode;
1739 }
else if (str == QLatin1String(
"table-header-group")) {
1740 return QSvgNode::TableHeaderGroupMode;
1741 }
else if (str == QLatin1String(
"table-footer-group")) {
1742 return QSvgNode::TableFooterGroupMode;
1743 }
else if (str == QLatin1String(
"table-row")) {
1744 return QSvgNode::TableRowMode;
1745 }
else if (str == QLatin1String(
"table-column-group")) {
1746 return QSvgNode::TableColumnGroupMode;
1747 }
else if (str == QLatin1String(
"table-column")) {
1748 return QSvgNode::TableColumnMode;
1749 }
else if (str == QLatin1String(
"table-cell")) {
1750 return QSvgNode::TableCellMode;
1751 }
else if (str == QLatin1String(
"table-caption")) {
1752 return QSvgNode::TableCaptionMode;
1753 }
else if (str == QLatin1String(
"none")) {
1754 return QSvgNode::NoneMode;
1756 return QSvgNode::InheritMode;
1758 return QSvgNode::BlockMode;
1765 if (attributes.display.isEmpty())
1767 QStringView displayStr = attributes.display.trimmed();
1769 if (!displayStr.isEmpty()) {
1770 node->setDisplayMode(displayStringToEnum(displayStr));
1776 if (attribute.isEmpty())
1777 return std::nullopt;
1779 constexpr QLatin1String urlKeyword(
"url");
1780 QStringView attrStr = attribute.trimmed();
1781 if (attrStr.startsWith(urlKeyword))
1782 attrStr.slice(urlKeyword.size());
1783 QStringView id = idFromUrl(attrStr);
1784 if (id.startsWith(QLatin1Char(
'#')))
1791 QSvgHandler *handler)
1796 if (
auto id = getAttributeId(attributes.mask))
1797 node->setMaskId(id->toString());
1798 if (
auto id = getAttributeId(attributes.markerStart))
1799 node->setMarkerStartId(id->toString());
1800 if (
auto id = getAttributeId(attributes.markerMid))
1801 node->setMarkerMidId(id->toString());
1802 if (
auto id = getAttributeId(attributes.markerEnd))
1803 node->setMarkerEndId(id->toString());
1804 if (
auto id = getAttributeId(attributes.filter))
1805 node->setFilterId(id->toString());
1812 if (attributes.imageRendering.isEmpty())
1815 QStringView ir = attributes.imageRendering.trimmed();
1816 QSvgQualityStyle *p =
new QSvgQualityStyle(0);
1817 if (ir == QLatin1String(
"auto"))
1818 p->setImageRendering(QSvgQualityStyle::ImageRenderingAuto);
1819 else if (ir == QLatin1String(
"optimizeSpeed"))
1820 p->setImageRendering(QSvgQualityStyle::ImageRenderingOptimizeSpeed);
1821 else if (ir == QLatin1String(
"optimizeQuality"))
1822 p->setImageRendering(QSvgQualityStyle::ImageRenderingOptimizeQuality);
1823 node->appendStyleProperty(p, attributes.id);
1827 const QXmlStreamAttributes &attributes,
1828 QSvgHandler *handler)
1836#ifndef QT_NO_CSSPARSER
1837 QXmlStreamAttributes cssAttributes;
1838 handler->cssHandler().styleLookup(node, cssAttributes);
1840 QStringView style = attributes.value(QLatin1String(
"style"));
1841 if (!style.isEmpty())
1842 handler->cssHandler().parseCSStoXMLAttrs(style.toString(), cssAttributes);
1843 svgAttributes.setAttributes(cssAttributes, handler);
1846 parseCssAnimations(node, cssAttributes, handler);
1865 const QXmlStreamAttributes &attributes,
1868 Q_UNUSED(parent); Q_UNUSED(attributes);
1873 const QXmlStreamAttributes &attributes,
1874 QSvgAnimateNode *anim,
1875 QSvgHandler *handler)
1877 const QStringView beginStr = attributes.value(QLatin1String(
"begin"));
1878 const QStringView durStr = attributes.value(QLatin1String(
"dur"));
1879 const QStringView endStr = attributes.value(QLatin1String(
"end"));
1880 const QStringView repeatStr = attributes.value(QLatin1String(
"repeatCount"));
1881 const QStringView fillStr = attributes.value(QLatin1String(
"fill"));
1882 const QStringView addtv = attributes.value(QLatin1String(
"additive"));
1883 QStringView linkId = attributes.value(QLatin1String(
"xlink:href"));
1885 if (linkId.isEmpty())
1886 linkId = attributes.value(QLatin1String(
"href"));
1888 linkId = linkId.mid(1);
1891 int begin = parseClockValue(beginStr, &ok);
1894 int dur = parseClockValue(durStr, &ok);
1897 int end = parseClockValue(endStr, &ok);
1900 qreal repeatCount = (repeatStr == QLatin1String(
"indefinite")) ? -1 :
1901 qMax(1.0, QSvgUtils::toDouble(repeatStr));
1903 QSvgAnimateNode::Fill fill = (fillStr == QLatin1String(
"freeze")) ? QSvgAnimateNode::Freeze :
1904 QSvgAnimateNode::Remove;
1906 QSvgAnimateNode::Additive additive = (addtv == QLatin1String(
"sum")) ? QSvgAnimateNode::Sum :
1907 QSvgAnimateNode::Replace;
1909 anim->setRunningTime(begin, dur, end, 0);
1910 anim->setRepeatCount(repeatCount);
1911 anim->setFill(fill);
1912 anim->setAdditiveType(additive);
1913 anim->setLinkId(linkId.toString());
1915 parent->document()->setAnimated(
true);
1917 handler->setAnimPeriod(begin, begin + dur);
1926 qreal spacing = 1.0f / (count - 1);
1927 for (uint i = 0; i < count; i++) {
1928 keyFrames.append(i * spacing);
1933 const QXmlStreamAttributes &attributes,
1934 QSvgHandler *handler)
1936 const QStringView fromStr = attributes.value(QLatin1String(
"from"));
1937 const QStringView toStr = attributes.value(QLatin1String(
"to"));
1938 const QStringView valuesStr = attributes.value(QLatin1String(
"values"));
1939 const QString targetStr = attributes.value(QLatin1String(
"attributeName")).toString();
1941 if (targetStr != QLatin1String(
"fill") && targetStr != QLatin1String(
"stroke"))
1944 QList<QColor> colors;
1945 if (valuesStr.isEmpty()) {
1946 QColor startColor, endColor;
1947 resolveColor(fromStr, startColor, handler);
1948 resolveColor(toStr, endColor, handler);
1950 colors.append(startColor);
1951 colors.append(endColor);
1953 for (
auto part : qTokenize(valuesStr, u';')) {
1955 resolveColor(part, color, handler);
1956 colors.append(color);
1960 QSvgAnimatedPropertyColor *prop =
static_cast<QSvgAnimatedPropertyColor *>
1961 (QSvgAbstractAnimatedProperty::createAnimatedProperty(targetStr));
1965 prop->setColors(colors);
1967 QList<qreal> keyFrames;
1968 generateKeyFrames(keyFrames, colors.size());
1969 prop->setKeyFrames(keyFrames);
1971 QSvgAnimateColor *anim =
new QSvgAnimateColor(parent);
1972 anim->appendProperty(prop);
1974 parseBaseAnimate(parent, attributes, anim, handler);
1980 const QXmlStreamAttributes &attributes,
1983 Q_UNUSED(parent); Q_UNUSED(attributes);
1989 QList<qreal> list = parseNumbersList(s);
1991 for (
int i = 3 - list.size(); i > 0; --i)
1996 const QXmlStreamAttributes &attributes,
1997 QSvgHandler *handler)
1999 const QStringView typeStr = attributes.value(QLatin1String(
"type"));
2000 const QStringView values = attributes.value(QLatin1String(
"values"));
2001 const QStringView fromStr = attributes.value(QLatin1String(
"from"));
2002 const QStringView toStr = attributes.value(QLatin1String(
"to"));
2003 const QStringView byStr = attributes.value(QLatin1String(
"by"));
2006 if (values.isEmpty()) {
2008 if (fromStr.isEmpty()) {
2009 if (!byStr.isEmpty()) {
2013 parseNumberTriplet(vals, s = byStr.constData());
2019 if (!toStr.isEmpty()) {
2021 parseNumberTriplet(vals, s = fromStr.constData());
2022 parseNumberTriplet(vals, s = toStr.constData());
2023 }
else if (!byStr.isEmpty()) {
2025 parseNumberTriplet(vals, s = fromStr.constData());
2026 parseNumberTriplet(vals, s = byStr.constData());
2027 for (
int i = vals.size() - 3; i < vals.size(); ++i)
2028 vals[i] += vals[i - 3];
2034 const QChar *s = values.constData();
2035 while (s && s != values.cend()) {
2036 parseNumberTriplet(vals, s);
2037 if (s == values.cend())
2042 if (vals.size() % 3 != 0)
2046 QList<QSvgAnimatedPropertyTransform::TransformComponent> components;
2047 for (
int i = 0; i < vals.size(); i+=3) {
2048 QSvgAnimatedPropertyTransform::TransformComponent component;
2049 if (typeStr == QLatin1String(
"translate")) {
2050 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Translate;
2051 component.values.append(vals.at(i));
2052 component.values.append(vals.at(i + 1));
2053 }
else if (typeStr == QLatin1String(
"scale")) {
2054 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Scale;
2055 component.values.append(vals.at(i));
2056 component.values.append(vals.at(i + 1));
2057 }
else if (typeStr == QLatin1String(
"rotate")) {
2058 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Rotate;
2059 component.values.append(vals.at(i));
2060 component.values.append(vals.at(i + 1));
2061 component.values.append(vals.at(i + 2));
2062 }
else if (typeStr == QLatin1String(
"skewX")) {
2063 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Skew;
2064 component.values.append(vals.at(i));
2065 component.values.append(0);
2066 }
else if (typeStr == QLatin1String(
"skewY")) {
2067 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Skew;
2068 component.values.append(0);
2069 component.values.append(vals.at(i));
2073 components.append(component);
2076 QSvgAnimatedPropertyTransform *prop =
static_cast<QSvgAnimatedPropertyTransform *>
2077 (QSvgAbstractAnimatedProperty::createAnimatedProperty(QLatin1String(
"transform")));
2081 prop->appendComponents(components);
2083 prop->setTransformCount(1);
2084 QList<qreal> keyFrames;
2085 generateKeyFrames(keyFrames, vals.size() / 3);
2086 prop->setKeyFrames(keyFrames);
2088 QSvgAnimateTransform *anim =
new QSvgAnimateTransform(parent);
2089 anim->appendProperty(prop);
2091 parseBaseAnimate(parent, attributes, anim, handler);
2097 const QXmlStreamAttributes &attributes,
2100 Q_UNUSED(parent); Q_UNUSED(attributes);
2105 const QXmlStreamAttributes &attributes,
2108 Q_UNUSED(parent); Q_UNUSED(attributes);
2113 const QXmlStreamAttributes &attributes,
2116 const QStringView cx = attributes.value(QLatin1String(
"cx"));
2117 const QStringView cy = attributes.value(QLatin1String(
"cy"));
2118 const QStringView r = attributes.value(QLatin1String(
"r"));
2119 qreal ncx = QSvgUtils::toDouble(cx);
2120 qreal ncy = QSvgUtils::toDouble(cy);
2121 qreal nr = QSvgUtils::toDouble(r);
2125 QRectF rect(ncx-nr, ncy-nr, nr*2, nr*2);
2126 QSvgNode *circle =
new QSvgCircle(parent, rect);
2131 const QXmlStreamAttributes &attributes,
2134 Q_UNUSED(attributes);
2135 QSvgDefs *defs =
new QSvgDefs(parent);
2140 const QXmlStreamAttributes &attributes,
2143 Q_UNUSED(parent); Q_UNUSED(attributes);
2148 const QXmlStreamAttributes &attributes,
2151 const QStringView cx = attributes.value(QLatin1String(
"cx"));
2152 const QStringView cy = attributes.value(QLatin1String(
"cy"));
2153 const QStringView rx = attributes.value(QLatin1String(
"rx"));
2154 const QStringView ry = attributes.value(QLatin1String(
"ry"));
2155 qreal ncx = QSvgUtils::toDouble(cx);
2156 qreal ncy = QSvgUtils::toDouble(cy);
2157 qreal nrx = QSvgUtils::toDouble(rx);
2158 qreal nry = QSvgUtils::toDouble(ry);
2160 QRectF rect(ncx-nrx, ncy-nry, nrx*2, nry*2);
2161 QSvgNode *ellipse =
new QSvgEllipse(parent, rect);
2166 const QXmlStreamAttributes &attributes,
2169 const QStringView hax = attributes.value(QLatin1String(
"horiz-adv-x"));
2170 QString myId = someId(attributes);
2172 qreal horizAdvX = QSvgUtils::toDouble(hax);
2174 while (parent && parent->type() != QSvgNode::Doc) {
2175 parent = parent->parent();
2178 if (parent && !myId.isEmpty()) {
2179 QSvgTinyDocument *doc =
static_cast<QSvgTinyDocument*>(parent);
2180 QSvgFont *font = doc->svgFont(myId);
2182 font =
new QSvgFont(horizAdvX);
2183 font->setFamilyName(myId);
2184 doc->addSvgFont(font);
2186 return new QSvgFontStyle(font, doc);
2192 const QXmlStreamAttributes &attributes,
2195 if (parent->type() != QSvgStyleProperty::FONT) {
2199 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
2200 QSvgFont *font = style->svgFont();
2201 const QStringView name = attributes.value(QLatin1String(
"font-family"));
2202 const QStringView unitsPerEmStr = attributes.value(QLatin1String(
"units-per-em"));
2204 qreal unitsPerEm = QSvgUtils::toDouble(unitsPerEmStr);
2206 unitsPerEm = QSvgFont::DEFAULT_UNITS_PER_EM;
2208 if (!name.isEmpty())
2209 font->setFamilyName(name.toString());
2210 font->setUnitsPerEm(unitsPerEm);
2212 if (!font->familyName().isEmpty())
2213 if (!style->doc()->svgFont(font->familyName()))
2214 style->doc()->addSvgFont(font);
2220 const QXmlStreamAttributes &attributes,
2223 if (parent->type() != QSvgStyleProperty::FONT) {
2227 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
2228 QSvgFont *font = style->svgFont();
2229 const QStringView name = attributes.value(QLatin1String(
"name"));
2231 if (!name.isEmpty())
2232 font->setFamilyName(name.toString());
2234 if (!font->familyName().isEmpty())
2235 if (!style->doc()->svgFont(font->familyName()))
2236 style->doc()->addSvgFont(font);
2242 const QXmlStreamAttributes &attributes,
2245 Q_UNUSED(parent); Q_UNUSED(attributes);
2250 const QXmlStreamAttributes &attributes,
2253 Q_UNUSED(parent); Q_UNUSED(attributes);
2258 const QXmlStreamAttributes &attributes,
2261 Q_UNUSED(parent); Q_UNUSED(attributes);
2266 const QXmlStreamAttributes &attributes,
2269 Q_UNUSED(attributes);
2270 QSvgG *node =
new QSvgG(parent);
2275 const QXmlStreamAttributes &attributes,
2278 if (parent->type() != QSvgStyleProperty::FONT) {
2282 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
2283 QSvgFont *font = style->svgFont();
2284 return createSvgGlyph(font, attributes,
false);
2288 const QXmlStreamAttributes &attributes,
2291 Q_UNUSED(parent); Q_UNUSED(attributes);
2296 const QXmlStreamAttributes &attributes,
2299 Q_UNUSED(parent); Q_UNUSED(attributes);
2304 const QXmlStreamAttributes &attributes,
2305 QSvgHandler *handler)
2307 const QStringView x = attributes.value(QLatin1String(
"x"));
2308 const QStringView y = attributes.value(QLatin1String(
"y"));
2309 const QStringView width = attributes.value(QLatin1String(
"width"));
2310 const QStringView height = attributes.value(QLatin1String(
"height"));
2311 QString filename = attributes.value(QLatin1String(
"xlink:href")).toString();
2313 filename = attributes.value(QLatin1String(
"href")).toString();
2314 qreal nx = QSvgUtils::toDouble(x);
2315 qreal ny = QSvgUtils::toDouble(y);
2317 qreal nwidth = QSvgUtils::parseLength(width.toString(), &type);
2318 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
2320 qreal nheight = QSvgUtils::parseLength(height.toString(), &type);
2321 nheight = QSvgUtils::convertToPixels(nheight,
false, type);
2323 filename = filename.trimmed();
2324 if (filename.isEmpty()) {
2325 qCWarning(lcSvgHandler) <<
"QSvgHandler: Image filename is empty";
2328 if (nwidth <= 0 || nheight <= 0) {
2329 qCWarning(lcSvgHandler) <<
"QSvgHandler: Width or height for" << filename <<
"image was not greater than 0";
2338 } filenameType = NotLoaded;
2340 if (filename.startsWith(QLatin1String(
"data"))) {
2343 if (qDecodeDataUrl(filename, mimeType, data)) {
2344 image = QImage::fromData(data);
2345 filenameType = LoadedFromData;
2349 if (image.isNull()) {
2350 const auto *file = qobject_cast<QFile *>(handler->device());
2353 if (url.isRelative()) {
2355 filename = info.absoluteDir().absoluteFilePath(filename);
2359 if (handler->trustedSourceMode() || !QImageReader::imageFormat(filename).startsWith(
"svg")) {
2360 image = QImage(filename);
2361 filenameType = LoadedFromFile;
2365 if (image.isNull()) {
2366 qCWarning(lcSvgHandler) <<
"Could not create image from" << filename;
2370 if (image.format() == QImage::Format_ARGB32)
2371 image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
2373 QSvgNode *img =
new QSvgImage(parent,
2375 filenameType == LoadedFromFile ? filename : QString{},
2384 const QXmlStreamAttributes &attributes,
2387 const QStringView x1 = attributes.value(QLatin1String(
"x1"));
2388 const QStringView y1 = attributes.value(QLatin1String(
"y1"));
2389 const QStringView x2 = attributes.value(QLatin1String(
"x2"));
2390 const QStringView y2 = attributes.value(QLatin1String(
"y2"));
2391 qreal nx1 = QSvgUtils::toDouble(x1);
2392 qreal ny1 = QSvgUtils::toDouble(y1);
2393 qreal nx2 = QSvgUtils::toDouble(x2);
2394 qreal ny2 = QSvgUtils::toDouble(y2);
2396 QLineF lineBounds(nx1, ny1, nx2, ny2);
2397 QSvgNode *line =
new QSvgLine(parent, lineBounds);
2403 const QXmlStreamAttributes &attributes,
2404 QSvgGradientStyle *gradProp,
2405 QSvgHandler *handler)
2407 const QStringView link = attributes.value(QLatin1String(
"xlink:href"));
2408 const QStringView trans = attributes.value(QLatin1String(
"gradientTransform"));
2409 const QStringView spread = attributes.value(QLatin1String(
"spreadMethod"));
2410 const QStringView units = attributes.value(QLatin1String(
"gradientUnits"));
2411 const QStringView colorStr = attributes.value(QLatin1String(
"color"));
2412 const QStringView colorOpacityStr = attributes.value(QLatin1String(
"color-opacity"));
2415 if (constructColor(colorStr, colorOpacityStr, color, handler)) {
2416 handler->popColor();
2417 handler->pushColor(color);
2421 QGradient *grad = gradProp->qgradient();
2422 if (node && !link.isEmpty()) {
2423 QSvgStyleProperty *prop = node->styleProperty(link);
2424 if (prop && prop->type() == QSvgStyleProperty::GRADIENT) {
2425 QSvgGradientStyle *inherited =
2426 static_cast<QSvgGradientStyle*>(prop);
2427 if (!inherited->stopLink().isEmpty()) {
2428 gradProp->setStopLink(inherited->stopLink(), handler->document());
2430 grad->setStops(inherited->qgradient()->stops());
2431 gradProp->setGradientStopsSet(inherited->gradientStopsSet());
2434 matrix = inherited->qtransform();
2436 gradProp->setStopLink(link.toString(), handler->document());
2440 if (!trans.isEmpty()) {
2441 matrix = parseTransformationMatrix(trans);
2442 gradProp->setTransform(matrix);
2443 }
else if (!matrix.isIdentity()) {
2444 gradProp->setTransform(matrix);
2447 if (!spread.isEmpty()) {
2448 if (spread == QLatin1String(
"pad")) {
2449 grad->setSpread(QGradient::PadSpread);
2450 }
else if (spread == QLatin1String(
"reflect")) {
2451 grad->setSpread(QGradient::ReflectSpread);
2452 }
else if (spread == QLatin1String(
"repeat")) {
2453 grad->setSpread(QGradient::RepeatSpread);
2457 if (units.isEmpty() || units == QLatin1String(
"objectBoundingBox")) {
2458 grad->setCoordinateMode(QGradient::ObjectMode);
2463 const QXmlStreamAttributes &attributes,
2464 QSvgHandler *handler)
2466 const QStringView x1 = attributes.value(QLatin1String(
"x1"));
2467 const QStringView y1 = attributes.value(QLatin1String(
"y1"));
2468 const QStringView x2 = attributes.value(QLatin1String(
"x2"));
2469 const QStringView y2 = attributes.value(QLatin1String(
"y2"));
2477 nx1 = convertToNumber(x1);
2479 ny1 = convertToNumber(y1);
2481 nx2 = convertToNumber(x2);
2483 ny2 = convertToNumber(y2);
2485 QSvgNode *itr = node;
2486 while (itr && itr->type() != QSvgNode::Doc) {
2487 itr = itr->parent();
2490 QLinearGradient *grad =
new QLinearGradient(nx1, ny1, nx2, ny2);
2491 grad->setInterpolationMode(QGradient::ComponentInterpolation);
2492 QSvgGradientStyle *prop =
new QSvgGradientStyle(grad);
2493 parseBaseGradient(node, attributes, prop, handler);
2499 const QXmlStreamAttributes &attributes,
2502 Q_UNUSED(parent); Q_UNUSED(attributes);
2507 const QXmlStreamAttributes &attributes,
2510 if (parent->type() != QSvgStyleProperty::FONT) {
2514 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
2515 QSvgFont *font = style->svgFont();
2516 return createSvgGlyph(font, attributes,
true);
2520 const QXmlStreamAttributes &attributes,
2523 Q_UNUSED(parent); Q_UNUSED(attributes);
2528 const QXmlStreamAttributes &attributes,
2531 Q_UNUSED(parent); Q_UNUSED(attributes);
2536 const QXmlStreamAttributes &,
2543 const QXmlStreamAttributes &attributes,
2544 QSvgHandler *handler)
2546 const QStringView x = attributes.value(QLatin1String(
"x"));
2547 const QStringView y = attributes.value(QLatin1String(
"y"));
2548 const QStringView width = attributes.value(QLatin1String(
"width"));
2549 const QStringView height = attributes.value(QLatin1String(
"height"));
2550 const QStringView mU = attributes.value(QLatin1String(
"maskUnits"));
2551 const QStringView mCU = attributes.value(QLatin1String(
"maskContentUnits"));
2553 QtSvg::
UnitTypes nmU = mU.contains(QLatin1String(
"userSpaceOnUse")) ?
2556 QtSvg::
UnitTypes nmCU = mCU.contains(QLatin1String(
"objectBoundingBox")) ?
2566 qreal nx = QSvgUtils::parseLength(x, &type, &ok);
2567 nx = QSvgUtils::convertToPixels(nx,
true, type);
2568 if (x.isEmpty() || !ok) {
2572 nx = nx / 100. * handler->document()->viewBox().width();
2577 qreal ny = QSvgUtils::parseLength(y, &type, &ok);
2578 ny = QSvgUtils::convertToPixels(ny,
true, type);
2579 if (y.isEmpty() || !ok) {
2583 ny = ny / 100. * handler->document()->viewBox().height();
2588 qreal nwidth = QSvgUtils::parseLength(width, &type, &ok);
2589 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
2590 if (width.isEmpty() || !ok) {
2594 nwidth = nwidth / 100. * handler->document()->viewBox().width();
2596 nwidth = nwidth / 100.;
2599 qreal nheight = QSvgUtils::parseLength(height, &type, &ok);
2600 nheight = QSvgUtils::convertToPixels(nheight,
true, type);
2601 if (height.isEmpty() || !ok) {
2605 nheight = nheight / 100. * handler->document()->viewBox().height();
2607 nheight = nheight / 100.;
2610 QRectF bounds(nx, ny, nwidth, nheight);
2611 if (bounds.isEmpty())
2614 QSvgNode *mask =
new QSvgMask(parent, QSvgRectF(bounds, nmUx, nmUy, nmUw, nmUh), nmCU);
2621 const QStringView xStr = attributes.value(QLatin1String(
"x"));
2622 const QStringView yStr = attributes.value(QLatin1String(
"y"));
2623 const QStringView widthStr = attributes.value(QLatin1String(
"width"));
2624 const QStringView heightStr = attributes.value(QLatin1String(
"height"));
2627 if (!xStr.isEmpty()) {
2629 x = QSvgUtils::parseLength(xStr, &type);
2631 x = QSvgUtils::convertToPixels(x,
true, type);
2641 if (!yStr.isEmpty()) {
2643 y = QSvgUtils::parseLength(yStr, &type);
2645 y = QSvgUtils::convertToPixels(y,
false, type);
2655 if (!widthStr.isEmpty()) {
2657 width = QSvgUtils::parseLength(widthStr, &type);
2659 width = QSvgUtils::convertToPixels(width,
true, type);
2666 rect->setWidth(width);
2669 if (!heightStr.isEmpty()) {
2671 height = QSvgUtils::parseLength(heightStr, &type);
2673 height = QSvgUtils::convertToPixels(height,
false, type);
2680 rect->setHeight(height);
2685 const QXmlStreamAttributes &attributes,
2686 QSvgHandler *handler)
2688 const QStringView fU = attributes.value(QLatin1String(
"filterUnits"));
2689 const QStringView pU = attributes.value(QLatin1String(
"primitiveUnits"));
2691 const QtSvg::
UnitTypes filterUnits = fU.contains(QLatin1String(
"userSpaceOnUse")) ?
2694 const QtSvg::
UnitTypes primitiveUnits = pU.contains(QLatin1String(
"objectBoundingBox")) ?
2702 qreal width = handler->document()->viewBox().width();
2703 qreal height = handler->document()->viewBox().height();
2704 rect = QSvgRectF(QRectF(-0.1 * width, -0.1 * height, 1.2 * width, 1.2 * height),
2705 QtSvg::UnitTypes::userSpaceOnUse, QtSvg::UnitTypes::userSpaceOnUse,
2706 QtSvg::UnitTypes::userSpaceOnUse, QtSvg::UnitTypes::userSpaceOnUse);
2708 rect = QSvgRectF(QRectF(-0.1, -0.1, 1.2, 1.2),
2709 QtSvg::UnitTypes::objectBoundingBox, QtSvg::UnitTypes::objectBoundingBox,
2710 QtSvg::UnitTypes::objectBoundingBox, QtSvg::UnitTypes::objectBoundingBox);
2713 parseFilterBounds(attributes, &rect);
2715 QSvgNode *filter =
new QSvgFilterContainer(parent, rect, filterUnits, primitiveUnits);
2720 QString *outString, QSvgRectF *rect)
2722 *inString = attributes.value(QLatin1String(
"in")).toString();
2723 *outString = attributes.value(QLatin1String(
"result")).toString();
2729 *rect = QSvgRectF(QRectF(0, 0, 1.0, 1.0),
2735 parseFilterBounds(attributes, rect);
2739 const QXmlStreamAttributes &attributes,
2742 const QStringView typeString = attributes.value(QLatin1String(
"type"));
2743 const QStringView valuesString = attributes.value(QLatin1String(
"values"));
2745 QString inputString;
2746 QString outputString;
2749 QSvgFeColorMatrix::ColorShiftType type;
2750 QSvgFeColorMatrix::Matrix values;
2753 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2755 if (typeString.startsWith(QLatin1String(
"saturate")))
2756 type = QSvgFeColorMatrix::ColorShiftType::Saturate;
2757 else if (typeString.startsWith(QLatin1String(
"hueRotate")))
2758 type = QSvgFeColorMatrix::ColorShiftType::HueRotate;
2759 else if (typeString.startsWith(QLatin1String(
"luminanceToAlpha")))
2760 type = QSvgFeColorMatrix::ColorShiftType::LuminanceToAlpha;
2762 type = QSvgFeColorMatrix::ColorShiftType::Matrix;
2764 if (!valuesString.isEmpty()) {
2765 const auto valueStringList = splitWithDelimiter(valuesString);
2766 for (
int i = 0, j = 0; i < qMin(20, valueStringList.size()); i++) {
2768 qreal v = QSvgUtils::toDouble(valueStringList.at(i), &ok);
2770 values.data()[j] = v;
2775 values.setToIdentity();
2778 QSvgNode *filter =
new QSvgFeColorMatrix(parent, inputString, outputString, rect,
2784 const QXmlStreamAttributes &attributes,
2787 const QStringView edgeModeString = attributes.value(QLatin1String(
"edgeMode"));
2788 const QStringView stdDeviationString = attributes.value(QLatin1String(
"stdDeviation"));
2790 QString inputString;
2791 QString outputString;
2794 QSvgFeGaussianBlur::EdgeMode edgemode = QSvgFeGaussianBlur::EdgeMode::Duplicate;
2796 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2797 qreal stdDeviationX = 0;
2798 qreal stdDeviationY = 0;
2800 stdDeviationX = qMax(0., QSvgUtils::toDouble(stdDeviationString.split(u" ").first()));
2801 stdDeviationY = qMax(0., QSvgUtils::toDouble(stdDeviationString.split(u" ").last()));
2803 stdDeviationY = stdDeviationX = qMax(0., QSvgUtils::toDouble(stdDeviationString));
2806 if (edgeModeString.startsWith(QLatin1String(
"wrap")))
2807 edgemode = QSvgFeGaussianBlur::EdgeMode::Wrap;
2808 else if (edgeModeString.startsWith(QLatin1String(
"none")))
2809 edgemode = QSvgFeGaussianBlur::EdgeMode::None;
2811 QSvgNode *filter =
new QSvgFeGaussianBlur(parent, inputString, outputString, rect,
2812 stdDeviationX, stdDeviationY, edgemode);
2817 const QXmlStreamAttributes &attributes,
2820 QStringView dxString = attributes.value(QLatin1String(
"dx"));
2821 QStringView dyString = attributes.value(QLatin1String(
"dy"));
2823 QString inputString;
2824 QString outputString;
2827 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2830 if (!dxString.isEmpty()) {
2832 dx = QSvgUtils::parseLength(dxString, &type);
2834 dx = QSvgUtils::convertToPixels(dx,
true, type);
2838 if (!dyString.isEmpty()) {
2840 dy = QSvgUtils::parseLength(dyString, &type);
2842 dy = QSvgUtils::convertToPixels(dy,
true, type);
2845 QSvgNode *filter =
new QSvgFeOffset(parent, inputString, outputString, rect,
2851 const QXmlStreamAttributes &attributes,
2854 const QStringView in2String = attributes.value(QLatin1String(
"in2"));
2855 const QStringView operatorString = attributes.value(QLatin1String(
"operator"));
2856 const QStringView k1String = attributes.value(QLatin1String(
"k1"));
2857 const QStringView k2String = attributes.value(QLatin1String(
"k2"));
2858 const QStringView k3String = attributes.value(QLatin1String(
"k3"));
2859 const QStringView k4String = attributes.value(QLatin1String(
"k4"));
2861 QString inputString;
2862 QString outputString;
2865 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2867 QSvgFeComposite::Operator op = QSvgFeComposite::Operator::Over;
2868 if (operatorString.startsWith(QLatin1String(
"in")))
2869 op = QSvgFeComposite::Operator::In;
2870 else if (operatorString.startsWith(QLatin1String(
"out")))
2871 op = QSvgFeComposite::Operator::Out;
2872 else if (operatorString.startsWith(QLatin1String(
"atop")))
2873 op = QSvgFeComposite::Operator::Atop;
2874 else if (operatorString.startsWith(QLatin1String(
"xor")))
2875 op = QSvgFeComposite::Operator::Xor;
2876 else if (operatorString.startsWith(QLatin1String(
"lighter")))
2877 op = QSvgFeComposite::Operator::Lighter;
2878 else if (operatorString.startsWith(QLatin1String(
"arithmetic")))
2879 op = QSvgFeComposite::Operator::Arithmetic;
2881 QVector4D k(0, 0, 0, 0);
2883 if (op == QSvgFeComposite::Operator::Arithmetic) {
2885 qreal v = QSvgUtils::toDouble(k1String, &ok);
2888 v = QSvgUtils::toDouble(k2String, &ok);
2891 v = QSvgUtils::toDouble(k3String, &ok);
2894 v = QSvgUtils::toDouble(k4String, &ok);
2899 QSvgNode *filter =
new QSvgFeComposite(parent, inputString, outputString, rect,
2900 in2String.toString(), op, k);
2906 const QXmlStreamAttributes &attributes,
2909 QString inputString;
2910 QString outputString;
2913 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2915 QSvgNode *filter =
new QSvgFeMerge(parent, inputString, outputString, rect);
2920 const QXmlStreamAttributes &attributes,
2921 QSvgHandler *handler)
2923 QStringView colorStr = attributes.value(QLatin1String(
"flood-color"));
2924 const QStringView opacityStr = attributes.value(QLatin1String(
"flood-opacity"));
2927 if (!constructColor(colorStr, opacityStr, color, handler)) {
2928 color = QColor(Qt::black);
2930 qreal op = qMin(qreal(1.0), qMax(qreal(0.0), QSvgUtils::toDouble(opacityStr, &ok)));
2932 color.setAlphaF(op);
2935 QString inputString;
2936 QString outputString;
2939 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2941 QSvgNode *filter =
new QSvgFeFlood(parent, inputString, outputString, rect, color);
2946 const QXmlStreamAttributes &attributes,
2949 QString inputString;
2950 QString outputString;
2953 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2955 QSvgNode *filter =
new QSvgFeMergeNode(parent, inputString, outputString, rect);
2960 const QXmlStreamAttributes &attributes,
2963 const QStringView in2String = attributes.value(QLatin1String(
"in2"));
2964 const QStringView modeString = attributes.value(QLatin1String(
"mode"));
2966 QString inputString;
2967 QString outputString;
2970 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2972 QSvgFeBlend::Mode mode = QSvgFeBlend::Mode::Normal;
2973 if (modeString.startsWith(QLatin1StringView(
"multiply")))
2974 mode = QSvgFeBlend::Mode::Multiply;
2975 else if (modeString.startsWith(QLatin1StringView(
"screen")))
2976 mode = QSvgFeBlend::Mode::Screen;
2977 else if (modeString.startsWith(QLatin1StringView(
"darken")))
2978 mode = QSvgFeBlend::Mode::Darken;
2979 else if (modeString.startsWith(QLatin1StringView(
"lighten")))
2980 mode = QSvgFeBlend::Mode::Lighten;
2982 QSvgNode *filter =
new QSvgFeBlend(parent, inputString, outputString, rect,
2983 in2String.toString(), mode);
2988 const QXmlStreamAttributes &attributes,
2991 QString inputString;
2992 QString outputString;
2995 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2997 QSvgNode *filter =
new QSvgFeUnsupported(parent, inputString, outputString, rect);
3003 QList<QStringView> viewBoxValues;
3006 viewBoxValues = splitWithDelimiter(str);
3007 if (viewBoxValues.size() == 4) {
3009 qreal x = QSvgUtils::parseLength(viewBoxValues.at(0).trimmed(), &type);
3010 qreal y = QSvgUtils::parseLength(viewBoxValues.at(1).trimmed(), &type);
3011 qreal w = QSvgUtils::parseLength(viewBoxValues.at(2).trimmed(), &type);
3012 qreal h = QSvgUtils::parseLength(viewBoxValues.at(3).trimmed(), &type);
3013 return QRectF(x, y, w, h);
3015 return std::nullopt;
3019 QRectF *rect, QRectF *viewBox, QPointF *refPoint,
3020 QSvgSymbolLike::PreserveAspectRatios *aspect,
3021 QSvgSymbolLike::Overflow *overflow,
3022 bool marker =
false)
3024 const QStringView xStr = attributes.value(QLatin1String(
"x"));
3025 const QStringView yStr = attributes.value(QLatin1String(
"y"));
3026 const QStringView refXStr = attributes.value(QLatin1String(
"refX"));
3027 const QStringView refYStr = attributes.value(QLatin1String(
"refY"));
3028 const QStringView widthStr = attributes.value(marker ? QLatin1String(
"markerWidth")
3029 : QLatin1String(
"width"));
3030 const QStringView heightStr = attributes.value(marker ? QLatin1String(
"markerHeight")
3031 : QLatin1String(
"height"));
3032 const QStringView pAspectRStr = attributes.value(QLatin1String(
"preserveAspectRatio"));
3033 const QStringView overflowStr = attributes.value(QLatin1String(
"overflow"));
3034 const QStringView viewBoxStr = attributes.value(QLatin1String(
"viewBox"));
3038 if (!xStr.isEmpty()) {
3040 x = QSvgUtils::parseLength(xStr, &type);
3042 x = QSvgUtils::convertToPixels(x,
true, type);
3045 if (!yStr.isEmpty()) {
3047 y = QSvgUtils::parseLength(yStr, &type);
3049 y = QSvgUtils::convertToPixels(y,
false, type);
3052 if (!widthStr.isEmpty()) {
3054 width = QSvgUtils::parseLength(widthStr, &type);
3056 width = QSvgUtils::convertToPixels(width,
true, type);
3059 if (!heightStr.isEmpty()) {
3061 height = QSvgUtils::parseLength(heightStr, &type);
3063 height = QSvgUtils::convertToPixels(height,
false, type);
3066 *rect = QRectF(x, y, width, height);
3069 if (!refXStr.isEmpty()) {
3071 x = QSvgUtils::parseLength(refXStr, &type);
3073 x = QSvgUtils::convertToPixels(x,
true, type);
3076 if (!refYStr.isEmpty()) {
3078 y = QSvgUtils::parseLength(refYStr, &type);
3080 y = QSvgUtils::convertToPixels(y,
false, type);
3082 *refPoint = QPointF(x,y);
3084 auto viewBoxResult = parseViewBox(viewBoxStr);
3086 *viewBox = *viewBoxResult;
3087 else if (width > 0 && height > 0)
3088 *viewBox = QRectF(0, 0, width, height);
3090 *viewBox = handler->document()->viewBox();
3092 if (viewBox->isNull())
3095 auto pAspectRStrs = pAspectRStr.split(u" ");
3096 QSvgSymbolLike::PreserveAspectRatio aspectX = QSvgSymbolLike::PreserveAspectRatio::xMid;
3097 QSvgSymbolLike::PreserveAspectRatio aspectY = QSvgSymbolLike::PreserveAspectRatio::yMid;
3098 QSvgSymbolLike::PreserveAspectRatio aspectMS = QSvgSymbolLike::PreserveAspectRatio::meet;
3100 for (
auto &pAStr : std::as_const(pAspectRStrs)) {
3101 if (pAStr.startsWith(QLatin1String(
"none"))) {
3102 aspectX = QSvgSymbolLike::PreserveAspectRatio::None;
3103 aspectY = QSvgSymbolLike::PreserveAspectRatio::None;
3105 if (pAStr.startsWith(QLatin1String(
"xMin")))
3106 aspectX = QSvgSymbolLike::PreserveAspectRatio::xMin;
3107 else if (pAStr.startsWith(QLatin1String(
"xMax")))
3108 aspectX = QSvgSymbolLike::PreserveAspectRatio::xMax;
3109 if (pAStr.endsWith(QLatin1String(
"YMin")))
3110 aspectY = QSvgSymbolLike::PreserveAspectRatio::yMin;
3111 else if (pAStr.endsWith(QLatin1String(
"YMax")))
3112 aspectY = QSvgSymbolLike::PreserveAspectRatio::yMax;
3115 if (pAStr.endsWith(QLatin1String(
"slice")))
3116 aspectMS = QSvgSymbolLike::PreserveAspectRatio::slice;
3118 *aspect = aspectX | aspectY | aspectMS;
3125 *overflow = QSvgSymbolLike::Overflow::Hidden;
3127 if (overflowStr.endsWith(QLatin1String(
"auto")))
3128 *overflow = QSvgSymbolLike::Overflow::Auto;
3129 else if (overflowStr.endsWith(QLatin1String(
"visible")))
3130 *overflow = QSvgSymbolLike::Overflow::Visible;
3131 else if (overflowStr.endsWith(QLatin1String(
"hidden")))
3132 *overflow = QSvgSymbolLike::Overflow::Hidden;
3133 else if (overflowStr.endsWith(QLatin1String(
"scroll")))
3134 *overflow = QSvgSymbolLike::Overflow::Scroll;
3140 const QXmlStreamAttributes &attributes,
3141 QSvgHandler *handler)
3143 QRectF rect, viewBox;
3145 QSvgSymbolLike::PreserveAspectRatios aspect;
3146 QSvgSymbolLike::Overflow overflow;
3148 if (!parseSymbolLikeAttributes(attributes, handler, &rect, &viewBox, &refP, &aspect, &overflow))
3151 refP = QPointF(0, 0);
3152 QSvgNode *symbol =
new QSvgSymbol(parent, rect, viewBox, refP, aspect, overflow);
3157 const QXmlStreamAttributes &attributes,
3158 QSvgHandler *handler)
3160 QRectF rect, viewBox;
3162 QSvgSymbolLike::PreserveAspectRatios aspect;
3163 QSvgSymbolLike::Overflow overflow;
3165 const QStringView orientStr = attributes.value(QLatin1String(
"orient"));
3166 const QStringView markerUnitsStr = attributes.value(QLatin1String(
"markerUnits"));
3168 qreal orientationAngle = 0;
3169 QSvgMarker::Orientation orientation;
3170 if (orientStr.startsWith(QLatin1String(
"auto-start-reverse")))
3171 orientation = QSvgMarker::Orientation::AutoStartReverse;
3172 else if (orientStr.startsWith(QLatin1String(
"auto")))
3173 orientation = QSvgMarker::Orientation::Auto;
3175 orientation = QSvgMarker::Orientation::Value;
3178 if (orientStr.endsWith(QLatin1String(
"turn")))
3179 a = 360. * QSvgUtils::toDouble(orientStr.mid(0, orientStr.length()-4), &ok);
3180 else if (orientStr.endsWith(QLatin1String(
"grad")))
3181 a = QSvgUtils::toDouble(orientStr.mid(0, orientStr.length()-4), &ok);
3182 else if (orientStr.endsWith(QLatin1String(
"rad")))
3183 a = 180. /
M_PI * QSvgUtils::toDouble(orientStr.mid(0, orientStr.length()-3), &ok);
3185 a = QSvgUtils::toDouble(orientStr, &ok);
3187 orientationAngle = a;
3190 QSvgMarker::MarkerUnits markerUnits = QSvgMarker::MarkerUnits::StrokeWidth;
3191 if (markerUnitsStr.startsWith(QLatin1String(
"userSpaceOnUse")))
3192 markerUnits = QSvgMarker::MarkerUnits::UserSpaceOnUse;
3194 if (!parseSymbolLikeAttributes(attributes, handler, &rect, &viewBox, &refP, &aspect, &overflow,
true))
3197 QSvgNode *marker =
new QSvgMarker(parent, rect, viewBox, refP, aspect, overflow,
3198 orientation, orientationAngle, markerUnits);
3203 const QXmlStreamAttributes &attributes,
3204 QSvgHandler *handler)
3206 QStringView data = attributes.value(QLatin1String(
"d"));
3209 qpath.setFillRule(Qt::WindingFill);
3210 if (!parsePathDataFast(data, qpath, !handler->trustedSourceMode()))
3211 qCWarning(lcSvgHandler,
"Invalid path data; path truncated.");
3213 QSvgNode *path =
new QSvgPath(parent, qpath);
3218 const QXmlStreamAttributes &attributes,
3221 const QString pointsStr = attributes.value(QLatin1String(
"points")).toString();
3222 const QChar *s = pointsStr.constData();
3223 const QList<qreal> points = parseNumbersList(s);
3224 if (points.size() < 4)
3227 for (
int i = 0; i < poly.size(); ++i)
3228 poly[i] = QPointF(points.at(2 * i), points.at(2 * i + 1));
3230 return new QSvgPolyline(parent, poly);
3232 return new QSvgPolygon(parent, poly);
3236 const QXmlStreamAttributes &attributes,
3239 return createPolyNode(parent, attributes,
false);
3243 const QXmlStreamAttributes &attributes,
3246 return createPolyNode(parent, attributes,
true);
3250 const QXmlStreamAttributes &attributes,
3253 Q_UNUSED(parent); Q_UNUSED(attributes);
3258 const QXmlStreamAttributes &attributes,
3259 QSvgHandler *handler)
3261 const QStringView cx = attributes.value(QLatin1String(
"cx"));
3262 const QStringView cy = attributes.value(QLatin1String(
"cy"));
3263 const QStringView r = attributes.value(QLatin1String(
"r"));
3264 const QStringView fx = attributes.value(QLatin1String(
"fx"));
3265 const QStringView fy = attributes.value(QLatin1String(
"fy"));
3270 ncx = convertToNumber(cx);
3272 ncy = convertToNumber(cy);
3276 nr = convertToNumber(r);
3282 nfx = convertToNumber(fx);
3285 nfy = convertToNumber(fy);
3287 QRadialGradient *grad =
new QRadialGradient(ncx, ncy, nr, nfx, nfy, 0);
3288 grad->setInterpolationMode(QGradient::ComponentInterpolation);
3290 QSvgGradientStyle *prop =
new QSvgGradientStyle(grad);
3291 parseBaseGradient(node, attributes, prop, handler);
3297 const QXmlStreamAttributes &attributes,
3300 const QStringView x = attributes.value(QLatin1String(
"x"));
3301 const QStringView y = attributes.value(QLatin1String(
"y"));
3302 const QStringView width = attributes.value(QLatin1String(
"width"));
3303 const QStringView height = attributes.value(QLatin1String(
"height"));
3304 const QStringView rx = attributes.value(QLatin1String(
"rx"));
3305 const QStringView ry = attributes.value(QLatin1String(
"ry"));
3309 qreal nwidth = QSvgUtils::parseLength(width, &type, &ok);
3312 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
3313 qreal nheight = QSvgUtils::parseLength(height, &type, &ok);
3316 nheight = QSvgUtils::convertToPixels(nheight,
true, type);
3317 qreal nrx = QSvgUtils::toDouble(rx);
3318 qreal nry = QSvgUtils::toDouble(ry);
3321 if (bounds.isEmpty())
3324 if (!rx.isEmpty() && ry.isEmpty())
3326 else if (!ry.isEmpty() && rx.isEmpty())
3332 if (nrx > bounds.width()/2)
3333 nrx = bounds.width()/2;
3334 if (nry > bounds.height()/2)
3335 nry = bounds.height()/2;
3340 nrx *= (100/(bounds.width()/2));
3341 nry *= (100/(bounds.height()/2));
3343 QSvgNode *rect =
new QSvgRect(parent, bounds, nrx, nry);
3348 const QXmlStreamAttributes &attributes,
3351 Q_UNUSED(parent); Q_UNUSED(attributes);
3356 const QXmlStreamAttributes &attributes,
3359 Q_UNUSED(parent); Q_UNUSED(attributes);
3364 const QXmlStreamAttributes &attributes,
3365 QSvgHandler *handler)
3367 Q_UNUSED(parent); Q_UNUSED(attributes);
3368 QStringView solidColorStr = attributes.value(QLatin1String(
"solid-color"));
3369 QStringView solidOpacityStr = attributes.value(QLatin1String(
"solid-opacity"));
3371 if (solidOpacityStr.isEmpty())
3372 solidOpacityStr = attributes.value(QLatin1String(
"opacity"));
3375 if (!constructColor(solidColorStr, solidOpacityStr, color, handler))
3377 QSvgSolidColorStyle *style =
new QSvgSolidColorStyle(color);
3382 const QXmlStreamAttributes &attributes,
3383 QSvgHandler *handler)
3385 if (parent->type() != QSvgStyleProperty::GRADIENT)
3387 QString nodeIdStr = someId(attributes);
3388 QString xmlClassStr = attributes.value(QLatin1String(
"class")).toString();
3394 QSvgDummyNode dummy;
3395 dummy.setNodeId(nodeIdStr);
3396 dummy.setXmlClass(xmlClassStr);
3398 QSvgAttributes attrs(attributes, handler);
3400#ifndef QT_NO_CSSPARSER
3401 QXmlStreamAttributes cssAttributes;
3402 handler->cssHandler().styleLookup(&dummy, cssAttributes);
3403 attrs.setAttributes(cssAttributes, handler);
3405 QXmlStreamAttributes styleCssAttributes;
3406 QStringView style = attributes.value(QLatin1String(
"style"));
3407 if (!style.isEmpty())
3408 handler->cssHandler().parseCSStoXMLAttrs(style.toString(), styleCssAttributes);
3409 attrs.setAttributes(styleCssAttributes, handler);
3412 QSvgGradientStyle *gradientStyle =
3413 static_cast<QSvgGradientStyle*>(parent);
3414 QStringView colorStr = attrs.stopColor;
3418 qreal offset = convertToNumber(attrs.offset, &ok);
3421 QString black = QString::fromLatin1(
"#000000");
3422 if (colorStr.isEmpty()) {
3426 constructColor(colorStr, attrs.stopOpacity, color, handler);
3428 QGradient *grad = gradientStyle->qgradient();
3430 offset = qMin(qreal(1), qMax(qreal(0), offset));
3431 QGradientStops stops;
3432 if (gradientStyle->gradientStopsSet()) {
3433 stops = grad->stops();
3435 if (offset <= stops.back().first)
3436 offset = stops.back().first + FLT_EPSILON;
3441 if ((stops.size() == 1) || (stops.at(stops.size() - 2).first < 1.0 - FLT_EPSILON)) {
3442 stops.back().first = 1.0 - FLT_EPSILON;
3443 grad->setStops(stops);
3448 grad->setColorAt(offset, color);
3449 gradientStyle->setGradientStopsSet(
true);
3454 const QXmlStreamAttributes &attributes,
3455 QSvgHandler *handler)
3458#ifdef QT_NO_CSSPARSER
3459 Q_UNUSED(attributes);
3462 const QStringView type = attributes.value(QLatin1String(
"type"));
3463 if (type.compare(QLatin1String(
"text/css"), Qt::CaseInsensitive) == 0 || type.isNull())
3464 handler->setInStyle(
true);
3471 const QXmlStreamAttributes &attributes,
3472 QSvgHandler *handler)
3474 Q_UNUSED(parent); Q_UNUSED(attributes);
3476 QSvgTinyDocument *node =
new QSvgTinyDocument(handler->options(), handler->animatorType());
3477 const QStringView widthStr = attributes.value(QLatin1String(
"width"));
3478 const QStringView heightStr = attributes.value(QLatin1String(
"height"));
3479 const QStringView viewBoxStr = attributes.value(QLatin1String(
"viewBox"));
3483 if (!widthStr.isEmpty()) {
3484 width = QSvgUtils::parseLength(widthStr, &type);
3486 width = QSvgUtils::convertToPixels(width,
true, type);
3490 if (!heightStr.isEmpty()) {
3491 height = QSvgUtils::parseLength(heightStr, &type);
3493 height = QSvgUtils::convertToPixels(height,
false, type);
3497 auto viewBoxResult = parseViewBox(viewBoxStr);
3498 if (viewBoxResult) {
3499 node->setViewBox(*viewBoxResult);
3500 }
else if (width && height) {
3502 width = QSvgUtils::convertToPixels(width,
false, type);
3503 height = QSvgUtils::convertToPixels(height,
false, type);
3505 node->setViewBox(QRectF(0, 0, width, height));
3513 const QXmlStreamAttributes &attributes,
3516 Q_UNUSED(attributes);
3517 QSvgSwitch *node =
new QSvgSwitch(parent);
3522 const QXmlStreamAttributes &attributes,
3523 QSvgHandler *handler)
3525 const QStringView x = attributes.value(QLatin1String(
"x"));
3526 const QStringView y = attributes.value(QLatin1String(
"y"));
3527 const QStringView width = attributes.value(QLatin1String(
"width"));
3528 const QStringView height = attributes.value(QLatin1String(
"height"));
3529 const QStringView patternUnits = attributes.value(QLatin1String(
"patternUnits"));
3530 const QStringView patternContentUnits = attributes.value(QLatin1String(
"patternContentUnits"));
3531 const QStringView patternTransform = attributes.value(QLatin1String(
"patternTransform"));
3533 QtSvg::
UnitTypes nPatternUnits = patternUnits.contains(QLatin1String(
"userSpaceOnUse")) ?
3536 QtSvg::
UnitTypes nPatternContentUnits = patternContentUnits.contains(QLatin1String(
"objectBoundingBox")) ?
3539 const QStringView viewBoxStr = attributes.value(QLatin1String(
"viewBox"));
3544 qreal nx = QSvgUtils::parseLength(x, &type, &ok);
3545 nx = QSvgUtils::convertToPixels(nx,
true, type);
3549 nx = (nx / 100.) * handler->document()->viewBox().width();
3553 qreal ny = QSvgUtils::parseLength(y, &type, &ok);
3554 ny = QSvgUtils::convertToPixels(ny,
true, type);
3558 ny = (ny / 100.) * handler->document()->viewBox().height();
3562 qreal nwidth = QSvgUtils::parseLength(width, &type, &ok);
3563 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
3567 nwidth = (nwidth / 100.) * handler->document()->viewBox().width();
3569 nwidth = nwidth / 100.;
3571 qreal nheight = QSvgUtils::parseLength(height, &type, &ok);
3572 nheight = QSvgUtils::convertToPixels(nheight,
true, type);
3576 nheight = (nheight / 100.) * handler->document()->viewBox().height();
3578 nheight = nheight / 100.;
3581 auto viewBoxResult = parseViewBox(viewBoxStr);
3582 if (viewBoxResult) {
3583 if (viewBoxResult->width() > 0 && viewBoxResult->height() > 0)
3584 viewBox = *viewBoxResult;
3588 if (!patternTransform.isEmpty())
3589 matrix = parseTransformationMatrix(patternTransform);
3591 QRectF bounds(nx, ny, nwidth, nheight);
3592 if (bounds.isEmpty())
3595 QSvgRectF patternRectF(bounds, nPatternUnits, nPatternUnits, nPatternUnits, nPatternUnits);
3596 QSvgPattern *node =
new QSvgPattern(parent, patternRectF, viewBox, nPatternContentUnits, matrix);
3599 QSvgPatternStyle *prop =
new QSvgPatternStyle(node);
3600 node->appendStyleProperty(prop, someId(attributes));
3606 const QXmlStreamAttributes &,
3609 if (parent->type() != QSvgNode::Textarea)
3611 static_cast<QSvgText*>(parent)->addLineBreak();
3616 const QXmlStreamAttributes &attributes,
3619 const QStringView x = attributes.value(QLatin1String(
"x"));
3620 const QStringView y = attributes.value(QLatin1String(
"y"));
3623 qreal nx = QSvgUtils::parseLength(x, &type);
3624 nx = QSvgUtils::convertToPixels(nx,
true, type);
3625 qreal ny = QSvgUtils::parseLength(y, &type);
3626 ny = QSvgUtils::convertToPixels(ny,
true, type);
3628 QSvgNode *text =
new QSvgText(parent, QPointF(nx, ny));
3633 const QXmlStreamAttributes &attributes,
3634 QSvgHandler *handler)
3636 QSvgText *node =
static_cast<QSvgText *>(createTextNode(parent, attributes, handler));
3639 qreal width = QSvgUtils::parseLength(attributes.value(QLatin1String(
"width")), &type);
3640 qreal height = QSvgUtils::parseLength(attributes.value(QLatin1String(
"height")), &type);
3641 node->setTextArea(QSizeF(width, height));
3647 const QXmlStreamAttributes &,
3650 return new QSvgTspan(parent);
3654 const QXmlStreamAttributes &attributes,
3657 QStringView linkId = attributes.value(QLatin1String(
"xlink:href"));
3658 const QStringView xStr = attributes.value(QLatin1String(
"x"));
3659 const QStringView yStr = attributes.value(QLatin1String(
"y"));
3660 QSvgStructureNode *group =
nullptr;
3662 if (linkId.isEmpty())
3663 linkId = attributes.value(QLatin1String(
"href"));
3664 QString linkIdStr = linkId.mid(1).toString();
3666 switch (parent->type()) {
3668 case QSvgNode::Defs:
3669 case QSvgNode::Group:
3670 case QSvgNode::Switch:
3671 case QSvgNode::Mask:
3672 group =
static_cast<QSvgStructureNode*>(parent);
3680 if (!xStr.isNull() || !yStr.isNull()) {
3682 qreal nx = QSvgUtils::parseLength(xStr, &type);
3683 nx = QSvgUtils::convertToPixels(nx,
true, type);
3685 qreal ny = QSvgUtils::parseLength(yStr, &type);
3686 ny = QSvgUtils::convertToPixels(ny,
true, type);
3687 pt = QPointF(nx, ny);
3690 QSvgNode *link = group->scopeNode(linkIdStr);
3692 if (parent->isDescendantOf(link))
3693 qCWarning(lcSvgHandler,
"link %ls is recursive!",
qUtf16Printable(linkIdStr));
3695 return new QSvgUse(pt, parent, link);
3699 return new QSvgUse(pt, parent, linkIdStr);
3702 qCWarning(lcSvgHandler,
"<use> element %ls in wrong context!",
qUtf16Printable(linkIdStr));
3707 const QXmlStreamAttributes &attributes,
3710 Q_UNUSED(parent); Q_UNUSED(attributes);
3714typedef QSvgNode *(*FactoryMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3721 QStringView ref = name.mid(1);
3722 switch (name.at(0).unicode()) {
3724 if (ref == QLatin1String(
"efs"))
return createDefsNode;
3730 if (ref.isEmpty())
return createGNode;
3737 if (ref == QLatin1String(
"vg"))
return createSvgNode;
3738 if (ref == QLatin1String(
"witch"))
return createSwitchNode;
3756 QStringView ref = name.mid(1);
3757 switch (name.at(0).unicode()) {
3759 if (ref == QLatin1String(
"ircle"))
return createCircleNode;
3762 if (ref == QLatin1String(
"llipse"))
return createEllipseNode;
3765 if (ref == QLatin1String(
"mage"))
return createImageNode;
3768 if (ref == QLatin1String(
"ine"))
return createLineNode;
3771 if (ref == QLatin1String(
"ath"))
return createPathNode;
3772 if (ref == QLatin1String(
"olygon"))
return createPolygonNode;
3773 if (ref == QLatin1String(
"olyline"))
return createPolylineNode;
3776 if (ref == QLatin1String(
"ect"))
return createRectNode;
3779 if (ref == QLatin1String(
"ext"))
return createTextNode;
3780 if (ref == QLatin1String(
"extArea"))
return createTextAreaNode;
3781 if (ref == QLatin1String(
"span"))
return createTspanNode;
3784 if (ref == QLatin1String(
"se"))
return createUseNode;
3787 if (ref == QLatin1String(
"ideo"))
return createVideoNode;
3803 if (!name.startsWith(QLatin1String(
"fe")))
3806 if (name == QLatin1String(
"feMerge"))
return createFeMergeNode;
3807 if (name == QLatin1String(
"feColorMatrix"))
return createFeColorMatrixNode;
3808 if (name == QLatin1String(
"feGaussianBlur"))
return createFeGaussianBlurNode;
3809 if (name == QLatin1String(
"feOffset"))
return createFeOffsetNode;
3810 if (name == QLatin1String(
"feMergeNode"))
return createFeMergeNodeNode;
3811 if (name == QLatin1String(
"feComposite"))
return createFeCompositeNode;
3812 if (name == QLatin1String(
"feFlood"))
return createFeFloodNode;
3813 if (name == QLatin1String(
"feBlend"))
return createFeBlendNode;
3815 static const QStringList unsupportedFilters = {
3832 if (unsupportedFilters.contains(name))
3833 return createFeUnsupportedNode;
3838typedef QSvgNode *(*AnimationMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3845 QStringView ref = name.mid(1);
3847 switch (name.at(0).unicode()) {
3849 if (ref == QLatin1String(
"nimate"))
return createAnimateNode;
3850 if (ref == QLatin1String(
"nimateColor"))
return createAnimateColorNode;
3851 if (ref == QLatin1String(
"nimateMotion"))
return createAimateMotionNode;
3852 if (ref == QLatin1String(
"nimateTransform"))
return createAnimateTransformNode;
3861typedef bool (*
ParseMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3868 QStringView ref = name.mid(1);
3869 switch (name.at(0).unicode()) {
3871 if (ref.isEmpty())
return parseAnchorNode;
3872 if (ref == QLatin1String(
"udio"))
return parseAudioNode;
3875 if (ref == QLatin1String(
"iscard"))
return parseDiscardNode;
3878 if (ref == QLatin1String(
"oreignObject"))
return parseForeignObjectNode;
3881 if (ref == QLatin1String(
"andler"))
return parseHandlerNode;
3882 if (ref == QLatin1String(
"kern"))
return parseHkernNode;
3885 if (ref == QLatin1String(
"etadata"))
return parseMetadataNode;
3886 if (ref == QLatin1String(
"path"))
return parseMpathNode;
3891 if (ref == QLatin1String(
"refetch"))
return parsePrefetchNode;
3894 if (ref == QLatin1String(
"cript"))
return parseScriptNode;
3895 if (ref == QLatin1String(
"et"))
return parseSetNode;
3896 if (ref == QLatin1String(
"tyle"))
return parseStyleNode;
3899 if (ref == QLatin1String(
"break"))
return parseTbreakNode;
3908 const QXmlStreamAttributes &,
3916 QStringView ref = name.mid(1);
3917 switch (name.at(0).unicode()) {
3919 if (ref == QLatin1String(
"ont"))
return createFontNode;
3922 if (ref == QLatin1String(
"inearGradient"))
return createLinearGradientNode;
3925 if (ref == QLatin1String(
"adialGradient"))
return createRadialGradientNode;
3928 if (ref == QLatin1String(
"olidColor"))
return createSolidColorNode;
3937 const QXmlStreamAttributes &,
3945 QStringView ref = name.mid(1);
3946 switch (name.at(0).unicode()) {
3968QSvgHandler::QSvgHandler(QIODevice *device, QtSvg::Options options,
3969 QtSvg::AnimatorType type)
3970 : xml(
new QXmlStreamReader(device))
3971 , m_ownsReader(
true)
3972 , m_options(options)
3973 , m_animatorType(type)
3978QSvgHandler::QSvgHandler(
const QByteArray &data, QtSvg::Options options,
3979 QtSvg::AnimatorType type)
3980 : xml(
new QXmlStreamReader(data))
3981 , m_ownsReader(
true)
3982 , m_options(options)
3983 , m_animatorType(type)
3988QSvgHandler::QSvgHandler(QXmlStreamReader *
const reader, QtSvg::Options options,
3989 QtSvg::AnimatorType type)
3991 , m_ownsReader(
false)
3992 , m_options(options)
3993 , m_animatorType(type)
3998void QSvgHandler::init()
4003 m_defaultCoords = QSvgUtils::LT_PX;
4004 m_defaultPen = QPen(Qt::black, 1, Qt::SolidLine, Qt::FlatCap, Qt::SvgMiterJoin);
4005 m_defaultPen.setMiterLimit(4);
4011 QSvgFillStyle *fillStyle =
static_cast<QSvgFillStyle*>
4012 (node->styleProperty(QSvgStyleProperty::FILL));
4013 if (fillStyle && fillStyle->style() && fillStyle->style()->type() == QSvgStyleProperty::PATTERN) {
4014 QSvgPatternStyle *patternStyle =
static_cast<QSvgPatternStyle *>(fillStyle->style());
4015 if (active.contains(patternStyle->patternNode()))
4019 QSvgStrokeStyle *strokeStyle =
static_cast<QSvgStrokeStyle*>
4020 (node->styleProperty(QSvgStyleProperty::STROKE));
4021 if (strokeStyle && strokeStyle->style() && strokeStyle->style()->type() == QSvgStyleProperty::PATTERN) {
4022 QSvgPatternStyle *patternStyle =
static_cast<QSvgPatternStyle *>(strokeStyle->style());
4023 if (active.contains(patternStyle->patternNode()))
4032 if (Q_UNLIKELY(!node))
4034 switch (node->type()) {
4036 case QSvgNode::Group:
4037 case QSvgNode::Defs:
4038 case QSvgNode::Pattern:
4040 if (node->type() == QSvgNode::Pattern)
4041 active.append(node);
4043 auto *g =
static_cast<
const QSvgStructureNode*>(node);
4044 for (
auto *r : g->renderers()) {
4045 if (detectCycles(r, active))
4052 if (active.contains(node))
4055 auto *u =
static_cast<
const QSvgUse*>(node);
4056 auto *target = u->link();
4059 if (detectCycles(target, active))
4064 case QSvgNode::Rect:
4065 case QSvgNode::Ellipse:
4066 case QSvgNode::Circle:
4067 case QSvgNode::Line:
4068 case QSvgNode::Path:
4069 case QSvgNode::Polygon:
4070 case QSvgNode::Polyline:
4071 case QSvgNode::Tspan:
4072 if (detectPatternCycles(node, active))
4082 const bool cycleFound = detectCycles(node);
4084 qCWarning(lcSvgHandler,
"Cycles detected in SVG, document discarded.");
4092void QSvgHandler::parse()
4094 xml->setNamespaceProcessing(
false);
4095#ifndef QT_NO_CSSPARSER
4099 int remainingUnfinishedElements = unfinishedElementsLimit;
4100 while (!xml->atEnd() && !done) {
4101 switch (xml->readNext()) {
4102 case QXmlStreamReader::StartElement:
4111 if (remainingUnfinishedElements && startElement(xml->name(), xml->attributes())) {
4112 --remainingUnfinishedElements;
4119 case QXmlStreamReader::EndElement:
4120 done = endElement(xml->name());
4121 ++remainingUnfinishedElements;
4123 case QXmlStreamReader::Characters:
4124 characters(xml->text());
4126 case QXmlStreamReader::ProcessingInstruction:
4127 processingInstruction(xml->processingInstructionTarget(), xml->processingInstructionData());
4133 resolvePaintServers(m_doc);
4135 if (detectCyclesAndWarn(m_doc)) {
4141bool QSvgHandler::startElement(
const QStringView localName,
4142 const QXmlStreamAttributes &attributes)
4144 QSvgNode *node =
nullptr;
4149
4150
4151 const QStringView xmlSpace(attributes.value(QLatin1String(
"xml:space")));
4152 if (xmlSpace.isNull()) {
4154 m_whitespaceMode.push(m_whitespaceMode.isEmpty() ? QSvgText::Default : m_whitespaceMode.top());
4155 }
else if (xmlSpace == QLatin1String(
"preserve")) {
4156 m_whitespaceMode.push(QSvgText::Preserve);
4157 }
else if (xmlSpace == QLatin1String(
"default")) {
4158 m_whitespaceMode.push(QSvgText::Default);
4160 const QByteArray msg =
'"' + xmlSpace.toLocal8Bit()
4161 +
"\" is an invalid value for attribute xml:space. "
4162 "Valid values are \"preserve\" and \"default\".";
4163 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
4164 m_whitespaceMode.push(QSvgText::Default);
4167 if (!m_doc && localName != QLatin1String(
"svg"))
4170 if (m_doc && localName == QLatin1String(
"svg")) {
4171 m_skipNodes.push(Doc);
4172 qCWarning(lcSvgHandler) <<
"Skipping a nested svg element, because "
4173 "SVG Document must not contain nested svg elements in Svg Tiny 1.2";
4176 if (!m_skipNodes.isEmpty() && m_skipNodes.top() == Doc)
4179 if (FactoryMethod method = findGroupFactory(localName, options())) {
4182 node = method(
nullptr, attributes,
this);
4184 Q_ASSERT(node->type() == QSvgNode::Doc);
4185 m_doc =
static_cast<QSvgTinyDocument*>(node);
4188 switch (m_nodes.top()->type()) {
4190 case QSvgNode::Group:
4191 case QSvgNode::Defs:
4192 case QSvgNode::Switch:
4193 case QSvgNode::Mask:
4194 case QSvgNode::Symbol:
4195 case QSvgNode::Marker:
4196 case QSvgNode::Pattern:
4198 node = method(m_nodes.top(), attributes,
this);
4200 QSvgStructureNode *group =
4201 static_cast<QSvgStructureNode*>(m_nodes.top());
4202 group->addChild(node, someId(attributes));
4207 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
4208 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
4214 parseCoreNode(node, attributes);
4215 parseStyle(node, attributes,
this);
4216 if (node->type() == QSvgNode::Filter)
4217 m_toBeResolved.append(node);
4219 }
else if (FactoryMethod method = findGraphicsFactory(localName, options())) {
4221 Q_ASSERT(!m_nodes.isEmpty());
4222 switch (m_nodes.top()->type()) {
4224 case QSvgNode::Group:
4225 case QSvgNode::Defs:
4226 case QSvgNode::Switch:
4227 case QSvgNode::Mask:
4228 case QSvgNode::Symbol:
4229 case QSvgNode::Marker:
4230 case QSvgNode::Pattern:
4232 if (localName == QLatin1String(
"tspan")) {
4233 const QByteArray msg = QByteArrayLiteral(
"\'tspan\' element in wrong context.");
4234 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
4237 node = method(m_nodes.top(), attributes,
this);
4239 QSvgStructureNode *group =
4240 static_cast<QSvgStructureNode*>(m_nodes.top());
4241 group->addChild(node, someId(attributes));
4245 case QSvgNode::Text:
4246 case QSvgNode::Textarea:
4247 if (localName == QLatin1String(
"tspan")) {
4248 node = method(m_nodes.top(), attributes,
this);
4250 static_cast<QSvgText *>(m_nodes.top())->addTspan(
static_cast<QSvgTspan *>(node));
4253 const QByteArray msg = QByteArrayLiteral(
"\'text\' or \'textArea\' element contains invalid element type.");
4254 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
4258 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
4259 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
4264 parseCoreNode(node, attributes);
4265 parseStyle(node, attributes,
this);
4266 if (node->type() == QSvgNode::Text || node->type() == QSvgNode::Textarea) {
4267 static_cast<QSvgText *>(node)->setWhitespaceMode(m_whitespaceMode.top());
4268 }
else if (node->type() == QSvgNode::Tspan) {
4269 static_cast<QSvgTspan *>(node)->setWhitespaceMode(m_whitespaceMode.top());
4270 }
else if (node->type() == QSvgNode::Use) {
4271 auto useNode =
static_cast<QSvgUse *>(node);
4272 if (!useNode->isResolved())
4273 m_toBeResolved.append(useNode);
4276 }
else if (FactoryMethod method = findFilterFactory(localName, options())) {
4278 Q_ASSERT(!m_nodes.isEmpty());
4279 if (m_nodes.top()->type() == QSvgNode::Filter ||
4280 (m_nodes.top()->type() == QSvgNode::FeMerge && localName == QLatin1String(
"feMergeNode"))) {
4281 node = method(m_nodes.top(), attributes,
this);
4283 QSvgStructureNode *container =
4284 static_cast<QSvgStructureNode*>(m_nodes.top());
4285 container->addChild(node, someId(attributes));
4288 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
4289 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
4291 }
else if (AnimationMethod method = findAnimationFactory(localName, options())) {
4292 Q_ASSERT(!m_nodes.isEmpty());
4293 node = method(m_nodes.top(), attributes,
this);
4295 QSvgAnimateNode *anim =
static_cast<QSvgAnimateNode *>(node);
4296 if (anim->linkId().isEmpty())
4297 m_doc->animator()->appendAnimation(m_nodes.top(), anim);
4298 else if (m_doc->namedNode(anim->linkId()))
4299 m_doc->animator()->appendAnimation(m_doc->namedNode(anim->linkId()), anim);
4301 m_toBeResolved.append(anim);
4303 }
else if (ParseMethod method = findUtilFactory(localName, options())) {
4304 Q_ASSERT(!m_nodes.isEmpty());
4305 if (!method(m_nodes.top(), attributes,
this))
4306 qCWarning(lcSvgHandler,
"%s", msgProblemParsing(localName, xml).constData());
4307 }
else if (StyleFactoryMethod method = findStyleFactoryMethod(localName)) {
4308 QSvgStyleProperty *prop = method(m_nodes.top(), attributes,
this);
4311 m_nodes.top()->appendStyleProperty(prop, someId(attributes));
4313 const QByteArray msg = QByteArrayLiteral(
"Could not parse node: ") + localName.toLocal8Bit();
4314 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
4316 }
else if (StyleParseMethod method = findStyleUtilFactoryMethod(localName)) {
4318 if (!method(m_style, attributes,
this))
4319 qCWarning(lcSvgHandler,
"%s", msgProblemParsing(localName, xml).constData());
4322 qCDebug(lcSvgHandler) <<
"Skipping unknown element" << localName;
4323 m_skipNodes.push(Unknown);
4329 m_skipNodes.push(Graphics);
4332 m_skipNodes.push(Style);
4337bool QSvgHandler::endElement(
const QStringView localName)
4339 CurrentNode node = m_skipNodes.top();
4341 if (node == Doc && localName != QLatin1String(
"svg"))
4345 m_whitespaceMode.pop();
4349 if (node == Unknown)
4352#ifdef QT_NO_CSSPARSER
4353 Q_UNUSED(localName);
4355 if (m_inStyle && localName == QLatin1String(
"style"))
4359 if (node == Graphics)
4361 else if (m_style && !m_skipNodes.isEmpty() && m_skipNodes.top() != Style)
4364 return ((localName == QLatin1String(
"svg")) && (node != Doc));
4367void QSvgHandler::resolvePaintServers(QSvgNode *node,
int nestedDepth)
4369 if (!node || (node->type() != QSvgNode::Doc && node->type() != QSvgNode::Group
4370 && node->type() != QSvgNode::Defs && node->type() != QSvgNode::Switch)) {
4374 QSvgStructureNode *structureNode =
static_cast<QSvgStructureNode *>(node);
4376 const QList<QSvgNode *> ren = structureNode->renderers();
4377 for (
auto it = ren.begin(); it != ren.end(); ++it) {
4378 QSvgFillStyle *fill =
static_cast<QSvgFillStyle *>((*it)->styleProperty(QSvgStyleProperty::FILL));
4379 if (fill && !fill->isPaintStyleResolved()) {
4380 QString id = fill->paintStyleId();
4381 QSvgPaintStyleProperty *style = structureNode->styleProperty(id);
4383 fill->setFillStyle(style);
4385 qCWarning(lcSvgHandler,
"%s", msgCouldNotResolveProperty(id, xml).constData());
4386 fill->setBrush(Qt::NoBrush);
4390 QSvgStrokeStyle *stroke =
static_cast<QSvgStrokeStyle *>((*it)->styleProperty(QSvgStyleProperty::STROKE));
4391 if (stroke && !stroke->isPaintStyleResolved()) {
4392 QString id = stroke->paintStyleId();
4393 QSvgPaintStyleProperty *style = structureNode->styleProperty(id);
4395 stroke->setStyle(style);
4397 qCWarning(lcSvgHandler,
"%s", msgCouldNotResolveProperty(id, xml).constData());
4398 stroke->setStroke(Qt::NoBrush);
4402 if (nestedDepth < 2048)
4403 resolvePaintServers(*it, nestedDepth + 1);
4407void QSvgHandler::resolveNodes()
4409 for (QSvgNode *node : std::as_const(m_toBeResolved)) {
4410 if (node->type() == QSvgNode::Use) {
4411 QSvgUse *useNode =
static_cast<QSvgUse *>(node);
4412 const auto parent = useNode->parent();
4416 QSvgNode::Type t = parent->type();
4417 if (t != QSvgNode::Doc && t != QSvgNode::Defs && t != QSvgNode::Group && t != QSvgNode::Switch)
4420 QSvgStructureNode *group =
static_cast<QSvgStructureNode *>(parent);
4421 QSvgNode *link = group->scopeNode(useNode->linkId());
4423 qCWarning(lcSvgHandler,
"link #%s is undefined!",
qPrintable(useNode->linkId()));
4427 if (useNode->parent()->isDescendantOf(link))
4428 qCWarning(lcSvgHandler,
"link #%s is recursive!",
qPrintable(useNode->linkId()));
4430 useNode->setLink(link);
4431 }
else if (node->type() == QSvgNode::Filter) {
4432 QSvgFilterContainer *filter =
static_cast<QSvgFilterContainer *>(node);
4433 for (
const QSvgNode *renderer : filter->renderers()) {
4434 const QSvgFeFilterPrimitive *primitive = QSvgFeFilterPrimitive::castToFilterPrimitive(renderer);
4435 if (!primitive || primitive->type() == QSvgNode::FeUnsupported) {
4436 filter->setSupported(
false);
4440 }
else if (node->type() == QSvgNode::AnimateTransform || node->type() == QSvgNode::AnimateColor) {
4441 QSvgAnimateNode *anim =
static_cast<QSvgAnimateNode *>(node);
4442 QSvgNode *targetNode = m_doc->namedNode(anim->linkId());
4444 m_doc->animator()->appendAnimation(targetNode, anim);
4447 m_toBeResolved.clear();
4450bool QSvgHandler::characters(
const QStringView str)
4452#ifndef QT_NO_CSSPARSER
4454 m_cssHandler.parseStyleSheet(str);
4458 if (m_skipNodes.isEmpty() || m_skipNodes.top() == Unknown || m_nodes.isEmpty())
4461 if (m_nodes.top()->type() == QSvgNode::Text || m_nodes.top()->type() == QSvgNode::Textarea) {
4462 static_cast<QSvgText*>(m_nodes.top())->addText(str);
4463 }
else if (m_nodes.top()->type() == QSvgNode::Tspan) {
4464 static_cast<QSvgTspan*>(m_nodes.top())->addText(str);
4470QIODevice *QSvgHandler::device()
const
4472 return xml->device();
4475QSvgTinyDocument *QSvgHandler::document()
const
4480QSvgUtils::LengthType QSvgHandler::defaultCoordinateSystem()
const
4482 return m_defaultCoords;
4485void QSvgHandler::setDefaultCoordinateSystem(QSvgUtils::LengthType type)
4487 m_defaultCoords = type;
4490void QSvgHandler::pushColor(
const QColor &color)
4492 m_colorStack.push(color);
4493 m_colorTagCount.push(1);
4496void QSvgHandler::pushColorCopy()
4498 if (m_colorTagCount.size())
4499 ++m_colorTagCount.top();
4501 pushColor(Qt::black);
4504void QSvgHandler::popColor()
4506 if (m_colorTagCount.size()) {
4507 if (!--m_colorTagCount.top()) {
4509 m_colorTagCount.pop();
4514QColor QSvgHandler::currentColor()
const
4516 if (!m_colorStack.isEmpty())
4517 return m_colorStack.top();
4519 return QColor(0, 0, 0);
4522#ifndef QT_NO_CSSPARSER
4524void QSvgHandler::setInStyle(
bool b)
4529bool QSvgHandler::inStyle()
const
4534QSvgCssHandler &QSvgHandler::cssHandler()
4536 return m_cssHandler;
4541bool QSvgHandler::processingInstruction(
const QStringView target,
const QStringView data)
4543#ifdef QT_NO_CSSPARSER
4547 if (target == QLatin1String(
"xml-stylesheet")) {
4548 static const QRegularExpression rx(
QStringLiteral(
"type=\\\"(.+)\\\""),
4549 QRegularExpression::InvertedGreedinessOption);
4550 QRegularExpressionMatchIterator iter = rx.globalMatchView(data);
4552 while (iter.hasNext()) {
4553 QRegularExpressionMatch match = iter.next();
4554 QString type = match.captured(1);
4555 if (type.toLower() == QLatin1String(
"text/css")) {
4561 static const QRegularExpression rx(
QStringLiteral(
"href=\\\"(.+)\\\""),
4562 QRegularExpression::InvertedGreedinessOption);
4563 QRegularExpressionMatch match = rx.matchView(data);
4564 QString addr = match.captured(1);
4568 QFile file(fi.absoluteFilePath());
4569 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
4572 QByteArray cssData = file.readAll();
4573 QString css = QString::fromUtf8(cssData);
4574 m_cssHandler.parseStyleSheet(css);
4584void QSvgHandler::setAnimPeriod(
int start,
int end)
4587 m_animEnd = qMax(end, m_animEnd);
4590int QSvgHandler::animationDuration()
const
4595QSvgHandler::~QSvgHandler()
The QPolygonF class provides a list of points using floating point precision.
qreal toDouble(QStringView str, bool *ok)
Q_STATIC_ASSERT(sizeof(SharedImageHeader) % 4==0)
#define qPrintable(string)
#define QStringLiteral(str)
#define qUtf16Printable(string)
static QSvgNode * createTspanNode(QSvgNode *parent, const QXmlStreamAttributes &, QSvgHandler *)
static bool parseStyle(QSvgNode *node, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static void parseVisibility(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static bool detectPatternCycles(const QSvgNode *node, QList< const QSvgNode * > active={})
static QSvgNode * createGNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
bool(* StyleParseMethod)(QSvgStyleProperty *, const QXmlStreamAttributes &, QSvgHandler *)
static QSvgNode::DisplayMode displayStringToEnum(const QStringView str)
static bool parseStyleNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static std::optional< QStringView > getAttributeId(const QStringView &attribute)
static QSvgStyleProperty * styleFromUrl(QSvgNode *node, QStringView url)
static const qreal sizeTable[]
static QSvgNode * createImageNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createPolygonNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseOthers(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static bool parsePathDataFast(QStringView data, QPainterPath &path, bool limitLength=true)
static QSvgNode * createMaskNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QByteArray msgProblemParsing(QStringView localName, const QXmlStreamReader *r)
static void parseRenderingHints(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static void parseExtendedAttributes(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *handler)
static bool parseMpathNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QByteArray prefixMessage(const QByteArray &msg, const QXmlStreamReader *r)
static QSvgNode * createPathNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createDefsNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgStyleProperty * createFontNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static int parseClockValue(QStringView str, bool *ok)
static bool parseSymbolLikeAttributes(const QXmlStreamAttributes &attributes, QSvgHandler *handler, QRectF *rect, QRectF *viewBox, QPointF *refPoint, QSvgSymbolLike::PreserveAspectRatios *aspect, QSvgSymbolLike::Overflow *overflow, bool marker=false)
static qreal convertToNumber(QStringView str, bool *ok=NULL)
static bool parseBaseAnimate(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgAnimateNode *anim, QSvgHandler *handler)
static bool parseFontFaceNameNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseFilterAttributes(const QXmlStreamAttributes &attributes, QString *inString, QString *outString, QSvgRectF *rect)
static QSvgNode * createPolyNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, bool createLine)
static void parseTransform(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static QSvgNode * createFeMergeNodeNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createAnimateColorNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QStringView idFromUrl(QStringView url)
static void parseColor(QSvgNode *, const QSvgAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createCircleNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
bool qsvg_get_hex_rgb(const QChar *str, int len, QRgb *rgb)
static std::optional< QRectF > parseViewBox(QStringView str)
static QSvgStyleProperty * createLinearGradientNode(QSvgNode *node, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static const int unfinishedElementsLimit
static QSvgNode * createLineNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static FactoryMethod findFilterFactory(const QStringView name, QtSvg::Options options)
static ParseMethod findUtilFactory(const QStringView name, QtSvg::Options options)
static FontSizeSpec fontSizeSpec(QStringView spec)
static QSvgStyleProperty * createSolidColorNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
bool qsvg_get_hex_rgb(const char *name, QRgb *rgb)
static QSvgNode * createTextNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseBaseGradient(QSvgNode *node, const QXmlStreamAttributes &attributes, QSvgGradientStyle *gradProp, QSvgHandler *handler)
static bool parseAudioNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static StyleParseMethod findStyleUtilFactoryMethod(const QStringView name)
static bool parseMarkerNode(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *)
static bool resolveColor(QStringView colorStr, QColor &color, QSvgHandler *handler)
static QList< qreal > parsePercentageList(const QChar *&str)
static FactoryMethod findGroupFactory(const QStringView name, QtSvg::Options options)
static bool parseMetadataNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QTransform parseTransformationMatrix(QStringView value)
static QSvgNode * createSwitchNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseDiscardNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool createSvgGlyph(QSvgFont *font, const QXmlStreamAttributes &attributes, bool isMissingGlyph)
static void parseOpacity(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static bool parseScriptNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
QSvgStyleProperty *(* StyleFactoryMethod)(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *)
static QSvgNode * createPatternNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static bool parseHandlerNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static AnimationMethod findAnimationFactory(const QStringView name, QtSvg::Options options)
static QList< qreal > parseNumbersList(const QChar *&str)
static bool parseCoreNode(QSvgNode *node, const QXmlStreamAttributes &attributes)
static bool constructColor(QStringView colorStr, QStringView opacity, QColor &color, QSvgHandler *handler)
static bool parseHkernNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void generateKeyFrames(QList< qreal > &keyFrames, uint count)
static void parsePen(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createSvgNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static void pathArc(QPainterPath &path, qreal rx, qreal ry, qreal x_axis_rotation, int large_arc_flag, int sweep_flag, qreal x, qreal y, qreal curx, qreal cury)
static void parseFont(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static bool parseForeignObjectNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseTbreakNode(QSvgNode *parent, const QXmlStreamAttributes &, QSvgHandler *)
static QSvgNode * createFeUnsupportedNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createAnimateTransformNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QList< QStringView > splitWithDelimiter(QStringView delimitedList)
static QSvgNode * createFeOffsetNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createFeFloodNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createMarkerNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createFeColorMatrixNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createFeGaussianBlurNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createPolylineNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
bool(* ParseMethod)(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *)
static bool parseFontFaceNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseMissingGlyphNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parsePrefetchNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseCssAnimations(QSvgNode *node, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static int qsvg_hex2int(const char *s, bool *ok=nullptr)
static bool parseGlyphNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static int qsvg_h2i(char hex, bool *ok=nullptr)
static int qsvg_hex2int(char s, bool *ok=nullptr)
static QSvgNode * createVideoNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseCompOp(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static bool parseMaskNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool detectCyclesAndWarn(const QSvgNode *node)
static QSvgNode * createUseNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseFontFaceSrcNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseAnchorNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseFontFaceUriNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool detectCycles(const QSvgNode *node, QList< const QSvgNode * > active={})
static QStringList stringToList(const QString &str)
static QSvgNode * createRectNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createAnimateNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createSymbolNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static StyleFactoryMethod findStyleFactoryMethod(const QStringView name)
static QPainter::CompositionMode svgToQtCompositionMode(const QStringView op)
static QByteArray msgCouldNotResolveProperty(QStringView id, const QXmlStreamReader *r)
static void parseFilterBounds(const QXmlStreamAttributes &attributes, QSvgRectF *rect)
static bool parseStopNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static void parseBrush(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *handler)
static void parseNumbersArray(const QChar *&str, QVarLengthArray< qreal, 8 > &points, const char *pattern=nullptr)
static void pathArcSegment(QPainterPath &path, qreal xc, qreal yc, qreal th0, qreal th1, qreal rx, qreal ry, qreal xAxisRotation)
static FactoryMethod findGraphicsFactory(const QStringView name, QtSvg::Options options)
static QSvgNode * createFeMergeNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createFeCompositeNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseSetNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseNumberTriplet(QList< qreal > &values, const QChar *&s)
static QSvgNode * createFeBlendNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createFilterNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createEllipseNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createTextAreaNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createAimateMotionNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgStyleProperty * createRadialGradientNode(QSvgNode *node, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QString someId(const QXmlStreamAttributes &attributes)
QStringView strokeDashOffset
QStringView strokeDashArray
QStringView strokeOpacity
QStringView strokeLineJoin
void setAttributes(const QXmlStreamAttributes &attributes, QSvgHandler *handler)
QSvgAttributes(const QXmlStreamAttributes &xmlAttributes, QSvgHandler *handler)
QStringView strokeLineCap
QStringView strokeMiterLimit
QStringView imageRendering