2016-10-12 02:39:10 -07:00
|
|
|
|
2016-11-02 03:25:45 -07:00
|
|
|
#include "vg_common.h"
|
2016-10-12 02:39:10 -07:00
|
|
|
|
|
|
|
static int _evas_vg_loader_svg_log_dom = -1;
|
|
|
|
|
|
|
|
#ifdef ERR
|
|
|
|
# undef ERR
|
|
|
|
#endif
|
|
|
|
#define ERR(...) EINA_LOG_DOM_ERR(_evas_vg_loader_svg_log_dom, __VA_ARGS__)
|
|
|
|
|
|
|
|
#ifdef INF
|
|
|
|
# undef INF
|
|
|
|
#endif
|
|
|
|
#define INF(...) EINA_LOG_DOM_INFO(_evas_vg_loader_svg_log_dom, __VA_ARGS__)
|
|
|
|
|
2017-11-06 18:41:36 -08:00
|
|
|
/* Global struct for working global cases during the parse */
|
|
|
|
typedef struct _Evas_SVG_Parser Evas_SVG_Parser;
|
|
|
|
struct _Evas_SVG_Parser {
|
|
|
|
struct {
|
|
|
|
int x, y, width, height;
|
|
|
|
} global;
|
|
|
|
struct {
|
|
|
|
Eina_Bool fx_parsed;
|
|
|
|
Eina_Bool fy_parsed;
|
|
|
|
} gradient;
|
2016-11-02 03:25:45 -07:00
|
|
|
|
2017-11-06 18:41:36 -08:00
|
|
|
Svg_Node *node;
|
|
|
|
Svg_Style_Gradient *style_grad;
|
|
|
|
Efl_Gfx_Gradient_Stop *grad_stop;
|
|
|
|
};
|
2016-11-02 03:25:45 -07:00
|
|
|
|
|
|
|
typedef struct _Evas_SVG_Loader Evas_SVG_Loader;
|
|
|
|
struct _Evas_SVG_Loader
|
|
|
|
{
|
|
|
|
Eina_Array *stack;
|
|
|
|
Svg_Node *doc;
|
|
|
|
Svg_Node *def;
|
2019-07-15 05:23:38 -07:00
|
|
|
Eina_List *gradients;
|
|
|
|
Svg_Style_Gradient *latest_gradient; //for stops
|
2017-11-06 18:41:36 -08:00
|
|
|
Evas_SVG_Parser *svg_parse;
|
2016-11-02 03:25:45 -07:00
|
|
|
int level;
|
|
|
|
Eina_Bool result:1;
|
|
|
|
};
|
|
|
|
|
2017-11-06 18:41:36 -08:00
|
|
|
|
|
|
|
typedef Svg_Node *(*Factory_Method)(Evas_SVG_Loader *loader, Svg_Node *parent, const char *buf, unsigned buflen);
|
|
|
|
|
|
|
|
typedef Svg_Style_Gradient *(*Gradient_Factory_Method)(Evas_SVG_Loader *loader, const char *buf, unsigned buflen);
|
|
|
|
|
2017-02-17 03:00:54 -08:00
|
|
|
/* length type to recalculate %, pt, pc, mm, cm etc*/
|
|
|
|
typedef enum {
|
|
|
|
SVG_PARSER_LENGTH_VERTICAL,
|
|
|
|
SVG_PARSER_LENGTH_HORIZONTAL,
|
|
|
|
/* in case of, for example, radius of radial gradient */
|
|
|
|
SVG_PARSER_LENGTH_OTHER
|
|
|
|
} SVG_Parser_Length_Type;
|
|
|
|
|
2016-11-02 03:25:45 -07:00
|
|
|
char *
|
|
|
|
_skip_space(const char *str, const char *end)
|
|
|
|
{
|
|
|
|
while (((end != NULL && str < end) || (end == NULL && *str != '\0')) && isspace(*str))
|
|
|
|
++str;
|
|
|
|
return (char *)str;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline Eina_Stringshare *
|
|
|
|
_copy_id(const char* str)
|
|
|
|
{
|
|
|
|
if (str == NULL) return NULL;
|
|
|
|
|
|
|
|
return eina_stringshare_add(str);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
_skipcomma(const char *content)
|
|
|
|
{
|
|
|
|
content = _skip_space(content, NULL);
|
|
|
|
if (*content == ',') return content + 1;
|
|
|
|
return content;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline Eina_Bool
|
|
|
|
_parse_number(const char **content, double *number)
|
|
|
|
{
|
|
|
|
char *end = NULL;
|
|
|
|
|
2019-09-17 05:18:47 -07:00
|
|
|
*number = eina_convert_strtod_c(*content, &end);
|
2016-11-02 03:25:45 -07:00
|
|
|
// if the start of string is not number
|
|
|
|
if ((*content) == end) return EINA_FALSE;
|
|
|
|
//skip comma if any
|
|
|
|
*content = _skipcomma(end);
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
2017-02-17 03:00:54 -08:00
|
|
|
/**
|
|
|
|
* According to https://www.w3.org/TR/SVG/coords.html#Units
|
|
|
|
*
|
|
|
|
* TODO
|
|
|
|
* Since this documentation is not obvious, more clean recalculation with dpi
|
|
|
|
* is required, but for now default w3 constants would be used
|
|
|
|
*/
|
2016-11-02 03:25:45 -07:00
|
|
|
static inline double
|
2017-11-06 18:41:36 -08:00
|
|
|
_to_double(Evas_SVG_Parser *svg_parse, const char *str, SVG_Parser_Length_Type type)
|
2017-02-17 03:00:54 -08:00
|
|
|
{
|
2019-09-17 05:18:47 -07:00
|
|
|
double parsed_value = eina_convert_strtod_c(str, NULL);
|
2017-02-17 03:00:54 -08:00
|
|
|
|
|
|
|
if (strstr(str, "cm"))
|
|
|
|
parsed_value = parsed_value * 35.43307;
|
|
|
|
else if (strstr(str, "mm"))
|
|
|
|
parsed_value = parsed_value * 3.543307;
|
|
|
|
else if (strstr(str, "pt"))
|
|
|
|
parsed_value = parsed_value * 1.25;
|
|
|
|
else if (strstr(str, "pc"))
|
|
|
|
parsed_value = parsed_value * 15;
|
|
|
|
else if (strstr(str, "in"))
|
|
|
|
parsed_value = parsed_value * 90;
|
|
|
|
else if (strstr(str, "%"))
|
|
|
|
{
|
|
|
|
if (type == SVG_PARSER_LENGTH_VERTICAL)
|
2017-11-06 18:41:36 -08:00
|
|
|
parsed_value = (parsed_value / 100.0) * svg_parse->global.height;
|
2017-02-17 03:00:54 -08:00
|
|
|
else if (type == SVG_PARSER_LENGTH_HORIZONTAL)
|
2017-11-06 18:41:36 -08:00
|
|
|
parsed_value = (parsed_value / 100.0) * svg_parse->global.width;
|
2017-02-17 03:00:54 -08:00
|
|
|
else // if other then it's radius
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
double max = svg_parse->global.width;
|
|
|
|
if (max < svg_parse->global.height) max = svg_parse->global.height;
|
2017-02-17 03:00:54 -08:00
|
|
|
parsed_value = (parsed_value / 100.0) * max;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//TODO: implement 'em', 'ex' attributes
|
|
|
|
|
|
|
|
return parsed_value;
|
|
|
|
}
|
|
|
|
|
2017-02-17 07:00:01 -08:00
|
|
|
/**
|
|
|
|
* Turn gradient variables into percentages
|
|
|
|
*/
|
|
|
|
static inline double
|
2017-11-06 18:41:36 -08:00
|
|
|
_gradient_to_double(Evas_SVG_Parser *svg_parse, const char *str, SVG_Parser_Length_Type type)
|
2017-02-17 07:00:01 -08:00
|
|
|
{
|
|
|
|
char *end = NULL;
|
|
|
|
|
2019-09-17 05:18:47 -07:00
|
|
|
double parsed_value = eina_convert_strtod_c(str, &end);
|
2017-02-17 07:00:01 -08:00
|
|
|
double max = 1;
|
|
|
|
|
2017-02-22 03:02:31 -08:00
|
|
|
/**
|
|
|
|
* That is according to Units in here
|
|
|
|
*
|
|
|
|
* https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
|
|
|
|
*/
|
2017-02-17 07:00:01 -08:00
|
|
|
if (type == SVG_PARSER_LENGTH_VERTICAL)
|
2017-11-06 18:41:36 -08:00
|
|
|
max = svg_parse->global.height;
|
2017-02-17 07:00:01 -08:00
|
|
|
else if (type == SVG_PARSER_LENGTH_HORIZONTAL)
|
2017-11-06 18:41:36 -08:00
|
|
|
max = svg_parse->global.width;
|
2017-02-22 03:02:31 -08:00
|
|
|
else if (type == SVG_PARSER_LENGTH_OTHER)
|
2017-11-06 18:41:36 -08:00
|
|
|
max = sqrt(pow(svg_parse->global.height, 2) +
|
|
|
|
pow(svg_parse->global.width, 2)) / sqrt(2.0);
|
2017-02-17 07:00:01 -08:00
|
|
|
|
2019-04-10 01:39:36 -07:00
|
|
|
if (strstr(str, "%"))
|
|
|
|
parsed_value = parsed_value / 100.0;
|
|
|
|
else if (strstr(str, "cm"))
|
2017-02-17 07:00:01 -08:00
|
|
|
parsed_value = parsed_value * 35.43307;
|
|
|
|
else if (strstr(str, "mm"))
|
|
|
|
parsed_value = parsed_value * 3.543307;
|
|
|
|
else if (strstr(str, "pt"))
|
|
|
|
parsed_value = parsed_value * 1.25;
|
|
|
|
else if (strstr(str, "pc"))
|
|
|
|
parsed_value = parsed_value * 15;
|
|
|
|
else if (strstr(str, "in"))
|
|
|
|
parsed_value = parsed_value * 90;
|
|
|
|
//TODO: implement 'em', 'ex' attributes
|
|
|
|
|
|
|
|
/* Transform into global percentage */
|
|
|
|
parsed_value = parsed_value / max;
|
|
|
|
|
|
|
|
return parsed_value;
|
|
|
|
}
|
|
|
|
|
2017-02-22 03:02:31 -08:00
|
|
|
static inline double
|
2017-02-17 03:00:54 -08:00
|
|
|
_to_offset(const char *str)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
2017-02-17 03:00:54 -08:00
|
|
|
char *end = NULL;
|
|
|
|
|
2019-09-17 05:18:47 -07:00
|
|
|
double parsed_value = eina_convert_strtod_c(str, &end);
|
2017-02-17 03:00:54 -08:00
|
|
|
|
|
|
|
if (strstr(str, "%"))
|
|
|
|
parsed_value = parsed_value / 100.0;
|
|
|
|
|
|
|
|
return parsed_value;
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
_to_opacity(const char *str)
|
|
|
|
{
|
|
|
|
char *end = NULL;
|
|
|
|
int a = 0;
|
2019-09-17 05:18:47 -07:00
|
|
|
double opacity = eina_convert_strtod_c(str, &end);
|
2016-11-02 03:25:45 -07:00
|
|
|
|
2019-10-18 10:29:38 -07:00
|
|
|
if (end && (*end == '\0'))
|
2016-11-02 03:25:45 -07:00
|
|
|
a = lrint(opacity * 255);
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define _PARSE_TAG(Type, Short_Name, Tags_Array, Default) \
|
|
|
|
static Type _to_##Short_Name(const char *str) \
|
|
|
|
{ \
|
|
|
|
unsigned int i; \
|
|
|
|
\
|
|
|
|
for (i = 0; i < sizeof (Tags_Array) / sizeof (Tags_Array[0]); i++) \
|
|
|
|
if (!strcmp(str, Tags_Array[i].tag)) \
|
|
|
|
return Tags_Array[i].Short_Name; \
|
|
|
|
return Default; \
|
|
|
|
}
|
|
|
|
/* parse the line cap used during stroking a path.
|
|
|
|
* Value: butt | round | square | inherit
|
|
|
|
* Initial: butt
|
|
|
|
* https://www.w3.org/TR/SVG/painting.html
|
|
|
|
*/
|
|
|
|
static struct {
|
|
|
|
Efl_Gfx_Cap line_cap;
|
|
|
|
const char *tag;
|
|
|
|
} line_cap_tags[] = {
|
|
|
|
{ EFL_GFX_CAP_BUTT, "butt" },
|
|
|
|
{ EFL_GFX_CAP_ROUND, "round" },
|
|
|
|
{ EFL_GFX_CAP_SQUARE, "square" }
|
|
|
|
};
|
|
|
|
|
|
|
|
_PARSE_TAG(Efl_Gfx_Cap, line_cap, line_cap_tags, EFL_GFX_CAP_LAST);
|
|
|
|
|
|
|
|
/* parse the line join used during stroking a path.
|
|
|
|
* Value: miter | round | bevel | inherit
|
|
|
|
* Initial: miter
|
|
|
|
* https://www.w3.org/TR/SVG/painting.html
|
|
|
|
*/
|
|
|
|
static struct {
|
|
|
|
Efl_Gfx_Join line_join;
|
|
|
|
const char *tag;
|
|
|
|
} line_join_tags[] = {
|
|
|
|
{ EFL_GFX_JOIN_MITER, "miter" },
|
|
|
|
{ EFL_GFX_JOIN_ROUND, "round" },
|
|
|
|
{ EFL_GFX_JOIN_BEVEL, "bevel" }
|
|
|
|
};
|
|
|
|
|
|
|
|
_PARSE_TAG(Efl_Gfx_Join, line_join, line_join_tags, EFL_GFX_JOIN_LAST);
|
|
|
|
|
|
|
|
/* parse the fill rule used during filling a path.
|
|
|
|
* Value: nonzero | evenodd | inherit
|
|
|
|
* Initial: nonzero
|
|
|
|
* https://www.w3.org/TR/SVG/painting.html
|
|
|
|
*/
|
|
|
|
|
|
|
|
static struct {
|
|
|
|
Efl_Gfx_Fill_Rule fill_rule;
|
|
|
|
const char *tag;
|
|
|
|
} fill_rule_tags[] = {
|
|
|
|
{ EFL_GFX_FILL_RULE_ODD_EVEN, "evenodd" }
|
|
|
|
};
|
|
|
|
|
|
|
|
_PARSE_TAG(Efl_Gfx_Fill_Rule, fill_rule, fill_rule_tags, EFL_GFX_FILL_RULE_WINDING);
|
|
|
|
|
|
|
|
/* parse the dash pattern used during stroking a path.
|
|
|
|
* Value: none | <dasharray> | inherit
|
|
|
|
* Initial: none
|
|
|
|
* https://www.w3.org/TR/SVG/painting.html
|
|
|
|
*/
|
|
|
|
static inline void
|
|
|
|
_parse_dash_array(const char *str, Efl_Gfx_Dash** dash, int *length)
|
|
|
|
{
|
2020-09-14 20:11:49 -07:00
|
|
|
// It is assumed that the length of the dasharray string is 255 or less.
|
|
|
|
double tmp[255];
|
2016-11-02 03:25:45 -07:00
|
|
|
char *end = NULL;
|
|
|
|
int leni, gapi, count = 0, index = 0;
|
|
|
|
|
|
|
|
while (*str)
|
|
|
|
{
|
|
|
|
// skip white space, comma
|
|
|
|
str = _skipcomma(str);
|
2019-09-17 05:18:47 -07:00
|
|
|
tmp[count++] = eina_convert_strtod_c(str, &end);
|
2016-11-02 03:25:45 -07:00
|
|
|
str = _skipcomma(end);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count & 0x1)
|
|
|
|
{ // odd case.
|
|
|
|
*length = count;
|
|
|
|
*dash = calloc(*length, sizeof(Efl_Gfx_Dash));
|
|
|
|
while (index < count)
|
|
|
|
{
|
|
|
|
leni = (2 * index) % count;
|
|
|
|
gapi = (2 * index + 1) % count;
|
|
|
|
(*dash)[index].length = tmp[leni];
|
|
|
|
(*dash)[index].gap = tmp[gapi];
|
2020-09-14 20:11:49 -07:00
|
|
|
index++;
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // even case
|
|
|
|
*length = count/2;
|
|
|
|
*dash = calloc(*length, sizeof(Efl_Gfx_Dash));
|
2020-09-14 20:11:49 -07:00
|
|
|
while (index < *length)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
|
|
|
(*dash)[index].length = tmp[2 * index];
|
|
|
|
(*dash)[index].gap = tmp[2 * index + 1];
|
2020-09-14 20:11:49 -07:00
|
|
|
index++;
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static Eina_Stringshare *
|
|
|
|
_id_from_url(const char *url)
|
|
|
|
{
|
|
|
|
char tmp[50];
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
url = _skip_space(url, NULL);
|
|
|
|
if ((*url) == '(')
|
|
|
|
{
|
|
|
|
++url;
|
|
|
|
url = _skip_space(url, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((*url) == '#')
|
|
|
|
++url;
|
|
|
|
|
|
|
|
while ((*url) != ')')
|
|
|
|
{
|
|
|
|
tmp[i++] = *url;
|
|
|
|
++url;
|
|
|
|
}
|
|
|
|
tmp[i] = '\0';
|
|
|
|
|
|
|
|
return eina_stringshare_add(tmp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned char
|
|
|
|
_color_parser(const char *value, char **end)
|
|
|
|
{
|
|
|
|
double r;
|
|
|
|
|
evas_vg_load_svg: Fix color parsing
Summary:
Remove unnecessary point movement in rgb(255, 255, 255) case in svg parsing.
In svg parsing, move the pointer by 'rgb(' before calling
_color_parser() in the rgb(255, 255, 255) case.
In function, string pointer moved unnecessary, so parsing is incorrect.
Therefore, remove unnecessary point movement.
Test Plan:
svg sample code
```
<svg xmlns:svg="http://www.w3.org/2000/svg" viewBox="0 0 300 300">
<path d="M 0 0 h 200 v 200 z" style="fill:rgb(255, 155, 55);"/>
</svg>
```
before
{F4504779}
after
{F4504778}
Reviewers: Hermet
Subscribers: cedric, #reviewers, #committers
Tags: #efl
Differential Revision: https://phab.enlightenment.org/D12284
2021-06-18 02:24:58 -07:00
|
|
|
r = eina_convert_strtod_c(value, end);
|
2016-11-02 03:25:45 -07:00
|
|
|
*end = _skip_space(*end, NULL);
|
|
|
|
if (**end == '%')
|
|
|
|
r = 255 * r / 100;
|
|
|
|
*end = _skip_space(*end, NULL);
|
|
|
|
|
|
|
|
if (r < 0 || r > 255)
|
|
|
|
{
|
|
|
|
*end = NULL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return lrint(r);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct {
|
|
|
|
const char *name;
|
|
|
|
unsigned int value;
|
|
|
|
} colors[] = {
|
|
|
|
{ "aliceblue", 0xfff0f8ff },
|
|
|
|
{ "antiquewhite", 0xfffaebd7 },
|
|
|
|
{ "aqua", 0xff00ffff },
|
|
|
|
{ "aquamarine", 0xff7fffd4 },
|
|
|
|
{ "azure", 0xfff0ffff },
|
|
|
|
{ "beige", 0xfff5f5dc },
|
|
|
|
{ "bisque", 0xffffe4c4 },
|
|
|
|
{ "black", 0xff000000 },
|
|
|
|
{ "blanchedalmond", 0xffffebcd },
|
|
|
|
{ "blue", 0xff0000ff },
|
|
|
|
{ "blueviolet", 0xff8a2be2 },
|
|
|
|
{ "brown", 0xffa52a2a },
|
|
|
|
{ "burlywood", 0xffdeb887 },
|
|
|
|
{ "cadetblue", 0xff5f9ea0 },
|
|
|
|
{ "chartreuse", 0xff7fff00 },
|
|
|
|
{ "chocolate", 0xffd2691e },
|
|
|
|
{ "coral", 0xffff7f50 },
|
|
|
|
{ "cornflowerblue", 0xff6495ed },
|
|
|
|
{ "cornsilk", 0xfffff8dc },
|
|
|
|
{ "crimson", 0xffdc143c },
|
|
|
|
{ "cyan", 0xff00ffff },
|
|
|
|
{ "darkblue", 0xff00008b },
|
|
|
|
{ "darkcyan", 0xff008b8b },
|
|
|
|
{ "darkgoldenrod", 0xffb8860b },
|
|
|
|
{ "darkgray", 0xffa9a9a9 },
|
|
|
|
{ "darkgrey", 0xffa9a9a9 },
|
|
|
|
{ "darkgreen", 0xff006400 },
|
|
|
|
{ "darkkhaki", 0xffbdb76b },
|
|
|
|
{ "darkmagenta", 0xff8b008b },
|
|
|
|
{ "darkolivegreen", 0xff556b2f },
|
|
|
|
{ "darkorange", 0xffff8c00 },
|
|
|
|
{ "darkorchid", 0xff9932cc },
|
|
|
|
{ "darkred", 0xff8b0000 },
|
|
|
|
{ "darksalmon", 0xffe9967a },
|
|
|
|
{ "darkseagreen", 0xff8fbc8f },
|
|
|
|
{ "darkslateblue", 0xff483d8b },
|
|
|
|
{ "darkslategray", 0xff2f4f4f },
|
|
|
|
{ "darkslategrey", 0xff2f4f4f },
|
|
|
|
{ "darkturquoise", 0xff00ced1 },
|
|
|
|
{ "darkviolet", 0xff9400d3 },
|
|
|
|
{ "deeppink", 0xffff1493 },
|
|
|
|
{ "deepskyblue", 0xff00bfff },
|
|
|
|
{ "dimgray", 0xff696969 },
|
|
|
|
{ "dimgrey", 0xff696969 },
|
|
|
|
{ "dodgerblue", 0xff1e90ff },
|
|
|
|
{ "firebrick", 0xffb22222 },
|
|
|
|
{ "floralwhite", 0xfffffaf0 },
|
|
|
|
{ "forestgreen", 0xff228b22 },
|
|
|
|
{ "fuchsia", 0xffff00ff },
|
|
|
|
{ "gainsboro", 0xffdcdcdc },
|
|
|
|
{ "ghostwhite", 0xfff8f8ff },
|
|
|
|
{ "gold", 0xffffd700 },
|
|
|
|
{ "goldenrod", 0xffdaa520 },
|
|
|
|
{ "gray", 0xff808080 },
|
|
|
|
{ "grey", 0xff808080 },
|
|
|
|
{ "green", 0xff008000 },
|
|
|
|
{ "greenyellow", 0xffadff2f },
|
|
|
|
{ "honeydew", 0xfff0fff0 },
|
|
|
|
{ "hotpink", 0xffff69b4 },
|
|
|
|
{ "indianred", 0xffcd5c5c },
|
|
|
|
{ "indigo", 0xff4b0082 },
|
|
|
|
{ "ivory", 0xfffffff0 },
|
|
|
|
{ "khaki", 0xfff0e68c },
|
|
|
|
{ "lavender", 0xffe6e6fa },
|
|
|
|
{ "lavenderblush", 0xfffff0f5 },
|
|
|
|
{ "lawngreen", 0xff7cfc00 },
|
|
|
|
{ "lemonchiffon", 0xfffffacd },
|
|
|
|
{ "lightblue", 0xffadd8e6 },
|
|
|
|
{ "lightcoral", 0xfff08080 },
|
|
|
|
{ "lightcyan", 0xffe0ffff },
|
|
|
|
{ "lightgoldenrodyellow", 0xfffafad2 },
|
|
|
|
{ "lightgray", 0xffd3d3d3 },
|
|
|
|
{ "lightgrey", 0xffd3d3d3 },
|
|
|
|
{ "lightgreen", 0xff90ee90 },
|
|
|
|
{ "lightpink", 0xffffb6c1 },
|
|
|
|
{ "lightsalmon", 0xffffa07a },
|
|
|
|
{ "lightseagreen", 0xff20b2aa },
|
|
|
|
{ "lightskyblue", 0xff87cefa },
|
|
|
|
{ "lightslategray", 0xff778899 },
|
|
|
|
{ "lightslategrey", 0xff778899 },
|
|
|
|
{ "lightsteelblue", 0xffb0c4de },
|
|
|
|
{ "lightyellow", 0xffffffe0 },
|
|
|
|
{ "lime", 0xff00ff00 },
|
|
|
|
{ "limegreen", 0xff32cd32 },
|
|
|
|
{ "linen", 0xfffaf0e6 },
|
|
|
|
{ "magenta", 0xffff00ff },
|
|
|
|
{ "maroon", 0xff800000 },
|
|
|
|
{ "mediumaquamarine", 0xff66cdaa },
|
|
|
|
{ "mediumblue", 0xff0000cd },
|
|
|
|
{ "mediumorchid", 0xffba55d3 },
|
|
|
|
{ "mediumpurple", 0xff9370d8 },
|
|
|
|
{ "mediumseagreen", 0xff3cb371 },
|
|
|
|
{ "mediumslateblue", 0xff7b68ee },
|
|
|
|
{ "mediumspringgreen", 0xff00fa9a },
|
|
|
|
{ "mediumturquoise", 0xff48d1cc },
|
|
|
|
{ "mediumvioletred", 0xffc71585 },
|
|
|
|
{ "midnightblue", 0xff191970 },
|
|
|
|
{ "mintcream", 0xfff5fffa },
|
|
|
|
{ "mistyrose", 0xffffe4e1 },
|
|
|
|
{ "moccasin", 0xffffe4b5 },
|
|
|
|
{ "navajowhite", 0xffffdead },
|
|
|
|
{ "navy", 0xff000080 },
|
|
|
|
{ "oldlace", 0xfffdf5e6 },
|
|
|
|
{ "olive", 0xff808000 },
|
|
|
|
{ "olivedrab", 0xff6b8e23 },
|
|
|
|
{ "orange", 0xffffa500 },
|
|
|
|
{ "orangered", 0xffff4500 },
|
|
|
|
{ "orchid", 0xffda70d6 },
|
|
|
|
{ "palegoldenrod", 0xffeee8aa },
|
|
|
|
{ "palegreen", 0xff98fb98 },
|
|
|
|
{ "paleturquoise", 0xffafeeee },
|
|
|
|
{ "palevioletred", 0xffd87093 },
|
|
|
|
{ "papayawhip", 0xffffefd5 },
|
|
|
|
{ "peachpuff", 0xffffdab9 },
|
|
|
|
{ "peru", 0xffcd853f },
|
|
|
|
{ "pink", 0xffffc0cb },
|
|
|
|
{ "plum", 0xffdda0dd },
|
|
|
|
{ "powderblue", 0xffb0e0e6 },
|
|
|
|
{ "purple", 0xff800080 },
|
|
|
|
{ "red", 0xffff0000 },
|
|
|
|
{ "rosybrown", 0xffbc8f8f },
|
|
|
|
{ "royalblue", 0xff4169e1 },
|
|
|
|
{ "saddlebrown", 0xff8b4513 },
|
|
|
|
{ "salmon", 0xfffa8072 },
|
|
|
|
{ "sandybrown", 0xfff4a460 },
|
|
|
|
{ "seagreen", 0xff2e8b57 },
|
|
|
|
{ "seashell", 0xfffff5ee },
|
|
|
|
{ "sienna", 0xffa0522d },
|
|
|
|
{ "silver", 0xffc0c0c0 },
|
|
|
|
{ "skyblue", 0xff87ceeb },
|
|
|
|
{ "slateblue", 0xff6a5acd },
|
|
|
|
{ "slategray", 0xff708090 },
|
|
|
|
{ "slategrey", 0xff708090 },
|
|
|
|
{ "snow", 0xfffffafa },
|
|
|
|
{ "springgreen", 0xff00ff7f },
|
|
|
|
{ "steelblue", 0xff4682b4 },
|
|
|
|
{ "tan", 0xffd2b48c },
|
|
|
|
{ "teal", 0xff008080 },
|
|
|
|
{ "thistle", 0xffd8bfd8 },
|
|
|
|
{ "tomato", 0xffff6347 },
|
|
|
|
{ "turquoise", 0xff40e0d0 },
|
|
|
|
{ "violet", 0xffee82ee },
|
|
|
|
{ "wheat", 0xfff5deb3 },
|
|
|
|
{ "white", 0xffffffff },
|
|
|
|
{ "whitesmoke", 0xfff5f5f5 },
|
|
|
|
{ "yellow", 0xffffff00 },
|
|
|
|
{ "yellowgreen", 0xff9acd32 }
|
|
|
|
};
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
_to_color(const char *str, int *r, int *g, int *b, Eina_Stringshare** ref)
|
|
|
|
{
|
|
|
|
unsigned int i, len = strlen(str);
|
|
|
|
char *red, *green, *blue;
|
|
|
|
unsigned char tr, tg, tb;
|
|
|
|
|
|
|
|
if (len == 4 && str[0] == '#')
|
|
|
|
{
|
|
|
|
// case for "#456" should be interprete as "#445566"
|
|
|
|
if (isxdigit(str[1]) &&
|
|
|
|
isxdigit(str[2]) &&
|
|
|
|
isxdigit(str[3]))
|
|
|
|
{
|
|
|
|
char tmp[3] = { '\0', '\0', '\0' };
|
|
|
|
tmp[0] = str[1]; tmp[1] = str[1]; *r = strtol(tmp, NULL, 16);
|
|
|
|
tmp[0] = str[2]; tmp[1] = str[2]; *g = strtol(tmp, NULL, 16);
|
|
|
|
tmp[0] = str[3]; tmp[1] = str[3]; *b = strtol(tmp, NULL, 16);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (len == 7 && str[0] == '#')
|
|
|
|
{
|
|
|
|
if (isxdigit(str[1]) &&
|
|
|
|
isxdigit(str[2]) &&
|
|
|
|
isxdigit(str[3]) &&
|
|
|
|
isxdigit(str[4]) &&
|
|
|
|
isxdigit(str[5]) &&
|
|
|
|
isxdigit(str[6]))
|
|
|
|
{
|
|
|
|
char tmp[3] = { '\0', '\0', '\0' };
|
|
|
|
tmp[0] = str[1]; tmp[1] = str[2]; *r = strtol(tmp, NULL, 16);
|
|
|
|
tmp[0] = str[3]; tmp[1] = str[4]; *g = strtol(tmp, NULL, 16);
|
|
|
|
tmp[0] = str[5]; tmp[1] = str[6]; *b = strtol(tmp, NULL, 16);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (len >= 10 &&
|
|
|
|
(str[0] == 'r' || str[0] == 'R') &&
|
|
|
|
(str[1] == 'g' || str[1] == 'G') &&
|
|
|
|
(str[2] == 'b' || str[2] == 'B') &&
|
|
|
|
str[3] == '(' &&
|
|
|
|
str[len - 1] == ')')
|
|
|
|
{
|
|
|
|
tr = _color_parser(str + 4, &red);
|
|
|
|
if (red && *red == ',')
|
|
|
|
{
|
|
|
|
tg = _color_parser(red + 1, &green);
|
|
|
|
if (green && *green == ',')
|
|
|
|
{
|
|
|
|
tb = _color_parser(green + 1, &blue);
|
|
|
|
if (blue && blue[0] == ')' && blue[1] == '\0')
|
|
|
|
{
|
|
|
|
*r = tr; *g = tg; *b = tb;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (len >= 3 && !strncmp(str, "url",3))
|
|
|
|
{
|
|
|
|
*ref = _id_from_url(str+3);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//handle named color
|
|
|
|
for (i = 0; i < (sizeof (colors) / sizeof (colors[0])); i++)
|
|
|
|
if (!strcasecmp(colors[i].name, str))
|
|
|
|
{
|
|
|
|
*r = R_VAL(&(colors[i].value));
|
|
|
|
*g = G_VAL(&(colors[i].value));
|
|
|
|
*b = B_VAL(&(colors[i].value));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline char *
|
|
|
|
parse_numbers_array(char *str, double *points, int *pt_count)
|
|
|
|
{
|
|
|
|
int count = 0;
|
|
|
|
char *end = NULL;
|
|
|
|
|
|
|
|
str = _skip_space(str, NULL);
|
|
|
|
while (isdigit(*str) ||
|
|
|
|
*str == '-' ||
|
|
|
|
*str == '+' ||
|
|
|
|
*str == '.')
|
|
|
|
{
|
2019-09-17 05:18:47 -07:00
|
|
|
points[count++] = eina_convert_strtod_c(str, &end);
|
2016-11-02 03:25:45 -07:00
|
|
|
str = end;
|
|
|
|
str = _skip_space(str, NULL);
|
|
|
|
if (*str == ',')
|
|
|
|
++str;
|
|
|
|
//eat the rest of space
|
|
|
|
str = _skip_space(str, NULL);
|
|
|
|
}
|
|
|
|
*pt_count = count;
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef enum _Matrix_State
|
|
|
|
{
|
|
|
|
SVG_MATRIX_UNKNOWN,
|
|
|
|
SVG_MATRIX_MATRIX,
|
|
|
|
SVG_MATRIX_TRANSLATE,
|
|
|
|
SVG_MATRIX_ROTATE,
|
|
|
|
SVG_MATRIX_SCALE,
|
|
|
|
SVG_MATRIX_SKEWX,
|
|
|
|
SVG_MATRIX_SKEWY
|
|
|
|
} Matrix_State;
|
|
|
|
|
|
|
|
#define MATRIX_DEF(Name, Value) \
|
|
|
|
{ #Name, sizeof (#Name), Value}
|
|
|
|
|
|
|
|
static const struct {
|
|
|
|
const char *tag;
|
|
|
|
int sz;
|
|
|
|
Matrix_State state;
|
|
|
|
} matrix_tags[] = {
|
|
|
|
MATRIX_DEF(matrix, SVG_MATRIX_MATRIX),
|
|
|
|
MATRIX_DEF(translate, SVG_MATRIX_TRANSLATE),
|
|
|
|
MATRIX_DEF(rotate, SVG_MATRIX_ROTATE),
|
|
|
|
MATRIX_DEF(scale, SVG_MATRIX_SCALE),
|
|
|
|
MATRIX_DEF(skewX, SVG_MATRIX_SKEWX),
|
|
|
|
MATRIX_DEF(skewY, SVG_MATRIX_SKEWY)
|
|
|
|
};
|
|
|
|
|
|
|
|
/* parse transform attribute
|
|
|
|
* https://www.w3.org/TR/SVG/coords.html#TransformAttribute
|
|
|
|
*/
|
|
|
|
static Eina_Matrix3 *
|
|
|
|
_parse_transformation_matrix(const char *value)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
double points[8];
|
|
|
|
int pt_count = 0;
|
|
|
|
double sx, sy;
|
|
|
|
Matrix_State state = SVG_MATRIX_UNKNOWN;
|
|
|
|
Eina_Matrix3 *matrix = calloc(1, sizeof(Eina_Matrix3));
|
|
|
|
char *str = (char *)value;
|
|
|
|
char *end = str + strlen(str);
|
|
|
|
|
|
|
|
eina_matrix3_identity(matrix);
|
|
|
|
while (str < end)
|
|
|
|
{
|
|
|
|
if (isspace(*str) || (*str == ','))
|
|
|
|
{
|
|
|
|
++str;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for (i = 0; i < sizeof (matrix_tags) / sizeof(matrix_tags[0]); i++)
|
|
|
|
if (!strncmp(matrix_tags[i].tag, str, matrix_tags[i].sz -1))
|
|
|
|
{
|
|
|
|
state = matrix_tags[i].state;
|
|
|
|
str += (matrix_tags[i].sz -1);
|
|
|
|
}
|
|
|
|
if ( state == SVG_MATRIX_UNKNOWN)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
str = _skip_space(str, end);
|
|
|
|
if (*str != '(')
|
|
|
|
goto error;
|
|
|
|
++str;
|
|
|
|
str = parse_numbers_array(str, points, &pt_count);
|
|
|
|
if (*str != ')')
|
|
|
|
goto error;
|
|
|
|
++str;
|
|
|
|
|
|
|
|
if (state == SVG_MATRIX_MATRIX)
|
|
|
|
{
|
|
|
|
Eina_Matrix3 tmp;
|
|
|
|
|
|
|
|
if (pt_count != 6) goto error;
|
|
|
|
|
|
|
|
eina_matrix3_identity(&tmp);
|
|
|
|
eina_matrix3_values_set(&tmp,
|
|
|
|
points[0], points[2], points[4],
|
|
|
|
points[1], points[3], points[5],
|
|
|
|
0, 0, 1);
|
|
|
|
eina_matrix3_compose(matrix, &tmp, matrix);
|
|
|
|
}
|
|
|
|
else if (state == SVG_MATRIX_TRANSLATE)
|
|
|
|
{
|
|
|
|
if (pt_count == 1)
|
|
|
|
eina_matrix3_translate(matrix, points[0], 0);
|
|
|
|
else if (pt_count == 2)
|
|
|
|
eina_matrix3_translate(matrix, points[0], points[1]);
|
|
|
|
else
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
else if (state == SVG_MATRIX_ROTATE)
|
|
|
|
{
|
2019-08-19 05:35:35 -07:00
|
|
|
//Transform to signed.
|
|
|
|
points[0] = fmod(points[0], 360);
|
|
|
|
if (points[0] < 0) points[0] += 360;
|
|
|
|
|
2016-11-02 03:25:45 -07:00
|
|
|
if (pt_count == 1)
|
|
|
|
{
|
|
|
|
eina_matrix3_rotate(matrix, points[0] * (M_PI/180.0));
|
|
|
|
}
|
|
|
|
else if (pt_count == 3)
|
|
|
|
{
|
|
|
|
eina_matrix3_translate(matrix, points[1], points[2]);
|
|
|
|
eina_matrix3_rotate(matrix, points[0] * (M_PI/180.0));
|
|
|
|
eina_matrix3_translate(matrix, -points[1], -points[2]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (state == SVG_MATRIX_SCALE)
|
|
|
|
{
|
|
|
|
if (pt_count < 1 || pt_count > 2) goto error;
|
|
|
|
|
|
|
|
sx = points[0];
|
|
|
|
sy = sx;
|
|
|
|
if (pt_count == 2)
|
|
|
|
sy = points[1];
|
|
|
|
eina_matrix3_scale(matrix, sx, sy);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
error:
|
|
|
|
return matrix;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define LENGTH_DEF(Name, Value) \
|
|
|
|
{ #Name, sizeof (#Name), Value}
|
|
|
|
|
|
|
|
static const struct {
|
|
|
|
const char *tag;
|
|
|
|
int sz;
|
|
|
|
Svg_Length_Type type;
|
|
|
|
} length_tags[] = {
|
|
|
|
LENGTH_DEF(%, SVG_LT_PERCENT),
|
|
|
|
LENGTH_DEF(px, SVG_LT_PX),
|
|
|
|
LENGTH_DEF(pc, SVG_LT_PC),
|
|
|
|
LENGTH_DEF(pt, SVG_LT_PT),
|
|
|
|
LENGTH_DEF(mm, SVG_LT_MM),
|
|
|
|
LENGTH_DEF(cm, SVG_LT_CM),
|
|
|
|
LENGTH_DEF(in, SVG_LT_IN)
|
|
|
|
};
|
|
|
|
|
|
|
|
static double
|
|
|
|
parse_length(const char *str, Svg_Length_Type *type)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
double value;
|
|
|
|
int sz = strlen(str);
|
|
|
|
|
|
|
|
*type = SVG_LT_PX;
|
|
|
|
for (i = 0; i < sizeof (length_tags) / sizeof(length_tags[0]); i++)
|
|
|
|
if (length_tags[i].sz - 1 == sz && !strncmp(length_tags[i].tag, str, sz))
|
|
|
|
{
|
|
|
|
*type = length_tags[i].type;
|
|
|
|
}
|
2019-09-17 05:18:47 -07:00
|
|
|
value = eina_convert_strtod_c(str, NULL);
|
2016-11-02 03:25:45 -07:00
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Eina_Bool _parse_style_attr(void *data, const char *key, const char *value);
|
|
|
|
static Eina_Bool _attr_style_node(void *data, const char *str);
|
|
|
|
|
|
|
|
static Eina_Bool
|
|
|
|
_attr_parse_svg_node(void *data, const char *key, const char *value)
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
Evas_SVG_Loader *loader = data;
|
|
|
|
Svg_Node *node = loader->svg_parse->node;
|
2016-11-02 03:25:45 -07:00
|
|
|
Svg_Doc_Node *doc = &(node->node.doc);
|
|
|
|
Svg_Length_Type type;
|
|
|
|
|
2020-06-22 11:24:38 -07:00
|
|
|
// @TODO handle length unit.
|
2016-11-02 03:25:45 -07:00
|
|
|
if (!strcmp(key, "width"))
|
|
|
|
{
|
|
|
|
doc->width = parse_length(value, &type);
|
|
|
|
}
|
|
|
|
else if (!strcmp(key, "height"))
|
|
|
|
{
|
|
|
|
doc->height = parse_length(value, &type);
|
|
|
|
}
|
|
|
|
else if (!strcmp(key, "viewBox"))
|
|
|
|
{
|
|
|
|
if (_parse_number(&value, &doc->vx))
|
2017-02-14 07:51:13 -08:00
|
|
|
{
|
|
|
|
if (_parse_number(&value, &doc->vy))
|
|
|
|
{
|
|
|
|
if (_parse_number(&value, &doc->vw))
|
|
|
|
{
|
|
|
|
_parse_number(&value, &doc->vh);
|
2017-11-06 18:41:36 -08:00
|
|
|
loader->svg_parse->global.height = doc->vh;
|
2017-02-14 07:51:13 -08:00
|
|
|
}
|
2017-11-06 18:41:36 -08:00
|
|
|
loader->svg_parse->global.width = doc->vw;
|
2017-02-14 07:51:13 -08:00
|
|
|
}
|
2017-11-06 18:41:36 -08:00
|
|
|
loader->svg_parse->global.y = doc->vy;
|
2017-02-14 07:51:13 -08:00
|
|
|
}
|
2017-11-06 18:41:36 -08:00
|
|
|
loader->svg_parse->global.x = doc->vx;
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
else if (!strcmp(key, "preserveAspectRatio"))
|
|
|
|
{
|
|
|
|
if (!strcmp(value, "none"))
|
|
|
|
doc->preserve_aspect = EINA_FALSE;
|
|
|
|
}
|
|
|
|
else if (!strcmp(key, "style"))
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
_attr_style_node(loader, value);
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
_parse_style_attr(loader, key, value);
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
//https://www.w3.org/TR/SVGTiny12/painting.html#SpecifyingPaint
|
|
|
|
static void
|
2017-11-06 18:41:36 -08:00
|
|
|
_handle_paint_attr(Evas_SVG_Loader *loader EINA_UNUSED, Svg_Paint* paint, const char *value)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
|
|
|
if (!strcmp(value, "none"))
|
|
|
|
{
|
|
|
|
// no paint property
|
|
|
|
paint->none = EINA_TRUE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
paint->none = EINA_FALSE;
|
|
|
|
if (!strcmp(value, "currentColor"))
|
|
|
|
{
|
|
|
|
paint->cur_color = EINA_TRUE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_to_color(value, &paint->r, &paint->g, &paint->b, &paint->url);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-11-06 18:41:36 -08:00
|
|
|
_handle_color_attr(Evas_SVG_Loader *loader EINA_UNUSED, Svg_Node* node, const char *value)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
|
|
|
Svg_Style_Property *style = node->style;
|
|
|
|
_to_color(value, &style->r, &style->g, &style->b, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-11-06 18:41:36 -08:00
|
|
|
_handle_fill_attr(Evas_SVG_Loader *loader EINA_UNUSED, Svg_Node* node, const char *value)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
|
|
|
Svg_Style_Property *style = node->style;
|
|
|
|
style->fill.flags |= SVG_FILL_FLAGS_PAINT;
|
2017-11-06 18:41:36 -08:00
|
|
|
_handle_paint_attr(loader, &style->fill.paint, value);
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-11-06 18:41:36 -08:00
|
|
|
_handle_stroke_attr(Evas_SVG_Loader *loader EINA_UNUSED, Svg_Node* node, const char *value)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
|
|
|
Svg_Style_Property *style = node->style;
|
|
|
|
style->stroke.flags |= SVG_STROKE_FLAGS_PAINT;
|
2017-11-06 18:41:36 -08:00
|
|
|
_handle_paint_attr(loader, &style->stroke.paint, value);
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-11-06 18:41:36 -08:00
|
|
|
_handle_stroke_opacity_attr(Evas_SVG_Loader *loader EINA_UNUSED, Svg_Node* node, const char *value)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
|
|
|
node->style->stroke.flags |= SVG_STROKE_FLAGS_OPACITY;
|
|
|
|
node->style->stroke.opacity = _to_opacity(value);
|
|
|
|
}
|
|
|
|
|
2020-09-14 20:11:49 -07:00
|
|
|
static void
|
|
|
|
_handle_stroke_dasharray_attr(Evas_SVG_Loader *loader EINA_UNUSED, Svg_Node* node, const char *value)
|
|
|
|
{
|
|
|
|
node->style->stroke.flags |= SVG_STROKE_FLAGS_DASH;
|
|
|
|
_parse_dash_array(value, &node->style->stroke.dash, &node->style->stroke.dash_count);
|
|
|
|
}
|
|
|
|
|
2016-11-02 03:25:45 -07:00
|
|
|
static void
|
2017-11-06 18:41:36 -08:00
|
|
|
_handle_stroke_width_attr(Evas_SVG_Loader *loader, Svg_Node* node, const char *value)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
|
|
|
node->style->stroke.flags |= SVG_STROKE_FLAGS_WIDTH;
|
2017-11-06 18:41:36 -08:00
|
|
|
node->style->stroke.width = _to_double(loader->svg_parse, value, SVG_PARSER_LENGTH_HORIZONTAL);
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-11-06 18:41:36 -08:00
|
|
|
_handle_stroke_linecap_attr(Evas_SVG_Loader *loader EINA_UNUSED, Svg_Node* node, const char *value)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
|
|
|
node->style->stroke.flags |= SVG_STROKE_FLAGS_CAP;
|
|
|
|
node->style->stroke.cap = _to_line_cap(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-11-06 18:41:36 -08:00
|
|
|
_handle_stroke_linejoin_attr(Evas_SVG_Loader *loader EINA_UNUSED, Svg_Node* node, const char *value)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
|
|
|
node->style->stroke.flags |= SVG_STROKE_FLAGS_JOIN;
|
|
|
|
node->style->stroke.join = _to_line_join(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-11-06 18:41:36 -08:00
|
|
|
_handle_fill_rule_attr(Evas_SVG_Loader *loader EINA_UNUSED, Svg_Node* node, const char *value)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
|
|
|
node->style->fill.flags |= SVG_FILL_FLAGS_FILL_RULE;
|
|
|
|
node->style->fill.fill_rule = _to_fill_rule(value);
|
|
|
|
}
|
|
|
|
|
2019-06-26 21:16:36 -07:00
|
|
|
static void
|
|
|
|
_handle_opacity_attr(Evas_SVG_Loader *loader EINA_UNUSED, Svg_Node* node, const char *value)
|
|
|
|
{
|
|
|
|
node->style->opacity = _to_opacity(value);
|
|
|
|
}
|
|
|
|
|
2016-11-02 03:25:45 -07:00
|
|
|
static void
|
2017-11-06 18:41:36 -08:00
|
|
|
_handle_fill_opacity_attr(Evas_SVG_Loader *loader EINA_UNUSED, Svg_Node* node, const char *value)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
|
|
|
node->style->fill.flags |= SVG_FILL_FLAGS_OPACITY;
|
|
|
|
node->style->fill.opacity = _to_opacity(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-11-06 18:41:36 -08:00
|
|
|
_handle_transform_attr(Evas_SVG_Loader *loader EINA_UNUSED, Svg_Node* node, const char *value)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
|
|
|
node->transform = _parse_transformation_matrix(value);
|
|
|
|
}
|
|
|
|
|
2020-10-14 03:16:53 -07:00
|
|
|
|
|
|
|
static void _handle_clip_path_attr(Evas_SVG_Loader* loader EINA_UNUSED, Svg_Node* node, const char* value)
|
|
|
|
{
|
|
|
|
Svg_Style_Property* style = node->style;
|
|
|
|
style->comp.flags |= SVG_COMPOSITE_FLAGS_CLIP_PATH;
|
|
|
|
|
|
|
|
int len = strlen(value);
|
|
|
|
if (len >= 3 && !strncmp(value, "url", 3)) style->comp.url = _id_from_url((const char*)(value + 3));
|
|
|
|
}
|
|
|
|
|
evas_vg_load_svg: Support "display" attribute.
Summary:
If the display attribute is "none", VG object is not show.
The default is "inline" which means visible and "none" means invisible.
Depending on the type of node, additional functionality may be required.
refer to https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/display
Test Plan:
[SVG]
<svg viewBox="0 0 220 100" xmlns="http://www.w3.org/2000/svg">
<!-- Here the yellow rectangle is displayed -->
<g display="none">
<rect x="0" y="0" width="100" height="100" fill="skyblue"></rect>
</g>
<rect x="20" y="20" width="60" height="60" fill="yellow"></rect>
<!-- Here the yellow rectangle is not displayed -->
<rect x="120" y="0" width="100" height="100" fill="skyblue"></rect>
<rect x="140" y="20" width="60" height="60" fill="yellow" display="none"></rect>
</svg>
[C CODE]
int main(int argc, char **argv)
{
setenv("ECTOR_BACKEND", "default", 1);
elm_init(argc, argv);
Evas_Object *win = elm_win_util_standard_add(NULL, "test");
evas_object_smart_callback_add(win, "delete,request", win_del, 0);
elm_win_autodel_set(win, 1);
Evas *evas = evas_object_evas_get(win);
Evas_Object *vg = evas_object_vg_add(evas);
evas_object_show(vg);
Evas_Object *container = evas_vg_container_add(vg);
evas_object_vg_root_node_set(vg, container);
Evas_Object *svg = efl_add(EFL_CANVAS_VG_OBJECT_CLASS, container);
efl_file_simple_load(svg, "./test.svg", NULL);
efl_gfx_entity_size_set(svg, EINA_SIZE2D(600, 600));
efl_gfx_entity_visible_set(svg, EINA_TRUE);
evas_object_size_hint_weight_set(svg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(svg, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_win_resize_object_add(win, vg);
evas_object_resize(win, WIDTH, HEIGHT);
evas_object_show(win);
elm_run();
elm_shutdown();
return 0;
}
Reviewers: Hermet, smohanty, kimcinoo
Reviewed By: Hermet
Subscribers: cedric, #reviewers, #committers
Tags: #efl
Differential Revision: https://phab.enlightenment.org/D9640
2019-08-20 04:32:15 -07:00
|
|
|
static void
|
|
|
|
_handle_display_attr(Evas_SVG_Loader *loader EINA_UNUSED, Svg_Node* node, const char *value)
|
|
|
|
{
|
|
|
|
//TODO : The display attribute can have various values as well as "none".
|
|
|
|
// The default is "inline" which means visible and "none" means invisible.
|
|
|
|
// Depending on the type of node, additional functionality may be required.
|
|
|
|
// refer to https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/display
|
|
|
|
if (!strcmp(value, "none")) node->display = EINA_FALSE;
|
|
|
|
else node->display = EINA_TRUE;
|
|
|
|
}
|
2016-11-02 03:25:45 -07:00
|
|
|
|
2017-11-06 18:41:36 -08:00
|
|
|
typedef void (*Style_Method)(Evas_SVG_Loader *loader, Svg_Node *node, const char *value);
|
2016-11-02 03:25:45 -07:00
|
|
|
|
|
|
|
#define STYLE_DEF(Name, Name1) \
|
|
|
|
{ #Name, sizeof (#Name), _handle_##Name1##_attr}
|
|
|
|
|
|
|
|
static const struct {
|
|
|
|
const char *tag;
|
|
|
|
int sz;
|
|
|
|
Style_Method tag_handler;;
|
|
|
|
} style_tags[] = {
|
|
|
|
STYLE_DEF(color, color),
|
|
|
|
STYLE_DEF(fill, fill),
|
|
|
|
STYLE_DEF(fill-rule, fill_rule),
|
|
|
|
STYLE_DEF(fill-opacity, fill_opacity),
|
2019-06-26 21:16:36 -07:00
|
|
|
STYLE_DEF(opacity, opacity),
|
2016-11-02 03:25:45 -07:00
|
|
|
STYLE_DEF(stroke, stroke),
|
|
|
|
STYLE_DEF(stroke-width, stroke_width),
|
|
|
|
STYLE_DEF(stroke-linejoin, stroke_linejoin),
|
|
|
|
STYLE_DEF(stroke-linecap, stroke_linecap),
|
|
|
|
STYLE_DEF(stroke-opacity, stroke_opacity),
|
2020-09-14 20:11:49 -07:00
|
|
|
STYLE_DEF(stroke-dasharray, stroke_dasharray),
|
evas_vg_load_svg: Support "display" attribute.
Summary:
If the display attribute is "none", VG object is not show.
The default is "inline" which means visible and "none" means invisible.
Depending on the type of node, additional functionality may be required.
refer to https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/display
Test Plan:
[SVG]
<svg viewBox="0 0 220 100" xmlns="http://www.w3.org/2000/svg">
<!-- Here the yellow rectangle is displayed -->
<g display="none">
<rect x="0" y="0" width="100" height="100" fill="skyblue"></rect>
</g>
<rect x="20" y="20" width="60" height="60" fill="yellow"></rect>
<!-- Here the yellow rectangle is not displayed -->
<rect x="120" y="0" width="100" height="100" fill="skyblue"></rect>
<rect x="140" y="20" width="60" height="60" fill="yellow" display="none"></rect>
</svg>
[C CODE]
int main(int argc, char **argv)
{
setenv("ECTOR_BACKEND", "default", 1);
elm_init(argc, argv);
Evas_Object *win = elm_win_util_standard_add(NULL, "test");
evas_object_smart_callback_add(win, "delete,request", win_del, 0);
elm_win_autodel_set(win, 1);
Evas *evas = evas_object_evas_get(win);
Evas_Object *vg = evas_object_vg_add(evas);
evas_object_show(vg);
Evas_Object *container = evas_vg_container_add(vg);
evas_object_vg_root_node_set(vg, container);
Evas_Object *svg = efl_add(EFL_CANVAS_VG_OBJECT_CLASS, container);
efl_file_simple_load(svg, "./test.svg", NULL);
efl_gfx_entity_size_set(svg, EINA_SIZE2D(600, 600));
efl_gfx_entity_visible_set(svg, EINA_TRUE);
evas_object_size_hint_weight_set(svg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(svg, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_win_resize_object_add(win, vg);
evas_object_resize(win, WIDTH, HEIGHT);
evas_object_show(win);
elm_run();
elm_shutdown();
return 0;
}
Reviewers: Hermet, smohanty, kimcinoo
Reviewed By: Hermet
Subscribers: cedric, #reviewers, #committers
Tags: #efl
Differential Revision: https://phab.enlightenment.org/D9640
2019-08-20 04:32:15 -07:00
|
|
|
STYLE_DEF(transform, transform),
|
|
|
|
STYLE_DEF(display, display)
|
2016-11-02 03:25:45 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
static Eina_Bool
|
|
|
|
_parse_style_attr(void *data, const char *key, const char *value)
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
Evas_SVG_Loader *loader = data;
|
|
|
|
Svg_Node* node = loader->svg_parse->node;
|
2016-11-02 03:25:45 -07:00
|
|
|
unsigned int i;
|
|
|
|
int sz;
|
|
|
|
|
|
|
|
// trim the white space
|
|
|
|
key = _skip_space(key, NULL);
|
|
|
|
|
|
|
|
value = _skip_space(value, NULL);
|
|
|
|
|
|
|
|
sz = strlen(key);
|
|
|
|
for (i = 0; i < sizeof (style_tags) / sizeof(style_tags[0]); i++)
|
|
|
|
if (style_tags[i].sz - 1 == sz && !strncmp(style_tags[i].tag, key, sz))
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
style_tags[i].tag_handler(loader, node, value);
|
2016-11-02 03:25:45 -07:00
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Eina_Bool
|
|
|
|
_attr_style_node(void *data, const char *str)
|
|
|
|
{
|
|
|
|
eina_simple_xml_attribute_w3c_parse(str,
|
|
|
|
_parse_style_attr, data);
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* parse g node
|
|
|
|
* https://www.w3.org/TR/SVG/struct.html#Groups
|
|
|
|
*/
|
|
|
|
static Eina_Bool
|
|
|
|
_attr_parse_g_node(void *data, const char *key, const char *value)
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
Evas_SVG_Loader *loader = data;
|
|
|
|
Svg_Node* node = loader->svg_parse->node;
|
2016-11-02 03:25:45 -07:00
|
|
|
|
|
|
|
if (!strcmp(key, "style"))
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
return _attr_style_node(loader, value);
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
else if (!strcmp(key, "transform"))
|
|
|
|
{
|
|
|
|
node->transform = _parse_transformation_matrix(value);
|
|
|
|
}
|
2020-10-14 03:16:53 -07:00
|
|
|
else if (!strcmp(key, "clip-path"))
|
|
|
|
{
|
|
|
|
_handle_clip_path_attr(loader, node, value);
|
|
|
|
}
|
2016-11-02 03:25:45 -07:00
|
|
|
else if (!strcmp(key, "id"))
|
|
|
|
{
|
|
|
|
node->id = _copy_id(value);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
_parse_style_attr(loader, key, value);
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-10-14 03:16:53 -07:00
|
|
|
/* parse clipPath node
|
|
|
|
* https://www.w3.org/TR/SVG/struct.html#Groups
|
|
|
|
*/
|
|
|
|
static Eina_Bool _attr_parse_clip_path_node(void* data, const char* key, const char* value)
|
|
|
|
{
|
|
|
|
Evas_SVG_Loader *loader = data;
|
|
|
|
Svg_Node* node = loader->svg_parse->node;
|
|
|
|
|
|
|
|
if (!strcmp(key, "style"))
|
|
|
|
{
|
|
|
|
return _attr_style_node(loader, value);
|
|
|
|
}
|
|
|
|
else if (!strcmp(key, "transform"))
|
|
|
|
{
|
|
|
|
node->transform = _parse_transformation_matrix(value);
|
|
|
|
}
|
|
|
|
else if (!strcmp(key, "clip-path"))
|
|
|
|
{
|
|
|
|
_handle_clip_path_attr(loader, node, value);
|
|
|
|
}
|
|
|
|
else if (!strcmp(key, "id"))
|
|
|
|
{
|
|
|
|
node->id = _copy_id(value);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_parse_style_attr(loader, key, value);
|
|
|
|
}
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
2016-11-02 03:25:45 -07:00
|
|
|
static Svg_Node *
|
|
|
|
_create_node(Svg_Node *parent, Svg_Node_Type type)
|
|
|
|
{
|
|
|
|
Svg_Node *node = calloc(1, sizeof(Svg_Node));
|
|
|
|
|
|
|
|
// default fill property
|
|
|
|
node->style = calloc(1, sizeof(Svg_Style_Property));
|
|
|
|
|
|
|
|
// update the default value of stroke and fill
|
|
|
|
//https://www.w3.org/TR/SVGTiny12/painting.html#SpecifyingPaint
|
|
|
|
// default fill color is black
|
2019-06-26 05:43:19 -07:00
|
|
|
//node->style->fill.paint.r = 0;
|
|
|
|
//node->style->fill.paint.g = 0;
|
|
|
|
//node->style->fill.paint.b = 0;
|
2016-11-02 03:25:45 -07:00
|
|
|
node->style->fill.paint.none = EINA_FALSE;
|
|
|
|
// default fill opacity is 1
|
|
|
|
node->style->fill.opacity = 255;
|
2019-06-26 21:16:36 -07:00
|
|
|
node->style->opacity = 255;
|
2016-11-02 03:25:45 -07:00
|
|
|
|
|
|
|
// default fill rule is nonzero
|
|
|
|
node->style->fill.fill_rule = EFL_GFX_FILL_RULE_WINDING;
|
|
|
|
|
|
|
|
// default stroke is none
|
|
|
|
node->style->stroke.paint.none = EINA_TRUE;
|
|
|
|
// default stroke opacity is 1
|
|
|
|
node->style->stroke.opacity = 255;
|
|
|
|
// default stroke width is 1
|
|
|
|
node->style->stroke.width = 1;
|
|
|
|
// default line cap is butt
|
|
|
|
node->style->stroke.cap = EFL_GFX_CAP_BUTT;
|
|
|
|
// default line join is miter
|
|
|
|
node->style->stroke.join = EFL_GFX_JOIN_MITER;
|
|
|
|
node->style->stroke.scale = 1.0;
|
|
|
|
|
evas_vg_load_svg: Support "display" attribute.
Summary:
If the display attribute is "none", VG object is not show.
The default is "inline" which means visible and "none" means invisible.
Depending on the type of node, additional functionality may be required.
refer to https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/display
Test Plan:
[SVG]
<svg viewBox="0 0 220 100" xmlns="http://www.w3.org/2000/svg">
<!-- Here the yellow rectangle is displayed -->
<g display="none">
<rect x="0" y="0" width="100" height="100" fill="skyblue"></rect>
</g>
<rect x="20" y="20" width="60" height="60" fill="yellow"></rect>
<!-- Here the yellow rectangle is not displayed -->
<rect x="120" y="0" width="100" height="100" fill="skyblue"></rect>
<rect x="140" y="20" width="60" height="60" fill="yellow" display="none"></rect>
</svg>
[C CODE]
int main(int argc, char **argv)
{
setenv("ECTOR_BACKEND", "default", 1);
elm_init(argc, argv);
Evas_Object *win = elm_win_util_standard_add(NULL, "test");
evas_object_smart_callback_add(win, "delete,request", win_del, 0);
elm_win_autodel_set(win, 1);
Evas *evas = evas_object_evas_get(win);
Evas_Object *vg = evas_object_vg_add(evas);
evas_object_show(vg);
Evas_Object *container = evas_vg_container_add(vg);
evas_object_vg_root_node_set(vg, container);
Evas_Object *svg = efl_add(EFL_CANVAS_VG_OBJECT_CLASS, container);
efl_file_simple_load(svg, "./test.svg", NULL);
efl_gfx_entity_size_set(svg, EINA_SIZE2D(600, 600));
efl_gfx_entity_visible_set(svg, EINA_TRUE);
evas_object_size_hint_weight_set(svg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(svg, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_win_resize_object_add(win, vg);
evas_object_resize(win, WIDTH, HEIGHT);
evas_object_show(win);
elm_run();
elm_shutdown();
return 0;
}
Reviewers: Hermet, smohanty, kimcinoo
Reviewed By: Hermet
Subscribers: cedric, #reviewers, #committers
Tags: #efl
Differential Revision: https://phab.enlightenment.org/D9640
2019-08-20 04:32:15 -07:00
|
|
|
// default display is true("inline").
|
|
|
|
node->display = EINA_TRUE;
|
|
|
|
|
2016-11-02 03:25:45 -07:00
|
|
|
node->parent = parent;
|
|
|
|
node->type = type;
|
|
|
|
node->child = NULL;
|
|
|
|
|
|
|
|
if (parent)
|
|
|
|
parent->child = eina_list_append(parent->child, node);
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Svg_Node *
|
2017-11-06 18:41:36 -08:00
|
|
|
_create_defs_node(Evas_SVG_Loader *loader EINA_UNUSED, Svg_Node *parent EINA_UNUSED, const char *buf EINA_UNUSED, unsigned buflen EINA_UNUSED)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
|
|
|
Svg_Node *node = _create_node(NULL, SVG_NODE_DEFS);
|
|
|
|
eina_simple_xml_attributes_parse(buf, buflen,
|
|
|
|
NULL, node);
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Svg_Node *
|
2017-11-06 18:41:36 -08:00
|
|
|
_create_g_node(Evas_SVG_Loader *loader, Svg_Node *parent, const char *buf, unsigned buflen)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
loader->svg_parse->node = _create_node(parent, SVG_NODE_G);
|
2016-11-02 03:25:45 -07:00
|
|
|
|
|
|
|
eina_simple_xml_attributes_parse(buf, buflen,
|
2017-11-06 18:41:36 -08:00
|
|
|
_attr_parse_g_node, loader);
|
|
|
|
return loader->svg_parse->node;
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static Svg_Node *
|
2017-11-06 18:41:36 -08:00
|
|
|
_create_svg_node(Evas_SVG_Loader *loader, Svg_Node *parent, const char *buf, unsigned buflen)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
loader->svg_parse->node = _create_node(parent, SVG_NODE_DOC);
|
|
|
|
Svg_Doc_Node *doc = &(loader->svg_parse->node->node.doc);
|
2016-11-02 03:25:45 -07:00
|
|
|
|
|
|
|
doc->preserve_aspect = EINA_TRUE;
|
|
|
|
eina_simple_xml_attributes_parse(buf, buflen,
|
2017-11-06 18:41:36 -08:00
|
|
|
_attr_parse_svg_node, loader);
|
|
|
|
|
|
|
|
return loader->svg_parse->node;
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static Svg_Node *
|
2017-11-06 18:41:36 -08:00
|
|
|
_create_switch_node(Evas_SVG_Loader *loader EINA_UNUSED, Svg_Node *parent EINA_UNUSED, const char *buf EINA_UNUSED, unsigned buflen EINA_UNUSED)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-09-10 19:49:09 -07:00
|
|
|
static Svg_Node *
|
|
|
|
_create_mask_node(Evas_SVG_Loader *loader EINA_UNUSED, Svg_Node *parent EINA_UNUSED, const char *buf EINA_UNUSED, unsigned buflen EINA_UNUSED)
|
|
|
|
{
|
|
|
|
Svg_Node *node = _create_node(NULL, SVG_NODE_UNKNOWN);
|
|
|
|
|
|
|
|
node->display = EINA_FALSE;
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Svg_Node *
|
|
|
|
_create_clipPath_node(Evas_SVG_Loader *loader EINA_UNUSED, Svg_Node *parent EINA_UNUSED, const char *buf EINA_UNUSED, unsigned buflen EINA_UNUSED)
|
|
|
|
{
|
2020-10-14 03:16:53 -07:00
|
|
|
loader->svg_parse->node = _create_node(parent, SVG_NODE_CLIP_PATH);
|
2020-09-10 19:49:09 -07:00
|
|
|
|
2020-10-14 03:16:53 -07:00
|
|
|
eina_simple_xml_attributes_parse(buf, buflen,
|
|
|
|
_attr_parse_clip_path_node, loader);
|
|
|
|
return loader->svg_parse->node;
|
2020-09-10 19:49:09 -07:00
|
|
|
}
|
|
|
|
|
2016-11-02 03:25:45 -07:00
|
|
|
static Eina_Bool
|
|
|
|
_attr_parse_path_node(void *data, const char *key, const char *value)
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
Evas_SVG_Loader *loader = data;
|
|
|
|
Svg_Node* node = loader->svg_parse->node;
|
2016-11-02 03:25:45 -07:00
|
|
|
Svg_Path_Node *path = &(node->node.path);
|
|
|
|
|
|
|
|
if (!strcmp(key, "d"))
|
|
|
|
{
|
|
|
|
path->path = eina_stringshare_add(value);
|
|
|
|
}
|
|
|
|
else if (!strcmp(key, "style"))
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
_attr_style_node(loader, value);
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
2020-10-14 03:16:53 -07:00
|
|
|
else if (!strcmp(key, "clip-path"))
|
|
|
|
{
|
|
|
|
_handle_clip_path_attr(loader, node, value);
|
|
|
|
}
|
2016-11-02 03:25:45 -07:00
|
|
|
else if (!strcmp(key, "id"))
|
|
|
|
{
|
|
|
|
node->id = _copy_id(value);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
_parse_style_attr(loader, key, value);
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Svg_Node *
|
2017-11-06 18:41:36 -08:00
|
|
|
_create_path_node(Evas_SVG_Loader *loader, Svg_Node *parent, const char *buf, unsigned buflen)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
loader->svg_parse->node = _create_node(parent, SVG_NODE_PATH);
|
2016-11-02 03:25:45 -07:00
|
|
|
|
|
|
|
eina_simple_xml_attributes_parse(buf, buflen,
|
2017-11-06 18:41:36 -08:00
|
|
|
_attr_parse_path_node, loader);
|
|
|
|
|
|
|
|
return loader->svg_parse->node;
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
2017-02-17 03:00:54 -08:00
|
|
|
#define CIRCLE_DEF(Name, Field, Type) \
|
2017-02-17 07:00:01 -08:00
|
|
|
{ #Name, Type, sizeof (#Name), offsetof(Svg_Circle_Node, Field)}
|
2016-11-02 03:25:45 -07:00
|
|
|
|
|
|
|
static const struct {
|
|
|
|
const char *tag;
|
2017-02-17 03:00:54 -08:00
|
|
|
SVG_Parser_Length_Type type;
|
2016-11-02 03:25:45 -07:00
|
|
|
int sz;
|
|
|
|
size_t offset;
|
|
|
|
} circle_tags[] = {
|
2017-02-17 03:00:54 -08:00
|
|
|
CIRCLE_DEF(cx, cx, SVG_PARSER_LENGTH_HORIZONTAL),
|
|
|
|
CIRCLE_DEF(cy, cy, SVG_PARSER_LENGTH_VERTICAL),
|
|
|
|
CIRCLE_DEF(r, r, SVG_PARSER_LENGTH_OTHER)
|
2016-11-02 03:25:45 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
/* parse the attributes for a circle element.
|
|
|
|
* https://www.w3.org/TR/SVG/shapes.html#CircleElement
|
|
|
|
*/
|
|
|
|
static Eina_Bool
|
|
|
|
_attr_parse_circle_node(void *data, const char *key, const char *value)
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
Evas_SVG_Loader *loader = data;
|
|
|
|
Svg_Node* node = loader->svg_parse->node;
|
2016-11-02 03:25:45 -07:00
|
|
|
Svg_Circle_Node *circle = &(node->node.circle);
|
|
|
|
unsigned int i;
|
|
|
|
unsigned char *array;
|
|
|
|
int sz = strlen(key);
|
|
|
|
|
|
|
|
array = (unsigned char*) circle;
|
|
|
|
for (i = 0; i < sizeof (circle_tags) / sizeof(circle_tags[0]); i++)
|
|
|
|
if (circle_tags[i].sz - 1 == sz && !strncmp(circle_tags[i].tag, key, sz))
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
*((double*) (array + circle_tags[i].offset)) =
|
|
|
|
_to_double(loader->svg_parse, value, circle_tags[i].type);
|
2016-11-02 03:25:45 -07:00
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp(key, "style"))
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
_attr_style_node(loader, value);
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
2020-10-14 03:16:53 -07:00
|
|
|
else if (!strcmp(key, "clip-path"))
|
|
|
|
{
|
|
|
|
_handle_clip_path_attr(loader, node, value);
|
|
|
|
}
|
2016-11-02 03:25:45 -07:00
|
|
|
else if (!strcmp(key, "id"))
|
|
|
|
{
|
|
|
|
node->id = _copy_id(value);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
_parse_style_attr(loader, key, value);
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Svg_Node *
|
2017-11-06 18:41:36 -08:00
|
|
|
_create_circle_node(Evas_SVG_Loader *loader, Svg_Node *parent, const char *buf, unsigned buflen)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
loader->svg_parse->node = _create_node(parent, SVG_NODE_CIRCLE);
|
2016-11-02 03:25:45 -07:00
|
|
|
|
|
|
|
eina_simple_xml_attributes_parse(buf, buflen,
|
2017-11-06 18:41:36 -08:00
|
|
|
_attr_parse_circle_node, loader);
|
|
|
|
return loader->svg_parse->node;
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
2017-02-17 03:00:54 -08:00
|
|
|
#define ELLIPSE_DEF(Name, Field, Type) \
|
2017-02-22 03:02:31 -08:00
|
|
|
{ #Name, Type, sizeof (#Name), offsetof(Svg_Ellipse_Node, Field)}
|
2016-11-02 03:25:45 -07:00
|
|
|
|
|
|
|
static const struct {
|
|
|
|
const char *tag;
|
2017-02-17 03:00:54 -08:00
|
|
|
SVG_Parser_Length_Type type;
|
2016-11-02 03:25:45 -07:00
|
|
|
int sz;
|
|
|
|
size_t offset;
|
|
|
|
} ellipse_tags[] = {
|
2017-02-17 03:00:54 -08:00
|
|
|
ELLIPSE_DEF(cx,cx, SVG_PARSER_LENGTH_HORIZONTAL),
|
|
|
|
ELLIPSE_DEF(cy,cy, SVG_PARSER_LENGTH_VERTICAL),
|
|
|
|
ELLIPSE_DEF(rx,rx, SVG_PARSER_LENGTH_HORIZONTAL),
|
|
|
|
ELLIPSE_DEF(ry,ry, SVG_PARSER_LENGTH_VERTICAL)
|
2016-11-02 03:25:45 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
/* parse the attributes for an ellipse element.
|
|
|
|
* https://www.w3.org/TR/SVG/shapes.html#EllipseElement
|
|
|
|
*/
|
|
|
|
static Eina_Bool
|
|
|
|
_attr_parse_ellipse_node(void *data, const char *key, const char *value)
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
Evas_SVG_Loader *loader = data;
|
|
|
|
Svg_Node* node = loader->svg_parse->node;
|
2016-11-02 03:25:45 -07:00
|
|
|
Svg_Ellipse_Node *ellipse = &(node->node.ellipse);
|
|
|
|
unsigned int i;
|
|
|
|
unsigned char *array;
|
|
|
|
int sz = strlen(key);
|
|
|
|
|
|
|
|
array = (unsigned char*) ellipse;
|
|
|
|
for (i = 0; i < sizeof (ellipse_tags) / sizeof(ellipse_tags[0]); i++)
|
|
|
|
if (ellipse_tags[i].sz - 1 == sz && !strncmp(ellipse_tags[i].tag, key, sz))
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
*((double*) (array + ellipse_tags[i].offset)) =
|
|
|
|
_to_double(loader->svg_parse, value, ellipse_tags[i].type);
|
2016-11-02 03:25:45 -07:00
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp(key, "id"))
|
|
|
|
{
|
|
|
|
node->id = _copy_id(value);
|
|
|
|
}
|
2020-10-14 03:16:53 -07:00
|
|
|
else if (!strcmp(key, "clip-path"))
|
|
|
|
{
|
|
|
|
_handle_clip_path_attr(loader, node, value);
|
|
|
|
}
|
2016-11-02 03:25:45 -07:00
|
|
|
else if (!strcmp(key, "style"))
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
_attr_style_node(loader, value);
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
_parse_style_attr(loader, key, value);
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Svg_Node *
|
2017-11-06 18:41:36 -08:00
|
|
|
_create_ellipse_node(Evas_SVG_Loader *loader, Svg_Node *parent, const char *buf, unsigned buflen)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
loader->svg_parse->node = _create_node(parent, SVG_NODE_ELLIPSE);
|
2016-11-02 03:25:45 -07:00
|
|
|
|
|
|
|
eina_simple_xml_attributes_parse(buf, buflen,
|
2017-11-06 18:41:36 -08:00
|
|
|
_attr_parse_ellipse_node, loader);
|
|
|
|
return loader->svg_parse->node;
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_attr_parse_polygon_points(const char *str, double **points, int *point_count)
|
|
|
|
{
|
|
|
|
double tmp[50];
|
|
|
|
int tmp_count=0;
|
|
|
|
int count = 0;
|
|
|
|
double num;
|
|
|
|
double *point_array = NULL, *tmp_array;
|
|
|
|
|
|
|
|
while (_parse_number(&str, &num))
|
|
|
|
{
|
|
|
|
tmp[tmp_count++] = num;
|
|
|
|
if (tmp_count == 50)
|
|
|
|
{
|
|
|
|
tmp_array = realloc(point_array, (count + tmp_count) * sizeof(double));
|
|
|
|
if (!tmp_array) goto error_alloc;
|
|
|
|
point_array = tmp_array;
|
|
|
|
memcpy(&point_array[count], tmp, tmp_count * sizeof(double));
|
|
|
|
count += tmp_count;
|
|
|
|
tmp_count = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tmp_count > 0)
|
|
|
|
{
|
|
|
|
tmp_array = realloc(point_array, (count + tmp_count) * sizeof(double));
|
|
|
|
if (!tmp_array) goto error_alloc;
|
|
|
|
point_array = tmp_array;
|
|
|
|
memcpy(&point_array[count], tmp, tmp_count * sizeof(double));
|
|
|
|
count += tmp_count;
|
|
|
|
}
|
|
|
|
*point_count = count;
|
|
|
|
*points = point_array;
|
|
|
|
return;
|
|
|
|
|
|
|
|
error_alloc:
|
|
|
|
ERR("allocation for point array failed. out of memory");
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* parse the attributes for a polygon element.
|
|
|
|
* https://www.w3.org/TR/SVG/shapes.html#PolylineElement
|
|
|
|
*/
|
|
|
|
static Eina_Bool
|
|
|
|
_attr_parse_polygon_node(void *data, const char *key, const char *value)
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
Evas_SVG_Loader *loader = data;
|
|
|
|
Svg_Node *node = loader->svg_parse->node;
|
2016-11-02 03:25:45 -07:00
|
|
|
Svg_Polygon_Node *polygon = NULL;
|
|
|
|
|
|
|
|
if (node->type == SVG_NODE_POLYGON)
|
|
|
|
polygon = &(node->node.polygon);
|
|
|
|
else
|
|
|
|
polygon = &(node->node.polyline);
|
|
|
|
|
|
|
|
|
|
|
|
if (!strcmp(key, "points"))
|
|
|
|
{
|
|
|
|
_attr_parse_polygon_points(value, &polygon->points, &polygon->points_count);
|
|
|
|
}
|
|
|
|
else if (!strcmp(key, "style"))
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
_attr_style_node(loader, value);
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
2020-10-14 03:16:53 -07:00
|
|
|
else if (!strcmp(key, "clip-path"))
|
|
|
|
{
|
|
|
|
_handle_clip_path_attr(loader, node, value);
|
|
|
|
}
|
2016-11-02 03:25:45 -07:00
|
|
|
else if (!strcmp(key, "id"))
|
|
|
|
{
|
|
|
|
node->id = _copy_id(value);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
_parse_style_attr(loader, key, value);
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Svg_Node *
|
2017-11-06 18:41:36 -08:00
|
|
|
_create_polygon_node(Evas_SVG_Loader *loader, Svg_Node *parent, const char *buf, unsigned buflen)
|
2016-10-12 02:39:10 -07:00
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
loader->svg_parse->node = _create_node(parent, SVG_NODE_POLYGON);
|
2016-11-02 03:25:45 -07:00
|
|
|
|
|
|
|
eina_simple_xml_attributes_parse(buf, buflen,
|
2017-11-06 18:41:36 -08:00
|
|
|
_attr_parse_polygon_node, loader);
|
|
|
|
return loader->svg_parse->node;
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static Svg_Node *
|
2017-11-06 18:41:36 -08:00
|
|
|
_create_polyline_node(Evas_SVG_Loader *loader, Svg_Node *parent, const char *buf, unsigned buflen)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
loader->svg_parse->node = _create_node(parent, SVG_NODE_POLYLINE);
|
2016-11-02 03:25:45 -07:00
|
|
|
|
|
|
|
eina_simple_xml_attributes_parse(buf, buflen,
|
2017-11-06 18:41:36 -08:00
|
|
|
_attr_parse_polygon_node, loader);
|
|
|
|
return loader->svg_parse->node;
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
2017-02-17 03:00:54 -08:00
|
|
|
#define RECT_DEF(Name, Field, Type) \
|
2017-02-22 03:02:31 -08:00
|
|
|
{ #Name, Type, sizeof (#Name), offsetof(Svg_Rect_Node, Field)}
|
2016-11-02 03:25:45 -07:00
|
|
|
|
|
|
|
static const struct {
|
|
|
|
const char *tag;
|
2017-02-17 03:00:54 -08:00
|
|
|
SVG_Parser_Length_Type type;
|
2016-11-02 03:25:45 -07:00
|
|
|
int sz;
|
|
|
|
size_t offset;
|
|
|
|
} rect_tags[] = {
|
2017-02-17 03:00:54 -08:00
|
|
|
RECT_DEF(x,x, SVG_PARSER_LENGTH_HORIZONTAL),
|
|
|
|
RECT_DEF(y, y, SVG_PARSER_LENGTH_VERTICAL),
|
|
|
|
RECT_DEF(width, w, SVG_PARSER_LENGTH_HORIZONTAL),
|
|
|
|
RECT_DEF(height, h, SVG_PARSER_LENGTH_VERTICAL),
|
|
|
|
RECT_DEF(rx, rx, SVG_PARSER_LENGTH_HORIZONTAL),
|
|
|
|
RECT_DEF(ry, ry, SVG_PARSER_LENGTH_VERTICAL)
|
2016-11-02 03:25:45 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
/* parse the attributes for a rect element.
|
|
|
|
* https://www.w3.org/TR/SVG/shapes.html#RectElement
|
|
|
|
*/
|
|
|
|
static Eina_Bool
|
|
|
|
_attr_parse_rect_node(void *data, const char *key, const char *value)
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
Evas_SVG_Loader *loader = data;
|
|
|
|
Svg_Node *node = loader->svg_parse->node;
|
2016-11-02 03:25:45 -07:00
|
|
|
Svg_Rect_Node *rect = & (node->node.rect);
|
|
|
|
unsigned int i;
|
|
|
|
unsigned char *array;
|
|
|
|
int sz = strlen(key);
|
|
|
|
|
|
|
|
array = (unsigned char*) rect;
|
|
|
|
for (i = 0; i < sizeof (rect_tags) / sizeof(rect_tags[0]); i++)
|
|
|
|
if (rect_tags[i].sz - 1 == sz && !strncmp(rect_tags[i].tag, key, sz))
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
*((double*) (array + rect_tags[i].offset)) = _to_double(loader->svg_parse, value, rect_tags[i].type);
|
2020-09-09 22:59:29 -07:00
|
|
|
|
|
|
|
//Case if only rx or ry is declared
|
|
|
|
if (!strncmp(rect_tags[i].tag, "rx", sz)) rect->has_rx = EINA_TRUE;
|
|
|
|
if (!strncmp(rect_tags[i].tag, "ry", sz)) rect->has_ry = EINA_TRUE;
|
|
|
|
|
|
|
|
if (!EINA_DBL_EQ(rect->rx, 0) && EINA_DBL_EQ(rect->ry, 0) && rect->has_rx && !rect->has_ry) rect->ry = rect->rx;
|
|
|
|
if (!EINA_DBL_EQ(rect->ry, 0) && EINA_DBL_EQ(rect->rx, 0) && !rect->has_rx && rect->has_ry) rect->rx = rect->ry;
|
2016-11-02 03:25:45 -07:00
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp(key, "id"))
|
|
|
|
{
|
|
|
|
node->id = _copy_id(value);
|
|
|
|
}
|
|
|
|
else if (!strcmp(key, "style"))
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
_attr_style_node(loader, value);
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
2020-10-14 03:16:53 -07:00
|
|
|
else if (!strcmp(key, "clip-path"))
|
|
|
|
{
|
|
|
|
_handle_clip_path_attr(loader, node, value);
|
|
|
|
}
|
2016-11-02 03:25:45 -07:00
|
|
|
else
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
_parse_style_attr(loader, key, value);
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Svg_Node *
|
2017-11-06 18:41:36 -08:00
|
|
|
_create_rect_node(Evas_SVG_Loader *loader, Svg_Node *parent, const char *buf, unsigned buflen)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
loader->svg_parse->node = _create_node(parent, SVG_NODE_RECT);
|
2016-11-02 03:25:45 -07:00
|
|
|
|
2020-09-09 22:59:29 -07:00
|
|
|
if (loader->svg_parse->node) {
|
|
|
|
loader->svg_parse->node->node.rect.has_rx = loader->svg_parse->node->node.rect.has_ry = EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
2016-11-02 03:25:45 -07:00
|
|
|
eina_simple_xml_attributes_parse(buf, buflen,
|
2017-11-06 18:41:36 -08:00
|
|
|
_attr_parse_rect_node, loader);
|
|
|
|
return loader->svg_parse->node;
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
2017-02-17 03:00:54 -08:00
|
|
|
#define LINE_DEF(Name, Field, Type) \
|
2017-02-22 03:02:31 -08:00
|
|
|
{ #Name, Type, sizeof (#Name), offsetof(Svg_Line_Node, Field)}
|
2016-11-02 03:25:45 -07:00
|
|
|
|
|
|
|
static const struct {
|
|
|
|
const char *tag;
|
2017-02-17 03:00:54 -08:00
|
|
|
SVG_Parser_Length_Type type;
|
2016-11-02 03:25:45 -07:00
|
|
|
int sz;
|
|
|
|
size_t offset;
|
|
|
|
} line_tags[] = {
|
2017-02-17 03:00:54 -08:00
|
|
|
LINE_DEF(x1, x1, SVG_PARSER_LENGTH_HORIZONTAL),
|
|
|
|
LINE_DEF(y1, y1, SVG_PARSER_LENGTH_VERTICAL),
|
|
|
|
LINE_DEF(x2, x2, SVG_PARSER_LENGTH_HORIZONTAL),
|
|
|
|
LINE_DEF(y2, y2, SVG_PARSER_LENGTH_VERTICAL)
|
2016-11-02 03:25:45 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
/* parse the attributes for a rect element.
|
|
|
|
* https://www.w3.org/TR/SVG/shapes.html#LineElement
|
|
|
|
*/
|
|
|
|
static Eina_Bool
|
|
|
|
_attr_parse_line_node(void *data, const char *key, const char *value)
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
Evas_SVG_Loader *loader = data;
|
|
|
|
Svg_Node *node = loader->svg_parse->node;
|
2016-11-02 03:25:45 -07:00
|
|
|
Svg_Line_Node *line = & (node->node.line);
|
|
|
|
unsigned int i;
|
|
|
|
unsigned char *array;
|
|
|
|
int sz = strlen(key);
|
|
|
|
|
|
|
|
array = (unsigned char*) line;
|
|
|
|
for (i = 0; i < sizeof (line_tags) / sizeof(line_tags[0]); i++)
|
|
|
|
if (line_tags[i].sz - 1 == sz && !strncmp(line_tags[i].tag, key, sz))
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
*((double*) (array + line_tags[i].offset)) = _to_double(loader->svg_parse, value, line_tags[i].type);
|
2016-11-02 03:25:45 -07:00
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp(key, "id"))
|
|
|
|
{
|
|
|
|
node->id = _copy_id(value);
|
|
|
|
}
|
|
|
|
else if (!strcmp(key, "style"))
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
_attr_style_node(loader, value);
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
2020-10-14 03:16:53 -07:00
|
|
|
else if (!strcmp(key, "clip-path"))
|
|
|
|
{
|
|
|
|
_handle_clip_path_attr(loader, node, value);
|
|
|
|
}
|
2016-11-02 03:25:45 -07:00
|
|
|
else
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
_parse_style_attr(loader, key, value);
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Svg_Node *
|
2017-11-06 18:41:36 -08:00
|
|
|
_create_line_node(Evas_SVG_Loader *loader, Svg_Node *parent, const char *buf, unsigned buflen)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
loader->svg_parse->node = _create_node(parent, SVG_NODE_LINE);
|
2016-11-02 03:25:45 -07:00
|
|
|
|
|
|
|
eina_simple_xml_attributes_parse(buf, buflen,
|
2017-11-06 18:41:36 -08:00
|
|
|
_attr_parse_line_node, loader);
|
|
|
|
return loader->svg_parse->node;
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static Eina_Stringshare *
|
|
|
|
_id_from_href(const char *href)
|
|
|
|
{
|
|
|
|
href = _skip_space(href, NULL);
|
|
|
|
if ((*href) == '#')
|
|
|
|
href++;
|
|
|
|
return eina_stringshare_add(href);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Svg_Node*
|
|
|
|
_get_defs_node(Svg_Node *node)
|
|
|
|
{
|
|
|
|
if (!node) return NULL;
|
|
|
|
|
|
|
|
while (node->parent != NULL)
|
|
|
|
{
|
|
|
|
node = node->parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (node->type == SVG_NODE_DOC)
|
|
|
|
return node->node.doc.defs;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Svg_Node*
|
|
|
|
_find_child_by_id(Svg_Node *node, const char *id)
|
|
|
|
{
|
|
|
|
Eina_List *l;
|
|
|
|
Svg_Node *child;
|
|
|
|
|
|
|
|
if (!node) return NULL;
|
|
|
|
|
|
|
|
EINA_LIST_FOREACH(node->child, l, child)
|
|
|
|
{
|
|
|
|
if ((child->id != NULL) && !strcmp(child->id, id))
|
|
|
|
return child;
|
|
|
|
}
|
2016-10-12 02:39:10 -07:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-10-14 03:16:53 -07:00
|
|
|
static Svg_Node* _find_node_by_id(Svg_Node *node, const char* id)
|
|
|
|
{
|
|
|
|
Svg_Node *child, *result = NULL;
|
|
|
|
Eina_List *l;
|
|
|
|
if ((node->id) && !strcmp(node->id, id)) return node;
|
|
|
|
|
|
|
|
EINA_LIST_FOREACH(node->child, l, child)
|
|
|
|
{
|
|
|
|
result = _find_node_by_id(child, id);
|
|
|
|
if (result) break;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-11-02 03:25:45 -07:00
|
|
|
static Eina_List *
|
|
|
|
_clone_grad_stops(Eina_List *from)
|
|
|
|
{
|
|
|
|
Efl_Gfx_Gradient_Stop *stop;
|
|
|
|
Eina_List *l;
|
|
|
|
Eina_List *res = NULL;
|
|
|
|
|
|
|
|
EINA_LIST_FOREACH(from, l, stop)
|
|
|
|
{
|
|
|
|
Efl_Gfx_Gradient_Stop *new_stop;
|
|
|
|
|
|
|
|
new_stop = calloc(1, sizeof(Efl_Gfx_Gradient_Stop));
|
|
|
|
memcpy(new_stop, stop, sizeof(Efl_Gfx_Gradient_Stop));
|
|
|
|
res = eina_list_append(res, new_stop);
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Svg_Style_Gradient *
|
|
|
|
_clone_gradient(Svg_Style_Gradient *from)
|
|
|
|
{
|
|
|
|
Svg_Style_Gradient *grad;
|
|
|
|
|
|
|
|
if (!from) return NULL;
|
|
|
|
|
|
|
|
grad= calloc(1, sizeof(Svg_Style_Gradient));
|
|
|
|
grad->type = from->type;
|
|
|
|
grad->id = _copy_id(from->id);
|
|
|
|
grad->ref = _copy_id(from->ref);
|
|
|
|
grad->spread = from->spread;
|
2019-04-18 21:56:48 -07:00
|
|
|
grad->use_percentage = from->use_percentage;
|
2017-02-14 07:37:27 -08:00
|
|
|
grad->user_space = from->user_space;
|
2019-04-18 23:07:11 -07:00
|
|
|
if (from->transform)
|
|
|
|
{
|
|
|
|
grad->transform = calloc(1, sizeof(Eina_Matrix3));
|
|
|
|
eina_matrix3_copy(grad->transform, from->transform);
|
|
|
|
}
|
2016-11-02 03:25:45 -07:00
|
|
|
grad->stops = _clone_grad_stops(from->stops);
|
|
|
|
if (grad->type == SVG_LINEAR_GRADIENT)
|
|
|
|
{
|
|
|
|
grad->linear = calloc(1, sizeof(Svg_Linear_Gradient));
|
|
|
|
memcpy(grad->linear, from->linear, sizeof(Svg_Linear_Gradient));
|
|
|
|
}
|
|
|
|
else if (grad->type == SVG_RADIAL_GRADIENT)
|
|
|
|
{
|
|
|
|
grad->radial = calloc(1, sizeof(Svg_Radial_Gradient));
|
|
|
|
memcpy(grad->radial, from->radial, sizeof(Svg_Radial_Gradient));
|
|
|
|
}
|
|
|
|
|
|
|
|
return grad;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_copy_attribute(Svg_Node *to, Svg_Node *from)
|
|
|
|
{
|
|
|
|
// copy matrix attribute
|
|
|
|
if (from->transform)
|
|
|
|
{
|
|
|
|
to->transform = calloc(1, sizeof(Eina_Matrix3));
|
|
|
|
eina_matrix3_copy(to->transform, from->transform);
|
|
|
|
}
|
|
|
|
// copy style attribute;
|
|
|
|
memcpy(to->style, from->style, sizeof(Svg_Style_Property));
|
|
|
|
|
|
|
|
// copy node attribute
|
|
|
|
switch (from->type)
|
|
|
|
{
|
|
|
|
case SVG_NODE_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 SVG_NODE_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 SVG_NODE_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;
|
2020-09-09 22:59:29 -07:00
|
|
|
to->node.rect.has_rx = from->node.rect.has_rx;
|
|
|
|
to->node.rect.has_ry = from->node.rect.has_ry;
|
2016-11-02 03:25:45 -07:00
|
|
|
break;
|
|
|
|
case SVG_NODE_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 SVG_NODE_PATH:
|
|
|
|
to->node.path.path = eina_stringshare_add(from->node.path.path);
|
|
|
|
break;
|
|
|
|
case SVG_NODE_POLYGON:
|
|
|
|
to->node.polygon.points_count = from->node.polygon.points_count;
|
2020-10-12 03:03:58 -07:00
|
|
|
to->node.polygon.points = malloc(to->node.polygon.points_count * sizeof(double));
|
vg_load_svg: Add points copy of missing polygon/polyline
Summary:
When using <use> node, do atrribute copy.
At that time, when target(url) is polygon or polyline,
points array is not copied, causing a problem in output.
So, add missing array copy.
Test Plan:
- Test SVG code
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve">
<g opacity="0.5">
<defs>
<polygon id="test" opacity="0.5" points="41.8,14.5 22.2,14.5 22.2,22.8 41.8,40.7"/>
</defs>
<use xlink:href="#test" overflow="visible"/>
</g>
</svg>
Reviewers: Hermet, smohanty
Reviewed By: Hermet
Subscribers: #reviewers, #committers, kimcinoo, herb, cedric
Tags: #efl
Differential Revision: https://phab.enlightenment.org/D12174
2020-10-12 02:36:31 -07:00
|
|
|
memcpy(to->node.polygon.points, from->node.polygon.points, to->node.polygon.points_count * sizeof(double));
|
2016-11-02 03:25:45 -07:00
|
|
|
break;
|
|
|
|
case SVG_NODE_POLYLINE:
|
|
|
|
to->node.polyline.points_count = from->node.polyline.points_count;
|
2020-10-12 03:03:58 -07:00
|
|
|
to->node.polyline.points = malloc(to->node.polyline.points_count * sizeof(double));
|
vg_load_svg: Add points copy of missing polygon/polyline
Summary:
When using <use> node, do atrribute copy.
At that time, when target(url) is polygon or polyline,
points array is not copied, causing a problem in output.
So, add missing array copy.
Test Plan:
- Test SVG code
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve">
<g opacity="0.5">
<defs>
<polygon id="test" opacity="0.5" points="41.8,14.5 22.2,14.5 22.2,22.8 41.8,40.7"/>
</defs>
<use xlink:href="#test" overflow="visible"/>
</g>
</svg>
Reviewers: Hermet, smohanty
Reviewed By: Hermet
Subscribers: #reviewers, #committers, kimcinoo, herb, cedric
Tags: #efl
Differential Revision: https://phab.enlightenment.org/D12174
2020-10-12 02:36:31 -07:00
|
|
|
memcpy(to->node.polyline.points, from->node.polyline.points, to->node.polyline.points_count * sizeof(double));
|
2016-11-02 03:25:45 -07:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_clone_node(Svg_Node *from, Svg_Node *parent)
|
|
|
|
{
|
|
|
|
Svg_Node *new_node;
|
|
|
|
Eina_List *l;
|
|
|
|
Svg_Node *child;
|
|
|
|
|
|
|
|
if (!from) return;
|
|
|
|
|
|
|
|
new_node = _create_node(parent, from->type);
|
|
|
|
_copy_attribute(new_node, from);
|
|
|
|
|
|
|
|
EINA_LIST_FOREACH(from->child, l, child)
|
|
|
|
{
|
|
|
|
_clone_node(child, new_node);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static Eina_Bool
|
|
|
|
_attr_parse_use_node(void *data, const char *key, const char *value)
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
Evas_SVG_Loader *loader = data;
|
|
|
|
Svg_Node *defs, *node_from, *node = loader->svg_parse->node;
|
2016-11-02 03:25:45 -07:00
|
|
|
Eina_Stringshare *id;
|
|
|
|
|
|
|
|
if (!strcmp(key, "xlink:href"))
|
|
|
|
{
|
|
|
|
id = _id_from_href(value);
|
|
|
|
defs = _get_defs_node(node);
|
|
|
|
node_from = _find_child_by_id(defs, id);
|
|
|
|
_clone_node(node_from, node);
|
|
|
|
eina_stringshare_del(id);
|
|
|
|
}
|
2020-10-14 03:16:53 -07:00
|
|
|
else if (!strcmp(key, "clip-path"))
|
|
|
|
{
|
|
|
|
_handle_clip_path_attr(loader, node, value);
|
|
|
|
}
|
2016-11-02 03:25:45 -07:00
|
|
|
else
|
|
|
|
{
|
|
|
|
_attr_parse_g_node(data, key, value);
|
|
|
|
}
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Svg_Node *
|
2017-11-06 18:41:36 -08:00
|
|
|
_create_use_node(Evas_SVG_Loader *loader, Svg_Node *parent, const char *buf, unsigned buflen)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
loader->svg_parse->node = _create_node(parent, SVG_NODE_G);
|
2016-11-02 03:25:45 -07:00
|
|
|
|
|
|
|
eina_simple_xml_attributes_parse(buf, buflen,
|
2017-11-06 18:41:36 -08:00
|
|
|
_attr_parse_use_node, loader);
|
|
|
|
return loader->svg_parse->node;
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#define TAG_DEF(Name) \
|
|
|
|
{ #Name, sizeof (#Name), _create_##Name##_node }
|
|
|
|
|
2017-02-17 03:00:54 -08:00
|
|
|
//TODO: implement 'text' primitive
|
2016-11-02 03:25:45 -07:00
|
|
|
static const struct {
|
|
|
|
const char *tag;
|
|
|
|
int sz;
|
|
|
|
Factory_Method tag_handler;
|
|
|
|
} graphics_tags[] = {
|
|
|
|
TAG_DEF(use),
|
|
|
|
TAG_DEF(circle),
|
|
|
|
TAG_DEF(ellipse),
|
|
|
|
TAG_DEF(path),
|
|
|
|
TAG_DEF(polygon),
|
|
|
|
TAG_DEF(rect),
|
|
|
|
TAG_DEF(polyline),
|
|
|
|
TAG_DEF(line),
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct {
|
|
|
|
const char *tag;
|
|
|
|
int sz;
|
|
|
|
Factory_Method tag_handler;
|
|
|
|
} group_tags[] = {
|
|
|
|
TAG_DEF(defs),
|
|
|
|
TAG_DEF(g),
|
|
|
|
TAG_DEF(svg),
|
2020-09-10 19:49:09 -07:00
|
|
|
TAG_DEF(switch),
|
|
|
|
TAG_DEF(mask),
|
|
|
|
TAG_DEF(clipPath)
|
2016-11-02 03:25:45 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
#define FIND_FACTORY(Short_Name, Tags_Array) \
|
|
|
|
static Factory_Method \
|
|
|
|
_find_##Short_Name##_factory(const char *name) \
|
|
|
|
{ \
|
|
|
|
unsigned int i; \
|
|
|
|
int sz = strlen(name); \
|
|
|
|
\
|
|
|
|
for (i = 0; i < sizeof (Tags_Array) / sizeof(Tags_Array[0]); i++) \
|
|
|
|
if (Tags_Array[i].sz - 1 == sz && !strncmp(Tags_Array[i].tag, name, sz)) \
|
|
|
|
{ \
|
|
|
|
return Tags_Array[i].tag_handler; \
|
|
|
|
} \
|
|
|
|
return NULL; \
|
|
|
|
}
|
|
|
|
|
|
|
|
FIND_FACTORY(group, group_tags);
|
|
|
|
FIND_FACTORY(graphics, graphics_tags);
|
|
|
|
|
|
|
|
Efl_Gfx_Gradient_Spread
|
|
|
|
_parse_spread_value(const char *value)
|
|
|
|
{
|
|
|
|
Efl_Gfx_Gradient_Spread spread = EFL_GFX_GRADIENT_SPREAD_PAD;
|
|
|
|
|
|
|
|
if (!strcmp(value, "reflect"))
|
|
|
|
{
|
|
|
|
spread = EFL_GFX_GRADIENT_SPREAD_REFLECT;
|
|
|
|
}
|
|
|
|
else if (!strcmp(value, "repeat"))
|
|
|
|
{
|
|
|
|
spread = EFL_GFX_GRADIENT_SPREAD_REPEAT;
|
|
|
|
}
|
|
|
|
|
|
|
|
return spread;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-11-06 18:41:36 -08:00
|
|
|
_handle_radial_cx_attr(Evas_SVG_Loader *loader, Svg_Radial_Gradient* radial, const char *value)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
radial->cx = _gradient_to_double(loader->svg_parse, value, SVG_PARSER_LENGTH_HORIZONTAL);
|
|
|
|
if (!loader->svg_parse->gradient.fx_parsed)
|
2017-02-07 08:59:03 -08:00
|
|
|
radial->fx = radial->cx;
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-11-06 18:41:36 -08:00
|
|
|
_handle_radial_cy_attr(Evas_SVG_Loader *loader, Svg_Radial_Gradient* radial, const char *value)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
radial->cy = _gradient_to_double(loader->svg_parse, value, SVG_PARSER_LENGTH_VERTICAL);
|
|
|
|
if (!loader->svg_parse->gradient.fy_parsed)
|
2017-02-07 08:59:03 -08:00
|
|
|
radial->fy = radial->cy;
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-11-06 18:41:36 -08:00
|
|
|
_handle_radial_fx_attr(Evas_SVG_Loader *loader, Svg_Radial_Gradient* radial, const char *value)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
radial->fx = _gradient_to_double(loader->svg_parse, value, SVG_PARSER_LENGTH_HORIZONTAL);
|
|
|
|
loader->svg_parse->gradient.fx_parsed = EINA_TRUE;
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-11-06 18:41:36 -08:00
|
|
|
_handle_radial_fy_attr(Evas_SVG_Loader *loader, Svg_Radial_Gradient* radial, const char *value)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
radial->fy = _gradient_to_double(loader->svg_parse, value, SVG_PARSER_LENGTH_VERTICAL);
|
|
|
|
loader->svg_parse->gradient.fy_parsed = EINA_TRUE;
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-11-06 18:41:36 -08:00
|
|
|
_handle_radial_r_attr(Evas_SVG_Loader *loader, Svg_Radial_Gradient* radial, const char *value)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
radial->r = _gradient_to_double(loader->svg_parse, value, SVG_PARSER_LENGTH_OTHER);
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
2019-07-15 05:22:03 -07:00
|
|
|
static void
|
|
|
|
_recalc_radial_cx_attr(Evas_SVG_Loader *loader, Svg_Radial_Gradient* radial, Eina_Bool user_space)
|
|
|
|
{
|
|
|
|
if (!user_space)
|
|
|
|
{
|
|
|
|
radial->cx = radial->cx * loader->svg_parse->global.width;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_recalc_radial_cy_attr(Evas_SVG_Loader *loader, Svg_Radial_Gradient* radial, Eina_Bool user_space)
|
|
|
|
{
|
|
|
|
if (!user_space)
|
|
|
|
{
|
|
|
|
radial->cy = radial->cy * loader->svg_parse->global.height;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_recalc_radial_fx_attr(Evas_SVG_Loader *loader, Svg_Radial_Gradient* radial, Eina_Bool user_space)
|
|
|
|
{
|
|
|
|
if (!user_space)
|
|
|
|
{
|
|
|
|
radial->fx = radial->fx * loader->svg_parse->global.width;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_recalc_radial_fy_attr(Evas_SVG_Loader *loader, Svg_Radial_Gradient* radial, Eina_Bool user_space)
|
|
|
|
{
|
|
|
|
if (!user_space)
|
|
|
|
{
|
|
|
|
radial->fy = radial->fy * loader->svg_parse->global.height;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_recalc_radial_r_attr(Evas_SVG_Loader *loader, Svg_Radial_Gradient* radial, Eina_Bool user_space)
|
|
|
|
{
|
|
|
|
if (!user_space)
|
|
|
|
{
|
|
|
|
radial->r = radial->r * (sqrt(pow(loader->svg_parse->global.height, 2) + pow(loader->svg_parse->global.width, 2)) / sqrt(2.0));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-11-06 18:41:36 -08:00
|
|
|
typedef void (*Radial_Method)(Evas_SVG_Loader *loader, Svg_Radial_Gradient *radial, const char *value);
|
2019-07-15 05:22:03 -07:00
|
|
|
typedef void (*Radial_Method_Recalc)(Evas_SVG_Loader *loader, Svg_Radial_Gradient *radial, Eina_Bool user_space);
|
2016-11-02 03:25:45 -07:00
|
|
|
|
|
|
|
#define RADIAL_DEF(Name) \
|
2019-07-15 05:22:03 -07:00
|
|
|
{ #Name, sizeof (#Name), _handle_radial_##Name##_attr, _recalc_radial_##Name##_attr}
|
2016-11-02 03:25:45 -07:00
|
|
|
|
|
|
|
static const struct {
|
|
|
|
const char *tag;
|
|
|
|
int sz;
|
|
|
|
Radial_Method tag_handler;;
|
2019-07-15 05:22:03 -07:00
|
|
|
Radial_Method_Recalc tag_recalc;
|
2016-11-02 03:25:45 -07:00
|
|
|
} radial_tags[] = {
|
|
|
|
RADIAL_DEF(cx),
|
|
|
|
RADIAL_DEF(cy),
|
|
|
|
RADIAL_DEF(fx),
|
|
|
|
RADIAL_DEF(fy),
|
|
|
|
RADIAL_DEF(r)
|
|
|
|
};
|
|
|
|
|
|
|
|
static Eina_Bool
|
|
|
|
_attr_parse_radial_gradient_node(void *data, const char *key, const char *value)
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
Evas_SVG_Loader *loader = data;
|
|
|
|
Svg_Style_Gradient *grad = loader->svg_parse->style_grad;
|
2016-11-02 03:25:45 -07:00
|
|
|
Svg_Radial_Gradient *radial = grad->radial;
|
|
|
|
unsigned int i;
|
|
|
|
int sz = strlen(key);
|
|
|
|
|
|
|
|
for (i = 0; i < sizeof (radial_tags) / sizeof(radial_tags[0]); i++)
|
|
|
|
if (radial_tags[i].sz - 1 == sz && !strncmp(radial_tags[i].tag, key, sz))
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
radial_tags[i].tag_handler(loader, radial, value);
|
2016-11-02 03:25:45 -07:00
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp(key, "id"))
|
|
|
|
{
|
|
|
|
grad->id = _copy_id(value);
|
|
|
|
}
|
|
|
|
else if (!strcmp(key, "spreadMethod"))
|
|
|
|
{
|
|
|
|
grad->spread = _parse_spread_value(value);
|
|
|
|
}
|
|
|
|
else if (!strcmp(key, "xlink:href"))
|
|
|
|
{
|
|
|
|
grad->ref = _id_from_href(value);
|
|
|
|
}
|
2019-04-10 02:03:05 -07:00
|
|
|
else if (!strcmp(key, "gradientUnits") && !strcmp(value, "userSpaceOnUse"))
|
2017-02-14 07:37:27 -08:00
|
|
|
{
|
2019-04-10 02:03:05 -07:00
|
|
|
grad->user_space = EINA_TRUE;
|
2017-02-14 07:37:27 -08:00
|
|
|
}
|
2016-11-02 03:25:45 -07:00
|
|
|
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Svg_Style_Gradient *
|
2017-11-06 18:41:36 -08:00
|
|
|
_create_radialGradient(Evas_SVG_Loader *loader, const char *buf, unsigned buflen)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
2019-07-15 05:22:03 -07:00
|
|
|
unsigned int i = 0;
|
2016-11-02 03:25:45 -07:00
|
|
|
Svg_Style_Gradient *grad = calloc(1, sizeof(Svg_Style_Gradient));
|
2017-11-06 18:41:36 -08:00
|
|
|
loader->svg_parse->style_grad = grad;
|
2016-11-02 03:25:45 -07:00
|
|
|
|
|
|
|
grad->type = SVG_RADIAL_GRADIENT;
|
2019-04-10 02:03:05 -07:00
|
|
|
grad->user_space = EINA_FALSE;
|
2016-11-02 03:25:45 -07:00
|
|
|
grad->radial = calloc(1, sizeof(Svg_Radial_Gradient));
|
2017-03-16 05:53:49 -07:00
|
|
|
/**
|
|
|
|
* Default values of gradient
|
|
|
|
*/
|
|
|
|
grad->radial->cx = 0.5;
|
|
|
|
grad->radial->cy = 0.5;
|
|
|
|
grad->radial->fx = 0.5;
|
|
|
|
grad->radial->fy = 0.5;
|
|
|
|
grad->radial->r = 0.5;
|
2017-02-07 08:59:03 -08:00
|
|
|
|
2017-11-06 18:41:36 -08:00
|
|
|
loader->svg_parse->gradient.fx_parsed = EINA_FALSE;
|
|
|
|
loader->svg_parse->gradient.fy_parsed = EINA_FALSE;
|
2016-11-02 03:25:45 -07:00
|
|
|
eina_simple_xml_attributes_parse(buf, buflen,
|
2017-11-06 18:41:36 -08:00
|
|
|
_attr_parse_radial_gradient_node, loader);
|
2016-11-02 03:25:45 -07:00
|
|
|
|
2019-07-15 05:22:03 -07:00
|
|
|
for (i = 0; i < sizeof (radial_tags) / sizeof(radial_tags[0]); i++)
|
|
|
|
radial_tags[i].tag_recalc(loader, grad->radial, grad->user_space);
|
|
|
|
|
|
|
|
grad->use_percentage = EINA_TRUE;
|
|
|
|
|
2017-11-06 18:41:36 -08:00
|
|
|
return loader->svg_parse->style_grad;
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static Eina_Bool
|
|
|
|
_attr_parse_stops(void *data, const char *key, const char *value)
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
Evas_SVG_Loader *loader = data;
|
|
|
|
Efl_Gfx_Gradient_Stop *stop = loader->svg_parse->grad_stop;
|
2016-11-02 03:25:45 -07:00
|
|
|
|
|
|
|
if (!strcmp(key, "offset"))
|
|
|
|
{
|
2017-02-17 03:00:54 -08:00
|
|
|
stop->offset = _to_offset(value);
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
else if (!strcmp(key, "stop-opacity"))
|
|
|
|
{
|
|
|
|
stop->a = _to_opacity(value);
|
|
|
|
}
|
|
|
|
else if (!strcmp(key, "stop-color"))
|
|
|
|
{
|
|
|
|
_to_color(value, &stop->r, &stop->g, &stop->b, NULL);
|
|
|
|
}
|
|
|
|
else if (!strcmp(key, "style"))
|
|
|
|
{
|
|
|
|
eina_simple_xml_attribute_w3c_parse(value,
|
|
|
|
_attr_parse_stops, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-11-06 18:41:36 -08:00
|
|
|
_handle_linear_x1_attr(Evas_SVG_Loader *loader, Svg_Linear_Gradient* linear, const char *value)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
linear->x1 = _gradient_to_double(loader->svg_parse, value, SVG_PARSER_LENGTH_HORIZONTAL);
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-11-06 18:41:36 -08:00
|
|
|
_handle_linear_y1_attr(Evas_SVG_Loader *loader, Svg_Linear_Gradient* linear, const char *value)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
linear->y1 = _gradient_to_double(loader->svg_parse, value, SVG_PARSER_LENGTH_VERTICAL);
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-11-06 18:41:36 -08:00
|
|
|
_handle_linear_x2_attr(Evas_SVG_Loader *loader, Svg_Linear_Gradient* linear, const char *value)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
linear->x2 = _gradient_to_double(loader->svg_parse, value, SVG_PARSER_LENGTH_HORIZONTAL);
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-11-06 18:41:36 -08:00
|
|
|
_handle_linear_y2_attr(Evas_SVG_Loader *loader, Svg_Linear_Gradient* linear, const char *value)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
linear->y2 = _gradient_to_double(loader->svg_parse, value, SVG_PARSER_LENGTH_VERTICAL);
|
2017-02-17 07:00:01 -08:00
|
|
|
}
|
2016-11-02 03:25:45 -07:00
|
|
|
|
2019-04-18 21:56:48 -07:00
|
|
|
static void
|
|
|
|
_recalc_linear_x1_attr(Evas_SVG_Loader *loader, Svg_Linear_Gradient* linear, Eina_Bool user_space)
|
|
|
|
{
|
|
|
|
if (!user_space)
|
|
|
|
{
|
|
|
|
linear->x1 = linear->x1 * loader->svg_parse->global.width;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_recalc_linear_y1_attr(Evas_SVG_Loader *loader, Svg_Linear_Gradient* linear, Eina_Bool user_space)
|
|
|
|
{
|
|
|
|
if (!user_space)
|
|
|
|
{
|
|
|
|
linear->y1 = linear->y1 * loader->svg_parse->global.height;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_recalc_linear_x2_attr(Evas_SVG_Loader *loader, Svg_Linear_Gradient* linear, Eina_Bool user_space)
|
|
|
|
{
|
|
|
|
if (!user_space)
|
|
|
|
{
|
|
|
|
linear->x2 = linear->x2 * loader->svg_parse->global.width;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_recalc_linear_y2_attr(Evas_SVG_Loader *loader, Svg_Linear_Gradient* linear, Eina_Bool user_space)
|
|
|
|
{
|
|
|
|
if (!user_space)
|
|
|
|
{
|
|
|
|
linear->y2 = linear->y2 * loader->svg_parse->global.height;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-06 18:41:36 -08:00
|
|
|
typedef void (*Linear_Method)(Evas_SVG_Loader *loader, Svg_Linear_Gradient *linear, const char *value);
|
2019-04-18 21:56:48 -07:00
|
|
|
typedef void (*Linear_Method_Recalc)(Evas_SVG_Loader *loader, Svg_Linear_Gradient *linear, Eina_Bool user_space);
|
2016-11-02 03:25:45 -07:00
|
|
|
|
|
|
|
#define LINEAR_DEF(Name) \
|
2019-04-18 21:56:48 -07:00
|
|
|
{ #Name, sizeof (#Name), _handle_linear_##Name##_attr, _recalc_linear_##Name##_attr}
|
2016-11-02 03:25:45 -07:00
|
|
|
|
|
|
|
static const struct {
|
|
|
|
const char *tag;
|
|
|
|
int sz;
|
2019-04-18 21:56:48 -07:00
|
|
|
Linear_Method tag_handler;
|
|
|
|
Linear_Method_Recalc tag_recalc;
|
2016-11-02 03:25:45 -07:00
|
|
|
} linear_tags[] = {
|
|
|
|
LINEAR_DEF(x1),
|
|
|
|
LINEAR_DEF(y1),
|
|
|
|
LINEAR_DEF(x2),
|
|
|
|
LINEAR_DEF(y2)
|
|
|
|
};
|
|
|
|
|
|
|
|
static Eina_Bool
|
|
|
|
_attr_parse_linear_gradient_node(void *data, const char *key, const char *value)
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
Evas_SVG_Loader *loader = data;
|
|
|
|
Svg_Style_Gradient *grad = loader->svg_parse->style_grad;
|
2016-11-02 03:25:45 -07:00
|
|
|
Svg_Linear_Gradient *linear = grad->linear;
|
|
|
|
unsigned int i;
|
|
|
|
int sz = strlen(key);
|
|
|
|
|
|
|
|
for (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))
|
|
|
|
{
|
2017-11-06 18:41:36 -08:00
|
|
|
linear_tags[i].tag_handler(loader, linear, value);
|
2016-11-02 03:25:45 -07:00
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp(key, "id"))
|
|
|
|
{
|
|
|
|
grad->id = _copy_id(value);
|
|
|
|
}
|
|
|
|
else if (!strcmp(key, "spreadMethod"))
|
|
|
|
{
|
|
|
|
grad->spread = _parse_spread_value(value);
|
|
|
|
}
|
|
|
|
else if (!strcmp(key, "xlink:href"))
|
|
|
|
{
|
|
|
|
grad->ref = _id_from_href(value);
|
|
|
|
}
|
2019-04-10 02:03:05 -07:00
|
|
|
else if (!strcmp(key, "gradientUnits") && !strcmp(value, "userSpaceOnUse"))
|
2017-02-14 07:37:27 -08:00
|
|
|
{
|
|
|
|
grad->user_space = EINA_TRUE;
|
|
|
|
}
|
2019-04-18 23:07:11 -07:00
|
|
|
else if (!strcmp(key, "gradientTransform"))
|
|
|
|
{
|
|
|
|
grad->transform = _parse_transformation_matrix(value);
|
|
|
|
}
|
2016-11-02 03:25:45 -07:00
|
|
|
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Svg_Style_Gradient *
|
2017-11-06 18:41:36 -08:00
|
|
|
_create_linearGradient(Evas_SVG_Loader *loader, const char *buf, unsigned buflen)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
|
|
|
Svg_Style_Gradient *grad = calloc(1, sizeof(Svg_Style_Gradient));
|
2017-11-06 18:41:36 -08:00
|
|
|
loader->svg_parse->style_grad = grad;
|
2019-04-18 21:56:48 -07:00
|
|
|
unsigned int i;
|
2017-11-06 18:41:36 -08:00
|
|
|
|
2016-11-02 03:25:45 -07:00
|
|
|
grad->type = SVG_LINEAR_GRADIENT;
|
2019-04-10 02:03:05 -07:00
|
|
|
grad->user_space = EINA_FALSE;
|
2016-11-02 03:25:45 -07:00
|
|
|
grad->linear = calloc(1, sizeof(Svg_Linear_Gradient));
|
2017-02-17 07:00:01 -08:00
|
|
|
/**
|
|
|
|
* Default value of x2 is 100%
|
|
|
|
*/
|
|
|
|
grad->linear->x2 = 1;
|
2016-11-02 03:25:45 -07:00
|
|
|
eina_simple_xml_attributes_parse(buf, buflen,
|
2017-11-06 18:41:36 -08:00
|
|
|
_attr_parse_linear_gradient_node, loader);
|
2016-11-02 03:25:45 -07:00
|
|
|
|
2019-04-18 21:56:48 -07:00
|
|
|
for (i = 0; i < sizeof (linear_tags) / sizeof(linear_tags[0]); i++)
|
|
|
|
linear_tags[i].tag_recalc(loader, grad->linear, grad->user_space);
|
|
|
|
|
|
|
|
grad->use_percentage = EINA_TRUE;
|
|
|
|
|
2017-11-06 18:41:36 -08:00
|
|
|
return loader->svg_parse->style_grad;
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#define GRADIENT_DEF(Name) \
|
|
|
|
{ #Name, sizeof (#Name), _create_##Name }
|
|
|
|
|
2017-02-17 07:00:01 -08:00
|
|
|
/**
|
|
|
|
* For all Gradients lengths would be calculated into percentages related to
|
|
|
|
* canvas width and height.
|
|
|
|
*
|
|
|
|
* if user then recalculate actual pixels into percentages
|
|
|
|
*/
|
2016-11-02 03:25:45 -07:00
|
|
|
static const struct {
|
|
|
|
const char *tag;
|
|
|
|
int sz;
|
|
|
|
Gradient_Factory_Method tag_handler;
|
|
|
|
} gradient_tags[] = {
|
|
|
|
GRADIENT_DEF(linearGradient),
|
|
|
|
GRADIENT_DEF(radialGradient)
|
|
|
|
};
|
|
|
|
|
|
|
|
static Gradient_Factory_Method
|
|
|
|
_find_gradient_factory(const char *name)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
int sz = strlen(name);
|
|
|
|
|
|
|
|
for (i = 0; i < sizeof (gradient_tags) / sizeof(gradient_tags[0]); i++)
|
|
|
|
if (gradient_tags[i].sz - 1 == sz && !strncmp(gradient_tags[i].tag, name, sz))
|
|
|
|
{
|
|
|
|
return gradient_tags[i].tag_handler;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-09-03 19:14:58 -07:00
|
|
|
static Svg_Node*
|
|
|
|
_get_parent_node_from_loader(Evas_SVG_Loader *loader)
|
|
|
|
{
|
|
|
|
if (eina_array_count(loader->stack) > 0)
|
|
|
|
return eina_array_data_get(loader->stack, eina_array_count(loader->stack) - 1);
|
|
|
|
else
|
|
|
|
return loader->doc;
|
|
|
|
}
|
|
|
|
|
2016-11-02 03:25:45 -07:00
|
|
|
static void
|
|
|
|
_evas_svg_loader_xml_open_parser(Evas_SVG_Loader *loader,
|
2020-09-03 19:14:58 -07:00
|
|
|
const char *content, unsigned int length, Eina_Bool empty)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
|
|
|
const char *attrs = NULL;
|
|
|
|
int attrs_length = 0;
|
|
|
|
int sz = length;
|
|
|
|
char tag_name[20] = "";
|
|
|
|
Factory_Method method;
|
|
|
|
Gradient_Factory_Method gradient_method;
|
|
|
|
Svg_Node *node = NULL, *parent;
|
|
|
|
loader->level++;
|
|
|
|
attrs = eina_simple_xml_tag_attributes_find(content, length);
|
|
|
|
|
|
|
|
if (!attrs)
|
|
|
|
{
|
|
|
|
// parse the empty tag
|
|
|
|
attrs = content;
|
|
|
|
while ((attrs != NULL) && *attrs != '>')
|
|
|
|
attrs++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (attrs)
|
|
|
|
{
|
|
|
|
// find out the tag name starting from content till sz length
|
|
|
|
sz = attrs - content;
|
|
|
|
attrs_length = length - sz;
|
|
|
|
while ((sz > 0) && (isspace(content[sz - 1])))
|
|
|
|
sz--;
|
2022-01-10 16:41:34 -08:00
|
|
|
if ((unsigned int)sz >= sizeof(tag_name)) return;
|
2016-11-02 03:25:45 -07:00
|
|
|
strncpy(tag_name, content, sz);
|
|
|
|
tag_name[sz] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((method = _find_group_factory(tag_name)))
|
|
|
|
{
|
|
|
|
//group
|
|
|
|
if (!loader->doc)
|
|
|
|
{
|
|
|
|
if (strcmp(tag_name, "svg"))
|
|
|
|
return; // Not a valid svg document
|
2017-11-06 18:41:36 -08:00
|
|
|
node = method(loader, NULL, attrs, attrs_length);
|
2016-11-02 03:25:45 -07:00
|
|
|
loader->doc = node;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-10-14 03:16:53 -07:00
|
|
|
if (!strcmp(tag_name, "svg")) return; //Already loadded <svg>(SvgNodeType::Doc) tag
|
2020-09-03 19:14:58 -07:00
|
|
|
parent = _get_parent_node_from_loader(loader);
|
2017-11-06 18:41:36 -08:00
|
|
|
node = method(loader, parent, attrs, attrs_length);
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (node->type == SVG_NODE_DEFS)
|
2020-09-03 19:14:58 -07:00
|
|
|
{
|
|
|
|
loader->doc->node.doc.defs = node;
|
|
|
|
loader->def = node;
|
|
|
|
if (!empty) eina_array_push(loader->stack, node);
|
|
|
|
}
|
|
|
|
else eina_array_push(loader->stack, node);
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
else if ((method = _find_graphics_factory(tag_name)))
|
|
|
|
{
|
2020-09-03 19:14:58 -07:00
|
|
|
parent = _get_parent_node_from_loader(loader);
|
2017-11-06 18:41:36 -08:00
|
|
|
node = method(loader, parent, attrs, attrs_length);
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
else if ((gradient_method = _find_gradient_factory(tag_name)))
|
|
|
|
{
|
|
|
|
Svg_Style_Gradient *gradient;
|
2017-11-06 18:41:36 -08:00
|
|
|
gradient = gradient_method(loader, attrs, attrs_length);
|
2019-07-15 05:23:38 -07:00
|
|
|
/*FIXME: The current parsing structure does not distinguish end tags.
|
|
|
|
There is no way to know if the currently parsed gradient is in defs.
|
|
|
|
If a gradient is declared outside of defs after defs is set, it is included in the gradients of defs.
|
|
|
|
But finally, the loader has a gradient style list regardless of defs.
|
|
|
|
This is only to support this when multiple gradients are declared, even if no defs are declared.
|
|
|
|
refer to: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs */
|
2016-11-02 03:25:45 -07:00
|
|
|
if (loader->doc->node.doc.defs)
|
|
|
|
{
|
|
|
|
loader->def->node.defs.gradients = eina_list_append(loader->def->node.defs.gradients, gradient);
|
|
|
|
}
|
2019-07-15 05:23:38 -07:00
|
|
|
else
|
|
|
|
{
|
|
|
|
loader->gradients = eina_list_append(loader->gradients, gradient);
|
|
|
|
}
|
|
|
|
loader->latest_gradient = gradient;
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
else if (!strcmp(tag_name, "stop"))
|
|
|
|
{
|
|
|
|
Efl_Gfx_Gradient_Stop *stop = calloc(1, sizeof(Efl_Gfx_Gradient_Stop));
|
2017-11-06 18:41:36 -08:00
|
|
|
loader->svg_parse->grad_stop = stop;
|
2017-03-16 08:55:26 -07:00
|
|
|
/* default value for opacity */
|
|
|
|
stop->a = 255;
|
2016-11-02 03:25:45 -07:00
|
|
|
eina_simple_xml_attributes_parse(attrs, attrs_length,
|
2017-11-06 18:41:36 -08:00
|
|
|
_attr_parse_stops, loader);
|
2019-07-15 05:23:38 -07:00
|
|
|
if (loader->latest_gradient)
|
|
|
|
{
|
|
|
|
loader->latest_gradient->stops = eina_list_append(loader->latest_gradient->stops, stop);
|
|
|
|
}
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#define POP_TAG(Tag) \
|
|
|
|
{ #Tag, sizeof (#Tag) }
|
|
|
|
|
|
|
|
static const struct {
|
|
|
|
const char *tag;
|
|
|
|
size_t sz;
|
|
|
|
} pop_array[] = {
|
|
|
|
POP_TAG(g),
|
|
|
|
POP_TAG(svg),
|
2020-09-10 19:49:09 -07:00
|
|
|
POP_TAG(defs),
|
|
|
|
POP_TAG(mask),
|
|
|
|
POP_TAG(clipPath)
|
2016-11-02 03:25:45 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
_evas_svg_loader_xml_close_parser(Evas_SVG_Loader *loader,
|
|
|
|
const char *content,
|
|
|
|
unsigned int length EINA_UNUSED)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
content = _skip_space(content, NULL);
|
|
|
|
|
|
|
|
for (i = 0; i < sizeof (pop_array) / sizeof (pop_array[0]); i++)
|
|
|
|
if (!strncmp(content, pop_array[i].tag, pop_array[i].sz - 1))
|
|
|
|
{
|
|
|
|
eina_array_pop(loader->stack);
|
|
|
|
break ;
|
|
|
|
}
|
|
|
|
|
|
|
|
loader->level--;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Eina_Bool
|
|
|
|
_evas_svg_loader_parser(void *data, Eina_Simple_XML_Type type,
|
|
|
|
const char *content,
|
|
|
|
unsigned int offset EINA_UNUSED, unsigned int length)
|
|
|
|
{
|
|
|
|
Evas_SVG_Loader *loader = data;
|
|
|
|
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case EINA_SIMPLE_XML_OPEN:
|
2020-09-03 19:14:58 -07:00
|
|
|
_evas_svg_loader_xml_open_parser(loader, content, length, EINA_FALSE);
|
2016-11-02 03:25:45 -07:00
|
|
|
break;
|
|
|
|
case EINA_SIMPLE_XML_OPEN_EMPTY:
|
2020-09-03 19:14:58 -07:00
|
|
|
_evas_svg_loader_xml_open_parser(loader, content, length, EINA_TRUE);
|
2016-11-02 03:25:45 -07:00
|
|
|
break;
|
|
|
|
case EINA_SIMPLE_XML_CLOSE:
|
|
|
|
_evas_svg_loader_xml_close_parser(loader, content, length);
|
|
|
|
break;
|
|
|
|
case EINA_SIMPLE_XML_DATA:
|
|
|
|
case EINA_SIMPLE_XML_CDATA:
|
|
|
|
case EINA_SIMPLE_XML_DOCTYPE_CHILD:
|
|
|
|
break;
|
|
|
|
case EINA_SIMPLE_XML_IGNORED:
|
|
|
|
case EINA_SIMPLE_XML_COMMENT:
|
|
|
|
case EINA_SIMPLE_XML_DOCTYPE:
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_inherit_style(Svg_Style_Property *child, Svg_Style_Property *parent)
|
|
|
|
{
|
|
|
|
if (parent == NULL)
|
|
|
|
return;
|
2020-06-20 02:54:26 -07:00
|
|
|
// inherit the property of parent if not present in child.
|
2016-11-02 03:25:45 -07:00
|
|
|
// fill
|
|
|
|
if (!(child->fill.flags & SVG_FILL_FLAGS_PAINT))
|
|
|
|
{
|
|
|
|
child->fill.paint.r = parent->fill.paint.r;
|
|
|
|
child->fill.paint.g = parent->fill.paint.g;
|
|
|
|
child->fill.paint.b = parent->fill.paint.b;
|
|
|
|
child->fill.paint.none = parent->fill.paint.none;
|
|
|
|
child->fill.paint.cur_color = parent->fill.paint.cur_color;
|
|
|
|
child->fill.paint.url = _copy_id(parent->fill.paint.url);
|
|
|
|
}
|
|
|
|
if (!(child->fill.flags & SVG_FILL_FLAGS_OPACITY))
|
|
|
|
{
|
|
|
|
child->fill.opacity = parent->fill.opacity;
|
|
|
|
}
|
|
|
|
if (!(child->fill.flags & SVG_FILL_FLAGS_FILL_RULE))
|
|
|
|
{
|
|
|
|
child->fill.fill_rule = parent->fill.fill_rule;
|
|
|
|
}
|
|
|
|
// stroke
|
|
|
|
if (!(child->stroke.flags & SVG_STROKE_FLAGS_PAINT))
|
|
|
|
{
|
|
|
|
child->stroke.paint.r = parent->stroke.paint.r;
|
|
|
|
child->stroke.paint.g = parent->stroke.paint.g;
|
|
|
|
child->stroke.paint.b = parent->stroke.paint.b;
|
|
|
|
child->stroke.paint.none = parent->stroke.paint.none;
|
|
|
|
child->stroke.paint.cur_color = parent->stroke.paint.cur_color;
|
|
|
|
child->stroke.paint.url = _copy_id(parent->stroke.paint.url);
|
|
|
|
}
|
|
|
|
if (!(child->stroke.flags & SVG_STROKE_FLAGS_OPACITY))
|
|
|
|
{
|
|
|
|
child->stroke.opacity = parent->stroke.opacity;
|
|
|
|
}
|
|
|
|
if (!(child->stroke.flags & SVG_STROKE_FLAGS_WIDTH))
|
|
|
|
{
|
|
|
|
child->stroke.width = parent->stroke.width;
|
|
|
|
}
|
|
|
|
if (!(child->stroke.flags & SVG_STROKE_FLAGS_CAP))
|
|
|
|
{
|
|
|
|
child->stroke.cap = parent->stroke.cap;
|
|
|
|
}
|
|
|
|
if (!(child->stroke.flags & SVG_STROKE_FLAGS_JOIN))
|
|
|
|
{
|
|
|
|
child->stroke.join = parent->stroke.join;
|
|
|
|
}
|
2020-09-14 20:11:49 -07:00
|
|
|
if (!(child->stroke.flags & SVG_STROKE_FLAGS_DASH))
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
int count = parent->stroke.dash_count;
|
|
|
|
if (count > 0)
|
|
|
|
{
|
|
|
|
if (child->stroke.dash) free(child->stroke.dash);
|
|
|
|
child->stroke.dash = calloc(count, sizeof(Efl_Gfx_Dash));
|
|
|
|
child->stroke.dash_count = count;
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
child->stroke.dash[i].length = parent->stroke.dash[i].length;
|
|
|
|
child->stroke.dash[i].gap = parent->stroke.dash[i].gap;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
_update_style(Svg_Node *node, Svg_Style_Property *parent_style)
|
|
|
|
{
|
|
|
|
Eina_List *l;
|
|
|
|
Svg_Node *child;
|
|
|
|
|
|
|
|
_inherit_style(node->style, parent_style);
|
|
|
|
|
|
|
|
EINA_LIST_FOREACH(node->child, l, child)
|
|
|
|
{
|
|
|
|
_update_style(child, node->style);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static Svg_Style_Gradient*
|
|
|
|
_dup_gradient(Eina_List *grad_list, const char *id)
|
|
|
|
{
|
|
|
|
Svg_Style_Gradient *grad;
|
|
|
|
Svg_Style_Gradient *result = NULL;
|
|
|
|
Eina_List *l;
|
|
|
|
|
|
|
|
EINA_LIST_FOREACH(grad_list, l, grad)
|
|
|
|
{
|
|
|
|
if (!strcmp(grad->id, id))
|
|
|
|
{
|
|
|
|
result = _clone_gradient(grad);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result && result->ref)
|
|
|
|
{
|
|
|
|
EINA_LIST_FOREACH(grad_list, l, grad)
|
|
|
|
{
|
|
|
|
if (!strcmp(grad->id, result->ref))
|
|
|
|
{
|
|
|
|
if (!result->stops)
|
|
|
|
{
|
|
|
|
result->stops = _clone_grad_stops(grad->stops);
|
|
|
|
}
|
|
|
|
//TODO properly inherit other property
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
_update_gradient(Svg_Node *node, Eina_List *grad_list)
|
|
|
|
{
|
|
|
|
Eina_List *l;
|
|
|
|
Svg_Node *child;
|
|
|
|
|
|
|
|
if (node->child)
|
|
|
|
{
|
|
|
|
EINA_LIST_FOREACH(node->child, l, child)
|
|
|
|
{
|
|
|
|
_update_gradient(child, grad_list);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (node->style->fill.paint.url)
|
|
|
|
{
|
|
|
|
node->style->fill.paint.gradient = _dup_gradient(grad_list, node->style->fill.paint.url);
|
|
|
|
}
|
|
|
|
else if (node->style->stroke.paint.url)
|
|
|
|
{
|
|
|
|
node->style->stroke.paint.gradient = _dup_gradient(grad_list, node->style->stroke.paint.url);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-10-14 03:16:53 -07:00
|
|
|
|
|
|
|
static void _update_composite(Svg_Node* node, Svg_Node* root)
|
|
|
|
{
|
|
|
|
Svg_Node *child;
|
|
|
|
Eina_List *l;
|
|
|
|
if (node->style->comp.url && !node->style->comp.node) {
|
|
|
|
Svg_Node *findResult = _find_node_by_id(root, node->style->comp.url);
|
|
|
|
if (findResult) node->style->comp.node = findResult;
|
|
|
|
}
|
|
|
|
EINA_LIST_FOREACH(node->child, l, child)
|
|
|
|
{
|
|
|
|
_update_composite(child, root);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-07 01:42:45 -08:00
|
|
|
static Eina_Bool
|
|
|
|
evas_vg_load_file_data_svg(Vg_File_Data *vfd EINA_UNUSED)
|
|
|
|
{
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Eina_Bool
|
|
|
|
evas_vg_load_file_close_svg(Vg_File_Data *vfd)
|
|
|
|
{
|
|
|
|
if (vfd->root) efl_unref(vfd->root);
|
2020-06-01 02:53:36 -07:00
|
|
|
free(vfd);
|
2018-12-07 01:42:45 -08:00
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
2016-11-02 03:25:45 -07:00
|
|
|
|
|
|
|
static Vg_File_Data*
|
2019-03-11 20:53:35 -07:00
|
|
|
evas_vg_load_file_open_svg(Eina_File *file,
|
|
|
|
const char *key EINA_UNUSED,
|
|
|
|
int *error EINA_UNUSED)
|
2016-11-02 03:25:45 -07:00
|
|
|
{
|
|
|
|
Evas_SVG_Loader loader = {
|
2017-11-06 18:41:36 -08:00
|
|
|
NULL, NULL, NULL, NULL, NULL, 0, EINA_FALSE
|
2016-11-02 03:25:45 -07:00
|
|
|
};
|
|
|
|
const char *content;
|
|
|
|
unsigned int length;
|
|
|
|
Svg_Node *defs;
|
|
|
|
|
2017-11-06 18:41:36 -08:00
|
|
|
loader.svg_parse = calloc(1, sizeof(Evas_SVG_Parser));
|
2019-03-11 20:53:35 -07:00
|
|
|
length = eina_file_size_get(file);
|
|
|
|
content = eina_file_map_all(file, EINA_FILE_SEQUENTIAL);
|
2016-11-02 03:25:45 -07:00
|
|
|
if (content)
|
|
|
|
{
|
|
|
|
loader.stack = eina_array_new(8);
|
|
|
|
eina_simple_xml_parse(content, length, EINA_TRUE,
|
|
|
|
_evas_svg_loader_parser, &loader);
|
|
|
|
|
|
|
|
eina_array_free(loader.stack);
|
2019-03-11 20:53:35 -07:00
|
|
|
eina_file_map_free(file, (void*) content);
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (loader.doc)
|
|
|
|
{
|
|
|
|
_update_style(loader.doc, NULL);
|
|
|
|
defs = loader.doc->node.doc.defs;
|
|
|
|
if (defs)
|
|
|
|
_update_gradient(loader.doc, defs->node.defs.gradients);
|
2020-09-10 19:48:30 -07:00
|
|
|
if (loader.gradients)
|
2019-04-11 01:18:10 -07:00
|
|
|
{
|
2020-09-10 19:48:30 -07:00
|
|
|
Eina_List* gradient_list = loader.gradients;
|
|
|
|
_update_gradient(loader.doc, gradient_list);
|
|
|
|
eina_list_free(gradient_list);
|
2019-04-11 01:18:10 -07:00
|
|
|
}
|
2016-11-02 03:25:45 -07:00
|
|
|
|
2020-10-14 03:16:53 -07:00
|
|
|
_update_composite(loader.doc, loader.doc);
|
|
|
|
if (defs) _update_composite(loader.doc, defs);
|
|
|
|
|
2016-11-02 03:25:45 -07:00
|
|
|
*error = EVAS_LOAD_ERROR_NONE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*error = EVAS_LOAD_ERROR_GENERIC;
|
|
|
|
}
|
2017-11-06 18:41:36 -08:00
|
|
|
free(loader.svg_parse);
|
2019-03-11 20:53:35 -07:00
|
|
|
|
2020-06-01 02:53:36 -07:00
|
|
|
Vg_File_Data* result = vg_common_svg_create_vg_node(loader.doc);
|
|
|
|
vg_common_svg_node_free(loader.doc);
|
|
|
|
return result;
|
2016-11-02 03:25:45 -07:00
|
|
|
}
|
|
|
|
|
2016-10-12 02:39:10 -07:00
|
|
|
static Evas_Vg_Load_Func evas_vg_load_svg_func =
|
|
|
|
{
|
2018-12-07 01:42:45 -08:00
|
|
|
evas_vg_load_file_open_svg,
|
|
|
|
evas_vg_load_file_close_svg,
|
|
|
|
evas_vg_load_file_data_svg,
|
2016-10-12 02:39:10 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
module_open(Evas_Module *em)
|
|
|
|
{
|
|
|
|
if (!em) return 0;
|
|
|
|
em->functions = (void *)(&evas_vg_load_svg_func);
|
2016-11-02 03:25:45 -07:00
|
|
|
_evas_vg_loader_svg_log_dom = eina_log_domain_register
|
|
|
|
("vg-load-svg", EVAS_DEFAULT_LOG_COLOR);
|
|
|
|
if (_evas_vg_loader_svg_log_dom < 0)
|
|
|
|
{
|
|
|
|
EINA_LOG_ERR("Can not create a module log domain.");
|
|
|
|
return 0;
|
|
|
|
}
|
2016-10-12 02:39:10 -07:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
module_close(Evas_Module *em EINA_UNUSED)
|
|
|
|
{
|
2016-11-02 03:25:45 -07:00
|
|
|
if (_evas_vg_loader_svg_log_dom >= 0)
|
|
|
|
{
|
|
|
|
eina_log_domain_unregister(_evas_vg_loader_svg_log_dom);
|
|
|
|
_evas_vg_loader_svg_log_dom = -1;
|
|
|
|
}
|
2016-10-12 02:39:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static Evas_Module_Api evas_modapi =
|
|
|
|
{
|
|
|
|
EVAS_MODULE_API_VERSION,
|
|
|
|
"svg",
|
|
|
|
"none",
|
|
|
|
{
|
|
|
|
module_open,
|
|
|
|
module_close
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_VG_LOADER, vg_loader, svg);
|
|
|
|
|
|
|
|
#ifndef EVAS_STATIC_BUILD_VG_SVG
|
|
|
|
EVAS_EINA_MODULE_DEFINE(vg_loader, svg);
|
2017-02-14 07:37:27 -08:00
|
|
|
#endif
|