svg_loader: postpone cloneNode()
authorMichal Maciola <71131832+mmaciola@users.noreply.github.com>
Wed, 7 Jul 2021 06:34:15 +0000 (08:34 +0200)
committerJunsuChoi <jsuya.choi@samsung.com>
Thu, 8 Jul 2021 01:29:10 +0000 (10:29 +0900)
Some svg export software puts <defs> element at the end of the file.
If so, the <defs> element won't be found, when parsing <use>.
In such scenario, this patch postpone node cloning until the whole file
is parsed.

@issue: #568

src/loaders/svg/tvgSvgLoader.cpp
src/loaders/svg/tvgSvgLoaderCommon.h

index 5f58c83e5056be2c8c66bcde2007e9564348dbb7..6bff1d421ead9d05a605c225704a687e1c046700 100644 (file)
@@ -1715,6 +1715,25 @@ static void _cloneNode(SvgNode* from, SvgNode* parent)
 }
 
 
+static void _postponeCloneNode(SvgLoaderData* loader, SvgNode *node, string* id) {
+    SvgNodeIdPair nodeIdPair;
+    nodeIdPair.node = node;
+    nodeIdPair.id = id;
+    loader->cloneNodes.push(nodeIdPair);
+}
+
+
+static void _clonePostponedNodes(Array<SvgNodeIdPair>* cloneNodes) {
+    for (uint32_t i = 0; i < cloneNodes->count; ++i) {
+        SvgNodeIdPair nodeIdPair = cloneNodes->data[i];
+        SvgNode *defs = _getDefsNode(nodeIdPair.node);
+        SvgNode *nodeFrom = _findChildById(defs, nodeIdPair.id->c_str());
+        _cloneNode(nodeFrom, nodeIdPair.node);
+        delete nodeIdPair.id;
+    }
+}
+
+
 static bool _attrParseUseNode(void* data, const char* key, const char* value)
 {
     SvgLoaderData* loader = (SvgLoaderData*)data;
@@ -1725,8 +1744,15 @@ static bool _attrParseUseNode(void* data, const char* key, const char* value)
         id = _idFromHref(value);
         defs = _getDefsNode(node);
         nodeFrom = _findChildById(defs, id->c_str());
-        _cloneNode(nodeFrom, node);
-        delete id;
+        if (nodeFrom) {
+            _cloneNode(nodeFrom, node);
+            delete id;
+        } else {
+            //some svg export software include <defs> element at the end of the file
+            //if so the 'from' element won't be found now and we have to repeat finding
+            //after the whole file is parsed
+            _postponeCloneNode(loader, node, id);
+        }
     } else if (!strcmp(key, "clip-path")) {
         _handleClipPathAttr(loader, node, value);
     } else if (!strcmp(key, "mask")) {
@@ -2682,6 +2708,8 @@ void SvgLoader::run(unsigned tid)
 
         _updateComposite(loaderData.doc, loaderData.doc);
         if (defs) _updateComposite(loaderData.doc, defs);
+
+        if (loaderData.cloneNodes.count > 0) _clonePostponedNodes(&loaderData.cloneNodes);
     }
     root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh);
 };
index 07b66e6894959523160426f7afc2ca84c8cf648a..b9f02b80fa5c685c4d5bea137be1548531b28d32 100644 (file)
@@ -333,6 +333,12 @@ struct SvgParser
     } gradient;
 };
 
+struct SvgNodeIdPair
+{
+    SvgNode* node;
+    string *id;
+};
+
 struct SvgLoaderData
 {
     Array<SvgNode *> stack = {nullptr, 0, 0};
@@ -341,6 +347,7 @@ struct SvgLoaderData
     Array<SvgStyleGradient*> gradients;
     SvgStyleGradient* latestGradient = nullptr; //For stops
     SvgParser* svgParse = nullptr;
+    Array<SvgNodeIdPair> cloneNodes;
     int level = 0;
     bool result = false;
 };