From 1b1fc8022cb83327e25b6ed6813fbc0267ffb5a5 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Mon, 19 May 2025 11:10:18 +0900 Subject: [PATCH] svg_loader: Fix 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. --- src/loaders/svg/tvgSvgLoader.cpp | 40 +++++++++++++++++----------- src/loaders/svg/tvgSvgLoaderCommon.h | 2 +- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 3400d002..fef05b1c 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -3401,6 +3401,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++) { if (!strncmp(tagName, graphicsTags[i].tag, sz)) { loader->currentGraphicsNode = nullptr; @@ -3472,7 +3479,6 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, if (node->type != SvgNodeType::Defs || !empty) { loader->stack.push(node); } - loader->latestGradient = nullptr; } else if ((method = _findGraphicsFactory(tagName))) { if (loader->stack.count > 0) parent = loader->stack.last(); else parent = loader->doc; @@ -3483,24 +3489,26 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, loader->stack.push(defs); loader->currentGraphicsNode = node; } - loader->latestGradient = nullptr; } else if ((gradientMethod = _findGradientFactory(tagName))) { SvgStyleGradient* gradient; gradient = gradientMethod(loader, attrs, attrsLength); - //FIXME: The current parsing structure does not distinguish end tags. - // There is no way to know if the currently parsed gradient is in defs. - // If a gradient is declared outside of defs after defs is set, it is included in the gradients of defs. - // But finally, the loader has a gradient style list regardless of defs. - // This is only to support this when multiple gradients are declared, even if no defs are declared. - // refer to: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs - if (loader->def && loader->doc->node.doc.defs) { - loader->def->node.defs.gradients.push(gradient); - } else { - loader->gradients.push(gradient); + //Gradients do not allow nested declarations, so only the earliest declared Gradient is valid. + if (loader->gradientStack.count == 0) { + //FIXME: The current parsing structure does not distinguish end tags. + // There is no way to know if the currently parsed gradient is in defs. + // If a gradient is declared outside of defs after defs is set, it is included in the gradients of defs. + // But finally, the loader has a gradient style list regardless of defs. + // This is only to support this when multiple gradients are declared, even if no defs are declared. + // refer to: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs + if (loader->def && loader->doc->node.doc.defs) { + loader->def->node.defs.gradients.push(gradient); + } else { + loader->gradients.push(gradient); + } } - loader->latestGradient = gradient; + if (!empty) loader->gradientStack.push(gradient); } else if (STR_AS(tagName, "stop")) { - if (!loader->latestGradient) { + if (loader->gradientStack.count == 0) { TVGLOG("SVG", "Stop element is used outside of the Gradient element"); return; } @@ -3508,9 +3516,8 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, loader->svgParse->gradStop = {0.0f, 0, 0, 0, 255}; loader->svgParse->flags = SvgStopStyleFlags::StopDefault; xmlParseAttributes(attrs, attrsLength, _attrParseStops, loader); - loader->latestGradient->stops.push(loader->svgParse->gradStop); + loader->gradientStack.last()->stops.push(loader->svgParse->gradStop); } else { - loader->latestGradient = nullptr; if (!isIgnoreUnsupportedLogElements(tagName)) TVGLOG("SVG", "Unsupported elements used [Elements: %s]", tagName); } } @@ -3831,6 +3838,7 @@ void SvgLoader::clear(bool all) tvg::free(*p); } loaderData.gradients.reset(); + loaderData.gradientStack.reset(); _freeNode(loaderData.doc); loaderData.doc = nullptr; diff --git a/src/loaders/svg/tvgSvgLoaderCommon.h b/src/loaders/svg/tvgSvgLoaderCommon.h index ce461b03..e3b917a8 100644 --- a/src/loaders/svg/tvgSvgLoaderCommon.h +++ b/src/loaders/svg/tvgSvgLoaderCommon.h @@ -590,7 +590,7 @@ struct SvgLoaderData SvgNode* def = nullptr; //also used to store nested graphic nodes SvgNode* cssStyle = nullptr; Array gradients; - SvgStyleGradient* latestGradient = nullptr; //For stops + Array gradientStack; //For stops SvgParser* svgParse = nullptr; Array cloneNodes; Array nodesToStyle;