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.cpp75
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h2
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp1120
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h5
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h110
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp11
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h2
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp61
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h4
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp6
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h2
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp2
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h2
13 files changed, 934 insertions, 468 deletions
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp
index 478ba5d3d1..694e6d1ebf 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2022 - 2023 the ThorVG project. 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,13 +32,13 @@ 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)) {
+ if (from->curColorSet && !(to->flags & SvgStyleFlags::Color)) {
to->color = from->color;
to->curColorSet = true;
- to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Color);
+ to->flags = (to->flags | SvgStyleFlags::Color);
}
//Fill
- if (((int)from->fill.flags & (int)SvgFillFlags::Paint) && !((int)to->flags & (int)SvgStyleFlags::Fill)) {
+ if ((from->fill.flags & SvgFillFlags::Paint) && !(to->flags & 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;
@@ -46,21 +46,21 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from)
if (to->fill.paint.url) free(to->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);
+ to->fill.flags = (to->fill.flags | SvgFillFlags::Paint);
+ to->flags = (to->flags | SvgStyleFlags::Fill);
}
- if (((int)from->fill.flags & (int)SvgFillFlags::Opacity) && !((int)to->flags & (int)SvgStyleFlags::FillOpacity)) {
+ if ((from->fill.flags & SvgFillFlags::Opacity) && !(to->flags & 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);
+ to->fill.flags = (to->fill.flags | SvgFillFlags::Opacity);
+ to->flags = (to->flags | SvgStyleFlags::FillOpacity);
}
- if (((int)from->fill.flags & (int)SvgFillFlags::FillRule) && !((int)to->flags & (int)SvgStyleFlags::FillRule)) {
+ if ((from->fill.flags & SvgFillFlags::FillRule) && !(to->flags & 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);
+ to->fill.flags = (to->fill.flags | SvgFillFlags::FillRule);
+ to->flags = (to->flags | SvgStyleFlags::FillRule);
}
//Stroke
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Paint) && !((int)to->flags & (int)SvgStyleFlags::Stroke)) {
+ if ((from->stroke.flags & SvgStrokeFlags::Paint) && !(to->flags & 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;
@@ -68,45 +68,45 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from)
if (to->stroke.paint.url) free(to->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);
+ to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Paint);
+ to->flags = (to->flags | SvgStyleFlags::Stroke);
}
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Opacity) && !((int)to->flags & (int)SvgStyleFlags::StrokeOpacity)) {
+ if ((from->stroke.flags & SvgStrokeFlags::Opacity) && !(to->flags & 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);
+ to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Opacity);
+ to->flags = (to->flags | SvgStyleFlags::StrokeOpacity);
}
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Width) && !((int)to->flags & (int)SvgStyleFlags::StrokeWidth)) {
+ if ((from->stroke.flags & SvgStrokeFlags::Width) && !(to->flags & 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);
+ to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Width);
+ to->flags = (to->flags | SvgStyleFlags::StrokeWidth);
}
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Dash) && !((int)to->flags & (int)SvgStyleFlags::StrokeDashArray)) {
+ if ((from->stroke.flags & SvgStrokeFlags::Dash) && !(to->flags & 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);
+ to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Dash);
+ to->flags = (to->flags | SvgStyleFlags::StrokeDashArray);
}
}
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Cap) && !((int)to->flags & (int)SvgStyleFlags::StrokeLineCap)) {
+ if ((from->stroke.flags & SvgStrokeFlags::Cap) && !(to->flags & 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);
+ to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Cap);
+ to->flags = (to->flags | SvgStyleFlags::StrokeLineCap);
}
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Join) && !((int)to->flags & (int)SvgStyleFlags::StrokeLineJoin)) {
+ if ((from->stroke.flags & SvgStrokeFlags::Join) && !(to->flags & 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);
+ to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Join);
+ to->flags = (to->flags | 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)) {
+ if (from->opacity < 255 && !(to->flags & SvgStyleFlags::Opacity)) {
to->opacity = from->opacity;
- to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Opacity);
+ to->flags = (to->flags | SvgStyleFlags::Opacity);
}
}
@@ -118,11 +118,11 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from)
void cssCopyStyleAttr(SvgNode* to, const SvgNode* from)
{
//Copy matrix attribute
- if (from->transform && !((int)to->style->flags & (int)SvgStyleFlags::Transform)) {
+ if (from->transform && !(to->style->flags & 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);
+ to->style->flags = (to->style->flags | SvgStyleFlags::Transform);
}
}
//Copy style attribute
@@ -155,12 +155,12 @@ SvgNode* cssFindStyleNode(const SvgNode* style, const char* title, SvgNodeType t
SvgNode* cssFindStyleNode(const SvgNode* style, const char* title)
{
- if (!style) return nullptr;
+ if (!style || !title) 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);
+ if ((*child)->id && !strcmp((*child)->id, title)) return (*child);
}
}
return nullptr;
@@ -175,9 +175,6 @@ void cssUpdateStyle(SvgNode* doc, SvgNode* style)
if (auto cssNode = cssFindStyleNode(style, nullptr, (*child)->type)) {
cssCopyStyleAttr(*child, cssNode);
}
- if (auto cssNode = cssFindStyleNode(style, nullptr)) {
- cssCopyStyleAttr(*child, cssNode);
- }
cssUpdateStyle(*child, style);
}
}
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h
index 66477c1a32..228c5996da 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2022 - 2023 the ThorVG project. 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/tvgSvgLoader.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
index bc350a0eb8..f9f08cdb81 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. 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
@@ -137,11 +137,11 @@ static constexpr struct
};
-static bool _parseAspectRatio(const char** content, AspectRatioAlign* align, AspectRatioMeetOrSlice* meetOrSlice)
+static void _parseAspectRatio(const char** content, AspectRatioAlign* align, AspectRatioMeetOrSlice* meetOrSlice)
{
if (!strcmp(*content, "none")) {
*align = AspectRatioAlign::None;
- return true;
+ return;
}
for (unsigned int i = 0; i < sizeof(alignTags) / sizeof(alignTags[0]); i++) {
@@ -158,8 +158,6 @@ static bool _parseAspectRatio(const char** content, AspectRatioAlign* align, Asp
} else if (!strcmp(*content, "slice")) {
*meetOrSlice = AspectRatioMeetOrSlice::Slice;
}
-
- return true;
}
@@ -254,6 +252,36 @@ static SvgMaskType _toMaskType(const char* str)
}
+//The default rendering order: fill, stroke, markers
+//If any is omitted, will be rendered in its default order after the specified ones.
+static bool _toPaintOrder(const char* str)
+{
+ uint8_t position = 1;
+ uint8_t strokePosition = 0;
+ uint8_t fillPosition = 0;
+
+ while (*str != '\0') {
+ str = _skipSpace(str, nullptr);
+ if (!strncmp(str, "fill", 4)) {
+ fillPosition = position++;
+ str += 4;
+ } else if (!strncmp(str, "stroke", 6)) {
+ strokePosition = position++;
+ str += 6;
+ } else if (!strncmp(str, "markers", 7)) {
+ str += 7;
+ } else {
+ return _toPaintOrder("fill stroke");
+ }
+ }
+
+ if (fillPosition == 0) fillPosition = position++;
+ if (strokePosition == 0) strokePosition = position++;
+
+ return fillPosition < strokePosition;
+}
+
+
#define _PARSE_TAG(Type, Name, Name1, Tags_Array, Default) \
static Type _to##Name1(const char* str) \
{ \
@@ -833,20 +861,40 @@ static bool _attrParseSvgNode(void* data, const char* key, const char* value)
if (!strcmp(key, "width")) {
doc->w = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
+ if (strstr(value, "%") && !(doc->viewFlag & SvgViewFlag::Viewbox)) {
+ doc->viewFlag = (doc->viewFlag | SvgViewFlag::WidthInPercent);
+ } else {
+ doc->viewFlag = (doc->viewFlag | SvgViewFlag::Width);
+ }
} else if (!strcmp(key, "height")) {
doc->h = _toFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
+ if (strstr(value, "%") && !(doc->viewFlag & SvgViewFlag::Viewbox)) {
+ doc->viewFlag = (doc->viewFlag | SvgViewFlag::HeightInPercent);
+ } else {
+ doc->viewFlag = (doc->viewFlag | SvgViewFlag::Height);
+ }
} else if (!strcmp(key, "viewBox")) {
if (_parseNumber(&value, &doc->vx)) {
if (_parseNumber(&value, &doc->vy)) {
if (_parseNumber(&value, &doc->vw)) {
- _parseNumber(&value, &doc->vh);
- loader->svgParse->global.h = doc->vh;
+ if (_parseNumber(&value, &doc->vh)) {
+ doc->viewFlag = (doc->viewFlag | SvgViewFlag::Viewbox);
+ loader->svgParse->global.h = doc->vh;
+ }
+ loader->svgParse->global.w = doc->vw;
}
- loader->svgParse->global.w = doc->vw;
+ loader->svgParse->global.y = doc->vy;
}
- loader->svgParse->global.y = doc->vy;
+ loader->svgParse->global.x = doc->vx;
+ }
+ if ((doc->viewFlag & SvgViewFlag::Viewbox) && (doc->vw < 0.0f || doc->vh < 0.0f)) {
+ doc->viewFlag = (SvgViewFlag)((uint32_t)doc->viewFlag & ~(uint32_t)SvgViewFlag::Viewbox);
+ TVGLOG("SVG", "Negative values of the <viewBox> width and/or height - the attribute invalidated.");
+ }
+ if (!(doc->viewFlag & SvgViewFlag::Viewbox)) {
+ loader->svgParse->global.x = loader->svgParse->global.y = 0.0f;
+ loader->svgParse->global.w = loader->svgParse->global.h = 1.0f;
}
- loader->svgParse->global.x = doc->vx;
} else if (!strcmp(key, "preserveAspectRatio")) {
_parseAspectRatio(&value, &doc->align, &doc->meetOrSlice);
} else if (!strcmp(key, "style")) {
@@ -890,7 +938,7 @@ static void _handleColorAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, co
static void _handleFillAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
{
SvgStyleProperty* style = node->style;
- style->fill.flags = (SvgFillFlags)((int)style->fill.flags | (int)SvgFillFlags::Paint);
+ style->fill.flags = (style->fill.flags | SvgFillFlags::Paint);
_handlePaintAttr(&style->fill.paint, value);
}
@@ -898,47 +946,47 @@ static void _handleFillAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, con
static void _handleStrokeAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
{
SvgStyleProperty* style = node->style;
- style->stroke.flags = (SvgStrokeFlags)((int)style->stroke.flags | (int)SvgStrokeFlags::Paint);
+ style->stroke.flags = (style->stroke.flags | SvgStrokeFlags::Paint);
_handlePaintAttr(&style->stroke.paint, value);
}
static void _handleStrokeOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
{
- node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Opacity);
+ node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Opacity);
node->style->stroke.opacity = _toOpacity(value);
}
static void _handleStrokeDashArrayAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
{
- node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Dash);
+ node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Dash);
_parseDashArray(loader, value, &node->style->stroke.dash);
}
static void _handleStrokeWidthAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
{
- node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Width);
+ node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Width);
node->style->stroke.width = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
}
static void _handleStrokeLineCapAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
{
- node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Cap);
+ node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Cap);
node->style->stroke.cap = _toLineCap(value);
}
static void _handleStrokeLineJoinAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
{
- node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Join);
+ node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Join);
node->style->stroke.join = _toLineJoin(value);
}
static void _handleFillRuleAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
{
- node->style->fill.flags = (SvgFillFlags)((int)node->style->fill.flags | (int)SvgFillFlags::FillRule);
+ node->style->fill.flags = (node->style->fill.flags | SvgFillFlags::FillRule);
node->style->fill.fillRule = _toFillRule(value);
}
@@ -951,7 +999,7 @@ static void _handleOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node,
static void _handleFillOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
{
- node->style->fill.flags = (SvgFillFlags)((int)node->style->fill.flags | (int)SvgFillFlags::Opacity);
+ node->style->fill.flags = (node->style->fill.flags | SvgFillFlags::Opacity);
node->style->fill.opacity = _toOpacity(value);
}
@@ -1001,6 +1049,13 @@ static void _handleDisplayAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node,
}
+static void _handlePaintOrderAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ node->style->flags = (node->style->flags | SvgStyleFlags::PaintOrder);
+ node->style->paintOrder = _toPaintOrder(value);
+}
+
+
static void _handleCssClassAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
{
auto cssClass = &node->style->cssClass;
@@ -1051,7 +1106,8 @@ static constexpr struct
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)
+ STYLE_DEF(display, Display, SvgStyleFlags::Display),
+ STYLE_DEF(paint-order, PaintOrder, SvgStyleFlags::PaintOrder)
};
@@ -1071,8 +1127,8 @@ static bool _parseStyleAttr(void* data, const char* key, const char* value, bool
if (styleTags[i].sz - 1 == sz && !strncmp(styleTags[i].tag, key, sz)) {
if (style) {
styleTags[i].tagHandler(loader, node, value);
- node->style->flags = (SvgStyleFlags)((int)node->style->flags | (int)styleTags[i].flag);
- } else if (!((int)node->style->flags & (int)styleTags[i].flag)) {
+ node->style->flags = (node->style->flags | styleTags[i].flag);
+ } else if (!(node->style->flags & styleTags[i].flag)) {
styleTags[i].tagHandler(loader, node, value);
}
return true;
@@ -1253,6 +1309,8 @@ static SvgNode* _createNode(SvgNode* parent, SvgNodeType type)
node->style->stroke.join = StrokeJoin::Miter;
node->style->stroke.scale = 1.0;
+ node->style->paintOrder = _toPaintOrder("fill stroke");
+
//Default display is true("inline").
node->display = true;
@@ -1291,22 +1349,22 @@ static SvgNode* _createSvgNode(SvgLoaderData* loader, SvgNode* parent, const cha
if (!loader->svgParse->node) return nullptr;
SvgDocNode* doc = &(loader->svgParse->node->node.doc);
- loader->svgParse->global.w = 0;
- loader->svgParse->global.h = 0;
+ loader->svgParse->global.w = 1.0f;
+ loader->svgParse->global.h = 1.0f;
doc->align = AspectRatioAlign::XMidYMid;
doc->meetOrSlice = AspectRatioMeetOrSlice::Meet;
+ doc->viewFlag = SvgViewFlag::None;
func(buf, bufLength, _attrParseSvgNode, loader);
- if (loader->svgParse->global.w == 0) {
- if (doc->w < FLT_EPSILON) loader->svgParse->global.w = 1;
- else loader->svgParse->global.w = doc->w;
- }
- if (loader->svgParse->global.h == 0) {
- if (doc->h < FLT_EPSILON) loader->svgParse->global.h = 1;
- else loader->svgParse->global.h = doc->h;
+ if (!(doc->viewFlag & SvgViewFlag::Viewbox)) {
+ if (doc->viewFlag & SvgViewFlag::Width) {
+ loader->svgParse->global.w = doc->w;
+ }
+ if (doc->viewFlag & SvgViewFlag::Height) {
+ loader->svgParse->global.h = doc->h;
+ }
}
-
return loader->svgParse->node;
}
@@ -1868,309 +1926,6 @@ static SvgNode* _findNodeById(SvgNode *node, const char* id)
}
-static void _cloneGradStops(Array<Fill::ColorStop>& dst, const Array<Fill::ColorStop>& src)
-{
- for (uint32_t i = 0; i < src.count; ++i) {
- dst.push(src.data[i]);
- }
-}
-
-
-static SvgStyleGradient* _cloneGradient(SvgStyleGradient* from)
-{
- if (!from) return nullptr;
-
- auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient)));
- if (!grad) return nullptr;
-
- grad->type = from->type;
- grad->id = from->id ? _copyId(from->id) : nullptr;
- grad->ref = from->ref ? _copyId(from->ref) : nullptr;
- grad->spread = from->spread;
- grad->userSpace = from->userSpace;
-
- if (from->transform) {
- grad->transform = (Matrix*)calloc(1, sizeof(Matrix));
- if (grad->transform) memcpy(grad->transform, from->transform, sizeof(Matrix));
- }
-
- if (grad->type == SvgGradientType::Linear) {
- grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient));
- if (!grad->linear) goto error_grad_alloc;
- memcpy(grad->linear, from->linear, sizeof(SvgLinearGradient));
- } else if (grad->type == SvgGradientType::Radial) {
- grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient));
- if (!grad->radial) goto error_grad_alloc;
- memcpy(grad->radial, from->radial, sizeof(SvgRadialGradient));
- }
-
- _cloneGradStops(grad->stops, from->stops);
-
- return grad;
-
-error_grad_alloc:
- if (grad) {
- grad->clear();
- free(grad);
- }
- return nullptr;
-}
-
-
-static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* parent)
-{
- if (parent == nullptr) return;
- //Inherit the property of parent if not present in child.
- if (!child->curColorSet) {
- child->color = parent->color;
- child->curColorSet = parent->curColorSet;
- }
- //Fill
- if (!((int)child->fill.flags & (int)SvgFillFlags::Paint)) {
- child->fill.paint.color = parent->fill.paint.color;
- child->fill.paint.none = parent->fill.paint.none;
- child->fill.paint.curColor = parent->fill.paint.curColor;
- if (parent->fill.paint.url) {
- if (child->fill.paint.url) free(child->fill.paint.url);
- child->fill.paint.url = _copyId(parent->fill.paint.url);
- }
- }
- if (!((int)child->fill.flags & (int)SvgFillFlags::Opacity)) {
- child->fill.opacity = parent->fill.opacity;
- }
- if (!((int)child->fill.flags & (int)SvgFillFlags::FillRule)) {
- child->fill.fillRule = parent->fill.fillRule;
- }
- //Stroke
- if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Paint)) {
- child->stroke.paint.color = parent->stroke.paint.color;
- child->stroke.paint.none = parent->stroke.paint.none;
- child->stroke.paint.curColor = parent->stroke.paint.curColor;
- if (parent->stroke.paint.url) {
- if (child->stroke.paint.url) free(child->stroke.paint.url);
- child->stroke.paint.url = _copyId(parent->stroke.paint.url);
- } else {
- child->stroke.paint.url = nullptr;
- }
- }
- if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Opacity)) {
- child->stroke.opacity = parent->stroke.opacity;
- }
- if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Width)) {
- child->stroke.width = parent->stroke.width;
- }
- if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Dash)) {
- if (parent->stroke.dash.array.count > 0) {
- child->stroke.dash.array.clear();
- child->stroke.dash.array.reserve(parent->stroke.dash.array.count);
- for (uint32_t i = 0; i < parent->stroke.dash.array.count; ++i) {
- child->stroke.dash.array.push(parent->stroke.dash.array.data[i]);
- }
- }
- }
- if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Cap)) {
- child->stroke.cap = parent->stroke.cap;
- }
- if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Join)) {
- child->stroke.join = parent->stroke.join;
- }
-}
-
-
-static void _styleCopy(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) {
- to->color = from->color;
- to->curColorSet = true;
- }
- //Fill
- to->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)from->fill.flags);
- if (((int)from->fill.flags & (int)SvgFillFlags::Paint)) {
- 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) {
- if (to->fill.paint.url) free(to->fill.paint.url);
- to->fill.paint.url = _copyId(from->fill.paint.url);
- }
- }
- if (((int)from->fill.flags & (int)SvgFillFlags::Opacity)) {
- to->fill.opacity = from->fill.opacity;
- }
- if (((int)from->fill.flags & (int)SvgFillFlags::FillRule)) {
- to->fill.fillRule = from->fill.fillRule;
- }
- //Stroke
- to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)from->stroke.flags);
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Paint)) {
- 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) {
- if (to->stroke.paint.url) free(to->stroke.paint.url);
- to->stroke.paint.url = _copyId(from->stroke.paint.url);
- } else {
- to->stroke.paint.url = nullptr;
- }
- }
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Opacity)) {
- to->stroke.opacity = from->stroke.opacity;
- }
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Width)) {
- to->stroke.width = from->stroke.width;
- }
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Dash)) {
- 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]);
- }
- }
- }
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Cap)) {
- to->stroke.cap = from->stroke.cap;
- }
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Join)) {
- to->stroke.join = from->stroke.join;
- }
-}
-
-
-static void _copyAttr(SvgNode* to, const SvgNode* from)
-{
- //Copy matrix attribute
- if (from->transform) {
- to->transform = (Matrix*)malloc(sizeof(Matrix));
- if (to->transform) *to->transform = *from->transform;
- }
- //Copy style attribute
- _styleCopy(to->style, from->style);
- to->style->flags = (SvgStyleFlags)((int)to->style->flags | (int)from->style->flags);
- if (from->style->clipPath.url) {
- if (to->style->clipPath.url) free(to->style->clipPath.url);
- to->style->clipPath.url = strdup(from->style->clipPath.url);
- }
- if (from->style->mask.url) {
- if (to->style->mask.url) free(to->style->mask.url);
- to->style->mask.url = strdup(from->style->mask.url);
- }
-
- //Copy node attribute
- switch (from->type) {
- case SvgNodeType::Circle: {
- to->node.circle.cx = from->node.circle.cx;
- to->node.circle.cy = from->node.circle.cy;
- to->node.circle.r = from->node.circle.r;
- break;
- }
- case SvgNodeType::Ellipse: {
- to->node.ellipse.cx = from->node.ellipse.cx;
- to->node.ellipse.cy = from->node.ellipse.cy;
- to->node.ellipse.rx = from->node.ellipse.rx;
- to->node.ellipse.ry = from->node.ellipse.ry;
- break;
- }
- case SvgNodeType::Rect: {
- to->node.rect.x = from->node.rect.x;
- to->node.rect.y = from->node.rect.y;
- to->node.rect.w = from->node.rect.w;
- to->node.rect.h = from->node.rect.h;
- to->node.rect.rx = from->node.rect.rx;
- to->node.rect.ry = from->node.rect.ry;
- to->node.rect.hasRx = from->node.rect.hasRx;
- to->node.rect.hasRy = from->node.rect.hasRy;
- break;
- }
- case SvgNodeType::Line: {
- to->node.line.x1 = from->node.line.x1;
- to->node.line.y1 = from->node.line.y1;
- to->node.line.x2 = from->node.line.x2;
- to->node.line.y2 = from->node.line.y2;
- break;
- }
- case SvgNodeType::Path: {
- if (from->node.path.path) {
- if (to->node.path.path) free(to->node.path.path);
- to->node.path.path = strdup(from->node.path.path);
- }
- break;
- }
- case SvgNodeType::Polygon: {
- if ((to->node.polygon.pointsCount = from->node.polygon.pointsCount)) {
- to->node.polygon.points = (float*)malloc(to->node.polygon.pointsCount * sizeof(float));
- memcpy(to->node.polygon.points, from->node.polygon.points, to->node.polygon.pointsCount * sizeof(float));
- }
- break;
- }
- case SvgNodeType::Polyline: {
- if ((to->node.polyline.pointsCount = from->node.polyline.pointsCount)) {
- to->node.polyline.points = (float*)malloc(to->node.polyline.pointsCount * sizeof(float));
- memcpy(to->node.polyline.points, from->node.polyline.points, to->node.polyline.pointsCount * sizeof(float));
- }
- break;
- }
- case SvgNodeType::Image: {
- to->node.image.x = from->node.image.x;
- to->node.image.y = from->node.image.y;
- to->node.image.w = from->node.image.w;
- to->node.image.h = from->node.image.h;
- if (from->node.image.href) {
- if (to->node.image.href) free(to->node.image.href);
- to->node.image.href = strdup(from->node.image.href);
- }
- break;
- }
- default: {
- break;
- }
- }
-}
-
-
-static void _cloneNode(SvgNode* from, SvgNode* parent, int depth)
-{
- /* Exception handling: Prevent invalid SVG data input.
- The size is the arbitrary value, we need an experimental size. */
- if (depth == 8192) {
- TVGERR("SVG", "Infinite recursive call - stopped after %d calls! Svg file may be incorrectly formatted.", depth);
- return;
- }
-
- SvgNode* newNode;
- if (!from || !parent || from == parent) return;
-
- newNode = _createNode(parent, from->type);
- if (!newNode) return;
-
- _styleInherit(newNode->style, parent->style);
- _copyAttr(newNode, from);
-
- auto child = from->child.data;
- for (uint32_t i = 0; i < from->child.count; ++i, ++child) {
- _cloneNode(*child, newNode, depth + 1);
- }
-}
-
-
-static void _clonePostponedNodes(Array<SvgNodeIdPair>* cloneNodes, SvgNode* doc)
-{
- for (uint32_t i = 0; i < cloneNodes->count; ++i) {
- auto nodeIdPair = cloneNodes->data[i];
- auto defs = _getDefsNode(nodeIdPair.node);
- auto nodeFrom = _findNodeById(defs, nodeIdPair.id);
- if (!nodeFrom) nodeFrom = _findNodeById(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);
- }
-}
-
-
static constexpr struct
{
const char* tag;
@@ -2185,6 +1940,7 @@ static constexpr struct
};
+static void _cloneNode(SvgNode* from, SvgNode* parent, int depth);
static bool _attrParseUseNode(void* data, const char* key, const char* value)
{
SvgLoaderData* loader = (SvgLoaderData*)data;
@@ -2379,13 +2135,99 @@ static void _recalcRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial,
}
+static void _recalcInheritedRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+ if (!radial->isCxPercentage) {
+ if (userSpace) radial->cx /= loader->svgParse->global.w;
+ else radial->cx *= loader->svgParse->global.w;
+ }
+}
+
+
+static void _recalcInheritedRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+ if (!radial->isCyPercentage) {
+ if (userSpace) radial->cy /= loader->svgParse->global.h;
+ else radial->cy *= loader->svgParse->global.h;
+ }
+}
+
+
+static void _recalcInheritedRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+ if (!radial->isFxPercentage) {
+ if (userSpace) radial->fx /= loader->svgParse->global.w;
+ else radial->fx *= loader->svgParse->global.w;
+ }
+}
+
+
+static void _recalcInheritedRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+ if (!radial->isFyPercentage) {
+ if (userSpace) radial->fy /= loader->svgParse->global.h;
+ else radial->fy *= loader->svgParse->global.h;
+ }
+}
+
+
+static void _recalcInheritedRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+ if (!radial->isRPercentage) {
+ if (userSpace) radial->r /= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0);
+ else radial->r *= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0);
+ }
+}
+
+
+static void _inheritRadialCxAttr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ to->radial->cx = from->radial->cx;
+ to->radial->isCxPercentage = from->radial->isCxPercentage;
+ to->flags = (to->flags | SvgGradientFlags::Cx);
+}
+
+
+static void _inheritRadialCyAttr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ to->radial->cy = from->radial->cy;
+ to->radial->isCyPercentage = from->radial->isCyPercentage;
+ to->flags = (to->flags | SvgGradientFlags::Cy);
+}
+
+
+static void _inheritRadialFxAttr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ to->radial->fx = from->radial->fx;
+ to->radial->isFxPercentage = from->radial->isFxPercentage;
+ to->flags = (to->flags | SvgGradientFlags::Fx);
+}
+
+
+static void _inheritRadialFyAttr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ to->radial->fy = from->radial->fy;
+ to->radial->isFyPercentage = from->radial->isFyPercentage;
+ to->flags = (to->flags | SvgGradientFlags::Fy);
+}
+
+
+static void _inheritRadialRAttr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ to->radial->r = from->radial->r;
+ to->radial->isRPercentage = from->radial->isRPercentage;
+ to->flags = (to->flags | SvgGradientFlags::R);
+}
+
+
typedef void (*radialMethod)(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value);
+typedef void (*radialInheritMethod)(SvgStyleGradient* to, SvgStyleGradient* from);
typedef void (*radialMethodRecalc)(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace);
-#define RADIAL_DEF(Name, Name1) \
+#define RADIAL_DEF(Name, Name1, Flag) \
{ \
-#Name, sizeof(#Name), _handleRadial##Name1##Attr, _recalcRadial##Name1##Attr \
+#Name, sizeof(#Name), _handleRadial##Name1##Attr, _inheritRadial##Name1##Attr, _recalcRadial##Name1##Attr, _recalcInheritedRadial##Name1##Attr, Flag \
}
@@ -2394,13 +2236,16 @@ static constexpr struct
const char* tag;
int sz;
radialMethod tagHandler;
+ radialInheritMethod tagInheritHandler;
radialMethodRecalc tagRecalc;
+ radialMethodRecalc tagInheritedRecalc;
+ SvgGradientFlags flag;
} radialTags[] = {
- RADIAL_DEF(cx, Cx),
- RADIAL_DEF(cy, Cy),
- RADIAL_DEF(fx, Fx),
- RADIAL_DEF(fy, Fy),
- RADIAL_DEF(r, R)
+ RADIAL_DEF(cx, Cx, SvgGradientFlags::Cx),
+ RADIAL_DEF(cy, Cy, SvgGradientFlags::Cy),
+ RADIAL_DEF(fx, Fx, SvgGradientFlags::Fx),
+ RADIAL_DEF(fy, Fy, SvgGradientFlags::Fy),
+ RADIAL_DEF(r, R, SvgGradientFlags::R)
};
@@ -2414,6 +2259,7 @@ static bool _attrParseRadialGradientNode(void* data, const char* key, const char
for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) {
if (radialTags[i].sz - 1 == sz && !strncmp(radialTags[i].tag, key, sz)) {
radialTags[i].tagHandler(loader, radial, value);
+ grad->flags = (grad->flags | radialTags[i].flag);
return true;
}
}
@@ -2423,11 +2269,13 @@ static bool _attrParseRadialGradientNode(void* data, const char* key, const char
grad->id = _copyId(value);
} else if (!strcmp(key, "spreadMethod")) {
grad->spread = _parseSpreadValue(value);
+ grad->flags = (grad->flags | SvgGradientFlags::SpreadMethod);
} 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")) {
if (!strcmp(value, "userSpaceOnUse")) grad->userSpace = true;
+ grad->flags = (grad->flags | SvgGradientFlags::GradientUnits);
} else if (!strcmp(key, "gradientTransform")) {
grad->transform = _parseTransformationMatrix(value);
} else {
@@ -2443,6 +2291,7 @@ static SvgStyleGradient* _createRadialGradient(SvgLoaderData* loader, const char
auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient)));
loader->svgParse->styleGrad = grad;
+ grad->flags = SvgGradientFlags::None;
grad->type = SvgGradientType::Radial;
grad->userSpace = false;
grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient));
@@ -2485,10 +2334,10 @@ static bool _attrParseStopsStyle(void* data, const char* key, const char* value)
if (!strcmp(key, "stop-opacity")) {
stop->a = _toOpacity(value);
- loader->svgParse->flags = (SvgStopStyleFlags)((int)loader->svgParse->flags | (int)SvgStopStyleFlags::StopOpacity);
+ loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopOpacity);
} else if (!strcmp(key, "stop-color")) {
_toColor(value, &stop->r, &stop->g, &stop->b, nullptr);
- loader->svgParse->flags = (SvgStopStyleFlags)((int)loader->svgParse->flags | (int)SvgStopStyleFlags::StopColor);
+ loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopColor);
} else {
return false;
}
@@ -2505,11 +2354,11 @@ static bool _attrParseStops(void* data, const char* key, const char* value)
if (!strcmp(key, "offset")) {
stop->offset = _toOffset(value);
} else if (!strcmp(key, "stop-opacity")) {
- if (!((int)loader->svgParse->flags & (int)SvgStopStyleFlags::StopOpacity)) {
+ if (!(loader->svgParse->flags & SvgStopStyleFlags::StopOpacity)) {
stop->a = _toOpacity(value);
}
} else if (!strcmp(key, "stop-color")) {
- if (!((int)loader->svgParse->flags & (int)SvgStopStyleFlags::StopColor)) {
+ if (!(loader->svgParse->flags & SvgStopStyleFlags::StopColor)) {
_toColor(value, &stop->r, &stop->g, &stop->b, nullptr);
}
} else if (!strcmp(key, "style")) {
@@ -2570,13 +2419,82 @@ static void _recalcLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear
}
+static void _recalcInheritedLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
+{
+ if (!linear->isX1Percentage) {
+ if (userSpace) linear->x1 /= loader->svgParse->global.w;
+ else linear->x1 *= loader->svgParse->global.w;
+ }
+}
+
+
+static void _recalcInheritedLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
+{
+ if (!linear->isX2Percentage) {
+ if (userSpace) linear->x2 /= loader->svgParse->global.w;
+ else linear->x2 *= loader->svgParse->global.w;
+ }
+}
+
+
+static void _recalcInheritedLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
+{
+ if (!linear->isY1Percentage) {
+ if (userSpace) linear->y1 /= loader->svgParse->global.h;
+ else linear->y1 *= loader->svgParse->global.h;
+ }
+}
+
+
+static void _recalcInheritedLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
+{
+ if (!linear->isY2Percentage) {
+ if (userSpace) linear->y2 /= loader->svgParse->global.h;
+ else linear->y2 *= loader->svgParse->global.h;
+ }
+}
+
+
+static void _inheritLinearX1Attr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ to->linear->x1 = from->linear->x1;
+ to->linear->isX1Percentage = from->linear->isX1Percentage;
+ to->flags = (to->flags | SvgGradientFlags::X1);
+}
+
+
+static void _inheritLinearX2Attr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ to->linear->x2 = from->linear->x2;
+ to->linear->isX2Percentage = from->linear->isX2Percentage;
+ to->flags = (to->flags | SvgGradientFlags::X2);
+}
+
+
+static void _inheritLinearY1Attr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ to->linear->y1 = from->linear->y1;
+ to->linear->isY1Percentage = from->linear->isY1Percentage;
+ to->flags = (to->flags | SvgGradientFlags::Y1);
+}
+
+
+static void _inheritLinearY2Attr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ to->linear->y2 = from->linear->y2;
+ to->linear->isY2Percentage = from->linear->isY2Percentage;
+ to->flags = (to->flags | SvgGradientFlags::Y2);
+}
+
+
typedef void (*Linear_Method)(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value);
+typedef void (*Linear_Inherit_Method)(SvgStyleGradient* to, SvgStyleGradient* from);
typedef void (*Linear_Method_Recalc)(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace);
-#define LINEAR_DEF(Name, Name1) \
+#define LINEAR_DEF(Name, Name1, Flag) \
{ \
-#Name, sizeof(#Name), _handleLinear##Name1##Attr, _recalcLinear##Name1##Attr \
+#Name, sizeof(#Name), _handleLinear##Name1##Attr, _inheritLinear##Name1##Attr, _recalcLinear##Name1##Attr, _recalcInheritedLinear##Name1##Attr, Flag \
}
@@ -2585,12 +2503,15 @@ static constexpr struct
const char* tag;
int sz;
Linear_Method tagHandler;
+ Linear_Inherit_Method tagInheritHandler;
Linear_Method_Recalc tagRecalc;
+ Linear_Method_Recalc tagInheritedRecalc;
+ SvgGradientFlags flag;
} linear_tags[] = {
- LINEAR_DEF(x1, X1),
- LINEAR_DEF(y1, Y1),
- LINEAR_DEF(x2, X2),
- LINEAR_DEF(y2, Y2)
+ LINEAR_DEF(x1, X1, SvgGradientFlags::X1),
+ LINEAR_DEF(y1, Y1, SvgGradientFlags::Y1),
+ LINEAR_DEF(x2, X2, SvgGradientFlags::X2),
+ LINEAR_DEF(y2, Y2, SvgGradientFlags::Y2)
};
@@ -2604,6 +2525,7 @@ static bool _attrParseLinearGradientNode(void* data, const char* key, const char
for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) {
if (linear_tags[i].sz - 1 == sz && !strncmp(linear_tags[i].tag, key, sz)) {
linear_tags[i].tagHandler(loader, linear, value);
+ grad->flags = (grad->flags | linear_tags[i].flag);
return true;
}
}
@@ -2613,11 +2535,13 @@ static bool _attrParseLinearGradientNode(void* data, const char* key, const char
grad->id = _copyId(value);
} else if (!strcmp(key, "spreadMethod")) {
grad->spread = _parseSpreadValue(value);
+ grad->flags = (grad->flags | SvgGradientFlags::SpreadMethod);
} 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")) {
if (!strcmp(value, "userSpaceOnUse")) grad->userSpace = true;
+ grad->flags = (grad->flags | SvgGradientFlags::GradientUnits);
} else if (!strcmp(key, "gradientTransform")) {
grad->transform = _parseTransformationMatrix(value);
} else {
@@ -2633,6 +2557,7 @@ static SvgStyleGradient* _createLinearGradient(SvgLoaderData* loader, const char
auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient)));
loader->svgParse->styleGrad = grad;
+ grad->flags = SvgGradientFlags::None;
grad->type = SvgGradientType::Linear;
grad->userSpace = false;
grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient));
@@ -2692,6 +2617,371 @@ static GradientFactoryMethod _findGradientFactory(const char* name)
}
+static void _cloneGradStops(Array<Fill::ColorStop>& dst, const Array<Fill::ColorStop>& src)
+{
+ for (uint32_t i = 0; i < src.count; ++i) {
+ dst.push(src.data[i]);
+ }
+}
+
+
+static void _inheritGradient(SvgLoaderData* loader, SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ if (!to || !from) return;
+
+ if (!(to->flags & SvgGradientFlags::SpreadMethod) && (from->flags & SvgGradientFlags::SpreadMethod)) {
+ to->spread = from->spread;
+ to->flags = (to->flags | SvgGradientFlags::SpreadMethod);
+ }
+ bool gradUnitSet = (to->flags & SvgGradientFlags::GradientUnits);
+ if (!(to->flags & SvgGradientFlags::GradientUnits) && (from->flags & SvgGradientFlags::GradientUnits)) {
+ to->userSpace = from->userSpace;
+ to->flags = (to->flags | SvgGradientFlags::GradientUnits);
+ }
+
+ if (!to->transform && from->transform) {
+ to->transform = (Matrix*)malloc(sizeof(Matrix));
+ if (to->transform) memcpy(to->transform, from->transform, sizeof(Matrix));
+ }
+
+ if (to->type == SvgGradientType::Linear && from->type == SvgGradientType::Linear) {
+ for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) {
+ bool coordSet = to->flags & linear_tags[i].flag;
+ if (!(to->flags & linear_tags[i].flag) && (from->flags & linear_tags[i].flag)) {
+ linear_tags[i].tagInheritHandler(to, from);
+ }
+
+ //GradUnits not set directly, coord set
+ if (!gradUnitSet && coordSet) {
+ linear_tags[i].tagRecalc(loader, to->linear, to->userSpace);
+ }
+ //GradUnits set, coord not set directly
+ if (to->userSpace == from->userSpace) continue;
+ if (gradUnitSet && !coordSet) {
+ linear_tags[i].tagInheritedRecalc(loader, to->linear, to->userSpace);
+ }
+ }
+ } else if (to->type == SvgGradientType::Radial && from->type == SvgGradientType::Radial) {
+ for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) {
+ bool coordSet = (to->flags & radialTags[i].flag);
+ if (!(to->flags & radialTags[i].flag) && (from->flags & radialTags[i].flag)) {
+ radialTags[i].tagInheritHandler(to, from);
+ }
+
+ //GradUnits not set directly, coord set
+ if (!gradUnitSet && coordSet) {
+ radialTags[i].tagRecalc(loader, to->radial, to->userSpace);
+ }
+ //GradUnits set, coord not set directly
+ if (to->userSpace == from->userSpace) continue;
+ if (gradUnitSet && !coordSet) {
+ radialTags[i].tagInheritedRecalc(loader, to->radial, to->userSpace);
+ }
+ }
+ }
+
+ if (to->stops.count == 0) _cloneGradStops(to->stops, from->stops);
+}
+
+
+static SvgStyleGradient* _cloneGradient(SvgStyleGradient* from)
+{
+ if (!from) return nullptr;
+
+ auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient)));
+ if (!grad) return nullptr;
+
+ grad->type = from->type;
+ grad->id = from->id ? _copyId(from->id) : nullptr;
+ grad->ref = from->ref ? _copyId(from->ref) : nullptr;
+ grad->spread = from->spread;
+ grad->userSpace = from->userSpace;
+ grad->flags = from->flags;
+
+ if (from->transform) {
+ grad->transform = (Matrix*)calloc(1, sizeof(Matrix));
+ if (grad->transform) memcpy(grad->transform, from->transform, sizeof(Matrix));
+ }
+
+ if (grad->type == SvgGradientType::Linear) {
+ grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient));
+ if (!grad->linear) goto error_grad_alloc;
+ memcpy(grad->linear, from->linear, sizeof(SvgLinearGradient));
+ } else if (grad->type == SvgGradientType::Radial) {
+ grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient));
+ if (!grad->radial) goto error_grad_alloc;
+ memcpy(grad->radial, from->radial, sizeof(SvgRadialGradient));
+ }
+
+ _cloneGradStops(grad->stops, from->stops);
+
+ return grad;
+
+ error_grad_alloc:
+ if (grad) {
+ grad->clear();
+ free(grad);
+ }
+ return nullptr;
+}
+
+
+static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* parent)
+{
+ if (parent == nullptr) return;
+ //Inherit the property of parent if not present in child.
+ if (!child->curColorSet) {
+ child->color = parent->color;
+ child->curColorSet = parent->curColorSet;
+ }
+ if (!(child->flags & SvgStyleFlags::PaintOrder)) {
+ child->paintOrder = parent->paintOrder;
+ }
+ //Fill
+ if (!(child->fill.flags & SvgFillFlags::Paint)) {
+ child->fill.paint.color = parent->fill.paint.color;
+ child->fill.paint.none = parent->fill.paint.none;
+ child->fill.paint.curColor = parent->fill.paint.curColor;
+ if (parent->fill.paint.url) {
+ if (child->fill.paint.url) free(child->fill.paint.url);
+ child->fill.paint.url = _copyId(parent->fill.paint.url);
+ }
+ }
+ if (!(child->fill.flags & SvgFillFlags::Opacity)) {
+ child->fill.opacity = parent->fill.opacity;
+ }
+ if (!(child->fill.flags & SvgFillFlags::FillRule)) {
+ child->fill.fillRule = parent->fill.fillRule;
+ }
+ //Stroke
+ if (!(child->stroke.flags & SvgStrokeFlags::Paint)) {
+ child->stroke.paint.color = parent->stroke.paint.color;
+ child->stroke.paint.none = parent->stroke.paint.none;
+ child->stroke.paint.curColor = parent->stroke.paint.curColor;
+ if (parent->stroke.paint.url) {
+ if (child->stroke.paint.url) free(child->stroke.paint.url);
+ child->stroke.paint.url = _copyId(parent->stroke.paint.url);
+ }
+ }
+ if (!(child->stroke.flags & SvgStrokeFlags::Opacity)) {
+ child->stroke.opacity = parent->stroke.opacity;
+ }
+ if (!(child->stroke.flags & SvgStrokeFlags::Width)) {
+ child->stroke.width = parent->stroke.width;
+ }
+ if (!(child->stroke.flags & SvgStrokeFlags::Dash)) {
+ if (parent->stroke.dash.array.count > 0) {
+ child->stroke.dash.array.clear();
+ child->stroke.dash.array.reserve(parent->stroke.dash.array.count);
+ for (uint32_t i = 0; i < parent->stroke.dash.array.count; ++i) {
+ child->stroke.dash.array.push(parent->stroke.dash.array.data[i]);
+ }
+ }
+ }
+ if (!(child->stroke.flags & SvgStrokeFlags::Cap)) {
+ child->stroke.cap = parent->stroke.cap;
+ }
+ if (!(child->stroke.flags & SvgStrokeFlags::Join)) {
+ child->stroke.join = parent->stroke.join;
+ }
+}
+
+
+static void _styleCopy(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) {
+ to->color = from->color;
+ to->curColorSet = true;
+ }
+ if (from->flags & SvgStyleFlags::PaintOrder) {
+ to->paintOrder = from->paintOrder;
+ }
+ //Fill
+ to->fill.flags = (to->fill.flags | from->fill.flags);
+ if (from->fill.flags & SvgFillFlags::Paint) {
+ 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) {
+ if (to->fill.paint.url) free(to->fill.paint.url);
+ to->fill.paint.url = _copyId(from->fill.paint.url);
+ }
+ }
+ if (from->fill.flags & SvgFillFlags::Opacity) {
+ to->fill.opacity = from->fill.opacity;
+ }
+ if (from->fill.flags & SvgFillFlags::FillRule) {
+ to->fill.fillRule = from->fill.fillRule;
+ }
+ //Stroke
+ to->stroke.flags = (to->stroke.flags | from->stroke.flags);
+ if (from->stroke.flags & SvgStrokeFlags::Paint) {
+ 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) {
+ if (to->stroke.paint.url) free(to->stroke.paint.url);
+ to->stroke.paint.url = _copyId(from->stroke.paint.url);
+ }
+ }
+ if (from->stroke.flags & SvgStrokeFlags::Opacity) {
+ to->stroke.opacity = from->stroke.opacity;
+ }
+ if (from->stroke.flags & SvgStrokeFlags::Width) {
+ to->stroke.width = from->stroke.width;
+ }
+ if (from->stroke.flags & SvgStrokeFlags::Dash) {
+ 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]);
+ }
+ }
+ }
+ if (from->stroke.flags & SvgStrokeFlags::Cap) {
+ to->stroke.cap = from->stroke.cap;
+ }
+ if (from->stroke.flags & SvgStrokeFlags::Join) {
+ to->stroke.join = from->stroke.join;
+ }
+}
+
+
+static void _copyAttr(SvgNode* to, const SvgNode* from)
+{
+ //Copy matrix attribute
+ if (from->transform) {
+ to->transform = (Matrix*)malloc(sizeof(Matrix));
+ if (to->transform) *to->transform = *from->transform;
+ }
+ //Copy style attribute
+ _styleCopy(to->style, from->style);
+ to->style->flags = (to->style->flags | from->style->flags);
+ if (from->style->clipPath.url) {
+ if (to->style->clipPath.url) free(to->style->clipPath.url);
+ to->style->clipPath.url = strdup(from->style->clipPath.url);
+ }
+ if (from->style->mask.url) {
+ if (to->style->mask.url) free(to->style->mask.url);
+ to->style->mask.url = strdup(from->style->mask.url);
+ }
+
+ //Copy node attribute
+ switch (from->type) {
+ case SvgNodeType::Circle: {
+ to->node.circle.cx = from->node.circle.cx;
+ to->node.circle.cy = from->node.circle.cy;
+ to->node.circle.r = from->node.circle.r;
+ break;
+ }
+ case SvgNodeType::Ellipse: {
+ to->node.ellipse.cx = from->node.ellipse.cx;
+ to->node.ellipse.cy = from->node.ellipse.cy;
+ to->node.ellipse.rx = from->node.ellipse.rx;
+ to->node.ellipse.ry = from->node.ellipse.ry;
+ break;
+ }
+ case SvgNodeType::Rect: {
+ to->node.rect.x = from->node.rect.x;
+ to->node.rect.y = from->node.rect.y;
+ to->node.rect.w = from->node.rect.w;
+ to->node.rect.h = from->node.rect.h;
+ to->node.rect.rx = from->node.rect.rx;
+ to->node.rect.ry = from->node.rect.ry;
+ to->node.rect.hasRx = from->node.rect.hasRx;
+ to->node.rect.hasRy = from->node.rect.hasRy;
+ break;
+ }
+ case SvgNodeType::Line: {
+ to->node.line.x1 = from->node.line.x1;
+ to->node.line.y1 = from->node.line.y1;
+ to->node.line.x2 = from->node.line.x2;
+ to->node.line.y2 = from->node.line.y2;
+ break;
+ }
+ case SvgNodeType::Path: {
+ if (from->node.path.path) {
+ if (to->node.path.path) free(to->node.path.path);
+ to->node.path.path = strdup(from->node.path.path);
+ }
+ break;
+ }
+ case SvgNodeType::Polygon: {
+ if ((to->node.polygon.pointsCount = from->node.polygon.pointsCount)) {
+ to->node.polygon.points = (float*)malloc(to->node.polygon.pointsCount * sizeof(float));
+ memcpy(to->node.polygon.points, from->node.polygon.points, to->node.polygon.pointsCount * sizeof(float));
+ }
+ break;
+ }
+ case SvgNodeType::Polyline: {
+ if ((to->node.polyline.pointsCount = from->node.polyline.pointsCount)) {
+ to->node.polyline.points = (float*)malloc(to->node.polyline.pointsCount * sizeof(float));
+ memcpy(to->node.polyline.points, from->node.polyline.points, to->node.polyline.pointsCount * sizeof(float));
+ }
+ break;
+ }
+ case SvgNodeType::Image: {
+ to->node.image.x = from->node.image.x;
+ to->node.image.y = from->node.image.y;
+ to->node.image.w = from->node.image.w;
+ to->node.image.h = from->node.image.h;
+ if (from->node.image.href) {
+ if (to->node.image.href) free(to->node.image.href);
+ to->node.image.href = strdup(from->node.image.href);
+ }
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+}
+
+
+static void _cloneNode(SvgNode* from, SvgNode* parent, int depth)
+{
+ /* Exception handling: Prevent invalid SVG data input.
+ The size is the arbitrary value, we need an experimental size. */
+ if (depth == 8192) {
+ TVGERR("SVG", "Infinite recursive call - stopped after %d calls! Svg file may be incorrectly formatted.", depth);
+ return;
+ }
+
+ SvgNode* newNode;
+ if (!from || !parent || from == parent) return;
+
+ newNode = _createNode(parent, from->type);
+ if (!newNode) return;
+
+ _styleInherit(newNode->style, parent->style);
+ _copyAttr(newNode, from);
+
+ auto child = from->child.data;
+ for (uint32_t i = 0; i < from->child.count; ++i, ++child) {
+ _cloneNode(*child, newNode, depth + 1);
+ }
+}
+
+
+static void _clonePostponedNodes(Array<SvgNodeIdPair>* cloneNodes, SvgNode* doc)
+{
+ for (uint32_t i = 0; i < cloneNodes->count; ++i) {
+ auto nodeIdPair = cloneNodes->data[i];
+ auto defs = _getDefsNode(nodeIdPair.node);
+ auto nodeFrom = _findNodeById(defs, nodeIdPair.id);
+ if (!nodeFrom) nodeFrom = _findNodeById(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);
+ }
+}
+
+
static constexpr struct
{
const char* tag;
@@ -2738,6 +3028,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
//Parse the empty tag
attrs = content;
while ((attrs != nullptr) && *attrs != '>') attrs++;
+ if (empty) attrs--;
}
if (attrs) {
@@ -2942,7 +3233,7 @@ static void _updateStyle(SvgNode* node, SvgStyleProperty* parentStyle)
}
-static SvgStyleGradient* _gradientDup(Array<SvgStyleGradient*>* gradients, const char* id)
+static SvgStyleGradient* _gradientDup(SvgLoaderData* loader, Array<SvgStyleGradient*>* gradients, const char* id)
{
SvgStyleGradient* result = nullptr;
@@ -2960,8 +3251,7 @@ static SvgStyleGradient* _gradientDup(Array<SvgStyleGradient*>* gradients, const
gradList = gradients->data;
for (uint32_t i = 0; i < gradients->count; ++i) {
if ((*gradList)->id && !strcmp((*gradList)->id, result->ref)) {
- if (result->stops.count == 0) _cloneGradStops(result->stops, (*gradList)->stops);
- //TODO: Properly inherit other property
+ _inheritGradient(loader, result, *gradList);
break;
}
++gradList;
@@ -2972,27 +3262,33 @@ static SvgStyleGradient* _gradientDup(Array<SvgStyleGradient*>* gradients, const
}
-static void _updateGradient(SvgNode* node, Array<SvgStyleGradient*>* gradients)
+static void _updateGradient(SvgLoaderData* loader, SvgNode* node, Array<SvgStyleGradient*>* gradients)
{
if (node->child.count > 0) {
auto child = node->child.data;
for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
- _updateGradient(*child, gradients);
+ _updateGradient(loader, *child, gradients);
}
} else {
if (node->style->fill.paint.url) {
- if (node->style->fill.paint.gradient) {
- node->style->fill.paint.gradient->clear();
- free(node->style->fill.paint.gradient);
+ auto newGrad = _gradientDup(loader, gradients, node->style->fill.paint.url);
+ if (newGrad) {
+ if (node->style->fill.paint.gradient) {
+ node->style->fill.paint.gradient->clear();
+ free(node->style->fill.paint.gradient);
+ }
+ node->style->fill.paint.gradient = newGrad;
}
- node->style->fill.paint.gradient = _gradientDup(gradients, node->style->fill.paint.url);
}
if (node->style->stroke.paint.url) {
- if (node->style->stroke.paint.gradient) {
- node->style->stroke.paint.gradient->clear();
- free(node->style->stroke.paint.gradient);
+ auto newGrad = _gradientDup(loader, gradients, node->style->stroke.paint.url);
+ if (newGrad) {
+ if (node->style->stroke.paint.gradient) {
+ node->style->stroke.paint.gradient->clear();
+ free(node->style->stroke.paint.gradient);
+ }
+ node->style->stroke.paint.gradient = newGrad;
}
- node->style->stroke.paint.gradient = _gradientDup(gradients, node->style->stroke.paint.url);
}
}
}
@@ -3181,7 +3477,8 @@ SvgLoader::~SvgLoader()
void SvgLoader::run(unsigned tid)
{
//According to the SVG standard the value of the width/height of the viewbox set to 0 disables rendering
- if (renderingDisabled) {
+ if ((viewFlag & SvgViewFlag::Viewbox) && (fabsf(vw) <= FLT_EPSILON || fabsf(vh) <= FLT_EPSILON)) {
+ TVGLOG("SVG", "The <viewBox> width and/or height set to 0 - rendering disabled.");
root = Scene::gen();
return;
}
@@ -3199,12 +3496,12 @@ void SvgLoader::run(unsigned tid)
_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);
+
+ if (loaderData.gradients.count > 0) _updateGradient(&loaderData, loaderData.doc, &loaderData.gradients);
+ if (defs) _updateGradient(&loaderData, loaderData.doc, &defs->node.defs.gradients);
}
- root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh, w, h, align, meetOrSlice, svgPath);
+ root = svgSceneBuild(loaderData, {vx, vy, vw, vh}, w, h, align, meetOrSlice, svgPath, viewFlag);
}
@@ -3217,34 +3514,78 @@ bool SvgLoader::header()
if (!loaderData.svgParse) return false;
loaderData.svgParse->flags = SvgStopStyleFlags::StopDefault;
+ viewFlag = SvgViewFlag::None;
simpleXmlParse(content, size, true, _svgLoaderParserForValidCheck, &(loaderData));
if (loaderData.doc && loaderData.doc->type == SvgNodeType::Doc) {
- //Return the brief resource info such as viewbox:
- vx = loaderData.doc->node.doc.vx;
- vy = loaderData.doc->node.doc.vy;
- w = vw = loaderData.doc->node.doc.vw;
- h = vh = loaderData.doc->node.doc.vh;
-
- //Override size
- if (loaderData.doc->node.doc.w > 0) {
+ viewFlag = loaderData.doc->node.doc.viewFlag;
+ align = loaderData.doc->node.doc.align;
+ meetOrSlice = loaderData.doc->node.doc.meetOrSlice;
+
+ if (viewFlag & SvgViewFlag::Viewbox) {
+ vx = loaderData.doc->node.doc.vx;
+ vy = loaderData.doc->node.doc.vy;
+ vw = loaderData.doc->node.doc.vw;
+ vh = loaderData.doc->node.doc.vh;
+
+ if (viewFlag & SvgViewFlag::Width) w = loaderData.doc->node.doc.w;
+ else {
+ w = loaderData.doc->node.doc.vw;
+ if (viewFlag & SvgViewFlag::WidthInPercent) {
+ w *= loaderData.doc->node.doc.w;
+ viewFlag = (viewFlag ^ SvgViewFlag::WidthInPercent);
+ }
+ viewFlag = (viewFlag | SvgViewFlag::Width);
+ }
+ if (viewFlag & SvgViewFlag::Height) h = loaderData.doc->node.doc.h;
+ else {
+ h = loaderData.doc->node.doc.vh;
+ if (viewFlag & SvgViewFlag::HeightInPercent) {
+ h *= loaderData.doc->node.doc.h;
+ viewFlag = (viewFlag ^ SvgViewFlag::HeightInPercent);
+ }
+ viewFlag = (viewFlag | SvgViewFlag::Height);
+ }
+ //In case no viewbox and width/height data is provided the completion of loading
+ //has to be forced, in order to establish this data based on the whole picture.
+ } else {
+ //Before loading, set default viewbox & size if they are empty
+ vx = vy = 0.0f;
+ if (viewFlag & SvgViewFlag::Width) {
+ vw = w = loaderData.doc->node.doc.w;
+ } else {
+ vw = 1.0f;
+ if (viewFlag & SvgViewFlag::WidthInPercent) {
+ w = loaderData.doc->node.doc.w;
+ } else w = 1.0f;
+ }
+
+ if (viewFlag & SvgViewFlag::Height) {
+ vh = h = loaderData.doc->node.doc.h;
+ } else {
+ vh = 1.0f;
+ if (viewFlag & SvgViewFlag::HeightInPercent) {
+ h = loaderData.doc->node.doc.h;
+ } else h = 1.0f;
+ }
+
+ run(0);
+
+ //Override viewbox & size again after svg loading.
+ vx = loaderData.doc->node.doc.vx;
+ vy = loaderData.doc->node.doc.vy;
+ vw = loaderData.doc->node.doc.vw;
+ vh = loaderData.doc->node.doc.vh;
w = loaderData.doc->node.doc.w;
- if (vw < FLT_EPSILON) vw = w;
- }
- if (loaderData.doc->node.doc.h > 0) {
h = loaderData.doc->node.doc.h;
- if (vh < FLT_EPSILON) vh = h;
}
- align = loaderData.doc->node.doc.align;
- meetOrSlice = loaderData.doc->node.doc.meetOrSlice;
- } else {
- TVGLOG("SVG", "No SVG File. There is no <svg/>");
- return false;
+ return true;
}
- return true;
+ TVGLOG("SVG", "No SVG File. There is no <svg/>");
+ return false;
}
@@ -3304,6 +3645,9 @@ bool SvgLoader::read()
{
if (!content || size == 0) return false;
+ //the loading has been already completed in header()
+ if (root) return true;
+
TaskScheduler::request(this);
return true;
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h
index c6fdde55af..5c74184ec8 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. 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
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#ifndef _TVG_SVG_LOADER_H_
#define _TVG_SVG_LOADER_H_
@@ -50,9 +51,9 @@ public:
unique_ptr<Paint> paint() override;
private:
+ SvgViewFlag viewFlag = SvgViewFlag::None;
AspectRatioAlign align = AspectRatioAlign::XMidYMid;
AspectRatioMeetOrSlice meetOrSlice = AspectRatioMeetOrSlice::Meet;
- bool renderingDisabled = false;
bool header();
void clear();
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
index 3588cabf0b..dec9fadebe 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. 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
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#ifndef _TVG_SVG_LOADER_COMMON_H_
#define _TVG_SVG_LOADER_COMMON_H_
@@ -79,6 +80,16 @@ enum class SvgFillFlags
ClipPath = 0x16
};
+constexpr bool operator &(SvgFillFlags a, SvgFillFlags b)
+{
+ return int(a) & int(b);
+}
+
+constexpr SvgFillFlags operator |(SvgFillFlags a, SvgFillFlags b)
+{
+ return SvgFillFlags(int(a) | int(b));
+}
+
enum class SvgStrokeFlags
{
Paint = 0x1,
@@ -91,6 +102,17 @@ enum class SvgStrokeFlags
Dash = 0x80,
};
+constexpr bool operator &(SvgStrokeFlags a, SvgStrokeFlags b)
+{
+ return int(a) & int(b);
+}
+
+constexpr SvgStrokeFlags operator |(SvgStrokeFlags a, SvgStrokeFlags b)
+{
+ return SvgStrokeFlags(int(a) | int(b));
+}
+
+
enum class SvgGradientType
{
Linear,
@@ -114,9 +136,20 @@ enum class SvgStyleFlags
ClipPath = 0x1000,
Mask = 0x2000,
MaskType = 0x4000,
- Display = 0x8000
+ Display = 0x8000,
+ PaintOrder = 0x10000
};
+constexpr bool operator &(SvgStyleFlags a, SvgStyleFlags b)
+{
+ return int(a) & int(b);
+}
+
+constexpr SvgStyleFlags operator |(SvgStyleFlags a, SvgStyleFlags b)
+{
+ return SvgStyleFlags(int(a) | int(b));
+}
+
enum class SvgStopStyleFlags
{
StopDefault = 0x0,
@@ -124,6 +157,42 @@ enum class SvgStopStyleFlags
StopColor = 0x02
};
+constexpr bool operator &(SvgStopStyleFlags a, SvgStopStyleFlags b)
+{
+ return int(a) & int(b);
+}
+
+constexpr SvgStopStyleFlags operator |(SvgStopStyleFlags a, SvgStopStyleFlags b)
+{
+ return SvgStopStyleFlags(int(a) | int(b));
+}
+
+enum class SvgGradientFlags
+{
+ None = 0x0,
+ GradientUnits = 0x1,
+ SpreadMethod = 0x2,
+ X1 = 0x4,
+ X2 = 0x8,
+ Y1 = 0x10,
+ Y2 = 0x20,
+ Cx = 0x40,
+ Cy = 0x80,
+ R = 0x100,
+ Fx = 0x200,
+ Fy = 0x400
+};
+
+constexpr bool operator &(SvgGradientFlags a, SvgGradientFlags b)
+{
+ return int(a) & int(b);
+}
+
+constexpr SvgGradientFlags operator |(SvgGradientFlags a, SvgGradientFlags b)
+{
+ return SvgGradientFlags(int(a) | int(b));
+}
+
enum class SvgFillRule
{
Winding = 0,
@@ -145,6 +214,31 @@ enum class SvgParserLengthType
Other
};
+enum class SvgViewFlag
+{
+ None = 0x0,
+ Width = 0x01, //viewPort width
+ Height = 0x02, //viewPort height
+ Viewbox = 0x04, //viewBox x,y,w,h - used only if all 4 are correctly set
+ WidthInPercent = 0x08,
+ HeightInPercent = 0x10
+};
+
+constexpr bool operator &(SvgViewFlag a, SvgViewFlag b)
+{
+ return static_cast<int>(a) & static_cast<int>(b);
+}
+
+constexpr SvgViewFlag operator |(SvgViewFlag a, SvgViewFlag b)
+{
+ return SvgViewFlag(int(a) | int(b));
+}
+
+constexpr SvgViewFlag operator ^(SvgViewFlag a, SvgViewFlag b)
+{
+ return SvgViewFlag(int(a) ^ int(b));
+}
+
enum class AspectRatioAlign
{
None,
@@ -167,12 +261,13 @@ enum class AspectRatioMeetOrSlice
struct SvgDocNode
{
- float w;
- float h;
+ float w; //unit: point or in percentage see: SvgViewFlag
+ float h; //unit: point or in percentage see: SvgViewFlag
float vx;
float vy;
float vw;
float vh;
+ SvgViewFlag viewFlag;
SvgNode* defs;
SvgNode* style;
AspectRatioAlign align;
@@ -339,6 +434,7 @@ struct SvgStyleGradient
SvgLinearGradient* linear;
Matrix* transform;
Array<Fill::ColorStop> stops;
+ SvgGradientFlags flags;
bool userSpace;
void clear()
@@ -384,6 +480,7 @@ struct SvgStyleProperty
SvgColor color;
bool curColorSet;
char* cssClass;
+ bool paintOrder; //true if default (fill, stroke), false otherwise
SvgStyleFlags flags;
};
@@ -456,6 +553,11 @@ struct SvgLoaderData
bool style = false;
};
+struct Box
+{
+ float x, y, w, h;
+};
+
/*
* https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtof-strtod-l-wcstod-wcstod-l?view=vs-2017
*
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
index a09a2797d0..e044931b51 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. 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
@@ -52,7 +52,6 @@
#include <cstring>
#include <math.h>
-#include <clocale>
#include <ctype.h>
#include "tvgSvgLoaderCommon.h"
#include "tvgSvgPath.h"
@@ -545,11 +544,6 @@ bool svgPathToTvgPath(const char* svgPath, Array<PathCommand>& cmds, Array<Point
char cmd = 0;
bool isQuadratic = false;
char* path = (char*)svgPath;
- char* curLocale;
-
- curLocale = setlocale(LC_NUMERIC, NULL);
- if (curLocale) curLocale = strdup(curLocale);
- setlocale(LC_NUMERIC, "POSIX");
while ((path[0] != '\0')) {
path = _nextCommand(path, &cmd, numberArray, &numberCount);
@@ -557,8 +551,5 @@ bool svgPathToTvgPath(const char* svgPath, Array<PathCommand>& cmds, Array<Point
if (!_processCommand(&cmds, &pts, cmd, numberArray, numberCount, &cur, &curCtl, &startPoint, &isQuadratic)) break;
}
- setlocale(LC_NUMERIC, curLocale);
- if (curLocale) free(curLocale);
-
return true;
}
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h
index 8f5f9035dc..4199088dc1 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. 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 254ee2d008..c5e5df893e 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. 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
@@ -61,12 +61,6 @@
/* Internal Class Implementation */
/************************************************************************/
-struct Box
-{
- float x, y, w, h;
-};
-
-
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, int depth, bool* isMaskWhite = nullptr);
@@ -255,7 +249,6 @@ static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox
node->style->clipPath.applying = true;
auto comp = Shape::gen();
- if (node->transform) comp->transform(*node->transform);
auto child = compNode->child.data;
auto valid = false; //Composite only when valid shapes are existed
@@ -264,6 +257,12 @@ static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox
if (_appendChildShape(*child, comp.get(), vBox, svgPath)) valid = true;
}
+ if (node->transform) {
+ auto m = comp->transform();
+ m = mathMultiply(node->transform, &m);
+ comp->transform(m);
+ }
+
if (valid) paint->composite(move(comp), CompositeMethod::ClipPath);
node->style->clipPath.applying = false;
@@ -331,6 +330,8 @@ static void _applyProperty(SvgNode* node, Shape* vg, const Box& vBox, const stri
//Apply the fill rule
vg->fill((tvg::FillRule)style->fill.fillRule);
+ //Rendering order
+ vg->order(!style->paintOrder);
//Apply node opacity
if (style->opacity < 255) vg->opacity(style->opacity);
@@ -444,9 +445,11 @@ enum class imageMimeTypeEncoding
base64 = 0x1,
utf8 = 0x2
};
+
constexpr imageMimeTypeEncoding operator|(imageMimeTypeEncoding a, imageMimeTypeEncoding b) {
return static_cast<imageMimeTypeEncoding>(static_cast<int>(a) | static_cast<int>(b));
}
+
constexpr bool operator&(imageMimeTypeEncoding a, imageMimeTypeEncoding b) {
return (static_cast<int>(a) & static_cast<int>(b));
}
@@ -755,24 +758,45 @@ static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox,
}
+static void _updateInvalidViewSize(const Scene* scene, Box& vBox, float& w, float& h, SvgViewFlag viewFlag)
+{
+ bool validWidth = (viewFlag & SvgViewFlag::Width);
+ bool validHeight = (viewFlag & SvgViewFlag::Height);
+
+ float x, y;
+ scene->bounds(&x, &y, &vBox.w, &vBox.h, false);
+ if (!validWidth && !validHeight) {
+ vBox.x = x;
+ vBox.y = y;
+ } else {
+ if (validWidth) vBox.w = w;
+ if (validHeight) vBox.h = h;
+ }
+
+ //the size would have 1x1 or percentage values.
+ if (!validWidth) w *= vBox.w;
+ if (!validHeight) h *= vBox.h;
+}
+
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
-unique_ptr<Scene> svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath)
+unique_ptr<Scene> svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath, SvgViewFlag viewFlag)
{
//TODO: aspect ratio is valid only if viewBox was set
- if (!node || (node->type != SvgNodeType::Doc)) return nullptr;
+ if (!loaderData.doc || (loaderData.doc->type != SvgNodeType::Doc)) return nullptr;
- Box vBox = {vx, vy, vw, vh};
- auto docNode = _sceneBuildHelper(node, vBox, svgPath, false, 0);
+ auto docNode = _sceneBuildHelper(loaderData.doc, vBox, svgPath, false, 0);
- if (!mathEqual(w, vw) || !mathEqual(h, vh)) {
+ if (!(viewFlag & SvgViewFlag::Viewbox)) _updateInvalidViewSize(docNode.get(), vBox, w, h, viewFlag);
+
+ if (!mathEqual(w, vBox.w) || !mathEqual(h, vBox.h)) {
Matrix m = _calculateAspectRatioMatrix(align, meetOrSlice, w, h, vBox);
docNode->transform(m);
- } else if (!mathZero(vx) || !mathZero(vy)) {
- docNode->translate(-vx, -vy);
+ } else if (!mathZero(vBox.x) || !mathZero(vBox.y)) {
+ docNode->translate(-vBox.x, -vBox.y);
}
auto viewBoxClip = Shape::gen();
@@ -786,5 +810,12 @@ unique_ptr<Scene> svgSceneBuild(SvgNode* node, float vx, float vy, float vw, flo
auto root = Scene::gen();
root->push(move(compositeLayer));
+ loaderData.doc->node.doc.vx = vBox.x;
+ loaderData.doc->node.doc.vy = vBox.y;
+ loaderData.doc->node.doc.vw = vBox.w;
+ loaderData.doc->node.doc.vh = vBox.h;
+ loaderData.doc->node.doc.w = w;
+ loaderData.doc->node.doc.h = h;
+
return root;
}
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h
index 311f3c80e6..f6a60f850d 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. 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
@@ -25,6 +25,6 @@
#include "tvgCommon.h"
-unique_ptr<Scene> svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath);
+unique_ptr<Scene> svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath, SvgViewFlag viewFlag);
#endif //_TVG_SVG_SCENE_BUILDER_H_
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
index 7fb108bc21..12d20c9731 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. 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
@@ -94,8 +94,8 @@ float svgUtilStrtof(const char *nPtr, char **endPtr)
if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'f')) iter += 3;
else goto error;
- if (tolower(*(iter + 3)) == 'i') {
- if ((tolower(*(iter + 4)) == 'n') && (tolower(*(iter + 5)) == 'i') && (tolower(*(iter + 6)) == 't') && (tolower(*(iter + 7)) == 'y')) iter += 5;
+ if (tolower(*(iter)) == 'i') {
+ if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'i') && (tolower(*(iter + 3)) == 't') && (tolower(*(iter + 4)) == 'y')) iter += 5;
else goto error;
}
if (endPtr) *endPtr = (char *)(iter);
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h
index b5e6e1bdb2..6f94367aeb 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. 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 0e2c3fa141..77467e071b 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. 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.h b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h
index e2761ca8da..7333bb09fb 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h
+++ b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. 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