svg_loader: Fix <stop> being registered in closed latestGradient

If stop is declared immediately after Gradient is closed, the stop is registered in latestGradient.
Change latestGradient to a Stack and add a stop only to the last added gradient.
When that gradient is closed, no more stops should be registered for that gradient.
And gradients do not allow nested declarations, so only the earliest declared Gradient is valid.
This commit is contained in:
JunsuChoi 2025-05-19 11:10:18 +09:00 committed by Hermet Park
parent d7db54ea5e
commit fda24c3f59
2 changed files with 24 additions and 17 deletions

View file

@ -3368,6 +3368,13 @@ static void _svgLoaderParserXmlClose(SvgLoaderData* loader, const char* content,
} }
} }
for (unsigned int i = 0; i < sizeof(gradientTags) / sizeof(gradientTags[0]); i++) {
if (!strncmp(tagName, gradientTags[i].tag, sz)) {
loader->gradientStack.pop();
break;
}
}
for (unsigned int i = 0; i < sizeof(graphicsTags) / sizeof(graphicsTags[0]); i++) { for (unsigned int i = 0; i < sizeof(graphicsTags) / sizeof(graphicsTags[0]); i++) {
if (!strncmp(tagName, graphicsTags[i].tag, sz)) { if (!strncmp(tagName, graphicsTags[i].tag, sz)) {
loader->currentGraphicsNode = nullptr; loader->currentGraphicsNode = nullptr;
@ -3439,7 +3446,6 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
if (node->type != SvgNodeType::Defs || !empty) { if (node->type != SvgNodeType::Defs || !empty) {
loader->stack.push(node); loader->stack.push(node);
} }
loader->latestGradient = nullptr;
} else if ((method = _findGraphicsFactory(tagName))) { } else if ((method = _findGraphicsFactory(tagName))) {
if (loader->stack.count > 0) parent = loader->stack.last(); if (loader->stack.count > 0) parent = loader->stack.last();
else parent = loader->doc; else parent = loader->doc;
@ -3450,24 +3456,26 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
loader->stack.push(defs); loader->stack.push(defs);
loader->currentGraphicsNode = node; loader->currentGraphicsNode = node;
} }
loader->latestGradient = nullptr;
} else if ((gradientMethod = _findGradientFactory(tagName))) { } else if ((gradientMethod = _findGradientFactory(tagName))) {
SvgStyleGradient* gradient; SvgStyleGradient* gradient;
gradient = gradientMethod(loader, attrs, attrsLength); gradient = gradientMethod(loader, attrs, attrsLength);
//FIXME: The current parsing structure does not distinguish end tags. //Gradients do not allow nested declarations, so only the earliest declared Gradient is valid.
// There is no way to know if the currently parsed gradient is in defs. if (loader->gradientStack.count == 0) {
// If a gradient is declared outside of defs after defs is set, it is included in the gradients of defs. //FIXME: The current parsing structure does not distinguish end tags.
// But finally, the loader has a gradient style list regardless of defs. // There is no way to know if the currently parsed gradient is in defs.
// This is only to support this when multiple gradients are declared, even if no defs are declared. // If a gradient is declared outside of defs after defs is set, it is included in the gradients of defs.
// refer to: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs // But finally, the loader has a gradient style list regardless of defs.
if (loader->def && loader->doc->node.doc.defs) { // This is only to support this when multiple gradients are declared, even if no defs are declared.
loader->def->node.defs.gradients.push(gradient); // refer to: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs
} else { if (loader->def && loader->doc->node.doc.defs) {
loader->gradients.push(gradient); loader->def->node.defs.gradients.push(gradient);
} else {
loader->gradients.push(gradient);
}
} }
loader->latestGradient = gradient; if (!empty) loader->gradientStack.push(gradient);
} else if (!strcmp(tagName, "stop")) { } else if (!strcmp(tagName, "stop")) {
if (!loader->latestGradient) { if (loader->gradientStack.count == 0) {
TVGLOG("SVG", "Stop element is used outside of the Gradient element"); TVGLOG("SVG", "Stop element is used outside of the Gradient element");
return; return;
} }
@ -3475,9 +3483,8 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
loader->svgParse->gradStop = {0.0f, 0, 0, 0, 255}; loader->svgParse->gradStop = {0.0f, 0, 0, 0, 255};
loader->svgParse->flags = SvgStopStyleFlags::StopDefault; loader->svgParse->flags = SvgStopStyleFlags::StopDefault;
simpleXmlParseAttributes(attrs, attrsLength, _attrParseStops, loader); simpleXmlParseAttributes(attrs, attrsLength, _attrParseStops, loader);
loader->latestGradient->stops.push(loader->svgParse->gradStop); loader->gradientStack.last()->stops.push(loader->svgParse->gradStop);
} else { } else {
loader->latestGradient = nullptr;
if (!isIgnoreUnsupportedLogElements(tagName)) TVGLOG("SVG", "Unsupported elements used [Elements: %s]", tagName); if (!isIgnoreUnsupportedLogElements(tagName)) TVGLOG("SVG", "Unsupported elements used [Elements: %s]", tagName);
} }
} }

View file

@ -577,7 +577,7 @@ struct SvgLoaderData
SvgNode* def = nullptr; //also used to store nested graphic nodes SvgNode* def = nullptr; //also used to store nested graphic nodes
SvgNode* cssStyle = nullptr; SvgNode* cssStyle = nullptr;
Array<SvgStyleGradient*> gradients; Array<SvgStyleGradient*> gradients;
SvgStyleGradient* latestGradient = nullptr; //For stops Array<SvgStyleGradient*> gradientStack; //For stops
SvgParser* svgParse = nullptr; SvgParser* svgParse = nullptr;
Array<SvgNodeIdPair> cloneNodes; Array<SvgNodeIdPair> cloneNodes;
Array<SvgNodeIdPair> nodesToStyle; Array<SvgNodeIdPair> nodesToStyle;