5#include "qplatformdefs.h"
23#include <QtCore/private/qdataurl_p.h>
30#include <qregularexpression.h>
39Q_LOGGING_CATEGORY(lcSvgHandler,
"qt.svg")
41static const char *qt_inherit_text =
"inherit";
42#define QT_INHERIT QLatin1String(qt_inherit_text)
48 if (
const QFile *file = qobject_cast<
const QFile *>(r->device()))
49 result.append(QFile::encodeName(QDir::toNativeSeparators(file->fileName())));
51 result.append(QByteArrayLiteral(
"<input>"));
53 result.append(QByteArray::number(r->lineNumber()));
54 if (
const qint64 column = r->columnNumber()) {
56 result.append(QByteArray::number(column));
58 result.append(QByteArrayLiteral(
": "));
66 return prefixMessage(
"Problem parsing " + localName.toLocal8Bit(), r);
71 return prefixMessage(
"Could not resolve property: " + id.toLocal8Bit(), r);
76 static const QRegularExpression delimiterRE(
QStringLiteral(
"[,\\s]+"));
77 return delimitedList.split(delimiterRE, Qt::SkipEmptyParts);
82static inline int qsvg_h2i(
char hex,
bool *ok =
nullptr)
84 if (hex >=
'0' && hex <=
'9')
86 if (hex >=
'a' && hex <=
'f')
87 return hex -
'a' + 10;
88 if (hex >=
'A' && hex <=
'F')
89 return hex -
'A' + 10;
111 const size_t len = qstrlen(name);
118 }
else if (len == 9) {
122 }
else if (len == 6) {
126 }
else if (len == 3) {
133 if ((uint)r > 255 || (uint)g > 255 || (uint)b > 255 || !ok) {
137 *rgb = qRgb(r, g ,b);
146 for(
int i = 0; i < len; ++i)
147 tmp[i] = str[i].toLatin1();
149 return qsvg_get_hex_rgb(tmp, rgb);
154static inline QString
someId(
const QXmlStreamAttributes &attributes)
156 QStringView id = attributes.value(QLatin1String(
"id"));
158 id = attributes.value(QLatin1String(
"xml:id"));
159 return id.toString();
165 void setAttributes(
const QXmlStreamAttributes &attributes, QSvgHandler *handler);
207 setAttributes(xmlAttributes, handler);
212 for (
const QXmlStreamAttribute &attribute : attributes) {
213 QStringView name = attribute.qualifiedName();
216 QStringView value = attribute.value();
218 switch (name.at(0).unicode()) {
221 if (name == QLatin1String(
"color"))
223 else if (name == QLatin1String(
"color-opacity"))
224 colorOpacity = value;
225 else if (name == QLatin1String(
"comp-op"))
230 if (name == QLatin1String(
"display"))
235 if (name == QLatin1String(
"fill"))
237 else if (name == QLatin1String(
"fill-rule"))
239 else if (name == QLatin1String(
"fill-opacity"))
241 else if (name == QLatin1String(
"font-family"))
243 else if (name == QLatin1String(
"font-size"))
245 else if (name == QLatin1String(
"font-style"))
247 else if (name == QLatin1String(
"font-weight"))
249 else if (name == QLatin1String(
"font-variant"))
251 else if (name == QLatin1String(
"filter") &&
252 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
257 if (name == QLatin1String(
"id"))
258 id = value.toString();
259 else if (name == QLatin1String(
"image-rendering"))
260 imageRendering = value;
264 if (name == QLatin1String(
"mask") &&
265 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
267 if (name == QLatin1String(
"marker-start") &&
268 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
270 if (name == QLatin1String(
"marker-mid") &&
271 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
273 if (name == QLatin1String(
"marker-end") &&
274 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
279 if (name == QLatin1String(
"opacity"))
281 if (name == QLatin1String(
"offset"))
286 if (name.size() > 5 && name.mid(1, 5) == QLatin1String(
"troke")) {
287 QStringView strokeRef = name.mid(6, name.size() - 6);
288 if (strokeRef.isEmpty())
290 else if (strokeRef == QLatin1String(
"-dasharray"))
291 strokeDashArray = value;
292 else if (strokeRef == QLatin1String(
"-dashoffset"))
293 strokeDashOffset = value;
294 else if (strokeRef == QLatin1String(
"-linecap"))
295 strokeLineCap = value;
296 else if (strokeRef == QLatin1String(
"-linejoin"))
297 strokeLineJoin = value;
298 else if (strokeRef == QLatin1String(
"-miterlimit"))
299 strokeMiterLimit = value;
300 else if (strokeRef == QLatin1String(
"-opacity"))
301 strokeOpacity = value;
302 else if (strokeRef == QLatin1String(
"-width"))
304 }
else if (name == QLatin1String(
"stop-color"))
306 else if (name == QLatin1String(
"stop-opacity"))
311 if (name == QLatin1String(
"text-anchor"))
313 else if (name == QLatin1String(
"transform"))
318 if (name == QLatin1String(
"vector-effect"))
319 vectorEffect = value;
320 else if (name == QLatin1String(
"visibility"))
325 if (name == QLatin1String(
"xml:id") && id.isEmpty())
326 id = value.toString();
342 while (str->isSpace())
345 *str == QLatin1Char(
'-') || *str == QLatin1Char(
'+') ||
346 *str == QLatin1Char(
'.')) {
348 points.append(QSvgUtils::toDouble(str));
350 while (str->isSpace())
352 if (*str == QLatin1Char(
','))
356 while (str->isSpace())
369 while (str->isSpace())
371 while ((*str >= QLatin1Char(
'0') && *str <= QLatin1Char(
'9')) ||
372 *str == QLatin1Char(
'-') || *str == QLatin1Char(
'+') ||
373 *str == QLatin1Char(
'.')) {
375 points.append(QSvgUtils::toDouble(str));
377 while (str->isSpace())
379 if (*str == QLatin1Char(
'%'))
381 while (str->isSpace())
383 if (*str == QLatin1Char(
','))
387 while (str->isSpace())
399 if (url.startsWith(QLatin1Char(
'(')))
402 return QStringView();
404 if (!url.startsWith(QLatin1Char(
'#')))
405 return QStringView();
406 const qsizetype closingBracePos = url.indexOf(QLatin1Char(
')'));
407 if (closingBracePos == -1)
408 return QStringView();
409 return url.first(closingBracePos).trimmed();
413
414
415
416static bool resolveColor(QStringView colorStr, QColor &color, QSvgHandler *handler)
418 QStringView colorStrTr = colorStr.trimmed();
419 if (colorStrTr.isEmpty())
422 switch(colorStrTr.at(0).unicode()) {
429 bool ok = qsvg_get_hex_rgb(colorStrTr.constData(), colorStrTr.size(), &rgb);
439 if (colorStrTr.size() >= 7 && colorStrTr.at(colorStrTr.size() - 1) == QLatin1Char(
')')
440 && colorStrTr.mid(0, 4) == QLatin1String(
"rgb(")) {
441 const QChar *s = colorStrTr.constData() + 4;
442 QList<qreal> compo = parseNumbersList(s);
445 if (compo.size() == 1) {
446 s = colorStrTr.constData() + 4;
447 compo = parsePercentageList(s);
448 for (
int i = 0; i < compo.size(); ++i)
449 compo[i] *= (qreal)2.55;
452 if (compo.size() == 3) {
453 color = QColor(
int(compo[0]),
464 if (colorStrTr == QLatin1String(
"currentColor")) {
465 color = handler->currentColor();
477 color = QColor::fromString(colorStrTr);
478 return color.isValid();
484 qreal op = qBound(qreal(0.0), QSvgUtils::toDouble(opacity, &ok), qreal(1.0));
487 color->setAlphaF(op);
491 QColor &color, QSvgHandler *handler)
493 if (!resolveColor(colorStr, color, handler))
495 if (!opacity.isEmpty())
496 setAlpha(opacity, &color);
503 qreal num = QSvgUtils::parseLength(str.toString(), &type, ok);
510static bool createSvgGlyph(QSvgFont *font,
const QXmlStreamAttributes &attributes,
513 QStringView uncStr = attributes.value(QLatin1String(
"unicode"));
514 QStringView havStr = attributes.value(QLatin1String(
"horiz-adv-x"));
515 QStringView pathStr = attributes.value(QLatin1String(
"d"));
517 qreal havx = (havStr.isEmpty()) ? -1 : QSvgUtils::toDouble(havStr);
518 QPainterPath path = QSvgUtils::parsePathDataFast(pathStr).value_or(QPainterPath());
520 path.setFillRule(Qt::WindingFill);
522 if (isMissingGlyph) {
523 if (!uncStr.isEmpty())
524 qWarning(
"Ignoring missing-glyph's 'unicode' attribute");
525 return font->addMissingGlyph(path, havx);
528 if (uncStr.isEmpty()) {
529 qWarning(
"glyph does not define a non-empty 'unicode' attribute and will be ignored");
532 font->addGlyph(uncStr.toString(), path, havx);
538 QSvgHandler *handler)
541 if (constructColor(attributes.color, attributes.colorOpacity, color, handler)) {
543 handler->pushColor(color);
549 return node ? node->styleProperty(idFromUrl(url)) : 0;
554 QSvgHandler *handler)
556 if (!attributes.fill.isEmpty() || !attributes.fillRule.isEmpty() || !attributes.fillOpacity.isEmpty()) {
557 QSvgFillStyle *prop =
new QSvgFillStyle;
560 if (!attributes.fillRule.isEmpty() && attributes.fillRule !=
QT_INHERIT) {
561 if (attributes.fillRule == QLatin1String(
"evenodd"))
562 prop->setFillRule(Qt::OddEvenFill);
563 else if (attributes.fillRule == QLatin1String(
"nonzero"))
564 prop->setFillRule(Qt::WindingFill);
568 if (!attributes.fillOpacity.isEmpty() && attributes.fillOpacity !=
QT_INHERIT) {
569 prop->setFillOpacity(qMin(qreal(1.0), qMax(qreal(0.0), QSvgUtils::toDouble(attributes.fillOpacity))));
573 if ((!attributes.fill.isEmpty()) && (attributes.fill !=
QT_INHERIT) ) {
574 if (attributes.fill.startsWith(QLatin1String(
"url"))) {
575 QStringView value = attributes.fill.sliced(3);
576 QSvgStyleProperty *style = styleFromUrl(node, value);
578 if (style->type() == QSvgStyleProperty::SOLID_COLOR || style->type() == QSvgStyleProperty::GRADIENT
579 || style->type() == QSvgStyleProperty::PATTERN)
580 prop->setFillStyle(
reinterpret_cast<QSvgPaintStyleProperty *>(style));
582 QString id = idFromUrl(value).toString();
583 prop->setPaintStyleId(id);
584 prop->setPaintStyleResolved(
false);
586 }
else if (attributes.fill != QLatin1String(
"none")) {
588 if (resolveColor(attributes.fill, color, handler))
589 prop->setBrush(QBrush(color));
591 prop->setBrush(QBrush(Qt::NoBrush));
594 node->appendStyleProperty(prop, attributes.id);
606 const QChar *str = value.constData();
607 const QChar *end = str + value.size();
610 if (str->isSpace() || *str == QLatin1Char(
',')) {
622 State state = Matrix;
623 if (*str == QLatin1Char(
'm')) {
624 const char *ident =
"atrix";
625 for (
int i = 0; i < 5; ++i)
626 if (*(++str) != QLatin1Char(ident[i]))
630 }
else if (*str == QLatin1Char(
't')) {
631 const char *ident =
"ranslate";
632 for (
int i = 0; i < 8; ++i)
633 if (*(++str) != QLatin1Char(ident[i]))
637 }
else if (*str == QLatin1Char(
'r')) {
638 const char *ident =
"otate";
639 for (
int i = 0; i < 5; ++i)
640 if (*(++str) != QLatin1Char(ident[i]))
644 }
else if (*str == QLatin1Char(
's')) {
646 if (*str == QLatin1Char(
'c')) {
647 const char *ident =
"ale";
648 for (
int i = 0; i < 3; ++i)
649 if (*(++str) != QLatin1Char(ident[i]))
653 }
else if (*str == QLatin1Char(
'k')) {
654 if (*(++str) != QLatin1Char(
'e'))
656 if (*(++str) != QLatin1Char(
'w'))
659 if (*str == QLatin1Char(
'X'))
661 else if (*str == QLatin1Char(
'Y'))
674 while (str < end && str->isSpace())
676 if (*str != QLatin1Char(
'('))
679 QVarLengthArray<qreal, 8> points;
681 if (*str != QLatin1Char(
')'))
685 if(state == Matrix) {
686 if(points.size() != 6)
688 matrix = QTransform(points[0], points[1],
689 points[2], points[3],
690 points[4], points[5]) * matrix;
691 }
else if (state == Translate) {
692 if (points.size() == 1)
693 matrix.translate(points[0], 0);
694 else if (points.size() == 2)
695 matrix.translate(points[0], points[1]);
698 }
else if (state == Rotate) {
699 if(points.size() == 1) {
700 matrix.rotate(points[0]);
701 }
else if (points.size() == 3) {
702 matrix.translate(points[1], points[2]);
703 matrix.rotate(points[0]);
704 matrix.translate(-points[1], -points[2]);
708 }
else if (state == Scale) {
709 if (points.size() < 1 || points.size() > 2)
711 qreal sx = points[0];
713 if(points.size() == 2)
715 matrix.scale(sx, sy);
716 }
else if (state == SkewX) {
717 if (points.size() != 1)
719 matrix.shear(qTan(qDegreesToRadians(points[0])), 0);
720 }
else if (state == SkewY) {
721 if (points.size() != 1)
723 matrix.shear(0, qTan(qDegreesToRadians(points[0])));
732 QSvgHandler *handler)
734 if (!attributes.stroke.isEmpty() || !attributes.strokeDashArray.isEmpty() || !attributes.strokeDashOffset.isEmpty() || !attributes.strokeLineCap.isEmpty()
735 || !attributes.strokeLineJoin.isEmpty() || !attributes.strokeMiterLimit.isEmpty() || !attributes.strokeOpacity.isEmpty() || !attributes.strokeWidth.isEmpty()
736 || !attributes.vectorEffect.isEmpty()) {
738 QSvgStrokeStyle *prop =
new QSvgStrokeStyle;
741 if ((!attributes.stroke.isEmpty()) && (attributes.stroke !=
QT_INHERIT) ) {
742 if (attributes.stroke.startsWith(QLatin1String(
"url"))) {
743 QStringView value = attributes.stroke.sliced(3);
744 QSvgStyleProperty *style = styleFromUrl(node, value);
746 if (style->type() == QSvgStyleProperty::SOLID_COLOR || style->type() == QSvgStyleProperty::GRADIENT
747 || style->type() == QSvgStyleProperty::PATTERN)
748 prop->setStyle(
reinterpret_cast<QSvgPaintStyleProperty *>(style));
750 QString id = idFromUrl(value).toString();
751 prop->setPaintStyleId(id);
752 prop->setPaintStyleResolved(
false);
754 }
else if (attributes.stroke != QLatin1String(
"none")) {
756 if (resolveColor(attributes.stroke, color, handler))
757 prop->setStroke(QBrush(color));
759 prop->setStroke(QBrush(Qt::NoBrush));
764 if (!attributes.strokeWidth.isEmpty() && attributes.strokeWidth !=
QT_INHERIT) {
766 prop->setWidth(QSvgUtils::parseLength(attributes.strokeWidth, <));
770 if (!attributes.strokeDashArray.isEmpty() && attributes.strokeDashArray !=
QT_INHERIT) {
771 if (attributes.strokeDashArray == QLatin1String(
"none")) {
772 prop->setDashArrayNone();
774 QString dashArray = attributes.strokeDashArray.toString();
775 const QChar *s = dashArray.constData();
776 QList<qreal> dashes = parseNumbersList(s);
777 bool allZeroes =
true;
778 for (qreal dash : dashes) {
787 if (allZeroes ==
false) {
789 if ((dashes.size() & 1) != 0)
790 dashes << QList<qreal>(dashes);
791 prop->setDashArray(dashes);
793 prop->setDashArrayNone();
799 if (!attributes.strokeLineJoin.isEmpty()) {
800 if (attributes.strokeLineJoin == QLatin1String(
"miter"))
801 prop->setLineJoin(Qt::SvgMiterJoin);
802 else if (attributes.strokeLineJoin == QLatin1String(
"round"))
803 prop->setLineJoin(Qt::RoundJoin);
804 else if (attributes.strokeLineJoin == QLatin1String(
"bevel"))
805 prop->setLineJoin(Qt::BevelJoin);
809 if (!attributes.strokeLineCap.isEmpty()) {
810 if (attributes.strokeLineCap == QLatin1String(
"butt"))
811 prop->setLineCap(Qt::FlatCap);
812 else if (attributes.strokeLineCap == QLatin1String(
"round"))
813 prop->setLineCap(Qt::RoundCap);
814 else if (attributes.strokeLineCap == QLatin1String(
"square"))
815 prop->setLineCap(Qt::SquareCap);
819 if (!attributes.strokeDashOffset.isEmpty() && attributes.strokeDashOffset !=
QT_INHERIT)
820 prop->setDashOffset(QSvgUtils::toDouble(attributes.strokeDashOffset));
823 if (!attributes.vectorEffect.isEmpty()) {
824 if (attributes.vectorEffect == QLatin1String(
"non-scaling-stroke"))
825 prop->setVectorEffect(
true);
826 else if (attributes.vectorEffect == QLatin1String(
"none"))
827 prop->setVectorEffect(
false);
831 if (!attributes.strokeMiterLimit.isEmpty() && attributes.strokeMiterLimit !=
QT_INHERIT)
832 prop->setMiterLimit(QSvgUtils::toDouble(attributes.strokeMiterLimit));
835 if (!attributes.strokeOpacity.isEmpty() && attributes.strokeOpacity !=
QT_INHERIT)
836 prop->setOpacity(qMin(qreal(1.0), qMax(qreal(0.0), QSvgUtils::toDouble(attributes.strokeOpacity))));
838 node->appendStyleProperty(prop, attributes.id);
846{ qreal(6.9), qreal(8.3), qreal(10.0), qreal(12.0), qreal(14.4), qreal(17.3), qreal(20.7) };
852 switch (spec.at(0).unicode()) {
854 if (spec == QLatin1String(
"xx-small"))
856 if (spec == QLatin1String(
"x-small"))
858 if (spec == QLatin1String(
"x-large"))
860 if (spec == QLatin1String(
"xx-large"))
864 if (spec == QLatin1String(
"small"))
868 if (spec == QLatin1String(
"medium"))
872 if (spec == QLatin1String(
"large"))
876 if (spec == QLatin1String(
"none"))
889 if (attributes.fontFamily.isEmpty() && attributes.fontSize.isEmpty() && attributes.fontStyle.isEmpty() &&
890 attributes.fontWeight.isEmpty() && attributes.fontVariant.isEmpty() && attributes.textAnchor.isEmpty())
893 QSvgFontStyle *fontStyle =
nullptr;
894 if (!attributes.fontFamily.isEmpty()) {
895 QSvgDocument *doc = node->document();
897 QSvgFont *svgFont = doc->svgFont(attributes.fontFamily.toString());
899 fontStyle =
new QSvgFontStyle(svgFont, doc);
903 fontStyle =
new QSvgFontStyle;
904 if (!attributes.fontFamily.isEmpty() && attributes.fontFamily !=
QT_INHERIT) {
905 QStringView family = attributes.fontFamily.trimmed();
906 if (!family.isEmpty() && (family.at(0) == QLatin1Char(
'\'') || family.at(0) == QLatin1Char(
'\"')))
907 family = family.mid(1, family.size() - 2);
908 fontStyle->setFamily(family.toString());
911 if (!attributes.fontSize.isEmpty() && attributes.fontSize !=
QT_INHERIT) {
913 const FontSizeSpec spec = fontSizeSpec(attributes.fontSize);
919 qreal fs = QSvgUtils::parseLength(attributes.fontSize, &type);
920 fs = QSvgUtils::convertToPixels(fs,
true, type);
921 fontStyle->setSize(qMin(fs, qreal(0xffff)));
925 fontStyle->setSize(sizeTable[spec]);
930 if (!attributes.fontStyle.isEmpty() && attributes.fontStyle !=
QT_INHERIT) {
931 if (attributes.fontStyle == QLatin1String(
"normal")) {
932 fontStyle->setStyle(QFont::StyleNormal);
933 }
else if (attributes.fontStyle == QLatin1String(
"italic")) {
934 fontStyle->setStyle(QFont::StyleItalic);
935 }
else if (attributes.fontStyle == QLatin1String(
"oblique")) {
936 fontStyle->setStyle(QFont::StyleOblique);
940 if (!attributes.fontWeight.isEmpty() && attributes.fontWeight !=
QT_INHERIT) {
942 const int weightNum = attributes.fontWeight.toInt(&ok);
944 fontStyle->setWeight(weightNum);
946 if (attributes.fontWeight == QLatin1String(
"normal")) {
947 fontStyle->setWeight(QFont::Normal);
948 }
else if (attributes.fontWeight == QLatin1String(
"bold")) {
949 fontStyle->setWeight(QFont::Bold);
950 }
else if (attributes.fontWeight == QLatin1String(
"bolder")) {
951 fontStyle->setWeight(QSvgFontStyle::BOLDER);
952 }
else if (attributes.fontWeight == QLatin1String(
"lighter")) {
953 fontStyle->setWeight(QSvgFontStyle::LIGHTER);
958 if (!attributes.fontVariant.isEmpty() && attributes.fontVariant !=
QT_INHERIT) {
959 if (attributes.fontVariant == QLatin1String(
"normal"))
960 fontStyle->setVariant(QFont::MixedCase);
961 else if (attributes.fontVariant == QLatin1String(
"small-caps"))
962 fontStyle->setVariant(QFont::SmallCaps);
965 if (!attributes.textAnchor.isEmpty() && attributes.textAnchor !=
QT_INHERIT) {
966 if (attributes.textAnchor == QLatin1String(
"start"))
967 fontStyle->setTextAnchor(Qt::AlignLeft);
968 if (attributes.textAnchor == QLatin1String(
"middle"))
969 fontStyle->setTextAnchor(Qt::AlignHCenter);
970 else if (attributes.textAnchor == QLatin1String(
"end"))
971 fontStyle->setTextAnchor(Qt::AlignRight);
974 node->appendStyleProperty(fontStyle, attributes.id);
981 if (attributes.transform.isEmpty())
983 QTransform matrix = parseTransformationMatrix(attributes.transform.trimmed());
985 if (!matrix.isIdentity()) {
986 node->appendStyleProperty(
new QSvgTransformStyle(QTransform(matrix)), attributes.id);
995 QSvgNode *parent = node->parent();
997 if (parent && (attributes.visibility.isEmpty() || attributes.visibility ==
QT_INHERIT))
998 node->setVisible(parent->isVisible());
999 else if (attributes.visibility == QLatin1String(
"hidden") || attributes.visibility == QLatin1String(
"collapse")) {
1000 node->setVisible(
false);
1002 node->setVisible(
true);
1006 const QXmlStreamAttributes &attributes,
1007 QSvgHandler *handler);
1013 str = str.trimmed();
1014 if (str.endsWith(QLatin1String(
"ms"))) {
1017 }
else if (str.endsWith(QLatin1String(
"s"))) {
1020 double val = ms * QSvgUtils::toDouble(str, ok);
1022 if (val >
std::numeric_limits<
int>::min() && val <
std::numeric_limits<
int>::max())
1023 res =
static_cast<
int>(val);
1030#ifndef QT_NO_CSSPARSER
1033 const QXmlStreamAttributes &attributes,
1034 QSvgHandler *handler)
1036 QSvgCssProperties cssAnimProps(attributes);
1037 QList<QSvgAnimationProperty> parsedProperties = cssAnimProps.animations();
1039 for (
auto &property : parsedProperties) {
1040 QSvgCssAnimation *anim = handler->cssHandler().createAnimation(property.name);
1044 anim->setRunningTime(property.delay, property.duration);
1045 anim->setIterationCount(property.iteration);
1046 QSvgCssEasingPtr easing = handler->cssHandler().createEasing(property.easingFunction, property.easingValues);
1047 anim->setEasing(std::move(easing));
1049 handler->setAnimPeriod(property.delay, property.delay + property.duration);
1050 handler->document()->animator()->appendAnimation(node, anim);
1051 handler->document()->setAnimated(
true);
1056 const QXmlStreamAttributes &attributes)
1058 QSvgCssProperties cssProperties(attributes);
1059 QSvgOffsetProperty offset = cssProperties.offset();
1064 QSvgOffsetStyle *offsetStyle =
new QSvgOffsetStyle();
1065 offsetStyle->setPath(offset.path.value());
1066 offsetStyle->setRotateAngle(offset.angle);
1067 offsetStyle->setRotateType(offset.rotateType);
1068 offsetStyle->setDistance(offset.distance);
1069 node->appendStyleProperty(offsetStyle, QString());
1074QtSvg::Options QSvgHandler::options()
const
1079QtSvg::AnimatorType QSvgHandler::animatorType()
const
1081 return m_animatorType;
1084bool QSvgHandler::trustedSourceMode()
const
1086 return m_options.testFlag(QtSvg::AssumeTrustedSource);
1091 QStringList lst = str.split(QLatin1Char(
','), Qt::SkipEmptyParts);
1096 const QXmlStreamAttributes &attributes)
1098 QStringList features;
1099 QStringList extensions;
1100 QStringList languages;
1101 QStringList formats;
1103 QStringView xmlClassStr;
1105 for (
const QXmlStreamAttribute &attribute : attributes) {
1106 QStringView name = attribute.qualifiedName();
1109 QStringView value = attribute.value();
1110 switch (name.at(0).unicode()) {
1112 if (name == QLatin1String(
"class"))
1113 xmlClassStr = value;
1116 if (name == QLatin1String(
"requiredFeatures"))
1117 features = stringToList(value.toString());
1118 else if (name == QLatin1String(
"requiredExtensions"))
1119 extensions = stringToList(value.toString());
1120 else if (name == QLatin1String(
"requiredFormats"))
1121 formats = stringToList(value.toString());
1122 else if (name == QLatin1String(
"requiredFonts"))
1123 fonts = stringToList(value.toString());
1126 if (name == QLatin1String(
"systemLanguage"))
1127 languages = stringToList(value.toString());
1134 node->setRequiredFeatures(features);
1135 node->setRequiredExtensions(extensions);
1136 node->setRequiredLanguages(languages);
1137 node->setRequiredFormats(formats);
1138 node->setRequiredFonts(fonts);
1139 node->setNodeId(someId(attributes));
1140 node->setXmlClass(xmlClassStr.toString());
1149 if (attributes.opacity.isEmpty())
1152 const QStringView value = attributes.opacity.trimmed();
1155 qreal op = value.toDouble(&ok);
1158 QSvgOpacityStyle *opacity =
new QSvgOpacityStyle(qBound(qreal(0.0), op, qreal(1.0)));
1159 node->appendStyleProperty(opacity, attributes.id);
1165#define NOOP qDebug()<<"Operation: "<<op<<" is not implemented"
1166 if (op == QLatin1String(
"clear")) {
1167 return QPainter::CompositionMode_Clear;
1168 }
else if (op == QLatin1String(
"src")) {
1169 return QPainter::CompositionMode_Source;
1170 }
else if (op == QLatin1String(
"dst")) {
1171 return QPainter::CompositionMode_Destination;
1172 }
else if (op == QLatin1String(
"src-over")) {
1173 return QPainter::CompositionMode_SourceOver;
1174 }
else if (op == QLatin1String(
"dst-over")) {
1175 return QPainter::CompositionMode_DestinationOver;
1176 }
else if (op == QLatin1String(
"src-in")) {
1177 return QPainter::CompositionMode_SourceIn;
1178 }
else if (op == QLatin1String(
"dst-in")) {
1179 return QPainter::CompositionMode_DestinationIn;
1180 }
else if (op == QLatin1String(
"src-out")) {
1181 return QPainter::CompositionMode_SourceOut;
1182 }
else if (op == QLatin1String(
"dst-out")) {
1183 return QPainter::CompositionMode_DestinationOut;
1184 }
else if (op == QLatin1String(
"src-atop")) {
1185 return QPainter::CompositionMode_SourceAtop;
1186 }
else if (op == QLatin1String(
"dst-atop")) {
1187 return QPainter::CompositionMode_DestinationAtop;
1188 }
else if (op == QLatin1String(
"xor")) {
1189 return QPainter::CompositionMode_Xor;
1190 }
else if (op == QLatin1String(
"plus")) {
1191 return QPainter::CompositionMode_Plus;
1192 }
else if (op == QLatin1String(
"multiply")) {
1193 return QPainter::CompositionMode_Multiply;
1194 }
else if (op == QLatin1String(
"screen")) {
1195 return QPainter::CompositionMode_Screen;
1196 }
else if (op == QLatin1String(
"overlay")) {
1197 return QPainter::CompositionMode_Overlay;
1198 }
else if (op == QLatin1String(
"darken")) {
1199 return QPainter::CompositionMode_Darken;
1200 }
else if (op == QLatin1String(
"lighten")) {
1201 return QPainter::CompositionMode_Lighten;
1202 }
else if (op == QLatin1String(
"color-dodge")) {
1203 return QPainter::CompositionMode_ColorDodge;
1204 }
else if (op == QLatin1String(
"color-burn")) {
1205 return QPainter::CompositionMode_ColorBurn;
1206 }
else if (op == QLatin1String(
"hard-light")) {
1207 return QPainter::CompositionMode_HardLight;
1208 }
else if (op == QLatin1String(
"soft-light")) {
1209 return QPainter::CompositionMode_SoftLight;
1210 }
else if (op == QLatin1String(
"difference")) {
1211 return QPainter::CompositionMode_Difference;
1212 }
else if (op == QLatin1String(
"exclusion")) {
1213 return QPainter::CompositionMode_Exclusion;
1218 return QPainter::CompositionMode_SourceOver;
1225 if (attributes.compOp.isEmpty())
1227 QStringView value = attributes.compOp.trimmed();
1229 if (!value.isEmpty()) {
1230 QSvgCompOpStyle *compop =
new QSvgCompOpStyle(svgToQtCompositionMode(value));
1231 node->appendStyleProperty(compop, attributes.id);
1237 if (str == QLatin1String(
"inline")) {
1238 return QSvgNode::InlineMode;
1239 }
else if (str == QLatin1String(
"block")) {
1240 return QSvgNode::BlockMode;
1241 }
else if (str == QLatin1String(
"list-item")) {
1242 return QSvgNode::ListItemMode;
1243 }
else if (str == QLatin1String(
"run-in")) {
1244 return QSvgNode::RunInMode;
1245 }
else if (str == QLatin1String(
"compact")) {
1246 return QSvgNode::CompactMode;
1247 }
else if (str == QLatin1String(
"marker")) {
1248 return QSvgNode::MarkerMode;
1249 }
else if (str == QLatin1String(
"table")) {
1250 return QSvgNode::TableMode;
1251 }
else if (str == QLatin1String(
"inline-table")) {
1252 return QSvgNode::InlineTableMode;
1253 }
else if (str == QLatin1String(
"table-row-group")) {
1254 return QSvgNode::TableRowGroupMode;
1255 }
else if (str == QLatin1String(
"table-header-group")) {
1256 return QSvgNode::TableHeaderGroupMode;
1257 }
else if (str == QLatin1String(
"table-footer-group")) {
1258 return QSvgNode::TableFooterGroupMode;
1259 }
else if (str == QLatin1String(
"table-row")) {
1260 return QSvgNode::TableRowMode;
1261 }
else if (str == QLatin1String(
"table-column-group")) {
1262 return QSvgNode::TableColumnGroupMode;
1263 }
else if (str == QLatin1String(
"table-column")) {
1264 return QSvgNode::TableColumnMode;
1265 }
else if (str == QLatin1String(
"table-cell")) {
1266 return QSvgNode::TableCellMode;
1267 }
else if (str == QLatin1String(
"table-caption")) {
1268 return QSvgNode::TableCaptionMode;
1269 }
else if (str == QLatin1String(
"none")) {
1270 return QSvgNode::NoneMode;
1272 return QSvgNode::InheritMode;
1274 return QSvgNode::BlockMode;
1281 if (attributes.display.isEmpty())
1283 QStringView displayStr = attributes.display.trimmed();
1285 if (!displayStr.isEmpty()) {
1286 node->setDisplayMode(displayStringToEnum(displayStr));
1292 if (attribute.isEmpty())
1293 return std::nullopt;
1295 constexpr QLatin1String urlKeyword(
"url");
1296 QStringView attrStr = attribute.trimmed();
1297 if (attrStr.startsWith(urlKeyword))
1298 attrStr.slice(urlKeyword.size());
1299 QStringView id = idFromUrl(attrStr);
1300 if (id.startsWith(QLatin1Char(
'#')))
1307 QSvgHandler *handler)
1312 if (
auto id = getAttributeId(attributes.mask))
1313 node->setMaskId(id->toString());
1314 if (
auto id = getAttributeId(attributes.markerStart))
1315 node->setMarkerStartId(id->toString());
1316 if (
auto id = getAttributeId(attributes.markerMid))
1317 node->setMarkerMidId(id->toString());
1318 if (
auto id = getAttributeId(attributes.markerEnd))
1319 node->setMarkerEndId(id->toString());
1320 if (
auto id = getAttributeId(attributes.filter))
1321 node->setFilterId(id->toString());
1328 if (attributes.imageRendering.isEmpty())
1331 QStringView ir = attributes.imageRendering.trimmed();
1332 QSvgQualityStyle *p =
new QSvgQualityStyle(0);
1333 if (ir == QLatin1String(
"auto"))
1334 p->setImageRendering(QSvgQualityStyle::ImageRenderingAuto);
1335 else if (ir == QLatin1String(
"optimizeSpeed"))
1336 p->setImageRendering(QSvgQualityStyle::ImageRenderingOptimizeSpeed);
1337 else if (ir == QLatin1String(
"optimizeQuality"))
1338 p->setImageRendering(QSvgQualityStyle::ImageRenderingOptimizeQuality);
1339 node->appendStyleProperty(p, attributes.id);
1343 const QXmlStreamAttributes &attributes,
1344 QSvgHandler *handler)
1352#ifndef QT_NO_CSSPARSER
1353 QXmlStreamAttributes cssAttributes;
1354 handler->cssHandler().styleLookup(node, cssAttributes);
1356 QStringView style = attributes.value(QLatin1String(
"style"));
1357 if (!style.isEmpty())
1358 handler->cssHandler().parseCSStoXMLAttrs(style.toString(), cssAttributes);
1359 svgAttributes.setAttributes(cssAttributes, handler);
1361 parseOffsetPath(node, cssAttributes);
1363 parseCssAnimations(node, cssAttributes, handler);
1382 const QXmlStreamAttributes &attributes,
1385 Q_UNUSED(parent); Q_UNUSED(attributes);
1390 const QXmlStreamAttributes &attributes,
1391 QSvgAnimateNode *anim,
1392 QSvgHandler *handler)
1394 const QStringView beginStr = attributes.value(QLatin1String(
"begin"));
1395 const QStringView durStr = attributes.value(QLatin1String(
"dur"));
1396 const QStringView endStr = attributes.value(QLatin1String(
"end"));
1397 const QStringView repeatStr = attributes.value(QLatin1String(
"repeatCount"));
1398 const QStringView fillStr = attributes.value(QLatin1String(
"fill"));
1399 const QStringView addtv = attributes.value(QLatin1String(
"additive"));
1400 QStringView linkId = attributes.value(QLatin1String(
"xlink:href"));
1402 if (linkId.isEmpty())
1403 linkId = attributes.value(QLatin1String(
"href"));
1405 linkId = linkId.mid(1);
1408 int begin = parseClockValue(beginStr, &ok);
1411 int dur = parseClockValue(durStr, &ok);
1414 int end = parseClockValue(endStr, &ok);
1417 qreal repeatCount = (repeatStr == QLatin1String(
"indefinite")) ? -1 :
1418 qMax(1.0, QSvgUtils::toDouble(repeatStr));
1420 QSvgAnimateNode::Fill fill = (fillStr == QLatin1String(
"freeze")) ? QSvgAnimateNode::Freeze :
1421 QSvgAnimateNode::Remove;
1423 QSvgAnimateNode::Additive additive = (addtv == QLatin1String(
"sum")) ? QSvgAnimateNode::Sum :
1424 QSvgAnimateNode::Replace;
1426 anim->setRunningTime(begin, dur, end, 0);
1427 anim->setRepeatCount(repeatCount);
1428 anim->setFill(fill);
1429 anim->setAdditiveType(additive);
1430 anim->setLinkId(linkId.toString());
1432 parent->document()->setAnimated(
true);
1434 handler->setAnimPeriod(begin, begin + dur);
1443 qreal spacing = 1.0f / (count - 1);
1444 for (uint i = 0; i < count; i++) {
1445 keyFrames.append(i * spacing);
1450 const QXmlStreamAttributes &attributes,
1451 QSvgHandler *handler)
1453 const QStringView fromStr = attributes.value(QLatin1String(
"from"));
1454 const QStringView toStr = attributes.value(QLatin1String(
"to"));
1455 const QStringView valuesStr = attributes.value(QLatin1String(
"values"));
1456 const QString targetStr = attributes.value(QLatin1String(
"attributeName")).toString();
1458 if (targetStr != QLatin1String(
"fill") && targetStr != QLatin1String(
"stroke"))
1461 QList<QColor> colors;
1462 if (valuesStr.isEmpty()) {
1463 QColor startColor, endColor;
1464 resolveColor(fromStr, startColor, handler);
1465 resolveColor(toStr, endColor, handler);
1467 colors.append(startColor);
1468 colors.append(endColor);
1470 for (
auto part : qTokenize(valuesStr, u';')) {
1472 resolveColor(part, color, handler);
1473 colors.append(color);
1477 QSvgAnimatedPropertyColor *prop =
static_cast<QSvgAnimatedPropertyColor *>
1478 (QSvgAbstractAnimatedProperty::createAnimatedProperty(targetStr));
1482 prop->setColors(colors);
1484 QList<qreal> keyFrames;
1485 generateKeyFrames(keyFrames, colors.size());
1486 prop->setKeyFrames(keyFrames);
1488 QSvgAnimateColor *anim =
new QSvgAnimateColor(parent);
1489 anim->appendProperty(prop);
1491 if (!parseBaseAnimate(parent, attributes, anim, handler)) {
1500 const QXmlStreamAttributes &attributes,
1503 Q_UNUSED(parent); Q_UNUSED(attributes);
1509 QList<qreal> list = parseNumbersList(s);
1511 for (
int i = 3 - list.size(); i > 0; --i)
1516 const QXmlStreamAttributes &attributes,
1517 QSvgHandler *handler)
1519 const QStringView typeStr = attributes.value(QLatin1String(
"type"));
1520 const QStringView values = attributes.value(QLatin1String(
"values"));
1521 const QStringView fromStr = attributes.value(QLatin1String(
"from"));
1522 const QStringView toStr = attributes.value(QLatin1String(
"to"));
1523 const QStringView byStr = attributes.value(QLatin1String(
"by"));
1526 if (values.isEmpty()) {
1528 if (fromStr.isEmpty()) {
1529 if (!byStr.isEmpty()) {
1533 parseNumberTriplet(vals, s = byStr.constData());
1539 if (!toStr.isEmpty()) {
1541 parseNumberTriplet(vals, s = fromStr.constData());
1542 parseNumberTriplet(vals, s = toStr.constData());
1543 }
else if (!byStr.isEmpty()) {
1545 parseNumberTriplet(vals, s = fromStr.constData());
1546 parseNumberTriplet(vals, s = byStr.constData());
1547 for (
int i = vals.size() - 3; i < vals.size(); ++i)
1548 vals[i] += vals[i - 3];
1554 const QChar *s = values.constData();
1555 while (s && s != values.cend()) {
1556 parseNumberTriplet(vals, s);
1557 if (s == values.cend())
1562 if (vals.size() % 3 != 0)
1566 QList<QSvgAnimatedPropertyTransform::TransformComponent> components;
1567 for (
int i = 0; i < vals.size(); i+=3) {
1568 QSvgAnimatedPropertyTransform::TransformComponent component;
1569 if (typeStr == QLatin1String(
"translate")) {
1570 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Translate;
1571 component.values.append(vals.at(i));
1572 component.values.append(vals.at(i + 1));
1573 }
else if (typeStr == QLatin1String(
"scale")) {
1574 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Scale;
1575 component.values.append(vals.at(i));
1576 component.values.append(vals.at(i + 1));
1577 }
else if (typeStr == QLatin1String(
"rotate")) {
1578 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Rotate;
1579 component.values.append(vals.at(i));
1580 component.values.append(vals.at(i + 1));
1581 component.values.append(vals.at(i + 2));
1582 }
else if (typeStr == QLatin1String(
"skewX")) {
1583 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Skew;
1584 component.values.append(vals.at(i));
1585 component.values.append(0);
1586 }
else if (typeStr == QLatin1String(
"skewY")) {
1587 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Skew;
1588 component.values.append(0);
1589 component.values.append(vals.at(i));
1593 components.append(component);
1596 QSvgAnimatedPropertyTransform *prop =
static_cast<QSvgAnimatedPropertyTransform *>
1597 (QSvgAbstractAnimatedProperty::createAnimatedProperty(QLatin1String(
"transform")));
1601 prop->appendComponents(components);
1603 prop->setTransformCount(1);
1604 QList<qreal> keyFrames;
1605 generateKeyFrames(keyFrames, vals.size() / 3);
1606 prop->setKeyFrames(keyFrames);
1608 QSvgAnimateTransform *anim =
new QSvgAnimateTransform(parent);
1609 anim->appendProperty(prop);
1611 if (!parseBaseAnimate(parent, attributes, anim, handler)) {
1620 const QXmlStreamAttributes &attributes,
1623 Q_UNUSED(parent); Q_UNUSED(attributes);
1628 const QXmlStreamAttributes &attributes,
1631 Q_UNUSED(parent); Q_UNUSED(attributes);
1636 const QXmlStreamAttributes &attributes,
1639 const QStringView cx = attributes.value(QLatin1String(
"cx"));
1640 const QStringView cy = attributes.value(QLatin1String(
"cy"));
1641 const QStringView r = attributes.value(QLatin1String(
"r"));
1642 qreal ncx = QSvgUtils::toDouble(cx);
1643 qreal ncy = QSvgUtils::toDouble(cy);
1644 qreal nr = QSvgUtils::toDouble(r);
1648 QRectF rect(ncx-nr, ncy-nr, nr*2, nr*2);
1649 QSvgNode *circle =
new QSvgCircle(parent, rect);
1654 const QXmlStreamAttributes &attributes,
1657 Q_UNUSED(attributes);
1658 QSvgDefs *defs =
new QSvgDefs(parent);
1663 const QXmlStreamAttributes &attributes,
1666 Q_UNUSED(parent); Q_UNUSED(attributes);
1671 const QXmlStreamAttributes &attributes,
1674 const QStringView cx = attributes.value(QLatin1String(
"cx"));
1675 const QStringView cy = attributes.value(QLatin1String(
"cy"));
1676 const QStringView rx = attributes.value(QLatin1String(
"rx"));
1677 const QStringView ry = attributes.value(QLatin1String(
"ry"));
1678 qreal ncx = QSvgUtils::toDouble(cx);
1679 qreal ncy = QSvgUtils::toDouble(cy);
1680 qreal nrx = QSvgUtils::toDouble(rx);
1681 qreal nry = QSvgUtils::toDouble(ry);
1683 QRectF rect(ncx-nrx, ncy-nry, nrx*2, nry*2);
1684 QSvgNode *ellipse =
new QSvgEllipse(parent, rect);
1689 const QXmlStreamAttributes &attributes,
1692 const QStringView hax = attributes.value(QLatin1String(
"horiz-adv-x"));
1693 QString myId = someId(attributes);
1695 qreal horizAdvX = QSvgUtils::toDouble(hax);
1697 while (parent && parent->type() != QSvgNode::Doc) {
1698 parent = parent->parent();
1701 if (parent && !myId.isEmpty()) {
1702 QSvgDocument *doc =
static_cast<QSvgDocument*>(parent);
1703 QSvgFont *font = doc->svgFont(myId);
1705 font =
new QSvgFont(horizAdvX);
1706 font->setFamilyName(myId);
1707 doc->addSvgFont(font);
1709 return new QSvgFontStyle(font, doc);
1715 const QXmlStreamAttributes &attributes,
1718 if (parent->type() != QSvgStyleProperty::FONT) {
1722 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
1723 QSvgFont *font = style->svgFont();
1724 const QStringView name = attributes.value(QLatin1String(
"font-family"));
1725 const QStringView unitsPerEmStr = attributes.value(QLatin1String(
"units-per-em"));
1727 qreal unitsPerEm = QSvgUtils::toDouble(unitsPerEmStr);
1729 unitsPerEm = QSvgFont::DEFAULT_UNITS_PER_EM;
1731 if (!name.isEmpty())
1732 font->setFamilyName(name.toString());
1733 font->setUnitsPerEm(unitsPerEm);
1735 if (!font->familyName().isEmpty())
1736 if (!style->doc()->svgFont(font->familyName()))
1737 style->doc()->addSvgFont(font);
1743 const QXmlStreamAttributes &attributes,
1746 if (parent->type() != QSvgStyleProperty::FONT) {
1750 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
1751 QSvgFont *font = style->svgFont();
1752 const QStringView name = attributes.value(QLatin1String(
"name"));
1754 if (!name.isEmpty())
1755 font->setFamilyName(name.toString());
1757 if (!font->familyName().isEmpty())
1758 if (!style->doc()->svgFont(font->familyName()))
1759 style->doc()->addSvgFont(font);
1765 const QXmlStreamAttributes &attributes,
1768 Q_UNUSED(parent); Q_UNUSED(attributes);
1773 const QXmlStreamAttributes &attributes,
1776 Q_UNUSED(parent); Q_UNUSED(attributes);
1781 const QXmlStreamAttributes &attributes,
1784 Q_UNUSED(parent); Q_UNUSED(attributes);
1789 const QXmlStreamAttributes &attributes,
1792 Q_UNUSED(attributes);
1793 QSvgG *node =
new QSvgG(parent);
1798 const QXmlStreamAttributes &attributes,
1801 if (parent->type() != QSvgStyleProperty::FONT) {
1805 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
1806 QSvgFont *font = style->svgFont();
1807 return createSvgGlyph(font, attributes,
false);
1811 const QXmlStreamAttributes &attributes,
1814 Q_UNUSED(parent); Q_UNUSED(attributes);
1819 const QXmlStreamAttributes &attributes,
1822 Q_UNUSED(parent); Q_UNUSED(attributes);
1827 const QXmlStreamAttributes &attributes,
1828 QSvgHandler *handler)
1830 const QStringView x = attributes.value(QLatin1String(
"x"));
1831 const QStringView y = attributes.value(QLatin1String(
"y"));
1832 const QStringView width = attributes.value(QLatin1String(
"width"));
1833 const QStringView height = attributes.value(QLatin1String(
"height"));
1834 QString filename = attributes.value(QLatin1String(
"xlink:href")).toString();
1836 filename = attributes.value(QLatin1String(
"href")).toString();
1837 qreal nx = QSvgUtils::toDouble(x);
1838 qreal ny = QSvgUtils::toDouble(y);
1840 qreal nwidth = QSvgUtils::parseLength(width.toString(), &type);
1841 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
1843 qreal nheight = QSvgUtils::parseLength(height.toString(), &type);
1844 nheight = QSvgUtils::convertToPixels(nheight,
false, type);
1846 filename = filename.trimmed();
1847 if (filename.isEmpty()) {
1848 qCWarning(lcSvgHandler) <<
"QSvgHandler: Image filename is empty";
1851 if (nwidth <= 0 || nheight <= 0) {
1852 qCWarning(lcSvgHandler) <<
"QSvgHandler: Width or height for" << filename <<
"image was not greater than 0";
1861 } filenameType = NotLoaded;
1863 if (filename.startsWith(QLatin1String(
"data"))) {
1866 if (qDecodeDataUrl(QUrl{filename}, mimeType, data)) {
1867 image = QImage::fromData(data);
1868 filenameType = LoadedFromData;
1872 if (image.isNull()) {
1873 const auto *file = qobject_cast<QFile *>(handler->device());
1876 if (url.isRelative()) {
1878 filename = info.absoluteDir().absoluteFilePath(filename);
1882 if (handler->trustedSourceMode() || !QImageReader::imageFormat(filename).startsWith(
"svg")) {
1883 image = QImage(filename);
1884 filenameType = LoadedFromFile;
1888 if (image.isNull()) {
1889 qCWarning(lcSvgHandler) <<
"Could not create image from" << filename;
1893 if (image.format() == QImage::Format_ARGB32)
1894 image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
1896 QSvgNode *img =
new QSvgImage(parent,
1898 filenameType == LoadedFromFile ? filename : QString{},
1907 const QXmlStreamAttributes &attributes,
1910 const QStringView x1 = attributes.value(QLatin1String(
"x1"));
1911 const QStringView y1 = attributes.value(QLatin1String(
"y1"));
1912 const QStringView x2 = attributes.value(QLatin1String(
"x2"));
1913 const QStringView y2 = attributes.value(QLatin1String(
"y2"));
1914 qreal nx1 = QSvgUtils::toDouble(x1);
1915 qreal ny1 = QSvgUtils::toDouble(y1);
1916 qreal nx2 = QSvgUtils::toDouble(x2);
1917 qreal ny2 = QSvgUtils::toDouble(y2);
1919 QLineF lineBounds(nx1, ny1, nx2, ny2);
1920 QSvgNode *line =
new QSvgLine(parent, lineBounds);
1926 const QXmlStreamAttributes &attributes,
1927 QSvgGradientStyle *gradProp,
1928 QSvgHandler *handler)
1930 const QStringView link = attributes.value(QLatin1String(
"xlink:href"));
1931 const QStringView trans = attributes.value(QLatin1String(
"gradientTransform"));
1932 const QStringView spread = attributes.value(QLatin1String(
"spreadMethod"));
1933 const QStringView units = attributes.value(QLatin1String(
"gradientUnits"));
1934 const QStringView colorStr = attributes.value(QLatin1String(
"color"));
1935 const QStringView colorOpacityStr = attributes.value(QLatin1String(
"color-opacity"));
1938 if (constructColor(colorStr, colorOpacityStr, color, handler)) {
1939 handler->popColor();
1940 handler->pushColor(color);
1944 QGradient *grad = gradProp->qgradient();
1945 if (node && !link.isEmpty()) {
1946 QSvgStyleProperty *prop = node->styleProperty(link);
1947 if (prop && prop->type() == QSvgStyleProperty::GRADIENT) {
1948 QSvgGradientStyle *inherited =
1949 static_cast<QSvgGradientStyle*>(prop);
1950 if (!inherited->stopLink().isEmpty()) {
1951 gradProp->setStopLink(inherited->stopLink(), handler->document());
1953 grad->setStops(inherited->qgradient()->stops());
1954 gradProp->setGradientStopsSet(inherited->gradientStopsSet());
1957 matrix = inherited->qtransform();
1959 gradProp->setStopLink(link.toString(), handler->document());
1963 if (!trans.isEmpty()) {
1964 matrix = parseTransformationMatrix(trans);
1965 gradProp->setTransform(matrix);
1966 }
else if (!matrix.isIdentity()) {
1967 gradProp->setTransform(matrix);
1970 if (!spread.isEmpty()) {
1971 if (spread == QLatin1String(
"pad")) {
1972 grad->setSpread(QGradient::PadSpread);
1973 }
else if (spread == QLatin1String(
"reflect")) {
1974 grad->setSpread(QGradient::ReflectSpread);
1975 }
else if (spread == QLatin1String(
"repeat")) {
1976 grad->setSpread(QGradient::RepeatSpread);
1980 if (units.isEmpty() || units == QLatin1String(
"objectBoundingBox")) {
1981 grad->setCoordinateMode(QGradient::ObjectMode);
1986 const QXmlStreamAttributes &attributes,
1987 QSvgHandler *handler)
1989 const QStringView x1 = attributes.value(QLatin1String(
"x1"));
1990 const QStringView y1 = attributes.value(QLatin1String(
"y1"));
1991 const QStringView x2 = attributes.value(QLatin1String(
"x2"));
1992 const QStringView y2 = attributes.value(QLatin1String(
"y2"));
2000 nx1 = convertToNumber(x1);
2002 ny1 = convertToNumber(y1);
2004 nx2 = convertToNumber(x2);
2006 ny2 = convertToNumber(y2);
2008 QSvgNode *itr = node;
2009 while (itr && itr->type() != QSvgNode::Doc) {
2010 itr = itr->parent();
2013 QLinearGradient *grad =
new QLinearGradient(nx1, ny1, nx2, ny2);
2014 grad->setInterpolationMode(QGradient::ComponentInterpolation);
2015 QSvgGradientStyle *prop =
new QSvgGradientStyle(grad);
2016 parseBaseGradient(node, attributes, prop, handler);
2022 const QXmlStreamAttributes &attributes,
2025 Q_UNUSED(parent); Q_UNUSED(attributes);
2030 const QXmlStreamAttributes &attributes,
2033 if (parent->type() != QSvgStyleProperty::FONT) {
2037 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
2038 QSvgFont *font = style->svgFont();
2039 return createSvgGlyph(font, attributes,
true);
2043 const QXmlStreamAttributes &attributes,
2046 Q_UNUSED(parent); Q_UNUSED(attributes);
2051 const QXmlStreamAttributes &attributes,
2054 Q_UNUSED(parent); Q_UNUSED(attributes);
2059 const QXmlStreamAttributes &,
2066 const QXmlStreamAttributes &attributes,
2067 QSvgHandler *handler)
2069 const QStringView x = attributes.value(QLatin1String(
"x"));
2070 const QStringView y = attributes.value(QLatin1String(
"y"));
2071 const QStringView width = attributes.value(QLatin1String(
"width"));
2072 const QStringView height = attributes.value(QLatin1String(
"height"));
2073 const QStringView mU = attributes.value(QLatin1String(
"maskUnits"));
2074 const QStringView mCU = attributes.value(QLatin1String(
"maskContentUnits"));
2076 QtSvg::
UnitTypes nmU = mU.contains(QLatin1String(
"userSpaceOnUse")) ?
2079 QtSvg::
UnitTypes nmCU = mCU.contains(QLatin1String(
"objectBoundingBox")) ?
2089 qreal nx = QSvgUtils::parseLength(x, &type, &ok);
2090 nx = QSvgUtils::convertToPixels(nx,
true, type);
2091 if (x.isEmpty() || !ok) {
2095 nx = nx / 100. * handler->document()->viewBox().width();
2100 qreal ny = QSvgUtils::parseLength(y, &type, &ok);
2101 ny = QSvgUtils::convertToPixels(ny,
true, type);
2102 if (y.isEmpty() || !ok) {
2106 ny = ny / 100. * handler->document()->viewBox().height();
2111 qreal nwidth = QSvgUtils::parseLength(width, &type, &ok);
2112 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
2113 if (width.isEmpty() || !ok) {
2117 nwidth = nwidth / 100. * handler->document()->viewBox().width();
2119 nwidth = nwidth / 100.;
2122 qreal nheight = QSvgUtils::parseLength(height, &type, &ok);
2123 nheight = QSvgUtils::convertToPixels(nheight,
true, type);
2124 if (height.isEmpty() || !ok) {
2128 nheight = nheight / 100. * handler->document()->viewBox().height();
2130 nheight = nheight / 100.;
2133 QRectF bounds(nx, ny, nwidth, nheight);
2134 if (bounds.isEmpty())
2137 QSvgNode *mask =
new QSvgMask(parent, QSvgRectF(bounds, nmUx, nmUy, nmUw, nmUh), nmCU);
2144 const QStringView xStr = attributes.value(QLatin1String(
"x"));
2145 const QStringView yStr = attributes.value(QLatin1String(
"y"));
2146 const QStringView widthStr = attributes.value(QLatin1String(
"width"));
2147 const QStringView heightStr = attributes.value(QLatin1String(
"height"));
2150 if (!xStr.isEmpty()) {
2152 x = QSvgUtils::parseLength(xStr, &type);
2154 x = QSvgUtils::convertToPixels(x,
true, type);
2164 if (!yStr.isEmpty()) {
2166 y = QSvgUtils::parseLength(yStr, &type);
2168 y = QSvgUtils::convertToPixels(y,
false, type);
2178 if (!widthStr.isEmpty()) {
2180 width = QSvgUtils::parseLength(widthStr, &type);
2182 width = QSvgUtils::convertToPixels(width,
true, type);
2189 rect->setWidth(width);
2192 if (!heightStr.isEmpty()) {
2194 height = QSvgUtils::parseLength(heightStr, &type);
2196 height = QSvgUtils::convertToPixels(height,
false, type);
2203 rect->setHeight(height);
2208 const QXmlStreamAttributes &attributes,
2209 QSvgHandler *handler)
2211 const QStringView fU = attributes.value(QLatin1String(
"filterUnits"));
2212 const QStringView pU = attributes.value(QLatin1String(
"primitiveUnits"));
2214 const QtSvg::
UnitTypes filterUnits = fU.contains(QLatin1String(
"userSpaceOnUse")) ?
2217 const QtSvg::
UnitTypes primitiveUnits = pU.contains(QLatin1String(
"objectBoundingBox")) ?
2225 qreal width = handler->document()->viewBox().width();
2226 qreal height = handler->document()->viewBox().height();
2227 rect = QSvgRectF(QRectF(-0.1 * width, -0.1 * height, 1.2 * width, 1.2 * height),
2231 rect = QSvgRectF(QRectF(-0.1, -0.1, 1.2, 1.2),
2236 parseFilterBounds(attributes, &rect);
2238 QSvgNode *filter =
new QSvgFilterContainer(parent, rect, filterUnits, primitiveUnits);
2243 QString *outString, QSvgRectF *rect)
2245 *inString = attributes.value(QLatin1String(
"in")).toString();
2246 *outString = attributes.value(QLatin1String(
"result")).toString();
2252 *rect = QSvgRectF(QRectF(0, 0, 1.0, 1.0),
2258 parseFilterBounds(attributes, rect);
2262 const QXmlStreamAttributes &attributes,
2265 const QStringView typeString = attributes.value(QLatin1String(
"type"));
2266 const QStringView valuesString = attributes.value(QLatin1String(
"values"));
2268 QString inputString;
2269 QString outputString;
2272 QSvgFeColorMatrix::ColorShiftType type;
2273 QSvgFeColorMatrix::Matrix values;
2276 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2278 if (typeString.startsWith(QLatin1String(
"saturate")))
2279 type = QSvgFeColorMatrix::ColorShiftType::Saturate;
2280 else if (typeString.startsWith(QLatin1String(
"hueRotate")))
2281 type = QSvgFeColorMatrix::ColorShiftType::HueRotate;
2282 else if (typeString.startsWith(QLatin1String(
"luminanceToAlpha")))
2283 type = QSvgFeColorMatrix::ColorShiftType::LuminanceToAlpha;
2285 type = QSvgFeColorMatrix::ColorShiftType::Matrix;
2287 if (!valuesString.isEmpty()) {
2288 const auto valueStringList = splitWithDelimiter(valuesString);
2289 for (
int i = 0, j = 0; i < qMin(20, valueStringList.size()); i++) {
2291 qreal v = QSvgUtils::toDouble(valueStringList.at(i), &ok);
2293 values.data()[j] = v;
2298 values.setToIdentity();
2301 QSvgNode *filter =
new QSvgFeColorMatrix(parent, inputString, outputString, rect,
2307 const QXmlStreamAttributes &attributes,
2310 const QStringView edgeModeString = attributes.value(QLatin1String(
"edgeMode"));
2311 const QStringView stdDeviationString = attributes.value(QLatin1String(
"stdDeviation"));
2313 QString inputString;
2314 QString outputString;
2317 QSvgFeGaussianBlur::EdgeMode edgemode = QSvgFeGaussianBlur::EdgeMode::Duplicate;
2319 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2320 qreal stdDeviationX = 0;
2321 qreal stdDeviationY = 0;
2323 stdDeviationX = qMax(0., QSvgUtils::toDouble(stdDeviationString.split(u" ").first()));
2324 stdDeviationY = qMax(0., QSvgUtils::toDouble(stdDeviationString.split(u" ").last()));
2326 stdDeviationY = stdDeviationX = qMax(0., QSvgUtils::toDouble(stdDeviationString));
2329 if (edgeModeString.startsWith(QLatin1String(
"wrap")))
2330 edgemode = QSvgFeGaussianBlur::EdgeMode::Wrap;
2331 else if (edgeModeString.startsWith(QLatin1String(
"none")))
2332 edgemode = QSvgFeGaussianBlur::EdgeMode::None;
2334 QSvgNode *filter =
new QSvgFeGaussianBlur(parent, inputString, outputString, rect,
2335 stdDeviationX, stdDeviationY, edgemode);
2340 const QXmlStreamAttributes &attributes,
2343 QStringView dxString = attributes.value(QLatin1String(
"dx"));
2344 QStringView dyString = attributes.value(QLatin1String(
"dy"));
2346 QString inputString;
2347 QString outputString;
2350 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2353 if (!dxString.isEmpty()) {
2355 dx = QSvgUtils::parseLength(dxString, &type);
2357 dx = QSvgUtils::convertToPixels(dx,
true, type);
2361 if (!dyString.isEmpty()) {
2363 dy = QSvgUtils::parseLength(dyString, &type);
2365 dy = QSvgUtils::convertToPixels(dy,
true, type);
2368 QSvgNode *filter =
new QSvgFeOffset(parent, inputString, outputString, rect,
2374 const QXmlStreamAttributes &attributes,
2377 const QStringView in2String = attributes.value(QLatin1String(
"in2"));
2378 const QStringView operatorString = attributes.value(QLatin1String(
"operator"));
2379 const QStringView k1String = attributes.value(QLatin1String(
"k1"));
2380 const QStringView k2String = attributes.value(QLatin1String(
"k2"));
2381 const QStringView k3String = attributes.value(QLatin1String(
"k3"));
2382 const QStringView k4String = attributes.value(QLatin1String(
"k4"));
2384 QString inputString;
2385 QString outputString;
2388 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2390 QSvgFeComposite::Operator op = QSvgFeComposite::Operator::Over;
2391 if (operatorString.startsWith(QLatin1String(
"in")))
2392 op = QSvgFeComposite::Operator::In;
2393 else if (operatorString.startsWith(QLatin1String(
"out")))
2394 op = QSvgFeComposite::Operator::Out;
2395 else if (operatorString.startsWith(QLatin1String(
"atop")))
2396 op = QSvgFeComposite::Operator::Atop;
2397 else if (operatorString.startsWith(QLatin1String(
"xor")))
2398 op = QSvgFeComposite::Operator::Xor;
2399 else if (operatorString.startsWith(QLatin1String(
"lighter")))
2400 op = QSvgFeComposite::Operator::Lighter;
2401 else if (operatorString.startsWith(QLatin1String(
"arithmetic")))
2402 op = QSvgFeComposite::Operator::Arithmetic;
2404 QVector4D k(0, 0, 0, 0);
2406 if (op == QSvgFeComposite::Operator::Arithmetic) {
2408 qreal v = QSvgUtils::toDouble(k1String, &ok);
2411 v = QSvgUtils::toDouble(k2String, &ok);
2414 v = QSvgUtils::toDouble(k3String, &ok);
2417 v = QSvgUtils::toDouble(k4String, &ok);
2422 QSvgNode *filter =
new QSvgFeComposite(parent, inputString, outputString, rect,
2423 in2String.toString(), op, k);
2429 const QXmlStreamAttributes &attributes,
2432 QString inputString;
2433 QString outputString;
2436 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2438 QSvgNode *filter =
new QSvgFeMerge(parent, inputString, outputString, rect);
2443 const QXmlStreamAttributes &attributes,
2444 QSvgHandler *handler)
2446 QStringView colorStr = attributes.value(QLatin1String(
"flood-color"));
2447 const QStringView opacityStr = attributes.value(QLatin1String(
"flood-opacity"));
2450 if (!constructColor(colorStr, opacityStr, color, handler)) {
2451 color = QColor(Qt::black);
2452 if (opacityStr.isEmpty())
2453 color.setAlphaF(1.0);
2455 setAlpha(opacityStr, &color);
2458 QString inputString;
2459 QString outputString;
2462 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2464 QSvgNode *filter =
new QSvgFeFlood(parent, inputString, outputString, rect, color);
2469 const QXmlStreamAttributes &attributes,
2472 QString inputString;
2473 QString outputString;
2476 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2478 QSvgNode *filter =
new QSvgFeMergeNode(parent, inputString, outputString, rect);
2483 const QXmlStreamAttributes &attributes,
2486 const QStringView in2String = attributes.value(QLatin1String(
"in2"));
2487 const QStringView modeString = attributes.value(QLatin1String(
"mode"));
2489 QString inputString;
2490 QString outputString;
2493 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2495 QSvgFeBlend::Mode mode = QSvgFeBlend::Mode::Normal;
2496 if (modeString.startsWith(QLatin1StringView(
"multiply")))
2497 mode = QSvgFeBlend::Mode::Multiply;
2498 else if (modeString.startsWith(QLatin1StringView(
"screen")))
2499 mode = QSvgFeBlend::Mode::Screen;
2500 else if (modeString.startsWith(QLatin1StringView(
"darken")))
2501 mode = QSvgFeBlend::Mode::Darken;
2502 else if (modeString.startsWith(QLatin1StringView(
"lighten")))
2503 mode = QSvgFeBlend::Mode::Lighten;
2505 QSvgNode *filter =
new QSvgFeBlend(parent, inputString, outputString, rect,
2506 in2String.toString(), mode);
2511 const QXmlStreamAttributes &attributes,
2514 QString inputString;
2515 QString outputString;
2518 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2520 QSvgNode *filter =
new QSvgFeUnsupported(parent, inputString, outputString, rect);
2526 QList<QStringView> viewBoxValues;
2529 viewBoxValues = splitWithDelimiter(str);
2530 if (viewBoxValues.size() == 4) {
2532 qreal x = QSvgUtils::parseLength(viewBoxValues.at(0).trimmed(), &type);
2533 qreal y = QSvgUtils::parseLength(viewBoxValues.at(1).trimmed(), &type);
2534 qreal w = QSvgUtils::parseLength(viewBoxValues.at(2).trimmed(), &type);
2535 qreal h = QSvgUtils::parseLength(viewBoxValues.at(3).trimmed(), &type);
2536 return QRectF(x, y, w, h);
2538 return std::nullopt;
2542 QRectF *rect, QRectF *viewBox, QPointF *refPoint,
2543 QSvgSymbolLike::PreserveAspectRatios *aspect,
2544 QSvgSymbolLike::Overflow *overflow,
2545 bool marker =
false)
2547 const QStringView xStr = attributes.value(QLatin1String(
"x"));
2548 const QStringView yStr = attributes.value(QLatin1String(
"y"));
2549 const QStringView refXStr = attributes.value(QLatin1String(
"refX"));
2550 const QStringView refYStr = attributes.value(QLatin1String(
"refY"));
2551 const QStringView widthStr = attributes.value(marker ? QLatin1String(
"markerWidth")
2552 : QLatin1String(
"width"));
2553 const QStringView heightStr = attributes.value(marker ? QLatin1String(
"markerHeight")
2554 : QLatin1String(
"height"));
2555 const QStringView pAspectRStr = attributes.value(QLatin1String(
"preserveAspectRatio"));
2556 const QStringView overflowStr = attributes.value(QLatin1String(
"overflow"));
2557 const QStringView viewBoxStr = attributes.value(QLatin1String(
"viewBox"));
2561 if (!xStr.isEmpty()) {
2563 x = QSvgUtils::parseLength(xStr, &type);
2565 x = QSvgUtils::convertToPixels(x,
true, type);
2568 if (!yStr.isEmpty()) {
2570 y = QSvgUtils::parseLength(yStr, &type);
2572 y = QSvgUtils::convertToPixels(y,
false, type);
2575 if (!widthStr.isEmpty()) {
2577 width = QSvgUtils::parseLength(widthStr, &type);
2579 width = QSvgUtils::convertToPixels(width,
true, type);
2582 if (!heightStr.isEmpty()) {
2584 height = QSvgUtils::parseLength(heightStr, &type);
2586 height = QSvgUtils::convertToPixels(height,
false, type);
2589 *rect = QRectF(x, y, width, height);
2592 if (!refXStr.isEmpty()) {
2594 x = QSvgUtils::parseLength(refXStr, &type);
2596 x = QSvgUtils::convertToPixels(x,
true, type);
2599 if (!refYStr.isEmpty()) {
2601 y = QSvgUtils::parseLength(refYStr, &type);
2603 y = QSvgUtils::convertToPixels(y,
false, type);
2605 *refPoint = QPointF(x,y);
2607 auto viewBoxResult = parseViewBox(viewBoxStr);
2609 *viewBox = *viewBoxResult;
2610 else if (width > 0 && height > 0)
2611 *viewBox = QRectF(0, 0, width, height);
2613 *viewBox = handler->document()->viewBox();
2615 if (viewBox->isNull())
2618 auto pAspectRStrs = pAspectRStr.split(u" ");
2619 QSvgSymbolLike::PreserveAspectRatio aspectX = QSvgSymbolLike::PreserveAspectRatio::xMid;
2620 QSvgSymbolLike::PreserveAspectRatio aspectY = QSvgSymbolLike::PreserveAspectRatio::yMid;
2621 QSvgSymbolLike::PreserveAspectRatio aspectMS = QSvgSymbolLike::PreserveAspectRatio::meet;
2623 for (
auto &pAStr : std::as_const(pAspectRStrs)) {
2624 if (pAStr.startsWith(QLatin1String(
"none"))) {
2625 aspectX = QSvgSymbolLike::PreserveAspectRatio::None;
2626 aspectY = QSvgSymbolLike::PreserveAspectRatio::None;
2628 if (pAStr.startsWith(QLatin1String(
"xMin")))
2629 aspectX = QSvgSymbolLike::PreserveAspectRatio::xMin;
2630 else if (pAStr.startsWith(QLatin1String(
"xMax")))
2631 aspectX = QSvgSymbolLike::PreserveAspectRatio::xMax;
2632 if (pAStr.endsWith(QLatin1String(
"YMin")))
2633 aspectY = QSvgSymbolLike::PreserveAspectRatio::yMin;
2634 else if (pAStr.endsWith(QLatin1String(
"YMax")))
2635 aspectY = QSvgSymbolLike::PreserveAspectRatio::yMax;
2638 if (pAStr.endsWith(QLatin1String(
"slice")))
2639 aspectMS = QSvgSymbolLike::PreserveAspectRatio::slice;
2641 *aspect = aspectX | aspectY | aspectMS;
2648 *overflow = QSvgSymbolLike::Overflow::Hidden;
2650 if (overflowStr.endsWith(QLatin1String(
"auto")))
2651 *overflow = QSvgSymbolLike::Overflow::Auto;
2652 else if (overflowStr.endsWith(QLatin1String(
"visible")))
2653 *overflow = QSvgSymbolLike::Overflow::Visible;
2654 else if (overflowStr.endsWith(QLatin1String(
"hidden")))
2655 *overflow = QSvgSymbolLike::Overflow::Hidden;
2656 else if (overflowStr.endsWith(QLatin1String(
"scroll")))
2657 *overflow = QSvgSymbolLike::Overflow::Scroll;
2663 const QXmlStreamAttributes &attributes,
2664 QSvgHandler *handler)
2666 QRectF rect, viewBox;
2668 QSvgSymbolLike::PreserveAspectRatios aspect;
2669 QSvgSymbolLike::Overflow overflow;
2671 if (!parseSymbolLikeAttributes(attributes, handler, &rect, &viewBox, &refP, &aspect, &overflow))
2674 refP = QPointF(0, 0);
2675 QSvgNode *symbol =
new QSvgSymbol(parent, rect, viewBox, refP, aspect, overflow);
2680 const QXmlStreamAttributes &attributes,
2681 QSvgHandler *handler)
2683 QRectF rect, viewBox;
2685 QSvgSymbolLike::PreserveAspectRatios aspect;
2686 QSvgSymbolLike::Overflow overflow;
2688 const QStringView orientStr = attributes.value(QLatin1String(
"orient"));
2689 const QStringView markerUnitsStr = attributes.value(QLatin1String(
"markerUnits"));
2691 qreal orientationAngle = 0;
2692 QSvgMarker::Orientation orientation;
2693 if (orientStr.startsWith(QLatin1String(
"auto-start-reverse")))
2694 orientation = QSvgMarker::Orientation::AutoStartReverse;
2695 else if (orientStr.startsWith(QLatin1String(
"auto")))
2696 orientation = QSvgMarker::Orientation::Auto;
2698 orientation = QSvgMarker::Orientation::Value;
2701 if (orientStr.endsWith(QLatin1String(
"turn")))
2702 a = 360. * QSvgUtils::toDouble(orientStr.mid(0, orientStr.length()-4), &ok);
2703 else if (orientStr.endsWith(QLatin1String(
"grad")))
2704 a = QSvgUtils::toDouble(orientStr.mid(0, orientStr.length()-4), &ok);
2705 else if (orientStr.endsWith(QLatin1String(
"rad")))
2706 a = 180. /
M_PI * QSvgUtils::toDouble(orientStr.mid(0, orientStr.length()-3), &ok);
2708 a = QSvgUtils::toDouble(orientStr, &ok);
2710 orientationAngle = a;
2713 QSvgMarker::MarkerUnits markerUnits = QSvgMarker::MarkerUnits::StrokeWidth;
2714 if (markerUnitsStr.startsWith(QLatin1String(
"userSpaceOnUse")))
2715 markerUnits = QSvgMarker::MarkerUnits::UserSpaceOnUse;
2717 if (!parseSymbolLikeAttributes(attributes, handler, &rect, &viewBox, &refP, &aspect, &overflow,
true))
2720 QSvgNode *marker =
new QSvgMarker(parent, rect, viewBox, refP, aspect, overflow,
2721 orientation, orientationAngle, markerUnits);
2726 const QXmlStreamAttributes &attributes,
2727 QSvgHandler *handler)
2729 QStringView data = attributes.value(QLatin1String(
"d"));
2731 std::optional<QPainterPath> qpath = QSvgUtils::parsePathDataFast(data,
2732 !handler->trustedSourceMode());
2734 qCWarning(lcSvgHandler,
"Invalid path data; path truncated.");
2738 qpath.value().setFillRule(Qt::WindingFill);
2739 QSvgNode *path =
new QSvgPath(parent, qpath.value());
2744 const QXmlStreamAttributes &attributes,
2747 const QString pointsStr = attributes.value(QLatin1String(
"points")).toString();
2748 const QChar *s = pointsStr.constData();
2749 const QList<qreal> points = parseNumbersList(s);
2750 if (points.size() < 4)
2753 for (
int i = 0; i < poly.size(); ++i)
2754 poly[i] = QPointF(points.at(2 * i), points.at(2 * i + 1));
2756 return new QSvgPolyline(parent, poly);
2758 return new QSvgPolygon(parent, poly);
2762 const QXmlStreamAttributes &attributes,
2765 return createPolyNode(parent, attributes,
false);
2769 const QXmlStreamAttributes &attributes,
2772 return createPolyNode(parent, attributes,
true);
2776 const QXmlStreamAttributes &attributes,
2779 Q_UNUSED(parent); Q_UNUSED(attributes);
2784 const QXmlStreamAttributes &attributes,
2785 QSvgHandler *handler)
2787 const QStringView cx = attributes.value(QLatin1String(
"cx"));
2788 const QStringView cy = attributes.value(QLatin1String(
"cy"));
2789 const QStringView r = attributes.value(QLatin1String(
"r"));
2790 const QStringView fx = attributes.value(QLatin1String(
"fx"));
2791 const QStringView fy = attributes.value(QLatin1String(
"fy"));
2796 ncx = convertToNumber(cx);
2798 ncy = convertToNumber(cy);
2802 nr = convertToNumber(r);
2808 nfx = convertToNumber(fx);
2811 nfy = convertToNumber(fy);
2813 QRadialGradient *grad =
new QRadialGradient(ncx, ncy, nr, nfx, nfy, 0);
2814 grad->setInterpolationMode(QGradient::ComponentInterpolation);
2816 QSvgGradientStyle *prop =
new QSvgGradientStyle(grad);
2817 parseBaseGradient(node, attributes, prop, handler);
2823 const QXmlStreamAttributes &attributes,
2826 const QStringView x = attributes.value(QLatin1String(
"x"));
2827 const QStringView y = attributes.value(QLatin1String(
"y"));
2828 const QStringView width = attributes.value(QLatin1String(
"width"));
2829 const QStringView height = attributes.value(QLatin1String(
"height"));
2830 const QStringView rx = attributes.value(QLatin1String(
"rx"));
2831 const QStringView ry = attributes.value(QLatin1String(
"ry"));
2835 qreal nwidth = QSvgUtils::parseLength(width, &type, &ok);
2838 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
2839 qreal nheight = QSvgUtils::parseLength(height, &type, &ok);
2842 nheight = QSvgUtils::convertToPixels(nheight,
true, type);
2843 qreal nrx = QSvgUtils::toDouble(rx);
2844 qreal nry = QSvgUtils::toDouble(ry);
2847 if (bounds.isEmpty())
2850 if (!rx.isEmpty() && ry.isEmpty())
2852 else if (!ry.isEmpty() && rx.isEmpty())
2858 if (nrx > bounds.width()/2)
2859 nrx = bounds.width()/2;
2860 if (nry > bounds.height()/2)
2861 nry = bounds.height()/2;
2866 nrx *= (100/(bounds.width()/2));
2867 nry *= (100/(bounds.height()/2));
2869 QSvgNode *rect =
new QSvgRect(parent, bounds, nrx, nry);
2874 const QXmlStreamAttributes &attributes,
2877 Q_UNUSED(parent); Q_UNUSED(attributes);
2882 const QXmlStreamAttributes &attributes,
2885 Q_UNUSED(parent); Q_UNUSED(attributes);
2890 const QXmlStreamAttributes &attributes,
2891 QSvgHandler *handler)
2893 Q_UNUSED(parent); Q_UNUSED(attributes);
2894 QStringView solidColorStr = attributes.value(QLatin1String(
"solid-color"));
2895 QStringView solidOpacityStr = attributes.value(QLatin1String(
"solid-opacity"));
2897 if (solidOpacityStr.isEmpty())
2898 solidOpacityStr = attributes.value(QLatin1String(
"opacity"));
2901 if (!constructColor(solidColorStr, solidOpacityStr, color, handler))
2903 QSvgSolidColorStyle *style =
new QSvgSolidColorStyle(color);
2908 const QXmlStreamAttributes &attributes,
2909 QSvgHandler *handler)
2911 if (parent->type() != QSvgStyleProperty::GRADIENT)
2913 QString nodeIdStr = someId(attributes);
2914 QString xmlClassStr = attributes.value(QLatin1String(
"class")).toString();
2920 QSvgDummyNode dummy;
2921 dummy.setNodeId(nodeIdStr);
2922 dummy.setXmlClass(xmlClassStr);
2924 QSvgAttributes attrs(attributes, handler);
2926#ifndef QT_NO_CSSPARSER
2927 QXmlStreamAttributes cssAttributes;
2928 handler->cssHandler().styleLookup(&dummy, cssAttributes);
2929 attrs.setAttributes(cssAttributes, handler);
2931 QXmlStreamAttributes styleCssAttributes;
2932 QStringView style = attributes.value(QLatin1String(
"style"));
2933 if (!style.isEmpty())
2934 handler->cssHandler().parseCSStoXMLAttrs(style.toString(), styleCssAttributes);
2935 attrs.setAttributes(styleCssAttributes, handler);
2939 parseColor(&dummy, attrs, handler);
2941 QSvgGradientStyle *gradientStyle =
2942 static_cast<QSvgGradientStyle*>(parent);
2943 QStringView colorStr = attrs.stopColor;
2947 qreal offset = convertToNumber(attrs.offset, &ok);
2951 if (!constructColor(colorStr, attrs.stopOpacity, color, handler)) {
2953 if (!attrs.stopOpacity.isEmpty())
2954 setAlpha(attrs.stopOpacity, &color);
2957 QGradient *grad = gradientStyle->qgradient();
2959 offset = qMin(qreal(1), qMax(qreal(0), offset));
2960 QGradientStops stops;
2961 if (gradientStyle->gradientStopsSet()) {
2962 stops = grad->stops();
2964 if (offset <= stops.back().first)
2965 offset = stops.back().first + FLT_EPSILON;
2970 if ((stops.size() == 1) || (stops.at(stops.size() - 2).first < 1.0 - FLT_EPSILON)) {
2971 stops.back().first = 1.0 - FLT_EPSILON;
2972 grad->setStops(stops);
2977 grad->setColorAt(offset, color);
2978 gradientStyle->setGradientStopsSet(
true);
2983 const QXmlStreamAttributes &attributes,
2984 QSvgHandler *handler)
2987#ifdef QT_NO_CSSPARSER
2988 Q_UNUSED(attributes);
2991 const QStringView type = attributes.value(QLatin1String(
"type"));
2992 if (type.compare(QLatin1String(
"text/css"), Qt::CaseInsensitive) == 0 || type.isNull())
2993 handler->setInStyle(
true);
3000 const QXmlStreamAttributes &attributes,
3001 QSvgHandler *handler)
3003 Q_UNUSED(parent); Q_UNUSED(attributes);
3005 QSvgDocument *node =
new QSvgDocument(handler->options(), handler->animatorType());
3006 const QStringView widthStr = attributes.value(QLatin1String(
"width"));
3007 const QStringView heightStr = attributes.value(QLatin1String(
"height"));
3008 const QStringView viewBoxStr = attributes.value(QLatin1String(
"viewBox"));
3012 if (!widthStr.isEmpty()) {
3013 width = QSvgUtils::parseLength(widthStr, &type);
3015 width = QSvgUtils::convertToPixels(width,
true, type);
3019 if (!heightStr.isEmpty()) {
3020 height = QSvgUtils::parseLength(heightStr, &type);
3022 height = QSvgUtils::convertToPixels(height,
false, type);
3026 auto viewBoxResult = parseViewBox(viewBoxStr);
3027 if (viewBoxResult) {
3028 node->setViewBox(*viewBoxResult);
3029 }
else if (width && height) {
3031 width = QSvgUtils::convertToPixels(width,
false, type);
3032 height = QSvgUtils::convertToPixels(height,
false, type);
3034 node->setViewBox(QRectF(0, 0, width, height));
3042 const QXmlStreamAttributes &attributes,
3045 Q_UNUSED(attributes);
3046 QSvgSwitch *node =
new QSvgSwitch(parent);
3051 const QXmlStreamAttributes &attributes,
3052 QSvgHandler *handler)
3054 const QStringView x = attributes.value(QLatin1String(
"x"));
3055 const QStringView y = attributes.value(QLatin1String(
"y"));
3056 const QStringView width = attributes.value(QLatin1String(
"width"));
3057 const QStringView height = attributes.value(QLatin1String(
"height"));
3058 const QStringView patternUnits = attributes.value(QLatin1String(
"patternUnits"));
3059 const QStringView patternContentUnits = attributes.value(QLatin1String(
"patternContentUnits"));
3060 const QStringView patternTransform = attributes.value(QLatin1String(
"patternTransform"));
3062 QtSvg::
UnitTypes nPatternUnits = patternUnits.contains(QLatin1String(
"userSpaceOnUse")) ?
3065 QtSvg::
UnitTypes nPatternContentUnits = patternContentUnits.contains(QLatin1String(
"objectBoundingBox")) ?
3068 const QStringView viewBoxStr = attributes.value(QLatin1String(
"viewBox"));
3073 qreal nx = QSvgUtils::parseLength(x, &type, &ok);
3074 nx = QSvgUtils::convertToPixels(nx,
true, type);
3078 nx = (nx / 100.) * handler->document()->viewBox().width();
3082 qreal ny = QSvgUtils::parseLength(y, &type, &ok);
3083 ny = QSvgUtils::convertToPixels(ny,
true, type);
3087 ny = (ny / 100.) * handler->document()->viewBox().height();
3091 qreal nwidth = QSvgUtils::parseLength(width, &type, &ok);
3092 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
3096 nwidth = (nwidth / 100.) * handler->document()->viewBox().width();
3098 nwidth = nwidth / 100.;
3100 qreal nheight = QSvgUtils::parseLength(height, &type, &ok);
3101 nheight = QSvgUtils::convertToPixels(nheight,
true, type);
3105 nheight = (nheight / 100.) * handler->document()->viewBox().height();
3107 nheight = nheight / 100.;
3110 auto viewBoxResult = parseViewBox(viewBoxStr);
3111 if (viewBoxResult) {
3112 if (viewBoxResult->width() > 0 && viewBoxResult->height() > 0)
3113 viewBox = *viewBoxResult;
3117 if (!patternTransform.isEmpty())
3118 matrix = parseTransformationMatrix(patternTransform);
3120 QRectF bounds(nx, ny, nwidth, nheight);
3121 if (bounds.isEmpty())
3124 QSvgRectF patternRectF(bounds, nPatternUnits, nPatternUnits, nPatternUnits, nPatternUnits);
3125 QSvgPattern *node =
new QSvgPattern(parent, patternRectF, viewBox, nPatternContentUnits, matrix);
3128 QSvgPatternStyle *prop =
new QSvgPatternStyle(node);
3129 node->appendStyleProperty(prop, someId(attributes));
3135 const QXmlStreamAttributes &,
3138 if (parent->type() != QSvgNode::Textarea)
3140 static_cast<QSvgText*>(parent)->addLineBreak();
3145 const QXmlStreamAttributes &attributes,
3148 const QStringView x = attributes.value(QLatin1String(
"x"));
3149 const QStringView y = attributes.value(QLatin1String(
"y"));
3152 qreal nx = QSvgUtils::parseLength(x, &type);
3153 nx = QSvgUtils::convertToPixels(nx,
true, type);
3154 qreal ny = QSvgUtils::parseLength(y, &type);
3155 ny = QSvgUtils::convertToPixels(ny,
true, type);
3157 QSvgNode *text =
new QSvgText(parent, QPointF(nx, ny));
3162 const QXmlStreamAttributes &attributes,
3163 QSvgHandler *handler)
3165 QSvgText *node =
static_cast<QSvgText *>(createTextNode(parent, attributes, handler));
3168 qreal width = QSvgUtils::parseLength(attributes.value(QLatin1String(
"width")), &type);
3169 qreal height = QSvgUtils::parseLength(attributes.value(QLatin1String(
"height")), &type);
3170 node->setTextArea(QSizeF(width, height));
3176 const QXmlStreamAttributes &,
3179 return new QSvgTspan(parent);
3183 const QXmlStreamAttributes &attributes,
3186 QStringView linkId = attributes.value(QLatin1String(
"xlink:href"));
3187 const QStringView xStr = attributes.value(QLatin1String(
"x"));
3188 const QStringView yStr = attributes.value(QLatin1String(
"y"));
3189 QSvgStructureNode *group =
nullptr;
3191 if (linkId.isEmpty())
3192 linkId = attributes.value(QLatin1String(
"href"));
3193 QString linkIdStr = linkId.mid(1).toString();
3195 switch (parent->type()) {
3197 case QSvgNode::Defs:
3198 case QSvgNode::Group:
3199 case QSvgNode::Switch:
3200 case QSvgNode::Mask:
3201 group =
static_cast<QSvgStructureNode*>(parent);
3209 if (!xStr.isNull() || !yStr.isNull()) {
3211 qreal nx = QSvgUtils::parseLength(xStr, &type);
3212 nx = QSvgUtils::convertToPixels(nx,
true, type);
3214 qreal ny = QSvgUtils::parseLength(yStr, &type);
3215 ny = QSvgUtils::convertToPixels(ny,
true, type);
3216 pt = QPointF(nx, ny);
3219 QSvgNode *link = group->scopeNode(linkIdStr);
3221 if (parent->isDescendantOf(link))
3222 qCWarning(lcSvgHandler,
"link %ls is recursive!",
qUtf16Printable(linkIdStr));
3224 return new QSvgUse(pt, parent, link);
3228 return new QSvgUse(pt, parent, linkIdStr);
3231 qCWarning(lcSvgHandler,
"<use> element %ls in wrong context!",
qUtf16Printable(linkIdStr));
3236 const QXmlStreamAttributes &attributes,
3239 Q_UNUSED(parent); Q_UNUSED(attributes);
3243typedef QSvgNode *(*FactoryMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3250 QStringView ref = name.mid(1);
3251 switch (name.at(0).unicode()) {
3253 if (ref == QLatin1String(
"efs"))
return createDefsNode;
3259 if (ref.isEmpty())
return createGNode;
3266 if (ref == QLatin1String(
"vg"))
return createSvgNode;
3267 if (ref == QLatin1String(
"witch"))
return createSwitchNode;
3285 QStringView ref = name.mid(1);
3286 switch (name.at(0).unicode()) {
3288 if (ref == QLatin1String(
"ircle"))
return createCircleNode;
3291 if (ref == QLatin1String(
"llipse"))
return createEllipseNode;
3294 if (ref == QLatin1String(
"mage"))
return createImageNode;
3297 if (ref == QLatin1String(
"ine"))
return createLineNode;
3300 if (ref == QLatin1String(
"ath"))
return createPathNode;
3301 if (ref == QLatin1String(
"olygon"))
return createPolygonNode;
3302 if (ref == QLatin1String(
"olyline"))
return createPolylineNode;
3305 if (ref == QLatin1String(
"ect"))
return createRectNode;
3308 if (ref == QLatin1String(
"ext"))
return createTextNode;
3309 if (ref == QLatin1String(
"extArea"))
return createTextAreaNode;
3310 if (ref == QLatin1String(
"span"))
return createTspanNode;
3313 if (ref == QLatin1String(
"se"))
return createUseNode;
3316 if (ref == QLatin1String(
"ideo"))
return createVideoNode;
3332 if (!name.startsWith(QLatin1String(
"fe")))
3335 if (name == QLatin1String(
"feMerge"))
return createFeMergeNode;
3336 if (name == QLatin1String(
"feColorMatrix"))
return createFeColorMatrixNode;
3337 if (name == QLatin1String(
"feGaussianBlur"))
return createFeGaussianBlurNode;
3338 if (name == QLatin1String(
"feOffset"))
return createFeOffsetNode;
3339 if (name == QLatin1String(
"feMergeNode"))
return createFeMergeNodeNode;
3340 if (name == QLatin1String(
"feComposite"))
return createFeCompositeNode;
3341 if (name == QLatin1String(
"feFlood"))
return createFeFloodNode;
3342 if (name == QLatin1String(
"feBlend"))
return createFeBlendNode;
3344 static const QStringList unsupportedFilters = {
3361 if (unsupportedFilters.contains(name))
3362 return createFeUnsupportedNode;
3367typedef QSvgNode *(*AnimationMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3374 QStringView ref = name.mid(1);
3376 switch (name.at(0).unicode()) {
3378 if (ref == QLatin1String(
"nimate"))
return createAnimateNode;
3379 if (ref == QLatin1String(
"nimateColor"))
return createAnimateColorNode;
3380 if (ref == QLatin1String(
"nimateMotion"))
return createAimateMotionNode;
3381 if (ref == QLatin1String(
"nimateTransform"))
return createAnimateTransformNode;
3390typedef bool (*
ParseMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3397 QStringView ref = name.mid(1);
3398 switch (name.at(0).unicode()) {
3400 if (ref.isEmpty())
return parseAnchorNode;
3401 if (ref == QLatin1String(
"udio"))
return parseAudioNode;
3404 if (ref == QLatin1String(
"iscard"))
return parseDiscardNode;
3407 if (ref == QLatin1String(
"oreignObject"))
return parseForeignObjectNode;
3410 if (ref == QLatin1String(
"andler"))
return parseHandlerNode;
3411 if (ref == QLatin1String(
"kern"))
return parseHkernNode;
3414 if (ref == QLatin1String(
"etadata"))
return parseMetadataNode;
3415 if (ref == QLatin1String(
"path"))
return parseMpathNode;
3420 if (ref == QLatin1String(
"refetch"))
return parsePrefetchNode;
3423 if (ref == QLatin1String(
"cript"))
return parseScriptNode;
3424 if (ref == QLatin1String(
"et"))
return parseSetNode;
3425 if (ref == QLatin1String(
"tyle"))
return parseStyleNode;
3428 if (ref == QLatin1String(
"break"))
return parseTbreakNode;
3437 const QXmlStreamAttributes &,
3445 QStringView ref = name.mid(1);
3446 switch (name.at(0).unicode()) {
3448 if (ref == QLatin1String(
"ont"))
return createFontNode;
3451 if (ref == QLatin1String(
"inearGradient"))
return createLinearGradientNode;
3454 if (ref == QLatin1String(
"adialGradient"))
return createRadialGradientNode;
3457 if (ref == QLatin1String(
"olidColor"))
return createSolidColorNode;
3466 const QXmlStreamAttributes &,
3474 QStringView ref = name.mid(1);
3475 switch (name.at(0).unicode()) {
3497QSvgHandler::QSvgHandler(QIODevice *device, QtSvg::Options options,
3498 QtSvg::AnimatorType type)
3499 : xml(
new QXmlStreamReader(device))
3500 , m_ownsReader(
true)
3501 , m_options(options)
3502 , m_animatorType(type)
3507QSvgHandler::QSvgHandler(
const QByteArray &data, QtSvg::Options options,
3508 QtSvg::AnimatorType type)
3509 : xml(
new QXmlStreamReader(data))
3510 , m_ownsReader(
true)
3511 , m_options(options)
3512 , m_animatorType(type)
3517QSvgHandler::QSvgHandler(QXmlStreamReader *
const reader, QtSvg::Options options,
3518 QtSvg::AnimatorType type)
3520 , m_ownsReader(
false)
3521 , m_options(options)
3522 , m_animatorType(type)
3527void QSvgHandler::init()
3532 m_defaultCoords = QSvgUtils::LT_PX;
3533 m_defaultPen = QPen(Qt::black, 1, Qt::SolidLine, Qt::FlatCap, Qt::SvgMiterJoin);
3534 m_defaultPen.setMiterLimit(4);
3540 QSvgFillStyle *fillStyle =
static_cast<QSvgFillStyle*>
3541 (node->styleProperty(QSvgStyleProperty::FILL));
3542 if (fillStyle && fillStyle->style() && fillStyle->style()->type() == QSvgStyleProperty::PATTERN) {
3543 QSvgPatternStyle *patternStyle =
static_cast<QSvgPatternStyle *>(fillStyle->style());
3544 if (active.contains(patternStyle->patternNode()))
3548 QSvgStrokeStyle *strokeStyle =
static_cast<QSvgStrokeStyle*>
3549 (node->styleProperty(QSvgStyleProperty::STROKE));
3550 if (strokeStyle && strokeStyle->style() && strokeStyle->style()->type() == QSvgStyleProperty::PATTERN) {
3551 QSvgPatternStyle *patternStyle =
static_cast<QSvgPatternStyle *>(strokeStyle->style());
3552 if (active.contains(patternStyle->patternNode()))
3561 if (Q_UNLIKELY(!node))
3563 switch (node->type()) {
3565 case QSvgNode::Group:
3566 case QSvgNode::Defs:
3567 case QSvgNode::Pattern:
3569 if (node->type() == QSvgNode::Pattern)
3570 active.append(node);
3572 auto *g =
static_cast<
const QSvgStructureNode*>(node);
3573 for (
auto &node : g->renderers()) {
3574 if (detectCycles(node.get(), active))
3581 if (active.contains(node))
3584 auto *u =
static_cast<
const QSvgUse*>(node);
3585 auto *target = u->link();
3588 if (detectCycles(target, active))
3593 case QSvgNode::Rect:
3594 case QSvgNode::Ellipse:
3595 case QSvgNode::Circle:
3596 case QSvgNode::Line:
3597 case QSvgNode::Path:
3598 case QSvgNode::Polygon:
3599 case QSvgNode::Polyline:
3600 case QSvgNode::Tspan:
3601 if (detectPatternCycles(node, active))
3611 const bool cycleFound = detectCycles(node);
3613 qCWarning(lcSvgHandler,
"Cycles detected in SVG, document discarded.");
3621void QSvgHandler::parse()
3623 xml->setNamespaceProcessing(
false);
3624#ifndef QT_NO_CSSPARSER
3628 int remainingUnfinishedElements = unfinishedElementsLimit;
3629 while (!xml->atEnd() && !done) {
3630 switch (xml->readNext()) {
3631 case QXmlStreamReader::StartElement:
3640 if (remainingUnfinishedElements && startElement(xml->name(), xml->attributes())) {
3641 --remainingUnfinishedElements;
3648 case QXmlStreamReader::EndElement:
3649 done = endElement(xml->name());
3650 ++remainingUnfinishedElements;
3652 case QXmlStreamReader::Characters:
3653 characters(xml->text());
3655 case QXmlStreamReader::ProcessingInstruction:
3656 processingInstruction(xml->processingInstructionTarget(), xml->processingInstructionData());
3662 resolvePaintServers(m_doc);
3664 if (detectCyclesAndWarn(m_doc)) {
3670bool QSvgHandler::startElement(
const QStringView localName,
3671 const QXmlStreamAttributes &attributes)
3673 QSvgNode *node =
nullptr;
3678
3679
3680 const QStringView xmlSpace(attributes.value(QLatin1String(
"xml:space")));
3681 if (xmlSpace.isNull()) {
3683 m_whitespaceMode.push(m_whitespaceMode.isEmpty() ? QSvgText::Default : m_whitespaceMode.top());
3684 }
else if (xmlSpace == QLatin1String(
"preserve")) {
3685 m_whitespaceMode.push(QSvgText::Preserve);
3686 }
else if (xmlSpace == QLatin1String(
"default")) {
3687 m_whitespaceMode.push(QSvgText::Default);
3689 const QByteArray msg =
'"' + xmlSpace.toLocal8Bit()
3690 +
"\" is an invalid value for attribute xml:space. "
3691 "Valid values are \"preserve\" and \"default\".";
3692 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3693 m_whitespaceMode.push(QSvgText::Default);
3696 if (!m_doc && localName != QLatin1String(
"svg"))
3699 if (m_doc && localName == QLatin1String(
"svg")) {
3700 m_skipNodes.push(Doc);
3701 qCWarning(lcSvgHandler) <<
"Skipping a nested svg element, because "
3702 "SVG Document must not contain nested svg elements in Svg Tiny 1.2";
3705 if (!m_skipNodes.isEmpty() && m_skipNodes.top() == Doc)
3708 if (FactoryMethod method = findGroupFactory(localName, options())) {
3711 node = method(
nullptr, attributes,
this);
3713 Q_ASSERT(node->type() == QSvgNode::Doc);
3714 m_doc =
static_cast<QSvgDocument*>(node);
3717 switch (m_nodes.top()->type()) {
3719 case QSvgNode::Group:
3720 case QSvgNode::Defs:
3721 case QSvgNode::Switch:
3722 case QSvgNode::Mask:
3723 case QSvgNode::Symbol:
3724 case QSvgNode::Marker:
3725 case QSvgNode::Pattern:
3727 node = method(m_nodes.top(), attributes,
this);
3729 QSvgStructureNode *group =
3730 static_cast<QSvgStructureNode*>(m_nodes.top());
3731 group->addChild(std::unique_ptr<QSvgNode>(node), someId(attributes));
3736 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
3737 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3743 parseCoreNode(node, attributes);
3744 parseStyle(node, attributes,
this);
3745 if (node->type() == QSvgNode::Filter)
3746 m_toBeResolved.append(node);
3748 }
else if (FactoryMethod method = findGraphicsFactory(localName, options())) {
3750 Q_ASSERT(!m_nodes.isEmpty());
3751 switch (m_nodes.top()->type()) {
3753 case QSvgNode::Group:
3754 case QSvgNode::Defs:
3755 case QSvgNode::Switch:
3756 case QSvgNode::Mask:
3757 case QSvgNode::Symbol:
3758 case QSvgNode::Marker:
3759 case QSvgNode::Pattern:
3761 if (localName == QLatin1String(
"tspan")) {
3762 const QByteArray msg = QByteArrayLiteral(
"\'tspan\' element in wrong context.");
3763 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3766 node = method(m_nodes.top(), attributes,
this);
3768 QSvgStructureNode *group =
3769 static_cast<QSvgStructureNode*>(m_nodes.top());
3770 group->addChild(std::unique_ptr<QSvgNode>(node), someId(attributes));
3774 case QSvgNode::Text:
3775 case QSvgNode::Textarea:
3776 if (localName == QLatin1String(
"tspan")) {
3777 node = method(m_nodes.top(), attributes,
this);
3779 static_cast<QSvgText *>(m_nodes.top())->addTspan(
static_cast<QSvgTspan *>(node));
3782 const QByteArray msg = QByteArrayLiteral(
"\'text\' or \'textArea\' element contains invalid element type.");
3783 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3787 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
3788 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3793 parseCoreNode(node, attributes);
3794 parseStyle(node, attributes,
this);
3795 if (node->type() == QSvgNode::Text || node->type() == QSvgNode::Textarea) {
3796 static_cast<QSvgText *>(node)->setWhitespaceMode(m_whitespaceMode.top());
3797 }
else if (node->type() == QSvgNode::Tspan) {
3798 static_cast<QSvgTspan *>(node)->setWhitespaceMode(m_whitespaceMode.top());
3799 }
else if (node->type() == QSvgNode::Use) {
3800 auto useNode =
static_cast<QSvgUse *>(node);
3801 if (!useNode->isResolved())
3802 m_toBeResolved.append(useNode);
3805 }
else if (FactoryMethod method = findFilterFactory(localName, options())) {
3807 Q_ASSERT(!m_nodes.isEmpty());
3808 if (m_nodes.top()->type() == QSvgNode::Filter ||
3809 (m_nodes.top()->type() == QSvgNode::FeMerge && localName == QLatin1String(
"feMergeNode"))) {
3810 node = method(m_nodes.top(), attributes,
this);
3812 QSvgStructureNode *container =
3813 static_cast<QSvgStructureNode*>(m_nodes.top());
3814 container->addChild(std::unique_ptr<QSvgNode>(node), someId(attributes));
3817 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
3818 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3820 }
else if (AnimationMethod method = findAnimationFactory(localName, options())) {
3821 Q_ASSERT(!m_nodes.isEmpty());
3822 node = method(m_nodes.top(), attributes,
this);
3824 QSvgAnimateNode *anim =
static_cast<QSvgAnimateNode *>(node);
3825 if (anim->linkId().isEmpty())
3826 m_doc->animator()->appendAnimation(m_nodes.top(), anim);
3827 else if (m_doc->namedNode(anim->linkId()))
3828 m_doc->animator()->appendAnimation(m_doc->namedNode(anim->linkId()), anim);
3830 m_toBeResolved.append(anim);
3832 }
else if (ParseMethod method = findUtilFactory(localName, options())) {
3833 Q_ASSERT(!m_nodes.isEmpty());
3834 if (!method(m_nodes.top(), attributes,
this))
3835 qCWarning(lcSvgHandler,
"%s", msgProblemParsing(localName, xml).constData());
3836 }
else if (StyleFactoryMethod method = findStyleFactoryMethod(localName)) {
3837 QSvgStyleProperty *prop = method(m_nodes.top(), attributes,
this);
3840 m_nodes.top()->appendStyleProperty(prop, someId(attributes));
3842 const QByteArray msg = QByteArrayLiteral(
"Could not parse node: ") + localName.toLocal8Bit();
3843 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3845 }
else if (StyleParseMethod method = findStyleUtilFactoryMethod(localName)) {
3847 if (!method(m_style, attributes,
this))
3848 qCWarning(lcSvgHandler,
"%s", msgProblemParsing(localName, xml).constData());
3851 qCDebug(lcSvgHandler) <<
"Skipping unknown element" << localName;
3852 m_skipNodes.push(Unknown);
3858 m_skipNodes.push(Graphics);
3861 m_skipNodes.push(Style);
3866bool QSvgHandler::endElement(
const QStringView localName)
3868 CurrentNode node = m_skipNodes.top();
3870 if (node == Doc && localName != QLatin1String(
"svg"))
3874 m_whitespaceMode.pop();
3878 if (node == Unknown)
3881#ifdef QT_NO_CSSPARSER
3882 Q_UNUSED(localName);
3884 if (m_inStyle && localName == QLatin1String(
"style"))
3888 if (node == Graphics)
3890 else if (m_style && !m_skipNodes.isEmpty() && m_skipNodes.top() != Style)
3893 return ((localName == QLatin1String(
"svg")) && (node != Doc));
3896void QSvgHandler::resolvePaintServers(QSvgNode *node,
int nestedDepth)
3898 if (!node || (node->type() != QSvgNode::Doc && node->type() != QSvgNode::Group
3899 && node->type() != QSvgNode::Defs && node->type() != QSvgNode::Switch)) {
3903 QSvgStructureNode *structureNode =
static_cast<QSvgStructureNode *>(node);
3905 for (
auto &node : structureNode->renderers()) {
3906 QSvgFillStyle *fill =
static_cast<QSvgFillStyle *>(node->styleProperty(QSvgStyleProperty::FILL));
3907 if (fill && !fill->isPaintStyleResolved()) {
3908 QString id = fill->paintStyleId();
3909 QSvgPaintStyleProperty *style = structureNode->styleProperty(id);
3911 fill->setFillStyle(style);
3913 qCWarning(lcSvgHandler,
"%s", msgCouldNotResolveProperty(id, xml).constData());
3914 fill->setBrush(Qt::NoBrush);
3918 QSvgStrokeStyle *stroke =
static_cast<QSvgStrokeStyle *>(node->styleProperty(QSvgStyleProperty::STROKE));
3919 if (stroke && !stroke->isPaintStyleResolved()) {
3920 QString id = stroke->paintStyleId();
3921 QSvgPaintStyleProperty *style = structureNode->styleProperty(id);
3923 stroke->setStyle(style);
3925 qCWarning(lcSvgHandler,
"%s", msgCouldNotResolveProperty(id, xml).constData());
3926 stroke->setStroke(Qt::NoBrush);
3930 if (nestedDepth < 2048)
3931 resolvePaintServers(node.get(), nestedDepth + 1);
3935void QSvgHandler::resolveNodes()
3937 for (QSvgNode *node : std::as_const(m_toBeResolved)) {
3938 if (node->type() == QSvgNode::Use) {
3939 QSvgUse *useNode =
static_cast<QSvgUse *>(node);
3940 const auto parent = useNode->parent();
3944 QSvgNode::Type t = parent->type();
3945 if (t != QSvgNode::Doc && t != QSvgNode::Defs && t != QSvgNode::Group && t != QSvgNode::Switch)
3948 QSvgStructureNode *group =
static_cast<QSvgStructureNode *>(parent);
3949 QSvgNode *link = group->scopeNode(useNode->linkId());
3951 qCWarning(lcSvgHandler,
"link #%s is undefined!",
qPrintable(useNode->linkId()));
3955 if (useNode->parent()->isDescendantOf(link))
3956 qCWarning(lcSvgHandler,
"link #%s is recursive!",
qPrintable(useNode->linkId()));
3958 useNode->setLink(link);
3959 }
else if (node->type() == QSvgNode::Filter) {
3960 QSvgFilterContainer *filter =
static_cast<QSvgFilterContainer *>(node);
3961 for (
auto &renderer : filter->renderers()) {
3962 const QSvgFeFilterPrimitive *primitive = QSvgFeFilterPrimitive::castToFilterPrimitive(renderer.get());
3963 if (!primitive || primitive->type() == QSvgNode::FeUnsupported) {
3964 filter->setSupported(
false);
3968 }
else if (node->type() == QSvgNode::AnimateTransform || node->type() == QSvgNode::AnimateColor) {
3969 QSvgAnimateNode *anim =
static_cast<QSvgAnimateNode *>(node);
3970 QSvgNode *targetNode = m_doc->namedNode(anim->linkId());
3972 m_doc->animator()->appendAnimation(targetNode, anim);
3974 qCWarning(lcSvgHandler,
"Cannot find target for link #%s!",
3980 m_toBeResolved.clear();
3983bool QSvgHandler::characters(
const QStringView str)
3985#ifndef QT_NO_CSSPARSER
3987 m_cssHandler.parseStyleSheet(str);
3991 if (m_skipNodes.isEmpty() || m_skipNodes.top() == Unknown || m_nodes.isEmpty())
3994 if (m_nodes.top()->type() == QSvgNode::Text || m_nodes.top()->type() == QSvgNode::Textarea) {
3995 static_cast<QSvgText*>(m_nodes.top())->addText(str);
3996 }
else if (m_nodes.top()->type() == QSvgNode::Tspan) {
3997 static_cast<QSvgTspan*>(m_nodes.top())->addText(str);
4003QIODevice *QSvgHandler::device()
const
4005 return xml->device();
4008QSvgDocument *QSvgHandler::document()
const
4013QSvgUtils::LengthType QSvgHandler::defaultCoordinateSystem()
const
4015 return m_defaultCoords;
4018void QSvgHandler::setDefaultCoordinateSystem(QSvgUtils::LengthType type)
4020 m_defaultCoords = type;
4023void QSvgHandler::pushColor(
const QColor &color)
4025 m_colorStack.push(color);
4026 m_colorTagCount.push(1);
4029void QSvgHandler::pushColorCopy()
4031 if (m_colorTagCount.size())
4032 ++m_colorTagCount.top();
4034 pushColor(Qt::black);
4037void QSvgHandler::popColor()
4039 if (m_colorTagCount.size()) {
4040 if (!--m_colorTagCount.top()) {
4042 m_colorTagCount.pop();
4047QColor QSvgHandler::currentColor()
const
4049 if (!m_colorStack.isEmpty())
4050 return m_colorStack.top();
4052 return QColor(0, 0, 0);
4055#ifndef QT_NO_CSSPARSER
4057void QSvgHandler::setInStyle(
bool b)
4062bool QSvgHandler::inStyle()
const
4067QSvgCssHandler &QSvgHandler::cssHandler()
4069 return m_cssHandler;
4074bool QSvgHandler::processingInstruction(
const QStringView target,
const QStringView data)
4076#ifdef QT_NO_CSSPARSER
4080 if (target == QLatin1String(
"xml-stylesheet")) {
4081 static const QRegularExpression rx(
QStringLiteral(
"type=\\\"(.+)\\\""),
4082 QRegularExpression::InvertedGreedinessOption);
4083 QRegularExpressionMatchIterator iter = rx.globalMatchView(data);
4085 while (iter.hasNext()) {
4086 QRegularExpressionMatch match = iter.next();
4087 QString type = match.captured(1);
4088 if (type.toLower() == QLatin1String(
"text/css")) {
4094 static const QRegularExpression rx(
QStringLiteral(
"href=\\\"(.+)\\\""),
4095 QRegularExpression::InvertedGreedinessOption);
4096 QRegularExpressionMatch match = rx.matchView(data);
4097 QString addr = match.captured(1);
4101 QFile file(fi.absoluteFilePath());
4102 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
4105 QByteArray cssData = file.readAll();
4106 QString css = QString::fromUtf8(cssData);
4107 m_cssHandler.parseStyleSheet(css);
4117void QSvgHandler::setAnimPeriod(
int start,
int end)
4120 m_animEnd = qMax(end, m_animEnd);
4123int QSvgHandler::animationDuration()
const
4128QSvgHandler::~QSvgHandler()
The QPolygonF class provides a list of points using floating point precision.
void parseNumbersArray(const QChar *&str, QVarLengthArray< qreal, 8 > &points, const char *pattern)
qreal toDouble(QStringView str, bool *ok)
Combined button and popup list for selecting options.
Q_STATIC_ASSERT(sizeof(SharedImageHeader) % 4==0)
#define qPrintable(string)
#define QStringLiteral(str)
#define qUtf16Printable(string)
static QSvgNode * createTspanNode(QSvgNode *parent, const QXmlStreamAttributes &, QSvgHandler *)
static bool parseStyle(QSvgNode *node, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static void parseVisibility(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static bool detectPatternCycles(const QSvgNode *node, QList< const QSvgNode * > active={})
static 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)
static QSvgStyleProperty * styleFromUrl(QSvgNode *node, QStringView url)
static const qreal sizeTable[]
static QSvgNode * createImageNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createPolygonNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseOthers(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static QSvgNode * createMaskNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QByteArray msgProblemParsing(QStringView localName, const QXmlStreamReader *r)
static void parseRenderingHints(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static void parseExtendedAttributes(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *handler)
static bool parseMpathNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QByteArray prefixMessage(const QByteArray &msg, const QXmlStreamReader *r)
static QSvgNode * createPathNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createDefsNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgStyleProperty * createFontNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static int parseClockValue(QStringView str, bool *ok)
static bool parseSymbolLikeAttributes(const QXmlStreamAttributes &attributes, QSvgHandler *handler, QRectF *rect, QRectF *viewBox, QPointF *refPoint, QSvgSymbolLike::PreserveAspectRatios *aspect, QSvgSymbolLike::Overflow *overflow, bool marker=false)
static qreal convertToNumber(QStringView str, bool *ok=NULL)
static bool parseBaseAnimate(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgAnimateNode *anim, QSvgHandler *handler)
static bool parseFontFaceNameNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseFilterAttributes(const QXmlStreamAttributes &attributes, QString *inString, QString *outString, QSvgRectF *rect)
static QSvgNode * createPolyNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, bool createLine)
static void parseTransform(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static QSvgNode * createFeMergeNodeNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createAnimateColorNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QStringView idFromUrl(QStringView url)
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 std::optional< QRectF > parseViewBox(QStringView str)
static QSvgStyleProperty * createLinearGradientNode(QSvgNode *node, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static const int unfinishedElementsLimit
static QSvgNode * createLineNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static FactoryMethod findFilterFactory(const QStringView name, QtSvg::Options options)
static ParseMethod findUtilFactory(const QStringView name, QtSvg::Options options)
static FontSizeSpec fontSizeSpec(QStringView spec)
static QSvgStyleProperty * createSolidColorNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
bool qsvg_get_hex_rgb(const char *name, QRgb *rgb)
static QSvgNode * createTextNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseBaseGradient(QSvgNode *node, const QXmlStreamAttributes &attributes, QSvgGradientStyle *gradProp, QSvgHandler *handler)
static bool parseAudioNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static StyleParseMethod findStyleUtilFactoryMethod(const QStringView name)
static bool parseMarkerNode(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *)
static bool resolveColor(QStringView colorStr, QColor &color, QSvgHandler *handler)
static QList< qreal > parsePercentageList(const QChar *&str)
static FactoryMethod findGroupFactory(const QStringView name, QtSvg::Options options)
static bool parseMetadataNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QTransform parseTransformationMatrix(QStringView value)
static QSvgNode * createSwitchNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseDiscardNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool createSvgGlyph(QSvgFont *font, const QXmlStreamAttributes &attributes, bool isMissingGlyph)
static void parseOpacity(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static bool parseScriptNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
QSvgStyleProperty *(* StyleFactoryMethod)(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *)
static QSvgNode * createPatternNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static bool parseHandlerNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static AnimationMethod findAnimationFactory(const QStringView name, QtSvg::Options options)
static QList< qreal > parseNumbersList(const QChar *&str)
static bool parseCoreNode(QSvgNode *node, const QXmlStreamAttributes &attributes)
static bool constructColor(QStringView colorStr, QStringView opacity, QColor &color, QSvgHandler *handler)
static bool parseHkernNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void generateKeyFrames(QList< qreal > &keyFrames, uint count)
static void parsePen(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createSvgNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static void parseFont(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static bool parseForeignObjectNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseTbreakNode(QSvgNode *parent, const QXmlStreamAttributes &, QSvgHandler *)
static QSvgNode * createFeUnsupportedNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createAnimateTransformNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QList< QStringView > splitWithDelimiter(QStringView delimitedList)
static QSvgNode * createFeOffsetNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createFeFloodNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createMarkerNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createFeColorMatrixNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createFeGaussianBlurNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createPolylineNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
bool(* ParseMethod)(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *)
static bool parseFontFaceNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseMissingGlyphNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parsePrefetchNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseCssAnimations(QSvgNode *node, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static int qsvg_hex2int(const char *s, bool *ok=nullptr)
static bool parseGlyphNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static int qsvg_h2i(char hex, bool *ok=nullptr)
static int qsvg_hex2int(char s, bool *ok=nullptr)
static QSvgNode * createVideoNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseCompOp(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static bool parseMaskNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool detectCyclesAndWarn(const QSvgNode *node)
static QSvgNode * createUseNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseFontFaceSrcNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseAnchorNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseFontFaceUriNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool detectCycles(const QSvgNode *node, QList< const QSvgNode * > active={})
static QStringList stringToList(const QString &str)
static QSvgNode * createRectNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createAnimateNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createSymbolNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static StyleFactoryMethod findStyleFactoryMethod(const QStringView name)
static QPainter::CompositionMode svgToQtCompositionMode(const QStringView op)
static QByteArray msgCouldNotResolveProperty(QStringView id, const QXmlStreamReader *r)
static void parseFilterBounds(const QXmlStreamAttributes &attributes, QSvgRectF *rect)
static bool parseStopNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static void parseBrush(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *handler)
static FactoryMethod findGraphicsFactory(const QStringView name, QtSvg::Options options)
static QSvgNode * createFeMergeNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createFeCompositeNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseSetNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseNumberTriplet(QList< qreal > &values, const QChar *&s)
static QSvgNode * createFeBlendNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createFilterNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createEllipseNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createTextAreaNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createAimateMotionNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgStyleProperty * createRadialGradientNode(QSvgNode *node, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QString someId(const QXmlStreamAttributes &attributes)
QStringView strokeDashOffset
QStringView strokeDashArray
QStringView strokeOpacity
QStringView strokeLineJoin
void setAttributes(const QXmlStreamAttributes &attributes, QSvgHandler *handler)
QSvgAttributes(const QXmlStreamAttributes &xmlAttributes, QSvgHandler *handler)
QStringView strokeLineCap
QStringView strokeMiterLimit
QStringView imageRendering