5#include "qplatformdefs.h"
23#include <QtCore/private/qdataurl_p.h>
31#include <qregularexpression.h>
43using namespace Qt::StringLiterals;
50constexpr auto inherit =
"inherit"_L1;
51constexpr auto normal =
"normal"_L1;
53constexpr auto italic =
"italic"_L1;
54constexpr auto oblique =
"oblique"_L1;
56constexpr auto bold =
"bold"_L1;
57constexpr auto bolder =
"bolder"_L1;
58constexpr auto lighter =
"lighter"_L1;
60constexpr auto small_caps =
"small-caps"_L1;
62constexpr auto start =
"start"_L1;
63constexpr auto middle =
"middle"_L1;
64constexpr auto end =
"end"_L1;
67constexpr auto clear =
"clear"_L1;
68constexpr auto src =
"src"_L1;
69constexpr auto dst =
"dst"_L1;
70constexpr auto srcOver =
"src-over"_L1;
71constexpr auto dstOver =
"dst-over"_L1;
72constexpr auto srcIn =
"src-in"_L1;
73constexpr auto dstIn =
"dst-in"_L1;
74constexpr auto srcOut =
"src-out"_L1;
75constexpr auto dstOut =
"dst-out"_L1;
76constexpr auto srcAtop =
"src-atop"_L1;
77constexpr auto dstAtop =
"dst-atop"_L1;
78constexpr auto xorOp =
"xor"_L1;
79constexpr auto plus =
"plus"_L1;
80constexpr auto multiply =
"multiply"_L1;
81constexpr auto screen =
"screen"_L1;
82constexpr auto overlay =
"overlay"_L1;
83constexpr auto darken =
"darken"_L1;
84constexpr auto lighten =
"lighten"_L1;
85constexpr auto colorDodge =
"color-dodge"_L1;
86constexpr auto colorBurn =
"color-burn"_L1;
87constexpr auto hardLight =
"hard-light"_L1;
88constexpr auto softLight =
"soft-light"_L1;
89constexpr auto difference =
"difference"_L1;
90constexpr auto exclusion =
"exclusion"_L1;
99 if (
const QFile *file = qobject_cast<
const QFile *>(r->device()))
100 result.append(QFile::encodeName(QDir::toNativeSeparators(file->fileName())));
102 result.append(QByteArrayLiteral(
"<input>"));
104 result.append(QByteArray::number(r->lineNumber()));
105 if (
const qint64 column = r->columnNumber()) {
107 result.append(QByteArray::number(column));
109 result.append(QByteArrayLiteral(
": "));
117 return prefixMessage(
"Problem parsing " + localName.toLocal8Bit(), r);
122 return prefixMessage(
"Could not resolve property: " + id.toLocal8Bit(), r);
127 static const QRegularExpression delimiterRE(
QStringLiteral(
"[,\\s]+"));
128 return delimitedList.split(delimiterRE, Qt::SkipEmptyParts);
133static inline int qsvg_h2i(
char hex,
bool *ok =
nullptr)
135 if (hex >=
'0' && hex <=
'9')
137 if (hex >=
'a' && hex <=
'f')
138 return hex -
'a' + 10;
139 if (hex >=
'A' && hex <=
'F')
140 return hex -
'A' + 10;
162 const size_t len = qstrlen(name);
169 }
else if (len == 9) {
173 }
else if (len == 6) {
177 }
else if (len == 3) {
184 if ((uint)r > 255 || (uint)g > 255 || (uint)b > 255 || !ok) {
188 *rgb = qRgb(r, g ,b);
197 for(
int i = 0; i < len; ++i)
198 tmp[i] = str[i].toLatin1();
200 return qsvg_get_hex_rgb(tmp, rgb);
205static inline QString
someId(
const QXmlStreamAttributes &attributes)
207 QStringView id = attributes.value(QLatin1String(
"id"));
209 id = attributes.value(QLatin1String(
"xml:id"));
210 return id.toString();
216 void setAttributes(
const QXmlStreamAttributes &attributes, QSvgHandler *handler);
258 setAttributes(xmlAttributes, handler);
263 for (
const QXmlStreamAttribute &attribute : attributes) {
264 QStringView name = attribute.qualifiedName();
267 QStringView value = attribute.value();
269 switch (name.at(0).unicode()) {
272 if (name == QLatin1String(
"color"))
274 else if (name == QLatin1String(
"color-opacity"))
275 colorOpacity = value;
276 else if (name == QLatin1String(
"comp-op"))
281 if (name == QLatin1String(
"display"))
286 if (name == QLatin1String(
"fill"))
288 else if (name == QLatin1String(
"fill-rule"))
290 else if (name == QLatin1String(
"fill-opacity"))
292 else if (name == QLatin1String(
"font-family"))
294 else if (name == QLatin1String(
"font-size"))
296 else if (name == QLatin1String(
"font-style"))
298 else if (name == QLatin1String(
"font-weight"))
300 else if (name == QLatin1String(
"font-variant"))
302 else if (name == QLatin1String(
"filter") &&
303 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
308 if (name == QLatin1String(
"id"))
309 id = value.toString();
310 else if (name == QLatin1String(
"image-rendering"))
311 imageRendering = value;
315 if (name == QLatin1String(
"mask") &&
316 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
318 if (name == QLatin1String(
"marker-start") &&
319 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
321 if (name == QLatin1String(
"marker-mid") &&
322 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
324 if (name == QLatin1String(
"marker-end") &&
325 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
330 if (name == QLatin1String(
"opacity"))
332 if (name == QLatin1String(
"offset"))
337 if (name.size() > 5 && name.mid(1, 5) == QLatin1String(
"troke")) {
338 QStringView strokeRef = name.mid(6, name.size() - 6);
339 if (strokeRef.isEmpty())
341 else if (strokeRef == QLatin1String(
"-dasharray"))
342 strokeDashArray = value;
343 else if (strokeRef == QLatin1String(
"-dashoffset"))
344 strokeDashOffset = value;
345 else if (strokeRef == QLatin1String(
"-linecap"))
346 strokeLineCap = value;
347 else if (strokeRef == QLatin1String(
"-linejoin"))
348 strokeLineJoin = value;
349 else if (strokeRef == QLatin1String(
"-miterlimit"))
350 strokeMiterLimit = value;
351 else if (strokeRef == QLatin1String(
"-opacity"))
352 strokeOpacity = value;
353 else if (strokeRef == QLatin1String(
"-width"))
355 }
else if (name == QLatin1String(
"stop-color"))
357 else if (name == QLatin1String(
"stop-opacity"))
362 if (name == QLatin1String(
"text-anchor"))
364 else if (name == QLatin1String(
"transform"))
369 if (name == QLatin1String(
"vector-effect"))
370 vectorEffect = value;
371 else if (name == QLatin1String(
"visibility"))
376 if (name == QLatin1String(
"xml:id") && id.isEmpty())
377 id = value.toString();
393 while (!str->isEmpty() && str->first().isSpace())
395 while (!str->isEmpty()
397 || str->startsWith(QLatin1Char(
'+')) || str->startsWith(QLatin1Char(
'.')))) {
399 points.append(QSvgUtils::toDouble(str));
401 while (!str->isEmpty() && str->first().isSpace())
403 if (str->startsWith(QLatin1Char(
',')))
407 while (!str->isEmpty() && str->first().isSpace())
418 while (!str.isEmpty() && str.first().isSpace())
420 while ((!str.isEmpty() && str.first() >= QLatin1Char(
'0') && str.first() <= QLatin1Char(
'9'))
421 || str.startsWith(QLatin1Char(
'-')) || str.startsWith(QLatin1Char(
'+'))
422 || str.startsWith(QLatin1Char(
'.'))) {
424 points.append(QSvgUtils::toDouble(&str));
426 while (!str.isEmpty() && str.first().isSpace())
428 if (str.startsWith(QLatin1Char(
'%')))
430 while (!str.isEmpty() && str.first().isSpace())
432 if (str.startsWith(QLatin1Char(
',')))
436 while (!str.isEmpty() && str.first().isSpace())
444
445
446
447
452 if (!iri.startsWith(QLatin1Char(
'#')))
453 return QStringView();
455 return iri.sliced(1);
459
460
461
462
463
468 if (!iri.startsWith(QLatin1StringView(
"url(")))
469 return QStringView();
473 const qsizetype closingBracePos = iri.indexOf(QLatin1Char(
')'));
474 if (closingBracePos == -1)
475 return QStringView();
477 iri = iri.first(closingBracePos);
478 return idFromIRI(iri);
482
483
484
485bool resolveColor(QStringView colorStr, QColor &color, QSvgHandler *handler)
487 QStringView colorStrTr = colorStr.trimmed();
488 if (colorStrTr.isEmpty())
491 switch(colorStrTr.at(0).unicode()) {
498 bool ok = qsvg_get_hex_rgb(colorStrTr.constData(), colorStrTr.size(), &rgb);
508 if (colorStrTr.size() >= 7 && colorStrTr.at(colorStrTr.size() - 1) == QLatin1Char(
')')
509 && colorStrTr.mid(0, 4) == QLatin1String(
"rgb(")) {
510 QStringView sv{ colorStrTr.sliced(4) };
511 QList<qreal> compo = parseNumbersList(&sv);
514 if (compo.size() == 1) {
515 compo = parsePercentageList(colorStrTr.sliced(4));
516 for (
int i = 0; i < compo.size(); ++i)
517 compo[i] *= (qreal)2.55;
520 if (compo.size() == 3) {
521 color = QColor(
int(compo[0]),
532 if (colorStrTr == QLatin1String(
"currentColor")) {
533 color = handler->currentColor();
538 if (colorStrTr == tokens::inherit)
545 color = QColor::fromString(colorStrTr);
546 return color.isValid();
552 qreal op = qBound(qreal(0.0), QSvgUtils::toDouble(opacity, &ok), qreal(1.0));
555 color->setAlphaF(op);
559 QColor &color, QSvgHandler *handler)
561 if (!resolveColor(colorStr, color, handler))
563 if (!opacity.isEmpty())
564 setAlpha(opacity, &color);
571 qreal num = QSvgUtils::parseLength(str.toString(), &type, ok);
578static bool createSvgGlyph(QSvgFont *font,
const QXmlStreamAttributes &attributes,
581 QStringView uncStr = attributes.value(QLatin1String(
"unicode"));
582 QStringView havStr = attributes.value(QLatin1String(
"horiz-adv-x"));
583 QStringView pathStr = attributes.value(QLatin1String(
"d"));
585 qreal havx = (havStr.isEmpty()) ? -1 : QSvgUtils::toDouble(havStr);
586 QPainterPath path = QSvgUtils::parsePathDataFast(pathStr).value_or(QPainterPath());
588 path.setFillRule(Qt::WindingFill);
590 if (isMissingGlyph) {
591 if (!uncStr.isEmpty())
592 qWarning(
"Ignoring missing-glyph's 'unicode' attribute");
593 return font->addMissingGlyph(path, havx);
596 if (uncStr.isEmpty()) {
597 qWarning(
"glyph does not define a non-empty 'unicode' attribute and will be ignored");
600 font->addGlyph(uncStr.toString(), path, havx);
606 QSvgHandler *handler)
609 if (constructColor(attributes.color, attributes.colorOpacity, color, handler)) {
611 handler->pushColor(color);
617 QStringView id = idFromFuncIRI(url);
618 return doc ? doc->paintServer(id) :
nullptr;
623 QSvgHandler *handler)
625 if (!attributes.fill.isEmpty() || !attributes.fillRule.isEmpty() || !attributes.fillOpacity.isEmpty()) {
629 if (!attributes.fillRule.isEmpty() && attributes.fillRule != tokens::inherit) {
630 if (attributes.fillRule == QLatin1String(
"evenodd"))
631 prop->setFillRule(Qt::OddEvenFill);
632 else if (attributes.fillRule == QLatin1String(
"nonzero"))
633 prop->setFillRule(Qt::WindingFill);
637 if (!attributes.fillOpacity.isEmpty() && attributes.fillOpacity != tokens::inherit) {
638 prop->setFillOpacity(qMin(qreal(1.0), qMax(qreal(0.0), QSvgUtils::toDouble(attributes.fillOpacity))));
642 if (!attributes.fill.isEmpty() && attributes.fill != tokens::inherit) {
643 if (attributes.fill.startsWith(QLatin1String(
"url"))) {
644 QStringView value = attributes.fill;
645 QSvgPaintServerSharedPtr paintServer = paintServerFromUrl(handler->document(), value);
647 prop->setPaintServer(
std::move(paintServer));
649 QString id = idFromFuncIRI(value).toString();
650 prop->setPaintStyleId(id);
651 handler->pushUnresolvedStyle(prop.get());
653 }
else if (attributes.fill != QLatin1String(
"none")) {
655 if (resolveColor(attributes.fill, color, handler))
656 prop->setBrush(QBrush(color));
658 prop->setBrush(QBrush(Qt::NoBrush));
661 node->appendStyleProperty(
std::move(prop));
674 while (!value.isEmpty()) {
675 if (value.first().isSpace() || value.startsWith(QLatin1Char(
','))) {
687 State state = Matrix;
688 if (value.startsWith(QLatin1Char(
'm'))) {
689 const char *ident =
"atrix";
690 for (
int i = 0; i < 5; ++i)
691 if (!value.slice(1).startsWith(QLatin1Char(ident[i])))
695 }
else if (value.startsWith(QLatin1Char(
't'))) {
696 const char *ident =
"ranslate";
697 for (
int i = 0; i < 8; ++i)
698 if (!value.slice(1).startsWith(QLatin1Char(ident[i])))
702 }
else if (value.startsWith(QLatin1Char(
'r'))) {
703 const char *ident =
"otate";
704 for (
int i = 0; i < 5; ++i)
705 if (!value.slice(1).startsWith(QLatin1Char(ident[i])))
709 }
else if (value.startsWith(QLatin1Char(
's'))) {
711 if (value.startsWith(QLatin1Char(
'c'))) {
712 const char *ident =
"ale";
713 for (
int i = 0; i < 3; ++i)
714 if (!value.slice(1).startsWith(QLatin1Char(ident[i])))
718 }
else if (value.startsWith(QLatin1Char(
'k'))) {
719 if (!value.slice(1).startsWith(QLatin1Char(
'e')))
721 if (!value.slice(1).startsWith(QLatin1Char(
'w')))
724 if (value.startsWith(QLatin1Char(
'X')))
726 else if (value.startsWith(QLatin1Char(
'Y')))
738 while (!value.isEmpty() && value.first().isSpace())
740 if (!value.startsWith(QLatin1Char(
'(')))
743 QVarLengthArray<qreal, 8> points;
745 if (!value.startsWith(QLatin1Char(
')')))
749 if(state == Matrix) {
750 if(points.size() != 6)
752 matrix = QTransform(points[0], points[1],
753 points[2], points[3],
754 points[4], points[5]) * matrix;
755 }
else if (state == Translate) {
756 if (points.size() == 1)
757 matrix.translate(points[0], 0);
758 else if (points.size() == 2)
759 matrix.translate(points[0], points[1]);
762 }
else if (state == Rotate) {
763 if(points.size() == 1) {
764 matrix.rotate(points[0]);
765 }
else if (points.size() == 3) {
766 matrix.translate(points[1], points[2]);
767 matrix.rotate(points[0]);
768 matrix.translate(-points[1], -points[2]);
772 }
else if (state == Scale) {
773 if (points.size() < 1 || points.size() > 2)
775 qreal sx = points[0];
777 if(points.size() == 2)
779 matrix.scale(sx, sy);
780 }
else if (state == SkewX) {
781 if (points.size() != 1)
783 matrix.shear(qTan(qDegreesToRadians(points[0])), 0);
784 }
else if (state == SkewY) {
785 if (points.size() != 1)
787 matrix.shear(0, qTan(qDegreesToRadians(points[0])));
796 QSvgHandler *handler)
798 if (!attributes.stroke.isEmpty() || !attributes.strokeDashArray.isEmpty() || !attributes.strokeDashOffset.isEmpty() || !attributes.strokeLineCap.isEmpty()
799 || !attributes.strokeLineJoin.isEmpty() || !attributes.strokeMiterLimit.isEmpty() || !attributes.strokeOpacity.isEmpty() || !attributes.strokeWidth.isEmpty()
800 || !attributes.vectorEffect.isEmpty()) {
805 if (!attributes.stroke.isEmpty() && attributes.stroke != tokens::inherit) {
806 if (attributes.stroke.startsWith(QLatin1String(
"url"))) {
807 QStringView value = attributes.stroke;
808 QSvgPaintServerSharedPtr paintServer = paintServerFromUrl(handler->document(), value);
810 prop->setPaintServer(
std::move(paintServer));
812 QString id = idFromFuncIRI(value).toString();
813 prop->setPaintStyleId(id);
814 handler->pushUnresolvedStyle(prop.get());
816 }
else if (attributes.stroke != QLatin1String(
"none")) {
818 if (resolveColor(attributes.stroke, color, handler))
819 prop->setStroke(QBrush(color));
821 prop->setStroke(QBrush(Qt::NoBrush));
826 if (!attributes.strokeWidth.isEmpty() && attributes.strokeWidth != tokens::inherit) {
828 prop->setWidth(QSvgUtils::parseLength(attributes.strokeWidth, <));
832 if (!attributes.strokeDashArray.isEmpty() && attributes.strokeDashArray != tokens::inherit) {
833 if (attributes.strokeDashArray == QLatin1String(
"none")) {
834 prop->setDashArrayNone();
836 QStringView dashArray = attributes.strokeDashArray;
837 QList<qreal> dashes = parseNumbersList(&dashArray);
838 const bool allZeroes =
std::all_of(dashes.cbegin(), dashes.cend(),
839 [](qreal i) {
return qFuzzyIsNull(i); });
840 const bool hasNegative = !allZeroes &&
std::any_of(dashes.cbegin(), dashes.cend(),
841 [](qreal i) {
return i < 0.; });
844 qCWarning(lcSvgHandler) <<
"QSvgHandler: Stroke dash array "
845 "with a negative value is invalid";
848 if (allZeroes || hasNegative) {
849 prop->setDashArrayNone();
852 if ((dashes.size() & 1) != 0)
853 dashes << QList<qreal>(dashes);
854 prop->setDashArray(dashes);
860 if (!attributes.strokeLineJoin.isEmpty()) {
861 if (attributes.strokeLineJoin == QLatin1String(
"miter"))
862 prop->setLineJoin(Qt::SvgMiterJoin);
863 else if (attributes.strokeLineJoin == QLatin1String(
"round"))
864 prop->setLineJoin(Qt::RoundJoin);
865 else if (attributes.strokeLineJoin == QLatin1String(
"bevel"))
866 prop->setLineJoin(Qt::BevelJoin);
870 if (!attributes.strokeLineCap.isEmpty()) {
871 if (attributes.strokeLineCap == QLatin1String(
"butt"))
872 prop->setLineCap(Qt::FlatCap);
873 else if (attributes.strokeLineCap == QLatin1String(
"round"))
874 prop->setLineCap(Qt::RoundCap);
875 else if (attributes.strokeLineCap == QLatin1String(
"square"))
876 prop->setLineCap(Qt::SquareCap);
880 if (!attributes.strokeDashOffset.isEmpty() && attributes.strokeDashOffset != tokens::inherit)
881 prop->setDashOffset(QSvgUtils::toDouble(attributes.strokeDashOffset));
884 if (!attributes.vectorEffect.isEmpty()) {
885 if (attributes.vectorEffect == QLatin1String(
"non-scaling-stroke"))
886 prop->setVectorEffect(
true);
887 else if (attributes.vectorEffect == QLatin1String(
"none"))
888 prop->setVectorEffect(
false);
892 if (!attributes.strokeMiterLimit.isEmpty() && attributes.strokeMiterLimit != tokens::inherit)
893 prop->setMiterLimit(QSvgUtils::toDouble(attributes.strokeMiterLimit));
896 if (!attributes.strokeOpacity.isEmpty() && attributes.strokeOpacity != tokens::inherit)
897 prop->setOpacity(qMin(qreal(1.0), qMax(qreal(0.0), QSvgUtils::toDouble(attributes.strokeOpacity))));
899 node->appendStyleProperty(
std::move(prop));
907{ qreal(6.9), qreal(8.3), qreal(10.0), qreal(12.0), qreal(14.4), qreal(17.3), qreal(20.7) };
913 switch (spec.at(0).unicode()) {
915 if (spec == QLatin1String(
"xx-small"))
917 if (spec == QLatin1String(
"x-small"))
919 if (spec == QLatin1String(
"x-large"))
921 if (spec == QLatin1String(
"xx-large"))
925 if (spec == QLatin1String(
"small"))
929 if (spec == QLatin1String(
"medium"))
933 if (spec == QLatin1String(
"large"))
937 if (spec == QLatin1String(
"none"))
951 if (s == tokens::normal)
952 return QFont::StyleNormal;
953 if (s == tokens::italic)
954 return QFont::StyleItalic;
955 if (s == tokens::oblique)
956 return QFont::StyleOblique;
970 if (s.isEmpty() || s == tokens::inherit)
980 qreal fs = QSvgUtils::parseLength(s, &type, &ok);
983 fs = QSvgUtils::convertToPixels(fs,
true, type);
984 return (
std::min)(fs, qreal(0xffff));
987 return sizeTable[spec];
990 Q_UNREACHABLE_RETURN(
std::nullopt);
998 if (s.isEmpty() || s == tokens::inherit)
1001 if (s == tokens::normal)
1002 return QFont::Normal;
1003 if (s == tokens::bold)
1005 if (s == tokens::bolder)
1006 return QSvgFontStyle::BOLDER;
1007 if (s == tokens::lighter)
1008 return QSvgFontStyle::LIGHTER;
1011 const int num = s.toInt(&ok);
1015 return std::nullopt;
1042 auto s = attributes.fontVariant;
1044 if (s == tokens::normal)
1045 return QFont::MixedCase;
1046 if (s == tokens::small_caps)
1047 return QFont::SmallCaps;
1049 return std::nullopt;
1060 if (s == tokens::start)
1061 return Qt::AlignLeft;
1062 if (s == tokens::middle)
1063 return Qt::AlignHCenter;
1064 if (s == tokens::end)
1065 return Qt::AlignRight;
1067 return std::nullopt;
1072 QSvgHandler *handler)
1074 auto parsedFontSize = parseFontSize(attributes.fontSize);
1075 auto parsedFontStyle = parseFontStyle(attributes.fontStyle);
1076 auto parsedFontWeight = parseFontWeight(attributes.fontWeight);
1077 auto parsedFontVariant = parseFontVariant(attributes);
1078 auto parsedTextAnchor = parseTextAnchor(attributes.textAnchor);
1080 if (attributes.fontFamily.isEmpty() && !parsedFontSize && !parsedFontStyle &&
1081 !parsedFontWeight && !parsedFontVariant && !parsedTextAnchor)
1084 QSvgFontStylePtr fontStyle;
1085 if (!attributes.fontFamily.isEmpty()) {
1086 QSvgDocument *doc = handler->document();
1088 QSvgFont *svgFont = doc->svgFont(attributes.fontFamily.toString());
1095 if (!attributes.fontFamily.isEmpty() && attributes.fontFamily != tokens::inherit) {
1096 QStringView family = attributes.fontFamily.trimmed();
1097 if (!family.isEmpty() && (family.at(0) == QLatin1Char(
'\'') || family.at(0) == QLatin1Char(
'\"')))
1098 family = family.mid(1, family.size() - 2);
1099 fontStyle->setFamily(family.toString());
1103 fontStyle->setSize(*parsedFontSize);
1105 if (parsedFontStyle)
1106 fontStyle->setStyle(*parsedFontStyle);
1108 if (parsedFontWeight)
1109 fontStyle->setWeight(*parsedFontWeight);
1111 if (parsedFontVariant)
1112 fontStyle->setVariant(*parsedFontVariant);
1114 if (parsedTextAnchor)
1115 fontStyle->setTextAnchor(*parsedTextAnchor);
1117 node->appendStyleProperty(
std::move(fontStyle));
1124 if (attributes.transform.isEmpty())
1126 QTransform matrix = parseTransformationMatrix(attributes.transform.trimmed());
1128 if (!matrix.isIdentity()) {
1138 QSvgNode *parent = node->parent();
1140 if (parent && (attributes.visibility.isEmpty() || attributes.visibility == tokens::inherit))
1141 node->setVisible(parent->isVisible());
1142 else if (attributes.visibility == QLatin1String(
"hidden") || attributes.visibility == QLatin1String(
"collapse")) {
1143 node->setVisible(
false);
1145 node->setVisible(
true);
1149 const QXmlStreamAttributes &attributes,
1150 QSvgHandler *handler);
1156 str = str.trimmed();
1157 if (str.endsWith(QLatin1String(
"ms"))) {
1160 }
else if (str.endsWith(QLatin1String(
"s"))) {
1163 double val = ms * QSvgUtils::toDouble(str, ok);
1165 if (val >
std::numeric_limits<
int>::min() && val <
std::numeric_limits<
int>::max())
1166 res =
static_cast<
int>(val);
1173#ifndef QT_NO_CSSPARSER
1176 const QXmlStreamAttributes &attributes,
1177 QSvgHandler *handler)
1179 QSvgCssProperties cssAnimProps(attributes);
1180 QList<QSvgAnimationProperty> parsedProperties = cssAnimProps.animations();
1182 for (
auto &property : parsedProperties) {
1183 QSvgCssAnimation *anim = handler->cssHandler().createAnimation(property.name);
1187 anim->setRunningTime(property.delay, property.duration);
1188 anim->setIterationCount(property.iteration);
1189 QSvgCssEasingPtr easing = handler->cssHandler().createEasing(property.easingFunction, property.easingValues);
1190 anim->setEasing(std::move(easing));
1192 handler->setAnimPeriod(property.delay, property.delay + property.duration);
1193 handler->document()->animator()->appendAnimation(node, anim);
1194 handler->document()->setAnimated(
true);
1199 const QXmlStreamAttributes &attributes)
1201 QSvgCssProperties cssProperties(attributes);
1202 QSvgOffsetProperty offset = cssProperties.offset();
1208 offsetStyle->setPath(offset.path.value());
1209 offsetStyle->setRotateAngle(offset.angle);
1210 offsetStyle->setRotateType(offset.rotateType);
1211 offsetStyle->setDistance(offset.distance);
1212 node->appendStyleProperty(
std::move(offsetStyle));
1217QtSvg::Options QSvgHandler::options()
const
1222QtSvg::AnimatorType QSvgHandler::animatorType()
const
1224 return m_animatorType;
1227bool QSvgHandler::trustedSourceMode()
const
1229 return m_options.testFlag(QtSvg::AssumeTrustedSource);
1234 QStringList lst = str.split(QLatin1Char(
','), Qt::SkipEmptyParts);
1239 const QXmlStreamAttributes &attributes)
1241 QStringList features;
1242 QStringList extensions;
1243 QStringList languages;
1244 QStringList formats;
1246 QStringView xmlClassStr;
1248 for (
const QXmlStreamAttribute &attribute : attributes) {
1249 QStringView name = attribute.qualifiedName();
1252 QStringView value = attribute.value();
1253 switch (name.at(0).unicode()) {
1255 if (name == QLatin1String(
"class"))
1256 xmlClassStr = value;
1259 if (name == QLatin1String(
"requiredFeatures"))
1260 features = stringToList(value.toString());
1261 else if (name == QLatin1String(
"requiredExtensions"))
1262 extensions = stringToList(value.toString());
1263 else if (name == QLatin1String(
"requiredFormats"))
1264 formats = stringToList(value.toString());
1265 else if (name == QLatin1String(
"requiredFonts"))
1266 fonts = stringToList(value.toString());
1269 if (name == QLatin1String(
"systemLanguage"))
1270 languages = stringToList(value.toString());
1277 node->setRequiredFeatures(features);
1278 node->setRequiredExtensions(extensions);
1279 node->setRequiredLanguages(languages);
1280 node->setRequiredFormats(formats);
1281 node->setRequiredFonts(fonts);
1282 node->setNodeId(someId(attributes));
1283 node->setXmlClass(xmlClassStr.toString());
1292 if (attributes.opacity.isEmpty())
1295 const QStringView value = attributes.opacity.trimmed();
1298 qreal op = value.toDouble(&ok);
1301 QSvgOpacityStylePtr opacity =
std::make_unique<
QSvgOpacityStyle>(qBound(qreal(0.0), op, qreal(1.0)));
1302 node->appendStyleProperty(
std::move(opacity));
1308 if (op == tokens::compOp::clear)
1309 return QPainter::CompositionMode_Clear;
1310 else if (op == tokens::compOp::src)
1311 return QPainter::CompositionMode_Source;
1312 else if (op == tokens::compOp::dst)
1313 return QPainter::CompositionMode_Destination;
1314 else if (op == tokens::compOp::srcOver)
1315 return QPainter::CompositionMode_SourceOver;
1316 else if (op == tokens::compOp::dstOver)
1317 return QPainter::CompositionMode_DestinationOver;
1318 else if (op == tokens::compOp::srcIn)
1319 return QPainter::CompositionMode_SourceIn;
1320 else if (op == tokens::compOp::dstIn)
1321 return QPainter::CompositionMode_DestinationIn;
1322 else if (op == tokens::compOp::srcOut)
1323 return QPainter::CompositionMode_SourceOut;
1324 else if (op == tokens::compOp::dstOut)
1325 return QPainter::CompositionMode_DestinationOut;
1326 else if (op == tokens::compOp::srcAtop)
1327 return QPainter::CompositionMode_SourceAtop;
1328 else if (op == tokens::compOp::dstAtop)
1329 return QPainter::CompositionMode_DestinationAtop;
1330 else if (op == tokens::compOp::xorOp)
1331 return QPainter::CompositionMode_Xor;
1332 else if (op == tokens::compOp::plus)
1333 return QPainter::CompositionMode_Plus;
1334 else if (op == tokens::compOp::multiply)
1335 return QPainter::CompositionMode_Multiply;
1336 else if (op == tokens::compOp::screen)
1337 return QPainter::CompositionMode_Screen;
1338 else if (op == tokens::compOp::overlay)
1339 return QPainter::CompositionMode_Overlay;
1340 else if (op == tokens::compOp::darken)
1341 return QPainter::CompositionMode_Darken;
1342 else if (op == tokens::compOp::lighten)
1343 return QPainter::CompositionMode_Lighten;
1344 else if (op == tokens::compOp::colorDodge)
1345 return QPainter::CompositionMode_ColorDodge;
1346 else if (op == tokens::compOp::colorBurn)
1347 return QPainter::CompositionMode_ColorBurn;
1348 else if (op == tokens::compOp::hardLight)
1349 return QPainter::CompositionMode_HardLight;
1350 else if (op == tokens::compOp::softLight)
1351 return QPainter::CompositionMode_SoftLight;
1352 else if (op == tokens::compOp::difference)
1353 return QPainter::CompositionMode_Difference;
1354 else if (op == tokens::compOp::exclusion)
1355 return QPainter::CompositionMode_Exclusion;
1357 qCWarning(lcSvgHandler) <<
"Composition mode not supported : "_L1 << op;
1358 return QPainter::CompositionMode_SourceOver;
1365 if (attributes.compOp.isEmpty())
1367 QStringView value = attributes.compOp.trimmed();
1369 if (!value.isEmpty()) {
1370 QSvgCompOpStylePtr compop =
std::make_unique<
QSvgCompOpStyle>(svgToQtCompositionMode(value));
1371 node->appendStyleProperty(
std::move(compop));
1377 if (str == QLatin1String(
"inline")) {
1378 return QSvgNode::InlineMode;
1379 }
else if (str == QLatin1String(
"block")) {
1380 return QSvgNode::BlockMode;
1381 }
else if (str == QLatin1String(
"list-item")) {
1382 return QSvgNode::ListItemMode;
1383 }
else if (str == QLatin1String(
"run-in")) {
1384 return QSvgNode::RunInMode;
1385 }
else if (str == QLatin1String(
"compact")) {
1386 return QSvgNode::CompactMode;
1387 }
else if (str == QLatin1String(
"marker")) {
1388 return QSvgNode::MarkerMode;
1389 }
else if (str == QLatin1String(
"table")) {
1390 return QSvgNode::TableMode;
1391 }
else if (str == QLatin1String(
"inline-table")) {
1392 return QSvgNode::InlineTableMode;
1393 }
else if (str == QLatin1String(
"table-row-group")) {
1394 return QSvgNode::TableRowGroupMode;
1395 }
else if (str == QLatin1String(
"table-header-group")) {
1396 return QSvgNode::TableHeaderGroupMode;
1397 }
else if (str == QLatin1String(
"table-footer-group")) {
1398 return QSvgNode::TableFooterGroupMode;
1399 }
else if (str == QLatin1String(
"table-row")) {
1400 return QSvgNode::TableRowMode;
1401 }
else if (str == QLatin1String(
"table-column-group")) {
1402 return QSvgNode::TableColumnGroupMode;
1403 }
else if (str == QLatin1String(
"table-column")) {
1404 return QSvgNode::TableColumnMode;
1405 }
else if (str == QLatin1String(
"table-cell")) {
1406 return QSvgNode::TableCellMode;
1407 }
else if (str == QLatin1String(
"table-caption")) {
1408 return QSvgNode::TableCaptionMode;
1409 }
else if (str == QLatin1String(
"none")) {
1410 return QSvgNode::NoneMode;
1411 }
else if (str == tokens::inherit) {
1412 return QSvgNode::InheritMode;
1414 return QSvgNode::BlockMode;
1421 if (attributes.display.isEmpty())
1423 QStringView displayStr = attributes.display.trimmed();
1425 if (!displayStr.isEmpty()) {
1426 node->setDisplayMode(displayStringToEnum(displayStr));
1432 if (attribute.isEmpty())
1433 return std::nullopt;
1435 return idFromFuncIRI(attribute);
1440 QSvgHandler *handler)
1445 if (
auto id = getAttributeId(attributes.mask))
1446 node->setMaskId(id->toString());
1447 if (
auto id = getAttributeId(attributes.markerStart))
1448 node->setMarkerStartId(id->toString());
1449 if (
auto id = getAttributeId(attributes.markerMid))
1450 node->setMarkerMidId(id->toString());
1451 if (
auto id = getAttributeId(attributes.markerEnd))
1452 node->setMarkerEndId(id->toString());
1453 if (
auto id = getAttributeId(attributes.filter))
1454 node->setFilterId(id->toString());
1461 if (attributes.imageRendering.isEmpty())
1464 QStringView ir = attributes.imageRendering.trimmed();
1466 if (ir == QLatin1String(
"auto"))
1467 quality->setImageRendering(QSvgQualityStyle::ImageRenderingAuto);
1468 else if (ir == QLatin1String(
"optimizeSpeed"))
1469 quality->setImageRendering(QSvgQualityStyle::ImageRenderingOptimizeSpeed);
1470 else if (ir == QLatin1String(
"optimizeQuality"))
1471 quality->setImageRendering(QSvgQualityStyle::ImageRenderingOptimizeQuality);
1472 node->appendStyleProperty(
std::move(quality));
1476 const QXmlStreamAttributes &attributes,
1477 QSvgHandler *handler)
1485#ifndef QT_NO_CSSPARSER
1486 QXmlStreamAttributes cssAttributes;
1487 handler->cssHandler().styleLookup(node, cssAttributes);
1489 QStringView style = attributes.value(QLatin1String(
"style"));
1490 if (!style.isEmpty())
1491 handler->cssHandler().parseCSStoXMLAttrs(style.toString(), cssAttributes);
1492 svgAttributes.setAttributes(cssAttributes, handler);
1494 parseOffsetPath(node, cssAttributes);
1496 parseCssAnimations(node, cssAttributes, handler);
1515 const QXmlStreamAttributes &attributes,
1518 Q_UNUSED(parent); Q_UNUSED(attributes);
1523 const QXmlStreamAttributes &attributes,
1524 QSvgAnimateNode *anim,
1525 QSvgHandler *handler)
1527 const QStringView beginStr = attributes.value(QLatin1String(
"begin"));
1528 const QStringView durStr = attributes.value(QLatin1String(
"dur"));
1529 const QStringView endStr = attributes.value(QLatin1String(
"end"));
1530 const QStringView repeatStr = attributes.value(QLatin1String(
"repeatCount"));
1531 const QStringView fillStr = attributes.value(QLatin1String(
"fill"));
1532 const QStringView addtv = attributes.value(QLatin1String(
"additive"));
1533 QStringView linkId = attributes.value(QLatin1String(
"xlink:href"));
1535 if (linkId.isEmpty())
1536 linkId = attributes.value(QLatin1String(
"href"));
1538 linkId = idFromIRI(linkId);
1541 int begin = parseClockValue(beginStr, &ok);
1544 int dur = parseClockValue(durStr, &ok);
1547 int end = parseClockValue(endStr, &ok);
1550 qreal repeatCount = (repeatStr == QLatin1String(
"indefinite")) ? -1 :
1551 qMax(1.0, QSvgUtils::toDouble(repeatStr));
1553 QSvgAnimateNode::Fill fill = (fillStr == QLatin1String(
"freeze")) ? QSvgAnimateNode::Freeze :
1554 QSvgAnimateNode::Remove;
1556 QSvgAnimateNode::Additive additive = (addtv == QLatin1String(
"sum")) ? QSvgAnimateNode::Sum :
1557 QSvgAnimateNode::Replace;
1559 anim->setRunningTime(begin, dur, end, 0);
1560 anim->setRepeatCount(repeatCount);
1561 anim->setFill(fill);
1562 anim->setAdditiveType(additive);
1563 anim->setLinkId(linkId.toString());
1565 handler->document()->setAnimated(
true);
1567 handler->setAnimPeriod(begin, begin + dur);
1576 qreal spacing = 1.0f / (count - 1);
1577 for (uint i = 0; i < count; i++) {
1578 keyFrames.append(i * spacing);
1583 const QXmlStreamAttributes &attributes,
1584 QSvgHandler *handler)
1586 const QStringView fromStr = attributes.value(QLatin1String(
"from"));
1587 const QStringView toStr = attributes.value(QLatin1String(
"to"));
1588 const QStringView valuesStr = attributes.value(QLatin1String(
"values"));
1589 const QString targetStr = attributes.value(QLatin1String(
"attributeName")).toString();
1591 if (targetStr != QLatin1String(
"fill") && targetStr != QLatin1String(
"stroke"))
1594 QList<QColor> colors;
1595 if (valuesStr.isEmpty()) {
1596 QColor startColor, endColor;
1597 resolveColor(fromStr, startColor, handler);
1598 resolveColor(toStr, endColor, handler);
1600 colors.append(startColor);
1601 colors.append(endColor);
1603 for (
auto part : qTokenize(valuesStr, u';')) {
1605 resolveColor(part, color, handler);
1606 colors.append(color);
1610 QSvgAnimatedPropertyColor *prop =
static_cast<QSvgAnimatedPropertyColor *>
1611 (QSvgAbstractAnimatedProperty::createAnimatedProperty(targetStr));
1615 prop->setColors(colors);
1617 QList<qreal> keyFrames;
1618 generateKeyFrames(keyFrames, colors.size());
1619 prop->setKeyFrames(keyFrames);
1621 QSvgAnimateColor *anim =
new QSvgAnimateColor(parent);
1622 anim->appendProperty(prop);
1624 if (!parseBaseAnimate(parent, attributes, anim, handler)) {
1633 const QXmlStreamAttributes &attributes,
1636 Q_UNUSED(parent); Q_UNUSED(attributes);
1642 QList<qreal> list = parseNumbersList(s);
1644 for (
int i = 3 - list.size(); i > 0; --i)
1650 parseNumberTriplet(values, &s);
1654 const QXmlStreamAttributes &attributes,
1655 QSvgHandler *handler)
1657 const QStringView typeStr = attributes.value(QLatin1String(
"type"));
1658 const QStringView values = attributes.value(QLatin1String(
"values"));
1659 const QStringView fromStr = attributes.value(QLatin1String(
"from"));
1660 const QStringView toStr = attributes.value(QLatin1String(
"to"));
1661 const QStringView byStr = attributes.value(QLatin1String(
"by"));
1664 if (values.isEmpty()) {
1665 if (fromStr.isEmpty()) {
1666 if (!byStr.isEmpty()) {
1670 parseNumberTriplet(vals, byStr);
1676 if (!toStr.isEmpty()) {
1678 parseNumberTriplet(vals, fromStr);
1679 parseNumberTriplet(vals, toStr);
1680 }
else if (!byStr.isEmpty()) {
1682 parseNumberTriplet(vals, fromStr);
1683 parseNumberTriplet(vals, byStr);
1684 for (
int i = vals.size() - 3; i < vals.size(); ++i)
1685 vals[i] += vals[i - 3];
1691 QStringView s = values;
1692 while (!s.isEmpty()) {
1693 parseNumberTriplet(vals, &s);
1698 if (vals.size() % 3 != 0)
1702 QList<QSvgAnimatedPropertyTransform::TransformComponent> components;
1703 for (
int i = 0; i <= vals.size() - 3; i += 3) {
1704 QSvgAnimatedPropertyTransform::TransformComponent component;
1705 if (typeStr == QLatin1String(
"translate")) {
1706 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Translate;
1707 component.values.append(vals.at(i));
1708 component.values.append(vals.at(i + 1));
1709 }
else if (typeStr == QLatin1String(
"scale")) {
1710 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Scale;
1711 component.values.append(vals.at(i));
1712 component.values.append(vals.at(i + 1));
1713 }
else if (typeStr == QLatin1String(
"rotate")) {
1714 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Rotate;
1715 component.values.append(vals.at(i));
1716 component.values.append(vals.at(i + 1));
1717 component.values.append(vals.at(i + 2));
1718 }
else if (typeStr == QLatin1String(
"skewX")) {
1719 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Skew;
1720 component.values.append(vals.at(i));
1721 component.values.append(0);
1722 }
else if (typeStr == QLatin1String(
"skewY")) {
1723 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Skew;
1724 component.values.append(0);
1725 component.values.append(vals.at(i));
1729 components.append(component);
1732 QSvgAnimatedPropertyTransform *prop =
static_cast<QSvgAnimatedPropertyTransform *>
1733 (QSvgAbstractAnimatedProperty::createAnimatedProperty(QLatin1String(
"transform")));
1737 prop->appendComponents(components);
1739 prop->setTransformCount(1);
1740 QList<qreal> keyFrames;
1741 generateKeyFrames(keyFrames, vals.size() / 3);
1742 prop->setKeyFrames(keyFrames);
1744 QSvgAnimateTransform *anim =
new QSvgAnimateTransform(parent);
1745 anim->appendProperty(prop);
1747 if (!parseBaseAnimate(parent, attributes, anim, handler)) {
1756 const QXmlStreamAttributes &attributes,
1759 Q_UNUSED(parent); Q_UNUSED(attributes);
1764 const QXmlStreamAttributes &attributes,
1767 Q_UNUSED(parent); Q_UNUSED(attributes);
1772 const QXmlStreamAttributes &attributes,
1775 const QStringView cx = attributes.value(QLatin1String(
"cx"));
1776 const QStringView cy = attributes.value(QLatin1String(
"cy"));
1777 const QStringView r = attributes.value(QLatin1String(
"r"));
1778 qreal ncx = QSvgUtils::toDouble(cx);
1779 qreal ncy = QSvgUtils::toDouble(cy);
1780 qreal nr = QSvgUtils::toDouble(r);
1784 QRectF rect(ncx-nr, ncy-nr, nr*2, nr*2);
1785 QSvgNode *circle =
new QSvgCircle(parent, rect);
1790 const QXmlStreamAttributes &attributes,
1793 Q_UNUSED(attributes);
1794 QSvgDefs *defs =
new QSvgDefs(parent);
1799 const QXmlStreamAttributes &attributes,
1802 Q_UNUSED(parent); Q_UNUSED(attributes);
1807 const QXmlStreamAttributes &attributes,
1810 const QStringView cx = attributes.value(QLatin1String(
"cx"));
1811 const QStringView cy = attributes.value(QLatin1String(
"cy"));
1812 const QStringView rx = attributes.value(QLatin1String(
"rx"));
1813 const QStringView ry = attributes.value(QLatin1String(
"ry"));
1814 qreal ncx = QSvgUtils::toDouble(cx);
1815 qreal ncy = QSvgUtils::toDouble(cy);
1816 qreal nrx = QSvgUtils::toDouble(rx);
1817 qreal nry = QSvgUtils::toDouble(ry);
1819 QRectF rect(ncx-nrx, ncy-nry, nrx*2, nry*2);
1820 QSvgNode *ellipse =
new QSvgEllipse(parent, rect);
1825 QSvgHandler *handler)
1827 const QStringView hax = attributes.value(QLatin1String(
"horiz-adv-x"));
1828 QString myId = someId(attributes);
1830 qreal horizAdvX = QSvgUtils::toDouble(hax);
1832 if (!myId.isEmpty()) {
1833 QSvgDocument *doc = handler->document();
1834 QSvgFont *font = doc->svgFont(myId);
1836 font =
new QSvgFont(horizAdvX);
1837 font->setFamilyName(myId);
1838 doc->addSvgFont(font);
1840 return new QSvgFontStyle(font);
1846 const QXmlStreamAttributes &attributes,
1847 QSvgHandler *handler)
1849 if (parent->type() != QSvgStyleProperty::Font) {
1853 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
1854 QSvgFont *font = style->svgFont();
1855 const QStringView name = attributes.value(QLatin1String(
"font-family"));
1856 const QStringView unitsPerEmStr = attributes.value(QLatin1String(
"units-per-em"));
1859
1860
1862 qreal unitsPerEm = QSvgUtils::toDouble(unitsPerEmStr, &ok);
1863 if (!qFuzzyIsNull(unitsPerEm))
1864 font->setUnitsPerEm(unitsPerEm);
1866 if (!name.isEmpty())
1867 font->setFamilyName(name.toString());
1869 if (!font->familyName().isEmpty())
1870 if (!handler->document()->svgFont(font->familyName()))
1871 handler->document()->addSvgFont(font);
1877 const QXmlStreamAttributes &attributes,
1878 QSvgHandler *handler)
1880 if (parent->type() != QSvgStyleProperty::Font) {
1884 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
1885 QSvgFont *font = style->svgFont();
1886 const QStringView name = attributes.value(QLatin1String(
"name"));
1888 if (!name.isEmpty())
1889 font->setFamilyName(name.toString());
1891 if (!font->familyName().isEmpty())
1892 if (!handler->document()->svgFont(font->familyName()))
1893 handler->document()->addSvgFont(font);
1899 const QXmlStreamAttributes &attributes,
1902 Q_UNUSED(parent); Q_UNUSED(attributes);
1907 const QXmlStreamAttributes &attributes,
1910 Q_UNUSED(parent); Q_UNUSED(attributes);
1915 const QXmlStreamAttributes &attributes,
1918 Q_UNUSED(parent); Q_UNUSED(attributes);
1923 const QXmlStreamAttributes &attributes,
1926 Q_UNUSED(attributes);
1927 QSvgG *node =
new QSvgG(parent);
1932 const QXmlStreamAttributes &attributes,
1935 if (parent->type() != QSvgStyleProperty::Font) {
1939 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
1940 QSvgFont *font = style->svgFont();
1941 return createSvgGlyph(font, attributes,
false);
1945 const QXmlStreamAttributes &attributes,
1948 Q_UNUSED(parent); Q_UNUSED(attributes);
1953 const QXmlStreamAttributes &attributes,
1956 Q_UNUSED(parent); Q_UNUSED(attributes);
1961 const QXmlStreamAttributes &attributes,
1962 QSvgHandler *handler)
1964 const QStringView x = attributes.value(QLatin1String(
"x"));
1965 const QStringView y = attributes.value(QLatin1String(
"y"));
1966 const QStringView width = attributes.value(QLatin1String(
"width"));
1967 const QStringView height = attributes.value(QLatin1String(
"height"));
1968 QString filename = attributes.value(QLatin1String(
"xlink:href")).toString();
1970 filename = attributes.value(QLatin1String(
"href")).toString();
1971 qreal nx = QSvgUtils::toDouble(x);
1972 qreal ny = QSvgUtils::toDouble(y);
1974 qreal nwidth = QSvgUtils::parseLength(width.toString(), &type);
1975 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
1977 qreal nheight = QSvgUtils::parseLength(height.toString(), &type);
1978 nheight = QSvgUtils::convertToPixels(nheight,
false, type);
1980 filename = filename.trimmed();
1981 if (filename.isEmpty()) {
1982 qCWarning(lcSvgHandler) <<
"QSvgHandler: Image filename is empty";
1985 if (nwidth <= 0 || nheight <= 0) {
1986 qCWarning(lcSvgHandler) <<
"QSvgHandler: Width or height for" << filename <<
"image was not greater than 0";
1995 } filenameType = NotLoaded;
1997 if (filename.startsWith(QLatin1String(
"data"))) {
2000 if (qDecodeDataUrl(QUrl{filename}, mimeType, data)) {
2001 image = QImage::fromData(data);
2002 filenameType = LoadedFromData;
2006 if (image.isNull()) {
2007 const auto *file = qobject_cast<QFile *>(handler->device());
2010 if (url.isRelative()) {
2012 filename = info.absoluteDir().absoluteFilePath(filename);
2016 if (handler->trustedSourceMode() || !QImageReader::imageFormat(filename).startsWith(
"svg")) {
2017 image = QImage(filename);
2018 filenameType = LoadedFromFile;
2022 if (image.isNull()) {
2023 qCWarning(lcSvgHandler) <<
"Could not create image from" << filename;
2027 if (image.format() == QImage::Format_ARGB32)
2028 image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
2030 if (filenameType != LoadedFromFile)
2031 filename = QString();
2032 QSvgNode *img =
new QSvgImage(parent,
2043 const QXmlStreamAttributes &attributes,
2046 const QStringView x1 = attributes.value(QLatin1String(
"x1"));
2047 const QStringView y1 = attributes.value(QLatin1String(
"y1"));
2048 const QStringView x2 = attributes.value(QLatin1String(
"x2"));
2049 const QStringView y2 = attributes.value(QLatin1String(
"y2"));
2050 qreal nx1 = QSvgUtils::toDouble(x1);
2051 qreal ny1 = QSvgUtils::toDouble(y1);
2052 qreal nx2 = QSvgUtils::toDouble(x2);
2053 qreal ny2 = QSvgUtils::toDouble(y2);
2055 QLineF lineBounds(nx1, ny1, nx2, ny2);
2056 QSvgNode *line =
new QSvgLine(parent, lineBounds);
2062 QSvgGradientPaint *gradProp,
2063 QSvgHandler *handler)
2065 QStringView linkId = attributes.value(QLatin1String(
"xlink:href"));
2066 const QStringView trans = attributes.value(QLatin1String(
"gradientTransform"));
2067 const QStringView spread = attributes.value(QLatin1String(
"spreadMethod"));
2068 const QStringView units = attributes.value(QLatin1String(
"gradientUnits"));
2069 const QStringView colorStr = attributes.value(QLatin1String(
"color"));
2070 const QStringView colorOpacityStr = attributes.value(QLatin1String(
"color-opacity"));
2073 if (constructColor(colorStr, colorOpacityStr, color, handler)) {
2074 handler->popColor();
2075 handler->pushColor(color);
2079 QGradient *grad = gradProp->qgradient();
2080 linkId = idFromIRI(linkId);
2082 if (!linkId.isEmpty()) {
2083 QSvgPaintServerSharedPtr paintServer = handler->document()->paintServer(linkId);
2084 if (paintServer && paintServer->type() == QSvgPaintServer::Type::Gradient) {
2085 QSvgGradientPaint *inherited =
2086 static_cast<QSvgGradientPaint*>(paintServer.get());
2087 if (!inherited->stopLink().isEmpty()) {
2088 gradProp->setStopLink(inherited->stopLink(), handler->document());
2090 grad->setStops(inherited->qgradient()->stops());
2091 gradProp->setGradientStopsSet(inherited->gradientStopsSet());
2094 matrix = inherited->qtransform();
2096 gradProp->setStopLink(linkId.toString(), handler->document());
2100 if (!trans.isEmpty()) {
2101 matrix = parseTransformationMatrix(trans);
2102 gradProp->setTransform(matrix);
2103 }
else if (!matrix.isIdentity()) {
2104 gradProp->setTransform(matrix);
2107 if (!spread.isEmpty()) {
2108 if (spread == QLatin1String(
"pad")) {
2109 grad->setSpread(QGradient::PadSpread);
2110 }
else if (spread == QLatin1String(
"reflect")) {
2111 grad->setSpread(QGradient::ReflectSpread);
2112 }
else if (spread == QLatin1String(
"repeat")) {
2113 grad->setSpread(QGradient::RepeatSpread);
2117 if (units.isEmpty() || units == QLatin1String(
"objectBoundingBox")) {
2118 grad->setCoordinateMode(QGradient::ObjectMode);
2124 QSvgHandler *handler)
2126 const QStringView x1 = attributes.value(QLatin1String(
"x1"));
2127 const QStringView y1 = attributes.value(QLatin1String(
"y1"));
2128 const QStringView x2 = attributes.value(QLatin1String(
"x2"));
2129 const QStringView y2 = attributes.value(QLatin1String(
"y2"));
2137 nx1 = convertToNumber(x1);
2139 ny1 = convertToNumber(y1);
2141 nx2 = convertToNumber(x2);
2143 ny2 = convertToNumber(y2);
2146 grad->setInterpolationMode(QGradient::ComponentInterpolation);
2148 QSvgGradientPaintSharedPtr paintServer = std::make_shared<QSvgGradientPaint>(std::move(grad));
2149 parseBaseGradient(attributes, paintServer.get(), handler);
2155 const QXmlStreamAttributes &attributes,
2158 Q_UNUSED(parent); Q_UNUSED(attributes);
2163 const QXmlStreamAttributes &attributes,
2166 if (parent->type() != QSvgStyleProperty::Font) {
2170 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
2171 QSvgFont *font = style->svgFont();
2172 return createSvgGlyph(font, attributes,
true);
2176 const QXmlStreamAttributes &attributes,
2179 Q_UNUSED(parent); Q_UNUSED(attributes);
2184 const QXmlStreamAttributes &attributes,
2187 Q_UNUSED(parent); Q_UNUSED(attributes);
2192 const QXmlStreamAttributes &,
2199 const QXmlStreamAttributes &attributes,
2200 QSvgHandler *handler)
2202 const QStringView x = attributes.value(QLatin1String(
"x"));
2203 const QStringView y = attributes.value(QLatin1String(
"y"));
2204 const QStringView width = attributes.value(QLatin1String(
"width"));
2205 const QStringView height = attributes.value(QLatin1String(
"height"));
2206 const QStringView mU = attributes.value(QLatin1String(
"maskUnits"));
2207 const QStringView mCU = attributes.value(QLatin1String(
"maskContentUnits"));
2209 QtSvg::
UnitTypes nmU = mU.contains(QLatin1String(
"userSpaceOnUse")) ?
2212 QtSvg::
UnitTypes nmCU = mCU.contains(QLatin1String(
"objectBoundingBox")) ?
2222 qreal nx = QSvgUtils::parseLength(x, &type, &ok);
2223 nx = QSvgUtils::convertToPixels(nx,
true, type);
2224 if (x.isEmpty() || !ok) {
2228 nx = nx / 100. * handler->document()->viewBox().width();
2233 qreal ny = QSvgUtils::parseLength(y, &type, &ok);
2234 ny = QSvgUtils::convertToPixels(ny,
true, type);
2235 if (y.isEmpty() || !ok) {
2239 ny = ny / 100. * handler->document()->viewBox().height();
2244 qreal nwidth = QSvgUtils::parseLength(width, &type, &ok);
2245 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
2246 if (width.isEmpty() || !ok) {
2250 nwidth = nwidth / 100. * handler->document()->viewBox().width();
2252 nwidth = nwidth / 100.;
2255 qreal nheight = QSvgUtils::parseLength(height, &type, &ok);
2256 nheight = QSvgUtils::convertToPixels(nheight,
true, type);
2257 if (height.isEmpty() || !ok) {
2261 nheight = nheight / 100. * handler->document()->viewBox().height();
2263 nheight = nheight / 100.;
2266 QRectF bounds(nx, ny, nwidth, nheight);
2267 if (bounds.isEmpty())
2270 QSvgNode *mask =
new QSvgMask(parent, QSvgRectF(bounds, nmUx, nmUy, nmUw, nmUh), nmCU);
2277 const QStringView xStr = attributes.value(QLatin1String(
"x"));
2278 const QStringView yStr = attributes.value(QLatin1String(
"y"));
2279 const QStringView widthStr = attributes.value(QLatin1String(
"width"));
2280 const QStringView heightStr = attributes.value(QLatin1String(
"height"));
2283 if (!xStr.isEmpty()) {
2285 x = QSvgUtils::parseLength(xStr, &type);
2287 x = QSvgUtils::convertToPixels(x,
true, type);
2297 if (!yStr.isEmpty()) {
2299 y = QSvgUtils::parseLength(yStr, &type);
2301 y = QSvgUtils::convertToPixels(y,
false, type);
2311 if (!widthStr.isEmpty()) {
2313 width = QSvgUtils::parseLength(widthStr, &type);
2315 width = QSvgUtils::convertToPixels(width,
true, type);
2322 rect->setWidth(width);
2325 if (!heightStr.isEmpty()) {
2327 height = QSvgUtils::parseLength(heightStr, &type);
2329 height = QSvgUtils::convertToPixels(height,
false, type);
2336 rect->setHeight(height);
2341 const QXmlStreamAttributes &attributes,
2342 QSvgHandler *handler)
2344 const QStringView fU = attributes.value(QLatin1String(
"filterUnits"));
2345 const QStringView pU = attributes.value(QLatin1String(
"primitiveUnits"));
2347 const QtSvg::
UnitTypes filterUnits = fU.contains(QLatin1String(
"userSpaceOnUse")) ?
2350 const QtSvg::
UnitTypes primitiveUnits = pU.contains(QLatin1String(
"objectBoundingBox")) ?
2358 qreal width = handler->document()->viewBox().width();
2359 qreal height = handler->document()->viewBox().height();
2360 rect = QSvgRectF(QRectF(-0.1 * width, -0.1 * height, 1.2 * width, 1.2 * height),
2364 rect = QSvgRectF(QRectF(-0.1, -0.1, 1.2, 1.2),
2369 parseFilterBounds(attributes, &rect);
2371 QSvgNode *filter =
new QSvgFilterContainer(parent, rect, filterUnits, primitiveUnits);
2376 QString *outString, QSvgRectF *rect)
2378 *inString = attributes.value(QLatin1String(
"in")).toString();
2379 *outString = attributes.value(QLatin1String(
"result")).toString();
2385 *rect = QSvgRectF(QRectF(0, 0, 1.0, 1.0),
2391 parseFilterBounds(attributes, rect);
2395 const QXmlStreamAttributes &attributes,
2398 const QStringView typeString = attributes.value(QLatin1String(
"type"));
2399 const QStringView valuesString = attributes.value(QLatin1String(
"values"));
2401 QString inputString;
2402 QString outputString;
2405 QSvgFeColorMatrix::ColorShiftType type;
2406 QSvgFeColorMatrix::Matrix values;
2409 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2411 if (typeString.startsWith(QLatin1String(
"saturate")))
2412 type = QSvgFeColorMatrix::ColorShiftType::Saturate;
2413 else if (typeString.startsWith(QLatin1String(
"hueRotate")))
2414 type = QSvgFeColorMatrix::ColorShiftType::HueRotate;
2415 else if (typeString.startsWith(QLatin1String(
"luminanceToAlpha")))
2416 type = QSvgFeColorMatrix::ColorShiftType::LuminanceToAlpha;
2418 type = QSvgFeColorMatrix::ColorShiftType::Matrix;
2420 if (!valuesString.isEmpty()) {
2421 const auto valueStringList = splitWithDelimiter(valuesString);
2422 for (
int i = 0, j = 0; i < qMin(20, valueStringList.size()); i++) {
2424 qreal v = QSvgUtils::toDouble(valueStringList.at(i), &ok);
2426 values.data()[j] = v;
2431 values.setToIdentity();
2434 QSvgNode *filter =
new QSvgFeColorMatrix(parent, inputString, outputString, rect,
2440 const QXmlStreamAttributes &attributes,
2443 const QStringView edgeModeString = attributes.value(QLatin1String(
"edgeMode"));
2444 const QStringView stdDeviationString = attributes.value(QLatin1String(
"stdDeviation"));
2446 QString inputString;
2447 QString outputString;
2450 QSvgFeGaussianBlur::EdgeMode edgemode = QSvgFeGaussianBlur::EdgeMode::Duplicate;
2452 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2453 qreal stdDeviationX = 0;
2454 qreal stdDeviationY = 0;
2456 stdDeviationX = qMax(0., QSvgUtils::toDouble(stdDeviationString.split(u" ").constFirst()));
2457 stdDeviationY = qMax(0., QSvgUtils::toDouble(stdDeviationString.split(u" ").constLast()));
2459 stdDeviationY = stdDeviationX = qMax(0., QSvgUtils::toDouble(stdDeviationString));
2462 if (edgeModeString.startsWith(QLatin1String(
"wrap")))
2463 edgemode = QSvgFeGaussianBlur::EdgeMode::Wrap;
2464 else if (edgeModeString.startsWith(QLatin1String(
"none")))
2465 edgemode = QSvgFeGaussianBlur::EdgeMode::None;
2467 QSvgNode *filter =
new QSvgFeGaussianBlur(parent, inputString, outputString, rect,
2468 stdDeviationX, stdDeviationY, edgemode);
2473 const QXmlStreamAttributes &attributes,
2476 QStringView dxString = attributes.value(QLatin1String(
"dx"));
2477 QStringView dyString = attributes.value(QLatin1String(
"dy"));
2479 QString inputString;
2480 QString outputString;
2483 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2486 if (!dxString.isEmpty()) {
2488 dx = QSvgUtils::parseLength(dxString, &type);
2490 dx = QSvgUtils::convertToPixels(dx,
true, type);
2494 if (!dyString.isEmpty()) {
2496 dy = QSvgUtils::parseLength(dyString, &type);
2498 dy = QSvgUtils::convertToPixels(dy,
true, type);
2501 QSvgNode *filter =
new QSvgFeOffset(parent, inputString, outputString, rect,
2507 const QXmlStreamAttributes &attributes,
2510 const QStringView in2String = attributes.value(QLatin1String(
"in2"));
2511 const QStringView operatorString = attributes.value(QLatin1String(
"operator"));
2512 const QStringView k1String = attributes.value(QLatin1String(
"k1"));
2513 const QStringView k2String = attributes.value(QLatin1String(
"k2"));
2514 const QStringView k3String = attributes.value(QLatin1String(
"k3"));
2515 const QStringView k4String = attributes.value(QLatin1String(
"k4"));
2517 QString inputString;
2518 QString outputString;
2521 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2523 QSvgFeComposite::Operator op = QSvgFeComposite::Operator::Over;
2524 if (operatorString.startsWith(QLatin1String(
"in")))
2525 op = QSvgFeComposite::Operator::In;
2526 else if (operatorString.startsWith(QLatin1String(
"out")))
2527 op = QSvgFeComposite::Operator::Out;
2528 else if (operatorString.startsWith(QLatin1String(
"atop")))
2529 op = QSvgFeComposite::Operator::Atop;
2530 else if (operatorString.startsWith(QLatin1String(
"xor")))
2531 op = QSvgFeComposite::Operator::Xor;
2532 else if (operatorString.startsWith(QLatin1String(
"lighter")))
2533 op = QSvgFeComposite::Operator::Lighter;
2534 else if (operatorString.startsWith(QLatin1String(
"arithmetic")))
2535 op = QSvgFeComposite::Operator::Arithmetic;
2537 QVector4D k(0, 0, 0, 0);
2539 if (op == QSvgFeComposite::Operator::Arithmetic) {
2541 qreal v = QSvgUtils::toDouble(k1String, &ok);
2544 v = QSvgUtils::toDouble(k2String, &ok);
2547 v = QSvgUtils::toDouble(k3String, &ok);
2550 v = QSvgUtils::toDouble(k4String, &ok);
2555 QSvgNode *filter =
new QSvgFeComposite(parent, inputString, outputString, rect,
2556 in2String.toString(), op, k);
2562 const QXmlStreamAttributes &attributes,
2565 QString inputString;
2566 QString outputString;
2569 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2571 QSvgNode *filter =
new QSvgFeMerge(parent, inputString, outputString, rect);
2576 const QXmlStreamAttributes &attributes,
2577 QSvgHandler *handler)
2579 QStringView colorStr = attributes.value(QLatin1String(
"flood-color"));
2580 const QStringView opacityStr = attributes.value(QLatin1String(
"flood-opacity"));
2583 if (!constructColor(colorStr, opacityStr, color, handler)) {
2584 color = QColor(Qt::black);
2585 if (opacityStr.isEmpty())
2586 color.setAlphaF(1.0);
2588 setAlpha(opacityStr, &color);
2591 QString inputString;
2592 QString outputString;
2595 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2597 QSvgNode *filter =
new QSvgFeFlood(parent, inputString, outputString, rect, color);
2602 const QXmlStreamAttributes &attributes,
2605 QString inputString;
2606 QString outputString;
2609 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2611 QSvgNode *filter =
new QSvgFeMergeNode(parent, inputString, outputString, rect);
2616 const QXmlStreamAttributes &attributes,
2619 const QStringView in2String = attributes.value(QLatin1String(
"in2"));
2620 const QStringView modeString = attributes.value(QLatin1String(
"mode"));
2622 QString inputString;
2623 QString outputString;
2626 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2628 QSvgFeBlend::Mode mode = QSvgFeBlend::Mode::Normal;
2629 if (modeString.startsWith(QLatin1StringView(
"multiply")))
2630 mode = QSvgFeBlend::Mode::Multiply;
2631 else if (modeString.startsWith(QLatin1StringView(
"screen")))
2632 mode = QSvgFeBlend::Mode::Screen;
2633 else if (modeString.startsWith(QLatin1StringView(
"darken")))
2634 mode = QSvgFeBlend::Mode::Darken;
2635 else if (modeString.startsWith(QLatin1StringView(
"lighten")))
2636 mode = QSvgFeBlend::Mode::Lighten;
2638 QSvgNode *filter =
new QSvgFeBlend(parent, inputString, outputString, rect,
2639 in2String.toString(), mode);
2644 const QXmlStreamAttributes &attributes,
2647 QString inputString;
2648 QString outputString;
2651 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2653 QSvgNode *filter =
new QSvgFeUnsupported(parent, inputString, outputString, rect);
2659 QList<QStringView> viewBoxValues;
2662 viewBoxValues = splitWithDelimiter(str);
2663 if (viewBoxValues.size() == 4) {
2665 qreal x = QSvgUtils::parseLength(viewBoxValues.at(0).trimmed(), &type);
2666 qreal y = QSvgUtils::parseLength(viewBoxValues.at(1).trimmed(), &type);
2667 qreal w = QSvgUtils::parseLength(viewBoxValues.at(2).trimmed(), &type);
2668 qreal h = QSvgUtils::parseLength(viewBoxValues.at(3).trimmed(), &type);
2669 return QRectF(x, y, w, h);
2671 return std::nullopt;
2675 QRectF *rect, QRectF *viewBox, QPointF *refPoint,
2676 QSvgSymbolLike::PreserveAspectRatios *aspect,
2677 QSvgSymbolLike::Overflow *overflow,
2678 bool marker =
false)
2680 const QStringView xStr = attributes.value(QLatin1String(
"x"));
2681 const QStringView yStr = attributes.value(QLatin1String(
"y"));
2682 const QStringView refXStr = attributes.value(QLatin1String(
"refX"));
2683 const QStringView refYStr = attributes.value(QLatin1String(
"refY"));
2684 const QStringView widthStr = attributes.value(marker ? QLatin1String(
"markerWidth")
2685 : QLatin1String(
"width"));
2686 const QStringView heightStr = attributes.value(marker ? QLatin1String(
"markerHeight")
2687 : QLatin1String(
"height"));
2688 const QStringView pAspectRStr = attributes.value(QLatin1String(
"preserveAspectRatio"));
2689 const QStringView overflowStr = attributes.value(QLatin1String(
"overflow"));
2690 const QStringView viewBoxStr = attributes.value(QLatin1String(
"viewBox"));
2694 if (!xStr.isEmpty()) {
2696 x = QSvgUtils::parseLength(xStr, &type);
2698 x = QSvgUtils::convertToPixels(x,
true, type);
2701 if (!yStr.isEmpty()) {
2703 y = QSvgUtils::parseLength(yStr, &type);
2705 y = QSvgUtils::convertToPixels(y,
false, type);
2708 if (!widthStr.isEmpty()) {
2710 width = QSvgUtils::parseLength(widthStr, &type);
2712 width = QSvgUtils::convertToPixels(width,
true, type);
2715 if (!heightStr.isEmpty()) {
2717 height = QSvgUtils::parseLength(heightStr, &type);
2719 height = QSvgUtils::convertToPixels(height,
false, type);
2722 *rect = QRectF(x, y, width, height);
2725 if (!refXStr.isEmpty()) {
2727 x = QSvgUtils::parseLength(refXStr, &type);
2729 x = QSvgUtils::convertToPixels(x,
true, type);
2732 if (!refYStr.isEmpty()) {
2734 y = QSvgUtils::parseLength(refYStr, &type);
2736 y = QSvgUtils::convertToPixels(y,
false, type);
2738 *refPoint = QPointF(x,y);
2740 auto viewBoxResult = parseViewBox(viewBoxStr);
2742 *viewBox = *viewBoxResult;
2743 else if (width > 0 && height > 0)
2744 *viewBox = QRectF(0, 0, width, height);
2746 *viewBox = handler->document()->viewBox();
2748 if (viewBox->isNull())
2751 auto pAspectRStrs = pAspectRStr.split(u" ");
2752 QSvgSymbolLike::PreserveAspectRatio aspectX = QSvgSymbolLike::PreserveAspectRatio::xMid;
2753 QSvgSymbolLike::PreserveAspectRatio aspectY = QSvgSymbolLike::PreserveAspectRatio::yMid;
2754 QSvgSymbolLike::PreserveAspectRatio aspectMS = QSvgSymbolLike::PreserveAspectRatio::meet;
2756 for (
auto &pAStr : std::as_const(pAspectRStrs)) {
2757 if (pAStr.startsWith(QLatin1String(
"none"))) {
2758 aspectX = QSvgSymbolLike::PreserveAspectRatio::None;
2759 aspectY = QSvgSymbolLike::PreserveAspectRatio::None;
2761 if (pAStr.startsWith(QLatin1String(
"xMin")))
2762 aspectX = QSvgSymbolLike::PreserveAspectRatio::xMin;
2763 else if (pAStr.startsWith(QLatin1String(
"xMax")))
2764 aspectX = QSvgSymbolLike::PreserveAspectRatio::xMax;
2765 if (pAStr.endsWith(QLatin1String(
"YMin")))
2766 aspectY = QSvgSymbolLike::PreserveAspectRatio::yMin;
2767 else if (pAStr.endsWith(QLatin1String(
"YMax")))
2768 aspectY = QSvgSymbolLike::PreserveAspectRatio::yMax;
2771 if (pAStr.endsWith(QLatin1String(
"slice")))
2772 aspectMS = QSvgSymbolLike::PreserveAspectRatio::slice;
2774 *aspect = aspectX | aspectY | aspectMS;
2781 *overflow = QSvgSymbolLike::Overflow::Hidden;
2783 if (overflowStr.endsWith(QLatin1String(
"auto")))
2784 *overflow = QSvgSymbolLike::Overflow::Auto;
2785 else if (overflowStr.endsWith(QLatin1String(
"visible")))
2786 *overflow = QSvgSymbolLike::Overflow::Visible;
2787 else if (overflowStr.endsWith(QLatin1String(
"hidden")))
2788 *overflow = QSvgSymbolLike::Overflow::Hidden;
2789 else if (overflowStr.endsWith(QLatin1String(
"scroll")))
2790 *overflow = QSvgSymbolLike::Overflow::Scroll;
2796 const QXmlStreamAttributes &attributes,
2797 QSvgHandler *handler)
2799 QRectF rect, viewBox;
2801 QSvgSymbolLike::PreserveAspectRatios aspect;
2802 QSvgSymbolLike::Overflow overflow;
2804 if (!parseSymbolLikeAttributes(attributes, handler, &rect, &viewBox, &refP, &aspect, &overflow))
2807 refP = QPointF(0, 0);
2808 QSvgNode *symbol =
new QSvgSymbol(parent, rect, viewBox, refP, aspect, overflow);
2813 const QXmlStreamAttributes &attributes,
2814 QSvgHandler *handler)
2816 QRectF rect, viewBox;
2818 QSvgSymbolLike::PreserveAspectRatios aspect;
2819 QSvgSymbolLike::Overflow overflow;
2821 const QStringView orientStr = attributes.value(QLatin1String(
"orient"));
2822 const QStringView markerUnitsStr = attributes.value(QLatin1String(
"markerUnits"));
2824 qreal orientationAngle = 0;
2825 QSvgMarker::Orientation orientation;
2826 if (orientStr.startsWith(QLatin1String(
"auto-start-reverse")))
2827 orientation = QSvgMarker::Orientation::AutoStartReverse;
2828 else if (orientStr.startsWith(QLatin1String(
"auto")))
2829 orientation = QSvgMarker::Orientation::Auto;
2831 orientation = QSvgMarker::Orientation::Value;
2834 if (orientStr.endsWith(QLatin1String(
"turn")))
2835 a = 360. * QSvgUtils::toDouble(orientStr.mid(0, orientStr.length()-4), &ok);
2836 else if (orientStr.endsWith(QLatin1String(
"grad")))
2837 a = QSvgUtils::toDouble(orientStr.mid(0, orientStr.length()-4), &ok);
2838 else if (orientStr.endsWith(QLatin1String(
"rad")))
2839 a = 180. /
M_PI * QSvgUtils::toDouble(orientStr.mid(0, orientStr.length()-3), &ok);
2841 a = QSvgUtils::toDouble(orientStr, &ok);
2843 orientationAngle = a;
2846 QSvgMarker::MarkerUnits markerUnits = QSvgMarker::MarkerUnits::StrokeWidth;
2847 if (markerUnitsStr.startsWith(QLatin1String(
"userSpaceOnUse")))
2848 markerUnits = QSvgMarker::MarkerUnits::UserSpaceOnUse;
2850 if (!parseSymbolLikeAttributes(attributes, handler, &rect, &viewBox, &refP, &aspect, &overflow,
true))
2853 QSvgNode *marker =
new QSvgMarker(parent, rect, viewBox, refP, aspect, overflow,
2854 orientation, orientationAngle, markerUnits);
2859 const QXmlStreamAttributes &attributes,
2860 QSvgHandler *handler)
2862 QStringView data = attributes.value(QLatin1String(
"d"));
2864 std::optional<QPainterPath> qpath = QSvgUtils::parsePathDataFast(data,
2865 !handler->trustedSourceMode());
2867 qCWarning(lcSvgHandler,
"Invalid path data; path truncated.");
2871 qpath.value().setFillRule(Qt::WindingFill);
2872 QSvgNode *path =
new QSvgPath(parent, qpath.value());
2877 const QXmlStreamAttributes &attributes,
2880 QStringView pointsStr = attributes.value(QLatin1String(
"points"));
2881 const QList<qreal> points = parseNumbersList(&pointsStr);
2882 if (points.size() < 4)
2885 for (
int i = 0; i < poly.size(); ++i)
2886 poly[i] = QPointF(points.at(2 * i), points.at(2 * i + 1));
2888 return new QSvgPolyline(parent, poly);
2890 return new QSvgPolygon(parent, poly);
2894 const QXmlStreamAttributes &attributes,
2897 return createPolyNode(parent, attributes,
false);
2901 const QXmlStreamAttributes &attributes,
2904 return createPolyNode(parent, attributes,
true);
2908 const QXmlStreamAttributes &attributes,
2911 Q_UNUSED(parent); Q_UNUSED(attributes);
2916 QSvgHandler *handler)
2918 const QStringView cx = attributes.value(QLatin1String(
"cx"));
2919 const QStringView cy = attributes.value(QLatin1String(
"cy"));
2920 const QStringView r = attributes.value(QLatin1String(
"r"));
2921 const QStringView fx = attributes.value(QLatin1String(
"fx"));
2922 const QStringView fy = attributes.value(QLatin1String(
"fy"));
2927 ncx = convertToNumber(cx);
2929 ncy = convertToNumber(cy);
2933 nr = convertToNumber(r);
2939 nfx = convertToNumber(fx);
2942 nfy = convertToNumber(fy);
2945 grad->setInterpolationMode(QGradient::ComponentInterpolation);
2947 QSvgGradientPaintSharedPtr paintServer = std::make_shared<QSvgGradientPaint>(std::move(grad));
2948 parseBaseGradient(attributes, paintServer.get(), handler);
2954 const QXmlStreamAttributes &attributes,
2957 const QStringView x = attributes.value(QLatin1String(
"x"));
2958 const QStringView y = attributes.value(QLatin1String(
"y"));
2959 const QStringView width = attributes.value(QLatin1String(
"width"));
2960 const QStringView height = attributes.value(QLatin1String(
"height"));
2961 const QStringView rx = attributes.value(QLatin1String(
"rx"));
2962 const QStringView ry = attributes.value(QLatin1String(
"ry"));
2966 qreal nwidth = QSvgUtils::parseLength(width, &type, &ok);
2969 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
2970 qreal nheight = QSvgUtils::parseLength(height, &type, &ok);
2973 nheight = QSvgUtils::convertToPixels(nheight,
true, type);
2974 qreal nrx = QSvgUtils::toDouble(rx);
2975 qreal nry = QSvgUtils::toDouble(ry);
2978 if (bounds.isEmpty())
2981 if (!rx.isEmpty() && ry.isEmpty())
2983 else if (!ry.isEmpty() && rx.isEmpty())
2989 if (nrx > bounds.width()/2)
2990 nrx = bounds.width()/2;
2991 if (nry > bounds.height()/2)
2992 nry = bounds.height()/2;
2997 nrx *= (100/(bounds.width()/2));
2998 nry *= (100/(bounds.height()/2));
3000 QSvgNode *rect =
new QSvgRect(parent, bounds, nrx, nry);
3005 const QXmlStreamAttributes &attributes,
3008 Q_UNUSED(parent); Q_UNUSED(attributes);
3013 const QXmlStreamAttributes &attributes,
3016 Q_UNUSED(parent); Q_UNUSED(attributes);
3021 QSvgHandler *handler)
3023 Q_UNUSED(attributes);
3024 QStringView solidColorStr = attributes.value(QLatin1String(
"solid-color"));
3025 QStringView solidOpacityStr = attributes.value(QLatin1String(
"solid-opacity"));
3027 if (solidOpacityStr.isEmpty())
3028 solidOpacityStr = attributes.value(QLatin1String(
"opacity"));
3031 if (!constructColor(solidColorStr, solidOpacityStr, color, handler))
3033 std::shared_ptr<QSvgSolidColorPaint> paintServer = std::make_shared<QSvgSolidColorPaint>(color);
3038 const QXmlStreamAttributes &attributes,
3039 QSvgHandler *handler)
3041 if (paintServer->type() != QSvgPaintServer::Type::Gradient)
3043 QString nodeIdStr = someId(attributes);
3044 QString xmlClassStr = attributes.value(QLatin1String(
"class")).toString();
3051 dummy.setNodeId(nodeIdStr);
3052 dummy.setXmlClass(xmlClassStr);
3056#ifndef QT_NO_CSSPARSER
3057 QXmlStreamAttributes cssAttributes;
3058 handler->cssHandler().styleLookup(&dummy, cssAttributes);
3059 attrs.setAttributes(cssAttributes, handler);
3061 QXmlStreamAttributes styleCssAttributes;
3062 QStringView style = attributes.value(QLatin1String(
"style"));
3063 if (!style.isEmpty())
3064 handler->cssHandler().parseCSStoXMLAttrs(style.toString(), styleCssAttributes);
3065 attrs.setAttributes(styleCssAttributes, handler);
3069 parseColor(&dummy, attrs, handler);
3071 QSvgGradientPaint *gradientStyle =
static_cast<QSvgGradientPaint*>(paintServer);
3072 QStringView colorStr = attrs.stopColor;
3076 qreal offset = convertToNumber(attrs.offset, &ok);
3080 if (!constructColor(colorStr, attrs.stopOpacity, color, handler)) {
3082 if (!attrs.stopOpacity.isEmpty())
3083 setAlpha(attrs.stopOpacity, &color);
3086 QGradient *grad = gradientStyle->qgradient();
3088 offset = qMin(qreal(1), qMax(qreal(0), offset));
3090 if (gradientStyle->gradientStopsSet()) {
3091 stops = grad->stops();
3093 if (offset <= stops.back().first)
3094 offset = stops.back().first + FLT_EPSILON;
3099 if ((stops.size() == 1) || (stops.at(stops.size() - 2).first < 1.0 - FLT_EPSILON)) {
3100 stops.back().first = 1.0 - FLT_EPSILON;
3101 grad->setStops(stops);
3106 grad->setColorAt(offset, color);
3107 gradientStyle->setGradientStopsSet(
true);
3112 const QXmlStreamAttributes &attributes,
3113 QSvgHandler *handler)
3116#ifdef QT_NO_CSSPARSER
3117 Q_UNUSED(attributes);
3120 const QStringView type = attributes.value(QLatin1String(
"type"));
3121 if (type.compare(QLatin1String(
"text/css"), Qt::CaseInsensitive) == 0 || type.isNull())
3122 handler->setInStyle(
true);
3129 const QXmlStreamAttributes &attributes,
3130 QSvgHandler *handler)
3132 Q_UNUSED(parent); Q_UNUSED(attributes);
3134 QSvgDocument *node =
new QSvgDocument(handler->options(), handler->animatorType());
3135 const QStringView widthStr = attributes.value(QLatin1String(
"width"));
3136 const QStringView heightStr = attributes.value(QLatin1String(
"height"));
3137 const QStringView viewBoxStr = attributes.value(QLatin1String(
"viewBox"));
3141 if (!widthStr.isEmpty()) {
3142 width = QSvgUtils::parseLength(widthStr, &type);
3144 width = QSvgUtils::convertToPixels(width,
true, type);
3148 if (!heightStr.isEmpty()) {
3149 height = QSvgUtils::parseLength(heightStr, &type);
3151 height = QSvgUtils::convertToPixels(height,
false, type);
3155 auto viewBoxResult = parseViewBox(viewBoxStr);
3156 if (viewBoxResult) {
3157 node->setViewBox(*viewBoxResult);
3158 }
else if (width && height) {
3160 width = QSvgUtils::convertToPixels(width,
false, type);
3161 height = QSvgUtils::convertToPixels(height,
false, type);
3163 node->setViewBox(QRectF(0, 0, width, height));
3171 const QXmlStreamAttributes &attributes,
3174 Q_UNUSED(attributes);
3175 QSvgSwitch *node =
new QSvgSwitch(parent);
3180 const QXmlStreamAttributes &attributes,
3181 QSvgHandler *handler)
3183 const QStringView x = attributes.value(QLatin1String(
"x"));
3184 const QStringView y = attributes.value(QLatin1String(
"y"));
3185 const QStringView width = attributes.value(QLatin1String(
"width"));
3186 const QStringView height = attributes.value(QLatin1String(
"height"));
3187 const QStringView patternUnits = attributes.value(QLatin1String(
"patternUnits"));
3188 const QStringView patternContentUnits = attributes.value(QLatin1String(
"patternContentUnits"));
3189 const QStringView patternTransform = attributes.value(QLatin1String(
"patternTransform"));
3191 QtSvg::
UnitTypes nPatternUnits = patternUnits.contains(QLatin1String(
"userSpaceOnUse")) ?
3194 QtSvg::
UnitTypes nPatternContentUnits = patternContentUnits.contains(QLatin1String(
"objectBoundingBox")) ?
3197 const QStringView viewBoxStr = attributes.value(QLatin1String(
"viewBox"));
3202 qreal nx = QSvgUtils::parseLength(x, &type, &ok);
3203 nx = QSvgUtils::convertToPixels(nx,
true, type);
3207 nx = (nx / 100.) * handler->document()->viewBox().width();
3211 qreal ny = QSvgUtils::parseLength(y, &type, &ok);
3212 ny = QSvgUtils::convertToPixels(ny,
true, type);
3216 ny = (ny / 100.) * handler->document()->viewBox().height();
3220 qreal nwidth = QSvgUtils::parseLength(width, &type, &ok);
3221 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
3225 nwidth = (nwidth / 100.) * handler->document()->viewBox().width();
3227 nwidth = nwidth / 100.;
3229 qreal nheight = QSvgUtils::parseLength(height, &type, &ok);
3230 nheight = QSvgUtils::convertToPixels(nheight,
true, type);
3234 nheight = (nheight / 100.) * handler->document()->viewBox().height();
3236 nheight = nheight / 100.;
3239 auto viewBoxResult = parseViewBox(viewBoxStr);
3240 if (viewBoxResult) {
3241 if (viewBoxResult->width() > 0 && viewBoxResult->height() > 0)
3242 viewBox = *viewBoxResult;
3246 if (!patternTransform.isEmpty())
3247 matrix = parseTransformationMatrix(patternTransform);
3249 QRectF bounds(nx, ny, nwidth, nheight);
3250 if (bounds.isEmpty())
3253 QSvgRectF patternRectF(bounds, nPatternUnits, nPatternUnits, nPatternUnits, nPatternUnits);
3254 QSvgPattern *node =
new QSvgPattern(parent, patternRectF, viewBox, nPatternContentUnits, matrix);
3257 QSvgPaintServerSharedPtr prop = std::make_shared<QSvgPatternPaint>(node);
3258 handler->document()->addPaintServer(
std::move(prop), someId(attributes));
3264 const QXmlStreamAttributes &,
3267 if (parent->type() != QSvgNode::Textarea)
3269 static_cast<QSvgText*>(parent)->addLineBreak();
3274 const QXmlStreamAttributes &attributes,
3277 const QStringView x = attributes.value(QLatin1String(
"x"));
3278 const QStringView y = attributes.value(QLatin1String(
"y"));
3281 qreal nx = QSvgUtils::parseLength(x, &type);
3282 nx = QSvgUtils::convertToPixels(nx,
true, type);
3283 qreal ny = QSvgUtils::parseLength(y, &type);
3284 ny = QSvgUtils::convertToPixels(ny,
true, type);
3286 QSvgNode *text =
new QSvgText(parent, QPointF(nx, ny));
3291 const QXmlStreamAttributes &attributes,
3292 QSvgHandler *handler)
3294 QSvgText *node =
static_cast<QSvgText *>(createTextNode(parent, attributes, handler));
3297 qreal width = QSvgUtils::parseLength(attributes.value(QLatin1String(
"width")), &type);
3298 qreal height = QSvgUtils::parseLength(attributes.value(QLatin1String(
"height")), &type);
3299 node->setTextArea(QSizeF(width, height));
3305 const QXmlStreamAttributes &,
3308 return new QSvgTspan(parent);
3312 const QXmlStreamAttributes &attributes,
3313 QSvgHandler *handler)
3315 QStringView linkId = attributes.value(QLatin1String(
"xlink:href"));
3316 const QStringView xStr = attributes.value(QLatin1String(
"x"));
3317 const QStringView yStr = attributes.value(QLatin1String(
"y"));
3318 QSvgStructureNode *group =
nullptr;
3320 if (linkId.isEmpty())
3321 linkId = attributes.value(QLatin1String(
"href"));
3322 QString linkIdStr = idFromIRI(linkId).toString();
3324 switch (parent->type()) {
3326 case QSvgNode::Defs:
3327 case QSvgNode::Group:
3328 case QSvgNode::Switch:
3329 case QSvgNode::Mask:
3330 case QSvgNode::Symbol:
3331 case QSvgNode::Marker:
3332 case QSvgNode::Pattern:
3333 group =
static_cast<QSvgStructureNode*>(parent);
3341 if (!xStr.isNull() || !yStr.isNull()) {
3343 qreal nx = QSvgUtils::parseLength(xStr, &type);
3344 nx = QSvgUtils::convertToPixels(nx,
true, type);
3346 qreal ny = QSvgUtils::parseLength(yStr, &type);
3347 ny = QSvgUtils::convertToPixels(ny,
true, type);
3348 pt = QPointF(nx, ny);
3351 QSvgNode *link = handler->document()->namedNode(linkIdStr);
3353 if (parent->isDescendantOf(link))
3354 qCWarning(lcSvgHandler,
"link %ls is recursive!",
qUtf16Printable(linkIdStr));
3356 return new QSvgUse(pt, parent, link);
3360 return new QSvgUse(pt, parent, linkIdStr);
3363 qCWarning(lcSvgHandler,
"<use> element %ls in wrong context!",
qUtf16Printable(linkIdStr));
3368 const QXmlStreamAttributes &attributes,
3371 Q_UNUSED(parent); Q_UNUSED(attributes);
3375typedef QSvgNode *(*FactoryMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3382 QStringView ref = name.mid(1);
3383 switch (name.at(0).unicode()) {
3385 if (ref == QLatin1String(
"efs"))
return createDefsNode;
3391 if (ref.isEmpty())
return createGNode;
3398 if (ref == QLatin1String(
"vg"))
return createSvgNode;
3399 if (ref == QLatin1String(
"witch"))
return createSwitchNode;
3417 QStringView ref = name.mid(1);
3418 switch (name.at(0).unicode()) {
3420 if (ref == QLatin1String(
"ircle"))
return createCircleNode;
3423 if (ref == QLatin1String(
"llipse"))
return createEllipseNode;
3426 if (ref == QLatin1String(
"mage"))
return createImageNode;
3429 if (ref == QLatin1String(
"ine"))
return createLineNode;
3432 if (ref == QLatin1String(
"ath"))
return createPathNode;
3433 if (ref == QLatin1String(
"olygon"))
return createPolygonNode;
3434 if (ref == QLatin1String(
"olyline"))
return createPolylineNode;
3437 if (ref == QLatin1String(
"ect"))
return createRectNode;
3440 if (ref == QLatin1String(
"ext"))
return createTextNode;
3441 if (ref == QLatin1String(
"extArea"))
return createTextAreaNode;
3442 if (ref == QLatin1String(
"span"))
return createTspanNode;
3445 if (ref == QLatin1String(
"se"))
return createUseNode;
3448 if (ref == QLatin1String(
"ideo"))
return createVideoNode;
3464 if (!name.startsWith(QLatin1String(
"fe")))
3467 if (name == QLatin1String(
"feMerge"))
return createFeMergeNode;
3468 if (name == QLatin1String(
"feColorMatrix"))
return createFeColorMatrixNode;
3469 if (name == QLatin1String(
"feGaussianBlur"))
return createFeGaussianBlurNode;
3470 if (name == QLatin1String(
"feOffset"))
return createFeOffsetNode;
3471 if (name == QLatin1String(
"feMergeNode"))
return createFeMergeNodeNode;
3472 if (name == QLatin1String(
"feComposite"))
return createFeCompositeNode;
3473 if (name == QLatin1String(
"feFlood"))
return createFeFloodNode;
3474 if (name == QLatin1String(
"feBlend"))
return createFeBlendNode;
3476 static const QStringList unsupportedFilters = {
3493 if (unsupportedFilters.contains(name))
3494 return createFeUnsupportedNode;
3499typedef QSvgNode *(*AnimationMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3506 QStringView ref = name.mid(1);
3508 switch (name.at(0).unicode()) {
3510 if (ref == QLatin1String(
"nimate"))
return createAnimateNode;
3511 if (ref == QLatin1String(
"nimateColor"))
return createAnimateColorNode;
3512 if (ref == QLatin1String(
"nimateMotion"))
return createAnimateMotionNode;
3513 if (ref == QLatin1String(
"nimateTransform"))
return createAnimateTransformNode;
3522typedef bool (*
ParseMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3529 QStringView ref = name.mid(1);
3530 switch (name.at(0).unicode()) {
3532 if (ref.isEmpty())
return parseAnchorNode;
3533 if (ref == QLatin1String(
"udio"))
return parseAudioNode;
3536 if (ref == QLatin1String(
"iscard"))
return parseDiscardNode;
3539 if (ref == QLatin1String(
"oreignObject"))
return parseForeignObjectNode;
3542 if (ref == QLatin1String(
"andler"))
return parseHandlerNode;
3543 if (ref == QLatin1String(
"kern"))
return parseHkernNode;
3546 if (ref == QLatin1String(
"etadata"))
return parseMetadataNode;
3547 if (ref == QLatin1String(
"path"))
return parseMpathNode;
3552 if (ref == QLatin1String(
"refetch"))
return parsePrefetchNode;
3555 if (ref == QLatin1String(
"cript"))
return parseScriptNode;
3556 if (ref == QLatin1String(
"et"))
return parseSetNode;
3557 if (ref == QLatin1String(
"tyle"))
return parseStyleNode;
3560 if (ref == QLatin1String(
"break"))
return parseTbreakNode;
3576 QStringView ref = name.mid(1);
3577 switch (name.at(0).unicode()) {
3579 if (ref == QLatin1String(
"ont"))
return createFontNode;
3588 const QXmlStreamAttributes &,
3596 QStringView ref = name.mid(1);
3597 switch (name.at(0).unicode()) {
3624 QStringView ref = name.sliced(1);
3625 switch (name.at(0).unicode()) {
3627 if (ref == QLatin1String(
"inearGradient"))
return createLinearGradientNode;
3630 if (ref == QLatin1String(
"adialGradient"))
return createRadialGradientNode;
3633 if (ref == QLatin1String(
"olidColor"))
return createSolidColorNode;
3642 const QXmlStreamAttributes &,
3650 QStringView ref = name.sliced(1);
3651 switch (name.at(0).unicode()) {
3653 if (ref == QLatin1String(
"top"))
return parseStopNode;
3661QSvgHandler::QSvgHandler(QIODevice *device, QtSvg::Options options,
3662 QtSvg::AnimatorType type)
3663 : xml(
new QXmlStreamReader(device))
3664 , m_ownsReader(
true)
3665 , m_options(options)
3666 , m_animatorType(type)
3671QSvgHandler::QSvgHandler(
const QByteArray &data, QtSvg::Options options,
3672 QtSvg::AnimatorType type)
3673 : xml(
new QXmlStreamReader(data))
3674 , m_ownsReader(
true)
3675 , m_options(options)
3676 , m_animatorType(type)
3681QSvgHandler::QSvgHandler(QXmlStreamReader *
const reader, QtSvg::Options options,
3682 QtSvg::AnimatorType type)
3684 , m_ownsReader(
false)
3685 , m_options(options)
3686 , m_animatorType(type)
3691void QSvgHandler::init()
3695 m_defaultCoords = QSvgUtils::LT_PX;
3696 m_defaultPen = QPen(Qt::black, 1, Qt::SolidLine, Qt::FlatCap, Qt::SvgMiterJoin);
3697 m_defaultPen.setMiterLimit(4);
3703 const QSvgFillStyle *fillStyle =
static_cast<
const QSvgFillStyle*>
3704 (node->styleProperty(QSvgStyleProperty::Fill));
3705 if (fillStyle && fillStyle->paintServer()
3706 && fillStyle->paintServer()->type() == QSvgPaintServer::Type::Pattern) {
3707 QSvgPatternPaint *patternStyle =
static_cast<QSvgPatternPaint *>(fillStyle->paintServer());
3708 if (linkable.contains(patternStyle->patternNode()))
3712 const QSvgStrokeStyle *strokeStyle =
static_cast<
const QSvgStrokeStyle*>
3713 (node->styleProperty(QSvgStyleProperty::Stroke));
3714 if (strokeStyle && strokeStyle->paintServer()
3715 && strokeStyle->paintServer()->type() == QSvgPaintServer::Type::Pattern) {
3716 QSvgPatternPaint *patternStyle =
static_cast<QSvgPatternPaint *>(strokeStyle->paintServer());
3717 if (linkable.contains(patternStyle->patternNode()))
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3746 QList<
const QSvgNode *> linkable;
3747 using NodeState =
std::pair<
const QSvgNode *,
bool>;
3748 QStack<NodeState> nodes;
3749 nodes.push({n,
false});
3752 auto current = nodes.pop();
3753 if (current.second) {
3754 Q_ASSERT(!linkable.isEmpty() && current.first == linkable.back());
3755 linkable.pop_back();
3759 switch (current.first->type()) {
3761 case QSvgNode::Group:
3762 case QSvgNode::Defs:
3763 case QSvgNode::Pattern:
3765 if (current.first->type() == QSvgNode::Pattern) {
3766 linkable.append(current.first);
3767 nodes.push({current.first,
true});
3769 auto *g =
static_cast<
const QSvgStructureNode*>(current.first);
3770 for (
auto it = g->renderers().crbegin(); it != g->renderers().crend(); it++)
3771 nodes.push({it->get(),
false});
3776 if (linkable.contains(current.first))
3778 auto *u =
static_cast<
const QSvgUse*>(current.first);
3779 auto *target = u->link();
3782 nodes.push({u,
true});
3783 nodes.push({target,
false});
3787 case QSvgNode::Rect:
3788 case QSvgNode::Ellipse:
3789 case QSvgNode::Circle:
3790 case QSvgNode::Line:
3791 case QSvgNode::Path:
3792 case QSvgNode::Polygon:
3793 case QSvgNode::Polyline:
3794 case QSvgNode::Tspan:
3795 if (detectPatternCycles(current.first, linkable))
3801 }
while (!nodes.isEmpty());
3808 qCWarning(lcSvgHandler,
"Cycles detected in SVG, document discarded.");
3812void QSvgHandler::parse()
3814 xml->setNamespaceProcessing(
false);
3815#ifndef QT_NO_CSSPARSER
3819 while (!xml->atEnd() && !done) {
3820 switch (xml->readNext()) {
3821 case QXmlStreamReader::StartElement:
3830 if (!startElement(xml->name(), xml->attributes())) {
3836 case QXmlStreamReader::EndElement:
3837 done = endElement(xml->name());
3839 case QXmlStreamReader::Characters:
3840 characters(xml->text());
3842 case QXmlStreamReader::ProcessingInstruction:
3843 processingInstruction(xml->processingInstructionTarget(), xml->processingInstructionData());
3853 resolvePaintServers();
3855 if (detectCyclesAndWarn(m_doc)) {
3861bool QSvgHandler::startElement(
const QStringView localName,
3862 const QXmlStreamAttributes &attributes)
3864 QSvgNode *node =
nullptr;
3869
3870
3871 const QStringView xmlSpace(attributes.value(QLatin1String(
"xml:space")));
3872 if (xmlSpace.isNull()) {
3874 m_whitespaceMode.push(m_whitespaceMode.isEmpty() ? QSvgText::Default : m_whitespaceMode.top());
3875 }
else if (xmlSpace == QLatin1String(
"preserve")) {
3876 m_whitespaceMode.push(QSvgText::Preserve);
3877 }
else if (xmlSpace == QLatin1String(
"default")) {
3878 m_whitespaceMode.push(QSvgText::Default);
3880 const QByteArray msg =
'"' + xmlSpace.toLocal8Bit()
3881 +
"\" is an invalid value for attribute xml:space. "
3882 "Valid values are \"preserve\" and \"default\".";
3883 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3884 m_whitespaceMode.push(QSvgText::Default);
3887 if (!m_doc && localName != QLatin1String(
"svg"))
3890 if (m_doc && localName == QLatin1String(
"svg")) {
3891 m_skipNodes.push(Doc);
3892 qCWarning(lcSvgHandler) <<
"Skipping a nested svg element, because "
3893 "SVG Document must not contain nested svg elements in Svg Tiny 1.2";
3896 if (!m_skipNodes.isEmpty() && m_skipNodes.top() == Doc)
3899 if (FactoryMethod method = findGroupFactory(localName, options())) {
3902 node = method(
nullptr, attributes,
this);
3904 Q_ASSERT(node->type() == QSvgNode::Doc);
3905 m_doc =
static_cast<QSvgDocument*>(node);
3908 switch (m_nodes.top()->type()) {
3910 case QSvgNode::Group:
3911 case QSvgNode::Defs:
3912 case QSvgNode::Switch:
3913 case QSvgNode::Mask:
3914 case QSvgNode::Symbol:
3915 case QSvgNode::Marker:
3916 case QSvgNode::Pattern:
3918 node = method(m_nodes.top(), attributes,
this);
3920 QSvgStructureNode *group =
3921 static_cast<QSvgStructureNode*>(m_nodes.top());
3922 group->addChild(std::unique_ptr<QSvgNode>(node), someId(attributes));
3927 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
3928 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3934 parseCoreNode(node, attributes);
3935 parseStyle(node, attributes,
this);
3936 if (node->type() == QSvgNode::Filter)
3937 m_toBeResolved.append(node);
3939 }
else if (FactoryMethod method = findGraphicsFactory(localName, options())) {
3941 Q_ASSERT(!m_nodes.isEmpty());
3942 switch (m_nodes.top()->type()) {
3944 case QSvgNode::Group:
3945 case QSvgNode::Defs:
3946 case QSvgNode::Switch:
3947 case QSvgNode::Mask:
3948 case QSvgNode::Symbol:
3949 case QSvgNode::Marker:
3950 case QSvgNode::Pattern:
3952 if (localName == QLatin1String(
"tspan")) {
3953 const QByteArray msg = QByteArrayLiteral(
"\'tspan\' element in wrong context.");
3954 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3957 node = method(m_nodes.top(), attributes,
this);
3959 QSvgStructureNode *group =
3960 static_cast<QSvgStructureNode*>(m_nodes.top());
3961 group->addChild(std::unique_ptr<QSvgNode>(node), someId(attributes));
3965 case QSvgNode::Text:
3966 case QSvgNode::Textarea:
3967 if (localName == QLatin1String(
"tspan")) {
3968 node = method(m_nodes.top(), attributes,
this);
3970 static_cast<QSvgText *>(m_nodes.top())->addTspan(
static_cast<QSvgTspan *>(node));
3973 const QByteArray msg = QByteArrayLiteral(
"\'text\' or \'textArea\' element contains invalid element type.");
3974 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3978 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
3979 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3984 parseCoreNode(node, attributes);
3985 parseStyle(node, attributes,
this);
3986 if (node->type() == QSvgNode::Text || node->type() == QSvgNode::Textarea) {
3987 static_cast<QSvgText *>(node)->setWhitespaceMode(m_whitespaceMode.top());
3988 }
else if (node->type() == QSvgNode::Tspan) {
3989 static_cast<QSvgTspan *>(node)->setWhitespaceMode(m_whitespaceMode.top());
3990 }
else if (node->type() == QSvgNode::Use) {
3991 auto useNode =
static_cast<QSvgUse *>(node);
3992 if (!useNode->isResolved())
3993 m_toBeResolved.append(useNode);
3996 }
else if (FactoryMethod method = findFilterFactory(localName, options())) {
3998 Q_ASSERT(!m_nodes.isEmpty());
3999 if (m_nodes.top()->type() == QSvgNode::Filter ||
4000 (m_nodes.top()->type() == QSvgNode::FeMerge && localName == QLatin1String(
"feMergeNode"))) {
4001 node = method(m_nodes.top(), attributes,
this);
4003 QSvgStructureNode *container =
4004 static_cast<QSvgStructureNode*>(m_nodes.top());
4005 container->addChild(std::unique_ptr<QSvgNode>(node), someId(attributes));
4008 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
4009 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
4011 }
else if (AnimationMethod method = findAnimationFactory(localName, options())) {
4012 Q_ASSERT(!m_nodes.isEmpty());
4013 node = method(m_nodes.top(), attributes,
this);
4015 QSvgAnimateNode *anim =
static_cast<QSvgAnimateNode *>(node);
4016 if (anim->linkId().isEmpty())
4017 m_doc->animator()->appendAnimation(m_nodes.top(), anim);
4018 else if (m_doc->namedNode(anim->linkId()))
4019 m_doc->animator()->appendAnimation(m_doc->namedNode(anim->linkId()), anim);
4021 m_toBeResolved.append(anim);
4023 }
else if (ParseMethod method = findUtilFactory(localName, options())) {
4024 Q_ASSERT(!m_nodes.isEmpty());
4025 if (!method(m_nodes.top(), attributes,
this))
4026 qCWarning(lcSvgHandler,
"%s", msgProblemParsing(localName, xml).constData());
4027 }
else if (StyleFactoryMethod method = findStyleFactoryMethod(localName)) {
4028 QSvgStyleProperty *prop = method(attributes,
this);
4031 m_nodes.top()->appendStyleProperty(std::unique_ptr<QSvgStyleProperty>(prop));
4033 const QByteArray msg = QByteArrayLiteral(
"Could not parse node: ") + localName.toLocal8Bit();
4034 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
4036 }
else if (PaintServerFactoryMethod method = findPaintServerFactoryMethod(localName)) {
4037 QSvgPaintServerSharedPtr paintServer = method(attributes,
this);
4039 m_paintServer = paintServer;
4040 m_doc->addPaintServer(std::move(paintServer), someId(attributes));
4042 const QByteArray msg = QByteArrayLiteral(
"Could not parse node: ") + localName.toLocal8Bit();
4043 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
4045 }
else if (StyleParseMethod method = findStyleUtilFactoryMethod(localName)) {
4047 if (!method(m_style, attributes,
this))
4048 qCWarning(lcSvgHandler,
"%s", msgProblemParsing(localName, xml).constData());
4050 }
else if (PaintServerParseMethod method = findPaintServerUtilFactoryMethod(localName)) {
4051 if (m_paintServer) {
4052 if (!method(m_paintServer.get(), attributes,
this))
4053 qCWarning(lcSvgHandler,
"%s", msgProblemParsing(localName, xml).constData());
4056 qCDebug(lcSvgHandler) <<
"Skipping unknown element" << localName;
4057 m_skipNodes.push(Unknown);
4063 m_skipNodes.push(Graphics);
4066 m_skipNodes.push(Style);
4071bool QSvgHandler::endElement(
const QStringView localName)
4073 CurrentNode node = m_skipNodes.top();
4075 if (node == Doc && localName != QLatin1String(
"svg"))
4079 m_whitespaceMode.pop();
4083 if (node == Unknown)
4086#ifdef QT_NO_CSSPARSER
4087 Q_UNUSED(localName);
4089 if (m_inStyle && localName == QLatin1String(
"style"))
4093 if (node == Graphics)
4095 else if (m_style && !m_skipNodes.isEmpty() && m_skipNodes.top() != Style)
4098 return ((localName == QLatin1String(
"svg")) && (node != Doc));
4101void QSvgHandler::resolvePaintServers()
4103 for (QSvgStyleProperty *prop : std::as_const(m_unresolvedStyles)) {
4104 if (prop->type() == QSvgStyleProperty::Fill) {
4105 QSvgFillStyle *fill =
static_cast<QSvgFillStyle *>(prop);
4106 QString id = fill->paintStyleId();
4107 QSvgPaintServerSharedPtr paintServer = m_doc->paintServer(id);
4109 fill->setPaintServer(std::move(paintServer));
4111 qCWarning(lcSvgHandler,
"%s", msgCouldNotResolveProperty(id, xml).constData());
4112 fill->setBrush(Qt::NoBrush);
4114 }
else if (prop->type() == QSvgStyleProperty::Stroke) {
4115 QSvgStrokeStyle *stroke =
static_cast<QSvgStrokeStyle *>(prop);
4116 QString id = stroke->paintStyleId();
4117 QSvgPaintServerSharedPtr paintServer = m_doc->paintServer(id);
4119 stroke->setPaintServer(std::move(paintServer));
4121 qCWarning(lcSvgHandler,
"%s", msgCouldNotResolveProperty(id, xml).constData());
4122 stroke->setStroke(Qt::NoBrush);
4127 m_unresolvedStyles.clear();
4130void QSvgHandler::resolveNodes()
4132 for (QSvgNode *node : std::as_const(m_toBeResolved)) {
4133 if (node->type() == QSvgNode::Use) {
4134 QSvgUse *useNode =
static_cast<QSvgUse *>(node);
4135 const auto parent = useNode->parent();
4139 QSvgNode::Type t = parent->type();
4140 if (t != QSvgNode::Doc && t != QSvgNode::Defs && t != QSvgNode::Group && t != QSvgNode::Switch)
4143 QSvgNode *link = m_doc->namedNode(useNode->linkId());
4145 qCWarning(lcSvgHandler,
"link #%s is undefined!",
qPrintable(useNode->linkId()));
4149 if (useNode->parent()->isDescendantOf(link))
4150 qCWarning(lcSvgHandler,
"link #%s is recursive!",
qPrintable(useNode->linkId()));
4152 useNode->setLink(link);
4153 }
else if (node->type() == QSvgNode::Filter) {
4154 QSvgFilterContainer *filter =
static_cast<QSvgFilterContainer *>(node);
4155 for (
auto &renderer : filter->renderers()) {
4156 const QSvgFeFilterPrimitive *primitive = QSvgFeFilterPrimitive::castToFilterPrimitive(renderer.get());
4157 if (!primitive || primitive->type() == QSvgNode::FeUnsupported) {
4158 filter->setSupported(
false);
4162 }
else if (node->type() == QSvgNode::AnimateTransform || node->type() == QSvgNode::AnimateColor) {
4163 QSvgAnimateNode *anim =
static_cast<QSvgAnimateNode *>(node);
4164 QSvgNode *targetNode = m_doc->namedNode(anim->linkId());
4166 m_doc->animator()->appendAnimation(targetNode, anim);
4168 qCWarning(lcSvgHandler,
"Cannot find target for link #%s!",
4174 m_toBeResolved.clear();
4177bool QSvgHandler::characters(
const QStringView str)
4179#ifndef QT_NO_CSSPARSER
4181 m_cssHandler.parseStyleSheet(str);
4185 if (m_skipNodes.isEmpty() || m_skipNodes.top() == Unknown || m_nodes.isEmpty())
4188 if (m_nodes.top()->type() == QSvgNode::Text || m_nodes.top()->type() == QSvgNode::Textarea) {
4189 static_cast<QSvgText*>(m_nodes.top())->addText(str);
4190 }
else if (m_nodes.top()->type() == QSvgNode::Tspan) {
4191 static_cast<QSvgTspan*>(m_nodes.top())->addText(str);
4197QIODevice *QSvgHandler::device()
const
4199 return xml->device();
4202QSvgDocument *QSvgHandler::document()
const
4207QSvgUtils::LengthType QSvgHandler::defaultCoordinateSystem()
const
4209 return m_defaultCoords;
4212void QSvgHandler::setDefaultCoordinateSystem(QSvgUtils::LengthType type)
4214 m_defaultCoords = type;
4217void QSvgHandler::pushColor(
const QColor &color)
4219 m_colorStack.push(color);
4220 m_colorTagCount.push(1);
4223void QSvgHandler::pushColorCopy()
4225 if (m_colorTagCount.size())
4226 ++m_colorTagCount.top();
4228 pushColor(Qt::black);
4231void QSvgHandler::popColor()
4233 if (m_colorTagCount.size()) {
4234 if (!--m_colorTagCount.top()) {
4236 m_colorTagCount.pop();
4241QColor QSvgHandler::currentColor()
const
4243 if (!m_colorStack.isEmpty())
4244 return m_colorStack.top();
4246 return QColor(0, 0, 0);
4249void QSvgHandler::pushUnresolvedStyle(QSvgStyleProperty *prop)
4251 m_unresolvedStyles.append(prop);
4254#ifndef QT_NO_CSSPARSER
4256void QSvgHandler::setInStyle(
bool b)
4261bool QSvgHandler::inStyle()
const
4266QSvgCssHandler &QSvgHandler::cssHandler()
4268 return m_cssHandler;
4273bool QSvgHandler::processingInstruction(
const QStringView target,
const QStringView data)
4275#ifdef QT_NO_CSSPARSER
4279 if (target == QLatin1String(
"xml-stylesheet")) {
4280 static const QRegularExpression rx(
QStringLiteral(
"type=\\\"(.+)\\\""),
4281 QRegularExpression::InvertedGreedinessOption);
4282 QRegularExpressionMatchIterator iter = rx.globalMatchView(data);
4284 while (iter.hasNext()) {
4285 QRegularExpressionMatch match = iter.next();
4286 QString type = match.captured(1);
4287 if (type.toLower() == QLatin1String(
"text/css")) {
4293 static const QRegularExpression rx(
QStringLiteral(
"href=\\\"(.+)\\\""),
4294 QRegularExpression::InvertedGreedinessOption);
4295 QRegularExpressionMatch match = rx.matchView(data);
4296 QString addr = match.captured(1);
4300 QFile file(fi.absoluteFilePath());
4301 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
4304 QByteArray cssData = file.readAll();
4305 QString css = QString::fromUtf8(cssData);
4306 m_cssHandler.parseStyleSheet(css);
4316void QSvgHandler::setAnimPeriod(
int start,
int end)
4319 m_animEnd = qMax(end, m_animEnd);
4322int QSvgHandler::animationDuration()
const
4327QSvgHandler::~QSvgHandler()
The QPolygonF class provides a list of points using floating point precision.
void parseNumbersArray(QStringView *str, QVarLengthArray< qreal, 8 > &points, const char *pattern)
qreal toDouble(QStringView *str)
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 bool parseFontFaceNameNode(QSvgStyleProperty *parent, 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 *)
bool(* StyleParseMethod)(QSvgStyleProperty *, const QXmlStreamAttributes &, QSvgHandler *)
static QSvgNode::DisplayMode displayStringToEnum(const QStringView str)
static bool parseStyleNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static std::optional< QStringView > getAttributeId(const QStringView &attribute)
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)
static bool parseFontFaceNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static void parseFilterAttributes(const QXmlStreamAttributes &attributes, QString *inString, QString *outString, QSvgRectF *rect)
static QSvgNode * createPolyNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, bool createLine)
static void parseTransform(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static QSvgNode * createFeMergeNodeNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool detectPatternCycles(const QSvgNode *node, QList< const QSvgNode * > &linkable)
static QSvgNode * createAnimateColorNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
void setAlpha(QStringView opacity, QColor *color)
static void parseColor(QSvgNode *, const QSvgAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createCircleNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
bool qsvg_get_hex_rgb(const QChar *str, int len, QRgb *rgb)
static void parseNumberTriplet(QList< qreal > &values, QStringView *s)
static std::optional< QRectF > parseViewBox(QStringView str)
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 StyleParseMethod findStyleUtilFactoryMethod(const QStringView name)
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 QSvgPaintServerSharedPtr createRadialGradientNode(const QXmlStreamAttributes &attributes, QSvgHandler *handler)
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 parseCoreNode(QSvgNode *node, const QXmlStreamAttributes &attributes)
static bool constructColor(QStringView colorStr, QStringView opacity, QColor &color, QSvgHandler *handler)
static bool parseHkernNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void generateKeyFrames(QList< qreal > &keyFrames, uint count)
static void parsePen(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createSvgNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static std::optional< Qt::Alignment > parseTextAnchor(QStringView s)
static QSvgStyleProperty * createFontNode(const QXmlStreamAttributes &attributes, QSvgHandler *handler)
QList< qreal > parseNumbersList(QStringView *str)
static bool parseForeignObjectNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseTbreakNode(QSvgNode *parent, const QXmlStreamAttributes &, QSvgHandler *)
static QSvgNode * createFeUnsupportedNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QList< QStringView > splitWithDelimiter(QStringView delimitedList)
static void parseFont(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createFeOffsetNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createFeFloodNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createMarkerNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createFeColorMatrixNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createFeGaussianBlurNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgPaintServerSharedPtr paintServerFromUrl(QSvgDocument *doc, QStringView url)
static QSvgNode * createPolylineNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgPaintServerSharedPtr createSolidColorNode(const QXmlStreamAttributes &attributes, QSvgHandler *handler)
bool(* ParseMethod)(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *)
static QStringView idFromIRI(QStringView iri)
static bool parseMissingGlyphNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parsePrefetchNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseCssAnimations(QSvgNode *node, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static int qsvg_hex2int(const char *s, bool *ok=nullptr)
static bool parseGlyphNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static int qsvg_h2i(char hex, bool *ok=nullptr)
static int qsvg_hex2int(char s, bool *ok=nullptr)
static QSvgNode * createVideoNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static 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 parseFontFaceSrcNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseAnchorNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseFontFaceUriNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static 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 StyleFactoryMethod findStyleFactoryMethod(const QStringView name)
static QPainter::CompositionMode svgToQtCompositionMode(const QStringView op)
static QByteArray msgCouldNotResolveProperty(QStringView id, const QXmlStreamReader *r)
static void parseFilterBounds(const QXmlStreamAttributes &attributes, QSvgRectF *rect)
static void parseBrush(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *handler)
static std::optional< QFont::Capitalization > parseFontVariant(const QSvgAttributes &attributes)
static QSvgNode * createAnimateMotionNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static FactoryMethod findGraphicsFactory(const QStringView name, QtSvg::Options options)
QSvgNode * createAnimateTransformNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createFeMergeNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createUseNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createFeCompositeNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseSetNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QStringView idFromFuncIRI(QStringView iri)
static QSvgNode * createFeBlendNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createFilterNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createEllipseNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createTextAreaNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
QSvgStyleProperty *(* StyleFactoryMethod)(const QXmlStreamAttributes &, QSvgHandler *)
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