svg_parse: parse linear gradient variables accroding to gradientUnits
authorVitalii Vorobiov <vi.vorobiov@samsung.com>
Fri, 17 Feb 2017 15:00:01 +0000 (17:00 +0200)
committerJean-Philippe Andre <jp.andre@samsung.com>
Tue, 7 Nov 2017 02:54:09 +0000 (11:54 +0900)
There are difficult cases according to
https://www.w3.org/TR/2000/CR-SVG-20000802/pservers.html

src/modules/evas/vg_loaders/svg/evas_vg_load_svg.c
src/static_libs/vg_common/vg_common.h

index 636b27f..ff1b2d8 100644 (file)
@@ -42,6 +42,7 @@ typedef struct {
       int x, y, width, height;
    } global;
    struct {
+      Eina_Bool x1_percent, x2_percent, y1_percent, y2_percent;
       Eina_Bool fx_parsed;
       Eina_Bool fy_parsed;
    } gradient;
@@ -126,6 +127,48 @@ _to_double(const char *str, SVG_Parser_Length_Type type)
    return parsed_value;
 }
 
+/**
+ * Turn gradient variables into percentages
+ */
+static inline double
+_gradient_to_double(const char *str, SVG_Parser_Length_Type type)
+{
+   char *end = NULL;
+
+   double parsed_value = strtod(str, &end);
+   double max = 1;
+
+   /* unique case, if that is percentage, just return it */
+   if (strstr(str, "%"))
+     {
+        parsed_value = parsed_value / 100.0;
+        return parsed_value;
+     }
+
+   if (type == SVG_PARSER_LENGTH_VERTICAL)
+     max = svg_parse.global.height;
+   else if (type == SVG_PARSER_LENGTH_HORIZONTAL)
+     max = svg_parse.global.width;
+   //TODO: what about radial?
+
+   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;
+   //TODO: implement 'em', 'ex' attributes
+
+   /* Transform into global percentage */
+   parsed_value = parsed_value / max;
+
+   return parsed_value;
+}
+
 static inline int
 _to_offset(const char *str)
 {
@@ -1046,7 +1089,7 @@ _create_path_node(Svg_Node *parent, const char *buf, unsigned buflen)
 }
 
 #define CIRCLE_DEF(Name, Field, Type)       \
-  { #Name, sizeof (#Name), offsetof(Svg_Circle_Node, Field)}
+  { #Name, Type, sizeof (#Name), offsetof(Svg_Circle_Node, Field)}
 
 static const struct {
    const char *tag;
@@ -1105,7 +1148,7 @@ _create_circle_node(Svg_Node *parent, const char *buf, unsigned buflen)
 }
 
 #define ELLIPSE_DEF(Name, Field, Type)       \
-  { #Name, Type, sizeof (#Name), offsetof(Svg_Ellipse_Node, Field)}
+  { #Name, Type, sizeof (#Name) + sizeof (Type), offsetof(Svg_Ellipse_Node, Field)}
 
 static const struct {
    const char *tag;
@@ -1259,7 +1302,7 @@ _create_polyline_node(Svg_Node *parent, const char *buf, unsigned buflen)
 }
 
 #define RECT_DEF(Name, Field, Type)       \
-  { #Name, Type, sizeof (#Name), offsetof(Svg_Rect_Node, Field)}
+  { #Name, Type, sizeof (#Name) + sizeof(Type), offsetof(Svg_Rect_Node, Field)}
 
 static const struct {
    const char *tag;
@@ -1325,7 +1368,7 @@ _create_rect_node(Svg_Node *parent, const char *buf, unsigned buflen)
 }
 
 #define LINE_DEF(Name, Field, Type)       \
-  { #Name, Type, sizeof (#Name), offsetof(Svg_Line_Node, Field)}
+  { #Name, Type, sizeof (#Name) + sizeof (Type), offsetof(Svg_Line_Node, Field)}
 
 static const struct {
    const char *tag;
@@ -1779,37 +1822,96 @@ _attr_parse_stops(void *data, const char *key, const char *value)
 static void
 _handle_linear_x1_attr(Svg_Linear_Gradient* linear, const char *value)
 {
-   linear->x1 = _to_double(value, SVG_PARSER_LENGTH_HORIZONTAL);
+   linear->x1 = _gradient_to_double(value, SVG_PARSER_LENGTH_HORIZONTAL);
+   if (strstr(value, "%"))
+     svg_parse.gradient.x1_percent = EINA_TRUE;
 }
 
 static void
 _handle_linear_y1_attr(Svg_Linear_Gradient* linear, const char *value)
 {
-   linear->y1 = _to_double(value, SVG_PARSER_LENGTH_VERTICAL);
+   linear->y1 = _gradient_to_double(value, SVG_PARSER_LENGTH_VERTICAL);
+   if (strstr(value, "%"))
+     svg_parse.gradient.y1_percent = EINA_TRUE;
 }
 
 static void
 _handle_linear_x2_attr(Svg_Linear_Gradient* linear, const char *value)
 {
-   linear->x2 = _to_double(value, SVG_PARSER_LENGTH_HORIZONTAL);
+   linear->x2 = _gradient_to_double(value, SVG_PARSER_LENGTH_HORIZONTAL);
+   /* checking if there are no percentage because x2 have default value
+    * already set in percentages (100%) */
+   if (!strstr(value, "%"))
+     svg_parse.gradient.x2_percent = EINA_FALSE;
 }
 
 static void
 _handle_linear_y2_attr(Svg_Linear_Gradient* linear, const char *value)
 {
-   linear->y2 = _to_double(value, SVG_PARSER_LENGTH_VERTICAL);
+   linear->y2 = _gradient_to_double(value, SVG_PARSER_LENGTH_VERTICAL);
+   if (strstr(value, "%"))
+     svg_parse.gradient.y2_percent = EINA_TRUE;
+}
+
+static void
+_recalc_linear_x1_attr(Svg_Linear_Gradient* linear, Eina_Bool user_space)
+{
+   if (!svg_parse.gradient.x1_percent && !user_space)
+     {
+        /* Since previous percentage is not required (it was already percent)
+         * so oops and make it all back */
+        linear->x1 = linear->x1 * svg_parse.global.width;
+     }
+   svg_parse.gradient.x1_percent = EINA_FALSE;
+}
+
+static void
+_recalc_linear_y1_attr(Svg_Linear_Gradient* linear, Eina_Bool user_space)
+{
+   if (!svg_parse.gradient.y1_percent && !user_space)
+     {
+        /* Since previous percentage is not required (it was already percent)
+         * so oops and make it all back */
+        linear->y1 = linear->y1 * svg_parse.global.height;
+     }
+   svg_parse.gradient.y1_percent = EINA_FALSE;
+}
+
+static void
+_recalc_linear_x2_attr(Svg_Linear_Gradient* linear, Eina_Bool user_space)
+{
+   if (!svg_parse.gradient.x2_percent && !user_space)
+     {
+        /* Since previous percentage is not required (it was already percent)
+         * so oops and make it all back */
+        linear->x2 = linear->x2 * svg_parse.global.width;
+     }
+   svg_parse.gradient.x2_percent = EINA_FALSE;
 }
 
+static void
+_recalc_linear_y2_attr(Svg_Linear_Gradient* linear, Eina_Bool user_space)
+{
+   if (!svg_parse.gradient.y2_percent && !user_space)
+     {
+        /* Since previous percentage is not required (it was already percent)
+         * so oops and make it all back */
+        linear->y2 = linear->y2 * svg_parse.global.height;
+     }
+   svg_parse.gradient.y2_percent = EINA_FALSE;
+}
 
 typedef void (*Linear_Method)(Svg_Linear_Gradient *linear, const char *value);
+typedef void (*Linear_Method_Recalc)(Svg_Linear_Gradient *linear, Eina_Bool user_space);
 
 #define LINEAR_DEF(Name)       \
-  { #Name, sizeof (#Name), _handle_linear_##Name##_attr}
+  { #Name, sizeof (#Name), _handle_linear_##Name##_attr, _recalc_linear_##Name##_attr}
 
 static const struct {
    const char *tag;
    int sz;
    Linear_Method tag_handler;;
+   Linear_Method_Recalc tag_recalc;;
 } linear_tags[] = {
   LINEAR_DEF(x1),
   LINEAR_DEF(y1),
@@ -1856,18 +1958,33 @@ static Svg_Style_Gradient *
 _create_linearGradient(const char *buf, unsigned buflen)
 {
    Svg_Style_Gradient *grad = calloc(1, sizeof(Svg_Style_Gradient));
+   unsigned int i;
 
    grad->type = SVG_LINEAR_GRADIENT;
    grad->linear = calloc(1, sizeof(Svg_Linear_Gradient));
+   /**
+    * Default value of x2 is 100%
+    */
+   grad->linear->x2 = 1;
+   svg_parse.gradient.x2_percent = EINA_TRUE;
    eina_simple_xml_attributes_parse(buf, buflen,
                                     _attr_parse_linear_gradient_node, grad);
-   return grad;
 
+   for (i = 0; i < sizeof (linear_tags) / sizeof(linear_tags[0]); i++)
+     linear_tags[i].tag_recalc(grad->linear, grad->user_space);
+
+   return grad;
 }
 
 #define GRADIENT_DEF(Name)                                   \
   { #Name, sizeof (#Name), _create_##Name }
 
+/**
+ * For all Gradients lengths would be calculated into percentages related to
+ * canvas width and height.
+ *
+ * if user then recalculate actual pixels into percentages
+ */
 static const struct {
    const char *tag;
    int sz;
index 65c70a8..174cb20 100644 (file)
@@ -176,6 +176,25 @@ struct _Svg_Radial_Gradient
    double r;
 };
 
+/**
+ * IMPORTANT!
+ * Talking about parsing gradient variables
+ *
+ * All variables (like x1,x2,y1,y2,fx,fy,rx,ry,r, etc) would be percentages
+ * and then all recalculations would be done after that if userSpaceOnUse
+ * is set or not (recalculation depends on that).
+ *
+ * If gradientUnits="userSpaceOnUse" (grad->user_space is set to true)
+ * > Gradient variables (x1,x2,r,fx etc) contains percentages of entire cavas
+ * > size.
+ *
+ * If gradientUnits="objectBoundingBox" (grad->user_space is set to false)
+ * > Gradient variables (x1,x2,r,fx etc) contain percentages of 'whatever'
+ * > figure.
+ *
+ * So later on, while using gradient, please be careful and
+ * check user_space to use and transform sizes correctly.
+ */
 struct _Svg_Style_Gradient
 {
    Svg_Gradient_Type type;