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;
72 if (
const QFile *file = qobject_cast<
const QFile *>(r->device()))
73 result.append(QFile::encodeName(QDir::toNativeSeparators(file->fileName())));
75 result.append(QByteArrayLiteral(
"<input>"));
77 result.append(QByteArray::number(r->lineNumber()));
78 if (
const qint64 column = r->columnNumber()) {
80 result.append(QByteArray::number(column));
82 result.append(QByteArrayLiteral(
": "));
90 return prefixMessage(
"Problem parsing " + localName.toLocal8Bit(), r);
95 return prefixMessage(
"Could not resolve property: " + id.toLocal8Bit(), r);
100 static const QRegularExpression delimiterRE(
QStringLiteral(
"[,\\s]+"));
101 return delimitedList.split(delimiterRE, Qt::SkipEmptyParts);
106static inline int qsvg_h2i(
char hex,
bool *ok =
nullptr)
108 if (hex >=
'0' && hex <=
'9')
110 if (hex >=
'a' && hex <=
'f')
111 return hex -
'a' + 10;
112 if (hex >=
'A' && hex <=
'F')
113 return hex -
'A' + 10;
135 const size_t len = qstrlen(name);
142 }
else if (len == 9) {
146 }
else if (len == 6) {
150 }
else if (len == 3) {
157 if ((uint)r > 255 || (uint)g > 255 || (uint)b > 255 || !ok) {
161 *rgb = qRgb(r, g ,b);
170 for(
int i = 0; i < len; ++i)
171 tmp[i] = str[i].toLatin1();
173 return qsvg_get_hex_rgb(tmp, rgb);
178static inline QString
someId(
const QXmlStreamAttributes &attributes)
180 QStringView id = attributes.value(QLatin1String(
"id"));
182 id = attributes.value(QLatin1String(
"xml:id"));
183 return id.toString();
189 void setAttributes(
const QXmlStreamAttributes &attributes, QSvgHandler *handler);
231 setAttributes(xmlAttributes, handler);
236 for (
const QXmlStreamAttribute &attribute : attributes) {
237 QStringView name = attribute.qualifiedName();
240 QStringView value = attribute.value();
242 switch (name.at(0).unicode()) {
245 if (name == QLatin1String(
"color"))
247 else if (name == QLatin1String(
"color-opacity"))
248 colorOpacity = value;
249 else if (name == QLatin1String(
"comp-op"))
254 if (name == QLatin1String(
"display"))
259 if (name == QLatin1String(
"fill"))
261 else if (name == QLatin1String(
"fill-rule"))
263 else if (name == QLatin1String(
"fill-opacity"))
265 else if (name == QLatin1String(
"font-family"))
267 else if (name == QLatin1String(
"font-size"))
269 else if (name == QLatin1String(
"font-style"))
271 else if (name == QLatin1String(
"font-weight"))
273 else if (name == QLatin1String(
"font-variant"))
275 else if (name == QLatin1String(
"filter") &&
276 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
281 if (name == QLatin1String(
"id"))
282 id = value.toString();
283 else if (name == QLatin1String(
"image-rendering"))
284 imageRendering = value;
288 if (name == QLatin1String(
"mask") &&
289 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
291 if (name == QLatin1String(
"marker-start") &&
292 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
294 if (name == QLatin1String(
"marker-mid") &&
295 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
297 if (name == QLatin1String(
"marker-end") &&
298 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
303 if (name == QLatin1String(
"opacity"))
305 if (name == QLatin1String(
"offset"))
310 if (name.size() > 5 && name.mid(1, 5) == QLatin1String(
"troke")) {
311 QStringView strokeRef = name.mid(6, name.size() - 6);
312 if (strokeRef.isEmpty())
314 else if (strokeRef == QLatin1String(
"-dasharray"))
315 strokeDashArray = value;
316 else if (strokeRef == QLatin1String(
"-dashoffset"))
317 strokeDashOffset = value;
318 else if (strokeRef == QLatin1String(
"-linecap"))
319 strokeLineCap = value;
320 else if (strokeRef == QLatin1String(
"-linejoin"))
321 strokeLineJoin = value;
322 else if (strokeRef == QLatin1String(
"-miterlimit"))
323 strokeMiterLimit = value;
324 else if (strokeRef == QLatin1String(
"-opacity"))
325 strokeOpacity = value;
326 else if (strokeRef == QLatin1String(
"-width"))
328 }
else if (name == QLatin1String(
"stop-color"))
330 else if (name == QLatin1String(
"stop-opacity"))
335 if (name == QLatin1String(
"text-anchor"))
337 else if (name == QLatin1String(
"transform"))
342 if (name == QLatin1String(
"vector-effect"))
343 vectorEffect = value;
344 else if (name == QLatin1String(
"visibility"))
349 if (name == QLatin1String(
"xml:id") && id.isEmpty())
350 id = value.toString();
366 while (!str->isEmpty() && str->first().isSpace())
368 while (!str->isEmpty()
370 || str->startsWith(QLatin1Char(
'+')) || str->startsWith(QLatin1Char(
'.')))) {
372 points.append(QSvgUtils::toDouble(str));
374 while (!str->isEmpty() && str->first().isSpace())
376 if (str->startsWith(QLatin1Char(
',')))
380 while (!str->isEmpty() && str->first().isSpace())
391 while (!str.isEmpty() && str.first().isSpace())
393 while ((!str.isEmpty() && str.first() >= QLatin1Char(
'0') && str.first() <= QLatin1Char(
'9'))
394 || str.startsWith(QLatin1Char(
'-')) || str.startsWith(QLatin1Char(
'+'))
395 || str.startsWith(QLatin1Char(
'.'))) {
397 points.append(QSvgUtils::toDouble(&str));
399 while (!str.isEmpty() && str.first().isSpace())
401 if (str.startsWith(QLatin1Char(
'%')))
403 while (!str.isEmpty() && str.first().isSpace())
405 if (str.startsWith(QLatin1Char(
',')))
409 while (!str.isEmpty() && str.first().isSpace())
417
418
419
420
425 if (!iri.startsWith(QLatin1Char(
'#')))
426 return QStringView();
428 return iri.sliced(1);
432
433
434
435
436
441 if (!iri.startsWith(QLatin1StringView(
"url(")))
442 return QStringView();
446 const qsizetype closingBracePos = iri.indexOf(QLatin1Char(
')'));
447 if (closingBracePos == -1)
448 return QStringView();
450 iri = iri.first(closingBracePos);
451 return idFromIRI(iri);
455
456
457
458bool resolveColor(QStringView colorStr, QColor &color, QSvgHandler *handler)
460 QStringView colorStrTr = colorStr.trimmed();
461 if (colorStrTr.isEmpty())
464 switch(colorStrTr.at(0).unicode()) {
471 bool ok = qsvg_get_hex_rgb(colorStrTr.constData(), colorStrTr.size(), &rgb);
481 if (colorStrTr.size() >= 7 && colorStrTr.at(colorStrTr.size() - 1) == QLatin1Char(
')')
482 && colorStrTr.mid(0, 4) == QLatin1String(
"rgb(")) {
483 QStringView sv{ colorStrTr.sliced(4) };
484 QList<qreal> compo = parseNumbersList(&sv);
487 if (compo.size() == 1) {
488 compo = parsePercentageList(colorStrTr.sliced(4));
489 for (
int i = 0; i < compo.size(); ++i)
490 compo[i] *= (qreal)2.55;
493 if (compo.size() == 3) {
494 color = QColor(
int(compo[0]),
505 if (colorStrTr == QLatin1String(
"currentColor")) {
506 color = handler->currentColor();
511 if (colorStrTr == tokens::inherit)
518 color = QColor::fromString(colorStrTr);
519 return color.isValid();
525 qreal op = qBound(qreal(0.0), QSvgUtils::toDouble(opacity, &ok), qreal(1.0));
528 color->setAlphaF(op);
532 QColor &color, QSvgHandler *handler)
534 if (!resolveColor(colorStr, color, handler))
536 if (!opacity.isEmpty())
537 setAlpha(opacity, &color);
544 qreal num = QSvgUtils::parseLength(str.toString(), &type, ok);
551static bool createSvgGlyph(QSvgFont *font,
const QXmlStreamAttributes &attributes,
554 QStringView uncStr = attributes.value(QLatin1String(
"unicode"));
555 QStringView havStr = attributes.value(QLatin1String(
"horiz-adv-x"));
556 QStringView pathStr = attributes.value(QLatin1String(
"d"));
558 qreal havx = (havStr.isEmpty()) ? -1 : QSvgUtils::toDouble(havStr);
559 QPainterPath path = QSvgUtils::parsePathDataFast(pathStr).value_or(QPainterPath());
561 path.setFillRule(Qt::WindingFill);
563 if (isMissingGlyph) {
564 if (!uncStr.isEmpty())
565 qWarning(
"Ignoring missing-glyph's 'unicode' attribute");
566 return font->addMissingGlyph(path, havx);
569 if (uncStr.isEmpty()) {
570 qWarning(
"glyph does not define a non-empty 'unicode' attribute and will be ignored");
573 font->addGlyph(uncStr.toString(), path, havx);
579 QSvgHandler *handler)
582 if (constructColor(attributes.color, attributes.colorOpacity, color, handler)) {
584 handler->pushColor(color);
590 QStringView id = idFromFuncIRI(url);
591 return doc ? doc->paintServer(id) :
nullptr;
596 QSvgHandler *handler)
598 if (!attributes.fill.isEmpty() || !attributes.fillRule.isEmpty() || !attributes.fillOpacity.isEmpty()) {
599 QSvgFillStyle *prop =
new QSvgFillStyle;
602 if (!attributes.fillRule.isEmpty() && attributes.fillRule != tokens::inherit) {
603 if (attributes.fillRule == QLatin1String(
"evenodd"))
604 prop->setFillRule(Qt::OddEvenFill);
605 else if (attributes.fillRule == QLatin1String(
"nonzero"))
606 prop->setFillRule(Qt::WindingFill);
610 if (!attributes.fillOpacity.isEmpty() && attributes.fillOpacity != tokens::inherit) {
611 prop->setFillOpacity(qMin(qreal(1.0), qMax(qreal(0.0), QSvgUtils::toDouble(attributes.fillOpacity))));
615 if (!attributes.fill.isEmpty() && attributes.fill != tokens::inherit) {
616 if (attributes.fill.startsWith(QLatin1String(
"url"))) {
617 QStringView value = attributes.fill;
618 QSvgPaintServerSharedPtr paintServer = paintServerFromUrl(handler->document(), value);
620 prop->setPaintServer(std::move(paintServer));
622 QString id = idFromFuncIRI(value).toString();
623 prop->setPaintStyleId(id);
624 handler->pushUnresolvedStyle(prop);
626 }
else if (attributes.fill != QLatin1String(
"none")) {
628 if (resolveColor(attributes.fill, color, handler))
629 prop->setBrush(QBrush(color));
631 prop->setBrush(QBrush(Qt::NoBrush));
634 node->appendStyleProperty(prop);
647 while (!value.isEmpty()) {
648 if (value.first().isSpace() || value.startsWith(QLatin1Char(
','))) {
660 State state = Matrix;
661 if (value.startsWith(QLatin1Char(
'm'))) {
662 const char *ident =
"atrix";
663 for (
int i = 0; i < 5; ++i)
664 if (!value.slice(1).startsWith(QLatin1Char(ident[i])))
668 }
else if (value.startsWith(QLatin1Char(
't'))) {
669 const char *ident =
"ranslate";
670 for (
int i = 0; i < 8; ++i)
671 if (!value.slice(1).startsWith(QLatin1Char(ident[i])))
675 }
else if (value.startsWith(QLatin1Char(
'r'))) {
676 const char *ident =
"otate";
677 for (
int i = 0; i < 5; ++i)
678 if (!value.slice(1).startsWith(QLatin1Char(ident[i])))
682 }
else if (value.startsWith(QLatin1Char(
's'))) {
684 if (value.startsWith(QLatin1Char(
'c'))) {
685 const char *ident =
"ale";
686 for (
int i = 0; i < 3; ++i)
687 if (!value.slice(1).startsWith(QLatin1Char(ident[i])))
691 }
else if (value.startsWith(QLatin1Char(
'k'))) {
692 if (!value.slice(1).startsWith(QLatin1Char(
'e')))
694 if (!value.slice(1).startsWith(QLatin1Char(
'w')))
697 if (value.startsWith(QLatin1Char(
'X')))
699 else if (value.startsWith(QLatin1Char(
'Y')))
711 while (!value.isEmpty() && value.first().isSpace())
713 if (!value.startsWith(QLatin1Char(
'(')))
716 QVarLengthArray<qreal, 8> points;
718 if (!value.startsWith(QLatin1Char(
')')))
722 if(state == Matrix) {
723 if(points.size() != 6)
725 matrix = QTransform(points[0], points[1],
726 points[2], points[3],
727 points[4], points[5]) * matrix;
728 }
else if (state == Translate) {
729 if (points.size() == 1)
730 matrix.translate(points[0], 0);
731 else if (points.size() == 2)
732 matrix.translate(points[0], points[1]);
735 }
else if (state == Rotate) {
736 if(points.size() == 1) {
737 matrix.rotate(points[0]);
738 }
else if (points.size() == 3) {
739 matrix.translate(points[1], points[2]);
740 matrix.rotate(points[0]);
741 matrix.translate(-points[1], -points[2]);
745 }
else if (state == Scale) {
746 if (points.size() < 1 || points.size() > 2)
748 qreal sx = points[0];
750 if(points.size() == 2)
752 matrix.scale(sx, sy);
753 }
else if (state == SkewX) {
754 if (points.size() != 1)
756 matrix.shear(qTan(qDegreesToRadians(points[0])), 0);
757 }
else if (state == SkewY) {
758 if (points.size() != 1)
760 matrix.shear(0, qTan(qDegreesToRadians(points[0])));
769 QSvgHandler *handler)
771 if (!attributes.stroke.isEmpty() || !attributes.strokeDashArray.isEmpty() || !attributes.strokeDashOffset.isEmpty() || !attributes.strokeLineCap.isEmpty()
772 || !attributes.strokeLineJoin.isEmpty() || !attributes.strokeMiterLimit.isEmpty() || !attributes.strokeOpacity.isEmpty() || !attributes.strokeWidth.isEmpty()
773 || !attributes.vectorEffect.isEmpty()) {
775 QSvgStrokeStyle *prop =
new QSvgStrokeStyle;
778 if (!attributes.stroke.isEmpty() && attributes.stroke != tokens::inherit) {
779 if (attributes.stroke.startsWith(QLatin1String(
"url"))) {
780 QStringView value = attributes.stroke;
781 QSvgPaintServerSharedPtr paintServer = paintServerFromUrl(handler->document(), value);
783 prop->setPaintServer(std::move(paintServer));
785 QString id = idFromFuncIRI(value).toString();
786 prop->setPaintStyleId(id);
787 handler->pushUnresolvedStyle(prop);
789 }
else if (attributes.stroke != QLatin1String(
"none")) {
791 if (resolveColor(attributes.stroke, color, handler))
792 prop->setStroke(QBrush(color));
794 prop->setStroke(QBrush(Qt::NoBrush));
799 if (!attributes.strokeWidth.isEmpty() && attributes.strokeWidth != tokens::inherit) {
801 prop->setWidth(QSvgUtils::parseLength(attributes.strokeWidth, <));
805 if (!attributes.strokeDashArray.isEmpty() && attributes.strokeDashArray != tokens::inherit) {
806 if (attributes.strokeDashArray == QLatin1String(
"none")) {
807 prop->setDashArrayNone();
809 QStringView dashArray = attributes.strokeDashArray;
810 QList<qreal> dashes = parseNumbersList(&dashArray);
811 const bool allZeroes =
std::all_of(dashes.cbegin(), dashes.cend(),
812 [](qreal i) {
return qFuzzyIsNull(i); });
813 const bool hasNegative = !allZeroes &&
std::any_of(dashes.cbegin(), dashes.cend(),
814 [](qreal i) {
return i < 0.; });
817 qCWarning(lcSvgHandler) <<
"QSvgHandler: Stroke dash array "
818 "with a negative value is invalid";
821 if (allZeroes || hasNegative) {
822 prop->setDashArrayNone();
825 if ((dashes.size() & 1) != 0)
826 dashes << QList<qreal>(dashes);
827 prop->setDashArray(dashes);
833 if (!attributes.strokeLineJoin.isEmpty()) {
834 if (attributes.strokeLineJoin == QLatin1String(
"miter"))
835 prop->setLineJoin(Qt::SvgMiterJoin);
836 else if (attributes.strokeLineJoin == QLatin1String(
"round"))
837 prop->setLineJoin(Qt::RoundJoin);
838 else if (attributes.strokeLineJoin == QLatin1String(
"bevel"))
839 prop->setLineJoin(Qt::BevelJoin);
843 if (!attributes.strokeLineCap.isEmpty()) {
844 if (attributes.strokeLineCap == QLatin1String(
"butt"))
845 prop->setLineCap(Qt::FlatCap);
846 else if (attributes.strokeLineCap == QLatin1String(
"round"))
847 prop->setLineCap(Qt::RoundCap);
848 else if (attributes.strokeLineCap == QLatin1String(
"square"))
849 prop->setLineCap(Qt::SquareCap);
853 if (!attributes.strokeDashOffset.isEmpty() && attributes.strokeDashOffset != tokens::inherit)
854 prop->setDashOffset(QSvgUtils::toDouble(attributes.strokeDashOffset));
857 if (!attributes.vectorEffect.isEmpty()) {
858 if (attributes.vectorEffect == QLatin1String(
"non-scaling-stroke"))
859 prop->setVectorEffect(
true);
860 else if (attributes.vectorEffect == QLatin1String(
"none"))
861 prop->setVectorEffect(
false);
865 if (!attributes.strokeMiterLimit.isEmpty() && attributes.strokeMiterLimit != tokens::inherit)
866 prop->setMiterLimit(QSvgUtils::toDouble(attributes.strokeMiterLimit));
869 if (!attributes.strokeOpacity.isEmpty() && attributes.strokeOpacity != tokens::inherit)
870 prop->setOpacity(qMin(qreal(1.0), qMax(qreal(0.0), QSvgUtils::toDouble(attributes.strokeOpacity))));
872 node->appendStyleProperty(prop);
880{ qreal(6.9), qreal(8.3), qreal(10.0), qreal(12.0), qreal(14.4), qreal(17.3), qreal(20.7) };
886 switch (spec.at(0).unicode()) {
888 if (spec == QLatin1String(
"xx-small"))
890 if (spec == QLatin1String(
"x-small"))
892 if (spec == QLatin1String(
"x-large"))
894 if (spec == QLatin1String(
"xx-large"))
898 if (spec == QLatin1String(
"small"))
902 if (spec == QLatin1String(
"medium"))
906 if (spec == QLatin1String(
"large"))
910 if (spec == QLatin1String(
"none"))
924 if (s == tokens::normal)
925 return QFont::StyleNormal;
926 if (s == tokens::italic)
927 return QFont::StyleItalic;
928 if (s == tokens::oblique)
929 return QFont::StyleOblique;
943 if (s.isEmpty() || s == tokens::inherit)
953 qreal fs = QSvgUtils::parseLength(s, &type, &ok);
956 fs = QSvgUtils::convertToPixels(fs,
true, type);
957 return (
std::min)(fs, qreal(0xffff));
960 return sizeTable[spec];
963 Q_UNREACHABLE_RETURN(
std::nullopt);
971 if (s.isEmpty() || s == tokens::inherit)
974 if (s == tokens::normal)
975 return QFont::Normal;
976 if (s == tokens::bold)
978 if (s == tokens::bolder)
979 return QSvgFontStyle::BOLDER;
980 if (s == tokens::lighter)
981 return QSvgFontStyle::LIGHTER;
984 const int num = s.toInt(&ok);
1015 auto s = attributes.fontVariant;
1017 if (s == tokens::normal)
1018 return QFont::MixedCase;
1019 if (s == tokens::small_caps)
1020 return QFont::SmallCaps;
1022 return std::nullopt;
1033 if (s == tokens::start)
1034 return Qt::AlignLeft;
1035 if (s == tokens::middle)
1036 return Qt::AlignHCenter;
1037 if (s == tokens::end)
1038 return Qt::AlignRight;
1040 return std::nullopt;
1047 auto parsedFontSize = parseFontSize(attributes.fontSize);
1048 auto parsedFontStyle = parseFontStyle(attributes.fontStyle);
1049 auto parsedFontWeight = parseFontWeight(attributes.fontWeight);
1050 auto parsedFontVariant = parseFontVariant(attributes);
1051 auto parsedTextAnchor = parseTextAnchor(attributes.textAnchor);
1053 if (attributes.fontFamily.isEmpty() && !parsedFontSize && !parsedFontStyle &&
1054 !parsedFontWeight && !parsedFontVariant && !parsedTextAnchor)
1057 QSvgFontStyle *fontStyle =
nullptr;
1058 if (!attributes.fontFamily.isEmpty()) {
1059 QSvgDocument *doc = node->document();
1061 QSvgFont *svgFont = doc->svgFont(attributes.fontFamily.toString());
1063 fontStyle =
new QSvgFontStyle(svgFont, doc);
1067 fontStyle =
new QSvgFontStyle;
1068 if (!attributes.fontFamily.isEmpty() && attributes.fontFamily != tokens::inherit) {
1069 QStringView family = attributes.fontFamily.trimmed();
1070 if (!family.isEmpty() && (family.at(0) == QLatin1Char(
'\'') || family.at(0) == QLatin1Char(
'\"')))
1071 family = family.mid(1, family.size() - 2);
1072 fontStyle->setFamily(family.toString());
1076 fontStyle->setSize(*parsedFontSize);
1078 if (parsedFontStyle)
1079 fontStyle->setStyle(*parsedFontStyle);
1081 if (parsedFontWeight)
1082 fontStyle->setWeight(*parsedFontWeight);
1084 if (parsedFontVariant)
1085 fontStyle->setVariant(*parsedFontVariant);
1087 if (parsedTextAnchor)
1088 fontStyle->setTextAnchor(*parsedTextAnchor);
1090 node->appendStyleProperty(fontStyle);
1097 if (attributes.transform.isEmpty())
1099 QTransform matrix = parseTransformationMatrix(attributes.transform.trimmed());
1101 if (!matrix.isIdentity()) {
1102 node->appendStyleProperty(
new QSvgTransformStyle(QTransform(matrix)));
1111 QSvgNode *parent = node->parent();
1113 if (parent && (attributes.visibility.isEmpty() || attributes.visibility == tokens::inherit))
1114 node->setVisible(parent->isVisible());
1115 else if (attributes.visibility == QLatin1String(
"hidden") || attributes.visibility == QLatin1String(
"collapse")) {
1116 node->setVisible(
false);
1118 node->setVisible(
true);
1122 const QXmlStreamAttributes &attributes,
1123 QSvgHandler *handler);
1129 str = str.trimmed();
1130 if (str.endsWith(QLatin1String(
"ms"))) {
1133 }
else if (str.endsWith(QLatin1String(
"s"))) {
1136 double val = ms * QSvgUtils::toDouble(str, ok);
1138 if (val >
std::numeric_limits<
int>::min() && val <
std::numeric_limits<
int>::max())
1139 res =
static_cast<
int>(val);
1146#ifndef QT_NO_CSSPARSER
1149 const QXmlStreamAttributes &attributes,
1150 QSvgHandler *handler)
1152 QSvgCssProperties cssAnimProps(attributes);
1153 QList<QSvgAnimationProperty> parsedProperties = cssAnimProps.animations();
1155 for (
auto &property : parsedProperties) {
1156 QSvgCssAnimation *anim = handler->cssHandler().createAnimation(property.name);
1160 anim->setRunningTime(property.delay, property.duration);
1161 anim->setIterationCount(property.iteration);
1162 QSvgCssEasingPtr easing = handler->cssHandler().createEasing(property.easingFunction, property.easingValues);
1163 anim->setEasing(std::move(easing));
1165 handler->setAnimPeriod(property.delay, property.delay + property.duration);
1166 handler->document()->animator()->appendAnimation(node, anim);
1167 handler->document()->setAnimated(
true);
1172 const QXmlStreamAttributes &attributes)
1174 QSvgCssProperties cssProperties(attributes);
1175 QSvgOffsetProperty offset = cssProperties.offset();
1180 QSvgOffsetStyle *offsetStyle =
new QSvgOffsetStyle();
1181 offsetStyle->setPath(offset.path.value());
1182 offsetStyle->setRotateAngle(offset.angle);
1183 offsetStyle->setRotateType(offset.rotateType);
1184 offsetStyle->setDistance(offset.distance);
1185 node->appendStyleProperty(offsetStyle);
1190QtSvg::Options QSvgHandler::options()
const
1195QtSvg::AnimatorType QSvgHandler::animatorType()
const
1197 return m_animatorType;
1200bool QSvgHandler::trustedSourceMode()
const
1202 return m_options.testFlag(QtSvg::AssumeTrustedSource);
1207 QStringList lst = str.split(QLatin1Char(
','), Qt::SkipEmptyParts);
1212 const QXmlStreamAttributes &attributes)
1214 QStringList features;
1215 QStringList extensions;
1216 QStringList languages;
1217 QStringList formats;
1219 QStringView xmlClassStr;
1221 for (
const QXmlStreamAttribute &attribute : attributes) {
1222 QStringView name = attribute.qualifiedName();
1225 QStringView value = attribute.value();
1226 switch (name.at(0).unicode()) {
1228 if (name == QLatin1String(
"class"))
1229 xmlClassStr = value;
1232 if (name == QLatin1String(
"requiredFeatures"))
1233 features = stringToList(value.toString());
1234 else if (name == QLatin1String(
"requiredExtensions"))
1235 extensions = stringToList(value.toString());
1236 else if (name == QLatin1String(
"requiredFormats"))
1237 formats = stringToList(value.toString());
1238 else if (name == QLatin1String(
"requiredFonts"))
1239 fonts = stringToList(value.toString());
1242 if (name == QLatin1String(
"systemLanguage"))
1243 languages = stringToList(value.toString());
1250 node->setRequiredFeatures(features);
1251 node->setRequiredExtensions(extensions);
1252 node->setRequiredLanguages(languages);
1253 node->setRequiredFormats(formats);
1254 node->setRequiredFonts(fonts);
1255 node->setNodeId(someId(attributes));
1256 node->setXmlClass(xmlClassStr.toString());
1265 if (attributes.opacity.isEmpty())
1268 const QStringView value = attributes.opacity.trimmed();
1271 qreal op = value.toDouble(&ok);
1274 QSvgOpacityStyle *opacity =
new QSvgOpacityStyle(qBound(qreal(0.0), op, qreal(1.0)));
1275 node->appendStyleProperty(opacity);
1281#define NOOP qDebug()<<"Operation: "<<op<<" is not implemented"
1282 if (op == QLatin1String(
"clear")) {
1283 return QPainter::CompositionMode_Clear;
1284 }
else if (op == QLatin1String(
"src")) {
1285 return QPainter::CompositionMode_Source;
1286 }
else if (op == QLatin1String(
"dst")) {
1287 return QPainter::CompositionMode_Destination;
1288 }
else if (op == QLatin1String(
"src-over")) {
1289 return QPainter::CompositionMode_SourceOver;
1290 }
else if (op == QLatin1String(
"dst-over")) {
1291 return QPainter::CompositionMode_DestinationOver;
1292 }
else if (op == QLatin1String(
"src-in")) {
1293 return QPainter::CompositionMode_SourceIn;
1294 }
else if (op == QLatin1String(
"dst-in")) {
1295 return QPainter::CompositionMode_DestinationIn;
1296 }
else if (op == QLatin1String(
"src-out")) {
1297 return QPainter::CompositionMode_SourceOut;
1298 }
else if (op == QLatin1String(
"dst-out")) {
1299 return QPainter::CompositionMode_DestinationOut;
1300 }
else if (op == QLatin1String(
"src-atop")) {
1301 return QPainter::CompositionMode_SourceAtop;
1302 }
else if (op == QLatin1String(
"dst-atop")) {
1303 return QPainter::CompositionMode_DestinationAtop;
1304 }
else if (op == QLatin1String(
"xor")) {
1305 return QPainter::CompositionMode_Xor;
1306 }
else if (op == QLatin1String(
"plus")) {
1307 return QPainter::CompositionMode_Plus;
1308 }
else if (op == QLatin1String(
"multiply")) {
1309 return QPainter::CompositionMode_Multiply;
1310 }
else if (op == QLatin1String(
"screen")) {
1311 return QPainter::CompositionMode_Screen;
1312 }
else if (op == QLatin1String(
"overlay")) {
1313 return QPainter::CompositionMode_Overlay;
1314 }
else if (op == QLatin1String(
"darken")) {
1315 return QPainter::CompositionMode_Darken;
1316 }
else if (op == QLatin1String(
"lighten")) {
1317 return QPainter::CompositionMode_Lighten;
1318 }
else if (op == QLatin1String(
"color-dodge")) {
1319 return QPainter::CompositionMode_ColorDodge;
1320 }
else if (op == QLatin1String(
"color-burn")) {
1321 return QPainter::CompositionMode_ColorBurn;
1322 }
else if (op == QLatin1String(
"hard-light")) {
1323 return QPainter::CompositionMode_HardLight;
1324 }
else if (op == QLatin1String(
"soft-light")) {
1325 return QPainter::CompositionMode_SoftLight;
1326 }
else if (op == QLatin1String(
"difference")) {
1327 return QPainter::CompositionMode_Difference;
1328 }
else if (op == QLatin1String(
"exclusion")) {
1329 return QPainter::CompositionMode_Exclusion;
1334 return QPainter::CompositionMode_SourceOver;
1341 if (attributes.compOp.isEmpty())
1343 QStringView value = attributes.compOp.trimmed();
1345 if (!value.isEmpty()) {
1346 QSvgCompOpStyle *compop =
new QSvgCompOpStyle(svgToQtCompositionMode(value));
1347 node->appendStyleProperty(compop);
1353 if (str == QLatin1String(
"inline")) {
1354 return QSvgNode::InlineMode;
1355 }
else if (str == QLatin1String(
"block")) {
1356 return QSvgNode::BlockMode;
1357 }
else if (str == QLatin1String(
"list-item")) {
1358 return QSvgNode::ListItemMode;
1359 }
else if (str == QLatin1String(
"run-in")) {
1360 return QSvgNode::RunInMode;
1361 }
else if (str == QLatin1String(
"compact")) {
1362 return QSvgNode::CompactMode;
1363 }
else if (str == QLatin1String(
"marker")) {
1364 return QSvgNode::MarkerMode;
1365 }
else if (str == QLatin1String(
"table")) {
1366 return QSvgNode::TableMode;
1367 }
else if (str == QLatin1String(
"inline-table")) {
1368 return QSvgNode::InlineTableMode;
1369 }
else if (str == QLatin1String(
"table-row-group")) {
1370 return QSvgNode::TableRowGroupMode;
1371 }
else if (str == QLatin1String(
"table-header-group")) {
1372 return QSvgNode::TableHeaderGroupMode;
1373 }
else if (str == QLatin1String(
"table-footer-group")) {
1374 return QSvgNode::TableFooterGroupMode;
1375 }
else if (str == QLatin1String(
"table-row")) {
1376 return QSvgNode::TableRowMode;
1377 }
else if (str == QLatin1String(
"table-column-group")) {
1378 return QSvgNode::TableColumnGroupMode;
1379 }
else if (str == QLatin1String(
"table-column")) {
1380 return QSvgNode::TableColumnMode;
1381 }
else if (str == QLatin1String(
"table-cell")) {
1382 return QSvgNode::TableCellMode;
1383 }
else if (str == QLatin1String(
"table-caption")) {
1384 return QSvgNode::TableCaptionMode;
1385 }
else if (str == QLatin1String(
"none")) {
1386 return QSvgNode::NoneMode;
1387 }
else if (str == tokens::inherit) {
1388 return QSvgNode::InheritMode;
1390 return QSvgNode::BlockMode;
1397 if (attributes.display.isEmpty())
1399 QStringView displayStr = attributes.display.trimmed();
1401 if (!displayStr.isEmpty()) {
1402 node->setDisplayMode(displayStringToEnum(displayStr));
1408 if (attribute.isEmpty())
1409 return std::nullopt;
1411 return idFromFuncIRI(attribute);
1416 QSvgHandler *handler)
1421 if (
auto id = getAttributeId(attributes.mask))
1422 node->setMaskId(id->toString());
1423 if (
auto id = getAttributeId(attributes.markerStart))
1424 node->setMarkerStartId(id->toString());
1425 if (
auto id = getAttributeId(attributes.markerMid))
1426 node->setMarkerMidId(id->toString());
1427 if (
auto id = getAttributeId(attributes.markerEnd))
1428 node->setMarkerEndId(id->toString());
1429 if (
auto id = getAttributeId(attributes.filter))
1430 node->setFilterId(id->toString());
1437 if (attributes.imageRendering.isEmpty())
1440 QStringView ir = attributes.imageRendering.trimmed();
1441 QSvgQualityStyle *p =
new QSvgQualityStyle(0);
1442 if (ir == QLatin1String(
"auto"))
1443 p->setImageRendering(QSvgQualityStyle::ImageRenderingAuto);
1444 else if (ir == QLatin1String(
"optimizeSpeed"))
1445 p->setImageRendering(QSvgQualityStyle::ImageRenderingOptimizeSpeed);
1446 else if (ir == QLatin1String(
"optimizeQuality"))
1447 p->setImageRendering(QSvgQualityStyle::ImageRenderingOptimizeQuality);
1448 node->appendStyleProperty(p);
1452 const QXmlStreamAttributes &attributes,
1453 QSvgHandler *handler)
1461#ifndef QT_NO_CSSPARSER
1462 QXmlStreamAttributes cssAttributes;
1463 handler->cssHandler().styleLookup(node, cssAttributes);
1465 QStringView style = attributes.value(QLatin1String(
"style"));
1466 if (!style.isEmpty())
1467 handler->cssHandler().parseCSStoXMLAttrs(style.toString(), cssAttributes);
1468 svgAttributes.setAttributes(cssAttributes, handler);
1470 parseOffsetPath(node, cssAttributes);
1472 parseCssAnimations(node, cssAttributes, handler);
1491 const QXmlStreamAttributes &attributes,
1494 Q_UNUSED(parent); Q_UNUSED(attributes);
1499 const QXmlStreamAttributes &attributes,
1500 QSvgAnimateNode *anim,
1501 QSvgHandler *handler)
1503 const QStringView beginStr = attributes.value(QLatin1String(
"begin"));
1504 const QStringView durStr = attributes.value(QLatin1String(
"dur"));
1505 const QStringView endStr = attributes.value(QLatin1String(
"end"));
1506 const QStringView repeatStr = attributes.value(QLatin1String(
"repeatCount"));
1507 const QStringView fillStr = attributes.value(QLatin1String(
"fill"));
1508 const QStringView addtv = attributes.value(QLatin1String(
"additive"));
1509 QStringView linkId = attributes.value(QLatin1String(
"xlink:href"));
1511 if (linkId.isEmpty())
1512 linkId = attributes.value(QLatin1String(
"href"));
1514 linkId = idFromIRI(linkId);
1517 int begin = parseClockValue(beginStr, &ok);
1520 int dur = parseClockValue(durStr, &ok);
1523 int end = parseClockValue(endStr, &ok);
1526 qreal repeatCount = (repeatStr == QLatin1String(
"indefinite")) ? -1 :
1527 qMax(1.0, QSvgUtils::toDouble(repeatStr));
1529 QSvgAnimateNode::Fill fill = (fillStr == QLatin1String(
"freeze")) ? QSvgAnimateNode::Freeze :
1530 QSvgAnimateNode::Remove;
1532 QSvgAnimateNode::Additive additive = (addtv == QLatin1String(
"sum")) ? QSvgAnimateNode::Sum :
1533 QSvgAnimateNode::Replace;
1535 anim->setRunningTime(begin, dur, end, 0);
1536 anim->setRepeatCount(repeatCount);
1537 anim->setFill(fill);
1538 anim->setAdditiveType(additive);
1539 anim->setLinkId(linkId.toString());
1541 parent->document()->setAnimated(
true);
1543 handler->setAnimPeriod(begin, begin + dur);
1552 qreal spacing = 1.0f / (count - 1);
1553 for (uint i = 0; i < count; i++) {
1554 keyFrames.append(i * spacing);
1559 const QXmlStreamAttributes &attributes,
1560 QSvgHandler *handler)
1562 const QStringView fromStr = attributes.value(QLatin1String(
"from"));
1563 const QStringView toStr = attributes.value(QLatin1String(
"to"));
1564 const QStringView valuesStr = attributes.value(QLatin1String(
"values"));
1565 const QString targetStr = attributes.value(QLatin1String(
"attributeName")).toString();
1567 if (targetStr != QLatin1String(
"fill") && targetStr != QLatin1String(
"stroke"))
1570 QList<QColor> colors;
1571 if (valuesStr.isEmpty()) {
1572 QColor startColor, endColor;
1573 resolveColor(fromStr, startColor, handler);
1574 resolveColor(toStr, endColor, handler);
1576 colors.append(startColor);
1577 colors.append(endColor);
1579 for (
auto part : qTokenize(valuesStr, u';')) {
1581 resolveColor(part, color, handler);
1582 colors.append(color);
1586 QSvgAnimatedPropertyColor *prop =
static_cast<QSvgAnimatedPropertyColor *>
1587 (QSvgAbstractAnimatedProperty::createAnimatedProperty(targetStr));
1591 prop->setColors(colors);
1593 QList<qreal> keyFrames;
1594 generateKeyFrames(keyFrames, colors.size());
1595 prop->setKeyFrames(keyFrames);
1597 QSvgAnimateColor *anim =
new QSvgAnimateColor(parent);
1598 anim->appendProperty(prop);
1600 if (!parseBaseAnimate(parent, attributes, anim, handler)) {
1609 const QXmlStreamAttributes &attributes,
1612 Q_UNUSED(parent); Q_UNUSED(attributes);
1618 QList<qreal> list = parseNumbersList(s);
1620 for (
int i = 3 - list.size(); i > 0; --i)
1626 parseNumberTriplet(values, &s);
1630 const QXmlStreamAttributes &attributes,
1631 QSvgHandler *handler)
1633 const QStringView typeStr = attributes.value(QLatin1String(
"type"));
1634 const QStringView values = attributes.value(QLatin1String(
"values"));
1635 const QStringView fromStr = attributes.value(QLatin1String(
"from"));
1636 const QStringView toStr = attributes.value(QLatin1String(
"to"));
1637 const QStringView byStr = attributes.value(QLatin1String(
"by"));
1640 if (values.isEmpty()) {
1641 if (fromStr.isEmpty()) {
1642 if (!byStr.isEmpty()) {
1646 parseNumberTriplet(vals, byStr);
1652 if (!toStr.isEmpty()) {
1654 parseNumberTriplet(vals, fromStr);
1655 parseNumberTriplet(vals, toStr);
1656 }
else if (!byStr.isEmpty()) {
1658 parseNumberTriplet(vals, fromStr);
1659 parseNumberTriplet(vals, byStr);
1660 for (
int i = vals.size() - 3; i < vals.size(); ++i)
1661 vals[i] += vals[i - 3];
1667 QStringView s = values;
1668 while (!s.isEmpty()) {
1669 parseNumberTriplet(vals, &s);
1674 if (vals.size() % 3 != 0)
1678 QList<QSvgAnimatedPropertyTransform::TransformComponent> components;
1679 for (
int i = 0; i <= vals.size() - 3; i += 3) {
1680 QSvgAnimatedPropertyTransform::TransformComponent component;
1681 if (typeStr == QLatin1String(
"translate")) {
1682 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Translate;
1683 component.values.append(vals.at(i));
1684 component.values.append(vals.at(i + 1));
1685 }
else if (typeStr == QLatin1String(
"scale")) {
1686 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Scale;
1687 component.values.append(vals.at(i));
1688 component.values.append(vals.at(i + 1));
1689 }
else if (typeStr == QLatin1String(
"rotate")) {
1690 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Rotate;
1691 component.values.append(vals.at(i));
1692 component.values.append(vals.at(i + 1));
1693 component.values.append(vals.at(i + 2));
1694 }
else if (typeStr == QLatin1String(
"skewX")) {
1695 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Skew;
1696 component.values.append(vals.at(i));
1697 component.values.append(0);
1698 }
else if (typeStr == QLatin1String(
"skewY")) {
1699 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Skew;
1700 component.values.append(0);
1701 component.values.append(vals.at(i));
1705 components.append(component);
1708 QSvgAnimatedPropertyTransform *prop =
static_cast<QSvgAnimatedPropertyTransform *>
1709 (QSvgAbstractAnimatedProperty::createAnimatedProperty(QLatin1String(
"transform")));
1713 prop->appendComponents(components);
1715 prop->setTransformCount(1);
1716 QList<qreal> keyFrames;
1717 generateKeyFrames(keyFrames, vals.size() / 3);
1718 prop->setKeyFrames(keyFrames);
1720 QSvgAnimateTransform *anim =
new QSvgAnimateTransform(parent);
1721 anim->appendProperty(prop);
1723 if (!parseBaseAnimate(parent, attributes, anim, handler)) {
1732 const QXmlStreamAttributes &attributes,
1735 Q_UNUSED(parent); Q_UNUSED(attributes);
1740 const QXmlStreamAttributes &attributes,
1743 Q_UNUSED(parent); Q_UNUSED(attributes);
1748 const QXmlStreamAttributes &attributes,
1751 const QStringView cx = attributes.value(QLatin1String(
"cx"));
1752 const QStringView cy = attributes.value(QLatin1String(
"cy"));
1753 const QStringView r = attributes.value(QLatin1String(
"r"));
1754 qreal ncx = QSvgUtils::toDouble(cx);
1755 qreal ncy = QSvgUtils::toDouble(cy);
1756 qreal nr = QSvgUtils::toDouble(r);
1760 QRectF rect(ncx-nr, ncy-nr, nr*2, nr*2);
1761 QSvgNode *circle =
new QSvgCircle(parent, rect);
1766 const QXmlStreamAttributes &attributes,
1769 Q_UNUSED(attributes);
1770 QSvgDefs *defs =
new QSvgDefs(parent);
1775 const QXmlStreamAttributes &attributes,
1778 Q_UNUSED(parent); Q_UNUSED(attributes);
1783 const QXmlStreamAttributes &attributes,
1786 const QStringView cx = attributes.value(QLatin1String(
"cx"));
1787 const QStringView cy = attributes.value(QLatin1String(
"cy"));
1788 const QStringView rx = attributes.value(QLatin1String(
"rx"));
1789 const QStringView ry = attributes.value(QLatin1String(
"ry"));
1790 qreal ncx = QSvgUtils::toDouble(cx);
1791 qreal ncy = QSvgUtils::toDouble(cy);
1792 qreal nrx = QSvgUtils::toDouble(rx);
1793 qreal nry = QSvgUtils::toDouble(ry);
1795 QRectF rect(ncx-nrx, ncy-nry, nrx*2, nry*2);
1796 QSvgNode *ellipse =
new QSvgEllipse(parent, rect);
1801 QSvgHandler *handler)
1803 const QStringView hax = attributes.value(QLatin1String(
"horiz-adv-x"));
1804 QString myId = someId(attributes);
1806 qreal horizAdvX = QSvgUtils::toDouble(hax);
1808 if (!myId.isEmpty()) {
1809 QSvgDocument *doc = handler->document();
1810 QSvgFont *font = doc->svgFont(myId);
1812 font =
new QSvgFont(horizAdvX);
1813 font->setFamilyName(myId);
1814 doc->addSvgFont(font);
1816 return new QSvgFontStyle(font, doc);
1822 const QXmlStreamAttributes &attributes,
1825 if (parent->type() != QSvgStyleProperty::FONT) {
1829 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
1830 QSvgFont *font = style->svgFont();
1831 const QStringView name = attributes.value(QLatin1String(
"font-family"));
1832 const QStringView unitsPerEmStr = attributes.value(QLatin1String(
"units-per-em"));
1834 qreal unitsPerEm = QSvgUtils::toDouble(unitsPerEmStr);
1836 unitsPerEm = QSvgFont::DEFAULT_UNITS_PER_EM;
1838 if (!name.isEmpty())
1839 font->setFamilyName(name.toString());
1840 font->setUnitsPerEm(unitsPerEm);
1842 if (!font->familyName().isEmpty())
1843 if (!style->doc()->svgFont(font->familyName()))
1844 style->doc()->addSvgFont(font);
1850 const QXmlStreamAttributes &attributes,
1853 if (parent->type() != QSvgStyleProperty::FONT) {
1857 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
1858 QSvgFont *font = style->svgFont();
1859 const QStringView name = attributes.value(QLatin1String(
"name"));
1861 if (!name.isEmpty())
1862 font->setFamilyName(name.toString());
1864 if (!font->familyName().isEmpty())
1865 if (!style->doc()->svgFont(font->familyName()))
1866 style->doc()->addSvgFont(font);
1872 const QXmlStreamAttributes &attributes,
1875 Q_UNUSED(parent); Q_UNUSED(attributes);
1880 const QXmlStreamAttributes &attributes,
1883 Q_UNUSED(parent); Q_UNUSED(attributes);
1888 const QXmlStreamAttributes &attributes,
1891 Q_UNUSED(parent); Q_UNUSED(attributes);
1896 const QXmlStreamAttributes &attributes,
1899 Q_UNUSED(attributes);
1900 QSvgG *node =
new QSvgG(parent);
1905 const QXmlStreamAttributes &attributes,
1908 if (parent->type() != QSvgStyleProperty::FONT) {
1912 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
1913 QSvgFont *font = style->svgFont();
1914 return createSvgGlyph(font, attributes,
false);
1918 const QXmlStreamAttributes &attributes,
1921 Q_UNUSED(parent); Q_UNUSED(attributes);
1926 const QXmlStreamAttributes &attributes,
1929 Q_UNUSED(parent); Q_UNUSED(attributes);
1934 const QXmlStreamAttributes &attributes,
1935 QSvgHandler *handler)
1937 const QStringView x = attributes.value(QLatin1String(
"x"));
1938 const QStringView y = attributes.value(QLatin1String(
"y"));
1939 const QStringView width = attributes.value(QLatin1String(
"width"));
1940 const QStringView height = attributes.value(QLatin1String(
"height"));
1941 QString filename = attributes.value(QLatin1String(
"xlink:href")).toString();
1943 filename = attributes.value(QLatin1String(
"href")).toString();
1944 qreal nx = QSvgUtils::toDouble(x);
1945 qreal ny = QSvgUtils::toDouble(y);
1947 qreal nwidth = QSvgUtils::parseLength(width.toString(), &type);
1948 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
1950 qreal nheight = QSvgUtils::parseLength(height.toString(), &type);
1951 nheight = QSvgUtils::convertToPixels(nheight,
false, type);
1953 filename = filename.trimmed();
1954 if (filename.isEmpty()) {
1955 qCWarning(lcSvgHandler) <<
"QSvgHandler: Image filename is empty";
1958 if (nwidth <= 0 || nheight <= 0) {
1959 qCWarning(lcSvgHandler) <<
"QSvgHandler: Width or height for" << filename <<
"image was not greater than 0";
1968 } filenameType = NotLoaded;
1970 if (filename.startsWith(QLatin1String(
"data"))) {
1973 if (qDecodeDataUrl(QUrl{filename}, mimeType, data)) {
1974 image = QImage::fromData(data);
1975 filenameType = LoadedFromData;
1979 if (image.isNull()) {
1980 const auto *file = qobject_cast<QFile *>(handler->device());
1983 if (url.isRelative()) {
1985 filename = info.absoluteDir().absoluteFilePath(filename);
1989 if (handler->trustedSourceMode() || !QImageReader::imageFormat(filename).startsWith(
"svg")) {
1990 image = QImage(filename);
1991 filenameType = LoadedFromFile;
1995 if (image.isNull()) {
1996 qCWarning(lcSvgHandler) <<
"Could not create image from" << filename;
2000 if (image.format() == QImage::Format_ARGB32)
2001 image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
2003 if (filenameType != LoadedFromFile)
2004 filename = QString();
2005 QSvgNode *img =
new QSvgImage(parent,
2016 const QXmlStreamAttributes &attributes,
2019 const QStringView x1 = attributes.value(QLatin1String(
"x1"));
2020 const QStringView y1 = attributes.value(QLatin1String(
"y1"));
2021 const QStringView x2 = attributes.value(QLatin1String(
"x2"));
2022 const QStringView y2 = attributes.value(QLatin1String(
"y2"));
2023 qreal nx1 = QSvgUtils::toDouble(x1);
2024 qreal ny1 = QSvgUtils::toDouble(y1);
2025 qreal nx2 = QSvgUtils::toDouble(x2);
2026 qreal ny2 = QSvgUtils::toDouble(y2);
2028 QLineF lineBounds(nx1, ny1, nx2, ny2);
2029 QSvgNode *line =
new QSvgLine(parent, lineBounds);
2035 QSvgGradientPaint *gradProp,
2036 QSvgHandler *handler)
2038 QStringView linkId = attributes.value(QLatin1String(
"xlink:href"));
2039 const QStringView trans = attributes.value(QLatin1String(
"gradientTransform"));
2040 const QStringView spread = attributes.value(QLatin1String(
"spreadMethod"));
2041 const QStringView units = attributes.value(QLatin1String(
"gradientUnits"));
2042 const QStringView colorStr = attributes.value(QLatin1String(
"color"));
2043 const QStringView colorOpacityStr = attributes.value(QLatin1String(
"color-opacity"));
2046 if (constructColor(colorStr, colorOpacityStr, color, handler)) {
2047 handler->popColor();
2048 handler->pushColor(color);
2052 QGradient *grad = gradProp->qgradient();
2053 linkId = idFromIRI(linkId);
2055 if (!linkId.isEmpty()) {
2056 QSvgPaintServerSharedPtr paintServer = handler->document()->paintServer(linkId);
2057 if (paintServer && paintServer->type() == QSvgPaintServer::Type::Gradient) {
2058 QSvgGradientPaint *inherited =
2059 static_cast<QSvgGradientPaint*>(paintServer.get());
2060 if (!inherited->stopLink().isEmpty()) {
2061 gradProp->setStopLink(inherited->stopLink(), handler->document());
2063 grad->setStops(inherited->qgradient()->stops());
2064 gradProp->setGradientStopsSet(inherited->gradientStopsSet());
2067 matrix = inherited->qtransform();
2069 gradProp->setStopLink(linkId.toString(), handler->document());
2073 if (!trans.isEmpty()) {
2074 matrix = parseTransformationMatrix(trans);
2075 gradProp->setTransform(matrix);
2076 }
else if (!matrix.isIdentity()) {
2077 gradProp->setTransform(matrix);
2080 if (!spread.isEmpty()) {
2081 if (spread == QLatin1String(
"pad")) {
2082 grad->setSpread(QGradient::PadSpread);
2083 }
else if (spread == QLatin1String(
"reflect")) {
2084 grad->setSpread(QGradient::ReflectSpread);
2085 }
else if (spread == QLatin1String(
"repeat")) {
2086 grad->setSpread(QGradient::RepeatSpread);
2090 if (units.isEmpty() || units == QLatin1String(
"objectBoundingBox")) {
2091 grad->setCoordinateMode(QGradient::ObjectMode);
2097 QSvgHandler *handler)
2099 const QStringView x1 = attributes.value(QLatin1String(
"x1"));
2100 const QStringView y1 = attributes.value(QLatin1String(
"y1"));
2101 const QStringView x2 = attributes.value(QLatin1String(
"x2"));
2102 const QStringView y2 = attributes.value(QLatin1String(
"y2"));
2110 nx1 = convertToNumber(x1);
2112 ny1 = convertToNumber(y1);
2114 nx2 = convertToNumber(x2);
2116 ny2 = convertToNumber(y2);
2119 grad->setInterpolationMode(QGradient::ComponentInterpolation);
2121 QSvgGradientPaintSharedPtr paintServer = std::make_shared<QSvgGradientPaint>(std::move(grad));
2122 parseBaseGradient(attributes, paintServer.get(), handler);
2128 const QXmlStreamAttributes &attributes,
2131 Q_UNUSED(parent); Q_UNUSED(attributes);
2136 const QXmlStreamAttributes &attributes,
2139 if (parent->type() != QSvgStyleProperty::FONT) {
2143 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
2144 QSvgFont *font = style->svgFont();
2145 return createSvgGlyph(font, attributes,
true);
2149 const QXmlStreamAttributes &attributes,
2152 Q_UNUSED(parent); Q_UNUSED(attributes);
2157 const QXmlStreamAttributes &attributes,
2160 Q_UNUSED(parent); Q_UNUSED(attributes);
2165 const QXmlStreamAttributes &,
2172 const QXmlStreamAttributes &attributes,
2173 QSvgHandler *handler)
2175 const QStringView x = attributes.value(QLatin1String(
"x"));
2176 const QStringView y = attributes.value(QLatin1String(
"y"));
2177 const QStringView width = attributes.value(QLatin1String(
"width"));
2178 const QStringView height = attributes.value(QLatin1String(
"height"));
2179 const QStringView mU = attributes.value(QLatin1String(
"maskUnits"));
2180 const QStringView mCU = attributes.value(QLatin1String(
"maskContentUnits"));
2182 QtSvg::
UnitTypes nmU = mU.contains(QLatin1String(
"userSpaceOnUse")) ?
2185 QtSvg::
UnitTypes nmCU = mCU.contains(QLatin1String(
"objectBoundingBox")) ?
2195 qreal nx = QSvgUtils::parseLength(x, &type, &ok);
2196 nx = QSvgUtils::convertToPixels(nx,
true, type);
2197 if (x.isEmpty() || !ok) {
2201 nx = nx / 100. * handler->document()->viewBox().width();
2206 qreal ny = QSvgUtils::parseLength(y, &type, &ok);
2207 ny = QSvgUtils::convertToPixels(ny,
true, type);
2208 if (y.isEmpty() || !ok) {
2212 ny = ny / 100. * handler->document()->viewBox().height();
2217 qreal nwidth = QSvgUtils::parseLength(width, &type, &ok);
2218 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
2219 if (width.isEmpty() || !ok) {
2223 nwidth = nwidth / 100. * handler->document()->viewBox().width();
2225 nwidth = nwidth / 100.;
2228 qreal nheight = QSvgUtils::parseLength(height, &type, &ok);
2229 nheight = QSvgUtils::convertToPixels(nheight,
true, type);
2230 if (height.isEmpty() || !ok) {
2234 nheight = nheight / 100. * handler->document()->viewBox().height();
2236 nheight = nheight / 100.;
2239 QRectF bounds(nx, ny, nwidth, nheight);
2240 if (bounds.isEmpty())
2243 QSvgNode *mask =
new QSvgMask(parent, QSvgRectF(bounds, nmUx, nmUy, nmUw, nmUh), nmCU);
2250 const QStringView xStr = attributes.value(QLatin1String(
"x"));
2251 const QStringView yStr = attributes.value(QLatin1String(
"y"));
2252 const QStringView widthStr = attributes.value(QLatin1String(
"width"));
2253 const QStringView heightStr = attributes.value(QLatin1String(
"height"));
2256 if (!xStr.isEmpty()) {
2258 x = QSvgUtils::parseLength(xStr, &type);
2260 x = QSvgUtils::convertToPixels(x,
true, type);
2270 if (!yStr.isEmpty()) {
2272 y = QSvgUtils::parseLength(yStr, &type);
2274 y = QSvgUtils::convertToPixels(y,
false, type);
2284 if (!widthStr.isEmpty()) {
2286 width = QSvgUtils::parseLength(widthStr, &type);
2288 width = QSvgUtils::convertToPixels(width,
true, type);
2295 rect->setWidth(width);
2298 if (!heightStr.isEmpty()) {
2300 height = QSvgUtils::parseLength(heightStr, &type);
2302 height = QSvgUtils::convertToPixels(height,
false, type);
2309 rect->setHeight(height);
2314 const QXmlStreamAttributes &attributes,
2315 QSvgHandler *handler)
2317 const QStringView fU = attributes.value(QLatin1String(
"filterUnits"));
2318 const QStringView pU = attributes.value(QLatin1String(
"primitiveUnits"));
2320 const QtSvg::
UnitTypes filterUnits = fU.contains(QLatin1String(
"userSpaceOnUse")) ?
2323 const QtSvg::
UnitTypes primitiveUnits = pU.contains(QLatin1String(
"objectBoundingBox")) ?
2331 qreal width = handler->document()->viewBox().width();
2332 qreal height = handler->document()->viewBox().height();
2333 rect = QSvgRectF(QRectF(-0.1 * width, -0.1 * height, 1.2 * width, 1.2 * height),
2337 rect = QSvgRectF(QRectF(-0.1, -0.1, 1.2, 1.2),
2342 parseFilterBounds(attributes, &rect);
2344 QSvgNode *filter =
new QSvgFilterContainer(parent, rect, filterUnits, primitiveUnits);
2349 QString *outString, QSvgRectF *rect)
2351 *inString = attributes.value(QLatin1String(
"in")).toString();
2352 *outString = attributes.value(QLatin1String(
"result")).toString();
2358 *rect = QSvgRectF(QRectF(0, 0, 1.0, 1.0),
2364 parseFilterBounds(attributes, rect);
2368 const QXmlStreamAttributes &attributes,
2371 const QStringView typeString = attributes.value(QLatin1String(
"type"));
2372 const QStringView valuesString = attributes.value(QLatin1String(
"values"));
2374 QString inputString;
2375 QString outputString;
2378 QSvgFeColorMatrix::ColorShiftType type;
2379 QSvgFeColorMatrix::Matrix values;
2382 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2384 if (typeString.startsWith(QLatin1String(
"saturate")))
2385 type = QSvgFeColorMatrix::ColorShiftType::Saturate;
2386 else if (typeString.startsWith(QLatin1String(
"hueRotate")))
2387 type = QSvgFeColorMatrix::ColorShiftType::HueRotate;
2388 else if (typeString.startsWith(QLatin1String(
"luminanceToAlpha")))
2389 type = QSvgFeColorMatrix::ColorShiftType::LuminanceToAlpha;
2391 type = QSvgFeColorMatrix::ColorShiftType::Matrix;
2393 if (!valuesString.isEmpty()) {
2394 const auto valueStringList = splitWithDelimiter(valuesString);
2395 for (
int i = 0, j = 0; i < qMin(20, valueStringList.size()); i++) {
2397 qreal v = QSvgUtils::toDouble(valueStringList.at(i), &ok);
2399 values.data()[j] = v;
2404 values.setToIdentity();
2407 QSvgNode *filter =
new QSvgFeColorMatrix(parent, inputString, outputString, rect,
2413 const QXmlStreamAttributes &attributes,
2416 const QStringView edgeModeString = attributes.value(QLatin1String(
"edgeMode"));
2417 const QStringView stdDeviationString = attributes.value(QLatin1String(
"stdDeviation"));
2419 QString inputString;
2420 QString outputString;
2423 QSvgFeGaussianBlur::EdgeMode edgemode = QSvgFeGaussianBlur::EdgeMode::Duplicate;
2425 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2426 qreal stdDeviationX = 0;
2427 qreal stdDeviationY = 0;
2429 stdDeviationX = qMax(0., QSvgUtils::toDouble(stdDeviationString.split(u" ").constFirst()));
2430 stdDeviationY = qMax(0., QSvgUtils::toDouble(stdDeviationString.split(u" ").constLast()));
2432 stdDeviationY = stdDeviationX = qMax(0., QSvgUtils::toDouble(stdDeviationString));
2435 if (edgeModeString.startsWith(QLatin1String(
"wrap")))
2436 edgemode = QSvgFeGaussianBlur::EdgeMode::Wrap;
2437 else if (edgeModeString.startsWith(QLatin1String(
"none")))
2438 edgemode = QSvgFeGaussianBlur::EdgeMode::None;
2440 QSvgNode *filter =
new QSvgFeGaussianBlur(parent, inputString, outputString, rect,
2441 stdDeviationX, stdDeviationY, edgemode);
2446 const QXmlStreamAttributes &attributes,
2449 QStringView dxString = attributes.value(QLatin1String(
"dx"));
2450 QStringView dyString = attributes.value(QLatin1String(
"dy"));
2452 QString inputString;
2453 QString outputString;
2456 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2459 if (!dxString.isEmpty()) {
2461 dx = QSvgUtils::parseLength(dxString, &type);
2463 dx = QSvgUtils::convertToPixels(dx,
true, type);
2467 if (!dyString.isEmpty()) {
2469 dy = QSvgUtils::parseLength(dyString, &type);
2471 dy = QSvgUtils::convertToPixels(dy,
true, type);
2474 QSvgNode *filter =
new QSvgFeOffset(parent, inputString, outputString, rect,
2480 const QXmlStreamAttributes &attributes,
2483 const QStringView in2String = attributes.value(QLatin1String(
"in2"));
2484 const QStringView operatorString = attributes.value(QLatin1String(
"operator"));
2485 const QStringView k1String = attributes.value(QLatin1String(
"k1"));
2486 const QStringView k2String = attributes.value(QLatin1String(
"k2"));
2487 const QStringView k3String = attributes.value(QLatin1String(
"k3"));
2488 const QStringView k4String = attributes.value(QLatin1String(
"k4"));
2490 QString inputString;
2491 QString outputString;
2494 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2496 QSvgFeComposite::Operator op = QSvgFeComposite::Operator::Over;
2497 if (operatorString.startsWith(QLatin1String(
"in")))
2498 op = QSvgFeComposite::Operator::In;
2499 else if (operatorString.startsWith(QLatin1String(
"out")))
2500 op = QSvgFeComposite::Operator::Out;
2501 else if (operatorString.startsWith(QLatin1String(
"atop")))
2502 op = QSvgFeComposite::Operator::Atop;
2503 else if (operatorString.startsWith(QLatin1String(
"xor")))
2504 op = QSvgFeComposite::Operator::Xor;
2505 else if (operatorString.startsWith(QLatin1String(
"lighter")))
2506 op = QSvgFeComposite::Operator::Lighter;
2507 else if (operatorString.startsWith(QLatin1String(
"arithmetic")))
2508 op = QSvgFeComposite::Operator::Arithmetic;
2510 QVector4D k(0, 0, 0, 0);
2512 if (op == QSvgFeComposite::Operator::Arithmetic) {
2514 qreal v = QSvgUtils::toDouble(k1String, &ok);
2517 v = QSvgUtils::toDouble(k2String, &ok);
2520 v = QSvgUtils::toDouble(k3String, &ok);
2523 v = QSvgUtils::toDouble(k4String, &ok);
2528 QSvgNode *filter =
new QSvgFeComposite(parent, inputString, outputString, rect,
2529 in2String.toString(), op, k);
2535 const QXmlStreamAttributes &attributes,
2538 QString inputString;
2539 QString outputString;
2542 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2544 QSvgNode *filter =
new QSvgFeMerge(parent, inputString, outputString, rect);
2549 const QXmlStreamAttributes &attributes,
2550 QSvgHandler *handler)
2552 QStringView colorStr = attributes.value(QLatin1String(
"flood-color"));
2553 const QStringView opacityStr = attributes.value(QLatin1String(
"flood-opacity"));
2556 if (!constructColor(colorStr, opacityStr, color, handler)) {
2557 color = QColor(Qt::black);
2558 if (opacityStr.isEmpty())
2559 color.setAlphaF(1.0);
2561 setAlpha(opacityStr, &color);
2564 QString inputString;
2565 QString outputString;
2568 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2570 QSvgNode *filter =
new QSvgFeFlood(parent, inputString, outputString, rect, color);
2575 const QXmlStreamAttributes &attributes,
2578 QString inputString;
2579 QString outputString;
2582 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2584 QSvgNode *filter =
new QSvgFeMergeNode(parent, inputString, outputString, rect);
2589 const QXmlStreamAttributes &attributes,
2592 const QStringView in2String = attributes.value(QLatin1String(
"in2"));
2593 const QStringView modeString = attributes.value(QLatin1String(
"mode"));
2595 QString inputString;
2596 QString outputString;
2599 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2601 QSvgFeBlend::Mode mode = QSvgFeBlend::Mode::Normal;
2602 if (modeString.startsWith(QLatin1StringView(
"multiply")))
2603 mode = QSvgFeBlend::Mode::Multiply;
2604 else if (modeString.startsWith(QLatin1StringView(
"screen")))
2605 mode = QSvgFeBlend::Mode::Screen;
2606 else if (modeString.startsWith(QLatin1StringView(
"darken")))
2607 mode = QSvgFeBlend::Mode::Darken;
2608 else if (modeString.startsWith(QLatin1StringView(
"lighten")))
2609 mode = QSvgFeBlend::Mode::Lighten;
2611 QSvgNode *filter =
new QSvgFeBlend(parent, inputString, outputString, rect,
2612 in2String.toString(), mode);
2617 const QXmlStreamAttributes &attributes,
2620 QString inputString;
2621 QString outputString;
2624 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2626 QSvgNode *filter =
new QSvgFeUnsupported(parent, inputString, outputString, rect);
2632 QList<QStringView> viewBoxValues;
2635 viewBoxValues = splitWithDelimiter(str);
2636 if (viewBoxValues.size() == 4) {
2638 qreal x = QSvgUtils::parseLength(viewBoxValues.at(0).trimmed(), &type);
2639 qreal y = QSvgUtils::parseLength(viewBoxValues.at(1).trimmed(), &type);
2640 qreal w = QSvgUtils::parseLength(viewBoxValues.at(2).trimmed(), &type);
2641 qreal h = QSvgUtils::parseLength(viewBoxValues.at(3).trimmed(), &type);
2642 return QRectF(x, y, w, h);
2644 return std::nullopt;
2648 QRectF *rect, QRectF *viewBox, QPointF *refPoint,
2649 QSvgSymbolLike::PreserveAspectRatios *aspect,
2650 QSvgSymbolLike::Overflow *overflow,
2651 bool marker =
false)
2653 const QStringView xStr = attributes.value(QLatin1String(
"x"));
2654 const QStringView yStr = attributes.value(QLatin1String(
"y"));
2655 const QStringView refXStr = attributes.value(QLatin1String(
"refX"));
2656 const QStringView refYStr = attributes.value(QLatin1String(
"refY"));
2657 const QStringView widthStr = attributes.value(marker ? QLatin1String(
"markerWidth")
2658 : QLatin1String(
"width"));
2659 const QStringView heightStr = attributes.value(marker ? QLatin1String(
"markerHeight")
2660 : QLatin1String(
"height"));
2661 const QStringView pAspectRStr = attributes.value(QLatin1String(
"preserveAspectRatio"));
2662 const QStringView overflowStr = attributes.value(QLatin1String(
"overflow"));
2663 const QStringView viewBoxStr = attributes.value(QLatin1String(
"viewBox"));
2667 if (!xStr.isEmpty()) {
2669 x = QSvgUtils::parseLength(xStr, &type);
2671 x = QSvgUtils::convertToPixels(x,
true, type);
2674 if (!yStr.isEmpty()) {
2676 y = QSvgUtils::parseLength(yStr, &type);
2678 y = QSvgUtils::convertToPixels(y,
false, type);
2681 if (!widthStr.isEmpty()) {
2683 width = QSvgUtils::parseLength(widthStr, &type);
2685 width = QSvgUtils::convertToPixels(width,
true, type);
2688 if (!heightStr.isEmpty()) {
2690 height = QSvgUtils::parseLength(heightStr, &type);
2692 height = QSvgUtils::convertToPixels(height,
false, type);
2695 *rect = QRectF(x, y, width, height);
2698 if (!refXStr.isEmpty()) {
2700 x = QSvgUtils::parseLength(refXStr, &type);
2702 x = QSvgUtils::convertToPixels(x,
true, type);
2705 if (!refYStr.isEmpty()) {
2707 y = QSvgUtils::parseLength(refYStr, &type);
2709 y = QSvgUtils::convertToPixels(y,
false, type);
2711 *refPoint = QPointF(x,y);
2713 auto viewBoxResult = parseViewBox(viewBoxStr);
2715 *viewBox = *viewBoxResult;
2716 else if (width > 0 && height > 0)
2717 *viewBox = QRectF(0, 0, width, height);
2719 *viewBox = handler->document()->viewBox();
2721 if (viewBox->isNull())
2724 auto pAspectRStrs = pAspectRStr.split(u" ");
2725 QSvgSymbolLike::PreserveAspectRatio aspectX = QSvgSymbolLike::PreserveAspectRatio::xMid;
2726 QSvgSymbolLike::PreserveAspectRatio aspectY = QSvgSymbolLike::PreserveAspectRatio::yMid;
2727 QSvgSymbolLike::PreserveAspectRatio aspectMS = QSvgSymbolLike::PreserveAspectRatio::meet;
2729 for (
auto &pAStr : std::as_const(pAspectRStrs)) {
2730 if (pAStr.startsWith(QLatin1String(
"none"))) {
2731 aspectX = QSvgSymbolLike::PreserveAspectRatio::None;
2732 aspectY = QSvgSymbolLike::PreserveAspectRatio::None;
2734 if (pAStr.startsWith(QLatin1String(
"xMin")))
2735 aspectX = QSvgSymbolLike::PreserveAspectRatio::xMin;
2736 else if (pAStr.startsWith(QLatin1String(
"xMax")))
2737 aspectX = QSvgSymbolLike::PreserveAspectRatio::xMax;
2738 if (pAStr.endsWith(QLatin1String(
"YMin")))
2739 aspectY = QSvgSymbolLike::PreserveAspectRatio::yMin;
2740 else if (pAStr.endsWith(QLatin1String(
"YMax")))
2741 aspectY = QSvgSymbolLike::PreserveAspectRatio::yMax;
2744 if (pAStr.endsWith(QLatin1String(
"slice")))
2745 aspectMS = QSvgSymbolLike::PreserveAspectRatio::slice;
2747 *aspect = aspectX | aspectY | aspectMS;
2754 *overflow = QSvgSymbolLike::Overflow::Hidden;
2756 if (overflowStr.endsWith(QLatin1String(
"auto")))
2757 *overflow = QSvgSymbolLike::Overflow::Auto;
2758 else if (overflowStr.endsWith(QLatin1String(
"visible")))
2759 *overflow = QSvgSymbolLike::Overflow::Visible;
2760 else if (overflowStr.endsWith(QLatin1String(
"hidden")))
2761 *overflow = QSvgSymbolLike::Overflow::Hidden;
2762 else if (overflowStr.endsWith(QLatin1String(
"scroll")))
2763 *overflow = QSvgSymbolLike::Overflow::Scroll;
2769 const QXmlStreamAttributes &attributes,
2770 QSvgHandler *handler)
2772 QRectF rect, viewBox;
2774 QSvgSymbolLike::PreserveAspectRatios aspect;
2775 QSvgSymbolLike::Overflow overflow;
2777 if (!parseSymbolLikeAttributes(attributes, handler, &rect, &viewBox, &refP, &aspect, &overflow))
2780 refP = QPointF(0, 0);
2781 QSvgNode *symbol =
new QSvgSymbol(parent, rect, viewBox, refP, aspect, overflow);
2786 const QXmlStreamAttributes &attributes,
2787 QSvgHandler *handler)
2789 QRectF rect, viewBox;
2791 QSvgSymbolLike::PreserveAspectRatios aspect;
2792 QSvgSymbolLike::Overflow overflow;
2794 const QStringView orientStr = attributes.value(QLatin1String(
"orient"));
2795 const QStringView markerUnitsStr = attributes.value(QLatin1String(
"markerUnits"));
2797 qreal orientationAngle = 0;
2798 QSvgMarker::Orientation orientation;
2799 if (orientStr.startsWith(QLatin1String(
"auto-start-reverse")))
2800 orientation = QSvgMarker::Orientation::AutoStartReverse;
2801 else if (orientStr.startsWith(QLatin1String(
"auto")))
2802 orientation = QSvgMarker::Orientation::Auto;
2804 orientation = QSvgMarker::Orientation::Value;
2807 if (orientStr.endsWith(QLatin1String(
"turn")))
2808 a = 360. * QSvgUtils::toDouble(orientStr.mid(0, orientStr.length()-4), &ok);
2809 else if (orientStr.endsWith(QLatin1String(
"grad")))
2810 a = QSvgUtils::toDouble(orientStr.mid(0, orientStr.length()-4), &ok);
2811 else if (orientStr.endsWith(QLatin1String(
"rad")))
2812 a = 180. /
M_PI * QSvgUtils::toDouble(orientStr.mid(0, orientStr.length()-3), &ok);
2814 a = QSvgUtils::toDouble(orientStr, &ok);
2816 orientationAngle = a;
2819 QSvgMarker::MarkerUnits markerUnits = QSvgMarker::MarkerUnits::StrokeWidth;
2820 if (markerUnitsStr.startsWith(QLatin1String(
"userSpaceOnUse")))
2821 markerUnits = QSvgMarker::MarkerUnits::UserSpaceOnUse;
2823 if (!parseSymbolLikeAttributes(attributes, handler, &rect, &viewBox, &refP, &aspect, &overflow,
true))
2826 QSvgNode *marker =
new QSvgMarker(parent, rect, viewBox, refP, aspect, overflow,
2827 orientation, orientationAngle, markerUnits);
2832 const QXmlStreamAttributes &attributes,
2833 QSvgHandler *handler)
2835 QStringView data = attributes.value(QLatin1String(
"d"));
2837 std::optional<QPainterPath> qpath = QSvgUtils::parsePathDataFast(data,
2838 !handler->trustedSourceMode());
2840 qCWarning(lcSvgHandler,
"Invalid path data; path truncated.");
2844 qpath.value().setFillRule(Qt::WindingFill);
2845 QSvgNode *path =
new QSvgPath(parent, qpath.value());
2850 const QXmlStreamAttributes &attributes,
2853 QStringView pointsStr = attributes.value(QLatin1String(
"points"));
2854 const QList<qreal> points = parseNumbersList(&pointsStr);
2855 if (points.size() < 4)
2858 for (
int i = 0; i < poly.size(); ++i)
2859 poly[i] = QPointF(points.at(2 * i), points.at(2 * i + 1));
2861 return new QSvgPolyline(parent, poly);
2863 return new QSvgPolygon(parent, poly);
2867 const QXmlStreamAttributes &attributes,
2870 return createPolyNode(parent, attributes,
false);
2874 const QXmlStreamAttributes &attributes,
2877 return createPolyNode(parent, attributes,
true);
2881 const QXmlStreamAttributes &attributes,
2884 Q_UNUSED(parent); Q_UNUSED(attributes);
2889 QSvgHandler *handler)
2891 const QStringView cx = attributes.value(QLatin1String(
"cx"));
2892 const QStringView cy = attributes.value(QLatin1String(
"cy"));
2893 const QStringView r = attributes.value(QLatin1String(
"r"));
2894 const QStringView fx = attributes.value(QLatin1String(
"fx"));
2895 const QStringView fy = attributes.value(QLatin1String(
"fy"));
2900 ncx = convertToNumber(cx);
2902 ncy = convertToNumber(cy);
2906 nr = convertToNumber(r);
2912 nfx = convertToNumber(fx);
2915 nfy = convertToNumber(fy);
2918 grad->setInterpolationMode(QGradient::ComponentInterpolation);
2920 QSvgGradientPaintSharedPtr paintServer = std::make_shared<QSvgGradientPaint>(std::move(grad));
2921 parseBaseGradient(attributes, paintServer.get(), handler);
2927 const QXmlStreamAttributes &attributes,
2930 const QStringView x = attributes.value(QLatin1String(
"x"));
2931 const QStringView y = attributes.value(QLatin1String(
"y"));
2932 const QStringView width = attributes.value(QLatin1String(
"width"));
2933 const QStringView height = attributes.value(QLatin1String(
"height"));
2934 const QStringView rx = attributes.value(QLatin1String(
"rx"));
2935 const QStringView ry = attributes.value(QLatin1String(
"ry"));
2939 qreal nwidth = QSvgUtils::parseLength(width, &type, &ok);
2942 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
2943 qreal nheight = QSvgUtils::parseLength(height, &type, &ok);
2946 nheight = QSvgUtils::convertToPixels(nheight,
true, type);
2947 qreal nrx = QSvgUtils::toDouble(rx);
2948 qreal nry = QSvgUtils::toDouble(ry);
2951 if (bounds.isEmpty())
2954 if (!rx.isEmpty() && ry.isEmpty())
2956 else if (!ry.isEmpty() && rx.isEmpty())
2962 if (nrx > bounds.width()/2)
2963 nrx = bounds.width()/2;
2964 if (nry > bounds.height()/2)
2965 nry = bounds.height()/2;
2970 nrx *= (100/(bounds.width()/2));
2971 nry *= (100/(bounds.height()/2));
2973 QSvgNode *rect =
new QSvgRect(parent, bounds, nrx, nry);
2978 const QXmlStreamAttributes &attributes,
2981 Q_UNUSED(parent); Q_UNUSED(attributes);
2986 const QXmlStreamAttributes &attributes,
2989 Q_UNUSED(parent); Q_UNUSED(attributes);
2994 QSvgHandler *handler)
2996 Q_UNUSED(attributes);
2997 QStringView solidColorStr = attributes.value(QLatin1String(
"solid-color"));
2998 QStringView solidOpacityStr = attributes.value(QLatin1String(
"solid-opacity"));
3000 if (solidOpacityStr.isEmpty())
3001 solidOpacityStr = attributes.value(QLatin1String(
"opacity"));
3004 if (!constructColor(solidColorStr, solidOpacityStr, color, handler))
3006 std::shared_ptr<QSvgSolidColorPaint> paintServer = std::make_shared<QSvgSolidColorPaint>(color);
3011 const QXmlStreamAttributes &attributes,
3012 QSvgHandler *handler)
3014 if (paintServer->type() != QSvgPaintServer::Type::Gradient)
3016 QString nodeIdStr = someId(attributes);
3017 QString xmlClassStr = attributes.value(QLatin1String(
"class")).toString();
3024 dummy.setNodeId(nodeIdStr);
3025 dummy.setXmlClass(xmlClassStr);
3029#ifndef QT_NO_CSSPARSER
3030 QXmlStreamAttributes cssAttributes;
3031 handler->cssHandler().styleLookup(&dummy, cssAttributes);
3032 attrs.setAttributes(cssAttributes, handler);
3034 QXmlStreamAttributes styleCssAttributes;
3035 QStringView style = attributes.value(QLatin1String(
"style"));
3036 if (!style.isEmpty())
3037 handler->cssHandler().parseCSStoXMLAttrs(style.toString(), styleCssAttributes);
3038 attrs.setAttributes(styleCssAttributes, handler);
3042 parseColor(&dummy, attrs, handler);
3044 QSvgGradientPaint *gradientStyle =
static_cast<QSvgGradientPaint*>(paintServer);
3045 QStringView colorStr = attrs.stopColor;
3049 qreal offset = convertToNumber(attrs.offset, &ok);
3053 if (!constructColor(colorStr, attrs.stopOpacity, color, handler)) {
3055 if (!attrs.stopOpacity.isEmpty())
3056 setAlpha(attrs.stopOpacity, &color);
3059 QGradient *grad = gradientStyle->qgradient();
3061 offset = qMin(qreal(1), qMax(qreal(0), offset));
3063 if (gradientStyle->gradientStopsSet()) {
3064 stops = grad->stops();
3066 if (offset <= stops.back().first)
3067 offset = stops.back().first + FLT_EPSILON;
3072 if ((stops.size() == 1) || (stops.at(stops.size() - 2).first < 1.0 - FLT_EPSILON)) {
3073 stops.back().first = 1.0 - FLT_EPSILON;
3074 grad->setStops(stops);
3079 grad->setColorAt(offset, color);
3080 gradientStyle->setGradientStopsSet(
true);
3085 const QXmlStreamAttributes &attributes,
3086 QSvgHandler *handler)
3089#ifdef QT_NO_CSSPARSER
3090 Q_UNUSED(attributes);
3093 const QStringView type = attributes.value(QLatin1String(
"type"));
3094 if (type.compare(QLatin1String(
"text/css"), Qt::CaseInsensitive) == 0 || type.isNull())
3095 handler->setInStyle(
true);
3102 const QXmlStreamAttributes &attributes,
3103 QSvgHandler *handler)
3105 Q_UNUSED(parent); Q_UNUSED(attributes);
3107 QSvgDocument *node =
new QSvgDocument(handler->options(), handler->animatorType());
3108 const QStringView widthStr = attributes.value(QLatin1String(
"width"));
3109 const QStringView heightStr = attributes.value(QLatin1String(
"height"));
3110 const QStringView viewBoxStr = attributes.value(QLatin1String(
"viewBox"));
3114 if (!widthStr.isEmpty()) {
3115 width = QSvgUtils::parseLength(widthStr, &type);
3117 width = QSvgUtils::convertToPixels(width,
true, type);
3121 if (!heightStr.isEmpty()) {
3122 height = QSvgUtils::parseLength(heightStr, &type);
3124 height = QSvgUtils::convertToPixels(height,
false, type);
3128 auto viewBoxResult = parseViewBox(viewBoxStr);
3129 if (viewBoxResult) {
3130 node->setViewBox(*viewBoxResult);
3131 }
else if (width && height) {
3133 width = QSvgUtils::convertToPixels(width,
false, type);
3134 height = QSvgUtils::convertToPixels(height,
false, type);
3136 node->setViewBox(QRectF(0, 0, width, height));
3144 const QXmlStreamAttributes &attributes,
3147 Q_UNUSED(attributes);
3148 QSvgSwitch *node =
new QSvgSwitch(parent);
3153 const QXmlStreamAttributes &attributes,
3154 QSvgHandler *handler)
3156 const QStringView x = attributes.value(QLatin1String(
"x"));
3157 const QStringView y = attributes.value(QLatin1String(
"y"));
3158 const QStringView width = attributes.value(QLatin1String(
"width"));
3159 const QStringView height = attributes.value(QLatin1String(
"height"));
3160 const QStringView patternUnits = attributes.value(QLatin1String(
"patternUnits"));
3161 const QStringView patternContentUnits = attributes.value(QLatin1String(
"patternContentUnits"));
3162 const QStringView patternTransform = attributes.value(QLatin1String(
"patternTransform"));
3164 QtSvg::
UnitTypes nPatternUnits = patternUnits.contains(QLatin1String(
"userSpaceOnUse")) ?
3167 QtSvg::
UnitTypes nPatternContentUnits = patternContentUnits.contains(QLatin1String(
"objectBoundingBox")) ?
3170 const QStringView viewBoxStr = attributes.value(QLatin1String(
"viewBox"));
3175 qreal nx = QSvgUtils::parseLength(x, &type, &ok);
3176 nx = QSvgUtils::convertToPixels(nx,
true, type);
3180 nx = (nx / 100.) * handler->document()->viewBox().width();
3184 qreal ny = QSvgUtils::parseLength(y, &type, &ok);
3185 ny = QSvgUtils::convertToPixels(ny,
true, type);
3189 ny = (ny / 100.) * handler->document()->viewBox().height();
3193 qreal nwidth = QSvgUtils::parseLength(width, &type, &ok);
3194 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
3198 nwidth = (nwidth / 100.) * handler->document()->viewBox().width();
3200 nwidth = nwidth / 100.;
3202 qreal nheight = QSvgUtils::parseLength(height, &type, &ok);
3203 nheight = QSvgUtils::convertToPixels(nheight,
true, type);
3207 nheight = (nheight / 100.) * handler->document()->viewBox().height();
3209 nheight = nheight / 100.;
3212 auto viewBoxResult = parseViewBox(viewBoxStr);
3213 if (viewBoxResult) {
3214 if (viewBoxResult->width() > 0 && viewBoxResult->height() > 0)
3215 viewBox = *viewBoxResult;
3219 if (!patternTransform.isEmpty())
3220 matrix = parseTransformationMatrix(patternTransform);
3222 QRectF bounds(nx, ny, nwidth, nheight);
3223 if (bounds.isEmpty())
3226 QSvgRectF patternRectF(bounds, nPatternUnits, nPatternUnits, nPatternUnits, nPatternUnits);
3227 QSvgPattern *node =
new QSvgPattern(parent, patternRectF, viewBox, nPatternContentUnits, matrix);
3230 QSvgPaintServerSharedPtr prop = std::make_shared<QSvgPatternPaint>(node);
3231 handler->document()->addPaintServer(
std::move(prop), someId(attributes));
3237 const QXmlStreamAttributes &,
3240 if (parent->type() != QSvgNode::Textarea)
3242 static_cast<QSvgText*>(parent)->addLineBreak();
3247 const QXmlStreamAttributes &attributes,
3250 const QStringView x = attributes.value(QLatin1String(
"x"));
3251 const QStringView y = attributes.value(QLatin1String(
"y"));
3254 qreal nx = QSvgUtils::parseLength(x, &type);
3255 nx = QSvgUtils::convertToPixels(nx,
true, type);
3256 qreal ny = QSvgUtils::parseLength(y, &type);
3257 ny = QSvgUtils::convertToPixels(ny,
true, type);
3259 QSvgNode *text =
new QSvgText(parent, QPointF(nx, ny));
3264 const QXmlStreamAttributes &attributes,
3265 QSvgHandler *handler)
3267 QSvgText *node =
static_cast<QSvgText *>(createTextNode(parent, attributes, handler));
3270 qreal width = QSvgUtils::parseLength(attributes.value(QLatin1String(
"width")), &type);
3271 qreal height = QSvgUtils::parseLength(attributes.value(QLatin1String(
"height")), &type);
3272 node->setTextArea(QSizeF(width, height));
3278 const QXmlStreamAttributes &,
3281 return new QSvgTspan(parent);
3285 const QXmlStreamAttributes &attributes,
3288 QStringView linkId = attributes.value(QLatin1String(
"xlink:href"));
3289 const QStringView xStr = attributes.value(QLatin1String(
"x"));
3290 const QStringView yStr = attributes.value(QLatin1String(
"y"));
3291 QSvgStructureNode *group =
nullptr;
3293 if (linkId.isEmpty())
3294 linkId = attributes.value(QLatin1String(
"href"));
3295 QString linkIdStr = idFromIRI(linkId).toString();
3297 switch (parent->type()) {
3299 case QSvgNode::Defs:
3300 case QSvgNode::Group:
3301 case QSvgNode::Switch:
3302 case QSvgNode::Mask:
3303 case QSvgNode::Symbol:
3304 case QSvgNode::Marker:
3305 case QSvgNode::Pattern:
3306 group =
static_cast<QSvgStructureNode*>(parent);
3314 if (!xStr.isNull() || !yStr.isNull()) {
3316 qreal nx = QSvgUtils::parseLength(xStr, &type);
3317 nx = QSvgUtils::convertToPixels(nx,
true, type);
3319 qreal ny = QSvgUtils::parseLength(yStr, &type);
3320 ny = QSvgUtils::convertToPixels(ny,
true, type);
3321 pt = QPointF(nx, ny);
3324 QSvgNode *link = group->scopeNode(linkIdStr);
3326 if (parent->isDescendantOf(link))
3327 qCWarning(lcSvgHandler,
"link %ls is recursive!",
qUtf16Printable(linkIdStr));
3329 return new QSvgUse(pt, parent, link);
3333 return new QSvgUse(pt, parent, linkIdStr);
3336 qCWarning(lcSvgHandler,
"<use> element %ls in wrong context!",
qUtf16Printable(linkIdStr));
3341 const QXmlStreamAttributes &attributes,
3344 Q_UNUSED(parent); Q_UNUSED(attributes);
3348typedef QSvgNode *(*FactoryMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3355 QStringView ref = name.mid(1);
3356 switch (name.at(0).unicode()) {
3358 if (ref == QLatin1String(
"efs"))
return createDefsNode;
3364 if (ref.isEmpty())
return createGNode;
3371 if (ref == QLatin1String(
"vg"))
return createSvgNode;
3372 if (ref == QLatin1String(
"witch"))
return createSwitchNode;
3390 QStringView ref = name.mid(1);
3391 switch (name.at(0).unicode()) {
3393 if (ref == QLatin1String(
"ircle"))
return createCircleNode;
3396 if (ref == QLatin1String(
"llipse"))
return createEllipseNode;
3399 if (ref == QLatin1String(
"mage"))
return createImageNode;
3402 if (ref == QLatin1String(
"ine"))
return createLineNode;
3405 if (ref == QLatin1String(
"ath"))
return createPathNode;
3406 if (ref == QLatin1String(
"olygon"))
return createPolygonNode;
3407 if (ref == QLatin1String(
"olyline"))
return createPolylineNode;
3410 if (ref == QLatin1String(
"ect"))
return createRectNode;
3413 if (ref == QLatin1String(
"ext"))
return createTextNode;
3414 if (ref == QLatin1String(
"extArea"))
return createTextAreaNode;
3415 if (ref == QLatin1String(
"span"))
return createTspanNode;
3418 if (ref == QLatin1String(
"se"))
return createUseNode;
3421 if (ref == QLatin1String(
"ideo"))
return createVideoNode;
3437 if (!name.startsWith(QLatin1String(
"fe")))
3440 if (name == QLatin1String(
"feMerge"))
return createFeMergeNode;
3441 if (name == QLatin1String(
"feColorMatrix"))
return createFeColorMatrixNode;
3442 if (name == QLatin1String(
"feGaussianBlur"))
return createFeGaussianBlurNode;
3443 if (name == QLatin1String(
"feOffset"))
return createFeOffsetNode;
3444 if (name == QLatin1String(
"feMergeNode"))
return createFeMergeNodeNode;
3445 if (name == QLatin1String(
"feComposite"))
return createFeCompositeNode;
3446 if (name == QLatin1String(
"feFlood"))
return createFeFloodNode;
3447 if (name == QLatin1String(
"feBlend"))
return createFeBlendNode;
3449 static const QStringList unsupportedFilters = {
3466 if (unsupportedFilters.contains(name))
3467 return createFeUnsupportedNode;
3472typedef QSvgNode *(*AnimationMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3479 QStringView ref = name.mid(1);
3481 switch (name.at(0).unicode()) {
3483 if (ref == QLatin1String(
"nimate"))
return createAnimateNode;
3484 if (ref == QLatin1String(
"nimateColor"))
return createAnimateColorNode;
3485 if (ref == QLatin1String(
"nimateMotion"))
return createAnimateMotionNode;
3486 if (ref == QLatin1String(
"nimateTransform"))
return createAnimateTransformNode;
3495typedef bool (*
ParseMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3502 QStringView ref = name.mid(1);
3503 switch (name.at(0).unicode()) {
3505 if (ref.isEmpty())
return parseAnchorNode;
3506 if (ref == QLatin1String(
"udio"))
return parseAudioNode;
3509 if (ref == QLatin1String(
"iscard"))
return parseDiscardNode;
3512 if (ref == QLatin1String(
"oreignObject"))
return parseForeignObjectNode;
3515 if (ref == QLatin1String(
"andler"))
return parseHandlerNode;
3516 if (ref == QLatin1String(
"kern"))
return parseHkernNode;
3519 if (ref == QLatin1String(
"etadata"))
return parseMetadataNode;
3520 if (ref == QLatin1String(
"path"))
return parseMpathNode;
3525 if (ref == QLatin1String(
"refetch"))
return parsePrefetchNode;
3528 if (ref == QLatin1String(
"cript"))
return parseScriptNode;
3529 if (ref == QLatin1String(
"et"))
return parseSetNode;
3530 if (ref == QLatin1String(
"tyle"))
return parseStyleNode;
3533 if (ref == QLatin1String(
"break"))
return parseTbreakNode;
3549 QStringView ref = name.mid(1);
3550 switch (name.at(0).unicode()) {
3552 if (ref == QLatin1String(
"ont"))
return createFontNode;
3561 const QXmlStreamAttributes &,
3569 QStringView ref = name.mid(1);
3570 switch (name.at(0).unicode()) {
3597 QStringView ref = name.sliced(1);
3598 switch (name.at(0).unicode()) {
3600 if (ref == QLatin1String(
"inearGradient"))
return createLinearGradientNode;
3603 if (ref == QLatin1String(
"adialGradient"))
return createRadialGradientNode;
3606 if (ref == QLatin1String(
"olidColor"))
return createSolidColorNode;
3615 const QXmlStreamAttributes &,
3623 QStringView ref = name.sliced(1);
3624 switch (name.at(0).unicode()) {
3626 if (ref == QLatin1String(
"top"))
return parseStopNode;
3634QSvgHandler::QSvgHandler(QIODevice *device, QtSvg::Options options,
3635 QtSvg::AnimatorType type)
3636 : xml(
new QXmlStreamReader(device))
3637 , m_ownsReader(
true)
3638 , m_options(options)
3639 , m_animatorType(type)
3644QSvgHandler::QSvgHandler(
const QByteArray &data, QtSvg::Options options,
3645 QtSvg::AnimatorType type)
3646 : xml(
new QXmlStreamReader(data))
3647 , m_ownsReader(
true)
3648 , m_options(options)
3649 , m_animatorType(type)
3654QSvgHandler::QSvgHandler(QXmlStreamReader *
const reader, QtSvg::Options options,
3655 QtSvg::AnimatorType type)
3657 , m_ownsReader(
false)
3658 , m_options(options)
3659 , m_animatorType(type)
3664void QSvgHandler::init()
3668 m_defaultCoords = QSvgUtils::LT_PX;
3669 m_defaultPen = QPen(Qt::black, 1, Qt::SolidLine, Qt::FlatCap, Qt::SvgMiterJoin);
3670 m_defaultPen.setMiterLimit(4);
3676 QSvgFillStyle *fillStyle =
static_cast<QSvgFillStyle*>
3677 (node->styleProperty(QSvgStyleProperty::FILL));
3678 if (fillStyle && fillStyle->paintServer()
3679 && fillStyle->paintServer()->type() == QSvgPaintServer::Type::Pattern) {
3680 QSvgPatternPaint *patternStyle =
static_cast<QSvgPatternPaint *>(fillStyle->paintServer());
3681 if (active.contains(patternStyle->patternNode()))
3685 QSvgStrokeStyle *strokeStyle =
static_cast<QSvgStrokeStyle*>
3686 (node->styleProperty(QSvgStyleProperty::STROKE));
3687 if (strokeStyle && strokeStyle->paintServer()
3688 && strokeStyle->paintServer()->type() == QSvgPaintServer::Type::Pattern) {
3689 QSvgPatternPaint *patternStyle =
static_cast<QSvgPatternPaint *>(strokeStyle->paintServer());
3690 if (active.contains(patternStyle->patternNode()))
3699 if (Q_UNLIKELY(!node))
3701 switch (node->type()) {
3703 case QSvgNode::Group:
3704 case QSvgNode::Defs:
3705 case QSvgNode::Pattern:
3707 if (node->type() == QSvgNode::Pattern)
3708 active.append(node);
3710 auto *g =
static_cast<
const QSvgStructureNode*>(node);
3711 for (
auto &node : g->renderers()) {
3712 if (detectCycles(node.get(), active))
3719 if (active.contains(node))
3722 auto *u =
static_cast<
const QSvgUse*>(node);
3723 auto *target = u->link();
3726 if (detectCycles(target, active))
3731 case QSvgNode::Rect:
3732 case QSvgNode::Ellipse:
3733 case QSvgNode::Circle:
3734 case QSvgNode::Line:
3735 case QSvgNode::Path:
3736 case QSvgNode::Polygon:
3737 case QSvgNode::Polyline:
3738 case QSvgNode::Tspan:
3739 if (detectPatternCycles(node, active))
3749 const bool cycleFound = detectCycles(node);
3751 qCWarning(lcSvgHandler,
"Cycles detected in SVG, document discarded.");
3755void QSvgHandler::parse()
3757 xml->setNamespaceProcessing(
false);
3758#ifndef QT_NO_CSSPARSER
3762 while (!xml->atEnd() && !done) {
3763 switch (xml->readNext()) {
3764 case QXmlStreamReader::StartElement:
3773 if (!startElement(xml->name(), xml->attributes())) {
3779 case QXmlStreamReader::EndElement:
3780 done = endElement(xml->name());
3782 case QXmlStreamReader::Characters:
3783 characters(xml->text());
3785 case QXmlStreamReader::ProcessingInstruction:
3786 processingInstruction(xml->processingInstructionTarget(), xml->processingInstructionData());
3792 resolvePaintServers();
3794 if (detectCyclesAndWarn(m_doc)) {
3800bool QSvgHandler::startElement(
const QStringView localName,
3801 const QXmlStreamAttributes &attributes)
3803 QSvgNode *node =
nullptr;
3808
3809
3810 const QStringView xmlSpace(attributes.value(QLatin1String(
"xml:space")));
3811 if (xmlSpace.isNull()) {
3813 m_whitespaceMode.push(m_whitespaceMode.isEmpty() ? QSvgText::Default : m_whitespaceMode.top());
3814 }
else if (xmlSpace == QLatin1String(
"preserve")) {
3815 m_whitespaceMode.push(QSvgText::Preserve);
3816 }
else if (xmlSpace == QLatin1String(
"default")) {
3817 m_whitespaceMode.push(QSvgText::Default);
3819 const QByteArray msg =
'"' + xmlSpace.toLocal8Bit()
3820 +
"\" is an invalid value for attribute xml:space. "
3821 "Valid values are \"preserve\" and \"default\".";
3822 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3823 m_whitespaceMode.push(QSvgText::Default);
3826 if (!m_doc && localName != QLatin1String(
"svg"))
3829 if (m_doc && localName == QLatin1String(
"svg")) {
3830 m_skipNodes.push(Doc);
3831 qCWarning(lcSvgHandler) <<
"Skipping a nested svg element, because "
3832 "SVG Document must not contain nested svg elements in Svg Tiny 1.2";
3835 if (!m_skipNodes.isEmpty() && m_skipNodes.top() == Doc)
3838 if (FactoryMethod method = findGroupFactory(localName, options())) {
3841 node = method(
nullptr, attributes,
this);
3843 Q_ASSERT(node->type() == QSvgNode::Doc);
3844 m_doc =
static_cast<QSvgDocument*>(node);
3847 switch (m_nodes.top()->type()) {
3849 case QSvgNode::Group:
3850 case QSvgNode::Defs:
3851 case QSvgNode::Switch:
3852 case QSvgNode::Mask:
3853 case QSvgNode::Symbol:
3854 case QSvgNode::Marker:
3855 case QSvgNode::Pattern:
3857 node = method(m_nodes.top(), attributes,
this);
3859 QSvgStructureNode *group =
3860 static_cast<QSvgStructureNode*>(m_nodes.top());
3861 group->addChild(std::unique_ptr<QSvgNode>(node), someId(attributes));
3866 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
3867 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3873 parseCoreNode(node, attributes);
3874 parseStyle(node, attributes,
this);
3875 if (node->type() == QSvgNode::Filter)
3876 m_toBeResolved.append(node);
3878 }
else if (FactoryMethod method = findGraphicsFactory(localName, options())) {
3880 Q_ASSERT(!m_nodes.isEmpty());
3881 switch (m_nodes.top()->type()) {
3883 case QSvgNode::Group:
3884 case QSvgNode::Defs:
3885 case QSvgNode::Switch:
3886 case QSvgNode::Mask:
3887 case QSvgNode::Symbol:
3888 case QSvgNode::Marker:
3889 case QSvgNode::Pattern:
3891 if (localName == QLatin1String(
"tspan")) {
3892 const QByteArray msg = QByteArrayLiteral(
"\'tspan\' element in wrong context.");
3893 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3896 node = method(m_nodes.top(), attributes,
this);
3898 QSvgStructureNode *group =
3899 static_cast<QSvgStructureNode*>(m_nodes.top());
3900 group->addChild(std::unique_ptr<QSvgNode>(node), someId(attributes));
3904 case QSvgNode::Text:
3905 case QSvgNode::Textarea:
3906 if (localName == QLatin1String(
"tspan")) {
3907 node = method(m_nodes.top(), attributes,
this);
3909 static_cast<QSvgText *>(m_nodes.top())->addTspan(
static_cast<QSvgTspan *>(node));
3912 const QByteArray msg = QByteArrayLiteral(
"\'text\' or \'textArea\' element contains invalid element type.");
3913 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3917 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
3918 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3923 parseCoreNode(node, attributes);
3924 parseStyle(node, attributes,
this);
3925 if (node->type() == QSvgNode::Text || node->type() == QSvgNode::Textarea) {
3926 static_cast<QSvgText *>(node)->setWhitespaceMode(m_whitespaceMode.top());
3927 }
else if (node->type() == QSvgNode::Tspan) {
3928 static_cast<QSvgTspan *>(node)->setWhitespaceMode(m_whitespaceMode.top());
3929 }
else if (node->type() == QSvgNode::Use) {
3930 auto useNode =
static_cast<QSvgUse *>(node);
3931 if (!useNode->isResolved())
3932 m_toBeResolved.append(useNode);
3935 }
else if (FactoryMethod method = findFilterFactory(localName, options())) {
3937 Q_ASSERT(!m_nodes.isEmpty());
3938 if (m_nodes.top()->type() == QSvgNode::Filter ||
3939 (m_nodes.top()->type() == QSvgNode::FeMerge && localName == QLatin1String(
"feMergeNode"))) {
3940 node = method(m_nodes.top(), attributes,
this);
3942 QSvgStructureNode *container =
3943 static_cast<QSvgStructureNode*>(m_nodes.top());
3944 container->addChild(std::unique_ptr<QSvgNode>(node), someId(attributes));
3947 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
3948 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3950 }
else if (AnimationMethod method = findAnimationFactory(localName, options())) {
3951 Q_ASSERT(!m_nodes.isEmpty());
3952 node = method(m_nodes.top(), attributes,
this);
3954 QSvgAnimateNode *anim =
static_cast<QSvgAnimateNode *>(node);
3955 if (anim->linkId().isEmpty())
3956 m_doc->animator()->appendAnimation(m_nodes.top(), anim);
3957 else if (m_doc->namedNode(anim->linkId()))
3958 m_doc->animator()->appendAnimation(m_doc->namedNode(anim->linkId()), anim);
3960 m_toBeResolved.append(anim);
3962 }
else if (ParseMethod method = findUtilFactory(localName, options())) {
3963 Q_ASSERT(!m_nodes.isEmpty());
3964 if (!method(m_nodes.top(), attributes,
this))
3965 qCWarning(lcSvgHandler,
"%s", msgProblemParsing(localName, xml).constData());
3966 }
else if (StyleFactoryMethod method = findStyleFactoryMethod(localName)) {
3967 QSvgStyleProperty *prop = method(attributes,
this);
3969 m_style.reset(prop);
3970 m_nodes.top()->appendStyleProperty(prop);
3972 const QByteArray msg = QByteArrayLiteral(
"Could not parse node: ") + localName.toLocal8Bit();
3973 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3975 }
else if (PaintServerFactoryMethod method = findPaintServerFactoryMethod(localName)) {
3976 QSvgPaintServerSharedPtr paintServer = method(attributes,
this);
3978 m_paintServer = paintServer;
3979 m_doc->addPaintServer(std::move(paintServer), someId(attributes));
3981 const QByteArray msg = QByteArrayLiteral(
"Could not parse node: ") + localName.toLocal8Bit();
3982 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3984 }
else if (StyleParseMethod method = findStyleUtilFactoryMethod(localName)) {
3986 if (!method(m_style, attributes,
this))
3987 qCWarning(lcSvgHandler,
"%s", msgProblemParsing(localName, xml).constData());
3989 }
else if (PaintServerParseMethod method = findPaintServerUtilFactoryMethod(localName)) {
3990 if (m_paintServer) {
3991 if (!method(m_paintServer.get(), attributes,
this))
3992 qCWarning(lcSvgHandler,
"%s", msgProblemParsing(localName, xml).constData());
3995 qCDebug(lcSvgHandler) <<
"Skipping unknown element" << localName;
3996 m_skipNodes.push(Unknown);
4002 m_skipNodes.push(Graphics);
4005 m_skipNodes.push(Style);
4010bool QSvgHandler::endElement(
const QStringView localName)
4012 CurrentNode node = m_skipNodes.top();
4014 if (node == Doc && localName != QLatin1String(
"svg"))
4018 m_whitespaceMode.pop();
4022 if (node == Unknown)
4025#ifdef QT_NO_CSSPARSER
4026 Q_UNUSED(localName);
4028 if (m_inStyle && localName == QLatin1String(
"style"))
4032 if (node == Graphics)
4034 else if (m_style && !m_skipNodes.isEmpty() && m_skipNodes.top() != Style)
4037 return ((localName == QLatin1String(
"svg")) && (node != Doc));
4040void QSvgHandler::resolvePaintServers()
4042 for (QSvgStyleProperty *prop : std::as_const(m_unresolvedStyles)) {
4043 if (prop->type() == QSvgStyleProperty::FILL) {
4044 QSvgFillStyle *fill =
static_cast<QSvgFillStyle *>(prop);
4045 QString id = fill->paintStyleId();
4046 QSvgPaintServerSharedPtr paintServer = m_doc->paintServer(id);
4048 fill->setPaintServer(std::move(paintServer));
4050 qCWarning(lcSvgHandler,
"%s", msgCouldNotResolveProperty(id, xml).constData());
4051 fill->setBrush(Qt::NoBrush);
4053 }
else if (prop->type() == QSvgStyleProperty::STROKE) {
4054 QSvgStrokeStyle *stroke =
static_cast<QSvgStrokeStyle *>(prop);
4055 QString id = stroke->paintStyleId();
4056 QSvgPaintServerSharedPtr paintServer = m_doc->paintServer(id);
4058 stroke->setPaintServer(std::move(paintServer));
4060 qCWarning(lcSvgHandler,
"%s", msgCouldNotResolveProperty(id, xml).constData());
4061 stroke->setStroke(Qt::NoBrush);
4066 m_unresolvedStyles.clear();
4069void QSvgHandler::resolveNodes()
4071 for (QSvgNode *node : std::as_const(m_toBeResolved)) {
4072 if (node->type() == QSvgNode::Use) {
4073 QSvgUse *useNode =
static_cast<QSvgUse *>(node);
4074 const auto parent = useNode->parent();
4078 QSvgNode::Type t = parent->type();
4079 if (t != QSvgNode::Doc && t != QSvgNode::Defs && t != QSvgNode::Group && t != QSvgNode::Switch)
4082 QSvgStructureNode *group =
static_cast<QSvgStructureNode *>(parent);
4083 QSvgNode *link = group->scopeNode(useNode->linkId());
4085 qCWarning(lcSvgHandler,
"link #%s is undefined!",
qPrintable(useNode->linkId()));
4089 if (useNode->parent()->isDescendantOf(link))
4090 qCWarning(lcSvgHandler,
"link #%s is recursive!",
qPrintable(useNode->linkId()));
4092 useNode->setLink(link);
4093 }
else if (node->type() == QSvgNode::Filter) {
4094 QSvgFilterContainer *filter =
static_cast<QSvgFilterContainer *>(node);
4095 for (
auto &renderer : filter->renderers()) {
4096 const QSvgFeFilterPrimitive *primitive = QSvgFeFilterPrimitive::castToFilterPrimitive(renderer.get());
4097 if (!primitive || primitive->type() == QSvgNode::FeUnsupported) {
4098 filter->setSupported(
false);
4102 }
else if (node->type() == QSvgNode::AnimateTransform || node->type() == QSvgNode::AnimateColor) {
4103 QSvgAnimateNode *anim =
static_cast<QSvgAnimateNode *>(node);
4104 QSvgNode *targetNode = m_doc->namedNode(anim->linkId());
4106 m_doc->animator()->appendAnimation(targetNode, anim);
4108 qCWarning(lcSvgHandler,
"Cannot find target for link #%s!",
4114 m_toBeResolved.clear();
4117bool QSvgHandler::characters(
const QStringView str)
4119#ifndef QT_NO_CSSPARSER
4121 m_cssHandler.parseStyleSheet(str);
4125 if (m_skipNodes.isEmpty() || m_skipNodes.top() == Unknown || m_nodes.isEmpty())
4128 if (m_nodes.top()->type() == QSvgNode::Text || m_nodes.top()->type() == QSvgNode::Textarea) {
4129 static_cast<QSvgText*>(m_nodes.top())->addText(str);
4130 }
else if (m_nodes.top()->type() == QSvgNode::Tspan) {
4131 static_cast<QSvgTspan*>(m_nodes.top())->addText(str);
4137QIODevice *QSvgHandler::device()
const
4139 return xml->device();
4142QSvgDocument *QSvgHandler::document()
const
4147QSvgUtils::LengthType QSvgHandler::defaultCoordinateSystem()
const
4149 return m_defaultCoords;
4152void QSvgHandler::setDefaultCoordinateSystem(QSvgUtils::LengthType type)
4154 m_defaultCoords = type;
4157void QSvgHandler::pushColor(
const QColor &color)
4159 m_colorStack.push(color);
4160 m_colorTagCount.push(1);
4163void QSvgHandler::pushColorCopy()
4165 if (m_colorTagCount.size())
4166 ++m_colorTagCount.top();
4168 pushColor(Qt::black);
4171void QSvgHandler::popColor()
4173 if (m_colorTagCount.size()) {
4174 if (!--m_colorTagCount.top()) {
4176 m_colorTagCount.pop();
4181QColor QSvgHandler::currentColor()
const
4183 if (!m_colorStack.isEmpty())
4184 return m_colorStack.top();
4186 return QColor(0, 0, 0);
4189void QSvgHandler::pushUnresolvedStyle(QSvgStyleProperty *prop)
4191 m_unresolvedStyles.append(prop);
4194#ifndef QT_NO_CSSPARSER
4196void QSvgHandler::setInStyle(
bool b)
4201bool QSvgHandler::inStyle()
const
4206QSvgCssHandler &QSvgHandler::cssHandler()
4208 return m_cssHandler;
4213bool QSvgHandler::processingInstruction(
const QStringView target,
const QStringView data)
4215#ifdef QT_NO_CSSPARSER
4219 if (target == QLatin1String(
"xml-stylesheet")) {
4220 static const QRegularExpression rx(
QStringLiteral(
"type=\\\"(.+)\\\""),
4221 QRegularExpression::InvertedGreedinessOption);
4222 QRegularExpressionMatchIterator iter = rx.globalMatchView(data);
4224 while (iter.hasNext()) {
4225 QRegularExpressionMatch match = iter.next();
4226 QString type = match.captured(1);
4227 if (type.toLower() == QLatin1String(
"text/css")) {
4233 static const QRegularExpression rx(
QStringLiteral(
"href=\\\"(.+)\\\""),
4234 QRegularExpression::InvertedGreedinessOption);
4235 QRegularExpressionMatch match = rx.matchView(data);
4236 QString addr = match.captured(1);
4240 QFile file(fi.absoluteFilePath());
4241 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
4244 QByteArray cssData = file.readAll();
4245 QString css = QString::fromUtf8(cssData);
4246 m_cssHandler.parseStyleSheet(css);
4256void QSvgHandler::setAnimPeriod(
int start,
int end)
4259 m_animEnd = qMax(end, m_animEnd);
4262int QSvgHandler::animationDuration()
const
4267QSvgHandler::~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 void parseVisibility(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static bool detectPatternCycles(const QSvgNode *node, QList< const QSvgNode * > active={})
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 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 bool parseBaseAnimate(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgAnimateNode *anim, QSvgHandler *handler)
static bool parseFontFaceNameNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static std::optional< int > parseFontWeight(QStringView s)
static void parseFilterAttributes(const QXmlStreamAttributes &attributes, QString *inString, QString *outString, QSvgRectF *rect)
static QSvgNode * createPolyNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, bool createLine)
static void parseTransform(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static QSvgNode * createFeMergeNodeNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static 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 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)
static void parseFont(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
QList< qreal > parseNumbersList(QStringView *str)
static bool parseForeignObjectNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseTbreakNode(QSvgNode *parent, const QXmlStreamAttributes &, QSvgHandler *)
static QSvgNode * createFeUnsupportedNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QList< QStringView > splitWithDelimiter(QStringView delimitedList)
static QSvgNode * createFeOffsetNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createFeFloodNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createMarkerNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createFeColorMatrixNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createFeGaussianBlurNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgPaintServerSharedPtr paintServerFromUrl(QSvgDocument *doc, QStringView url)
static QSvgNode * createPolylineNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgPaintServerSharedPtr createSolidColorNode(const QXmlStreamAttributes &attributes, QSvgHandler *handler)
bool(* ParseMethod)(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *)
static bool parseFontFaceNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, 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 QSvgNode * createUseNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseFontFaceSrcNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseAnchorNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseFontFaceUriNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool detectCycles(const QSvgNode *node, QList< const QSvgNode * > active={})
static QStringList stringToList(const QString &str)
static 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 * 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