From bf89e88b07936b623901dc9bc41c781ec38fbb90 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Mon, 17 Jan 2022 22:32:19 +0100 Subject: [PATCH 01/16] svg_loader: additional check while style parsing This check is needed so the function can be used to parse not only the inline styles, but also the css internal style sheet. Change-Id: I6e325d81781326deaccdad8170f11f1acf5f43f2 --- src/loaders/svg/tvgXmlParser.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/loaders/svg/tvgXmlParser.cpp b/src/loaders/svg/tvgXmlParser.cpp index 4b54e06..20fd85c 100644 --- a/src/loaders/svg/tvgXmlParser.cpp +++ b/src/loaders/svg/tvgXmlParser.cpp @@ -468,6 +468,13 @@ bool simpleXmlParseW3CAttribute(const char* buf, TVG_UNUSED unsigned buflen, sim do { char* sep = (char*)strchr(buf, ':'); next = (char*)strchr(buf, ';'); + if (sep >= end) + { + next = nullptr; + sep = nullptr; + } + if (next >= end) next = nullptr; + key[0] = '\0'; val[0] = '\0'; -- 2.7.4 From a134be4f28bf1d3521f00b0aeee6fb7d3a289755 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Mon, 17 Jan 2022 22:55:02 +0100 Subject: [PATCH 02/16] svg_loader: buflen arg used in the simpleXmlParseW3CAttribute() While parsing the css internal style sheets the buflen has to be passed. Change-Id: Iad755186b4ed2e73a93b8d4ed82fb9098a75bbeb --- src/loaders/svg/tvgSvgLoader.cpp | 33 +++++++++++++++++---------------- src/loaders/svg/tvgXmlParser.cpp | 5 ++--- src/loaders/svg/tvgXmlParser.h | 2 +- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index e074f20..38af850 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -798,7 +798,7 @@ static bool _attrParseSvgNode(void* data, const char* key, const char* value) } else if (!strcmp(key, "preserveAspectRatio")) { if (!strcmp(value, "none")) doc->preserveAspect = false; } else if (!strcmp(key, "style")) { - return simpleXmlParseW3CAttribute(value, 0, _parseStyleAttr, loader); + return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); } #ifdef THORVG_LOG_ENABLED else if ((!strcmp(key, "x") || !strcmp(key, "y")) && fabsf(svgUtilStrtof(value, nullptr)) > FLT_EPSILON) { @@ -951,13 +951,13 @@ static void _handleDisplayAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, } -static SvgNode* _findCssStyleNode(const SvgNode* cssStyle, const char* title) +static SvgNode* _findCssStyleNode(const SvgNode* cssStyle, const char* title, SvgNodeType type) { if (!cssStyle) return nullptr; auto child = cssStyle->child.data; for (uint32_t i = 0; i < cssStyle->child.count; ++i, ++child) { - if (((*child)->id) && !strcmp((*child)->id, title)) return (*child); + if ((*child)->type == type && ((*child)->id) && !strcmp((*child)->id, title)) return (*child); } return nullptr; @@ -973,8 +973,8 @@ static void _handleCssClassAttr(SvgLoaderData* loader, SvgNode* node, const char *cssClass = _copyId(value); //TODO: works only if style was defined before it is used - if (auto cssNode = _findCssStyleNode(loader->cssStyle, *cssClass)) { - //TODO: check SVG2 stndard - should the geometric properties be copied? + if (auto cssNode = _findCssStyleNode(loader->cssStyle, *cssClass, node->type)) { + //TODO: check SVG2 standard - should the geometric properties be copied? _copyAttr(node, cssNode, false); } } @@ -1054,7 +1054,7 @@ static bool _attrParseGNode(void* data, const char* key, const char* value) SvgNode* node = loader->svgParse->node; if (!strcmp(key, "style")) { - return simpleXmlParseW3CAttribute(value, 0, _parseStyleAttr, loader); + return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); } else if (!strcmp(key, "transform")) { node->transform = _parseTransformationMatrix(value); } else if (!strcmp(key, "id")) { @@ -1083,7 +1083,7 @@ static bool _attrParseClipPathNode(void* data, const char* key, const char* valu SvgClipNode* clip = &(node->node.clip); if (!strcmp(key, "style")) { - return simpleXmlParseW3CAttribute(value, 0, _parseStyleAttr, loader); + return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); } else if (!strcmp(key, "transform")) { node->transform = _parseTransformationMatrix(value); } else if (!strcmp(key, "id")) { @@ -1107,7 +1107,7 @@ static bool _attrParseMaskNode(void* data, const char* key, const char* value) SvgMaskNode* mask = &(node->node.mask); if (!strcmp(key, "style")) { - return simpleXmlParseW3CAttribute(value, 0, _parseStyleAttr, loader); + return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); } else if (!strcmp(key, "transform")) { node->transform = _parseTransformationMatrix(value); } else if (!strcmp(key, "id")) { @@ -1286,7 +1286,7 @@ static bool _attrParsePathNode(void* data, const char* key, const char* value) //Temporary: need to copy path->path = _copyId(value); } else if (!strcmp(key, "style")) { - return simpleXmlParseW3CAttribute(value, 0, _parseStyleAttr, loader); + return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); } else if (!strcmp(key, "clip-path")) { _handleClipPathAttr(loader, node, value); } else if (!strcmp(key, "mask")) { @@ -1348,7 +1348,7 @@ static bool _attrParseCircleNode(void* data, const char* key, const char* value) } if (!strcmp(key, "style")) { - return simpleXmlParseW3CAttribute(value, 0, _parseStyleAttr, loader); + return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); } else if (!strcmp(key, "clip-path")) { _handleClipPathAttr(loader, node, value); } else if (!strcmp(key, "mask")) { @@ -1415,7 +1415,7 @@ static bool _attrParseEllipseNode(void* data, const char* key, const char* value } else if (!strcmp(key, "class")) { _handleCssClassAttr(loader, node, value); } else if (!strcmp(key, "style")) { - return simpleXmlParseW3CAttribute(value, 0, _parseStyleAttr, loader); + return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); } else if (!strcmp(key, "clip-path")) { _handleClipPathAttr(loader, node, value); } else if (!strcmp(key, "mask")) { @@ -1489,7 +1489,7 @@ static bool _attrParsePolygonNode(void* data, const char* key, const char* value if (!strcmp(key, "points")) { return _attrParsePolygonPoints(value, &polygon->points, &polygon->pointsCount); } else if (!strcmp(key, "style")) { - return simpleXmlParseW3CAttribute(value, 0, _parseStyleAttr, loader); + return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); } else if (!strcmp(key, "clip-path")) { _handleClipPathAttr(loader, node, value); } else if (!strcmp(key, "mask")) { @@ -1576,7 +1576,7 @@ static bool _attrParseRectNode(void* data, const char* key, const char* value) } else if (!strcmp(key, "class")) { _handleCssClassAttr(loader, node, value); } else if (!strcmp(key, "style")) { - ret = simpleXmlParseW3CAttribute(value, 0, _parseStyleAttr, loader); + ret = simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); } else if (!strcmp(key, "clip-path")) { _handleClipPathAttr(loader, node, value); } else if (!strcmp(key, "mask")) { @@ -1641,7 +1641,7 @@ static bool _attrParseLineNode(void* data, const char* key, const char* value) } else if (!strcmp(key, "class")) { _handleCssClassAttr(loader, node, value); } else if (!strcmp(key, "style")) { - return simpleXmlParseW3CAttribute(value, 0, _parseStyleAttr, loader); + return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); } else if (!strcmp(key, "clip-path")) { _handleClipPathAttr(loader, node, value); } else if (!strcmp(key, "mask")) { @@ -1714,7 +1714,7 @@ static bool _attrParseImageNode(void* data, const char* key, const char* value) } else if (!strcmp(key, "class")) { _handleCssClassAttr(loader, node, value); } else if (!strcmp(key, "style")) { - return simpleXmlParseW3CAttribute(value, 0, _parseStyleAttr, loader); + return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); } else if (!strcmp(key, "clip-path")) { _handleClipPathAttr(loader, node, value); } else if (!strcmp(key, "mask")) { @@ -2390,7 +2390,7 @@ static bool _attrParseStops(void* data, const char* key, const char* value) _toColor(value, &stop->r, &stop->g, &stop->b, nullptr); } } else if (!strcmp(key, "style")) { - simpleXmlParseW3CAttribute(value, 0, _attrParseStopsStyle, data); + simpleXmlParseW3CAttribute(value, strlen(value), _attrParseStopsStyle, data); } else { return false; } @@ -2717,6 +2717,7 @@ static void _svgLoaderParserXmlStyle(SvgLoaderData* loader, const char* content, buflen -= next - buf; buf = next; + free(tag); free(name); } diff --git a/src/loaders/svg/tvgXmlParser.cpp b/src/loaders/svg/tvgXmlParser.cpp index 20fd85c..e3e42b0 100644 --- a/src/loaders/svg/tvgXmlParser.cpp +++ b/src/loaders/svg/tvgXmlParser.cpp @@ -450,7 +450,7 @@ bool simpleXmlParse(const char* buf, unsigned bufLength, bool strip, simpleXMLCb } -bool simpleXmlParseW3CAttribute(const char* buf, TVG_UNUSED unsigned buflen, simpleXMLAttributeCb func, const void* data) +bool simpleXmlParseW3CAttribute(const char* buf, unsigned buflen, simpleXMLAttributeCb func, const void* data) { const char* end; char* key; @@ -459,7 +459,7 @@ bool simpleXmlParseW3CAttribute(const char* buf, TVG_UNUSED unsigned buflen, sim if (!buf) return false; - end = buf + strlen(buf); + end = buf + buflen; key = (char*)alloca(end - buf + 1); val = (char*)alloca(end - buf + 1); @@ -475,7 +475,6 @@ bool simpleXmlParseW3CAttribute(const char* buf, TVG_UNUSED unsigned buflen, sim } if (next >= end) next = nullptr; - key[0] = '\0'; val[0] = '\0'; diff --git a/src/loaders/svg/tvgXmlParser.h b/src/loaders/svg/tvgXmlParser.h index 832fb18..2ce7179 100644 --- a/src/loaders/svg/tvgXmlParser.h +++ b/src/loaders/svg/tvgXmlParser.h @@ -49,7 +49,7 @@ typedef bool (*simpleXMLAttributeCb)(void* data, const char* key, const char* va bool simpleXmlParseAttributes(const char* buf, unsigned buflen, simpleXMLAttributeCb func, const void* data); bool simpleXmlParse(const char* buf, unsigned buflen, bool strip, simpleXMLCb func, const void* data); -bool simpleXmlParseW3CAttribute(const char* buf, TVG_UNUSED unsigned buflen, simpleXMLAttributeCb func, const void* data); +bool simpleXmlParseW3CAttribute(const char* buf, unsigned buflen, simpleXMLAttributeCb func, const void* data); const char* simpleXmlParseCSSAttribute(const char* buf, unsigned bufLength, char** tag, char** name, const char** attrs, unsigned* attrsLength); const char* simpleXmlFindAttributesTag(const char* buf, unsigned buflen); bool isIgnoreUnsupportedLogElements(const char* tagName); -- 2.7.4 From 3685015cebf508480befb8c230b36ea61203b29f Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Mon, 17 Jan 2022 23:11:24 +0100 Subject: [PATCH 03/16] svg_loader: remove unnecessary variables Change-Id: If5f1418c3aab366c977db0f5ac4078cc915cb46e --- src/loaders/svg/tvgSvgLoader.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 38af850..88637d6 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -2694,11 +2694,7 @@ static void _svgLoaderParserXmlStyle(SvgLoaderData* loader, const char* content, GradientFactoryMethod gradientMethod; SvgNode *node = nullptr; - const char *buf = content; - unsigned buflen = length; - - - while (auto next = simpleXmlParseCSSAttribute(buf, buflen, &tag, &name, &attrs, &attrsLength)) { + while (auto next = simpleXmlParseCSSAttribute(content, length, &tag, &name, &attrs, &attrsLength)) { if ((method = _findGroupFactory(tag))) { //TODO - node->id ??? add additional var for svgnode? if ((node = method(loader, loader->cssStyle, attrs, attrsLength, simpleXmlParseW3CAttribute))) node->id = _copyId(name); @@ -2714,9 +2710,8 @@ static void _svgLoaderParserXmlStyle(SvgLoaderData* loader, const char* content, TVGLOG("SVG", "Unsupported elements used [Elements: %s]", tag); } - buflen -= next - buf; - buf = next; - + length -= next - content; + content = next; free(tag); free(name); -- 2.7.4 From 3c14a2cc49adb491f96c722d452c1cd909ca92a6 Mon Sep 17 00:00:00 2001 From: jykeon Date: Mon, 27 Feb 2023 18:07:26 +0900 Subject: [PATCH 04/16] Bump up 0.7.7 Change-Id: I7a248987ab997c664910bc3d16e4cb9bef775048 Signed-off-by: jykeon --- packaging/thorvg.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/thorvg.spec b/packaging/thorvg.spec index 295f2a1..1519072 100644 --- a/packaging/thorvg.spec +++ b/packaging/thorvg.spec @@ -1,6 +1,6 @@ Name: thorvg Summary: Thor Vector Graphics Library -Version: 0.7.6 +Version: 0.7.7 Release: 1 Group: Graphics System/Rendering Engine License: MIT -- 2.7.4 From 6932709d535b23dbaaab67ce300adf96c8e9332c Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Tue, 18 Jan 2022 00:06:21 +0100 Subject: [PATCH 05/16] svg_loader: proper precedence of a styling 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 | 218 +++++++++++++++++++++++++++------------ 1 file changed, 150 insertions(+), 68 deletions(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 88637d6..c13ef0d 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -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: { -- 2.7.4 From ffef3d952c4c5ee133594931bb4b647156859864 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Tue, 18 Jan 2022 02:36:26 +0100 Subject: [PATCH 06/16] svg_loader: supporting different target formats in css Css styling supports now targets defined only by tag or only by name (tag.name was already supported before). Also proper precedence of a styling is fixed - flags were not set. Change-Id: I757fa8ac54ccd38603f250691add141d0bacad21 --- src/loaders/svg/tvgSvgLoader.cpp | 50 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index c13ef0d..9b0097d 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -954,7 +954,23 @@ static SvgNode* _findCssStyleNode(const SvgNode* cssStyle, const char* title, Sv auto child = cssStyle->child.data; for (uint32_t i = 0; i < cssStyle->child.count; ++i, ++child) { - if ((*child)->type == type && ((*child)->id) && !strcmp((*child)->id, title)) return (*child); + if ((*child)->type == type) { + if ((!title && !(*child)->id) || (title && (*child)->id && !strcmp((*child)->id, title))) return (*child); + } + } + return nullptr; +} + + +static SvgNode* _findCssStyleNode(const SvgNode* cssStyle, const char* title) +{ + if (!cssStyle) return nullptr; + + auto child = cssStyle->child.data; + for (uint32_t i = 0; i < cssStyle->child.count; ++i, ++child) { + if ((*child)->type == SvgNodeType::CssStyle) { + if ((title && (*child)->id && !strcmp((*child)->id, title))) return (*child); + } } return nullptr; } @@ -980,10 +996,12 @@ static void _cssStyleCopy(SvgStyleProperty* to, const SvgStyleProperty* from) } if (((int)from->fill.flags & (int)SvgFillFlags::Opacity) && !((int)to->flags & (int)SvgStyleFlags::FillOpacity)) { to->fill.opacity = from->fill.opacity; + to->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)SvgFillFlags::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->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)SvgFillFlags::FillRule); to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::FillRule); } //Stroke @@ -997,10 +1015,12 @@ static void _cssStyleCopy(SvgStyleProperty* to, const SvgStyleProperty* from) } if (((int)from->stroke.flags & (int)SvgStrokeFlags::Opacity) && !((int)to->flags & (int)SvgStyleFlags::StrokeOpacity)) { to->stroke.opacity = from->stroke.opacity; + to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::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->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Width); to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeWidth); } if (((int)from->stroke.flags & (int)SvgStrokeFlags::Dash) && !((int)to->flags & (int)SvgStyleFlags::StrokeDashArray)) { @@ -1010,15 +1030,18 @@ static void _cssStyleCopy(SvgStyleProperty* to, const SvgStyleProperty* from) for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) { to->stroke.dash.array.push(from->stroke.dash.array.data[i]); } + to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Dash); 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->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::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->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Join); to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeLineJoin); } //Opacity @@ -1061,6 +1084,9 @@ static void _handleCssClassAttr(SvgLoaderData* loader, SvgNode* node, const char //TODO: check SVG2 standard - should the geometric properties be copied? _copyCssStyleAttr(node, cssNode); } + if (auto cssNode = _findCssStyleNode(loader->cssStyle, *cssClass)) { + _copyCssStyleAttr(node, cssNode); + } } @@ -2788,6 +2814,8 @@ static void _svgLoaderParserXmlCssStyle(SvgLoaderData* loader, const char* conte //SvgStyleGradient* gradient = gradientMethod(loader, attrs, attrsLength); } else if (!strcmp(tag, "stop")) { //TODO - implement + } else if (!strcmp(tag, "all")) { + if ((node = _createCssStyleNode(loader, loader->cssStyle, attrs, attrsLength, simpleXmlParseW3CAttribute))) node->id = _copyId(name); } else if (!isIgnoreUnsupportedLogElements(tag)) { TVGLOG("SVG", "Unsupported elements used [Elements: %s]", tag); } @@ -2968,6 +2996,23 @@ static void _updateComposite(SvgNode* node, SvgNode* root) } +static void _updateCssStyle(SvgNode* doc, SvgNode* cssStyle) +{ + if (doc->child.count > 0) { + auto child = doc->child.data; + for (uint32_t i = 0; i < doc->child.count; ++i, ++child) { + if (auto cssNode = _findCssStyleNode(cssStyle, nullptr, (*child)->type)) { + _copyCssStyleAttr(*child, cssNode); + } + if (auto cssNode = _findCssStyleNode(cssStyle, nullptr)) { + _copyCssStyleAttr(*child, cssNode); + } + _updateCssStyle(*child, cssStyle); + } + } +} + + static void _freeNodeStyle(SvgStyleProperty* style) { if (!style) return; @@ -3143,6 +3188,9 @@ void SvgLoader::run(unsigned tid) if (loaderData.gradients.count > 0) _updateGradient(loaderData.doc, &loaderData.gradients); if (defs) _updateGradient(loaderData.doc, &defs->node.defs.gradients); + + //TODO: defs should be updated as well? + if (loaderData.cssStyle) _updateCssStyle(loaderData.doc, loaderData.cssStyle); } root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh, w, h, preserveAspect, svgPath); } -- 2.7.4 From 08c3d45c5244ac413d711f1c65aa84f82d667ce0 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Tue, 18 Jan 2022 14:33:55 +0100 Subject: [PATCH 07/16] svg_loader: css styling supported also when defs were postponed Defs can be defined at the end of the file. In such a case each node with a defined class attribute has to be marked and checked at the final stage of processing - the proper style node has to be found and its style has to be applied. Change-Id: If464fa8970e13c71820553b04caecc5c3512edc9 --- src/loaders/svg/tvgSvgLoader.cpp | 34 +++++++++++++++++++++++++++++----- src/loaders/svg/tvgSvgLoaderCommon.h | 1 + 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 9b0097d..6ebd882 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -80,6 +80,7 @@ typedef bool (*parseAttributes)(const char*, unsigned, simpleXMLAttributeCb, con typedef SvgNode* (*FactoryMethod)(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func); typedef SvgStyleGradient* (*GradientFactoryMethod)(SvgLoaderData* loader, const char* buf, unsigned bufLength); +static void _postponeCloneNode(Array* nodes, SvgNode *node, char* id); static char* _skipSpace(const char* str, const char* end) { @@ -1054,6 +1055,7 @@ static void _cssStyleCopy(SvgStyleProperty* to, const SvgStyleProperty* from) } +//TODO: check SVG2 standard - should the geometric properties be copied? static void _copyCssStyleAttr(SvgNode* to, const SvgNode* from) { //Copy matrix attribute @@ -1079,14 +1081,19 @@ static void _handleCssClassAttr(SvgLoaderData* loader, SvgNode* node, const char if (*cssClass && value) free(*cssClass); *cssClass = _copyId(value); - //TODO: works only if style was defined before it is used + bool cssClassFound = false; + + //css styling: tag.name has higher priority than .name if (auto cssNode = _findCssStyleNode(loader->cssStyle, *cssClass, node->type)) { - //TODO: check SVG2 standard - should the geometric properties be copied? + cssClassFound = true; _copyCssStyleAttr(node, cssNode); } if (auto cssNode = _findCssStyleNode(loader->cssStyle, *cssClass)) { + cssClassFound = true; _copyCssStyleAttr(node, cssNode); } + + if (!cssClassFound) _postponeCloneNode(&loader->cloneCssStyleNodes, node, *cssClass); } @@ -2146,9 +2153,9 @@ static void _cloneNode(SvgNode* from, SvgNode* parent, int depth) } -static void _postponeCloneNode(SvgLoaderData* loader, SvgNode *node, char* id) +static void _postponeCloneNode(Array* nodes, SvgNode *node, char* id) { - loader->cloneNodes.push({node, id}); + nodes->push({node, id}); } @@ -2206,7 +2213,7 @@ static bool _attrParseUseNode(void* data, const char* key, const char* value) //some svg export software include 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); + _postponeCloneNode(&loader->cloneNodes, node, id); } } else { return _attrParseGNode(data, key, value); @@ -3013,6 +3020,22 @@ static void _updateCssStyle(SvgNode* doc, SvgNode* cssStyle) } +static void _clonePostponedCssStyleNodes(Array* cloneCssStyleNodes, SvgNode* cssStyle) +{ + for (uint32_t i = 0; i < cloneCssStyleNodes->count; ++i) { + auto nodeIdPair = cloneCssStyleNodes->data[i]; + + //css styling: tag.name has higher priority than .name + if (auto cssNode = _findCssStyleNode(cssStyle, nodeIdPair.id, nodeIdPair.node->type)) { + _copyCssStyleAttr(nodeIdPair.node, cssNode); + } + if (auto cssNode = _findCssStyleNode(cssStyle, nodeIdPair.id)) { + _copyCssStyleAttr(nodeIdPair.node, cssNode); + } + } +} + + static void _freeNodeStyle(SvgStyleProperty* style) { if (!style) return; @@ -3189,6 +3212,7 @@ void SvgLoader::run(unsigned tid) if (loaderData.gradients.count > 0) _updateGradient(loaderData.doc, &loaderData.gradients); if (defs) _updateGradient(loaderData.doc, &defs->node.defs.gradients); + if (loaderData.cloneCssStyleNodes.count > 0) _clonePostponedCssStyleNodes(&loaderData.cloneCssStyleNodes, loaderData.cssStyle); //TODO: defs should be updated as well? if (loaderData.cssStyle) _updateCssStyle(loaderData.doc, loaderData.cssStyle); } diff --git a/src/loaders/svg/tvgSvgLoaderCommon.h b/src/loaders/svg/tvgSvgLoaderCommon.h index 9669660..ad88897 100644 --- a/src/loaders/svg/tvgSvgLoaderCommon.h +++ b/src/loaders/svg/tvgSvgLoaderCommon.h @@ -413,6 +413,7 @@ struct SvgLoaderData SvgStyleGradient* latestGradient = nullptr; //For stops SvgParser* svgParse = nullptr; Array cloneNodes; + Array cloneCssStyleNodes; int level = 0; bool result = false; bool style = false; //TODO: find a better sollution? -- 2.7.4 From 6ec669ec7145fbe90b3b6a926eedf0daac136100 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Wed, 19 Jan 2022 02:30:01 +0100 Subject: [PATCH 08/16] svg_loader: restoring changes from ed3b17b2281df03f2df67ebc34cafa12b2aea737 (preventing memcpy from a nullptr) Change-Id: If5e0628815d375d93334003f4d4e71da1c9f7b51 --- src/loaders/svg/tvgSvgLoader.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 6ebd882..c0d547e 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -2102,15 +2102,17 @@ static void _copyAttr(SvgNode* to, const SvgNode* from) 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)); + if ((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)); + if ((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: { -- 2.7.4 From c57338e42d449f58fda0274b62b932ed13712864 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Wed, 19 Jan 2022 02:34:39 +0100 Subject: [PATCH 09/16] svg_loader: some names changed, no logical changes Change-Id: I4101cac14d2792f9b2fb85529e3908267362eac6 --- src/loaders/svg/tvgSvgLoader.cpp | 10 +++++----- src/loaders/svg/tvgSvgLoaderCommon.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index c0d547e..23628fd 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -1093,7 +1093,7 @@ static void _handleCssClassAttr(SvgLoaderData* loader, SvgNode* node, const char _copyCssStyleAttr(node, cssNode); } - if (!cssClassFound) _postponeCloneNode(&loader->cloneCssStyleNodes, node, *cssClass); + if (!cssClassFound) _postponeCloneNode(&loader->nodesToStyle, node, *cssClass); } @@ -3022,10 +3022,10 @@ static void _updateCssStyle(SvgNode* doc, SvgNode* cssStyle) } -static void _clonePostponedCssStyleNodes(Array* cloneCssStyleNodes, SvgNode* cssStyle) +static void _stylePostponedNodes(Array* nodesToStyle, SvgNode* cssStyle) { - for (uint32_t i = 0; i < cloneCssStyleNodes->count; ++i) { - auto nodeIdPair = cloneCssStyleNodes->data[i]; + for (uint32_t i = 0; i < nodesToStyle->count; ++i) { + auto nodeIdPair = nodesToStyle->data[i]; //css styling: tag.name has higher priority than .name if (auto cssNode = _findCssStyleNode(cssStyle, nodeIdPair.id, nodeIdPair.node->type)) { @@ -3214,7 +3214,7 @@ void SvgLoader::run(unsigned tid) if (loaderData.gradients.count > 0) _updateGradient(loaderData.doc, &loaderData.gradients); if (defs) _updateGradient(loaderData.doc, &defs->node.defs.gradients); - if (loaderData.cloneCssStyleNodes.count > 0) _clonePostponedCssStyleNodes(&loaderData.cloneCssStyleNodes, loaderData.cssStyle); + if (loaderData.nodesToStyle.count > 0) _stylePostponedNodes(&loaderData.nodesToStyle, loaderData.cssStyle); //TODO: defs should be updated as well? if (loaderData.cssStyle) _updateCssStyle(loaderData.doc, loaderData.cssStyle); } diff --git a/src/loaders/svg/tvgSvgLoaderCommon.h b/src/loaders/svg/tvgSvgLoaderCommon.h index ad88897..1453b6d 100644 --- a/src/loaders/svg/tvgSvgLoaderCommon.h +++ b/src/loaders/svg/tvgSvgLoaderCommon.h @@ -413,7 +413,7 @@ struct SvgLoaderData SvgStyleGradient* latestGradient = nullptr; //For stops SvgParser* svgParse = nullptr; Array cloneNodes; - Array cloneCssStyleNodes; + Array nodesToStyle; int level = 0; bool result = false; bool style = false; //TODO: find a better sollution? -- 2.7.4 From 98884dc8e5a9454e5b57912a98aac66378eebf02 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Thu, 20 Jan 2022 02:07:30 +0100 Subject: [PATCH 10/16] svg_loader: custom _strndup added For the compatibility reasons a custom version of strndup is introduced. Change-Id: I8c1d0581eed8e708d7d3d5af121a7933b33ab77c --- src/loaders/svg/tvgXmlParser.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/loaders/svg/tvgXmlParser.cpp b/src/loaders/svg/tvgXmlParser.cpp index e3e42b0..7c3bb56 100644 --- a/src/loaders/svg/tvgXmlParser.cpp +++ b/src/loaders/svg/tvgXmlParser.cpp @@ -236,6 +236,14 @@ static SimpleXMLType _getXMLType(const char* itr, const char* itrEnd, size_t &to } +static char* _strndup(const char* src, unsigned len) +{ + auto ret = (char*)malloc(len + 1); + if (!ret) return nullptr; + ret[len] = '\0'; + return (char*)memcpy(ret, src, len); +} + /************************************************************************/ /* External Class Implementation */ /************************************************************************/ @@ -547,10 +555,10 @@ const char* simpleXmlParseCSSAttribute(const char* buf, unsigned bufLength, char } if (p == itr) *tag = strdup("all"); - else *tag = strndup(itr, p - itr); + else *tag = _strndup(itr, p - itr); if (p == itrEnd) *name = nullptr; - else *name = strndup(p + 1, itrEnd - p - 1); + else *name = _strndup(p + 1, itrEnd - p - 1); return (nextElement ? nextElement + 1 : nullptr); } -- 2.7.4 From 84d1bacc48b1e09f168654260339bb3941279daa Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Tue, 25 Jan 2022 12:20:22 +0100 Subject: [PATCH 11/16] svg_loader: tvg styling applied to the code Change-Id: I836926e52873e836c7dfdd64eae8ab56f0e4fe5d --- src/loaders/svg/tvgXmlParser.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/loaders/svg/tvgXmlParser.cpp b/src/loaders/svg/tvgXmlParser.cpp index 7c3bb56..7e8fdae 100644 --- a/src/loaders/svg/tvgXmlParser.cpp +++ b/src/loaders/svg/tvgXmlParser.cpp @@ -476,8 +476,7 @@ bool simpleXmlParseW3CAttribute(const char* buf, unsigned buflen, simpleXMLAttri do { char* sep = (char*)strchr(buf, ':'); next = (char*)strchr(buf, ';'); - if (sep >= end) - { + if (sep >= end) { next = nullptr; sep = nullptr; } -- 2.7.4 From 84a5f7e3982d373134113700edf8140f8db4faf5 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Thu, 27 Jan 2022 22:58:52 +0100 Subject: [PATCH 12/16] svg_loader: fixing cdata block reading The CData block is read if it's inside the style tag. In a case when the CData block was just after the style block, the data was still read, although it shouldn't be. Fixed. Change-Id: I3cd242d0738475c0cc986f6dc8a375e3b9d5b7a5 --- src/loaders/svg/tvgSvgLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 23628fd..351d872 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -2725,7 +2725,6 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, GradientFactoryMethod gradientMethod; SvgNode *node = nullptr, *parent = nullptr; loader->level++; - loader->style = false; attrs = simpleXmlFindAttributesTag(content, length); if (!attrs) { @@ -2835,6 +2834,7 @@ static void _svgLoaderParserXmlCssStyle(SvgLoaderData* loader, const char* conte free(tag); free(name); } + loader->style = false; } -- 2.7.4 From 8db4c1dbe42008458f767f737fab29ba0e8f07f2 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Fri, 28 Jan 2022 01:42:02 +0100 Subject: [PATCH 13/16] svg_loader: styling++ (no logical changes) Change-Id: I8cde1a51aaf567aa65b9cb838bd39242a8e4d61f --- src/loaders/svg/tvgSvgLoader.cpp | 17 ++++++----------- src/loaders/svg/tvgSvgLoaderCommon.h | 2 +- src/loaders/svg/tvgXmlParser.cpp | 4 ++-- src/loaders/svg/tvgXmlParser.h | 8 ++++---- 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 351d872..e68b776 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -75,13 +75,13 @@ #define PX_PER_MM 3.779528f //1 in = 25.4 mm -> PX_PER_IN/25.4 #define PX_PER_CM 37.79528f //1 in = 2.54 cm -> PX_PER_IN/2.54 - -typedef bool (*parseAttributes)(const char*, unsigned, simpleXMLAttributeCb, const void*); +typedef bool (*parseAttributes)(const char* buf, unsigned bufLength, simpleXMLAttributeCb func, const void* data); typedef SvgNode* (*FactoryMethod)(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func); typedef SvgStyleGradient* (*GradientFactoryMethod)(SvgLoaderData* loader, const char* buf, unsigned bufLength); static void _postponeCloneNode(Array* nodes, SvgNode *node, char* id); + static char* _skipSpace(const char* str, const char* end) { while (((end && str < end) || (!end && *str != '\0')) && isspace(*str)) { @@ -1055,7 +1055,6 @@ static void _cssStyleCopy(SvgStyleProperty* to, const SvgStyleProperty* from) } -//TODO: check SVG2 standard - should the geometric properties be copied? static void _copyCssStyleAttr(SvgNode* to, const SvgNode* from) { //Copy matrix attribute @@ -2812,20 +2811,17 @@ static void _svgLoaderParserXmlCssStyle(SvgLoaderData* loader, const char* conte while (auto next = simpleXmlParseCSSAttribute(content, length, &tag, &name, &attrs, &attrsLength)) { if ((method = _findGroupFactory(tag))) { - //TODO - node->id ??? add additional var for svgnode? - if ((node = method(loader, loader->cssStyle, attrs, attrsLength, simpleXmlParseW3CAttribute))) node->id = _copyId(name); + TVGLOG("SVG", "Unsupported elements used in the internal CSS style sheets [Elements: %s]", tag); } else if ((method = _findGraphicsFactory(tag))) { if ((node = method(loader, loader->cssStyle, attrs, attrsLength, simpleXmlParseW3CAttribute))) node->id = _copyId(name); - //TODO - implement } else if ((gradientMethod = _findGradientFactory(tag))) { - //TODO - implement - //SvgStyleGradient* gradient = gradientMethod(loader, attrs, attrsLength); + TVGLOG("SVG", "Unsupported elements used in the internal CSS style sheets [Elements: %s]", tag); } else if (!strcmp(tag, "stop")) { - //TODO - implement + TVGLOG("SVG", "Unsupported elements used in the internal CSS style sheets [Elements: %s]", tag); } else if (!strcmp(tag, "all")) { if ((node = _createCssStyleNode(loader, loader->cssStyle, attrs, attrsLength, simpleXmlParseW3CAttribute))) node->id = _copyId(name); } else if (!isIgnoreUnsupportedLogElements(tag)) { - TVGLOG("SVG", "Unsupported elements used [Elements: %s]", tag); + TVGLOG("SVG", "Unsupported elements used in the internal CSS style sheets [Elements: %s]", tag); } length -= next - content; @@ -3215,7 +3211,6 @@ void SvgLoader::run(unsigned tid) if (defs) _updateGradient(loaderData.doc, &defs->node.defs.gradients); if (loaderData.nodesToStyle.count > 0) _stylePostponedNodes(&loaderData.nodesToStyle, loaderData.cssStyle); - //TODO: defs should be updated as well? if (loaderData.cssStyle) _updateCssStyle(loaderData.doc, loaderData.cssStyle); } root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh, w, h, preserveAspect, svgPath); diff --git a/src/loaders/svg/tvgSvgLoaderCommon.h b/src/loaders/svg/tvgSvgLoaderCommon.h index 1453b6d..364ec15 100644 --- a/src/loaders/svg/tvgSvgLoaderCommon.h +++ b/src/loaders/svg/tvgSvgLoaderCommon.h @@ -416,7 +416,7 @@ struct SvgLoaderData Array nodesToStyle; int level = 0; bool result = false; - bool style = false; //TODO: find a better sollution? + bool style = false; }; /* diff --git a/src/loaders/svg/tvgXmlParser.cpp b/src/loaders/svg/tvgXmlParser.cpp index 7e8fdae..aa93870 100644 --- a/src/loaders/svg/tvgXmlParser.cpp +++ b/src/loaders/svg/tvgXmlParser.cpp @@ -458,7 +458,7 @@ bool simpleXmlParse(const char* buf, unsigned bufLength, bool strip, simpleXMLCb } -bool simpleXmlParseW3CAttribute(const char* buf, unsigned buflen, simpleXMLAttributeCb func, const void* data) +bool simpleXmlParseW3CAttribute(const char* buf, unsigned bufLength, simpleXMLAttributeCb func, const void* data) { const char* end; char* key; @@ -467,7 +467,7 @@ bool simpleXmlParseW3CAttribute(const char* buf, unsigned buflen, simpleXMLAttri if (!buf) return false; - end = buf + buflen; + end = buf + bufLength; key = (char*)alloca(end - buf + 1); val = (char*)alloca(end - buf + 1); diff --git a/src/loaders/svg/tvgXmlParser.h b/src/loaders/svg/tvgXmlParser.h index 2ce7179..e2761ca 100644 --- a/src/loaders/svg/tvgXmlParser.h +++ b/src/loaders/svg/tvgXmlParser.h @@ -47,11 +47,11 @@ enum class SimpleXMLType typedef bool (*simpleXMLCb)(void* data, SimpleXMLType type, const char* content, unsigned int length); typedef bool (*simpleXMLAttributeCb)(void* data, const char* key, const char* value); -bool simpleXmlParseAttributes(const char* buf, unsigned buflen, simpleXMLAttributeCb func, const void* data); -bool simpleXmlParse(const char* buf, unsigned buflen, bool strip, simpleXMLCb func, const void* data); -bool simpleXmlParseW3CAttribute(const char* buf, unsigned buflen, simpleXMLAttributeCb func, const void* data); +bool simpleXmlParseAttributes(const char* buf, unsigned bufLength, simpleXMLAttributeCb func, const void* data); +bool simpleXmlParse(const char* buf, unsigned bufLength, bool strip, simpleXMLCb func, const void* data); +bool simpleXmlParseW3CAttribute(const char* buf, unsigned bufLength, simpleXMLAttributeCb func, const void* data); const char* simpleXmlParseCSSAttribute(const char* buf, unsigned bufLength, char** tag, char** name, const char** attrs, unsigned* attrsLength); -const char* simpleXmlFindAttributesTag(const char* buf, unsigned buflen); +const char* simpleXmlFindAttributesTag(const char* buf, unsigned bufLength); bool isIgnoreUnsupportedLogElements(const char* tagName); const char* simpleXmlNodeTypeToString(SvgNodeType type); -- 2.7.4 From 662fb7a5112737396269c793c93813b580c4562c Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Sat, 29 Jan 2022 00:40:09 +0100 Subject: [PATCH 14/16] svg_loader: css style functions moved to a separate file Change-Id: I6ea53062f358e0128a3b8eafba4e6217b7b9666e --- src/loaders/svg/meson.build | 2 + src/loaders/svg/tvgSvgCssStyle.cpp | 189 +++++++++++++++++++++++++++++++++++++ src/loaders/svg/tvgSvgCssStyle.h | 34 +++++++ src/loaders/svg/tvgSvgLoader.cpp | 170 ++------------------------------- 4 files changed, 232 insertions(+), 163 deletions(-) create mode 100644 src/loaders/svg/tvgSvgCssStyle.cpp create mode 100644 src/loaders/svg/tvgSvgCssStyle.h diff --git a/src/loaders/svg/meson.build b/src/loaders/svg/meson.build index d6d8cec..8048dd1 100644 --- a/src/loaders/svg/meson.build +++ b/src/loaders/svg/meson.build @@ -1,10 +1,12 @@ source_file = [ + 'tvgSvgCssStyle.h', 'tvgSvgLoader.h', 'tvgSvgLoaderCommon.h', 'tvgSvgPath.h', 'tvgSvgSceneBuilder.h', 'tvgSvgUtil.h', 'tvgXmlParser.h', + 'tvgSvgCssStyle.cpp', 'tvgSvgLoader.cpp', 'tvgSvgPath.cpp', 'tvgSvgSceneBuilder.cpp', diff --git a/src/loaders/svg/tvgSvgCssStyle.cpp b/src/loaders/svg/tvgSvgCssStyle.cpp new file mode 100644 index 0000000..60edf92 --- /dev/null +++ b/src/loaders/svg/tvgSvgCssStyle.cpp @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "tvgSvgCssStyle.h" + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + +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 = strdup(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->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)SvgFillFlags::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->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)SvgFillFlags::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; + if (from->stroke.paint.url) to->stroke.paint.url = strdup(from->stroke.paint.url); + 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->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::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->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::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->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Dash); + 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->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::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->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::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 +} + + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +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); +} + + +SvgNode* findCssStyleNode(const SvgNode* cssStyle, const char* title, SvgNodeType type) +{ + if (!cssStyle) return nullptr; + + auto child = cssStyle->child.data; + for (uint32_t i = 0; i < cssStyle->child.count; ++i, ++child) { + if ((*child)->type == type) { + if ((!title && !(*child)->id) || (title && (*child)->id && !strcmp((*child)->id, title))) return (*child); + } + } + return nullptr; +} + + +SvgNode* findCssStyleNode(const SvgNode* cssStyle, const char* title) +{ + if (!cssStyle) return nullptr; + + auto child = cssStyle->child.data; + for (uint32_t i = 0; i < cssStyle->child.count; ++i, ++child) { + if ((*child)->type == SvgNodeType::CssStyle) { + if ((title && (*child)->id && !strcmp((*child)->id, title))) return (*child); + } + } + return nullptr; +} + + +void updateCssStyle(SvgNode* doc, SvgNode* cssStyle) +{ + if (doc->child.count > 0) { + auto child = doc->child.data; + for (uint32_t i = 0; i < doc->child.count; ++i, ++child) { + if (auto cssNode = findCssStyleNode(cssStyle, nullptr, (*child)->type)) { + copyCssStyleAttr(*child, cssNode); + } + if (auto cssNode = findCssStyleNode(cssStyle, nullptr)) { + copyCssStyleAttr(*child, cssNode); + } + updateCssStyle(*child, cssStyle); + } + } +} + + +void stylePostponedNodes(Array* nodesToStyle, SvgNode* cssStyle) +{ + for (uint32_t i = 0; i < nodesToStyle->count; ++i) { + auto nodeIdPair = nodesToStyle->data[i]; + + //css styling: tag.name has higher priority than .name + if (auto cssNode = findCssStyleNode(cssStyle, nodeIdPair.id, nodeIdPair.node->type)) { + copyCssStyleAttr(nodeIdPair.node, cssNode); + } + if (auto cssNode = findCssStyleNode(cssStyle, nodeIdPair.id)) { + copyCssStyleAttr(nodeIdPair.node, cssNode); + } + } +} + diff --git a/src/loaders/svg/tvgSvgCssStyle.h b/src/loaders/svg/tvgSvgCssStyle.h new file mode 100644 index 0000000..90fbcbf --- /dev/null +++ b/src/loaders/svg/tvgSvgCssStyle.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All rights reserved. + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _TVG_SVG_CSS_STYLE_H_ +#define _TVG_SVG_CSS_STYLE_H_ + +#include "tvgSvgLoaderCommon.h" + +void copyCssStyleAttr(SvgNode* to, const SvgNode* from); +SvgNode* findCssStyleNode(const SvgNode* cssStyle, const char* title, SvgNodeType type); +SvgNode* findCssStyleNode(const SvgNode* cssStyle, const char* title); +void updateCssStyle(SvgNode* doc, SvgNode* cssStyle); +void stylePostponedNodes(Array* nodesToStyle, SvgNode* cssStyle); + +#endif //_TVG_SVG_CSS_STYLE_H_ diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index e68b776..196de4d 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -60,6 +60,7 @@ #include "tvgSvgLoader.h" #include "tvgSvgSceneBuilder.h" #include "tvgSvgUtil.h" +#include "tvgSvgCssStyle.h" /************************************************************************/ /* Internal Class Implementation */ @@ -949,130 +950,6 @@ static void _handleDisplayAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, } -static SvgNode* _findCssStyleNode(const SvgNode* cssStyle, const char* title, SvgNodeType type) -{ - if (!cssStyle) return nullptr; - - auto child = cssStyle->child.data; - for (uint32_t i = 0; i < cssStyle->child.count; ++i, ++child) { - if ((*child)->type == type) { - if ((!title && !(*child)->id) || (title && (*child)->id && !strcmp((*child)->id, title))) return (*child); - } - } - return nullptr; -} - - -static SvgNode* _findCssStyleNode(const SvgNode* cssStyle, const char* title) -{ - if (!cssStyle) return nullptr; - - auto child = cssStyle->child.data; - for (uint32_t i = 0; i < cssStyle->child.count; ++i, ++child) { - if ((*child)->type == SvgNodeType::CssStyle) { - if ((title && (*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->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)SvgFillFlags::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->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)SvgFillFlags::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->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::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->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::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->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Dash); - 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->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::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->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::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); -} - - static void _handleCssClassAttr(SvgLoaderData* loader, SvgNode* node, const char* value) { auto cssClass = &node->style->cssClass; @@ -1083,13 +960,13 @@ static void _handleCssClassAttr(SvgLoaderData* loader, SvgNode* node, const char bool cssClassFound = false; //css styling: tag.name has higher priority than .name - if (auto cssNode = _findCssStyleNode(loader->cssStyle, *cssClass, node->type)) { + if (auto cssNode = findCssStyleNode(loader->cssStyle, *cssClass, node->type)) { cssClassFound = true; - _copyCssStyleAttr(node, cssNode); + copyCssStyleAttr(node, cssNode); } - if (auto cssNode = _findCssStyleNode(loader->cssStyle, *cssClass)) { + if (auto cssNode = findCssStyleNode(loader->cssStyle, *cssClass)) { cssClassFound = true; - _copyCssStyleAttr(node, cssNode); + copyCssStyleAttr(node, cssNode); } if (!cssClassFound) _postponeCloneNode(&loader->nodesToStyle, node, *cssClass); @@ -3001,39 +2878,6 @@ static void _updateComposite(SvgNode* node, SvgNode* root) } -static void _updateCssStyle(SvgNode* doc, SvgNode* cssStyle) -{ - if (doc->child.count > 0) { - auto child = doc->child.data; - for (uint32_t i = 0; i < doc->child.count; ++i, ++child) { - if (auto cssNode = _findCssStyleNode(cssStyle, nullptr, (*child)->type)) { - _copyCssStyleAttr(*child, cssNode); - } - if (auto cssNode = _findCssStyleNode(cssStyle, nullptr)) { - _copyCssStyleAttr(*child, cssNode); - } - _updateCssStyle(*child, cssStyle); - } - } -} - - -static void _stylePostponedNodes(Array* nodesToStyle, SvgNode* cssStyle) -{ - for (uint32_t i = 0; i < nodesToStyle->count; ++i) { - auto nodeIdPair = nodesToStyle->data[i]; - - //css styling: tag.name has higher priority than .name - if (auto cssNode = _findCssStyleNode(cssStyle, nodeIdPair.id, nodeIdPair.node->type)) { - _copyCssStyleAttr(nodeIdPair.node, cssNode); - } - if (auto cssNode = _findCssStyleNode(cssStyle, nodeIdPair.id)) { - _copyCssStyleAttr(nodeIdPair.node, cssNode); - } - } -} - - static void _freeNodeStyle(SvgStyleProperty* style) { if (!style) return; @@ -3210,8 +3054,8 @@ void SvgLoader::run(unsigned tid) if (loaderData.gradients.count > 0) _updateGradient(loaderData.doc, &loaderData.gradients); if (defs) _updateGradient(loaderData.doc, &defs->node.defs.gradients); - if (loaderData.nodesToStyle.count > 0) _stylePostponedNodes(&loaderData.nodesToStyle, loaderData.cssStyle); - if (loaderData.cssStyle) _updateCssStyle(loaderData.doc, loaderData.cssStyle); + if (loaderData.nodesToStyle.count > 0) stylePostponedNodes(&loaderData.nodesToStyle, loaderData.cssStyle); + if (loaderData.cssStyle) updateCssStyle(loaderData.doc, loaderData.cssStyle); } root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh, w, h, preserveAspect, svgPath); } -- 2.7.4 From 3f2d3b2d57b943b2de2e53e05f0625b3a43bbd15 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Sat, 29 Jan 2022 23:08:53 +0100 Subject: [PATCH 15/16] svg_loader: fixing css style for group selectors, clips/masks The css styling has to be applied as the first step of the node updates. Whereas the updateStyle function should be called as the last step, after all other node updates are made. Change-Id: I59d11f6d372ff4f32c22b067edff90b552900ef8 --- src/loaders/svg/tvgSvgCssStyle.cpp | 3 +-- src/loaders/svg/tvgSvgLoader.cpp | 9 +++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/loaders/svg/tvgSvgCssStyle.cpp b/src/loaders/svg/tvgSvgCssStyle.cpp index 60edf92..67fb39c 100644 --- a/src/loaders/svg/tvgSvgCssStyle.cpp +++ b/src/loaders/svg/tvgSvgCssStyle.cpp @@ -101,7 +101,6 @@ static void _cssStyleCopy(SvgStyleProperty* to, const SvgStyleProperty* from) to->opacity = from->opacity; to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Opacity); } - //TODO: support clip-path, mask, mask-type, display } @@ -121,7 +120,7 @@ void copyCssStyleAttr(SvgNode* to, const SvgNode* from) } //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); } diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 196de4d..0fdd883 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -2688,7 +2688,7 @@ static void _svgLoaderParserXmlCssStyle(SvgLoaderData* loader, const char* conte while (auto next = simpleXmlParseCSSAttribute(content, length, &tag, &name, &attrs, &attrsLength)) { if ((method = _findGroupFactory(tag))) { - TVGLOG("SVG", "Unsupported elements used in the internal CSS style sheets [Elements: %s]", tag); + if ((node = method(loader, loader->cssStyle, attrs, attrsLength, simpleXmlParseW3CAttribute))) node->id = _copyId(name); } else if ((method = _findGraphicsFactory(tag))) { if ((node = method(loader, loader->cssStyle, attrs, attrsLength, simpleXmlParseW3CAttribute))) node->id = _copyId(name); } else if ((gradientMethod = _findGradientFactory(tag))) { @@ -3043,9 +3043,11 @@ void SvgLoader::run(unsigned tid) if (!simpleXmlParse(content, size, true, _svgLoaderParser, &(loaderData))) return; if (loaderData.doc) { - _updateStyle(loaderData.doc, nullptr); auto defs = loaderData.doc->node.doc.defs; + if (loaderData.nodesToStyle.count > 0) stylePostponedNodes(&loaderData.nodesToStyle, loaderData.cssStyle); + if (loaderData.cssStyle) updateCssStyle(loaderData.doc, loaderData.cssStyle); + _updateComposite(loaderData.doc, loaderData.doc); if (defs) _updateComposite(loaderData.doc, defs); @@ -3054,8 +3056,7 @@ void SvgLoader::run(unsigned tid) if (loaderData.gradients.count > 0) _updateGradient(loaderData.doc, &loaderData.gradients); if (defs) _updateGradient(loaderData.doc, &defs->node.defs.gradients); - if (loaderData.nodesToStyle.count > 0) stylePostponedNodes(&loaderData.nodesToStyle, loaderData.cssStyle); - if (loaderData.cssStyle) updateCssStyle(loaderData.doc, loaderData.cssStyle); + _updateStyle(loaderData.doc, nullptr); } root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh, w, h, preserveAspect, svgPath); } -- 2.7.4 From fc155c8096b701a26c031a9ba52d8a4d46da7f8c Mon Sep 17 00:00:00 2001 From: jykeon Date: Mon, 27 Feb 2023 19:12:37 +0900 Subject: [PATCH 16/16] Bump up 0.7.8 Change-Id: Ib9bd24a04afecb67ba77e560119dddc7b0b2219a Signed-off-by: jykeon --- packaging/thorvg.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/thorvg.spec b/packaging/thorvg.spec index 1519072..2f5fab7 100644 --- a/packaging/thorvg.spec +++ b/packaging/thorvg.spec @@ -1,6 +1,6 @@ Name: thorvg Summary: Thor Vector Graphics Library -Version: 0.7.7 +Version: 0.7.8 Release: 1 Group: Graphics System/Rendering Engine License: MIT -- 2.7.4