5#include "qplatformdefs.h"
23#include <QtCore/private/qdataurl_p.h>
31#include <qregularexpression.h>
42Q_LOGGING_CATEGORY(lcSvgHandler,
"qt.svg")
44static const char *qt_inherit_text =
"inherit";
45#define QT_INHERIT QLatin1String(qt_inherit_text)
51 if (
const QFile *file = qobject_cast<
const QFile *>(r->device()))
52 result.append(QFile::encodeName(QDir::toNativeSeparators(file->fileName())));
54 result.append(QByteArrayLiteral(
"<input>"));
56 result.append(QByteArray::number(r->lineNumber()));
57 if (
const qint64 column = r->columnNumber()) {
59 result.append(QByteArray::number(column));
61 result.append(QByteArrayLiteral(
": "));
69 return prefixMessage(
"Problem parsing " + localName.toLocal8Bit(), r);
74 return prefixMessage(
"Could not resolve property: " + id.toLocal8Bit(), r);
79 static const QRegularExpression delimiterRE(
QStringLiteral(
"[,\\s]+"));
80 return delimitedList.split(delimiterRE, Qt::SkipEmptyParts);
85static inline int qsvg_h2i(
char hex,
bool *ok =
nullptr)
87 if (hex >=
'0' && hex <=
'9')
89 if (hex >=
'a' && hex <=
'f')
90 return hex -
'a' + 10;
91 if (hex >=
'A' && hex <=
'F')
92 return hex -
'A' + 10;
114 const size_t len = qstrlen(name);
121 }
else if (len == 9) {
125 }
else if (len == 6) {
129 }
else if (len == 3) {
136 if ((uint)r > 255 || (uint)g > 255 || (uint)b > 255 || !ok) {
140 *rgb = qRgb(r, g ,b);
149 for(
int i = 0; i < len; ++i)
150 tmp[i] = str[i].toLatin1();
152 return qsvg_get_hex_rgb(tmp, rgb);
157static inline QString
someId(
const QXmlStreamAttributes &attributes)
159 QStringView id = attributes.value(QLatin1String(
"id"));
161 id = attributes.value(QLatin1String(
"xml:id"));
162 return id.toString();
168 void setAttributes(
const QXmlStreamAttributes &attributes, QSvgHandler *handler);
210 setAttributes(xmlAttributes, handler);
215 for (
const QXmlStreamAttribute &attribute : attributes) {
216 QStringView name = attribute.qualifiedName();
219 QStringView value = attribute.value();
221 switch (name.at(0).unicode()) {
224 if (name == QLatin1String(
"color"))
226 else if (name == QLatin1String(
"color-opacity"))
227 colorOpacity = value;
228 else if (name == QLatin1String(
"comp-op"))
233 if (name == QLatin1String(
"display"))
238 if (name == QLatin1String(
"fill"))
240 else if (name == QLatin1String(
"fill-rule"))
242 else if (name == QLatin1String(
"fill-opacity"))
244 else if (name == QLatin1String(
"font-family"))
246 else if (name == QLatin1String(
"font-size"))
248 else if (name == QLatin1String(
"font-style"))
250 else if (name == QLatin1String(
"font-weight"))
252 else if (name == QLatin1String(
"font-variant"))
254 else if (name == QLatin1String(
"filter") &&
255 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
260 if (name == QLatin1String(
"id"))
261 id = value.toString();
262 else if (name == QLatin1String(
"image-rendering"))
263 imageRendering = value;
267 if (name == QLatin1String(
"mask") &&
268 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
270 if (name == QLatin1String(
"marker-start") &&
271 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
273 if (name == QLatin1String(
"marker-mid") &&
274 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
276 if (name == QLatin1String(
"marker-end") &&
277 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
282 if (name == QLatin1String(
"opacity"))
284 if (name == QLatin1String(
"offset"))
289 if (name.size() > 5 && name.mid(1, 5) == QLatin1String(
"troke")) {
290 QStringView strokeRef = name.mid(6, name.size() - 6);
291 if (strokeRef.isEmpty())
293 else if (strokeRef == QLatin1String(
"-dasharray"))
294 strokeDashArray = value;
295 else if (strokeRef == QLatin1String(
"-dashoffset"))
296 strokeDashOffset = value;
297 else if (strokeRef == QLatin1String(
"-linecap"))
298 strokeLineCap = value;
299 else if (strokeRef == QLatin1String(
"-linejoin"))
300 strokeLineJoin = value;
301 else if (strokeRef == QLatin1String(
"-miterlimit"))
302 strokeMiterLimit = value;
303 else if (strokeRef == QLatin1String(
"-opacity"))
304 strokeOpacity = value;
305 else if (strokeRef == QLatin1String(
"-width"))
307 }
else if (name == QLatin1String(
"stop-color"))
309 else if (name == QLatin1String(
"stop-opacity"))
314 if (name == QLatin1String(
"text-anchor"))
316 else if (name == QLatin1String(
"transform"))
321 if (name == QLatin1String(
"vector-effect"))
322 vectorEffect = value;
323 else if (name == QLatin1String(
"visibility"))
328 if (name == QLatin1String(
"xml:id") && id.isEmpty())
329 id = value.toString();
345 while (str->isSpace())
348 *str == QLatin1Char(
'-') || *str == QLatin1Char(
'+') ||
349 *str == QLatin1Char(
'.')) {
351 points.append(QSvgUtils::toDouble(str));
353 while (str->isSpace())
355 if (*str == QLatin1Char(
','))
359 while (str->isSpace())
372 while (str->isSpace())
374 while ((*str >= QLatin1Char(
'0') && *str <= QLatin1Char(
'9')) ||
375 *str == QLatin1Char(
'-') || *str == QLatin1Char(
'+') ||
376 *str == QLatin1Char(
'.')) {
378 points.append(QSvgUtils::toDouble(str));
380 while (str->isSpace())
382 if (*str == QLatin1Char(
'%'))
384 while (str->isSpace())
386 if (*str == QLatin1Char(
','))
390 while (str->isSpace())
402 if (url.startsWith(QLatin1Char(
'(')))
405 return QStringView();
407 if (!url.startsWith(QLatin1Char(
'#')))
408 return QStringView();
409 const qsizetype closingBracePos = url.indexOf(QLatin1Char(
')'));
410 if (closingBracePos == -1)
411 return QStringView();
412 return url.first(closingBracePos).trimmed();
416
417
418
419static bool resolveColor(QStringView colorStr, QColor &color, QSvgHandler *handler)
421 QStringView colorStrTr = colorStr.trimmed();
422 if (colorStrTr.isEmpty())
425 switch(colorStrTr.at(0).unicode()) {
432 bool ok = qsvg_get_hex_rgb(colorStrTr.constData(), colorStrTr.size(), &rgb);
442 if (colorStrTr.size() >= 7 && colorStrTr.at(colorStrTr.size() - 1) == QLatin1Char(
')')
443 && colorStrTr.mid(0, 4) == QLatin1String(
"rgb(")) {
444 const QChar *s = colorStrTr.constData() + 4;
445 QList<qreal> compo = parseNumbersList(s);
448 if (compo.size() == 1) {
449 s = colorStrTr.constData() + 4;
450 compo = parsePercentageList(s);
451 for (
int i = 0; i < compo.size(); ++i)
452 compo[i] *= (qreal)2.55;
455 if (compo.size() == 3) {
456 color = QColor(
int(compo[0]),
467 if (colorStrTr == QLatin1String(
"currentColor")) {
468 color = handler->currentColor();
480 color = QColor::fromString(colorStrTr);
481 return color.isValid();
487 qreal op = qBound(qreal(0.0), QSvgUtils::toDouble(opacity, &ok), qreal(1.0));
490 color->setAlphaF(op);
494 QColor &color, QSvgHandler *handler)
496 if (!resolveColor(colorStr, color, handler))
498 if (!opacity.isEmpty())
499 setAlpha(opacity, &color);
506 qreal num = QSvgUtils::parseLength(str.toString(), &type, ok);
513static bool createSvgGlyph(QSvgFont *font,
const QXmlStreamAttributes &attributes,
516 QStringView uncStr = attributes.value(QLatin1String(
"unicode"));
517 QStringView havStr = attributes.value(QLatin1String(
"horiz-adv-x"));
518 QStringView pathStr = attributes.value(QLatin1String(
"d"));
520 qreal havx = (havStr.isEmpty()) ? -1 : QSvgUtils::toDouble(havStr);
521 QPainterPath path = QSvgUtils::parsePathDataFast(pathStr).value_or(QPainterPath());
523 path.setFillRule(Qt::WindingFill);
525 if (isMissingGlyph) {
526 if (!uncStr.isEmpty())
527 qWarning(
"Ignoring missing-glyph's 'unicode' attribute");
528 return font->addMissingGlyph(path, havx);
531 if (uncStr.isEmpty()) {
532 qWarning(
"glyph does not define a non-empty 'unicode' attribute and will be ignored");
535 font->addGlyph(uncStr.toString(), path, havx);
541 QSvgHandler *handler)
544 if (constructColor(attributes.color, attributes.colorOpacity, color, handler)) {
546 handler->pushColor(color);
552 return node ? node->styleProperty(idFromUrl(url)) : 0;
557 QSvgHandler *handler)
559 if (!attributes.fill.isEmpty() || !attributes.fillRule.isEmpty() || !attributes.fillOpacity.isEmpty()) {
560 QSvgFillStyle *prop =
new QSvgFillStyle;
563 if (!attributes.fillRule.isEmpty() && attributes.fillRule !=
QT_INHERIT) {
564 if (attributes.fillRule == QLatin1String(
"evenodd"))
565 prop->setFillRule(Qt::OddEvenFill);
566 else if (attributes.fillRule == QLatin1String(
"nonzero"))
567 prop->setFillRule(Qt::WindingFill);
571 if (!attributes.fillOpacity.isEmpty() && attributes.fillOpacity !=
QT_INHERIT) {
572 prop->setFillOpacity(qMin(qreal(1.0), qMax(qreal(0.0), QSvgUtils::toDouble(attributes.fillOpacity))));
576 if ((!attributes.fill.isEmpty()) && (attributes.fill !=
QT_INHERIT) ) {
577 if (attributes.fill.startsWith(QLatin1String(
"url"))) {
578 QStringView value = attributes.fill.sliced(3);
579 QSvgStyleProperty *style = styleFromUrl(node, value);
581 if (style->type() == QSvgStyleProperty::SOLID_COLOR || style->type() == QSvgStyleProperty::GRADIENT
582 || style->type() == QSvgStyleProperty::PATTERN)
583 prop->setFillStyle(
reinterpret_cast<QSvgPaintStyleProperty *>(style));
585 QString id = idFromUrl(value).toString();
586 prop->setPaintStyleId(id);
587 prop->setPaintStyleResolved(
false);
589 }
else if (attributes.fill != QLatin1String(
"none")) {
591 if (resolveColor(attributes.fill, color, handler))
592 prop->setBrush(QBrush(color));
594 prop->setBrush(QBrush(Qt::NoBrush));
597 node->appendStyleProperty(prop, attributes.id);
609 const QChar *str = value.constData();
610 const QChar *end = str + value.size();
613 if (str->isSpace() || *str == QLatin1Char(
',')) {
625 State state = Matrix;
626 if (*str == QLatin1Char(
'm')) {
627 const char *ident =
"atrix";
628 for (
int i = 0; i < 5; ++i)
629 if (*(++str) != QLatin1Char(ident[i]))
633 }
else if (*str == QLatin1Char(
't')) {
634 const char *ident =
"ranslate";
635 for (
int i = 0; i < 8; ++i)
636 if (*(++str) != QLatin1Char(ident[i]))
640 }
else if (*str == QLatin1Char(
'r')) {
641 const char *ident =
"otate";
642 for (
int i = 0; i < 5; ++i)
643 if (*(++str) != QLatin1Char(ident[i]))
647 }
else if (*str == QLatin1Char(
's')) {
649 if (*str == QLatin1Char(
'c')) {
650 const char *ident =
"ale";
651 for (
int i = 0; i < 3; ++i)
652 if (*(++str) != QLatin1Char(ident[i]))
656 }
else if (*str == QLatin1Char(
'k')) {
657 if (*(++str) != QLatin1Char(
'e'))
659 if (*(++str) != QLatin1Char(
'w'))
662 if (*str == QLatin1Char(
'X'))
664 else if (*str == QLatin1Char(
'Y'))
677 while (str < end && str->isSpace())
679 if (*str != QLatin1Char(
'('))
682 QVarLengthArray<qreal, 8> points;
684 if (*str != QLatin1Char(
')'))
688 if(state == Matrix) {
689 if(points.size() != 6)
691 matrix = QTransform(points[0], points[1],
692 points[2], points[3],
693 points[4], points[5]) * matrix;
694 }
else if (state == Translate) {
695 if (points.size() == 1)
696 matrix.translate(points[0], 0);
697 else if (points.size() == 2)
698 matrix.translate(points[0], points[1]);
701 }
else if (state == Rotate) {
702 if(points.size() == 1) {
703 matrix.rotate(points[0]);
704 }
else if (points.size() == 3) {
705 matrix.translate(points[1], points[2]);
706 matrix.rotate(points[0]);
707 matrix.translate(-points[1], -points[2]);
711 }
else if (state == Scale) {
712 if (points.size() < 1 || points.size() > 2)
714 qreal sx = points[0];
716 if(points.size() == 2)
718 matrix.scale(sx, sy);
719 }
else if (state == SkewX) {
720 if (points.size() != 1)
722 matrix.shear(qTan(qDegreesToRadians(points[0])), 0);
723 }
else if (state == SkewY) {
724 if (points.size() != 1)
726 matrix.shear(0, qTan(qDegreesToRadians(points[0])));
735 QSvgHandler *handler)
737 if (!attributes.stroke.isEmpty() || !attributes.strokeDashArray.isEmpty() || !attributes.strokeDashOffset.isEmpty() || !attributes.strokeLineCap.isEmpty()
738 || !attributes.strokeLineJoin.isEmpty() || !attributes.strokeMiterLimit.isEmpty() || !attributes.strokeOpacity.isEmpty() || !attributes.strokeWidth.isEmpty()
739 || !attributes.vectorEffect.isEmpty()) {
741 QSvgStrokeStyle *prop =
new QSvgStrokeStyle;
744 if ((!attributes.stroke.isEmpty()) && (attributes.stroke !=
QT_INHERIT) ) {
745 if (attributes.stroke.startsWith(QLatin1String(
"url"))) {
746 QStringView value = attributes.stroke.sliced(3);
747 QSvgStyleProperty *style = styleFromUrl(node, value);
749 if (style->type() == QSvgStyleProperty::SOLID_COLOR || style->type() == QSvgStyleProperty::GRADIENT
750 || style->type() == QSvgStyleProperty::PATTERN)
751 prop->setStyle(
reinterpret_cast<QSvgPaintStyleProperty *>(style));
753 QString id = idFromUrl(value).toString();
754 prop->setPaintStyleId(id);
755 prop->setPaintStyleResolved(
false);
757 }
else if (attributes.stroke != QLatin1String(
"none")) {
759 if (resolveColor(attributes.stroke, color, handler))
760 prop->setStroke(QBrush(color));
762 prop->setStroke(QBrush(Qt::NoBrush));
767 if (!attributes.strokeWidth.isEmpty() && attributes.strokeWidth !=
QT_INHERIT) {
769 prop->setWidth(QSvgUtils::parseLength(attributes.strokeWidth, <));
773 if (!attributes.strokeDashArray.isEmpty() && attributes.strokeDashArray !=
QT_INHERIT) {
774 if (attributes.strokeDashArray == QLatin1String(
"none")) {
775 prop->setDashArrayNone();
777 QString dashArray = attributes.strokeDashArray.toString();
778 const QChar *s = dashArray.constData();
779 QList<qreal> dashes = parseNumbersList(s);
780 const bool allZeroes =
std::all_of(dashes.cbegin(), dashes.cend(),
781 [](qreal i) {
return qFuzzyIsNull(i); });
782 const bool hasNegative = !allZeroes &&
std::any_of(dashes.cbegin(), dashes.cend(),
783 [](qreal i) {
return i < 0.; });
786 qCWarning(lcSvgHandler) <<
"QSvgHandler: Stroke dash array "
787 "with a negative value is invalid";
790 if (allZeroes || hasNegative) {
791 prop->setDashArrayNone();
794 if ((dashes.size() & 1) != 0)
795 dashes << QList<qreal>(dashes);
796 prop->setDashArray(dashes);
802 if (!attributes.strokeLineJoin.isEmpty()) {
803 if (attributes.strokeLineJoin == QLatin1String(
"miter"))
804 prop->setLineJoin(Qt::SvgMiterJoin);
805 else if (attributes.strokeLineJoin == QLatin1String(
"round"))
806 prop->setLineJoin(Qt::RoundJoin);
807 else if (attributes.strokeLineJoin == QLatin1String(
"bevel"))
808 prop->setLineJoin(Qt::BevelJoin);
812 if (!attributes.strokeLineCap.isEmpty()) {
813 if (attributes.strokeLineCap == QLatin1String(
"butt"))
814 prop->setLineCap(Qt::FlatCap);
815 else if (attributes.strokeLineCap == QLatin1String(
"round"))
816 prop->setLineCap(Qt::RoundCap);
817 else if (attributes.strokeLineCap == QLatin1String(
"square"))
818 prop->setLineCap(Qt::SquareCap);
822 if (!attributes.strokeDashOffset.isEmpty() && attributes.strokeDashOffset !=
QT_INHERIT)
823 prop->setDashOffset(QSvgUtils::toDouble(attributes.strokeDashOffset));
826 if (!attributes.vectorEffect.isEmpty()) {
827 if (attributes.vectorEffect == QLatin1String(
"non-scaling-stroke"))
828 prop->setVectorEffect(
true);
829 else if (attributes.vectorEffect == QLatin1String(
"none"))
830 prop->setVectorEffect(
false);
834 if (!attributes.strokeMiterLimit.isEmpty() && attributes.strokeMiterLimit !=
QT_INHERIT)
835 prop->setMiterLimit(QSvgUtils::toDouble(attributes.strokeMiterLimit));
838 if (!attributes.strokeOpacity.isEmpty() && attributes.strokeOpacity !=
QT_INHERIT)
839 prop->setOpacity(qMin(qreal(1.0), qMax(qreal(0.0), QSvgUtils::toDouble(attributes.strokeOpacity))));
841 node->appendStyleProperty(prop, attributes.id);
849{ qreal(6.9), qreal(8.3), qreal(10.0), qreal(12.0), qreal(14.4), qreal(17.3), qreal(20.7) };
855 switch (spec.at(0).unicode()) {
857 if (spec == QLatin1String(
"xx-small"))
859 if (spec == QLatin1String(
"x-small"))
861 if (spec == QLatin1String(
"x-large"))
863 if (spec == QLatin1String(
"xx-large"))
867 if (spec == QLatin1String(
"small"))
871 if (spec == QLatin1String(
"medium"))
875 if (spec == QLatin1String(
"large"))
879 if (spec == QLatin1String(
"none"))
892 if (attributes.fontFamily.isEmpty() && attributes.fontSize.isEmpty() && attributes.fontStyle.isEmpty() &&
893 attributes.fontWeight.isEmpty() && attributes.fontVariant.isEmpty() && attributes.textAnchor.isEmpty())
896 QSvgFontStyle *fontStyle =
nullptr;
897 if (!attributes.fontFamily.isEmpty()) {
898 QSvgDocument *doc = node->document();
900 QSvgFont *svgFont = doc->svgFont(attributes.fontFamily.toString());
902 fontStyle =
new QSvgFontStyle(svgFont, doc);
906 fontStyle =
new QSvgFontStyle;
907 if (!attributes.fontFamily.isEmpty() && attributes.fontFamily !=
QT_INHERIT) {
908 QStringView family = attributes.fontFamily.trimmed();
909 if (!family.isEmpty() && (family.at(0) == QLatin1Char(
'\'') || family.at(0) == QLatin1Char(
'\"')))
910 family = family.mid(1, family.size() - 2);
911 fontStyle->setFamily(family.toString());
914 if (!attributes.fontSize.isEmpty() && attributes.fontSize !=
QT_INHERIT) {
916 const FontSizeSpec spec = fontSizeSpec(attributes.fontSize);
922 qreal fs = QSvgUtils::parseLength(attributes.fontSize, &type);
923 fs = QSvgUtils::convertToPixels(fs,
true, type);
924 fontStyle->setSize(qMin(fs, qreal(0xffff)));
928 fontStyle->setSize(sizeTable[spec]);
933 if (!attributes.fontStyle.isEmpty() && attributes.fontStyle !=
QT_INHERIT) {
934 if (attributes.fontStyle == QLatin1String(
"normal")) {
935 fontStyle->setStyle(QFont::StyleNormal);
936 }
else if (attributes.fontStyle == QLatin1String(
"italic")) {
937 fontStyle->setStyle(QFont::StyleItalic);
938 }
else if (attributes.fontStyle == QLatin1String(
"oblique")) {
939 fontStyle->setStyle(QFont::StyleOblique);
943 if (!attributes.fontWeight.isEmpty() && attributes.fontWeight !=
QT_INHERIT) {
945 const int weightNum = attributes.fontWeight.toInt(&ok);
947 fontStyle->setWeight(weightNum);
949 if (attributes.fontWeight == QLatin1String(
"normal")) {
950 fontStyle->setWeight(QFont::Normal);
951 }
else if (attributes.fontWeight == QLatin1String(
"bold")) {
952 fontStyle->setWeight(QFont::Bold);
953 }
else if (attributes.fontWeight == QLatin1String(
"bolder")) {
954 fontStyle->setWeight(QSvgFontStyle::BOLDER);
955 }
else if (attributes.fontWeight == QLatin1String(
"lighter")) {
956 fontStyle->setWeight(QSvgFontStyle::LIGHTER);
961 if (!attributes.fontVariant.isEmpty() && attributes.fontVariant !=
QT_INHERIT) {
962 if (attributes.fontVariant == QLatin1String(
"normal"))
963 fontStyle->setVariant(QFont::MixedCase);
964 else if (attributes.fontVariant == QLatin1String(
"small-caps"))
965 fontStyle->setVariant(QFont::SmallCaps);
968 if (!attributes.textAnchor.isEmpty() && attributes.textAnchor !=
QT_INHERIT) {
969 if (attributes.textAnchor == QLatin1String(
"start"))
970 fontStyle->setTextAnchor(Qt::AlignLeft);
971 if (attributes.textAnchor == QLatin1String(
"middle"))
972 fontStyle->setTextAnchor(Qt::AlignHCenter);
973 else if (attributes.textAnchor == QLatin1String(
"end"))
974 fontStyle->setTextAnchor(Qt::AlignRight);
977 node->appendStyleProperty(fontStyle, attributes.id);
984 if (attributes.transform.isEmpty())
986 QTransform matrix = parseTransformationMatrix(attributes.transform.trimmed());
988 if (!matrix.isIdentity()) {
989 node->appendStyleProperty(
new QSvgTransformStyle(QTransform(matrix)), attributes.id);
998 QSvgNode *parent = node->parent();
1000 if (parent && (attributes.visibility.isEmpty() || attributes.visibility ==
QT_INHERIT))
1001 node->setVisible(parent->isVisible());
1002 else if (attributes.visibility == QLatin1String(
"hidden") || attributes.visibility == QLatin1String(
"collapse")) {
1003 node->setVisible(
false);
1005 node->setVisible(
true);
1009 const QXmlStreamAttributes &attributes,
1010 QSvgHandler *handler);
1016 str = str.trimmed();
1017 if (str.endsWith(QLatin1String(
"ms"))) {
1020 }
else if (str.endsWith(QLatin1String(
"s"))) {
1023 double val = ms * QSvgUtils::toDouble(str, ok);
1025 if (val >
std::numeric_limits<
int>::min() && val <
std::numeric_limits<
int>::max())
1026 res =
static_cast<
int>(val);
1033#ifndef QT_NO_CSSPARSER
1036 const QXmlStreamAttributes &attributes,
1037 QSvgHandler *handler)
1039 QSvgCssProperties cssAnimProps(attributes);
1040 QList<QSvgAnimationProperty> parsedProperties = cssAnimProps.animations();
1042 for (
auto &property : parsedProperties) {
1043 QSvgCssAnimation *anim = handler->cssHandler().createAnimation(property.name);
1047 anim->setRunningTime(property.delay, property.duration);
1048 anim->setIterationCount(property.iteration);
1049 QSvgCssEasingPtr easing = handler->cssHandler().createEasing(property.easingFunction, property.easingValues);
1050 anim->setEasing(std::move(easing));
1052 handler->setAnimPeriod(property.delay, property.delay + property.duration);
1053 handler->document()->animator()->appendAnimation(node, anim);
1054 handler->document()->setAnimated(
true);
1059 const QXmlStreamAttributes &attributes)
1061 QSvgCssProperties cssProperties(attributes);
1062 QSvgOffsetProperty offset = cssProperties.offset();
1067 QSvgOffsetStyle *offsetStyle =
new QSvgOffsetStyle();
1068 offsetStyle->setPath(offset.path.value());
1069 offsetStyle->setRotateAngle(offset.angle);
1070 offsetStyle->setRotateType(offset.rotateType);
1071 offsetStyle->setDistance(offset.distance);
1072 node->appendStyleProperty(offsetStyle, QString());
1077QtSvg::Options QSvgHandler::options()
const
1082QtSvg::AnimatorType QSvgHandler::animatorType()
const
1084 return m_animatorType;
1087bool QSvgHandler::trustedSourceMode()
const
1089 return m_options.testFlag(QtSvg::AssumeTrustedSource);
1094 QStringList lst = str.split(QLatin1Char(
','), Qt::SkipEmptyParts);
1099 const QXmlStreamAttributes &attributes)
1101 QStringList features;
1102 QStringList extensions;
1103 QStringList languages;
1104 QStringList formats;
1106 QStringView xmlClassStr;
1108 for (
const QXmlStreamAttribute &attribute : attributes) {
1109 QStringView name = attribute.qualifiedName();
1112 QStringView value = attribute.value();
1113 switch (name.at(0).unicode()) {
1115 if (name == QLatin1String(
"class"))
1116 xmlClassStr = value;
1119 if (name == QLatin1String(
"requiredFeatures"))
1120 features = stringToList(value.toString());
1121 else if (name == QLatin1String(
"requiredExtensions"))
1122 extensions = stringToList(value.toString());
1123 else if (name == QLatin1String(
"requiredFormats"))
1124 formats = stringToList(value.toString());
1125 else if (name == QLatin1String(
"requiredFonts"))
1126 fonts = stringToList(value.toString());
1129 if (name == QLatin1String(
"systemLanguage"))
1130 languages = stringToList(value.toString());
1137 node->setRequiredFeatures(features);
1138 node->setRequiredExtensions(extensions);
1139 node->setRequiredLanguages(languages);
1140 node->setRequiredFormats(formats);
1141 node->setRequiredFonts(fonts);
1142 node->setNodeId(someId(attributes));
1143 node->setXmlClass(xmlClassStr.toString());
1152 if (attributes.opacity.isEmpty())
1155 const QStringView value = attributes.opacity.trimmed();
1158 qreal op = value.toDouble(&ok);
1161 QSvgOpacityStyle *opacity =
new QSvgOpacityStyle(qBound(qreal(0.0), op, qreal(1.0)));
1162 node->appendStyleProperty(opacity, attributes.id);
1168#define NOOP qDebug()<<"Operation: "<<op<<" is not implemented"
1169 if (op == QLatin1String(
"clear")) {
1170 return QPainter::CompositionMode_Clear;
1171 }
else if (op == QLatin1String(
"src")) {
1172 return QPainter::CompositionMode_Source;
1173 }
else if (op == QLatin1String(
"dst")) {
1174 return QPainter::CompositionMode_Destination;
1175 }
else if (op == QLatin1String(
"src-over")) {
1176 return QPainter::CompositionMode_SourceOver;
1177 }
else if (op == QLatin1String(
"dst-over")) {
1178 return QPainter::CompositionMode_DestinationOver;
1179 }
else if (op == QLatin1String(
"src-in")) {
1180 return QPainter::CompositionMode_SourceIn;
1181 }
else if (op == QLatin1String(
"dst-in")) {
1182 return QPainter::CompositionMode_DestinationIn;
1183 }
else if (op == QLatin1String(
"src-out")) {
1184 return QPainter::CompositionMode_SourceOut;
1185 }
else if (op == QLatin1String(
"dst-out")) {
1186 return QPainter::CompositionMode_DestinationOut;
1187 }
else if (op == QLatin1String(
"src-atop")) {
1188 return QPainter::CompositionMode_SourceAtop;
1189 }
else if (op == QLatin1String(
"dst-atop")) {
1190 return QPainter::CompositionMode_DestinationAtop;
1191 }
else if (op == QLatin1String(
"xor")) {
1192 return QPainter::CompositionMode_Xor;
1193 }
else if (op == QLatin1String(
"plus")) {
1194 return QPainter::CompositionMode_Plus;
1195 }
else if (op == QLatin1String(
"multiply")) {
1196 return QPainter::CompositionMode_Multiply;
1197 }
else if (op == QLatin1String(
"screen")) {
1198 return QPainter::CompositionMode_Screen;
1199 }
else if (op == QLatin1String(
"overlay")) {
1200 return QPainter::CompositionMode_Overlay;
1201 }
else if (op == QLatin1String(
"darken")) {
1202 return QPainter::CompositionMode_Darken;
1203 }
else if (op == QLatin1String(
"lighten")) {
1204 return QPainter::CompositionMode_Lighten;
1205 }
else if (op == QLatin1String(
"color-dodge")) {
1206 return QPainter::CompositionMode_ColorDodge;
1207 }
else if (op == QLatin1String(
"color-burn")) {
1208 return QPainter::CompositionMode_ColorBurn;
1209 }
else if (op == QLatin1String(
"hard-light")) {
1210 return QPainter::CompositionMode_HardLight;
1211 }
else if (op == QLatin1String(
"soft-light")) {
1212 return QPainter::CompositionMode_SoftLight;
1213 }
else if (op == QLatin1String(
"difference")) {
1214 return QPainter::CompositionMode_Difference;
1215 }
else if (op == QLatin1String(
"exclusion")) {
1216 return QPainter::CompositionMode_Exclusion;
1221 return QPainter::CompositionMode_SourceOver;
1228 if (attributes.compOp.isEmpty())
1230 QStringView value = attributes.compOp.trimmed();
1232 if (!value.isEmpty()) {
1233 QSvgCompOpStyle *compop =
new QSvgCompOpStyle(svgToQtCompositionMode(value));
1234 node->appendStyleProperty(compop, attributes.id);
1240 if (str == QLatin1String(
"inline")) {
1241 return QSvgNode::InlineMode;
1242 }
else if (str == QLatin1String(
"block")) {
1243 return QSvgNode::BlockMode;
1244 }
else if (str == QLatin1String(
"list-item")) {
1245 return QSvgNode::ListItemMode;
1246 }
else if (str == QLatin1String(
"run-in")) {
1247 return QSvgNode::RunInMode;
1248 }
else if (str == QLatin1String(
"compact")) {
1249 return QSvgNode::CompactMode;
1250 }
else if (str == QLatin1String(
"marker")) {
1251 return QSvgNode::MarkerMode;
1252 }
else if (str == QLatin1String(
"table")) {
1253 return QSvgNode::TableMode;
1254 }
else if (str == QLatin1String(
"inline-table")) {
1255 return QSvgNode::InlineTableMode;
1256 }
else if (str == QLatin1String(
"table-row-group")) {
1257 return QSvgNode::TableRowGroupMode;
1258 }
else if (str == QLatin1String(
"table-header-group")) {
1259 return QSvgNode::TableHeaderGroupMode;
1260 }
else if (str == QLatin1String(
"table-footer-group")) {
1261 return QSvgNode::TableFooterGroupMode;
1262 }
else if (str == QLatin1String(
"table-row")) {
1263 return QSvgNode::TableRowMode;
1264 }
else if (str == QLatin1String(
"table-column-group")) {
1265 return QSvgNode::TableColumnGroupMode;
1266 }
else if (str == QLatin1String(
"table-column")) {
1267 return QSvgNode::TableColumnMode;
1268 }
else if (str == QLatin1String(
"table-cell")) {
1269 return QSvgNode::TableCellMode;
1270 }
else if (str == QLatin1String(
"table-caption")) {
1271 return QSvgNode::TableCaptionMode;
1272 }
else if (str == QLatin1String(
"none")) {
1273 return QSvgNode::NoneMode;
1275 return QSvgNode::InheritMode;
1277 return QSvgNode::BlockMode;
1284 if (attributes.display.isEmpty())
1286 QStringView displayStr = attributes.display.trimmed();
1288 if (!displayStr.isEmpty()) {
1289 node->setDisplayMode(displayStringToEnum(displayStr));
1295 if (attribute.isEmpty())
1296 return std::nullopt;
1298 constexpr QLatin1String urlKeyword(
"url");
1299 QStringView attrStr = attribute.trimmed();
1300 if (attrStr.startsWith(urlKeyword))
1301 attrStr.slice(urlKeyword.size());
1302 QStringView id = idFromUrl(attrStr);
1303 if (id.startsWith(QLatin1Char(
'#')))
1310 QSvgHandler *handler)
1315 if (
auto id = getAttributeId(attributes.mask))
1316 node->setMaskId(id->toString());
1317 if (
auto id = getAttributeId(attributes.markerStart))
1318 node->setMarkerStartId(id->toString());
1319 if (
auto id = getAttributeId(attributes.markerMid))
1320 node->setMarkerMidId(id->toString());
1321 if (
auto id = getAttributeId(attributes.markerEnd))
1322 node->setMarkerEndId(id->toString());
1323 if (
auto id = getAttributeId(attributes.filter))
1324 node->setFilterId(id->toString());
1331 if (attributes.imageRendering.isEmpty())
1334 QStringView ir = attributes.imageRendering.trimmed();
1335 QSvgQualityStyle *p =
new QSvgQualityStyle(0);
1336 if (ir == QLatin1String(
"auto"))
1337 p->setImageRendering(QSvgQualityStyle::ImageRenderingAuto);
1338 else if (ir == QLatin1String(
"optimizeSpeed"))
1339 p->setImageRendering(QSvgQualityStyle::ImageRenderingOptimizeSpeed);
1340 else if (ir == QLatin1String(
"optimizeQuality"))
1341 p->setImageRendering(QSvgQualityStyle::ImageRenderingOptimizeQuality);
1342 node->appendStyleProperty(p, attributes.id);
1346 const QXmlStreamAttributes &attributes,
1347 QSvgHandler *handler)
1355#ifndef QT_NO_CSSPARSER
1356 QXmlStreamAttributes cssAttributes;
1357 handler->cssHandler().styleLookup(node, cssAttributes);
1359 QStringView style = attributes.value(QLatin1String(
"style"));
1360 if (!style.isEmpty())
1361 handler->cssHandler().parseCSStoXMLAttrs(style.toString(), cssAttributes);
1362 svgAttributes.setAttributes(cssAttributes, handler);
1364 parseOffsetPath(node, cssAttributes);
1366 parseCssAnimations(node, cssAttributes, handler);
1385 const QXmlStreamAttributes &attributes,
1388 Q_UNUSED(parent); Q_UNUSED(attributes);
1393 const QXmlStreamAttributes &attributes,
1394 QSvgAnimateNode *anim,
1395 QSvgHandler *handler)
1397 const QStringView beginStr = attributes.value(QLatin1String(
"begin"));
1398 const QStringView durStr = attributes.value(QLatin1String(
"dur"));
1399 const QStringView endStr = attributes.value(QLatin1String(
"end"));
1400 const QStringView repeatStr = attributes.value(QLatin1String(
"repeatCount"));
1401 const QStringView fillStr = attributes.value(QLatin1String(
"fill"));
1402 const QStringView addtv = attributes.value(QLatin1String(
"additive"));
1403 QStringView linkId = attributes.value(QLatin1String(
"xlink:href"));
1405 if (linkId.isEmpty())
1406 linkId = attributes.value(QLatin1String(
"href"));
1408 linkId = linkId.mid(1);
1411 int begin = parseClockValue(beginStr, &ok);
1414 int dur = parseClockValue(durStr, &ok);
1417 int end = parseClockValue(endStr, &ok);
1420 qreal repeatCount = (repeatStr == QLatin1String(
"indefinite")) ? -1 :
1421 qMax(1.0, QSvgUtils::toDouble(repeatStr));
1423 QSvgAnimateNode::Fill fill = (fillStr == QLatin1String(
"freeze")) ? QSvgAnimateNode::Freeze :
1424 QSvgAnimateNode::Remove;
1426 QSvgAnimateNode::Additive additive = (addtv == QLatin1String(
"sum")) ? QSvgAnimateNode::Sum :
1427 QSvgAnimateNode::Replace;
1429 anim->setRunningTime(begin, dur, end, 0);
1430 anim->setRepeatCount(repeatCount);
1431 anim->setFill(fill);
1432 anim->setAdditiveType(additive);
1433 anim->setLinkId(linkId.toString());
1435 parent->document()->setAnimated(
true);
1437 handler->setAnimPeriod(begin, begin + dur);
1446 qreal spacing = 1.0f / (count - 1);
1447 for (uint i = 0; i < count; i++) {
1448 keyFrames.append(i * spacing);
1453 const QXmlStreamAttributes &attributes,
1454 QSvgHandler *handler)
1456 const QStringView fromStr = attributes.value(QLatin1String(
"from"));
1457 const QStringView toStr = attributes.value(QLatin1String(
"to"));
1458 const QStringView valuesStr = attributes.value(QLatin1String(
"values"));
1459 const QString targetStr = attributes.value(QLatin1String(
"attributeName")).toString();
1461 if (targetStr != QLatin1String(
"fill") && targetStr != QLatin1String(
"stroke"))
1464 QList<QColor> colors;
1465 if (valuesStr.isEmpty()) {
1466 QColor startColor, endColor;
1467 resolveColor(fromStr, startColor, handler);
1468 resolveColor(toStr, endColor, handler);
1470 colors.append(startColor);
1471 colors.append(endColor);
1473 for (
auto part : qTokenize(valuesStr, u';')) {
1475 resolveColor(part, color, handler);
1476 colors.append(color);
1480 QSvgAnimatedPropertyColor *prop =
static_cast<QSvgAnimatedPropertyColor *>
1481 (QSvgAbstractAnimatedProperty::createAnimatedProperty(targetStr));
1485 prop->setColors(colors);
1487 QList<qreal> keyFrames;
1488 generateKeyFrames(keyFrames, colors.size());
1489 prop->setKeyFrames(keyFrames);
1491 QSvgAnimateColor *anim =
new QSvgAnimateColor(parent);
1492 anim->appendProperty(prop);
1494 if (!parseBaseAnimate(parent, attributes, anim, handler)) {
1503 const QXmlStreamAttributes &attributes,
1506 Q_UNUSED(parent); Q_UNUSED(attributes);
1512 QList<qreal> list = parseNumbersList(s);
1514 for (
int i = 3 - list.size(); i > 0; --i)
1519 const QXmlStreamAttributes &attributes,
1520 QSvgHandler *handler)
1522 const QStringView typeStr = attributes.value(QLatin1String(
"type"));
1523 const QStringView values = attributes.value(QLatin1String(
"values"));
1524 const QStringView fromStr = attributes.value(QLatin1String(
"from"));
1525 const QStringView toStr = attributes.value(QLatin1String(
"to"));
1526 const QStringView byStr = attributes.value(QLatin1String(
"by"));
1529 if (values.isEmpty()) {
1531 if (fromStr.isEmpty()) {
1532 if (!byStr.isEmpty()) {
1536 parseNumberTriplet(vals, s = byStr.constData());
1542 if (!toStr.isEmpty()) {
1544 parseNumberTriplet(vals, s = fromStr.constData());
1545 parseNumberTriplet(vals, s = toStr.constData());
1546 }
else if (!byStr.isEmpty()) {
1548 parseNumberTriplet(vals, s = fromStr.constData());
1549 parseNumberTriplet(vals, s = byStr.constData());
1550 for (
int i = vals.size() - 3; i < vals.size(); ++i)
1551 vals[i] += vals[i - 3];
1557 const QChar *s = values.constData();
1558 while (s && s != values.cend()) {
1559 parseNumberTriplet(vals, s);
1560 if (s == values.cend())
1565 if (vals.size() % 3 != 0)
1569 QList<QSvgAnimatedPropertyTransform::TransformComponent> components;
1570 for (
int i = 0; i <= vals.size() - 3; i += 3) {
1571 QSvgAnimatedPropertyTransform::TransformComponent component;
1572 if (typeStr == QLatin1String(
"translate")) {
1573 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Translate;
1574 component.values.append(vals.at(i));
1575 component.values.append(vals.at(i + 1));
1576 }
else if (typeStr == QLatin1String(
"scale")) {
1577 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Scale;
1578 component.values.append(vals.at(i));
1579 component.values.append(vals.at(i + 1));
1580 }
else if (typeStr == QLatin1String(
"rotate")) {
1581 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Rotate;
1582 component.values.append(vals.at(i));
1583 component.values.append(vals.at(i + 1));
1584 component.values.append(vals.at(i + 2));
1585 }
else if (typeStr == QLatin1String(
"skewX")) {
1586 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Skew;
1587 component.values.append(vals.at(i));
1588 component.values.append(0);
1589 }
else if (typeStr == QLatin1String(
"skewY")) {
1590 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Skew;
1591 component.values.append(0);
1592 component.values.append(vals.at(i));
1596 components.append(component);
1599 QSvgAnimatedPropertyTransform *prop =
static_cast<QSvgAnimatedPropertyTransform *>
1600 (QSvgAbstractAnimatedProperty::createAnimatedProperty(QLatin1String(
"transform")));
1604 prop->appendComponents(components);
1606 prop->setTransformCount(1);
1607 QList<qreal> keyFrames;
1608 generateKeyFrames(keyFrames, vals.size() / 3);
1609 prop->setKeyFrames(keyFrames);
1611 QSvgAnimateTransform *anim =
new QSvgAnimateTransform(parent);
1612 anim->appendProperty(prop);
1614 if (!parseBaseAnimate(parent, attributes, anim, handler)) {
1623 const QXmlStreamAttributes &attributes,
1626 Q_UNUSED(parent); Q_UNUSED(attributes);
1631 const QXmlStreamAttributes &attributes,
1634 Q_UNUSED(parent); Q_UNUSED(attributes);
1639 const QXmlStreamAttributes &attributes,
1642 const QStringView cx = attributes.value(QLatin1String(
"cx"));
1643 const QStringView cy = attributes.value(QLatin1String(
"cy"));
1644 const QStringView r = attributes.value(QLatin1String(
"r"));
1645 qreal ncx = QSvgUtils::toDouble(cx);
1646 qreal ncy = QSvgUtils::toDouble(cy);
1647 qreal nr = QSvgUtils::toDouble(r);
1651 QRectF rect(ncx-nr, ncy-nr, nr*2, nr*2);
1652 QSvgNode *circle =
new QSvgCircle(parent, rect);
1657 const QXmlStreamAttributes &attributes,
1660 Q_UNUSED(attributes);
1661 QSvgDefs *defs =
new QSvgDefs(parent);
1666 const QXmlStreamAttributes &attributes,
1669 Q_UNUSED(parent); Q_UNUSED(attributes);
1674 const QXmlStreamAttributes &attributes,
1677 const QStringView cx = attributes.value(QLatin1String(
"cx"));
1678 const QStringView cy = attributes.value(QLatin1String(
"cy"));
1679 const QStringView rx = attributes.value(QLatin1String(
"rx"));
1680 const QStringView ry = attributes.value(QLatin1String(
"ry"));
1681 qreal ncx = QSvgUtils::toDouble(cx);
1682 qreal ncy = QSvgUtils::toDouble(cy);
1683 qreal nrx = QSvgUtils::toDouble(rx);
1684 qreal nry = QSvgUtils::toDouble(ry);
1686 QRectF rect(ncx-nrx, ncy-nry, nrx*2, nry*2);
1687 QSvgNode *ellipse =
new QSvgEllipse(parent, rect);
1692 const QXmlStreamAttributes &attributes,
1695 const QStringView hax = attributes.value(QLatin1String(
"horiz-adv-x"));
1696 QString myId = someId(attributes);
1698 qreal horizAdvX = QSvgUtils::toDouble(hax);
1700 while (parent && parent->type() != QSvgNode::Doc) {
1701 parent = parent->parent();
1704 if (parent && !myId.isEmpty()) {
1705 QSvgDocument *doc =
static_cast<QSvgDocument*>(parent);
1706 QSvgFont *font = doc->svgFont(myId);
1708 font =
new QSvgFont(horizAdvX);
1709 font->setFamilyName(myId);
1710 doc->addSvgFont(font);
1712 return new QSvgFontStyle(font, doc);
1718 const QXmlStreamAttributes &attributes,
1721 if (parent->type() != QSvgStyleProperty::FONT) {
1725 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
1726 QSvgFont *font = style->svgFont();
1727 const QStringView name = attributes.value(QLatin1String(
"font-family"));
1728 const QStringView unitsPerEmStr = attributes.value(QLatin1String(
"units-per-em"));
1730 qreal unitsPerEm = QSvgUtils::toDouble(unitsPerEmStr);
1732 unitsPerEm = QSvgFont::DEFAULT_UNITS_PER_EM;
1734 if (!name.isEmpty())
1735 font->setFamilyName(name.toString());
1736 font->setUnitsPerEm(unitsPerEm);
1738 if (!font->familyName().isEmpty())
1739 if (!style->doc()->svgFont(font->familyName()))
1740 style->doc()->addSvgFont(font);
1746 const QXmlStreamAttributes &attributes,
1749 if (parent->type() != QSvgStyleProperty::FONT) {
1753 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
1754 QSvgFont *font = style->svgFont();
1755 const QStringView name = attributes.value(QLatin1String(
"name"));
1757 if (!name.isEmpty())
1758 font->setFamilyName(name.toString());
1760 if (!font->familyName().isEmpty())
1761 if (!style->doc()->svgFont(font->familyName()))
1762 style->doc()->addSvgFont(font);
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(parent); Q_UNUSED(attributes);
1792 const QXmlStreamAttributes &attributes,
1795 Q_UNUSED(attributes);
1796 QSvgG *node =
new QSvgG(parent);
1801 const QXmlStreamAttributes &attributes,
1804 if (parent->type() != QSvgStyleProperty::FONT) {
1808 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
1809 QSvgFont *font = style->svgFont();
1810 return createSvgGlyph(font, attributes,
false);
1814 const QXmlStreamAttributes &attributes,
1817 Q_UNUSED(parent); Q_UNUSED(attributes);
1822 const QXmlStreamAttributes &attributes,
1825 Q_UNUSED(parent); Q_UNUSED(attributes);
1830 const QXmlStreamAttributes &attributes,
1831 QSvgHandler *handler)
1833 const QStringView x = attributes.value(QLatin1String(
"x"));
1834 const QStringView y = attributes.value(QLatin1String(
"y"));
1835 const QStringView width = attributes.value(QLatin1String(
"width"));
1836 const QStringView height = attributes.value(QLatin1String(
"height"));
1837 QString filename = attributes.value(QLatin1String(
"xlink:href")).toString();
1839 filename = attributes.value(QLatin1String(
"href")).toString();
1840 qreal nx = QSvgUtils::toDouble(x);
1841 qreal ny = QSvgUtils::toDouble(y);
1843 qreal nwidth = QSvgUtils::parseLength(width.toString(), &type);
1844 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
1846 qreal nheight = QSvgUtils::parseLength(height.toString(), &type);
1847 nheight = QSvgUtils::convertToPixels(nheight,
false, type);
1849 filename = filename.trimmed();
1850 if (filename.isEmpty()) {
1851 qCWarning(lcSvgHandler) <<
"QSvgHandler: Image filename is empty";
1854 if (nwidth <= 0 || nheight <= 0) {
1855 qCWarning(lcSvgHandler) <<
"QSvgHandler: Width or height for" << filename <<
"image was not greater than 0";
1864 } filenameType = NotLoaded;
1866 if (filename.startsWith(QLatin1String(
"data"))) {
1869 if (qDecodeDataUrl(QUrl{filename}, mimeType, data)) {
1870 image = QImage::fromData(data);
1871 filenameType = LoadedFromData;
1875 if (image.isNull()) {
1876 const auto *file = qobject_cast<QFile *>(handler->device());
1879 if (url.isRelative()) {
1881 filename = info.absoluteDir().absoluteFilePath(filename);
1885 if (handler->trustedSourceMode() || !QImageReader::imageFormat(filename).startsWith(
"svg")) {
1886 image = QImage(filename);
1887 filenameType = LoadedFromFile;
1891 if (image.isNull()) {
1892 qCWarning(lcSvgHandler) <<
"Could not create image from" << filename;
1896 if (image.format() == QImage::Format_ARGB32)
1897 image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
1899 QSvgNode *img =
new QSvgImage(parent,
1901 filenameType == LoadedFromFile ? filename : QString{},
1910 const QXmlStreamAttributes &attributes,
1913 const QStringView x1 = attributes.value(QLatin1String(
"x1"));
1914 const QStringView y1 = attributes.value(QLatin1String(
"y1"));
1915 const QStringView x2 = attributes.value(QLatin1String(
"x2"));
1916 const QStringView y2 = attributes.value(QLatin1String(
"y2"));
1917 qreal nx1 = QSvgUtils::toDouble(x1);
1918 qreal ny1 = QSvgUtils::toDouble(y1);
1919 qreal nx2 = QSvgUtils::toDouble(x2);
1920 qreal ny2 = QSvgUtils::toDouble(y2);
1922 QLineF lineBounds(nx1, ny1, nx2, ny2);
1923 QSvgNode *line =
new QSvgLine(parent, lineBounds);
1929 const QXmlStreamAttributes &attributes,
1930 QSvgGradientStyle *gradProp,
1931 QSvgHandler *handler)
1933 const QStringView link = attributes.value(QLatin1String(
"xlink:href"));
1934 const QStringView trans = attributes.value(QLatin1String(
"gradientTransform"));
1935 const QStringView spread = attributes.value(QLatin1String(
"spreadMethod"));
1936 const QStringView units = attributes.value(QLatin1String(
"gradientUnits"));
1937 const QStringView colorStr = attributes.value(QLatin1String(
"color"));
1938 const QStringView colorOpacityStr = attributes.value(QLatin1String(
"color-opacity"));
1941 if (constructColor(colorStr, colorOpacityStr, color, handler)) {
1942 handler->popColor();
1943 handler->pushColor(color);
1947 QGradient *grad = gradProp->qgradient();
1948 if (node && !link.isEmpty()) {
1949 QSvgStyleProperty *prop = node->styleProperty(link);
1950 if (prop && prop->type() == QSvgStyleProperty::GRADIENT) {
1951 QSvgGradientStyle *inherited =
1952 static_cast<QSvgGradientStyle*>(prop);
1953 if (!inherited->stopLink().isEmpty()) {
1954 gradProp->setStopLink(inherited->stopLink(), handler->document());
1956 grad->setStops(inherited->qgradient()->stops());
1957 gradProp->setGradientStopsSet(inherited->gradientStopsSet());
1960 matrix = inherited->qtransform();
1962 gradProp->setStopLink(link.toString(), handler->document());
1966 if (!trans.isEmpty()) {
1967 matrix = parseTransformationMatrix(trans);
1968 gradProp->setTransform(matrix);
1969 }
else if (!matrix.isIdentity()) {
1970 gradProp->setTransform(matrix);
1973 if (!spread.isEmpty()) {
1974 if (spread == QLatin1String(
"pad")) {
1975 grad->setSpread(QGradient::PadSpread);
1976 }
else if (spread == QLatin1String(
"reflect")) {
1977 grad->setSpread(QGradient::ReflectSpread);
1978 }
else if (spread == QLatin1String(
"repeat")) {
1979 grad->setSpread(QGradient::RepeatSpread);
1983 if (units.isEmpty() || units == QLatin1String(
"objectBoundingBox")) {
1984 grad->setCoordinateMode(QGradient::ObjectMode);
1989 const QXmlStreamAttributes &attributes,
1990 QSvgHandler *handler)
1992 const QStringView x1 = attributes.value(QLatin1String(
"x1"));
1993 const QStringView y1 = attributes.value(QLatin1String(
"y1"));
1994 const QStringView x2 = attributes.value(QLatin1String(
"x2"));
1995 const QStringView y2 = attributes.value(QLatin1String(
"y2"));
2003 nx1 = convertToNumber(x1);
2005 ny1 = convertToNumber(y1);
2007 nx2 = convertToNumber(x2);
2009 ny2 = convertToNumber(y2);
2011 QSvgNode *itr = node;
2012 while (itr && itr->type() != QSvgNode::Doc) {
2013 itr = itr->parent();
2016 QLinearGradient *grad =
new QLinearGradient(nx1, ny1, nx2, ny2);
2017 grad->setInterpolationMode(QGradient::ComponentInterpolation);
2018 QSvgGradientStyle *prop =
new QSvgGradientStyle(grad);
2019 parseBaseGradient(node, attributes, prop, handler);
2025 const QXmlStreamAttributes &attributes,
2028 Q_UNUSED(parent); Q_UNUSED(attributes);
2033 const QXmlStreamAttributes &attributes,
2036 if (parent->type() != QSvgStyleProperty::FONT) {
2040 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
2041 QSvgFont *font = style->svgFont();
2042 return createSvgGlyph(font, attributes,
true);
2046 const QXmlStreamAttributes &attributes,
2049 Q_UNUSED(parent); Q_UNUSED(attributes);
2054 const QXmlStreamAttributes &attributes,
2057 Q_UNUSED(parent); Q_UNUSED(attributes);
2062 const QXmlStreamAttributes &,
2069 const QXmlStreamAttributes &attributes,
2070 QSvgHandler *handler)
2072 const QStringView x = attributes.value(QLatin1String(
"x"));
2073 const QStringView y = attributes.value(QLatin1String(
"y"));
2074 const QStringView width = attributes.value(QLatin1String(
"width"));
2075 const QStringView height = attributes.value(QLatin1String(
"height"));
2076 const QStringView mU = attributes.value(QLatin1String(
"maskUnits"));
2077 const QStringView mCU = attributes.value(QLatin1String(
"maskContentUnits"));
2079 QtSvg::
UnitTypes nmU = mU.contains(QLatin1String(
"userSpaceOnUse")) ?
2082 QtSvg::
UnitTypes nmCU = mCU.contains(QLatin1String(
"objectBoundingBox")) ?
2092 qreal nx = QSvgUtils::parseLength(x, &type, &ok);
2093 nx = QSvgUtils::convertToPixels(nx,
true, type);
2094 if (x.isEmpty() || !ok) {
2098 nx = nx / 100. * handler->document()->viewBox().width();
2103 qreal ny = QSvgUtils::parseLength(y, &type, &ok);
2104 ny = QSvgUtils::convertToPixels(ny,
true, type);
2105 if (y.isEmpty() || !ok) {
2109 ny = ny / 100. * handler->document()->viewBox().height();
2114 qreal nwidth = QSvgUtils::parseLength(width, &type, &ok);
2115 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
2116 if (width.isEmpty() || !ok) {
2120 nwidth = nwidth / 100. * handler->document()->viewBox().width();
2122 nwidth = nwidth / 100.;
2125 qreal nheight = QSvgUtils::parseLength(height, &type, &ok);
2126 nheight = QSvgUtils::convertToPixels(nheight,
true, type);
2127 if (height.isEmpty() || !ok) {
2131 nheight = nheight / 100. * handler->document()->viewBox().height();
2133 nheight = nheight / 100.;
2136 QRectF bounds(nx, ny, nwidth, nheight);
2137 if (bounds.isEmpty())
2140 QSvgNode *mask =
new QSvgMask(parent, QSvgRectF(bounds, nmUx, nmUy, nmUw, nmUh), nmCU);
2147 const QStringView xStr = attributes.value(QLatin1String(
"x"));
2148 const QStringView yStr = attributes.value(QLatin1String(
"y"));
2149 const QStringView widthStr = attributes.value(QLatin1String(
"width"));
2150 const QStringView heightStr = attributes.value(QLatin1String(
"height"));
2153 if (!xStr.isEmpty()) {
2155 x = QSvgUtils::parseLength(xStr, &type);
2157 x = QSvgUtils::convertToPixels(x,
true, type);
2167 if (!yStr.isEmpty()) {
2169 y = QSvgUtils::parseLength(yStr, &type);
2171 y = QSvgUtils::convertToPixels(y,
false, type);
2181 if (!widthStr.isEmpty()) {
2183 width = QSvgUtils::parseLength(widthStr, &type);
2185 width = QSvgUtils::convertToPixels(width,
true, type);
2192 rect->setWidth(width);
2195 if (!heightStr.isEmpty()) {
2197 height = QSvgUtils::parseLength(heightStr, &type);
2199 height = QSvgUtils::convertToPixels(height,
false, type);
2206 rect->setHeight(height);
2211 const QXmlStreamAttributes &attributes,
2212 QSvgHandler *handler)
2214 const QStringView fU = attributes.value(QLatin1String(
"filterUnits"));
2215 const QStringView pU = attributes.value(QLatin1String(
"primitiveUnits"));
2217 const QtSvg::
UnitTypes filterUnits = fU.contains(QLatin1String(
"userSpaceOnUse")) ?
2220 const QtSvg::
UnitTypes primitiveUnits = pU.contains(QLatin1String(
"objectBoundingBox")) ?
2228 qreal width = handler->document()->viewBox().width();
2229 qreal height = handler->document()->viewBox().height();
2230 rect = QSvgRectF(QRectF(-0.1 * width, -0.1 * height, 1.2 * width, 1.2 * height),
2234 rect = QSvgRectF(QRectF(-0.1, -0.1, 1.2, 1.2),
2239 parseFilterBounds(attributes, &rect);
2241 QSvgNode *filter =
new QSvgFilterContainer(parent, rect, filterUnits, primitiveUnits);
2246 QString *outString, QSvgRectF *rect)
2248 *inString = attributes.value(QLatin1String(
"in")).toString();
2249 *outString = attributes.value(QLatin1String(
"result")).toString();
2255 *rect = QSvgRectF(QRectF(0, 0, 1.0, 1.0),
2261 parseFilterBounds(attributes, rect);
2265 const QXmlStreamAttributes &attributes,
2268 const QStringView typeString = attributes.value(QLatin1String(
"type"));
2269 const QStringView valuesString = attributes.value(QLatin1String(
"values"));
2271 QString inputString;
2272 QString outputString;
2275 QSvgFeColorMatrix::ColorShiftType type;
2276 QSvgFeColorMatrix::Matrix values;
2279 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2281 if (typeString.startsWith(QLatin1String(
"saturate")))
2282 type = QSvgFeColorMatrix::ColorShiftType::Saturate;
2283 else if (typeString.startsWith(QLatin1String(
"hueRotate")))
2284 type = QSvgFeColorMatrix::ColorShiftType::HueRotate;
2285 else if (typeString.startsWith(QLatin1String(
"luminanceToAlpha")))
2286 type = QSvgFeColorMatrix::ColorShiftType::LuminanceToAlpha;
2288 type = QSvgFeColorMatrix::ColorShiftType::Matrix;
2290 if (!valuesString.isEmpty()) {
2291 const auto valueStringList = splitWithDelimiter(valuesString);
2292 for (
int i = 0, j = 0; i < qMin(20, valueStringList.size()); i++) {
2294 qreal v = QSvgUtils::toDouble(valueStringList.at(i), &ok);
2296 values.data()[j] = v;
2301 values.setToIdentity();
2304 QSvgNode *filter =
new QSvgFeColorMatrix(parent, inputString, outputString, rect,
2310 const QXmlStreamAttributes &attributes,
2313 const QStringView edgeModeString = attributes.value(QLatin1String(
"edgeMode"));
2314 const QStringView stdDeviationString = attributes.value(QLatin1String(
"stdDeviation"));
2316 QString inputString;
2317 QString outputString;
2320 QSvgFeGaussianBlur::EdgeMode edgemode = QSvgFeGaussianBlur::EdgeMode::Duplicate;
2322 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2323 qreal stdDeviationX = 0;
2324 qreal stdDeviationY = 0;
2326 stdDeviationX = qMax(0., QSvgUtils::toDouble(stdDeviationString.split(u" ").first()));
2327 stdDeviationY = qMax(0., QSvgUtils::toDouble(stdDeviationString.split(u" ").last()));
2329 stdDeviationY = stdDeviationX = qMax(0., QSvgUtils::toDouble(stdDeviationString));
2332 if (edgeModeString.startsWith(QLatin1String(
"wrap")))
2333 edgemode = QSvgFeGaussianBlur::EdgeMode::Wrap;
2334 else if (edgeModeString.startsWith(QLatin1String(
"none")))
2335 edgemode = QSvgFeGaussianBlur::EdgeMode::None;
2337 QSvgNode *filter =
new QSvgFeGaussianBlur(parent, inputString, outputString, rect,
2338 stdDeviationX, stdDeviationY, edgemode);
2343 const QXmlStreamAttributes &attributes,
2346 QStringView dxString = attributes.value(QLatin1String(
"dx"));
2347 QStringView dyString = attributes.value(QLatin1String(
"dy"));
2349 QString inputString;
2350 QString outputString;
2353 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2356 if (!dxString.isEmpty()) {
2358 dx = QSvgUtils::parseLength(dxString, &type);
2360 dx = QSvgUtils::convertToPixels(dx,
true, type);
2364 if (!dyString.isEmpty()) {
2366 dy = QSvgUtils::parseLength(dyString, &type);
2368 dy = QSvgUtils::convertToPixels(dy,
true, type);
2371 QSvgNode *filter =
new QSvgFeOffset(parent, inputString, outputString, rect,
2377 const QXmlStreamAttributes &attributes,
2380 const QStringView in2String = attributes.value(QLatin1String(
"in2"));
2381 const QStringView operatorString = attributes.value(QLatin1String(
"operator"));
2382 const QStringView k1String = attributes.value(QLatin1String(
"k1"));
2383 const QStringView k2String = attributes.value(QLatin1String(
"k2"));
2384 const QStringView k3String = attributes.value(QLatin1String(
"k3"));
2385 const QStringView k4String = attributes.value(QLatin1String(
"k4"));
2387 QString inputString;
2388 QString outputString;
2391 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2393 QSvgFeComposite::Operator op = QSvgFeComposite::Operator::Over;
2394 if (operatorString.startsWith(QLatin1String(
"in")))
2395 op = QSvgFeComposite::Operator::In;
2396 else if (operatorString.startsWith(QLatin1String(
"out")))
2397 op = QSvgFeComposite::Operator::Out;
2398 else if (operatorString.startsWith(QLatin1String(
"atop")))
2399 op = QSvgFeComposite::Operator::Atop;
2400 else if (operatorString.startsWith(QLatin1String(
"xor")))
2401 op = QSvgFeComposite::Operator::Xor;
2402 else if (operatorString.startsWith(QLatin1String(
"lighter")))
2403 op = QSvgFeComposite::Operator::Lighter;
2404 else if (operatorString.startsWith(QLatin1String(
"arithmetic")))
2405 op = QSvgFeComposite::Operator::Arithmetic;
2407 QVector4D k(0, 0, 0, 0);
2409 if (op == QSvgFeComposite::Operator::Arithmetic) {
2411 qreal v = QSvgUtils::toDouble(k1String, &ok);
2414 v = QSvgUtils::toDouble(k2String, &ok);
2417 v = QSvgUtils::toDouble(k3String, &ok);
2420 v = QSvgUtils::toDouble(k4String, &ok);
2425 QSvgNode *filter =
new QSvgFeComposite(parent, inputString, outputString, rect,
2426 in2String.toString(), op, k);
2432 const QXmlStreamAttributes &attributes,
2435 QString inputString;
2436 QString outputString;
2439 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2441 QSvgNode *filter =
new QSvgFeMerge(parent, inputString, outputString, rect);
2446 const QXmlStreamAttributes &attributes,
2447 QSvgHandler *handler)
2449 QStringView colorStr = attributes.value(QLatin1String(
"flood-color"));
2450 const QStringView opacityStr = attributes.value(QLatin1String(
"flood-opacity"));
2453 if (!constructColor(colorStr, opacityStr, color, handler)) {
2454 color = QColor(Qt::black);
2455 if (opacityStr.isEmpty())
2456 color.setAlphaF(1.0);
2458 setAlpha(opacityStr, &color);
2461 QString inputString;
2462 QString outputString;
2465 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2467 QSvgNode *filter =
new QSvgFeFlood(parent, inputString, outputString, rect, color);
2472 const QXmlStreamAttributes &attributes,
2475 QString inputString;
2476 QString outputString;
2479 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2481 QSvgNode *filter =
new QSvgFeMergeNode(parent, inputString, outputString, rect);
2486 const QXmlStreamAttributes &attributes,
2489 const QStringView in2String = attributes.value(QLatin1String(
"in2"));
2490 const QStringView modeString = attributes.value(QLatin1String(
"mode"));
2492 QString inputString;
2493 QString outputString;
2496 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2498 QSvgFeBlend::Mode mode = QSvgFeBlend::Mode::Normal;
2499 if (modeString.startsWith(QLatin1StringView(
"multiply")))
2500 mode = QSvgFeBlend::Mode::Multiply;
2501 else if (modeString.startsWith(QLatin1StringView(
"screen")))
2502 mode = QSvgFeBlend::Mode::Screen;
2503 else if (modeString.startsWith(QLatin1StringView(
"darken")))
2504 mode = QSvgFeBlend::Mode::Darken;
2505 else if (modeString.startsWith(QLatin1StringView(
"lighten")))
2506 mode = QSvgFeBlend::Mode::Lighten;
2508 QSvgNode *filter =
new QSvgFeBlend(parent, inputString, outputString, rect,
2509 in2String.toString(), mode);
2514 const QXmlStreamAttributes &attributes,
2517 QString inputString;
2518 QString outputString;
2521 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2523 QSvgNode *filter =
new QSvgFeUnsupported(parent, inputString, outputString, rect);
2529 QList<QStringView> viewBoxValues;
2532 viewBoxValues = splitWithDelimiter(str);
2533 if (viewBoxValues.size() == 4) {
2535 qreal x = QSvgUtils::parseLength(viewBoxValues.at(0).trimmed(), &type);
2536 qreal y = QSvgUtils::parseLength(viewBoxValues.at(1).trimmed(), &type);
2537 qreal w = QSvgUtils::parseLength(viewBoxValues.at(2).trimmed(), &type);
2538 qreal h = QSvgUtils::parseLength(viewBoxValues.at(3).trimmed(), &type);
2539 return QRectF(x, y, w, h);
2541 return std::nullopt;
2545 QRectF *rect, QRectF *viewBox, QPointF *refPoint,
2546 QSvgSymbolLike::PreserveAspectRatios *aspect,
2547 QSvgSymbolLike::Overflow *overflow,
2548 bool marker =
false)
2550 const QStringView xStr = attributes.value(QLatin1String(
"x"));
2551 const QStringView yStr = attributes.value(QLatin1String(
"y"));
2552 const QStringView refXStr = attributes.value(QLatin1String(
"refX"));
2553 const QStringView refYStr = attributes.value(QLatin1String(
"refY"));
2554 const QStringView widthStr = attributes.value(marker ? QLatin1String(
"markerWidth")
2555 : QLatin1String(
"width"));
2556 const QStringView heightStr = attributes.value(marker ? QLatin1String(
"markerHeight")
2557 : QLatin1String(
"height"));
2558 const QStringView pAspectRStr = attributes.value(QLatin1String(
"preserveAspectRatio"));
2559 const QStringView overflowStr = attributes.value(QLatin1String(
"overflow"));
2560 const QStringView viewBoxStr = attributes.value(QLatin1String(
"viewBox"));
2564 if (!xStr.isEmpty()) {
2566 x = QSvgUtils::parseLength(xStr, &type);
2568 x = QSvgUtils::convertToPixels(x,
true, type);
2571 if (!yStr.isEmpty()) {
2573 y = QSvgUtils::parseLength(yStr, &type);
2575 y = QSvgUtils::convertToPixels(y,
false, type);
2578 if (!widthStr.isEmpty()) {
2580 width = QSvgUtils::parseLength(widthStr, &type);
2582 width = QSvgUtils::convertToPixels(width,
true, type);
2585 if (!heightStr.isEmpty()) {
2587 height = QSvgUtils::parseLength(heightStr, &type);
2589 height = QSvgUtils::convertToPixels(height,
false, type);
2592 *rect = QRectF(x, y, width, height);
2595 if (!refXStr.isEmpty()) {
2597 x = QSvgUtils::parseLength(refXStr, &type);
2599 x = QSvgUtils::convertToPixels(x,
true, type);
2602 if (!refYStr.isEmpty()) {
2604 y = QSvgUtils::parseLength(refYStr, &type);
2606 y = QSvgUtils::convertToPixels(y,
false, type);
2608 *refPoint = QPointF(x,y);
2610 auto viewBoxResult = parseViewBox(viewBoxStr);
2612 *viewBox = *viewBoxResult;
2613 else if (width > 0 && height > 0)
2614 *viewBox = QRectF(0, 0, width, height);
2616 *viewBox = handler->document()->viewBox();
2618 if (viewBox->isNull())
2621 auto pAspectRStrs = pAspectRStr.split(u" ");
2622 QSvgSymbolLike::PreserveAspectRatio aspectX = QSvgSymbolLike::PreserveAspectRatio::xMid;
2623 QSvgSymbolLike::PreserveAspectRatio aspectY = QSvgSymbolLike::PreserveAspectRatio::yMid;
2624 QSvgSymbolLike::PreserveAspectRatio aspectMS = QSvgSymbolLike::PreserveAspectRatio::meet;
2626 for (
auto &pAStr : std::as_const(pAspectRStrs)) {
2627 if (pAStr.startsWith(QLatin1String(
"none"))) {
2628 aspectX = QSvgSymbolLike::PreserveAspectRatio::None;
2629 aspectY = QSvgSymbolLike::PreserveAspectRatio::None;
2631 if (pAStr.startsWith(QLatin1String(
"xMin")))
2632 aspectX = QSvgSymbolLike::PreserveAspectRatio::xMin;
2633 else if (pAStr.startsWith(QLatin1String(
"xMax")))
2634 aspectX = QSvgSymbolLike::PreserveAspectRatio::xMax;
2635 if (pAStr.endsWith(QLatin1String(
"YMin")))
2636 aspectY = QSvgSymbolLike::PreserveAspectRatio::yMin;
2637 else if (pAStr.endsWith(QLatin1String(
"YMax")))
2638 aspectY = QSvgSymbolLike::PreserveAspectRatio::yMax;
2641 if (pAStr.endsWith(QLatin1String(
"slice")))
2642 aspectMS = QSvgSymbolLike::PreserveAspectRatio::slice;
2644 *aspect = aspectX | aspectY | aspectMS;
2651 *overflow = QSvgSymbolLike::Overflow::Hidden;
2653 if (overflowStr.endsWith(QLatin1String(
"auto")))
2654 *overflow = QSvgSymbolLike::Overflow::Auto;
2655 else if (overflowStr.endsWith(QLatin1String(
"visible")))
2656 *overflow = QSvgSymbolLike::Overflow::Visible;
2657 else if (overflowStr.endsWith(QLatin1String(
"hidden")))
2658 *overflow = QSvgSymbolLike::Overflow::Hidden;
2659 else if (overflowStr.endsWith(QLatin1String(
"scroll")))
2660 *overflow = QSvgSymbolLike::Overflow::Scroll;
2666 const QXmlStreamAttributes &attributes,
2667 QSvgHandler *handler)
2669 QRectF rect, viewBox;
2671 QSvgSymbolLike::PreserveAspectRatios aspect;
2672 QSvgSymbolLike::Overflow overflow;
2674 if (!parseSymbolLikeAttributes(attributes, handler, &rect, &viewBox, &refP, &aspect, &overflow))
2677 refP = QPointF(0, 0);
2678 QSvgNode *symbol =
new QSvgSymbol(parent, rect, viewBox, refP, aspect, overflow);
2683 const QXmlStreamAttributes &attributes,
2684 QSvgHandler *handler)
2686 QRectF rect, viewBox;
2688 QSvgSymbolLike::PreserveAspectRatios aspect;
2689 QSvgSymbolLike::Overflow overflow;
2691 const QStringView orientStr = attributes.value(QLatin1String(
"orient"));
2692 const QStringView markerUnitsStr = attributes.value(QLatin1String(
"markerUnits"));
2694 qreal orientationAngle = 0;
2695 QSvgMarker::Orientation orientation;
2696 if (orientStr.startsWith(QLatin1String(
"auto-start-reverse")))
2697 orientation = QSvgMarker::Orientation::AutoStartReverse;
2698 else if (orientStr.startsWith(QLatin1String(
"auto")))
2699 orientation = QSvgMarker::Orientation::Auto;
2701 orientation = QSvgMarker::Orientation::Value;
2704 if (orientStr.endsWith(QLatin1String(
"turn")))
2705 a = 360. * QSvgUtils::toDouble(orientStr.mid(0, orientStr.length()-4), &ok);
2706 else if (orientStr.endsWith(QLatin1String(
"grad")))
2707 a = QSvgUtils::toDouble(orientStr.mid(0, orientStr.length()-4), &ok);
2708 else if (orientStr.endsWith(QLatin1String(
"rad")))
2709 a = 180. /
M_PI * QSvgUtils::toDouble(orientStr.mid(0, orientStr.length()-3), &ok);
2711 a = QSvgUtils::toDouble(orientStr, &ok);
2713 orientationAngle = a;
2716 QSvgMarker::MarkerUnits markerUnits = QSvgMarker::MarkerUnits::StrokeWidth;
2717 if (markerUnitsStr.startsWith(QLatin1String(
"userSpaceOnUse")))
2718 markerUnits = QSvgMarker::MarkerUnits::UserSpaceOnUse;
2720 if (!parseSymbolLikeAttributes(attributes, handler, &rect, &viewBox, &refP, &aspect, &overflow,
true))
2723 QSvgNode *marker =
new QSvgMarker(parent, rect, viewBox, refP, aspect, overflow,
2724 orientation, orientationAngle, markerUnits);
2729 const QXmlStreamAttributes &attributes,
2730 QSvgHandler *handler)
2732 QStringView data = attributes.value(QLatin1String(
"d"));
2734 std::optional<QPainterPath> qpath = QSvgUtils::parsePathDataFast(data,
2735 !handler->trustedSourceMode());
2737 qCWarning(lcSvgHandler,
"Invalid path data; path truncated.");
2741 qpath.value().setFillRule(Qt::WindingFill);
2742 QSvgNode *path =
new QSvgPath(parent, qpath.value());
2747 const QXmlStreamAttributes &attributes,
2750 const QString pointsStr = attributes.value(QLatin1String(
"points")).toString();
2751 const QChar *s = pointsStr.constData();
2752 const QList<qreal> points = parseNumbersList(s);
2753 if (points.size() < 4)
2756 for (
int i = 0; i < poly.size(); ++i)
2757 poly[i] = QPointF(points.at(2 * i), points.at(2 * i + 1));
2759 return new QSvgPolyline(parent, poly);
2761 return new QSvgPolygon(parent, poly);
2765 const QXmlStreamAttributes &attributes,
2768 return createPolyNode(parent, attributes,
false);
2772 const QXmlStreamAttributes &attributes,
2775 return createPolyNode(parent, attributes,
true);
2779 const QXmlStreamAttributes &attributes,
2782 Q_UNUSED(parent); Q_UNUSED(attributes);
2787 const QXmlStreamAttributes &attributes,
2788 QSvgHandler *handler)
2790 const QStringView cx = attributes.value(QLatin1String(
"cx"));
2791 const QStringView cy = attributes.value(QLatin1String(
"cy"));
2792 const QStringView r = attributes.value(QLatin1String(
"r"));
2793 const QStringView fx = attributes.value(QLatin1String(
"fx"));
2794 const QStringView fy = attributes.value(QLatin1String(
"fy"));
2799 ncx = convertToNumber(cx);
2801 ncy = convertToNumber(cy);
2805 nr = convertToNumber(r);
2811 nfx = convertToNumber(fx);
2814 nfy = convertToNumber(fy);
2816 QRadialGradient *grad =
new QRadialGradient(ncx, ncy, nr, nfx, nfy, 0);
2817 grad->setInterpolationMode(QGradient::ComponentInterpolation);
2819 QSvgGradientStyle *prop =
new QSvgGradientStyle(grad);
2820 parseBaseGradient(node, attributes, prop, handler);
2826 const QXmlStreamAttributes &attributes,
2829 const QStringView x = attributes.value(QLatin1String(
"x"));
2830 const QStringView y = attributes.value(QLatin1String(
"y"));
2831 const QStringView width = attributes.value(QLatin1String(
"width"));
2832 const QStringView height = attributes.value(QLatin1String(
"height"));
2833 const QStringView rx = attributes.value(QLatin1String(
"rx"));
2834 const QStringView ry = attributes.value(QLatin1String(
"ry"));
2838 qreal nwidth = QSvgUtils::parseLength(width, &type, &ok);
2841 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
2842 qreal nheight = QSvgUtils::parseLength(height, &type, &ok);
2845 nheight = QSvgUtils::convertToPixels(nheight,
true, type);
2846 qreal nrx = QSvgUtils::toDouble(rx);
2847 qreal nry = QSvgUtils::toDouble(ry);
2850 if (bounds.isEmpty())
2853 if (!rx.isEmpty() && ry.isEmpty())
2855 else if (!ry.isEmpty() && rx.isEmpty())
2861 if (nrx > bounds.width()/2)
2862 nrx = bounds.width()/2;
2863 if (nry > bounds.height()/2)
2864 nry = bounds.height()/2;
2869 nrx *= (100/(bounds.width()/2));
2870 nry *= (100/(bounds.height()/2));
2872 QSvgNode *rect =
new QSvgRect(parent, bounds, nrx, nry);
2877 const QXmlStreamAttributes &attributes,
2880 Q_UNUSED(parent); Q_UNUSED(attributes);
2885 const QXmlStreamAttributes &attributes,
2888 Q_UNUSED(parent); Q_UNUSED(attributes);
2893 const QXmlStreamAttributes &attributes,
2894 QSvgHandler *handler)
2896 Q_UNUSED(parent); Q_UNUSED(attributes);
2897 QStringView solidColorStr = attributes.value(QLatin1String(
"solid-color"));
2898 QStringView solidOpacityStr = attributes.value(QLatin1String(
"solid-opacity"));
2900 if (solidOpacityStr.isEmpty())
2901 solidOpacityStr = attributes.value(QLatin1String(
"opacity"));
2904 if (!constructColor(solidColorStr, solidOpacityStr, color, handler))
2906 QSvgSolidColorStyle *style =
new QSvgSolidColorStyle(color);
2911 const QXmlStreamAttributes &attributes,
2912 QSvgHandler *handler)
2914 if (parent->type() != QSvgStyleProperty::GRADIENT)
2916 QString nodeIdStr = someId(attributes);
2917 QString xmlClassStr = attributes.value(QLatin1String(
"class")).toString();
2923 QSvgDummyNode dummy;
2924 dummy.setNodeId(nodeIdStr);
2925 dummy.setXmlClass(xmlClassStr);
2927 QSvgAttributes attrs(attributes, handler);
2929#ifndef QT_NO_CSSPARSER
2930 QXmlStreamAttributes cssAttributes;
2931 handler->cssHandler().styleLookup(&dummy, cssAttributes);
2932 attrs.setAttributes(cssAttributes, handler);
2934 QXmlStreamAttributes styleCssAttributes;
2935 QStringView style = attributes.value(QLatin1String(
"style"));
2936 if (!style.isEmpty())
2937 handler->cssHandler().parseCSStoXMLAttrs(style.toString(), styleCssAttributes);
2938 attrs.setAttributes(styleCssAttributes, handler);
2942 parseColor(&dummy, attrs, handler);
2944 QSvgGradientStyle *gradientStyle =
2945 static_cast<QSvgGradientStyle*>(parent);
2946 QStringView colorStr = attrs.stopColor;
2950 qreal offset = convertToNumber(attrs.offset, &ok);
2954 if (!constructColor(colorStr, attrs.stopOpacity, color, handler)) {
2956 if (!attrs.stopOpacity.isEmpty())
2957 setAlpha(attrs.stopOpacity, &color);
2960 QGradient *grad = gradientStyle->qgradient();
2962 offset = qMin(qreal(1), qMax(qreal(0), offset));
2963 QGradientStops stops;
2964 if (gradientStyle->gradientStopsSet()) {
2965 stops = grad->stops();
2967 if (offset <= stops.back().first)
2968 offset = stops.back().first + FLT_EPSILON;
2973 if ((stops.size() == 1) || (stops.at(stops.size() - 2).first < 1.0 - FLT_EPSILON)) {
2974 stops.back().first = 1.0 - FLT_EPSILON;
2975 grad->setStops(stops);
2980 grad->setColorAt(offset, color);
2981 gradientStyle->setGradientStopsSet(
true);
2986 const QXmlStreamAttributes &attributes,
2987 QSvgHandler *handler)
2990#ifdef QT_NO_CSSPARSER
2991 Q_UNUSED(attributes);
2994 const QStringView type = attributes.value(QLatin1String(
"type"));
2995 if (type.compare(QLatin1String(
"text/css"), Qt::CaseInsensitive) == 0 || type.isNull())
2996 handler->setInStyle(
true);
3003 const QXmlStreamAttributes &attributes,
3004 QSvgHandler *handler)
3006 Q_UNUSED(parent); Q_UNUSED(attributes);
3008 QSvgDocument *node =
new QSvgDocument(handler->options(), handler->animatorType());
3009 const QStringView widthStr = attributes.value(QLatin1String(
"width"));
3010 const QStringView heightStr = attributes.value(QLatin1String(
"height"));
3011 const QStringView viewBoxStr = attributes.value(QLatin1String(
"viewBox"));
3015 if (!widthStr.isEmpty()) {
3016 width = QSvgUtils::parseLength(widthStr, &type);
3018 width = QSvgUtils::convertToPixels(width,
true, type);
3022 if (!heightStr.isEmpty()) {
3023 height = QSvgUtils::parseLength(heightStr, &type);
3025 height = QSvgUtils::convertToPixels(height,
false, type);
3029 auto viewBoxResult = parseViewBox(viewBoxStr);
3030 if (viewBoxResult) {
3031 node->setViewBox(*viewBoxResult);
3032 }
else if (width && height) {
3034 width = QSvgUtils::convertToPixels(width,
false, type);
3035 height = QSvgUtils::convertToPixels(height,
false, type);
3037 node->setViewBox(QRectF(0, 0, width, height));
3045 const QXmlStreamAttributes &attributes,
3048 Q_UNUSED(attributes);
3049 QSvgSwitch *node =
new QSvgSwitch(parent);
3054 const QXmlStreamAttributes &attributes,
3055 QSvgHandler *handler)
3057 const QStringView x = attributes.value(QLatin1String(
"x"));
3058 const QStringView y = attributes.value(QLatin1String(
"y"));
3059 const QStringView width = attributes.value(QLatin1String(
"width"));
3060 const QStringView height = attributes.value(QLatin1String(
"height"));
3061 const QStringView patternUnits = attributes.value(QLatin1String(
"patternUnits"));
3062 const QStringView patternContentUnits = attributes.value(QLatin1String(
"patternContentUnits"));
3063 const QStringView patternTransform = attributes.value(QLatin1String(
"patternTransform"));
3065 QtSvg::
UnitTypes nPatternUnits = patternUnits.contains(QLatin1String(
"userSpaceOnUse")) ?
3068 QtSvg::
UnitTypes nPatternContentUnits = patternContentUnits.contains(QLatin1String(
"objectBoundingBox")) ?
3071 const QStringView viewBoxStr = attributes.value(QLatin1String(
"viewBox"));
3076 qreal nx = QSvgUtils::parseLength(x, &type, &ok);
3077 nx = QSvgUtils::convertToPixels(nx,
true, type);
3081 nx = (nx / 100.) * handler->document()->viewBox().width();
3085 qreal ny = QSvgUtils::parseLength(y, &type, &ok);
3086 ny = QSvgUtils::convertToPixels(ny,
true, type);
3090 ny = (ny / 100.) * handler->document()->viewBox().height();
3094 qreal nwidth = QSvgUtils::parseLength(width, &type, &ok);
3095 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
3099 nwidth = (nwidth / 100.) * handler->document()->viewBox().width();
3101 nwidth = nwidth / 100.;
3103 qreal nheight = QSvgUtils::parseLength(height, &type, &ok);
3104 nheight = QSvgUtils::convertToPixels(nheight,
true, type);
3108 nheight = (nheight / 100.) * handler->document()->viewBox().height();
3110 nheight = nheight / 100.;
3113 auto viewBoxResult = parseViewBox(viewBoxStr);
3114 if (viewBoxResult) {
3115 if (viewBoxResult->width() > 0 && viewBoxResult->height() > 0)
3116 viewBox = *viewBoxResult;
3120 if (!patternTransform.isEmpty())
3121 matrix = parseTransformationMatrix(patternTransform);
3123 QRectF bounds(nx, ny, nwidth, nheight);
3124 if (bounds.isEmpty())
3127 QSvgRectF patternRectF(bounds, nPatternUnits, nPatternUnits, nPatternUnits, nPatternUnits);
3128 QSvgPattern *node =
new QSvgPattern(parent, patternRectF, viewBox, nPatternContentUnits, matrix);
3131 QSvgPatternStyle *prop =
new QSvgPatternStyle(node);
3132 node->appendStyleProperty(prop, someId(attributes));
3138 const QXmlStreamAttributes &,
3141 if (parent->type() != QSvgNode::Textarea)
3143 static_cast<QSvgText*>(parent)->addLineBreak();
3148 const QXmlStreamAttributes &attributes,
3151 const QStringView x = attributes.value(QLatin1String(
"x"));
3152 const QStringView y = attributes.value(QLatin1String(
"y"));
3155 qreal nx = QSvgUtils::parseLength(x, &type);
3156 nx = QSvgUtils::convertToPixels(nx,
true, type);
3157 qreal ny = QSvgUtils::parseLength(y, &type);
3158 ny = QSvgUtils::convertToPixels(ny,
true, type);
3160 QSvgNode *text =
new QSvgText(parent, QPointF(nx, ny));
3165 const QXmlStreamAttributes &attributes,
3166 QSvgHandler *handler)
3168 QSvgText *node =
static_cast<QSvgText *>(createTextNode(parent, attributes, handler));
3171 qreal width = QSvgUtils::parseLength(attributes.value(QLatin1String(
"width")), &type);
3172 qreal height = QSvgUtils::parseLength(attributes.value(QLatin1String(
"height")), &type);
3173 node->setTextArea(QSizeF(width, height));
3179 const QXmlStreamAttributes &,
3182 return new QSvgTspan(parent);
3186 const QXmlStreamAttributes &attributes,
3189 QStringView linkId = attributes.value(QLatin1String(
"xlink:href"));
3190 const QStringView xStr = attributes.value(QLatin1String(
"x"));
3191 const QStringView yStr = attributes.value(QLatin1String(
"y"));
3192 QSvgStructureNode *group =
nullptr;
3194 if (linkId.isEmpty())
3195 linkId = attributes.value(QLatin1String(
"href"));
3196 QString linkIdStr = linkId.mid(1).toString();
3198 switch (parent->type()) {
3200 case QSvgNode::Defs:
3201 case QSvgNode::Group:
3202 case QSvgNode::Switch:
3203 case QSvgNode::Mask:
3204 group =
static_cast<QSvgStructureNode*>(parent);
3212 if (!xStr.isNull() || !yStr.isNull()) {
3214 qreal nx = QSvgUtils::parseLength(xStr, &type);
3215 nx = QSvgUtils::convertToPixels(nx,
true, type);
3217 qreal ny = QSvgUtils::parseLength(yStr, &type);
3218 ny = QSvgUtils::convertToPixels(ny,
true, type);
3219 pt = QPointF(nx, ny);
3222 QSvgNode *link = group->scopeNode(linkIdStr);
3224 if (parent->isDescendantOf(link))
3225 qCWarning(lcSvgHandler,
"link %ls is recursive!",
qUtf16Printable(linkIdStr));
3227 return new QSvgUse(pt, parent, link);
3231 return new QSvgUse(pt, parent, linkIdStr);
3234 qCWarning(lcSvgHandler,
"<use> element %ls in wrong context!",
qUtf16Printable(linkIdStr));
3239 const QXmlStreamAttributes &attributes,
3242 Q_UNUSED(parent); Q_UNUSED(attributes);
3246typedef QSvgNode *(*FactoryMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3253 QStringView ref = name.mid(1);
3254 switch (name.at(0).unicode()) {
3256 if (ref == QLatin1String(
"efs"))
return createDefsNode;
3262 if (ref.isEmpty())
return createGNode;
3269 if (ref == QLatin1String(
"vg"))
return createSvgNode;
3270 if (ref == QLatin1String(
"witch"))
return createSwitchNode;
3288 QStringView ref = name.mid(1);
3289 switch (name.at(0).unicode()) {
3291 if (ref == QLatin1String(
"ircle"))
return createCircleNode;
3294 if (ref == QLatin1String(
"llipse"))
return createEllipseNode;
3297 if (ref == QLatin1String(
"mage"))
return createImageNode;
3300 if (ref == QLatin1String(
"ine"))
return createLineNode;
3303 if (ref == QLatin1String(
"ath"))
return createPathNode;
3304 if (ref == QLatin1String(
"olygon"))
return createPolygonNode;
3305 if (ref == QLatin1String(
"olyline"))
return createPolylineNode;
3308 if (ref == QLatin1String(
"ect"))
return createRectNode;
3311 if (ref == QLatin1String(
"ext"))
return createTextNode;
3312 if (ref == QLatin1String(
"extArea"))
return createTextAreaNode;
3313 if (ref == QLatin1String(
"span"))
return createTspanNode;
3316 if (ref == QLatin1String(
"se"))
return createUseNode;
3319 if (ref == QLatin1String(
"ideo"))
return createVideoNode;
3335 if (!name.startsWith(QLatin1String(
"fe")))
3338 if (name == QLatin1String(
"feMerge"))
return createFeMergeNode;
3339 if (name == QLatin1String(
"feColorMatrix"))
return createFeColorMatrixNode;
3340 if (name == QLatin1String(
"feGaussianBlur"))
return createFeGaussianBlurNode;
3341 if (name == QLatin1String(
"feOffset"))
return createFeOffsetNode;
3342 if (name == QLatin1String(
"feMergeNode"))
return createFeMergeNodeNode;
3343 if (name == QLatin1String(
"feComposite"))
return createFeCompositeNode;
3344 if (name == QLatin1String(
"feFlood"))
return createFeFloodNode;
3345 if (name == QLatin1String(
"feBlend"))
return createFeBlendNode;
3347 static const QStringList unsupportedFilters = {
3364 if (unsupportedFilters.contains(name))
3365 return createFeUnsupportedNode;
3370typedef QSvgNode *(*AnimationMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3377 QStringView ref = name.mid(1);
3379 switch (name.at(0).unicode()) {
3381 if (ref == QLatin1String(
"nimate"))
return createAnimateNode;
3382 if (ref == QLatin1String(
"nimateColor"))
return createAnimateColorNode;
3383 if (ref == QLatin1String(
"nimateMotion"))
return createAimateMotionNode;
3384 if (ref == QLatin1String(
"nimateTransform"))
return createAnimateTransformNode;
3393typedef bool (*
ParseMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3400 QStringView ref = name.mid(1);
3401 switch (name.at(0).unicode()) {
3403 if (ref.isEmpty())
return parseAnchorNode;
3404 if (ref == QLatin1String(
"udio"))
return parseAudioNode;
3407 if (ref == QLatin1String(
"iscard"))
return parseDiscardNode;
3410 if (ref == QLatin1String(
"oreignObject"))
return parseForeignObjectNode;
3413 if (ref == QLatin1String(
"andler"))
return parseHandlerNode;
3414 if (ref == QLatin1String(
"kern"))
return parseHkernNode;
3417 if (ref == QLatin1String(
"etadata"))
return parseMetadataNode;
3418 if (ref == QLatin1String(
"path"))
return parseMpathNode;
3423 if (ref == QLatin1String(
"refetch"))
return parsePrefetchNode;
3426 if (ref == QLatin1String(
"cript"))
return parseScriptNode;
3427 if (ref == QLatin1String(
"et"))
return parseSetNode;
3428 if (ref == QLatin1String(
"tyle"))
return parseStyleNode;
3431 if (ref == QLatin1String(
"break"))
return parseTbreakNode;
3440 const QXmlStreamAttributes &,
3448 QStringView ref = name.mid(1);
3449 switch (name.at(0).unicode()) {
3451 if (ref == QLatin1String(
"ont"))
return createFontNode;
3454 if (ref == QLatin1String(
"inearGradient"))
return createLinearGradientNode;
3457 if (ref == QLatin1String(
"adialGradient"))
return createRadialGradientNode;
3460 if (ref == QLatin1String(
"olidColor"))
return createSolidColorNode;
3469 const QXmlStreamAttributes &,
3477 QStringView ref = name.mid(1);
3478 switch (name.at(0).unicode()) {
3500QSvgHandler::QSvgHandler(QIODevice *device, QtSvg::Options options,
3501 QtSvg::AnimatorType type)
3502 : xml(
new QXmlStreamReader(device))
3503 , m_ownsReader(
true)
3504 , m_options(options)
3505 , m_animatorType(type)
3510QSvgHandler::QSvgHandler(
const QByteArray &data, QtSvg::Options options,
3511 QtSvg::AnimatorType type)
3512 : xml(
new QXmlStreamReader(data))
3513 , m_ownsReader(
true)
3514 , m_options(options)
3515 , m_animatorType(type)
3520QSvgHandler::QSvgHandler(QXmlStreamReader *
const reader, QtSvg::Options options,
3521 QtSvg::AnimatorType type)
3523 , m_ownsReader(
false)
3524 , m_options(options)
3525 , m_animatorType(type)
3530void QSvgHandler::init()
3535 m_defaultCoords = QSvgUtils::LT_PX;
3536 m_defaultPen = QPen(Qt::black, 1, Qt::SolidLine, Qt::FlatCap, Qt::SvgMiterJoin);
3537 m_defaultPen.setMiterLimit(4);
3543 QSvgFillStyle *fillStyle =
static_cast<QSvgFillStyle*>
3544 (node->styleProperty(QSvgStyleProperty::FILL));
3545 if (fillStyle && fillStyle->style() && fillStyle->style()->type() == QSvgStyleProperty::PATTERN) {
3546 QSvgPatternStyle *patternStyle =
static_cast<QSvgPatternStyle *>(fillStyle->style());
3547 if (active.contains(patternStyle->patternNode()))
3551 QSvgStrokeStyle *strokeStyle =
static_cast<QSvgStrokeStyle*>
3552 (node->styleProperty(QSvgStyleProperty::STROKE));
3553 if (strokeStyle && strokeStyle->style() && strokeStyle->style()->type() == QSvgStyleProperty::PATTERN) {
3554 QSvgPatternStyle *patternStyle =
static_cast<QSvgPatternStyle *>(strokeStyle->style());
3555 if (active.contains(patternStyle->patternNode()))
3564 if (Q_UNLIKELY(!node))
3566 switch (node->type()) {
3568 case QSvgNode::Group:
3569 case QSvgNode::Defs:
3570 case QSvgNode::Pattern:
3572 if (node->type() == QSvgNode::Pattern)
3573 active.append(node);
3575 auto *g =
static_cast<
const QSvgStructureNode*>(node);
3576 for (
auto &node : g->renderers()) {
3577 if (detectCycles(node.get(), active))
3584 if (active.contains(node))
3587 auto *u =
static_cast<
const QSvgUse*>(node);
3588 auto *target = u->link();
3591 if (detectCycles(target, active))
3596 case QSvgNode::Rect:
3597 case QSvgNode::Ellipse:
3598 case QSvgNode::Circle:
3599 case QSvgNode::Line:
3600 case QSvgNode::Path:
3601 case QSvgNode::Polygon:
3602 case QSvgNode::Polyline:
3603 case QSvgNode::Tspan:
3604 if (detectPatternCycles(node, active))
3614 const bool cycleFound = detectCycles(node);
3616 qCWarning(lcSvgHandler,
"Cycles detected in SVG, document discarded.");
3624void QSvgHandler::parse()
3626 xml->setNamespaceProcessing(
false);
3627#ifndef QT_NO_CSSPARSER
3631 int remainingUnfinishedElements = unfinishedElementsLimit;
3632 while (!xml->atEnd() && !done) {
3633 switch (xml->readNext()) {
3634 case QXmlStreamReader::StartElement:
3643 if (remainingUnfinishedElements && startElement(xml->name(), xml->attributes())) {
3644 --remainingUnfinishedElements;
3651 case QXmlStreamReader::EndElement:
3652 done = endElement(xml->name());
3653 ++remainingUnfinishedElements;
3655 case QXmlStreamReader::Characters:
3656 characters(xml->text());
3658 case QXmlStreamReader::ProcessingInstruction:
3659 processingInstruction(xml->processingInstructionTarget(), xml->processingInstructionData());
3665 resolvePaintServers(m_doc);
3667 if (detectCyclesAndWarn(m_doc)) {
3673bool QSvgHandler::startElement(
const QStringView localName,
3674 const QXmlStreamAttributes &attributes)
3676 QSvgNode *node =
nullptr;
3681
3682
3683 const QStringView xmlSpace(attributes.value(QLatin1String(
"xml:space")));
3684 if (xmlSpace.isNull()) {
3686 m_whitespaceMode.push(m_whitespaceMode.isEmpty() ? QSvgText::Default : m_whitespaceMode.top());
3687 }
else if (xmlSpace == QLatin1String(
"preserve")) {
3688 m_whitespaceMode.push(QSvgText::Preserve);
3689 }
else if (xmlSpace == QLatin1String(
"default")) {
3690 m_whitespaceMode.push(QSvgText::Default);
3692 const QByteArray msg =
'"' + xmlSpace.toLocal8Bit()
3693 +
"\" is an invalid value for attribute xml:space. "
3694 "Valid values are \"preserve\" and \"default\".";
3695 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3696 m_whitespaceMode.push(QSvgText::Default);
3699 if (!m_doc && localName != QLatin1String(
"svg"))
3702 if (m_doc && localName == QLatin1String(
"svg")) {
3703 m_skipNodes.push(Doc);
3704 qCWarning(lcSvgHandler) <<
"Skipping a nested svg element, because "
3705 "SVG Document must not contain nested svg elements in Svg Tiny 1.2";
3708 if (!m_skipNodes.isEmpty() && m_skipNodes.top() == Doc)
3711 if (FactoryMethod method = findGroupFactory(localName, options())) {
3714 node = method(
nullptr, attributes,
this);
3716 Q_ASSERT(node->type() == QSvgNode::Doc);
3717 m_doc =
static_cast<QSvgDocument*>(node);
3720 switch (m_nodes.top()->type()) {
3722 case QSvgNode::Group:
3723 case QSvgNode::Defs:
3724 case QSvgNode::Switch:
3725 case QSvgNode::Mask:
3726 case QSvgNode::Symbol:
3727 case QSvgNode::Marker:
3728 case QSvgNode::Pattern:
3730 node = method(m_nodes.top(), attributes,
this);
3732 QSvgStructureNode *group =
3733 static_cast<QSvgStructureNode*>(m_nodes.top());
3734 group->addChild(std::unique_ptr<QSvgNode>(node), someId(attributes));
3739 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
3740 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3746 parseCoreNode(node, attributes);
3747 parseStyle(node, attributes,
this);
3748 if (node->type() == QSvgNode::Filter)
3749 m_toBeResolved.append(node);
3751 }
else if (FactoryMethod method = findGraphicsFactory(localName, options())) {
3753 Q_ASSERT(!m_nodes.isEmpty());
3754 switch (m_nodes.top()->type()) {
3756 case QSvgNode::Group:
3757 case QSvgNode::Defs:
3758 case QSvgNode::Switch:
3759 case QSvgNode::Mask:
3760 case QSvgNode::Symbol:
3761 case QSvgNode::Marker:
3762 case QSvgNode::Pattern:
3764 if (localName == QLatin1String(
"tspan")) {
3765 const QByteArray msg = QByteArrayLiteral(
"\'tspan\' element in wrong context.");
3766 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3769 node = method(m_nodes.top(), attributes,
this);
3771 QSvgStructureNode *group =
3772 static_cast<QSvgStructureNode*>(m_nodes.top());
3773 group->addChild(std::unique_ptr<QSvgNode>(node), someId(attributes));
3777 case QSvgNode::Text:
3778 case QSvgNode::Textarea:
3779 if (localName == QLatin1String(
"tspan")) {
3780 node = method(m_nodes.top(), attributes,
this);
3782 static_cast<QSvgText *>(m_nodes.top())->addTspan(
static_cast<QSvgTspan *>(node));
3785 const QByteArray msg = QByteArrayLiteral(
"\'text\' or \'textArea\' element contains invalid element type.");
3786 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3790 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
3791 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3796 parseCoreNode(node, attributes);
3797 parseStyle(node, attributes,
this);
3798 if (node->type() == QSvgNode::Text || node->type() == QSvgNode::Textarea) {
3799 static_cast<QSvgText *>(node)->setWhitespaceMode(m_whitespaceMode.top());
3800 }
else if (node->type() == QSvgNode::Tspan) {
3801 static_cast<QSvgTspan *>(node)->setWhitespaceMode(m_whitespaceMode.top());
3802 }
else if (node->type() == QSvgNode::Use) {
3803 auto useNode =
static_cast<QSvgUse *>(node);
3804 if (!useNode->isResolved())
3805 m_toBeResolved.append(useNode);
3808 }
else if (FactoryMethod method = findFilterFactory(localName, options())) {
3810 Q_ASSERT(!m_nodes.isEmpty());
3811 if (m_nodes.top()->type() == QSvgNode::Filter ||
3812 (m_nodes.top()->type() == QSvgNode::FeMerge && localName == QLatin1String(
"feMergeNode"))) {
3813 node = method(m_nodes.top(), attributes,
this);
3815 QSvgStructureNode *container =
3816 static_cast<QSvgStructureNode*>(m_nodes.top());
3817 container->addChild(std::unique_ptr<QSvgNode>(node), someId(attributes));
3820 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
3821 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3823 }
else if (AnimationMethod method = findAnimationFactory(localName, options())) {
3824 Q_ASSERT(!m_nodes.isEmpty());
3825 node = method(m_nodes.top(), attributes,
this);
3827 QSvgAnimateNode *anim =
static_cast<QSvgAnimateNode *>(node);
3828 if (anim->linkId().isEmpty())
3829 m_doc->animator()->appendAnimation(m_nodes.top(), anim);
3830 else if (m_doc->namedNode(anim->linkId()))
3831 m_doc->animator()->appendAnimation(m_doc->namedNode(anim->linkId()), anim);
3833 m_toBeResolved.append(anim);
3835 }
else if (ParseMethod method = findUtilFactory(localName, options())) {
3836 Q_ASSERT(!m_nodes.isEmpty());
3837 if (!method(m_nodes.top(), attributes,
this))
3838 qCWarning(lcSvgHandler,
"%s", msgProblemParsing(localName, xml).constData());
3839 }
else if (StyleFactoryMethod method = findStyleFactoryMethod(localName)) {
3840 QSvgStyleProperty *prop = method(m_nodes.top(), attributes,
this);
3843 m_nodes.top()->appendStyleProperty(prop, someId(attributes));
3845 const QByteArray msg = QByteArrayLiteral(
"Could not parse node: ") + localName.toLocal8Bit();
3846 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3848 }
else if (StyleParseMethod method = findStyleUtilFactoryMethod(localName)) {
3850 if (!method(m_style, attributes,
this))
3851 qCWarning(lcSvgHandler,
"%s", msgProblemParsing(localName, xml).constData());
3854 qCDebug(lcSvgHandler) <<
"Skipping unknown element" << localName;
3855 m_skipNodes.push(Unknown);
3861 m_skipNodes.push(Graphics);
3864 m_skipNodes.push(Style);
3869bool QSvgHandler::endElement(
const QStringView localName)
3871 CurrentNode node = m_skipNodes.top();
3873 if (node == Doc && localName != QLatin1String(
"svg"))
3877 m_whitespaceMode.pop();
3881 if (node == Unknown)
3884#ifdef QT_NO_CSSPARSER
3885 Q_UNUSED(localName);
3887 if (m_inStyle && localName == QLatin1String(
"style"))
3891 if (node == Graphics)
3893 else if (m_style && !m_skipNodes.isEmpty() && m_skipNodes.top() != Style)
3896 return ((localName == QLatin1String(
"svg")) && (node != Doc));
3899void QSvgHandler::resolvePaintServers(QSvgNode *node,
int nestedDepth)
3901 if (!node || (node->type() != QSvgNode::Doc && node->type() != QSvgNode::Group
3902 && node->type() != QSvgNode::Defs && node->type() != QSvgNode::Switch)) {
3906 QSvgStructureNode *structureNode =
static_cast<QSvgStructureNode *>(node);
3908 for (
auto &node : structureNode->renderers()) {
3909 QSvgFillStyle *fill =
static_cast<QSvgFillStyle *>(node->styleProperty(QSvgStyleProperty::FILL));
3910 if (fill && !fill->isPaintStyleResolved()) {
3911 QString id = fill->paintStyleId();
3912 QSvgPaintStyleProperty *style = structureNode->styleProperty(id);
3914 fill->setFillStyle(style);
3916 qCWarning(lcSvgHandler,
"%s", msgCouldNotResolveProperty(id, xml).constData());
3917 fill->setBrush(Qt::NoBrush);
3921 QSvgStrokeStyle *stroke =
static_cast<QSvgStrokeStyle *>(node->styleProperty(QSvgStyleProperty::STROKE));
3922 if (stroke && !stroke->isPaintStyleResolved()) {
3923 QString id = stroke->paintStyleId();
3924 QSvgPaintStyleProperty *style = structureNode->styleProperty(id);
3926 stroke->setStyle(style);
3928 qCWarning(lcSvgHandler,
"%s", msgCouldNotResolveProperty(id, xml).constData());
3929 stroke->setStroke(Qt::NoBrush);
3933 if (nestedDepth < 2048)
3934 resolvePaintServers(node.get(), nestedDepth + 1);
3938void QSvgHandler::resolveNodes()
3940 for (QSvgNode *node : std::as_const(m_toBeResolved)) {
3941 if (node->type() == QSvgNode::Use) {
3942 QSvgUse *useNode =
static_cast<QSvgUse *>(node);
3943 const auto parent = useNode->parent();
3947 QSvgNode::Type t = parent->type();
3948 if (t != QSvgNode::Doc && t != QSvgNode::Defs && t != QSvgNode::Group && t != QSvgNode::Switch)
3951 QSvgStructureNode *group =
static_cast<QSvgStructureNode *>(parent);
3952 QSvgNode *link = group->scopeNode(useNode->linkId());
3954 qCWarning(lcSvgHandler,
"link #%s is undefined!",
qPrintable(useNode->linkId()));
3958 if (useNode->parent()->isDescendantOf(link))
3959 qCWarning(lcSvgHandler,
"link #%s is recursive!",
qPrintable(useNode->linkId()));
3961 useNode->setLink(link);
3962 }
else if (node->type() == QSvgNode::Filter) {
3963 QSvgFilterContainer *filter =
static_cast<QSvgFilterContainer *>(node);
3964 for (
auto &renderer : filter->renderers()) {
3965 const QSvgFeFilterPrimitive *primitive = QSvgFeFilterPrimitive::castToFilterPrimitive(renderer.get());
3966 if (!primitive || primitive->type() == QSvgNode::FeUnsupported) {
3967 filter->setSupported(
false);
3971 }
else if (node->type() == QSvgNode::AnimateTransform || node->type() == QSvgNode::AnimateColor) {
3972 QSvgAnimateNode *anim =
static_cast<QSvgAnimateNode *>(node);
3973 QSvgNode *targetNode = m_doc->namedNode(anim->linkId());
3975 m_doc->animator()->appendAnimation(targetNode, anim);
3977 qCWarning(lcSvgHandler,
"Cannot find target for link #%s!",
3983 m_toBeResolved.clear();
3986bool QSvgHandler::characters(
const QStringView str)
3988#ifndef QT_NO_CSSPARSER
3990 m_cssHandler.parseStyleSheet(str);
3994 if (m_skipNodes.isEmpty() || m_skipNodes.top() == Unknown || m_nodes.isEmpty())
3997 if (m_nodes.top()->type() == QSvgNode::Text || m_nodes.top()->type() == QSvgNode::Textarea) {
3998 static_cast<QSvgText*>(m_nodes.top())->addText(str);
3999 }
else if (m_nodes.top()->type() == QSvgNode::Tspan) {
4000 static_cast<QSvgTspan*>(m_nodes.top())->addText(str);
4006QIODevice *QSvgHandler::device()
const
4008 return xml->device();
4011QSvgDocument *QSvgHandler::document()
const
4016QSvgUtils::LengthType QSvgHandler::defaultCoordinateSystem()
const
4018 return m_defaultCoords;
4021void QSvgHandler::setDefaultCoordinateSystem(QSvgUtils::LengthType type)
4023 m_defaultCoords = type;
4026void QSvgHandler::pushColor(
const QColor &color)
4028 m_colorStack.push(color);
4029 m_colorTagCount.push(1);
4032void QSvgHandler::pushColorCopy()
4034 if (m_colorTagCount.size())
4035 ++m_colorTagCount.top();
4037 pushColor(Qt::black);
4040void QSvgHandler::popColor()
4042 if (m_colorTagCount.size()) {
4043 if (!--m_colorTagCount.top()) {
4045 m_colorTagCount.pop();
4050QColor QSvgHandler::currentColor()
const
4052 if (!m_colorStack.isEmpty())
4053 return m_colorStack.top();
4055 return QColor(0, 0, 0);
4058#ifndef QT_NO_CSSPARSER
4060void QSvgHandler::setInStyle(
bool b)
4065bool QSvgHandler::inStyle()
const
4070QSvgCssHandler &QSvgHandler::cssHandler()
4072 return m_cssHandler;
4077bool QSvgHandler::processingInstruction(
const QStringView target,
const QStringView data)
4079#ifdef QT_NO_CSSPARSER
4083 if (target == QLatin1String(
"xml-stylesheet")) {
4084 static const QRegularExpression rx(
QStringLiteral(
"type=\\\"(.+)\\\""),
4085 QRegularExpression::InvertedGreedinessOption);
4086 QRegularExpressionMatchIterator iter = rx.globalMatchView(data);
4088 while (iter.hasNext()) {
4089 QRegularExpressionMatch match = iter.next();
4090 QString type = match.captured(1);
4091 if (type.toLower() == QLatin1String(
"text/css")) {
4097 static const QRegularExpression rx(
QStringLiteral(
"href=\\\"(.+)\\\""),
4098 QRegularExpression::InvertedGreedinessOption);
4099 QRegularExpressionMatch match = rx.matchView(data);
4100 QString addr = match.captured(1);
4104 QFile file(fi.absoluteFilePath());
4105 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
4108 QByteArray cssData = file.readAll();
4109 QString css = QString::fromUtf8(cssData);
4110 m_cssHandler.parseStyleSheet(css);
4120void QSvgHandler::setAnimPeriod(
int start,
int end)
4123 m_animEnd = qMax(end, m_animEnd);
4126int QSvgHandler::animationDuration()
const
4131QSvgHandler::~QSvgHandler()
The QPolygonF class provides a list of points using floating point precision.
void parseNumbersArray(const QChar *&str, QVarLengthArray< qreal, 8 > &points, const char *pattern)
qreal toDouble(QStringView str, bool *ok)
Combined button and popup list for selecting options.
Q_STATIC_ASSERT(sizeof(SharedImageHeader) % 4==0)
#define qPrintable(string)
#define QStringLiteral(str)
#define qUtf16Printable(string)
static QSvgNode * createTspanNode(QSvgNode *parent, const QXmlStreamAttributes &, QSvgHandler *)
static bool parseStyle(QSvgNode *node, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static void parseVisibility(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static bool detectPatternCycles(const QSvgNode *node, QList< const QSvgNode * > active={})
static void parseOffsetPath(QSvgNode *node, const QXmlStreamAttributes &attributes)
static QSvgNode * createGNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
bool(* StyleParseMethod)(QSvgStyleProperty *, const QXmlStreamAttributes &, QSvgHandler *)
static QSvgNode::DisplayMode displayStringToEnum(const QStringView str)
static bool parseStyleNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static std::optional< QStringView > getAttributeId(const QStringView &attribute)
static QSvgStyleProperty * styleFromUrl(QSvgNode *node, QStringView url)
static const qreal sizeTable[]
static QSvgNode * createImageNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createPolygonNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseOthers(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static QSvgNode * createMaskNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QByteArray msgProblemParsing(QStringView localName, const QXmlStreamReader *r)
static void parseRenderingHints(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static void parseExtendedAttributes(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *handler)
static bool parseMpathNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QByteArray prefixMessage(const QByteArray &msg, const QXmlStreamReader *r)
static QSvgNode * createPathNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createDefsNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgStyleProperty * createFontNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static int parseClockValue(QStringView str, bool *ok)
static bool parseSymbolLikeAttributes(const QXmlStreamAttributes &attributes, QSvgHandler *handler, QRectF *rect, QRectF *viewBox, QPointF *refPoint, QSvgSymbolLike::PreserveAspectRatios *aspect, QSvgSymbolLike::Overflow *overflow, bool marker=false)
static qreal convertToNumber(QStringView str, bool *ok=NULL)
static bool parseBaseAnimate(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgAnimateNode *anim, QSvgHandler *handler)
static bool parseFontFaceNameNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseFilterAttributes(const QXmlStreamAttributes &attributes, QString *inString, QString *outString, QSvgRectF *rect)
static QSvgNode * createPolyNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, bool createLine)
static void parseTransform(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static QSvgNode * createFeMergeNodeNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createAnimateColorNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QStringView idFromUrl(QStringView url)
void setAlpha(QStringView opacity, QColor *color)
static void parseColor(QSvgNode *, const QSvgAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createCircleNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
bool qsvg_get_hex_rgb(const QChar *str, int len, QRgb *rgb)
static std::optional< QRectF > parseViewBox(QStringView str)
static QSvgStyleProperty * createLinearGradientNode(QSvgNode *node, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static const int unfinishedElementsLimit
static QSvgNode * createLineNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static FactoryMethod findFilterFactory(const QStringView name, QtSvg::Options options)
static ParseMethod findUtilFactory(const QStringView name, QtSvg::Options options)
static FontSizeSpec fontSizeSpec(QStringView spec)
static QSvgStyleProperty * createSolidColorNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
bool qsvg_get_hex_rgb(const char *name, QRgb *rgb)
static QSvgNode * createTextNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseBaseGradient(QSvgNode *node, const QXmlStreamAttributes &attributes, QSvgGradientStyle *gradProp, QSvgHandler *handler)
static bool parseAudioNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static StyleParseMethod findStyleUtilFactoryMethod(const QStringView name)
static bool parseMarkerNode(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *)
static bool resolveColor(QStringView colorStr, QColor &color, QSvgHandler *handler)
static QList< qreal > parsePercentageList(const QChar *&str)
static FactoryMethod findGroupFactory(const QStringView name, QtSvg::Options options)
static bool parseMetadataNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QTransform parseTransformationMatrix(QStringView value)
static QSvgNode * createSwitchNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseDiscardNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool createSvgGlyph(QSvgFont *font, const QXmlStreamAttributes &attributes, bool isMissingGlyph)
static void parseOpacity(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static bool parseScriptNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
QSvgStyleProperty *(* StyleFactoryMethod)(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *)
static QSvgNode * createPatternNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static bool parseHandlerNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static AnimationMethod findAnimationFactory(const QStringView name, QtSvg::Options options)
static QList< qreal > parseNumbersList(const QChar *&str)
static bool parseCoreNode(QSvgNode *node, const QXmlStreamAttributes &attributes)
static bool constructColor(QStringView colorStr, QStringView opacity, QColor &color, QSvgHandler *handler)
static bool parseHkernNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void generateKeyFrames(QList< qreal > &keyFrames, uint count)
static void parsePen(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createSvgNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static void parseFont(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static bool parseForeignObjectNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseTbreakNode(QSvgNode *parent, const QXmlStreamAttributes &, QSvgHandler *)
static QSvgNode * createFeUnsupportedNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createAnimateTransformNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QList< QStringView > splitWithDelimiter(QStringView delimitedList)
static QSvgNode * createFeOffsetNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createFeFloodNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createMarkerNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createFeColorMatrixNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createFeGaussianBlurNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createPolylineNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
bool(* ParseMethod)(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *)
static bool parseFontFaceNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseMissingGlyphNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parsePrefetchNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseCssAnimations(QSvgNode *node, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static int qsvg_hex2int(const char *s, bool *ok=nullptr)
static bool parseGlyphNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static int qsvg_h2i(char hex, bool *ok=nullptr)
static int qsvg_hex2int(char s, bool *ok=nullptr)
static QSvgNode * createVideoNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseCompOp(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static bool parseMaskNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool detectCyclesAndWarn(const QSvgNode *node)
static QSvgNode * createUseNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseFontFaceSrcNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseAnchorNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseFontFaceUriNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool detectCycles(const QSvgNode *node, QList< const QSvgNode * > active={})
static QStringList stringToList(const QString &str)
static QSvgNode * createRectNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createAnimateNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createSymbolNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static StyleFactoryMethod findStyleFactoryMethod(const QStringView name)
static QPainter::CompositionMode svgToQtCompositionMode(const QStringView op)
static QByteArray msgCouldNotResolveProperty(QStringView id, const QXmlStreamReader *r)
static void parseFilterBounds(const QXmlStreamAttributes &attributes, QSvgRectF *rect)
static bool parseStopNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static void parseBrush(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *handler)
static FactoryMethod findGraphicsFactory(const QStringView name, QtSvg::Options options)
static QSvgNode * createFeMergeNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createFeCompositeNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseSetNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseNumberTriplet(QList< qreal > &values, const QChar *&s)
static QSvgNode * createFeBlendNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createFilterNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createEllipseNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createTextAreaNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createAimateMotionNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgStyleProperty * createRadialGradientNode(QSvgNode *node, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QString someId(const QXmlStreamAttributes &attributes)
QStringView strokeDashOffset
QStringView strokeDashArray
QStringView strokeOpacity
QStringView strokeLineJoin
void setAttributes(const QXmlStreamAttributes &attributes, QSvgHandler *handler)
QSvgAttributes(const QXmlStreamAttributes &xmlAttributes, QSvgHandler *handler)
QStringView strokeLineCap
QStringView strokeMiterLimit
QStringView imageRendering