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->isEmpty() && str->first().isSpace())
347 while (!str->isEmpty()
349 || str->startsWith(QLatin1Char(
'+')) || str->startsWith(QLatin1Char(
'.')))) {
351 points.append(QSvgUtils::toDouble(str));
353 while (!str->isEmpty() && str->first().isSpace())
355 if (str->startsWith(QLatin1Char(
',')))
359 while (!str->isEmpty() && str->first().isSpace())
370 while (!str.isEmpty() && str.first().isSpace())
372 while ((!str.isEmpty() && str.first() >= QLatin1Char(
'0') && str.first() <= QLatin1Char(
'9'))
373 || str.startsWith(QLatin1Char(
'-')) || str.startsWith(QLatin1Char(
'+'))
374 || str.startsWith(QLatin1Char(
'.'))) {
376 points.append(QSvgUtils::toDouble(&str));
378 while (!str.isEmpty() && str.first().isSpace())
380 if (str.startsWith(QLatin1Char(
'%')))
382 while (!str.isEmpty() && str.first().isSpace())
384 if (str.startsWith(QLatin1Char(
',')))
388 while (!str.isEmpty() && str.first().isSpace())
396
397
398
399
404 if (!iri.startsWith(QLatin1Char(
'#')))
405 return QStringView();
407 return iri.sliced(1);
411
412
413
414
415
420 if (!iri.startsWith(QLatin1StringView(
"url(")))
421 return QStringView();
425 const qsizetype closingBracePos = iri.indexOf(QLatin1Char(
')'));
426 if (closingBracePos == -1)
427 return QStringView();
429 iri = iri.first(closingBracePos);
430 return idFromIRI(iri);
434
435
436
437static bool resolveColor(QStringView colorStr, QColor &color, QSvgHandler *handler)
439 QStringView colorStrTr = colorStr.trimmed();
440 if (colorStrTr.isEmpty())
443 switch(colorStrTr.at(0).unicode()) {
450 bool ok = qsvg_get_hex_rgb(colorStrTr.constData(), colorStrTr.size(), &rgb);
460 if (colorStrTr.size() >= 7 && colorStrTr.at(colorStrTr.size() - 1) == QLatin1Char(
')')
461 && colorStrTr.mid(0, 4) == QLatin1String(
"rgb(")) {
462 QStringView sv{ colorStrTr.sliced(4) };
463 QList<qreal> compo = parseNumbersList(&sv);
466 if (compo.size() == 1) {
467 compo = parsePercentageList(colorStrTr.sliced(4));
468 for (
int i = 0; i < compo.size(); ++i)
469 compo[i] *= (qreal)2.55;
472 if (compo.size() == 3) {
473 color = QColor(
int(compo[0]),
484 if (colorStrTr == QLatin1String(
"currentColor")) {
485 color = handler->currentColor();
497 color = QColor::fromString(colorStrTr);
498 return color.isValid();
504 qreal op = qBound(qreal(0.0), QSvgUtils::toDouble(opacity, &ok), qreal(1.0));
507 color->setAlphaF(op);
511 QColor &color, QSvgHandler *handler)
513 if (!resolveColor(colorStr, color, handler))
515 if (!opacity.isEmpty())
516 setAlpha(opacity, &color);
523 qreal num = QSvgUtils::parseLength(str.toString(), &type, ok);
530static bool createSvgGlyph(QSvgFont *font,
const QXmlStreamAttributes &attributes,
533 QStringView uncStr = attributes.value(QLatin1String(
"unicode"));
534 QStringView havStr = attributes.value(QLatin1String(
"horiz-adv-x"));
535 QStringView pathStr = attributes.value(QLatin1String(
"d"));
537 qreal havx = (havStr.isEmpty()) ? -1 : QSvgUtils::toDouble(havStr);
538 QPainterPath path = QSvgUtils::parsePathDataFast(pathStr).value_or(QPainterPath());
540 path.setFillRule(Qt::WindingFill);
542 if (isMissingGlyph) {
543 if (!uncStr.isEmpty())
544 qWarning(
"Ignoring missing-glyph's 'unicode' attribute");
545 return font->addMissingGlyph(path, havx);
548 if (uncStr.isEmpty()) {
549 qWarning(
"glyph does not define a non-empty 'unicode' attribute and will be ignored");
552 font->addGlyph(uncStr.toString(), path, havx);
558 QSvgHandler *handler)
561 if (constructColor(attributes.color, attributes.colorOpacity, color, handler)) {
563 handler->pushColor(color);
569 QStringView id = idFromFuncIRI(url);
570 return node ? node->document()->namedStyle(id.toString()) :
nullptr;
575 QSvgHandler *handler)
577 if (!attributes.fill.isEmpty() || !attributes.fillRule.isEmpty() || !attributes.fillOpacity.isEmpty()) {
578 QSvgFillStyle *prop =
new QSvgFillStyle;
581 if (!attributes.fillRule.isEmpty() && attributes.fillRule !=
QT_INHERIT) {
582 if (attributes.fillRule == QLatin1String(
"evenodd"))
583 prop->setFillRule(Qt::OddEvenFill);
584 else if (attributes.fillRule == QLatin1String(
"nonzero"))
585 prop->setFillRule(Qt::WindingFill);
589 if (!attributes.fillOpacity.isEmpty() && attributes.fillOpacity !=
QT_INHERIT) {
590 prop->setFillOpacity(qMin(qreal(1.0), qMax(qreal(0.0), QSvgUtils::toDouble(attributes.fillOpacity))));
594 if ((!attributes.fill.isEmpty()) && (attributes.fill !=
QT_INHERIT) ) {
595 if (attributes.fill.startsWith(QLatin1String(
"url"))) {
596 QStringView value = attributes.fill;
597 QSvgStyleProperty *style = styleFromUrl(node, value);
599 if (style->type() == QSvgStyleProperty::SOLID_COLOR || style->type() == QSvgStyleProperty::GRADIENT
600 || style->type() == QSvgStyleProperty::PATTERN)
601 prop->setFillStyle(
reinterpret_cast<QSvgPaintStyleProperty *>(style));
603 QString id = idFromFuncIRI(value).toString();
604 prop->setPaintStyleId(id);
605 prop->setPaintStyleResolved(
false);
607 }
else if (attributes.fill != QLatin1String(
"none")) {
609 if (resolveColor(attributes.fill, color, handler))
610 prop->setBrush(QBrush(color));
612 prop->setBrush(QBrush(Qt::NoBrush));
615 node->appendStyleProperty(prop, attributes.id);
628 while (!value.isEmpty()) {
629 if (value.first().isSpace() || value.startsWith(QLatin1Char(
','))) {
641 State state = Matrix;
642 if (value.startsWith(QLatin1Char(
'm'))) {
643 const char *ident =
"atrix";
644 for (
int i = 0; i < 5; ++i)
645 if (!value.slice(1).startsWith(QLatin1Char(ident[i])))
649 }
else if (value.startsWith(QLatin1Char(
't'))) {
650 const char *ident =
"ranslate";
651 for (
int i = 0; i < 8; ++i)
652 if (!value.slice(1).startsWith(QLatin1Char(ident[i])))
656 }
else if (value.startsWith(QLatin1Char(
'r'))) {
657 const char *ident =
"otate";
658 for (
int i = 0; i < 5; ++i)
659 if (!value.slice(1).startsWith(QLatin1Char(ident[i])))
663 }
else if (value.startsWith(QLatin1Char(
's'))) {
665 if (value.startsWith(QLatin1Char(
'c'))) {
666 const char *ident =
"ale";
667 for (
int i = 0; i < 3; ++i)
668 if (!value.slice(1).startsWith(QLatin1Char(ident[i])))
672 }
else if (value.startsWith(QLatin1Char(
'k'))) {
673 if (!value.slice(1).startsWith(QLatin1Char(
'e')))
675 if (!value.slice(1).startsWith(QLatin1Char(
'w')))
678 if (value.startsWith(QLatin1Char(
'X')))
680 else if (value.startsWith(QLatin1Char(
'Y')))
692 while (!value.isEmpty() && value.first().isSpace())
694 if (!value.startsWith(QLatin1Char(
'(')))
697 QVarLengthArray<qreal, 8> points;
699 if (!value.startsWith(QLatin1Char(
')')))
703 if(state == Matrix) {
704 if(points.size() != 6)
706 matrix = QTransform(points[0], points[1],
707 points[2], points[3],
708 points[4], points[5]) * matrix;
709 }
else if (state == Translate) {
710 if (points.size() == 1)
711 matrix.translate(points[0], 0);
712 else if (points.size() == 2)
713 matrix.translate(points[0], points[1]);
716 }
else if (state == Rotate) {
717 if(points.size() == 1) {
718 matrix.rotate(points[0]);
719 }
else if (points.size() == 3) {
720 matrix.translate(points[1], points[2]);
721 matrix.rotate(points[0]);
722 matrix.translate(-points[1], -points[2]);
726 }
else if (state == Scale) {
727 if (points.size() < 1 || points.size() > 2)
729 qreal sx = points[0];
731 if(points.size() == 2)
733 matrix.scale(sx, sy);
734 }
else if (state == SkewX) {
735 if (points.size() != 1)
737 matrix.shear(qTan(qDegreesToRadians(points[0])), 0);
738 }
else if (state == SkewY) {
739 if (points.size() != 1)
741 matrix.shear(0, qTan(qDegreesToRadians(points[0])));
750 QSvgHandler *handler)
752 if (!attributes.stroke.isEmpty() || !attributes.strokeDashArray.isEmpty() || !attributes.strokeDashOffset.isEmpty() || !attributes.strokeLineCap.isEmpty()
753 || !attributes.strokeLineJoin.isEmpty() || !attributes.strokeMiterLimit.isEmpty() || !attributes.strokeOpacity.isEmpty() || !attributes.strokeWidth.isEmpty()
754 || !attributes.vectorEffect.isEmpty()) {
756 QSvgStrokeStyle *prop =
new QSvgStrokeStyle;
759 if ((!attributes.stroke.isEmpty()) && (attributes.stroke !=
QT_INHERIT) ) {
760 if (attributes.stroke.startsWith(QLatin1String(
"url"))) {
761 QStringView value = attributes.stroke;
762 QSvgStyleProperty *style = styleFromUrl(node, value);
764 if (style->type() == QSvgStyleProperty::SOLID_COLOR || style->type() == QSvgStyleProperty::GRADIENT
765 || style->type() == QSvgStyleProperty::PATTERN)
766 prop->setStyle(
reinterpret_cast<QSvgPaintStyleProperty *>(style));
768 QString id = idFromFuncIRI(value).toString();
769 prop->setPaintStyleId(id);
770 prop->setPaintStyleResolved(
false);
772 }
else if (attributes.stroke != QLatin1String(
"none")) {
774 if (resolveColor(attributes.stroke, color, handler))
775 prop->setStroke(QBrush(color));
777 prop->setStroke(QBrush(Qt::NoBrush));
782 if (!attributes.strokeWidth.isEmpty() && attributes.strokeWidth !=
QT_INHERIT) {
784 prop->setWidth(QSvgUtils::parseLength(attributes.strokeWidth, <));
788 if (!attributes.strokeDashArray.isEmpty() && attributes.strokeDashArray !=
QT_INHERIT) {
789 if (attributes.strokeDashArray == QLatin1String(
"none")) {
790 prop->setDashArrayNone();
792 QStringView dashArray = attributes.strokeDashArray;
793 QList<qreal> dashes = parseNumbersList(&dashArray);
794 const bool allZeroes =
std::all_of(dashes.cbegin(), dashes.cend(),
795 [](qreal i) {
return qFuzzyIsNull(i); });
796 const bool hasNegative = !allZeroes &&
std::any_of(dashes.cbegin(), dashes.cend(),
797 [](qreal i) {
return i < 0.; });
800 qCWarning(lcSvgHandler) <<
"QSvgHandler: Stroke dash array "
801 "with a negative value is invalid";
804 if (allZeroes || hasNegative) {
805 prop->setDashArrayNone();
808 if ((dashes.size() & 1) != 0)
809 dashes << QList<qreal>(dashes);
810 prop->setDashArray(dashes);
816 if (!attributes.strokeLineJoin.isEmpty()) {
817 if (attributes.strokeLineJoin == QLatin1String(
"miter"))
818 prop->setLineJoin(Qt::SvgMiterJoin);
819 else if (attributes.strokeLineJoin == QLatin1String(
"round"))
820 prop->setLineJoin(Qt::RoundJoin);
821 else if (attributes.strokeLineJoin == QLatin1String(
"bevel"))
822 prop->setLineJoin(Qt::BevelJoin);
826 if (!attributes.strokeLineCap.isEmpty()) {
827 if (attributes.strokeLineCap == QLatin1String(
"butt"))
828 prop->setLineCap(Qt::FlatCap);
829 else if (attributes.strokeLineCap == QLatin1String(
"round"))
830 prop->setLineCap(Qt::RoundCap);
831 else if (attributes.strokeLineCap == QLatin1String(
"square"))
832 prop->setLineCap(Qt::SquareCap);
836 if (!attributes.strokeDashOffset.isEmpty() && attributes.strokeDashOffset !=
QT_INHERIT)
837 prop->setDashOffset(QSvgUtils::toDouble(attributes.strokeDashOffset));
840 if (!attributes.vectorEffect.isEmpty()) {
841 if (attributes.vectorEffect == QLatin1String(
"non-scaling-stroke"))
842 prop->setVectorEffect(
true);
843 else if (attributes.vectorEffect == QLatin1String(
"none"))
844 prop->setVectorEffect(
false);
848 if (!attributes.strokeMiterLimit.isEmpty() && attributes.strokeMiterLimit !=
QT_INHERIT)
849 prop->setMiterLimit(QSvgUtils::toDouble(attributes.strokeMiterLimit));
852 if (!attributes.strokeOpacity.isEmpty() && attributes.strokeOpacity !=
QT_INHERIT)
853 prop->setOpacity(qMin(qreal(1.0), qMax(qreal(0.0), QSvgUtils::toDouble(attributes.strokeOpacity))));
855 node->appendStyleProperty(prop, attributes.id);
863{ qreal(6.9), qreal(8.3), qreal(10.0), qreal(12.0), qreal(14.4), qreal(17.3), qreal(20.7) };
869 switch (spec.at(0).unicode()) {
871 if (spec == QLatin1String(
"xx-small"))
873 if (spec == QLatin1String(
"x-small"))
875 if (spec == QLatin1String(
"x-large"))
877 if (spec == QLatin1String(
"xx-large"))
881 if (spec == QLatin1String(
"small"))
885 if (spec == QLatin1String(
"medium"))
889 if (spec == QLatin1String(
"large"))
893 if (spec == QLatin1String(
"none"))
906 if (attributes.fontFamily.isEmpty() && attributes.fontSize.isEmpty() && attributes.fontStyle.isEmpty() &&
907 attributes.fontWeight.isEmpty() && attributes.fontVariant.isEmpty() && attributes.textAnchor.isEmpty())
910 QSvgFontStyle *fontStyle =
nullptr;
911 if (!attributes.fontFamily.isEmpty()) {
912 QSvgDocument *doc = node->document();
914 QSvgFont *svgFont = doc->svgFont(attributes.fontFamily.toString());
916 fontStyle =
new QSvgFontStyle(svgFont, doc);
920 fontStyle =
new QSvgFontStyle;
921 if (!attributes.fontFamily.isEmpty() && attributes.fontFamily !=
QT_INHERIT) {
922 QStringView family = attributes.fontFamily.trimmed();
923 if (!family.isEmpty() && (family.at(0) == QLatin1Char(
'\'') || family.at(0) == QLatin1Char(
'\"')))
924 family = family.mid(1, family.size() - 2);
925 fontStyle->setFamily(family.toString());
928 if (!attributes.fontSize.isEmpty() && attributes.fontSize !=
QT_INHERIT) {
930 const FontSizeSpec spec = fontSizeSpec(attributes.fontSize);
936 qreal fs = QSvgUtils::parseLength(attributes.fontSize, &type);
937 fs = QSvgUtils::convertToPixels(fs,
true, type);
938 fontStyle->setSize(qMin(fs, qreal(0xffff)));
942 fontStyle->setSize(sizeTable[spec]);
947 if (!attributes.fontStyle.isEmpty() && attributes.fontStyle !=
QT_INHERIT) {
948 if (attributes.fontStyle == QLatin1String(
"normal")) {
949 fontStyle->setStyle(QFont::StyleNormal);
950 }
else if (attributes.fontStyle == QLatin1String(
"italic")) {
951 fontStyle->setStyle(QFont::StyleItalic);
952 }
else if (attributes.fontStyle == QLatin1String(
"oblique")) {
953 fontStyle->setStyle(QFont::StyleOblique);
957 if (!attributes.fontWeight.isEmpty() && attributes.fontWeight !=
QT_INHERIT) {
959 const int weightNum = attributes.fontWeight.toInt(&ok);
961 fontStyle->setWeight(weightNum);
963 if (attributes.fontWeight == QLatin1String(
"normal")) {
964 fontStyle->setWeight(QFont::Normal);
965 }
else if (attributes.fontWeight == QLatin1String(
"bold")) {
966 fontStyle->setWeight(QFont::Bold);
967 }
else if (attributes.fontWeight == QLatin1String(
"bolder")) {
968 fontStyle->setWeight(QSvgFontStyle::BOLDER);
969 }
else if (attributes.fontWeight == QLatin1String(
"lighter")) {
970 fontStyle->setWeight(QSvgFontStyle::LIGHTER);
975 if (!attributes.fontVariant.isEmpty() && attributes.fontVariant !=
QT_INHERIT) {
976 if (attributes.fontVariant == QLatin1String(
"normal"))
977 fontStyle->setVariant(QFont::MixedCase);
978 else if (attributes.fontVariant == QLatin1String(
"small-caps"))
979 fontStyle->setVariant(QFont::SmallCaps);
982 if (!attributes.textAnchor.isEmpty() && attributes.textAnchor !=
QT_INHERIT) {
983 if (attributes.textAnchor == QLatin1String(
"start"))
984 fontStyle->setTextAnchor(Qt::AlignLeft);
985 if (attributes.textAnchor == QLatin1String(
"middle"))
986 fontStyle->setTextAnchor(Qt::AlignHCenter);
987 else if (attributes.textAnchor == QLatin1String(
"end"))
988 fontStyle->setTextAnchor(Qt::AlignRight);
991 node->appendStyleProperty(fontStyle, attributes.id);
998 if (attributes.transform.isEmpty())
1000 QTransform matrix = parseTransformationMatrix(attributes.transform.trimmed());
1002 if (!matrix.isIdentity()) {
1003 node->appendStyleProperty(
new QSvgTransformStyle(QTransform(matrix)), attributes.id);
1012 QSvgNode *parent = node->parent();
1014 if (parent && (attributes.visibility.isEmpty() || attributes.visibility ==
QT_INHERIT))
1015 node->setVisible(parent->isVisible());
1016 else if (attributes.visibility == QLatin1String(
"hidden") || attributes.visibility == QLatin1String(
"collapse")) {
1017 node->setVisible(
false);
1019 node->setVisible(
true);
1023 const QXmlStreamAttributes &attributes,
1024 QSvgHandler *handler);
1030 str = str.trimmed();
1031 if (str.endsWith(QLatin1String(
"ms"))) {
1034 }
else if (str.endsWith(QLatin1String(
"s"))) {
1037 double val = ms * QSvgUtils::toDouble(str, ok);
1039 if (val >
std::numeric_limits<
int>::min() && val <
std::numeric_limits<
int>::max())
1040 res =
static_cast<
int>(val);
1047#ifndef QT_NO_CSSPARSER
1050 const QXmlStreamAttributes &attributes,
1051 QSvgHandler *handler)
1053 QSvgCssProperties cssAnimProps(attributes);
1054 QList<QSvgAnimationProperty> parsedProperties = cssAnimProps.animations();
1056 for (
auto &property : parsedProperties) {
1057 QSvgCssAnimation *anim = handler->cssHandler().createAnimation(property.name);
1061 anim->setRunningTime(property.delay, property.duration);
1062 anim->setIterationCount(property.iteration);
1063 QSvgCssEasingPtr easing = handler->cssHandler().createEasing(property.easingFunction, property.easingValues);
1064 anim->setEasing(std::move(easing));
1066 handler->setAnimPeriod(property.delay, property.delay + property.duration);
1067 handler->document()->animator()->appendAnimation(node, anim);
1068 handler->document()->setAnimated(
true);
1073 const QXmlStreamAttributes &attributes)
1075 QSvgCssProperties cssProperties(attributes);
1076 QSvgOffsetProperty offset = cssProperties.offset();
1081 QSvgOffsetStyle *offsetStyle =
new QSvgOffsetStyle();
1082 offsetStyle->setPath(offset.path.value());
1083 offsetStyle->setRotateAngle(offset.angle);
1084 offsetStyle->setRotateType(offset.rotateType);
1085 offsetStyle->setDistance(offset.distance);
1086 node->appendStyleProperty(offsetStyle, QString());
1091QtSvg::Options QSvgHandler::options()
const
1096QtSvg::AnimatorType QSvgHandler::animatorType()
const
1098 return m_animatorType;
1101bool QSvgHandler::trustedSourceMode()
const
1103 return m_options.testFlag(QtSvg::AssumeTrustedSource);
1108 QStringList lst = str.split(QLatin1Char(
','), Qt::SkipEmptyParts);
1113 const QXmlStreamAttributes &attributes)
1115 QStringList features;
1116 QStringList extensions;
1117 QStringList languages;
1118 QStringList formats;
1120 QStringView xmlClassStr;
1122 for (
const QXmlStreamAttribute &attribute : attributes) {
1123 QStringView name = attribute.qualifiedName();
1126 QStringView value = attribute.value();
1127 switch (name.at(0).unicode()) {
1129 if (name == QLatin1String(
"class"))
1130 xmlClassStr = value;
1133 if (name == QLatin1String(
"requiredFeatures"))
1134 features = stringToList(value.toString());
1135 else if (name == QLatin1String(
"requiredExtensions"))
1136 extensions = stringToList(value.toString());
1137 else if (name == QLatin1String(
"requiredFormats"))
1138 formats = stringToList(value.toString());
1139 else if (name == QLatin1String(
"requiredFonts"))
1140 fonts = stringToList(value.toString());
1143 if (name == QLatin1String(
"systemLanguage"))
1144 languages = stringToList(value.toString());
1151 node->setRequiredFeatures(features);
1152 node->setRequiredExtensions(extensions);
1153 node->setRequiredLanguages(languages);
1154 node->setRequiredFormats(formats);
1155 node->setRequiredFonts(fonts);
1156 node->setNodeId(someId(attributes));
1157 node->setXmlClass(xmlClassStr.toString());
1166 if (attributes.opacity.isEmpty())
1169 const QStringView value = attributes.opacity.trimmed();
1172 qreal op = value.toDouble(&ok);
1175 QSvgOpacityStyle *opacity =
new QSvgOpacityStyle(qBound(qreal(0.0), op, qreal(1.0)));
1176 node->appendStyleProperty(opacity, attributes.id);
1182#define NOOP qDebug()<<"Operation: "<<op<<" is not implemented"
1183 if (op == QLatin1String(
"clear")) {
1184 return QPainter::CompositionMode_Clear;
1185 }
else if (op == QLatin1String(
"src")) {
1186 return QPainter::CompositionMode_Source;
1187 }
else if (op == QLatin1String(
"dst")) {
1188 return QPainter::CompositionMode_Destination;
1189 }
else if (op == QLatin1String(
"src-over")) {
1190 return QPainter::CompositionMode_SourceOver;
1191 }
else if (op == QLatin1String(
"dst-over")) {
1192 return QPainter::CompositionMode_DestinationOver;
1193 }
else if (op == QLatin1String(
"src-in")) {
1194 return QPainter::CompositionMode_SourceIn;
1195 }
else if (op == QLatin1String(
"dst-in")) {
1196 return QPainter::CompositionMode_DestinationIn;
1197 }
else if (op == QLatin1String(
"src-out")) {
1198 return QPainter::CompositionMode_SourceOut;
1199 }
else if (op == QLatin1String(
"dst-out")) {
1200 return QPainter::CompositionMode_DestinationOut;
1201 }
else if (op == QLatin1String(
"src-atop")) {
1202 return QPainter::CompositionMode_SourceAtop;
1203 }
else if (op == QLatin1String(
"dst-atop")) {
1204 return QPainter::CompositionMode_DestinationAtop;
1205 }
else if (op == QLatin1String(
"xor")) {
1206 return QPainter::CompositionMode_Xor;
1207 }
else if (op == QLatin1String(
"plus")) {
1208 return QPainter::CompositionMode_Plus;
1209 }
else if (op == QLatin1String(
"multiply")) {
1210 return QPainter::CompositionMode_Multiply;
1211 }
else if (op == QLatin1String(
"screen")) {
1212 return QPainter::CompositionMode_Screen;
1213 }
else if (op == QLatin1String(
"overlay")) {
1214 return QPainter::CompositionMode_Overlay;
1215 }
else if (op == QLatin1String(
"darken")) {
1216 return QPainter::CompositionMode_Darken;
1217 }
else if (op == QLatin1String(
"lighten")) {
1218 return QPainter::CompositionMode_Lighten;
1219 }
else if (op == QLatin1String(
"color-dodge")) {
1220 return QPainter::CompositionMode_ColorDodge;
1221 }
else if (op == QLatin1String(
"color-burn")) {
1222 return QPainter::CompositionMode_ColorBurn;
1223 }
else if (op == QLatin1String(
"hard-light")) {
1224 return QPainter::CompositionMode_HardLight;
1225 }
else if (op == QLatin1String(
"soft-light")) {
1226 return QPainter::CompositionMode_SoftLight;
1227 }
else if (op == QLatin1String(
"difference")) {
1228 return QPainter::CompositionMode_Difference;
1229 }
else if (op == QLatin1String(
"exclusion")) {
1230 return QPainter::CompositionMode_Exclusion;
1235 return QPainter::CompositionMode_SourceOver;
1242 if (attributes.compOp.isEmpty())
1244 QStringView value = attributes.compOp.trimmed();
1246 if (!value.isEmpty()) {
1247 QSvgCompOpStyle *compop =
new QSvgCompOpStyle(svgToQtCompositionMode(value));
1248 node->appendStyleProperty(compop, attributes.id);
1254 if (str == QLatin1String(
"inline")) {
1255 return QSvgNode::InlineMode;
1256 }
else if (str == QLatin1String(
"block")) {
1257 return QSvgNode::BlockMode;
1258 }
else if (str == QLatin1String(
"list-item")) {
1259 return QSvgNode::ListItemMode;
1260 }
else if (str == QLatin1String(
"run-in")) {
1261 return QSvgNode::RunInMode;
1262 }
else if (str == QLatin1String(
"compact")) {
1263 return QSvgNode::CompactMode;
1264 }
else if (str == QLatin1String(
"marker")) {
1265 return QSvgNode::MarkerMode;
1266 }
else if (str == QLatin1String(
"table")) {
1267 return QSvgNode::TableMode;
1268 }
else if (str == QLatin1String(
"inline-table")) {
1269 return QSvgNode::InlineTableMode;
1270 }
else if (str == QLatin1String(
"table-row-group")) {
1271 return QSvgNode::TableRowGroupMode;
1272 }
else if (str == QLatin1String(
"table-header-group")) {
1273 return QSvgNode::TableHeaderGroupMode;
1274 }
else if (str == QLatin1String(
"table-footer-group")) {
1275 return QSvgNode::TableFooterGroupMode;
1276 }
else if (str == QLatin1String(
"table-row")) {
1277 return QSvgNode::TableRowMode;
1278 }
else if (str == QLatin1String(
"table-column-group")) {
1279 return QSvgNode::TableColumnGroupMode;
1280 }
else if (str == QLatin1String(
"table-column")) {
1281 return QSvgNode::TableColumnMode;
1282 }
else if (str == QLatin1String(
"table-cell")) {
1283 return QSvgNode::TableCellMode;
1284 }
else if (str == QLatin1String(
"table-caption")) {
1285 return QSvgNode::TableCaptionMode;
1286 }
else if (str == QLatin1String(
"none")) {
1287 return QSvgNode::NoneMode;
1289 return QSvgNode::InheritMode;
1291 return QSvgNode::BlockMode;
1298 if (attributes.display.isEmpty())
1300 QStringView displayStr = attributes.display.trimmed();
1302 if (!displayStr.isEmpty()) {
1303 node->setDisplayMode(displayStringToEnum(displayStr));
1309 if (attribute.isEmpty())
1310 return std::nullopt;
1312 return idFromFuncIRI(attribute);
1317 QSvgHandler *handler)
1322 if (
auto id = getAttributeId(attributes.mask))
1323 node->setMaskId(id->toString());
1324 if (
auto id = getAttributeId(attributes.markerStart))
1325 node->setMarkerStartId(id->toString());
1326 if (
auto id = getAttributeId(attributes.markerMid))
1327 node->setMarkerMidId(id->toString());
1328 if (
auto id = getAttributeId(attributes.markerEnd))
1329 node->setMarkerEndId(id->toString());
1330 if (
auto id = getAttributeId(attributes.filter))
1331 node->setFilterId(id->toString());
1338 if (attributes.imageRendering.isEmpty())
1341 QStringView ir = attributes.imageRendering.trimmed();
1342 QSvgQualityStyle *p =
new QSvgQualityStyle(0);
1343 if (ir == QLatin1String(
"auto"))
1344 p->setImageRendering(QSvgQualityStyle::ImageRenderingAuto);
1345 else if (ir == QLatin1String(
"optimizeSpeed"))
1346 p->setImageRendering(QSvgQualityStyle::ImageRenderingOptimizeSpeed);
1347 else if (ir == QLatin1String(
"optimizeQuality"))
1348 p->setImageRendering(QSvgQualityStyle::ImageRenderingOptimizeQuality);
1349 node->appendStyleProperty(p, attributes.id);
1353 const QXmlStreamAttributes &attributes,
1354 QSvgHandler *handler)
1362#ifndef QT_NO_CSSPARSER
1363 QXmlStreamAttributes cssAttributes;
1364 handler->cssHandler().styleLookup(node, cssAttributes);
1366 QStringView style = attributes.value(QLatin1String(
"style"));
1367 if (!style.isEmpty())
1368 handler->cssHandler().parseCSStoXMLAttrs(style.toString(), cssAttributes);
1369 svgAttributes.setAttributes(cssAttributes, handler);
1371 parseOffsetPath(node, cssAttributes);
1373 parseCssAnimations(node, cssAttributes, handler);
1392 const QXmlStreamAttributes &attributes,
1395 Q_UNUSED(parent); Q_UNUSED(attributes);
1400 const QXmlStreamAttributes &attributes,
1401 QSvgAnimateNode *anim,
1402 QSvgHandler *handler)
1404 const QStringView beginStr = attributes.value(QLatin1String(
"begin"));
1405 const QStringView durStr = attributes.value(QLatin1String(
"dur"));
1406 const QStringView endStr = attributes.value(QLatin1String(
"end"));
1407 const QStringView repeatStr = attributes.value(QLatin1String(
"repeatCount"));
1408 const QStringView fillStr = attributes.value(QLatin1String(
"fill"));
1409 const QStringView addtv = attributes.value(QLatin1String(
"additive"));
1410 QStringView linkId = attributes.value(QLatin1String(
"xlink:href"));
1412 if (linkId.isEmpty())
1413 linkId = attributes.value(QLatin1String(
"href"));
1415 linkId = idFromIRI(linkId);
1418 int begin = parseClockValue(beginStr, &ok);
1421 int dur = parseClockValue(durStr, &ok);
1424 int end = parseClockValue(endStr, &ok);
1427 qreal repeatCount = (repeatStr == QLatin1String(
"indefinite")) ? -1 :
1428 qMax(1.0, QSvgUtils::toDouble(repeatStr));
1430 QSvgAnimateNode::Fill fill = (fillStr == QLatin1String(
"freeze")) ? QSvgAnimateNode::Freeze :
1431 QSvgAnimateNode::Remove;
1433 QSvgAnimateNode::Additive additive = (addtv == QLatin1String(
"sum")) ? QSvgAnimateNode::Sum :
1434 QSvgAnimateNode::Replace;
1436 anim->setRunningTime(begin, dur, end, 0);
1437 anim->setRepeatCount(repeatCount);
1438 anim->setFill(fill);
1439 anim->setAdditiveType(additive);
1440 anim->setLinkId(linkId.toString());
1442 parent->document()->setAnimated(
true);
1444 handler->setAnimPeriod(begin, begin + dur);
1453 qreal spacing = 1.0f / (count - 1);
1454 for (uint i = 0; i < count; i++) {
1455 keyFrames.append(i * spacing);
1460 const QXmlStreamAttributes &attributes,
1461 QSvgHandler *handler)
1463 const QStringView fromStr = attributes.value(QLatin1String(
"from"));
1464 const QStringView toStr = attributes.value(QLatin1String(
"to"));
1465 const QStringView valuesStr = attributes.value(QLatin1String(
"values"));
1466 const QString targetStr = attributes.value(QLatin1String(
"attributeName")).toString();
1468 if (targetStr != QLatin1String(
"fill") && targetStr != QLatin1String(
"stroke"))
1471 QList<QColor> colors;
1472 if (valuesStr.isEmpty()) {
1473 QColor startColor, endColor;
1474 resolveColor(fromStr, startColor, handler);
1475 resolveColor(toStr, endColor, handler);
1477 colors.append(startColor);
1478 colors.append(endColor);
1480 for (
auto part : qTokenize(valuesStr, u';')) {
1482 resolveColor(part, color, handler);
1483 colors.append(color);
1487 QSvgAnimatedPropertyColor *prop =
static_cast<QSvgAnimatedPropertyColor *>
1488 (QSvgAbstractAnimatedProperty::createAnimatedProperty(targetStr));
1492 prop->setColors(colors);
1494 QList<qreal> keyFrames;
1495 generateKeyFrames(keyFrames, colors.size());
1496 prop->setKeyFrames(keyFrames);
1498 QSvgAnimateColor *anim =
new QSvgAnimateColor(parent);
1499 anim->appendProperty(prop);
1501 if (!parseBaseAnimate(parent, attributes, anim, handler)) {
1510 const QXmlStreamAttributes &attributes,
1513 Q_UNUSED(parent); Q_UNUSED(attributes);
1519 QList<qreal> list = parseNumbersList(s);
1521 for (
int i = 3 - list.size(); i > 0; --i)
1527 parseNumberTriplet(values, &s);
1531 const QXmlStreamAttributes &attributes,
1532 QSvgHandler *handler)
1534 const QStringView typeStr = attributes.value(QLatin1String(
"type"));
1535 const QStringView values = attributes.value(QLatin1String(
"values"));
1536 const QStringView fromStr = attributes.value(QLatin1String(
"from"));
1537 const QStringView toStr = attributes.value(QLatin1String(
"to"));
1538 const QStringView byStr = attributes.value(QLatin1String(
"by"));
1541 if (values.isEmpty()) {
1542 if (fromStr.isEmpty()) {
1543 if (!byStr.isEmpty()) {
1547 parseNumberTriplet(vals, byStr);
1553 if (!toStr.isEmpty()) {
1555 parseNumberTriplet(vals, fromStr);
1556 parseNumberTriplet(vals, toStr);
1557 }
else if (!byStr.isEmpty()) {
1559 parseNumberTriplet(vals, fromStr);
1560 parseNumberTriplet(vals, byStr);
1561 for (
int i = vals.size() - 3; i < vals.size(); ++i)
1562 vals[i] += vals[i - 3];
1568 QStringView s = values;
1569 while (!s.isEmpty()) {
1570 parseNumberTriplet(vals, &s);
1575 if (vals.size() % 3 != 0)
1579 QList<QSvgAnimatedPropertyTransform::TransformComponent> components;
1580 for (
int i = 0; i <= vals.size() - 3; i += 3) {
1581 QSvgAnimatedPropertyTransform::TransformComponent component;
1582 if (typeStr == QLatin1String(
"translate")) {
1583 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Translate;
1584 component.values.append(vals.at(i));
1585 component.values.append(vals.at(i + 1));
1586 }
else if (typeStr == QLatin1String(
"scale")) {
1587 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Scale;
1588 component.values.append(vals.at(i));
1589 component.values.append(vals.at(i + 1));
1590 }
else if (typeStr == QLatin1String(
"rotate")) {
1591 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Rotate;
1592 component.values.append(vals.at(i));
1593 component.values.append(vals.at(i + 1));
1594 component.values.append(vals.at(i + 2));
1595 }
else if (typeStr == QLatin1String(
"skewX")) {
1596 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Skew;
1597 component.values.append(vals.at(i));
1598 component.values.append(0);
1599 }
else if (typeStr == QLatin1String(
"skewY")) {
1600 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Skew;
1601 component.values.append(0);
1602 component.values.append(vals.at(i));
1606 components.append(component);
1609 QSvgAnimatedPropertyTransform *prop =
static_cast<QSvgAnimatedPropertyTransform *>
1610 (QSvgAbstractAnimatedProperty::createAnimatedProperty(QLatin1String(
"transform")));
1614 prop->appendComponents(components);
1616 prop->setTransformCount(1);
1617 QList<qreal> keyFrames;
1618 generateKeyFrames(keyFrames, vals.size() / 3);
1619 prop->setKeyFrames(keyFrames);
1621 QSvgAnimateTransform *anim =
new QSvgAnimateTransform(parent);
1622 anim->appendProperty(prop);
1624 if (!parseBaseAnimate(parent, attributes, anim, handler)) {
1633 const QXmlStreamAttributes &attributes,
1636 Q_UNUSED(parent); Q_UNUSED(attributes);
1641 const QXmlStreamAttributes &attributes,
1644 Q_UNUSED(parent); Q_UNUSED(attributes);
1649 const QXmlStreamAttributes &attributes,
1652 const QStringView cx = attributes.value(QLatin1String(
"cx"));
1653 const QStringView cy = attributes.value(QLatin1String(
"cy"));
1654 const QStringView r = attributes.value(QLatin1String(
"r"));
1655 qreal ncx = QSvgUtils::toDouble(cx);
1656 qreal ncy = QSvgUtils::toDouble(cy);
1657 qreal nr = QSvgUtils::toDouble(r);
1661 QRectF rect(ncx-nr, ncy-nr, nr*2, nr*2);
1662 QSvgNode *circle =
new QSvgCircle(parent, rect);
1667 const QXmlStreamAttributes &attributes,
1670 Q_UNUSED(attributes);
1671 QSvgDefs *defs =
new QSvgDefs(parent);
1676 const QXmlStreamAttributes &attributes,
1679 Q_UNUSED(parent); Q_UNUSED(attributes);
1684 const QXmlStreamAttributes &attributes,
1687 const QStringView cx = attributes.value(QLatin1String(
"cx"));
1688 const QStringView cy = attributes.value(QLatin1String(
"cy"));
1689 const QStringView rx = attributes.value(QLatin1String(
"rx"));
1690 const QStringView ry = attributes.value(QLatin1String(
"ry"));
1691 qreal ncx = QSvgUtils::toDouble(cx);
1692 qreal ncy = QSvgUtils::toDouble(cy);
1693 qreal nrx = QSvgUtils::toDouble(rx);
1694 qreal nry = QSvgUtils::toDouble(ry);
1696 QRectF rect(ncx-nrx, ncy-nry, nrx*2, nry*2);
1697 QSvgNode *ellipse =
new QSvgEllipse(parent, rect);
1702 QSvgHandler *handler)
1704 const QStringView hax = attributes.value(QLatin1String(
"horiz-adv-x"));
1705 QString myId = someId(attributes);
1707 qreal horizAdvX = QSvgUtils::toDouble(hax);
1709 if (!myId.isEmpty()) {
1710 QSvgDocument *doc = handler->document();
1711 QSvgFont *font = doc->svgFont(myId);
1713 font =
new QSvgFont(horizAdvX);
1714 font->setFamilyName(myId);
1715 doc->addSvgFont(font);
1717 return new QSvgFontStyle(font, doc);
1723 const QXmlStreamAttributes &attributes,
1726 if (parent->type() != QSvgStyleProperty::FONT) {
1730 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
1731 QSvgFont *font = style->svgFont();
1732 const QStringView name = attributes.value(QLatin1String(
"font-family"));
1733 const QStringView unitsPerEmStr = attributes.value(QLatin1String(
"units-per-em"));
1735 qreal unitsPerEm = QSvgUtils::toDouble(unitsPerEmStr);
1737 unitsPerEm = QSvgFont::DEFAULT_UNITS_PER_EM;
1739 if (!name.isEmpty())
1740 font->setFamilyName(name.toString());
1741 font->setUnitsPerEm(unitsPerEm);
1743 if (!font->familyName().isEmpty())
1744 if (!style->doc()->svgFont(font->familyName()))
1745 style->doc()->addSvgFont(font);
1751 const QXmlStreamAttributes &attributes,
1754 if (parent->type() != QSvgStyleProperty::FONT) {
1758 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
1759 QSvgFont *font = style->svgFont();
1760 const QStringView name = attributes.value(QLatin1String(
"name"));
1762 if (!name.isEmpty())
1763 font->setFamilyName(name.toString());
1765 if (!font->familyName().isEmpty())
1766 if (!style->doc()->svgFont(font->familyName()))
1767 style->doc()->addSvgFont(font);
1773 const QXmlStreamAttributes &attributes,
1776 Q_UNUSED(parent); Q_UNUSED(attributes);
1781 const QXmlStreamAttributes &attributes,
1784 Q_UNUSED(parent); Q_UNUSED(attributes);
1789 const QXmlStreamAttributes &attributes,
1792 Q_UNUSED(parent); Q_UNUSED(attributes);
1797 const QXmlStreamAttributes &attributes,
1800 Q_UNUSED(attributes);
1801 QSvgG *node =
new QSvgG(parent);
1806 const QXmlStreamAttributes &attributes,
1809 if (parent->type() != QSvgStyleProperty::FONT) {
1813 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
1814 QSvgFont *font = style->svgFont();
1815 return createSvgGlyph(font, attributes,
false);
1819 const QXmlStreamAttributes &attributes,
1822 Q_UNUSED(parent); Q_UNUSED(attributes);
1827 const QXmlStreamAttributes &attributes,
1830 Q_UNUSED(parent); Q_UNUSED(attributes);
1835 const QXmlStreamAttributes &attributes,
1836 QSvgHandler *handler)
1838 const QStringView x = attributes.value(QLatin1String(
"x"));
1839 const QStringView y = attributes.value(QLatin1String(
"y"));
1840 const QStringView width = attributes.value(QLatin1String(
"width"));
1841 const QStringView height = attributes.value(QLatin1String(
"height"));
1842 QString filename = attributes.value(QLatin1String(
"xlink:href")).toString();
1844 filename = attributes.value(QLatin1String(
"href")).toString();
1845 qreal nx = QSvgUtils::toDouble(x);
1846 qreal ny = QSvgUtils::toDouble(y);
1848 qreal nwidth = QSvgUtils::parseLength(width.toString(), &type);
1849 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
1851 qreal nheight = QSvgUtils::parseLength(height.toString(), &type);
1852 nheight = QSvgUtils::convertToPixels(nheight,
false, type);
1854 filename = filename.trimmed();
1855 if (filename.isEmpty()) {
1856 qCWarning(lcSvgHandler) <<
"QSvgHandler: Image filename is empty";
1859 if (nwidth <= 0 || nheight <= 0) {
1860 qCWarning(lcSvgHandler) <<
"QSvgHandler: Width or height for" << filename <<
"image was not greater than 0";
1869 } filenameType = NotLoaded;
1871 if (filename.startsWith(QLatin1String(
"data"))) {
1874 if (qDecodeDataUrl(QUrl{filename}, mimeType, data)) {
1875 image = QImage::fromData(data);
1876 filenameType = LoadedFromData;
1880 if (image.isNull()) {
1881 const auto *file = qobject_cast<QFile *>(handler->device());
1884 if (url.isRelative()) {
1886 filename = info.absoluteDir().absoluteFilePath(filename);
1890 if (handler->trustedSourceMode() || !QImageReader::imageFormat(filename).startsWith(
"svg")) {
1891 image = QImage(filename);
1892 filenameType = LoadedFromFile;
1896 if (image.isNull()) {
1897 qCWarning(lcSvgHandler) <<
"Could not create image from" << filename;
1901 if (image.format() == QImage::Format_ARGB32)
1902 image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
1904 QSvgNode *img =
new QSvgImage(parent,
1906 filenameType == LoadedFromFile ? filename : QString{},
1915 const QXmlStreamAttributes &attributes,
1918 const QStringView x1 = attributes.value(QLatin1String(
"x1"));
1919 const QStringView y1 = attributes.value(QLatin1String(
"y1"));
1920 const QStringView x2 = attributes.value(QLatin1String(
"x2"));
1921 const QStringView y2 = attributes.value(QLatin1String(
"y2"));
1922 qreal nx1 = QSvgUtils::toDouble(x1);
1923 qreal ny1 = QSvgUtils::toDouble(y1);
1924 qreal nx2 = QSvgUtils::toDouble(x2);
1925 qreal ny2 = QSvgUtils::toDouble(y2);
1927 QLineF lineBounds(nx1, ny1, nx2, ny2);
1928 QSvgNode *line =
new QSvgLine(parent, lineBounds);
1934 QSvgGradientStyle *gradProp,
1935 QSvgHandler *handler)
1937 QStringView linkId = attributes.value(QLatin1String(
"xlink:href"));
1938 const QStringView trans = attributes.value(QLatin1String(
"gradientTransform"));
1939 const QStringView spread = attributes.value(QLatin1String(
"spreadMethod"));
1940 const QStringView units = attributes.value(QLatin1String(
"gradientUnits"));
1941 const QStringView colorStr = attributes.value(QLatin1String(
"color"));
1942 const QStringView colorOpacityStr = attributes.value(QLatin1String(
"color-opacity"));
1945 if (constructColor(colorStr, colorOpacityStr, color, handler)) {
1946 handler->popColor();
1947 handler->pushColor(color);
1951 QGradient *grad = gradProp->qgradient();
1952 linkId = idFromIRI(linkId);
1953 if (!linkId.isEmpty()) {
1954 QSvgStyleProperty *prop = handler->document()->namedStyle(linkId.toString());
1955 if (prop && prop->type() == QSvgStyleProperty::GRADIENT) {
1956 QSvgGradientStyle *inherited =
1957 static_cast<QSvgGradientStyle*>(prop);
1958 if (!inherited->stopLink().isEmpty()) {
1959 gradProp->setStopLink(inherited->stopLink(), handler->document());
1961 grad->setStops(inherited->qgradient()->stops());
1962 gradProp->setGradientStopsSet(inherited->gradientStopsSet());
1965 matrix = inherited->qtransform();
1967 gradProp->setStopLink(linkId.toString(), handler->document());
1971 if (!trans.isEmpty()) {
1972 matrix = parseTransformationMatrix(trans);
1973 gradProp->setTransform(matrix);
1974 }
else if (!matrix.isIdentity()) {
1975 gradProp->setTransform(matrix);
1978 if (!spread.isEmpty()) {
1979 if (spread == QLatin1String(
"pad")) {
1980 grad->setSpread(QGradient::PadSpread);
1981 }
else if (spread == QLatin1String(
"reflect")) {
1982 grad->setSpread(QGradient::ReflectSpread);
1983 }
else if (spread == QLatin1String(
"repeat")) {
1984 grad->setSpread(QGradient::RepeatSpread);
1988 if (units.isEmpty() || units == QLatin1String(
"objectBoundingBox")) {
1989 grad->setCoordinateMode(QGradient::ObjectMode);
1994 QSvgHandler *handler)
1996 const QStringView x1 = attributes.value(QLatin1String(
"x1"));
1997 const QStringView y1 = attributes.value(QLatin1String(
"y1"));
1998 const QStringView x2 = attributes.value(QLatin1String(
"x2"));
1999 const QStringView y2 = attributes.value(QLatin1String(
"y2"));
2007 nx1 = convertToNumber(x1);
2009 ny1 = convertToNumber(y1);
2011 nx2 = convertToNumber(x2);
2013 ny2 = convertToNumber(y2);
2015 QLinearGradient *grad =
new QLinearGradient(nx1, ny1, nx2, ny2);
2016 grad->setInterpolationMode(QGradient::ComponentInterpolation);
2017 QSvgGradientStyle *prop =
new QSvgGradientStyle(grad);
2018 parseBaseGradient(attributes, prop, handler);
2024 const QXmlStreamAttributes &attributes,
2027 Q_UNUSED(parent); Q_UNUSED(attributes);
2032 const QXmlStreamAttributes &attributes,
2035 if (parent->type() != QSvgStyleProperty::FONT) {
2039 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
2040 QSvgFont *font = style->svgFont();
2041 return createSvgGlyph(font, attributes,
true);
2045 const QXmlStreamAttributes &attributes,
2048 Q_UNUSED(parent); Q_UNUSED(attributes);
2053 const QXmlStreamAttributes &attributes,
2056 Q_UNUSED(parent); Q_UNUSED(attributes);
2061 const QXmlStreamAttributes &,
2068 const QXmlStreamAttributes &attributes,
2069 QSvgHandler *handler)
2071 const QStringView x = attributes.value(QLatin1String(
"x"));
2072 const QStringView y = attributes.value(QLatin1String(
"y"));
2073 const QStringView width = attributes.value(QLatin1String(
"width"));
2074 const QStringView height = attributes.value(QLatin1String(
"height"));
2075 const QStringView mU = attributes.value(QLatin1String(
"maskUnits"));
2076 const QStringView mCU = attributes.value(QLatin1String(
"maskContentUnits"));
2078 QtSvg::
UnitTypes nmU = mU.contains(QLatin1String(
"userSpaceOnUse")) ?
2081 QtSvg::
UnitTypes nmCU = mCU.contains(QLatin1String(
"objectBoundingBox")) ?
2091 qreal nx = QSvgUtils::parseLength(x, &type, &ok);
2092 nx = QSvgUtils::convertToPixels(nx,
true, type);
2093 if (x.isEmpty() || !ok) {
2097 nx = nx / 100. * handler->document()->viewBox().width();
2102 qreal ny = QSvgUtils::parseLength(y, &type, &ok);
2103 ny = QSvgUtils::convertToPixels(ny,
true, type);
2104 if (y.isEmpty() || !ok) {
2108 ny = ny / 100. * handler->document()->viewBox().height();
2113 qreal nwidth = QSvgUtils::parseLength(width, &type, &ok);
2114 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
2115 if (width.isEmpty() || !ok) {
2119 nwidth = nwidth / 100. * handler->document()->viewBox().width();
2121 nwidth = nwidth / 100.;
2124 qreal nheight = QSvgUtils::parseLength(height, &type, &ok);
2125 nheight = QSvgUtils::convertToPixels(nheight,
true, type);
2126 if (height.isEmpty() || !ok) {
2130 nheight = nheight / 100. * handler->document()->viewBox().height();
2132 nheight = nheight / 100.;
2135 QRectF bounds(nx, ny, nwidth, nheight);
2136 if (bounds.isEmpty())
2139 QSvgNode *mask =
new QSvgMask(parent, QSvgRectF(bounds, nmUx, nmUy, nmUw, nmUh), nmCU);
2146 const QStringView xStr = attributes.value(QLatin1String(
"x"));
2147 const QStringView yStr = attributes.value(QLatin1String(
"y"));
2148 const QStringView widthStr = attributes.value(QLatin1String(
"width"));
2149 const QStringView heightStr = attributes.value(QLatin1String(
"height"));
2152 if (!xStr.isEmpty()) {
2154 x = QSvgUtils::parseLength(xStr, &type);
2156 x = QSvgUtils::convertToPixels(x,
true, type);
2166 if (!yStr.isEmpty()) {
2168 y = QSvgUtils::parseLength(yStr, &type);
2170 y = QSvgUtils::convertToPixels(y,
false, type);
2180 if (!widthStr.isEmpty()) {
2182 width = QSvgUtils::parseLength(widthStr, &type);
2184 width = QSvgUtils::convertToPixels(width,
true, type);
2191 rect->setWidth(width);
2194 if (!heightStr.isEmpty()) {
2196 height = QSvgUtils::parseLength(heightStr, &type);
2198 height = QSvgUtils::convertToPixels(height,
false, type);
2205 rect->setHeight(height);
2210 const QXmlStreamAttributes &attributes,
2211 QSvgHandler *handler)
2213 const QStringView fU = attributes.value(QLatin1String(
"filterUnits"));
2214 const QStringView pU = attributes.value(QLatin1String(
"primitiveUnits"));
2216 const QtSvg::
UnitTypes filterUnits = fU.contains(QLatin1String(
"userSpaceOnUse")) ?
2219 const QtSvg::
UnitTypes primitiveUnits = pU.contains(QLatin1String(
"objectBoundingBox")) ?
2227 qreal width = handler->document()->viewBox().width();
2228 qreal height = handler->document()->viewBox().height();
2229 rect = QSvgRectF(QRectF(-0.1 * width, -0.1 * height, 1.2 * width, 1.2 * height),
2233 rect = QSvgRectF(QRectF(-0.1, -0.1, 1.2, 1.2),
2238 parseFilterBounds(attributes, &rect);
2240 QSvgNode *filter =
new QSvgFilterContainer(parent, rect, filterUnits, primitiveUnits);
2245 QString *outString, QSvgRectF *rect)
2247 *inString = attributes.value(QLatin1String(
"in")).toString();
2248 *outString = attributes.value(QLatin1String(
"result")).toString();
2254 *rect = QSvgRectF(QRectF(0, 0, 1.0, 1.0),
2260 parseFilterBounds(attributes, rect);
2264 const QXmlStreamAttributes &attributes,
2267 const QStringView typeString = attributes.value(QLatin1String(
"type"));
2268 const QStringView valuesString = attributes.value(QLatin1String(
"values"));
2270 QString inputString;
2271 QString outputString;
2274 QSvgFeColorMatrix::ColorShiftType type;
2275 QSvgFeColorMatrix::Matrix values;
2278 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2280 if (typeString.startsWith(QLatin1String(
"saturate")))
2281 type = QSvgFeColorMatrix::ColorShiftType::Saturate;
2282 else if (typeString.startsWith(QLatin1String(
"hueRotate")))
2283 type = QSvgFeColorMatrix::ColorShiftType::HueRotate;
2284 else if (typeString.startsWith(QLatin1String(
"luminanceToAlpha")))
2285 type = QSvgFeColorMatrix::ColorShiftType::LuminanceToAlpha;
2287 type = QSvgFeColorMatrix::ColorShiftType::Matrix;
2289 if (!valuesString.isEmpty()) {
2290 const auto valueStringList = splitWithDelimiter(valuesString);
2291 for (
int i = 0, j = 0; i < qMin(20, valueStringList.size()); i++) {
2293 qreal v = QSvgUtils::toDouble(valueStringList.at(i), &ok);
2295 values.data()[j] = v;
2300 values.setToIdentity();
2303 QSvgNode *filter =
new QSvgFeColorMatrix(parent, inputString, outputString, rect,
2309 const QXmlStreamAttributes &attributes,
2312 const QStringView edgeModeString = attributes.value(QLatin1String(
"edgeMode"));
2313 const QStringView stdDeviationString = attributes.value(QLatin1String(
"stdDeviation"));
2315 QString inputString;
2316 QString outputString;
2319 QSvgFeGaussianBlur::EdgeMode edgemode = QSvgFeGaussianBlur::EdgeMode::Duplicate;
2321 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2322 qreal stdDeviationX = 0;
2323 qreal stdDeviationY = 0;
2325 stdDeviationX = qMax(0., QSvgUtils::toDouble(stdDeviationString.split(u" ").first()));
2326 stdDeviationY = qMax(0., QSvgUtils::toDouble(stdDeviationString.split(u" ").last()));
2328 stdDeviationY = stdDeviationX = qMax(0., QSvgUtils::toDouble(stdDeviationString));
2331 if (edgeModeString.startsWith(QLatin1String(
"wrap")))
2332 edgemode = QSvgFeGaussianBlur::EdgeMode::Wrap;
2333 else if (edgeModeString.startsWith(QLatin1String(
"none")))
2334 edgemode = QSvgFeGaussianBlur::EdgeMode::None;
2336 QSvgNode *filter =
new QSvgFeGaussianBlur(parent, inputString, outputString, rect,
2337 stdDeviationX, stdDeviationY, edgemode);
2342 const QXmlStreamAttributes &attributes,
2345 QStringView dxString = attributes.value(QLatin1String(
"dx"));
2346 QStringView dyString = attributes.value(QLatin1String(
"dy"));
2348 QString inputString;
2349 QString outputString;
2352 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2355 if (!dxString.isEmpty()) {
2357 dx = QSvgUtils::parseLength(dxString, &type);
2359 dx = QSvgUtils::convertToPixels(dx,
true, type);
2363 if (!dyString.isEmpty()) {
2365 dy = QSvgUtils::parseLength(dyString, &type);
2367 dy = QSvgUtils::convertToPixels(dy,
true, type);
2370 QSvgNode *filter =
new QSvgFeOffset(parent, inputString, outputString, rect,
2376 const QXmlStreamAttributes &attributes,
2379 const QStringView in2String = attributes.value(QLatin1String(
"in2"));
2380 const QStringView operatorString = attributes.value(QLatin1String(
"operator"));
2381 const QStringView k1String = attributes.value(QLatin1String(
"k1"));
2382 const QStringView k2String = attributes.value(QLatin1String(
"k2"));
2383 const QStringView k3String = attributes.value(QLatin1String(
"k3"));
2384 const QStringView k4String = attributes.value(QLatin1String(
"k4"));
2386 QString inputString;
2387 QString outputString;
2390 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2392 QSvgFeComposite::Operator op = QSvgFeComposite::Operator::Over;
2393 if (operatorString.startsWith(QLatin1String(
"in")))
2394 op = QSvgFeComposite::Operator::In;
2395 else if (operatorString.startsWith(QLatin1String(
"out")))
2396 op = QSvgFeComposite::Operator::Out;
2397 else if (operatorString.startsWith(QLatin1String(
"atop")))
2398 op = QSvgFeComposite::Operator::Atop;
2399 else if (operatorString.startsWith(QLatin1String(
"xor")))
2400 op = QSvgFeComposite::Operator::Xor;
2401 else if (operatorString.startsWith(QLatin1String(
"lighter")))
2402 op = QSvgFeComposite::Operator::Lighter;
2403 else if (operatorString.startsWith(QLatin1String(
"arithmetic")))
2404 op = QSvgFeComposite::Operator::Arithmetic;
2406 QVector4D k(0, 0, 0, 0);
2408 if (op == QSvgFeComposite::Operator::Arithmetic) {
2410 qreal v = QSvgUtils::toDouble(k1String, &ok);
2413 v = QSvgUtils::toDouble(k2String, &ok);
2416 v = QSvgUtils::toDouble(k3String, &ok);
2419 v = QSvgUtils::toDouble(k4String, &ok);
2424 QSvgNode *filter =
new QSvgFeComposite(parent, inputString, outputString, rect,
2425 in2String.toString(), op, k);
2431 const QXmlStreamAttributes &attributes,
2434 QString inputString;
2435 QString outputString;
2438 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2440 QSvgNode *filter =
new QSvgFeMerge(parent, inputString, outputString, rect);
2445 const QXmlStreamAttributes &attributes,
2446 QSvgHandler *handler)
2448 QStringView colorStr = attributes.value(QLatin1String(
"flood-color"));
2449 const QStringView opacityStr = attributes.value(QLatin1String(
"flood-opacity"));
2452 if (!constructColor(colorStr, opacityStr, color, handler)) {
2453 color = QColor(Qt::black);
2454 if (opacityStr.isEmpty())
2455 color.setAlphaF(1.0);
2457 setAlpha(opacityStr, &color);
2460 QString inputString;
2461 QString outputString;
2464 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2466 QSvgNode *filter =
new QSvgFeFlood(parent, inputString, outputString, rect, color);
2471 const QXmlStreamAttributes &attributes,
2474 QString inputString;
2475 QString outputString;
2478 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2480 QSvgNode *filter =
new QSvgFeMergeNode(parent, inputString, outputString, rect);
2485 const QXmlStreamAttributes &attributes,
2488 const QStringView in2String = attributes.value(QLatin1String(
"in2"));
2489 const QStringView modeString = attributes.value(QLatin1String(
"mode"));
2491 QString inputString;
2492 QString outputString;
2495 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2497 QSvgFeBlend::Mode mode = QSvgFeBlend::Mode::Normal;
2498 if (modeString.startsWith(QLatin1StringView(
"multiply")))
2499 mode = QSvgFeBlend::Mode::Multiply;
2500 else if (modeString.startsWith(QLatin1StringView(
"screen")))
2501 mode = QSvgFeBlend::Mode::Screen;
2502 else if (modeString.startsWith(QLatin1StringView(
"darken")))
2503 mode = QSvgFeBlend::Mode::Darken;
2504 else if (modeString.startsWith(QLatin1StringView(
"lighten")))
2505 mode = QSvgFeBlend::Mode::Lighten;
2507 QSvgNode *filter =
new QSvgFeBlend(parent, inputString, outputString, rect,
2508 in2String.toString(), mode);
2513 const QXmlStreamAttributes &attributes,
2516 QString inputString;
2517 QString outputString;
2520 parseFilterAttributes(attributes, &inputString, &outputString, &rect);
2522 QSvgNode *filter =
new QSvgFeUnsupported(parent, inputString, outputString, rect);
2528 QList<QStringView> viewBoxValues;
2531 viewBoxValues = splitWithDelimiter(str);
2532 if (viewBoxValues.size() == 4) {
2534 qreal x = QSvgUtils::parseLength(viewBoxValues.at(0).trimmed(), &type);
2535 qreal y = QSvgUtils::parseLength(viewBoxValues.at(1).trimmed(), &type);
2536 qreal w = QSvgUtils::parseLength(viewBoxValues.at(2).trimmed(), &type);
2537 qreal h = QSvgUtils::parseLength(viewBoxValues.at(3).trimmed(), &type);
2538 return QRectF(x, y, w, h);
2540 return std::nullopt;
2544 QRectF *rect, QRectF *viewBox, QPointF *refPoint,
2545 QSvgSymbolLike::PreserveAspectRatios *aspect,
2546 QSvgSymbolLike::Overflow *overflow,
2547 bool marker =
false)
2549 const QStringView xStr = attributes.value(QLatin1String(
"x"));
2550 const QStringView yStr = attributes.value(QLatin1String(
"y"));
2551 const QStringView refXStr = attributes.value(QLatin1String(
"refX"));
2552 const QStringView refYStr = attributes.value(QLatin1String(
"refY"));
2553 const QStringView widthStr = attributes.value(marker ? QLatin1String(
"markerWidth")
2554 : QLatin1String(
"width"));
2555 const QStringView heightStr = attributes.value(marker ? QLatin1String(
"markerHeight")
2556 : QLatin1String(
"height"));
2557 const QStringView pAspectRStr = attributes.value(QLatin1String(
"preserveAspectRatio"));
2558 const QStringView overflowStr = attributes.value(QLatin1String(
"overflow"));
2559 const QStringView viewBoxStr = attributes.value(QLatin1String(
"viewBox"));
2563 if (!xStr.isEmpty()) {
2565 x = QSvgUtils::parseLength(xStr, &type);
2567 x = QSvgUtils::convertToPixels(x,
true, type);
2570 if (!yStr.isEmpty()) {
2572 y = QSvgUtils::parseLength(yStr, &type);
2574 y = QSvgUtils::convertToPixels(y,
false, type);
2577 if (!widthStr.isEmpty()) {
2579 width = QSvgUtils::parseLength(widthStr, &type);
2581 width = QSvgUtils::convertToPixels(width,
true, type);
2584 if (!heightStr.isEmpty()) {
2586 height = QSvgUtils::parseLength(heightStr, &type);
2588 height = QSvgUtils::convertToPixels(height,
false, type);
2591 *rect = QRectF(x, y, width, height);
2594 if (!refXStr.isEmpty()) {
2596 x = QSvgUtils::parseLength(refXStr, &type);
2598 x = QSvgUtils::convertToPixels(x,
true, type);
2601 if (!refYStr.isEmpty()) {
2603 y = QSvgUtils::parseLength(refYStr, &type);
2605 y = QSvgUtils::convertToPixels(y,
false, type);
2607 *refPoint = QPointF(x,y);
2609 auto viewBoxResult = parseViewBox(viewBoxStr);
2611 *viewBox = *viewBoxResult;
2612 else if (width > 0 && height > 0)
2613 *viewBox = QRectF(0, 0, width, height);
2615 *viewBox = handler->document()->viewBox();
2617 if (viewBox->isNull())
2620 auto pAspectRStrs = pAspectRStr.split(u" ");
2621 QSvgSymbolLike::PreserveAspectRatio aspectX = QSvgSymbolLike::PreserveAspectRatio::xMid;
2622 QSvgSymbolLike::PreserveAspectRatio aspectY = QSvgSymbolLike::PreserveAspectRatio::yMid;
2623 QSvgSymbolLike::PreserveAspectRatio aspectMS = QSvgSymbolLike::PreserveAspectRatio::meet;
2625 for (
auto &pAStr : std::as_const(pAspectRStrs)) {
2626 if (pAStr.startsWith(QLatin1String(
"none"))) {
2627 aspectX = QSvgSymbolLike::PreserveAspectRatio::None;
2628 aspectY = QSvgSymbolLike::PreserveAspectRatio::None;
2630 if (pAStr.startsWith(QLatin1String(
"xMin")))
2631 aspectX = QSvgSymbolLike::PreserveAspectRatio::xMin;
2632 else if (pAStr.startsWith(QLatin1String(
"xMax")))
2633 aspectX = QSvgSymbolLike::PreserveAspectRatio::xMax;
2634 if (pAStr.endsWith(QLatin1String(
"YMin")))
2635 aspectY = QSvgSymbolLike::PreserveAspectRatio::yMin;
2636 else if (pAStr.endsWith(QLatin1String(
"YMax")))
2637 aspectY = QSvgSymbolLike::PreserveAspectRatio::yMax;
2640 if (pAStr.endsWith(QLatin1String(
"slice")))
2641 aspectMS = QSvgSymbolLike::PreserveAspectRatio::slice;
2643 *aspect = aspectX | aspectY | aspectMS;
2650 *overflow = QSvgSymbolLike::Overflow::Hidden;
2652 if (overflowStr.endsWith(QLatin1String(
"auto")))
2653 *overflow = QSvgSymbolLike::Overflow::Auto;
2654 else if (overflowStr.endsWith(QLatin1String(
"visible")))
2655 *overflow = QSvgSymbolLike::Overflow::Visible;
2656 else if (overflowStr.endsWith(QLatin1String(
"hidden")))
2657 *overflow = QSvgSymbolLike::Overflow::Hidden;
2658 else if (overflowStr.endsWith(QLatin1String(
"scroll")))
2659 *overflow = QSvgSymbolLike::Overflow::Scroll;
2665 const QXmlStreamAttributes &attributes,
2666 QSvgHandler *handler)
2668 QRectF rect, viewBox;
2670 QSvgSymbolLike::PreserveAspectRatios aspect;
2671 QSvgSymbolLike::Overflow overflow;
2673 if (!parseSymbolLikeAttributes(attributes, handler, &rect, &viewBox, &refP, &aspect, &overflow))
2676 refP = QPointF(0, 0);
2677 QSvgNode *symbol =
new QSvgSymbol(parent, rect, viewBox, refP, aspect, overflow);
2682 const QXmlStreamAttributes &attributes,
2683 QSvgHandler *handler)
2685 QRectF rect, viewBox;
2687 QSvgSymbolLike::PreserveAspectRatios aspect;
2688 QSvgSymbolLike::Overflow overflow;
2690 const QStringView orientStr = attributes.value(QLatin1String(
"orient"));
2691 const QStringView markerUnitsStr = attributes.value(QLatin1String(
"markerUnits"));
2693 qreal orientationAngle = 0;
2694 QSvgMarker::Orientation orientation;
2695 if (orientStr.startsWith(QLatin1String(
"auto-start-reverse")))
2696 orientation = QSvgMarker::Orientation::AutoStartReverse;
2697 else if (orientStr.startsWith(QLatin1String(
"auto")))
2698 orientation = QSvgMarker::Orientation::Auto;
2700 orientation = QSvgMarker::Orientation::Value;
2703 if (orientStr.endsWith(QLatin1String(
"turn")))
2704 a = 360. * QSvgUtils::toDouble(orientStr.mid(0, orientStr.length()-4), &ok);
2705 else if (orientStr.endsWith(QLatin1String(
"grad")))
2706 a = QSvgUtils::toDouble(orientStr.mid(0, orientStr.length()-4), &ok);
2707 else if (orientStr.endsWith(QLatin1String(
"rad")))
2708 a = 180. /
M_PI * QSvgUtils::toDouble(orientStr.mid(0, orientStr.length()-3), &ok);
2710 a = QSvgUtils::toDouble(orientStr, &ok);
2712 orientationAngle = a;
2715 QSvgMarker::MarkerUnits markerUnits = QSvgMarker::MarkerUnits::StrokeWidth;
2716 if (markerUnitsStr.startsWith(QLatin1String(
"userSpaceOnUse")))
2717 markerUnits = QSvgMarker::MarkerUnits::UserSpaceOnUse;
2719 if (!parseSymbolLikeAttributes(attributes, handler, &rect, &viewBox, &refP, &aspect, &overflow,
true))
2722 QSvgNode *marker =
new QSvgMarker(parent, rect, viewBox, refP, aspect, overflow,
2723 orientation, orientationAngle, markerUnits);
2728 const QXmlStreamAttributes &attributes,
2729 QSvgHandler *handler)
2731 QStringView data = attributes.value(QLatin1String(
"d"));
2733 std::optional<QPainterPath> qpath = QSvgUtils::parsePathDataFast(data,
2734 !handler->trustedSourceMode());
2736 qCWarning(lcSvgHandler,
"Invalid path data; path truncated.");
2740 qpath.value().setFillRule(Qt::WindingFill);
2741 QSvgNode *path =
new QSvgPath(parent, qpath.value());
2746 const QXmlStreamAttributes &attributes,
2749 QStringView pointsStr = attributes.value(QLatin1String(
"points"));
2750 const QList<qreal> points = parseNumbersList(&pointsStr);
2751 if (points.size() < 4)
2754 for (
int i = 0; i < poly.size(); ++i)
2755 poly[i] = QPointF(points.at(2 * i), points.at(2 * i + 1));
2757 return new QSvgPolyline(parent, poly);
2759 return new QSvgPolygon(parent, poly);
2763 const QXmlStreamAttributes &attributes,
2766 return createPolyNode(parent, attributes,
false);
2770 const QXmlStreamAttributes &attributes,
2773 return createPolyNode(parent, attributes,
true);
2777 const QXmlStreamAttributes &attributes,
2780 Q_UNUSED(parent); Q_UNUSED(attributes);
2785 QSvgHandler *handler)
2787 const QStringView cx = attributes.value(QLatin1String(
"cx"));
2788 const QStringView cy = attributes.value(QLatin1String(
"cy"));
2789 const QStringView r = attributes.value(QLatin1String(
"r"));
2790 const QStringView fx = attributes.value(QLatin1String(
"fx"));
2791 const QStringView fy = attributes.value(QLatin1String(
"fy"));
2796 ncx = convertToNumber(cx);
2798 ncy = convertToNumber(cy);
2802 nr = convertToNumber(r);
2808 nfx = convertToNumber(fx);
2811 nfy = convertToNumber(fy);
2813 QRadialGradient *grad =
new QRadialGradient(ncx, ncy, nr, nfx, nfy, 0);
2814 grad->setInterpolationMode(QGradient::ComponentInterpolation);
2816 QSvgGradientStyle *prop =
new QSvgGradientStyle(grad);
2817 parseBaseGradient(attributes, prop, handler);
2823 const QXmlStreamAttributes &attributes,
2826 const QStringView x = attributes.value(QLatin1String(
"x"));
2827 const QStringView y = attributes.value(QLatin1String(
"y"));
2828 const QStringView width = attributes.value(QLatin1String(
"width"));
2829 const QStringView height = attributes.value(QLatin1String(
"height"));
2830 const QStringView rx = attributes.value(QLatin1String(
"rx"));
2831 const QStringView ry = attributes.value(QLatin1String(
"ry"));
2835 qreal nwidth = QSvgUtils::parseLength(width, &type, &ok);
2838 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
2839 qreal nheight = QSvgUtils::parseLength(height, &type, &ok);
2842 nheight = QSvgUtils::convertToPixels(nheight,
true, type);
2843 qreal nrx = QSvgUtils::toDouble(rx);
2844 qreal nry = QSvgUtils::toDouble(ry);
2847 if (bounds.isEmpty())
2850 if (!rx.isEmpty() && ry.isEmpty())
2852 else if (!ry.isEmpty() && rx.isEmpty())
2858 if (nrx > bounds.width()/2)
2859 nrx = bounds.width()/2;
2860 if (nry > bounds.height()/2)
2861 nry = bounds.height()/2;
2866 nrx *= (100/(bounds.width()/2));
2867 nry *= (100/(bounds.height()/2));
2869 QSvgNode *rect =
new QSvgRect(parent, bounds, nrx, nry);
2874 const QXmlStreamAttributes &attributes,
2877 Q_UNUSED(parent); Q_UNUSED(attributes);
2882 const QXmlStreamAttributes &attributes,
2885 Q_UNUSED(parent); Q_UNUSED(attributes);
2890 QSvgHandler *handler)
2892 Q_UNUSED(attributes);
2893 QStringView solidColorStr = attributes.value(QLatin1String(
"solid-color"));
2894 QStringView solidOpacityStr = attributes.value(QLatin1String(
"solid-opacity"));
2896 if (solidOpacityStr.isEmpty())
2897 solidOpacityStr = attributes.value(QLatin1String(
"opacity"));
2900 if (!constructColor(solidColorStr, solidOpacityStr, color, handler))
2902 QSvgSolidColorStyle *style =
new QSvgSolidColorStyle(color);
2907 const QXmlStreamAttributes &attributes,
2908 QSvgHandler *handler)
2910 if (parent->type() != QSvgStyleProperty::GRADIENT)
2912 QString nodeIdStr = someId(attributes);
2913 QString xmlClassStr = attributes.value(QLatin1String(
"class")).toString();
2919 QSvgDummyNode dummy;
2920 dummy.setNodeId(nodeIdStr);
2921 dummy.setXmlClass(xmlClassStr);
2923 QSvgAttributes attrs(attributes, handler);
2925#ifndef QT_NO_CSSPARSER
2926 QXmlStreamAttributes cssAttributes;
2927 handler->cssHandler().styleLookup(&dummy, cssAttributes);
2928 attrs.setAttributes(cssAttributes, handler);
2930 QXmlStreamAttributes styleCssAttributes;
2931 QStringView style = attributes.value(QLatin1String(
"style"));
2932 if (!style.isEmpty())
2933 handler->cssHandler().parseCSStoXMLAttrs(style.toString(), styleCssAttributes);
2934 attrs.setAttributes(styleCssAttributes, handler);
2938 parseColor(&dummy, attrs, handler);
2940 QSvgGradientStyle *gradientStyle =
2941 static_cast<QSvgGradientStyle*>(parent);
2942 QStringView colorStr = attrs.stopColor;
2946 qreal offset = convertToNumber(attrs.offset, &ok);
2950 if (!constructColor(colorStr, attrs.stopOpacity, color, handler)) {
2952 if (!attrs.stopOpacity.isEmpty())
2953 setAlpha(attrs.stopOpacity, &color);
2956 QGradient *grad = gradientStyle->qgradient();
2958 offset = qMin(qreal(1), qMax(qreal(0), offset));
2959 QGradientStops stops;
2960 if (gradientStyle->gradientStopsSet()) {
2961 stops = grad->stops();
2963 if (offset <= stops.back().first)
2964 offset = stops.back().first + FLT_EPSILON;
2969 if ((stops.size() == 1) || (stops.at(stops.size() - 2).first < 1.0 - FLT_EPSILON)) {
2970 stops.back().first = 1.0 - FLT_EPSILON;
2971 grad->setStops(stops);
2976 grad->setColorAt(offset, color);
2977 gradientStyle->setGradientStopsSet(
true);
2982 const QXmlStreamAttributes &attributes,
2983 QSvgHandler *handler)
2986#ifdef QT_NO_CSSPARSER
2987 Q_UNUSED(attributes);
2990 const QStringView type = attributes.value(QLatin1String(
"type"));
2991 if (type.compare(QLatin1String(
"text/css"), Qt::CaseInsensitive) == 0 || type.isNull())
2992 handler->setInStyle(
true);
2999 const QXmlStreamAttributes &attributes,
3000 QSvgHandler *handler)
3002 Q_UNUSED(parent); Q_UNUSED(attributes);
3004 QSvgDocument *node =
new QSvgDocument(handler->options(), handler->animatorType());
3005 const QStringView widthStr = attributes.value(QLatin1String(
"width"));
3006 const QStringView heightStr = attributes.value(QLatin1String(
"height"));
3007 const QStringView viewBoxStr = attributes.value(QLatin1String(
"viewBox"));
3011 if (!widthStr.isEmpty()) {
3012 width = QSvgUtils::parseLength(widthStr, &type);
3014 width = QSvgUtils::convertToPixels(width,
true, type);
3018 if (!heightStr.isEmpty()) {
3019 height = QSvgUtils::parseLength(heightStr, &type);
3021 height = QSvgUtils::convertToPixels(height,
false, type);
3025 auto viewBoxResult = parseViewBox(viewBoxStr);
3026 if (viewBoxResult) {
3027 node->setViewBox(*viewBoxResult);
3028 }
else if (width && height) {
3030 width = QSvgUtils::convertToPixels(width,
false, type);
3031 height = QSvgUtils::convertToPixels(height,
false, type);
3033 node->setViewBox(QRectF(0, 0, width, height));
3041 const QXmlStreamAttributes &attributes,
3044 Q_UNUSED(attributes);
3045 QSvgSwitch *node =
new QSvgSwitch(parent);
3050 const QXmlStreamAttributes &attributes,
3051 QSvgHandler *handler)
3053 const QStringView x = attributes.value(QLatin1String(
"x"));
3054 const QStringView y = attributes.value(QLatin1String(
"y"));
3055 const QStringView width = attributes.value(QLatin1String(
"width"));
3056 const QStringView height = attributes.value(QLatin1String(
"height"));
3057 const QStringView patternUnits = attributes.value(QLatin1String(
"patternUnits"));
3058 const QStringView patternContentUnits = attributes.value(QLatin1String(
"patternContentUnits"));
3059 const QStringView patternTransform = attributes.value(QLatin1String(
"patternTransform"));
3061 QtSvg::
UnitTypes nPatternUnits = patternUnits.contains(QLatin1String(
"userSpaceOnUse")) ?
3064 QtSvg::
UnitTypes nPatternContentUnits = patternContentUnits.contains(QLatin1String(
"objectBoundingBox")) ?
3067 const QStringView viewBoxStr = attributes.value(QLatin1String(
"viewBox"));
3072 qreal nx = QSvgUtils::parseLength(x, &type, &ok);
3073 nx = QSvgUtils::convertToPixels(nx,
true, type);
3077 nx = (nx / 100.) * handler->document()->viewBox().width();
3081 qreal ny = QSvgUtils::parseLength(y, &type, &ok);
3082 ny = QSvgUtils::convertToPixels(ny,
true, type);
3086 ny = (ny / 100.) * handler->document()->viewBox().height();
3090 qreal nwidth = QSvgUtils::parseLength(width, &type, &ok);
3091 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
3095 nwidth = (nwidth / 100.) * handler->document()->viewBox().width();
3097 nwidth = nwidth / 100.;
3099 qreal nheight = QSvgUtils::parseLength(height, &type, &ok);
3100 nheight = QSvgUtils::convertToPixels(nheight,
true, type);
3104 nheight = (nheight / 100.) * handler->document()->viewBox().height();
3106 nheight = nheight / 100.;
3109 auto viewBoxResult = parseViewBox(viewBoxStr);
3110 if (viewBoxResult) {
3111 if (viewBoxResult->width() > 0 && viewBoxResult->height() > 0)
3112 viewBox = *viewBoxResult;
3116 if (!patternTransform.isEmpty())
3117 matrix = parseTransformationMatrix(patternTransform);
3119 QRectF bounds(nx, ny, nwidth, nheight);
3120 if (bounds.isEmpty())
3123 QSvgRectF patternRectF(bounds, nPatternUnits, nPatternUnits, nPatternUnits, nPatternUnits);
3124 QSvgPattern *node =
new QSvgPattern(parent, patternRectF, viewBox, nPatternContentUnits, matrix);
3127 QSvgPatternStyle *prop =
new QSvgPatternStyle(node);
3128 node->appendStyleProperty(prop, someId(attributes));
3134 const QXmlStreamAttributes &,
3137 if (parent->type() != QSvgNode::Textarea)
3139 static_cast<QSvgText*>(parent)->addLineBreak();
3144 const QXmlStreamAttributes &attributes,
3147 const QStringView x = attributes.value(QLatin1String(
"x"));
3148 const QStringView y = attributes.value(QLatin1String(
"y"));
3151 qreal nx = QSvgUtils::parseLength(x, &type);
3152 nx = QSvgUtils::convertToPixels(nx,
true, type);
3153 qreal ny = QSvgUtils::parseLength(y, &type);
3154 ny = QSvgUtils::convertToPixels(ny,
true, type);
3156 QSvgNode *text =
new QSvgText(parent, QPointF(nx, ny));
3161 const QXmlStreamAttributes &attributes,
3162 QSvgHandler *handler)
3164 QSvgText *node =
static_cast<QSvgText *>(createTextNode(parent, attributes, handler));
3167 qreal width = QSvgUtils::parseLength(attributes.value(QLatin1String(
"width")), &type);
3168 qreal height = QSvgUtils::parseLength(attributes.value(QLatin1String(
"height")), &type);
3169 node->setTextArea(QSizeF(width, height));
3175 const QXmlStreamAttributes &,
3178 return new QSvgTspan(parent);
3182 const QXmlStreamAttributes &attributes,
3185 QStringView linkId = attributes.value(QLatin1String(
"xlink:href"));
3186 const QStringView xStr = attributes.value(QLatin1String(
"x"));
3187 const QStringView yStr = attributes.value(QLatin1String(
"y"));
3188 QSvgStructureNode *group =
nullptr;
3190 if (linkId.isEmpty())
3191 linkId = attributes.value(QLatin1String(
"href"));
3192 QString linkIdStr = idFromIRI(linkId).toString();
3194 switch (parent->type()) {
3196 case QSvgNode::Defs:
3197 case QSvgNode::Group:
3198 case QSvgNode::Switch:
3199 case QSvgNode::Mask:
3200 group =
static_cast<QSvgStructureNode*>(parent);
3208 if (!xStr.isNull() || !yStr.isNull()) {
3210 qreal nx = QSvgUtils::parseLength(xStr, &type);
3211 nx = QSvgUtils::convertToPixels(nx,
true, type);
3213 qreal ny = QSvgUtils::parseLength(yStr, &type);
3214 ny = QSvgUtils::convertToPixels(ny,
true, type);
3215 pt = QPointF(nx, ny);
3218 QSvgNode *link = group->scopeNode(linkIdStr);
3220 if (parent->isDescendantOf(link))
3221 qCWarning(lcSvgHandler,
"link %ls is recursive!",
qUtf16Printable(linkIdStr));
3223 return new QSvgUse(pt, parent, link);
3227 return new QSvgUse(pt, parent, linkIdStr);
3230 qCWarning(lcSvgHandler,
"<use> element %ls in wrong context!",
qUtf16Printable(linkIdStr));
3235 const QXmlStreamAttributes &attributes,
3238 Q_UNUSED(parent); Q_UNUSED(attributes);
3242typedef QSvgNode *(*FactoryMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3249 QStringView ref = name.mid(1);
3250 switch (name.at(0).unicode()) {
3252 if (ref == QLatin1String(
"efs"))
return createDefsNode;
3258 if (ref.isEmpty())
return createGNode;
3265 if (ref == QLatin1String(
"vg"))
return createSvgNode;
3266 if (ref == QLatin1String(
"witch"))
return createSwitchNode;
3284 QStringView ref = name.mid(1);
3285 switch (name.at(0).unicode()) {
3287 if (ref == QLatin1String(
"ircle"))
return createCircleNode;
3290 if (ref == QLatin1String(
"llipse"))
return createEllipseNode;
3293 if (ref == QLatin1String(
"mage"))
return createImageNode;
3296 if (ref == QLatin1String(
"ine"))
return createLineNode;
3299 if (ref == QLatin1String(
"ath"))
return createPathNode;
3300 if (ref == QLatin1String(
"olygon"))
return createPolygonNode;
3301 if (ref == QLatin1String(
"olyline"))
return createPolylineNode;
3304 if (ref == QLatin1String(
"ect"))
return createRectNode;
3307 if (ref == QLatin1String(
"ext"))
return createTextNode;
3308 if (ref == QLatin1String(
"extArea"))
return createTextAreaNode;
3309 if (ref == QLatin1String(
"span"))
return createTspanNode;
3312 if (ref == QLatin1String(
"se"))
return createUseNode;
3315 if (ref == QLatin1String(
"ideo"))
return createVideoNode;
3331 if (!name.startsWith(QLatin1String(
"fe")))
3334 if (name == QLatin1String(
"feMerge"))
return createFeMergeNode;
3335 if (name == QLatin1String(
"feColorMatrix"))
return createFeColorMatrixNode;
3336 if (name == QLatin1String(
"feGaussianBlur"))
return createFeGaussianBlurNode;
3337 if (name == QLatin1String(
"feOffset"))
return createFeOffsetNode;
3338 if (name == QLatin1String(
"feMergeNode"))
return createFeMergeNodeNode;
3339 if (name == QLatin1String(
"feComposite"))
return createFeCompositeNode;
3340 if (name == QLatin1String(
"feFlood"))
return createFeFloodNode;
3341 if (name == QLatin1String(
"feBlend"))
return createFeBlendNode;
3343 static const QStringList unsupportedFilters = {
3360 if (unsupportedFilters.contains(name))
3361 return createFeUnsupportedNode;
3366typedef QSvgNode *(*AnimationMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3373 QStringView ref = name.mid(1);
3375 switch (name.at(0).unicode()) {
3377 if (ref == QLatin1String(
"nimate"))
return createAnimateNode;
3378 if (ref == QLatin1String(
"nimateColor"))
return createAnimateColorNode;
3379 if (ref == QLatin1String(
"nimateMotion"))
return createAimateMotionNode;
3380 if (ref == QLatin1String(
"nimateTransform"))
return createAnimateTransformNode;
3389typedef bool (*
ParseMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3396 QStringView ref = name.mid(1);
3397 switch (name.at(0).unicode()) {
3399 if (ref.isEmpty())
return parseAnchorNode;
3400 if (ref == QLatin1String(
"udio"))
return parseAudioNode;
3403 if (ref == QLatin1String(
"iscard"))
return parseDiscardNode;
3406 if (ref == QLatin1String(
"oreignObject"))
return parseForeignObjectNode;
3409 if (ref == QLatin1String(
"andler"))
return parseHandlerNode;
3410 if (ref == QLatin1String(
"kern"))
return parseHkernNode;
3413 if (ref == QLatin1String(
"etadata"))
return parseMetadataNode;
3414 if (ref == QLatin1String(
"path"))
return parseMpathNode;
3419 if (ref == QLatin1String(
"refetch"))
return parsePrefetchNode;
3422 if (ref == QLatin1String(
"cript"))
return parseScriptNode;
3423 if (ref == QLatin1String(
"et"))
return parseSetNode;
3424 if (ref == QLatin1String(
"tyle"))
return parseStyleNode;
3427 if (ref == QLatin1String(
"break"))
return parseTbreakNode;
3443 QStringView ref = name.mid(1);
3444 switch (name.at(0).unicode()) {
3446 if (ref == QLatin1String(
"ont"))
return createFontNode;
3449 if (ref == QLatin1String(
"inearGradient"))
return createLinearGradientNode;
3452 if (ref == QLatin1String(
"adialGradient"))
return createRadialGradientNode;
3455 if (ref == QLatin1String(
"olidColor"))
return createSolidColorNode;
3464 const QXmlStreamAttributes &,
3472 QStringView ref = name.mid(1);
3473 switch (name.at(0).unicode()) {
3495QSvgHandler::QSvgHandler(QIODevice *device, QtSvg::Options options,
3496 QtSvg::AnimatorType type)
3497 : xml(
new QXmlStreamReader(device))
3498 , m_ownsReader(
true)
3499 , m_options(options)
3500 , m_animatorType(type)
3505QSvgHandler::QSvgHandler(
const QByteArray &data, QtSvg::Options options,
3506 QtSvg::AnimatorType type)
3507 : xml(
new QXmlStreamReader(data))
3508 , m_ownsReader(
true)
3509 , m_options(options)
3510 , m_animatorType(type)
3515QSvgHandler::QSvgHandler(QXmlStreamReader *
const reader, QtSvg::Options options,
3516 QtSvg::AnimatorType type)
3518 , m_ownsReader(
false)
3519 , m_options(options)
3520 , m_animatorType(type)
3525void QSvgHandler::init()
3530 m_defaultCoords = QSvgUtils::LT_PX;
3531 m_defaultPen = QPen(Qt::black, 1, Qt::SolidLine, Qt::FlatCap, Qt::SvgMiterJoin);
3532 m_defaultPen.setMiterLimit(4);
3538 QSvgFillStyle *fillStyle =
static_cast<QSvgFillStyle*>
3539 (node->styleProperty(QSvgStyleProperty::FILL));
3540 if (fillStyle && fillStyle->style() && fillStyle->style()->type() == QSvgStyleProperty::PATTERN) {
3541 QSvgPatternStyle *patternStyle =
static_cast<QSvgPatternStyle *>(fillStyle->style());
3542 if (active.contains(patternStyle->patternNode()))
3546 QSvgStrokeStyle *strokeStyle =
static_cast<QSvgStrokeStyle*>
3547 (node->styleProperty(QSvgStyleProperty::STROKE));
3548 if (strokeStyle && strokeStyle->style() && strokeStyle->style()->type() == QSvgStyleProperty::PATTERN) {
3549 QSvgPatternStyle *patternStyle =
static_cast<QSvgPatternStyle *>(strokeStyle->style());
3550 if (active.contains(patternStyle->patternNode()))
3559 if (Q_UNLIKELY(!node))
3561 switch (node->type()) {
3563 case QSvgNode::Group:
3564 case QSvgNode::Defs:
3565 case QSvgNode::Pattern:
3567 if (node->type() == QSvgNode::Pattern)
3568 active.append(node);
3570 auto *g =
static_cast<
const QSvgStructureNode*>(node);
3571 for (
auto &node : g->renderers()) {
3572 if (detectCycles(node.get(), active))
3579 if (active.contains(node))
3582 auto *u =
static_cast<
const QSvgUse*>(node);
3583 auto *target = u->link();
3586 if (detectCycles(target, active))
3591 case QSvgNode::Rect:
3592 case QSvgNode::Ellipse:
3593 case QSvgNode::Circle:
3594 case QSvgNode::Line:
3595 case QSvgNode::Path:
3596 case QSvgNode::Polygon:
3597 case QSvgNode::Polyline:
3598 case QSvgNode::Tspan:
3599 if (detectPatternCycles(node, active))
3609 const bool cycleFound = detectCycles(node);
3611 qCWarning(lcSvgHandler,
"Cycles detected in SVG, document discarded.");
3619void QSvgHandler::parse()
3621 xml->setNamespaceProcessing(
false);
3622#ifndef QT_NO_CSSPARSER
3626 int remainingUnfinishedElements = unfinishedElementsLimit;
3627 while (!xml->atEnd() && !done) {
3628 switch (xml->readNext()) {
3629 case QXmlStreamReader::StartElement:
3638 if (remainingUnfinishedElements && startElement(xml->name(), xml->attributes())) {
3639 --remainingUnfinishedElements;
3646 case QXmlStreamReader::EndElement:
3647 done = endElement(xml->name());
3648 ++remainingUnfinishedElements;
3650 case QXmlStreamReader::Characters:
3651 characters(xml->text());
3653 case QXmlStreamReader::ProcessingInstruction:
3654 processingInstruction(xml->processingInstructionTarget(), xml->processingInstructionData());
3660 resolvePaintServers(m_doc);
3662 if (detectCyclesAndWarn(m_doc)) {
3668bool QSvgHandler::startElement(
const QStringView localName,
3669 const QXmlStreamAttributes &attributes)
3671 QSvgNode *node =
nullptr;
3676
3677
3678 const QStringView xmlSpace(attributes.value(QLatin1String(
"xml:space")));
3679 if (xmlSpace.isNull()) {
3681 m_whitespaceMode.push(m_whitespaceMode.isEmpty() ? QSvgText::Default : m_whitespaceMode.top());
3682 }
else if (xmlSpace == QLatin1String(
"preserve")) {
3683 m_whitespaceMode.push(QSvgText::Preserve);
3684 }
else if (xmlSpace == QLatin1String(
"default")) {
3685 m_whitespaceMode.push(QSvgText::Default);
3687 const QByteArray msg =
'"' + xmlSpace.toLocal8Bit()
3688 +
"\" is an invalid value for attribute xml:space. "
3689 "Valid values are \"preserve\" and \"default\".";
3690 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3691 m_whitespaceMode.push(QSvgText::Default);
3694 if (!m_doc && localName != QLatin1String(
"svg"))
3697 if (m_doc && localName == QLatin1String(
"svg")) {
3698 m_skipNodes.push(Doc);
3699 qCWarning(lcSvgHandler) <<
"Skipping a nested svg element, because "
3700 "SVG Document must not contain nested svg elements in Svg Tiny 1.2";
3703 if (!m_skipNodes.isEmpty() && m_skipNodes.top() == Doc)
3706 if (FactoryMethod method = findGroupFactory(localName, options())) {
3709 node = method(
nullptr, attributes,
this);
3711 Q_ASSERT(node->type() == QSvgNode::Doc);
3712 m_doc =
static_cast<QSvgDocument*>(node);
3715 switch (m_nodes.top()->type()) {
3717 case QSvgNode::Group:
3718 case QSvgNode::Defs:
3719 case QSvgNode::Switch:
3720 case QSvgNode::Mask:
3721 case QSvgNode::Symbol:
3722 case QSvgNode::Marker:
3723 case QSvgNode::Pattern:
3725 node = method(m_nodes.top(), attributes,
this);
3727 QSvgStructureNode *group =
3728 static_cast<QSvgStructureNode*>(m_nodes.top());
3729 group->addChild(std::unique_ptr<QSvgNode>(node), someId(attributes));
3734 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
3735 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3741 parseCoreNode(node, attributes);
3742 parseStyle(node, attributes,
this);
3743 if (node->type() == QSvgNode::Filter)
3744 m_toBeResolved.append(node);
3746 }
else if (FactoryMethod method = findGraphicsFactory(localName, options())) {
3748 Q_ASSERT(!m_nodes.isEmpty());
3749 switch (m_nodes.top()->type()) {
3751 case QSvgNode::Group:
3752 case QSvgNode::Defs:
3753 case QSvgNode::Switch:
3754 case QSvgNode::Mask:
3755 case QSvgNode::Symbol:
3756 case QSvgNode::Marker:
3757 case QSvgNode::Pattern:
3759 if (localName == QLatin1String(
"tspan")) {
3760 const QByteArray msg = QByteArrayLiteral(
"\'tspan\' element in wrong context.");
3761 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3764 node = method(m_nodes.top(), attributes,
this);
3766 QSvgStructureNode *group =
3767 static_cast<QSvgStructureNode*>(m_nodes.top());
3768 group->addChild(std::unique_ptr<QSvgNode>(node), someId(attributes));
3772 case QSvgNode::Text:
3773 case QSvgNode::Textarea:
3774 if (localName == QLatin1String(
"tspan")) {
3775 node = method(m_nodes.top(), attributes,
this);
3777 static_cast<QSvgText *>(m_nodes.top())->addTspan(
static_cast<QSvgTspan *>(node));
3780 const QByteArray msg = QByteArrayLiteral(
"\'text\' or \'textArea\' element contains invalid element type.");
3781 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3785 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
3786 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3791 parseCoreNode(node, attributes);
3792 parseStyle(node, attributes,
this);
3793 if (node->type() == QSvgNode::Text || node->type() == QSvgNode::Textarea) {
3794 static_cast<QSvgText *>(node)->setWhitespaceMode(m_whitespaceMode.top());
3795 }
else if (node->type() == QSvgNode::Tspan) {
3796 static_cast<QSvgTspan *>(node)->setWhitespaceMode(m_whitespaceMode.top());
3797 }
else if (node->type() == QSvgNode::Use) {
3798 auto useNode =
static_cast<QSvgUse *>(node);
3799 if (!useNode->isResolved())
3800 m_toBeResolved.append(useNode);
3803 }
else if (FactoryMethod method = findFilterFactory(localName, options())) {
3805 Q_ASSERT(!m_nodes.isEmpty());
3806 if (m_nodes.top()->type() == QSvgNode::Filter ||
3807 (m_nodes.top()->type() == QSvgNode::FeMerge && localName == QLatin1String(
"feMergeNode"))) {
3808 node = method(m_nodes.top(), attributes,
this);
3810 QSvgStructureNode *container =
3811 static_cast<QSvgStructureNode*>(m_nodes.top());
3812 container->addChild(std::unique_ptr<QSvgNode>(node), someId(attributes));
3815 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
3816 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3818 }
else if (AnimationMethod method = findAnimationFactory(localName, options())) {
3819 Q_ASSERT(!m_nodes.isEmpty());
3820 node = method(m_nodes.top(), attributes,
this);
3822 QSvgAnimateNode *anim =
static_cast<QSvgAnimateNode *>(node);
3823 if (anim->linkId().isEmpty())
3824 m_doc->animator()->appendAnimation(m_nodes.top(), anim);
3825 else if (m_doc->namedNode(anim->linkId()))
3826 m_doc->animator()->appendAnimation(m_doc->namedNode(anim->linkId()), anim);
3828 m_toBeResolved.append(anim);
3830 }
else if (ParseMethod method = findUtilFactory(localName, options())) {
3831 Q_ASSERT(!m_nodes.isEmpty());
3832 if (!method(m_nodes.top(), attributes,
this))
3833 qCWarning(lcSvgHandler,
"%s", msgProblemParsing(localName, xml).constData());
3834 }
else if (StyleFactoryMethod method = findStyleFactoryMethod(localName)) {
3835 QSvgStyleProperty *prop = method(attributes,
this);
3838 m_nodes.top()->appendStyleProperty(prop, someId(attributes));
3840 const QByteArray msg = QByteArrayLiteral(
"Could not parse node: ") + localName.toLocal8Bit();
3841 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3843 }
else if (StyleParseMethod method = findStyleUtilFactoryMethod(localName)) {
3845 if (!method(m_style, attributes,
this))
3846 qCWarning(lcSvgHandler,
"%s", msgProblemParsing(localName, xml).constData());
3849 qCDebug(lcSvgHandler) <<
"Skipping unknown element" << localName;
3850 m_skipNodes.push(Unknown);
3856 m_skipNodes.push(Graphics);
3859 m_skipNodes.push(Style);
3864bool QSvgHandler::endElement(
const QStringView localName)
3866 CurrentNode node = m_skipNodes.top();
3868 if (node == Doc && localName != QLatin1String(
"svg"))
3872 m_whitespaceMode.pop();
3876 if (node == Unknown)
3879#ifdef QT_NO_CSSPARSER
3880 Q_UNUSED(localName);
3882 if (m_inStyle && localName == QLatin1String(
"style"))
3886 if (node == Graphics)
3888 else if (m_style && !m_skipNodes.isEmpty() && m_skipNodes.top() != Style)
3891 return ((localName == QLatin1String(
"svg")) && (node != Doc));
3894void QSvgHandler::resolvePaintServers(QSvgNode *node,
int nestedDepth)
3896 if (!node || (node->type() != QSvgNode::Doc && node->type() != QSvgNode::Group
3897 && node->type() != QSvgNode::Defs && node->type() != QSvgNode::Switch)) {
3901 QSvgStructureNode *structureNode =
static_cast<QSvgStructureNode *>(node);
3903 for (
auto &node : structureNode->renderers()) {
3904 QSvgFillStyle *fill =
static_cast<QSvgFillStyle *>(node->styleProperty(QSvgStyleProperty::FILL));
3905 if (fill && !fill->isPaintStyleResolved()) {
3906 QString id = fill->paintStyleId();
3907 QSvgPaintStyleProperty *style = m_doc->namedStyle(id);
3909 fill->setFillStyle(style);
3911 qCWarning(lcSvgHandler,
"%s", msgCouldNotResolveProperty(id, xml).constData());
3912 fill->setBrush(Qt::NoBrush);
3916 QSvgStrokeStyle *stroke =
static_cast<QSvgStrokeStyle *>(node->styleProperty(QSvgStyleProperty::STROKE));
3917 if (stroke && !stroke->isPaintStyleResolved()) {
3918 QString id = stroke->paintStyleId();
3919 QSvgPaintStyleProperty *style = m_doc->namedStyle(id);
3921 stroke->setStyle(style);
3923 qCWarning(lcSvgHandler,
"%s", msgCouldNotResolveProperty(id, xml).constData());
3924 stroke->setStroke(Qt::NoBrush);
3928 if (nestedDepth < 2048)
3929 resolvePaintServers(node.get(), nestedDepth + 1);
3933void QSvgHandler::resolveNodes()
3935 for (QSvgNode *node : std::as_const(m_toBeResolved)) {
3936 if (node->type() == QSvgNode::Use) {
3937 QSvgUse *useNode =
static_cast<QSvgUse *>(node);
3938 const auto parent = useNode->parent();
3942 QSvgNode::Type t = parent->type();
3943 if (t != QSvgNode::Doc && t != QSvgNode::Defs && t != QSvgNode::Group && t != QSvgNode::Switch)
3946 QSvgStructureNode *group =
static_cast<QSvgStructureNode *>(parent);
3947 QSvgNode *link = group->scopeNode(useNode->linkId());
3949 qCWarning(lcSvgHandler,
"link #%s is undefined!",
qPrintable(useNode->linkId()));
3953 if (useNode->parent()->isDescendantOf(link))
3954 qCWarning(lcSvgHandler,
"link #%s is recursive!",
qPrintable(useNode->linkId()));
3956 useNode->setLink(link);
3957 }
else if (node->type() == QSvgNode::Filter) {
3958 QSvgFilterContainer *filter =
static_cast<QSvgFilterContainer *>(node);
3959 for (
auto &renderer : filter->renderers()) {
3960 const QSvgFeFilterPrimitive *primitive = QSvgFeFilterPrimitive::castToFilterPrimitive(renderer.get());
3961 if (!primitive || primitive->type() == QSvgNode::FeUnsupported) {
3962 filter->setSupported(
false);
3966 }
else if (node->type() == QSvgNode::AnimateTransform || node->type() == QSvgNode::AnimateColor) {
3967 QSvgAnimateNode *anim =
static_cast<QSvgAnimateNode *>(node);
3968 QSvgNode *targetNode = m_doc->namedNode(anim->linkId());
3970 m_doc->animator()->appendAnimation(targetNode, anim);
3972 qCWarning(lcSvgHandler,
"Cannot find target for link #%s!",
3978 m_toBeResolved.clear();
3981bool QSvgHandler::characters(
const QStringView str)
3983#ifndef QT_NO_CSSPARSER
3985 m_cssHandler.parseStyleSheet(str);
3989 if (m_skipNodes.isEmpty() || m_skipNodes.top() == Unknown || m_nodes.isEmpty())
3992 if (m_nodes.top()->type() == QSvgNode::Text || m_nodes.top()->type() == QSvgNode::Textarea) {
3993 static_cast<QSvgText*>(m_nodes.top())->addText(str);
3994 }
else if (m_nodes.top()->type() == QSvgNode::Tspan) {
3995 static_cast<QSvgTspan*>(m_nodes.top())->addText(str);
4001QIODevice *QSvgHandler::device()
const
4003 return xml->device();
4006QSvgDocument *QSvgHandler::document()
const
4011QSvgUtils::LengthType QSvgHandler::defaultCoordinateSystem()
const
4013 return m_defaultCoords;
4016void QSvgHandler::setDefaultCoordinateSystem(QSvgUtils::LengthType type)
4018 m_defaultCoords = type;
4021void QSvgHandler::pushColor(
const QColor &color)
4023 m_colorStack.push(color);
4024 m_colorTagCount.push(1);
4027void QSvgHandler::pushColorCopy()
4029 if (m_colorTagCount.size())
4030 ++m_colorTagCount.top();
4032 pushColor(Qt::black);
4035void QSvgHandler::popColor()
4037 if (m_colorTagCount.size()) {
4038 if (!--m_colorTagCount.top()) {
4040 m_colorTagCount.pop();
4045QColor QSvgHandler::currentColor()
const
4047 if (!m_colorStack.isEmpty())
4048 return m_colorStack.top();
4050 return QColor(0, 0, 0);
4053#ifndef QT_NO_CSSPARSER
4055void QSvgHandler::setInStyle(
bool b)
4060bool QSvgHandler::inStyle()
const
4065QSvgCssHandler &QSvgHandler::cssHandler()
4067 return m_cssHandler;
4072bool QSvgHandler::processingInstruction(
const QStringView target,
const QStringView data)
4074#ifdef QT_NO_CSSPARSER
4078 if (target == QLatin1String(
"xml-stylesheet")) {
4079 static const QRegularExpression rx(
QStringLiteral(
"type=\\\"(.+)\\\""),
4080 QRegularExpression::InvertedGreedinessOption);
4081 QRegularExpressionMatchIterator iter = rx.globalMatchView(data);
4083 while (iter.hasNext()) {
4084 QRegularExpressionMatch match = iter.next();
4085 QString type = match.captured(1);
4086 if (type.toLower() == QLatin1String(
"text/css")) {
4092 static const QRegularExpression rx(
QStringLiteral(
"href=\\\"(.+)\\\""),
4093 QRegularExpression::InvertedGreedinessOption);
4094 QRegularExpressionMatch match = rx.matchView(data);
4095 QString addr = match.captured(1);
4099 QFile file(fi.absoluteFilePath());
4100 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
4103 QByteArray cssData = file.readAll();
4104 QString css = QString::fromUtf8(cssData);
4105 m_cssHandler.parseStyleSheet(css);
4115void QSvgHandler::setAnimPeriod(
int start,
int end)
4118 m_animEnd = qMax(end, m_animEnd);
4121int QSvgHandler::animationDuration()
const
4126QSvgHandler::~QSvgHandler()
The QPolygonF class provides a list of points using floating point precision.
void parseNumbersArray(QStringView *str, QVarLengthArray< qreal, 8 > &points, const char *pattern)
qreal toDouble(QStringView *str)
Combined button and popup list for selecting options.
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 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)
void setAlpha(QStringView opacity, QColor *color)
static void parseColor(QSvgNode *, const QSvgAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createCircleNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
bool qsvg_get_hex_rgb(const QChar *str, int len, QRgb *rgb)
static void parseNumberTriplet(QList< qreal > &values, QStringView *s)
static std::optional< QRectF > parseViewBox(QStringView str)
static 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 void parseNumberTriplet(QList< qreal > &values, QStringView s)
static FontSizeSpec fontSizeSpec(QStringView spec)
bool qsvg_get_hex_rgb(const char *name, QRgb *rgb)
static QSvgNode * createTextNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgStyleProperty * createRadialGradientNode(const QXmlStreamAttributes &attributes, 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 void parseBaseGradient(const QXmlStreamAttributes &attributes, QSvgGradientStyle *gradProp, QSvgHandler *handler)
static QList< qreal > parsePercentageList(QStringView 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 *)
static QSvgNode * createPatternNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static bool parseHandlerNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static AnimationMethod findAnimationFactory(const QStringView name, QtSvg::Options options)
static bool parseCoreNode(QSvgNode *node, const QXmlStreamAttributes &attributes)
static bool constructColor(QStringView colorStr, QStringView opacity, QColor &color, QSvgHandler *handler)
static bool parseHkernNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void generateKeyFrames(QList< qreal > &keyFrames, uint count)
static void parsePen(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createSvgNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgStyleProperty * createFontNode(const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static void parseFont(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
QList< qreal > parseNumbersList(QStringView *str)
static bool parseForeignObjectNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseTbreakNode(QSvgNode *parent, const QXmlStreamAttributes &, QSvgHandler *)
static QSvgNode * createFeUnsupportedNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QList< QStringView > splitWithDelimiter(QStringView delimitedList)
static QSvgNode * createFeOffsetNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createFeFloodNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createMarkerNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createFeColorMatrixNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createFeGaussianBlurNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createPolylineNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
bool(* ParseMethod)(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *)
static bool parseFontFaceNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QStringView idFromIRI(QStringView iri)
static bool parseMissingGlyphNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parsePrefetchNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseCssAnimations(QSvgNode *node, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static int qsvg_hex2int(const char *s, bool *ok=nullptr)
static bool parseGlyphNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static int qsvg_h2i(char hex, bool *ok=nullptr)
static int qsvg_hex2int(char s, bool *ok=nullptr)
static QSvgNode * createVideoNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static 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)
QSvgNode * createAnimateTransformNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgStyleProperty * createLinearGradientNode(const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createFeMergeNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createFeCompositeNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseSetNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgStyleProperty * createSolidColorNode(const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QStringView idFromFuncIRI(QStringView iri)
static QSvgNode * createFeBlendNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createFilterNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createEllipseNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createTextAreaNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
QSvgStyleProperty *(* StyleFactoryMethod)(const QXmlStreamAttributes &, QSvgHandler *)
static QSvgNode * createAimateMotionNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
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