[gnumeric] xlsx: import of gradients.



commit 43ded377229f25c879d85731e752a3f47f3f1c7f
Author: Morten Welinder <terra gnome org>
Date:   Wed Feb 18 19:27:27 2015 -0500

    xlsx: import of gradients.
    
    Our reversed gradients are handled by an extension.

 NEWS                               |    2 +-
 plugins/excel/xlsx-read-drawing.c  |   85 ++++++++++++++++++++++++++++-------
 plugins/excel/xlsx-read.c          |    1 +
 plugins/excel/xlsx-utils.c         |   53 +++++------------------
 plugins/excel/xlsx-utils.h         |   22 ++++++++-
 plugins/excel/xlsx-write-drawing.c |   34 ++++++++-------
 6 files changed, 119 insertions(+), 78 deletions(-)
---
diff --git a/NEWS b/NEWS
index 51b70cd..d708135 100644
--- a/NEWS
+++ b/NEWS
@@ -21,7 +21,7 @@ Morten:
        * Arrow properties editor.  [#158327]
        * Fix xlsx export of line style None.
        * Fix search-and-replace problem with text format.
-       * xlsx export of gradients.
+       * xlsx import/export of gradients.
 
 --------------------------------------------------------------------------
 Gnumeric 1.12.20
diff --git a/plugins/excel/xlsx-read-drawing.c b/plugins/excel/xlsx-read-drawing.c
index 888bd99..aa85e50 100644
--- a/plugins/excel/xlsx-read-drawing.c
+++ b/plugins/excel/xlsx-read-drawing.c
@@ -1665,6 +1665,7 @@ xlsx_chart_grad_fill (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
                if (!(state->sp_type & GO_STYLE_LINE)) {
                        state->cur_style->fill.type = GO_STYLE_FILL_GRADIENT;
                        state->cur_style->fill.auto_type = FALSE;
+                       state->gradient_count = 0;
                }
        }
 }
@@ -1673,12 +1674,36 @@ static void
 xlsx_chart_grad_linear (GsfXMLIn *xin, xmlChar const **attrs)
 {
        XLSXReadState *state = (XLSXReadState *)xin->user_state;
-       int ang;
+       int ang = 0, ang_deg;
+       GOGradientDirection dir;
+
        g_return_if_fail (state->cur_style);
-       for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
+
+       for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2) {
                if (attr_int (xin, attrs, "ang", &ang))
-                       state->cur_style->fill.gradient.dir
-                               = xlsx_get_gradient_direction (ang / 60000.);
+                       ; /* Nothing */
+       }
+
+       ang_deg = ((ang + 30000) / 60000);
+       for (dir = 0; dir < GO_GRADIENT_MAX; dir++) {
+               int this_ang = xlsx_gradient_info[dir].angle;
+               gboolean this_mirrored = xlsx_gradient_info[dir].mirrored;
+               int a;
+
+               if (state->gradient_count != (this_mirrored ? 3 : 2))
+                       continue;
+               /* We cannot distinguish the reversed case */
+
+               /* Different angle convention. */
+               this_ang = (360 - this_ang) % (this_mirrored ? 180 : 360);
+               a = ang_deg % (this_mirrored ? 180 : 360);
+               if (this_ang != a)
+                       continue;
+
+               state->cur_style->fill.gradient.dir = dir;
+               break;
+       }
+
        /* FIXME: we do not support the "scaled" attribute */
 }
 
@@ -1686,20 +1711,24 @@ static void
 xlsx_chart_grad_stop (GsfXMLIn *xin, xmlChar const **attrs)
 {
        XLSXReadState *state = (XLSXReadState *)xin->user_state;
-       int pos;
+       int pos = 0;
        XLSXColorState s = XLSX_CS_NONE;
 
        g_return_if_fail (state->cur_style);
-       for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
-               if (attr_int (xin, attrs, "pos", &pos)) {
-                       if (pos <= 50000) {
-                               /* FIXME: use betstate->auto_colorter gradients
-                                * for now, we only support stops at 0 and 1 */
-                               s = XLSX_CS_FILL_BACK;
-                       } else {
-                               s = XLSX_CS_FILL_FORE;
-                       }
-               }
+       for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2) {
+               if (attr_int (xin, attrs, "pos", &pos))
+                       ; /* Nothing */
+       }
+
+       state->gradient_count++;
+
+       if (state->gradient_count == 1 && pos == 0)
+               s = XLSX_CS_FILL_BACK;
+       else if (state->gradient_count == 2 && (pos == 50000 || pos == 100000))
+               s = XLSX_CS_FILL_FORE;
+       else
+               s = XLSX_CS_NONE; /* I.e., ignore.  */
+
        xlsx_chart_push_color_state (state, s);
 }
 
@@ -2188,7 +2217,7 @@ GSF_XML_IN_NODE_FULL (START, CHART_SPACE, XL_NS_CHART, "chartSpace", GSF_XML_NO_
 
     GSF_XML_IN_NODE (SHAPE_PR, FILL_GRAD,      XL_NS_DRAW, "gradFill", GSF_XML_NO_CONTENT, 
&xlsx_chart_grad_fill, NULL),
       GSF_XML_IN_NODE (FILL_GRAD, GRAD_LIST,   XL_NS_DRAW, "gsLst", GSF_XML_NO_CONTENT, NULL, NULL),
-       GSF_XML_IN_NODE (GRAD_LIST, GRAD_LIST_ITEM, XL_NS_DRAW, "gs", GSF_XML_NO_CONTENT, NULL, NULL),
+       GSF_XML_IN_NODE (GRAD_LIST, GRAD_LIST_ITEM, XL_NS_DRAW, "gs", GSF_XML_NO_CONTENT, 
xlsx_chart_grad_stop, xlsx_chart_grad_stop_end),
          GSF_XML_IN_NODE (GRAD_LIST_ITEM, COLOR_RGB, XL_NS_DRAW, "srgbClr", GSF_XML_NO_CONTENT, NULL, NULL), 
  /* 2nd Def */
       GSF_XML_IN_NODE (FILL_GRAD, GRAD_LINE,   XL_NS_DRAW, "lin", GSF_XML_NO_CONTENT, 
&xlsx_chart_grad_linear, NULL),
 
@@ -2959,6 +2988,7 @@ xlsx_ext_gostyle (GsfXMLIn *xin, xmlChar const **attrs)
        GOArrow *start_arrow = NULL;
        GOArrow *end_arrow = NULL;
        gboolean has_arrow = IS_GNM_SO_LINE (state->so);
+       int rev_gradient = 0;
 
        if (!style)
                return;
@@ -2996,6 +3026,8 @@ xlsx_ext_gostyle (GsfXMLIn *xin, xmlChar const **attrs)
                        end_arrow->b = f;
                } else if (end_arrow && attr_float (xin, attrs, "EndArrowShapeC", &f)) {
                        end_arrow->c = f;
+               } else if (attr_bool (xin, attrs, "reverse-gradient", &rev_gradient)) {
+                       /* Nothing */
                }
        }
 
@@ -3004,6 +3036,25 @@ xlsx_ext_gostyle (GsfXMLIn *xin, xmlChar const **attrs)
                g_free (start_arrow);
                g_free (end_arrow);
        }
+
+       if (rev_gradient) {
+               GOGradientDirection dir0 = style->fill.gradient.dir;
+               GOGradientDirection dir;
+               for (dir = 0; dir < GO_GRADIENT_MAX; dir++) {
+                       if (xlsx_gradient_info[dir0].angle == xlsx_gradient_info[dir].angle &&
+                           xlsx_gradient_info[dir0].mirrored == xlsx_gradient_info[dir].mirrored &&
+                           xlsx_gradient_info[dir0].reversed == !xlsx_gradient_info[dir].reversed) {
+                               GOColor c = style->fill.pattern.back;
+                               gboolean a = style->fill.auto_back;
+                               style->fill.gradient.dir = dir;
+                               style->fill.pattern.back = style->fill.pattern.fore;
+                               style->fill.pattern.fore = c;
+                               style->fill.auto_back = style->fill.auto_fore;
+                               style->fill.auto_fore = a;
+                               break;
+                       }
+               }
+       }
 }
 
 static GsfXMLInNode const xlsx_drawing_dtd[] = {
@@ -3079,7 +3130,7 @@ GSF_XML_IN_NODE_FULL (START, DRAWING, XL_NS_SS_DRAW, "wsDr", GSF_XML_NO_CONTENT,
          GSF_XML_IN_NODE (FILL_GRAD, GRAD_LIST,        XL_NS_DRAW, "gsLst", GSF_XML_NO_CONTENT, NULL, NULL),
           GSF_XML_IN_NODE (GRAD_LIST, GRAD_LIST_ITEM, XL_NS_DRAW, "gs", GSF_XML_NO_CONTENT, 
xlsx_chart_grad_stop, xlsx_chart_grad_stop_end),
             GSF_XML_IN_NODE (GRAD_LIST_ITEM, COLOR_RGB, XL_NS_DRAW, "srgbClr", GSF_XML_NO_CONTENT, NULL, 
NULL),        /* 2nd Def */
-         GSF_XML_IN_NODE (FILL_GRAD, GRAD_LINE,        XL_NS_DRAW, "lin", GSF_XML_NO_CONTENT, NULL, NULL),
+         GSF_XML_IN_NODE (FILL_GRAD, GRAD_LINE,        XL_NS_DRAW, "lin", GSF_XML_NO_CONTENT, 
&xlsx_chart_grad_linear, NULL),
 
         GSF_XML_IN_NODE (SHAPE_PR, FILL_PATT,  XL_NS_DRAW, "pattFill", GSF_XML_NO_CONTENT, 
&xlsx_chart_patt_fill, NULL),
           GSF_XML_IN_NODE_FULL (FILL_PATT, FILL_PATT_BG,       XL_NS_DRAW, "bgClr", GSF_XML_NO_CONTENT, 
FALSE, TRUE, &xlsx_chart_patt_fill_clr, &xlsx_chart_patt_fill_clr_end, FALSE),
diff --git a/plugins/excel/xlsx-read.c b/plugins/excel/xlsx-read.c
index 41c6836..b91bd69 100644
--- a/plugins/excel/xlsx-read.c
+++ b/plugins/excel/xlsx-read.c
@@ -210,6 +210,7 @@ typedef struct {
        GogObject        *series_pt;
        gboolean          series_pt_has_index;
        GOStyle  *cur_style;
+       int               gradient_count;
        guint32           chart_color_state;
        GOColor           color;
        GOMarker         *marker;
diff --git a/plugins/excel/xlsx-utils.c b/plugins/excel/xlsx-utils.c
index a3c51b0..610c077 100644
--- a/plugins/excel/xlsx-utils.c
+++ b/plugins/excel/xlsx-utils.c
@@ -709,48 +709,6 @@ xlsx_pivot_date_fmt (void)
        return go_format_new_from_XL ("yyyy-mm-dd\"T\"hh:mm:ss");
 }
 
-/**
- * xlsx_get_direction :
- *
- * Returns a GOGradientDirection corresponding to the angle ang (0...360)
- **/
-GOGradientDirection
-xlsx_get_gradient_direction (double ang)
-{
-       int ang_i;
-       g_return_val_if_fail (ang >=-360. && ang <= 360., GO_GRADIENT_N_TO_S);
-
-       ang_i = ang;
-       while (ang_i < 0)
-               ang_i += 360;
-       while (ang_i >= 360)
-               ang_i -= 360;
-
-       ang_i = (ang_i + 22) / 45; /* now ang is between 0 and 8 */
-
-       switch (ang_i) {
-       case 1:
-               return GO_GRADIENT_NW_TO_SE;
-       case 2:
-               return GO_GRADIENT_W_TO_E;
-       case 3:
-               return GO_GRADIENT_SW_TO_NE;
-       case 4:
-               return GO_GRADIENT_S_TO_N;
-       case 5:
-               return GO_GRADIENT_SE_TO_NW;
-       case 6:
-               return GO_GRADIENT_E_TO_W;
-       case 7:
-               return GO_GRADIENT_NE_TO_SW;
-       case 0:
-       case 8:
-       default:
-               return GO_GRADIENT_N_TO_S;
-       }
-}
-
-
 XLSXPlotType
 xlsx_plottype_from_type_name (const char *type_name)
 {
@@ -777,3 +735,14 @@ xlsx_plottype_from_type_name (const char *type_name)
 
        return XLSX_PT_UNKNOWN;
 }
+
+/*****************************************************************************/
+
+XLSXGradientInfo xlsx_gradient_info[GO_GRADIENT_MAX] = {
+       { 270, FALSE, FALSE }, {  90, FALSE, FALSE }, {  90, TRUE, FALSE }, {  90, TRUE, TRUE },
+       { 180, FALSE, FALSE }, {   0, FALSE, FALSE }, {   0, TRUE, FALSE }, {   0, TRUE, TRUE },
+       { 315, FALSE, FALSE }, { 135, FALSE, FALSE }, { 135, TRUE, FALSE }, { 135, TRUE, TRUE },
+       { 225, FALSE, FALSE }, {  45, FALSE, FALSE }, {  45, TRUE, FALSE }, {  45, TRUE, TRUE }
+};
+
+/*****************************************************************************/
diff --git a/plugins/excel/xlsx-utils.h b/plugins/excel/xlsx-utils.h
index 0faab42..f53c692 100644
--- a/plugins/excel/xlsx-utils.h
+++ b/plugins/excel/xlsx-utils.h
@@ -58,8 +58,6 @@ Workbook      *xlsx_conventions_add_extern_ref (GnmConventions *conv,
                                                  char const *path);
 GOFormat        *xlsx_pivot_date_fmt   (void);
 
-GOGradientDirection xlsx_get_gradient_direction (double ang);
-
 typedef enum {
        XLSX_PT_UNKNOWN,
        XLSX_PT_GOGAREAPLOT,
@@ -76,5 +74,25 @@ typedef enum {
 } XLSXPlotType;
 XLSXPlotType xlsx_plottype_from_type_name (const char *type_name);
 
+/*****************************************************************************/
+
+typedef struct {
+       /*
+        * Angle in degrees for the starting point.
+        * 0 is west, 90 is north; 180 is east; 270 is south.
+        * range is [0-360[ normally, but [0;180[ for mirrored.
+        */
+       unsigned angle : 16;
+
+       /* Gradient has three stop like F to B to F. */
+       unsigned mirrored : 1;
+
+       /* Gradient goes from B to F to B. */
+       unsigned reversed : 1;
+} XLSXGradientInfo;
+
+extern XLSXGradientInfo xlsx_gradient_info[GO_GRADIENT_MAX];
+
+/*****************************************************************************/
 
 #endif /* GNM_XLSX_UTILS_H */
diff --git a/plugins/excel/xlsx-write-drawing.c b/plugins/excel/xlsx-write-drawing.c
index f7d393e..f9e06d4 100644
--- a/plugins/excel/xlsx-write-drawing.c
+++ b/plugins/excel/xlsx-write-drawing.c
@@ -217,6 +217,7 @@ xlsx_write_go_style_full (GsfXMLOut *xml, GOStyle *style, const XLSXStyleContext
        gboolean ext_fill_pattern = FALSE;
        gboolean ext_start_arrow = FALSE;
        gboolean ext_end_arrow = FALSE;
+       gboolean ext_gradient_rev = FALSE;
 
        char *spPr_tag = g_strconcat (sctx->spPr_ns, ":spPr", NULL);
 
@@ -319,22 +320,16 @@ xlsx_write_go_style_full (GsfXMLOut *xml, GOStyle *style, const XLSXStyleContext
                }
                case GO_STYLE_FILL_GRADIENT: {
                        GOGradientDirection dir = style->fill.gradient.dir;
-                       static gint16 angles[GO_GRADIENT_MAX] = {
-                               90, 270, 90, 90,
-                               0, 180, 0, 0,
-                               45, 225, 45, 45,
-                               135, 315, 135, 135
-                       };
-                       static gint8 flags[GO_GRADIENT_MAX] = {
-                               0, 0, 1, 3,
-                               0, 0, 1, 3,
-                               0, 0, 1, 3,
-                               0, 0, 1, 3
-                       };
-                       int i, N = (flags[dir] & 1) ? 3 : 2;
-                       gboolean rev = (flags[dir] & 2) != 0;
+                       gboolean mirrored = xlsx_gradient_info[dir].mirrored;
+                       gboolean rev = xlsx_gradient_info[dir].reversed;
+                       unsigned angle = xlsx_gradient_info[dir].angle;
+                       int i, N = mirrored ? 3 : 2;
+
+                       /* Different angle convention. */
+                       angle = (360 - angle) % (mirrored ? 180 : 360);
 
                        /* FIXME: Unicolor? */
+
                        gsf_xml_out_start_element (xml, "a:gradFill");
                        gsf_xml_out_start_element (xml, "a:gsLst");
                        for (i = 0; i < N; i++) {
@@ -352,9 +347,12 @@ xlsx_write_go_style_full (GsfXMLOut *xml, GOStyle *style, const XLSXStyleContext
                        }
                        gsf_xml_out_end_element (xml); /* "a:gsLst" */
                        gsf_xml_out_start_element (xml, "a:lin");
-                       gsf_xml_out_add_uint (xml, "ang", 60000 * angles[dir]);
+                       gsf_xml_out_add_uint (xml, "ang", 60000 * angle);
                        gsf_xml_out_end_element (xml);
                        gsf_xml_out_end_element (xml); /* "a:gradFill" */
+
+                       if (rev)
+                               ext_gradient_rev = TRUE;
                        break;
                }
                }
@@ -449,7 +447,8 @@ xlsx_write_go_style_full (GsfXMLOut *xml, GOStyle *style, const XLSXStyleContext
        }
 
        if (sctx->state->with_extension &&
-           (ext_fill_pattern || ext_start_arrow || ext_end_arrow)) {
+           (ext_fill_pattern || ext_start_arrow || ext_end_arrow ||
+            ext_gradient_rev)) {
                /* What namespace do we use?  */
                gsf_xml_out_start_element (xml, "a:extLst");
                gsf_xml_out_start_element (xml, "a:ext");
@@ -472,6 +471,9 @@ xlsx_write_go_style_full (GsfXMLOut *xml, GOStyle *style, const XLSXStyleContext
                        gsf_xml_out_add_float (xml, "EndArrowShapeB", arrow->b, -1);
                        gsf_xml_out_add_float (xml, "EndArrowShapeC", arrow->c, -1);
                }
+               if (ext_gradient_rev) {
+                       gsf_xml_out_add_uint (xml, "reverse-gradient", 1);
+               }
                gsf_xml_out_end_element (xml);  /* "gnmx:gostyle" */
                gsf_xml_out_end_element (xml);  /* "a:ext" */
                gsf_xml_out_end_element (xml);  /* "a:extLst" */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]