summaryrefslogtreecommitdiff
path: root/src/modules/evas/vg_loaders/svg/evas_vg_load_svg.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/evas/vg_loaders/svg/evas_vg_load_svg.c')
-rw-r--r--src/modules/evas/vg_loaders/svg/evas_vg_load_svg.c239
1 files changed, 208 insertions, 31 deletions
diff --git a/src/modules/evas/vg_loaders/svg/evas_vg_load_svg.c b/src/modules/evas/vg_loaders/svg/evas_vg_load_svg.c
index 313d3eed15..e8c46ceb1f 100644
--- a/src/modules/evas/vg_loaders/svg/evas_vg_load_svg.c
+++ b/src/modules/evas/vg_loaders/svg/evas_vg_load_svg.c
@@ -259,8 +259,6 @@ static struct {
_PARSE_TAG(Efl_Gfx_Fill_Rule, fill_rule, fill_rule_tags, EFL_GFX_FILL_RULE_WINDING);
-#if 0
-// unused at the moment
/* parse the dash pattern used during stroking a path.
* Value: none | <dasharray> | inherit
* Initial: none
@@ -269,7 +267,8 @@ _PARSE_TAG(Efl_Gfx_Fill_Rule, fill_rule, fill_rule_tags, EFL_GFX_FILL_RULE_WINDI
static inline void
_parse_dash_array(const char *str, Efl_Gfx_Dash** dash, int *length)
{
- double tmp[30];
+ // It is assumed that the length of the dasharray string is 255 or less.
+ double tmp[255];
char *end = NULL;
int leni, gapi, count = 0, index = 0;
@@ -291,20 +290,21 @@ _parse_dash_array(const char *str, Efl_Gfx_Dash** dash, int *length)
gapi = (2 * index + 1) % count;
(*dash)[index].length = tmp[leni];
(*dash)[index].gap = tmp[gapi];
+ index++;
}
}
else
{ // even case
*length = count/2;
*dash = calloc(*length, sizeof(Efl_Gfx_Dash));
- while (index < count)
+ while (index < *length)
{
(*dash)[index].length = tmp[2 * index];
(*dash)[index].gap = tmp[2 * index + 1];
+ index++;
}
}
}
-#endif
static Eina_Stringshare *
_id_from_url(const char *url)
@@ -773,7 +773,7 @@ _attr_parse_svg_node(void *data, const char *key, const char *value)
Svg_Doc_Node *doc = &(node->node.doc);
Svg_Length_Type type;
- // @TODO handle lenght unit.
+ // @TODO handle length unit.
if (!strcmp(key, "width"))
{
doc->width = parse_length(value, &type);
@@ -865,6 +865,13 @@ _handle_stroke_opacity_attr(Evas_SVG_Loader *loader EINA_UNUSED, Svg_Node* node,
}
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);
+}
+
+static void
_handle_stroke_width_attr(Evas_SVG_Loader *loader, Svg_Node* node, const char *value)
{
node->style->stroke.flags |= SVG_STROKE_FLAGS_WIDTH;
@@ -911,6 +918,16 @@ _handle_transform_attr(Evas_SVG_Loader *loader EINA_UNUSED, Svg_Node* node, cons
node->transform = _parse_transformation_matrix(value);
}
+
+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));
+}
+
static void
_handle_display_attr(Evas_SVG_Loader *loader EINA_UNUSED, Svg_Node* node, const char *value)
{
@@ -942,6 +959,7 @@ static const struct {
STYLE_DEF(stroke-linejoin, stroke_linejoin),
STYLE_DEF(stroke-linecap, stroke_linecap),
STYLE_DEF(stroke-opacity, stroke_opacity),
+ STYLE_DEF(stroke-dasharray, stroke_dasharray),
STYLE_DEF(transform, transform),
STYLE_DEF(display, display)
};
@@ -995,6 +1013,10 @@ _attr_parse_g_node(void *data, const char *key, const char *value)
{
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);
@@ -1007,6 +1029,37 @@ _attr_parse_g_node(void *data, const char *key, const char *value)
}
+/* 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;
+}
+
static Svg_Node *
_create_node(Svg_Node *parent, Svg_Node_Type type)
{
@@ -1091,6 +1144,25 @@ _create_switch_node(Evas_SVG_Loader *loader EINA_UNUSED, Svg_Node *parent EINA_U
return NULL;
}
+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)
+{
+ loader->svg_parse->node = _create_node(parent, SVG_NODE_CLIP_PATH);
+
+ eina_simple_xml_attributes_parse(buf, buflen,
+ _attr_parse_clip_path_node, loader);
+ return loader->svg_parse->node;
+}
+
static Eina_Bool
_attr_parse_path_node(void *data, const char *key, const char *value)
{
@@ -1106,6 +1178,10 @@ _attr_parse_path_node(void *data, const char *key, const char *value)
{
_attr_style_node(loader, value);
}
+ else if (!strcmp(key, "clip-path"))
+ {
+ _handle_clip_path_attr(loader, node, value);
+ }
else if (!strcmp(key, "id"))
{
node->id = _copy_id(value);
@@ -1168,6 +1244,10 @@ _attr_parse_circle_node(void *data, const char *key, const char *value)
{
_attr_style_node(loader, value);
}
+ else if (!strcmp(key, "clip-path"))
+ {
+ _handle_clip_path_attr(loader, node, value);
+ }
else if (!strcmp(key, "id"))
{
node->id = _copy_id(value);
@@ -1230,6 +1310,10 @@ _attr_parse_ellipse_node(void *data, const char *key, const char *value)
{
node->id = _copy_id(value);
}
+ else if (!strcmp(key, "clip-path"))
+ {
+ _handle_clip_path_attr(loader, node, value);
+ }
else if (!strcmp(key, "style"))
{
_attr_style_node(loader, value);
@@ -1315,6 +1399,10 @@ _attr_parse_polygon_node(void *data, const char *key, const char *value)
{
_attr_style_node(loader, value);
}
+ else if (!strcmp(key, "clip-path"))
+ {
+ _handle_clip_path_attr(loader, node, value);
+ }
else if (!strcmp(key, "id"))
{
node->id = _copy_id(value);
@@ -1381,6 +1469,13 @@ _attr_parse_rect_node(void *data, const char *key, const char *value)
if (rect_tags[i].sz - 1 == sz && !strncmp(rect_tags[i].tag, key, sz))
{
*((double*) (array + rect_tags[i].offset)) = _to_double(loader->svg_parse, value, rect_tags[i].type);
+
+ //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;
return EINA_TRUE;
}
@@ -1392,13 +1487,15 @@ _attr_parse_rect_node(void *data, const char *key, const char *value)
{
_attr_style_node(loader, value);
}
+ else if (!strcmp(key, "clip-path"))
+ {
+ _handle_clip_path_attr(loader, node, value);
+ }
else
{
_parse_style_attr(loader, key, value);
}
- if (!EINA_DBL_EQ(rect->rx, 0) && EINA_DBL_EQ(rect->ry, 0)) rect->ry = rect->rx;
- if (!EINA_DBL_EQ(rect->ry, 0) && EINA_DBL_EQ(rect->rx, 0)) rect->rx = rect->ry;
return EINA_TRUE;
}
@@ -1408,6 +1505,10 @@ _create_rect_node(Evas_SVG_Loader *loader, Svg_Node *parent, const char *buf, un
{
loader->svg_parse->node = _create_node(parent, SVG_NODE_RECT);
+ if (loader->svg_parse->node) {
+ loader->svg_parse->node->node.rect.has_rx = loader->svg_parse->node->node.rect.has_ry = EINA_FALSE;
+ }
+
eina_simple_xml_attributes_parse(buf, buflen,
_attr_parse_rect_node, loader);
return loader->svg_parse->node;
@@ -1457,6 +1558,10 @@ _attr_parse_line_node(void *data, const char *key, const char *value)
{
_attr_style_node(loader, value);
}
+ else if (!strcmp(key, "clip-path"))
+ {
+ _handle_clip_path_attr(loader, node, value);
+ }
else
{
_parse_style_attr(loader, key, value);
@@ -1515,6 +1620,20 @@ _find_child_by_id(Svg_Node *node, const char *id)
return NULL;
}
+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;
+}
+
static Eina_List *
_clone_grad_stops(Eina_List *from)
{
@@ -1601,6 +1720,8 @@ _copy_attribute(Svg_Node *to, Svg_Node *from)
to->node.rect.h = from->node.rect.h;
to->node.rect.rx = from->node.rect.rx;
to->node.rect.ry = from->node.rect.ry;
+ to->node.rect.has_rx = from->node.rect.has_rx;
+ to->node.rect.has_ry = from->node.rect.has_ry;
break;
case SVG_NODE_LINE:
to->node.line.x1 = from->node.line.x1;
@@ -1613,11 +1734,13 @@ _copy_attribute(Svg_Node *to, Svg_Node *from)
break;
case SVG_NODE_POLYGON:
to->node.polygon.points_count = from->node.polygon.points_count;
- to->node.polygon.points = calloc(to->node.polygon.points_count, sizeof(double));
+ to->node.polygon.points = malloc(to->node.polygon.points_count * sizeof(double));
+ memcpy(to->node.polygon.points, from->node.polygon.points, to->node.polygon.points_count * sizeof(double));
break;
case SVG_NODE_POLYLINE:
to->node.polyline.points_count = from->node.polyline.points_count;
- to->node.polyline.points = calloc(to->node.polyline.points_count, sizeof(double));
+ to->node.polyline.points = malloc(to->node.polyline.points_count * sizeof(double));
+ memcpy(to->node.polyline.points, from->node.polyline.points, to->node.polyline.points_count * sizeof(double));
break;
default:
break;
@@ -1659,6 +1782,10 @@ _attr_parse_use_node(void *data, const char *key, const char *value)
_clone_node(node_from, node);
eina_stringshare_del(id);
}
+ else if (!strcmp(key, "clip-path"))
+ {
+ _handle_clip_path_attr(loader, node, value);
+ }
else
{
_attr_parse_g_node(data, key, value);
@@ -1703,7 +1830,9 @@ static const struct {
TAG_DEF(defs),
TAG_DEF(g),
TAG_DEF(svg),
- TAG_DEF(switch)
+ TAG_DEF(switch),
+ TAG_DEF(mask),
+ TAG_DEF(clipPath)
};
#define FIND_FACTORY(Short_Name, Tags_Array) \
@@ -2112,9 +2241,18 @@ _find_gradient_factory(const char *name)
return NULL;
}
+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;
+}
+
static void
_evas_svg_loader_xml_open_parser(Evas_SVG_Loader *loader,
- const char *content, unsigned int length)
+ const char *content, unsigned int length, Eina_Bool empty)
{
const char *attrs = NULL;
int attrs_length = 0;
@@ -2141,6 +2279,7 @@ _evas_svg_loader_xml_open_parser(Evas_SVG_Loader *loader,
attrs_length = length - sz;
while ((sz > 0) && (isspace(content[sz - 1])))
sz--;
+ if ((unsigned int)sz > sizeof(tag_name)) return;
strncpy(tag_name, content, sz);
tag_name[sz] = '\0';
}
@@ -2157,20 +2296,22 @@ _evas_svg_loader_xml_open_parser(Evas_SVG_Loader *loader,
}
else
{
- parent = eina_array_data_get(loader->stack, eina_array_count(loader->stack) - 1);
+ if (!strcmp(tag_name, "svg")) return; //Already loadded <svg>(SvgNodeType::Doc) tag
+ parent = _get_parent_node_from_loader(loader);
node = method(loader, parent, attrs, attrs_length);
}
- eina_array_push(loader->stack, node);
if (node->type == SVG_NODE_DEFS)
- {
- loader->doc->node.doc.defs = node;
- loader->def = node;
- }
+ {
+ loader->doc->node.doc.defs = node;
+ loader->def = node;
+ if (!empty) eina_array_push(loader->stack, node);
+ }
+ else eina_array_push(loader->stack, node);
}
else if ((method = _find_graphics_factory(tag_name)))
{
- parent = eina_array_data_get(loader->stack, eina_array_count(loader->stack) - 1);
+ parent = _get_parent_node_from_loader(loader);
node = method(loader, parent, attrs, attrs_length);
}
else if ((gradient_method = _find_gradient_factory(tag_name)))
@@ -2218,7 +2359,9 @@ static const struct {
} pop_array[] = {
POP_TAG(g),
POP_TAG(svg),
- POP_TAG(defs)
+ POP_TAG(defs),
+ POP_TAG(mask),
+ POP_TAG(clipPath)
};
static void
@@ -2250,10 +2393,10 @@ _evas_svg_loader_parser(void *data, Eina_Simple_XML_Type type,
switch (type)
{
case EINA_SIMPLE_XML_OPEN:
- _evas_svg_loader_xml_open_parser(loader, content, length);
+ _evas_svg_loader_xml_open_parser(loader, content, length, EINA_FALSE);
break;
case EINA_SIMPLE_XML_OPEN_EMPTY:
- _evas_svg_loader_xml_open_parser(loader, content, length);
+ _evas_svg_loader_xml_open_parser(loader, content, length, EINA_TRUE);
break;
case EINA_SIMPLE_XML_CLOSE:
_evas_svg_loader_xml_close_parser(loader, content, length);
@@ -2279,7 +2422,7 @@ _inherit_style(Svg_Style_Property *child, Svg_Style_Property *parent)
{
if (parent == NULL)
return;
- // inherit the property of parent if not present in child.
+ // inherit the property of parent if not present in child.
// fill
if (!(child->fill.flags & SVG_FILL_FLAGS_PAINT))
{
@@ -2324,6 +2467,22 @@ _inherit_style(Svg_Style_Property *child, Svg_Style_Property *parent)
{
child->stroke.join = parent->stroke.join;
}
+ 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;
+ }
+ }
+ }
}
void
@@ -2400,6 +2559,21 @@ _update_gradient(Svg_Node *node, Eina_List *grad_list)
}
}
}
+
+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);
+ }
+}
+
static Eina_Bool
evas_vg_load_file_data_svg(Vg_File_Data *vfd EINA_UNUSED)
{
@@ -2410,6 +2584,7 @@ static Eina_Bool
evas_vg_load_file_close_svg(Vg_File_Data *vfd)
{
if (vfd->root) efl_unref(vfd->root);
+ free(vfd);
return EINA_TRUE;
}
@@ -2444,16 +2619,16 @@ evas_vg_load_file_open_svg(Eina_File *file,
defs = loader.doc->node.doc.defs;
if (defs)
_update_gradient(loader.doc, defs->node.defs.gradients);
- else
+ if (loader.gradients)
{
- if (loader.gradients)
- {
- Eina_List* gradient_list = loader.gradients;
- _update_gradient(loader.doc, gradient_list);
- eina_list_free(gradient_list);
- }
+ Eina_List* gradient_list = loader.gradients;
+ _update_gradient(loader.doc, gradient_list);
+ eina_list_free(gradient_list);
}
+ _update_composite(loader.doc, loader.doc);
+ if (defs) _update_composite(loader.doc, defs);
+
*error = EVAS_LOAD_ERROR_NONE;
}
else
@@ -2462,7 +2637,9 @@ evas_vg_load_file_open_svg(Eina_File *file,
}
free(loader.svg_parse);
- return vg_common_svg_create_vg_node(loader.doc);
+ Vg_File_Data* result = vg_common_svg_create_vg_node(loader.doc);
+ vg_common_svg_node_free(loader.doc);
+ return result;
}
static Evas_Vg_Load_Func evas_vg_load_svg_func =