summaryrefslogtreecommitdiff
path: root/thirdparty/thorvg/src/loaders/svg
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/thorvg/src/loaders/svg')
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp186
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h34
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp359
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h2
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h49
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp2
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h2
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp134
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h2
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp2
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h2
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp61
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h15
13 files changed, 721 insertions, 129 deletions
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp
new file mode 100644
index 0000000000..e537cfa27f
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp
@@ -0,0 +1,186 @@
+/*
+ * 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.
+ */
+
+#include "tvgSvgCssStyle.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+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).
+ 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);
+ }
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+void cssCopyStyleAttr(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
+ _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* cssFindStyleNode(const SvgNode* style, const char* title, SvgNodeType type)
+{
+ if (!style) return nullptr;
+
+ 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);
+ }
+ }
+ return nullptr;
+}
+
+
+SvgNode* cssFindStyleNode(const SvgNode* style, const char* title)
+{
+ if (!style) return nullptr;
+
+ 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);
+ }
+ }
+ return nullptr;
+}
+
+
+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 = cssFindStyleNode(style, nullptr, (*child)->type)) {
+ cssCopyStyleAttr(*child, cssNode);
+ }
+ if (auto cssNode = cssFindStyleNode(style, nullptr)) {
+ cssCopyStyleAttr(*child, cssNode);
+ }
+ cssUpdateStyle(*child, style);
+ }
+ }
+}
+
+
+void cssApplyStyleToPostponeds(Array<SvgNodeIdPair>& postponeds, SvgNode* style)
+{
+ 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 = cssFindStyleNode(style, nodeIdPair.id, nodeIdPair.node->type)) {
+ cssCopyStyleAttr(nodeIdPair.node, cssNode);
+ }
+ if (auto cssNode = cssFindStyleNode(style, nodeIdPair.id)) {
+ cssCopyStyleAttr(nodeIdPair.node, cssNode);
+ }
+ }
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h
new file mode 100644
index 0000000000..66477c1a32
--- /dev/null
+++ b/thirdparty/thorvg/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 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<SvgNodeIdPair>& postponeds, SvgNode* style);
+
+#endif //_TVG_SVG_CSS_STYLE_H_
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
index 08b3308165..a842b7fa8b 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+ * 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
@@ -60,6 +60,8 @@
#include "tvgSvgLoader.h"
#include "tvgSvgSceneBuilder.h"
#include "tvgSvgUtil.h"
+#include "tvgSvgCssStyle.h"
+#include "tvgMath.h"
/************************************************************************/
/* Internal Class Implementation */
@@ -75,11 +77,10 @@
#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 SvgNode* (*FactoryMethod)(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength);
+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 char* _skipSpace(const char* str, const char* end)
{
while (((end && str < end) || (!end && *str != '\0')) && isspace(*str)) {
@@ -200,6 +201,14 @@ static int _toOpacity(const char* str)
}
+static SvgMaskType _toMaskType(const char* str)
+{
+ if (!strcmp(str, "Alpha")) return SvgMaskType::Alpha;
+
+ return SvgMaskType::Luminance;
+}
+
+
#define _PARSE_TAG(Type, Name, Name1, Tags_Array, Default) \
static Type _to##Name1(const char* str) \
{ \
@@ -726,6 +735,12 @@ error:
}
+static void _postpone(Array<SvgNodeIdPair>& nodes, SvgNode *node, char* id)
+{
+ nodes.push({node, id});
+}
+
+
/*
// TODO - remove?
static constexpr struct
@@ -786,14 +801,12 @@ 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, _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) {
+ } else if ((!strcmp(key, "x") || !strcmp(key, "y")) && fabsf(svgUtilStrtof(value, nullptr)) > FLT_EPSILON) {
TVGLOG("SVG", "Unsupported attributes used [Elements type: Svg][Attribute: %s][Value: %s]", key, value);
- }
#endif
- else {
+ } else {
return _parseStyleAttr(loader, key, value, false);
}
return true;
@@ -922,6 +935,12 @@ static void _handleMaskAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, con
}
+static void _handleMaskTypeAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ node->node.mask.type = _toMaskType(value);
+}
+
+
static void _handleDisplayAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
{
//TODO : The display attribute can have various values as well as "none".
@@ -933,6 +952,29 @@ static void _handleDisplayAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node,
}
+static void _handleCssClassAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ auto cssClass = &node->style->cssClass;
+
+ if (*cssClass && value) free(*cssClass);
+ *cssClass = _copyId(value);
+
+ bool cssClassFound = false;
+
+ //css styling: tag.name has higher priority than .name
+ if (auto cssNode = cssFindStyleNode(loader->cssStyle, *cssClass, node->type)) {
+ cssClassFound = true;
+ cssCopyStyleAttr(node, cssNode);
+ }
+ if (auto cssNode = cssFindStyleNode(loader->cssStyle, *cssClass)) {
+ cssClassFound = true;
+ cssCopyStyleAttr(node, cssNode);
+ }
+
+ if (!cssClassFound) _postpone(loader->nodesToStyle, node, *cssClass);
+}
+
+
typedef void (*styleMethod)(SvgLoaderData* loader, SvgNode* node, const char* value);
#define STYLE_DEF(Name, Name1, Flag) { #Name, sizeof(#Name), _handle##Name1##Attr, Flag }
@@ -959,6 +1001,7 @@ static constexpr struct
STYLE_DEF(transform, Transform, SvgStyleFlags::Transform),
STYLE_DEF(clip-path, ClipPath, SvgStyleFlags::ClipPath),
STYLE_DEF(mask, Mask, SvgStyleFlags::Mask),
+ STYLE_DEF(mask-type, MaskType, SvgStyleFlags::MaskType),
STYLE_DEF(display, Display, SvgStyleFlags::Display)
};
@@ -1006,12 +1049,14 @@ static bool _attrParseGNode(void* data, const char* key, const char* value)
SvgNode* node = loader->svgParse->node;
if (!strcmp(key, "style")) {
- return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
+ return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
} else if (!strcmp(key, "transform")) {
node->transform = _parseTransformationMatrix(value);
} else if (!strcmp(key, "id")) {
if (node->id && value) free(node->id);
node->id = _copyId(value);
+ } else if (!strcmp(key, "class")) {
+ _handleCssClassAttr(loader, node, value);
} else if (!strcmp(key, "clip-path")) {
_handleClipPathAttr(loader, node, value);
} else if (!strcmp(key, "mask")) {
@@ -1030,17 +1075,19 @@ static bool _attrParseClipPathNode(void* data, const char* key, const char* valu
{
SvgLoaderData* loader = (SvgLoaderData*)data;
SvgNode* node = loader->svgParse->node;
- SvgCompositeNode* comp = &(node->node.comp);
+ SvgClipNode* clip = &(node->node.clip);
if (!strcmp(key, "style")) {
- return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
+ return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
} else if (!strcmp(key, "transform")) {
node->transform = _parseTransformationMatrix(value);
} else if (!strcmp(key, "id")) {
if (node->id && value) free(node->id);
node->id = _copyId(value);
+ } else if (!strcmp(key, "class")) {
+ _handleCssClassAttr(loader, node, value);
} else if (!strcmp(key, "clipPathUnits")) {
- if (!strcmp(value, "objectBoundingBox")) comp->userSpace = false;
+ if (!strcmp(value, "objectBoundingBox")) clip->userSpace = false;
} else {
return _parseStyleAttr(loader, key, value, false);
}
@@ -1052,17 +1099,36 @@ static bool _attrParseMaskNode(void* data, const char* key, const char* value)
{
SvgLoaderData* loader = (SvgLoaderData*)data;
SvgNode* node = loader->svgParse->node;
- SvgCompositeNode* comp = &(node->node.comp);
+ SvgMaskNode* mask = &(node->node.mask);
if (!strcmp(key, "style")) {
- return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
+ return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
} else if (!strcmp(key, "transform")) {
node->transform = _parseTransformationMatrix(value);
} else if (!strcmp(key, "id")) {
if (node->id && value) free(node->id);
node->id = _copyId(value);
+ } else if (!strcmp(key, "class")) {
+ _handleCssClassAttr(loader, node, value);
} else if (!strcmp(key, "maskContentUnits")) {
- if (!strcmp(value, "objectBoundingBox")) comp->userSpace = false;
+ if (!strcmp(value, "objectBoundingBox")) mask->userSpace = false;
+ } else if (!strcmp(key, "mask-type")) {
+ mask->type = _toMaskType(value);
+ } else {
+ return _parseStyleAttr(loader, key, value, false);
+ }
+ return true;
+}
+
+
+static bool _attrParseCssStyleNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode* node = loader->svgParse->node;
+
+ if (!strcmp(key, "id")) {
+ if (node->id && value) free(node->id);
+ node->id = _copyId(value);
} else {
return _parseStyleAttr(loader, key, value, false);
}
@@ -1070,6 +1136,31 @@ static bool _attrParseMaskNode(void* data, const char* key, const char* value)
}
+static bool _attrParseSymbolNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode* node = loader->svgParse->node;
+ SvgSymbolNode* symbol = &(node->node.symbol);
+
+ if (!strcmp(key, "viewBox")) {
+ if (!_parseNumber(&value, &symbol->vx) || !_parseNumber(&value, &symbol->vy)) return false;
+ if (!_parseNumber(&value, &symbol->vw) || !_parseNumber(&value, &symbol->vh)) return false;
+ } else if (!strcmp(key, "width")) {
+ symbol->w = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
+ } else if (!strcmp(key, "height")) {
+ symbol->h = _toFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
+ } else if (!strcmp(key, "preserveAspectRatio")) {
+ if (!strcmp(value, "none")) symbol->preserveAspect = false;
+ } else if (!strcmp(key, "overflow")) {
+ if (!strcmp(value, "visible")) symbol->overflowVisible = true;
+ } else {
+ return _attrParseGNode(data, key, value);
+ }
+
+ return true;
+}
+
+
static SvgNode* _createNode(SvgNode* parent, SvgNodeType type)
{
SvgNode* node = (SvgNode*)calloc(1, sizeof(SvgNode));
@@ -1121,7 +1212,7 @@ static SvgNode* _createNode(SvgNode* parent, SvgNodeType type)
}
-static SvgNode* _createDefsNode(TVG_UNUSED SvgLoaderData* loader, TVG_UNUSED SvgNode* parent, const char* buf, unsigned bufLength)
+static SvgNode* _createDefsNode(TVG_UNUSED SvgLoaderData* loader, TVG_UNUSED SvgNode* parent, const char* buf, unsigned bufLength, TVG_UNUSED parseAttributes func)
{
if (loader->def && loader->doc->node.doc.defs) return loader->def;
SvgNode* node = _createNode(nullptr, SvgNodeType::Defs);
@@ -1132,17 +1223,17 @@ static SvgNode* _createDefsNode(TVG_UNUSED SvgLoaderData* loader, TVG_UNUSED Svg
}
-static SvgNode* _createGNode(TVG_UNUSED SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
+static SvgNode* _createGNode(TVG_UNUSED SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
{
loader->svgParse->node = _createNode(parent, SvgNodeType::G);
if (!loader->svgParse->node) return nullptr;
- simpleXmlParseAttributes(buf, bufLength, _attrParseGNode, loader);
+ func(buf, bufLength, _attrParseGNode, loader);
return loader->svgParse->node;
}
-static SvgNode* _createSvgNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
+static SvgNode* _createSvgNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
{
loader->svgParse->node = _createNode(parent, SvgNodeType::Doc);
if (!loader->svgParse->node) return nullptr;
@@ -1152,7 +1243,7 @@ static SvgNode* _createSvgNode(SvgLoaderData* loader, SvgNode* parent, const cha
loader->svgParse->global.h = 0;
doc->preserveAspect = true;
- simpleXmlParseAttributes(buf, bufLength, _attrParseSvgNode, loader);
+ func(buf, bufLength, _attrParseSvgNode, loader);
if (loader->svgParse->global.w == 0) {
if (doc->w < FLT_EPSILON) loader->svgParse->global.w = 1;
@@ -1167,32 +1258,60 @@ static SvgNode* _createSvgNode(SvgLoaderData* loader, SvgNode* parent, const cha
}
-static SvgNode* _createMaskNode(SvgLoaderData* loader, SvgNode* parent, TVG_UNUSED const char* buf, TVG_UNUSED unsigned bufLength)
+static SvgNode* _createMaskNode(SvgLoaderData* loader, SvgNode* parent, TVG_UNUSED const char* buf, TVG_UNUSED unsigned bufLength, parseAttributes func)
{
loader->svgParse->node = _createNode(parent, SvgNodeType::Mask);
if (!loader->svgParse->node) return nullptr;
- loader->svgParse->node->node.comp.userSpace = true;
+ loader->svgParse->node->node.mask.userSpace = true;
+ loader->svgParse->node->node.mask.type = SvgMaskType::Luminance;
- simpleXmlParseAttributes(buf, bufLength, _attrParseMaskNode, loader);
+ func(buf, bufLength, _attrParseMaskNode, loader);
return loader->svgParse->node;
}
-static SvgNode* _createClipPathNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
+static SvgNode* _createClipPathNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
{
loader->svgParse->node = _createNode(parent, SvgNodeType::ClipPath);
if (!loader->svgParse->node) return nullptr;
loader->svgParse->node->display = false;
- loader->svgParse->node->node.comp.userSpace = true;
+ loader->svgParse->node->node.clip.userSpace = true;
- simpleXmlParseAttributes(buf, bufLength, _attrParseClipPathNode, loader);
+ func(buf, bufLength, _attrParseClipPathNode, loader);
return loader->svgParse->node;
}
+
+static SvgNode* _createCssStyleNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::CssStyle);
+ if (!loader->svgParse->node) return nullptr;
+
+ func(buf, bufLength, _attrParseCssStyleNode, loader);
+
+ return loader->svgParse->node;
+}
+
+
+static SvgNode* _createSymbolNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::Symbol);
+ if (!loader->svgParse->node) return nullptr;
+
+ loader->svgParse->node->display = false;
+ loader->svgParse->node->node.symbol.preserveAspect = true;
+ loader->svgParse->node->node.symbol.overflowVisible = false;
+
+ func(buf, bufLength, _attrParseSymbolNode, loader);
+
+ return loader->svgParse->node;
+}
+
+
static bool _attrParsePathNode(void* data, const char* key, const char* value)
{
SvgLoaderData* loader = (SvgLoaderData*)data;
@@ -1203,7 +1322,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, _parseStyleAttr, loader);
+ return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
} else if (!strcmp(key, "clip-path")) {
_handleClipPathAttr(loader, node, value);
} else if (!strcmp(key, "mask")) {
@@ -1211,6 +1330,8 @@ static bool _attrParsePathNode(void* data, const char* key, const char* value)
} else if (!strcmp(key, "id")) {
if (node->id && value) free(node->id);
node->id = _copyId(value);
+ } else if (!strcmp(key, "class")) {
+ _handleCssClassAttr(loader, node, value);
} else {
return _parseStyleAttr(loader, key, value, false);
}
@@ -1218,13 +1339,13 @@ static bool _attrParsePathNode(void* data, const char* key, const char* value)
}
-static SvgNode* _createPathNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
+static SvgNode* _createPathNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
{
loader->svgParse->node = _createNode(parent, SvgNodeType::Path);
if (!loader->svgParse->node) return nullptr;
- simpleXmlParseAttributes(buf, bufLength, _attrParsePathNode, loader);
+ func(buf, bufLength, _attrParsePathNode, loader);
return loader->svgParse->node;
}
@@ -1263,7 +1384,7 @@ static bool _attrParseCircleNode(void* data, const char* key, const char* value)
}
if (!strcmp(key, "style")) {
- return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
+ return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
} else if (!strcmp(key, "clip-path")) {
_handleClipPathAttr(loader, node, value);
} else if (!strcmp(key, "mask")) {
@@ -1271,6 +1392,8 @@ static bool _attrParseCircleNode(void* data, const char* key, const char* value)
} else if (!strcmp(key, "id")) {
if (node->id && value) free(node->id);
node->id = _copyId(value);
+ } else if (!strcmp(key, "class")) {
+ _handleCssClassAttr(loader, node, value);
} else {
return _parseStyleAttr(loader, key, value, false);
}
@@ -1278,13 +1401,13 @@ static bool _attrParseCircleNode(void* data, const char* key, const char* value)
}
-static SvgNode* _createCircleNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
+static SvgNode* _createCircleNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
{
loader->svgParse->node = _createNode(parent, SvgNodeType::Circle);
if (!loader->svgParse->node) return nullptr;
- simpleXmlParseAttributes(buf, bufLength, _attrParseCircleNode, loader);
+ func(buf, bufLength, _attrParseCircleNode, loader);
return loader->svgParse->node;
}
@@ -1325,8 +1448,10 @@ static bool _attrParseEllipseNode(void* data, const char* key, const char* value
if (!strcmp(key, "id")) {
if (node->id && value) free(node->id);
node->id = _copyId(value);
+ } else if (!strcmp(key, "class")) {
+ _handleCssClassAttr(loader, node, value);
} else if (!strcmp(key, "style")) {
- return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
+ return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
} else if (!strcmp(key, "clip-path")) {
_handleClipPathAttr(loader, node, value);
} else if (!strcmp(key, "mask")) {
@@ -1338,13 +1463,13 @@ static bool _attrParseEllipseNode(void* data, const char* key, const char* value
}
-static SvgNode* _createEllipseNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
+static SvgNode* _createEllipseNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
{
loader->svgParse->node = _createNode(parent, SvgNodeType::Ellipse);
if (!loader->svgParse->node) return nullptr;
- simpleXmlParseAttributes(buf, bufLength, _attrParseEllipseNode, loader);
+ func(buf, bufLength, _attrParseEllipseNode, loader);
return loader->svgParse->node;
}
@@ -1400,7 +1525,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, _parseStyleAttr, loader);
+ return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
} else if (!strcmp(key, "clip-path")) {
_handleClipPathAttr(loader, node, value);
} else if (!strcmp(key, "mask")) {
@@ -1408,6 +1533,8 @@ static bool _attrParsePolygonNode(void* data, const char* key, const char* value
} else if (!strcmp(key, "id")) {
if (node->id && value) free(node->id);
node->id = _copyId(value);
+ } else if (!strcmp(key, "class")) {
+ _handleCssClassAttr(loader, node, value);
} else {
return _parseStyleAttr(loader, key, value, false);
}
@@ -1415,24 +1542,24 @@ static bool _attrParsePolygonNode(void* data, const char* key, const char* value
}
-static SvgNode* _createPolygonNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
+static SvgNode* _createPolygonNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
{
loader->svgParse->node = _createNode(parent, SvgNodeType::Polygon);
if (!loader->svgParse->node) return nullptr;
- simpleXmlParseAttributes(buf, bufLength, _attrParsePolygonNode, loader);
+ func(buf, bufLength, _attrParsePolygonNode, loader);
return loader->svgParse->node;
}
-static SvgNode* _createPolylineNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
+static SvgNode* _createPolylineNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
{
loader->svgParse->node = _createNode(parent, SvgNodeType::Polyline);
if (!loader->svgParse->node) return nullptr;
- simpleXmlParseAttributes(buf, bufLength, _attrParsePolygonNode, loader);
+ func(buf, bufLength, _attrParsePolygonNode, loader);
return loader->svgParse->node;
}
@@ -1482,8 +1609,10 @@ static bool _attrParseRectNode(void* data, const char* key, const char* value)
if (!strcmp(key, "id")) {
if (node->id && value) free(node->id);
node->id = _copyId(value);
+ } else if (!strcmp(key, "class")) {
+ _handleCssClassAttr(loader, node, value);
} else if (!strcmp(key, "style")) {
- ret = simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
+ ret = simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
} else if (!strcmp(key, "clip-path")) {
_handleClipPathAttr(loader, node, value);
} else if (!strcmp(key, "mask")) {
@@ -1496,7 +1625,7 @@ static bool _attrParseRectNode(void* data, const char* key, const char* value)
}
-static SvgNode* _createRectNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
+static SvgNode* _createRectNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
{
loader->svgParse->node = _createNode(parent, SvgNodeType::Rect);
@@ -1504,7 +1633,7 @@ static SvgNode* _createRectNode(SvgLoaderData* loader, SvgNode* parent, const ch
loader->svgParse->node->node.rect.hasRx = loader->svgParse->node->node.rect.hasRy = false;
- simpleXmlParseAttributes(buf, bufLength, _attrParseRectNode, loader);
+ func(buf, bufLength, _attrParseRectNode, loader);
return loader->svgParse->node;
}
@@ -1545,8 +1674,10 @@ static bool _attrParseLineNode(void* data, const char* key, const char* value)
if (!strcmp(key, "id")) {
if (node->id && value) free(node->id);
node->id = _copyId(value);
+ } else if (!strcmp(key, "class")) {
+ _handleCssClassAttr(loader, node, value);
} else if (!strcmp(key, "style")) {
- return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
+ return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
} else if (!strcmp(key, "clip-path")) {
_handleClipPathAttr(loader, node, value);
} else if (!strcmp(key, "mask")) {
@@ -1558,13 +1689,13 @@ static bool _attrParseLineNode(void* data, const char* key, const char* value)
}
-static SvgNode* _createLineNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
+static SvgNode* _createLineNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
{
loader->svgParse->node = _createNode(parent, SvgNodeType::Line);
if (!loader->svgParse->node) return nullptr;
- simpleXmlParseAttributes(buf, bufLength, _attrParseLineNode, loader);
+ func(buf, bufLength, _attrParseLineNode, loader);
return loader->svgParse->node;
}
@@ -1616,12 +1747,16 @@ static bool _attrParseImageNode(void* data, const char* key, const char* value)
} else if (!strcmp(key, "id")) {
if (node->id && value) free(node->id);
node->id = _copyId(value);
+ } else if (!strcmp(key, "class")) {
+ _handleCssClassAttr(loader, node, value);
} else if (!strcmp(key, "style")) {
- return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
+ return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
} else if (!strcmp(key, "clip-path")) {
_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);
}
@@ -1629,13 +1764,13 @@ static bool _attrParseImageNode(void* data, const char* key, const char* value)
}
-static SvgNode* _createImageNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
+static SvgNode* _createImageNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
{
loader->svgParse->node = _createNode(parent, SvgNodeType::Image);
if (!loader->svgParse->node) return nullptr;
- simpleXmlParseAttributes(buf, bufLength, _attrParseImageNode, loader);
+ func(buf, bufLength, _attrParseImageNode, loader);
return loader->svgParse->node;
}
@@ -1941,12 +2076,6 @@ static void _cloneNode(SvgNode* from, SvgNode* parent, int depth)
}
-static void _postponeCloneNode(SvgLoaderData* loader, SvgNode *node, char* id)
-{
- loader->cloneNodes.push({node, id});
-}
-
-
static void _clonePostponedNodes(Array<SvgNodeIdPair>* cloneNodes, SvgNode* doc)
{
for (uint32_t i = 0; i < cloneNodes->count; ++i) {
@@ -1955,6 +2084,9 @@ static void _clonePostponedNodes(Array<SvgNodeIdPair>* cloneNodes, SvgNode* doc)
auto nodeFrom = _findChildById(defs, nodeIdPair.id);
if (!nodeFrom) nodeFrom = _findChildById(doc, nodeIdPair.id);
_cloneNode(nodeFrom, nodeIdPair.node, 0);
+ if (nodeFrom && nodeFrom->type == SvgNodeType::Symbol && nodeIdPair.node->type == SvgNodeType::Use) {
+ nodeIdPair.node->node.use.symbol = nodeFrom;
+ }
free(nodeIdPair.id);
}
}
@@ -1967,10 +2099,10 @@ static constexpr struct
int sz;
size_t offset;
} useTags[] = {
- {"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(SvgRectNode, x)},
- {"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(SvgRectNode, y)},
- {"width", SvgParserLengthType::Horizontal, sizeof("width"), offsetof(SvgRectNode, w)},
- {"height", SvgParserLengthType::Vertical, sizeof("height"), offsetof(SvgRectNode, h)}
+ {"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(SvgUseNode, x)},
+ {"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(SvgUseNode, y)},
+ {"width", SvgParserLengthType::Horizontal, sizeof("width"), offsetof(SvgUseNode, w)},
+ {"height", SvgParserLengthType::Vertical, sizeof("height"), offsetof(SvgUseNode, h)}
};
@@ -1986,6 +2118,10 @@ static bool _attrParseUseNode(void* data, const char* key, const char* value)
for (unsigned int i = 0; i < sizeof(useTags) / sizeof(useTags[0]); i++) {
if (useTags[i].sz - 1 == sz && !strncmp(useTags[i].tag, key, sz)) {
*((float*)(array + useTags[i].offset)) = _toFloat(loader->svgParse, value, useTags[i].type);
+
+ if (useTags[i].offset == offsetof(SvgUseNode, w)) use->isWidthSet = true;
+ else if (useTags[i].offset == offsetof(SvgUseNode, h)) use->isHeightSet = true;
+
return true;
}
}
@@ -1996,12 +2132,13 @@ static bool _attrParseUseNode(void* data, const char* key, const char* value)
nodeFrom = _findChildById(defs, id);
if (nodeFrom) {
_cloneNode(nodeFrom, node, 0);
+ if (nodeFrom->type == SvgNodeType::Symbol) use->symbol = nodeFrom;
free(id);
} else {
//some svg export software include <defs> element at the end of the file
//if so the 'from' element won't be found now and we have to repeat finding
//after the whole file is parsed
- _postponeCloneNode(loader, node, id);
+ _postpone(loader->cloneNodes, node, id);
}
} else {
return _attrParseGNode(data, key, value);
@@ -2010,16 +2147,20 @@ static bool _attrParseUseNode(void* data, const char* key, const char* value)
}
-static SvgNode* _createUseNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
+static SvgNode* _createUseNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
{
loader->svgParse->node = _createNode(parent, SvgNodeType::Use);
if (!loader->svgParse->node) return nullptr;
- simpleXmlParseAttributes(buf, bufLength, _attrParseUseNode, loader);
+ loader->svgParse->node->node.use.isWidthSet = false;
+ loader->svgParse->node->node.use.isHeightSet = false;
+
+ func(buf, bufLength, _attrParseUseNode, loader);
return loader->svgParse->node;
}
+
//TODO: Implement 'text' primitive
static constexpr struct
{
@@ -2049,7 +2190,9 @@ static constexpr struct
{"g", sizeof("g"), _createGNode},
{"svg", sizeof("svg"), _createSvgNode},
{"mask", sizeof("mask"), _createMaskNode},
- {"clipPath", sizeof("clipPath"), _createClipPathNode}
+ {"clipPath", sizeof("clipPath"), _createClipPathNode},
+ {"style", sizeof("style"), _createCssStyleNode},
+ {"symbol", sizeof("symbol"), _createSymbolNode}
};
@@ -2204,8 +2347,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 {
@@ -2291,7 +2434,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, _attrParseStopsStyle, data);
+ simpleXmlParseW3CAttribute(value, strlen(value), _attrParseStopsStyle, data);
} else {
return false;
}
@@ -2394,8 +2537,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 {
@@ -2479,11 +2622,13 @@ static constexpr struct
{"svg", sizeof("svg")},
{"defs", sizeof("defs")},
{"mask", sizeof("mask")},
- {"clipPath", sizeof("clipPath")}
+ {"clipPath", sizeof("clipPath")},
+ {"style", sizeof("style")},
+ {"symbol", sizeof("symbol")}
};
-static void _svgLoaderParerXmlClose(SvgLoaderData* loader, const char* content)
+static void _svgLoaderParserXmlClose(SvgLoaderData* loader, const char* content)
{
content = _skipSpace(content, nullptr);
@@ -2531,13 +2676,20 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
if (empty) return;
if (!loader->doc) {
if (strcmp(tagName, "svg")) return; //Not a valid svg document
- node = method(loader, nullptr, attrs, attrsLength);
+ node = method(loader, nullptr, attrs, attrsLength, simpleXmlParseAttributes);
loader->doc = node;
} else {
if (!strcmp(tagName, "svg")) return; //Already loaded <svg>(SvgNodeType::Doc) tag
if (loader->stack.count > 0) parent = loader->stack.data[loader->stack.count - 1];
else parent = loader->doc;
- node = method(loader, parent, attrs, attrsLength);
+ if (!strcmp(tagName, "style")) {
+ node = method(loader, nullptr, attrs, attrsLength, simpleXmlParseAttributes);
+ loader->cssStyle = node;
+ loader->doc->node.doc.style = node;
+ loader->style = true;
+ } else {
+ node = method(loader, parent, attrs, attrsLength, simpleXmlParseAttributes);
+ }
}
if (!node) return;
@@ -2547,7 +2699,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
} else if ((method = _findGraphicsFactory(tagName))) {
if (loader->stack.count > 0) parent = loader->stack.data[loader->stack.count - 1];
else parent = loader->doc;
- node = method(loader, parent, attrs, attrsLength);
+ node = method(loader, parent, attrs, attrsLength, simpleXmlParseAttributes);
} else if ((gradientMethod = _findGradientFactory(tagName))) {
SvgStyleGradient* gradient;
gradient = gradientMethod(loader, attrs, attrsLength);
@@ -2578,6 +2730,42 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
}
+static void _svgLoaderParserXmlCssStyle(SvgLoaderData* loader, const char* content, unsigned int length)
+{
+ char* tag;
+ char* name;
+ const char* attrs = nullptr;
+ unsigned int attrsLength = 0;
+
+ FactoryMethod method;
+ GradientFactoryMethod gradientMethod;
+ SvgNode *node = nullptr;
+
+ while (auto next = simpleXmlParseCSSAttribute(content, length, &tag, &name, &attrs, &attrsLength)) {
+ if ((method = _findGroupFactory(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))) {
+ TVGLOG("SVG", "Unsupported elements used in the internal CSS style sheets [Elements: %s]", tag);
+ } else if (!strcmp(tag, "stop")) {
+ 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 in the internal CSS style sheets [Elements: %s]", tag);
+ }
+
+ length -= next - content;
+ content = next;
+
+ free(tag);
+ free(name);
+ }
+ loader->style = false;
+}
+
+
static bool _svgLoaderParser(void* data, SimpleXMLType type, const char* content, unsigned int length)
{
SvgLoaderData* loader = (SvgLoaderData*)data;
@@ -2592,11 +2780,14 @@ static bool _svgLoaderParser(void* data, SimpleXMLType type, const char* content
break;
}
case SimpleXMLType::Close: {
- _svgLoaderParerXmlClose(loader, content);
+ _svgLoaderParserXmlClose(loader, content);
break;
}
case SimpleXMLType::Data:
- case SimpleXMLType::CData:
+ case SimpleXMLType::CData: {
+ if (loader->style) _svgLoaderParserXmlCssStyle(loader, content, length);
+ break;
+ }
case SimpleXMLType::DoctypeChild: {
break;
}
@@ -2619,7 +2810,7 @@ static void _inefficientNodeCheck(TVG_UNUSED SvgNode* node)
#ifdef THORVG_LOG_ENABLED
auto type = simpleXmlNodeTypeToString(node->type);
- if (!node->display && node->type != SvgNodeType::ClipPath) TVGLOG("SVG", "Inefficient elements used [Display is none][Node Type : %s]", type);
+ if (!node->display && node->type != SvgNodeType::ClipPath && node->type != SvgNodeType::Symbol) TVGLOG("SVG", "Inefficient elements used [Display is none][Node Type : %s]", type);
if (node->style->opacity == 0) TVGLOG("SVG", "Inefficient elements used [Opacity is zero][Node Type : %s]", type);
if (node->style->fill.opacity == 0 && node->style->stroke.opacity == 0) TVGLOG("SVG", "Inefficient elements used [Fill opacity and stroke opacity are zero][Node Type : %s]", type);
@@ -2749,6 +2940,7 @@ static void _freeNodeStyle(SvgStyleProperty* style)
//style->clipPath.node and style->mask.node has only the addresses of node. Therefore, node is released from _freeNode.
free(style->clipPath.url);
free(style->mask.url);
+ free(style->cssClass);
if (style->fill.paint.gradient) {
style->fill.paint.gradient->clear();
@@ -2793,6 +2985,7 @@ static void _freeNode(SvgNode* node)
}
case SvgNodeType::Doc: {
_freeNode(node->node.doc.defs);
+ _freeNode(node->node.doc.style);
break;
}
case SvgNodeType::Defs: {
@@ -2846,7 +3039,7 @@ static bool _svgLoaderParserForValidCheckXmlOpen(SvgLoaderData* loader, const ch
if ((method = _findGroupFactory(tagName))) {
if (!loader->doc) {
if (strcmp(tagName, "svg")) return true; //Not a valid svg document
- node = method(loader, nullptr, attrs, attrsLength);
+ node = method(loader, nullptr, attrs, attrsLength, simpleXmlParseAttributes);
loader->doc = node;
loader->stack.push(node);
return false;
@@ -2906,16 +3099,20 @@ 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;
- _updateComposite(loaderData.doc, loaderData.doc);
- if (defs) _updateComposite(loaderData.doc, defs);
+ if (loaderData.nodesToStyle.count > 0) cssApplyStyleToPostponeds(loaderData.nodesToStyle, loaderData.cssStyle);
+ if (loaderData.cssStyle) cssUpdateStyle(loaderData.doc, loaderData.cssStyle);
if (loaderData.cloneNodes.count > 0) _clonePostponedNodes(&loaderData.cloneNodes, loaderData.doc);
+ _updateComposite(loaderData.doc, loaderData.doc);
+ if (defs) _updateComposite(loaderData.doc, defs);
+
if (loaderData.gradients.count > 0) _updateGradient(loaderData.doc, &loaderData.gradients);
if (defs) _updateGradient(loaderData.doc, &defs->node.defs.gradients);
+
+ _updateStyle(loaderData.doc, nullptr);
}
root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh, w, h, preserveAspect, svgPath);
}
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h
index 468f05801d..093fb671b3 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+ * 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
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
index cceef915f0..1f25e82adc 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+ * 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
@@ -51,6 +51,8 @@ enum class SvgNodeType
Video,
ClipPath,
Mask,
+ CssStyle,
+ Symbol,
Unknown
};
@@ -111,7 +113,8 @@ enum class SvgStyleFlags
Transform = 0x800,
ClipPath = 0x1000,
Mask = 0x2000,
- Display = 0x4000
+ MaskType = 0x4000,
+ Display = 0x8000
};
enum class SvgStopStyleFlags
@@ -127,6 +130,12 @@ enum class SvgFillRule
OddEven = 1
};
+enum class SvgMaskType
+{
+ Luminance = 0,
+ Alpha
+};
+
//Length type to recalculate %, pt, pc, mm, cm etc
enum class SvgParserLengthType
{
@@ -145,6 +154,7 @@ struct SvgDocNode
float vw;
float vh;
SvgNode* defs;
+ SvgNode* style;
bool preserveAspect;
};
@@ -157,12 +167,22 @@ struct SvgDefsNode
Array<SvgStyleGradient*> gradients;
};
+struct SvgSymbolNode
+{
+ float w, h;
+ float vx, vy, vw, vh;
+ bool preserveAspect;
+ bool overflowVisible;
+};
+
struct SvgUseNode
{
float x, y, w, h;
+ bool isWidthSet;
+ bool isHeightSet;
+ SvgNode* symbol;
};
-
struct SvgEllipseNode
{
float cx;
@@ -215,11 +235,21 @@ struct SvgPolygonNode
float* points;
};
-struct SvgCompositeNode
+struct SvgClipNode
{
bool userSpace;
};
+struct SvgMaskNode
+{
+ SvgMaskType type;
+ bool userSpace;
+};
+
+struct SvgCssStyleNode
+{
+};
+
struct SvgLinearGradient
{
float x1;
@@ -328,6 +358,7 @@ struct SvgStyleProperty
int opacity;
SvgColor color;
bool curColorSet;
+ char* cssClass;
SvgStyleFlags flags;
};
@@ -352,7 +383,10 @@ struct SvgNode
SvgPathNode path;
SvgLineNode line;
SvgImageNode image;
- SvgCompositeNode comp;
+ SvgMaskNode mask;
+ SvgClipNode clip;
+ SvgCssStyleNode cssStyle;
+ SvgSymbolNode symbol;
} node;
bool display;
~SvgNode();
@@ -384,15 +418,18 @@ struct SvgNodeIdPair
struct SvgLoaderData
{
- Array<SvgNode *> stack = {nullptr, 0, 0};
+ Array<SvgNode*> stack = {nullptr, 0, 0};
SvgNode* doc = nullptr;
SvgNode* def = nullptr;
+ SvgNode* cssStyle = nullptr;
Array<SvgStyleGradient*> gradients;
SvgStyleGradient* latestGradient = nullptr; //For stops
SvgParser* svgParse = nullptr;
Array<SvgNodeIdPair> cloneNodes;
+ Array<SvgNodeIdPair> nodesToStyle;
int level = 0;
bool result = false;
+ bool style = false;
};
/*
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
index 32685ee620..a09a2797d0 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+ * 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
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h
index 7f26c4a213..8f5f9035dc 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+ * 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
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
index ae17634f31..90705f2523 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+ * 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
@@ -68,12 +68,12 @@ struct Box
static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath);
-static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask);
+static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, bool* isMaskWhite = nullptr);
static inline bool _isGroupType(SvgNodeType type)
{
- if (type == SvgNodeType::Doc || type == SvgNodeType::G || type == SvgNodeType::Use || type == SvgNodeType::ClipPath) return true;
+ if (type == SvgNodeType::Doc || type == SvgNodeType::G || type == SvgNodeType::Use || type == SvgNodeType::ClipPath || type == SvgNodeType::Symbol) return true;
return false;
}
@@ -276,15 +276,21 @@ static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox
Composition can be applied recursively if its children nodes have composition target to this one. */
if (node->style->mask.applying) {
TVGLOG("SVG", "Multiple Composition Tried! Check out Circular dependency?");
- } else {
+ } else {
auto compNode = node->style->mask.node;
if (compNode && compNode->child.count > 0) {
node->style->mask.applying = true;
- auto comp = _sceneBuildHelper(compNode, vBox, svgPath, true);
+ bool isMaskWhite = true;
+ auto comp = _sceneBuildHelper(compNode, vBox, svgPath, true, &isMaskWhite);
if (comp) {
if (node->transform) comp->transform(*node->transform);
- paint->composite(move(comp), CompositeMethod::AlphaMask);
+
+ if (compNode->node.mask.type == SvgMaskType::Luminance && !isMaskWhite) {
+ paint->composite(move(comp), CompositeMethod::LumaMask);
+ } else {
+ paint->composite(move(comp), CompositeMethod::AlphaMask);
+ }
}
node->style->mask.applying = false;
@@ -534,57 +540,137 @@ static unique_ptr<Picture> _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;
}
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;
}
-static unique_ptr<Scene> _useBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath)
+static unique_ptr<Scene> _useBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool* isMaskWhite)
{
- auto scene = _sceneBuildHelper(node, vBox, svgPath, false);
+ unique_ptr<Scene> finalScene;
+ auto scene = _sceneBuildHelper(node, vBox, svgPath, false, isMaskWhite);
+
+ // mUseTransform = mUseTransform * mTranslate
+ Matrix mUseTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
+ if (node->transform) mUseTransform = *node->transform;
if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) {
- scene->translate(node->node.use.x, node->node.use.y);
+ Matrix mTranslate = {1, 0, node->node.use.x, 0, 1, node->node.use.y, 0, 0, 1};
+ mUseTransform = mathMultiply(&mUseTransform, &mTranslate);
}
- if (node->node.use.w > 0.0f && node->node.use.h > 0.0f) {
- //TODO: handle width/height properties
+
+ if (node->node.use.symbol) {
+ auto symbol = node->node.use.symbol->node.symbol;
+
+ auto width = symbol.w;
+ if (node->node.use.isWidthSet) width = node->node.use.w;
+ auto height = symbol.h;
+ if (node->node.use.isHeightSet) height = node->node.use.h;
+
+ Matrix mViewBox = {1, 0, 0, 0, 1, 0, 0, 0, 1};
+ if ((!mathEqual(width, symbol.vw) || !mathEqual(height, symbol.vh)) && symbol.vw > 0 && symbol.vh > 0) {
+ auto sx = width / symbol.vw;
+ auto sy = height / symbol.vh;
+ if (symbol.preserveAspect) {
+ if (sx < sy) sy = sx;
+ else sx = sy;
+ }
+
+ auto tvx = symbol.vx * sx;
+ auto tvy = symbol.vy * sy;
+ auto tvw = symbol.vw * sx;
+ auto tvh = symbol.vh * sy;
+ tvy -= (symbol.h - tvh) * 0.5f;
+ tvx -= (symbol.w - tvw) * 0.5f;
+ mViewBox = {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1};
+ } else if (!mathZero(symbol.vx) || !mathZero(symbol.vy)) {
+ mViewBox = {1, 0, -symbol.vx, 0, 1, -symbol.vy, 0, 0, 1};
+ }
+
+ // mSceneTransform = mUseTransform * mSymbolTransform * mViewBox
+ Matrix mSceneTransform = mViewBox;
+ if (node->node.use.symbol->transform) {
+ mSceneTransform = mathMultiply(node->node.use.symbol->transform, &mViewBox);
+ }
+ mSceneTransform = mathMultiply(&mUseTransform, &mSceneTransform);
+ scene->transform(mSceneTransform);
+
+ if (node->node.use.symbol->node.symbol.overflowVisible) {
+ finalScene = move(scene);
+ } else {
+ auto viewBoxClip = Shape::gen();
+ viewBoxClip->appendRect(0, 0, width, height, 0, 0);
+
+ // mClipTransform = mUseTransform * mSymbolTransform
+ Matrix mClipTransform = mUseTransform;
+ if (node->node.use.symbol->transform) {
+ mClipTransform = mathMultiply(&mUseTransform, node->node.use.symbol->transform);
+ }
+ viewBoxClip->transform(mClipTransform);
+
+ auto compositeLayer = Scene::gen();
+ compositeLayer->composite(move(viewBoxClip), CompositeMethod::ClipPath);
+ compositeLayer->push(move(scene));
+
+ auto root = Scene::gen();
+ root->push(move(compositeLayer));
+
+ finalScene = move(root);
+ }
+ } else {
+ if (!mathIdentity((const Matrix*)(&mUseTransform))) scene->transform(mUseTransform);
+ finalScene = move(scene);
}
- return scene;
+
+ return finalScene;
}
-static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask)
+static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, bool* isMaskWhite)
{
if (_isGroupType(node->type) || mask) {
auto scene = Scene::gen();
- if (!mask && node->transform) scene->transform(*node->transform);
+ // For a Symbol node, the viewBox transformation has to be applied first - see _useBuildHelper()
+ if (!mask && node->transform && node->type != SvgNodeType::Symbol) scene->transform(*node->transform);
if (node->display && node->style->opacity != 0) {
auto child = node->child.data;
for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
if (_isGroupType((*child)->type)) {
if ((*child)->type == SvgNodeType::Use)
- scene->push(_useBuildHelper(*child, vBox, svgPath));
+ scene->push(_useBuildHelper(*child, vBox, svgPath, isMaskWhite));
else
- scene->push(_sceneBuildHelper(*child, vBox, svgPath, false));
+ scene->push(_sceneBuildHelper(*child, vBox, svgPath, false, isMaskWhite));
} else if ((*child)->type == SvgNodeType::Image) {
auto image = _imageBuildHelper(*child, vBox, svgPath);
if (image) scene->push(move(image));
} else if ((*child)->type != SvgNodeType::Mask) {
auto shape = _shapeBuildHelper(*child, vBox, svgPath);
- if (shape) scene->push(move(shape));
+ if (shape) {
+ if (isMaskWhite) {
+ uint8_t r, g, b;
+ shape->fillColor(&r, &g, &b, nullptr);
+ 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));
+ }
}
}
_applyComposition(scene.get(), node, vBox, svgPath);
@@ -620,17 +706,13 @@ unique_ptr<Scene> svgSceneBuild(SvgNode* node, float vx, float vy, float vw, flo
auto tvy = vy * scale;
auto tvw = vw * scale;
auto tvh = vh * scale;
- if (vw > vh) tvy -= (h - tvh) * 0.5f;
- else tvx -= (w - tvw) * 0.5f;
+ tvx -= (w - tvw) * 0.5f;
+ tvy -= (h - tvh) * 0.5f;
docNode->translate(-tvx, -tvy);
} else {
//Align
auto tvx = vx * sx;
auto tvy = vy * sy;
- auto tvw = vw * sx;
- auto tvh = vh * sy;
- if (tvw > tvh) tvy -= (h - tvh) * 0.5f;
- else tvx -= (w - tvw) * 0.5f;
Matrix m = {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1};
docNode->transform(m);
}
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h
index 4232aca612..cecbbf02a8 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+ * 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
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
index 9f269b29a2..1f1fe2a718 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+ * 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
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h
index 4320cfed4e..b5e6e1bdb2 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+ * 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
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
index ee199da231..a12689c7dd 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+ * 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
@@ -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 */
/************************************************************************/
@@ -264,6 +272,7 @@ const char* simpleXmlNodeTypeToString(TVG_UNUSED SvgNodeType type)
"Video",
"ClipPath",
"Mask",
+ "Symbol",
"Unknown",
};
return TYPE_NAMES[(int) type];
@@ -450,7 +459,7 @@ bool simpleXmlParse(const char* buf, unsigned bufLength, bool strip, simpleXMLCb
}
-bool simpleXmlParseW3CAttribute(const char* buf, simpleXMLAttributeCb func, const void* data)
+bool simpleXmlParseW3CAttribute(const char* buf, unsigned bufLength, simpleXMLAttributeCb func, const void* data)
{
const char* end;
char* key;
@@ -459,7 +468,7 @@ bool simpleXmlParseW3CAttribute(const char* buf, simpleXMLAttributeCb func, cons
if (!buf) return false;
- end = buf + strlen(buf);
+ end = buf + bufLength;
key = (char*)alloca(end - buf + 1);
val = (char*)alloca(end - buf + 1);
@@ -468,6 +477,11 @@ bool simpleXmlParseW3CAttribute(const char* buf, simpleXMLAttributeCb func, cons
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';
@@ -509,6 +523,47 @@ bool simpleXmlParseW3CAttribute(const char* buf, simpleXMLAttributeCb func, cons
}
+/*
+ * Supported formats:
+ * tag {}, .name {}, tag.name{}
+ */
+const char* simpleXmlParseCSSAttribute(const char* buf, unsigned bufLength, char** tag, char** name, const char** attrs, unsigned* attrsLength)
+{
+ if (!buf) return nullptr;
+
+ *tag = *name = nullptr;
+ *attrsLength = 0;
+
+ auto itr = _simpleXmlSkipWhiteSpace(buf, buf + bufLength);
+ auto itrEnd = (const char*)memchr(buf, '{', bufLength);
+
+ if (!itrEnd || itr == itrEnd) return nullptr;
+
+ auto nextElement = (const char*)memchr(itrEnd, '}', bufLength - (itrEnd - buf));
+ if (!nextElement) return nullptr;
+
+ *attrs = itrEnd + 1;
+ *attrsLength = nextElement - *attrs;
+
+ const char *p;
+
+ itrEnd = _simpleXmlUnskipWhiteSpace(itrEnd, itr);
+ if (*(itrEnd - 1) == '.') return nullptr;
+
+ for (p = itr; p < itrEnd; p++) {
+ if (*p == '.') break;
+ }
+
+ if (p == itr) *tag = strdup("all");
+ else *tag = _strndup(itr, p - itr);
+
+ if (p == itrEnd) *name = nullptr;
+ else *name = _strndup(p + 1, itrEnd - p - 1);
+
+ return (nextElement ? nextElement + 1 : nullptr);
+}
+
+
const char* simpleXmlFindAttributesTag(const char* buf, unsigned bufLength)
{
const char *itr = buf, *itrEnd = buf + bufLength;
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h
index d96a631528..e2761ca8da 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h
+++ b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+ * 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
@@ -32,7 +32,7 @@ const int xmlEntityLength[] = {6, 6, 6, 5, 4, 4, 6, 6};
enum class SimpleXMLType
{
Open = 0, //!< \<tag attribute="value"\>
- OpenEmpty, //!< \<tag attribute="value" /\>
+ OpenEmpty, //!< \<tag attribute="value" /\>
Close, //!< \</tag\>
Data, //!< tag text data
CData, //!< \<![cdata[something]]\>
@@ -41,16 +41,17 @@ enum class SimpleXMLType
Doctype, //!< \<!doctype html
Comment, //!< \<!-- something --\>
Ignored, //!< whatever is ignored by parser, like whitespace
- DoctypeChild //!< \<!doctype_child
+ DoctypeChild //!< \<!doctype_child
};
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, simpleXMLAttributeCb func, const void* data);
-const char *simpleXmlFindAttributesTag(const char* buf, unsigned buflen);
+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 bufLength);
bool isIgnoreUnsupportedLogElements(const char* tagName);
const char* simpleXmlNodeTypeToString(SvgNodeType type);