5#include "qplatformdefs.h"
21#include <QtCore/private/qdataurl_p.h>
29#include <qregularexpression.h>
41using namespace Qt::StringLiterals;
48constexpr auto inherit =
"inherit"_L1;
49constexpr auto normal =
"normal"_L1;
51constexpr auto italic =
"italic"_L1;
52constexpr auto oblique =
"oblique"_L1;
54constexpr auto bold =
"bold"_L1;
55constexpr auto bolder =
"bolder"_L1;
56constexpr auto lighter =
"lighter"_L1;
58constexpr auto small_caps =
"small-caps"_L1;
60constexpr auto start =
"start"_L1;
61constexpr auto middle =
"middle"_L1;
62constexpr auto end =
"end"_L1;
65constexpr auto clear =
"clear"_L1;
66constexpr auto src =
"src"_L1;
67constexpr auto dst =
"dst"_L1;
68constexpr auto srcOver =
"src-over"_L1;
69constexpr auto dstOver =
"dst-over"_L1;
70constexpr auto srcIn =
"src-in"_L1;
71constexpr auto dstIn =
"dst-in"_L1;
72constexpr auto srcOut =
"src-out"_L1;
73constexpr auto dstOut =
"dst-out"_L1;
74constexpr auto srcAtop =
"src-atop"_L1;
75constexpr auto dstAtop =
"dst-atop"_L1;
76constexpr auto xorOp =
"xor"_L1;
77constexpr auto plus =
"plus"_L1;
78constexpr auto multiply =
"multiply"_L1;
79constexpr auto screen =
"screen"_L1;
80constexpr auto overlay =
"overlay"_L1;
81constexpr auto darken =
"darken"_L1;
82constexpr auto lighten =
"lighten"_L1;
83constexpr auto colorDodge =
"color-dodge"_L1;
84constexpr auto colorBurn =
"color-burn"_L1;
85constexpr auto hardLight =
"hard-light"_L1;
86constexpr auto softLight =
"soft-light"_L1;
87constexpr auto difference =
"difference"_L1;
88constexpr auto exclusion =
"exclusion"_L1;
97 if (
const QFile *file = qobject_cast<
const QFile *>(r->device()))
98 result.append(QFile::encodeName(QDir::toNativeSeparators(file->fileName())));
100 result.append(QByteArrayLiteral(
"<input>"));
102 result.append(QByteArray::number(r->lineNumber()));
103 if (
const qint64 column = r->columnNumber()) {
105 result.append(QByteArray::number(column));
107 result.append(QByteArrayLiteral(
": "));
115 return prefixMessage(
"Problem parsing " + localName.toLocal8Bit(), r);
120 return prefixMessage(
"Could not resolve property: " + id.toLocal8Bit(), r);
125 static const QRegularExpression delimiterRE(
QStringLiteral(
"[,\\s]+"));
126 return delimitedList.split(delimiterRE, Qt::SkipEmptyParts);
131static inline int qsvg_h2i(
char hex,
bool *ok =
nullptr)
133 if (hex >=
'0' && hex <=
'9')
135 if (hex >=
'a' && hex <=
'f')
136 return hex -
'a' + 10;
137 if (hex >=
'A' && hex <=
'F')
138 return hex -
'A' + 10;
160 const size_t len = qstrlen(name);
167 }
else if (len == 9) {
171 }
else if (len == 6) {
175 }
else if (len == 3) {
182 if ((uint)r > 255 || (uint)g > 255 || (uint)b > 255 || !ok) {
186 *rgb = qRgb(r, g ,b);
195 for(
int i = 0; i < len; ++i)
196 tmp[i] = str[i].toLatin1();
198 return qsvg_get_hex_rgb(tmp, rgb);
203static inline QString
someId(
const QXmlStreamAttributes &attributes)
205 QStringView id = attributes.value(QLatin1String(
"id"));
207 id = attributes.value(QLatin1String(
"xml:id"));
208 return id.toString();
214 void setAttributes(
const QXmlStreamAttributes &attributes, QSvgHandler *handler);
256 setAttributes(xmlAttributes, handler);
261 for (
const QXmlStreamAttribute &attribute : attributes) {
262 QStringView name = attribute.qualifiedName();
265 QStringView value = attribute.value();
267 switch (name.at(0).unicode()) {
270 if (name == QLatin1String(
"color"))
272 else if (name == QLatin1String(
"color-opacity"))
273 colorOpacity = value;
274 else if (name == QLatin1String(
"comp-op"))
279 if (name == QLatin1String(
"display"))
284 if (name == QLatin1String(
"fill"))
286 else if (name == QLatin1String(
"fill-rule"))
288 else if (name == QLatin1String(
"fill-opacity"))
290 else if (name == QLatin1String(
"font-family"))
292 else if (name == QLatin1String(
"font-size"))
294 else if (name == QLatin1String(
"font-style"))
296 else if (name == QLatin1String(
"font-weight"))
298 else if (name == QLatin1String(
"font-variant"))
300 else if (name == QLatin1String(
"filter") &&
301 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
306 if (name == QLatin1String(
"id"))
307 id = value.toString();
308 else if (name == QLatin1String(
"image-rendering"))
309 imageRendering = value;
313 if (name == QLatin1String(
"mask") &&
314 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
316 if (name == QLatin1String(
"marker-start") &&
317 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
319 if (name == QLatin1String(
"marker-mid") &&
320 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
322 if (name == QLatin1String(
"marker-end") &&
323 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
328 if (name == QLatin1String(
"opacity"))
330 if (name == QLatin1String(
"offset"))
335 if (name.size() > 5 && name.mid(1, 5) == QLatin1String(
"troke")) {
336 QStringView strokeRef = name.mid(6, name.size() - 6);
337 if (strokeRef.isEmpty())
339 else if (strokeRef == QLatin1String(
"-dasharray"))
340 strokeDashArray = value;
341 else if (strokeRef == QLatin1String(
"-dashoffset"))
342 strokeDashOffset = value;
343 else if (strokeRef == QLatin1String(
"-linecap"))
344 strokeLineCap = value;
345 else if (strokeRef == QLatin1String(
"-linejoin"))
346 strokeLineJoin = value;
347 else if (strokeRef == QLatin1String(
"-miterlimit"))
348 strokeMiterLimit = value;
349 else if (strokeRef == QLatin1String(
"-opacity"))
350 strokeOpacity = value;
351 else if (strokeRef == QLatin1String(
"-width"))
353 }
else if (name == QLatin1String(
"stop-color"))
355 else if (name == QLatin1String(
"stop-opacity"))
360 if (name == QLatin1String(
"text-anchor"))
362 else if (name == QLatin1String(
"transform"))
367 if (name == QLatin1String(
"vector-effect"))
368 vectorEffect = value;
369 else if (name == QLatin1String(
"visibility"))
374 if (name == QLatin1String(
"xml:id") && id.isEmpty())
375 id = value.toString();
391 while (!str->isEmpty() && str->first().isSpace())
393 while (!str->isEmpty()
394 && (QGuiSvg::isDigit(str->first().unicode()) || str->startsWith(QLatin1Char(
'-'))
395 || str->startsWith(QLatin1Char(
'+')) || str->startsWith(QLatin1Char(
'.')))) {
397 points.append(QGuiSvg::toDouble(str));
399 while (!str->isEmpty() && str->first().isSpace())
401 if (str->startsWith(QLatin1Char(
',')))
405 while (!str->isEmpty() && str->first().isSpace())
416 while (!str.isEmpty() && str.first().isSpace())
418 while ((!str.isEmpty() && str.first() >= QLatin1Char(
'0') && str.first() <= QLatin1Char(
'9'))
419 || str.startsWith(QLatin1Char(
'-')) || str.startsWith(QLatin1Char(
'+'))
420 || str.startsWith(QLatin1Char(
'.'))) {
422 points.append(QGuiSvg::toDouble(&str));
424 while (!str.isEmpty() && str.first().isSpace())
426 if (str.startsWith(QLatin1Char(
'%')))
428 while (!str.isEmpty() && str.first().isSpace())
430 if (str.startsWith(QLatin1Char(
',')))
434 while (!str.isEmpty() && str.first().isSpace())
442
443
444
445
450 if (!iri.startsWith(QLatin1Char(
'#')))
451 return QStringView();
453 return iri.sliced(1);
457
458
459
460
461
466 if (!iri.startsWith(QLatin1StringView(
"url(")))
467 return QStringView();
471 const qsizetype closingBracePos = iri.indexOf(QLatin1Char(
')'));
472 if (closingBracePos == -1)
473 return QStringView();
475 iri = iri.first(closingBracePos);
476 return idFromIRI(iri);
480
481
482
483bool resolveColor(QStringView colorStr, QColor &color, QSvgHandler *handler)
485 QStringView colorStrTr = colorStr.trimmed();
486 if (colorStrTr.isEmpty())
489 switch(colorStrTr.at(0).unicode()) {
496 bool ok = qsvg_get_hex_rgb(colorStrTr.constData(), colorStrTr.size(), &rgb);
506 if (colorStrTr.size() >= 7 && colorStrTr.at(colorStrTr.size() - 1) == QLatin1Char(
')')
507 && colorStrTr.mid(0, 4) == QLatin1String(
"rgb(")) {
508 QStringView sv{ colorStrTr.sliced(4) };
509 QList<qreal> compo = parseNumbersList(&sv);
512 if (compo.size() == 1) {
513 compo = parsePercentageList(colorStrTr.sliced(4));
514 for (
int i = 0; i < compo.size(); ++i)
515 compo[i] *= (qreal)2.55;
518 if (compo.size() == 3) {
519 color = QColor(
int(compo[0]),
530 if (colorStrTr == QLatin1String(
"currentColor")) {
531 color = handler->currentColor();
536 if (colorStrTr == tokens::inherit)
543 color = QColor::fromString(colorStrTr);
544 return color.isValid();
550 qreal op = qBound(qreal(0.0), QGuiSvg::toDouble(opacity, &ok), qreal(1.0));
553 color->setAlphaF(op);
557 QColor &color, QSvgHandler *handler)
559 if (!resolveColor(colorStr, color, handler))
561 if (!opacity.isEmpty())
562 setAlpha(opacity, &color);
568 QGuiSvg::LengthType type;
569 qreal num = QGuiSvg::parseLength(str.toString(), &type, ok, tiny12FeaturesOnly);
570 if (type == QGuiSvg::LengthType::LT_PERCENT) {
576static bool createSvgGlyph(QSvgFont *font,
const QXmlStreamAttributes &attributes,
577 bool isMissingGlyph, QSvgHandler *handler)
579 QStringView uncStr = attributes.value(QLatin1String(
"unicode"));
580 QStringView havStr = attributes.value(QLatin1String(
"horiz-adv-x"));
581 QStringView pathStr = attributes.value(QLatin1String(
"d"));
583 qreal havx = (havStr.isEmpty()) ? -1 : QGuiSvg::toDouble(havStr);
585 QGuiSvg::parsePath(pathStr, !handler->trustedSourceMode()).value_or(QPainterPath());
587 path.setFillRule(Qt::WindingFill);
589 if (isMissingGlyph) {
590 if (!uncStr.isEmpty())
591 qWarning(
"Ignoring missing-glyph's 'unicode' attribute");
592 return font->addMissingGlyph(path, havx);
595 if (uncStr.isEmpty()) {
596 qWarning(
"glyph does not define a non-empty 'unicode' attribute and will be ignored");
599 font->addGlyph(uncStr.toString(), path, havx);
605 QSvgHandler *handler)
608 if (constructColor(attributes.color, attributes.colorOpacity, color, handler)) {
610 handler->pushColor(color);
616 QStringView id = idFromFuncIRI(url);
617 return doc ? doc->paintServer(id) :
nullptr;
622 QSvgHandler *handler)
624 if (!attributes.fill.isEmpty() || !attributes.fillRule.isEmpty() || !attributes.fillOpacity.isEmpty()) {
628 if (!attributes.fillRule.isEmpty() && attributes.fillRule != tokens::inherit) {
629 if (attributes.fillRule == QLatin1String(
"evenodd"))
630 prop->setFillRule(Qt::OddEvenFill);
631 else if (attributes.fillRule == QLatin1String(
"nonzero"))
632 prop->setFillRule(Qt::WindingFill);
636 if (!attributes.fillOpacity.isEmpty() && attributes.fillOpacity != tokens::inherit) {
637 prop->setFillOpacity(qMin(qreal(1.0), qMax(qreal(0.0), QGuiSvg::toDouble(attributes.fillOpacity))));
641 if (!attributes.fill.isEmpty() && attributes.fill != tokens::inherit) {
642 if (attributes.fill.startsWith(QLatin1String(
"url"))) {
643 QStringView value = attributes.fill;
644 QSvgPaintServerSharedPtr paintServer = paintServerFromUrl(handler->document(), value);
646 prop->setPaintServer(
std::move(paintServer));
648 QString id = idFromFuncIRI(value).toString();
649 prop->setPaintStyleId(id);
650 handler->pushUnresolvedStyle(prop.get());
652 }
else if (attributes.fill != QLatin1String(
"none")) {
654 if (resolveColor(attributes.fill, color, handler))
655 prop->setBrush(QBrush(color));
657 prop->setBrush(QBrush(Qt::NoBrush));
660 node->appendStyleProperty(
std::move(prop));
673 while (!value.isEmpty()) {
674 if (value.first().isSpace() || value.startsWith(QLatin1Char(
','))) {
686 State state = Matrix;
687 if (value.startsWith(QLatin1Char(
'm'))) {
688 const char *ident =
"atrix";
689 for (
int i = 0; i < 5; ++i)
690 if (!value.slice(1).startsWith(QLatin1Char(ident[i])))
694 }
else if (value.startsWith(QLatin1Char(
't'))) {
695 const char *ident =
"ranslate";
696 for (
int i = 0; i < 8; ++i)
697 if (!value.slice(1).startsWith(QLatin1Char(ident[i])))
701 }
else if (value.startsWith(QLatin1Char(
'r'))) {
702 const char *ident =
"otate";
703 for (
int i = 0; i < 5; ++i)
704 if (!value.slice(1).startsWith(QLatin1Char(ident[i])))
708 }
else if (value.startsWith(QLatin1Char(
's'))) {
710 if (value.startsWith(QLatin1Char(
'c'))) {
711 const char *ident =
"ale";
712 for (
int i = 0; i < 3; ++i)
713 if (!value.slice(1).startsWith(QLatin1Char(ident[i])))
717 }
else if (value.startsWith(QLatin1Char(
'k'))) {
718 if (!value.slice(1).startsWith(QLatin1Char(
'e')))
720 if (!value.slice(1).startsWith(QLatin1Char(
'w')))
723 if (value.startsWith(QLatin1Char(
'X')))
725 else if (value.startsWith(QLatin1Char(
'Y')))
737 while (!value.isEmpty() && value.first().isSpace())
739 if (!value.startsWith(QLatin1Char(
'(')))
742 QVarLengthArray<qreal, 8> points;
743 QGuiSvg::parseNumbersArray(&value, points);
744 if (!value.startsWith(QLatin1Char(
')')))
748 if(state == Matrix) {
749 if(points.size() != 6)
751 matrix = QTransform(points[0], points[1],
752 points[2], points[3],
753 points[4], points[5]) * matrix;
754 }
else if (state == Translate) {
755 if (points.size() == 1)
756 matrix.translate(points[0], 0);
757 else if (points.size() == 2)
758 matrix.translate(points[0], points[1]);
761 }
else if (state == Rotate) {
762 if(points.size() == 1) {
763 matrix.rotate(points[0]);
764 }
else if (points.size() == 3) {
765 matrix.translate(points[1], points[2]);
766 matrix.rotate(points[0]);
767 matrix.translate(-points[1], -points[2]);
771 }
else if (state == Scale) {
772 if (points.size() < 1 || points.size() > 2)
774 qreal sx = points[0];
776 if(points.size() == 2)
778 matrix.scale(sx, sy);
779 }
else if (state == SkewX) {
780 if (points.size() != 1)
782 matrix.shear(qTan(qDegreesToRadians(points[0])), 0);
783 }
else if (state == SkewY) {
784 if (points.size() != 1)
786 matrix.shear(0, qTan(qDegreesToRadians(points[0])));
795 QSvgHandler *handler)
797 if (!attributes.stroke.isEmpty() || !attributes.strokeDashArray.isEmpty() || !attributes.strokeDashOffset.isEmpty() || !attributes.strokeLineCap.isEmpty()
798 || !attributes.strokeLineJoin.isEmpty() || !attributes.strokeMiterLimit.isEmpty() || !attributes.strokeOpacity.isEmpty() || !attributes.strokeWidth.isEmpty()
799 || !attributes.vectorEffect.isEmpty()) {
804 if (!attributes.stroke.isEmpty() && attributes.stroke != tokens::inherit) {
805 if (attributes.stroke.startsWith(QLatin1String(
"url"))) {
806 QStringView value = attributes.stroke;
807 QSvgPaintServerSharedPtr paintServer = paintServerFromUrl(handler->document(), value);
809 prop->setPaintServer(
std::move(paintServer));
811 QString id = idFromFuncIRI(value).toString();
812 prop->setPaintStyleId(id);
813 handler->pushUnresolvedStyle(prop.get());
815 }
else if (attributes.stroke != QLatin1String(
"none")) {
817 if (resolveColor(attributes.stroke, color, handler))
818 prop->setStroke(QBrush(color));
820 prop->setStroke(QBrush(Qt::NoBrush));
825 if (!attributes.strokeWidth.isEmpty() && attributes.strokeWidth != tokens::inherit) {
826 QGuiSvg::LengthType lt;
827 prop->setWidth(QGuiSvg::parseLength(attributes.strokeWidth, <));
831 if (!attributes.strokeDashArray.isEmpty() && attributes.strokeDashArray != tokens::inherit) {
832 if (attributes.strokeDashArray == QLatin1String(
"none")) {
833 prop->setDashArrayNone();
835 QStringView dashArray = attributes.strokeDashArray;
836 QList<qreal> dashes = parseNumbersList(&dashArray);
837 const bool allZeroes =
std::all_of(dashes.cbegin(), dashes.cend(),
838 [](qreal i) {
return qFuzzyIsNull(i); });
839 const bool hasNegative = !allZeroes &&
std::any_of(dashes.cbegin(), dashes.cend(),
840 [](qreal i) {
return i < 0.; });
843 qCWarning(lcSvgHandler) <<
"QSvgHandler: Stroke dash array "
844 "with a negative value is invalid";
847 if (allZeroes || hasNegative) {
848 prop->setDashArrayNone();
851 if ((dashes.size() & 1) != 0)
852 dashes << QList<qreal>(dashes);
853 prop->setDashArray(dashes);
859 if (!attributes.strokeLineJoin.isEmpty()) {
860 if (attributes.strokeLineJoin == QLatin1String(
"miter"))
861 prop->setLineJoin(Qt::SvgMiterJoin);
862 else if (attributes.strokeLineJoin == QLatin1String(
"round"))
863 prop->setLineJoin(Qt::RoundJoin);
864 else if (attributes.strokeLineJoin == QLatin1String(
"bevel"))
865 prop->setLineJoin(Qt::BevelJoin);
869 if (!attributes.strokeLineCap.isEmpty()) {
870 if (attributes.strokeLineCap == QLatin1String(
"butt"))
871 prop->setLineCap(Qt::FlatCap);
872 else if (attributes.strokeLineCap == QLatin1String(
"round"))
873 prop->setLineCap(Qt::RoundCap);
874 else if (attributes.strokeLineCap == QLatin1String(
"square"))
875 prop->setLineCap(Qt::SquareCap);
879 if (!attributes.strokeDashOffset.isEmpty() && attributes.strokeDashOffset != tokens::inherit)
880 prop->setDashOffset(QGuiSvg::toDouble(attributes.strokeDashOffset));
883 if (!attributes.vectorEffect.isEmpty()) {
884 if (attributes.vectorEffect == QLatin1String(
"non-scaling-stroke"))
885 prop->setVectorEffect(
true);
886 else if (attributes.vectorEffect == QLatin1String(
"none"))
887 prop->setVectorEffect(
false);
891 if (!attributes.strokeMiterLimit.isEmpty() && attributes.strokeMiterLimit != tokens::inherit)
892 prop->setMiterLimit(QGuiSvg::toDouble(attributes.strokeMiterLimit));
895 if (!attributes.strokeOpacity.isEmpty() && attributes.strokeOpacity != tokens::inherit)
896 prop->setOpacity(qMin(qreal(1.0), qMax(qreal(0.0), QGuiSvg::toDouble(attributes.strokeOpacity))));
898 node->appendStyleProperty(
std::move(prop));
906{ qreal(6.9), qreal(8.3), qreal(10.0), qreal(12.0), qreal(14.4), qreal(17.3), qreal(20.7) };
912 switch (spec.at(0).unicode()) {
914 if (spec == QLatin1String(
"xx-small"))
916 if (spec == QLatin1String(
"x-small"))
918 if (spec == QLatin1String(
"x-large"))
920 if (spec == QLatin1String(
"xx-large"))
924 if (spec == QLatin1String(
"small"))
928 if (spec == QLatin1String(
"medium"))
932 if (spec == QLatin1String(
"large"))
936 if (spec == QLatin1String(
"none"))
950 if (s == tokens::normal)
951 return QFont::StyleNormal;
952 if (s == tokens::italic)
953 return QFont::StyleItalic;
954 if (s == tokens::oblique)
955 return QFont::StyleOblique;
969 if (s.isEmpty() || s == tokens::inherit)
977 QGuiSvg::LengthType type;
979 qreal fs = QGuiSvg::parseLength(s, &type, &ok, tiny12FeaturesOnly);
982 fs = QGuiSvg::convertToPixels(fs,
true, type);
983 return (
std::min)(fs, qreal(0xffff));
992 return sizeTable[spec];
995 Q_UNREACHABLE_RETURN(
std::nullopt);
1003 if (s.isEmpty() || s == tokens::inherit)
1004 return std::nullopt;
1006 if (s == tokens::normal)
1007 return QFont::Normal;
1008 if (s == tokens::bold)
1010 if (s == tokens::bolder)
1011 return QSvgFontStyle::BOLDER;
1012 if (s == tokens::lighter)
1013 return QSvgFontStyle::LIGHTER;
1016 const int num = s.toInt(&ok);
1020 return std::nullopt;
1047 auto s = attributes.fontVariant;
1049 if (s == tokens::normal)
1050 return QFont::MixedCase;
1051 if (s == tokens::small_caps)
1052 return QFont::SmallCaps;
1054 return std::nullopt;
1065 if (s == tokens::start)
1066 return Qt::AlignLeft;
1067 if (s == tokens::middle)
1068 return Qt::AlignHCenter;
1069 if (s == tokens::end)
1070 return Qt::AlignRight;
1072 return std::nullopt;
1077 QSvgHandler *handler)
1079 auto parsedFontSize = parseFontSize(attributes.fontSize,
1080 handler->options().testFlag(QtSvg::Tiny12FeaturesOnly));
1081 auto parsedFontStyle = parseFontStyle(attributes.fontStyle);
1082 auto parsedFontWeight = parseFontWeight(attributes.fontWeight);
1083 auto parsedFontVariant = parseFontVariant(attributes);
1084 auto parsedTextAnchor = parseTextAnchor(attributes.textAnchor);
1086 if (attributes.fontFamily.isEmpty() && !parsedFontSize && !parsedFontStyle &&
1087 !parsedFontWeight && !parsedFontVariant && !parsedTextAnchor)
1092 if (!attributes.fontFamily.isEmpty() && attributes.fontFamily != tokens::inherit) {
1093 QStringView family = attributes.fontFamily.trimmed();
1094 if (!family.isEmpty() && (family.at(0) == QLatin1Char(
'\'') || family.at(0) == QLatin1Char(
'\"')))
1095 family = family.mid(1, family.size() - 2);
1096 fontStyle->setFamily(family.toString());
1100 fontStyle->setSize(*parsedFontSize);
1102 if (parsedFontStyle)
1103 fontStyle->setStyle(*parsedFontStyle);
1105 if (parsedFontWeight)
1106 fontStyle->setWeight(*parsedFontWeight);
1108 if (parsedFontVariant)
1109 fontStyle->setVariant(*parsedFontVariant);
1111 if (parsedTextAnchor)
1112 fontStyle->setTextAnchor(*parsedTextAnchor);
1114 node->appendStyleProperty(
std::move(fontStyle));
1121 if (attributes.transform.isEmpty())
1123 QTransform matrix = parseTransformationMatrix(attributes.transform.trimmed());
1125 if (!matrix.isIdentity()) {
1135 QSvgNode *parent = node->parent();
1137 if (parent && (attributes.visibility.isEmpty() || attributes.visibility == tokens::inherit))
1138 node->setVisible(parent->isVisible());
1139 else if (attributes.visibility == QLatin1String(
"hidden") || attributes.visibility == QLatin1String(
"collapse")) {
1140 node->setVisible(
false);
1142 node->setVisible(
true);
1146 const QXmlStreamAttributes &attributes,
1147 QSvgHandler *handler);
1153 str = str.trimmed();
1154 if (str.endsWith(QLatin1String(
"ms"))) {
1157 }
else if (str.endsWith(QLatin1String(
"s"))) {
1160 double val = ms * QGuiSvg::toDouble(str, ok);
1162 if (val >
std::numeric_limits<
int>::min() && val <
std::numeric_limits<
int>::max())
1163 res =
static_cast<
int>(val);
1170#ifndef QT_NO_CSSPARSER
1173 const QSvgCssProperties &properties,
1174 QSvgHandler *handler)
1176 QList<QSvgAnimationProperty> parsedProperties = properties.animations();
1178 for (
auto &property : parsedProperties) {
1179 QSvgCssAnimation *anim = handler->cssHandler().createAnimation(property.name);
1183 anim->setRunningTime(property.delay, property.duration);
1184 anim->setIterationCount(property.iteration);
1185 QSvgCssEasingPtr easing = handler->cssHandler().createEasing(property.easingFunction, property.easingValues);
1186 anim->setEasing(std::move(easing));
1188 handler->setAnimPeriod(property.delay, property.delay + property.duration);
1189 handler->document()->animator()->appendAnimation(node, anim);
1190 handler->document()->setAnimated(
true);
1195 const QSvgCssProperties &properties)
1197 QSvgOffsetProperty offset = properties.offset();
1203 offsetStyle->setPath(offset.path.value());
1204 offsetStyle->setRotateAngle(offset.angle);
1205 offsetStyle->setRotateType(offset.rotateType);
1206 offsetStyle->setDistance(offset.distance);
1207 node->appendStyleProperty(
std::move(offsetStyle));
1212QtSvg::Options QSvgHandler::options()
const
1217QtSvg::AnimatorType QSvgHandler::animatorType()
const
1219 return m_animatorType;
1222bool QSvgHandler::trustedSourceMode()
const
1224 return m_options.testFlag(QtSvg::AssumeTrustedSource);
1229 QStringList lst = str.split(QLatin1Char(
','), Qt::SkipEmptyParts);
1234 const QXmlStreamAttributes &attributes)
1236 QStringList features;
1237 QStringList extensions;
1238 QStringList languages;
1239 QStringList formats;
1241 QStringView xmlClassStr;
1243 for (
const QXmlStreamAttribute &attribute : attributes) {
1244 QStringView name = attribute.qualifiedName();
1247 QStringView value = attribute.value();
1248 switch (name.at(0).unicode()) {
1250 if (name == QLatin1String(
"class"))
1251 xmlClassStr = value;
1254 if (name == QLatin1String(
"requiredFeatures"))
1255 features = stringToList(value.toString());
1256 else if (name == QLatin1String(
"requiredExtensions"))
1257 extensions = stringToList(value.toString());
1258 else if (name == QLatin1String(
"requiredFormats"))
1259 formats = stringToList(value.toString());
1260 else if (name == QLatin1String(
"requiredFonts"))
1261 fonts = stringToList(value.toString());
1264 if (name == QLatin1String(
"systemLanguage"))
1265 languages = stringToList(value.toString());
1272 node->setRequiredFeatures(features);
1273 node->setRequiredExtensions(extensions);
1274 node->setRequiredLanguages(languages);
1275 node->setRequiredFormats(formats);
1276 node->setRequiredFonts(fonts);
1277 node->setNodeId(someId(attributes));
1278 node->setXmlClass(xmlClassStr.toString());
1287 if (attributes.opacity.isEmpty())
1290 const QStringView value = attributes.opacity.trimmed();
1293 qreal op = value.toDouble(&ok);
1296 QSvgOpacityStylePtr opacity =
std::make_unique<
QSvgOpacityStyle>(qBound(qreal(0.0), op, qreal(1.0)));
1297 node->appendStyleProperty(
std::move(opacity));
1303 if (op == tokens::compOp::clear)
1304 return QPainter::CompositionMode_Clear;
1305 else if (op == tokens::compOp::src)
1306 return QPainter::CompositionMode_Source;
1307 else if (op == tokens::compOp::dst)
1308 return QPainter::CompositionMode_Destination;
1309 else if (op == tokens::compOp::srcOver)
1310 return QPainter::CompositionMode_SourceOver;
1311 else if (op == tokens::compOp::dstOver)
1312 return QPainter::CompositionMode_DestinationOver;
1313 else if (op == tokens::compOp::srcIn)
1314 return QPainter::CompositionMode_SourceIn;
1315 else if (op == tokens::compOp::dstIn)
1316 return QPainter::CompositionMode_DestinationIn;
1317 else if (op == tokens::compOp::srcOut)
1318 return QPainter::CompositionMode_SourceOut;
1319 else if (op == tokens::compOp::dstOut)
1320 return QPainter::CompositionMode_DestinationOut;
1321 else if (op == tokens::compOp::srcAtop)
1322 return QPainter::CompositionMode_SourceAtop;
1323 else if (op == tokens::compOp::dstAtop)
1324 return QPainter::CompositionMode_DestinationAtop;
1325 else if (op == tokens::compOp::xorOp)
1326 return QPainter::CompositionMode_Xor;
1327 else if (op == tokens::compOp::plus)
1328 return QPainter::CompositionMode_Plus;
1329 else if (op == tokens::compOp::multiply)
1330 return QPainter::CompositionMode_Multiply;
1331 else if (op == tokens::compOp::screen)
1332 return QPainter::CompositionMode_Screen;
1333 else if (op == tokens::compOp::overlay)
1334 return QPainter::CompositionMode_Overlay;
1335 else if (op == tokens::compOp::darken)
1336 return QPainter::CompositionMode_Darken;
1337 else if (op == tokens::compOp::lighten)
1338 return QPainter::CompositionMode_Lighten;
1339 else if (op == tokens::compOp::colorDodge)
1340 return QPainter::CompositionMode_ColorDodge;
1341 else if (op == tokens::compOp::colorBurn)
1342 return QPainter::CompositionMode_ColorBurn;
1343 else if (op == tokens::compOp::hardLight)
1344 return QPainter::CompositionMode_HardLight;
1345 else if (op == tokens::compOp::softLight)
1346 return QPainter::CompositionMode_SoftLight;
1347 else if (op == tokens::compOp::difference)
1348 return QPainter::CompositionMode_Difference;
1349 else if (op == tokens::compOp::exclusion)
1350 return QPainter::CompositionMode_Exclusion;
1352 qCWarning(lcSvgHandler) <<
"Composition mode not supported : "_L1 << op;
1353 return QPainter::CompositionMode_SourceOver;
1360 if (attributes.compOp.isEmpty())
1362 QStringView value = attributes.compOp.trimmed();
1364 if (!value.isEmpty()) {
1365 QSvgCompOpStylePtr compop =
std::make_unique<
QSvgCompOpStyle>(svgToQtCompositionMode(value));
1366 node->appendStyleProperty(
std::move(compop));
1372 if (str == QLatin1String(
"inline")) {
1373 return QSvgNode::InlineMode;
1374 }
else if (str == QLatin1String(
"block")) {
1375 return QSvgNode::BlockMode;
1376 }
else if (str == QLatin1String(
"list-item")) {
1377 return QSvgNode::ListItemMode;
1378 }
else if (str == QLatin1String(
"run-in")) {
1379 return QSvgNode::RunInMode;
1380 }
else if (str == QLatin1String(
"compact")) {
1381 return QSvgNode::CompactMode;
1382 }
else if (str == QLatin1String(
"marker")) {
1383 return QSvgNode::MarkerMode;
1384 }
else if (str == QLatin1String(
"table")) {
1385 return QSvgNode::TableMode;
1386 }
else if (str == QLatin1String(
"inline-table")) {
1387 return QSvgNode::InlineTableMode;
1388 }
else if (str == QLatin1String(
"table-row-group")) {
1389 return QSvgNode::TableRowGroupMode;
1390 }
else if (str == QLatin1String(
"table-header-group")) {
1391 return QSvgNode::TableHeaderGroupMode;
1392 }
else if (str == QLatin1String(
"table-footer-group")) {
1393 return QSvgNode::TableFooterGroupMode;
1394 }
else if (str == QLatin1String(
"table-row")) {
1395 return QSvgNode::TableRowMode;
1396 }
else if (str == QLatin1String(
"table-column-group")) {
1397 return QSvgNode::TableColumnGroupMode;
1398 }
else if (str == QLatin1String(
"table-column")) {
1399 return QSvgNode::TableColumnMode;
1400 }
else if (str == QLatin1String(
"table-cell")) {
1401 return QSvgNode::TableCellMode;
1402 }
else if (str == QLatin1String(
"table-caption")) {
1403 return QSvgNode::TableCaptionMode;
1404 }
else if (str == QLatin1String(
"none")) {
1405 return QSvgNode::NoneMode;
1406 }
else if (str == tokens::inherit) {
1407 return QSvgNode::InheritMode;
1409 return QSvgNode::BlockMode;
1416 if (attributes.display.isEmpty())
1418 QStringView displayStr = attributes.display.trimmed();
1420 if (!displayStr.isEmpty()) {
1421 node->setDisplayMode(displayStringToEnum(displayStr));
1427 if (attribute.isEmpty())
1428 return std::nullopt;
1430 return idFromFuncIRI(attribute);
1435 QSvgHandler *handler)
1440 if (
auto id = getAttributeId(attributes.mask))
1441 node->setMaskId(id->toString());
1442 if (
auto id = getAttributeId(attributes.markerStart))
1443 node->setMarkerStartId(id->toString());
1444 if (
auto id = getAttributeId(attributes.markerMid))
1445 node->setMarkerMidId(id->toString());
1446 if (
auto id = getAttributeId(attributes.markerEnd))
1447 node->setMarkerEndId(id->toString());
1448 if (
auto id = getAttributeId(attributes.filter))
1449 node->setFilterId(id->toString());
1456 if (attributes.imageRendering.isEmpty())
1459 QStringView ir = attributes.imageRendering.trimmed();
1461 if (ir == QLatin1String(
"auto"))
1462 quality->setImageRendering(QSvgQualityStyle::ImageRenderingAuto);
1463 else if (ir == QLatin1String(
"optimizeSpeed"))
1464 quality->setImageRendering(QSvgQualityStyle::ImageRenderingOptimizeSpeed);
1465 else if (ir == QLatin1String(
"optimizeQuality"))
1466 quality->setImageRendering(QSvgQualityStyle::ImageRenderingOptimizeQuality);
1467 node->appendStyleProperty(
std::move(quality));
1471 const QXmlStreamAttributes &attributes,
1472 QSvgHandler *handler)
1480#ifndef QT_NO_CSSPARSER
1481 QXmlStreamAttributes cssAttributes;
1482 handler->cssHandler().styleLookup(node, cssAttributes);
1484 QStringView style = attributes.value(QLatin1String(
"style"));
1485 if (!style.isEmpty())
1486 handler->cssHandler().parseCSStoXMLAttrs(style.toString(), cssAttributes);
1487 svgAttributes.setAttributes(cssAttributes, handler);
1489 QSvgCssProperties cssProperties(cssAttributes);
1490 parseOffsetPath(node, cssProperties);
1492 parseCssAnimations(node, cssProperties, handler);
1511 const QXmlStreamAttributes &attributes,
1514 Q_UNUSED(parent); Q_UNUSED(attributes);
1519 const QXmlStreamAttributes &attributes,
1520 QSvgAnimateNode *anim,
1521 QSvgHandler *handler)
1523 const QStringView beginStr = attributes.value(QLatin1String(
"begin"));
1524 const QStringView durStr = attributes.value(QLatin1String(
"dur"));
1525 const QStringView endStr = attributes.value(QLatin1String(
"end"));
1526 const QStringView repeatStr = attributes.value(QLatin1String(
"repeatCount"));
1527 const QStringView fillStr = attributes.value(QLatin1String(
"fill"));
1528 const QStringView addtv = attributes.value(QLatin1String(
"additive"));
1529 QStringView linkId = attributes.value(QLatin1String(
"xlink:href"));
1531 if (linkId.isEmpty())
1532 linkId = attributes.value(QLatin1String(
"href"));
1534 linkId = idFromIRI(linkId);
1537 int begin = parseClockValue(beginStr, &ok);
1540 int dur = parseClockValue(durStr, &ok);
1543 int end = parseClockValue(endStr, &ok);
1546 qreal repeatCount = (repeatStr == QLatin1String(
"indefinite")) ? -1 :
1547 qMax(1.0, QGuiSvg::toDouble(repeatStr));
1549 QSvgAnimateNode::Fill fill = (fillStr == QLatin1String(
"freeze")) ? QSvgAnimateNode::Freeze :
1550 QSvgAnimateNode::Remove;
1552 QSvgAnimateNode::Additive additive = (addtv == QLatin1String(
"sum")) ? QSvgAnimateNode::Sum :
1553 QSvgAnimateNode::Replace;
1555 anim->setRunningTime(begin, dur, end, 0);
1556 anim->setRepeatCount(repeatCount);
1557 anim->setFill(fill);
1558 anim->setAdditiveType(additive);
1559 anim->setLinkId(linkId.toString());
1561 handler->document()->setAnimated(
true);
1563 handler->setAnimPeriod(begin, begin + dur);
1572 qreal spacing = 1.0f / (count - 1);
1573 for (uint i = 0; i < count; i++) {
1574 keyFrames.append(i * spacing);
1579 const QXmlStreamAttributes &attributes,
1580 QSvgHandler *handler)
1582 const QStringView fromStr = attributes.value(QLatin1String(
"from"));
1583 const QStringView toStr = attributes.value(QLatin1String(
"to"));
1584 const QStringView valuesStr = attributes.value(QLatin1String(
"values"));
1585 const QString targetStr = attributes.value(QLatin1String(
"attributeName")).toString();
1587 if (targetStr != QLatin1String(
"fill") && targetStr != QLatin1String(
"stroke"))
1590 QList<QColor> colors;
1591 if (valuesStr.isEmpty()) {
1592 QColor startColor, endColor;
1593 resolveColor(fromStr, startColor, handler);
1594 resolveColor(toStr, endColor, handler);
1596 colors.append(startColor);
1597 colors.append(endColor);
1599 for (
auto part : qTokenize(valuesStr, u';')) {
1601 resolveColor(part, color, handler);
1602 colors.append(color);
1606 QSvgAnimatedPropertyColor *prop =
static_cast<QSvgAnimatedPropertyColor *>
1607 (QSvgAbstractAnimatedProperty::createAnimatedProperty(targetStr));
1611 prop->setColors(colors);
1613 QList<qreal> keyFrames;
1614 generateKeyFrames(keyFrames, colors.size());
1615 prop->setKeyFrames(keyFrames);
1617 QSvgAnimateColor *anim =
new QSvgAnimateColor(parent);
1618 anim->appendProperty(prop);
1620 if (!parseBaseAnimate(parent, attributes, anim, handler)) {
1629 const QXmlStreamAttributes &attributes,
1632 Q_UNUSED(parent); Q_UNUSED(attributes);
1638 QList<qreal> list = parseNumbersList(s);
1640 for (
int i = 3 - list.size(); i > 0; --i)
1646 parseNumberTriplet(values, &s);
1650 const QXmlStreamAttributes &attributes,
1651 QSvgHandler *handler)
1653 const QStringView typeStr = attributes.value(QLatin1String(
"type"));
1654 const QStringView values = attributes.value(QLatin1String(
"values"));
1655 const QStringView fromStr = attributes.value(QLatin1String(
"from"));
1656 const QStringView toStr = attributes.value(QLatin1String(
"to"));
1657 const QStringView byStr = attributes.value(QLatin1String(
"by"));
1660 if (values.isEmpty()) {
1661 if (fromStr.isEmpty()) {
1662 if (!byStr.isEmpty()) {
1666 parseNumberTriplet(vals, byStr);
1672 if (!toStr.isEmpty()) {
1674 parseNumberTriplet(vals, fromStr);
1675 parseNumberTriplet(vals, toStr);
1676 }
else if (!byStr.isEmpty()) {
1678 parseNumberTriplet(vals, fromStr);
1679 parseNumberTriplet(vals, byStr);
1680 for (
int i = vals.size() - 3; i < vals.size(); ++i)
1681 vals[i] += vals[i - 3];
1687 QStringView s = values;
1688 while (!s.isEmpty()) {
1689 parseNumberTriplet(vals, &s);
1694 if (vals.size() % 3 != 0)
1698 QList<QSvgAnimatedPropertyTransform::TransformComponent> components;
1699 for (
int i = 0; i <= vals.size() - 3; i += 3) {
1700 QSvgAnimatedPropertyTransform::TransformComponent component;
1701 if (typeStr == QLatin1String(
"translate")) {
1702 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Translate;
1703 component.values.append(vals.at(i));
1704 component.values.append(vals.at(i + 1));
1705 }
else if (typeStr == QLatin1String(
"scale")) {
1706 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Scale;
1707 component.values.append(vals.at(i));
1708 component.values.append(vals.at(i + 1));
1709 }
else if (typeStr == QLatin1String(
"rotate")) {
1710 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Rotate;
1711 component.values.append(vals.at(i));
1712 component.values.append(vals.at(i + 1));
1713 component.values.append(vals.at(i + 2));
1714 }
else if (typeStr == QLatin1String(
"skewX")) {
1715 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Skew;
1716 component.values.append(vals.at(i));
1717 component.values.append(0);
1718 }
else if (typeStr == QLatin1String(
"skewY")) {
1719 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Skew;
1720 component.values.append(0);
1721 component.values.append(vals.at(i));
1725 components.append(component);
1728 QSvgAnimatedPropertyTransform *prop =
static_cast<QSvgAnimatedPropertyTransform *>
1729 (QSvgAbstractAnimatedProperty::createAnimatedProperty(QLatin1String(
"transform")));
1733 prop->appendComponents(components);
1735 prop->setTransformCount(1);
1736 QList<qreal> keyFrames;
1737 generateKeyFrames(keyFrames, vals.size() / 3);
1738 prop->setKeyFrames(keyFrames);
1740 QSvgAnimateTransform *anim =
new QSvgAnimateTransform(parent);
1741 anim->appendProperty(prop);
1743 if (!parseBaseAnimate(parent, attributes, anim, handler)) {
1752 const QXmlStreamAttributes &attributes,
1755 Q_UNUSED(parent); Q_UNUSED(attributes);
1760 const QXmlStreamAttributes &attributes,
1763 Q_UNUSED(parent); Q_UNUSED(attributes);
1768 const QXmlStreamAttributes &attributes,
1771 const QStringView cx = attributes.value(QLatin1String(
"cx"));
1772 const QStringView cy = attributes.value(QLatin1String(
"cy"));
1773 const QStringView r = attributes.value(QLatin1String(
"r"));
1774 qreal ncx = QGuiSvg::toDouble(cx);
1775 qreal ncy = QGuiSvg::toDouble(cy);
1776 qreal nr = QGuiSvg::toDouble(r);
1780 QRectF rect(ncx-nr, ncy-nr, nr*2, nr*2);
1781 QSvgNode *circle =
new QSvgCircle(parent, rect);
1786 const QXmlStreamAttributes &attributes,
1789 Q_UNUSED(attributes);
1790 QSvgDefs *defs =
new QSvgDefs(parent);
1795 const QXmlStreamAttributes &attributes,
1798 Q_UNUSED(parent); Q_UNUSED(attributes);
1803 const QXmlStreamAttributes &attributes,
1806 const QStringView cx = attributes.value(QLatin1String(
"cx"));
1807 const QStringView cy = attributes.value(QLatin1String(
"cy"));
1808 const QStringView rx = attributes.value(QLatin1String(
"rx"));
1809 const QStringView ry = attributes.value(QLatin1String(
"ry"));
1810 qreal ncx = QGuiSvg::toDouble(cx);
1811 qreal ncy = QGuiSvg::toDouble(cy);
1812 qreal nrx = QGuiSvg::toDouble(rx);
1813 qreal nry = QGuiSvg::toDouble(ry);
1815 QRectF rect(ncx-nrx, ncy-nry, nrx*2, nry*2);
1816 QSvgNode *ellipse =
new QSvgEllipse(parent, rect);
1823 const QStringView hax = attributes.value(QLatin1String(
"horiz-adv-x"));
1825 qreal horizAdvX = QGuiSvg::toDouble(hax);
1827 return std::make_unique<QSvgFont>(horizAdvX);
1831 const QXmlStreamAttributes &attributes,
1832 QSvgHandler *handler)
1834 const QStringView name = attributes.value(QLatin1String(
"font-family"));
1839 const QStringView unitsPerEmStr = attributes.value(QLatin1String(
"units-per-em"));
1842
1843
1845 qreal unitsPerEm = QGuiSvg::toDouble(unitsPerEmStr, &ok);
1846 if (!qFuzzyIsNull(unitsPerEm))
1847 parent->setUnitsPerEm(unitsPerEm);
1849 handler->setCurrentSvgFontFamily(name);
1855 const QXmlStreamAttributes &attributes,
1858 Q_UNUSED(parent); Q_UNUSED(attributes);
1863 const QXmlStreamAttributes &attributes,
1866 Q_UNUSED(parent); Q_UNUSED(attributes);
1871 const QXmlStreamAttributes &attributes,
1874 Q_UNUSED(parent); Q_UNUSED(attributes);
1879 const QXmlStreamAttributes &attributes,
1880 QSvgHandler *handler)
1882 return createSvgGlyph(parent, attributes,
false, handler);
1886 const QXmlStreamAttributes &attributes,
1887 QSvgHandler *handler)
1889 return createSvgGlyph(parent, attributes,
true, handler);
1893 const QXmlStreamAttributes &attributes,
1896 Q_UNUSED(parent); Q_UNUSED(attributes);
1901 const QXmlStreamAttributes &attributes,
1904 Q_UNUSED(attributes);
1905 QSvgG *node =
new QSvgG(parent);
1910 const QXmlStreamAttributes &attributes,
1913 Q_UNUSED(parent); Q_UNUSED(attributes);
1918 const QXmlStreamAttributes &attributes,
1921 Q_UNUSED(parent); Q_UNUSED(attributes);
1926 const QXmlStreamAttributes &attributes,
1927 QSvgHandler *handler)
1929 const QStringView x = attributes.value(QLatin1String(
"x"));
1930 const QStringView y = attributes.value(QLatin1String(
"y"));
1931 const QStringView width = attributes.value(QLatin1String(
"width"));
1932 const QStringView height = attributes.value(QLatin1String(
"height"));
1933 QString filename = attributes.value(QLatin1String(
"xlink:href")).toString();
1935 filename = attributes.value(QLatin1String(
"href")).toString();
1936 qreal nx = QGuiSvg::toDouble(x);
1937 qreal ny = QGuiSvg::toDouble(y);
1938 QGuiSvg::LengthType type;
1939 qreal nwidth = QGuiSvg::parseLength(width.toString(), &type);
1940 nwidth = QGuiSvg::convertToPixels(nwidth,
true, type);
1942 qreal nheight = QGuiSvg::parseLength(height.toString(), &type);
1943 nheight = QGuiSvg::convertToPixels(nheight,
false, type);
1945 filename = filename.trimmed();
1946 if (filename.isEmpty()) {
1947 qCWarning(lcSvgHandler) <<
"QSvgHandler: Image filename is empty";
1950 if (nwidth <= 0 || nheight <= 0) {
1951 qCWarning(lcSvgHandler) <<
"QSvgHandler: Width or height for" << filename <<
"image was not greater than 0";
1960 } filenameType = NotLoaded;
1962 if (filename.startsWith(QLatin1String(
"data"))) {
1965 if (qDecodeDataUrl(QUrl{filename}, mimeType, data)) {
1966 image = QImage::fromData(data);
1967 filenameType = LoadedFromData;
1971 if (image.isNull()) {
1972 const auto *file = qobject_cast<QFile *>(handler->device());
1975 if (url.isRelative()) {
1977 filename = info.absoluteDir().absoluteFilePath(filename);
1981 if (handler->trustedSourceMode() || !QImageReader::imageFormat(filename).startsWith(
"svg")) {
1982 image = QImage(filename);
1983 filenameType = LoadedFromFile;
1987 if (image.isNull()) {
1988 qCWarning(lcSvgHandler) <<
"Could not create image from" << filename;
1992 if (image.format() == QImage::Format_ARGB32)
1993 image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
1995 if (filenameType != LoadedFromFile)
1996 filename = QString();
1997 QSvgNode *img =
new QSvgImage(parent,
2008 const QXmlStreamAttributes &attributes,
2011 const QStringView x1 = attributes.value(QLatin1String(
"x1"));
2012 const QStringView y1 = attributes.value(QLatin1String(
"y1"));
2013 const QStringView x2 = attributes.value(QLatin1String(
"x2"));
2014 const QStringView y2 = attributes.value(QLatin1String(
"y2"));
2015 qreal nx1 = QGuiSvg::toDouble(x1);
2016 qreal ny1 = QGuiSvg::toDouble(y1);
2017 qreal nx2 = QGuiSvg::toDouble(x2);
2018 qreal ny2 = QGuiSvg::toDouble(y2);
2020 QLineF lineBounds(nx1, ny1, nx2, ny2);
2021 QSvgNode *line =
new QSvgLine(parent, lineBounds);
2027 QSvgGradientPaint *gradProp,
2028 QSvgHandler *handler)
2030 QStringView linkId = attributes.value(QLatin1String(
"xlink:href"));
2031 const QStringView trans = attributes.value(QLatin1String(
"gradientTransform"));
2032 const QStringView spread = attributes.value(QLatin1String(
"spreadMethod"));
2033 const QStringView units = attributes.value(QLatin1String(
"gradientUnits"));
2034 const QStringView colorStr = attributes.value(QLatin1String(
"color"));
2035 const QStringView colorOpacityStr = attributes.value(QLatin1String(
"color-opacity"));
2038 if (constructColor(colorStr, colorOpacityStr, color, handler)) {
2039 handler->popColor();
2040 handler->pushColor(color);
2044 QGradient *grad = gradProp->qgradient();
2045 linkId = idFromIRI(linkId);
2047 if (!linkId.isEmpty()) {
2048 QSvgPaintServerSharedPtr paintServer = handler->document()->paintServer(linkId);
2049 if (paintServer && paintServer->type() == QSvgPaintServer::Type::Gradient) {
2050 QSvgGradientPaint *inherited =
2051 static_cast<QSvgGradientPaint*>(paintServer.get());
2052 if (!inherited->stopLink().isEmpty()) {
2053 gradProp->setStopLink(inherited->stopLink(), handler->document());
2055 grad->setStops(inherited->qgradient()->stops());
2056 gradProp->setGradientStopsSet(inherited->gradientStopsSet());
2059 matrix = inherited->qtransform();
2061 gradProp->setStopLink(linkId.toString(), handler->document());
2065 if (!trans.isEmpty()) {
2066 matrix = parseTransformationMatrix(trans);
2067 gradProp->setTransform(matrix);
2068 }
else if (!matrix.isIdentity()) {
2069 gradProp->setTransform(matrix);
2072 if (!spread.isEmpty()) {
2073 if (spread == QLatin1String(
"pad")) {
2074 grad->setSpread(QGradient::PadSpread);
2075 }
else if (spread == QLatin1String(
"reflect")) {
2076 grad->setSpread(QGradient::ReflectSpread);
2077 }
else if (spread == QLatin1String(
"repeat")) {
2078 grad->setSpread(QGradient::RepeatSpread);
2082 if (units.isEmpty() || units == QLatin1String(
"objectBoundingBox")) {
2083 grad->setCoordinateMode(QGradient::ObjectMode);
2089 QSvgHandler *handler)
2091 const QStringView x1 = attributes.value(QLatin1String(
"x1"));
2092 const QStringView y1 = attributes.value(QLatin1String(
"y1"));
2093 const QStringView x2 = attributes.value(QLatin1String(
"x2"));
2094 const QStringView y2 = attributes.value(QLatin1String(
"y2"));
2103 nx1 = convertToNumber(x1, tiny12FeaturesOnly);
2105 ny1 = convertToNumber(y1, tiny12FeaturesOnly);
2107 nx2 = convertToNumber(x2, tiny12FeaturesOnly);
2109 ny2 = convertToNumber(y2, tiny12FeaturesOnly);
2112 grad->setInterpolationMode(QGradient::ComponentInterpolation);
2114 QSvgGradientPaintSharedPtr paintServer = std::make_shared<QSvgGradientPaint>(std::move(grad));
2115 parseBaseGradient(attributes, paintServer.get(), handler);
2121 const QXmlStreamAttributes &attributes,
2124 Q_UNUSED(parent); Q_UNUSED(attributes);
2129 const QXmlStreamAttributes &attributes,
2132 Q_UNUSED(parent); Q_UNUSED(attributes);
2137 const QXmlStreamAttributes &attributes,
2140 Q_UNUSED(parent); Q_UNUSED(attributes);
2145 const QXmlStreamAttributes &,
2152 const QXmlStreamAttributes &attributes,
2153 QSvgHandler *handler)
2155 constexpr bool tiny12FeaturesOnly =
false;
2156 const QStringView x = attributes.value(QLatin1String(
"x"));
2157 const QStringView y = attributes.value(QLatin1String(
"y"));
2158 const QStringView width = attributes.value(QLatin1String(
"width"));
2159 const QStringView height = attributes.value(QLatin1String(
"height"));
2160 const QStringView mU = attributes.value(QLatin1String(
"maskUnits"));
2161 const QStringView mCU = attributes.value(QLatin1String(
"maskContentUnits"));
2163 QtSvg::
UnitTypes nmU = mU.contains(QLatin1String(
"userSpaceOnUse")) ?
2166 QtSvg::
UnitTypes nmCU = mCU.contains(QLatin1String(
"objectBoundingBox")) ?
2170 QGuiSvg::LengthType type;
2176 qreal nx = QGuiSvg::parseLength(x, &type, &ok, tiny12FeaturesOnly);
2177 nx = QGuiSvg::convertToPixels(nx,
true, type);
2178 if (x.isEmpty() || !ok) {
2181 }
else if (type == QGuiSvg::LengthType::LT_PERCENT && nmU == QtSvg::UnitTypes::userSpaceOnUse) {
2182 nx = nx / 100. * handler->document()->viewBox().width();
2183 }
else if (type == QGuiSvg::LengthType::LT_PERCENT) {
2187 qreal ny = QGuiSvg::parseLength(y, &type, &ok, tiny12FeaturesOnly);
2188 ny = QGuiSvg::convertToPixels(ny,
true, type);
2189 if (y.isEmpty() || !ok) {
2192 }
else if (type == QGuiSvg::LengthType::LT_PERCENT && nmU == QtSvg::UnitTypes::userSpaceOnUse) {
2193 ny = ny / 100. * handler->document()->viewBox().height();
2194 }
else if (type == QGuiSvg::LengthType::LT_PERCENT) {
2198 qreal nwidth = QGuiSvg::parseLength(width, &type, &ok, tiny12FeaturesOnly);
2199 nwidth = QGuiSvg::convertToPixels(nwidth,
true, type);
2200 if (width.isEmpty() || !ok) {
2203 }
else if (type == QGuiSvg::LengthType::LT_PERCENT && nmU == QtSvg::UnitTypes::userSpaceOnUse) {
2204 nwidth = nwidth / 100. * handler->document()->viewBox().width();
2205 }
else if (type == QGuiSvg::LengthType::LT_PERCENT) {
2206 nwidth = nwidth / 100.;
2209 qreal nheight = QGuiSvg::parseLength(height, &type, &ok, tiny12FeaturesOnly);
2210 nheight = QGuiSvg::convertToPixels(nheight,
true, type);
2211 if (height.isEmpty() || !ok) {
2214 }
else if (type == QGuiSvg::LengthType::LT_PERCENT && nmU == QtSvg::UnitTypes::userSpaceOnUse) {
2215 nheight = nheight / 100. * handler->document()->viewBox().height();
2216 }
else if (type == QGuiSvg::LengthType::LT_PERCENT) {
2217 nheight = nheight / 100.;
2220 QRectF bounds(nx, ny, nwidth, nheight);
2221 if (bounds.isEmpty())
2224 QSvgNode *mask =
new QSvgMask(parent, QSvgRectF(bounds, nmUx, nmUy, nmUw, nmUh), nmCU);
2231 const QStringView xStr = attributes.value(QLatin1String(
"x"));
2232 const QStringView yStr = attributes.value(QLatin1String(
"y"));
2233 const QStringView widthStr = attributes.value(QLatin1String(
"width"));
2234 const QStringView heightStr = attributes.value(QLatin1String(
"height"));
2237 if (!xStr.isEmpty()) {
2238 QGuiSvg::LengthType type;
2239 x = QGuiSvg::parseLength(xStr, &type);
2240 if (type != QGuiSvg::LengthType::LT_PT) {
2241 x = QGuiSvg::convertToPixels(x,
true, type);
2244 if (type == QGuiSvg::LengthType::LT_PERCENT) {
2251 if (!yStr.isEmpty()) {
2252 QGuiSvg::LengthType type;
2253 y = QGuiSvg::parseLength(yStr, &type);
2254 if (type != QGuiSvg::LengthType::LT_PT) {
2255 y = QGuiSvg::convertToPixels(y,
false, type);
2258 if (type == QGuiSvg::LengthType::LT_PERCENT) {
2265 if (!widthStr.isEmpty()) {
2266 QGuiSvg::LengthType type;
2267 width = QGuiSvg::parseLength(widthStr, &type);
2268 if (type != QGuiSvg::LengthType::LT_PT) {
2269 width = QGuiSvg::convertToPixels(width,
true, type);
2272 if (type == QGuiSvg::LengthType::LT_PERCENT) {
2276 rect->setWidth(width);
2279 if (!heightStr.isEmpty()) {
2280 QGuiSvg::LengthType type;
2281 height = QGuiSvg::parseLength(heightStr, &type);
2282 if (type != QGuiSvg::LengthType::LT_PT) {
2283 height = QGuiSvg::convertToPixels(height,
false, type);
2286 if (type == QGuiSvg::LengthType::LT_PERCENT) {
2290 rect->setHeight(height);
2295 const QXmlStreamAttributes &attributes,
2296 QSvgHandler *handler)
2298 const QStringView fU = attributes.value(QLatin1String(
"filterUnits"));
2299 const QStringView pU = attributes.value(QLatin1String(
"primitiveUnits"));
2301 const QtSvg::
UnitTypes filterUnits = fU.contains(QLatin1String(
"userSpaceOnUse")) ?
2304 const QtSvg::
UnitTypes primitiveUnits = pU.contains(QLatin1String(
"objectBoundingBox")) ?
2312 qreal width = handler->document()->viewBox().width();
2313 qreal height = handler->document()->viewBox().height();
2314 rect = QSvgRectF(QRectF(-0.1 * width, -0.1 * height, 1.2 * width, 1.2 * height),
2318 rect = QSvgRectF(QRectF(-0.1, -0.1, 1.2, 1.2),
2323 parseFilterBounds(attributes, &rect);
2325 QSvgNode *filter =
new QSvgFilterContainer(parent, rect, filterUnits, primitiveUnits);
2330 QString *outString, QSvgRectF *rect)
2332 *inString = attributes.value(QLatin1String(
"in")).toString();
2333 *outString = attributes.value(QLatin1String(
"result")).toString();
2339 *rect = QSvgRectF(QRectF(0, 0, 1.0, 1.0),
2345 parseFilterBounds(attributes, rect);
2349 const QXmlStreamAttributes &attributes,
2352 const QStringView typeString = attributes.value(QLatin1String(
"type"));
2353 const QStringView valuesString = attributes.value(QLatin1String(
"values"));
2355 QString inputString;
2356 QString outputString;
2359 QSvgFeColorMatrix::ColorShiftType type;
2360 QSvgFeColorMatrix::Matrix values;
2363 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2365 if (typeString.startsWith(QLatin1String(
"saturate")))
2366 type = QSvgFeColorMatrix::ColorShiftType::Saturate;
2367 else if (typeString.startsWith(QLatin1String(
"hueRotate")))
2368 type = QSvgFeColorMatrix::ColorShiftType::HueRotate;
2369 else if (typeString.startsWith(QLatin1String(
"luminanceToAlpha")))
2370 type = QSvgFeColorMatrix::ColorShiftType::LuminanceToAlpha;
2372 type = QSvgFeColorMatrix::ColorShiftType::Matrix;
2374 if (!valuesString.isEmpty()) {
2375 const auto valueStringList = splitWithDelimiter(valuesString);
2376 for (
int i = 0, j = 0; i < qMin(20, valueStringList.size()); i++) {
2378 qreal v = QGuiSvg::toDouble(valueStringList.at(i), &ok);
2380 values.data()[j] = v;
2385 values.setToIdentity();
2388 QSvgNode *filter =
new QSvgFeColorMatrix(parent, inputString, outputString, rect,
2394 const QXmlStreamAttributes &attributes,
2397 const QStringView edgeModeString = attributes.value(QLatin1String(
"edgeMode"));
2398 const QStringView stdDeviationString = attributes.value(QLatin1String(
"stdDeviation"));
2400 QString inputString;
2401 QString outputString;
2404 QSvgFeGaussianBlur::EdgeMode edgemode = QSvgFeGaussianBlur::EdgeMode::Duplicate;
2406 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2407 qreal stdDeviationX = 0;
2408 qreal stdDeviationY = 0;
2410 stdDeviationX = qMax(0., QGuiSvg::toDouble(stdDeviationString.split(u" ").constFirst()));
2411 stdDeviationY = qMax(0., QGuiSvg::toDouble(stdDeviationString.split(u" ").constLast()));
2413 stdDeviationY = stdDeviationX = qMax(0., QGuiSvg::toDouble(stdDeviationString));
2416 if (edgeModeString.startsWith(QLatin1String(
"wrap")))
2417 edgemode = QSvgFeGaussianBlur::EdgeMode::Wrap;
2418 else if (edgeModeString.startsWith(QLatin1String(
"none")))
2419 edgemode = QSvgFeGaussianBlur::EdgeMode::None;
2421 QSvgNode *filter =
new QSvgFeGaussianBlur(parent, inputString, outputString, rect,
2422 stdDeviationX, stdDeviationY, edgemode);
2427 const QXmlStreamAttributes &attributes,
2430 QStringView dxString = attributes.value(QLatin1String(
"dx"));
2431 QStringView dyString = attributes.value(QLatin1String(
"dy"));
2433 QString inputString;
2434 QString outputString;
2437 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2440 if (!dxString.isEmpty()) {
2441 QGuiSvg::LengthType type;
2442 dx = QGuiSvg::parseLength(dxString, &type);
2443 if (type != QGuiSvg::LengthType::LT_PT)
2444 dx = QGuiSvg::convertToPixels(dx,
true, type);
2448 if (!dyString.isEmpty()) {
2449 QGuiSvg::LengthType type;
2450 dy = QGuiSvg::parseLength(dyString, &type);
2451 if (type != QGuiSvg::LengthType::LT_PT)
2452 dy = QGuiSvg::convertToPixels(dy,
true, type);
2455 QSvgNode *filter =
new QSvgFeOffset(parent, inputString, outputString, rect,
2461 const QXmlStreamAttributes &attributes,
2464 const QStringView in2String = attributes.value(QLatin1String(
"in2"));
2465 const QStringView operatorString = attributes.value(QLatin1String(
"operator"));
2466 const QStringView k1String = attributes.value(QLatin1String(
"k1"));
2467 const QStringView k2String = attributes.value(QLatin1String(
"k2"));
2468 const QStringView k3String = attributes.value(QLatin1String(
"k3"));
2469 const QStringView k4String = attributes.value(QLatin1String(
"k4"));
2471 QString inputString;
2472 QString outputString;
2475 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2477 QSvgFeComposite::Operator op = QSvgFeComposite::Operator::Over;
2478 if (operatorString.startsWith(QLatin1String(
"in")))
2479 op = QSvgFeComposite::Operator::In;
2480 else if (operatorString.startsWith(QLatin1String(
"out")))
2481 op = QSvgFeComposite::Operator::Out;
2482 else if (operatorString.startsWith(QLatin1String(
"atop")))
2483 op = QSvgFeComposite::Operator::Atop;
2484 else if (operatorString.startsWith(QLatin1String(
"xor")))
2485 op = QSvgFeComposite::Operator::Xor;
2486 else if (operatorString.startsWith(QLatin1String(
"lighter")))
2487 op = QSvgFeComposite::Operator::Lighter;
2488 else if (operatorString.startsWith(QLatin1String(
"arithmetic")))
2489 op = QSvgFeComposite::Operator::Arithmetic;
2491 QVector4D k(0, 0, 0, 0);
2493 if (op == QSvgFeComposite::Operator::Arithmetic) {
2495 qreal v = QGuiSvg::toDouble(k1String, &ok);
2498 v = QGuiSvg::toDouble(k2String, &ok);
2501 v = QGuiSvg::toDouble(k3String, &ok);
2504 v = QGuiSvg::toDouble(k4String, &ok);
2509 QSvgNode *filter =
new QSvgFeComposite(parent, inputString, outputString, rect,
2510 in2String.toString(), op, k);
2516 const QXmlStreamAttributes &attributes,
2519 QString inputString;
2520 QString outputString;
2523 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2525 QSvgNode *filter =
new QSvgFeMerge(parent, inputString, outputString, rect);
2530 const QXmlStreamAttributes &attributes,
2531 QSvgHandler *handler)
2533 QStringView colorStr = attributes.value(QLatin1String(
"flood-color"));
2534 const QStringView opacityStr = attributes.value(QLatin1String(
"flood-opacity"));
2537 if (!constructColor(colorStr, opacityStr, color, handler)) {
2538 color = QColor(Qt::black);
2539 if (opacityStr.isEmpty())
2540 color.setAlphaF(1.0);
2542 setAlpha(opacityStr, &color);
2545 QString inputString;
2546 QString outputString;
2549 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2551 QSvgNode *filter =
new QSvgFeFlood(parent, inputString, outputString, rect, color);
2556 const QXmlStreamAttributes &attributes,
2559 QString inputString;
2560 QString outputString;
2563 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2565 QSvgNode *filter =
new QSvgFeMergeNode(parent, inputString, outputString, rect);
2570 const QXmlStreamAttributes &attributes,
2573 const QStringView in2String = attributes.value(QLatin1String(
"in2"));
2574 const QStringView modeString = attributes.value(QLatin1String(
"mode"));
2576 QString inputString;
2577 QString outputString;
2580 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2582 QSvgFeBlend::Mode mode = QSvgFeBlend::Mode::Normal;
2583 if (modeString.startsWith(QLatin1StringView(
"multiply")))
2584 mode = QSvgFeBlend::Mode::Multiply;
2585 else if (modeString.startsWith(QLatin1StringView(
"screen")))
2586 mode = QSvgFeBlend::Mode::Screen;
2587 else if (modeString.startsWith(QLatin1StringView(
"darken")))
2588 mode = QSvgFeBlend::Mode::Darken;
2589 else if (modeString.startsWith(QLatin1StringView(
"lighten")))
2590 mode = QSvgFeBlend::Mode::Lighten;
2592 QSvgNode *filter =
new QSvgFeBlend(parent, inputString, outputString, rect,
2593 in2String.toString(), mode);
2598 const QXmlStreamAttributes &attributes,
2601 QString inputString;
2602 QString outputString;
2605 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2607 QSvgNode *filter =
new QSvgFeUnsupported(parent, inputString, outputString, rect);
2613 QList<QStringView> viewBoxValues;
2616 viewBoxValues = splitWithDelimiter(str);
2617 if (viewBoxValues.size() == 4) {
2618 QGuiSvg::LengthType type;
2619 qreal x = QGuiSvg::parseLength(viewBoxValues.at(0).trimmed(), &type);
2620 qreal y = QGuiSvg::parseLength(viewBoxValues.at(1).trimmed(), &type);
2621 qreal w = QGuiSvg::parseLength(viewBoxValues.at(2).trimmed(), &type);
2622 qreal h = QGuiSvg::parseLength(viewBoxValues.at(3).trimmed(), &type);
2623 return QRectF(x, y, w, h);
2625 return std::nullopt;
2629 QRectF *rect, QRectF *viewBox, QPointF *refPoint,
2630 QSvgSymbolLike::PreserveAspectRatios *aspect,
2631 QSvgSymbolLike::Overflow *overflow,
2632 bool marker =
false)
2634 const QStringView xStr = attributes.value(QLatin1String(
"x"));
2635 const QStringView yStr = attributes.value(QLatin1String(
"y"));
2636 const QStringView refXStr = attributes.value(QLatin1String(
"refX"));
2637 const QStringView refYStr = attributes.value(QLatin1String(
"refY"));
2638 const QStringView widthStr = attributes.value(marker ? QLatin1String(
"markerWidth")
2639 : QLatin1String(
"width"));
2640 const QStringView heightStr = attributes.value(marker ? QLatin1String(
"markerHeight")
2641 : QLatin1String(
"height"));
2642 const QStringView pAspectRStr = attributes.value(QLatin1String(
"preserveAspectRatio"));
2643 const QStringView overflowStr = attributes.value(QLatin1String(
"overflow"));
2644 const QStringView viewBoxStr = attributes.value(QLatin1String(
"viewBox"));
2648 if (!xStr.isEmpty()) {
2649 QGuiSvg::LengthType type;
2650 x = QGuiSvg::parseLength(xStr, &type);
2651 if (type != QGuiSvg::LengthType::LT_PT)
2652 x = QGuiSvg::convertToPixels(x,
true, type);
2655 if (!yStr.isEmpty()) {
2656 QGuiSvg::LengthType type;
2657 y = QGuiSvg::parseLength(yStr, &type);
2658 if (type != QGuiSvg::LengthType::LT_PT)
2659 y = QGuiSvg::convertToPixels(y,
false, type);
2662 if (!widthStr.isEmpty()) {
2663 QGuiSvg::LengthType type;
2664 width = QGuiSvg::parseLength(widthStr, &type);
2665 if (type != QGuiSvg::LengthType::LT_PT)
2666 width = QGuiSvg::convertToPixels(width,
true, type);
2669 if (!heightStr.isEmpty()) {
2670 QGuiSvg::LengthType type;
2671 height = QGuiSvg::parseLength(heightStr, &type);
2672 if (type != QGuiSvg::LengthType::LT_PT)
2673 height = QGuiSvg::convertToPixels(height,
false, type);
2676 *rect = QRectF(x, y, width, height);
2679 if (!refXStr.isEmpty()) {
2680 QGuiSvg::LengthType type;
2681 x = QGuiSvg::parseLength(refXStr, &type);
2682 if (type != QGuiSvg::LengthType::LT_PT)
2683 x = QGuiSvg::convertToPixels(x,
true, type);
2686 if (!refYStr.isEmpty()) {
2687 QGuiSvg::LengthType type;
2688 y = QGuiSvg::parseLength(refYStr, &type);
2689 if (type != QGuiSvg::LengthType::LT_PT)
2690 y = QGuiSvg::convertToPixels(y,
false, type);
2692 *refPoint = QPointF(x,y);
2694 auto viewBoxResult = parseViewBox(viewBoxStr);
2696 *viewBox = *viewBoxResult;
2697 else if (width > 0 && height > 0)
2698 *viewBox = QRectF(0, 0, width, height);
2700 *viewBox = handler->document()->viewBox();
2702 if (viewBox->isNull())
2705 auto pAspectRStrs = pAspectRStr.split(u" ");
2706 QSvgSymbolLike::PreserveAspectRatio aspectX = QSvgSymbolLike::PreserveAspectRatio::xMid;
2707 QSvgSymbolLike::PreserveAspectRatio aspectY = QSvgSymbolLike::PreserveAspectRatio::yMid;
2708 QSvgSymbolLike::PreserveAspectRatio aspectMS = QSvgSymbolLike::PreserveAspectRatio::meet;
2710 for (
auto &pAStr : std::as_const(pAspectRStrs)) {
2711 if (pAStr.startsWith(QLatin1String(
"none"))) {
2712 aspectX = QSvgSymbolLike::PreserveAspectRatio::None;
2713 aspectY = QSvgSymbolLike::PreserveAspectRatio::None;
2715 if (pAStr.startsWith(QLatin1String(
"xMin")))
2716 aspectX = QSvgSymbolLike::PreserveAspectRatio::xMin;
2717 else if (pAStr.startsWith(QLatin1String(
"xMax")))
2718 aspectX = QSvgSymbolLike::PreserveAspectRatio::xMax;
2719 if (pAStr.endsWith(QLatin1String(
"YMin")))
2720 aspectY = QSvgSymbolLike::PreserveAspectRatio::yMin;
2721 else if (pAStr.endsWith(QLatin1String(
"YMax")))
2722 aspectY = QSvgSymbolLike::PreserveAspectRatio::yMax;
2725 if (pAStr.endsWith(QLatin1String(
"slice")))
2726 aspectMS = QSvgSymbolLike::PreserveAspectRatio::slice;
2728 *aspect = aspectX | aspectY | aspectMS;
2735 *overflow = QSvgSymbolLike::Overflow::Hidden;
2737 if (overflowStr.endsWith(QLatin1String(
"auto")))
2738 *overflow = QSvgSymbolLike::Overflow::Auto;
2739 else if (overflowStr.endsWith(QLatin1String(
"visible")))
2740 *overflow = QSvgSymbolLike::Overflow::Visible;
2741 else if (overflowStr.endsWith(QLatin1String(
"hidden")))
2742 *overflow = QSvgSymbolLike::Overflow::Hidden;
2743 else if (overflowStr.endsWith(QLatin1String(
"scroll")))
2744 *overflow = QSvgSymbolLike::Overflow::Scroll;
2750 const QXmlStreamAttributes &attributes,
2751 QSvgHandler *handler)
2753 QRectF rect, viewBox;
2755 QSvgSymbolLike::PreserveAspectRatios aspect;
2756 QSvgSymbolLike::Overflow overflow;
2758 if (!parseSymbolLikeAttributes(attributes, handler, &rect, &viewBox, &refP, &aspect, &overflow))
2761 refP = QPointF(0, 0);
2762 QSvgNode *symbol =
new QSvgSymbol(parent, rect, viewBox, refP, aspect, overflow);
2767 const QXmlStreamAttributes &attributes,
2768 QSvgHandler *handler)
2770 QRectF rect, viewBox;
2772 QSvgSymbolLike::PreserveAspectRatios aspect;
2773 QSvgSymbolLike::Overflow overflow;
2775 const QStringView orientStr = attributes.value(QLatin1String(
"orient"));
2776 const QStringView markerUnitsStr = attributes.value(QLatin1String(
"markerUnits"));
2778 qreal orientationAngle = 0;
2779 QSvgMarker::Orientation orientation;
2780 if (orientStr.startsWith(QLatin1String(
"auto-start-reverse")))
2781 orientation = QSvgMarker::Orientation::AutoStartReverse;
2782 else if (orientStr.startsWith(QLatin1String(
"auto")))
2783 orientation = QSvgMarker::Orientation::Auto;
2785 orientation = QSvgMarker::Orientation::Value;
2788 if (orientStr.endsWith(QLatin1String(
"turn")))
2789 a = 360. * QGuiSvg::toDouble(orientStr.mid(0, orientStr.length()-4), &ok);
2790 else if (orientStr.endsWith(QLatin1String(
"grad")))
2791 a = QGuiSvg::toDouble(orientStr.mid(0, orientStr.length()-4), &ok);
2792 else if (orientStr.endsWith(QLatin1String(
"rad")))
2793 a = 180. /
M_PI * QGuiSvg::toDouble(orientStr.mid(0, orientStr.length()-3), &ok);
2795 a = QGuiSvg::toDouble(orientStr, &ok);
2797 orientationAngle = a;
2800 QSvgMarker::MarkerUnits markerUnits = QSvgMarker::MarkerUnits::StrokeWidth;
2801 if (markerUnitsStr.startsWith(QLatin1String(
"userSpaceOnUse")))
2802 markerUnits = QSvgMarker::MarkerUnits::UserSpaceOnUse;
2804 if (!parseSymbolLikeAttributes(attributes, handler, &rect, &viewBox, &refP, &aspect, &overflow,
true))
2807 QSvgNode *marker =
new QSvgMarker(parent, rect, viewBox, refP, aspect, overflow,
2808 orientation, orientationAngle, markerUnits);
2813 const QXmlStreamAttributes &attributes,
2814 QSvgHandler *handler)
2816 QStringView data = attributes.value(QLatin1String(
"d"));
2818 std::optional<QPainterPath> qpath = QGuiSvg::parsePath(data,
2819 !handler->trustedSourceMode());
2821 qCWarning(lcSvgHandler,
"Invalid path data; path truncated.");
2825 qpath.value().setFillRule(Qt::WindingFill);
2826 QSvgNode *path =
new QSvgPath(parent, qpath.value());
2831 const QXmlStreamAttributes &attributes,
2834 QStringView pointsStr = attributes.value(QLatin1String(
"points"));
2835 const QList<qreal> points = parseNumbersList(&pointsStr);
2836 if (points.size() < 4)
2839 for (
int i = 0; i < poly.size(); ++i)
2840 poly[i] = QPointF(points.at(2 * i), points.at(2 * i + 1));
2842 return new QSvgPolyline(parent, poly);
2844 return new QSvgPolygon(parent, poly);
2848 const QXmlStreamAttributes &attributes,
2851 return createPolyNode(parent, attributes,
false);
2855 const QXmlStreamAttributes &attributes,
2858 return createPolyNode(parent, attributes,
true);
2862 const QXmlStreamAttributes &attributes,
2865 Q_UNUSED(parent); Q_UNUSED(attributes);
2870 QSvgHandler *handler)
2872 const QStringView cx = attributes.value(QLatin1String(
"cx"));
2873 const QStringView cy = attributes.value(QLatin1String(
"cy"));
2874 const QStringView r = attributes.value(QLatin1String(
"r"));
2875 const QStringView fx = attributes.value(QLatin1String(
"fx"));
2876 const QStringView fy = attributes.value(QLatin1String(
"fy"));
2882 ncx = convertToNumber(cx, tiny12FeaturesOnly);
2884 ncy = convertToNumber(cy, tiny12FeaturesOnly);
2888 nr = convertToNumber(r, tiny12FeaturesOnly);
2894 nfx = convertToNumber(fx, tiny12FeaturesOnly);
2897 nfy = convertToNumber(fy, tiny12FeaturesOnly);
2900 grad->setInterpolationMode(QGradient::ComponentInterpolation);
2902 QSvgGradientPaintSharedPtr paintServer = std::make_shared<QSvgGradientPaint>(std::move(grad));
2903 parseBaseGradient(attributes, paintServer.get(), handler);
2909 const QXmlStreamAttributes &attributes,
2912 const QStringView x = attributes.value(QLatin1String(
"x"));
2913 const QStringView y = attributes.value(QLatin1String(
"y"));
2914 const QStringView width = attributes.value(QLatin1String(
"width"));
2915 const QStringView height = attributes.value(QLatin1String(
"height"));
2916 const QStringView rx = attributes.value(QLatin1String(
"rx"));
2917 const QStringView ry = attributes.value(QLatin1String(
"ry"));
2920 QGuiSvg::LengthType type;
2921 qreal nwidth = QGuiSvg::parseLength(width, &type, &ok);
2924 nwidth = QGuiSvg::convertToPixels(nwidth,
true, type);
2925 qreal nheight = QGuiSvg::parseLength(height, &type, &ok);
2928 nheight = QGuiSvg::convertToPixels(nheight,
true, type);
2929 qreal nrx = QGuiSvg::toDouble(rx);
2930 qreal nry = QGuiSvg::toDouble(ry);
2932 QRectF bounds(QGuiSvg::toDouble(x), QGuiSvg::toDouble(y), nwidth, nheight);
2933 if (bounds.isEmpty())
2936 if (!rx.isEmpty() && ry.isEmpty())
2938 else if (!ry.isEmpty() && rx.isEmpty())
2944 if (nrx > bounds.width()/2)
2945 nrx = bounds.width()/2;
2946 if (nry > bounds.height()/2)
2947 nry = bounds.height()/2;
2952 nrx *= (100/(bounds.width()/2));
2953 nry *= (100/(bounds.height()/2));
2955 QSvgNode *rect =
new QSvgRect(parent, bounds, nrx, nry);
2960 const QXmlStreamAttributes &attributes,
2963 Q_UNUSED(parent); Q_UNUSED(attributes);
2968 const QXmlStreamAttributes &attributes,
2971 Q_UNUSED(parent); Q_UNUSED(attributes);
2976 QSvgHandler *handler)
2978 Q_UNUSED(attributes);
2979 QStringView solidColorStr = attributes.value(QLatin1String(
"solid-color"));
2980 QStringView solidOpacityStr = attributes.value(QLatin1String(
"solid-opacity"));
2982 if (solidOpacityStr.isEmpty())
2983 solidOpacityStr = attributes.value(QLatin1String(
"opacity"));
2986 if (!constructColor(solidColorStr, solidOpacityStr, color, handler))
2988 std::shared_ptr<QSvgSolidColorPaint> paintServer = std::make_shared<QSvgSolidColorPaint>(color);
2993 const QXmlStreamAttributes &attributes,
2994 QSvgHandler *handler)
2996 if (paintServer->type() != QSvgPaintServer::Type::Gradient)
2998 QString nodeIdStr = someId(attributes);
2999 QString xmlClassStr = attributes.value(QLatin1String(
"class")).toString();
3006 dummy.setNodeId(nodeIdStr);
3007 dummy.setXmlClass(xmlClassStr);
3011#ifndef QT_NO_CSSPARSER
3012 QXmlStreamAttributes cssAttributes;
3013 handler->cssHandler().styleLookup(&dummy, cssAttributes);
3014 attrs.setAttributes(cssAttributes, handler);
3016 QXmlStreamAttributes styleCssAttributes;
3017 QStringView style = attributes.value(QLatin1String(
"style"));
3018 if (!style.isEmpty())
3019 handler->cssHandler().parseCSStoXMLAttrs(style.toString(), styleCssAttributes);
3020 attrs.setAttributes(styleCssAttributes, handler);
3024 parseColor(&dummy, attrs, handler);
3026 QSvgGradientPaint *gradientStyle =
static_cast<QSvgGradientPaint*>(paintServer);
3027 QStringView colorStr = attrs.stopColor;
3031 qreal offset = convertToNumber(attrs.offset,
3032 handler->options().testFlag(QtSvg::Tiny12FeaturesOnly), &ok);
3036 if (!constructColor(colorStr, attrs.stopOpacity, color, handler)) {
3038 if (!attrs.stopOpacity.isEmpty())
3039 setAlpha(attrs.stopOpacity, &color);
3042 QGradient *grad = gradientStyle->qgradient();
3044 offset = qMin(qreal(1), qMax(qreal(0), offset));
3046 if (gradientStyle->gradientStopsSet()) {
3047 stops = grad->stops();
3049 if (offset <= stops.back().first)
3050 offset = stops.back().first + FLT_EPSILON;
3055 if ((stops.size() == 1) || (stops.at(stops.size() - 2).first < 1.0 - FLT_EPSILON)) {
3056 stops.back().first = 1.0 - FLT_EPSILON;
3057 grad->setStops(stops);
3062 grad->setColorAt(offset, color);
3063 gradientStyle->setGradientStopsSet(
true);
3068 const QXmlStreamAttributes &attributes,
3069 QSvgHandler *handler)
3073 const QStringView type = attributes.value(QLatin1String(
"type"));
3074 if (type.compare(QLatin1String(
"text/css"), Qt::CaseInsensitive) == 0 || type.isNull())
3075 handler->readStyle();
3081 const QXmlStreamAttributes &attributes,
3082 QSvgHandler *handler)
3084 Q_UNUSED(parent); Q_UNUSED(attributes);
3086 QSvgDocument *node =
new QSvgDocument(handler->options(), handler->animatorType());
3087 const QStringView widthStr = attributes.value(QLatin1String(
"width"));
3088 const QStringView heightStr = attributes.value(QLatin1String(
"height"));
3089 const QStringView viewBoxStr = attributes.value(QLatin1String(
"viewBox"));
3091 QGuiSvg::LengthType type = QGuiSvg::LengthType::LT_PX;
3093 if (!widthStr.isEmpty()) {
3094 width = QGuiSvg::parseLength(widthStr, &type);
3095 if (type != QGuiSvg::LengthType::LT_PT)
3096 width = QGuiSvg::convertToPixels(width,
true, type);
3097 node->setWidth(
int(width), type == QGuiSvg::LengthType::LT_PERCENT);
3100 if (!heightStr.isEmpty()) {
3101 height = QGuiSvg::parseLength(heightStr, &type);
3102 if (type != QGuiSvg::LengthType::LT_PT)
3103 height = QGuiSvg::convertToPixels(height,
false, type);
3104 node->setHeight(
int(height), type == QGuiSvg::LengthType::LT_PERCENT);
3107 auto viewBoxResult = parseViewBox(viewBoxStr);
3108 if (viewBoxResult) {
3109 node->setViewBox(*viewBoxResult);
3110 }
else if (width && height) {
3111 if (type == QGuiSvg::LengthType::LT_PT) {
3112 width = QGuiSvg::convertToPixels(width,
false, type);
3113 height = QGuiSvg::convertToPixels(height,
false, type);
3115 node->setViewBox(QRectF(0, 0, width, height));
3117 handler->setDefaultCoordinateSystem(QGuiSvg::LengthType::LT_PX);
3123 const QXmlStreamAttributes &attributes,
3126 Q_UNUSED(attributes);
3127 QSvgSwitch *node =
new QSvgSwitch(parent);
3132 const QXmlStreamAttributes &attributes,
3133 QSvgHandler *handler)
3135 const QStringView x = attributes.value(QLatin1String(
"x"));
3136 const QStringView y = attributes.value(QLatin1String(
"y"));
3137 const QStringView width = attributes.value(QLatin1String(
"width"));
3138 const QStringView height = attributes.value(QLatin1String(
"height"));
3139 const QStringView patternUnits = attributes.value(QLatin1String(
"patternUnits"));
3140 const QStringView patternContentUnits = attributes.value(QLatin1String(
"patternContentUnits"));
3141 const QStringView patternTransform = attributes.value(QLatin1String(
"patternTransform"));
3143 QtSvg::
UnitTypes nPatternUnits = patternUnits.contains(QLatin1String(
"userSpaceOnUse")) ?
3146 QtSvg::
UnitTypes nPatternContentUnits = patternContentUnits.contains(QLatin1String(
"objectBoundingBox")) ?
3149 const QStringView viewBoxStr = attributes.value(QLatin1String(
"viewBox"));
3152 QGuiSvg::LengthType type;
3154 qreal nx = QGuiSvg::parseLength(x, &type, &ok);
3155 nx = QGuiSvg::convertToPixels(nx,
true, type);
3158 else if (type == QGuiSvg::LengthType::LT_PERCENT && nPatternUnits == QtSvg::UnitTypes::userSpaceOnUse)
3159 nx = (nx / 100.) * handler->document()->viewBox().width();
3160 else if (type == QGuiSvg::LengthType::LT_PERCENT)
3163 qreal ny = QGuiSvg::parseLength(y, &type, &ok);
3164 ny = QGuiSvg::convertToPixels(ny,
true, type);
3167 else if (type == QGuiSvg::LengthType::LT_PERCENT && nPatternUnits == QtSvg::UnitTypes::userSpaceOnUse)
3168 ny = (ny / 100.) * handler->document()->viewBox().height();
3169 else if (type == QGuiSvg::LengthType::LT_PERCENT)
3172 qreal nwidth = QGuiSvg::parseLength(width, &type, &ok);
3173 nwidth = QGuiSvg::convertToPixels(nwidth,
true, type);
3176 else if (type == QGuiSvg::LengthType::LT_PERCENT && nPatternUnits == QtSvg::UnitTypes::userSpaceOnUse)
3177 nwidth = (nwidth / 100.) * handler->document()->viewBox().width();
3178 else if (type == QGuiSvg::LengthType::LT_PERCENT)
3179 nwidth = nwidth / 100.;
3181 qreal nheight = QGuiSvg::parseLength(height, &type, &ok);
3182 nheight = QGuiSvg::convertToPixels(nheight,
true, type);
3185 else if (type == QGuiSvg::LengthType::LT_PERCENT && nPatternUnits == QtSvg::UnitTypes::userSpaceOnUse)
3186 nheight = (nheight / 100.) * handler->document()->viewBox().height();
3187 else if (type == QGuiSvg::LengthType::LT_PERCENT)
3188 nheight = nheight / 100.;
3191 auto viewBoxResult = parseViewBox(viewBoxStr);
3192 if (viewBoxResult) {
3193 if (viewBoxResult->width() > 0 && viewBoxResult->height() > 0)
3194 viewBox = *viewBoxResult;
3198 if (!patternTransform.isEmpty())
3199 matrix = parseTransformationMatrix(patternTransform);
3201 QRectF bounds(nx, ny, nwidth, nheight);
3202 if (bounds.isEmpty())
3205 QSvgRectF patternRectF(bounds, nPatternUnits, nPatternUnits, nPatternUnits, nPatternUnits);
3206 QSvgPattern *node =
new QSvgPattern(parent, patternRectF, viewBox, nPatternContentUnits, matrix);
3209 QSvgPaintServerSharedPtr prop = std::make_shared<QSvgPatternPaint>(node);
3210 handler->document()->addPaintServer(
std::move(prop), someId(attributes));
3216 const QXmlStreamAttributes &,
3219 if (parent->type() != QSvgNode::Textarea)
3221 static_cast<QSvgText*>(parent)->addLineBreak();
3226 const QXmlStreamAttributes &attributes,
3229 const QStringView x = attributes.value(QLatin1String(
"x"));
3230 const QStringView y = attributes.value(QLatin1String(
"y"));
3232 QGuiSvg::LengthType type;
3233 qreal nx = QGuiSvg::parseLength(x, &type);
3234 nx = QGuiSvg::convertToPixels(nx,
true, type);
3235 qreal ny = QGuiSvg::parseLength(y, &type);
3236 ny = QGuiSvg::convertToPixels(ny,
true, type);
3238 QSvgNode *text =
new QSvgText(parent, QPointF(nx, ny));
3243 const QXmlStreamAttributes &attributes,
3244 QSvgHandler *handler)
3246 QSvgText *node =
static_cast<QSvgText *>(createTextNode(parent, attributes, handler));
3248 QGuiSvg::LengthType type;
3249 qreal width = QGuiSvg::parseLength(attributes.value(QLatin1String(
"width")), &type);
3250 qreal height = QGuiSvg::parseLength(attributes.value(QLatin1String(
"height")), &type);
3251 node->setTextArea(QSizeF(width, height));
3257 const QXmlStreamAttributes &attributes,
3258 QSvgHandler *handler)
3260 QSvgTspan *tspan =
new QSvgTspan(parent);
3267 const QStringView x = attributes.value(QLatin1String(
"x"));
3269 QGuiSvg::LengthType type;
3270 qreal nx = QGuiSvg::parseLength(x, &type);
3271 tspan->setX(QGuiSvg::convertToPixels(nx,
true, type));
3273 const QStringView y = attributes.value(QLatin1String(
"y"));
3275 QGuiSvg::LengthType type;
3276 qreal ny = QGuiSvg::parseLength(y, &type);
3277 tspan->setY(QGuiSvg::convertToPixels(ny,
false, type));
3283 const QXmlStreamAttributes &attributes,
3284 QSvgHandler *handler)
3286 QStringView linkId = attributes.value(QLatin1String(
"xlink:href"));
3287 const QStringView xStr = attributes.value(QLatin1String(
"x"));
3288 const QStringView yStr = attributes.value(QLatin1String(
"y"));
3290 if (linkId.isEmpty())
3291 linkId = attributes.value(QLatin1String(
"href"));
3292 QString linkIdStr = idFromIRI(linkId).toString();
3294 switch (parent->type()) {
3296 case QSvgNode::Defs:
3297 case QSvgNode::Group:
3298 case QSvgNode::Switch:
3299 case QSvgNode::Mask:
3300 case QSvgNode::Symbol:
3301 case QSvgNode::Marker:
3302 case QSvgNode::Pattern:
3305 qCWarning(lcSvgHandler,
"<use> element %ls in wrong context!",
qUtf16Printable(linkIdStr));
3310 if (!xStr.isNull() || !yStr.isNull()) {
3311 QGuiSvg::LengthType type;
3312 qreal nx = QGuiSvg::parseLength(xStr, &type);
3313 nx = QGuiSvg::convertToPixels(nx,
true, type);
3315 qreal ny = QGuiSvg::parseLength(yStr, &type);
3316 ny = QGuiSvg::convertToPixels(ny,
true, type);
3317 pt = QPointF(nx, ny);
3320 QSvgNode *link = handler->document()->namedNode(linkIdStr);
3322 if (parent->isDescendantOf(link))
3323 qCWarning(lcSvgHandler,
"link %ls is recursive!",
qUtf16Printable(linkIdStr));
3325 return new QSvgUse(pt, parent, link);
3329 return new QSvgUse(pt, parent, linkIdStr);
3333 const QXmlStreamAttributes &attributes,
3336 Q_UNUSED(parent); Q_UNUSED(attributes);
3340typedef QSvgNode *(*FactoryMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3347 QStringView ref = name.mid(1);
3348 switch (name.at(0).unicode()) {
3350 if (ref == QLatin1String(
"efs"))
return createDefsNode;
3356 if (ref.isEmpty())
return createGNode;
3363 if (ref == QLatin1String(
"vg"))
return createSvgNode;
3364 if (ref == QLatin1String(
"witch"))
return createSwitchNode;
3382 QStringView ref = name.mid(1);
3383 switch (name.at(0).unicode()) {
3385 if (ref == QLatin1String(
"ircle"))
return createCircleNode;
3388 if (ref == QLatin1String(
"llipse"))
return createEllipseNode;
3391 if (ref == QLatin1String(
"mage"))
return createImageNode;
3394 if (ref == QLatin1String(
"ine"))
return createLineNode;
3397 if (ref == QLatin1String(
"ath"))
return createPathNode;
3398 if (ref == QLatin1String(
"olygon"))
return createPolygonNode;
3399 if (ref == QLatin1String(
"olyline"))
return createPolylineNode;
3402 if (ref == QLatin1String(
"ect"))
return createRectNode;
3405 if (ref == QLatin1String(
"ext"))
return createTextNode;
3406 if (ref == QLatin1String(
"extArea"))
return createTextAreaNode;
3407 if (ref == QLatin1String(
"span"))
return createTspanNode;
3410 if (ref == QLatin1String(
"se"))
return createUseNode;
3413 if (ref == QLatin1String(
"ideo"))
return createVideoNode;
3429 if (!name.startsWith(QLatin1String(
"fe")))
3432 if (name == QLatin1String(
"feMerge"))
return createFeMergeNode;
3433 if (name == QLatin1String(
"feColorMatrix"))
return createFeColorMatrixNode;
3434 if (name == QLatin1String(
"feGaussianBlur"))
return createFeGaussianBlurNode;
3435 if (name == QLatin1String(
"feOffset"))
return createFeOffsetNode;
3436 if (name == QLatin1String(
"feMergeNode"))
return createFeMergeNodeNode;
3437 if (name == QLatin1String(
"feComposite"))
return createFeCompositeNode;
3438 if (name == QLatin1String(
"feFlood"))
return createFeFloodNode;
3439 if (name == QLatin1String(
"feBlend"))
return createFeBlendNode;
3441 static const QStringList unsupportedFilters = {
3458 if (unsupportedFilters.contains(name))
3459 return createFeUnsupportedNode;
3464typedef QSvgNode *(*AnimationMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3471 QStringView ref = name.mid(1);
3473 switch (name.at(0).unicode()) {
3475 if (ref == QLatin1String(
"nimate"))
return createAnimateNode;
3476 if (ref == QLatin1String(
"nimateColor"))
return createAnimateColorNode;
3477 if (ref == QLatin1String(
"nimateMotion"))
return createAnimateMotionNode;
3478 if (ref == QLatin1String(
"nimateTransform"))
return createAnimateTransformNode;
3487typedef bool (*
ParseMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3494 QStringView ref = name.mid(1);
3495 switch (name.at(0).unicode()) {
3497 if (ref.isEmpty())
return parseAnchorNode;
3498 if (ref == QLatin1String(
"udio"))
return parseAudioNode;
3501 if (ref == QLatin1String(
"iscard"))
return parseDiscardNode;
3504 if (ref == QLatin1String(
"oreignObject"))
return parseForeignObjectNode;
3507 if (ref == QLatin1String(
"andler"))
return parseHandlerNode;
3508 if (ref == QLatin1String(
"kern"))
return parseHkernNode;
3511 if (ref == QLatin1String(
"etadata"))
return parseMetadataNode;
3512 if (ref == QLatin1String(
"path"))
return parseMpathNode;
3517 if (ref == QLatin1String(
"refetch"))
return parsePrefetchNode;
3520 if (ref == QLatin1String(
"cript"))
return parseScriptNode;
3521 if (ref == QLatin1String(
"et"))
return parseSetNode;
3522 if (ref == QLatin1String(
"tyle"))
return parseStyleNode;
3525 if (ref == QLatin1String(
"break"))
return parseTbreakNode;
3538 if (name ==
"font"_L1)
3539 return createFontNode;
3545 const QXmlStreamAttributes &,
3553 QStringView ref = name.mid(1);
3554 switch (name.at(0).unicode()) {
3556 if (ref == QLatin1String(
"ont-face"))
return parseFontFaceNode;
3557 if (ref == QLatin1String(
"ont-face-name"))
return parseFontFaceNameNode;
3558 if (ref == QLatin1String(
"ont-face-src"))
return parseFontFaceSrcNode;
3559 if (ref == QLatin1String(
"ont-face-uri"))
return parseFontFaceUriNode;
3562 if (ref == QLatin1String(
"lyph"))
return parseGlyphNode;
3565 if (ref == QLatin1String(
"issing-glyph"))
return parseMissingGlyphNode;
3581 QStringView ref = name.sliced(1);
3582 switch (name.at(0).unicode()) {
3584 if (ref == QLatin1String(
"inearGradient"))
return createLinearGradientNode;
3587 if (ref == QLatin1String(
"adialGradient"))
return createRadialGradientNode;
3590 if (ref == QLatin1String(
"olidColor"))
return createSolidColorNode;
3599 const QXmlStreamAttributes &,
3607 QStringView ref = name.sliced(1);
3608 switch (name.at(0).unicode()) {
3610 if (ref == QLatin1String(
"top"))
return parseStopNode;
3618QSvgHandler::QSvgHandler(QIODevice *device, QtSvg::Options options,
3619 QtSvg::AnimatorType type)
3620 : m_reader{std::make_unique<QXmlStreamReader>(device)}
3621 , xml{m_reader.get()}
3622 , m_options(options)
3623 , m_animatorType(type)
3628QSvgHandler::QSvgHandler(
const QByteArray &data, QtSvg::Options options,
3629 QtSvg::AnimatorType type)
3630 : m_reader{std::make_unique<QXmlStreamReader>(data)}
3631 , xml{m_reader.get()}
3632 , m_options(options)
3633 , m_animatorType(type)
3638QSvgHandler::QSvgHandler(QXmlStreamReader *
const reader, QtSvg::Options options,
3639 QtSvg::AnimatorType type)
3642 , m_options(options)
3643 , m_animatorType(type)
3648void QSvgHandler::init()
3650 m_defaultPen = QPen(Qt::black, 1, Qt::SolidLine, Qt::FlatCap, Qt::SvgMiterJoin);
3651 m_defaultPen.setMiterLimit(4);
3657 const QSvgFillStyle *fillStyle =
static_cast<
const QSvgFillStyle*>
3658 (node->styleProperty(QSvgStyleProperty::Fill));
3659 if (fillStyle && fillStyle->paintServer()
3660 && fillStyle->paintServer()->type() == QSvgPaintServer::Type::Pattern) {
3661 QSvgPatternPaint *patternStyle =
static_cast<QSvgPatternPaint *>(fillStyle->paintServer());
3662 if (linkable.contains(patternStyle->patternNode()))
3666 const QSvgStrokeStyle *strokeStyle =
static_cast<
const QSvgStrokeStyle*>
3667 (node->styleProperty(QSvgStyleProperty::Stroke));
3668 if (strokeStyle && strokeStyle->paintServer()
3669 && strokeStyle->paintServer()->type() == QSvgPaintServer::Type::Pattern) {
3670 QSvgPatternPaint *patternStyle =
static_cast<QSvgPatternPaint *>(strokeStyle->paintServer());
3671 if (linkable.contains(patternStyle->patternNode()))
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3700 QList<
const QSvgNode *> linkable;
3701 using NodeState =
std::pair<
const QSvgNode *,
bool>;
3702 QStack<NodeState> nodes;
3703 nodes.push({n,
false});
3706 auto current = nodes.pop();
3707 if (current.second) {
3708 Q_ASSERT(!linkable.isEmpty() && current.first == linkable.back());
3709 linkable.pop_back();
3713 switch (current.first->type()) {
3715 case QSvgNode::Group:
3716 case QSvgNode::Defs:
3717 case QSvgNode::Pattern:
3719 if (current.first->type() == QSvgNode::Pattern) {
3720 linkable.append(current.first);
3721 nodes.push({current.first,
true});
3723 auto *g =
static_cast<
const QSvgStructureNode*>(current.first);
3724 for (
auto it = g->renderers().crbegin(); it != g->renderers().crend(); it++)
3725 nodes.push({it->get(),
false});
3730 if (linkable.contains(current.first))
3732 auto *u =
static_cast<
const QSvgUse*>(current.first);
3733 auto *target = u->link();
3736 nodes.push({u,
true});
3737 nodes.push({target,
false});
3741 case QSvgNode::Rect:
3742 case QSvgNode::Ellipse:
3743 case QSvgNode::Circle:
3744 case QSvgNode::Line:
3745 case QSvgNode::Path:
3746 case QSvgNode::Polygon:
3747 case QSvgNode::Polyline:
3748 case QSvgNode::Tspan:
3749 if (detectPatternCycles(current.first, linkable))
3755 }
while (!nodes.isEmpty());
3762 qCWarning(lcSvgHandler,
"Cycles detected in SVG, document discarded.");
3770void QSvgHandler::parse()
3772 xml->setNamespaceProcessing(
false);
3774 int remainingUnfinishedElements = unfinishedElementsLimit;
3775 while (!xml->atEnd() && !done) {
3776 switch (xml->readNext()) {
3777 case QXmlStreamReader::StartElement:
3786 if (remainingUnfinishedElements && startElement(xml->name().toString(),
3787 xml->attributes())) {
3788 --remainingUnfinishedElements;
3794 case QXmlStreamReader::EndElement:
3795 done = endElement(xml->name());
3796 ++remainingUnfinishedElements;
3798 case QXmlStreamReader::Characters:
3799 characters(xml->text());
3801 case QXmlStreamReader::ProcessingInstruction:
3802 processingInstruction(xml->processingInstructionTarget(), xml->processingInstructionData());
3812 resolvePaintServers();
3814 if (detectCyclesAndWarn(m_doc.get()))
3818bool QSvgHandler::startElement(
const QString localName,
const QXmlStreamAttributes &attributes)
3820 QSvgNode *node =
nullptr;
3825
3826
3827 const QStringView xmlSpace(attributes.value(QLatin1String(
"xml:space")));
3828 if (xmlSpace.isNull()) {
3830 m_whitespaceMode.push(m_whitespaceMode.isEmpty() ? QSvgText::Default : m_whitespaceMode.top());
3831 }
else if (xmlSpace == QLatin1String(
"preserve")) {
3832 m_whitespaceMode.push(QSvgText::Preserve);
3833 }
else if (xmlSpace == QLatin1String(
"default")) {
3834 m_whitespaceMode.push(QSvgText::Default);
3836 const QByteArray msg =
'"' + xmlSpace.toLocal8Bit()
3837 +
"\" is an invalid value for attribute xml:space. "
3838 "Valid values are \"preserve\" and \"default\".";
3839 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3840 m_whitespaceMode.push(QSvgText::Default);
3843 if (!m_doc && localName != QLatin1String(
"svg"))
3846 if (m_doc && localName == QLatin1String(
"svg")) {
3847 m_skipNodes.push(Doc);
3848 qCWarning(lcSvgHandler) <<
"Skipping a nested svg element, because "
3849 "SVG Document must not contain nested svg elements in Svg Tiny 1.2";
3852 if (!m_skipNodes.isEmpty() && m_skipNodes.top() == Doc)
3855 if (FactoryMethod method = findGroupFactory(localName, options())) {
3858 node = method(
nullptr, attributes,
this);
3860 Q_ASSERT(node->type() == QSvgNode::Doc);
3861 m_doc.reset(
static_cast<QSvgDocument*>(node));
3864 switch (m_nodes.top()->type()) {
3866 case QSvgNode::Group:
3867 case QSvgNode::Defs:
3868 case QSvgNode::Switch:
3869 case QSvgNode::Mask:
3870 case QSvgNode::Symbol:
3871 case QSvgNode::Marker:
3872 case QSvgNode::Pattern:
3874 node = method(m_nodes.top(), attributes,
this);
3876 QSvgStructureNode *group =
3877 static_cast<QSvgStructureNode*>(m_nodes.top());
3878 group->addChild(m_doc.get(), std::unique_ptr<QSvgNode>(node), someId(attributes));
3883 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
3884 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3890 parseCoreNode(node, attributes);
3891 parseStyle(node, attributes,
this);
3892 if (node->type() == QSvgNode::Filter)
3893 m_toBeResolved.append(node);
3895 }
else if (FactoryMethod method = findGraphicsFactory(localName, options())) {
3897 Q_ASSERT(!m_nodes.isEmpty());
3898 switch (m_nodes.top()->type()) {
3900 case QSvgNode::Group:
3901 case QSvgNode::Defs:
3902 case QSvgNode::Switch:
3903 case QSvgNode::Mask:
3904 case QSvgNode::Symbol:
3905 case QSvgNode::Marker:
3906 case QSvgNode::Pattern:
3908 if (localName == QLatin1String(
"tspan")) {
3909 const QByteArray msg = QByteArrayLiteral(
"\'tspan\' element in wrong context.");
3910 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3913 node = method(m_nodes.top(), attributes,
this);
3915 QSvgStructureNode *group =
static_cast<QSvgStructureNode *>(m_nodes.top());
3917 group->addChild(m_doc.get(), std::unique_ptr<QSvgNode>(node), someId(attributes));
3921 case QSvgNode::Text:
3922 case QSvgNode::Textarea:
3923 if (localName == QLatin1String(
"tspan")) {
3924 node = method(m_nodes.top(), attributes,
this);
3926 static_cast<QSvgText *>(m_nodes.top())->addTspan(
static_cast<QSvgTspan *>(node));
3929 const QByteArray msg = QByteArrayLiteral(
"\'text\' or \'textArea\' element contains invalid element type.");
3930 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3934 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
3935 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3940 parseCoreNode(node, attributes);
3941 parseStyle(node, attributes,
this);
3942 if (node->type() == QSvgNode::Text || node->type() == QSvgNode::Textarea) {
3943 static_cast<QSvgText *>(node)->setWhitespaceMode(m_whitespaceMode.top());
3944 }
else if (node->type() == QSvgNode::Tspan) {
3945 static_cast<QSvgTspan *>(node)->setWhitespaceMode(m_whitespaceMode.top());
3946 }
else if (node->type() == QSvgNode::Use) {
3947 auto useNode =
static_cast<QSvgUse *>(node);
3948 if (!useNode->isResolved())
3949 m_toBeResolved.append(useNode);
3952 }
else if (FactoryMethod method = findFilterFactory(localName, options())) {
3954 Q_ASSERT(!m_nodes.isEmpty());
3955 if (m_nodes.top()->type() == QSvgNode::Filter ||
3956 (m_nodes.top()->type() == QSvgNode::FeMerge && localName == QLatin1String(
"feMergeNode"))) {
3957 node = method(m_nodes.top(), attributes,
this);
3959 QSvgStructureNode *container =
static_cast<QSvgStructureNode *>(m_nodes.top());
3961 container->addChild(m_doc.get(), std::unique_ptr<QSvgNode>(node), someId(attributes));
3964 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
3965 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3967 }
else if (AnimationMethod method = findAnimationFactory(localName, options())) {
3968 Q_ASSERT(!m_nodes.isEmpty());
3969 node = method(m_nodes.top(), attributes,
this);
3971 QSvgAnimateNode *anim =
static_cast<QSvgAnimateNode *>(node);
3972 if (anim->linkId().isEmpty())
3973 m_doc->animator()->appendAnimation(m_nodes.top(), anim);
3974 else if (m_doc->namedNode(anim->linkId()))
3975 m_doc->animator()->appendAnimation(m_doc->namedNode(anim->linkId()), anim);
3977 m_toBeResolved.append(anim);
3979 }
else if (ParseMethod method = findUtilFactory(localName, options())) {
3980 Q_ASSERT(!m_nodes.isEmpty());
3981 if (method(m_nodes.top(), attributes,
this)) {
3982 if (method == parseStyleNode)
3985 qCWarning(lcSvgHandler,
"%s", msgProblemParsing(localName, xml).constData());
3987 }
else if (FontFactoryMethod method = findFontFactoryMethod(localName)) {
3988 QSvgFontPtr font = method(attributes,
this);
3990 m_currentSvgFontData.font = std::move(font);
3992 const QByteArray msg = QByteArrayLiteral(
"Could not parse node: ") + localName.toLocal8Bit();
3993 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3995 }
else if (PaintServerFactoryMethod method = findPaintServerFactoryMethod(localName)) {
3996 QSvgPaintServerSharedPtr paintServer = method(attributes,
this);
3998 m_paintServer = paintServer;
3999 m_doc->addPaintServer(std::move(paintServer), someId(attributes));
4001 const QByteArray msg = QByteArrayLiteral(
"Could not parse node: ") + localName.toLocal8Bit();
4002 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
4004 }
else if (FontParseMethod method = findFontParseFactoryMethod(localName)) {
4005 if (m_currentSvgFontData.font) {
4006 if (!method(m_currentSvgFontData.font.get(), attributes,
this))
4007 qCWarning(lcSvgHandler,
"%s", msgProblemParsing(localName, xml).constData());
4009 }
else if (PaintServerParseMethod method = findPaintServerUtilFactoryMethod(localName)) {
4010 if (m_paintServer) {
4011 if (!method(m_paintServer.get(), attributes,
this))
4012 qCWarning(lcSvgHandler,
"%s", msgProblemParsing(localName, xml).constData());
4015 qCDebug(lcSvgHandler) <<
"Skipping unknown element" << localName;
4016 m_skipNodes.push(Unknown);
4022 m_skipNodes.push(Graphics);
4025 m_skipNodes.push(Style);
4030bool QSvgHandler::endElement(
const QStringView localName)
4032 CurrentNode node = m_skipNodes.top();
4034 if (node == Doc && localName != QLatin1String(
"svg"))
4038 m_whitespaceMode.pop();
4042 if (node == Unknown)
4045 if (node == Graphics)
4048 if (localName ==
"font"_L1) {
4049 if (!m_currentSvgFontData.isEmpty()) {
4050 document()->addSvgFont(m_currentSvgFontData.fontFamily,
4051 std::move(m_currentSvgFontData.font));
4053 m_currentSvgFontData.reset();
4056 return ((localName == QLatin1String(
"svg")) && (node != Doc));
4059void QSvgHandler::resolvePaintServers()
4061 for (QSvgStyleProperty *prop : std::as_const(m_unresolvedStyles)) {
4062 if (prop->type() == QSvgStyleProperty::Fill) {
4063 QSvgFillStyle *fill =
static_cast<QSvgFillStyle *>(prop);
4064 QString id = fill->paintStyleId();
4065 QSvgPaintServerSharedPtr paintServer = m_doc->paintServer(id);
4067 fill->setPaintServer(std::move(paintServer));
4069 qCWarning(lcSvgHandler,
"%s", msgCouldNotResolveProperty(id, xml).constData());
4070 fill->setBrush(Qt::NoBrush);
4072 }
else if (prop->type() == QSvgStyleProperty::Stroke) {
4073 QSvgStrokeStyle *stroke =
static_cast<QSvgStrokeStyle *>(prop);
4074 QString id = stroke->paintStyleId();
4075 QSvgPaintServerSharedPtr paintServer = m_doc->paintServer(id);
4077 stroke->setPaintServer(std::move(paintServer));
4079 qCWarning(lcSvgHandler,
"%s", msgCouldNotResolveProperty(id, xml).constData());
4080 stroke->setStroke(Qt::NoBrush);
4085 m_unresolvedStyles.clear();
4088void QSvgHandler::resolveNodes()
4090 for (QSvgNode *node : std::as_const(m_toBeResolved)) {
4091 if (node->type() == QSvgNode::Use) {
4092 QSvgUse *useNode =
static_cast<QSvgUse *>(node);
4093 const auto parent = useNode->parent();
4097 QSvgNode::Type t = parent->type();
4098 if (t != QSvgNode::Doc && t != QSvgNode::Defs && t != QSvgNode::Group && t != QSvgNode::Switch)
4101 QSvgNode *link = m_doc->namedNode(useNode->linkId());
4103 qCWarning(lcSvgHandler,
"link #%s is undefined!",
qPrintable(useNode->linkId()));
4107 if (useNode->parent()->isDescendantOf(link))
4108 qCWarning(lcSvgHandler,
"link #%s is recursive!",
qPrintable(useNode->linkId()));
4110 useNode->setLink(link);
4111 }
else if (node->type() == QSvgNode::Filter) {
4112 QSvgFilterContainer *filter =
static_cast<QSvgFilterContainer *>(node);
4113 for (
auto &renderer : filter->renderers()) {
4114 const QSvgFeFilterPrimitive *primitive = QSvgFeFilterPrimitive::castToFilterPrimitive(renderer.get());
4115 if (!primitive || primitive->type() == QSvgNode::FeUnsupported) {
4116 filter->setSupported(
false);
4120 }
else if (node->type() == QSvgNode::AnimateTransform || node->type() == QSvgNode::AnimateColor) {
4121 QSvgAnimateNode *anim =
static_cast<QSvgAnimateNode *>(node);
4122 QSvgNode *targetNode = m_doc->namedNode(anim->linkId());
4124 m_doc->animator()->appendAnimation(targetNode, anim);
4126 qCWarning(lcSvgHandler,
"Cannot find target for link #%s!",
4132 m_toBeResolved.clear();
4135bool QSvgHandler::characters(
const QStringView str)
4137 if (m_skipNodes.isEmpty() || m_skipNodes.top() == Unknown || m_nodes.isEmpty())
4140 if (m_nodes.top()->type() == QSvgNode::Text || m_nodes.top()->type() == QSvgNode::Textarea) {
4141 static_cast<QSvgText*>(m_nodes.top())->addText(str);
4142 }
else if (m_nodes.top()->type() == QSvgNode::Tspan) {
4143 static_cast<QSvgTspan*>(m_nodes.top())->addText(str);
4149QIODevice *QSvgHandler::device()
const
4151 return xml->device();
4154QSvgDocument *QSvgHandler::document()
const
4159std::unique_ptr<QSvgDocument> QSvgHandler::takeDocument()
4161 return std::move(m_doc);
4164QGuiSvg::LengthType QSvgHandler::defaultCoordinateSystem()
const
4166 return m_defaultCoords;
4169void QSvgHandler::setDefaultCoordinateSystem(QGuiSvg::LengthType type)
4171 m_defaultCoords = type;
4174void QSvgHandler::pushColor(
const QColor &color)
4176 m_colorStack.push(color);
4177 m_colorTagCount.push(1);
4180void QSvgHandler::pushColorCopy()
4182 if (m_colorTagCount.size())
4183 ++m_colorTagCount.top();
4185 pushColor(Qt::black);
4188void QSvgHandler::popColor()
4190 if (m_colorTagCount.size()) {
4191 if (!--m_colorTagCount.top()) {
4193 m_colorTagCount.pop();
4198QColor QSvgHandler::currentColor()
const
4200 if (!m_colorStack.isEmpty())
4201 return m_colorStack.top();
4203 return QColor(0, 0, 0);
4206void QSvgHandler::pushUnresolvedStyle(QSvgStyleProperty *prop)
4208 m_unresolvedStyles.append(prop);
4211void QSvgHandler::setCurrentSvgFontFamily(QStringView family)
4213 m_currentSvgFontData.fontFamily = family.toString();
4216#ifndef QT_NO_CSSPARSER
4218QSvgCssHandler &QSvgHandler::cssHandler()
4220 return m_cssHandler;
4225bool QSvgHandler::processingInstruction(
const QStringView target,
const QStringView data)
4227#ifdef QT_NO_CSSPARSER
4231 if (target == QLatin1String(
"xml-stylesheet")) {
4232 static const QRegularExpression rx(
QStringLiteral(
"type=\\\"(.+)\\\""),
4233 QRegularExpression::InvertedGreedinessOption);
4234 QRegularExpressionMatchIterator iter = rx.globalMatchView(data);
4236 while (iter.hasNext()) {
4237 QRegularExpressionMatch match = iter.next();
4238 QString type = match.captured(1);
4239 if (type.toLower() == QLatin1String(
"text/css")) {
4245 static const QRegularExpression rx(
QStringLiteral(
"href=\\\"(.+)\\\""),
4246 QRegularExpression::InvertedGreedinessOption);
4247 QRegularExpressionMatch match = rx.matchView(data);
4248 QString addr = match.captured(1);
4252 QFile file(fi.absoluteFilePath());
4253 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
4256 QByteArray cssData = file.readAll();
4257 QString css = QString::fromUtf8(cssData);
4258 m_cssHandler.parseStyleSheet(css);
4268void QSvgHandler::readStyle()
4270#ifndef QT_NO_CSSPARSER
4271 QString css = xml->readElementText();
4272 m_cssHandler.parseStyleSheet(css);
4274 xml->skipCurrentElement();
4278void QSvgHandler::setAnimPeriod(
int start,
int end)
4281 m_animEnd = qMax(end, m_animEnd);
4284int QSvgHandler::animationDuration()
const
4289QSvgHandler::~QSvgHandler()
The QPolygonF class provides a list of points using floating point precision.
Combined button and popup list for selecting options.
QList< QGradientStop > QGradientStops
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")
Q_STATIC_ASSERT(sizeof(SharedImageHeader) % 4==0)
#define qPrintable(string)
#define QStringLiteral(str)
#define qUtf16Printable(string)
static PaintServerParseMethod findPaintServerUtilFactoryMethod(const QStringView name)
static bool parseStyle(QSvgNode *node, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
bool(*) FontParseMethod(QSvgFont *, const QXmlStreamAttributes &, QSvgHandler *)
static void parseVisibility(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static void parseCssAnimations(QSvgNode *node, const QSvgCssProperties &properties, QSvgHandler *handler)
static QSvgNode * createGNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, 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 const qreal sizeTable[]
static QSvgNode * createImageNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createPolygonNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseBaseAnimate(QSvgNode *, const QXmlStreamAttributes &attributes, QSvgAnimateNode *anim, QSvgHandler *handler)
bool(*) ParseMethod(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *)
static void parseOthers(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static qreal convertToNumber(QStringView str, bool tiny12FeaturesOnly, bool *ok=NULL)
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 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 std::optional< int > parseFontWeight(QStringView s)
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 bool detectPatternCycles(const QSvgNode *node, QList< const QSvgNode * > &linkable)
static QSvgNode * createAnimateColorNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
void setAlpha(QStringView opacity, QColor *color)
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)
QSvgPaintServerSharedPtr(*) PaintServerFactoryMethod(const QXmlStreamAttributes &, QSvgHandler *)
static bool parseFontFaceUriNode(QSvgFont *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseNumberTriplet(QList< qreal > &values, QStringView *s)
static std::optional< QRectF > parseViewBox(QStringView str)
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 void parseNumberTriplet(QList< qreal > &values, QStringView s)
static bool parseGlyphNode(QSvgFont *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static FontSizeSpec fontSizeSpec(QStringView spec)
static void parseBaseGradient(const QXmlStreamAttributes &attributes, QSvgGradientPaint *gradProp, QSvgHandler *handler)
bool qsvg_get_hex_rgb(const char *name, QRgb *rgb)
static QSvgNode * createTextNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static std::optional< QFont::Style > parseFontStyle(QStringView s)
static bool createSvgGlyph(QSvgFont *font, const QXmlStreamAttributes &attributes, bool isMissingGlyph, QSvgHandler *handler)
static bool parseAudioNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseMarkerNode(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *)
static QList< qreal > parsePercentageList(QStringView 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 void parseOpacity(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static bool parseScriptNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgPaintServerSharedPtr createRadialGradientNode(const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static FontFactoryMethod findFontFactoryMethod(const QStringView name)
static bool detectCycles(const QSvgNode *n)
static bool parseMissingGlyphNode(QSvgFont *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createPatternNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
QSvgFontPtr(*) FontFactoryMethod(const QXmlStreamAttributes &, QSvgHandler *)
static bool parseHandlerNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static AnimationMethod findAnimationFactory(const QStringView name, QtSvg::Options options)
static bool parseFontFaceNode(QSvgFont *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
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 FontParseMethod findFontParseFactoryMethod(const QStringView name)
static void parsePen(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createSvgNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static bool parseFontFaceNameNode(QSvgFont *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static std::optional< qreal > parseFontSize(QStringView s, bool tiny12FeaturesOnly)
static std::optional< Qt::Alignment > parseTextAnchor(QStringView s)
QList< qreal > parseNumbersList(QStringView *str)
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 QList< QStringView > splitWithDelimiter(QStringView delimitedList)
static void parseFont(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *handler)
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 QSvgPaintServerSharedPtr paintServerFromUrl(QSvgDocument *doc, QStringView url)
static QSvgNode * createPolylineNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgPaintServerSharedPtr createSolidColorNode(const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QStringView idFromIRI(QStringView iri)
static bool parsePrefetchNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static int qsvg_hex2int(const char *s, bool *ok=nullptr)
static void parseOffsetPath(QSvgNode *node, const QSvgCssProperties &properties)
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 PaintServerFactoryMethod findPaintServerFactoryMethod(const QStringView name)
static void parseCompOp(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static bool parseMaskNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseStopNode(QSvgPaintServer *paintServer, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static bool detectCyclesAndWarn(const QSvgNode *node)
bool(*) PaintServerParseMethod(QSvgPaintServer *, const QXmlStreamAttributes &, QSvgHandler *)
static bool parseAnchorNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
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 QPainter::CompositionMode svgToQtCompositionMode(const QStringView op)
static QByteArray msgCouldNotResolveProperty(QStringView id, const QXmlStreamReader *r)
static void parseFilterBounds(const QXmlStreamAttributes &attributes, QSvgRectF *rect)
static void parseBrush(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createTspanNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static std::optional< QFont::Capitalization > parseFontVariant(const QSvgAttributes &attributes)
static QSvgFontPtr createFontNode(const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createAnimateMotionNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static FactoryMethod findGraphicsFactory(const QStringView name, QtSvg::Options options)
static bool parseFontFaceSrcNode(QSvgFont *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
QSvgNode * createAnimateTransformNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createFeMergeNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createUseNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createFeCompositeNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseSetNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QStringView idFromFuncIRI(QStringView iri)
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 QSvgPaintServerSharedPtr createLinearGradientNode(const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QString someId(const QXmlStreamAttributes &attributes)
Q_AUTOTEST_EXPORT bool resolveColor(QStringView colorStr, QColor &color, QSvgHandler *handler)
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