5#include "qplatformdefs.h"
23#include <QtCore/private/qdataurl_p.h>
31#include <qregularexpression.h>
34#include "private/qmath_p.h"
41Q_LOGGING_CATEGORY(lcSvgHandler,
"qt.svg")
43static const char *qt_inherit_text =
"inherit";
44#define QT_INHERIT QLatin1String(qt_inherit_text)
50 if (
const QFile *file = qobject_cast<
const QFile *>(r->device()))
51 result.append(QFile::encodeName(QDir::toNativeSeparators(file->fileName())));
53 result.append(QByteArrayLiteral(
"<input>"));
55 result.append(QByteArray::number(r->lineNumber()));
56 if (
const qint64 column = r->columnNumber()) {
58 result.append(QByteArray::number(column));
60 result.append(QByteArrayLiteral(
": "));
68 return prefixMessage(
"Problem parsing " + localName.toLocal8Bit(), r);
73 return prefixMessage(
"Could not resolve property: " + id.toLocal8Bit(), r);
78 static const QRegularExpression delimiterRE(
QStringLiteral(
"[,\\s]+"));
79 return delimitedList.split(delimiterRE, Qt::SkipEmptyParts);
84static inline int qsvg_h2i(
char hex,
bool *ok =
nullptr)
86 if (hex >=
'0' && hex <=
'9')
88 if (hex >=
'a' && hex <=
'f')
89 return hex -
'a' + 10;
90 if (hex >=
'A' && hex <=
'F')
91 return hex -
'A' + 10;
113 const size_t len = qstrlen(name);
120 }
else if (len == 9) {
124 }
else if (len == 6) {
128 }
else if (len == 3) {
135 if ((uint)r > 255 || (uint)g > 255 || (uint)b > 255 || !ok) {
139 *rgb = qRgb(r, g ,b);
148 for(
int i = 0; i < len; ++i)
149 tmp[i] = str[i].toLatin1();
151 return qsvg_get_hex_rgb(tmp, rgb);
156static inline QString
someId(
const QXmlStreamAttributes &attributes)
158 QStringView id = attributes.value(QLatin1String(
"id"));
160 id = attributes.value(QLatin1String(
"xml:id"));
161 return id.toString();
167 void setAttributes(
const QXmlStreamAttributes &attributes, QSvgHandler *handler);
209 setAttributes(xmlAttributes, handler);
214 for (
const QXmlStreamAttribute &attribute : attributes) {
215 QStringView name = attribute.qualifiedName();
218 QStringView value = attribute.value();
220 switch (name.at(0).unicode()) {
223 if (name == QLatin1String(
"color"))
225 else if (name == QLatin1String(
"color-opacity"))
226 colorOpacity = value;
227 else if (name == QLatin1String(
"comp-op"))
232 if (name == QLatin1String(
"display"))
237 if (name == QLatin1String(
"fill"))
239 else if (name == QLatin1String(
"fill-rule"))
241 else if (name == QLatin1String(
"fill-opacity"))
243 else if (name == QLatin1String(
"font-family"))
245 else if (name == QLatin1String(
"font-size"))
247 else if (name == QLatin1String(
"font-style"))
249 else if (name == QLatin1String(
"font-weight"))
251 else if (name == QLatin1String(
"font-variant"))
253 else if (name == QLatin1String(
"filter") &&
254 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
259 if (name == QLatin1String(
"id"))
260 id = value.toString();
261 else if (name == QLatin1String(
"image-rendering"))
262 imageRendering = value;
266 if (name == QLatin1String(
"mask") &&
267 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
269 if (name == QLatin1String(
"marker-start") &&
270 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
272 if (name == QLatin1String(
"marker-mid") &&
273 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
275 if (name == QLatin1String(
"marker-end") &&
276 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
281 if (name == QLatin1String(
"opacity"))
283 if (name == QLatin1String(
"offset"))
288 if (name.size() > 5 && name.mid(1, 5) == QLatin1String(
"troke")) {
289 QStringView strokeRef = name.mid(6, name.size() - 6);
290 if (strokeRef.isEmpty())
292 else if (strokeRef == QLatin1String(
"-dasharray"))
293 strokeDashArray = value;
294 else if (strokeRef == QLatin1String(
"-dashoffset"))
295 strokeDashOffset = value;
296 else if (strokeRef == QLatin1String(
"-linecap"))
297 strokeLineCap = value;
298 else if (strokeRef == QLatin1String(
"-linejoin"))
299 strokeLineJoin = value;
300 else if (strokeRef == QLatin1String(
"-miterlimit"))
301 strokeMiterLimit = value;
302 else if (strokeRef == QLatin1String(
"-opacity"))
303 strokeOpacity = value;
304 else if (strokeRef == QLatin1String(
"-width"))
306 }
else if (name == QLatin1String(
"stop-color"))
308 else if (name == QLatin1String(
"stop-opacity"))
313 if (name == QLatin1String(
"text-anchor"))
315 else if (name == QLatin1String(
"transform"))
320 if (name == QLatin1String(
"vector-effect"))
321 vectorEffect = value;
322 else if (name == QLatin1String(
"visibility"))
327 if (name == QLatin1String(
"xml:id") && id.isEmpty())
328 id = value.toString();
344 while (str->isSpace())
347 *str == QLatin1Char(
'-') || *str == QLatin1Char(
'+') ||
348 *str == QLatin1Char(
'.')) {
350 points.append(QSvgUtils::toDouble(str));
352 while (str->isSpace())
354 if (*str == QLatin1Char(
','))
358 while (str->isSpace())
371 while (str->isSpace())
373 while ((*str >= QLatin1Char(
'0') && *str <= QLatin1Char(
'9')) ||
374 *str == QLatin1Char(
'-') || *str == QLatin1Char(
'+') ||
375 *str == QLatin1Char(
'.')) {
377 points.append(QSvgUtils::toDouble(str));
379 while (str->isSpace())
381 if (*str == QLatin1Char(
'%'))
383 while (str->isSpace())
385 if (*str == QLatin1Char(
','))
389 while (str->isSpace())
401 if (url.startsWith(QLatin1Char(
'(')))
404 return QStringView();
406 if (!url.startsWith(QLatin1Char(
'#')))
407 return QStringView();
408 const qsizetype closingBracePos = url.indexOf(QLatin1Char(
')'));
409 if (closingBracePos == -1)
410 return QStringView();
411 return url.first(closingBracePos).trimmed();
415
416
417
418static bool resolveColor(QStringView colorStr, QColor &color, QSvgHandler *handler)
420 QStringView colorStrTr = colorStr.trimmed();
421 if (colorStrTr.isEmpty())
424 switch(colorStrTr.at(0).unicode()) {
431 bool ok = qsvg_get_hex_rgb(colorStrTr.constData(), colorStrTr.size(), &rgb);
441 if (colorStrTr.size() >= 7 && colorStrTr.at(colorStrTr.size() - 1) == QLatin1Char(
')')
442 && colorStrTr.mid(0, 4) == QLatin1String(
"rgb(")) {
443 const QChar *s = colorStrTr.constData() + 4;
444 QList<qreal> compo = parseNumbersList(s);
447 if (compo.size() == 1) {
448 s = colorStrTr.constData() + 4;
449 compo = parsePercentageList(s);
450 for (
int i = 0; i < compo.size(); ++i)
451 compo[i] *= (qreal)2.55;
454 if (compo.size() == 3) {
455 color = QColor(
int(compo[0]),
466 if (colorStrTr == QLatin1String(
"currentColor")) {
467 color = handler->currentColor();
479 color = QColor::fromString(colorStrTr);
480 return color.isValid();
484 QColor &color, QSvgHandler *handler)
486 if (!resolveColor(colorStr, color, handler))
488 if (!opacity.isEmpty()) {
490 qreal op = qMin(qreal(1.0), qMax(qreal(0.0), QSvgUtils::toDouble(opacity, &ok)));
501 qreal num = QSvgUtils::parseLength(str.toString(), &type, ok);
508static bool createSvgGlyph(QSvgFont *font,
const QXmlStreamAttributes &attributes,
511 QStringView uncStr = attributes.value(QLatin1String(
"unicode"));
512 QStringView havStr = attributes.value(QLatin1String(
"horiz-adv-x"));
513 QStringView pathStr = attributes.value(QLatin1String(
"d"));
515 qreal havx = (havStr.isEmpty()) ? -1 : QSvgUtils::toDouble(havStr);
516 QPainterPath path = QSvgUtils::parsePathDataFast(pathStr).value_or(QPainterPath());
518 path.setFillRule(Qt::WindingFill);
520 if (isMissingGlyph) {
521 if (!uncStr.isEmpty())
522 qWarning(
"Ignoring missing-glyph's 'unicode' attribute");
523 return font->addMissingGlyph(path, havx);
526 if (uncStr.isEmpty()) {
527 qWarning(
"glyph does not define a non-empty 'unicode' attribute and will be ignored");
530 font->addGlyph(uncStr.at(0), path, havx);
536 QSvgHandler *handler)
539 if (constructColor(attributes.color, attributes.colorOpacity, color, handler)) {
541 handler->pushColor(color);
547 return node ? node->styleProperty(idFromUrl(url)) : 0;
552 QSvgHandler *handler)
554 if (!attributes.fill.isEmpty() || !attributes.fillRule.isEmpty() || !attributes.fillOpacity.isEmpty()) {
555 QSvgFillStyle *prop =
new QSvgFillStyle;
558 if (!attributes.fillRule.isEmpty() && attributes.fillRule !=
QT_INHERIT) {
559 if (attributes.fillRule == QLatin1String(
"evenodd"))
560 prop->setFillRule(Qt::OddEvenFill);
561 else if (attributes.fillRule == QLatin1String(
"nonzero"))
562 prop->setFillRule(Qt::WindingFill);
566 if (!attributes.fillOpacity.isEmpty() && attributes.fillOpacity !=
QT_INHERIT) {
567 prop->setFillOpacity(qMin(qreal(1.0), qMax(qreal(0.0), QSvgUtils::toDouble(attributes.fillOpacity))));
571 if ((!attributes.fill.isEmpty()) && (attributes.fill !=
QT_INHERIT) ) {
572 if (attributes.fill.startsWith(QLatin1String(
"url"))) {
573 QStringView value = attributes.fill.sliced(3);
574 QSvgStyleProperty *style = styleFromUrl(node, value);
576 if (style->type() == QSvgStyleProperty::SOLID_COLOR || style->type() == QSvgStyleProperty::GRADIENT
577 || style->type() == QSvgStyleProperty::PATTERN)
578 prop->setFillStyle(
reinterpret_cast<QSvgPaintStyleProperty *>(style));
580 QString id = idFromUrl(value).toString();
581 prop->setPaintStyleId(id);
582 prop->setPaintStyleResolved(
false);
584 }
else if (attributes.fill != QLatin1String(
"none")) {
586 if (resolveColor(attributes.fill, color, handler))
587 prop->setBrush(QBrush(color));
589 prop->setBrush(QBrush(Qt::NoBrush));
592 node->appendStyleProperty(prop, attributes.id);
604 const QChar *str = value.constData();
605 const QChar *end = str + value.size();
608 if (str->isSpace() || *str == QLatin1Char(
',')) {
620 State state = Matrix;
621 if (*str == QLatin1Char(
'm')) {
622 const char *ident =
"atrix";
623 for (
int i = 0; i < 5; ++i)
624 if (*(++str) != QLatin1Char(ident[i]))
628 }
else if (*str == QLatin1Char(
't')) {
629 const char *ident =
"ranslate";
630 for (
int i = 0; i < 8; ++i)
631 if (*(++str) != QLatin1Char(ident[i]))
635 }
else if (*str == QLatin1Char(
'r')) {
636 const char *ident =
"otate";
637 for (
int i = 0; i < 5; ++i)
638 if (*(++str) != QLatin1Char(ident[i]))
642 }
else if (*str == QLatin1Char(
's')) {
644 if (*str == QLatin1Char(
'c')) {
645 const char *ident =
"ale";
646 for (
int i = 0; i < 3; ++i)
647 if (*(++str) != QLatin1Char(ident[i]))
651 }
else if (*str == QLatin1Char(
'k')) {
652 if (*(++str) != QLatin1Char(
'e'))
654 if (*(++str) != QLatin1Char(
'w'))
657 if (*str == QLatin1Char(
'X'))
659 else if (*str == QLatin1Char(
'Y'))
672 while (str < end && str->isSpace())
674 if (*str != QLatin1Char(
'('))
677 QVarLengthArray<qreal, 8> points;
679 if (*str != QLatin1Char(
')'))
683 if(state == Matrix) {
684 if(points.size() != 6)
686 matrix = QTransform(points[0], points[1],
687 points[2], points[3],
688 points[4], points[5]) * matrix;
689 }
else if (state == Translate) {
690 if (points.size() == 1)
691 matrix.translate(points[0], 0);
692 else if (points.size() == 2)
693 matrix.translate(points[0], points[1]);
696 }
else if (state == Rotate) {
697 if(points.size() == 1) {
698 matrix.rotate(points[0]);
699 }
else if (points.size() == 3) {
700 matrix.translate(points[1], points[2]);
701 matrix.rotate(points[0]);
702 matrix.translate(-points[1], -points[2]);
706 }
else if (state == Scale) {
707 if (points.size() < 1 || points.size() > 2)
709 qreal sx = points[0];
711 if(points.size() == 2)
713 matrix.scale(sx, sy);
714 }
else if (state == SkewX) {
715 if (points.size() != 1)
717 matrix.shear(qTan(qDegreesToRadians(points[0])), 0);
718 }
else if (state == SkewY) {
719 if (points.size() != 1)
721 matrix.shear(0, qTan(qDegreesToRadians(points[0])));
730 QSvgHandler *handler)
732 if (!attributes.stroke.isEmpty() || !attributes.strokeDashArray.isEmpty() || !attributes.strokeDashOffset.isEmpty() || !attributes.strokeLineCap.isEmpty()
733 || !attributes.strokeLineJoin.isEmpty() || !attributes.strokeMiterLimit.isEmpty() || !attributes.strokeOpacity.isEmpty() || !attributes.strokeWidth.isEmpty()
734 || !attributes.vectorEffect.isEmpty()) {
736 QSvgStrokeStyle *prop =
new QSvgStrokeStyle;
739 if ((!attributes.stroke.isEmpty()) && (attributes.stroke !=
QT_INHERIT) ) {
740 if (attributes.stroke.startsWith(QLatin1String(
"url"))) {
741 QStringView value = attributes.stroke.sliced(3);
742 QSvgStyleProperty *style = styleFromUrl(node, value);
744 if (style->type() == QSvgStyleProperty::SOLID_COLOR || style->type() == QSvgStyleProperty::GRADIENT
745 || style->type() == QSvgStyleProperty::PATTERN)
746 prop->setStyle(
reinterpret_cast<QSvgPaintStyleProperty *>(style));
748 QString id = idFromUrl(value).toString();
749 prop->setPaintStyleId(id);
750 prop->setPaintStyleResolved(
false);
752 }
else if (attributes.stroke != QLatin1String(
"none")) {
754 if (resolveColor(attributes.stroke, color, handler))
755 prop->setStroke(QBrush(color));
757 prop->setStroke(QBrush(Qt::NoBrush));
762 if (!attributes.strokeWidth.isEmpty() && attributes.strokeWidth !=
QT_INHERIT) {
764 prop->setWidth(QSvgUtils::parseLength(attributes.strokeWidth, <));
768 if (!attributes.strokeDashArray.isEmpty() && attributes.strokeDashArray !=
QT_INHERIT) {
769 if (attributes.strokeDashArray == QLatin1String(
"none")) {
770 prop->setDashArrayNone();
772 QString dashArray = attributes.strokeDashArray.toString();
773 const QChar *s = dashArray.constData();
774 QList<qreal> dashes = parseNumbersList(s);
775 bool allZeroes =
true;
776 for (qreal dash : dashes) {
785 if (allZeroes ==
false) {
787 if ((dashes.size() & 1) != 0)
788 dashes << QList<qreal>(dashes);
789 prop->setDashArray(dashes);
791 prop->setDashArrayNone();
797 if (!attributes.strokeLineJoin.isEmpty()) {
798 if (attributes.strokeLineJoin == QLatin1String(
"miter"))
799 prop->setLineJoin(Qt::SvgMiterJoin);
800 else if (attributes.strokeLineJoin == QLatin1String(
"round"))
801 prop->setLineJoin(Qt::RoundJoin);
802 else if (attributes.strokeLineJoin == QLatin1String(
"bevel"))
803 prop->setLineJoin(Qt::BevelJoin);
807 if (!attributes.strokeLineCap.isEmpty()) {
808 if (attributes.strokeLineCap == QLatin1String(
"butt"))
809 prop->setLineCap(Qt::FlatCap);
810 else if (attributes.strokeLineCap == QLatin1String(
"round"))
811 prop->setLineCap(Qt::RoundCap);
812 else if (attributes.strokeLineCap == QLatin1String(
"square"))
813 prop->setLineCap(Qt::SquareCap);
817 if (!attributes.strokeDashOffset.isEmpty() && attributes.strokeDashOffset !=
QT_INHERIT)
818 prop->setDashOffset(QSvgUtils::toDouble(attributes.strokeDashOffset));
821 if (!attributes.vectorEffect.isEmpty()) {
822 if (attributes.vectorEffect == QLatin1String(
"non-scaling-stroke"))
823 prop->setVectorEffect(
true);
824 else if (attributes.vectorEffect == QLatin1String(
"none"))
825 prop->setVectorEffect(
false);
829 if (!attributes.strokeMiterLimit.isEmpty() && attributes.strokeMiterLimit !=
QT_INHERIT)
830 prop->setMiterLimit(QSvgUtils::toDouble(attributes.strokeMiterLimit));
833 if (!attributes.strokeOpacity.isEmpty() && attributes.strokeOpacity !=
QT_INHERIT)
834 prop->setOpacity(qMin(qreal(1.0), qMax(qreal(0.0), QSvgUtils::toDouble(attributes.strokeOpacity))));
836 node->appendStyleProperty(prop, attributes.id);
844{ qreal(6.9), qreal(8.3), qreal(10.0), qreal(12.0), qreal(14.4), qreal(17.3), qreal(20.7) };
850 switch (spec.at(0).unicode()) {
852 if (spec == QLatin1String(
"xx-small"))
854 if (spec == QLatin1String(
"x-small"))
856 if (spec == QLatin1String(
"x-large"))
858 if (spec == QLatin1String(
"xx-large"))
862 if (spec == QLatin1String(
"small"))
866 if (spec == QLatin1String(
"medium"))
870 if (spec == QLatin1String(
"large"))
874 if (spec == QLatin1String(
"none"))
887 if (attributes.fontFamily.isEmpty() && attributes.fontSize.isEmpty() && attributes.fontStyle.isEmpty() &&
888 attributes.fontWeight.isEmpty() && attributes.fontVariant.isEmpty() && attributes.textAnchor.isEmpty())
891 QSvgFontStyle *fontStyle =
nullptr;
892 if (!attributes.fontFamily.isEmpty()) {
893 QSvgTinyDocument *doc = node->document();
895 QSvgFont *svgFont = doc->svgFont(attributes.fontFamily.toString());
897 fontStyle =
new QSvgFontStyle(svgFont, doc);
901 fontStyle =
new QSvgFontStyle;
902 if (!attributes.fontFamily.isEmpty() && attributes.fontFamily !=
QT_INHERIT) {
903 QStringView family = attributes.fontFamily.trimmed();
904 if (!family.isEmpty() && (family.at(0) == QLatin1Char(
'\'') || family.at(0) == QLatin1Char(
'\"')))
905 family = family.mid(1, family.size() - 2);
906 fontStyle->setFamily(family.toString());
909 if (!attributes.fontSize.isEmpty() && attributes.fontSize !=
QT_INHERIT) {
911 const FontSizeSpec spec = fontSizeSpec(attributes.fontSize);
917 qreal fs = QSvgUtils::parseLength(attributes.fontSize, &type);
918 fs = QSvgUtils::convertToPixels(fs,
true, type);
919 fontStyle->setSize(qMin(fs, qreal(0xffff)));
923 fontStyle->setSize(sizeTable[spec]);
928 if (!attributes.fontStyle.isEmpty() && attributes.fontStyle !=
QT_INHERIT) {
929 if (attributes.fontStyle == QLatin1String(
"normal")) {
930 fontStyle->setStyle(QFont::StyleNormal);
931 }
else if (attributes.fontStyle == QLatin1String(
"italic")) {
932 fontStyle->setStyle(QFont::StyleItalic);
933 }
else if (attributes.fontStyle == QLatin1String(
"oblique")) {
934 fontStyle->setStyle(QFont::StyleOblique);
938 if (!attributes.fontWeight.isEmpty() && attributes.fontWeight !=
QT_INHERIT) {
940 const int weightNum = attributes.fontWeight.toInt(&ok);
942 fontStyle->setWeight(weightNum);
944 if (attributes.fontWeight == QLatin1String(
"normal")) {
945 fontStyle->setWeight(QFont::Normal);
946 }
else if (attributes.fontWeight == QLatin1String(
"bold")) {
947 fontStyle->setWeight(QFont::Bold);
948 }
else if (attributes.fontWeight == QLatin1String(
"bolder")) {
949 fontStyle->setWeight(QSvgFontStyle::BOLDER);
950 }
else if (attributes.fontWeight == QLatin1String(
"lighter")) {
951 fontStyle->setWeight(QSvgFontStyle::LIGHTER);
956 if (!attributes.fontVariant.isEmpty() && attributes.fontVariant !=
QT_INHERIT) {
957 if (attributes.fontVariant == QLatin1String(
"normal"))
958 fontStyle->setVariant(QFont::MixedCase);
959 else if (attributes.fontVariant == QLatin1String(
"small-caps"))
960 fontStyle->setVariant(QFont::SmallCaps);
963 if (!attributes.textAnchor.isEmpty() && attributes.textAnchor !=
QT_INHERIT) {
964 if (attributes.textAnchor == QLatin1String(
"start"))
965 fontStyle->setTextAnchor(Qt::AlignLeft);
966 if (attributes.textAnchor == QLatin1String(
"middle"))
967 fontStyle->setTextAnchor(Qt::AlignHCenter);
968 else if (attributes.textAnchor == QLatin1String(
"end"))
969 fontStyle->setTextAnchor(Qt::AlignRight);
972 node->appendStyleProperty(fontStyle, attributes.id);
979 if (attributes.transform.isEmpty())
981 QTransform matrix = parseTransformationMatrix(attributes.transform.trimmed());
983 if (!matrix.isIdentity()) {
984 node->appendStyleProperty(
new QSvgTransformStyle(QTransform(matrix)), attributes.id);
993 QSvgNode *parent = node->parent();
995 if (parent && (attributes.visibility.isEmpty() || attributes.visibility ==
QT_INHERIT))
996 node->setVisible(parent->isVisible());
997 else if (attributes.visibility == QLatin1String(
"hidden") || attributes.visibility == QLatin1String(
"collapse")) {
998 node->setVisible(
false);
1000 node->setVisible(
true);
1004 const QXmlStreamAttributes &attributes,
1005 QSvgHandler *handler);
1011 str = str.trimmed();
1012 if (str.endsWith(QLatin1String(
"ms"))) {
1015 }
else if (str.endsWith(QLatin1String(
"s"))) {
1018 double val = ms * QSvgUtils::toDouble(str, ok);
1020 if (val >
std::numeric_limits<
int>::min() && val <
std::numeric_limits<
int>::max())
1021 res =
static_cast<
int>(val);
1028#ifndef QT_NO_CSSPARSER
1031 const QXmlStreamAttributes &attributes,
1032 QSvgHandler *handler)
1034 QSvgCssProperties cssAnimProps(attributes);
1035 QList<QSvgAnimationProperty> parsedProperties = cssAnimProps.animations();
1037 for (QSvgAnimationProperty property : parsedProperties) {
1038 QSvgCssAnimation *anim = handler->cssHandler().createAnimation(property.name);
1042 anim->setRunningTime(property.delay, property.duration);
1043 anim->setIterationCount(property.iteration);
1044 handler->setAnimPeriod(property.delay, property.delay + property.duration);
1045 handler->document()->animator()->appendAnimation(node, anim);
1046 handler->document()->setAnimated(
true);
1051 const QXmlStreamAttributes &attributes)
1053 QSvgCssProperties cssProperties(attributes);
1054 QSvgOffsetProperty offset = cssProperties.offset();
1059 QSvgOffsetStyle *offsetStyle =
new QSvgOffsetStyle();
1060 offsetStyle->setPath(offset.path.value());
1061 offsetStyle->setRotateAngle(offset.angle);
1062 offsetStyle->setRotateType(offset.rotateType);
1063 offsetStyle->setDistance(offset.distance);
1064 node->appendStyleProperty(offsetStyle, QString());
1069QtSvg::Options QSvgHandler::options()
const
1074QtSvg::AnimatorType QSvgHandler::animatorType()
const
1076 return m_animatorType;
1079bool QSvgHandler::trustedSourceMode()
const
1081 return m_options.testFlag(QtSvg::AssumeTrustedSource);
1086 QStringList lst = str.split(QLatin1Char(
','), Qt::SkipEmptyParts);
1091 const QXmlStreamAttributes &attributes)
1093 QStringList features;
1094 QStringList extensions;
1095 QStringList languages;
1096 QStringList formats;
1098 QStringView xmlClassStr;
1100 for (
const QXmlStreamAttribute &attribute : attributes) {
1101 QStringView name = attribute.qualifiedName();
1104 QStringView value = attribute.value();
1105 switch (name.at(0).unicode()) {
1107 if (name == QLatin1String(
"class"))
1108 xmlClassStr = value;
1111 if (name == QLatin1String(
"requiredFeatures"))
1112 features = stringToList(value.toString());
1113 else if (name == QLatin1String(
"requiredExtensions"))
1114 extensions = stringToList(value.toString());
1115 else if (name == QLatin1String(
"requiredFormats"))
1116 formats = stringToList(value.toString());
1117 else if (name == QLatin1String(
"requiredFonts"))
1118 fonts = stringToList(value.toString());
1121 if (name == QLatin1String(
"systemLanguage"))
1122 languages = stringToList(value.toString());
1129 node->setRequiredFeatures(features);
1130 node->setRequiredExtensions(extensions);
1131 node->setRequiredLanguages(languages);
1132 node->setRequiredFormats(formats);
1133 node->setRequiredFonts(fonts);
1134 node->setNodeId(someId(attributes));
1135 node->setXmlClass(xmlClassStr.toString());
1144 if (attributes.opacity.isEmpty())
1147 const QStringView value = attributes.opacity.trimmed();
1150 qreal op = value.toDouble(&ok);
1153 QSvgOpacityStyle *opacity =
new QSvgOpacityStyle(qBound(qreal(0.0), op, qreal(1.0)));
1154 node->appendStyleProperty(opacity, attributes.id);
1160#define NOOP qDebug()<<"Operation: "<<op<<" is not implemented"
1161 if (op == QLatin1String(
"clear")) {
1162 return QPainter::CompositionMode_Clear;
1163 }
else if (op == QLatin1String(
"src")) {
1164 return QPainter::CompositionMode_Source;
1165 }
else if (op == QLatin1String(
"dst")) {
1166 return QPainter::CompositionMode_Destination;
1167 }
else if (op == QLatin1String(
"src-over")) {
1168 return QPainter::CompositionMode_SourceOver;
1169 }
else if (op == QLatin1String(
"dst-over")) {
1170 return QPainter::CompositionMode_DestinationOver;
1171 }
else if (op == QLatin1String(
"src-in")) {
1172 return QPainter::CompositionMode_SourceIn;
1173 }
else if (op == QLatin1String(
"dst-in")) {
1174 return QPainter::CompositionMode_DestinationIn;
1175 }
else if (op == QLatin1String(
"src-out")) {
1176 return QPainter::CompositionMode_SourceOut;
1177 }
else if (op == QLatin1String(
"dst-out")) {
1178 return QPainter::CompositionMode_DestinationOut;
1179 }
else if (op == QLatin1String(
"src-atop")) {
1180 return QPainter::CompositionMode_SourceAtop;
1181 }
else if (op == QLatin1String(
"dst-atop")) {
1182 return QPainter::CompositionMode_DestinationAtop;
1183 }
else if (op == QLatin1String(
"xor")) {
1184 return QPainter::CompositionMode_Xor;
1185 }
else if (op == QLatin1String(
"plus")) {
1186 return QPainter::CompositionMode_Plus;
1187 }
else if (op == QLatin1String(
"multiply")) {
1188 return QPainter::CompositionMode_Multiply;
1189 }
else if (op == QLatin1String(
"screen")) {
1190 return QPainter::CompositionMode_Screen;
1191 }
else if (op == QLatin1String(
"overlay")) {
1192 return QPainter::CompositionMode_Overlay;
1193 }
else if (op == QLatin1String(
"darken")) {
1194 return QPainter::CompositionMode_Darken;
1195 }
else if (op == QLatin1String(
"lighten")) {
1196 return QPainter::CompositionMode_Lighten;
1197 }
else if (op == QLatin1String(
"color-dodge")) {
1198 return QPainter::CompositionMode_ColorDodge;
1199 }
else if (op == QLatin1String(
"color-burn")) {
1200 return QPainter::CompositionMode_ColorBurn;
1201 }
else if (op == QLatin1String(
"hard-light")) {
1202 return QPainter::CompositionMode_HardLight;
1203 }
else if (op == QLatin1String(
"soft-light")) {
1204 return QPainter::CompositionMode_SoftLight;
1205 }
else if (op == QLatin1String(
"difference")) {
1206 return QPainter::CompositionMode_Difference;
1207 }
else if (op == QLatin1String(
"exclusion")) {
1208 return QPainter::CompositionMode_Exclusion;
1213 return QPainter::CompositionMode_SourceOver;
1220 if (attributes.compOp.isEmpty())
1222 QStringView value = attributes.compOp.trimmed();
1224 if (!value.isEmpty()) {
1225 QSvgCompOpStyle *compop =
new QSvgCompOpStyle(svgToQtCompositionMode(value));
1226 node->appendStyleProperty(compop, attributes.id);
1232 if (str == QLatin1String(
"inline")) {
1233 return QSvgNode::InlineMode;
1234 }
else if (str == QLatin1String(
"block")) {
1235 return QSvgNode::BlockMode;
1236 }
else if (str == QLatin1String(
"list-item")) {
1237 return QSvgNode::ListItemMode;
1238 }
else if (str == QLatin1String(
"run-in")) {
1239 return QSvgNode::RunInMode;
1240 }
else if (str == QLatin1String(
"compact")) {
1241 return QSvgNode::CompactMode;
1242 }
else if (str == QLatin1String(
"marker")) {
1243 return QSvgNode::MarkerMode;
1244 }
else if (str == QLatin1String(
"table")) {
1245 return QSvgNode::TableMode;
1246 }
else if (str == QLatin1String(
"inline-table")) {
1247 return QSvgNode::InlineTableMode;
1248 }
else if (str == QLatin1String(
"table-row-group")) {
1249 return QSvgNode::TableRowGroupMode;
1250 }
else if (str == QLatin1String(
"table-header-group")) {
1251 return QSvgNode::TableHeaderGroupMode;
1252 }
else if (str == QLatin1String(
"table-footer-group")) {
1253 return QSvgNode::TableFooterGroupMode;
1254 }
else if (str == QLatin1String(
"table-row")) {
1255 return QSvgNode::TableRowMode;
1256 }
else if (str == QLatin1String(
"table-column-group")) {
1257 return QSvgNode::TableColumnGroupMode;
1258 }
else if (str == QLatin1String(
"table-column")) {
1259 return QSvgNode::TableColumnMode;
1260 }
else if (str == QLatin1String(
"table-cell")) {
1261 return QSvgNode::TableCellMode;
1262 }
else if (str == QLatin1String(
"table-caption")) {
1263 return QSvgNode::TableCaptionMode;
1264 }
else if (str == QLatin1String(
"none")) {
1265 return QSvgNode::NoneMode;
1267 return QSvgNode::InheritMode;
1269 return QSvgNode::BlockMode;
1276 if (attributes.display.isEmpty())
1278 QStringView displayStr = attributes.display.trimmed();
1280 if (!displayStr.isEmpty()) {
1281 node->setDisplayMode(displayStringToEnum(displayStr));
1287 if (attribute.isEmpty())
1288 return std::nullopt;
1290 constexpr QLatin1String urlKeyword(
"url");
1291 QStringView attrStr = attribute.trimmed();
1292 if (attrStr.startsWith(urlKeyword))
1293 attrStr.slice(urlKeyword.size());
1294 QStringView id = idFromUrl(attrStr);
1295 if (id.startsWith(QLatin1Char(
'#')))
1302 QSvgHandler *handler)
1307 if (
auto id = getAttributeId(attributes.mask))
1308 node->setMaskId(id->toString());
1309 if (
auto id = getAttributeId(attributes.markerStart))
1310 node->setMarkerStartId(id->toString());
1311 if (
auto id = getAttributeId(attributes.markerMid))
1312 node->setMarkerMidId(id->toString());
1313 if (
auto id = getAttributeId(attributes.markerEnd))
1314 node->setMarkerEndId(id->toString());
1315 if (
auto id = getAttributeId(attributes.filter))
1316 node->setFilterId(id->toString());
1323 if (attributes.imageRendering.isEmpty())
1326 QStringView ir = attributes.imageRendering.trimmed();
1327 QSvgQualityStyle *p =
new QSvgQualityStyle(0);
1328 if (ir == QLatin1String(
"auto"))
1329 p->setImageRendering(QSvgQualityStyle::ImageRenderingAuto);
1330 else if (ir == QLatin1String(
"optimizeSpeed"))
1331 p->setImageRendering(QSvgQualityStyle::ImageRenderingOptimizeSpeed);
1332 else if (ir == QLatin1String(
"optimizeQuality"))
1333 p->setImageRendering(QSvgQualityStyle::ImageRenderingOptimizeQuality);
1334 node->appendStyleProperty(p, attributes.id);
1338 const QXmlStreamAttributes &attributes,
1339 QSvgHandler *handler)
1347#ifndef QT_NO_CSSPARSER
1348 QXmlStreamAttributes cssAttributes;
1349 handler->cssHandler().styleLookup(node, cssAttributes);
1351 QStringView style = attributes.value(QLatin1String(
"style"));
1352 if (!style.isEmpty())
1353 handler->cssHandler().parseCSStoXMLAttrs(style.toString(), cssAttributes);
1354 svgAttributes.setAttributes(cssAttributes, handler);
1356 parseOffsetPath(node, cssAttributes);
1358 parseCssAnimations(node, cssAttributes, handler);
1377 const QXmlStreamAttributes &attributes,
1380 Q_UNUSED(parent); Q_UNUSED(attributes);
1385 const QXmlStreamAttributes &attributes,
1386 QSvgAnimateNode *anim,
1387 QSvgHandler *handler)
1389 const QStringView beginStr = attributes.value(QLatin1String(
"begin"));
1390 const QStringView durStr = attributes.value(QLatin1String(
"dur"));
1391 const QStringView endStr = attributes.value(QLatin1String(
"end"));
1392 const QStringView repeatStr = attributes.value(QLatin1String(
"repeatCount"));
1393 const QStringView fillStr = attributes.value(QLatin1String(
"fill"));
1394 const QStringView addtv = attributes.value(QLatin1String(
"additive"));
1395 QStringView linkId = attributes.value(QLatin1String(
"xlink:href"));
1397 if (linkId.isEmpty())
1398 linkId = attributes.value(QLatin1String(
"href"));
1400 linkId = linkId.mid(1);
1403 int begin = parseClockValue(beginStr, &ok);
1406 int dur = parseClockValue(durStr, &ok);
1409 int end = parseClockValue(endStr, &ok);
1412 qreal repeatCount = (repeatStr == QLatin1String(
"indefinite")) ? -1 :
1413 qMax(1.0, QSvgUtils::toDouble(repeatStr));
1415 QSvgAnimateNode::Fill fill = (fillStr == QLatin1String(
"freeze")) ? QSvgAnimateNode::Freeze :
1416 QSvgAnimateNode::Remove;
1418 QSvgAnimateNode::Additive additive = (addtv == QLatin1String(
"sum")) ? QSvgAnimateNode::Sum :
1419 QSvgAnimateNode::Replace;
1421 anim->setRunningTime(begin, dur, end, 0);
1422 anim->setRepeatCount(repeatCount);
1423 anim->setFill(fill);
1424 anim->setAdditiveType(additive);
1425 anim->setLinkId(linkId.toString());
1427 parent->document()->setAnimated(
true);
1429 handler->setAnimPeriod(begin, begin + dur);
1438 qreal spacing = 1.0f / (count - 1);
1439 for (uint i = 0; i < count; i++) {
1440 keyFrames.append(i * spacing);
1445 const QXmlStreamAttributes &attributes,
1446 QSvgHandler *handler)
1448 const QStringView fromStr = attributes.value(QLatin1String(
"from"));
1449 const QStringView toStr = attributes.value(QLatin1String(
"to"));
1450 const QStringView valuesStr = attributes.value(QLatin1String(
"values"));
1451 const QString targetStr = attributes.value(QLatin1String(
"attributeName")).toString();
1453 if (targetStr != QLatin1String(
"fill") && targetStr != QLatin1String(
"stroke"))
1456 QList<QColor> colors;
1457 if (valuesStr.isEmpty()) {
1458 QColor startColor, endColor;
1459 resolveColor(fromStr, startColor, handler);
1460 resolveColor(toStr, endColor, handler);
1462 colors.append(startColor);
1463 colors.append(endColor);
1465 for (
auto part : qTokenize(valuesStr, u';')) {
1467 resolveColor(part, color, handler);
1468 colors.append(color);
1472 QSvgAnimatedPropertyColor *prop =
static_cast<QSvgAnimatedPropertyColor *>
1473 (QSvgAbstractAnimatedProperty::createAnimatedProperty(targetStr));
1477 prop->setColors(colors);
1479 QList<qreal> keyFrames;
1480 generateKeyFrames(keyFrames, colors.size());
1481 prop->setKeyFrames(keyFrames);
1483 QSvgAnimateColor *anim =
new QSvgAnimateColor(parent);
1484 anim->appendProperty(prop);
1486 if (!parseBaseAnimate(parent, attributes, anim, handler)) {
1495 const QXmlStreamAttributes &attributes,
1498 Q_UNUSED(parent); Q_UNUSED(attributes);
1504 QList<qreal> list = parseNumbersList(s);
1506 for (
int i = 3 - list.size(); i > 0; --i)
1511 const QXmlStreamAttributes &attributes,
1512 QSvgHandler *handler)
1514 const QStringView typeStr = attributes.value(QLatin1String(
"type"));
1515 const QStringView values = attributes.value(QLatin1String(
"values"));
1516 const QStringView fromStr = attributes.value(QLatin1String(
"from"));
1517 const QStringView toStr = attributes.value(QLatin1String(
"to"));
1518 const QStringView byStr = attributes.value(QLatin1String(
"by"));
1521 if (values.isEmpty()) {
1523 if (fromStr.isEmpty()) {
1524 if (!byStr.isEmpty()) {
1528 parseNumberTriplet(vals, s = byStr.constData());
1534 if (!toStr.isEmpty()) {
1536 parseNumberTriplet(vals, s = fromStr.constData());
1537 parseNumberTriplet(vals, s = toStr.constData());
1538 }
else if (!byStr.isEmpty()) {
1540 parseNumberTriplet(vals, s = fromStr.constData());
1541 parseNumberTriplet(vals, s = byStr.constData());
1542 for (
int i = vals.size() - 3; i < vals.size(); ++i)
1543 vals[i] += vals[i - 3];
1549 const QChar *s = values.constData();
1550 while (s && s != values.cend()) {
1551 parseNumberTriplet(vals, s);
1552 if (s == values.cend())
1557 if (vals.size() % 3 != 0)
1561 QList<QSvgAnimatedPropertyTransform::TransformComponent> components;
1562 for (
int i = 0; i < vals.size(); i+=3) {
1563 QSvgAnimatedPropertyTransform::TransformComponent component;
1564 if (typeStr == QLatin1String(
"translate")) {
1565 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Translate;
1566 component.values.append(vals.at(i));
1567 component.values.append(vals.at(i + 1));
1568 }
else if (typeStr == QLatin1String(
"scale")) {
1569 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Scale;
1570 component.values.append(vals.at(i));
1571 component.values.append(vals.at(i + 1));
1572 }
else if (typeStr == QLatin1String(
"rotate")) {
1573 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Rotate;
1574 component.values.append(vals.at(i));
1575 component.values.append(vals.at(i + 1));
1576 component.values.append(vals.at(i + 2));
1577 }
else if (typeStr == QLatin1String(
"skewX")) {
1578 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Skew;
1579 component.values.append(vals.at(i));
1580 component.values.append(0);
1581 }
else if (typeStr == QLatin1String(
"skewY")) {
1582 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Skew;
1583 component.values.append(0);
1584 component.values.append(vals.at(i));
1588 components.append(component);
1591 QSvgAnimatedPropertyTransform *prop =
static_cast<QSvgAnimatedPropertyTransform *>
1592 (QSvgAbstractAnimatedProperty::createAnimatedProperty(QLatin1String(
"transform")));
1596 prop->appendComponents(components);
1598 prop->setTransformCount(1);
1599 QList<qreal> keyFrames;
1600 generateKeyFrames(keyFrames, vals.size() / 3);
1601 prop->setKeyFrames(keyFrames);
1603 QSvgAnimateTransform *anim =
new QSvgAnimateTransform(parent);
1604 anim->appendProperty(prop);
1606 if (!parseBaseAnimate(parent, attributes, anim, handler)) {
1615 const QXmlStreamAttributes &attributes,
1618 Q_UNUSED(parent); Q_UNUSED(attributes);
1623 const QXmlStreamAttributes &attributes,
1626 Q_UNUSED(parent); Q_UNUSED(attributes);
1631 const QXmlStreamAttributes &attributes,
1634 const QStringView cx = attributes.value(QLatin1String(
"cx"));
1635 const QStringView cy = attributes.value(QLatin1String(
"cy"));
1636 const QStringView r = attributes.value(QLatin1String(
"r"));
1637 qreal ncx = QSvgUtils::toDouble(cx);
1638 qreal ncy = QSvgUtils::toDouble(cy);
1639 qreal nr = QSvgUtils::toDouble(r);
1643 QRectF rect(ncx-nr, ncy-nr, nr*2, nr*2);
1644 QSvgNode *circle =
new QSvgCircle(parent, rect);
1649 const QXmlStreamAttributes &attributes,
1652 Q_UNUSED(attributes);
1653 QSvgDefs *defs =
new QSvgDefs(parent);
1658 const QXmlStreamAttributes &attributes,
1661 Q_UNUSED(parent); Q_UNUSED(attributes);
1666 const QXmlStreamAttributes &attributes,
1669 const QStringView cx = attributes.value(QLatin1String(
"cx"));
1670 const QStringView cy = attributes.value(QLatin1String(
"cy"));
1671 const QStringView rx = attributes.value(QLatin1String(
"rx"));
1672 const QStringView ry = attributes.value(QLatin1String(
"ry"));
1673 qreal ncx = QSvgUtils::toDouble(cx);
1674 qreal ncy = QSvgUtils::toDouble(cy);
1675 qreal nrx = QSvgUtils::toDouble(rx);
1676 qreal nry = QSvgUtils::toDouble(ry);
1678 QRectF rect(ncx-nrx, ncy-nry, nrx*2, nry*2);
1679 QSvgNode *ellipse =
new QSvgEllipse(parent, rect);
1684 const QXmlStreamAttributes &attributes,
1687 const QStringView hax = attributes.value(QLatin1String(
"horiz-adv-x"));
1688 QString myId = someId(attributes);
1690 qreal horizAdvX = QSvgUtils::toDouble(hax);
1692 while (parent && parent->type() != QSvgNode::Doc) {
1693 parent = parent->parent();
1696 if (parent && !myId.isEmpty()) {
1697 QSvgTinyDocument *doc =
static_cast<QSvgTinyDocument*>(parent);
1698 QSvgFont *font = doc->svgFont(myId);
1700 font =
new QSvgFont(horizAdvX);
1701 font->setFamilyName(myId);
1702 doc->addSvgFont(font);
1704 return new QSvgFontStyle(font, doc);
1710 const QXmlStreamAttributes &attributes,
1713 if (parent->type() != QSvgStyleProperty::FONT) {
1717 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
1718 QSvgFont *font = style->svgFont();
1719 const QStringView name = attributes.value(QLatin1String(
"font-family"));
1720 const QStringView unitsPerEmStr = attributes.value(QLatin1String(
"units-per-em"));
1722 qreal unitsPerEm = QSvgUtils::toDouble(unitsPerEmStr);
1724 unitsPerEm = QSvgFont::DEFAULT_UNITS_PER_EM;
1726 if (!name.isEmpty())
1727 font->setFamilyName(name.toString());
1728 font->setUnitsPerEm(unitsPerEm);
1730 if (!font->familyName().isEmpty())
1731 if (!style->doc()->svgFont(font->familyName()))
1732 style->doc()->addSvgFont(font);
1738 const QXmlStreamAttributes &attributes,
1741 if (parent->type() != QSvgStyleProperty::FONT) {
1745 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
1746 QSvgFont *font = style->svgFont();
1747 const QStringView name = attributes.value(QLatin1String(
"name"));
1749 if (!name.isEmpty())
1750 font->setFamilyName(name.toString());
1752 if (!font->familyName().isEmpty())
1753 if (!style->doc()->svgFont(font->familyName()))
1754 style->doc()->addSvgFont(font);
1760 const QXmlStreamAttributes &attributes,
1763 Q_UNUSED(parent); Q_UNUSED(attributes);
1768 const QXmlStreamAttributes &attributes,
1771 Q_UNUSED(parent); Q_UNUSED(attributes);
1776 const QXmlStreamAttributes &attributes,
1779 Q_UNUSED(parent); Q_UNUSED(attributes);
1784 const QXmlStreamAttributes &attributes,
1787 Q_UNUSED(attributes);
1788 QSvgG *node =
new QSvgG(parent);
1793 const QXmlStreamAttributes &attributes,
1796 if (parent->type() != QSvgStyleProperty::FONT) {
1800 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
1801 QSvgFont *font = style->svgFont();
1802 return createSvgGlyph(font, attributes,
false);
1806 const QXmlStreamAttributes &attributes,
1809 Q_UNUSED(parent); Q_UNUSED(attributes);
1814 const QXmlStreamAttributes &attributes,
1817 Q_UNUSED(parent); Q_UNUSED(attributes);
1822 const QXmlStreamAttributes &attributes,
1823 QSvgHandler *handler)
1825 const QStringView x = attributes.value(QLatin1String(
"x"));
1826 const QStringView y = attributes.value(QLatin1String(
"y"));
1827 const QStringView width = attributes.value(QLatin1String(
"width"));
1828 const QStringView height = attributes.value(QLatin1String(
"height"));
1829 QString filename = attributes.value(QLatin1String(
"xlink:href")).toString();
1831 filename = attributes.value(QLatin1String(
"href")).toString();
1832 qreal nx = QSvgUtils::toDouble(x);
1833 qreal ny = QSvgUtils::toDouble(y);
1835 qreal nwidth = QSvgUtils::parseLength(width.toString(), &type);
1836 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
1838 qreal nheight = QSvgUtils::parseLength(height.toString(), &type);
1839 nheight = QSvgUtils::convertToPixels(nheight,
false, type);
1841 filename = filename.trimmed();
1842 if (filename.isEmpty()) {
1843 qCWarning(lcSvgHandler) <<
"QSvgHandler: Image filename is empty";
1846 if (nwidth <= 0 || nheight <= 0) {
1847 qCWarning(lcSvgHandler) <<
"QSvgHandler: Width or height for" << filename <<
"image was not greater than 0";
1856 } filenameType = NotLoaded;
1858 if (filename.startsWith(QLatin1String(
"data"))) {
1861 if (qDecodeDataUrl(filename, mimeType, data)) {
1862 image = QImage::fromData(data);
1863 filenameType = LoadedFromData;
1867 if (image.isNull()) {
1868 const auto *file = qobject_cast<QFile *>(handler->device());
1871 if (url.isRelative()) {
1873 filename = info.absoluteDir().absoluteFilePath(filename);
1877 if (handler->trustedSourceMode() || !QImageReader::imageFormat(filename).startsWith(
"svg")) {
1878 image = QImage(filename);
1879 filenameType = LoadedFromFile;
1883 if (image.isNull()) {
1884 qCWarning(lcSvgHandler) <<
"Could not create image from" << filename;
1888 if (image.format() == QImage::Format_ARGB32)
1889 image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
1891 QSvgNode *img =
new QSvgImage(parent,
1893 filenameType == LoadedFromFile ? filename : QString{},
1902 const QXmlStreamAttributes &attributes,
1905 const QStringView x1 = attributes.value(QLatin1String(
"x1"));
1906 const QStringView y1 = attributes.value(QLatin1String(
"y1"));
1907 const QStringView x2 = attributes.value(QLatin1String(
"x2"));
1908 const QStringView y2 = attributes.value(QLatin1String(
"y2"));
1909 qreal nx1 = QSvgUtils::toDouble(x1);
1910 qreal ny1 = QSvgUtils::toDouble(y1);
1911 qreal nx2 = QSvgUtils::toDouble(x2);
1912 qreal ny2 = QSvgUtils::toDouble(y2);
1914 QLineF lineBounds(nx1, ny1, nx2, ny2);
1915 QSvgNode *line =
new QSvgLine(parent, lineBounds);
1921 const QXmlStreamAttributes &attributes,
1922 QSvgGradientStyle *gradProp,
1923 QSvgHandler *handler)
1925 const QStringView link = attributes.value(QLatin1String(
"xlink:href"));
1926 const QStringView trans = attributes.value(QLatin1String(
"gradientTransform"));
1927 const QStringView spread = attributes.value(QLatin1String(
"spreadMethod"));
1928 const QStringView units = attributes.value(QLatin1String(
"gradientUnits"));
1929 const QStringView colorStr = attributes.value(QLatin1String(
"color"));
1930 const QStringView colorOpacityStr = attributes.value(QLatin1String(
"color-opacity"));
1933 if (constructColor(colorStr, colorOpacityStr, color, handler)) {
1934 handler->popColor();
1935 handler->pushColor(color);
1939 QGradient *grad = gradProp->qgradient();
1940 if (node && !link.isEmpty()) {
1941 QSvgStyleProperty *prop = node->styleProperty(link);
1942 if (prop && prop->type() == QSvgStyleProperty::GRADIENT) {
1943 QSvgGradientStyle *inherited =
1944 static_cast<QSvgGradientStyle*>(prop);
1945 if (!inherited->stopLink().isEmpty()) {
1946 gradProp->setStopLink(inherited->stopLink(), handler->document());
1948 grad->setStops(inherited->qgradient()->stops());
1949 gradProp->setGradientStopsSet(inherited->gradientStopsSet());
1952 matrix = inherited->qtransform();
1954 gradProp->setStopLink(link.toString(), handler->document());
1958 if (!trans.isEmpty()) {
1959 matrix = parseTransformationMatrix(trans);
1960 gradProp->setTransform(matrix);
1961 }
else if (!matrix.isIdentity()) {
1962 gradProp->setTransform(matrix);
1965 if (!spread.isEmpty()) {
1966 if (spread == QLatin1String(
"pad")) {
1967 grad->setSpread(QGradient::PadSpread);
1968 }
else if (spread == QLatin1String(
"reflect")) {
1969 grad->setSpread(QGradient::ReflectSpread);
1970 }
else if (spread == QLatin1String(
"repeat")) {
1971 grad->setSpread(QGradient::RepeatSpread);
1975 if (units.isEmpty() || units == QLatin1String(
"objectBoundingBox")) {
1976 grad->setCoordinateMode(QGradient::ObjectMode);
1981 const QXmlStreamAttributes &attributes,
1982 QSvgHandler *handler)
1984 const QStringView x1 = attributes.value(QLatin1String(
"x1"));
1985 const QStringView y1 = attributes.value(QLatin1String(
"y1"));
1986 const QStringView x2 = attributes.value(QLatin1String(
"x2"));
1987 const QStringView y2 = attributes.value(QLatin1String(
"y2"));
1995 nx1 = convertToNumber(x1);
1997 ny1 = convertToNumber(y1);
1999 nx2 = convertToNumber(x2);
2001 ny2 = convertToNumber(y2);
2003 QSvgNode *itr = node;
2004 while (itr && itr->type() != QSvgNode::Doc) {
2005 itr = itr->parent();
2008 QLinearGradient *grad =
new QLinearGradient(nx1, ny1, nx2, ny2);
2009 grad->setInterpolationMode(QGradient::ComponentInterpolation);
2010 QSvgGradientStyle *prop =
new QSvgGradientStyle(grad);
2011 parseBaseGradient(node, attributes, prop, handler);
2017 const QXmlStreamAttributes &attributes,
2020 Q_UNUSED(parent); Q_UNUSED(attributes);
2025 const QXmlStreamAttributes &attributes,
2028 if (parent->type() != QSvgStyleProperty::FONT) {
2032 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
2033 QSvgFont *font = style->svgFont();
2034 return createSvgGlyph(font, attributes,
true);
2038 const QXmlStreamAttributes &attributes,
2041 Q_UNUSED(parent); Q_UNUSED(attributes);
2046 const QXmlStreamAttributes &attributes,
2049 Q_UNUSED(parent); Q_UNUSED(attributes);
2054 const QXmlStreamAttributes &,
2061 const QXmlStreamAttributes &attributes,
2062 QSvgHandler *handler)
2064 const QStringView x = attributes.value(QLatin1String(
"x"));
2065 const QStringView y = attributes.value(QLatin1String(
"y"));
2066 const QStringView width = attributes.value(QLatin1String(
"width"));
2067 const QStringView height = attributes.value(QLatin1String(
"height"));
2068 const QStringView mU = attributes.value(QLatin1String(
"maskUnits"));
2069 const QStringView mCU = attributes.value(QLatin1String(
"maskContentUnits"));
2071 QtSvg::
UnitTypes nmU = mU.contains(QLatin1String(
"userSpaceOnUse")) ?
2074 QtSvg::
UnitTypes nmCU = mCU.contains(QLatin1String(
"objectBoundingBox")) ?
2084 qreal nx = QSvgUtils::parseLength(x, &type, &ok);
2085 nx = QSvgUtils::convertToPixels(nx,
true, type);
2086 if (x.isEmpty() || !ok) {
2090 nx = nx / 100. * handler->document()->viewBox().width();
2095 qreal ny = QSvgUtils::parseLength(y, &type, &ok);
2096 ny = QSvgUtils::convertToPixels(ny,
true, type);
2097 if (y.isEmpty() || !ok) {
2101 ny = ny / 100. * handler->document()->viewBox().height();
2106 qreal nwidth = QSvgUtils::parseLength(width, &type, &ok);
2107 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
2108 if (width.isEmpty() || !ok) {
2112 nwidth = nwidth / 100. * handler->document()->viewBox().width();
2114 nwidth = nwidth / 100.;
2117 qreal nheight = QSvgUtils::parseLength(height, &type, &ok);
2118 nheight = QSvgUtils::convertToPixels(nheight,
true, type);
2119 if (height.isEmpty() || !ok) {
2123 nheight = nheight / 100. * handler->document()->viewBox().height();
2125 nheight = nheight / 100.;
2128 QRectF bounds(nx, ny, nwidth, nheight);
2129 if (bounds.isEmpty())
2132 QSvgNode *mask =
new QSvgMask(parent, QSvgRectF(bounds, nmUx, nmUy, nmUw, nmUh), nmCU);
2139 const QStringView xStr = attributes.value(QLatin1String(
"x"));
2140 const QStringView yStr = attributes.value(QLatin1String(
"y"));
2141 const QStringView widthStr = attributes.value(QLatin1String(
"width"));
2142 const QStringView heightStr = attributes.value(QLatin1String(
"height"));
2145 if (!xStr.isEmpty()) {
2147 x = QSvgUtils::parseLength(xStr, &type);
2149 x = QSvgUtils::convertToPixels(x,
true, type);
2159 if (!yStr.isEmpty()) {
2161 y = QSvgUtils::parseLength(yStr, &type);
2163 y = QSvgUtils::convertToPixels(y,
false, type);
2173 if (!widthStr.isEmpty()) {
2175 width = QSvgUtils::parseLength(widthStr, &type);
2177 width = QSvgUtils::convertToPixels(width,
true, type);
2184 rect->setWidth(width);
2187 if (!heightStr.isEmpty()) {
2189 height = QSvgUtils::parseLength(heightStr, &type);
2191 height = QSvgUtils::convertToPixels(height,
false, type);
2198 rect->setHeight(height);
2203 const QXmlStreamAttributes &attributes,
2204 QSvgHandler *handler)
2206 const QStringView fU = attributes.value(QLatin1String(
"filterUnits"));
2207 const QStringView pU = attributes.value(QLatin1String(
"primitiveUnits"));
2209 const QtSvg::
UnitTypes filterUnits = fU.contains(QLatin1String(
"userSpaceOnUse")) ?
2212 const QtSvg::
UnitTypes primitiveUnits = pU.contains(QLatin1String(
"objectBoundingBox")) ?
2220 qreal width = handler->document()->viewBox().width();
2221 qreal height = handler->document()->viewBox().height();
2222 rect = QSvgRectF(QRectF(-0.1 * width, -0.1 * height, 1.2 * width, 1.2 * height),
2223 QtSvg::UnitTypes::userSpaceOnUse, QtSvg::UnitTypes::userSpaceOnUse,
2224 QtSvg::UnitTypes::userSpaceOnUse, QtSvg::UnitTypes::userSpaceOnUse);
2226 rect = QSvgRectF(QRectF(-0.1, -0.1, 1.2, 1.2),
2227 QtSvg::UnitTypes::objectBoundingBox, QtSvg::UnitTypes::objectBoundingBox,
2228 QtSvg::UnitTypes::objectBoundingBox, QtSvg::UnitTypes::objectBoundingBox);
2231 parseFilterBounds(attributes, &rect);
2233 QSvgNode *filter =
new QSvgFilterContainer(parent, rect, filterUnits, primitiveUnits);
2238 QString *outString, QSvgRectF *rect)
2240 *inString = attributes.value(QLatin1String(
"in")).toString();
2241 *outString = attributes.value(QLatin1String(
"result")).toString();
2247 *rect = QSvgRectF(QRectF(0, 0, 1.0, 1.0),
2253 parseFilterBounds(attributes, rect);
2257 const QXmlStreamAttributes &attributes,
2260 const QStringView typeString = attributes.value(QLatin1String(
"type"));
2261 const QStringView valuesString = attributes.value(QLatin1String(
"values"));
2263 QString inputString;
2264 QString outputString;
2267 QSvgFeColorMatrix::ColorShiftType type;
2268 QSvgFeColorMatrix::Matrix values;
2271 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2273 if (typeString.startsWith(QLatin1String(
"saturate")))
2274 type = QSvgFeColorMatrix::ColorShiftType::Saturate;
2275 else if (typeString.startsWith(QLatin1String(
"hueRotate")))
2276 type = QSvgFeColorMatrix::ColorShiftType::HueRotate;
2277 else if (typeString.startsWith(QLatin1String(
"luminanceToAlpha")))
2278 type = QSvgFeColorMatrix::ColorShiftType::LuminanceToAlpha;
2280 type = QSvgFeColorMatrix::ColorShiftType::Matrix;
2282 if (!valuesString.isEmpty()) {
2283 const auto valueStringList = splitWithDelimiter(valuesString);
2284 for (
int i = 0, j = 0; i < qMin(20, valueStringList.size()); i++) {
2286 qreal v = QSvgUtils::toDouble(valueStringList.at(i), &ok);
2288 values.data()[j] = v;
2293 values.setToIdentity();
2296 QSvgNode *filter =
new QSvgFeColorMatrix(parent, inputString, outputString, rect,
2302 const QXmlStreamAttributes &attributes,
2305 const QStringView edgeModeString = attributes.value(QLatin1String(
"edgeMode"));
2306 const QStringView stdDeviationString = attributes.value(QLatin1String(
"stdDeviation"));
2308 QString inputString;
2309 QString outputString;
2312 QSvgFeGaussianBlur::EdgeMode edgemode = QSvgFeGaussianBlur::EdgeMode::Duplicate;
2314 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2315 qreal stdDeviationX = 0;
2316 qreal stdDeviationY = 0;
2318 stdDeviationX = qMax(0., QSvgUtils::toDouble(stdDeviationString.split(u" ").first()));
2319 stdDeviationY = qMax(0., QSvgUtils::toDouble(stdDeviationString.split(u" ").last()));
2321 stdDeviationY = stdDeviationX = qMax(0., QSvgUtils::toDouble(stdDeviationString));
2324 if (edgeModeString.startsWith(QLatin1String(
"wrap")))
2325 edgemode = QSvgFeGaussianBlur::EdgeMode::Wrap;
2326 else if (edgeModeString.startsWith(QLatin1String(
"none")))
2327 edgemode = QSvgFeGaussianBlur::EdgeMode::None;
2329 QSvgNode *filter =
new QSvgFeGaussianBlur(parent, inputString, outputString, rect,
2330 stdDeviationX, stdDeviationY, edgemode);
2335 const QXmlStreamAttributes &attributes,
2338 QStringView dxString = attributes.value(QLatin1String(
"dx"));
2339 QStringView dyString = attributes.value(QLatin1String(
"dy"));
2341 QString inputString;
2342 QString outputString;
2345 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2348 if (!dxString.isEmpty()) {
2350 dx = QSvgUtils::parseLength(dxString, &type);
2352 dx = QSvgUtils::convertToPixels(dx,
true, type);
2356 if (!dyString.isEmpty()) {
2358 dy = QSvgUtils::parseLength(dyString, &type);
2360 dy = QSvgUtils::convertToPixels(dy,
true, type);
2363 QSvgNode *filter =
new QSvgFeOffset(parent, inputString, outputString, rect,
2369 const QXmlStreamAttributes &attributes,
2372 const QStringView in2String = attributes.value(QLatin1String(
"in2"));
2373 const QStringView operatorString = attributes.value(QLatin1String(
"operator"));
2374 const QStringView k1String = attributes.value(QLatin1String(
"k1"));
2375 const QStringView k2String = attributes.value(QLatin1String(
"k2"));
2376 const QStringView k3String = attributes.value(QLatin1String(
"k3"));
2377 const QStringView k4String = attributes.value(QLatin1String(
"k4"));
2379 QString inputString;
2380 QString outputString;
2383 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2385 QSvgFeComposite::Operator op = QSvgFeComposite::Operator::Over;
2386 if (operatorString.startsWith(QLatin1String(
"in")))
2387 op = QSvgFeComposite::Operator::In;
2388 else if (operatorString.startsWith(QLatin1String(
"out")))
2389 op = QSvgFeComposite::Operator::Out;
2390 else if (operatorString.startsWith(QLatin1String(
"atop")))
2391 op = QSvgFeComposite::Operator::Atop;
2392 else if (operatorString.startsWith(QLatin1String(
"xor")))
2393 op = QSvgFeComposite::Operator::Xor;
2394 else if (operatorString.startsWith(QLatin1String(
"lighter")))
2395 op = QSvgFeComposite::Operator::Lighter;
2396 else if (operatorString.startsWith(QLatin1String(
"arithmetic")))
2397 op = QSvgFeComposite::Operator::Arithmetic;
2399 QVector4D k(0, 0, 0, 0);
2401 if (op == QSvgFeComposite::Operator::Arithmetic) {
2403 qreal v = QSvgUtils::toDouble(k1String, &ok);
2406 v = QSvgUtils::toDouble(k2String, &ok);
2409 v = QSvgUtils::toDouble(k3String, &ok);
2412 v = QSvgUtils::toDouble(k4String, &ok);
2417 QSvgNode *filter =
new QSvgFeComposite(parent, inputString, outputString, rect,
2418 in2String.toString(), op, k);
2424 const QXmlStreamAttributes &attributes,
2427 QString inputString;
2428 QString outputString;
2431 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2433 QSvgNode *filter =
new QSvgFeMerge(parent, inputString, outputString, rect);
2438 const QXmlStreamAttributes &attributes,
2439 QSvgHandler *handler)
2441 QStringView colorStr = attributes.value(QLatin1String(
"flood-color"));
2442 const QStringView opacityStr = attributes.value(QLatin1String(
"flood-opacity"));
2445 if (!constructColor(colorStr, opacityStr, color, handler)) {
2446 color = QColor(Qt::black);
2448 qreal op = qMin(qreal(1.0), qMax(qreal(0.0), QSvgUtils::toDouble(opacityStr, &ok)));
2450 color.setAlphaF(op);
2453 QString inputString;
2454 QString outputString;
2457 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2459 QSvgNode *filter =
new QSvgFeFlood(parent, inputString, outputString, rect, color);
2464 const QXmlStreamAttributes &attributes,
2467 QString inputString;
2468 QString outputString;
2471 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2473 QSvgNode *filter =
new QSvgFeMergeNode(parent, inputString, outputString, rect);
2478 const QXmlStreamAttributes &attributes,
2481 const QStringView in2String = attributes.value(QLatin1String(
"in2"));
2482 const QStringView modeString = attributes.value(QLatin1String(
"mode"));
2484 QString inputString;
2485 QString outputString;
2488 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2490 QSvgFeBlend::Mode mode = QSvgFeBlend::Mode::Normal;
2491 if (modeString.startsWith(QLatin1StringView(
"multiply")))
2492 mode = QSvgFeBlend::Mode::Multiply;
2493 else if (modeString.startsWith(QLatin1StringView(
"screen")))
2494 mode = QSvgFeBlend::Mode::Screen;
2495 else if (modeString.startsWith(QLatin1StringView(
"darken")))
2496 mode = QSvgFeBlend::Mode::Darken;
2497 else if (modeString.startsWith(QLatin1StringView(
"lighten")))
2498 mode = QSvgFeBlend::Mode::Lighten;
2500 QSvgNode *filter =
new QSvgFeBlend(parent, inputString, outputString, rect,
2501 in2String.toString(), mode);
2506 const QXmlStreamAttributes &attributes,
2509 QString inputString;
2510 QString outputString;
2513 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2515 QSvgNode *filter =
new QSvgFeUnsupported(parent, inputString, outputString, rect);
2521 QList<QStringView> viewBoxValues;
2524 viewBoxValues = splitWithDelimiter(str);
2525 if (viewBoxValues.size() == 4) {
2527 qreal x = QSvgUtils::parseLength(viewBoxValues.at(0).trimmed(), &type);
2528 qreal y = QSvgUtils::parseLength(viewBoxValues.at(1).trimmed(), &type);
2529 qreal w = QSvgUtils::parseLength(viewBoxValues.at(2).trimmed(), &type);
2530 qreal h = QSvgUtils::parseLength(viewBoxValues.at(3).trimmed(), &type);
2531 return QRectF(x, y, w, h);
2533 return std::nullopt;
2537 QRectF *rect, QRectF *viewBox, QPointF *refPoint,
2538 QSvgSymbolLike::PreserveAspectRatios *aspect,
2539 QSvgSymbolLike::Overflow *overflow,
2540 bool marker =
false)
2542 const QStringView xStr = attributes.value(QLatin1String(
"x"));
2543 const QStringView yStr = attributes.value(QLatin1String(
"y"));
2544 const QStringView refXStr = attributes.value(QLatin1String(
"refX"));
2545 const QStringView refYStr = attributes.value(QLatin1String(
"refY"));
2546 const QStringView widthStr = attributes.value(marker ? QLatin1String(
"markerWidth")
2547 : QLatin1String(
"width"));
2548 const QStringView heightStr = attributes.value(marker ? QLatin1String(
"markerHeight")
2549 : QLatin1String(
"height"));
2550 const QStringView pAspectRStr = attributes.value(QLatin1String(
"preserveAspectRatio"));
2551 const QStringView overflowStr = attributes.value(QLatin1String(
"overflow"));
2552 const QStringView viewBoxStr = attributes.value(QLatin1String(
"viewBox"));
2556 if (!xStr.isEmpty()) {
2558 x = QSvgUtils::parseLength(xStr, &type);
2560 x = QSvgUtils::convertToPixels(x,
true, type);
2563 if (!yStr.isEmpty()) {
2565 y = QSvgUtils::parseLength(yStr, &type);
2567 y = QSvgUtils::convertToPixels(y,
false, type);
2570 if (!widthStr.isEmpty()) {
2572 width = QSvgUtils::parseLength(widthStr, &type);
2574 width = QSvgUtils::convertToPixels(width,
true, type);
2577 if (!heightStr.isEmpty()) {
2579 height = QSvgUtils::parseLength(heightStr, &type);
2581 height = QSvgUtils::convertToPixels(height,
false, type);
2584 *rect = QRectF(x, y, width, height);
2587 if (!refXStr.isEmpty()) {
2589 x = QSvgUtils::parseLength(refXStr, &type);
2591 x = QSvgUtils::convertToPixels(x,
true, type);
2594 if (!refYStr.isEmpty()) {
2596 y = QSvgUtils::parseLength(refYStr, &type);
2598 y = QSvgUtils::convertToPixels(y,
false, type);
2600 *refPoint = QPointF(x,y);
2602 auto viewBoxResult = parseViewBox(viewBoxStr);
2604 *viewBox = *viewBoxResult;
2605 else if (width > 0 && height > 0)
2606 *viewBox = QRectF(0, 0, width, height);
2608 *viewBox = handler->document()->viewBox();
2610 if (viewBox->isNull())
2613 auto pAspectRStrs = pAspectRStr.split(u" ");
2614 QSvgSymbolLike::PreserveAspectRatio aspectX = QSvgSymbolLike::PreserveAspectRatio::xMid;
2615 QSvgSymbolLike::PreserveAspectRatio aspectY = QSvgSymbolLike::PreserveAspectRatio::yMid;
2616 QSvgSymbolLike::PreserveAspectRatio aspectMS = QSvgSymbolLike::PreserveAspectRatio::meet;
2618 for (
auto &pAStr : std::as_const(pAspectRStrs)) {
2619 if (pAStr.startsWith(QLatin1String(
"none"))) {
2620 aspectX = QSvgSymbolLike::PreserveAspectRatio::None;
2621 aspectY = QSvgSymbolLike::PreserveAspectRatio::None;
2623 if (pAStr.startsWith(QLatin1String(
"xMin")))
2624 aspectX = QSvgSymbolLike::PreserveAspectRatio::xMin;
2625 else if (pAStr.startsWith(QLatin1String(
"xMax")))
2626 aspectX = QSvgSymbolLike::PreserveAspectRatio::xMax;
2627 if (pAStr.endsWith(QLatin1String(
"YMin")))
2628 aspectY = QSvgSymbolLike::PreserveAspectRatio::yMin;
2629 else if (pAStr.endsWith(QLatin1String(
"YMax")))
2630 aspectY = QSvgSymbolLike::PreserveAspectRatio::yMax;
2633 if (pAStr.endsWith(QLatin1String(
"slice")))
2634 aspectMS = QSvgSymbolLike::PreserveAspectRatio::slice;
2636 *aspect = aspectX | aspectY | aspectMS;
2643 *overflow = QSvgSymbolLike::Overflow::Hidden;
2645 if (overflowStr.endsWith(QLatin1String(
"auto")))
2646 *overflow = QSvgSymbolLike::Overflow::Auto;
2647 else if (overflowStr.endsWith(QLatin1String(
"visible")))
2648 *overflow = QSvgSymbolLike::Overflow::Visible;
2649 else if (overflowStr.endsWith(QLatin1String(
"hidden")))
2650 *overflow = QSvgSymbolLike::Overflow::Hidden;
2651 else if (overflowStr.endsWith(QLatin1String(
"scroll")))
2652 *overflow = QSvgSymbolLike::Overflow::Scroll;
2658 const QXmlStreamAttributes &attributes,
2659 QSvgHandler *handler)
2661 QRectF rect, viewBox;
2663 QSvgSymbolLike::PreserveAspectRatios aspect;
2664 QSvgSymbolLike::Overflow overflow;
2666 if (!parseSymbolLikeAttributes(attributes, handler, &rect, &viewBox, &refP, &aspect, &overflow))
2669 refP = QPointF(0, 0);
2670 QSvgNode *symbol =
new QSvgSymbol(parent, rect, viewBox, refP, aspect, overflow);
2675 const QXmlStreamAttributes &attributes,
2676 QSvgHandler *handler)
2678 QRectF rect, viewBox;
2680 QSvgSymbolLike::PreserveAspectRatios aspect;
2681 QSvgSymbolLike::Overflow overflow;
2683 const QStringView orientStr = attributes.value(QLatin1String(
"orient"));
2684 const QStringView markerUnitsStr = attributes.value(QLatin1String(
"markerUnits"));
2686 qreal orientationAngle = 0;
2687 QSvgMarker::Orientation orientation;
2688 if (orientStr.startsWith(QLatin1String(
"auto-start-reverse")))
2689 orientation = QSvgMarker::Orientation::AutoStartReverse;
2690 else if (orientStr.startsWith(QLatin1String(
"auto")))
2691 orientation = QSvgMarker::Orientation::Auto;
2693 orientation = QSvgMarker::Orientation::Value;
2696 if (orientStr.endsWith(QLatin1String(
"turn")))
2697 a = 360. * QSvgUtils::toDouble(orientStr.mid(0, orientStr.length()-4), &ok);
2698 else if (orientStr.endsWith(QLatin1String(
"grad")))
2699 a = QSvgUtils::toDouble(orientStr.mid(0, orientStr.length()-4), &ok);
2700 else if (orientStr.endsWith(QLatin1String(
"rad")))
2701 a = 180. /
M_PI * QSvgUtils::toDouble(orientStr.mid(0, orientStr.length()-3), &ok);
2703 a = QSvgUtils::toDouble(orientStr, &ok);
2705 orientationAngle = a;
2708 QSvgMarker::MarkerUnits markerUnits = QSvgMarker::MarkerUnits::StrokeWidth;
2709 if (markerUnitsStr.startsWith(QLatin1String(
"userSpaceOnUse")))
2710 markerUnits = QSvgMarker::MarkerUnits::UserSpaceOnUse;
2712 if (!parseSymbolLikeAttributes(attributes, handler, &rect, &viewBox, &refP, &aspect, &overflow,
true))
2715 QSvgNode *marker =
new QSvgMarker(parent, rect, viewBox, refP, aspect, overflow,
2716 orientation, orientationAngle, markerUnits);
2721 const QXmlStreamAttributes &attributes,
2722 QSvgHandler *handler)
2724 QStringView data = attributes.value(QLatin1String(
"d"));
2726 std::optional<QPainterPath> qpath = QSvgUtils::parsePathDataFast(data,
2727 !handler->trustedSourceMode());
2729 qCWarning(lcSvgHandler,
"Invalid path data; path truncated.");
2733 qpath.value().setFillRule(Qt::WindingFill);
2734 QSvgNode *path =
new QSvgPath(parent, qpath.value());
2739 const QXmlStreamAttributes &attributes,
2742 const QString pointsStr = attributes.value(QLatin1String(
"points")).toString();
2743 const QChar *s = pointsStr.constData();
2744 const QList<qreal> points = parseNumbersList(s);
2745 if (points.size() < 4)
2748 for (
int i = 0; i < poly.size(); ++i)
2749 poly[i] = QPointF(points.at(2 * i), points.at(2 * i + 1));
2751 return new QSvgPolyline(parent, poly);
2753 return new QSvgPolygon(parent, poly);
2757 const QXmlStreamAttributes &attributes,
2760 return createPolyNode(parent, attributes,
false);
2764 const QXmlStreamAttributes &attributes,
2767 return createPolyNode(parent, attributes,
true);
2771 const QXmlStreamAttributes &attributes,
2774 Q_UNUSED(parent); Q_UNUSED(attributes);
2779 const QXmlStreamAttributes &attributes,
2780 QSvgHandler *handler)
2782 const QStringView cx = attributes.value(QLatin1String(
"cx"));
2783 const QStringView cy = attributes.value(QLatin1String(
"cy"));
2784 const QStringView r = attributes.value(QLatin1String(
"r"));
2785 const QStringView fx = attributes.value(QLatin1String(
"fx"));
2786 const QStringView fy = attributes.value(QLatin1String(
"fy"));
2791 ncx = convertToNumber(cx);
2793 ncy = convertToNumber(cy);
2797 nr = convertToNumber(r);
2803 nfx = convertToNumber(fx);
2806 nfy = convertToNumber(fy);
2808 QRadialGradient *grad =
new QRadialGradient(ncx, ncy, nr, nfx, nfy, 0);
2809 grad->setInterpolationMode(QGradient::ComponentInterpolation);
2811 QSvgGradientStyle *prop =
new QSvgGradientStyle(grad);
2812 parseBaseGradient(node, attributes, prop, handler);
2818 const QXmlStreamAttributes &attributes,
2821 const QStringView x = attributes.value(QLatin1String(
"x"));
2822 const QStringView y = attributes.value(QLatin1String(
"y"));
2823 const QStringView width = attributes.value(QLatin1String(
"width"));
2824 const QStringView height = attributes.value(QLatin1String(
"height"));
2825 const QStringView rx = attributes.value(QLatin1String(
"rx"));
2826 const QStringView ry = attributes.value(QLatin1String(
"ry"));
2830 qreal nwidth = QSvgUtils::parseLength(width, &type, &ok);
2833 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
2834 qreal nheight = QSvgUtils::parseLength(height, &type, &ok);
2837 nheight = QSvgUtils::convertToPixels(nheight,
true, type);
2838 qreal nrx = QSvgUtils::toDouble(rx);
2839 qreal nry = QSvgUtils::toDouble(ry);
2842 if (bounds.isEmpty())
2845 if (!rx.isEmpty() && ry.isEmpty())
2847 else if (!ry.isEmpty() && rx.isEmpty())
2853 if (nrx > bounds.width()/2)
2854 nrx = bounds.width()/2;
2855 if (nry > bounds.height()/2)
2856 nry = bounds.height()/2;
2861 nrx *= (100/(bounds.width()/2));
2862 nry *= (100/(bounds.height()/2));
2864 QSvgNode *rect =
new QSvgRect(parent, bounds, nrx, nry);
2869 const QXmlStreamAttributes &attributes,
2872 Q_UNUSED(parent); Q_UNUSED(attributes);
2877 const QXmlStreamAttributes &attributes,
2880 Q_UNUSED(parent); Q_UNUSED(attributes);
2885 const QXmlStreamAttributes &attributes,
2886 QSvgHandler *handler)
2888 Q_UNUSED(parent); Q_UNUSED(attributes);
2889 QStringView solidColorStr = attributes.value(QLatin1String(
"solid-color"));
2890 QStringView solidOpacityStr = attributes.value(QLatin1String(
"solid-opacity"));
2892 if (solidOpacityStr.isEmpty())
2893 solidOpacityStr = attributes.value(QLatin1String(
"opacity"));
2896 if (!constructColor(solidColorStr, solidOpacityStr, color, handler))
2898 QSvgSolidColorStyle *style =
new QSvgSolidColorStyle(color);
2903 const QXmlStreamAttributes &attributes,
2904 QSvgHandler *handler)
2906 if (parent->type() != QSvgStyleProperty::GRADIENT)
2908 QString nodeIdStr = someId(attributes);
2909 QString xmlClassStr = attributes.value(QLatin1String(
"class")).toString();
2915 QSvgDummyNode dummy;
2916 dummy.setNodeId(nodeIdStr);
2917 dummy.setXmlClass(xmlClassStr);
2919 QSvgAttributes attrs(attributes, handler);
2921#ifndef QT_NO_CSSPARSER
2922 QXmlStreamAttributes cssAttributes;
2923 handler->cssHandler().styleLookup(&dummy, cssAttributes);
2924 attrs.setAttributes(cssAttributes, handler);
2926 QXmlStreamAttributes styleCssAttributes;
2927 QStringView style = attributes.value(QLatin1String(
"style"));
2928 if (!style.isEmpty())
2929 handler->cssHandler().parseCSStoXMLAttrs(style.toString(), styleCssAttributes);
2930 attrs.setAttributes(styleCssAttributes, handler);
2934 parseColor(&dummy, attrs, handler);
2936 QSvgGradientStyle *gradientStyle =
2937 static_cast<QSvgGradientStyle*>(parent);
2938 QStringView colorStr = attrs.stopColor;
2942 qreal offset = convertToNumber(attrs.offset, &ok);
2946 if (!constructColor(colorStr, attrs.stopOpacity, color, handler))
2949 QGradient *grad = gradientStyle->qgradient();
2951 offset = qMin(qreal(1), qMax(qreal(0), offset));
2952 QGradientStops stops;
2953 if (gradientStyle->gradientStopsSet()) {
2954 stops = grad->stops();
2956 if (offset <= stops.back().first)
2957 offset = stops.back().first + FLT_EPSILON;
2962 if ((stops.size() == 1) || (stops.at(stops.size() - 2).first < 1.0 - FLT_EPSILON)) {
2963 stops.back().first = 1.0 - FLT_EPSILON;
2964 grad->setStops(stops);
2969 grad->setColorAt(offset, color);
2970 gradientStyle->setGradientStopsSet(
true);
2975 const QXmlStreamAttributes &attributes,
2976 QSvgHandler *handler)
2979#ifdef QT_NO_CSSPARSER
2980 Q_UNUSED(attributes);
2983 const QStringView type = attributes.value(QLatin1String(
"type"));
2984 if (type.compare(QLatin1String(
"text/css"), Qt::CaseInsensitive) == 0 || type.isNull())
2985 handler->setInStyle(
true);
2992 const QXmlStreamAttributes &attributes,
2993 QSvgHandler *handler)
2995 Q_UNUSED(parent); Q_UNUSED(attributes);
2997 QSvgTinyDocument *node =
new QSvgTinyDocument(handler->options(), handler->animatorType());
2998 const QStringView widthStr = attributes.value(QLatin1String(
"width"));
2999 const QStringView heightStr = attributes.value(QLatin1String(
"height"));
3000 const QStringView viewBoxStr = attributes.value(QLatin1String(
"viewBox"));
3004 if (!widthStr.isEmpty()) {
3005 width = QSvgUtils::parseLength(widthStr, &type);
3007 width = QSvgUtils::convertToPixels(width,
true, type);
3011 if (!heightStr.isEmpty()) {
3012 height = QSvgUtils::parseLength(heightStr, &type);
3014 height = QSvgUtils::convertToPixels(height,
false, type);
3018 auto viewBoxResult = parseViewBox(viewBoxStr);
3019 if (viewBoxResult) {
3020 node->setViewBox(*viewBoxResult);
3021 }
else if (width && height) {
3023 width = QSvgUtils::convertToPixels(width,
false, type);
3024 height = QSvgUtils::convertToPixels(height,
false, type);
3026 node->setViewBox(QRectF(0, 0, width, height));
3034 const QXmlStreamAttributes &attributes,
3037 Q_UNUSED(attributes);
3038 QSvgSwitch *node =
new QSvgSwitch(parent);
3043 const QXmlStreamAttributes &attributes,
3044 QSvgHandler *handler)
3046 const QStringView x = attributes.value(QLatin1String(
"x"));
3047 const QStringView y = attributes.value(QLatin1String(
"y"));
3048 const QStringView width = attributes.value(QLatin1String(
"width"));
3049 const QStringView height = attributes.value(QLatin1String(
"height"));
3050 const QStringView patternUnits = attributes.value(QLatin1String(
"patternUnits"));
3051 const QStringView patternContentUnits = attributes.value(QLatin1String(
"patternContentUnits"));
3052 const QStringView patternTransform = attributes.value(QLatin1String(
"patternTransform"));
3054 QtSvg::
UnitTypes nPatternUnits = patternUnits.contains(QLatin1String(
"userSpaceOnUse")) ?
3057 QtSvg::
UnitTypes nPatternContentUnits = patternContentUnits.contains(QLatin1String(
"objectBoundingBox")) ?
3060 const QStringView viewBoxStr = attributes.value(QLatin1String(
"viewBox"));
3065 qreal nx = QSvgUtils::parseLength(x, &type, &ok);
3066 nx = QSvgUtils::convertToPixels(nx,
true, type);
3070 nx = (nx / 100.) * handler->document()->viewBox().width();
3074 qreal ny = QSvgUtils::parseLength(y, &type, &ok);
3075 ny = QSvgUtils::convertToPixels(ny,
true, type);
3079 ny = (ny / 100.) * handler->document()->viewBox().height();
3083 qreal nwidth = QSvgUtils::parseLength(width, &type, &ok);
3084 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
3088 nwidth = (nwidth / 100.) * handler->document()->viewBox().width();
3090 nwidth = nwidth / 100.;
3092 qreal nheight = QSvgUtils::parseLength(height, &type, &ok);
3093 nheight = QSvgUtils::convertToPixels(nheight,
true, type);
3097 nheight = (nheight / 100.) * handler->document()->viewBox().height();
3099 nheight = nheight / 100.;
3102 auto viewBoxResult = parseViewBox(viewBoxStr);
3103 if (viewBoxResult) {
3104 if (viewBoxResult->width() > 0 && viewBoxResult->height() > 0)
3105 viewBox = *viewBoxResult;
3109 if (!patternTransform.isEmpty())
3110 matrix = parseTransformationMatrix(patternTransform);
3112 QRectF bounds(nx, ny, nwidth, nheight);
3113 if (bounds.isEmpty())
3116 QSvgRectF patternRectF(bounds, nPatternUnits, nPatternUnits, nPatternUnits, nPatternUnits);
3117 QSvgPattern *node =
new QSvgPattern(parent, patternRectF, viewBox, nPatternContentUnits, matrix);
3120 QSvgPatternStyle *prop =
new QSvgPatternStyle(node);
3121 node->appendStyleProperty(prop, someId(attributes));
3127 const QXmlStreamAttributes &,
3130 if (parent->type() != QSvgNode::Textarea)
3132 static_cast<QSvgText*>(parent)->addLineBreak();
3137 const QXmlStreamAttributes &attributes,
3140 const QStringView x = attributes.value(QLatin1String(
"x"));
3141 const QStringView y = attributes.value(QLatin1String(
"y"));
3144 qreal nx = QSvgUtils::parseLength(x, &type);
3145 nx = QSvgUtils::convertToPixels(nx,
true, type);
3146 qreal ny = QSvgUtils::parseLength(y, &type);
3147 ny = QSvgUtils::convertToPixels(ny,
true, type);
3149 QSvgNode *text =
new QSvgText(parent, QPointF(nx, ny));
3154 const QXmlStreamAttributes &attributes,
3155 QSvgHandler *handler)
3157 QSvgText *node =
static_cast<QSvgText *>(createTextNode(parent, attributes, handler));
3160 qreal width = QSvgUtils::parseLength(attributes.value(QLatin1String(
"width")), &type);
3161 qreal height = QSvgUtils::parseLength(attributes.value(QLatin1String(
"height")), &type);
3162 node->setTextArea(QSizeF(width, height));
3168 const QXmlStreamAttributes &,
3171 return new QSvgTspan(parent);
3175 const QXmlStreamAttributes &attributes,
3178 QStringView linkId = attributes.value(QLatin1String(
"xlink:href"));
3179 const QStringView xStr = attributes.value(QLatin1String(
"x"));
3180 const QStringView yStr = attributes.value(QLatin1String(
"y"));
3181 QSvgStructureNode *group =
nullptr;
3183 if (linkId.isEmpty())
3184 linkId = attributes.value(QLatin1String(
"href"));
3185 QString linkIdStr = linkId.mid(1).toString();
3187 switch (parent->type()) {
3189 case QSvgNode::Defs:
3190 case QSvgNode::Group:
3191 case QSvgNode::Switch:
3192 case QSvgNode::Mask:
3193 group =
static_cast<QSvgStructureNode*>(parent);
3201 if (!xStr.isNull() || !yStr.isNull()) {
3203 qreal nx = QSvgUtils::parseLength(xStr, &type);
3204 nx = QSvgUtils::convertToPixels(nx,
true, type);
3206 qreal ny = QSvgUtils::parseLength(yStr, &type);
3207 ny = QSvgUtils::convertToPixels(ny,
true, type);
3208 pt = QPointF(nx, ny);
3211 QSvgNode *link = group->scopeNode(linkIdStr);
3213 if (parent->isDescendantOf(link))
3214 qCWarning(lcSvgHandler,
"link %ls is recursive!",
qUtf16Printable(linkIdStr));
3216 return new QSvgUse(pt, parent, link);
3220 return new QSvgUse(pt, parent, linkIdStr);
3223 qCWarning(lcSvgHandler,
"<use> element %ls in wrong context!",
qUtf16Printable(linkIdStr));
3228 const QXmlStreamAttributes &attributes,
3231 Q_UNUSED(parent); Q_UNUSED(attributes);
3235typedef QSvgNode *(*FactoryMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3242 QStringView ref = name.mid(1);
3243 switch (name.at(0).unicode()) {
3245 if (ref == QLatin1String(
"efs"))
return createDefsNode;
3251 if (ref.isEmpty())
return createGNode;
3258 if (ref == QLatin1String(
"vg"))
return createSvgNode;
3259 if (ref == QLatin1String(
"witch"))
return createSwitchNode;
3277 QStringView ref = name.mid(1);
3278 switch (name.at(0).unicode()) {
3280 if (ref == QLatin1String(
"ircle"))
return createCircleNode;
3283 if (ref == QLatin1String(
"llipse"))
return createEllipseNode;
3286 if (ref == QLatin1String(
"mage"))
return createImageNode;
3289 if (ref == QLatin1String(
"ine"))
return createLineNode;
3292 if (ref == QLatin1String(
"ath"))
return createPathNode;
3293 if (ref == QLatin1String(
"olygon"))
return createPolygonNode;
3294 if (ref == QLatin1String(
"olyline"))
return createPolylineNode;
3297 if (ref == QLatin1String(
"ect"))
return createRectNode;
3300 if (ref == QLatin1String(
"ext"))
return createTextNode;
3301 if (ref == QLatin1String(
"extArea"))
return createTextAreaNode;
3302 if (ref == QLatin1String(
"span"))
return createTspanNode;
3305 if (ref == QLatin1String(
"se"))
return createUseNode;
3308 if (ref == QLatin1String(
"ideo"))
return createVideoNode;
3324 if (!name.startsWith(QLatin1String(
"fe")))
3327 if (name == QLatin1String(
"feMerge"))
return createFeMergeNode;
3328 if (name == QLatin1String(
"feColorMatrix"))
return createFeColorMatrixNode;
3329 if (name == QLatin1String(
"feGaussianBlur"))
return createFeGaussianBlurNode;
3330 if (name == QLatin1String(
"feOffset"))
return createFeOffsetNode;
3331 if (name == QLatin1String(
"feMergeNode"))
return createFeMergeNodeNode;
3332 if (name == QLatin1String(
"feComposite"))
return createFeCompositeNode;
3333 if (name == QLatin1String(
"feFlood"))
return createFeFloodNode;
3334 if (name == QLatin1String(
"feBlend"))
return createFeBlendNode;
3336 static const QStringList unsupportedFilters = {
3353 if (unsupportedFilters.contains(name))
3354 return createFeUnsupportedNode;
3359typedef QSvgNode *(*AnimationMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3366 QStringView ref = name.mid(1);
3368 switch (name.at(0).unicode()) {
3370 if (ref == QLatin1String(
"nimate"))
return createAnimateNode;
3371 if (ref == QLatin1String(
"nimateColor"))
return createAnimateColorNode;
3372 if (ref == QLatin1String(
"nimateMotion"))
return createAimateMotionNode;
3373 if (ref == QLatin1String(
"nimateTransform"))
return createAnimateTransformNode;
3382typedef bool (*
ParseMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3389 QStringView ref = name.mid(1);
3390 switch (name.at(0).unicode()) {
3392 if (ref.isEmpty())
return parseAnchorNode;
3393 if (ref == QLatin1String(
"udio"))
return parseAudioNode;
3396 if (ref == QLatin1String(
"iscard"))
return parseDiscardNode;
3399 if (ref == QLatin1String(
"oreignObject"))
return parseForeignObjectNode;
3402 if (ref == QLatin1String(
"andler"))
return parseHandlerNode;
3403 if (ref == QLatin1String(
"kern"))
return parseHkernNode;
3406 if (ref == QLatin1String(
"etadata"))
return parseMetadataNode;
3407 if (ref == QLatin1String(
"path"))
return parseMpathNode;
3412 if (ref == QLatin1String(
"refetch"))
return parsePrefetchNode;
3415 if (ref == QLatin1String(
"cript"))
return parseScriptNode;
3416 if (ref == QLatin1String(
"et"))
return parseSetNode;
3417 if (ref == QLatin1String(
"tyle"))
return parseStyleNode;
3420 if (ref == QLatin1String(
"break"))
return parseTbreakNode;
3429 const QXmlStreamAttributes &,
3437 QStringView ref = name.mid(1);
3438 switch (name.at(0).unicode()) {
3440 if (ref == QLatin1String(
"ont"))
return createFontNode;
3443 if (ref == QLatin1String(
"inearGradient"))
return createLinearGradientNode;
3446 if (ref == QLatin1String(
"adialGradient"))
return createRadialGradientNode;
3449 if (ref == QLatin1String(
"olidColor"))
return createSolidColorNode;
3458 const QXmlStreamAttributes &,
3466 QStringView ref = name.mid(1);
3467 switch (name.at(0).unicode()) {
3489QSvgHandler::QSvgHandler(QIODevice *device, QtSvg::Options options,
3490 QtSvg::AnimatorType type)
3491 : xml(
new QXmlStreamReader(device))
3492 , m_ownsReader(
true)
3493 , m_options(options)
3494 , m_animatorType(type)
3499QSvgHandler::QSvgHandler(
const QByteArray &data, QtSvg::Options options,
3500 QtSvg::AnimatorType type)
3501 : xml(
new QXmlStreamReader(data))
3502 , m_ownsReader(
true)
3503 , m_options(options)
3504 , m_animatorType(type)
3509QSvgHandler::QSvgHandler(QXmlStreamReader *
const reader, QtSvg::Options options,
3510 QtSvg::AnimatorType type)
3512 , m_ownsReader(
false)
3513 , m_options(options)
3514 , m_animatorType(type)
3519void QSvgHandler::init()
3524 m_defaultCoords = QSvgUtils::LT_PX;
3525 m_defaultPen = QPen(Qt::black, 1, Qt::SolidLine, Qt::FlatCap, Qt::SvgMiterJoin);
3526 m_defaultPen.setMiterLimit(4);
3532 QSvgFillStyle *fillStyle =
static_cast<QSvgFillStyle*>
3533 (node->styleProperty(QSvgStyleProperty::FILL));
3534 if (fillStyle && fillStyle->style() && fillStyle->style()->type() == QSvgStyleProperty::PATTERN) {
3535 QSvgPatternStyle *patternStyle =
static_cast<QSvgPatternStyle *>(fillStyle->style());
3536 if (active.contains(patternStyle->patternNode()))
3540 QSvgStrokeStyle *strokeStyle =
static_cast<QSvgStrokeStyle*>
3541 (node->styleProperty(QSvgStyleProperty::STROKE));
3542 if (strokeStyle && strokeStyle->style() && strokeStyle->style()->type() == QSvgStyleProperty::PATTERN) {
3543 QSvgPatternStyle *patternStyle =
static_cast<QSvgPatternStyle *>(strokeStyle->style());
3544 if (active.contains(patternStyle->patternNode()))
3553 if (Q_UNLIKELY(!node))
3555 switch (node->type()) {
3557 case QSvgNode::Group:
3558 case QSvgNode::Defs:
3559 case QSvgNode::Pattern:
3561 if (node->type() == QSvgNode::Pattern)
3562 active.append(node);
3564 auto *g =
static_cast<
const QSvgStructureNode*>(node);
3565 for (
auto *r : g->renderers()) {
3566 if (detectCycles(r, active))
3573 if (active.contains(node))
3576 auto *u =
static_cast<
const QSvgUse*>(node);
3577 auto *target = u->link();
3580 if (detectCycles(target, active))
3585 case QSvgNode::Rect:
3586 case QSvgNode::Ellipse:
3587 case QSvgNode::Circle:
3588 case QSvgNode::Line:
3589 case QSvgNode::Path:
3590 case QSvgNode::Polygon:
3591 case QSvgNode::Polyline:
3592 case QSvgNode::Tspan:
3593 if (detectPatternCycles(node, active))
3603 const bool cycleFound = detectCycles(node);
3605 qCWarning(lcSvgHandler,
"Cycles detected in SVG, document discarded.");
3613void QSvgHandler::parse()
3615 xml->setNamespaceProcessing(
false);
3616#ifndef QT_NO_CSSPARSER
3620 int remainingUnfinishedElements = unfinishedElementsLimit;
3621 while (!xml->atEnd() && !done) {
3622 switch (xml->readNext()) {
3623 case QXmlStreamReader::StartElement:
3632 if (remainingUnfinishedElements && startElement(xml->name(), xml->attributes())) {
3633 --remainingUnfinishedElements;
3640 case QXmlStreamReader::EndElement:
3641 done = endElement(xml->name());
3642 ++remainingUnfinishedElements;
3644 case QXmlStreamReader::Characters:
3645 characters(xml->text());
3647 case QXmlStreamReader::ProcessingInstruction:
3648 processingInstruction(xml->processingInstructionTarget(), xml->processingInstructionData());
3654 resolvePaintServers(m_doc);
3656 if (detectCyclesAndWarn(m_doc)) {
3662bool QSvgHandler::startElement(
const QStringView localName,
3663 const QXmlStreamAttributes &attributes)
3665 QSvgNode *node =
nullptr;
3670
3671
3672 const QStringView xmlSpace(attributes.value(QLatin1String(
"xml:space")));
3673 if (xmlSpace.isNull()) {
3675 m_whitespaceMode.push(m_whitespaceMode.isEmpty() ? QSvgText::Default : m_whitespaceMode.top());
3676 }
else if (xmlSpace == QLatin1String(
"preserve")) {
3677 m_whitespaceMode.push(QSvgText::Preserve);
3678 }
else if (xmlSpace == QLatin1String(
"default")) {
3679 m_whitespaceMode.push(QSvgText::Default);
3681 const QByteArray msg =
'"' + xmlSpace.toLocal8Bit()
3682 +
"\" is an invalid value for attribute xml:space. "
3683 "Valid values are \"preserve\" and \"default\".";
3684 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3685 m_whitespaceMode.push(QSvgText::Default);
3688 if (!m_doc && localName != QLatin1String(
"svg"))
3691 if (m_doc && localName == QLatin1String(
"svg")) {
3692 m_skipNodes.push(Doc);
3693 qCWarning(lcSvgHandler) <<
"Skipping a nested svg element, because "
3694 "SVG Document must not contain nested svg elements in Svg Tiny 1.2";
3697 if (!m_skipNodes.isEmpty() && m_skipNodes.top() == Doc)
3700 if (FactoryMethod method = findGroupFactory(localName, options())) {
3703 node = method(
nullptr, attributes,
this);
3705 Q_ASSERT(node->type() == QSvgNode::Doc);
3706 m_doc =
static_cast<QSvgTinyDocument*>(node);
3709 switch (m_nodes.top()->type()) {
3711 case QSvgNode::Group:
3712 case QSvgNode::Defs:
3713 case QSvgNode::Switch:
3714 case QSvgNode::Mask:
3715 case QSvgNode::Symbol:
3716 case QSvgNode::Marker:
3717 case QSvgNode::Pattern:
3719 node = method(m_nodes.top(), attributes,
this);
3721 QSvgStructureNode *group =
3722 static_cast<QSvgStructureNode*>(m_nodes.top());
3723 group->addChild(node, someId(attributes));
3728 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
3729 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3735 parseCoreNode(node, attributes);
3736 parseStyle(node, attributes,
this);
3737 if (node->type() == QSvgNode::Filter)
3738 m_toBeResolved.append(node);
3740 }
else if (FactoryMethod method = findGraphicsFactory(localName, options())) {
3742 Q_ASSERT(!m_nodes.isEmpty());
3743 switch (m_nodes.top()->type()) {
3745 case QSvgNode::Group:
3746 case QSvgNode::Defs:
3747 case QSvgNode::Switch:
3748 case QSvgNode::Mask:
3749 case QSvgNode::Symbol:
3750 case QSvgNode::Marker:
3751 case QSvgNode::Pattern:
3753 if (localName == QLatin1String(
"tspan")) {
3754 const QByteArray msg = QByteArrayLiteral(
"\'tspan\' element in wrong context.");
3755 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3758 node = method(m_nodes.top(), attributes,
this);
3760 QSvgStructureNode *group =
3761 static_cast<QSvgStructureNode*>(m_nodes.top());
3762 group->addChild(node, someId(attributes));
3766 case QSvgNode::Text:
3767 case QSvgNode::Textarea:
3768 if (localName == QLatin1String(
"tspan")) {
3769 node = method(m_nodes.top(), attributes,
this);
3771 static_cast<QSvgText *>(m_nodes.top())->addTspan(
static_cast<QSvgTspan *>(node));
3774 const QByteArray msg = QByteArrayLiteral(
"\'text\' or \'textArea\' element contains invalid element type.");
3775 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3779 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
3780 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3785 parseCoreNode(node, attributes);
3786 parseStyle(node, attributes,
this);
3787 if (node->type() == QSvgNode::Text || node->type() == QSvgNode::Textarea) {
3788 static_cast<QSvgText *>(node)->setWhitespaceMode(m_whitespaceMode.top());
3789 }
else if (node->type() == QSvgNode::Tspan) {
3790 static_cast<QSvgTspan *>(node)->setWhitespaceMode(m_whitespaceMode.top());
3791 }
else if (node->type() == QSvgNode::Use) {
3792 auto useNode =
static_cast<QSvgUse *>(node);
3793 if (!useNode->isResolved())
3794 m_toBeResolved.append(useNode);
3797 }
else if (FactoryMethod method = findFilterFactory(localName, options())) {
3799 Q_ASSERT(!m_nodes.isEmpty());
3800 if (m_nodes.top()->type() == QSvgNode::Filter ||
3801 (m_nodes.top()->type() == QSvgNode::FeMerge && localName == QLatin1String(
"feMergeNode"))) {
3802 node = method(m_nodes.top(), attributes,
this);
3804 QSvgStructureNode *container =
3805 static_cast<QSvgStructureNode*>(m_nodes.top());
3806 container->addChild(node, someId(attributes));
3809 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
3810 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3812 }
else if (AnimationMethod method = findAnimationFactory(localName, options())) {
3813 Q_ASSERT(!m_nodes.isEmpty());
3814 node = method(m_nodes.top(), attributes,
this);
3816 QSvgAnimateNode *anim =
static_cast<QSvgAnimateNode *>(node);
3817 if (anim->linkId().isEmpty())
3818 m_doc->animator()->appendAnimation(m_nodes.top(), anim);
3819 else if (m_doc->namedNode(anim->linkId()))
3820 m_doc->animator()->appendAnimation(m_doc->namedNode(anim->linkId()), anim);
3822 m_toBeResolved.append(anim);
3824 }
else if (ParseMethod method = findUtilFactory(localName, options())) {
3825 Q_ASSERT(!m_nodes.isEmpty());
3826 if (!method(m_nodes.top(), attributes,
this))
3827 qCWarning(lcSvgHandler,
"%s", msgProblemParsing(localName, xml).constData());
3828 }
else if (StyleFactoryMethod method = findStyleFactoryMethod(localName)) {
3829 QSvgStyleProperty *prop = method(m_nodes.top(), attributes,
this);
3832 m_nodes.top()->appendStyleProperty(prop, someId(attributes));
3834 const QByteArray msg = QByteArrayLiteral(
"Could not parse node: ") + localName.toLocal8Bit();
3835 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3837 }
else if (StyleParseMethod method = findStyleUtilFactoryMethod(localName)) {
3839 if (!method(m_style, attributes,
this))
3840 qCWarning(lcSvgHandler,
"%s", msgProblemParsing(localName, xml).constData());
3843 qCDebug(lcSvgHandler) <<
"Skipping unknown element" << localName;
3844 m_skipNodes.push(Unknown);
3850 m_skipNodes.push(Graphics);
3853 m_skipNodes.push(Style);
3858bool QSvgHandler::endElement(
const QStringView localName)
3860 CurrentNode node = m_skipNodes.top();
3862 if (node == Doc && localName != QLatin1String(
"svg"))
3866 m_whitespaceMode.pop();
3870 if (node == Unknown)
3873#ifdef QT_NO_CSSPARSER
3874 Q_UNUSED(localName);
3876 if (m_inStyle && localName == QLatin1String(
"style"))
3880 if (node == Graphics)
3882 else if (m_style && !m_skipNodes.isEmpty() && m_skipNodes.top() != Style)
3885 return ((localName == QLatin1String(
"svg")) && (node != Doc));
3888void QSvgHandler::resolvePaintServers(QSvgNode *node,
int nestedDepth)
3890 if (!node || (node->type() != QSvgNode::Doc && node->type() != QSvgNode::Group
3891 && node->type() != QSvgNode::Defs && node->type() != QSvgNode::Switch)) {
3895 QSvgStructureNode *structureNode =
static_cast<QSvgStructureNode *>(node);
3897 const QList<QSvgNode *> ren = structureNode->renderers();
3898 for (
auto it = ren.begin(); it != ren.end(); ++it) {
3899 QSvgFillStyle *fill =
static_cast<QSvgFillStyle *>((*it)->styleProperty(QSvgStyleProperty::FILL));
3900 if (fill && !fill->isPaintStyleResolved()) {
3901 QString id = fill->paintStyleId();
3902 QSvgPaintStyleProperty *style = structureNode->styleProperty(id);
3904 fill->setFillStyle(style);
3906 qCWarning(lcSvgHandler,
"%s", msgCouldNotResolveProperty(id, xml).constData());
3907 fill->setBrush(Qt::NoBrush);
3911 QSvgStrokeStyle *stroke =
static_cast<QSvgStrokeStyle *>((*it)->styleProperty(QSvgStyleProperty::STROKE));
3912 if (stroke && !stroke->isPaintStyleResolved()) {
3913 QString id = stroke->paintStyleId();
3914 QSvgPaintStyleProperty *style = structureNode->styleProperty(id);
3916 stroke->setStyle(style);
3918 qCWarning(lcSvgHandler,
"%s", msgCouldNotResolveProperty(id, xml).constData());
3919 stroke->setStroke(Qt::NoBrush);
3923 if (nestedDepth < 2048)
3924 resolvePaintServers(*it, nestedDepth + 1);
3928void QSvgHandler::resolveNodes()
3930 for (QSvgNode *node : std::as_const(m_toBeResolved)) {
3931 if (node->type() == QSvgNode::Use) {
3932 QSvgUse *useNode =
static_cast<QSvgUse *>(node);
3933 const auto parent = useNode->parent();
3937 QSvgNode::Type t = parent->type();
3938 if (t != QSvgNode::Doc && t != QSvgNode::Defs && t != QSvgNode::Group && t != QSvgNode::Switch)
3941 QSvgStructureNode *group =
static_cast<QSvgStructureNode *>(parent);
3942 QSvgNode *link = group->scopeNode(useNode->linkId());
3944 qCWarning(lcSvgHandler,
"link #%s is undefined!",
qPrintable(useNode->linkId()));
3948 if (useNode->parent()->isDescendantOf(link))
3949 qCWarning(lcSvgHandler,
"link #%s is recursive!",
qPrintable(useNode->linkId()));
3951 useNode->setLink(link);
3952 }
else if (node->type() == QSvgNode::Filter) {
3953 QSvgFilterContainer *filter =
static_cast<QSvgFilterContainer *>(node);
3954 for (
const QSvgNode *renderer : filter->renderers()) {
3955 const QSvgFeFilterPrimitive *primitive = QSvgFeFilterPrimitive::castToFilterPrimitive(renderer);
3956 if (!primitive || primitive->type() == QSvgNode::FeUnsupported) {
3957 filter->setSupported(
false);
3961 }
else if (node->type() == QSvgNode::AnimateTransform || node->type() == QSvgNode::AnimateColor) {
3962 QSvgAnimateNode *anim =
static_cast<QSvgAnimateNode *>(node);
3963 QSvgNode *targetNode = m_doc->namedNode(anim->linkId());
3965 m_doc->animator()->appendAnimation(targetNode, anim);
3967 qCWarning(lcSvgHandler,
"Cannot find target for link #%s!",
3973 m_toBeResolved.clear();
3976bool QSvgHandler::characters(
const QStringView str)
3978#ifndef QT_NO_CSSPARSER
3980 m_cssHandler.parseStyleSheet(str);
3984 if (m_skipNodes.isEmpty() || m_skipNodes.top() == Unknown || m_nodes.isEmpty())
3987 if (m_nodes.top()->type() == QSvgNode::Text || m_nodes.top()->type() == QSvgNode::Textarea) {
3988 static_cast<QSvgText*>(m_nodes.top())->addText(str);
3989 }
else if (m_nodes.top()->type() == QSvgNode::Tspan) {
3990 static_cast<QSvgTspan*>(m_nodes.top())->addText(str);
3996QIODevice *QSvgHandler::device()
const
3998 return xml->device();
4001QSvgTinyDocument *QSvgHandler::document()
const
4006QSvgUtils::LengthType QSvgHandler::defaultCoordinateSystem()
const
4008 return m_defaultCoords;
4011void QSvgHandler::setDefaultCoordinateSystem(QSvgUtils::LengthType type)
4013 m_defaultCoords = type;
4016void QSvgHandler::pushColor(
const QColor &color)
4018 m_colorStack.push(color);
4019 m_colorTagCount.push(1);
4022void QSvgHandler::pushColorCopy()
4024 if (m_colorTagCount.size())
4025 ++m_colorTagCount.top();
4027 pushColor(Qt::black);
4030void QSvgHandler::popColor()
4032 if (m_colorTagCount.size()) {
4033 if (!--m_colorTagCount.top()) {
4035 m_colorTagCount.pop();
4040QColor QSvgHandler::currentColor()
const
4042 if (!m_colorStack.isEmpty())
4043 return m_colorStack.top();
4045 return QColor(0, 0, 0);
4048#ifndef QT_NO_CSSPARSER
4050void QSvgHandler::setInStyle(
bool b)
4055bool QSvgHandler::inStyle()
const
4060QSvgCssHandler &QSvgHandler::cssHandler()
4062 return m_cssHandler;
4067bool QSvgHandler::processingInstruction(
const QStringView target,
const QStringView data)
4069#ifdef QT_NO_CSSPARSER
4073 if (target == QLatin1String(
"xml-stylesheet")) {
4074 static const QRegularExpression rx(
QStringLiteral(
"type=\\\"(.+)\\\""),
4075 QRegularExpression::InvertedGreedinessOption);
4076 QRegularExpressionMatchIterator iter = rx.globalMatchView(data);
4078 while (iter.hasNext()) {
4079 QRegularExpressionMatch match = iter.next();
4080 QString type = match.captured(1);
4081 if (type.toLower() == QLatin1String(
"text/css")) {
4087 static const QRegularExpression rx(
QStringLiteral(
"href=\\\"(.+)\\\""),
4088 QRegularExpression::InvertedGreedinessOption);
4089 QRegularExpressionMatch match = rx.matchView(data);
4090 QString addr = match.captured(1);
4094 QFile file(fi.absoluteFilePath());
4095 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
4098 QByteArray cssData = file.readAll();
4099 QString css = QString::fromUtf8(cssData);
4100 m_cssHandler.parseStyleSheet(css);
4110void QSvgHandler::setAnimPeriod(
int start,
int end)
4113 m_animEnd = qMax(end, m_animEnd);
4116int QSvgHandler::animationDuration()
const
4121QSvgHandler::~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)
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