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);
570 if (type == QGuiSvg::LengthType::LT_PERCENT) {
576static bool createSvgGlyph(QSvgFont *font,
const QXmlStreamAttributes &attributes,
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);
584 QPainterPath path = QGuiSvg::parsePath(pathStr).value_or(QPainterPath());
586 path.setFillRule(Qt::WindingFill);
588 if (isMissingGlyph) {
589 if (!uncStr.isEmpty())
590 qWarning(
"Ignoring missing-glyph's 'unicode' attribute");
591 return font->addMissingGlyph(path, havx);
594 if (uncStr.isEmpty()) {
595 qWarning(
"glyph does not define a non-empty 'unicode' attribute and will be ignored");
598 font->addGlyph(uncStr.toString(), path, havx);
604 QSvgHandler *handler)
607 if (constructColor(attributes.color, attributes.colorOpacity, color, handler)) {
609 handler->pushColor(color);
615 QStringView id = idFromFuncIRI(url);
616 return doc ? doc->paintServer(id) :
nullptr;
621 QSvgHandler *handler)
623 if (!attributes.fill.isEmpty() || !attributes.fillRule.isEmpty() || !attributes.fillOpacity.isEmpty()) {
627 if (!attributes.fillRule.isEmpty() && attributes.fillRule != tokens::inherit) {
628 if (attributes.fillRule == QLatin1String(
"evenodd"))
629 prop->setFillRule(Qt::OddEvenFill);
630 else if (attributes.fillRule == QLatin1String(
"nonzero"))
631 prop->setFillRule(Qt::WindingFill);
635 if (!attributes.fillOpacity.isEmpty() && attributes.fillOpacity != tokens::inherit) {
636 prop->setFillOpacity(qMin(qreal(1.0), qMax(qreal(0.0), QGuiSvg::toDouble(attributes.fillOpacity))));
640 if (!attributes.fill.isEmpty() && attributes.fill != tokens::inherit) {
641 if (attributes.fill.startsWith(QLatin1String(
"url"))) {
642 QStringView value = attributes.fill;
643 QSvgPaintServerSharedPtr paintServer = paintServerFromUrl(handler->document(), value);
645 prop->setPaintServer(
std::move(paintServer));
647 QString id = idFromFuncIRI(value).toString();
648 prop->setPaintStyleId(id);
649 handler->pushUnresolvedStyle(prop.get());
651 }
else if (attributes.fill != QLatin1String(
"none")) {
653 if (resolveColor(attributes.fill, color, handler))
654 prop->setBrush(QBrush(color));
656 prop->setBrush(QBrush(Qt::NoBrush));
659 node->appendStyleProperty(
std::move(prop));
672 while (!value.isEmpty()) {
673 if (value.first().isSpace() || value.startsWith(QLatin1Char(
','))) {
685 State state = Matrix;
686 if (value.startsWith(QLatin1Char(
'm'))) {
687 const char *ident =
"atrix";
688 for (
int i = 0; i < 5; ++i)
689 if (!value.slice(1).startsWith(QLatin1Char(ident[i])))
693 }
else if (value.startsWith(QLatin1Char(
't'))) {
694 const char *ident =
"ranslate";
695 for (
int i = 0; i < 8; ++i)
696 if (!value.slice(1).startsWith(QLatin1Char(ident[i])))
700 }
else if (value.startsWith(QLatin1Char(
'r'))) {
701 const char *ident =
"otate";
702 for (
int i = 0; i < 5; ++i)
703 if (!value.slice(1).startsWith(QLatin1Char(ident[i])))
707 }
else if (value.startsWith(QLatin1Char(
's'))) {
709 if (value.startsWith(QLatin1Char(
'c'))) {
710 const char *ident =
"ale";
711 for (
int i = 0; i < 3; ++i)
712 if (!value.slice(1).startsWith(QLatin1Char(ident[i])))
716 }
else if (value.startsWith(QLatin1Char(
'k'))) {
717 if (!value.slice(1).startsWith(QLatin1Char(
'e')))
719 if (!value.slice(1).startsWith(QLatin1Char(
'w')))
722 if (value.startsWith(QLatin1Char(
'X')))
724 else if (value.startsWith(QLatin1Char(
'Y')))
736 while (!value.isEmpty() && value.first().isSpace())
738 if (!value.startsWith(QLatin1Char(
'(')))
741 QVarLengthArray<qreal, 8> points;
742 QGuiSvg::parseNumbersArray(&value, points);
743 if (!value.startsWith(QLatin1Char(
')')))
747 if(state == Matrix) {
748 if(points.size() != 6)
750 matrix = QTransform(points[0], points[1],
751 points[2], points[3],
752 points[4], points[5]) * matrix;
753 }
else if (state == Translate) {
754 if (points.size() == 1)
755 matrix.translate(points[0], 0);
756 else if (points.size() == 2)
757 matrix.translate(points[0], points[1]);
760 }
else if (state == Rotate) {
761 if(points.size() == 1) {
762 matrix.rotate(points[0]);
763 }
else if (points.size() == 3) {
764 matrix.translate(points[1], points[2]);
765 matrix.rotate(points[0]);
766 matrix.translate(-points[1], -points[2]);
770 }
else if (state == Scale) {
771 if (points.size() < 1 || points.size() > 2)
773 qreal sx = points[0];
775 if(points.size() == 2)
777 matrix.scale(sx, sy);
778 }
else if (state == SkewX) {
779 if (points.size() != 1)
781 matrix.shear(qTan(qDegreesToRadians(points[0])), 0);
782 }
else if (state == SkewY) {
783 if (points.size() != 1)
785 matrix.shear(0, qTan(qDegreesToRadians(points[0])));
794 QSvgHandler *handler)
796 if (!attributes.stroke.isEmpty() || !attributes.strokeDashArray.isEmpty() || !attributes.strokeDashOffset.isEmpty() || !attributes.strokeLineCap.isEmpty()
797 || !attributes.strokeLineJoin.isEmpty() || !attributes.strokeMiterLimit.isEmpty() || !attributes.strokeOpacity.isEmpty() || !attributes.strokeWidth.isEmpty()
798 || !attributes.vectorEffect.isEmpty()) {
803 if (!attributes.stroke.isEmpty() && attributes.stroke != tokens::inherit) {
804 if (attributes.stroke.startsWith(QLatin1String(
"url"))) {
805 QStringView value = attributes.stroke;
806 QSvgPaintServerSharedPtr paintServer = paintServerFromUrl(handler->document(), value);
808 prop->setPaintServer(
std::move(paintServer));
810 QString id = idFromFuncIRI(value).toString();
811 prop->setPaintStyleId(id);
812 handler->pushUnresolvedStyle(prop.get());
814 }
else if (attributes.stroke != QLatin1String(
"none")) {
816 if (resolveColor(attributes.stroke, color, handler))
817 prop->setStroke(QBrush(color));
819 prop->setStroke(QBrush(Qt::NoBrush));
824 if (!attributes.strokeWidth.isEmpty() && attributes.strokeWidth != tokens::inherit) {
825 QGuiSvg::LengthType lt;
826 prop->setWidth(QGuiSvg::parseLength(attributes.strokeWidth, <));
830 if (!attributes.strokeDashArray.isEmpty() && attributes.strokeDashArray != tokens::inherit) {
831 if (attributes.strokeDashArray == QLatin1String(
"none")) {
832 prop->setDashArrayNone();
834 QStringView dashArray = attributes.strokeDashArray;
835 QList<qreal> dashes = parseNumbersList(&dashArray);
836 const bool allZeroes =
std::all_of(dashes.cbegin(), dashes.cend(),
837 [](qreal i) {
return qFuzzyIsNull(i); });
838 const bool hasNegative = !allZeroes &&
std::any_of(dashes.cbegin(), dashes.cend(),
839 [](qreal i) {
return i < 0.; });
842 qCWarning(lcSvgHandler) <<
"QSvgHandler: Stroke dash array "
843 "with a negative value is invalid";
846 if (allZeroes || hasNegative) {
847 prop->setDashArrayNone();
850 if ((dashes.size() & 1) != 0)
851 dashes << QList<qreal>(dashes);
852 prop->setDashArray(dashes);
858 if (!attributes.strokeLineJoin.isEmpty()) {
859 if (attributes.strokeLineJoin == QLatin1String(
"miter"))
860 prop->setLineJoin(Qt::SvgMiterJoin);
861 else if (attributes.strokeLineJoin == QLatin1String(
"round"))
862 prop->setLineJoin(Qt::RoundJoin);
863 else if (attributes.strokeLineJoin == QLatin1String(
"bevel"))
864 prop->setLineJoin(Qt::BevelJoin);
868 if (!attributes.strokeLineCap.isEmpty()) {
869 if (attributes.strokeLineCap == QLatin1String(
"butt"))
870 prop->setLineCap(Qt::FlatCap);
871 else if (attributes.strokeLineCap == QLatin1String(
"round"))
872 prop->setLineCap(Qt::RoundCap);
873 else if (attributes.strokeLineCap == QLatin1String(
"square"))
874 prop->setLineCap(Qt::SquareCap);
878 if (!attributes.strokeDashOffset.isEmpty() && attributes.strokeDashOffset != tokens::inherit)
879 prop->setDashOffset(QGuiSvg::toDouble(attributes.strokeDashOffset));
882 if (!attributes.vectorEffect.isEmpty()) {
883 if (attributes.vectorEffect == QLatin1String(
"non-scaling-stroke"))
884 prop->setVectorEffect(
true);
885 else if (attributes.vectorEffect == QLatin1String(
"none"))
886 prop->setVectorEffect(
false);
890 if (!attributes.strokeMiterLimit.isEmpty() && attributes.strokeMiterLimit != tokens::inherit)
891 prop->setMiterLimit(QGuiSvg::toDouble(attributes.strokeMiterLimit));
894 if (!attributes.strokeOpacity.isEmpty() && attributes.strokeOpacity != tokens::inherit)
895 prop->setOpacity(qMin(qreal(1.0), qMax(qreal(0.0), QGuiSvg::toDouble(attributes.strokeOpacity))));
897 node->appendStyleProperty(
std::move(prop));
905{ qreal(6.9), qreal(8.3), qreal(10.0), qreal(12.0), qreal(14.4), qreal(17.3), qreal(20.7) };
911 switch (spec.at(0).unicode()) {
913 if (spec == QLatin1String(
"xx-small"))
915 if (spec == QLatin1String(
"x-small"))
917 if (spec == QLatin1String(
"x-large"))
919 if (spec == QLatin1String(
"xx-large"))
923 if (spec == QLatin1String(
"small"))
927 if (spec == QLatin1String(
"medium"))
931 if (spec == QLatin1String(
"large"))
935 if (spec == QLatin1String(
"none"))
949 if (s == tokens::normal)
950 return QFont::StyleNormal;
951 if (s == tokens::italic)
952 return QFont::StyleItalic;
953 if (s == tokens::oblique)
954 return QFont::StyleOblique;
968 if (s.isEmpty() || s == tokens::inherit)
976 QGuiSvg::LengthType type;
978 qreal fs = QGuiSvg::parseLength(s, &type, &ok);
981 fs = QGuiSvg::convertToPixels(fs,
true, type);
982 return (
std::min)(fs, qreal(0xffff));
991 return sizeTable[spec];
994 Q_UNREACHABLE_RETURN(
std::nullopt);
1002 if (s.isEmpty() || s == tokens::inherit)
1003 return std::nullopt;
1005 if (s == tokens::normal)
1006 return QFont::Normal;
1007 if (s == tokens::bold)
1009 if (s == tokens::bolder)
1010 return QSvgFontStyle::BOLDER;
1011 if (s == tokens::lighter)
1012 return QSvgFontStyle::LIGHTER;
1015 const int num = s.toInt(&ok);
1019 return std::nullopt;
1046 auto s = attributes.fontVariant;
1048 if (s == tokens::normal)
1049 return QFont::MixedCase;
1050 if (s == tokens::small_caps)
1051 return QFont::SmallCaps;
1053 return std::nullopt;
1064 if (s == tokens::start)
1065 return Qt::AlignLeft;
1066 if (s == tokens::middle)
1067 return Qt::AlignHCenter;
1068 if (s == tokens::end)
1069 return Qt::AlignRight;
1071 return std::nullopt;
1078 auto parsedFontSize = parseFontSize(attributes.fontSize);
1079 auto parsedFontStyle = parseFontStyle(attributes.fontStyle);
1080 auto parsedFontWeight = parseFontWeight(attributes.fontWeight);
1081 auto parsedFontVariant = parseFontVariant(attributes);
1082 auto parsedTextAnchor = parseTextAnchor(attributes.textAnchor);
1084 if (attributes.fontFamily.isEmpty() && !parsedFontSize && !parsedFontStyle &&
1085 !parsedFontWeight && !parsedFontVariant && !parsedTextAnchor)
1090 if (!attributes.fontFamily.isEmpty() && attributes.fontFamily != tokens::inherit) {
1091 QStringView family = attributes.fontFamily.trimmed();
1092 if (!family.isEmpty() && (family.at(0) == QLatin1Char(
'\'') || family.at(0) == QLatin1Char(
'\"')))
1093 family = family.mid(1, family.size() - 2);
1094 fontStyle->setFamily(family.toString());
1098 fontStyle->setSize(*parsedFontSize);
1100 if (parsedFontStyle)
1101 fontStyle->setStyle(*parsedFontStyle);
1103 if (parsedFontWeight)
1104 fontStyle->setWeight(*parsedFontWeight);
1106 if (parsedFontVariant)
1107 fontStyle->setVariant(*parsedFontVariant);
1109 if (parsedTextAnchor)
1110 fontStyle->setTextAnchor(*parsedTextAnchor);
1112 node->appendStyleProperty(
std::move(fontStyle));
1119 if (attributes.transform.isEmpty())
1121 QTransform matrix = parseTransformationMatrix(attributes.transform.trimmed());
1123 if (!matrix.isIdentity()) {
1133 QSvgNode *parent = node->parent();
1135 if (parent && (attributes.visibility.isEmpty() || attributes.visibility == tokens::inherit))
1136 node->setVisible(parent->isVisible());
1137 else if (attributes.visibility == QLatin1String(
"hidden") || attributes.visibility == QLatin1String(
"collapse")) {
1138 node->setVisible(
false);
1140 node->setVisible(
true);
1144 const QXmlStreamAttributes &attributes,
1145 QSvgHandler *handler);
1151 str = str.trimmed();
1152 if (str.endsWith(QLatin1String(
"ms"))) {
1155 }
else if (str.endsWith(QLatin1String(
"s"))) {
1158 double val = ms * QGuiSvg::toDouble(str, ok);
1160 if (val >
std::numeric_limits<
int>::min() && val <
std::numeric_limits<
int>::max())
1161 res =
static_cast<
int>(val);
1168#ifndef QT_NO_CSSPARSER
1171 const QXmlStreamAttributes &attributes,
1172 QSvgHandler *handler)
1174 QSvgCssProperties cssAnimProps(attributes);
1175 QList<QSvgAnimationProperty> parsedProperties = cssAnimProps.animations();
1177 for (
auto &property : parsedProperties) {
1178 QSvgCssAnimation *anim = handler->cssHandler().createAnimation(property.name);
1182 anim->setRunningTime(property.delay, property.duration);
1183 anim->setIterationCount(property.iteration);
1184 QSvgCssEasingPtr easing = handler->cssHandler().createEasing(property.easingFunction, property.easingValues);
1185 anim->setEasing(std::move(easing));
1187 handler->setAnimPeriod(property.delay, property.delay + property.duration);
1188 handler->document()->animator()->appendAnimation(node, anim);
1189 handler->document()->setAnimated(
true);
1194 const QXmlStreamAttributes &attributes)
1196 QSvgCssProperties cssProperties(attributes);
1197 QSvgOffsetProperty offset = cssProperties.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 parseOffsetPath(node, cssAttributes);
1491 parseCssAnimations(node, cssAttributes, handler);
1510 const QXmlStreamAttributes &attributes,
1513 Q_UNUSED(parent); Q_UNUSED(attributes);
1518 const QXmlStreamAttributes &attributes,
1519 QSvgAnimateNode *anim,
1520 QSvgHandler *handler)
1522 const QStringView beginStr = attributes.value(QLatin1String(
"begin"));
1523 const QStringView durStr = attributes.value(QLatin1String(
"dur"));
1524 const QStringView endStr = attributes.value(QLatin1String(
"end"));
1525 const QStringView repeatStr = attributes.value(QLatin1String(
"repeatCount"));
1526 const QStringView fillStr = attributes.value(QLatin1String(
"fill"));
1527 const QStringView addtv = attributes.value(QLatin1String(
"additive"));
1528 QStringView linkId = attributes.value(QLatin1String(
"xlink:href"));
1530 if (linkId.isEmpty())
1531 linkId = attributes.value(QLatin1String(
"href"));
1533 linkId = idFromIRI(linkId);
1536 int begin = parseClockValue(beginStr, &ok);
1539 int dur = parseClockValue(durStr, &ok);
1542 int end = parseClockValue(endStr, &ok);
1545 qreal repeatCount = (repeatStr == QLatin1String(
"indefinite")) ? -1 :
1546 qMax(1.0, QGuiSvg::toDouble(repeatStr));
1548 QSvgAnimateNode::Fill fill = (fillStr == QLatin1String(
"freeze")) ? QSvgAnimateNode::Freeze :
1549 QSvgAnimateNode::Remove;
1551 QSvgAnimateNode::Additive additive = (addtv == QLatin1String(
"sum")) ? QSvgAnimateNode::Sum :
1552 QSvgAnimateNode::Replace;
1554 anim->setRunningTime(begin, dur, end, 0);
1555 anim->setRepeatCount(repeatCount);
1556 anim->setFill(fill);
1557 anim->setAdditiveType(additive);
1558 anim->setLinkId(linkId.toString());
1560 handler->document()->setAnimated(
true);
1562 handler->setAnimPeriod(begin, begin + dur);
1571 qreal spacing = 1.0f / (count - 1);
1572 for (uint i = 0; i < count; i++) {
1573 keyFrames.append(i * spacing);
1578 const QXmlStreamAttributes &attributes,
1579 QSvgHandler *handler)
1581 const QStringView fromStr = attributes.value(QLatin1String(
"from"));
1582 const QStringView toStr = attributes.value(QLatin1String(
"to"));
1583 const QStringView valuesStr = attributes.value(QLatin1String(
"values"));
1584 const QString targetStr = attributes.value(QLatin1String(
"attributeName")).toString();
1586 if (targetStr != QLatin1String(
"fill") && targetStr != QLatin1String(
"stroke"))
1589 QList<QColor> colors;
1590 if (valuesStr.isEmpty()) {
1591 QColor startColor, endColor;
1592 resolveColor(fromStr, startColor, handler);
1593 resolveColor(toStr, endColor, handler);
1595 colors.append(startColor);
1596 colors.append(endColor);
1598 for (
auto part : qTokenize(valuesStr, u';')) {
1600 resolveColor(part, color, handler);
1601 colors.append(color);
1605 QSvgAnimatedPropertyColor *prop =
static_cast<QSvgAnimatedPropertyColor *>
1606 (QSvgAbstractAnimatedProperty::createAnimatedProperty(targetStr));
1610 prop->setColors(colors);
1612 QList<qreal> keyFrames;
1613 generateKeyFrames(keyFrames, colors.size());
1614 prop->setKeyFrames(keyFrames);
1616 QSvgAnimateColor *anim =
new QSvgAnimateColor(parent);
1617 anim->appendProperty(prop);
1619 if (!parseBaseAnimate(parent, attributes, anim, handler)) {
1628 const QXmlStreamAttributes &attributes,
1631 Q_UNUSED(parent); Q_UNUSED(attributes);
1637 QList<qreal> list = parseNumbersList(s);
1639 for (
int i = 3 - list.size(); i > 0; --i)
1645 parseNumberTriplet(values, &s);
1649 const QXmlStreamAttributes &attributes,
1650 QSvgHandler *handler)
1652 const QStringView typeStr = attributes.value(QLatin1String(
"type"));
1653 const QStringView values = attributes.value(QLatin1String(
"values"));
1654 const QStringView fromStr = attributes.value(QLatin1String(
"from"));
1655 const QStringView toStr = attributes.value(QLatin1String(
"to"));
1656 const QStringView byStr = attributes.value(QLatin1String(
"by"));
1659 if (values.isEmpty()) {
1660 if (fromStr.isEmpty()) {
1661 if (!byStr.isEmpty()) {
1665 parseNumberTriplet(vals, byStr);
1671 if (!toStr.isEmpty()) {
1673 parseNumberTriplet(vals, fromStr);
1674 parseNumberTriplet(vals, toStr);
1675 }
else if (!byStr.isEmpty()) {
1677 parseNumberTriplet(vals, fromStr);
1678 parseNumberTriplet(vals, byStr);
1679 for (
int i = vals.size() - 3; i < vals.size(); ++i)
1680 vals[i] += vals[i - 3];
1686 QStringView s = values;
1687 while (!s.isEmpty()) {
1688 parseNumberTriplet(vals, &s);
1693 if (vals.size() % 3 != 0)
1697 QList<QSvgAnimatedPropertyTransform::TransformComponent> components;
1698 for (
int i = 0; i <= vals.size() - 3; i += 3) {
1699 QSvgAnimatedPropertyTransform::TransformComponent component;
1700 if (typeStr == QLatin1String(
"translate")) {
1701 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Translate;
1702 component.values.append(vals.at(i));
1703 component.values.append(vals.at(i + 1));
1704 }
else if (typeStr == QLatin1String(
"scale")) {
1705 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Scale;
1706 component.values.append(vals.at(i));
1707 component.values.append(vals.at(i + 1));
1708 }
else if (typeStr == QLatin1String(
"rotate")) {
1709 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Rotate;
1710 component.values.append(vals.at(i));
1711 component.values.append(vals.at(i + 1));
1712 component.values.append(vals.at(i + 2));
1713 }
else if (typeStr == QLatin1String(
"skewX")) {
1714 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Skew;
1715 component.values.append(vals.at(i));
1716 component.values.append(0);
1717 }
else if (typeStr == QLatin1String(
"skewY")) {
1718 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Skew;
1719 component.values.append(0);
1720 component.values.append(vals.at(i));
1724 components.append(component);
1727 QSvgAnimatedPropertyTransform *prop =
static_cast<QSvgAnimatedPropertyTransform *>
1728 (QSvgAbstractAnimatedProperty::createAnimatedProperty(QLatin1String(
"transform")));
1732 prop->appendComponents(components);
1734 prop->setTransformCount(1);
1735 QList<qreal> keyFrames;
1736 generateKeyFrames(keyFrames, vals.size() / 3);
1737 prop->setKeyFrames(keyFrames);
1739 QSvgAnimateTransform *anim =
new QSvgAnimateTransform(parent);
1740 anim->appendProperty(prop);
1742 if (!parseBaseAnimate(parent, attributes, anim, handler)) {
1751 const QXmlStreamAttributes &attributes,
1754 Q_UNUSED(parent); Q_UNUSED(attributes);
1759 const QXmlStreamAttributes &attributes,
1762 Q_UNUSED(parent); Q_UNUSED(attributes);
1767 const QXmlStreamAttributes &attributes,
1770 const QStringView cx = attributes.value(QLatin1String(
"cx"));
1771 const QStringView cy = attributes.value(QLatin1String(
"cy"));
1772 const QStringView r = attributes.value(QLatin1String(
"r"));
1773 qreal ncx = QGuiSvg::toDouble(cx);
1774 qreal ncy = QGuiSvg::toDouble(cy);
1775 qreal nr = QGuiSvg::toDouble(r);
1779 QRectF rect(ncx-nr, ncy-nr, nr*2, nr*2);
1780 QSvgNode *circle =
new QSvgCircle(parent, rect);
1785 const QXmlStreamAttributes &attributes,
1788 Q_UNUSED(attributes);
1789 QSvgDefs *defs =
new QSvgDefs(parent);
1794 const QXmlStreamAttributes &attributes,
1797 Q_UNUSED(parent); Q_UNUSED(attributes);
1802 const QXmlStreamAttributes &attributes,
1805 const QStringView cx = attributes.value(QLatin1String(
"cx"));
1806 const QStringView cy = attributes.value(QLatin1String(
"cy"));
1807 const QStringView rx = attributes.value(QLatin1String(
"rx"));
1808 const QStringView ry = attributes.value(QLatin1String(
"ry"));
1809 qreal ncx = QGuiSvg::toDouble(cx);
1810 qreal ncy = QGuiSvg::toDouble(cy);
1811 qreal nrx = QGuiSvg::toDouble(rx);
1812 qreal nry = QGuiSvg::toDouble(ry);
1814 QRectF rect(ncx-nrx, ncy-nry, nrx*2, nry*2);
1815 QSvgNode *ellipse =
new QSvgEllipse(parent, rect);
1822 const QStringView hax = attributes.value(QLatin1String(
"horiz-adv-x"));
1824 qreal horizAdvX = QGuiSvg::toDouble(hax);
1826 return std::make_unique<QSvgFont>(horizAdvX);
1830 const QXmlStreamAttributes &attributes,
1831 QSvgHandler *handler)
1833 const QStringView name = attributes.value(QLatin1String(
"font-family"));
1838 const QStringView unitsPerEmStr = attributes.value(QLatin1String(
"units-per-em"));
1841
1842
1844 qreal unitsPerEm = QGuiSvg::toDouble(unitsPerEmStr, &ok);
1845 if (!qFuzzyIsNull(unitsPerEm))
1846 parent->setUnitsPerEm(unitsPerEm);
1848 handler->setCurrentSvgFontFamily(name);
1854 const QXmlStreamAttributes &attributes,
1857 Q_UNUSED(parent); Q_UNUSED(attributes);
1862 const QXmlStreamAttributes &attributes,
1865 Q_UNUSED(parent); Q_UNUSED(attributes);
1870 const QXmlStreamAttributes &attributes,
1873 Q_UNUSED(parent); Q_UNUSED(attributes);
1878 const QXmlStreamAttributes &attributes,
1881 return createSvgGlyph(parent, attributes,
false);
1885 const QXmlStreamAttributes &attributes,
1888 return createSvgGlyph(parent, attributes,
true);
1892 const QXmlStreamAttributes &attributes,
1895 Q_UNUSED(parent); Q_UNUSED(attributes);
1900 const QXmlStreamAttributes &attributes,
1903 Q_UNUSED(attributes);
1904 QSvgG *node =
new QSvgG(parent);
1909 const QXmlStreamAttributes &attributes,
1912 Q_UNUSED(parent); Q_UNUSED(attributes);
1917 const QXmlStreamAttributes &attributes,
1920 Q_UNUSED(parent); Q_UNUSED(attributes);
1925 const QXmlStreamAttributes &attributes,
1926 QSvgHandler *handler)
1928 const QStringView x = attributes.value(QLatin1String(
"x"));
1929 const QStringView y = attributes.value(QLatin1String(
"y"));
1930 const QStringView width = attributes.value(QLatin1String(
"width"));
1931 const QStringView height = attributes.value(QLatin1String(
"height"));
1932 QString filename = attributes.value(QLatin1String(
"xlink:href")).toString();
1934 filename = attributes.value(QLatin1String(
"href")).toString();
1935 qreal nx = QGuiSvg::toDouble(x);
1936 qreal ny = QGuiSvg::toDouble(y);
1937 QGuiSvg::LengthType type;
1938 qreal nwidth = QGuiSvg::parseLength(width.toString(), &type);
1939 nwidth = QGuiSvg::convertToPixels(nwidth,
true, type);
1941 qreal nheight = QGuiSvg::parseLength(height.toString(), &type);
1942 nheight = QGuiSvg::convertToPixels(nheight,
false, type);
1944 filename = filename.trimmed();
1945 if (filename.isEmpty()) {
1946 qCWarning(lcSvgHandler) <<
"QSvgHandler: Image filename is empty";
1949 if (nwidth <= 0 || nheight <= 0) {
1950 qCWarning(lcSvgHandler) <<
"QSvgHandler: Width or height for" << filename <<
"image was not greater than 0";
1959 } filenameType = NotLoaded;
1961 if (filename.startsWith(QLatin1String(
"data"))) {
1964 if (qDecodeDataUrl(QUrl{filename}, mimeType, data)) {
1965 image = QImage::fromData(data);
1966 filenameType = LoadedFromData;
1970 if (image.isNull()) {
1971 const auto *file = qobject_cast<QFile *>(handler->device());
1974 if (url.isRelative()) {
1976 filename = info.absoluteDir().absoluteFilePath(filename);
1980 if (handler->trustedSourceMode() || !QImageReader::imageFormat(filename).startsWith(
"svg")) {
1981 image = QImage(filename);
1982 filenameType = LoadedFromFile;
1986 if (image.isNull()) {
1987 qCWarning(lcSvgHandler) <<
"Could not create image from" << filename;
1991 if (image.format() == QImage::Format_ARGB32)
1992 image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
1994 if (filenameType != LoadedFromFile)
1995 filename = QString();
1996 QSvgNode *img =
new QSvgImage(parent,
2007 const QXmlStreamAttributes &attributes,
2010 const QStringView x1 = attributes.value(QLatin1String(
"x1"));
2011 const QStringView y1 = attributes.value(QLatin1String(
"y1"));
2012 const QStringView x2 = attributes.value(QLatin1String(
"x2"));
2013 const QStringView y2 = attributes.value(QLatin1String(
"y2"));
2014 qreal nx1 = QGuiSvg::toDouble(x1);
2015 qreal ny1 = QGuiSvg::toDouble(y1);
2016 qreal nx2 = QGuiSvg::toDouble(x2);
2017 qreal ny2 = QGuiSvg::toDouble(y2);
2019 QLineF lineBounds(nx1, ny1, nx2, ny2);
2020 QSvgNode *line =
new QSvgLine(parent, lineBounds);
2026 QSvgGradientPaint *gradProp,
2027 QSvgHandler *handler)
2029 QStringView linkId = attributes.value(QLatin1String(
"xlink:href"));
2030 const QStringView trans = attributes.value(QLatin1String(
"gradientTransform"));
2031 const QStringView spread = attributes.value(QLatin1String(
"spreadMethod"));
2032 const QStringView units = attributes.value(QLatin1String(
"gradientUnits"));
2033 const QStringView colorStr = attributes.value(QLatin1String(
"color"));
2034 const QStringView colorOpacityStr = attributes.value(QLatin1String(
"color-opacity"));
2037 if (constructColor(colorStr, colorOpacityStr, color, handler)) {
2038 handler->popColor();
2039 handler->pushColor(color);
2043 QGradient *grad = gradProp->qgradient();
2044 linkId = idFromIRI(linkId);
2046 if (!linkId.isEmpty()) {
2047 QSvgPaintServerSharedPtr paintServer = handler->document()->paintServer(linkId);
2048 if (paintServer && paintServer->type() == QSvgPaintServer::Type::Gradient) {
2049 QSvgGradientPaint *inherited =
2050 static_cast<QSvgGradientPaint*>(paintServer.get());
2051 if (!inherited->stopLink().isEmpty()) {
2052 gradProp->setStopLink(inherited->stopLink(), handler->document());
2054 grad->setStops(inherited->qgradient()->stops());
2055 gradProp->setGradientStopsSet(inherited->gradientStopsSet());
2058 matrix = inherited->qtransform();
2060 gradProp->setStopLink(linkId.toString(), handler->document());
2064 if (!trans.isEmpty()) {
2065 matrix = parseTransformationMatrix(trans);
2066 gradProp->setTransform(matrix);
2067 }
else if (!matrix.isIdentity()) {
2068 gradProp->setTransform(matrix);
2071 if (!spread.isEmpty()) {
2072 if (spread == QLatin1String(
"pad")) {
2073 grad->setSpread(QGradient::PadSpread);
2074 }
else if (spread == QLatin1String(
"reflect")) {
2075 grad->setSpread(QGradient::ReflectSpread);
2076 }
else if (spread == QLatin1String(
"repeat")) {
2077 grad->setSpread(QGradient::RepeatSpread);
2081 if (units.isEmpty() || units == QLatin1String(
"objectBoundingBox")) {
2082 grad->setCoordinateMode(QGradient::ObjectMode);
2088 QSvgHandler *handler)
2090 const QStringView x1 = attributes.value(QLatin1String(
"x1"));
2091 const QStringView y1 = attributes.value(QLatin1String(
"y1"));
2092 const QStringView x2 = attributes.value(QLatin1String(
"x2"));
2093 const QStringView y2 = attributes.value(QLatin1String(
"y2"));
2101 nx1 = convertToNumber(x1);
2103 ny1 = convertToNumber(y1);
2105 nx2 = convertToNumber(x2);
2107 ny2 = convertToNumber(y2);
2110 grad->setInterpolationMode(QGradient::ComponentInterpolation);
2112 QSvgGradientPaintSharedPtr paintServer = std::make_shared<QSvgGradientPaint>(std::move(grad));
2113 parseBaseGradient(attributes, paintServer.get(), handler);
2119 const QXmlStreamAttributes &attributes,
2122 Q_UNUSED(parent); Q_UNUSED(attributes);
2127 const QXmlStreamAttributes &attributes,
2130 Q_UNUSED(parent); Q_UNUSED(attributes);
2135 const QXmlStreamAttributes &attributes,
2138 Q_UNUSED(parent); Q_UNUSED(attributes);
2143 const QXmlStreamAttributes &,
2150 const QXmlStreamAttributes &attributes,
2151 QSvgHandler *handler)
2153 const QStringView x = attributes.value(QLatin1String(
"x"));
2154 const QStringView y = attributes.value(QLatin1String(
"y"));
2155 const QStringView width = attributes.value(QLatin1String(
"width"));
2156 const QStringView height = attributes.value(QLatin1String(
"height"));
2157 const QStringView mU = attributes.value(QLatin1String(
"maskUnits"));
2158 const QStringView mCU = attributes.value(QLatin1String(
"maskContentUnits"));
2160 QtSvg::
UnitTypes nmU = mU.contains(QLatin1String(
"userSpaceOnUse")) ?
2163 QtSvg::
UnitTypes nmCU = mCU.contains(QLatin1String(
"objectBoundingBox")) ?
2167 QGuiSvg::LengthType type;
2173 qreal nx = QGuiSvg::parseLength(x, &type, &ok);
2174 nx = QGuiSvg::convertToPixels(nx,
true, type);
2175 if (x.isEmpty() || !ok) {
2178 }
else if (type == QGuiSvg::LengthType::LT_PERCENT && nmU == QtSvg::UnitTypes::userSpaceOnUse) {
2179 nx = nx / 100. * handler->document()->viewBox().width();
2180 }
else if (type == QGuiSvg::LengthType::LT_PERCENT) {
2184 qreal ny = QGuiSvg::parseLength(y, &type, &ok);
2185 ny = QGuiSvg::convertToPixels(ny,
true, type);
2186 if (y.isEmpty() || !ok) {
2189 }
else if (type == QGuiSvg::LengthType::LT_PERCENT && nmU == QtSvg::UnitTypes::userSpaceOnUse) {
2190 ny = ny / 100. * handler->document()->viewBox().height();
2191 }
else if (type == QGuiSvg::LengthType::LT_PERCENT) {
2195 qreal nwidth = QGuiSvg::parseLength(width, &type, &ok);
2196 nwidth = QGuiSvg::convertToPixels(nwidth,
true, type);
2197 if (width.isEmpty() || !ok) {
2200 }
else if (type == QGuiSvg::LengthType::LT_PERCENT && nmU == QtSvg::UnitTypes::userSpaceOnUse) {
2201 nwidth = nwidth / 100. * handler->document()->viewBox().width();
2202 }
else if (type == QGuiSvg::LengthType::LT_PERCENT) {
2203 nwidth = nwidth / 100.;
2206 qreal nheight = QGuiSvg::parseLength(height, &type, &ok);
2207 nheight = QGuiSvg::convertToPixels(nheight,
true, type);
2208 if (height.isEmpty() || !ok) {
2211 }
else if (type == QGuiSvg::LengthType::LT_PERCENT && nmU == QtSvg::UnitTypes::userSpaceOnUse) {
2212 nheight = nheight / 100. * handler->document()->viewBox().height();
2213 }
else if (type == QGuiSvg::LengthType::LT_PERCENT) {
2214 nheight = nheight / 100.;
2217 QRectF bounds(nx, ny, nwidth, nheight);
2218 if (bounds.isEmpty())
2221 QSvgNode *mask =
new QSvgMask(parent, QSvgRectF(bounds, nmUx, nmUy, nmUw, nmUh), nmCU);
2228 const QStringView xStr = attributes.value(QLatin1String(
"x"));
2229 const QStringView yStr = attributes.value(QLatin1String(
"y"));
2230 const QStringView widthStr = attributes.value(QLatin1String(
"width"));
2231 const QStringView heightStr = attributes.value(QLatin1String(
"height"));
2234 if (!xStr.isEmpty()) {
2235 QGuiSvg::LengthType type;
2236 x = QGuiSvg::parseLength(xStr, &type);
2237 if (type != QGuiSvg::LengthType::LT_PT) {
2238 x = QGuiSvg::convertToPixels(x,
true, type);
2241 if (type == QGuiSvg::LengthType::LT_PERCENT) {
2248 if (!yStr.isEmpty()) {
2249 QGuiSvg::LengthType type;
2250 y = QGuiSvg::parseLength(yStr, &type);
2251 if (type != QGuiSvg::LengthType::LT_PT) {
2252 y = QGuiSvg::convertToPixels(y,
false, type);
2255 if (type == QGuiSvg::LengthType::LT_PERCENT) {
2262 if (!widthStr.isEmpty()) {
2263 QGuiSvg::LengthType type;
2264 width = QGuiSvg::parseLength(widthStr, &type);
2265 if (type != QGuiSvg::LengthType::LT_PT) {
2266 width = QGuiSvg::convertToPixels(width,
true, type);
2269 if (type == QGuiSvg::LengthType::LT_PERCENT) {
2273 rect->setWidth(width);
2276 if (!heightStr.isEmpty()) {
2277 QGuiSvg::LengthType type;
2278 height = QGuiSvg::parseLength(heightStr, &type);
2279 if (type != QGuiSvg::LengthType::LT_PT) {
2280 height = QGuiSvg::convertToPixels(height,
false, type);
2283 if (type == QGuiSvg::LengthType::LT_PERCENT) {
2287 rect->setHeight(height);
2292 const QXmlStreamAttributes &attributes,
2293 QSvgHandler *handler)
2295 const QStringView fU = attributes.value(QLatin1String(
"filterUnits"));
2296 const QStringView pU = attributes.value(QLatin1String(
"primitiveUnits"));
2298 const QtSvg::
UnitTypes filterUnits = fU.contains(QLatin1String(
"userSpaceOnUse")) ?
2301 const QtSvg::
UnitTypes primitiveUnits = pU.contains(QLatin1String(
"objectBoundingBox")) ?
2309 qreal width = handler->document()->viewBox().width();
2310 qreal height = handler->document()->viewBox().height();
2311 rect = QSvgRectF(QRectF(-0.1 * width, -0.1 * height, 1.2 * width, 1.2 * height),
2315 rect = QSvgRectF(QRectF(-0.1, -0.1, 1.2, 1.2),
2320 parseFilterBounds(attributes, &rect);
2322 QSvgNode *filter =
new QSvgFilterContainer(parent, rect, filterUnits, primitiveUnits);
2327 QString *outString, QSvgRectF *rect)
2329 *inString = attributes.value(QLatin1String(
"in")).toString();
2330 *outString = attributes.value(QLatin1String(
"result")).toString();
2336 *rect = QSvgRectF(QRectF(0, 0, 1.0, 1.0),
2342 parseFilterBounds(attributes, rect);
2346 const QXmlStreamAttributes &attributes,
2349 const QStringView typeString = attributes.value(QLatin1String(
"type"));
2350 const QStringView valuesString = attributes.value(QLatin1String(
"values"));
2352 QString inputString;
2353 QString outputString;
2356 QSvgFeColorMatrix::ColorShiftType type;
2357 QSvgFeColorMatrix::Matrix values;
2360 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2362 if (typeString.startsWith(QLatin1String(
"saturate")))
2363 type = QSvgFeColorMatrix::ColorShiftType::Saturate;
2364 else if (typeString.startsWith(QLatin1String(
"hueRotate")))
2365 type = QSvgFeColorMatrix::ColorShiftType::HueRotate;
2366 else if (typeString.startsWith(QLatin1String(
"luminanceToAlpha")))
2367 type = QSvgFeColorMatrix::ColorShiftType::LuminanceToAlpha;
2369 type = QSvgFeColorMatrix::ColorShiftType::Matrix;
2371 if (!valuesString.isEmpty()) {
2372 const auto valueStringList = splitWithDelimiter(valuesString);
2373 for (
int i = 0, j = 0; i < qMin(20, valueStringList.size()); i++) {
2375 qreal v = QGuiSvg::toDouble(valueStringList.at(i), &ok);
2377 values.data()[j] = v;
2382 values.setToIdentity();
2385 QSvgNode *filter =
new QSvgFeColorMatrix(parent, inputString, outputString, rect,
2391 const QXmlStreamAttributes &attributes,
2394 const QStringView edgeModeString = attributes.value(QLatin1String(
"edgeMode"));
2395 const QStringView stdDeviationString = attributes.value(QLatin1String(
"stdDeviation"));
2397 QString inputString;
2398 QString outputString;
2401 QSvgFeGaussianBlur::EdgeMode edgemode = QSvgFeGaussianBlur::EdgeMode::Duplicate;
2403 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2404 qreal stdDeviationX = 0;
2405 qreal stdDeviationY = 0;
2407 stdDeviationX = qMax(0., QGuiSvg::toDouble(stdDeviationString.split(u" ").constFirst()));
2408 stdDeviationY = qMax(0., QGuiSvg::toDouble(stdDeviationString.split(u" ").constLast()));
2410 stdDeviationY = stdDeviationX = qMax(0., QGuiSvg::toDouble(stdDeviationString));
2413 if (edgeModeString.startsWith(QLatin1String(
"wrap")))
2414 edgemode = QSvgFeGaussianBlur::EdgeMode::Wrap;
2415 else if (edgeModeString.startsWith(QLatin1String(
"none")))
2416 edgemode = QSvgFeGaussianBlur::EdgeMode::None;
2418 QSvgNode *filter =
new QSvgFeGaussianBlur(parent, inputString, outputString, rect,
2419 stdDeviationX, stdDeviationY, edgemode);
2424 const QXmlStreamAttributes &attributes,
2427 QStringView dxString = attributes.value(QLatin1String(
"dx"));
2428 QStringView dyString = attributes.value(QLatin1String(
"dy"));
2430 QString inputString;
2431 QString outputString;
2434 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2437 if (!dxString.isEmpty()) {
2438 QGuiSvg::LengthType type;
2439 dx = QGuiSvg::parseLength(dxString, &type);
2440 if (type != QGuiSvg::LengthType::LT_PT)
2441 dx = QGuiSvg::convertToPixels(dx,
true, type);
2445 if (!dyString.isEmpty()) {
2446 QGuiSvg::LengthType type;
2447 dy = QGuiSvg::parseLength(dyString, &type);
2448 if (type != QGuiSvg::LengthType::LT_PT)
2449 dy = QGuiSvg::convertToPixels(dy,
true, type);
2452 QSvgNode *filter =
new QSvgFeOffset(parent, inputString, outputString, rect,
2458 const QXmlStreamAttributes &attributes,
2461 const QStringView in2String = attributes.value(QLatin1String(
"in2"));
2462 const QStringView operatorString = attributes.value(QLatin1String(
"operator"));
2463 const QStringView k1String = attributes.value(QLatin1String(
"k1"));
2464 const QStringView k2String = attributes.value(QLatin1String(
"k2"));
2465 const QStringView k3String = attributes.value(QLatin1String(
"k3"));
2466 const QStringView k4String = attributes.value(QLatin1String(
"k4"));
2468 QString inputString;
2469 QString outputString;
2472 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2474 QSvgFeComposite::Operator op = QSvgFeComposite::Operator::Over;
2475 if (operatorString.startsWith(QLatin1String(
"in")))
2476 op = QSvgFeComposite::Operator::In;
2477 else if (operatorString.startsWith(QLatin1String(
"out")))
2478 op = QSvgFeComposite::Operator::Out;
2479 else if (operatorString.startsWith(QLatin1String(
"atop")))
2480 op = QSvgFeComposite::Operator::Atop;
2481 else if (operatorString.startsWith(QLatin1String(
"xor")))
2482 op = QSvgFeComposite::Operator::Xor;
2483 else if (operatorString.startsWith(QLatin1String(
"lighter")))
2484 op = QSvgFeComposite::Operator::Lighter;
2485 else if (operatorString.startsWith(QLatin1String(
"arithmetic")))
2486 op = QSvgFeComposite::Operator::Arithmetic;
2488 QVector4D k(0, 0, 0, 0);
2490 if (op == QSvgFeComposite::Operator::Arithmetic) {
2492 qreal v = QGuiSvg::toDouble(k1String, &ok);
2495 v = QGuiSvg::toDouble(k2String, &ok);
2498 v = QGuiSvg::toDouble(k3String, &ok);
2501 v = QGuiSvg::toDouble(k4String, &ok);
2506 QSvgNode *filter =
new QSvgFeComposite(parent, inputString, outputString, rect,
2507 in2String.toString(), op, k);
2513 const QXmlStreamAttributes &attributes,
2516 QString inputString;
2517 QString outputString;
2520 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2522 QSvgNode *filter =
new QSvgFeMerge(parent, inputString, outputString, rect);
2527 const QXmlStreamAttributes &attributes,
2528 QSvgHandler *handler)
2530 QStringView colorStr = attributes.value(QLatin1String(
"flood-color"));
2531 const QStringView opacityStr = attributes.value(QLatin1String(
"flood-opacity"));
2534 if (!constructColor(colorStr, opacityStr, color, handler)) {
2535 color = QColor(Qt::black);
2536 if (opacityStr.isEmpty())
2537 color.setAlphaF(1.0);
2539 setAlpha(opacityStr, &color);
2542 QString inputString;
2543 QString outputString;
2546 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2548 QSvgNode *filter =
new QSvgFeFlood(parent, inputString, outputString, rect, color);
2553 const QXmlStreamAttributes &attributes,
2556 QString inputString;
2557 QString outputString;
2560 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2562 QSvgNode *filter =
new QSvgFeMergeNode(parent, inputString, outputString, rect);
2567 const QXmlStreamAttributes &attributes,
2570 const QStringView in2String = attributes.value(QLatin1String(
"in2"));
2571 const QStringView modeString = attributes.value(QLatin1String(
"mode"));
2573 QString inputString;
2574 QString outputString;
2577 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2579 QSvgFeBlend::Mode mode = QSvgFeBlend::Mode::Normal;
2580 if (modeString.startsWith(QLatin1StringView(
"multiply")))
2581 mode = QSvgFeBlend::Mode::Multiply;
2582 else if (modeString.startsWith(QLatin1StringView(
"screen")))
2583 mode = QSvgFeBlend::Mode::Screen;
2584 else if (modeString.startsWith(QLatin1StringView(
"darken")))
2585 mode = QSvgFeBlend::Mode::Darken;
2586 else if (modeString.startsWith(QLatin1StringView(
"lighten")))
2587 mode = QSvgFeBlend::Mode::Lighten;
2589 QSvgNode *filter =
new QSvgFeBlend(parent, inputString, outputString, rect,
2590 in2String.toString(), mode);
2595 const QXmlStreamAttributes &attributes,
2598 QString inputString;
2599 QString outputString;
2602 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2604 QSvgNode *filter =
new QSvgFeUnsupported(parent, inputString, outputString, rect);
2610 QList<QStringView> viewBoxValues;
2613 viewBoxValues = splitWithDelimiter(str);
2614 if (viewBoxValues.size() == 4) {
2615 QGuiSvg::LengthType type;
2616 qreal x = QGuiSvg::parseLength(viewBoxValues.at(0).trimmed(), &type);
2617 qreal y = QGuiSvg::parseLength(viewBoxValues.at(1).trimmed(), &type);
2618 qreal w = QGuiSvg::parseLength(viewBoxValues.at(2).trimmed(), &type);
2619 qreal h = QGuiSvg::parseLength(viewBoxValues.at(3).trimmed(), &type);
2620 return QRectF(x, y, w, h);
2622 return std::nullopt;
2626 QRectF *rect, QRectF *viewBox, QPointF *refPoint,
2627 QSvgSymbolLike::PreserveAspectRatios *aspect,
2628 QSvgSymbolLike::Overflow *overflow,
2629 bool marker =
false)
2631 const QStringView xStr = attributes.value(QLatin1String(
"x"));
2632 const QStringView yStr = attributes.value(QLatin1String(
"y"));
2633 const QStringView refXStr = attributes.value(QLatin1String(
"refX"));
2634 const QStringView refYStr = attributes.value(QLatin1String(
"refY"));
2635 const QStringView widthStr = attributes.value(marker ? QLatin1String(
"markerWidth")
2636 : QLatin1String(
"width"));
2637 const QStringView heightStr = attributes.value(marker ? QLatin1String(
"markerHeight")
2638 : QLatin1String(
"height"));
2639 const QStringView pAspectRStr = attributes.value(QLatin1String(
"preserveAspectRatio"));
2640 const QStringView overflowStr = attributes.value(QLatin1String(
"overflow"));
2641 const QStringView viewBoxStr = attributes.value(QLatin1String(
"viewBox"));
2645 if (!xStr.isEmpty()) {
2646 QGuiSvg::LengthType type;
2647 x = QGuiSvg::parseLength(xStr, &type);
2648 if (type != QGuiSvg::LengthType::LT_PT)
2649 x = QGuiSvg::convertToPixels(x,
true, type);
2652 if (!yStr.isEmpty()) {
2653 QGuiSvg::LengthType type;
2654 y = QGuiSvg::parseLength(yStr, &type);
2655 if (type != QGuiSvg::LengthType::LT_PT)
2656 y = QGuiSvg::convertToPixels(y,
false, type);
2659 if (!widthStr.isEmpty()) {
2660 QGuiSvg::LengthType type;
2661 width = QGuiSvg::parseLength(widthStr, &type);
2662 if (type != QGuiSvg::LengthType::LT_PT)
2663 width = QGuiSvg::convertToPixels(width,
true, type);
2666 if (!heightStr.isEmpty()) {
2667 QGuiSvg::LengthType type;
2668 height = QGuiSvg::parseLength(heightStr, &type);
2669 if (type != QGuiSvg::LengthType::LT_PT)
2670 height = QGuiSvg::convertToPixels(height,
false, type);
2673 *rect = QRectF(x, y, width, height);
2676 if (!refXStr.isEmpty()) {
2677 QGuiSvg::LengthType type;
2678 x = QGuiSvg::parseLength(refXStr, &type);
2679 if (type != QGuiSvg::LengthType::LT_PT)
2680 x = QGuiSvg::convertToPixels(x,
true, type);
2683 if (!refYStr.isEmpty()) {
2684 QGuiSvg::LengthType type;
2685 y = QGuiSvg::parseLength(refYStr, &type);
2686 if (type != QGuiSvg::LengthType::LT_PT)
2687 y = QGuiSvg::convertToPixels(y,
false, type);
2689 *refPoint = QPointF(x,y);
2691 auto viewBoxResult = parseViewBox(viewBoxStr);
2693 *viewBox = *viewBoxResult;
2694 else if (width > 0 && height > 0)
2695 *viewBox = QRectF(0, 0, width, height);
2697 *viewBox = handler->document()->viewBox();
2699 if (viewBox->isNull())
2702 auto pAspectRStrs = pAspectRStr.split(u" ");
2703 QSvgSymbolLike::PreserveAspectRatio aspectX = QSvgSymbolLike::PreserveAspectRatio::xMid;
2704 QSvgSymbolLike::PreserveAspectRatio aspectY = QSvgSymbolLike::PreserveAspectRatio::yMid;
2705 QSvgSymbolLike::PreserveAspectRatio aspectMS = QSvgSymbolLike::PreserveAspectRatio::meet;
2707 for (
auto &pAStr : std::as_const(pAspectRStrs)) {
2708 if (pAStr.startsWith(QLatin1String(
"none"))) {
2709 aspectX = QSvgSymbolLike::PreserveAspectRatio::None;
2710 aspectY = QSvgSymbolLike::PreserveAspectRatio::None;
2712 if (pAStr.startsWith(QLatin1String(
"xMin")))
2713 aspectX = QSvgSymbolLike::PreserveAspectRatio::xMin;
2714 else if (pAStr.startsWith(QLatin1String(
"xMax")))
2715 aspectX = QSvgSymbolLike::PreserveAspectRatio::xMax;
2716 if (pAStr.endsWith(QLatin1String(
"YMin")))
2717 aspectY = QSvgSymbolLike::PreserveAspectRatio::yMin;
2718 else if (pAStr.endsWith(QLatin1String(
"YMax")))
2719 aspectY = QSvgSymbolLike::PreserveAspectRatio::yMax;
2722 if (pAStr.endsWith(QLatin1String(
"slice")))
2723 aspectMS = QSvgSymbolLike::PreserveAspectRatio::slice;
2725 *aspect = aspectX | aspectY | aspectMS;
2732 *overflow = QSvgSymbolLike::Overflow::Hidden;
2734 if (overflowStr.endsWith(QLatin1String(
"auto")))
2735 *overflow = QSvgSymbolLike::Overflow::Auto;
2736 else if (overflowStr.endsWith(QLatin1String(
"visible")))
2737 *overflow = QSvgSymbolLike::Overflow::Visible;
2738 else if (overflowStr.endsWith(QLatin1String(
"hidden")))
2739 *overflow = QSvgSymbolLike::Overflow::Hidden;
2740 else if (overflowStr.endsWith(QLatin1String(
"scroll")))
2741 *overflow = QSvgSymbolLike::Overflow::Scroll;
2747 const QXmlStreamAttributes &attributes,
2748 QSvgHandler *handler)
2750 QRectF rect, viewBox;
2752 QSvgSymbolLike::PreserveAspectRatios aspect;
2753 QSvgSymbolLike::Overflow overflow;
2755 if (!parseSymbolLikeAttributes(attributes, handler, &rect, &viewBox, &refP, &aspect, &overflow))
2758 refP = QPointF(0, 0);
2759 QSvgNode *symbol =
new QSvgSymbol(parent, rect, viewBox, refP, aspect, overflow);
2764 const QXmlStreamAttributes &attributes,
2765 QSvgHandler *handler)
2767 QRectF rect, viewBox;
2769 QSvgSymbolLike::PreserveAspectRatios aspect;
2770 QSvgSymbolLike::Overflow overflow;
2772 const QStringView orientStr = attributes.value(QLatin1String(
"orient"));
2773 const QStringView markerUnitsStr = attributes.value(QLatin1String(
"markerUnits"));
2775 qreal orientationAngle = 0;
2776 QSvgMarker::Orientation orientation;
2777 if (orientStr.startsWith(QLatin1String(
"auto-start-reverse")))
2778 orientation = QSvgMarker::Orientation::AutoStartReverse;
2779 else if (orientStr.startsWith(QLatin1String(
"auto")))
2780 orientation = QSvgMarker::Orientation::Auto;
2782 orientation = QSvgMarker::Orientation::Value;
2785 if (orientStr.endsWith(QLatin1String(
"turn")))
2786 a = 360. * QGuiSvg::toDouble(orientStr.mid(0, orientStr.length()-4), &ok);
2787 else if (orientStr.endsWith(QLatin1String(
"grad")))
2788 a = QGuiSvg::toDouble(orientStr.mid(0, orientStr.length()-4), &ok);
2789 else if (orientStr.endsWith(QLatin1String(
"rad")))
2790 a = 180. /
M_PI * QGuiSvg::toDouble(orientStr.mid(0, orientStr.length()-3), &ok);
2792 a = QGuiSvg::toDouble(orientStr, &ok);
2794 orientationAngle = a;
2797 QSvgMarker::MarkerUnits markerUnits = QSvgMarker::MarkerUnits::StrokeWidth;
2798 if (markerUnitsStr.startsWith(QLatin1String(
"userSpaceOnUse")))
2799 markerUnits = QSvgMarker::MarkerUnits::UserSpaceOnUse;
2801 if (!parseSymbolLikeAttributes(attributes, handler, &rect, &viewBox, &refP, &aspect, &overflow,
true))
2804 QSvgNode *marker =
new QSvgMarker(parent, rect, viewBox, refP, aspect, overflow,
2805 orientation, orientationAngle, markerUnits);
2810 const QXmlStreamAttributes &attributes,
2811 QSvgHandler *handler)
2813 QStringView data = attributes.value(QLatin1String(
"d"));
2815 std::optional<QPainterPath> qpath = QGuiSvg::parsePath(data,
2816 !handler->trustedSourceMode());
2818 qCWarning(lcSvgHandler,
"Invalid path data; path truncated.");
2822 qpath.value().setFillRule(Qt::WindingFill);
2823 QSvgNode *path =
new QSvgPath(parent, qpath.value());
2828 const QXmlStreamAttributes &attributes,
2831 QStringView pointsStr = attributes.value(QLatin1String(
"points"));
2832 const QList<qreal> points = parseNumbersList(&pointsStr);
2833 if (points.size() < 4)
2836 for (
int i = 0; i < poly.size(); ++i)
2837 poly[i] = QPointF(points.at(2 * i), points.at(2 * i + 1));
2839 return new QSvgPolyline(parent, poly);
2841 return new QSvgPolygon(parent, poly);
2845 const QXmlStreamAttributes &attributes,
2848 return createPolyNode(parent, attributes,
false);
2852 const QXmlStreamAttributes &attributes,
2855 return createPolyNode(parent, attributes,
true);
2859 const QXmlStreamAttributes &attributes,
2862 Q_UNUSED(parent); Q_UNUSED(attributes);
2867 QSvgHandler *handler)
2869 const QStringView cx = attributes.value(QLatin1String(
"cx"));
2870 const QStringView cy = attributes.value(QLatin1String(
"cy"));
2871 const QStringView r = attributes.value(QLatin1String(
"r"));
2872 const QStringView fx = attributes.value(QLatin1String(
"fx"));
2873 const QStringView fy = attributes.value(QLatin1String(
"fy"));
2878 ncx = convertToNumber(cx);
2880 ncy = convertToNumber(cy);
2884 nr = convertToNumber(r);
2890 nfx = convertToNumber(fx);
2893 nfy = convertToNumber(fy);
2896 grad->setInterpolationMode(QGradient::ComponentInterpolation);
2898 QSvgGradientPaintSharedPtr paintServer = std::make_shared<QSvgGradientPaint>(std::move(grad));
2899 parseBaseGradient(attributes, paintServer.get(), handler);
2905 const QXmlStreamAttributes &attributes,
2908 const QStringView x = attributes.value(QLatin1String(
"x"));
2909 const QStringView y = attributes.value(QLatin1String(
"y"));
2910 const QStringView width = attributes.value(QLatin1String(
"width"));
2911 const QStringView height = attributes.value(QLatin1String(
"height"));
2912 const QStringView rx = attributes.value(QLatin1String(
"rx"));
2913 const QStringView ry = attributes.value(QLatin1String(
"ry"));
2916 QGuiSvg::LengthType type;
2917 qreal nwidth = QGuiSvg::parseLength(width, &type, &ok);
2920 nwidth = QGuiSvg::convertToPixels(nwidth,
true, type);
2921 qreal nheight = QGuiSvg::parseLength(height, &type, &ok);
2924 nheight = QGuiSvg::convertToPixels(nheight,
true, type);
2925 qreal nrx = QGuiSvg::toDouble(rx);
2926 qreal nry = QGuiSvg::toDouble(ry);
2928 QRectF bounds(QGuiSvg::toDouble(x), QGuiSvg::toDouble(y), nwidth, nheight);
2929 if (bounds.isEmpty())
2932 if (!rx.isEmpty() && ry.isEmpty())
2934 else if (!ry.isEmpty() && rx.isEmpty())
2940 if (nrx > bounds.width()/2)
2941 nrx = bounds.width()/2;
2942 if (nry > bounds.height()/2)
2943 nry = bounds.height()/2;
2948 nrx *= (100/(bounds.width()/2));
2949 nry *= (100/(bounds.height()/2));
2951 QSvgNode *rect =
new QSvgRect(parent, bounds, nrx, nry);
2956 const QXmlStreamAttributes &attributes,
2959 Q_UNUSED(parent); Q_UNUSED(attributes);
2964 const QXmlStreamAttributes &attributes,
2967 Q_UNUSED(parent); Q_UNUSED(attributes);
2972 QSvgHandler *handler)
2974 Q_UNUSED(attributes);
2975 QStringView solidColorStr = attributes.value(QLatin1String(
"solid-color"));
2976 QStringView solidOpacityStr = attributes.value(QLatin1String(
"solid-opacity"));
2978 if (solidOpacityStr.isEmpty())
2979 solidOpacityStr = attributes.value(QLatin1String(
"opacity"));
2982 if (!constructColor(solidColorStr, solidOpacityStr, color, handler))
2984 std::shared_ptr<QSvgSolidColorPaint> paintServer = std::make_shared<QSvgSolidColorPaint>(color);
2989 const QXmlStreamAttributes &attributes,
2990 QSvgHandler *handler)
2992 if (paintServer->type() != QSvgPaintServer::Type::Gradient)
2994 QString nodeIdStr = someId(attributes);
2995 QString xmlClassStr = attributes.value(QLatin1String(
"class")).toString();
3002 dummy.setNodeId(nodeIdStr);
3003 dummy.setXmlClass(xmlClassStr);
3007#ifndef QT_NO_CSSPARSER
3008 QXmlStreamAttributes cssAttributes;
3009 handler->cssHandler().styleLookup(&dummy, cssAttributes);
3010 attrs.setAttributes(cssAttributes, handler);
3012 QXmlStreamAttributes styleCssAttributes;
3013 QStringView style = attributes.value(QLatin1String(
"style"));
3014 if (!style.isEmpty())
3015 handler->cssHandler().parseCSStoXMLAttrs(style.toString(), styleCssAttributes);
3016 attrs.setAttributes(styleCssAttributes, handler);
3020 parseColor(&dummy, attrs, handler);
3022 QSvgGradientPaint *gradientStyle =
static_cast<QSvgGradientPaint*>(paintServer);
3023 QStringView colorStr = attrs.stopColor;
3027 qreal offset = convertToNumber(attrs.offset, &ok);
3031 if (!constructColor(colorStr, attrs.stopOpacity, color, handler)) {
3033 if (!attrs.stopOpacity.isEmpty())
3034 setAlpha(attrs.stopOpacity, &color);
3037 QGradient *grad = gradientStyle->qgradient();
3039 offset = qMin(qreal(1), qMax(qreal(0), offset));
3041 if (gradientStyle->gradientStopsSet()) {
3042 stops = grad->stops();
3044 if (offset <= stops.back().first)
3045 offset = stops.back().first + FLT_EPSILON;
3050 if ((stops.size() == 1) || (stops.at(stops.size() - 2).first < 1.0 - FLT_EPSILON)) {
3051 stops.back().first = 1.0 - FLT_EPSILON;
3052 grad->setStops(stops);
3057 grad->setColorAt(offset, color);
3058 gradientStyle->setGradientStopsSet(
true);
3063 const QXmlStreamAttributes &attributes,
3064 QSvgHandler *handler)
3067#ifdef QT_NO_CSSPARSER
3068 Q_UNUSED(attributes);
3071 const QStringView type = attributes.value(QLatin1String(
"type"));
3072 if (type.compare(QLatin1String(
"text/css"), Qt::CaseInsensitive) == 0 || type.isNull())
3073 handler->setInStyle(
true);
3080 const QXmlStreamAttributes &attributes,
3081 QSvgHandler *handler)
3083 Q_UNUSED(parent); Q_UNUSED(attributes);
3085 QSvgDocument *node =
new QSvgDocument(handler->options(), handler->animatorType());
3086 const QStringView widthStr = attributes.value(QLatin1String(
"width"));
3087 const QStringView heightStr = attributes.value(QLatin1String(
"height"));
3088 const QStringView viewBoxStr = attributes.value(QLatin1String(
"viewBox"));
3090 QGuiSvg::LengthType type = QGuiSvg::LengthType::LT_PX;
3092 if (!widthStr.isEmpty()) {
3093 width = QGuiSvg::parseLength(widthStr, &type);
3094 if (type != QGuiSvg::LengthType::LT_PT)
3095 width = QGuiSvg::convertToPixels(width,
true, type);
3096 node->setWidth(
int(width), type == QGuiSvg::LengthType::LT_PERCENT);
3099 if (!heightStr.isEmpty()) {
3100 height = QGuiSvg::parseLength(heightStr, &type);
3101 if (type != QGuiSvg::LengthType::LT_PT)
3102 height = QGuiSvg::convertToPixels(height,
false, type);
3103 node->setHeight(
int(height), type == QGuiSvg::LengthType::LT_PERCENT);
3106 auto viewBoxResult = parseViewBox(viewBoxStr);
3107 if (viewBoxResult) {
3108 node->setViewBox(*viewBoxResult);
3109 }
else if (width && height) {
3110 if (type == QGuiSvg::LengthType::LT_PT) {
3111 width = QGuiSvg::convertToPixels(width,
false, type);
3112 height = QGuiSvg::convertToPixels(height,
false, type);
3114 node->setViewBox(QRectF(0, 0, width, height));
3116 handler->setDefaultCoordinateSystem(QGuiSvg::LengthType::LT_PX);
3122 const QXmlStreamAttributes &attributes,
3125 Q_UNUSED(attributes);
3126 QSvgSwitch *node =
new QSvgSwitch(parent);
3131 const QXmlStreamAttributes &attributes,
3132 QSvgHandler *handler)
3134 const QStringView x = attributes.value(QLatin1String(
"x"));
3135 const QStringView y = attributes.value(QLatin1String(
"y"));
3136 const QStringView width = attributes.value(QLatin1String(
"width"));
3137 const QStringView height = attributes.value(QLatin1String(
"height"));
3138 const QStringView patternUnits = attributes.value(QLatin1String(
"patternUnits"));
3139 const QStringView patternContentUnits = attributes.value(QLatin1String(
"patternContentUnits"));
3140 const QStringView patternTransform = attributes.value(QLatin1String(
"patternTransform"));
3142 QtSvg::
UnitTypes nPatternUnits = patternUnits.contains(QLatin1String(
"userSpaceOnUse")) ?
3145 QtSvg::
UnitTypes nPatternContentUnits = patternContentUnits.contains(QLatin1String(
"objectBoundingBox")) ?
3148 const QStringView viewBoxStr = attributes.value(QLatin1String(
"viewBox"));
3151 QGuiSvg::LengthType type;
3153 qreal nx = QGuiSvg::parseLength(x, &type, &ok);
3154 nx = QGuiSvg::convertToPixels(nx,
true, type);
3157 else if (type == QGuiSvg::LengthType::LT_PERCENT && nPatternUnits == QtSvg::UnitTypes::userSpaceOnUse)
3158 nx = (nx / 100.) * handler->document()->viewBox().width();
3159 else if (type == QGuiSvg::LengthType::LT_PERCENT)
3162 qreal ny = QGuiSvg::parseLength(y, &type, &ok);
3163 ny = QGuiSvg::convertToPixels(ny,
true, type);
3166 else if (type == QGuiSvg::LengthType::LT_PERCENT && nPatternUnits == QtSvg::UnitTypes::userSpaceOnUse)
3167 ny = (ny / 100.) * handler->document()->viewBox().height();
3168 else if (type == QGuiSvg::LengthType::LT_PERCENT)
3171 qreal nwidth = QGuiSvg::parseLength(width, &type, &ok);
3172 nwidth = QGuiSvg::convertToPixels(nwidth,
true, type);
3175 else if (type == QGuiSvg::LengthType::LT_PERCENT && nPatternUnits == QtSvg::UnitTypes::userSpaceOnUse)
3176 nwidth = (nwidth / 100.) * handler->document()->viewBox().width();
3177 else if (type == QGuiSvg::LengthType::LT_PERCENT)
3178 nwidth = nwidth / 100.;
3180 qreal nheight = QGuiSvg::parseLength(height, &type, &ok);
3181 nheight = QGuiSvg::convertToPixels(nheight,
true, type);
3184 else if (type == QGuiSvg::LengthType::LT_PERCENT && nPatternUnits == QtSvg::UnitTypes::userSpaceOnUse)
3185 nheight = (nheight / 100.) * handler->document()->viewBox().height();
3186 else if (type == QGuiSvg::LengthType::LT_PERCENT)
3187 nheight = nheight / 100.;
3190 auto viewBoxResult = parseViewBox(viewBoxStr);
3191 if (viewBoxResult) {
3192 if (viewBoxResult->width() > 0 && viewBoxResult->height() > 0)
3193 viewBox = *viewBoxResult;
3197 if (!patternTransform.isEmpty())
3198 matrix = parseTransformationMatrix(patternTransform);
3200 QRectF bounds(nx, ny, nwidth, nheight);
3201 if (bounds.isEmpty())
3204 QSvgRectF patternRectF(bounds, nPatternUnits, nPatternUnits, nPatternUnits, nPatternUnits);
3205 QSvgPattern *node =
new QSvgPattern(parent, patternRectF, viewBox, nPatternContentUnits, matrix);
3208 QSvgPaintServerSharedPtr prop = std::make_shared<QSvgPatternPaint>(node);
3209 handler->document()->addPaintServer(
std::move(prop), someId(attributes));
3215 const QXmlStreamAttributes &,
3218 if (parent->type() != QSvgNode::Textarea)
3220 static_cast<QSvgText*>(parent)->addLineBreak();
3225 const QXmlStreamAttributes &attributes,
3228 const QStringView x = attributes.value(QLatin1String(
"x"));
3229 const QStringView y = attributes.value(QLatin1String(
"y"));
3231 QGuiSvg::LengthType type;
3232 qreal nx = QGuiSvg::parseLength(x, &type);
3233 nx = QGuiSvg::convertToPixels(nx,
true, type);
3234 qreal ny = QGuiSvg::parseLength(y, &type);
3235 ny = QGuiSvg::convertToPixels(ny,
true, type);
3237 QSvgNode *text =
new QSvgText(parent, QPointF(nx, ny));
3242 const QXmlStreamAttributes &attributes,
3243 QSvgHandler *handler)
3245 QSvgText *node =
static_cast<QSvgText *>(createTextNode(parent, attributes, handler));
3247 QGuiSvg::LengthType type;
3248 qreal width = QGuiSvg::parseLength(attributes.value(QLatin1String(
"width")), &type);
3249 qreal height = QGuiSvg::parseLength(attributes.value(QLatin1String(
"height")), &type);
3250 node->setTextArea(QSizeF(width, height));
3256 const QXmlStreamAttributes &,
3259 return new QSvgTspan(parent);
3263 const QXmlStreamAttributes &attributes,
3264 QSvgHandler *handler)
3266 QStringView linkId = attributes.value(QLatin1String(
"xlink:href"));
3267 const QStringView xStr = attributes.value(QLatin1String(
"x"));
3268 const QStringView yStr = attributes.value(QLatin1String(
"y"));
3270 if (linkId.isEmpty())
3271 linkId = attributes.value(QLatin1String(
"href"));
3272 QString linkIdStr = idFromIRI(linkId).toString();
3274 switch (parent->type()) {
3276 case QSvgNode::Defs:
3277 case QSvgNode::Group:
3278 case QSvgNode::Switch:
3279 case QSvgNode::Mask:
3280 case QSvgNode::Symbol:
3281 case QSvgNode::Marker:
3282 case QSvgNode::Pattern:
3285 qCWarning(lcSvgHandler,
"<use> element %ls in wrong context!",
qUtf16Printable(linkIdStr));
3290 if (!xStr.isNull() || !yStr.isNull()) {
3291 QGuiSvg::LengthType type;
3292 qreal nx = QGuiSvg::parseLength(xStr, &type);
3293 nx = QGuiSvg::convertToPixels(nx,
true, type);
3295 qreal ny = QGuiSvg::parseLength(yStr, &type);
3296 ny = QGuiSvg::convertToPixels(ny,
true, type);
3297 pt = QPointF(nx, ny);
3300 QSvgNode *link = handler->document()->namedNode(linkIdStr);
3302 if (parent->isDescendantOf(link))
3303 qCWarning(lcSvgHandler,
"link %ls is recursive!",
qUtf16Printable(linkIdStr));
3305 return new QSvgUse(pt, parent, link);
3309 return new QSvgUse(pt, parent, linkIdStr);
3313 const QXmlStreamAttributes &attributes,
3316 Q_UNUSED(parent); Q_UNUSED(attributes);
3320typedef QSvgNode *(*FactoryMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3327 QStringView ref = name.mid(1);
3328 switch (name.at(0).unicode()) {
3330 if (ref == QLatin1String(
"efs"))
return createDefsNode;
3336 if (ref.isEmpty())
return createGNode;
3343 if (ref == QLatin1String(
"vg"))
return createSvgNode;
3344 if (ref == QLatin1String(
"witch"))
return createSwitchNode;
3362 QStringView ref = name.mid(1);
3363 switch (name.at(0).unicode()) {
3365 if (ref == QLatin1String(
"ircle"))
return createCircleNode;
3368 if (ref == QLatin1String(
"llipse"))
return createEllipseNode;
3371 if (ref == QLatin1String(
"mage"))
return createImageNode;
3374 if (ref == QLatin1String(
"ine"))
return createLineNode;
3377 if (ref == QLatin1String(
"ath"))
return createPathNode;
3378 if (ref == QLatin1String(
"olygon"))
return createPolygonNode;
3379 if (ref == QLatin1String(
"olyline"))
return createPolylineNode;
3382 if (ref == QLatin1String(
"ect"))
return createRectNode;
3385 if (ref == QLatin1String(
"ext"))
return createTextNode;
3386 if (ref == QLatin1String(
"extArea"))
return createTextAreaNode;
3387 if (ref == QLatin1String(
"span"))
return createTspanNode;
3390 if (ref == QLatin1String(
"se"))
return createUseNode;
3393 if (ref == QLatin1String(
"ideo"))
return createVideoNode;
3409 if (!name.startsWith(QLatin1String(
"fe")))
3412 if (name == QLatin1String(
"feMerge"))
return createFeMergeNode;
3413 if (name == QLatin1String(
"feColorMatrix"))
return createFeColorMatrixNode;
3414 if (name == QLatin1String(
"feGaussianBlur"))
return createFeGaussianBlurNode;
3415 if (name == QLatin1String(
"feOffset"))
return createFeOffsetNode;
3416 if (name == QLatin1String(
"feMergeNode"))
return createFeMergeNodeNode;
3417 if (name == QLatin1String(
"feComposite"))
return createFeCompositeNode;
3418 if (name == QLatin1String(
"feFlood"))
return createFeFloodNode;
3419 if (name == QLatin1String(
"feBlend"))
return createFeBlendNode;
3421 static const QStringList unsupportedFilters = {
3438 if (unsupportedFilters.contains(name))
3439 return createFeUnsupportedNode;
3444typedef QSvgNode *(*AnimationMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3451 QStringView ref = name.mid(1);
3453 switch (name.at(0).unicode()) {
3455 if (ref == QLatin1String(
"nimate"))
return createAnimateNode;
3456 if (ref == QLatin1String(
"nimateColor"))
return createAnimateColorNode;
3457 if (ref == QLatin1String(
"nimateMotion"))
return createAnimateMotionNode;
3458 if (ref == QLatin1String(
"nimateTransform"))
return createAnimateTransformNode;
3467typedef bool (*
ParseMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3474 QStringView ref = name.mid(1);
3475 switch (name.at(0).unicode()) {
3477 if (ref.isEmpty())
return parseAnchorNode;
3478 if (ref == QLatin1String(
"udio"))
return parseAudioNode;
3481 if (ref == QLatin1String(
"iscard"))
return parseDiscardNode;
3484 if (ref == QLatin1String(
"oreignObject"))
return parseForeignObjectNode;
3487 if (ref == QLatin1String(
"andler"))
return parseHandlerNode;
3488 if (ref == QLatin1String(
"kern"))
return parseHkernNode;
3491 if (ref == QLatin1String(
"etadata"))
return parseMetadataNode;
3492 if (ref == QLatin1String(
"path"))
return parseMpathNode;
3497 if (ref == QLatin1String(
"refetch"))
return parsePrefetchNode;
3500 if (ref == QLatin1String(
"cript"))
return parseScriptNode;
3501 if (ref == QLatin1String(
"et"))
return parseSetNode;
3502 if (ref == QLatin1String(
"tyle"))
return parseStyleNode;
3505 if (ref == QLatin1String(
"break"))
return parseTbreakNode;
3518 if (name ==
"font"_L1)
3519 return createFontNode;
3525 const QXmlStreamAttributes &,
3533 QStringView ref = name.mid(1);
3534 switch (name.at(0).unicode()) {
3536 if (ref == QLatin1String(
"ont-face"))
return parseFontFaceNode;
3537 if (ref == QLatin1String(
"ont-face-name"))
return parseFontFaceNameNode;
3538 if (ref == QLatin1String(
"ont-face-src"))
return parseFontFaceSrcNode;
3539 if (ref == QLatin1String(
"ont-face-uri"))
return parseFontFaceUriNode;
3542 if (ref == QLatin1String(
"lyph"))
return parseGlyphNode;
3545 if (ref == QLatin1String(
"issing-glyph"))
return parseMissingGlyphNode;
3561 QStringView ref = name.sliced(1);
3562 switch (name.at(0).unicode()) {
3564 if (ref == QLatin1String(
"inearGradient"))
return createLinearGradientNode;
3567 if (ref == QLatin1String(
"adialGradient"))
return createRadialGradientNode;
3570 if (ref == QLatin1String(
"olidColor"))
return createSolidColorNode;
3579 const QXmlStreamAttributes &,
3587 QStringView ref = name.sliced(1);
3588 switch (name.at(0).unicode()) {
3590 if (ref == QLatin1String(
"top"))
return parseStopNode;
3598QSvgHandler::QSvgHandler(QIODevice *device, QtSvg::Options options,
3599 QtSvg::AnimatorType type)
3600 : xml(
new QXmlStreamReader(device))
3601 , m_ownsReader(
true)
3602 , m_options(options)
3603 , m_animatorType(type)
3608QSvgHandler::QSvgHandler(
const QByteArray &data, QtSvg::Options options,
3609 QtSvg::AnimatorType type)
3610 : xml(
new QXmlStreamReader(data))
3611 , m_ownsReader(
true)
3612 , m_options(options)
3613 , m_animatorType(type)
3618QSvgHandler::QSvgHandler(QXmlStreamReader *
const reader, QtSvg::Options options,
3619 QtSvg::AnimatorType type)
3621 , m_ownsReader(
false)
3622 , m_options(options)
3623 , m_animatorType(type)
3628void QSvgHandler::init()
3631 m_defaultCoords = QGuiSvg::LT_PX;
3632 m_defaultPen = QPen(Qt::black, 1, Qt::SolidLine, Qt::FlatCap, Qt::SvgMiterJoin);
3633 m_defaultPen.setMiterLimit(4);
3639 const QSvgFillStyle *fillStyle =
static_cast<
const QSvgFillStyle*>
3640 (node->styleProperty(QSvgStyleProperty::Fill));
3641 if (fillStyle && fillStyle->paintServer()
3642 && fillStyle->paintServer()->type() == QSvgPaintServer::Type::Pattern) {
3643 QSvgPatternPaint *patternStyle =
static_cast<QSvgPatternPaint *>(fillStyle->paintServer());
3644 if (linkable.contains(patternStyle->patternNode()))
3648 const QSvgStrokeStyle *strokeStyle =
static_cast<
const QSvgStrokeStyle*>
3649 (node->styleProperty(QSvgStyleProperty::Stroke));
3650 if (strokeStyle && strokeStyle->paintServer()
3651 && strokeStyle->paintServer()->type() == QSvgPaintServer::Type::Pattern) {
3652 QSvgPatternPaint *patternStyle =
static_cast<QSvgPatternPaint *>(strokeStyle->paintServer());
3653 if (linkable.contains(patternStyle->patternNode()))
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3682 QList<
const QSvgNode *> linkable;
3683 using NodeState =
std::pair<
const QSvgNode *,
bool>;
3684 QStack<NodeState> nodes;
3685 nodes.push({n,
false});
3688 auto current = nodes.pop();
3689 if (current.second) {
3690 Q_ASSERT(!linkable.isEmpty() && current.first == linkable.back());
3691 linkable.pop_back();
3695 switch (current.first->type()) {
3697 case QSvgNode::Group:
3698 case QSvgNode::Defs:
3699 case QSvgNode::Pattern:
3701 if (current.first->type() == QSvgNode::Pattern) {
3702 linkable.append(current.first);
3703 nodes.push({current.first,
true});
3705 auto *g =
static_cast<
const QSvgStructureNode*>(current.first);
3706 for (
auto it = g->renderers().crbegin(); it != g->renderers().crend(); it++)
3707 nodes.push({it->get(),
false});
3712 if (linkable.contains(current.first))
3714 auto *u =
static_cast<
const QSvgUse*>(current.first);
3715 auto *target = u->link();
3718 nodes.push({u,
true});
3719 nodes.push({target,
false});
3723 case QSvgNode::Rect:
3724 case QSvgNode::Ellipse:
3725 case QSvgNode::Circle:
3726 case QSvgNode::Line:
3727 case QSvgNode::Path:
3728 case QSvgNode::Polygon:
3729 case QSvgNode::Polyline:
3730 case QSvgNode::Tspan:
3731 if (detectPatternCycles(current.first, linkable))
3737 }
while (!nodes.isEmpty());
3744 qCWarning(lcSvgHandler,
"Cycles detected in SVG, document discarded.");
3752void QSvgHandler::parse()
3754 xml->setNamespaceProcessing(
false);
3755#ifndef QT_NO_CSSPARSER
3759 int remainingUnfinishedElements = unfinishedElementsLimit;
3760 while (!xml->atEnd() && !done) {
3761 switch (xml->readNext()) {
3762 case QXmlStreamReader::StartElement:
3771 if (remainingUnfinishedElements && startElement(xml->name(), xml->attributes())) {
3772 --remainingUnfinishedElements;
3778 case QXmlStreamReader::EndElement:
3779 done = endElement(xml->name());
3780 ++remainingUnfinishedElements;
3782 case QXmlStreamReader::Characters:
3783 characters(xml->text());
3785 case QXmlStreamReader::ProcessingInstruction:
3786 processingInstruction(xml->processingInstructionTarget(), xml->processingInstructionData());
3796 resolvePaintServers();
3798 if (detectCyclesAndWarn(m_doc.get()))
3802bool QSvgHandler::startElement(
const QStringView localName,
3803 const QXmlStreamAttributes &attributes)
3805 QSvgNode *node =
nullptr;
3810
3811
3812 const QStringView xmlSpace(attributes.value(QLatin1String(
"xml:space")));
3813 if (xmlSpace.isNull()) {
3815 m_whitespaceMode.push(m_whitespaceMode.isEmpty() ? QSvgText::Default : m_whitespaceMode.top());
3816 }
else if (xmlSpace == QLatin1String(
"preserve")) {
3817 m_whitespaceMode.push(QSvgText::Preserve);
3818 }
else if (xmlSpace == QLatin1String(
"default")) {
3819 m_whitespaceMode.push(QSvgText::Default);
3821 const QByteArray msg =
'"' + xmlSpace.toLocal8Bit()
3822 +
"\" is an invalid value for attribute xml:space. "
3823 "Valid values are \"preserve\" and \"default\".";
3824 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3825 m_whitespaceMode.push(QSvgText::Default);
3828 if (!m_doc && localName != QLatin1String(
"svg"))
3831 if (m_doc && localName == QLatin1String(
"svg")) {
3832 m_skipNodes.push(Doc);
3833 qCWarning(lcSvgHandler) <<
"Skipping a nested svg element, because "
3834 "SVG Document must not contain nested svg elements in Svg Tiny 1.2";
3837 if (!m_skipNodes.isEmpty() && m_skipNodes.top() == Doc)
3840 if (FactoryMethod method = findGroupFactory(localName, options())) {
3843 node = method(
nullptr, attributes,
this);
3845 Q_ASSERT(node->type() == QSvgNode::Doc);
3846 m_doc.reset(
static_cast<QSvgDocument*>(node));
3849 switch (m_nodes.top()->type()) {
3851 case QSvgNode::Group:
3852 case QSvgNode::Defs:
3853 case QSvgNode::Switch:
3854 case QSvgNode::Mask:
3855 case QSvgNode::Symbol:
3856 case QSvgNode::Marker:
3857 case QSvgNode::Pattern:
3859 node = method(m_nodes.top(), attributes,
this);
3861 QSvgStructureNode *group =
3862 static_cast<QSvgStructureNode*>(m_nodes.top());
3863 group->addChild(std::unique_ptr<QSvgNode>(node), someId(attributes));
3868 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
3869 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3875 parseCoreNode(node, attributes);
3876 parseStyle(node, attributes,
this);
3877 if (node->type() == QSvgNode::Filter)
3878 m_toBeResolved.append(node);
3880 }
else if (FactoryMethod method = findGraphicsFactory(localName, options())) {
3882 Q_ASSERT(!m_nodes.isEmpty());
3883 switch (m_nodes.top()->type()) {
3885 case QSvgNode::Group:
3886 case QSvgNode::Defs:
3887 case QSvgNode::Switch:
3888 case QSvgNode::Mask:
3889 case QSvgNode::Symbol:
3890 case QSvgNode::Marker:
3891 case QSvgNode::Pattern:
3893 if (localName == QLatin1String(
"tspan")) {
3894 const QByteArray msg = QByteArrayLiteral(
"\'tspan\' element in wrong context.");
3895 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3898 node = method(m_nodes.top(), attributes,
this);
3900 QSvgStructureNode *group =
3901 static_cast<QSvgStructureNode*>(m_nodes.top());
3902 group->addChild(std::unique_ptr<QSvgNode>(node), someId(attributes));
3906 case QSvgNode::Text:
3907 case QSvgNode::Textarea:
3908 if (localName == QLatin1String(
"tspan")) {
3909 node = method(m_nodes.top(), attributes,
this);
3911 static_cast<QSvgText *>(m_nodes.top())->addTspan(
static_cast<QSvgTspan *>(node));
3914 const QByteArray msg = QByteArrayLiteral(
"\'text\' or \'textArea\' element contains invalid element type.");
3915 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3919 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
3920 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3925 parseCoreNode(node, attributes);
3926 parseStyle(node, attributes,
this);
3927 if (node->type() == QSvgNode::Text || node->type() == QSvgNode::Textarea) {
3928 static_cast<QSvgText *>(node)->setWhitespaceMode(m_whitespaceMode.top());
3929 }
else if (node->type() == QSvgNode::Tspan) {
3930 static_cast<QSvgTspan *>(node)->setWhitespaceMode(m_whitespaceMode.top());
3931 }
else if (node->type() == QSvgNode::Use) {
3932 auto useNode =
static_cast<QSvgUse *>(node);
3933 if (!useNode->isResolved())
3934 m_toBeResolved.append(useNode);
3937 }
else if (FactoryMethod method = findFilterFactory(localName, options())) {
3939 Q_ASSERT(!m_nodes.isEmpty());
3940 if (m_nodes.top()->type() == QSvgNode::Filter ||
3941 (m_nodes.top()->type() == QSvgNode::FeMerge && localName == QLatin1String(
"feMergeNode"))) {
3942 node = method(m_nodes.top(), attributes,
this);
3944 QSvgStructureNode *container =
3945 static_cast<QSvgStructureNode*>(m_nodes.top());
3946 container->addChild(std::unique_ptr<QSvgNode>(node), someId(attributes));
3949 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
3950 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3952 }
else if (AnimationMethod method = findAnimationFactory(localName, options())) {
3953 Q_ASSERT(!m_nodes.isEmpty());
3954 node = method(m_nodes.top(), attributes,
this);
3956 QSvgAnimateNode *anim =
static_cast<QSvgAnimateNode *>(node);
3957 if (anim->linkId().isEmpty())
3958 m_doc->animator()->appendAnimation(m_nodes.top(), anim);
3959 else if (m_doc->namedNode(anim->linkId()))
3960 m_doc->animator()->appendAnimation(m_doc->namedNode(anim->linkId()), anim);
3962 m_toBeResolved.append(anim);
3964 }
else if (ParseMethod method = findUtilFactory(localName, options())) {
3965 Q_ASSERT(!m_nodes.isEmpty());
3966 if (!method(m_nodes.top(), attributes,
this))
3967 qCWarning(lcSvgHandler,
"%s", msgProblemParsing(localName, xml).constData());
3968 }
else if (FontFactoryMethod method = findFontFactoryMethod(localName)) {
3969 QSvgFontPtr font = method(attributes,
this);
3971 m_currentSvgFontData.font = std::move(font);
3973 const QByteArray msg = QByteArrayLiteral(
"Could not parse node: ") + localName.toLocal8Bit();
3974 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3976 }
else if (PaintServerFactoryMethod method = findPaintServerFactoryMethod(localName)) {
3977 QSvgPaintServerSharedPtr paintServer = method(attributes,
this);
3979 m_paintServer = paintServer;
3980 m_doc->addPaintServer(std::move(paintServer), someId(attributes));
3982 const QByteArray msg = QByteArrayLiteral(
"Could not parse node: ") + localName.toLocal8Bit();
3983 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3985 }
else if (FontParseMethod method = findFontParseFactoryMethod(localName)) {
3986 if (m_currentSvgFontData.font) {
3987 if (!method(m_currentSvgFontData.font.get(), attributes,
this))
3988 qCWarning(lcSvgHandler,
"%s", msgProblemParsing(localName, xml).constData());
3990 }
else if (PaintServerParseMethod method = findPaintServerUtilFactoryMethod(localName)) {
3991 if (m_paintServer) {
3992 if (!method(m_paintServer.get(), attributes,
this))
3993 qCWarning(lcSvgHandler,
"%s", msgProblemParsing(localName, xml).constData());
3996 qCDebug(lcSvgHandler) <<
"Skipping unknown element" << localName;
3997 m_skipNodes.push(Unknown);
4003 m_skipNodes.push(Graphics);
4006 m_skipNodes.push(Style);
4011bool QSvgHandler::endElement(
const QStringView localName)
4013 CurrentNode node = m_skipNodes.top();
4015 if (node == Doc && localName != QLatin1String(
"svg"))
4019 m_whitespaceMode.pop();
4023 if (node == Unknown)
4026#ifdef QT_NO_CSSPARSER
4027 Q_UNUSED(localName);
4029 if (m_inStyle && localName == QLatin1String(
"style"))
4033 if (node == Graphics)
4036 if (localName ==
"font"_L1) {
4037 if (!m_currentSvgFontData.isEmpty()) {
4038 document()->addSvgFont(m_currentSvgFontData.fontFamily,
4039 std::move(m_currentSvgFontData.font));
4041 m_currentSvgFontData.reset();
4044 return ((localName == QLatin1String(
"svg")) && (node != Doc));
4047void QSvgHandler::resolvePaintServers()
4049 for (QSvgStyleProperty *prop : std::as_const(m_unresolvedStyles)) {
4050 if (prop->type() == QSvgStyleProperty::Fill) {
4051 QSvgFillStyle *fill =
static_cast<QSvgFillStyle *>(prop);
4052 QString id = fill->paintStyleId();
4053 QSvgPaintServerSharedPtr paintServer = m_doc->paintServer(id);
4055 fill->setPaintServer(std::move(paintServer));
4057 qCWarning(lcSvgHandler,
"%s", msgCouldNotResolveProperty(id, xml).constData());
4058 fill->setBrush(Qt::NoBrush);
4060 }
else if (prop->type() == QSvgStyleProperty::Stroke) {
4061 QSvgStrokeStyle *stroke =
static_cast<QSvgStrokeStyle *>(prop);
4062 QString id = stroke->paintStyleId();
4063 QSvgPaintServerSharedPtr paintServer = m_doc->paintServer(id);
4065 stroke->setPaintServer(std::move(paintServer));
4067 qCWarning(lcSvgHandler,
"%s", msgCouldNotResolveProperty(id, xml).constData());
4068 stroke->setStroke(Qt::NoBrush);
4073 m_unresolvedStyles.clear();
4076void QSvgHandler::resolveNodes()
4078 for (QSvgNode *node : std::as_const(m_toBeResolved)) {
4079 if (node->type() == QSvgNode::Use) {
4080 QSvgUse *useNode =
static_cast<QSvgUse *>(node);
4081 const auto parent = useNode->parent();
4085 QSvgNode::Type t = parent->type();
4086 if (t != QSvgNode::Doc && t != QSvgNode::Defs && t != QSvgNode::Group && t != QSvgNode::Switch)
4089 QSvgNode *link = m_doc->namedNode(useNode->linkId());
4091 qCWarning(lcSvgHandler,
"link #%s is undefined!",
qPrintable(useNode->linkId()));
4095 if (useNode->parent()->isDescendantOf(link))
4096 qCWarning(lcSvgHandler,
"link #%s is recursive!",
qPrintable(useNode->linkId()));
4098 useNode->setLink(link);
4099 }
else if (node->type() == QSvgNode::Filter) {
4100 QSvgFilterContainer *filter =
static_cast<QSvgFilterContainer *>(node);
4101 for (
auto &renderer : filter->renderers()) {
4102 const QSvgFeFilterPrimitive *primitive = QSvgFeFilterPrimitive::castToFilterPrimitive(renderer.get());
4103 if (!primitive || primitive->type() == QSvgNode::FeUnsupported) {
4104 filter->setSupported(
false);
4108 }
else if (node->type() == QSvgNode::AnimateTransform || node->type() == QSvgNode::AnimateColor) {
4109 QSvgAnimateNode *anim =
static_cast<QSvgAnimateNode *>(node);
4110 QSvgNode *targetNode = m_doc->namedNode(anim->linkId());
4112 m_doc->animator()->appendAnimation(targetNode, anim);
4114 qCWarning(lcSvgHandler,
"Cannot find target for link #%s!",
4120 m_toBeResolved.clear();
4123bool QSvgHandler::characters(
const QStringView str)
4125#ifndef QT_NO_CSSPARSER
4127 m_cssHandler.parseStyleSheet(str);
4131 if (m_skipNodes.isEmpty() || m_skipNodes.top() == Unknown || m_nodes.isEmpty())
4134 if (m_nodes.top()->type() == QSvgNode::Text || m_nodes.top()->type() == QSvgNode::Textarea) {
4135 static_cast<QSvgText*>(m_nodes.top())->addText(str);
4136 }
else if (m_nodes.top()->type() == QSvgNode::Tspan) {
4137 static_cast<QSvgTspan*>(m_nodes.top())->addText(str);
4143QIODevice *QSvgHandler::device()
const
4145 return xml->device();
4148QSvgDocument *QSvgHandler::document()
const
4153std::unique_ptr<QSvgDocument> QSvgHandler::takeDocument()
4155 return std::move(m_doc);
4158QGuiSvg::LengthType QSvgHandler::defaultCoordinateSystem()
const
4160 return m_defaultCoords;
4163void QSvgHandler::setDefaultCoordinateSystem(QGuiSvg::LengthType type)
4165 m_defaultCoords = type;
4168void QSvgHandler::pushColor(
const QColor &color)
4170 m_colorStack.push(color);
4171 m_colorTagCount.push(1);
4174void QSvgHandler::pushColorCopy()
4176 if (m_colorTagCount.size())
4177 ++m_colorTagCount.top();
4179 pushColor(Qt::black);
4182void QSvgHandler::popColor()
4184 if (m_colorTagCount.size()) {
4185 if (!--m_colorTagCount.top()) {
4187 m_colorTagCount.pop();
4192QColor QSvgHandler::currentColor()
const
4194 if (!m_colorStack.isEmpty())
4195 return m_colorStack.top();
4197 return QColor(0, 0, 0);
4200void QSvgHandler::pushUnresolvedStyle(QSvgStyleProperty *prop)
4202 m_unresolvedStyles.append(prop);
4205void QSvgHandler::setCurrentSvgFontFamily(QStringView family)
4207 m_currentSvgFontData.fontFamily = family.toString();
4210#ifndef QT_NO_CSSPARSER
4212void QSvgHandler::setInStyle(
bool b)
4217bool QSvgHandler::inStyle()
const
4222QSvgCssHandler &QSvgHandler::cssHandler()
4224 return m_cssHandler;
4229bool QSvgHandler::processingInstruction(
const QStringView target,
const QStringView data)
4231#ifdef QT_NO_CSSPARSER
4235 if (target == QLatin1String(
"xml-stylesheet")) {
4236 static const QRegularExpression rx(
QStringLiteral(
"type=\\\"(.+)\\\""),
4237 QRegularExpression::InvertedGreedinessOption);
4238 QRegularExpressionMatchIterator iter = rx.globalMatchView(data);
4240 while (iter.hasNext()) {
4241 QRegularExpressionMatch match = iter.next();
4242 QString type = match.captured(1);
4243 if (type.toLower() == QLatin1String(
"text/css")) {
4249 static const QRegularExpression rx(
QStringLiteral(
"href=\\\"(.+)\\\""),
4250 QRegularExpression::InvertedGreedinessOption);
4251 QRegularExpressionMatch match = rx.matchView(data);
4252 QString addr = match.captured(1);
4256 QFile file(fi.absoluteFilePath());
4257 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
4260 QByteArray cssData = file.readAll();
4261 QString css = QString::fromUtf8(cssData);
4262 m_cssHandler.parseStyleSheet(css);
4272void QSvgHandler::setAnimPeriod(
int start,
int end)
4275 m_animEnd = qMax(end, m_animEnd);
4278int QSvgHandler::animationDuration()
const
4283QSvgHandler::~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 QSvgNode * createTspanNode(QSvgNode *parent, const QXmlStreamAttributes &, QSvgHandler *)
static bool parseStyle(QSvgNode *node, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static void parseVisibility(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static void parseOffsetPath(QSvgNode *node, const QXmlStreamAttributes &attributes)
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)
QSvgPaintServerSharedPtr(* PaintServerFactoryMethod)(const QXmlStreamAttributes &, QSvgHandler *)
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)
static void parseOthers(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
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 qreal convertToNumber(QStringView str, bool *ok=NULL)
static std::optional< int > parseFontWeight(QStringView s)
QSvgFontPtr(* FontFactoryMethod)(const QXmlStreamAttributes &, QSvgHandler *)
static void parseFilterAttributes(const QXmlStreamAttributes &attributes, QString *inString, QString *outString, QSvgRectF *rect)
static QSvgNode * createPolyNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, bool createLine)
bool(* FontParseMethod)(QSvgFont *, const QXmlStreamAttributes &, QSvgHandler *)
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)
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 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 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)
bool(* PaintServerParseMethod)(QSvgPaintServer *, const QXmlStreamAttributes &, QSvgHandler *)
static bool parseMetadataNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QTransform parseTransformationMatrix(QStringView value)
static QSvgNode * createSwitchNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseDiscardNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool createSvgGlyph(QSvgFont *font, const QXmlStreamAttributes &attributes, bool isMissingGlyph)
static void parseOpacity(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static bool parseScriptNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseGlyphNode(QSvgFont *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 QSvgNode * createPatternNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static bool parseHandlerNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static AnimationMethod findAnimationFactory(const QStringView name, QtSvg::Options options)
static 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< Qt::Alignment > parseTextAnchor(QStringView s)
static void parseFont(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
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 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)
bool(* ParseMethod)(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *)
static QStringView idFromIRI(QStringView iri)
static bool parsePrefetchNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseCssAnimations(QSvgNode *node, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static int qsvg_hex2int(const char *s, bool *ok=nullptr)
static 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)
static bool parseAnchorNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QStringList stringToList(const QString &str)
static std::optional< qreal > parseFontSize(QStringView s)
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 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 bool parseMissingGlyphNode(QSvgFont *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
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