5#include "qplatformdefs.h"
23#include <QtCore/private/qdataurl_p.h>
31#include <qregularexpression.h>
43Q_LOGGING_CATEGORY(lcSvgHandler,
"qt.svg")
45static const char *qt_inherit_text =
"inherit";
46#define QT_INHERIT QLatin1String(qt_inherit_text)
52 if (
const QFile *file = qobject_cast<
const QFile *>(r->device()))
53 result.append(QFile::encodeName(QDir::toNativeSeparators(file->fileName())));
55 result.append(QByteArrayLiteral(
"<input>"));
57 result.append(QByteArray::number(r->lineNumber()));
58 if (
const qint64 column = r->columnNumber()) {
60 result.append(QByteArray::number(column));
62 result.append(QByteArrayLiteral(
": "));
70 return prefixMessage(
"Problem parsing " + localName.toLocal8Bit(), r);
75 return prefixMessage(
"Could not resolve property: " + id.toLocal8Bit(), r);
80 static const QRegularExpression delimiterRE(
QStringLiteral(
"[,\\s]+"));
81 return delimitedList.split(delimiterRE, Qt::SkipEmptyParts);
86static inline int qsvg_h2i(
char hex,
bool *ok =
nullptr)
88 if (hex >=
'0' && hex <=
'9')
90 if (hex >=
'a' && hex <=
'f')
91 return hex -
'a' + 10;
92 if (hex >=
'A' && hex <=
'F')
93 return hex -
'A' + 10;
115 const size_t len = qstrlen(name);
122 }
else if (len == 9) {
126 }
else if (len == 6) {
130 }
else if (len == 3) {
137 if ((uint)r > 255 || (uint)g > 255 || (uint)b > 255 || !ok) {
141 *rgb = qRgb(r, g ,b);
150 for(
int i = 0; i < len; ++i)
151 tmp[i] = str[i].toLatin1();
153 return qsvg_get_hex_rgb(tmp, rgb);
158static inline QString
someId(
const QXmlStreamAttributes &attributes)
160 QStringView id = attributes.value(QLatin1String(
"id"));
162 id = attributes.value(QLatin1String(
"xml:id"));
163 return id.toString();
169 void setAttributes(
const QXmlStreamAttributes &attributes, QSvgHandler *handler);
211 setAttributes(xmlAttributes, handler);
216 for (
const QXmlStreamAttribute &attribute : attributes) {
217 QStringView name = attribute.qualifiedName();
220 QStringView value = attribute.value();
222 switch (name.at(0).unicode()) {
225 if (name == QLatin1String(
"color"))
227 else if (name == QLatin1String(
"color-opacity"))
228 colorOpacity = value;
229 else if (name == QLatin1String(
"comp-op"))
234 if (name == QLatin1String(
"display"))
239 if (name == QLatin1String(
"fill"))
241 else if (name == QLatin1String(
"fill-rule"))
243 else if (name == QLatin1String(
"fill-opacity"))
245 else if (name == QLatin1String(
"font-family"))
247 else if (name == QLatin1String(
"font-size"))
249 else if (name == QLatin1String(
"font-style"))
251 else if (name == QLatin1String(
"font-weight"))
253 else if (name == QLatin1String(
"font-variant"))
255 else if (name == QLatin1String(
"filter") &&
256 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
261 if (name == QLatin1String(
"id"))
262 id = value.toString();
263 else if (name == QLatin1String(
"image-rendering"))
264 imageRendering = value;
268 if (name == QLatin1String(
"mask") &&
269 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
271 if (name == QLatin1String(
"marker-start") &&
272 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
274 if (name == QLatin1String(
"marker-mid") &&
275 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
277 if (name == QLatin1String(
"marker-end") &&
278 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
283 if (name == QLatin1String(
"opacity"))
285 if (name == QLatin1String(
"offset"))
290 if (name.size() > 5 && name.mid(1, 5) == QLatin1String(
"troke")) {
291 QStringView strokeRef = name.mid(6, name.size() - 6);
292 if (strokeRef.isEmpty())
294 else if (strokeRef == QLatin1String(
"-dasharray"))
295 strokeDashArray = value;
296 else if (strokeRef == QLatin1String(
"-dashoffset"))
297 strokeDashOffset = value;
298 else if (strokeRef == QLatin1String(
"-linecap"))
299 strokeLineCap = value;
300 else if (strokeRef == QLatin1String(
"-linejoin"))
301 strokeLineJoin = value;
302 else if (strokeRef == QLatin1String(
"-miterlimit"))
303 strokeMiterLimit = value;
304 else if (strokeRef == QLatin1String(
"-opacity"))
305 strokeOpacity = value;
306 else if (strokeRef == QLatin1String(
"-width"))
308 }
else if (name == QLatin1String(
"stop-color"))
310 else if (name == QLatin1String(
"stop-opacity"))
315 if (name == QLatin1String(
"text-anchor"))
317 else if (name == QLatin1String(
"transform"))
322 if (name == QLatin1String(
"vector-effect"))
323 vectorEffect = value;
324 else if (name == QLatin1String(
"visibility"))
329 if (name == QLatin1String(
"xml:id") && id.isEmpty())
330 id = value.toString();
346 while (!str->isEmpty() && str->first().isSpace())
348 while (!str->isEmpty()
350 || str->startsWith(QLatin1Char(
'+')) || str->startsWith(QLatin1Char(
'.')))) {
352 points.append(QSvgUtils::toDouble(str));
354 while (!str->isEmpty() && str->first().isSpace())
356 if (str->startsWith(QLatin1Char(
',')))
360 while (!str->isEmpty() && str->first().isSpace())
371 while (!str.isEmpty() && str.first().isSpace())
373 while ((!str.isEmpty() && str.first() >= QLatin1Char(
'0') && str.first() <= QLatin1Char(
'9'))
374 || str.startsWith(QLatin1Char(
'-')) || str.startsWith(QLatin1Char(
'+'))
375 || str.startsWith(QLatin1Char(
'.'))) {
377 points.append(QSvgUtils::toDouble(&str));
379 while (!str.isEmpty() && str.first().isSpace())
381 if (str.startsWith(QLatin1Char(
'%')))
383 while (!str.isEmpty() && str.first().isSpace())
385 if (str.startsWith(QLatin1Char(
',')))
389 while (!str.isEmpty() && str.first().isSpace())
397
398
399
400
405 if (!iri.startsWith(QLatin1Char(
'#')))
406 return QStringView();
408 return iri.sliced(1);
412
413
414
415
416
421 if (!iri.startsWith(QLatin1StringView(
"url(")))
422 return QStringView();
426 const qsizetype closingBracePos = iri.indexOf(QLatin1Char(
')'));
427 if (closingBracePos == -1)
428 return QStringView();
430 iri = iri.first(closingBracePos);
431 return idFromIRI(iri);
435
436
437
438bool resolveColor(QStringView colorStr, QColor &color, QSvgHandler *handler)
440 QStringView colorStrTr = colorStr.trimmed();
441 if (colorStrTr.isEmpty())
444 switch(colorStrTr.at(0).unicode()) {
451 bool ok = qsvg_get_hex_rgb(colorStrTr.constData(), colorStrTr.size(), &rgb);
461 if (colorStrTr.size() >= 7 && colorStrTr.at(colorStrTr.size() - 1) == QLatin1Char(
')')
462 && colorStrTr.mid(0, 4) == QLatin1String(
"rgb(")) {
463 QStringView sv{ colorStrTr.sliced(4) };
464 QList<qreal> compo = parseNumbersList(&sv);
467 if (compo.size() == 1) {
468 compo = parsePercentageList(colorStrTr.sliced(4));
469 for (
int i = 0; i < compo.size(); ++i)
470 compo[i] *= (qreal)2.55;
473 if (compo.size() == 3) {
474 color = QColor(
int(compo[0]),
485 if (colorStrTr == QLatin1String(
"currentColor")) {
486 color = handler->currentColor();
498 color = QColor::fromString(colorStrTr);
499 return color.isValid();
505 qreal op = qBound(qreal(0.0), QSvgUtils::toDouble(opacity, &ok), qreal(1.0));
508 color->setAlphaF(op);
512 QColor &color, QSvgHandler *handler)
514 if (!resolveColor(colorStr, color, handler))
516 if (!opacity.isEmpty())
517 setAlpha(opacity, &color);
524 qreal num = QSvgUtils::parseLength(str.toString(), &type, ok);
531static bool createSvgGlyph(QSvgFont *font,
const QXmlStreamAttributes &attributes,
534 QStringView uncStr = attributes.value(QLatin1String(
"unicode"));
535 QStringView havStr = attributes.value(QLatin1String(
"horiz-adv-x"));
536 QStringView pathStr = attributes.value(QLatin1String(
"d"));
538 qreal havx = (havStr.isEmpty()) ? -1 : QSvgUtils::toDouble(havStr);
539 QPainterPath path = QSvgUtils::parsePathDataFast(pathStr).value_or(QPainterPath());
541 path.setFillRule(Qt::WindingFill);
543 if (isMissingGlyph) {
544 if (!uncStr.isEmpty())
545 qWarning(
"Ignoring missing-glyph's 'unicode' attribute");
546 return font->addMissingGlyph(path, havx);
549 if (uncStr.isEmpty()) {
550 qWarning(
"glyph does not define a non-empty 'unicode' attribute and will be ignored");
553 font->addGlyph(uncStr.toString(), path, havx);
559 QSvgHandler *handler)
562 if (constructColor(attributes.color, attributes.colorOpacity, color, handler)) {
564 handler->pushColor(color);
570 QStringView id = idFromFuncIRI(url);
571 return doc ? doc->paintServer(id) :
nullptr;
576 QSvgHandler *handler)
578 if (!attributes.fill.isEmpty() || !attributes.fillRule.isEmpty() || !attributes.fillOpacity.isEmpty()) {
579 QSvgFillStyle *prop =
new QSvgFillStyle;
582 if (!attributes.fillRule.isEmpty() && attributes.fillRule !=
QT_INHERIT) {
583 if (attributes.fillRule == QLatin1String(
"evenodd"))
584 prop->setFillRule(Qt::OddEvenFill);
585 else if (attributes.fillRule == QLatin1String(
"nonzero"))
586 prop->setFillRule(Qt::WindingFill);
590 if (!attributes.fillOpacity.isEmpty() && attributes.fillOpacity !=
QT_INHERIT) {
591 prop->setFillOpacity(qMin(qreal(1.0), qMax(qreal(0.0), QSvgUtils::toDouble(attributes.fillOpacity))));
595 if ((!attributes.fill.isEmpty()) && (attributes.fill !=
QT_INHERIT) ) {
596 if (attributes.fill.startsWith(QLatin1String(
"url"))) {
597 QStringView value = attributes.fill;
598 QSvgPaintServerSharedPtr paintServer = paintServerFromUrl(handler->document(), value);
600 prop->setPaintServer(paintServer);
602 QString id = idFromFuncIRI(value).toString();
603 prop->setPaintStyleId(id);
604 handler->pushUnresolvedStyle(prop);
606 }
else if (attributes.fill != QLatin1String(
"none")) {
608 if (resolveColor(attributes.fill, color, handler))
609 prop->setBrush(QBrush(color));
611 prop->setBrush(QBrush(Qt::NoBrush));
614 node->appendStyleProperty(prop);
627 while (!value.isEmpty()) {
628 if (value.first().isSpace() || value.startsWith(QLatin1Char(
','))) {
640 State state = Matrix;
641 if (value.startsWith(QLatin1Char(
'm'))) {
642 const char *ident =
"atrix";
643 for (
int i = 0; i < 5; ++i)
644 if (!value.slice(1).startsWith(QLatin1Char(ident[i])))
648 }
else if (value.startsWith(QLatin1Char(
't'))) {
649 const char *ident =
"ranslate";
650 for (
int i = 0; i < 8; ++i)
651 if (!value.slice(1).startsWith(QLatin1Char(ident[i])))
655 }
else if (value.startsWith(QLatin1Char(
'r'))) {
656 const char *ident =
"otate";
657 for (
int i = 0; i < 5; ++i)
658 if (!value.slice(1).startsWith(QLatin1Char(ident[i])))
662 }
else if (value.startsWith(QLatin1Char(
's'))) {
664 if (value.startsWith(QLatin1Char(
'c'))) {
665 const char *ident =
"ale";
666 for (
int i = 0; i < 3; ++i)
667 if (!value.slice(1).startsWith(QLatin1Char(ident[i])))
671 }
else if (value.startsWith(QLatin1Char(
'k'))) {
672 if (!value.slice(1).startsWith(QLatin1Char(
'e')))
674 if (!value.slice(1).startsWith(QLatin1Char(
'w')))
677 if (value.startsWith(QLatin1Char(
'X')))
679 else if (value.startsWith(QLatin1Char(
'Y')))
691 while (!value.isEmpty() && value.first().isSpace())
693 if (!value.startsWith(QLatin1Char(
'(')))
696 QVarLengthArray<qreal, 8> points;
698 if (!value.startsWith(QLatin1Char(
')')))
702 if(state == Matrix) {
703 if(points.size() != 6)
705 matrix = QTransform(points[0], points[1],
706 points[2], points[3],
707 points[4], points[5]) * matrix;
708 }
else if (state == Translate) {
709 if (points.size() == 1)
710 matrix.translate(points[0], 0);
711 else if (points.size() == 2)
712 matrix.translate(points[0], points[1]);
715 }
else if (state == Rotate) {
716 if(points.size() == 1) {
717 matrix.rotate(points[0]);
718 }
else if (points.size() == 3) {
719 matrix.translate(points[1], points[2]);
720 matrix.rotate(points[0]);
721 matrix.translate(-points[1], -points[2]);
725 }
else if (state == Scale) {
726 if (points.size() < 1 || points.size() > 2)
728 qreal sx = points[0];
730 if(points.size() == 2)
732 matrix.scale(sx, sy);
733 }
else if (state == SkewX) {
734 if (points.size() != 1)
736 matrix.shear(qTan(qDegreesToRadians(points[0])), 0);
737 }
else if (state == SkewY) {
738 if (points.size() != 1)
740 matrix.shear(0, qTan(qDegreesToRadians(points[0])));
749 QSvgHandler *handler)
751 if (!attributes.stroke.isEmpty() || !attributes.strokeDashArray.isEmpty() || !attributes.strokeDashOffset.isEmpty() || !attributes.strokeLineCap.isEmpty()
752 || !attributes.strokeLineJoin.isEmpty() || !attributes.strokeMiterLimit.isEmpty() || !attributes.strokeOpacity.isEmpty() || !attributes.strokeWidth.isEmpty()
753 || !attributes.vectorEffect.isEmpty()) {
755 QSvgStrokeStyle *prop =
new QSvgStrokeStyle;
758 if ((!attributes.stroke.isEmpty()) && (attributes.stroke !=
QT_INHERIT) ) {
759 if (attributes.stroke.startsWith(QLatin1String(
"url"))) {
760 QStringView value = attributes.stroke;
761 QSvgPaintServerSharedPtr paintServer = paintServerFromUrl(handler->document(), value);
763 prop->setPaintServer(paintServer);
765 QString id = idFromFuncIRI(value).toString();
766 prop->setPaintStyleId(id);
767 handler->pushUnresolvedStyle(prop);
769 }
else if (attributes.stroke != QLatin1String(
"none")) {
771 if (resolveColor(attributes.stroke, color, handler))
772 prop->setStroke(QBrush(color));
774 prop->setStroke(QBrush(Qt::NoBrush));
779 if (!attributes.strokeWidth.isEmpty() && attributes.strokeWidth !=
QT_INHERIT) {
781 prop->setWidth(QSvgUtils::parseLength(attributes.strokeWidth, <));
785 if (!attributes.strokeDashArray.isEmpty() && attributes.strokeDashArray !=
QT_INHERIT) {
786 if (attributes.strokeDashArray == QLatin1String(
"none")) {
787 prop->setDashArrayNone();
789 QStringView dashArray = attributes.strokeDashArray;
790 QList<qreal> dashes = parseNumbersList(&dashArray);
791 const bool allZeroes =
std::all_of(dashes.cbegin(), dashes.cend(),
792 [](qreal i) {
return qFuzzyIsNull(i); });
793 const bool hasNegative = !allZeroes &&
std::any_of(dashes.cbegin(), dashes.cend(),
794 [](qreal i) {
return i < 0.; });
797 qCWarning(lcSvgHandler) <<
"QSvgHandler: Stroke dash array "
798 "with a negative value is invalid";
801 if (allZeroes || hasNegative) {
802 prop->setDashArrayNone();
805 if ((dashes.size() & 1) != 0)
806 dashes << QList<qreal>(dashes);
807 prop->setDashArray(dashes);
813 if (!attributes.strokeLineJoin.isEmpty()) {
814 if (attributes.strokeLineJoin == QLatin1String(
"miter"))
815 prop->setLineJoin(Qt::SvgMiterJoin);
816 else if (attributes.strokeLineJoin == QLatin1String(
"round"))
817 prop->setLineJoin(Qt::RoundJoin);
818 else if (attributes.strokeLineJoin == QLatin1String(
"bevel"))
819 prop->setLineJoin(Qt::BevelJoin);
823 if (!attributes.strokeLineCap.isEmpty()) {
824 if (attributes.strokeLineCap == QLatin1String(
"butt"))
825 prop->setLineCap(Qt::FlatCap);
826 else if (attributes.strokeLineCap == QLatin1String(
"round"))
827 prop->setLineCap(Qt::RoundCap);
828 else if (attributes.strokeLineCap == QLatin1String(
"square"))
829 prop->setLineCap(Qt::SquareCap);
833 if (!attributes.strokeDashOffset.isEmpty() && attributes.strokeDashOffset !=
QT_INHERIT)
834 prop->setDashOffset(QSvgUtils::toDouble(attributes.strokeDashOffset));
837 if (!attributes.vectorEffect.isEmpty()) {
838 if (attributes.vectorEffect == QLatin1String(
"non-scaling-stroke"))
839 prop->setVectorEffect(
true);
840 else if (attributes.vectorEffect == QLatin1String(
"none"))
841 prop->setVectorEffect(
false);
845 if (!attributes.strokeMiterLimit.isEmpty() && attributes.strokeMiterLimit !=
QT_INHERIT)
846 prop->setMiterLimit(QSvgUtils::toDouble(attributes.strokeMiterLimit));
849 if (!attributes.strokeOpacity.isEmpty() && attributes.strokeOpacity !=
QT_INHERIT)
850 prop->setOpacity(qMin(qreal(1.0), qMax(qreal(0.0), QSvgUtils::toDouble(attributes.strokeOpacity))));
852 node->appendStyleProperty(prop);
860{ qreal(6.9), qreal(8.3), qreal(10.0), qreal(12.0), qreal(14.4), qreal(17.3), qreal(20.7) };
866 switch (spec.at(0).unicode()) {
868 if (spec == QLatin1String(
"xx-small"))
870 if (spec == QLatin1String(
"x-small"))
872 if (spec == QLatin1String(
"x-large"))
874 if (spec == QLatin1String(
"xx-large"))
878 if (spec == QLatin1String(
"small"))
882 if (spec == QLatin1String(
"medium"))
886 if (spec == QLatin1String(
"large"))
890 if (spec == QLatin1String(
"none"))
903 if (attributes.fontFamily.isEmpty() && attributes.fontSize.isEmpty() && attributes.fontStyle.isEmpty() &&
904 attributes.fontWeight.isEmpty() && attributes.fontVariant.isEmpty() && attributes.textAnchor.isEmpty())
907 QSvgFontStyle *fontStyle =
nullptr;
908 if (!attributes.fontFamily.isEmpty()) {
909 QSvgDocument *doc = node->document();
911 QSvgFont *svgFont = doc->svgFont(attributes.fontFamily.toString());
913 fontStyle =
new QSvgFontStyle(svgFont, doc);
917 fontStyle =
new QSvgFontStyle;
918 if (!attributes.fontFamily.isEmpty() && attributes.fontFamily !=
QT_INHERIT) {
919 QStringView family = attributes.fontFamily.trimmed();
920 if (!family.isEmpty() && (family.at(0) == QLatin1Char(
'\'') || family.at(0) == QLatin1Char(
'\"')))
921 family = family.mid(1, family.size() - 2);
922 fontStyle->setFamily(family.toString());
925 if (!attributes.fontSize.isEmpty() && attributes.fontSize !=
QT_INHERIT) {
927 const FontSizeSpec spec = fontSizeSpec(attributes.fontSize);
933 qreal fs = QSvgUtils::parseLength(attributes.fontSize, &type);
934 fs = QSvgUtils::convertToPixels(fs,
true, type);
935 fontStyle->setSize(qMin(fs, qreal(0xffff)));
939 fontStyle->setSize(sizeTable[spec]);
944 if (!attributes.fontStyle.isEmpty() && attributes.fontStyle !=
QT_INHERIT) {
945 if (attributes.fontStyle == QLatin1String(
"normal")) {
946 fontStyle->setStyle(QFont::StyleNormal);
947 }
else if (attributes.fontStyle == QLatin1String(
"italic")) {
948 fontStyle->setStyle(QFont::StyleItalic);
949 }
else if (attributes.fontStyle == QLatin1String(
"oblique")) {
950 fontStyle->setStyle(QFont::StyleOblique);
954 if (!attributes.fontWeight.isEmpty() && attributes.fontWeight !=
QT_INHERIT) {
956 const int weightNum = attributes.fontWeight.toInt(&ok);
958 fontStyle->setWeight(weightNum);
960 if (attributes.fontWeight == QLatin1String(
"normal")) {
961 fontStyle->setWeight(QFont::Normal);
962 }
else if (attributes.fontWeight == QLatin1String(
"bold")) {
963 fontStyle->setWeight(QFont::Bold);
964 }
else if (attributes.fontWeight == QLatin1String(
"bolder")) {
965 fontStyle->setWeight(QSvgFontStyle::BOLDER);
966 }
else if (attributes.fontWeight == QLatin1String(
"lighter")) {
967 fontStyle->setWeight(QSvgFontStyle::LIGHTER);
972 if (!attributes.fontVariant.isEmpty() && attributes.fontVariant !=
QT_INHERIT) {
973 if (attributes.fontVariant == QLatin1String(
"normal"))
974 fontStyle->setVariant(QFont::MixedCase);
975 else if (attributes.fontVariant == QLatin1String(
"small-caps"))
976 fontStyle->setVariant(QFont::SmallCaps);
979 if (!attributes.textAnchor.isEmpty() && attributes.textAnchor !=
QT_INHERIT) {
980 if (attributes.textAnchor == QLatin1String(
"start"))
981 fontStyle->setTextAnchor(Qt::AlignLeft);
982 if (attributes.textAnchor == QLatin1String(
"middle"))
983 fontStyle->setTextAnchor(Qt::AlignHCenter);
984 else if (attributes.textAnchor == QLatin1String(
"end"))
985 fontStyle->setTextAnchor(Qt::AlignRight);
988 node->appendStyleProperty(fontStyle);
995 if (attributes.transform.isEmpty())
997 QTransform matrix = parseTransformationMatrix(attributes.transform.trimmed());
999 if (!matrix.isIdentity()) {
1000 node->appendStyleProperty(
new QSvgTransformStyle(QTransform(matrix)));
1009 QSvgNode *parent = node->parent();
1011 if (parent && (attributes.visibility.isEmpty() || attributes.visibility ==
QT_INHERIT))
1012 node->setVisible(parent->isVisible());
1013 else if (attributes.visibility == QLatin1String(
"hidden") || attributes.visibility == QLatin1String(
"collapse")) {
1014 node->setVisible(
false);
1016 node->setVisible(
true);
1020 const QXmlStreamAttributes &attributes,
1021 QSvgHandler *handler);
1027 str = str.trimmed();
1028 if (str.endsWith(QLatin1String(
"ms"))) {
1031 }
else if (str.endsWith(QLatin1String(
"s"))) {
1034 double val = ms * QSvgUtils::toDouble(str, ok);
1036 if (val >
std::numeric_limits<
int>::min() && val <
std::numeric_limits<
int>::max())
1037 res =
static_cast<
int>(val);
1044#ifndef QT_NO_CSSPARSER
1047 const QXmlStreamAttributes &attributes,
1048 QSvgHandler *handler)
1050 QSvgCssProperties cssAnimProps(attributes);
1051 QList<QSvgAnimationProperty> parsedProperties = cssAnimProps.animations();
1053 for (
auto &property : parsedProperties) {
1054 QSvgCssAnimation *anim = handler->cssHandler().createAnimation(property.name);
1058 anim->setRunningTime(property.delay, property.duration);
1059 anim->setIterationCount(property.iteration);
1060 QSvgCssEasingPtr easing = handler->cssHandler().createEasing(property.easingFunction, property.easingValues);
1061 anim->setEasing(std::move(easing));
1063 handler->setAnimPeriod(property.delay, property.delay + property.duration);
1064 handler->document()->animator()->appendAnimation(node, anim);
1065 handler->document()->setAnimated(
true);
1070 const QXmlStreamAttributes &attributes)
1072 QSvgCssProperties cssProperties(attributes);
1073 QSvgOffsetProperty offset = cssProperties.offset();
1078 QSvgOffsetStyle *offsetStyle =
new QSvgOffsetStyle();
1079 offsetStyle->setPath(offset.path.value());
1080 offsetStyle->setRotateAngle(offset.angle);
1081 offsetStyle->setRotateType(offset.rotateType);
1082 offsetStyle->setDistance(offset.distance);
1083 node->appendStyleProperty(offsetStyle);
1088QtSvg::Options QSvgHandler::options()
const
1093QtSvg::AnimatorType QSvgHandler::animatorType()
const
1095 return m_animatorType;
1098bool QSvgHandler::trustedSourceMode()
const
1100 return m_options.testFlag(QtSvg::AssumeTrustedSource);
1105 QStringList lst = str.split(QLatin1Char(
','), Qt::SkipEmptyParts);
1110 const QXmlStreamAttributes &attributes)
1112 QStringList features;
1113 QStringList extensions;
1114 QStringList languages;
1115 QStringList formats;
1117 QStringView xmlClassStr;
1119 for (
const QXmlStreamAttribute &attribute : attributes) {
1120 QStringView name = attribute.qualifiedName();
1123 QStringView value = attribute.value();
1124 switch (name.at(0).unicode()) {
1126 if (name == QLatin1String(
"class"))
1127 xmlClassStr = value;
1130 if (name == QLatin1String(
"requiredFeatures"))
1131 features = stringToList(value.toString());
1132 else if (name == QLatin1String(
"requiredExtensions"))
1133 extensions = stringToList(value.toString());
1134 else if (name == QLatin1String(
"requiredFormats"))
1135 formats = stringToList(value.toString());
1136 else if (name == QLatin1String(
"requiredFonts"))
1137 fonts = stringToList(value.toString());
1140 if (name == QLatin1String(
"systemLanguage"))
1141 languages = stringToList(value.toString());
1148 node->setRequiredFeatures(features);
1149 node->setRequiredExtensions(extensions);
1150 node->setRequiredLanguages(languages);
1151 node->setRequiredFormats(formats);
1152 node->setRequiredFonts(fonts);
1153 node->setNodeId(someId(attributes));
1154 node->setXmlClass(xmlClassStr.toString());
1163 if (attributes.opacity.isEmpty())
1166 const QStringView value = attributes.opacity.trimmed();
1169 qreal op = value.toDouble(&ok);
1172 QSvgOpacityStyle *opacity =
new QSvgOpacityStyle(qBound(qreal(0.0), op, qreal(1.0)));
1173 node->appendStyleProperty(opacity);
1179#define NOOP qDebug()<<"Operation: "<<op<<" is not implemented"
1180 if (op == QLatin1String(
"clear")) {
1181 return QPainter::CompositionMode_Clear;
1182 }
else if (op == QLatin1String(
"src")) {
1183 return QPainter::CompositionMode_Source;
1184 }
else if (op == QLatin1String(
"dst")) {
1185 return QPainter::CompositionMode_Destination;
1186 }
else if (op == QLatin1String(
"src-over")) {
1187 return QPainter::CompositionMode_SourceOver;
1188 }
else if (op == QLatin1String(
"dst-over")) {
1189 return QPainter::CompositionMode_DestinationOver;
1190 }
else if (op == QLatin1String(
"src-in")) {
1191 return QPainter::CompositionMode_SourceIn;
1192 }
else if (op == QLatin1String(
"dst-in")) {
1193 return QPainter::CompositionMode_DestinationIn;
1194 }
else if (op == QLatin1String(
"src-out")) {
1195 return QPainter::CompositionMode_SourceOut;
1196 }
else if (op == QLatin1String(
"dst-out")) {
1197 return QPainter::CompositionMode_DestinationOut;
1198 }
else if (op == QLatin1String(
"src-atop")) {
1199 return QPainter::CompositionMode_SourceAtop;
1200 }
else if (op == QLatin1String(
"dst-atop")) {
1201 return QPainter::CompositionMode_DestinationAtop;
1202 }
else if (op == QLatin1String(
"xor")) {
1203 return QPainter::CompositionMode_Xor;
1204 }
else if (op == QLatin1String(
"plus")) {
1205 return QPainter::CompositionMode_Plus;
1206 }
else if (op == QLatin1String(
"multiply")) {
1207 return QPainter::CompositionMode_Multiply;
1208 }
else if (op == QLatin1String(
"screen")) {
1209 return QPainter::CompositionMode_Screen;
1210 }
else if (op == QLatin1String(
"overlay")) {
1211 return QPainter::CompositionMode_Overlay;
1212 }
else if (op == QLatin1String(
"darken")) {
1213 return QPainter::CompositionMode_Darken;
1214 }
else if (op == QLatin1String(
"lighten")) {
1215 return QPainter::CompositionMode_Lighten;
1216 }
else if (op == QLatin1String(
"color-dodge")) {
1217 return QPainter::CompositionMode_ColorDodge;
1218 }
else if (op == QLatin1String(
"color-burn")) {
1219 return QPainter::CompositionMode_ColorBurn;
1220 }
else if (op == QLatin1String(
"hard-light")) {
1221 return QPainter::CompositionMode_HardLight;
1222 }
else if (op == QLatin1String(
"soft-light")) {
1223 return QPainter::CompositionMode_SoftLight;
1224 }
else if (op == QLatin1String(
"difference")) {
1225 return QPainter::CompositionMode_Difference;
1226 }
else if (op == QLatin1String(
"exclusion")) {
1227 return QPainter::CompositionMode_Exclusion;
1232 return QPainter::CompositionMode_SourceOver;
1239 if (attributes.compOp.isEmpty())
1241 QStringView value = attributes.compOp.trimmed();
1243 if (!value.isEmpty()) {
1244 QSvgCompOpStyle *compop =
new QSvgCompOpStyle(svgToQtCompositionMode(value));
1245 node->appendStyleProperty(compop);
1251 if (str == QLatin1String(
"inline")) {
1252 return QSvgNode::InlineMode;
1253 }
else if (str == QLatin1String(
"block")) {
1254 return QSvgNode::BlockMode;
1255 }
else if (str == QLatin1String(
"list-item")) {
1256 return QSvgNode::ListItemMode;
1257 }
else if (str == QLatin1String(
"run-in")) {
1258 return QSvgNode::RunInMode;
1259 }
else if (str == QLatin1String(
"compact")) {
1260 return QSvgNode::CompactMode;
1261 }
else if (str == QLatin1String(
"marker")) {
1262 return QSvgNode::MarkerMode;
1263 }
else if (str == QLatin1String(
"table")) {
1264 return QSvgNode::TableMode;
1265 }
else if (str == QLatin1String(
"inline-table")) {
1266 return QSvgNode::InlineTableMode;
1267 }
else if (str == QLatin1String(
"table-row-group")) {
1268 return QSvgNode::TableRowGroupMode;
1269 }
else if (str == QLatin1String(
"table-header-group")) {
1270 return QSvgNode::TableHeaderGroupMode;
1271 }
else if (str == QLatin1String(
"table-footer-group")) {
1272 return QSvgNode::TableFooterGroupMode;
1273 }
else if (str == QLatin1String(
"table-row")) {
1274 return QSvgNode::TableRowMode;
1275 }
else if (str == QLatin1String(
"table-column-group")) {
1276 return QSvgNode::TableColumnGroupMode;
1277 }
else if (str == QLatin1String(
"table-column")) {
1278 return QSvgNode::TableColumnMode;
1279 }
else if (str == QLatin1String(
"table-cell")) {
1280 return QSvgNode::TableCellMode;
1281 }
else if (str == QLatin1String(
"table-caption")) {
1282 return QSvgNode::TableCaptionMode;
1283 }
else if (str == QLatin1String(
"none")) {
1284 return QSvgNode::NoneMode;
1286 return QSvgNode::InheritMode;
1288 return QSvgNode::BlockMode;
1295 if (attributes.display.isEmpty())
1297 QStringView displayStr = attributes.display.trimmed();
1299 if (!displayStr.isEmpty()) {
1300 node->setDisplayMode(displayStringToEnum(displayStr));
1306 if (attribute.isEmpty())
1307 return std::nullopt;
1309 return idFromFuncIRI(attribute);
1314 QSvgHandler *handler)
1319 if (
auto id = getAttributeId(attributes.mask))
1320 node->setMaskId(id->toString());
1321 if (
auto id = getAttributeId(attributes.markerStart))
1322 node->setMarkerStartId(id->toString());
1323 if (
auto id = getAttributeId(attributes.markerMid))
1324 node->setMarkerMidId(id->toString());
1325 if (
auto id = getAttributeId(attributes.markerEnd))
1326 node->setMarkerEndId(id->toString());
1327 if (
auto id = getAttributeId(attributes.filter))
1328 node->setFilterId(id->toString());
1335 if (attributes.imageRendering.isEmpty())
1338 QStringView ir = attributes.imageRendering.trimmed();
1339 QSvgQualityStyle *p =
new QSvgQualityStyle(0);
1340 if (ir == QLatin1String(
"auto"))
1341 p->setImageRendering(QSvgQualityStyle::ImageRenderingAuto);
1342 else if (ir == QLatin1String(
"optimizeSpeed"))
1343 p->setImageRendering(QSvgQualityStyle::ImageRenderingOptimizeSpeed);
1344 else if (ir == QLatin1String(
"optimizeQuality"))
1345 p->setImageRendering(QSvgQualityStyle::ImageRenderingOptimizeQuality);
1346 node->appendStyleProperty(p);
1350 const QXmlStreamAttributes &attributes,
1351 QSvgHandler *handler)
1359#ifndef QT_NO_CSSPARSER
1360 QXmlStreamAttributes cssAttributes;
1361 handler->cssHandler().styleLookup(node, cssAttributes);
1363 QStringView style = attributes.value(QLatin1String(
"style"));
1364 if (!style.isEmpty())
1365 handler->cssHandler().parseCSStoXMLAttrs(style.toString(), cssAttributes);
1366 svgAttributes.setAttributes(cssAttributes, handler);
1368 parseOffsetPath(node, cssAttributes);
1370 parseCssAnimations(node, cssAttributes, handler);
1389 const QXmlStreamAttributes &attributes,
1392 Q_UNUSED(parent); Q_UNUSED(attributes);
1397 const QXmlStreamAttributes &attributes,
1398 QSvgAnimateNode *anim,
1399 QSvgHandler *handler)
1401 const QStringView beginStr = attributes.value(QLatin1String(
"begin"));
1402 const QStringView durStr = attributes.value(QLatin1String(
"dur"));
1403 const QStringView endStr = attributes.value(QLatin1String(
"end"));
1404 const QStringView repeatStr = attributes.value(QLatin1String(
"repeatCount"));
1405 const QStringView fillStr = attributes.value(QLatin1String(
"fill"));
1406 const QStringView addtv = attributes.value(QLatin1String(
"additive"));
1407 QStringView linkId = attributes.value(QLatin1String(
"xlink:href"));
1409 if (linkId.isEmpty())
1410 linkId = attributes.value(QLatin1String(
"href"));
1412 linkId = idFromIRI(linkId);
1415 int begin = parseClockValue(beginStr, &ok);
1418 int dur = parseClockValue(durStr, &ok);
1421 int end = parseClockValue(endStr, &ok);
1424 qreal repeatCount = (repeatStr == QLatin1String(
"indefinite")) ? -1 :
1425 qMax(1.0, QSvgUtils::toDouble(repeatStr));
1427 QSvgAnimateNode::Fill fill = (fillStr == QLatin1String(
"freeze")) ? QSvgAnimateNode::Freeze :
1428 QSvgAnimateNode::Remove;
1430 QSvgAnimateNode::Additive additive = (addtv == QLatin1String(
"sum")) ? QSvgAnimateNode::Sum :
1431 QSvgAnimateNode::Replace;
1433 anim->setRunningTime(begin, dur, end, 0);
1434 anim->setRepeatCount(repeatCount);
1435 anim->setFill(fill);
1436 anim->setAdditiveType(additive);
1437 anim->setLinkId(linkId.toString());
1439 parent->document()->setAnimated(
true);
1441 handler->setAnimPeriod(begin, begin + dur);
1450 qreal spacing = 1.0f / (count - 1);
1451 for (uint i = 0; i < count; i++) {
1452 keyFrames.append(i * spacing);
1457 const QXmlStreamAttributes &attributes,
1458 QSvgHandler *handler)
1460 const QStringView fromStr = attributes.value(QLatin1String(
"from"));
1461 const QStringView toStr = attributes.value(QLatin1String(
"to"));
1462 const QStringView valuesStr = attributes.value(QLatin1String(
"values"));
1463 const QString targetStr = attributes.value(QLatin1String(
"attributeName")).toString();
1465 if (targetStr != QLatin1String(
"fill") && targetStr != QLatin1String(
"stroke"))
1468 QList<QColor> colors;
1469 if (valuesStr.isEmpty()) {
1470 QColor startColor, endColor;
1471 resolveColor(fromStr, startColor, handler);
1472 resolveColor(toStr, endColor, handler);
1474 colors.append(startColor);
1475 colors.append(endColor);
1477 for (
auto part : qTokenize(valuesStr, u';')) {
1479 resolveColor(part, color, handler);
1480 colors.append(color);
1484 QSvgAnimatedPropertyColor *prop =
static_cast<QSvgAnimatedPropertyColor *>
1485 (QSvgAbstractAnimatedProperty::createAnimatedProperty(targetStr));
1489 prop->setColors(colors);
1491 QList<qreal> keyFrames;
1492 generateKeyFrames(keyFrames, colors.size());
1493 prop->setKeyFrames(keyFrames);
1495 QSvgAnimateColor *anim =
new QSvgAnimateColor(parent);
1496 anim->appendProperty(prop);
1498 if (!parseBaseAnimate(parent, attributes, anim, handler)) {
1507 const QXmlStreamAttributes &attributes,
1510 Q_UNUSED(parent); Q_UNUSED(attributes);
1516 QList<qreal> list = parseNumbersList(s);
1518 for (
int i = 3 - list.size(); i > 0; --i)
1524 parseNumberTriplet(values, &s);
1528 const QXmlStreamAttributes &attributes,
1529 QSvgHandler *handler)
1531 const QStringView typeStr = attributes.value(QLatin1String(
"type"));
1532 const QStringView values = attributes.value(QLatin1String(
"values"));
1533 const QStringView fromStr = attributes.value(QLatin1String(
"from"));
1534 const QStringView toStr = attributes.value(QLatin1String(
"to"));
1535 const QStringView byStr = attributes.value(QLatin1String(
"by"));
1538 if (values.isEmpty()) {
1539 if (fromStr.isEmpty()) {
1540 if (!byStr.isEmpty()) {
1544 parseNumberTriplet(vals, byStr);
1550 if (!toStr.isEmpty()) {
1552 parseNumberTriplet(vals, fromStr);
1553 parseNumberTriplet(vals, toStr);
1554 }
else if (!byStr.isEmpty()) {
1556 parseNumberTriplet(vals, fromStr);
1557 parseNumberTriplet(vals, byStr);
1558 for (
int i = vals.size() - 3; i < vals.size(); ++i)
1559 vals[i] += vals[i - 3];
1565 QStringView s = values;
1566 while (!s.isEmpty()) {
1567 parseNumberTriplet(vals, &s);
1572 if (vals.size() % 3 != 0)
1576 QList<QSvgAnimatedPropertyTransform::TransformComponent> components;
1577 for (
int i = 0; i <= vals.size() - 3; i += 3) {
1578 QSvgAnimatedPropertyTransform::TransformComponent component;
1579 if (typeStr == QLatin1String(
"translate")) {
1580 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Translate;
1581 component.values.append(vals.at(i));
1582 component.values.append(vals.at(i + 1));
1583 }
else if (typeStr == QLatin1String(
"scale")) {
1584 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Scale;
1585 component.values.append(vals.at(i));
1586 component.values.append(vals.at(i + 1));
1587 }
else if (typeStr == QLatin1String(
"rotate")) {
1588 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Rotate;
1589 component.values.append(vals.at(i));
1590 component.values.append(vals.at(i + 1));
1591 component.values.append(vals.at(i + 2));
1592 }
else if (typeStr == QLatin1String(
"skewX")) {
1593 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Skew;
1594 component.values.append(vals.at(i));
1595 component.values.append(0);
1596 }
else if (typeStr == QLatin1String(
"skewY")) {
1597 component.type = QSvgAnimatedPropertyTransform::TransformComponent::Skew;
1598 component.values.append(0);
1599 component.values.append(vals.at(i));
1603 components.append(component);
1606 QSvgAnimatedPropertyTransform *prop =
static_cast<QSvgAnimatedPropertyTransform *>
1607 (QSvgAbstractAnimatedProperty::createAnimatedProperty(QLatin1String(
"transform")));
1611 prop->appendComponents(components);
1613 prop->setTransformCount(1);
1614 QList<qreal> keyFrames;
1615 generateKeyFrames(keyFrames, vals.size() / 3);
1616 prop->setKeyFrames(keyFrames);
1618 QSvgAnimateTransform *anim =
new QSvgAnimateTransform(parent);
1619 anim->appendProperty(prop);
1621 if (!parseBaseAnimate(parent, attributes, anim, handler)) {
1630 const QXmlStreamAttributes &attributes,
1633 Q_UNUSED(parent); Q_UNUSED(attributes);
1638 const QXmlStreamAttributes &attributes,
1641 Q_UNUSED(parent); Q_UNUSED(attributes);
1646 const QXmlStreamAttributes &attributes,
1649 const QStringView cx = attributes.value(QLatin1String(
"cx"));
1650 const QStringView cy = attributes.value(QLatin1String(
"cy"));
1651 const QStringView r = attributes.value(QLatin1String(
"r"));
1652 qreal ncx = QSvgUtils::toDouble(cx);
1653 qreal ncy = QSvgUtils::toDouble(cy);
1654 qreal nr = QSvgUtils::toDouble(r);
1658 QRectF rect(ncx-nr, ncy-nr, nr*2, nr*2);
1659 QSvgNode *circle =
new QSvgCircle(parent, rect);
1664 const QXmlStreamAttributes &attributes,
1667 Q_UNUSED(attributes);
1668 QSvgDefs *defs =
new QSvgDefs(parent);
1673 const QXmlStreamAttributes &attributes,
1676 Q_UNUSED(parent); Q_UNUSED(attributes);
1681 const QXmlStreamAttributes &attributes,
1684 const QStringView cx = attributes.value(QLatin1String(
"cx"));
1685 const QStringView cy = attributes.value(QLatin1String(
"cy"));
1686 const QStringView rx = attributes.value(QLatin1String(
"rx"));
1687 const QStringView ry = attributes.value(QLatin1String(
"ry"));
1688 qreal ncx = QSvgUtils::toDouble(cx);
1689 qreal ncy = QSvgUtils::toDouble(cy);
1690 qreal nrx = QSvgUtils::toDouble(rx);
1691 qreal nry = QSvgUtils::toDouble(ry);
1693 QRectF rect(ncx-nrx, ncy-nry, nrx*2, nry*2);
1694 QSvgNode *ellipse =
new QSvgEllipse(parent, rect);
1699 QSvgHandler *handler)
1701 const QStringView hax = attributes.value(QLatin1String(
"horiz-adv-x"));
1702 QString myId = someId(attributes);
1704 qreal horizAdvX = QSvgUtils::toDouble(hax);
1706 if (!myId.isEmpty()) {
1707 QSvgDocument *doc = handler->document();
1708 QSvgFont *font = doc->svgFont(myId);
1710 font =
new QSvgFont(horizAdvX);
1711 font->setFamilyName(myId);
1712 doc->addSvgFont(font);
1714 return new QSvgFontStyle(font, doc);
1720 const QXmlStreamAttributes &attributes,
1723 if (parent->type() != QSvgStyleProperty::FONT) {
1727 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
1728 QSvgFont *font = style->svgFont();
1729 const QStringView name = attributes.value(QLatin1String(
"font-family"));
1730 const QStringView unitsPerEmStr = attributes.value(QLatin1String(
"units-per-em"));
1732 qreal unitsPerEm = QSvgUtils::toDouble(unitsPerEmStr);
1734 unitsPerEm = QSvgFont::DEFAULT_UNITS_PER_EM;
1736 if (!name.isEmpty())
1737 font->setFamilyName(name.toString());
1738 font->setUnitsPerEm(unitsPerEm);
1740 if (!font->familyName().isEmpty())
1741 if (!style->doc()->svgFont(font->familyName()))
1742 style->doc()->addSvgFont(font);
1748 const QXmlStreamAttributes &attributes,
1751 if (parent->type() != QSvgStyleProperty::FONT) {
1755 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
1756 QSvgFont *font = style->svgFont();
1757 const QStringView name = attributes.value(QLatin1String(
"name"));
1759 if (!name.isEmpty())
1760 font->setFamilyName(name.toString());
1762 if (!font->familyName().isEmpty())
1763 if (!style->doc()->svgFont(font->familyName()))
1764 style->doc()->addSvgFont(font);
1770 const QXmlStreamAttributes &attributes,
1773 Q_UNUSED(parent); Q_UNUSED(attributes);
1778 const QXmlStreamAttributes &attributes,
1781 Q_UNUSED(parent); Q_UNUSED(attributes);
1786 const QXmlStreamAttributes &attributes,
1789 Q_UNUSED(parent); Q_UNUSED(attributes);
1794 const QXmlStreamAttributes &attributes,
1797 Q_UNUSED(attributes);
1798 QSvgG *node =
new QSvgG(parent);
1803 const QXmlStreamAttributes &attributes,
1806 if (parent->type() != QSvgStyleProperty::FONT) {
1810 QSvgFontStyle *style =
static_cast<QSvgFontStyle*>(parent);
1811 QSvgFont *font = style->svgFont();
1812 return createSvgGlyph(font, attributes,
false);
1816 const QXmlStreamAttributes &attributes,
1819 Q_UNUSED(parent); Q_UNUSED(attributes);
1824 const QXmlStreamAttributes &attributes,
1827 Q_UNUSED(parent); Q_UNUSED(attributes);
1832 const QXmlStreamAttributes &attributes,
1833 QSvgHandler *handler)
1835 const QStringView x = attributes.value(QLatin1String(
"x"));
1836 const QStringView y = attributes.value(QLatin1String(
"y"));
1837 const QStringView width = attributes.value(QLatin1String(
"width"));
1838 const QStringView height = attributes.value(QLatin1String(
"height"));
1839 QString filename = attributes.value(QLatin1String(
"xlink:href")).toString();
1841 filename = attributes.value(QLatin1String(
"href")).toString();
1842 qreal nx = QSvgUtils::toDouble(x);
1843 qreal ny = QSvgUtils::toDouble(y);
1845 qreal nwidth = QSvgUtils::parseLength(width.toString(), &type);
1846 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
1848 qreal nheight = QSvgUtils::parseLength(height.toString(), &type);
1849 nheight = QSvgUtils::convertToPixels(nheight,
false, type);
1851 filename = filename.trimmed();
1852 if (filename.isEmpty()) {
1853 qCWarning(lcSvgHandler) <<
"QSvgHandler: Image filename is empty";
1856 if (nwidth <= 0 || nheight <= 0) {
1857 qCWarning(lcSvgHandler) <<
"QSvgHandler: Width or height for" << filename <<
"image was not greater than 0";
1866 } filenameType = NotLoaded;
1868 if (filename.startsWith(QLatin1String(
"data"))) {
1871 if (qDecodeDataUrl(QUrl{filename}, mimeType, data)) {
1872 image = QImage::fromData(data);
1873 filenameType = LoadedFromData;
1877 if (image.isNull()) {
1878 const auto *file = qobject_cast<QFile *>(handler->device());
1881 if (url.isRelative()) {
1883 filename = info.absoluteDir().absoluteFilePath(filename);
1887 if (handler->trustedSourceMode() || !QImageReader::imageFormat(filename).startsWith(
"svg")) {
1888 image = QImage(filename);
1889 filenameType = LoadedFromFile;
1893 if (image.isNull()) {
1894 qCWarning(lcSvgHandler) <<
"Could not create image from" << filename;
1898 if (image.format() == QImage::Format_ARGB32)
1899 image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
1901 QSvgNode *img =
new QSvgImage(parent,
1903 filenameType == LoadedFromFile ? filename : QString{},
1912 const QXmlStreamAttributes &attributes,
1915 const QStringView x1 = attributes.value(QLatin1String(
"x1"));
1916 const QStringView y1 = attributes.value(QLatin1String(
"y1"));
1917 const QStringView x2 = attributes.value(QLatin1String(
"x2"));
1918 const QStringView y2 = attributes.value(QLatin1String(
"y2"));
1919 qreal nx1 = QSvgUtils::toDouble(x1);
1920 qreal ny1 = QSvgUtils::toDouble(y1);
1921 qreal nx2 = QSvgUtils::toDouble(x2);
1922 qreal ny2 = QSvgUtils::toDouble(y2);
1924 QLineF lineBounds(nx1, ny1, nx2, ny2);
1925 QSvgNode *line =
new QSvgLine(parent, lineBounds);
1931 QSvgGradientPaint *gradProp,
1932 QSvgHandler *handler)
1934 QStringView linkId = attributes.value(QLatin1String(
"xlink:href"));
1935 const QStringView trans = attributes.value(QLatin1String(
"gradientTransform"));
1936 const QStringView spread = attributes.value(QLatin1String(
"spreadMethod"));
1937 const QStringView units = attributes.value(QLatin1String(
"gradientUnits"));
1938 const QStringView colorStr = attributes.value(QLatin1String(
"color"));
1939 const QStringView colorOpacityStr = attributes.value(QLatin1String(
"color-opacity"));
1942 if (constructColor(colorStr, colorOpacityStr, color, handler)) {
1943 handler->popColor();
1944 handler->pushColor(color);
1948 QGradient *grad = gradProp->qgradient();
1949 linkId = idFromIRI(linkId);
1951 if (!linkId.isEmpty()) {
1952 QSvgPaintServerSharedPtr paintServer = handler->document()->paintServer(linkId);
1953 if (paintServer && paintServer->type() == QSvgPaintServer::Type::Gradient) {
1954 QSvgGradientPaint *inherited =
1955 static_cast<QSvgGradientPaint*>(paintServer.get());
1956 if (!inherited->stopLink().isEmpty()) {
1957 gradProp->setStopLink(inherited->stopLink(), handler->document());
1959 grad->setStops(inherited->qgradient()->stops());
1960 gradProp->setGradientStopsSet(inherited->gradientStopsSet());
1963 matrix = inherited->qtransform();
1965 gradProp->setStopLink(linkId.toString(), handler->document());
1969 if (!trans.isEmpty()) {
1970 matrix = parseTransformationMatrix(trans);
1971 gradProp->setTransform(matrix);
1972 }
else if (!matrix.isIdentity()) {
1973 gradProp->setTransform(matrix);
1976 if (!spread.isEmpty()) {
1977 if (spread == QLatin1String(
"pad")) {
1978 grad->setSpread(QGradient::PadSpread);
1979 }
else if (spread == QLatin1String(
"reflect")) {
1980 grad->setSpread(QGradient::ReflectSpread);
1981 }
else if (spread == QLatin1String(
"repeat")) {
1982 grad->setSpread(QGradient::RepeatSpread);
1986 if (units.isEmpty() || units == QLatin1String(
"objectBoundingBox")) {
1987 grad->setCoordinateMode(QGradient::ObjectMode);
1993 QSvgHandler *handler)
1995 const QStringView x1 = attributes.value(QLatin1String(
"x1"));
1996 const QStringView y1 = attributes.value(QLatin1String(
"y1"));
1997 const QStringView x2 = attributes.value(QLatin1String(
"x2"));
1998 const QStringView y2 = attributes.value(QLatin1String(
"y2"));
2006 nx1 = convertToNumber(x1);
2008 ny1 = convertToNumber(y1);
2010 nx2 = convertToNumber(x2);
2012 ny2 = convertToNumber(y2);
2015 grad->setInterpolationMode(QGradient::ComponentInterpolation);
2017 QSvgGradientPaintSharedPtr paintServer = std::make_shared<QSvgGradientPaint>(std::move(grad));
2018 parseBaseGradient(attributes, paintServer.get(), 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" ").constFirst()));
2326 stdDeviationY = qMax(0., QSvgUtils::toDouble(stdDeviationString.split(u" ").constLast()));
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);
2814 grad->setInterpolationMode(QGradient::ComponentInterpolation);
2816 QSvgGradientPaintSharedPtr paintServer = std::make_shared<QSvgGradientPaint>(std::move(grad));
2817 parseBaseGradient(attributes, paintServer.get(), 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 std::shared_ptr<QSvgSolidColorPaint> paintServer = std::make_shared<QSvgSolidColorPaint>(color);
2907 const QXmlStreamAttributes &attributes,
2908 QSvgHandler *handler)
2910 if (paintServer->type() != QSvgPaintServer::Type::Gradient)
2912 QString nodeIdStr = someId(attributes);
2913 QString xmlClassStr = attributes.value(QLatin1String(
"class")).toString();
2920 dummy.setNodeId(nodeIdStr);
2921 dummy.setXmlClass(xmlClassStr);
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 QSvgGradientPaint *gradientStyle =
static_cast<QSvgGradientPaint*>(paintServer);
2941 QStringView colorStr = attrs.stopColor;
2945 qreal offset = convertToNumber(attrs.offset, &ok);
2949 if (!constructColor(colorStr, attrs.stopOpacity, color, handler)) {
2951 if (!attrs.stopOpacity.isEmpty())
2952 setAlpha(attrs.stopOpacity, &color);
2955 QGradient *grad = gradientStyle->qgradient();
2957 offset = qMin(qreal(1), qMax(qreal(0), offset));
2959 if (gradientStyle->gradientStopsSet()) {
2960 stops = grad->stops();
2962 if (offset <= stops.back().first)
2963 offset = stops.back().first + FLT_EPSILON;
2968 if ((stops.size() == 1) || (stops.at(stops.size() - 2).first < 1.0 - FLT_EPSILON)) {
2969 stops.back().first = 1.0 - FLT_EPSILON;
2970 grad->setStops(stops);
2975 grad->setColorAt(offset, color);
2976 gradientStyle->setGradientStopsSet(
true);
2981 const QXmlStreamAttributes &attributes,
2982 QSvgHandler *handler)
2985#ifdef QT_NO_CSSPARSER
2986 Q_UNUSED(attributes);
2989 const QStringView type = attributes.value(QLatin1String(
"type"));
2990 if (type.compare(QLatin1String(
"text/css"), Qt::CaseInsensitive) == 0 || type.isNull())
2991 handler->setInStyle(
true);
2998 const QXmlStreamAttributes &attributes,
2999 QSvgHandler *handler)
3001 Q_UNUSED(parent); Q_UNUSED(attributes);
3003 QSvgDocument *node =
new QSvgDocument(handler->options(), handler->animatorType());
3004 const QStringView widthStr = attributes.value(QLatin1String(
"width"));
3005 const QStringView heightStr = attributes.value(QLatin1String(
"height"));
3006 const QStringView viewBoxStr = attributes.value(QLatin1String(
"viewBox"));
3010 if (!widthStr.isEmpty()) {
3011 width = QSvgUtils::parseLength(widthStr, &type);
3013 width = QSvgUtils::convertToPixels(width,
true, type);
3017 if (!heightStr.isEmpty()) {
3018 height = QSvgUtils::parseLength(heightStr, &type);
3020 height = QSvgUtils::convertToPixels(height,
false, type);
3024 auto viewBoxResult = parseViewBox(viewBoxStr);
3025 if (viewBoxResult) {
3026 node->setViewBox(*viewBoxResult);
3027 }
else if (width && height) {
3029 width = QSvgUtils::convertToPixels(width,
false, type);
3030 height = QSvgUtils::convertToPixels(height,
false, type);
3032 node->setViewBox(QRectF(0, 0, width, height));
3040 const QXmlStreamAttributes &attributes,
3043 Q_UNUSED(attributes);
3044 QSvgSwitch *node =
new QSvgSwitch(parent);
3049 const QXmlStreamAttributes &attributes,
3050 QSvgHandler *handler)
3052 const QStringView x = attributes.value(QLatin1String(
"x"));
3053 const QStringView y = attributes.value(QLatin1String(
"y"));
3054 const QStringView width = attributes.value(QLatin1String(
"width"));
3055 const QStringView height = attributes.value(QLatin1String(
"height"));
3056 const QStringView patternUnits = attributes.value(QLatin1String(
"patternUnits"));
3057 const QStringView patternContentUnits = attributes.value(QLatin1String(
"patternContentUnits"));
3058 const QStringView patternTransform = attributes.value(QLatin1String(
"patternTransform"));
3060 QtSvg::
UnitTypes nPatternUnits = patternUnits.contains(QLatin1String(
"userSpaceOnUse")) ?
3063 QtSvg::
UnitTypes nPatternContentUnits = patternContentUnits.contains(QLatin1String(
"objectBoundingBox")) ?
3066 const QStringView viewBoxStr = attributes.value(QLatin1String(
"viewBox"));
3071 qreal nx = QSvgUtils::parseLength(x, &type, &ok);
3072 nx = QSvgUtils::convertToPixels(nx,
true, type);
3076 nx = (nx / 100.) * handler->document()->viewBox().width();
3080 qreal ny = QSvgUtils::parseLength(y, &type, &ok);
3081 ny = QSvgUtils::convertToPixels(ny,
true, type);
3085 ny = (ny / 100.) * handler->document()->viewBox().height();
3089 qreal nwidth = QSvgUtils::parseLength(width, &type, &ok);
3090 nwidth = QSvgUtils::convertToPixels(nwidth,
true, type);
3094 nwidth = (nwidth / 100.) * handler->document()->viewBox().width();
3096 nwidth = nwidth / 100.;
3098 qreal nheight = QSvgUtils::parseLength(height, &type, &ok);
3099 nheight = QSvgUtils::convertToPixels(nheight,
true, type);
3103 nheight = (nheight / 100.) * handler->document()->viewBox().height();
3105 nheight = nheight / 100.;
3108 auto viewBoxResult = parseViewBox(viewBoxStr);
3109 if (viewBoxResult) {
3110 if (viewBoxResult->width() > 0 && viewBoxResult->height() > 0)
3111 viewBox = *viewBoxResult;
3115 if (!patternTransform.isEmpty())
3116 matrix = parseTransformationMatrix(patternTransform);
3118 QRectF bounds(nx, ny, nwidth, nheight);
3119 if (bounds.isEmpty())
3122 QSvgRectF patternRectF(bounds, nPatternUnits, nPatternUnits, nPatternUnits, nPatternUnits);
3123 QSvgPattern *node =
new QSvgPattern(parent, patternRectF, viewBox, nPatternContentUnits, matrix);
3126 QSvgPaintServerSharedPtr prop = std::make_shared<QSvgPatternPaint>(node);
3127 handler->document()->addPaintServer(prop, someId(attributes));
3133 const QXmlStreamAttributes &,
3136 if (parent->type() != QSvgNode::Textarea)
3138 static_cast<QSvgText*>(parent)->addLineBreak();
3143 const QXmlStreamAttributes &attributes,
3146 const QStringView x = attributes.value(QLatin1String(
"x"));
3147 const QStringView y = attributes.value(QLatin1String(
"y"));
3150 qreal nx = QSvgUtils::parseLength(x, &type);
3151 nx = QSvgUtils::convertToPixels(nx,
true, type);
3152 qreal ny = QSvgUtils::parseLength(y, &type);
3153 ny = QSvgUtils::convertToPixels(ny,
true, type);
3155 QSvgNode *text =
new QSvgText(parent, QPointF(nx, ny));
3160 const QXmlStreamAttributes &attributes,
3161 QSvgHandler *handler)
3163 QSvgText *node =
static_cast<QSvgText *>(createTextNode(parent, attributes, handler));
3166 qreal width = QSvgUtils::parseLength(attributes.value(QLatin1String(
"width")), &type);
3167 qreal height = QSvgUtils::parseLength(attributes.value(QLatin1String(
"height")), &type);
3168 node->setTextArea(QSizeF(width, height));
3174 const QXmlStreamAttributes &,
3177 return new QSvgTspan(parent);
3181 const QXmlStreamAttributes &attributes,
3184 QStringView linkId = attributes.value(QLatin1String(
"xlink:href"));
3185 const QStringView xStr = attributes.value(QLatin1String(
"x"));
3186 const QStringView yStr = attributes.value(QLatin1String(
"y"));
3187 QSvgStructureNode *group =
nullptr;
3189 if (linkId.isEmpty())
3190 linkId = attributes.value(QLatin1String(
"href"));
3191 QString linkIdStr = idFromIRI(linkId).toString();
3193 switch (parent->type()) {
3195 case QSvgNode::Defs:
3196 case QSvgNode::Group:
3197 case QSvgNode::Switch:
3198 case QSvgNode::Mask:
3199 case QSvgNode::Symbol:
3200 case QSvgNode::Marker:
3201 case QSvgNode::Pattern:
3202 group =
static_cast<QSvgStructureNode*>(parent);
3210 if (!xStr.isNull() || !yStr.isNull()) {
3212 qreal nx = QSvgUtils::parseLength(xStr, &type);
3213 nx = QSvgUtils::convertToPixels(nx,
true, type);
3215 qreal ny = QSvgUtils::parseLength(yStr, &type);
3216 ny = QSvgUtils::convertToPixels(ny,
true, type);
3217 pt = QPointF(nx, ny);
3220 QSvgNode *link = group->scopeNode(linkIdStr);
3222 if (parent->isDescendantOf(link))
3223 qCWarning(lcSvgHandler,
"link %ls is recursive!",
qUtf16Printable(linkIdStr));
3225 return new QSvgUse(pt, parent, link);
3229 return new QSvgUse(pt, parent, linkIdStr);
3232 qCWarning(lcSvgHandler,
"<use> element %ls in wrong context!",
qUtf16Printable(linkIdStr));
3237 const QXmlStreamAttributes &attributes,
3240 Q_UNUSED(parent); Q_UNUSED(attributes);
3244typedef QSvgNode *(*FactoryMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3251 QStringView ref = name.mid(1);
3252 switch (name.at(0).unicode()) {
3254 if (ref == QLatin1String(
"efs"))
return createDefsNode;
3260 if (ref.isEmpty())
return createGNode;
3267 if (ref == QLatin1String(
"vg"))
return createSvgNode;
3268 if (ref == QLatin1String(
"witch"))
return createSwitchNode;
3286 QStringView ref = name.mid(1);
3287 switch (name.at(0).unicode()) {
3289 if (ref == QLatin1String(
"ircle"))
return createCircleNode;
3292 if (ref == QLatin1String(
"llipse"))
return createEllipseNode;
3295 if (ref == QLatin1String(
"mage"))
return createImageNode;
3298 if (ref == QLatin1String(
"ine"))
return createLineNode;
3301 if (ref == QLatin1String(
"ath"))
return createPathNode;
3302 if (ref == QLatin1String(
"olygon"))
return createPolygonNode;
3303 if (ref == QLatin1String(
"olyline"))
return createPolylineNode;
3306 if (ref == QLatin1String(
"ect"))
return createRectNode;
3309 if (ref == QLatin1String(
"ext"))
return createTextNode;
3310 if (ref == QLatin1String(
"extArea"))
return createTextAreaNode;
3311 if (ref == QLatin1String(
"span"))
return createTspanNode;
3314 if (ref == QLatin1String(
"se"))
return createUseNode;
3317 if (ref == QLatin1String(
"ideo"))
return createVideoNode;
3333 if (!name.startsWith(QLatin1String(
"fe")))
3336 if (name == QLatin1String(
"feMerge"))
return createFeMergeNode;
3337 if (name == QLatin1String(
"feColorMatrix"))
return createFeColorMatrixNode;
3338 if (name == QLatin1String(
"feGaussianBlur"))
return createFeGaussianBlurNode;
3339 if (name == QLatin1String(
"feOffset"))
return createFeOffsetNode;
3340 if (name == QLatin1String(
"feMergeNode"))
return createFeMergeNodeNode;
3341 if (name == QLatin1String(
"feComposite"))
return createFeCompositeNode;
3342 if (name == QLatin1String(
"feFlood"))
return createFeFloodNode;
3343 if (name == QLatin1String(
"feBlend"))
return createFeBlendNode;
3345 static const QStringList unsupportedFilters = {
3362 if (unsupportedFilters.contains(name))
3363 return createFeUnsupportedNode;
3368typedef QSvgNode *(*AnimationMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3375 QStringView ref = name.mid(1);
3377 switch (name.at(0).unicode()) {
3379 if (ref == QLatin1String(
"nimate"))
return createAnimateNode;
3380 if (ref == QLatin1String(
"nimateColor"))
return createAnimateColorNode;
3381 if (ref == QLatin1String(
"nimateMotion"))
return createAnimateMotionNode;
3382 if (ref == QLatin1String(
"nimateTransform"))
return createAnimateTransformNode;
3391typedef bool (*
ParseMethod)(QSvgNode *,
const QXmlStreamAttributes &, QSvgHandler *);
3398 QStringView ref = name.mid(1);
3399 switch (name.at(0).unicode()) {
3401 if (ref.isEmpty())
return parseAnchorNode;
3402 if (ref == QLatin1String(
"udio"))
return parseAudioNode;
3405 if (ref == QLatin1String(
"iscard"))
return parseDiscardNode;
3408 if (ref == QLatin1String(
"oreignObject"))
return parseForeignObjectNode;
3411 if (ref == QLatin1String(
"andler"))
return parseHandlerNode;
3412 if (ref == QLatin1String(
"kern"))
return parseHkernNode;
3415 if (ref == QLatin1String(
"etadata"))
return parseMetadataNode;
3416 if (ref == QLatin1String(
"path"))
return parseMpathNode;
3421 if (ref == QLatin1String(
"refetch"))
return parsePrefetchNode;
3424 if (ref == QLatin1String(
"cript"))
return parseScriptNode;
3425 if (ref == QLatin1String(
"et"))
return parseSetNode;
3426 if (ref == QLatin1String(
"tyle"))
return parseStyleNode;
3429 if (ref == QLatin1String(
"break"))
return parseTbreakNode;
3445 QStringView ref = name.mid(1);
3446 switch (name.at(0).unicode()) {
3448 if (ref == QLatin1String(
"ont"))
return createFontNode;
3457 const QXmlStreamAttributes &,
3465 QStringView ref = name.mid(1);
3466 switch (name.at(0).unicode()) {
3493 QStringView ref = name.sliced(1);
3494 switch (name.at(0).unicode()) {
3496 if (ref == QLatin1String(
"inearGradient"))
return createLinearGradientNode;
3499 if (ref == QLatin1String(
"adialGradient"))
return createRadialGradientNode;
3502 if (ref == QLatin1String(
"olidColor"))
return createSolidColorNode;
3511 const QXmlStreamAttributes &,
3519 QStringView ref = name.sliced(1);
3520 switch (name.at(0).unicode()) {
3522 if (ref == QLatin1String(
"top"))
return parseStopNode;
3530QSvgHandler::QSvgHandler(QIODevice *device, QtSvg::Options options,
3531 QtSvg::AnimatorType type)
3532 : xml(
new QXmlStreamReader(device))
3533 , m_ownsReader(
true)
3534 , m_options(options)
3535 , m_animatorType(type)
3540QSvgHandler::QSvgHandler(
const QByteArray &data, QtSvg::Options options,
3541 QtSvg::AnimatorType type)
3542 : xml(
new QXmlStreamReader(data))
3543 , m_ownsReader(
true)
3544 , m_options(options)
3545 , m_animatorType(type)
3550QSvgHandler::QSvgHandler(QXmlStreamReader *
const reader, QtSvg::Options options,
3551 QtSvg::AnimatorType type)
3553 , m_ownsReader(
false)
3554 , m_options(options)
3555 , m_animatorType(type)
3560void QSvgHandler::init()
3565 m_defaultCoords = QSvgUtils::LT_PX;
3566 m_defaultPen = QPen(Qt::black, 1, Qt::SolidLine, Qt::FlatCap, Qt::SvgMiterJoin);
3567 m_defaultPen.setMiterLimit(4);
3573 QSvgFillStyle *fillStyle =
static_cast<QSvgFillStyle*>
3574 (node->styleProperty(QSvgStyleProperty::FILL));
3575 if (fillStyle && fillStyle->paintServer()
3576 && fillStyle->paintServer()->type() == QSvgPaintServer::Type::Pattern) {
3577 QSvgPatternPaint *patternStyle =
static_cast<QSvgPatternPaint *>(fillStyle->paintServer());
3578 if (active.contains(patternStyle->patternNode()))
3582 QSvgStrokeStyle *strokeStyle =
static_cast<QSvgStrokeStyle*>
3583 (node->styleProperty(QSvgStyleProperty::STROKE));
3584 if (strokeStyle && strokeStyle->paintServer()
3585 && strokeStyle->paintServer()->type() == QSvgPaintServer::Type::Pattern) {
3586 QSvgPatternPaint *patternStyle =
static_cast<QSvgPatternPaint *>(strokeStyle->paintServer());
3587 if (active.contains(patternStyle->patternNode()))
3596 if (Q_UNLIKELY(!node))
3598 switch (node->type()) {
3600 case QSvgNode::Group:
3601 case QSvgNode::Defs:
3602 case QSvgNode::Pattern:
3604 if (node->type() == QSvgNode::Pattern)
3605 active.append(node);
3607 auto *g =
static_cast<
const QSvgStructureNode*>(node);
3608 for (
auto &node : g->renderers()) {
3609 if (detectCycles(node.get(), active))
3616 if (active.contains(node))
3619 auto *u =
static_cast<
const QSvgUse*>(node);
3620 auto *target = u->link();
3623 if (detectCycles(target, active))
3628 case QSvgNode::Rect:
3629 case QSvgNode::Ellipse:
3630 case QSvgNode::Circle:
3631 case QSvgNode::Line:
3632 case QSvgNode::Path:
3633 case QSvgNode::Polygon:
3634 case QSvgNode::Polyline:
3635 case QSvgNode::Tspan:
3636 if (detectPatternCycles(node, active))
3646 const bool cycleFound = detectCycles(node);
3648 qCWarning(lcSvgHandler,
"Cycles detected in SVG, document discarded.");
3656void QSvgHandler::parse()
3658 xml->setNamespaceProcessing(
false);
3659#ifndef QT_NO_CSSPARSER
3663 int remainingUnfinishedElements = unfinishedElementsLimit;
3664 while (!xml->atEnd() && !done) {
3665 switch (xml->readNext()) {
3666 case QXmlStreamReader::StartElement:
3675 if (remainingUnfinishedElements && startElement(xml->name(), xml->attributes())) {
3676 --remainingUnfinishedElements;
3683 case QXmlStreamReader::EndElement:
3684 done = endElement(xml->name());
3685 ++remainingUnfinishedElements;
3687 case QXmlStreamReader::Characters:
3688 characters(xml->text());
3690 case QXmlStreamReader::ProcessingInstruction:
3691 processingInstruction(xml->processingInstructionTarget(), xml->processingInstructionData());
3697 resolvePaintServers();
3699 if (detectCyclesAndWarn(m_doc)) {
3705bool QSvgHandler::startElement(
const QStringView localName,
3706 const QXmlStreamAttributes &attributes)
3708 QSvgNode *node =
nullptr;
3713
3714
3715 const QStringView xmlSpace(attributes.value(QLatin1String(
"xml:space")));
3716 if (xmlSpace.isNull()) {
3718 m_whitespaceMode.push(m_whitespaceMode.isEmpty() ? QSvgText::Default : m_whitespaceMode.top());
3719 }
else if (xmlSpace == QLatin1String(
"preserve")) {
3720 m_whitespaceMode.push(QSvgText::Preserve);
3721 }
else if (xmlSpace == QLatin1String(
"default")) {
3722 m_whitespaceMode.push(QSvgText::Default);
3724 const QByteArray msg =
'"' + xmlSpace.toLocal8Bit()
3725 +
"\" is an invalid value for attribute xml:space. "
3726 "Valid values are \"preserve\" and \"default\".";
3727 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3728 m_whitespaceMode.push(QSvgText::Default);
3731 if (!m_doc && localName != QLatin1String(
"svg"))
3734 if (m_doc && localName == QLatin1String(
"svg")) {
3735 m_skipNodes.push(Doc);
3736 qCWarning(lcSvgHandler) <<
"Skipping a nested svg element, because "
3737 "SVG Document must not contain nested svg elements in Svg Tiny 1.2";
3740 if (!m_skipNodes.isEmpty() && m_skipNodes.top() == Doc)
3743 if (FactoryMethod method = findGroupFactory(localName, options())) {
3746 node = method(
nullptr, attributes,
this);
3748 Q_ASSERT(node->type() == QSvgNode::Doc);
3749 m_doc =
static_cast<QSvgDocument*>(node);
3752 switch (m_nodes.top()->type()) {
3754 case QSvgNode::Group:
3755 case QSvgNode::Defs:
3756 case QSvgNode::Switch:
3757 case QSvgNode::Mask:
3758 case QSvgNode::Symbol:
3759 case QSvgNode::Marker:
3760 case QSvgNode::Pattern:
3762 node = method(m_nodes.top(), attributes,
this);
3764 QSvgStructureNode *group =
3765 static_cast<QSvgStructureNode*>(m_nodes.top());
3766 group->addChild(std::unique_ptr<QSvgNode>(node), someId(attributes));
3771 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
3772 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3778 parseCoreNode(node, attributes);
3779 parseStyle(node, attributes,
this);
3780 if (node->type() == QSvgNode::Filter)
3781 m_toBeResolved.append(node);
3783 }
else if (FactoryMethod method = findGraphicsFactory(localName, options())) {
3785 Q_ASSERT(!m_nodes.isEmpty());
3786 switch (m_nodes.top()->type()) {
3788 case QSvgNode::Group:
3789 case QSvgNode::Defs:
3790 case QSvgNode::Switch:
3791 case QSvgNode::Mask:
3792 case QSvgNode::Symbol:
3793 case QSvgNode::Marker:
3794 case QSvgNode::Pattern:
3796 if (localName == QLatin1String(
"tspan")) {
3797 const QByteArray msg = QByteArrayLiteral(
"\'tspan\' element in wrong context.");
3798 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3801 node = method(m_nodes.top(), attributes,
this);
3803 QSvgStructureNode *group =
3804 static_cast<QSvgStructureNode*>(m_nodes.top());
3805 group->addChild(std::unique_ptr<QSvgNode>(node), someId(attributes));
3809 case QSvgNode::Text:
3810 case QSvgNode::Textarea:
3811 if (localName == QLatin1String(
"tspan")) {
3812 node = method(m_nodes.top(), attributes,
this);
3814 static_cast<QSvgText *>(m_nodes.top())->addTspan(
static_cast<QSvgTspan *>(node));
3817 const QByteArray msg = QByteArrayLiteral(
"\'text\' or \'textArea\' element contains invalid element type.");
3818 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3822 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
3823 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3828 parseCoreNode(node, attributes);
3829 parseStyle(node, attributes,
this);
3830 if (node->type() == QSvgNode::Text || node->type() == QSvgNode::Textarea) {
3831 static_cast<QSvgText *>(node)->setWhitespaceMode(m_whitespaceMode.top());
3832 }
else if (node->type() == QSvgNode::Tspan) {
3833 static_cast<QSvgTspan *>(node)->setWhitespaceMode(m_whitespaceMode.top());
3834 }
else if (node->type() == QSvgNode::Use) {
3835 auto useNode =
static_cast<QSvgUse *>(node);
3836 if (!useNode->isResolved())
3837 m_toBeResolved.append(useNode);
3840 }
else if (FactoryMethod method = findFilterFactory(localName, options())) {
3842 Q_ASSERT(!m_nodes.isEmpty());
3843 if (m_nodes.top()->type() == QSvgNode::Filter ||
3844 (m_nodes.top()->type() == QSvgNode::FeMerge && localName == QLatin1String(
"feMergeNode"))) {
3845 node = method(m_nodes.top(), attributes,
this);
3847 QSvgStructureNode *container =
3848 static_cast<QSvgStructureNode*>(m_nodes.top());
3849 container->addChild(std::unique_ptr<QSvgNode>(node), someId(attributes));
3852 const QByteArray msg = QByteArrayLiteral(
"Could not add child element to parent element because the types are incorrect.");
3853 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3855 }
else if (AnimationMethod method = findAnimationFactory(localName, options())) {
3856 Q_ASSERT(!m_nodes.isEmpty());
3857 node = method(m_nodes.top(), attributes,
this);
3859 QSvgAnimateNode *anim =
static_cast<QSvgAnimateNode *>(node);
3860 if (anim->linkId().isEmpty())
3861 m_doc->animator()->appendAnimation(m_nodes.top(), anim);
3862 else if (m_doc->namedNode(anim->linkId()))
3863 m_doc->animator()->appendAnimation(m_doc->namedNode(anim->linkId()), anim);
3865 m_toBeResolved.append(anim);
3867 }
else if (ParseMethod method = findUtilFactory(localName, options())) {
3868 Q_ASSERT(!m_nodes.isEmpty());
3869 if (!method(m_nodes.top(), attributes,
this))
3870 qCWarning(lcSvgHandler,
"%s", msgProblemParsing(localName, xml).constData());
3871 }
else if (StyleFactoryMethod method = findStyleFactoryMethod(localName)) {
3872 QSvgStyleProperty *prop = method(attributes,
this);
3875 m_nodes.top()->appendStyleProperty(prop);
3877 const QByteArray msg = QByteArrayLiteral(
"Could not parse node: ") + localName.toLocal8Bit();
3878 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3880 }
else if (PaintServerFactoryMethod method = findPaintServerFactoryMethod(localName)) {
3881 QSvgPaintServerSharedPtr paintServer = method(attributes,
this);
3883 m_paintServer = paintServer;
3884 m_doc->addPaintServer(paintServer, someId(attributes));
3886 const QByteArray msg = QByteArrayLiteral(
"Could not parse node: ") + localName.toLocal8Bit();
3887 qCWarning(lcSvgHandler,
"%s", prefixMessage(msg, xml).constData());
3889 }
else if (StyleParseMethod method = findStyleUtilFactoryMethod(localName)) {
3891 if (!method(m_style, attributes,
this))
3892 qCWarning(lcSvgHandler,
"%s", msgProblemParsing(localName, xml).constData());
3894 }
else if (PaintServerParseMethod method = findPaintServerUtilFactoryMethod(localName)) {
3895 if (m_paintServer) {
3896 if (!method(m_paintServer.get(), attributes,
this))
3897 qCWarning(lcSvgHandler,
"%s", msgProblemParsing(localName, xml).constData());
3900 qCDebug(lcSvgHandler) <<
"Skipping unknown element" << localName;
3901 m_skipNodes.push(Unknown);
3907 m_skipNodes.push(Graphics);
3910 m_skipNodes.push(Style);
3915bool QSvgHandler::endElement(
const QStringView localName)
3917 CurrentNode node = m_skipNodes.top();
3919 if (node == Doc && localName != QLatin1String(
"svg"))
3923 m_whitespaceMode.pop();
3927 if (node == Unknown)
3930#ifdef QT_NO_CSSPARSER
3931 Q_UNUSED(localName);
3933 if (m_inStyle && localName == QLatin1String(
"style"))
3937 if (node == Graphics)
3939 else if (m_style && !m_skipNodes.isEmpty() && m_skipNodes.top() != Style)
3942 return ((localName == QLatin1String(
"svg")) && (node != Doc));
3945void QSvgHandler::resolvePaintServers()
3947 for (QSvgStyleProperty *prop : std::as_const(m_unresolvedStyles)) {
3948 if (prop->type() == QSvgStyleProperty::FILL) {
3949 QSvgFillStyle *fill =
static_cast<QSvgFillStyle *>(prop);
3950 QString id = fill->paintStyleId();
3951 QSvgPaintServerSharedPtr paintServer = m_doc->paintServer(id);
3953 fill->setPaintServer(paintServer);
3955 qCWarning(lcSvgHandler,
"%s", msgCouldNotResolveProperty(id, xml).constData());
3956 fill->setBrush(Qt::NoBrush);
3958 }
else if (prop->type() == QSvgStyleProperty::STROKE) {
3959 QSvgStrokeStyle *stroke =
static_cast<QSvgStrokeStyle *>(prop);
3960 QString id = stroke->paintStyleId();
3961 QSvgPaintServerSharedPtr paintServer = m_doc->paintServer(id);
3963 stroke->setPaintServer(paintServer);
3965 qCWarning(lcSvgHandler,
"%s", msgCouldNotResolveProperty(id, xml).constData());
3966 stroke->setStroke(Qt::NoBrush);
3971 m_unresolvedStyles.clear();
3974void QSvgHandler::resolveNodes()
3976 for (QSvgNode *node : std::as_const(m_toBeResolved)) {
3977 if (node->type() == QSvgNode::Use) {
3978 QSvgUse *useNode =
static_cast<QSvgUse *>(node);
3979 const auto parent = useNode->parent();
3983 QSvgNode::Type t = parent->type();
3984 if (t != QSvgNode::Doc && t != QSvgNode::Defs && t != QSvgNode::Group && t != QSvgNode::Switch)
3987 QSvgStructureNode *group =
static_cast<QSvgStructureNode *>(parent);
3988 QSvgNode *link = group->scopeNode(useNode->linkId());
3990 qCWarning(lcSvgHandler,
"link #%s is undefined!",
qPrintable(useNode->linkId()));
3994 if (useNode->parent()->isDescendantOf(link))
3995 qCWarning(lcSvgHandler,
"link #%s is recursive!",
qPrintable(useNode->linkId()));
3997 useNode->setLink(link);
3998 }
else if (node->type() == QSvgNode::Filter) {
3999 QSvgFilterContainer *filter =
static_cast<QSvgFilterContainer *>(node);
4000 for (
auto &renderer : filter->renderers()) {
4001 const QSvgFeFilterPrimitive *primitive = QSvgFeFilterPrimitive::castToFilterPrimitive(renderer.get());
4002 if (!primitive || primitive->type() == QSvgNode::FeUnsupported) {
4003 filter->setSupported(
false);
4007 }
else if (node->type() == QSvgNode::AnimateTransform || node->type() == QSvgNode::AnimateColor) {
4008 QSvgAnimateNode *anim =
static_cast<QSvgAnimateNode *>(node);
4009 QSvgNode *targetNode = m_doc->namedNode(anim->linkId());
4011 m_doc->animator()->appendAnimation(targetNode, anim);
4013 qCWarning(lcSvgHandler,
"Cannot find target for link #%s!",
4019 m_toBeResolved.clear();
4022bool QSvgHandler::characters(
const QStringView str)
4024#ifndef QT_NO_CSSPARSER
4026 m_cssHandler.parseStyleSheet(str);
4030 if (m_skipNodes.isEmpty() || m_skipNodes.top() == Unknown || m_nodes.isEmpty())
4033 if (m_nodes.top()->type() == QSvgNode::Text || m_nodes.top()->type() == QSvgNode::Textarea) {
4034 static_cast<QSvgText*>(m_nodes.top())->addText(str);
4035 }
else if (m_nodes.top()->type() == QSvgNode::Tspan) {
4036 static_cast<QSvgTspan*>(m_nodes.top())->addText(str);
4042QIODevice *QSvgHandler::device()
const
4044 return xml->device();
4047QSvgDocument *QSvgHandler::document()
const
4052QSvgUtils::LengthType QSvgHandler::defaultCoordinateSystem()
const
4054 return m_defaultCoords;
4057void QSvgHandler::setDefaultCoordinateSystem(QSvgUtils::LengthType type)
4059 m_defaultCoords = type;
4062void QSvgHandler::pushColor(
const QColor &color)
4064 m_colorStack.push(color);
4065 m_colorTagCount.push(1);
4068void QSvgHandler::pushColorCopy()
4070 if (m_colorTagCount.size())
4071 ++m_colorTagCount.top();
4073 pushColor(Qt::black);
4076void QSvgHandler::popColor()
4078 if (m_colorTagCount.size()) {
4079 if (!--m_colorTagCount.top()) {
4081 m_colorTagCount.pop();
4086QColor QSvgHandler::currentColor()
const
4088 if (!m_colorStack.isEmpty())
4089 return m_colorStack.top();
4091 return QColor(0, 0, 0);
4094void QSvgHandler::pushUnresolvedStyle(QSvgStyleProperty *prop)
4096 m_unresolvedStyles.append(prop);
4099#ifndef QT_NO_CSSPARSER
4101void QSvgHandler::setInStyle(
bool b)
4106bool QSvgHandler::inStyle()
const
4111QSvgCssHandler &QSvgHandler::cssHandler()
4113 return m_cssHandler;
4118bool QSvgHandler::processingInstruction(
const QStringView target,
const QStringView data)
4120#ifdef QT_NO_CSSPARSER
4124 if (target == QLatin1String(
"xml-stylesheet")) {
4125 static const QRegularExpression rx(
QStringLiteral(
"type=\\\"(.+)\\\""),
4126 QRegularExpression::InvertedGreedinessOption);
4127 QRegularExpressionMatchIterator iter = rx.globalMatchView(data);
4129 while (iter.hasNext()) {
4130 QRegularExpressionMatch match = iter.next();
4131 QString type = match.captured(1);
4132 if (type.toLower() == QLatin1String(
"text/css")) {
4138 static const QRegularExpression rx(
QStringLiteral(
"href=\\\"(.+)\\\""),
4139 QRegularExpression::InvertedGreedinessOption);
4140 QRegularExpressionMatch match = rx.matchView(data);
4141 QString addr = match.captured(1);
4145 QFile file(fi.absoluteFilePath());
4146 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
4149 QByteArray cssData = file.readAll();
4150 QString css = QString::fromUtf8(cssData);
4151 m_cssHandler.parseStyleSheet(css);
4161void QSvgHandler::setAnimPeriod(
int start,
int end)
4164 m_animEnd = qMax(end, m_animEnd);
4167int QSvgHandler::animationDuration()
const
4172QSvgHandler::~QSvgHandler()
The QPolygonF class provides a list of points using floating point precision.
void parseNumbersArray(QStringView *str, QVarLengthArray< qreal, 8 > &points, const char *pattern)
qreal toDouble(QStringView *str)
Combined button and popup list for selecting options.
QList< QGradientStop > QGradientStops
Q_STATIC_ASSERT(sizeof(SharedImageHeader) % 4==0)
#define qPrintable(string)
#define QStringLiteral(str)
#define qUtf16Printable(string)
static PaintServerParseMethod findPaintServerUtilFactoryMethod(const QStringView name)
static QSvgNode * createTspanNode(QSvgNode *parent, const QXmlStreamAttributes &, QSvgHandler *)
static bool parseStyle(QSvgNode *node, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static void parseVisibility(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static bool detectPatternCycles(const QSvgNode *node, QList< const QSvgNode * > active={})
static void parseOffsetPath(QSvgNode *node, const QXmlStreamAttributes &attributes)
static QSvgNode * createGNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
bool(* StyleParseMethod)(QSvgStyleProperty *, const QXmlStreamAttributes &, QSvgHandler *)
static QSvgNode::DisplayMode displayStringToEnum(const QStringView str)
static bool parseStyleNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static std::optional< QStringView > getAttributeId(const QStringView &attribute)
QSvgPaintServerSharedPtr(* PaintServerFactoryMethod)(const QXmlStreamAttributes &, QSvgHandler *)
static const qreal sizeTable[]
static QSvgNode * createImageNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createPolygonNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseOthers(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static QSvgNode * createMaskNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QByteArray msgProblemParsing(QStringView localName, const QXmlStreamReader *r)
static void parseRenderingHints(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static void parseExtendedAttributes(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *handler)
static bool parseMpathNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QByteArray prefixMessage(const QByteArray &msg, const QXmlStreamReader *r)
static QSvgNode * createPathNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createDefsNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static int parseClockValue(QStringView str, bool *ok)
static bool parseSymbolLikeAttributes(const QXmlStreamAttributes &attributes, QSvgHandler *handler, QRectF *rect, QRectF *viewBox, QPointF *refPoint, QSvgSymbolLike::PreserveAspectRatios *aspect, QSvgSymbolLike::Overflow *overflow, bool marker=false)
static qreal convertToNumber(QStringView str, bool *ok=NULL)
static bool parseBaseAnimate(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgAnimateNode *anim, QSvgHandler *handler)
static bool parseFontFaceNameNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static 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)
static void parseBaseGradient(const QXmlStreamAttributes &attributes, QSvgGradientPaint *gradProp, QSvgHandler *handler)
bool qsvg_get_hex_rgb(const char *name, QRgb *rgb)
static QSvgNode * createTextNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseAudioNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static StyleParseMethod findStyleUtilFactoryMethod(const QStringView name)
static bool parseMarkerNode(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *)
static QList< qreal > parsePercentageList(QStringView str)
static FactoryMethod findGroupFactory(const QStringView name, QtSvg::Options options)
bool(* PaintServerParseMethod)(QSvgPaintServer *, const QXmlStreamAttributes &, QSvgHandler *)
static bool parseMetadataNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QTransform parseTransformationMatrix(QStringView value)
static QSvgNode * createSwitchNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseDiscardNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool createSvgGlyph(QSvgFont *font, const QXmlStreamAttributes &attributes, bool isMissingGlyph)
static void parseOpacity(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static bool parseScriptNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgPaintServerSharedPtr createRadialGradientNode(const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createPatternNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static bool parseHandlerNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static AnimationMethod findAnimationFactory(const QStringView name, QtSvg::Options options)
static bool parseCoreNode(QSvgNode *node, const QXmlStreamAttributes &attributes)
static bool constructColor(QStringView colorStr, QStringView opacity, QColor &color, QSvgHandler *handler)
static bool parseHkernNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void generateKeyFrames(QList< qreal > &keyFrames, uint count)
static void parsePen(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createSvgNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgStyleProperty * createFontNode(const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static void parseFont(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
QList< qreal > parseNumbersList(QStringView *str)
static bool parseForeignObjectNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseTbreakNode(QSvgNode *parent, const QXmlStreamAttributes &, QSvgHandler *)
static QSvgNode * createFeUnsupportedNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QList< QStringView > splitWithDelimiter(QStringView delimitedList)
static QSvgNode * createFeOffsetNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createFeFloodNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createMarkerNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createFeColorMatrixNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createFeGaussianBlurNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgPaintServerSharedPtr paintServerFromUrl(QSvgDocument *doc, QStringView url)
static QSvgNode * createPolylineNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgPaintServerSharedPtr createSolidColorNode(const QXmlStreamAttributes &attributes, QSvgHandler *handler)
bool(* ParseMethod)(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *)
static bool parseFontFaceNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QStringView idFromIRI(QStringView iri)
static bool parseMissingGlyphNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parsePrefetchNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseCssAnimations(QSvgNode *node, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static int qsvg_hex2int(const char *s, bool *ok=nullptr)
static bool parseGlyphNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static int qsvg_h2i(char hex, bool *ok=nullptr)
static int qsvg_hex2int(char s, bool *ok=nullptr)
static QSvgNode * createVideoNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static PaintServerFactoryMethod findPaintServerFactoryMethod(const QStringView name)
static void parseCompOp(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static bool parseMaskNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseStopNode(QSvgPaintServer *paintServer, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static bool detectCyclesAndWarn(const QSvgNode *node)
static QSvgNode * createUseNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseFontFaceSrcNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseAnchorNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseFontFaceUriNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool detectCycles(const QSvgNode *node, QList< const QSvgNode * > active={})
static QStringList stringToList(const QString &str)
static QSvgNode * createRectNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createAnimateNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createSymbolNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static StyleFactoryMethod findStyleFactoryMethod(const QStringView name)
static QPainter::CompositionMode svgToQtCompositionMode(const QStringView op)
static QByteArray msgCouldNotResolveProperty(QStringView id, const QXmlStreamReader *r)
static void parseFilterBounds(const QXmlStreamAttributes &attributes, QSvgRectF *rect)
static void parseBrush(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createAnimateMotionNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static FactoryMethod findGraphicsFactory(const QStringView name, QtSvg::Options options)
QSvgNode * createAnimateTransformNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createFeMergeNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createFeCompositeNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseSetNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QStringView idFromFuncIRI(QStringView iri)
static QSvgNode * createFeBlendNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createFilterNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createEllipseNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createTextAreaNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
QSvgStyleProperty *(* StyleFactoryMethod)(const QXmlStreamAttributes &, QSvgHandler *)
static QSvgPaintServerSharedPtr createLinearGradientNode(const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QString someId(const QXmlStreamAttributes &attributes)
Q_AUTOTEST_EXPORT bool resolveColor(QStringView colorStr, QColor &color, QSvgHandler *handler)
QStringView strokeDashOffset
QStringView strokeDashArray
QStringView strokeOpacity
QStringView strokeLineJoin
void setAttributes(const QXmlStreamAttributes &attributes, QSvgHandler *handler)
QSvgAttributes(const QXmlStreamAttributes &xmlAttributes, QSvgHandler *handler)
QStringView strokeLineCap
QStringView strokeMiterLimit
QStringView imageRendering