svg_loader: proper precedence of a styling 24/289024/1
authorMira Grudzinska <m.grudzinska@samsung.com>
Mon, 17 Jan 2022 23:06:21 +0000 (00:06 +0100)
committerMichal Szczecinski <mihashco89@gmail.com>
Mon, 27 Feb 2023 09:52:05 +0000 (10:52 +0100)
The attribute values should be copied from a style node only if they
were set in the destination node using the attributes (in opocity to
a style attribute). A proper copyCssStyleAttr() function is introduced.

Change-Id: Iaaa0c2b561c2bea276617bfd87dc26ed80bb4bbd

src/loaders/svg/tvgSvgLoader.cpp

index 88637d6..c13ef0d 100644 (file)
@@ -81,9 +81,6 @@ typedef SvgNode* (*FactoryMethod)(SvgLoaderData* loader, SvgNode* parent, const
 typedef SvgStyleGradient* (*GradientFactoryMethod)(SvgLoaderData* loader, const char* buf, unsigned bufLength);
 
 
-static void _copyAttr(SvgNode* to, const SvgNode* from, bool copyGeomAttrib = true);
-
-
 static char* _skipSpace(const char* str, const char* end)
 {
     while (((end && str < end) || (!end && *str != '\0')) && isspace(*str)) {
@@ -960,8 +957,95 @@ static SvgNode* _findCssStyleNode(const SvgNode* cssStyle, const char* title, Sv
         if ((*child)->type == type && ((*child)->id) && !strcmp((*child)->id, title)) return (*child);
     }
     return nullptr;
+}
 
 
+static void _cssStyleCopy(SvgStyleProperty* to, const SvgStyleProperty* from)
+{
+    if (from == nullptr) return;
+    //Copy the properties of 'from' only if they were explicitly set (not the default ones).
+    if (from->curColorSet && !((int)to->flags & (int)SvgStyleFlags::Color)) {
+        to->color = from->color;
+        to->curColorSet = true;
+        to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Color);
+    }
+    //Fill
+    if (((int)from->fill.flags & (int)SvgFillFlags::Paint) && !((int)to->flags & (int)SvgStyleFlags::Fill)) {
+        to->fill.paint.color = from->fill.paint.color;
+        to->fill.paint.none = from->fill.paint.none;
+        to->fill.paint.curColor = from->fill.paint.curColor;
+        if (from->fill.paint.url) to->fill.paint.url = _copyId(from->fill.paint.url);
+        to->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)SvgFillFlags::Paint);
+        to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Fill);
+    }
+    if (((int)from->fill.flags & (int)SvgFillFlags::Opacity) && !((int)to->flags & (int)SvgStyleFlags::FillOpacity)) {
+        to->fill.opacity = from->fill.opacity;
+        to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::FillOpacity);
+    }
+    if (((int)from->fill.flags & (int)SvgFillFlags::FillRule) && !((int)to->flags & (int)SvgStyleFlags::FillRule)) {
+        to->fill.fillRule = from->fill.fillRule;
+        to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::FillRule);
+    }
+    //Stroke
+    if (((int)from->stroke.flags & (int)SvgStrokeFlags::Paint) && !((int)to->flags & (int)SvgStyleFlags::Stroke)) {
+        to->stroke.paint.color = from->stroke.paint.color;
+        to->stroke.paint.none = from->stroke.paint.none;
+        to->stroke.paint.curColor = from->stroke.paint.curColor;
+        to->stroke.paint.url = from->stroke.paint.url ? _copyId(from->stroke.paint.url) : nullptr;
+        to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Paint);
+        to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Stroke);
+    }
+    if (((int)from->stroke.flags & (int)SvgStrokeFlags::Opacity) && !((int)to->flags & (int)SvgStyleFlags::StrokeOpacity)) {
+        to->stroke.opacity = from->stroke.opacity;
+        to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeOpacity);
+    }
+    if (((int)from->stroke.flags & (int)SvgStrokeFlags::Width) && !((int)to->flags & (int)SvgStyleFlags::StrokeWidth)) {
+        to->stroke.width = from->stroke.width;
+        to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeWidth);
+    }
+    if (((int)from->stroke.flags & (int)SvgStrokeFlags::Dash) && !((int)to->flags & (int)SvgStyleFlags::StrokeDashArray)) {
+        if (from->stroke.dash.array.count > 0) {
+            to->stroke.dash.array.clear();
+            to->stroke.dash.array.reserve(from->stroke.dash.array.count);
+            for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) {
+                to->stroke.dash.array.push(from->stroke.dash.array.data[i]);
+            }
+            to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeDashArray);
+        }
+    }
+    if (((int)from->stroke.flags & (int)SvgStrokeFlags::Cap) && !((int)to->flags & (int)SvgStyleFlags::StrokeLineCap)) {
+        to->stroke.cap = from->stroke.cap;
+        to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeLineCap);
+    }
+    if (((int)from->stroke.flags & (int)SvgStrokeFlags::Join) && !((int)to->flags & (int)SvgStyleFlags::StrokeLineJoin)) {
+        to->stroke.join = from->stroke.join;
+        to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeLineJoin);
+    }
+    //Opacity
+    //TODO: it can be set to be 255 and shouldn't be changed by attribute 'opacity'
+    if (from->opacity < 255 && !((int)to->flags & (int)SvgStyleFlags::Opacity)) {
+        to->opacity = from->opacity;
+        to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Opacity);
+    }
+    //TODO: support clip-path, mask, mask-type, display
+}
+
+
+static void _copyCssStyleAttr(SvgNode* to, const SvgNode* from)
+{
+    //Copy matrix attribute
+    if (from->transform && !((int)to->style->flags & (int)SvgStyleFlags::Transform)) {
+        to->transform = (Matrix*)malloc(sizeof(Matrix));
+        if (to->transform) {
+            *to->transform = *from->transform;
+            to->style->flags = (SvgStyleFlags)((int)to->style->flags | (int)SvgStyleFlags::Transform);
+        }
+    }
+    //Copy style attribute
+    _cssStyleCopy(to->style, from->style);
+    //TODO: clips and masks are not supported yet in css style
+    if (from->style->clipPath.url) to->style->clipPath.url = strdup(from->style->clipPath.url);
+    if (from->style->mask.url) to->style->mask.url = strdup(from->style->mask.url);
 }
 
 
@@ -975,7 +1059,7 @@ static void _handleCssClassAttr(SvgLoaderData* loader, SvgNode* node, const char
     //TODO: works only if style was defined before it is used
     if (auto cssNode = _findCssStyleNode(loader->cssStyle, *cssClass, node->type)) {
         //TODO: check SVG2 standard - should the geometric properties be copied?
-        _copyAttr(node, cssNode, false);
+        _copyCssStyleAttr(node, cssNode);
     }
 }
 
@@ -1932,7 +2016,7 @@ static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from)
 }
 
 
-static void _copyAttr(SvgNode* to, const SvgNode* from, bool copyGeomAttrib)
+static void _copyAttr(SvgNode* to, const SvgNode* from)
 {
     //Copy matrix attribute
     if (from->transform) {
@@ -1947,67 +2031,65 @@ static void _copyAttr(SvgNode* to, const SvgNode* from, bool copyGeomAttrib)
     if (from->style->clipPath.url) to->style->clipPath.url = strdup(from->style->clipPath.url);
     if (from->style->mask.url) to->style->mask.url = strdup(from->style->mask.url);
 
-    if (copyGeomAttrib) {
-        //Copy node attribute
-        switch (from->type) {
-            case SvgNodeType::Circle: {
-                to->node.circle.cx = from->node.circle.cx;
-                to->node.circle.cy = from->node.circle.cy;
-                to->node.circle.r = from->node.circle.r;
-                break;
-            }
-            case SvgNodeType::Ellipse: {
-                to->node.ellipse.cx = from->node.ellipse.cx;
-                to->node.ellipse.cy = from->node.ellipse.cy;
-                to->node.ellipse.rx = from->node.ellipse.rx;
-                to->node.ellipse.ry = from->node.ellipse.ry;
-                break;
-            }
-            case SvgNodeType::Rect: {
-                to->node.rect.x = from->node.rect.x;
-                to->node.rect.y = from->node.rect.y;
-                to->node.rect.w = from->node.rect.w;
-                to->node.rect.h = from->node.rect.h;
-                to->node.rect.rx = from->node.rect.rx;
-                to->node.rect.ry = from->node.rect.ry;
-                to->node.rect.hasRx = from->node.rect.hasRx;
-                to->node.rect.hasRy = from->node.rect.hasRy;
-                break;
-            }
-            case SvgNodeType::Line: {
-                to->node.line.x1 = from->node.line.x1;
-                to->node.line.y1 = from->node.line.y1;
-                to->node.line.x2 = from->node.line.x2;
-                to->node.line.y2 = from->node.line.y2;
-                break;
-            }
-            case SvgNodeType::Path: {
-                if (from->node.path.path) to->node.path.path = strdup(from->node.path.path);
-                break;
-            }
-            case SvgNodeType::Polygon: {
-                to->node.polygon.pointsCount = from->node.polygon.pointsCount;
-                to->node.polygon.points = (float*)malloc(to->node.polygon.pointsCount * sizeof(float));
-                memcpy(to->node.polygon.points, from->node.polygon.points, to->node.polygon.pointsCount * sizeof(float));
-                break;
-            }
-            case SvgNodeType::Polyline: {
-                to->node.polyline.pointsCount = from->node.polyline.pointsCount;
-                to->node.polyline.points = (float*)malloc(to->node.polyline.pointsCount * sizeof(float));
-                memcpy(to->node.polyline.points, from->node.polyline.points, to->node.polyline.pointsCount * sizeof(float));
-                break;
-            }
-            case SvgNodeType::Image: {
-                to->node.image.x = from->node.image.x;
-                to->node.image.y = from->node.image.y;
-                to->node.image.w = from->node.image.w;
-                to->node.image.h = from->node.image.h;
-                if (from->node.image.href) to->node.image.href = strdup(from->node.image.href);
-                break;
-            }
-            default: {
-                break;
-            }
+    //Copy node attribute
+    switch (from->type) {
+        case SvgNodeType::Circle: {
+            to->node.circle.cx = from->node.circle.cx;
+            to->node.circle.cy = from->node.circle.cy;
+            to->node.circle.r = from->node.circle.r;
+            break;
+        }
+        case SvgNodeType::Ellipse: {
+            to->node.ellipse.cx = from->node.ellipse.cx;
+            to->node.ellipse.cy = from->node.ellipse.cy;
+            to->node.ellipse.rx = from->node.ellipse.rx;
+            to->node.ellipse.ry = from->node.ellipse.ry;
+            break;
+        }
+        case SvgNodeType::Rect: {
+            to->node.rect.x = from->node.rect.x;
+            to->node.rect.y = from->node.rect.y;
+            to->node.rect.w = from->node.rect.w;
+            to->node.rect.h = from->node.rect.h;
+            to->node.rect.rx = from->node.rect.rx;
+            to->node.rect.ry = from->node.rect.ry;
+            to->node.rect.hasRx = from->node.rect.hasRx;
+            to->node.rect.hasRy = from->node.rect.hasRy;
+            break;
+        }
+        case SvgNodeType::Line: {
+            to->node.line.x1 = from->node.line.x1;
+            to->node.line.y1 = from->node.line.y1;
+            to->node.line.x2 = from->node.line.x2;
+            to->node.line.y2 = from->node.line.y2;
+            break;
+        }
+        case SvgNodeType::Path: {
+            if (from->node.path.path) to->node.path.path = strdup(from->node.path.path);
+            break;
+        }
+        case SvgNodeType::Polygon: {
+            to->node.polygon.pointsCount = from->node.polygon.pointsCount;
+            to->node.polygon.points = (float*)malloc(to->node.polygon.pointsCount * sizeof(float));
+            memcpy(to->node.polygon.points, from->node.polygon.points, to->node.polygon.pointsCount * sizeof(float));
+            break;
+        }
+        case SvgNodeType::Polyline: {
+            to->node.polyline.pointsCount = from->node.polyline.pointsCount;
+            to->node.polyline.points = (float*)malloc(to->node.polyline.pointsCount * sizeof(float));
+            memcpy(to->node.polyline.points, from->node.polyline.points, to->node.polyline.pointsCount * sizeof(float));
+            break;
+        }
+        case SvgNodeType::Image: {
+            to->node.image.x = from->node.image.x;
+            to->node.image.y = from->node.image.y;
+            to->node.image.w = from->node.image.w;
+            to->node.image.h = from->node.image.h;
+            if (from->node.image.href) to->node.image.href = strdup(from->node.image.href);
+            break;
+        }
+        default: {
+            break;
         }
     }
 }
@@ -2683,7 +2765,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
 }
 
 
-static void _svgLoaderParserXmlStyle(SvgLoaderData* loader, const char* content, unsigned int length)
+static void _svgLoaderParserXmlCssStyle(SvgLoaderData* loader, const char* content, unsigned int length)
 {
     char* tag;
     char* name;
@@ -2738,7 +2820,7 @@ static bool _svgLoaderParser(void* data, SimpleXMLType type, const char* content
         }
         case SimpleXMLType::Data:
         case SimpleXMLType::CData: {
-            if (loader->style) _svgLoaderParserXmlStyle(loader, content, length);
+            if (loader->style) _svgLoaderParserXmlCssStyle(loader, content, length);
             break;
         }
         case SimpleXMLType::DoctypeChild: {