From 84d1bacc48b1e09f168654260339bb3941279daa Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Tue, 25 Jan 2022 12:20:22 +0100 Subject: [PATCH 01/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 02/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 03/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 04/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 05/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 06/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 From 742aac04b5d27ada809aed95768f9005cf22bcac Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Tue, 25 Jan 2022 12:38:49 +0100 Subject: [PATCH 07/16] example: cssStyle.svg add For now it's a simple file used to check the styling precedence and overall correctness. After the css stylling will be fully introduced a more complex file can be used, Change-Id: I10fe2b7d4c201e29893ab6a919d132a2e2a47e24 --- src/examples/images/cssStyle.svg | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/examples/images/cssStyle.svg diff --git a/src/examples/images/cssStyle.svg b/src/examples/images/cssStyle.svg new file mode 100644 index 0000000..53d071f --- /dev/null +++ b/src/examples/images/cssStyle.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + -- 2.7.4 From 68f20ef932abde4a057d7634dc030de177cebd4e Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Tue, 8 Feb 2022 18:02:20 +0900 Subject: [PATCH 08/16] example: just renamed the svg file. Change-Id: Ib880280971d5d2d93cab3b59938e94e230b7a4f5 --- src/examples/images/{cssStyle.svg => css-style.svg} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/examples/images/{cssStyle.svg => css-style.svg} (100%) diff --git a/src/examples/images/cssStyle.svg b/src/examples/images/css-style.svg similarity index 100% rename from src/examples/images/cssStyle.svg rename to src/examples/images/css-style.svg -- 2.7.4 From f168e9636a0cc6e345f521a44e57eb4177d7cac2 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Tue, 8 Feb 2022 18:43:03 +0900 Subject: [PATCH 09/16] svg_loader: code refactoring. follow strict coding-style. no logic changes. Change-Id: Iab335dbafe4a32f321244f7df8068176f069d2c9 --- src/loaders/svg/tvgSvgCssStyle.cpp | 54 ++++++++++++++++++-------------------- src/loaders/svg/tvgSvgCssStyle.h | 10 +++---- src/loaders/svg/tvgSvgLoader.cpp | 31 ++++++++++------------ 3 files changed, 45 insertions(+), 50 deletions(-) diff --git a/src/loaders/svg/tvgSvgCssStyle.cpp b/src/loaders/svg/tvgSvgCssStyle.cpp index 67fb39c..e537cfa 100644 --- a/src/loaders/svg/tvgSvgCssStyle.cpp +++ b/src/loaders/svg/tvgSvgCssStyle.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved. + * 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 @@ -20,14 +20,13 @@ * SOFTWARE. */ -#include #include "tvgSvgCssStyle.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ -static void _cssStyleCopy(SvgStyleProperty* to, const SvgStyleProperty* from) +static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from) { if (from == nullptr) return; //Copy the properties of 'from' only if they were explicitly set (not the default ones). @@ -108,7 +107,7 @@ static void _cssStyleCopy(SvgStyleProperty* to, const SvgStyleProperty* from) /* External Class Implementation */ /************************************************************************/ -void copyCssStyleAttr(SvgNode* to, const SvgNode* from) +void cssCopyStyleAttr(SvgNode* to, const SvgNode* from) { //Copy matrix attribute if (from->transform && !((int)to->style->flags & (int)SvgStyleFlags::Transform)) { @@ -119,19 +118,19 @@ void copyCssStyleAttr(SvgNode* to, const SvgNode* from) } } //Copy style attribute - _cssStyleCopy(to->style, from->style); + _copyStyle(to->style, from->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) +SvgNode* cssFindStyleNode(const SvgNode* style, const char* title, SvgNodeType type) { - if (!cssStyle) return nullptr; + if (!style) return nullptr; - auto child = cssStyle->child.data; - for (uint32_t i = 0; i < cssStyle->child.count; ++i, ++child) { + auto child = style->child.data; + for (uint32_t i = 0; i < style->child.count; ++i, ++child) { if ((*child)->type == type) { if ((!title && !(*child)->id) || (title && (*child)->id && !strcmp((*child)->id, title))) return (*child); } @@ -140,12 +139,12 @@ SvgNode* findCssStyleNode(const SvgNode* cssStyle, const char* title, SvgNodeTyp } -SvgNode* findCssStyleNode(const SvgNode* cssStyle, const char* title) +SvgNode* cssFindStyleNode(const SvgNode* style, const char* title) { - if (!cssStyle) return nullptr; + if (!style) return nullptr; - auto child = cssStyle->child.data; - for (uint32_t i = 0; i < cssStyle->child.count; ++i, ++child) { + auto child = style->child.data; + for (uint32_t i = 0; i < style->child.count; ++i, ++child) { if ((*child)->type == SvgNodeType::CssStyle) { if ((title && (*child)->id && !strcmp((*child)->id, title))) return (*child); } @@ -154,35 +153,34 @@ SvgNode* findCssStyleNode(const SvgNode* cssStyle, const char* title) } -void updateCssStyle(SvgNode* doc, SvgNode* cssStyle) +void cssUpdateStyle(SvgNode* doc, SvgNode* style) { 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 = cssFindStyleNode(style, nullptr, (*child)->type)) { + cssCopyStyleAttr(*child, cssNode); } - if (auto cssNode = findCssStyleNode(cssStyle, nullptr)) { - copyCssStyleAttr(*child, cssNode); + if (auto cssNode = cssFindStyleNode(style, nullptr)) { + cssCopyStyleAttr(*child, cssNode); } - updateCssStyle(*child, cssStyle); + cssUpdateStyle(*child, style); } } } -void stylePostponedNodes(Array* nodesToStyle, SvgNode* cssStyle) +void cssApplyStyleToPostponeds(Array& postponeds, SvgNode* style) { - for (uint32_t i = 0; i < nodesToStyle->count; ++i) { - auto nodeIdPair = nodesToStyle->data[i]; + for (uint32_t i = 0; i < postponeds.count; ++i) { + auto nodeIdPair = postponeds.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 = cssFindStyleNode(style, nodeIdPair.id, nodeIdPair.node->type)) { + cssCopyStyleAttr(nodeIdPair.node, cssNode); } - if (auto cssNode = findCssStyleNode(cssStyle, nodeIdPair.id)) { - copyCssStyleAttr(nodeIdPair.node, cssNode); + if (auto cssNode = cssFindStyleNode(style, nodeIdPair.id)) { + cssCopyStyleAttr(nodeIdPair.node, cssNode); } } -} - +} \ No newline at end of file diff --git a/src/loaders/svg/tvgSvgCssStyle.h b/src/loaders/svg/tvgSvgCssStyle.h index 90fbcbf..66477c1 100644 --- a/src/loaders/svg/tvgSvgCssStyle.h +++ b/src/loaders/svg/tvgSvgCssStyle.h @@ -25,10 +25,10 @@ #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); +void cssCopyStyleAttr(SvgNode* to, const SvgNode* from); +SvgNode* cssFindStyleNode(const SvgNode* style, const char* title, SvgNodeType type); +SvgNode* cssFindStyleNode(const SvgNode* style, const char* title); +void cssUpdateStyle(SvgNode* doc, SvgNode* style); +void cssApplyStyleToPostponeds(Array& postponeds, SvgNode* style); #endif //_TVG_SVG_CSS_STYLE_H_ diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 0fdd883..39ee166 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -80,9 +80,6 @@ typedef bool (*parseAttributes)(const char* buf, unsigned bufLength, simpleXMLAt 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)) { @@ -737,6 +734,12 @@ error: } +static void _postpone(Array& nodes, SvgNode *node, char* id) +{ + nodes.push({node, id}); +} + + /* // TODO - remove? static constexpr struct @@ -960,16 +963,16 @@ 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 = cssFindStyleNode(loader->cssStyle, *cssClass, node->type)) { cssClassFound = true; - copyCssStyleAttr(node, cssNode); + cssCopyStyleAttr(node, cssNode); } - if (auto cssNode = findCssStyleNode(loader->cssStyle, *cssClass)) { + if (auto cssNode = cssFindStyleNode(loader->cssStyle, *cssClass)) { cssClassFound = true; - copyCssStyleAttr(node, cssNode); + cssCopyStyleAttr(node, cssNode); } - if (!cssClassFound) _postponeCloneNode(&loader->nodesToStyle, node, *cssClass); + if (!cssClassFound) _postpone(loader->nodesToStyle, node, *cssClass); } @@ -2031,12 +2034,6 @@ static void _cloneNode(SvgNode* from, SvgNode* parent, int depth) } -static void _postponeCloneNode(Array* nodes, SvgNode *node, char* id) -{ - nodes->push({node, id}); -} - - static void _clonePostponedNodes(Array* cloneNodes, SvgNode* doc) { for (uint32_t i = 0; i < cloneNodes->count; ++i) { @@ -2091,7 +2088,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->cloneNodes, node, id); + _postpone(loader->cloneNodes, node, id); } } else { return _attrParseGNode(data, key, value); @@ -3045,8 +3042,8 @@ void SvgLoader::run(unsigned tid) if (loaderData.doc) { 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); + if (loaderData.nodesToStyle.count > 0) cssApplyStyleToPostponeds(loaderData.nodesToStyle, loaderData.cssStyle); + if (loaderData.cssStyle) cssUpdateStyle(loaderData.doc, loaderData.cssStyle); _updateComposite(loaderData.doc, loaderData.doc); if (defs) _updateComposite(loaderData.doc, defs); -- 2.7.4 From 9143d88ec76ce479cabdccc15e8db1d8b28e0e5c Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Tue, 1 Feb 2022 23:34:41 +0100 Subject: [PATCH 10/16] svg_loader: fixing the used mask-type For the performance reasons, regardless of the set/default mask-type value, if the mask is white, the alpha masking is used. To qualify a mask as white, not only its fill has to be checked, but its stroke as well. The second was missing. Change-Id: I407ec17f1b8dfd8fda5948319cf11bd609e7ab91 --- src/loaders/svg/tvgSvgSceneBuilder.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/loaders/svg/tvgSvgSceneBuilder.cpp b/src/loaders/svg/tvgSvgSceneBuilder.cpp index e9b8f42..9abcc6a 100644 --- a/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -594,7 +594,10 @@ static unique_ptr _sceneBuildHelper(const SvgNode* node, const Box& vBox, if (isMaskWhite) { uint8_t r, g, b; shape->fillColor(&r, &g, &b, nullptr); - if (shape->fill() || r < 255 || g < 255 || b < 255) *isMaskWhite = false; + if (shape->fill() || r < 255 || g < 255 || b < 255 || shape->strokeFill() || + (shape->strokeColor(&r, &g, &b, nullptr) == Result::Success && (r < 255 || g < 255 || b < 255))) { + *isMaskWhite = false; + } } scene->push(move(shape)); } -- 2.7.4 From d3c640ded06cb48dc01bec5dd3302617a29946be Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Fri, 4 Feb 2022 18:59:00 +0100 Subject: [PATCH 11/16] svg_loader: the 'use' node properly transformed The translation of the use node shouldn't overwrite its transformation. Change-Id: Ibc6761404bc98bd5a82ac1c2d76bf664a98e5d91 --- src/loaders/svg/tvgSvgSceneBuilder.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/loaders/svg/tvgSvgSceneBuilder.cpp b/src/loaders/svg/tvgSvgSceneBuilder.cpp index 9abcc6a..ed9576b 100644 --- a/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -540,7 +540,7 @@ static unique_ptr _imageBuildHelper(SvgNode* node, const Box& vBox, con string imagePath = href; if (strncmp(href, "/", 1)) { auto last = svgPath.find_last_of("/"); - imagePath = svgPath.substr(0, (last == string::npos ? 0 : last + 1 )) + imagePath; + imagePath = svgPath.substr(0, (last == string::npos ? 0 : last + 1)) + imagePath; } if (picture->load(imagePath) != Result::Success) return nullptr; } @@ -561,12 +561,17 @@ static unique_ptr _imageBuildHelper(SvgNode* node, const Box& vBox, con static unique_ptr _useBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool* isMaskWhite) { auto scene = _sceneBuildHelper(node, vBox, svgPath, false, isMaskWhite); + if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) { - scene->translate(node->node.use.x, node->node.use.y); + Matrix m = {1, 0, node->node.use.x, 0, 1, node->node.use.y, 0, 0, 1}; + Matrix transform = scene->transform(); + m = mathMultiply(&transform, &m); + scene->transform(m); } if (node->node.use.w > 0.0f && node->node.use.h > 0.0f) { //TODO: handle width/height properties } + return scene; } -- 2.7.4 From 8917e3cfdafceefc76ec76112e2597ffbaea0623 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Mon, 7 Feb 2022 02:39:48 +0100 Subject: [PATCH 12/16] svg_loader: proper image transformation One of the image's attributes can be a transformation matrix. Now it's applied. Change-Id: I7056aa0a5deff519bf60e5fd99b4786c2a6cb8bf --- src/loaders/svg/tvgSvgLoader.cpp | 2 ++ src/loaders/svg/tvgSvgSceneBuilder.cpp | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 39ee166..adc8de8 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -1715,6 +1715,8 @@ static bool _attrParseImageNode(void* data, const char* key, const char* value) _handleClipPathAttr(loader, node, value); } else if (!strcmp(key, "mask")) { _handleMaskAttr(loader, node, value); + } else if (!strcmp(key, "transform")) { + node->transform = _parseTransformationMatrix(value); } else { return _parseStyleAttr(loader, key, value); } diff --git a/src/loaders/svg/tvgSvgSceneBuilder.cpp b/src/loaders/svg/tvgSvgSceneBuilder.cpp index ed9576b..737d99b 100644 --- a/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -546,12 +546,14 @@ static unique_ptr _imageBuildHelper(SvgNode* node, const Box& vBox, con } float w, h; + Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1}; if (picture->size(&w, &h) == Result::Success && w > 0 && h > 0) { auto sx = node->node.image.w / w; auto sy = node->node.image.h / h; - Matrix m = {sx, 0, node->node.image.x, 0, sy, node->node.image.y, 0, 0, 1}; - picture->transform(m); + m = {sx, 0, node->node.image.x, 0, sy, node->node.image.y, 0, 0, 1}; } + if (node->transform) m = mathMultiply(node->transform, &m); + picture->transform(m); _applyComposition(picture.get(), node, vBox, svgPath); return picture; -- 2.7.4 From cb813767473f619f779b9307fa235a5452cc6802 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Wed, 9 Feb 2022 19:39:08 +0100 Subject: [PATCH 13/16] svg_loader: preventing invalid log msg Change-Id: I3f93189028debb1c9449bc085d2849c3156c035d --- src/loaders/svg/tvgSvgLoader.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index adc8de8..f597942 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -2295,8 +2295,8 @@ static bool _attrParseRadialGradientNode(void* data, const char* key, const char } else if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) { if (grad->ref && value) free(grad->ref); grad->ref = _idFromHref(value); - } else if (!strcmp(key, "gradientUnits") && !strcmp(value, "userSpaceOnUse")) { - grad->userSpace = true; + } else if (!strcmp(key, "gradientUnits")) { + if (!strcmp(value, "userSpaceOnUse")) grad->userSpace = true; } else if (!strcmp(key, "gradientTransform")) { grad->transform = _parseTransformationMatrix(value); } else { @@ -2485,8 +2485,8 @@ static bool _attrParseLinearGradientNode(void* data, const char* key, const char } else if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) { if (grad->ref && value) free(grad->ref); grad->ref = _idFromHref(value); - } else if (!strcmp(key, "gradientUnits") && !strcmp(value, "userSpaceOnUse")) { - grad->userSpace = true; + } else if (!strcmp(key, "gradientUnits")) { + if (!strcmp(value, "userSpaceOnUse")) grad->userSpace = true; } else if (!strcmp(key, "gradientTransform")) { grad->transform = _parseTransformationMatrix(value); } else { -- 2.7.4 From 41106dd0a3ffb4878452b402f1998e59f4f1af27 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Wed, 9 Feb 2022 17:50:57 -0800 Subject: [PATCH 14/16] infra: Disable unit test in window CI Symbol file generation for compiling test code in CI fails. It's not caused by recent patches. We don't test until we know the exact cause. However, loader test is added to check the safety of window build. Error Log [45/68] Generating symbol file src/thorvg-0.dll.p/thorvg-0.dll.symbols FAILED: src/thorvg-0.dll.p/thorvg-0.dll.symbols "C:\hostedtoolcache\windows\Python\3.7.9\x64\Scripts\meson" "--internal" "symbolextractor" "D:\a\thorvg\thorvg\build" src/thorvg-0.dll "src\thorvg.lib" src/thorvg-0.dll.p/thorvg-0.dll.symbols Change-Id: I03401e691d7a21b59ab2f1a2e40ddbdf4062be08 --- .github/workflows/build_win.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_win.yml b/.github/workflows/build_win.yml index f1b38b0..be9f23c 100644 --- a/.github/workflows/build_win.yml +++ b/.github/workflows/build_win.yml @@ -28,7 +28,7 @@ jobs: name: result path: build/src\thorvg* - unit_test: + build_loaders: runs-on: windows-latest steps: - uses: actions/checkout@v2 @@ -39,7 +39,7 @@ jobs: - name: Build run: | - meson --backend=ninja build -Dloaders="svg, tvg, png, jpg" -Dsavers="tvg" -Dbindings="capi" -Dtests=true + meson --backend=ninja build -Dloaders="svg, tvg, png, jpg" -Dsavers="tvg" -Dbindings="capi" where link - ninja -C build test + ninja -C build install -- 2.7.4 From 176f8f4261400c76c110c10c4384b18e4d6075f5 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Wed, 9 Feb 2022 19:25:00 +0100 Subject: [PATCH 15/16] svg_loader: fixing memory leak The css style node was improperly freed. Change-Id: I362ea9d30d974a65ddc45e167ef755b260722d56 --- src/loaders/svg/tvgSvgLoader.cpp | 2 ++ src/loaders/svg/tvgSvgLoaderCommon.h | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index f597942..05d67f7 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -2632,6 +2632,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, node = method(loader, parent, attrs, attrsLength, simpleXmlParseAttributes); if (!strcmp(tagName, "style")) { loader->cssStyle = node; + loader->doc->node.doc.style = node; loader->style = true; } } @@ -2929,6 +2930,7 @@ static void _freeNode(SvgNode* node) } case SvgNodeType::Doc: { _freeNode(node->node.doc.defs); + _freeNode(node->node.doc.style); break; } case SvgNodeType::Defs: { diff --git a/src/loaders/svg/tvgSvgLoaderCommon.h b/src/loaders/svg/tvgSvgLoaderCommon.h index 364ec15..d9e9828 100644 --- a/src/loaders/svg/tvgSvgLoaderCommon.h +++ b/src/loaders/svg/tvgSvgLoaderCommon.h @@ -153,6 +153,7 @@ struct SvgDocNode float vw; float vh; SvgNode* defs; + SvgNode* style; bool preserveAspect; }; @@ -405,7 +406,7 @@ struct SvgNodeIdPair struct SvgLoaderData { - Array stack = {nullptr, 0, 0}; + Array stack = {nullptr, 0, 0}; SvgNode* doc = nullptr; SvgNode* def = nullptr; SvgNode* cssStyle = nullptr; -- 2.7.4 From 5705cbfc44594c63e0d8b6de874c5db88b89d612 Mon Sep 17 00:00:00 2001 From: jykeon Date: Thu, 2 Mar 2023 10:31:00 +0900 Subject: [PATCH 16/16] Bump up 0.7.9 Change-Id: Iaacfb1959ac48a334562dea752c14af8c530e434 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 2f5fab7..b7226fb 100644 --- a/packaging/thorvg.spec +++ b/packaging/thorvg.spec @@ -1,6 +1,6 @@ Name: thorvg Summary: Thor Vector Graphics Library -Version: 0.7.8 +Version: 0.7.9 Release: 1 Group: Graphics System/Rendering Engine License: MIT -- 2.7.4