aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHatem ElKharashy <hatem.elkharashy@qt.io>2024-03-28 15:58:50 +0200
committerHatem ElKharashy <hatem.elkharashy@qt.io>2024-04-18 15:42:40 +0300
commit5ce8fd73430c86cc0b52f8eaf5dbfc000605b6a0 (patch)
tree3c96e65750bbbdba59faa9bfad112fbc4133401a
parentf1051cb48243ab27a44ec4f30259fa38116c2783 (diff)
VectorImage: tweak the default pen and fix opacity behavior
There are many changes done in this commit, which either prepare the generator for features implemented in the future or fix the current behavior. The default QPen in the style resolver class is changed to match the default QPen used by Qt Svg module, because this default pen sets the CapStyle and JoinStyle. This is going to be important when implementing those features in VectorImage. There were also some issues with the opacity for several reasons. The first one was because "transparent" color was not taken into account. This is not part of the SVG standard, but the browsers, as well as Qt Svg, implements it anyway. Although we get the correct color from style resolver, the currentFillColor function overrides the opacity with the fillOpacity from the extra states. Finally, this takes us to the other issue with opacity. The fill and stroke attributes define an RGB color or a paint server like gradients. The opacity can be controlled on those using fill-opacity and stroke-opacity. Those opacities can be applied on gradient as well and not only on normal colors. This change adds new member variables in the NodeInfo structs to handle the fillOpacity and strokeOpacity. Change-Id: I267126aecbab488700f6f7490634341893b21a1c Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
-rw-r--r--src/quickvectorimage/generator/qquickitemgenerator.cpp18
-rw-r--r--src/quickvectorimage/generator/qquicknodeinfo_p.h8
-rw-r--r--src/quickvectorimage/generator/qquickqmlgenerator.cpp23
-rw-r--r--src/quickvectorimage/generator/qsvgvisitorimpl.cpp126
-rw-r--r--src/quickvectorimage/generator/qsvgvisitorimpl_p.h1
-rw-r--r--tests/manual/svg/data/painting/fill_color.svg7
-rw-r--r--tests/manual/svg/data/painting/fill_gradient.svg15
-rw-r--r--tests/manual/svg/data/painting/fill_stroke.svg3
-rw-r--r--tests/manual/svg/data/painting/stroke_color.svg7
9 files changed, 147 insertions, 61 deletions
diff --git a/src/quickvectorimage/generator/qquickitemgenerator.cpp b/src/quickvectorimage/generator/qquickitemgenerator.cpp
index c939ffccd7..1917c53013 100644
--- a/src/quickvectorimage/generator/qquickitemgenerator.cpp
+++ b/src/quickvectorimage/generator/qquickitemgenerator.cpp
@@ -123,12 +123,12 @@ void QQuickItemGenerator::outputShapePath(const PathNodeInfo &info, const QPaint
Q_UNUSED(pathSelector)
Q_ASSERT(painterPath || quadPath);
- QString penName = info.strokeColor;
- const bool noPen = penName.isEmpty() || penName == u"transparent";
+ const bool noPen = info.strokeColor == QColorConstants::Transparent;
if (pathSelector == QQuickVectorImageGenerator::StrokePath && noPen)
return;
- const bool noFill = info.grad.type() == QGradient::NoGradient && info.fillColor == u"transparent";
+ const bool noFill = info.grad.type() == QGradient::NoGradient && info.fillColor == QColorConstants::Transparent;
+
if (pathSelector == QQuickVectorImageGenerator::FillPath && noFill)
return;
@@ -145,7 +145,7 @@ void QQuickItemGenerator::outputShapePath(const PathNodeInfo &info, const QPaint
if (noPen || !(pathSelector & QQuickVectorImageGenerator::StrokePath)) {
shapePath->setStrokeColor(Qt::transparent);
} else {
- shapePath->setStrokeColor(QColor::fromString(penName));
+ shapePath->setStrokeColor(info.strokeColor);
shapePath->setStrokeWidth(info.strokeWidth);
}
@@ -156,7 +156,7 @@ void QQuickItemGenerator::outputShapePath(const PathNodeInfo &info, const QPaint
else if (info.grad.type() != QGradient::NoGradient)
generateGradient(&info.grad, shapePath, boundingRect);
else
- shapePath->setFillColor(QColor::fromString(info.fillColor));
+ shapePath->setFillColor(info.fillColor);
shapePath->setFillRule(fillRule);
@@ -179,7 +179,7 @@ void QQuickItemGenerator::generateGradient(const QGradient *grad, QQuickShapePat
if (!shapePath)
return;
- auto setStops = [](QQuickShapeGradient *quickGrad, const QGradientStops &stops) {
+ auto setStops = [=](QQuickShapeGradient *quickGrad, const QGradientStops &stops) {
auto stopsProp = quickGrad->stops();
for (auto &stop : stops) {
auto *stopObj = new QQuickGradientStop(quickGrad);
@@ -281,13 +281,13 @@ void QQuickItemGenerator::generateTextNode(const TextNodeInfo &info)
}
}
- textItem->setColor(QColor::fromString(info.color));
+ textItem->setColor(info.fillColor);
textItem->setTextFormat(info.needsRichText ? QQuickText::RichText : QQuickText::StyledText);
textItem->setText(info.text);
textItem->setFont(info.font);
- if (!info.strokeColor.isEmpty()) {
- textItem->setStyleColor(QColor::fromString(info.strokeColor));
+ if (info.strokeColor != QColorConstants::Transparent) {
+ textItem->setStyleColor(info.strokeColor);
textItem->setStyle(QQuickText::Outline);
}
diff --git a/src/quickvectorimage/generator/qquicknodeinfo_p.h b/src/quickvectorimage/generator/qquicknodeinfo_p.h
index 012e8f8c69..28629bf3e1 100644
--- a/src/quickvectorimage/generator/qquicknodeinfo_p.h
+++ b/src/quickvectorimage/generator/qquicknodeinfo_p.h
@@ -49,9 +49,9 @@ struct PathNodeInfo : NodeInfo
QPainterPath painterPath;
Qt::FillRule fillRule = Qt::FillRule::WindingFill;
Qt::PenCapStyle capStyle = Qt::SquareCap;
- QString strokeColor;
+ QColor strokeColor;
qreal strokeWidth;
- QString fillColor;
+ QColor fillColor;
QGradient grad;
};
@@ -64,8 +64,8 @@ struct TextNodeInfo : NodeInfo
QString text;
QFont font;
Qt::Alignment alignment;
- QString color;
- QString strokeColor;
+ QColor fillColor;
+ QColor strokeColor;
};
enum class StructureNodeStage
diff --git a/src/quickvectorimage/generator/qquickqmlgenerator.cpp b/src/quickvectorimage/generator/qquickqmlgenerator.cpp
index bdd3a29ed1..731d80f2da 100644
--- a/src/quickvectorimage/generator/qquickqmlgenerator.cpp
+++ b/src/quickvectorimage/generator/qquickqmlgenerator.cpp
@@ -220,9 +220,8 @@ void QQuickQmlGenerator::generateGradient(const QGradient *grad, const QRectF &b
stream() << "y1: " << logRect.top();
stream() << "x2: " << logRect.right();
stream() << "y2: " << logRect.bottom();
- for (auto &stop : linGrad->stops()) {
+ for (auto &stop : linGrad->stops())
stream() << "GradientStop { position: " << stop.first << "; color: \"" << stop.second.name(QColor::HexArgb) << "\" }";
- }
m_indentLevel--;
stream() << "}";
} else if (grad->type() == QGradient::RadialGradient) {
@@ -235,9 +234,8 @@ void QQuickQmlGenerator::generateGradient(const QGradient *grad, const QRectF &b
stream() << "centerRadius: " << radGrad->radius();
stream() << "focalX:" << radGrad->focalPoint().x();
stream() << "focalY:" << radGrad->focalPoint().y();
- for (auto &stop : radGrad->stops()) {
+ for (auto &stop : radGrad->stops())
stream() << "GradientStop { position: " << stop.first << "; color: \"" << stop.second.name(QColor::HexArgb) << "\" }";
- }
m_indentLevel--;
stream() << "}";
}
@@ -248,12 +246,12 @@ void QQuickQmlGenerator::outputShapePath(const PathNodeInfo &info, const QPainte
Q_UNUSED(pathSelector)
Q_ASSERT(painterPath || quadPath);
- QString penName = info.strokeColor;
- const bool noPen = penName.isEmpty() || penName == u"transparent";
+ const bool noPen = info.strokeColor == QColorConstants::Transparent;
if (pathSelector == QQuickVectorImageGenerator::StrokePath && noPen)
return;
- const bool noFill = info.grad.type() == QGradient::NoGradient && info.fillColor == u"transparent";
+ const bool noFill = info.grad.type() == QGradient::NoGradient && info.fillColor == QColorConstants::Transparent;
+
if (pathSelector == QQuickVectorImageGenerator::FillPath && noFill)
return;
@@ -277,7 +275,7 @@ void QQuickQmlGenerator::outputShapePath(const PathNodeInfo &info, const QPainte
if (noPen || !(pathSelector & QQuickVectorImageGenerator::StrokePath)) {
stream() << "strokeColor: \"transparent\"";
} else {
- stream() << "strokeColor: \"" << penName << "\"";
+ stream() << "strokeColor: \"" << info.strokeColor.name(QColor::HexArgb) << "\"";
stream() << "strokeWidth: " << info.strokeWidth;
}
if (info.capStyle == Qt::FlatCap)
@@ -288,8 +286,7 @@ void QQuickQmlGenerator::outputShapePath(const PathNodeInfo &info, const QPainte
} else if (info.grad.type() != QGradient::NoGradient) {
generateGradient(&info.grad, boundingRect);
} else {
- stream() << "fillColor: \"" << info.fillColor << "\"";
-
+ stream() << "fillColor: \"" << info.fillColor.name(QColor::HexArgb) << "\"";
}
if (fillRule == QQuickShapePath::WindingFill)
stream() << "fillRule: ShapePath.WindingFill";
@@ -368,7 +365,7 @@ void QQuickQmlGenerator::generateTextNode(const TextNodeInfo &info)
}
counter++;
- stream() << "color: \"" << info.color << "\"";
+ stream() << "color: \"" << info.fillColor.name(QColor::HexArgb) << "\"";
stream() << "textFormat:" << (info.needsRichText ? "Text.RichText" : "Text.StyledText");
QString s = info.text;
@@ -386,8 +383,8 @@ void QQuickQmlGenerator::generateTextNode(const TextNodeInfo &info)
if (info.font.italic())
stream() << "font.italic: true";
- if (!info.strokeColor.isEmpty()) {
- stream() << "styleColor: \"" << info.strokeColor << "\"";
+ if (info.strokeColor != QColorConstants::Transparent) {
+ stream() << "styleColor: \"" << info.strokeColor.name(QColor::HexArgb) << "\"";
stream() << "style: Text.Outline";
}
diff --git a/src/quickvectorimage/generator/qsvgvisitorimpl.cpp b/src/quickvectorimage/generator/qsvgvisitorimpl.cpp
index 6ccd51ee39..ce595f9a6b 100644
--- a/src/quickvectorimage/generator/qsvgvisitorimpl.cpp
+++ b/src/quickvectorimage/generator/qsvgvisitorimpl.cpp
@@ -87,9 +87,9 @@ public:
{
m_dummyImage = QImage(1, 1, QImage::Format_RGB32);
m_dummyPainter.begin(&m_dummyImage);
- QPen noPen(Qt::NoPen);
- noPen.setBrush(Qt::NoBrush);
- m_dummyPainter.setPen(noPen);
+ QPen defaultPen(Qt::NoBrush, 1, Qt::SolidLine, Qt::FlatCap, Qt::SvgMiterJoin);
+ defaultPen.setMiterLimit(4);
+ m_dummyPainter.setPen(defaultPen);
m_dummyPainter.setBrush(Qt::black);
}
@@ -101,16 +101,23 @@ public:
QPainter& painter() { return m_dummyPainter; }
QSvgExtraStates& states() { return m_svgState; }
- QString currentFillColor() const
+ QColor currentFillColor() const
{
- if (m_dummyPainter.brush().style() != Qt::NoBrush) {
- QColor c(m_dummyPainter.brush().color());
- c.setAlphaF(m_svgState.fillOpacity);
- //qCDebug(lcQuickVectorGraphics) << "FILL" << c << m_svgState.fillOpacity << c.name();
- return c.name(QColor::HexArgb);
- } else {
- return QStringLiteral("transparent");
+ if (m_dummyPainter.brush().style() == Qt::NoBrush ||
+ m_dummyPainter.brush().color() == QColorConstants::Transparent) {
+ return QColor(QColorConstants::Transparent);
}
+
+ QColor fillColor;
+ fillColor = m_dummyPainter.brush().color();
+ fillColor.setAlphaF(m_svgState.fillOpacity);
+
+ return fillColor;
+ }
+
+ qreal currentFillOpacity() const
+ {
+ return m_svgState.fillOpacity;
}
const QGradient *currentFillGradient() const
@@ -120,13 +127,23 @@ public:
return nullptr;
}
- QString currentStrokeColor() const
+ QColor currentStrokeColor() const
+ {
+ if (m_dummyPainter.pen().brush().style() == Qt::NoBrush ||
+ m_dummyPainter.pen().brush().color() == QColorConstants::Transparent) {
+ return QColor(QColorConstants::Transparent);
+ }
+
+ QColor strokeColor;
+ strokeColor = m_dummyPainter.pen().brush().color();
+ strokeColor.setAlphaF(m_svgState.strokeOpacity);
+
+ return strokeColor;
+ }
+
+ qreal currentStrokeOpacity() const
{
- if (m_dummyPainter.pen().style() != Qt::NoPen)
- return m_dummyPainter.pen().color().name();
- else if (m_dummyPainter.pen().brush().style() == Qt::SolidPattern)
- return m_dummyPainter.pen().brush().color().name();
- return {};
+ return m_svgState.strokeOpacity;
}
float currentStrokeWidth() const
@@ -135,6 +152,20 @@ public:
return penWidth ? penWidth : 1;
}
+ static QGradient applyOpacityToGradient(const QGradient &gradient, float opacity)
+ {
+ QGradient grad = gradient;
+ QGradientStops stops;
+ for (auto &stop : grad.stops()) {
+ stop.second.setAlphaF(stop.second.alphaF() * opacity);
+ stops.append(stop);
+ }
+
+ grad.setStops(stops);
+
+ return grad;
+ }
+
protected:
QPainter m_dummyPainter;
QImage m_dummyImage;
@@ -307,7 +338,7 @@ QString QSvgVisitorImpl::gradientCssDescription(const QGradient *gradient)
cssDescription += ",stop:"_L1;
cssDescription += QString::number(stop.first);
cssDescription += u' ';
- cssDescription += stop.second.name();
+ cssDescription += stop.second.name(QColor::HexArgb);
}
cssDescription += ");"_L1;
@@ -315,6 +346,18 @@ QString QSvgVisitorImpl::gradientCssDescription(const QGradient *gradient)
return cssDescription;
}
+QString QSvgVisitorImpl::colorCssDescription(QColor color)
+{
+ QString cssDescription;
+ cssDescription += QStringLiteral("rgba(");
+ cssDescription += QString::number(color.red()) + QStringLiteral(",");
+ cssDescription += QString::number(color.blue()) + QStringLiteral(",");
+ cssDescription += QString::number(color.green()) + QStringLiteral(",");
+ cssDescription += QString::number(color.alphaF()) + QStringLiteral(")");
+
+ return cssDescription;
+}
+
void QSvgVisitorImpl::visitTextNode(const QSvgText *node)
{
handleBaseNodeSetup(node);
@@ -361,11 +404,12 @@ void QSvgVisitorImpl::visitTextNode(const QSvgText *node)
if (styleResolver->currentFillGradient() != nullptr
&& styleResolver->currentFillGradient() != mainGradient) {
- styleTagContent += gradientCssDescription(styleResolver->currentFillGradient()) + u';';
+ const QGradient grad = styleResolver->applyOpacityToGradient(*styleResolver->currentFillGradient(), styleResolver->currentFillOpacity());
+ styleTagContent += gradientCssDescription(&grad) + u';';
needsPathNode = true;
}
- QString strokeColor = styleResolver->currentStrokeColor();
+ QString strokeColor = colorCssDescription(styleResolver->currentStrokeColor());
if (!strokeColor.isEmpty()) {
styleTagContent += QStringLiteral("-qt-stroke-color:%1;").arg(strokeColor);
styleTagContent += QStringLiteral("-qt-stroke-width:%1;").arg(styleResolver->currentStrokeWidth());
@@ -379,6 +423,26 @@ void QSvgVisitorImpl::visitTextNode(const QSvgText *node)
content.replace(QLatin1Char('\t'), QLatin1Char(' '));
content.replace(QLatin1Char('\n'), QLatin1Char(' '));
+ bool fontTag = false;
+ if (!tspan->style().fill.isDefault()) {
+ auto &b = tspan->style().fill->qbrush();
+ qCDebug(lcQuickVectorImage) << "tspan FILL:" << b;
+ if (b.style() != Qt::NoBrush)
+ {
+ if (qFuzzyCompare(b.color().alphaF() + 1.0, 2.0))
+ {
+ QString spanColor = b.color().name();
+ fontTag = !spanColor.isEmpty();
+ if (fontTag)
+ text += QStringLiteral("<font color=\"%1\">").arg(spanColor);
+ } else {
+ QString spanColor = colorCssDescription(b.color());
+ styleTagContent += QStringLiteral("color:%1").arg(spanColor);
+ }
+ }
+ }
+
+
needsRichText = needsRichText || !styleTagContent.isEmpty();
if (!styleTagContent.isEmpty())
text += QStringLiteral("<span style=\"%1\">").arg(styleTagContent);
@@ -389,16 +453,6 @@ void QSvgVisitorImpl::visitTextNode(const QSvgText *node)
if (font.resolveMask() & QFont::StyleResolved && font.italic())
text += QStringLiteral("<i>");
- QString spanColor;
- if (!tspan->style().fill.isDefault()) {
- auto &b = tspan->style().fill->qbrush();
- qCDebug(lcQuickVectorImage) << "tspan FILL:" << b;
- if (b.style() != Qt::NoBrush)
- spanColor = b.color().name();
- }
- bool fontTag = !spanColor.isEmpty();
- if (fontTag)
- text += QStringLiteral("<font color=\"%1\">").arg(spanColor);
if (font.resolveMask() & QFont::CapitalizationResolved) {
switch (font.capitalization()) {
@@ -462,7 +516,7 @@ void QSvgVisitorImpl::visitTextNode(const QSvgText *node)
info.fillRule = fillStyle->fillRule();
if (fmt.hasProperty(QTextCharFormat::ForegroundBrush)) {
- info.fillColor = fmt.foreground().color().name();
+ info.fillColor = fmt.foreground().color();
if (fmt.foreground().gradient() != nullptr && fmt.foreground().gradient()->type() != QGradient::NoGradient)
info.grad = *fmt.foreground().gradient();
} else {
@@ -470,15 +524,17 @@ void QSvgVisitorImpl::visitTextNode(const QSvgText *node)
}
info.painterPath = p;
+
if (fmt.hasProperty(QTextCharFormat::TextOutline)) {
info.strokeWidth = fmt.textOutline().widthF();
- info.strokeColor = fmt.textOutline().color().name();
+ info.strokeColor = fmt.textOutline().color();
} else {
info.strokeColor = styleResolver->currentStrokeColor();
info.strokeWidth = styleResolver->currentStrokeWidth();
}
+
if (info.grad.type() == QGradient::NoGradient && styleResolver->currentFillGradient() != nullptr)
- info.grad = *styleResolver->currentFillGradient();
+ info.grad = styleResolver->applyOpacityToGradient(*styleResolver->currentFillGradient(), styleResolver->currentFillOpacity());
m_generator->generatePath(info);
};
@@ -542,7 +598,7 @@ void QSvgVisitorImpl::visitTextNode(const QSvgText *node)
info.text = text;
info.isTextArea = isTextArea;
info.needsRichText = needsRichText;
- info.color = styleResolver->currentFillColor();
+ info.fillColor = styleResolver->currentFillColor();
info.alignment = styleResolver->states().textAnchor;
info.strokeColor = styleResolver->currentStrokeColor();
@@ -698,7 +754,7 @@ void QSvgVisitorImpl::handlePathNode(const QSvgNode *node, const QPainterPath &p
info.strokeColor = styleResolver->currentStrokeColor();
info.strokeWidth = styleResolver->currentStrokeWidth();
if (styleResolver->currentFillGradient() != nullptr)
- info.grad = *styleResolver->currentFillGradient();
+ info.grad = styleResolver->applyOpacityToGradient(*styleResolver->currentFillGradient(), styleResolver->currentFillOpacity());
m_generator->generatePath(info);
diff --git a/src/quickvectorimage/generator/qsvgvisitorimpl_p.h b/src/quickvectorimage/generator/qsvgvisitorimpl_p.h
index 12da4b68ca..d80c9ec992 100644
--- a/src/quickvectorimage/generator/qsvgvisitorimpl_p.h
+++ b/src/quickvectorimage/generator/qsvgvisitorimpl_p.h
@@ -57,6 +57,7 @@ private:
void handlePathNode(const QSvgNode *node, const QPainterPath &path, Qt::PenCapStyle capStyle = Qt::SquareCap);
void outputShapePath(QPainterPath pathCopy, const PathNodeInfo &info);
static QString gradientCssDescription(const QGradient *gradient);
+ static QString colorCssDescription(QColor color);
private:
QString m_svgFileName;
diff --git a/tests/manual/svg/data/painting/fill_color.svg b/tests/manual/svg/data/painting/fill_color.svg
new file mode 100644
index 0000000000..7772839369
--- /dev/null
+++ b/tests/manual/svg/data/painting/fill_color.svg
@@ -0,0 +1,7 @@
+<svg viewBox="0 0 400 1000" xmlns="http://www.w3.org/2000/svg">
+ <g fill-opacity="0.3">
+ <rect width="300" height="300" fill="red"/>
+ </g>
+ <rect transform="translate(0, 350)" width="300" height="300" fill="red"/>
+ <rect transform="translate(0, 700)" width="300" height="300" fill="transparent" stroke="yellow" stroke-width="5"/>
+</svg>
diff --git a/tests/manual/svg/data/painting/fill_gradient.svg b/tests/manual/svg/data/painting/fill_gradient.svg
new file mode 100644
index 0000000000..5fefb40442
--- /dev/null
+++ b/tests/manual/svg/data/painting/fill_gradient.svg
@@ -0,0 +1,15 @@
+<svg viewBox="0 0 200 400" xmlns="http://www.w3.org/2000/svg">
+ <defs>
+ <linearGradient id="grad1" x1="0%" x2="100%" y1="0%" y2="0%">
+ <stop offset="0%" stop-color="green" stop-opacity="0.8"/>
+ <stop offset="100%" stop-color="red" stop-opacity="0.8"/>
+ </linearGradient>
+ <linearGradient id="grad2" x1="0%" x2="100%" y1="0%" y2="0%">
+ <stop offset="0%" stop-color="green"/>
+ <stop offset="100%" stop-color="red"/>
+ </linearGradient>
+ </defs>
+ <ellipse cx="100" cy="70" rx="85" ry="55" fill="url(#grad1)"/>
+ <ellipse cx="100" cy="200" rx="85" ry="55" fill="url(#grad2)" fill-opacity="0.8"/>
+ <ellipse cx="100" cy="330" rx="85" ry="55" fill="url(#grad1)" fill-opacity="0.5"/>
+</svg>
diff --git a/tests/manual/svg/data/painting/fill_stroke.svg b/tests/manual/svg/data/painting/fill_stroke.svg
new file mode 100644
index 0000000000..8069faf516
--- /dev/null
+++ b/tests/manual/svg/data/painting/fill_stroke.svg
@@ -0,0 +1,3 @@
+<svg viewBox="0 0 800 800" xmlns="http://www.w3.org/2000/svg">
+ <rect transform="translate(50, 50)" width="400" height="400" fill="green" stroke="black" fill-opacity="0.5" stroke-opacity="0.2" stroke-width="20"/>
+</svg> \ No newline at end of file
diff --git a/tests/manual/svg/data/painting/stroke_color.svg b/tests/manual/svg/data/painting/stroke_color.svg
new file mode 100644
index 0000000000..d77755e1fd
--- /dev/null
+++ b/tests/manual/svg/data/painting/stroke_color.svg
@@ -0,0 +1,7 @@
+<svg viewBox="0 0 400 1000" xmlns="http://www.w3.org/2000/svg">
+ <g stroke-opacity="0.3">
+ <rect transform="translate(25, 25)" width="300" height="300" stroke="red" stroke-width="10"/>
+ </g>
+ <rect transform="translate(25, 350)" width="300" height="300" stroke="red" stroke-width="10"/>
+ <rect transform="translate(25, 700)" width="300" height="300" fill="yellow" stroke="transparent"/>
+</svg>