only draw the new type of marker in the graph legend when called with --dynamic-label...
[rrdtool.git] / src / rrd_graph.c
index f9c7a11..258eb52 100644 (file)
@@ -1,5 +1,5 @@
 /****************************************************************************
- * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ * RRDtool 1.4.2  Copyright by Tobi Oetiker, 1997-2009
  ****************************************************************************
  * rrd__graph.c  produce graphs from data in rrdfiles
  ****************************************************************************/
 #ifdef WIN32
 #include "strftime.h"
 #endif
+
 #include "rrd_tool.h"
 
+/* for basename */
+#ifdef HAVE_LIBGEN_H
+#  include <libgen.h>
+#else
+#include "plbasename.h"
+#endif
+
 #if defined(WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
 #include <io.h>
 #include <fcntl.h>
 #endif
 
-#ifdef HAVE_TIME_H
 #include <time.h>
-#endif
 
-#ifdef HAVE_LOCALE_H
 #include <locale.h>
-#endif
 
 #include "rrd_graph.h"
+#include "rrd_client.h"
 
 /* some constant definitions */
 
 
 #ifndef RRD_DEFAULT_FONT
 /* there is special code later to pick Cour.ttf when running on windows */
-#define RRD_DEFAULT_FONT "DejaVuSansMono-Roman.ttf"
+#define RRD_DEFAULT_FONT "DejaVu Sans Mono,Bitstream Vera Sans Mono,monospace,Courier"
 #endif
 
 text_prop_t text_prop[] = {
-    {8.0, RRD_DEFAULT_FONT}
+    {8.0, RRD_DEFAULT_FONT,NULL}
     ,                   /* default */
-    {9.0, RRD_DEFAULT_FONT}
+    {9.0, RRD_DEFAULT_FONT,NULL}
     ,                   /* title */
-    {7.0, RRD_DEFAULT_FONT}
+    {7.0, RRD_DEFAULT_FONT,NULL}
     ,                   /* axis */
-    {8.0, RRD_DEFAULT_FONT}
+    {8.0, RRD_DEFAULT_FONT,NULL}
     ,                   /* unit */
-    {8.0, RRD_DEFAULT_FONT} /* legend */
+    {8.0, RRD_DEFAULT_FONT,NULL} /* legend */
+    ,
+    {5.5, RRD_DEFAULT_FONT,NULL} /* watermark */
 };
 
 xlab_t    xlab[] = {
@@ -61,7 +68,7 @@ xlab_t    xlab[] = {
     ,
     {60, 0, TMT_MINUTE, 30, TMT_HOUR, 2, TMT_HOUR, 2, 0, "%H:%M"}
     ,
-    {60, 24 * 3600, TMT_MINUTE, 30, TMT_HOUR, 2, TMT_HOUR, 4, 0, "%a %H:%M"}
+    {60, 24 * 3600, TMT_MINUTE, 30, TMT_HOUR, 2, TMT_HOUR, 6, 0, "%a %H:%M"}
     ,
     {180, 0, TMT_HOUR, 1, TMT_HOUR, 6, TMT_HOUR, 6, 0, "%H:%M"}
     ,
@@ -140,16 +147,17 @@ ylab_t    ylab[] = {
 
 
 gfx_color_t graph_col[] =   /* default colors */
-{ 0xFFFFFFFF,           /* canvas     */
-    0xF0F0F0FF,         /* background */
-    0xD0D0D0FF,         /* shade A    */
-    0xA0A0A0FF,         /* shade B    */
-    0x90909080,         /* grid       */
-    0xE0505080,         /* major grid */
-    0x000000FF,         /* font       */
-    0x802020FF,         /* arrow      */
-    0x202020FF,         /* axis       */
-    0x000000FF          /* frame      */
+{
+    {1.00, 1.00, 1.00, 1.00},   /* canvas     */
+    {0.95, 0.95, 0.95, 1.00},   /* background */
+    {0.81, 0.81, 0.81, 1.00},   /* shade A    */
+    {0.62, 0.62, 0.62, 1.00},   /* shade B    */
+    {0.56, 0.56, 0.56, 0.75},   /* grid       */
+    {0.87, 0.31, 0.31, 0.60},   /* major grid */
+    {0.00, 0.00, 0.00, 1.00},   /* font       */
+    {0.50, 0.12, 0.12, 1.00},   /* arrow      */
+    {0.12, 0.12, 0.12, 1.00},   /* axis       */
+    {0.00, 0.00, 0.00, 1.00}    /* frame      */
 };
 
 
@@ -200,16 +208,6 @@ double ytr(
             yval = im->yorigin - pixie * (log10(value) - log10(im->minval));
         }
     }
-    /* make sure we don't return anything too unreasonable. GD lib can
-       get terribly slow when drawing lines outside its scope. This is 
-       especially problematic in connection with the rigid option */
-    if (!im->rigid) {
-        /* keep yval as-is */
-    } else if (yval > im->yorigin) {
-        yval = im->yorigin + 0.00001;
-    } else if (yval < im->yorigin - im->ysize) {
-        yval = im->yorigin - im->ysize - 0.00001;
-    }
     return yval;
 }
 
@@ -225,81 +223,80 @@ enum gf_en gf_conv(
     char *string)
 {
 
-    conv_if(PRINT, GF_PRINT)
-        conv_if(GPRINT, GF_GPRINT)
-        conv_if(COMMENT, GF_COMMENT)
-        conv_if(HRULE, GF_HRULE)
-        conv_if(VRULE, GF_VRULE)
-        conv_if(LINE, GF_LINE)
-        conv_if(AREA, GF_AREA)
-        conv_if(STACK, GF_STACK)
-        conv_if(TICK, GF_TICK)
-        conv_if(DEF, GF_DEF)
-        conv_if(CDEF, GF_CDEF)
-        conv_if(VDEF, GF_VDEF)
-#ifdef WITH_PIECHART
-        conv_if(PART, GF_PART)
-#endif
-        conv_if(XPORT, GF_XPORT)
-        conv_if(SHIFT, GF_SHIFT)
-
-        return (-1);
+    conv_if(PRINT, GF_PRINT);
+    conv_if(GPRINT, GF_GPRINT);
+    conv_if(COMMENT, GF_COMMENT);
+    conv_if(HRULE, GF_HRULE);
+    conv_if(VRULE, GF_VRULE);
+    conv_if(LINE, GF_LINE);
+    conv_if(AREA, GF_AREA);
+    conv_if(STACK, GF_STACK);
+    conv_if(TICK, GF_TICK);
+    conv_if(TEXTALIGN, GF_TEXTALIGN);
+    conv_if(DEF, GF_DEF);
+    conv_if(CDEF, GF_CDEF);
+    conv_if(VDEF, GF_VDEF);
+    conv_if(XPORT, GF_XPORT);
+    conv_if(SHIFT, GF_SHIFT);
+
+    return (enum gf_en)(-1);
 }
 
 enum gfx_if_en if_conv(
     char *string)
 {
 
-    conv_if(PNG, IF_PNG)
-        conv_if(SVG, IF_SVG)
-        conv_if(EPS, IF_EPS)
-        conv_if(PDF, IF_PDF)
+    conv_if(PNG, IF_PNG);
+    conv_if(SVG, IF_SVG);
+    conv_if(EPS, IF_EPS);
+    conv_if(PDF, IF_PDF);
 
-        return (-1);
+    return (enum gfx_if_en)(-1);
 }
 
 enum tmt_en tmt_conv(
     char *string)
 {
 
-    conv_if(SECOND, TMT_SECOND)
-        conv_if(MINUTE, TMT_MINUTE)
-        conv_if(HOUR, TMT_HOUR)
-        conv_if(DAY, TMT_DAY)
-        conv_if(WEEK, TMT_WEEK)
-        conv_if(MONTH, TMT_MONTH)
-        conv_if(YEAR, TMT_YEAR)
-        return (-1);
+    conv_if(SECOND, TMT_SECOND);
+    conv_if(MINUTE, TMT_MINUTE);
+    conv_if(HOUR, TMT_HOUR);
+    conv_if(DAY, TMT_DAY);
+    conv_if(WEEK, TMT_WEEK);
+    conv_if(MONTH, TMT_MONTH);
+    conv_if(YEAR, TMT_YEAR);
+    return (enum tmt_en)(-1);
 }
 
 enum grc_en grc_conv(
     char *string)
 {
 
-    conv_if(BACK, GRC_BACK)
-        conv_if(CANVAS, GRC_CANVAS)
-        conv_if(SHADEA, GRC_SHADEA)
-        conv_if(SHADEB, GRC_SHADEB)
-        conv_if(GRID, GRC_GRID)
-        conv_if(MGRID, GRC_MGRID)
-        conv_if(FONT, GRC_FONT)
-        conv_if(ARROW, GRC_ARROW)
-        conv_if(AXIS, GRC_AXIS)
-        conv_if(FRAME, GRC_FRAME)
-
-        return -1;
+    conv_if(BACK, GRC_BACK);
+    conv_if(CANVAS, GRC_CANVAS);
+    conv_if(SHADEA, GRC_SHADEA);
+    conv_if(SHADEB, GRC_SHADEB);
+    conv_if(GRID, GRC_GRID);
+    conv_if(MGRID, GRC_MGRID);
+    conv_if(FONT, GRC_FONT);
+    conv_if(ARROW, GRC_ARROW);
+    conv_if(AXIS, GRC_AXIS);
+    conv_if(FRAME, GRC_FRAME);
+
+    return (enum grc_en)(-1);
 }
 
 enum text_prop_en text_prop_conv(
     char *string)
 {
 
-    conv_if(DEFAULT, TEXT_PROP_DEFAULT)
-        conv_if(TITLE, TEXT_PROP_TITLE)
-        conv_if(AXIS, TEXT_PROP_AXIS)
-        conv_if(UNIT, TEXT_PROP_UNIT)
-        conv_if(LEGEND, TEXT_PROP_LEGEND)
-        return -1;
+    conv_if(DEFAULT, TEXT_PROP_DEFAULT);
+    conv_if(TITLE, TEXT_PROP_TITLE);
+    conv_if(AXIS, TEXT_PROP_AXIS);
+    conv_if(UNIT, TEXT_PROP_UNIT);
+    conv_if(LEGEND, TEXT_PROP_LEGEND);
+    conv_if(WATERMARK, TEXT_PROP_WATERMARK);
+    return (enum text_prop_en)(-1);
 }
 
 
@@ -309,9 +306,14 @@ int im_free(
     image_desc_t *im)
 {
     unsigned long i, ii;
+    cairo_status_t status = (cairo_status_t) 0;
 
     if (im == NULL)
         return 0;
+
+    if (im->daemon_addr != NULL)
+      free(im->daemon_addr);
+
     for (i = 0; i < (unsigned) im->gdes_c; i++) {
         if (im->gdes[i].data_first) {
             /* careful here, because a single pointer can occur several times */
@@ -322,11 +324,36 @@ int im_free(
                 free(im->gdes[i].ds_namv);
             }
         }
+        /* free allocated memory used for dashed lines */
+        if (im->gdes[i].p_dashes != NULL)
+            free(im->gdes[i].p_dashes);
+
         free(im->gdes[i].p_data);
         free(im->gdes[i].rpnp);
     }
     free(im->gdes);
-    gfx_destroy(im->canvas);
+    if (im->font_options)
+        cairo_font_options_destroy(im->font_options);
+
+    if (im->cr) {
+        status = cairo_status(im->cr);
+        cairo_destroy(im->cr);
+    }
+    if (im->rendered_image) {
+        free(im->rendered_image);
+    }
+
+    if (im->layout) {
+        g_object_unref (im->layout);
+    }
+
+    if (im->surface)
+        cairo_surface_destroy(im->surface);
+
+    if (status)
+        fprintf(stderr, "OOPS: Cairo has issues it can't even die: %s\n",
+                cairo_status_to_string(status));
+
     return 0;
 }
 
@@ -403,7 +430,7 @@ void si_unit(
 
     if (im->unitsexponent != 9999) {
         /* unitsexponent = 9, 6, 3, 0, -3, -6, -9, etc */
-        viewdigits = floor(im->unitsexponent / 3);
+        viewdigits = floor((double)(im->unitsexponent / 3));
     } else {
         viewdigits = digits;
     }
@@ -520,6 +547,7 @@ void expand_range(
 #endif
 }
 
+
 void apply_gridfit(
     image_desc_t *im)
 {
@@ -549,8 +577,8 @@ void apply_gridfit(
             ytr(im, DNAN);  /* reset precalc */
             log10_range = log10(im->maxval) - log10(im->minval);
         }
-        /* make sure first y=10^x gridline is located on 
-           integer pixel position by moving scale slightly 
+        /* make sure first y=10^x gridline is located on
+           integer pixel position by moving scale slightly
            downwards (sub-pixel movement) */
         ypix = ytr(im, ya) + im->ysize; /* add im->ysize so it always is positive */
         ypixfrac = ypix - floor(ypix);
@@ -704,6 +732,7 @@ void reduce_data(
                 else {
                     switch (cf) {
                     case CF_HWPREDICT:
+                    case CF_MHWPREDICT:
                     case CF_DEVSEASONAL:
                     case CF_DEVPREDICT:
                     case CF_SEASONAL:
@@ -729,6 +758,7 @@ void reduce_data(
             } else {
                 switch (cf) {
                 case CF_HWPREDICT:
+                case CF_MHWPREDICT:
                 case CF_DEVSEASONAL:
                 case CF_DEVPREDICT:
                 case CF_SEASONAL:
@@ -768,7 +798,7 @@ void reduce_data(
 }
 
 
-/* get the data required for the graphs from the 
+/* get the data required for the graphs from the
    relevant rrds ... */
 
 int data_fetch(
@@ -812,6 +842,36 @@ int data_fetch(
         if (!skip) {
             unsigned long ft_step = im->gdes[i].step;   /* ft_step will record what we got from fetch */
 
+            /* Flush the file if
+             * - a connection to the daemon has been established
+             * - this is the first occurrence of that RRD file
+             */
+            if (rrdc_is_connected(im->daemon_addr))
+            {
+                int status;
+
+                status = 0;
+                for (ii = 0; ii < i; ii++)
+                {
+                    if (strcmp (im->gdes[i].rrd, im->gdes[ii].rrd) == 0)
+                    {
+                        status = 1;
+                        break;
+                    }
+                }
+
+                if (status == 0)
+                {
+                    status = rrdc_flush (im->gdes[i].rrd);
+                    if (status != 0)
+                    {
+                        rrd_set_error ("rrdc_flush (%s) failed with status %i.",
+                                im->gdes[i].rrd, status);
+                        return (-1);
+                    }
+                }
+            } /* if (rrdc_is_connected()) */
+
             if ((rrd_fetch_fn(im->gdes[i].rrd,
                               im->gdes[i].cf,
                               &im->gdes[i].start,
@@ -855,7 +915,7 @@ int data_fetch(
 /* evaluate the expressions in the CDEF functions */
 
 /*************************************************************
- * CDEF stuff 
+ * CDEF stuff
  *************************************************************/
 
 long find_var_wrapper(
@@ -882,7 +942,7 @@ long find_var(
     return -1;
 }
 
-/* find the largest common denominator for all the numbers
+/* find the greatest common divisor for all the numbers
    in the 0 terminated num array */
 long lcd(
     long *num)
@@ -996,7 +1056,7 @@ int data_calc(
                         /* add one entry to the array that keeps track of the step sizes of the
                          * data sources going into the CDEF. */
                         if ((steparray =
-                             rrd_realloc(steparray,
+                             (long*)rrd_realloc(steparray,
                                          (++stepcnt +
                                           1) * sizeof(*steparray))) == NULL) {
                             rrd_set_error("realloc steparray");
@@ -1064,7 +1124,7 @@ int data_calc(
              */
             im->gdes[gdi].step = lcd(steparray);
             free(steparray);
-            if ((im->gdes[gdi].data = malloc(((im->gdes[gdi].end -
+            if ((im->gdes[gdi].data = (rrd_value_t*)malloc(((im->gdes[gdi].end -
                                                im->gdes[gdi].start)
                                               / im->gdes[gdi].step)
                                              * sizeof(double))) == NULL) {
@@ -1100,13 +1160,50 @@ int data_calc(
     return 0;
 }
 
+/* from http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm */
+/* yes we are loosing precision by doing tos with floats instead of doubles
+   but it seems more stable this way. */
+
+static int AlmostEqual2sComplement(
+    float A,
+    float B,
+    int maxUlps)
+{
+
+    int       aInt = *(int *) &A;
+    int       bInt = *(int *) &B;
+    int       intDiff;
+
+    /* Make sure maxUlps is non-negative and small enough that the
+       default NAN won't compare as equal to anything.  */
+
+    /* assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024); */
+
+    /* Make aInt lexicographically ordered as a twos-complement int */
+
+    if (aInt < 0)
+        aInt = 0x80000000l - aInt;
+
+    /* Make bInt lexicographically ordered as a twos-complement int */
+
+    if (bInt < 0)
+        bInt = 0x80000000l - bInt;
+
+    intDiff = abs(aInt - bInt);
+
+    if (intDiff <= maxUlps)
+        return 1;
+
+    return 0;
+}
+
 /* massage data so, that we get one value for each x coordinate in the graph */
 int data_proc(
     image_desc_t *im)
 {
     long      i, ii;
     double    pixstep = (double) (im->end - im->start)
-        / (double) im->xsize;   /* how much time 
+        / (double) im->xsize;   /* how much time
                                    passes in one pixel */
     double    paintval;
     double    minval = DNAN, maxval = DNAN;
@@ -1117,7 +1214,7 @@ int data_proc(
     for (i = 0; i < im->gdes_c; i++) {
         if ((im->gdes[i].gf == GF_LINE) ||
             (im->gdes[i].gf == GF_AREA) || (im->gdes[i].gf == GF_TICK)) {
-            if ((im->gdes[i].p_data = malloc((im->xsize + 1)
+            if ((im->gdes[i].p_data = (rrd_value_t*)malloc((im->xsize + 1)
                                              * sizeof(rrd_value_t))) == NULL) {
                 rrd_set_error("malloc data_proc");
                 return -1;
@@ -1151,7 +1248,7 @@ int data_proc(
                     } else
                         if (((long int) gr_time >=
                              (long int) im->gdes[vidx].start)
-                            && ((long int) gr_time <=
+                            && ((long int) gr_time <
                                 (long int) im->gdes[vidx].end)) {
                         value = im->gdes[vidx].data[(unsigned long)
                                                     floor((double)
@@ -1200,24 +1297,29 @@ int data_proc(
        lets set these to dummy values then ... */
 
     if (im->logarithmic) {
-        if (isnan(minval))
-            minval = 0.2;
-        if (isnan(maxval))
+        if (isnan(minval) || isnan(maxval) || maxval <= 0) {
+            minval = 0.0;   /* catching this right away below */
             maxval = 5.1;
+        }
+        /* in logarithm mode, where minval is smaller or equal
+           to 0 make the beast just way smaller than maxval */
+        if (minval <= 0) {
+            minval = maxval / 10e8;
+        }
     } else {
-        if (isnan(minval))
+        if (isnan(minval) || isnan(maxval)) {
             minval = 0.0;
-        if (isnan(maxval))
             maxval = 1.0;
+        }
     }
 
-    /* adjust min and max values */
+    /* adjust min and max values given by the user */
+    /* for logscale we add something on top */
     if (isnan(im->minval)
-        /* don't adjust low-end with log scale *//* why not? */
         || ((!im->rigid) && im->minval > minval)
         ) {
         if (im->logarithmic)
-            im->minval = minval * 0.5;
+            im->minval = minval / 2.0;
         else
             im->minval = minval;
     }
@@ -1229,19 +1331,24 @@ int data_proc(
         else
             im->maxval = maxval;
     }
+
     /* make sure min is smaller than max */
     if (im->minval > im->maxval) {
-        im->minval = 0.99 * im->maxval;
+        if (im->minval > 0)
+            im->minval = 0.99 * im->maxval;
+        else
+            im->minval = 1.01 * im->maxval;
     }
 
     /* make sure min and max are not equal */
-    if (im->minval == im->maxval) {
-        im->maxval *= 1.01;
-        if (!im->logarithmic) {
-            im->minval *= 0.99;
-        }
+    if (AlmostEqual2sComplement(im->minval, im->maxval, 4)) {
+        if (im->maxval > 0)
+            im->maxval *= 1.01;
+        else
+            im->maxval *= 0.99;
+
         /* make sure min and max are not both zero */
-        if (im->maxval == 0.0) {
+        if (AlmostEqual2sComplement(im->maxval, 0, 4)) {
             im->maxval = 1.0;
         }
     }
@@ -1370,8 +1477,7 @@ time_t find_next_time(
 /* calculate values required for PRINT and GPRINT functions */
 
 int print_calc(
-    image_desc_t *im,
-    char ***prdata)
+    image_desc_t *im)
 {
     long      i, ii, validsteps;
     double    printval;
@@ -1382,24 +1488,16 @@ int print_calc(
     double    magfact = -1;
     char     *si_symb = "";
     char     *percent_s;
-    int       prlines = 1;
+    int       prline_cnt = 0;
 
     /* wow initializing tmvdef is quite a task :-) */
     time_t    now = time(NULL);
 
     localtime_r(&now, &tmvdef);
-    if (im->imginfo)
-        prlines++;
     for (i = 0; i < im->gdes_c; i++) {
         vidx = im->gdes[i].vidx;
         switch (im->gdes[i].gf) {
         case GF_PRINT:
-            prlines++;
-            if (((*prdata) =
-                 rrd_realloc((*prdata), prlines * sizeof(char *))) == NULL) {
-                rrd_set_error("realloc prdata");
-                return 0;
-            }
         case GF_GPRINT:
             /* PRINT and GPRINT can now print VDEF generated values.
              * There's no need to do any calculations on them as these
@@ -1425,6 +1523,7 @@ int print_calc(
 
                     switch (im->gdes[i].cf) {
                     case CF_HWPREDICT:
+                    case CF_MHWPREDICT:
                     case CF_DEVPREDICT:
                     case CF_DEVSEASONAL:
                     case CF_SEASONAL:
@@ -1469,44 +1568,44 @@ int print_calc(
             }
 
             if (im->gdes[i].gf == GF_PRINT) {
-                (*prdata)[prlines - 2] =
-                    malloc((FMT_LEG_LEN + 2) * sizeof(char));
-                (*prdata)[prlines - 1] = NULL;
+                rrd_infoval_t prline;
+
                 if (im->gdes[i].strftm) {
-                    strftime((*prdata)[prlines - 2], FMT_LEG_LEN,
-                             im->gdes[i].format, &tmvdef);
+                    prline.u_str = (char*)malloc((FMT_LEG_LEN + 2) * sizeof(char));
+                    strftime(prline.u_str,
+                             FMT_LEG_LEN, im->gdes[i].format, &tmvdef);
+                } else if (bad_format(im->gdes[i].format)) {
+                    rrd_set_error
+                        ("bad format for PRINT in '%s'", im->gdes[i].format);
+                    return -1;
                 } else {
-                    if (bad_format(im->gdes[i].format)) {
-                        rrd_set_error("bad format for PRINT in '%s'",
-                                      im->gdes[i].format);
-                        return -1;
-                    }
-#ifdef HAVE_SNPRINTF
-                    snprintf((*prdata)[prlines - 2], FMT_LEG_LEN,
-                             im->gdes[i].format, printval, si_symb);
-#else
-                    sprintf((*prdata)[prlines - 2], im->gdes[i].format,
-                            printval, si_symb);
-#endif
+                    prline.u_str =
+                        sprintf_alloc(im->gdes[i].format, printval, si_symb);
                 }
+                grinfo_push(im,
+                            sprintf_alloc
+                            ("print[%ld]", prline_cnt++), RD_I_STR, prline);
+                free(prline.u_str);
             } else {
                 /* GF_GPRINT */
 
                 if (im->gdes[i].strftm) {
-                    strftime(im->gdes[i].legend, FMT_LEG_LEN,
-                             im->gdes[i].format, &tmvdef);
+                    strftime(im->gdes[i].legend,
+                             FMT_LEG_LEN, im->gdes[i].format, &tmvdef);
                 } else {
                     if (bad_format(im->gdes[i].format)) {
-                        rrd_set_error("bad format for GPRINT in '%s'",
-                                      im->gdes[i].format);
+                        rrd_set_error
+                            ("bad format for GPRINT in '%s'",
+                             im->gdes[i].format);
                         return -1;
                     }
 #ifdef HAVE_SNPRINTF
-                    snprintf(im->gdes[i].legend, FMT_LEG_LEN - 2,
+                    snprintf(im->gdes[i].legend,
+                             FMT_LEG_LEN - 2,
                              im->gdes[i].format, printval, si_symb);
 #else
-                    sprintf(im->gdes[i].legend, im->gdes[i].format, printval,
-                            si_symb);
+                    sprintf(im->gdes[i].legend,
+                            im->gdes[i].format, printval, si_symb);
 #endif
                 }
                 graphelement = 1;
@@ -1530,6 +1629,7 @@ int print_calc(
             graphelement = 1;
             break;
         case GF_COMMENT:
+        case GF_TEXTALIGN:
         case GF_DEF:
         case GF_CDEF:
         case GF_VDEF:
@@ -1550,57 +1650,75 @@ int print_calc(
 }
 
 
+
 /* place legends with color spots */
 int leg_place(
     image_desc_t *im,
-    int *gY)
+    int calc_width)
 {
     /* graph labels */
     int       interleg = im->text_prop[TEXT_PROP_LEGEND].size * 2.0;
     int       border = im->text_prop[TEXT_PROP_LEGEND].size * 2.0;
     int       fill = 0, fill_last;
+    double    legendwidth; // = im->ximg - 2 * border;
     int       leg_c = 0;
-    int       leg_x = border, leg_y = im->yimg;
-    int       leg_y_prev = im->yimg;
+    double    leg_x = border;
+    int       leg_y = 0; //im->yimg;
+    int       leg_y_prev = 0; // im->yimg;
     int       leg_cc;
-    int       glue = 0;
+    double    glue = 0;
     int       i, ii, mark = 0;
-    char      prt_fctn; /*special printfunctions */
+    char      default_txtalign = TXA_JUSTIFIED; /*default line orientation */
     int      *legspace;
+    char     *tab;
+    char      saved_legend[FMT_LEG_LEN + 5];
+
+    if(calc_width){
+        legendwidth = 0;
+    }
+    else{
+        legendwidth = im->legendwidth - 2 * border;
+    }
 
-    if (!(im->extra_flags & NOLEGEND) & !(im->extra_flags & ONLY_GRAPH)) {
-        if ((legspace = malloc(im->gdes_c * sizeof(int))) == NULL) {
+
+    if (!(im->extra_flags & NOLEGEND) && !(im->extra_flags & ONLY_GRAPH)) {
+        if ((legspace = (int*)malloc(im->gdes_c * sizeof(int))) == NULL) {
             rrd_set_error("malloc for legspace");
             return -1;
         }
 
-        if (im->extra_flags & FULL_SIZE_MODE)
-            leg_y = leg_y_prev =
-                leg_y - (int) (im->text_prop[TEXT_PROP_LEGEND].size * 1.8);
-
         for (i = 0; i < im->gdes_c; i++) {
-            fill_last = fill;
+            char      prt_fctn; /*special printfunctions */
+            if(calc_width){
+                strcpy(saved_legend, im->gdes[i].legend);
+            }
 
+            fill_last = fill;
             /* hide legends for rules which are not displayed */
+            if (im->gdes[i].gf == GF_TEXTALIGN) {
+                default_txtalign = im->gdes[i].txtalign;
+            }
 
             if (!(im->extra_flags & FORCE_RULES_LEGEND)) {
-                if (im->gdes[i].gf == GF_HRULE &&
-                    (im->gdes[i].yrule < im->minval
-                     || im->gdes[i].yrule > im->maxval))
+                if (im->gdes[i].gf == GF_HRULE
+                    && (im->gdes[i].yrule <
+                        im->minval || im->gdes[i].yrule > im->maxval))
                     im->gdes[i].legend[0] = '\0';
-
-                if (im->gdes[i].gf == GF_VRULE &&
-                    (im->gdes[i].xrule < im->start
-                     || im->gdes[i].xrule > im->end))
+                if (im->gdes[i].gf == GF_VRULE
+                    && (im->gdes[i].xrule <
+                        im->start || im->gdes[i].xrule > im->end))
                     im->gdes[i].legend[0] = '\0';
             }
 
-            leg_cc = strlen(im->gdes[i].legend);
+            /* turn \\t into tab */
+            while ((tab = strstr(im->gdes[i].legend, "\\t"))) {
+                memmove(tab, tab + 1, strlen(tab));
+                tab[0] = (char) 9;
+            }
 
-            /* is there a controle code ant the end of the legend string ? */
-            /* and it is not a tab \\t */
-            if (leg_cc >= 2 && im->gdes[i].legend[leg_cc - 2] == '\\'
-                && im->gdes[i].legend[leg_cc - 1] != 't') {
+            leg_cc = strlen(im->gdes[i].legend);
+            /* is there a controle code at the end of the legend string ? */
+            if (leg_cc >= 2 && im->gdes[i].legend[leg_cc - 2] == '\\') {
                 prt_fctn = im->gdes[i].legend[leg_cc - 1];
                 leg_cc -= 2;
                 im->gdes[i].legend[leg_cc] = '\0';
@@ -1612,38 +1730,40 @@ int leg_place(
                 prt_fctn != 'r' &&
                 prt_fctn != 'j' &&
                 prt_fctn != 'c' &&
-                prt_fctn != 's' &&
-                prt_fctn != 't' && prt_fctn != '\0' && prt_fctn != 'g') {
+                prt_fctn != 's' && prt_fctn != '\0' && prt_fctn != 'g') {
                 free(legspace);
-                rrd_set_error("Unknown control code at the end of '%s\\%c'",
-                              im->gdes[i].legend, prt_fctn);
+                rrd_set_error
+                    ("Unknown control code at the end of '%s\\%c'",
+                     im->gdes[i].legend, prt_fctn);
                 return -1;
-
             }
-
-            /* remove exess space */
+            /* \n -> \l */
             if (prt_fctn == 'n') {
                 prt_fctn = 'l';
             }
 
+            /* remove exess space from the end of the legend for \g */
             while (prt_fctn == 'g' &&
                    leg_cc > 0 && im->gdes[i].legend[leg_cc - 1] == ' ') {
                 leg_cc--;
                 im->gdes[i].legend[leg_cc] = '\0';
             }
+
             if (leg_cc != 0) {
-                legspace[i] = (prt_fctn == 'g' ? 0 : interleg);
 
+                /* no interleg space if string ends in \g */
+                legspace[i] = (prt_fctn == 'g' ? 0 : interleg);
                 if (fill > 0) {
-                    /* no interleg space if string ends in \g */
                     fill += legspace[i];
                 }
-                fill += gfx_get_text_width(im->canvas, fill + border,
-                                           im->text_prop[TEXT_PROP_LEGEND].
-                                           font,
-                                           im->text_prop[TEXT_PROP_LEGEND].
-                                           size, im->tabwidth,
-                                           im->gdes[i].legend, 0);
+                fill +=
+                    gfx_get_text_width(im,
+                                       fill + border,
+                                       im->
+                                       text_prop
+                                       [TEXT_PROP_LEGEND].
+                                       font_desc,
+                                       im->tabwidth, im->gdes[i].legend);
                 leg_c++;
             } else {
                 legspace[i] = 0;
@@ -1652,83 +1772,92 @@ int leg_place(
             if (prt_fctn == 'g') {
                 prt_fctn = '\0';
             }
-            if (prt_fctn == '\0') {
-                if (i == im->gdes_c - 1)
-                    prt_fctn = 'l';
 
+            if (prt_fctn == '\0') {
+                if(calc_width && (fill > legendwidth)){
+                    legendwidth = fill;
+                }
+                if (i == im->gdes_c - 1 || fill > legendwidth) {
+                    /* just one legend item is left right or center */
+                    switch (default_txtalign) {
+                    case TXA_RIGHT:
+                        prt_fctn = 'r';
+                        break;
+                    case TXA_CENTER:
+                        prt_fctn = 'c';
+                        break;
+                    case TXA_JUSTIFIED:
+                        prt_fctn = 'j';
+                        break;
+                    default:
+                        prt_fctn = 'l';
+                        break;
+                    }
+                }
                 /* is it time to place the legends ? */
-                if (fill > im->ximg - 2 * border) {
+                if (fill > legendwidth) {
                     if (leg_c > 1) {
                         /* go back one */
                         i--;
                         fill = fill_last;
                         leg_c--;
-                        prt_fctn = 'j';
-                    } else {
-                        prt_fctn = 'l';
                     }
-
+                }
+                if (leg_c == 1 && prt_fctn == 'j') {
+                    prt_fctn = 'l';
                 }
             }
 
-
             if (prt_fctn != '\0') {
                 leg_x = border;
                 if (leg_c >= 2 && prt_fctn == 'j') {
-                    glue = (im->ximg - fill - 2 * border) / (leg_c - 1);
+                    glue = (double)(legendwidth - fill) / (double)(leg_c - 1);
                 } else {
                     glue = 0;
                 }
                 if (prt_fctn == 'c')
-                    leg_x = (im->ximg - fill) / 2.0;
+                    leg_x = (double)(legendwidth - fill) / 2.0;
                 if (prt_fctn == 'r')
-                    leg_x = im->ximg - fill - border;
-
+                    leg_x = legendwidth - fill - border;
                 for (ii = mark; ii <= i; ii++) {
                     if (im->gdes[ii].legend[0] == '\0')
                         continue;   /* skip empty legends */
                     im->gdes[ii].leg_x = leg_x;
-                    im->gdes[ii].leg_y = leg_y;
+                    im->gdes[ii].leg_y = leg_y + border;
                     leg_x +=
-                        gfx_get_text_width(im->canvas, leg_x,
-                                           im->text_prop[TEXT_PROP_LEGEND].
-                                           font,
-                                           im->text_prop[TEXT_PROP_LEGEND].
-                                           size, im->tabwidth,
-                                           im->gdes[ii].legend, 0)
-                        + legspace[ii]
+                        (double)gfx_get_text_width(im, leg_x,
+                                           im->
+                                           text_prop
+                                           [TEXT_PROP_LEGEND].
+                                           font_desc,
+                                           im->tabwidth, im->gdes[ii].legend)
+                        +(double)legspace[ii]
                         + glue;
                 }
                 leg_y_prev = leg_y;
-                if (im->extra_flags & FULL_SIZE_MODE) {
-                    /* only add y space if there was text on the line */
-                    if (leg_x > border || prt_fctn == 's')
-                        leg_y -= im->text_prop[TEXT_PROP_LEGEND].size * 1.8;
-                    if (prt_fctn == 's')
-                        leg_y += im->text_prop[TEXT_PROP_LEGEND].size;
-                } else {
-                    if (leg_x > border || prt_fctn == 's')
-                        leg_y += im->text_prop[TEXT_PROP_LEGEND].size * 1.8;
-                    if (prt_fctn == 's')
-                        leg_y -= im->text_prop[TEXT_PROP_LEGEND].size;
+                if (leg_x > border || prt_fctn == 's')
+                    leg_y += im->text_prop[TEXT_PROP_LEGEND].size * 1.8;
+                if (prt_fctn == 's')
+                    leg_y -= im->text_prop[TEXT_PROP_LEGEND].size;
+
+                if(calc_width && (fill > legendwidth)){
+                    legendwidth = fill;
                 }
                 fill = 0;
                 leg_c = 0;
                 mark = ii;
             }
-        }
 
-        if (im->extra_flags & FULL_SIZE_MODE) {
-            if (leg_y != leg_y_prev) {
-                *gY = leg_y - im->text_prop[TEXT_PROP_LEGEND].size * 1.8;
-                im->yorigin =
-                    leg_y - im->text_prop[TEXT_PROP_LEGEND].size * 1.8;
+            if(calc_width){
+                strcpy(im->gdes[i].legend, saved_legend);
             }
-        } else {
-            im->yimg = leg_y_prev;
-            /* if we did place some legends we have to add vertical space */
-            if (leg_y != im->yimg)
-                im->yimg += im->text_prop[TEXT_PROP_LEGEND].size * 1.8;
+        }
+
+        if(calc_width){
+            im->legendwidth = legendwidth + 2 * border;
+        }
+        else{
+            im->legendheight = leg_y + border * 0.6;
         }
         free(legspace);
     }
@@ -1741,10 +1870,9 @@ int leg_place(
 /* the xaxis labels are determined from the number of seconds per pixel
    in the requested graph */
 
-
-
 int calc_horizontal_grid(
-    image_desc_t *im)
+    image_desc_t
+    *im)
 {
     double    range;
     double    scaledrange;
@@ -1755,7 +1883,6 @@ int calc_horizontal_grid(
     im->ygrid_scale.labfact = 2;
     range = im->maxval - im->minval;
     scaledrange = range / im->magfact;
-
     /* does the scale of this graph make it impossible to put lines
        on it? If so, give up. */
     if (isnan(scaledrange)) {
@@ -1773,22 +1900,23 @@ int calc_horizontal_grid(
                       im->viewfactor / im->magfact));
             if (decimals <= 0)  /* everything is small. make place for zero */
                 decimals = 1;
-
             im->ygrid_scale.gridstep =
                 pow((double) 10,
                     floor(log10(range * im->viewfactor / im->magfact))) /
                 im->viewfactor * im->magfact;
-
             if (im->ygrid_scale.gridstep == 0)  /* range is one -> 0.1 is reasonable scale */
                 im->ygrid_scale.gridstep = 0.1;
             /* should have at least 5 lines but no more then 15 */
-            if (range / im->ygrid_scale.gridstep < 5)
+            if (range / im->ygrid_scale.gridstep < 5
+                && im->ygrid_scale.gridstep >= 30)
                 im->ygrid_scale.gridstep /= 10;
             if (range / im->ygrid_scale.gridstep > 15)
                 im->ygrid_scale.gridstep *= 10;
             if (range / im->ygrid_scale.gridstep > 5) {
                 im->ygrid_scale.labfact = 1;
-                if (range / im->ygrid_scale.gridstep > 8)
+                if (range / im->ygrid_scale.gridstep > 8
+                    || im->ygrid_scale.gridstep <
+                    1.8 * im->text_prop[TEXT_PROP_AXIS].size)
                     im->ygrid_scale.labfact = 2;
             } else {
                 im->ygrid_scale.gridstep /= 5;
@@ -1804,27 +1932,28 @@ int calc_horizontal_grid(
 
                 if (im->unitslength < len + 2)
                     im->unitslength = len + 2;
-                sprintf(im->ygrid_scale.labfmt, "%%%d.%df%s", len,
+                sprintf(im->ygrid_scale.labfmt,
+                        "%%%d.%df%s", len,
                         -fractionals, (im->symbol != ' ' ? " %c" : ""));
             } else {
                 int       len = decimals + 1;
 
                 if (im->unitslength < len + 2)
                     im->unitslength = len + 2;
-                sprintf(im->ygrid_scale.labfmt, "%%%d.0f%s", len,
-                        (im->symbol != ' ' ? " %c" : ""));
+                sprintf(im->ygrid_scale.labfmt,
+                        "%%%d.0f%s", len, (im->symbol != ' ' ? " %c" : ""));
             }
-        } else {
+        } else {        /* classic rrd grid */
             for (i = 0; ylab[i].grid > 0; i++) {
                 pixel = im->ysize / (scaledrange / ylab[i].grid);
                 gridind = i;
-                if (pixel > 7)
+                if (pixel >= 5)
                     break;
             }
 
             for (i = 0; i < 4; i++) {
                 if (pixel * ylab[gridind].lfac[i] >=
-                    2.5 * im->text_prop[TEXT_PROP_AXIS].size) {
+                    1.8 * im->text_prop[TEXT_PROP_AXIS].size) {
                     im->ygrid_scale.labfact = ylab[gridind].lfac[i];
                     break;
                 }
@@ -1840,7 +1969,8 @@ int calc_horizontal_grid(
 }
 
 int draw_horizontal_grid(
-    image_desc_t *im)
+    image_desc_t
+    *im)
 {
     int       i;
     double    scaledstep;
@@ -1848,21 +1978,24 @@ int draw_horizontal_grid(
     int       nlabels = 0;
     double    X0 = im->xorigin;
     double    X1 = im->xorigin + im->xsize;
-
     int       sgrid = (int) (im->minval / im->ygrid_scale.gridstep - 1);
     int       egrid = (int) (im->maxval / im->ygrid_scale.gridstep + 1);
     double    MaxY;
+    double second_axis_magfact = 0;
+    char *second_axis_symb = "";
 
     scaledstep =
-        im->ygrid_scale.gridstep / (double) im->magfact *
-        (double) im->viewfactor;
+        im->ygrid_scale.gridstep /
+        (double) im->magfact * (double) im->viewfactor;
     MaxY = scaledstep * (double) egrid;
     for (i = sgrid; i <= egrid; i++) {
-        double    Y0 = ytr(im, im->ygrid_scale.gridstep * i);
-        double    YN = ytr(im, im->ygrid_scale.gridstep * (i + 1));
+        double    Y0 = ytr(im,
+                           im->ygrid_scale.gridstep * i);
+        double    YN = ytr(im,
+                           im->ygrid_scale.gridstep * (i + 1));
 
-        if (floor(Y0 + 0.5) >= im->yorigin - im->ysize
-            && floor(Y0 + 0.5) <= im->yorigin) {
+        if (floor(Y0 + 0.5) >=
+            im->yorigin - im->ysize && floor(Y0 + 0.5) <= im->yorigin) {
             /* Make sure at least 2 grid labels are shown, even if it doesn't agree
                with the chosen settings. Add a label if required by settings, or if
                there is only one label so far and the next grid line is out of bounds. */
@@ -1871,7 +2004,8 @@ int draw_horizontal_grid(
                     && (YN < im->yorigin - im->ysize || YN > im->yorigin))) {
                 if (im->symbol == ' ') {
                     if (im->extra_flags & ALTYGRID) {
-                        sprintf(graph_label, im->ygrid_scale.labfmt,
+                        sprintf(graph_label,
+                                im->ygrid_scale.labfmt,
                                 scaledstep * (double) i);
                     } else {
                         if (MaxY < 10) {
@@ -1886,7 +2020,8 @@ int draw_horizontal_grid(
                     char      sisym = (i == 0 ? ' ' : im->symbol);
 
                     if (im->extra_flags & ALTYGRID) {
-                        sprintf(graph_label, im->ygrid_scale.labfmt,
+                        sprintf(graph_label,
+                                im->ygrid_scale.labfmt,
                                 scaledstep * (double) i, sisym);
                     } else {
                         if (MaxY < 10) {
@@ -1899,27 +2034,67 @@ int draw_horizontal_grid(
                     }
                 }
                 nlabels++;
+                if (im->second_axis_scale != 0){
+                        char graph_label_right[100];
+                        double sval = im->ygrid_scale.gridstep*(double)i*im->second_axis_scale+im->second_axis_shift;
+                        if (im->second_axis_format[0] == '\0'){
+                            if (!second_axis_magfact){
+                                double dummy = im->ygrid_scale.gridstep*(double)(sgrid+egrid)/2.0*im->second_axis_scale+im->second_axis_shift;
+                                auto_scale(im,&dummy,&second_axis_symb,&second_axis_magfact);
+                            }
+                            sval /= second_axis_magfact;
 
-                gfx_new_text(im->canvas,
-                             X0 - im->text_prop[TEXT_PROP_AXIS].size, Y0,
-                             im->graph_col[GRC_FONT],
-                             im->text_prop[TEXT_PROP_AXIS].font,
-                             im->text_prop[TEXT_PROP_AXIS].size,
-                             im->tabwidth, 0.0, GFX_H_RIGHT, GFX_V_CENTER,
-                             graph_label);
-                gfx_new_dashed_line(im->canvas,
-                                    X0 - 2, Y0,
-                                    X1 + 2, Y0,
-                                    MGRIDWIDTH, im->graph_col[GRC_MGRID],
-                                    im->grid_dash_on, im->grid_dash_off);
+                            if(MaxY < 10) {
+                                sprintf(graph_label_right,"%5.1f %s",sval,second_axis_symb);
+                            } else {
+                                sprintf(graph_label_right,"%5.0f %s",sval,second_axis_symb);
+                            }
+                        }
+                        else {
+                           sprintf(graph_label_right,im->second_axis_format,sval);
+                        }
+                        gfx_text ( im,
+                               X1+7, Y0,
+                               im->graph_col[GRC_FONT],
+                               im->text_prop[TEXT_PROP_AXIS].font_desc,
+                               im->tabwidth,0.0, GFX_H_LEFT, GFX_V_CENTER,
+                               graph_label_right );
+                }
 
+                gfx_text(im,
+                         X0 -
+                         im->
+                         text_prop[TEXT_PROP_AXIS].
+                         size, Y0,
+                         im->graph_col[GRC_FONT],
+                         im->
+                         text_prop[TEXT_PROP_AXIS].
+                         font_desc,
+                         im->tabwidth, 0.0,
+                         GFX_H_RIGHT, GFX_V_CENTER, graph_label);
+                gfx_line(im, X0 - 2, Y0, X0, Y0,
+                         MGRIDWIDTH, im->graph_col[GRC_MGRID]);
+                gfx_line(im, X1, Y0, X1 + 2, Y0,
+                         MGRIDWIDTH, im->graph_col[GRC_MGRID]);
+                gfx_dashed_line(im, X0 - 2, Y0,
+                                X1 + 2, Y0,
+                                MGRIDWIDTH,
+                                im->
+                                graph_col
+                                [GRC_MGRID],
+                                im->grid_dash_on, im->grid_dash_off);
             } else if (!(im->extra_flags & NOMINOR)) {
-                gfx_new_dashed_line(im->canvas,
-                                    X0 - 1, Y0,
-                                    X1 + 1, Y0,
-                                    GRIDWIDTH, im->graph_col[GRC_GRID],
-                                    im->grid_dash_on, im->grid_dash_off);
-
+                gfx_line(im,
+                         X0 - 2, Y0,
+                         X0, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]);
+                gfx_line(im, X1, Y0, X1 + 2, Y0,
+                         GRIDWIDTH, im->graph_col[GRC_GRID]);
+                gfx_dashed_line(im, X0 - 1, Y0,
+                                X1 + 1, Y0,
+                                GRIDWIDTH,
+                                im->
+                                graph_col[GRC_GRID],
+                                im->grid_dash_on, im->grid_dash_off);
             }
         }
     }
@@ -1937,7 +2112,7 @@ double frexp10(
     double    mnt;
     int       iexp;
 
-    iexp = floor(log(fabs(x)) / log(10));
+    iexp = floor(log((double)fabs(x)) / log((double)10));
     mnt = x / pow(10.0, iexp);
     if (mnt >= 10.0) {
         iexp++;
@@ -1947,52 +2122,37 @@ double frexp10(
     return mnt;
 }
 
-static int AlmostEqual2sComplement(
-    float A,
-    float B,
-    int maxUlps)
-{
-
-    int       aInt = *(int *) &A;
-    int       bInt = *(int *) &B;
-    int       intDiff;
-
-    /* Make sure maxUlps is non-negative and small enough that the
-       default NAN won't compare as equal to anything.  */
-
-    /* assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024); */
-
-    /* Make aInt lexicographically ordered as a twos-complement int */
-
-    if (aInt < 0)
-        aInt = 0x80000000l - aInt;
-
-    /* Make bInt lexicographically ordered as a twos-complement int */
-
-    if (bInt < 0)
-        bInt = 0x80000000l - bInt;
-
-    intDiff = abs(aInt - bInt);
-
-    if (intDiff <= maxUlps)
-        return 1;
-
-    return 0;
-}
 
 /* logaritmic horizontal grid */
 int horizontal_log_grid(
-    image_desc_t *im)
+    image_desc_t
+    *im)
 {
     double    yloglab[][10] = {
-        {1.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
-        {1.0, 5.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
-        {1.0, 2.0, 5.0, 7.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0},
-        {1.0, 2.0, 4.0, 6.0, 8.0, 10., 0.0, 0.0, 0.0, 0.0},
-        {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.},
-        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}  /* last line */
+        {
+         1.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0,
+         0.0, 0.0, 0.0}, {
+                          1.0, 5.0, 10., 0.0, 0.0, 0.0, 0.0,
+                          0.0, 0.0, 0.0}, {
+                                           1.0, 2.0, 5.0, 7.0, 10., 0.0, 0.0,
+                                           0.0, 0.0, 0.0}, {
+                                                            1.0, 2.0, 4.0,
+                                                            6.0, 8.0, 10.,
+                                                            0.0,
+                                                            0.0, 0.0, 0.0}, {
+                                                                             1.0,
+                                                                             2.0,
+                                                                             3.0,
+                                                                             4.0,
+                                                                             5.0,
+                                                                             6.0,
+                                                                             7.0,
+                                                                             8.0,
+                                                                             9.0,
+                                                                             10.},
+        {
+         0, 0, 0, 0, 0, 0, 0, 0, 0, 0}  /* last line */
     };
-
     int       i, j, val_exp, min_exp;
     double    nex;      /* number of decades in data */
     double    logscale; /* scale in logarithmic space */
@@ -2006,7 +2166,6 @@ int horizontal_log_grid(
 
     nex = log10(im->maxval / im->minval);
     logscale = im->ysize / nex;
-
     /* major spacing for data with high dynamic range */
     while (logscale * exfrac < 3 * im->text_prop[TEXT_PROP_LEGEND].size) {
         if (exfrac == 1)
@@ -2021,11 +2180,11 @@ int horizontal_log_grid(
         mid++;
         for (i = 0; yloglab[mid][i + 1] < 10.0; i++);
         mspac = logscale * log10(10.0 / yloglab[mid][i]);
-    } while (mspac > 2 * im->text_prop[TEXT_PROP_LEGEND].size
-             && yloglab[mid][0] > 0);
+    }
+    while (mspac >
+           2 * im->text_prop[TEXT_PROP_LEGEND].size && yloglab[mid][0] > 0);
     if (mid)
         mid--;
-
     /* find first value in yloglab */
     for (flab = 0;
          yloglab[mid][flab] < 10
@@ -2037,10 +2196,8 @@ int horizontal_log_grid(
     val_exp = tmp;
     if (val_exp % exfrac)
         val_exp += abs(-val_exp % exfrac);
-
     X0 = im->xorigin;
     X1 = im->xorigin + im->xsize;
-
     /* draw grid */
     pre_value = DNAN;
     while (1) {
@@ -2048,20 +2205,21 @@ int horizontal_log_grid(
         value = yloglab[mid][flab] * pow(10.0, val_exp);
         if (AlmostEqual2sComplement(value, pre_value, 4))
             break;      /* it seems we are not converging */
-
         pre_value = value;
-
         Y0 = ytr(im, value);
         if (floor(Y0 + 0.5) <= im->yorigin - im->ysize)
             break;
-
         /* major grid line */
-        gfx_new_dashed_line(im->canvas,
-                            X0 - 2, Y0,
-                            X1 + 2, Y0,
-                            MGRIDWIDTH, im->graph_col[GRC_MGRID],
-                            im->grid_dash_on, im->grid_dash_off);
-
+        gfx_line(im,
+                 X0 - 2, Y0, X0, Y0, MGRIDWIDTH, im->graph_col[GRC_MGRID]);
+        gfx_line(im, X1, Y0, X1 + 2, Y0,
+                 MGRIDWIDTH, im->graph_col[GRC_MGRID]);
+        gfx_dashed_line(im, X0 - 2, Y0,
+                        X1 + 2, Y0,
+                        MGRIDWIDTH,
+                        im->
+                        graph_col
+                        [GRC_MGRID], im->grid_dash_on, im->grid_dash_off);
         /* label */
         if (im->extra_flags & FORCE_UNITS_SI) {
             int       scale;
@@ -2074,24 +2232,52 @@ int horizontal_log_grid(
             else
                 pvalue = pow(10.0, ((val_exp + 1) % 3) + 2);
             pvalue *= yloglab[mid][flab];
-
-            if (((scale + si_symbcenter) < (int) sizeof(si_symbol)) &&
-                ((scale + si_symbcenter) >= 0))
+            if (((scale + si_symbcenter) < (int) sizeof(si_symbol))
+                && ((scale + si_symbcenter) >= 0))
                 symbol = si_symbol[scale + si_symbcenter];
             else
                 symbol = '?';
-
             sprintf(graph_label, "%3.0f %c", pvalue, symbol);
-        } else
+        } else {
             sprintf(graph_label, "%3.0e", value);
-        gfx_new_text(im->canvas,
-                     X0 - im->text_prop[TEXT_PROP_AXIS].size, Y0,
-                     im->graph_col[GRC_FONT],
-                     im->text_prop[TEXT_PROP_AXIS].font,
-                     im->text_prop[TEXT_PROP_AXIS].size,
-                     im->tabwidth, 0.0, GFX_H_RIGHT, GFX_V_CENTER,
-                     graph_label);
+        }
+        if (im->second_axis_scale != 0){
+                char graph_label_right[100];
+                double sval = value*im->second_axis_scale+im->second_axis_shift;
+                if (im->second_axis_format[0] == '\0'){
+                        if (im->extra_flags & FORCE_UNITS_SI) {
+                                double mfac = 1;
+                                char   *symb = "";
+                                auto_scale(im,&sval,&symb,&mfac);
+                                sprintf(graph_label_right,"%4.0f %s", sval,symb);
+                        }
+                        else {
+                                sprintf(graph_label_right,"%3.0e", sval);
+                        }
+                }
+                else {
+                      sprintf(graph_label_right,im->second_axis_format,sval);
+                }
+
+                gfx_text ( im,
+                               X1+7, Y0,
+                               im->graph_col[GRC_FONT],
+                               im->text_prop[TEXT_PROP_AXIS].font_desc,
+                               im->tabwidth,0.0, GFX_H_LEFT, GFX_V_CENTER,
+                               graph_label_right );
+        }
 
+        gfx_text(im,
+                 X0 -
+                 im->
+                 text_prop[TEXT_PROP_AXIS].
+                 size, Y0,
+                 im->graph_col[GRC_FONT],
+                 im->
+                 text_prop[TEXT_PROP_AXIS].
+                 font_desc,
+                 im->tabwidth, 0.0,
+                 GFX_H_RIGHT, GFX_V_CENTER, graph_label);
         /* minor grid */
         if (mid < 4 && exfrac == 1) {
             /* find first and last minor line behind current major line
@@ -2113,34 +2299,42 @@ int horizontal_log_grid(
                 value = i * pow(10.0, min_exp);
                 if (value < im->minval)
                     continue;
-
                 Y0 = ytr(im, value);
                 if (floor(Y0 + 0.5) <= im->yorigin - im->ysize)
                     break;
-
                 /* draw lines */
-                gfx_new_dashed_line(im->canvas,
-                                    X0 - 1, Y0,
-                                    X1 + 1, Y0,
-                                    GRIDWIDTH, im->graph_col[GRC_GRID],
-                                    im->grid_dash_on, im->grid_dash_off);
+                gfx_line(im,
+                         X0 - 2, Y0,
+                         X0, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]);
+                gfx_line(im, X1, Y0, X1 + 2, Y0,
+                         GRIDWIDTH, im->graph_col[GRC_GRID]);
+                gfx_dashed_line(im, X0 - 1, Y0,
+                                X1 + 1, Y0,
+                                GRIDWIDTH,
+                                im->
+                                graph_col[GRC_GRID],
+                                im->grid_dash_on, im->grid_dash_off);
             }
         } else if (exfrac > 1) {
             for (i = val_exp - exfrac / 3 * 2; i < val_exp; i += exfrac / 3) {
                 value = pow(10.0, i);
                 if (value < im->minval)
                     continue;
-
                 Y0 = ytr(im, value);
                 if (floor(Y0 + 0.5) <= im->yorigin - im->ysize)
                     break;
-
                 /* draw lines */
-                gfx_new_dashed_line(im->canvas,
-                                    X0 - 1, Y0,
-                                    X1 + 1, Y0,
-                                    GRIDWIDTH, im->graph_col[GRC_GRID],
-                                    im->grid_dash_on, im->grid_dash_off);
+                gfx_line(im,
+                         X0 - 2, Y0,
+                         X0, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]);
+                gfx_line(im, X1, Y0, X1 + 2, Y0,
+                         GRIDWIDTH, im->graph_col[GRC_GRID]);
+                gfx_dashed_line(im, X0 - 1, Y0,
+                                X1 + 1, Y0,
+                                GRIDWIDTH,
+                                im->
+                                graph_col[GRC_GRID],
+                                im->grid_dash_on, im->grid_dash_off);
             }
         }
 
@@ -2172,17 +2366,20 @@ int horizontal_log_grid(
             value = i * pow(10.0, min_exp);
             if (value < im->minval)
                 continue;
-
             Y0 = ytr(im, value);
             if (floor(Y0 + 0.5) <= im->yorigin - im->ysize)
                 break;
-
             /* draw lines */
-            gfx_new_dashed_line(im->canvas,
-                                X0 - 1, Y0,
-                                X1 + 1, Y0,
-                                GRIDWIDTH, im->graph_col[GRC_GRID],
-                                im->grid_dash_on, im->grid_dash_off);
+            gfx_line(im,
+                     X0 - 2, Y0, X0, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]);
+            gfx_line(im, X1, Y0, X1 + 2, Y0,
+                     GRIDWIDTH, im->graph_col[GRC_GRID]);
+            gfx_dashed_line(im, X0 - 1, Y0,
+                            X1 + 1, Y0,
+                            GRIDWIDTH,
+                            im->
+                            graph_col[GRC_GRID],
+                            im->grid_dash_on, im->grid_dash_off);
         }
     }
     /* fancy minor gridlines */
@@ -2191,17 +2388,20 @@ int horizontal_log_grid(
             value = pow(10.0, i);
             if (value < im->minval)
                 continue;
-
             Y0 = ytr(im, value);
             if (floor(Y0 + 0.5) <= im->yorigin - im->ysize)
                 break;
-
             /* draw lines */
-            gfx_new_dashed_line(im->canvas,
-                                X0 - 1, Y0,
-                                X1 + 1, Y0,
-                                GRIDWIDTH, im->graph_col[GRC_GRID],
-                                im->grid_dash_on, im->grid_dash_off);
+            gfx_line(im,
+                     X0 - 2, Y0, X0, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]);
+            gfx_line(im, X1, Y0, X1 + 2, Y0,
+                     GRIDWIDTH, im->graph_col[GRC_GRID]);
+            gfx_dashed_line(im, X0 - 1, Y0,
+                            X1 + 1, Y0,
+                            GRIDWIDTH,
+                            im->
+                            graph_col[GRC_GRID],
+                            im->grid_dash_on, im->grid_dash_off);
         }
     }
 
@@ -2221,16 +2421,15 @@ void vertical_grid(
 
     /* the type of time grid is determined by finding
        the number of seconds per pixel in the graph */
-
-
     if (im->xlab_user.minsec == -1) {
         factor = (im->end - im->start) / im->xsize;
         xlab_sel = 0;
-        while (xlab[xlab_sel + 1].minsec != -1
-               && xlab[xlab_sel + 1].minsec <= factor) {
+        while (xlab[xlab_sel + 1].minsec !=
+               -1 && xlab[xlab_sel + 1].minsec <= factor) {
             xlab_sel++;
         }               /* pick the last one */
-        while (xlab[xlab_sel - 1].minsec == xlab[xlab_sel].minsec
+        while (xlab[xlab_sel - 1].minsec ==
+               xlab[xlab_sel].minsec
                && xlab[xlab_sel].length > (im->end - im->start)) {
             xlab_sel--;
         }               /* go back to the smallest size */
@@ -2247,16 +2446,21 @@ void vertical_grid(
     /* y coords are the same for every line ... */
     Y0 = im->yorigin;
     Y1 = im->yorigin - im->ysize;
-
-
     /* paint the minor grid */
     if (!(im->extra_flags & NOMINOR)) {
         for (ti = find_first_time(im->start,
-                                  im->xlab_user.gridtm,
-                                  im->xlab_user.gridst),
-             timajor = find_first_time(im->start,
-                                       im->xlab_user.mgridtm,
-                                       im->xlab_user.mgridst);
+                                  im->
+                                  xlab_user.
+                                  gridtm,
+                                  im->
+                                  xlab_user.
+                                  gridst),
+             timajor =
+             find_first_time(im->start,
+                             im->xlab_user.
+                             mgridtm,
+                             im->xlab_user.
+                             mgridst);
              ti < im->end;
              ti =
              find_next_time(ti, im->xlab_user.gridtm, im->xlab_user.gridst)
@@ -2266,23 +2470,33 @@ void vertical_grid(
                 continue;
             while (timajor < ti) {
                 timajor = find_next_time(timajor,
-                                         im->xlab_user.mgridtm,
-                                         im->xlab_user.mgridst);
+                                         im->
+                                         xlab_user.
+                                         mgridtm, im->xlab_user.mgridst);
             }
             if (ti == timajor)
                 continue;   /* skip as falls on major grid line */
             X0 = xtr(im, ti);
-            gfx_new_dashed_line(im->canvas, X0, Y0 + 1, X0, Y1 - 1, GRIDWIDTH,
-                                im->graph_col[GRC_GRID],
-                                im->grid_dash_on, im->grid_dash_off);
-
+            gfx_line(im, X0, Y1 - 2, X0, Y1,
+                     GRIDWIDTH, im->graph_col[GRC_GRID]);
+            gfx_line(im, X0, Y0, X0, Y0 + 2,
+                     GRIDWIDTH, im->graph_col[GRC_GRID]);
+            gfx_dashed_line(im, X0, Y0 + 1, X0,
+                            Y1 - 1, GRIDWIDTH,
+                            im->
+                            graph_col[GRC_GRID],
+                            im->grid_dash_on, im->grid_dash_off);
         }
     }
 
     /* paint the major grid */
     for (ti = find_first_time(im->start,
-                              im->xlab_user.mgridtm,
-                              im->xlab_user.mgridst);
+                              im->
+                              xlab_user.
+                              mgridtm,
+                              im->
+                              xlab_user.
+                              mgridst);
          ti < im->end;
          ti = find_next_time(ti, im->xlab_user.mgridtm, im->xlab_user.mgridst)
         ) {
@@ -2290,37 +2504,49 @@ void vertical_grid(
         if (ti < im->start || ti > im->end)
             continue;
         X0 = xtr(im, ti);
-        gfx_new_dashed_line(im->canvas, X0, Y0 + 3, X0, Y1 - 2, MGRIDWIDTH,
-                            im->graph_col[GRC_MGRID],
-                            im->grid_dash_on, im->grid_dash_off);
-
+        gfx_line(im, X0, Y1 - 2, X0, Y1,
+                 MGRIDWIDTH, im->graph_col[GRC_MGRID]);
+        gfx_line(im, X0, Y0, X0, Y0 + 3,
+                 MGRIDWIDTH, im->graph_col[GRC_MGRID]);
+        gfx_dashed_line(im, X0, Y0 + 3, X0,
+                        Y1 - 2, MGRIDWIDTH,
+                        im->
+                        graph_col
+                        [GRC_MGRID], im->grid_dash_on, im->grid_dash_off);
     }
     /* paint the labels below the graph */
-    for (ti = find_first_time(im->start - im->xlab_user.precis / 2,
-                              im->xlab_user.labtm,
-                              im->xlab_user.labst);
-         ti <= im->end - im->xlab_user.precis / 2;
+    for (ti =
+         find_first_time(im->start -
+                         im->xlab_user.
+                         precis / 2,
+                         im->xlab_user.
+                         labtm,
+                         im->xlab_user.
+                         labst);
+         ti <=
+         im->end -
+         im->xlab_user.precis / 2;
          ti = find_next_time(ti, im->xlab_user.labtm, im->xlab_user.labst)
         ) {
         tilab = ti + im->xlab_user.precis / 2;  /* correct time for the label */
         /* are we inside the graph ? */
         if (tilab < im->start || tilab > im->end)
             continue;
-
 #if HAVE_STRFTIME
         localtime_r(&tilab, &tm);
         strftime(graph_label, 99, im->xlab_user.stst, &tm);
 #else
 # error "your libc has no strftime I guess we'll abort the exercise here."
 #endif
-        gfx_new_text(im->canvas,
-                     xtr(im, tilab),
-                     Y0 + im->text_prop[TEXT_PROP_AXIS].size * 1.4 + 5,
-                     im->graph_col[GRC_FONT],
-                     im->text_prop[TEXT_PROP_AXIS].font,
-                     im->text_prop[TEXT_PROP_AXIS].size, im->tabwidth, 0.0,
-                     GFX_H_CENTER, GFX_V_BOTTOM, graph_label);
-
+        gfx_text(im,
+                 xtr(im, tilab),
+                 Y0 + 3,
+                 im->graph_col[GRC_FONT],
+                 im->
+                 text_prop[TEXT_PROP_AXIS].
+                 font_desc,
+                 im->tabwidth, 0.0,
+                 GFX_H_CENTER, GFX_V_TOP, graph_label);
     }
 
 }
@@ -2330,29 +2556,41 @@ void axis_paint(
     image_desc_t *im)
 {
     /* draw x and y axis */
-    /* gfx_new_line ( im->canvas, im->xorigin+im->xsize,im->yorigin,
+    /* gfx_line ( im->canvas, im->xorigin+im->xsize,im->yorigin,
        im->xorigin+im->xsize,im->yorigin-im->ysize,
        GRIDWIDTH, im->graph_col[GRC_AXIS]);
 
-       gfx_new_line ( im->canvas, im->xorigin,im->yorigin-im->ysize,
+       gfx_line ( im->canvas, im->xorigin,im->yorigin-im->ysize,
        im->xorigin+im->xsize,im->yorigin-im->ysize,
        GRIDWIDTH, im->graph_col[GRC_AXIS]); */
 
-    gfx_new_line(im->canvas, im->xorigin - 4, im->yorigin,
-                 im->xorigin + im->xsize + 4, im->yorigin,
-                 MGRIDWIDTH, im->graph_col[GRC_AXIS]);
-
-    gfx_new_line(im->canvas, im->xorigin, im->yorigin + 4,
-                 im->xorigin, im->yorigin - im->ysize - 4,
-                 MGRIDWIDTH, im->graph_col[GRC_AXIS]);
-
-
+    gfx_line(im, im->xorigin - 4,
+             im->yorigin,
+             im->xorigin + im->xsize +
+             4, im->yorigin, MGRIDWIDTH, im->graph_col[GRC_AXIS]);
+    gfx_line(im, im->xorigin,
+             im->yorigin + 4,
+             im->xorigin,
+             im->yorigin - im->ysize -
+             4, MGRIDWIDTH, im->graph_col[GRC_AXIS]);
     /* arrow for X and Y axis direction */
-    gfx_new_area(im->canvas, im->xorigin + im->xsize + 2, im->yorigin - 2, im->xorigin + im->xsize + 2, im->yorigin + 3, im->xorigin + im->xsize + 7, im->yorigin + 0.5,    /* LINEOFFSET */
+    gfx_new_area(im, im->xorigin + im->xsize + 2, im->yorigin - 3, im->xorigin + im->xsize + 2, im->yorigin + 3, im->xorigin + im->xsize + 7, im->yorigin,  /* horyzontal */
                  im->graph_col[GRC_ARROW]);
-
-    gfx_new_area(im->canvas, im->xorigin - 2, im->yorigin - im->ysize - 2, im->xorigin + 3, im->yorigin - im->ysize - 2, im->xorigin + 0.5, im->yorigin - im->ysize - 7,    /* LINEOFFSET */
+    gfx_close_path(im);
+    gfx_new_area(im, im->xorigin - 3, im->yorigin - im->ysize - 2, im->xorigin + 3, im->yorigin - im->ysize - 2, im->xorigin, im->yorigin - im->ysize - 7,  /* vertical */
                  im->graph_col[GRC_ARROW]);
+    gfx_close_path(im);
+    if (im->second_axis_scale != 0){
+       gfx_line ( im, im->xorigin+im->xsize,im->yorigin+4,
+                         im->xorigin+im->xsize,im->yorigin-im->ysize-4,
+                         MGRIDWIDTH, im->graph_col[GRC_AXIS]);
+       gfx_new_area ( im,
+                   im->xorigin+im->xsize-2,  im->yorigin-im->ysize-2,
+                   im->xorigin+im->xsize+3,  im->yorigin-im->ysize-2,
+                   im->xorigin+im->xsize,    im->yorigin-im->ysize-7, /* LINEOFFSET */
+                   im->graph_col[GRC_ARROW]);
+       gfx_close_path(im);
+    }
 
 }
 
@@ -2362,28 +2600,27 @@ void grid_paint(
     long      i;
     int       res = 0;
     double    X0, Y0;   /* points for filled graph and more */
-    gfx_node_t *node;
-
-    /* draw 3d border */
-    node = gfx_new_area(im->canvas, 0, im->yimg,
-                        2, im->yimg - 2, 2, 2, im->graph_col[GRC_SHADEA]);
-    gfx_add_point(node, im->ximg - 2, 2);
-    gfx_add_point(node, im->ximg, 0);
-    gfx_add_point(node, 0, 0);
-/*    gfx_add_point( node , 0,im->yimg ); */
-
-    node = gfx_new_area(im->canvas, 2, im->yimg - 2,
-                        im->ximg - 2, im->yimg - 2,
-                        im->ximg - 2, 2, im->graph_col[GRC_SHADEB]);
-    gfx_add_point(node, im->ximg, 0);
-    gfx_add_point(node, im->ximg, im->yimg);
-    gfx_add_point(node, 0, im->yimg);
-/*    gfx_add_point( node , 0,im->yimg ); */
-
-
+    struct gfx_color_t water_color;
+
+    if (im->draw_3d_border > 0) {
+           /* draw 3d border */
+           i = im->draw_3d_border;
+           gfx_new_area(im, 0, im->yimg,
+                        i, im->yimg - i, i, i, im->graph_col[GRC_SHADEA]);
+           gfx_add_point(im, im->ximg - i, i);
+           gfx_add_point(im, im->ximg, 0);
+           gfx_add_point(im, 0, 0);
+           gfx_close_path(im);
+           gfx_new_area(im, i, im->yimg - i,
+                        im->ximg - i,
+                        im->yimg - i, im->ximg - i, i, im->graph_col[GRC_SHADEB]);
+           gfx_add_point(im, im->ximg, 0);
+           gfx_add_point(im, im->ximg, im->yimg);
+           gfx_add_point(im, 0, im->yimg);
+           gfx_close_path(im);
+    }
     if (im->draw_x_grid == 1)
         vertical_grid(im);
-
     if (im->draw_y_grid == 1) {
         if (im->logarithmic) {
             res = horizontal_log_grid(im);
@@ -2395,94 +2632,173 @@ void grid_paint(
         if (!res) {
             char     *nodata = "No Data found";
 
-            gfx_new_text(im->canvas, im->ximg / 2,
-                         (2 * im->yorigin - im->ysize) / 2,
-                         im->graph_col[GRC_FONT],
-                         im->text_prop[TEXT_PROP_AXIS].font,
-                         im->text_prop[TEXT_PROP_AXIS].size, im->tabwidth,
-                         0.0, GFX_H_CENTER, GFX_V_CENTER, nodata);
+            gfx_text(im, im->ximg / 2,
+                     (2 * im->yorigin -
+                      im->ysize) / 2,
+                     im->graph_col[GRC_FONT],
+                     im->
+                     text_prop[TEXT_PROP_AXIS].
+                     font_desc,
+                     im->tabwidth, 0.0,
+                     GFX_H_CENTER, GFX_V_CENTER, nodata);
         }
     }
 
     /* yaxis unit description */
-    gfx_new_text(im->canvas,
-                 10, (im->yorigin - im->ysize / 2),
+    if (im->ylegend[0] != '\0'){
+        gfx_text(im,
+                 im->xOriginLegendY+10,
+                 im->yOriginLegendY,
                  im->graph_col[GRC_FONT],
-                 im->text_prop[TEXT_PROP_UNIT].font,
-                 im->text_prop[TEXT_PROP_UNIT].size, im->tabwidth,
-                 RRDGRAPH_YLEGEND_ANGLE,
-                 GFX_H_LEFT, GFX_V_CENTER, im->ylegend);
+                 im->
+                 text_prop[TEXT_PROP_UNIT].
+                 font_desc,
+                 im->tabwidth,
+                 RRDGRAPH_YLEGEND_ANGLE, GFX_H_CENTER, GFX_V_CENTER, im->ylegend);
+
+    }
+    if (im->second_axis_legend[0] != '\0'){
+            gfx_text( im,
+                  im->xOriginLegendY2+10,
+                  im->yOriginLegendY2,
+                  im->graph_col[GRC_FONT],
+                  im->text_prop[TEXT_PROP_UNIT].font_desc,
+                  im->tabwidth,
+                  RRDGRAPH_YLEGEND_ANGLE,
+                  GFX_H_CENTER, GFX_V_CENTER,
+                  im->second_axis_legend);
+    }
 
     /* graph title */
-    gfx_new_text(im->canvas,
-                 im->ximg / 2, im->text_prop[TEXT_PROP_TITLE].size * 1.3 + 4,
-                 im->graph_col[GRC_FONT],
-                 im->text_prop[TEXT_PROP_TITLE].font,
-                 im->text_prop[TEXT_PROP_TITLE].size, im->tabwidth, 0.0,
-                 GFX_H_CENTER, GFX_V_CENTER, im->title);
+    gfx_text(im,
+             im->xOriginTitle, im->yOriginTitle+6,
+             im->graph_col[GRC_FONT],
+             im->
+             text_prop[TEXT_PROP_TITLE].
+             font_desc,
+             im->tabwidth, 0.0, GFX_H_CENTER, GFX_V_TOP, im->title);
     /* rrdtool 'logo' */
-    gfx_new_text(im->canvas,
-                 im->ximg - 7, 7,
-                 (im->graph_col[GRC_FONT] & 0xffffff00) | 0x00000044,
-                 im->text_prop[TEXT_PROP_AXIS].font,
-                 5.5, im->tabwidth, 270,
-                 GFX_H_RIGHT, GFX_V_TOP, "RRDTOOL / TOBI OETIKER");
-
+    if (!(im->extra_flags & NO_RRDTOOL_TAG)){
+        water_color = im->graph_col[GRC_FONT];
+        water_color.alpha = 0.3;
+        double xpos = im->legendposition == EAST ? im->xOriginLegendY : im->ximg - 4;
+        gfx_text(im, xpos, 5,
+                 water_color,
+                 im->
+                 text_prop[TEXT_PROP_WATERMARK].
+                 font_desc, im->tabwidth,
+                 -90, GFX_H_LEFT, GFX_V_TOP, "RRDTOOL / TOBI OETIKER");
+    }
     /* graph watermark */
     if (im->watermark[0] != '\0') {
-        gfx_new_text(im->canvas,
-                     im->ximg / 2, im->yimg - 6,
-                     (im->graph_col[GRC_FONT] & 0xffffff00) | 0x00000044,
-                     im->text_prop[TEXT_PROP_AXIS].font,
-                     5.5, im->tabwidth, 0,
-                     GFX_H_CENTER, GFX_V_BOTTOM, im->watermark);
+        water_color = im->graph_col[GRC_FONT];
+        water_color.alpha = 0.3;
+        gfx_text(im,
+                 im->ximg / 2, im->yimg - 6,
+                 water_color,
+                 im->
+                 text_prop[TEXT_PROP_WATERMARK].
+                 font_desc, im->tabwidth, 0,
+                 GFX_H_CENTER, GFX_V_BOTTOM, im->watermark);
     }
 
     /* graph labels */
-    if (!(im->extra_flags & NOLEGEND) & !(im->extra_flags & ONLY_GRAPH)) {
+    if (!(im->extra_flags & NOLEGEND) && !(im->extra_flags & ONLY_GRAPH)) {
         for (i = 0; i < im->gdes_c; i++) {
             if (im->gdes[i].legend[0] == '\0')
                 continue;
-
             /* im->gdes[i].leg_y is the bottom of the legend */
-            X0 = im->gdes[i].leg_x;
-            Y0 = im->gdes[i].leg_y;
-            gfx_new_text(im->canvas, X0, Y0,
-                         im->graph_col[GRC_FONT],
-                         im->text_prop[TEXT_PROP_LEGEND].font,
-                         im->text_prop[TEXT_PROP_LEGEND].size,
-                         im->tabwidth, 0.0, GFX_H_LEFT, GFX_V_BOTTOM,
-                         im->gdes[i].legend);
+            X0 = im->xOriginLegend + im->gdes[i].leg_x;
+            Y0 = im->legenddirection == TOP_DOWN ? im->yOriginLegend + im->gdes[i].leg_y : im->yOriginLegend + im->legendheight - im->gdes[i].leg_y;
+            gfx_text(im, X0, Y0,
+                     im->graph_col[GRC_FONT],
+                     im->
+                     text_prop
+                     [TEXT_PROP_LEGEND].font_desc,
+                     im->tabwidth, 0.0,
+                     GFX_H_LEFT, GFX_V_BOTTOM, im->gdes[i].legend);
             /* The legend for GRAPH items starts with "M " to have
                enough space for the box */
             if (im->gdes[i].gf != GF_PRINT &&
                 im->gdes[i].gf != GF_GPRINT && im->gdes[i].gf != GF_COMMENT) {
-                int       boxH, boxV;
-
-                boxH = gfx_get_text_width(im->canvas, 0,
-                                          im->text_prop[TEXT_PROP_LEGEND].
-                                          font,
-                                          im->text_prop[TEXT_PROP_LEGEND].
-                                          size, im->tabwidth, "o", 0) * 1.2;
-                boxV = boxH * 1.1;
-
-                /* make sure transparent colors show up the same way as in the graph */
-                node = gfx_new_area(im->canvas,
-                                    X0, Y0 - boxV,
-                                    X0, Y0,
-                                    X0 + boxH, Y0, im->graph_col[GRC_BACK]);
-                gfx_add_point(node, X0 + boxH, Y0 - boxV);
-
-                node = gfx_new_area(im->canvas,
-                                    X0, Y0 - boxV,
-                                    X0, Y0, X0 + boxH, Y0, im->gdes[i].col);
-                gfx_add_point(node, X0 + boxH, Y0 - boxV);
-                node = gfx_new_line(im->canvas,
-                                    X0, Y0 - boxV,
-                                    X0, Y0, 1.0, im->graph_col[GRC_FRAME]);
-                gfx_add_point(node, X0 + boxH, Y0);
-                gfx_add_point(node, X0 + boxH, Y0 - boxV);
-                gfx_close_path(node);
+                double    boxH, boxV;
+                double    X1, Y1;
+
+                boxH = gfx_get_text_width(im, 0,
+                                          im->
+                                          text_prop
+                                          [TEXT_PROP_LEGEND].
+                                          font_desc,
+                                          im->tabwidth, "o") * 1.2;
+                boxV = boxH;
+                /* shift the box up a bit */
+                Y0 -= boxV * 0.4;
+
+        if (im->dynamic_labels && im->gdes[i].gf == GF_HRULE) { /* [-] */ 
+                       cairo_save(im->cr);
+                       cairo_new_path(im->cr);
+                       cairo_set_line_width(im->cr, 1.0);
+                       gfx_line(im,
+                               X0, Y0 - boxV / 2,
+                               X0 + boxH, Y0 - boxV / 2,
+                               1.0, im->gdes[i].col);
+                       gfx_close_path(im);
+               } else if (im->dynamic_labels && im->gdes[i].gf == GF_VRULE) { /* [|] */
+                       cairo_save(im->cr);
+                       cairo_new_path(im->cr);
+                       cairo_set_line_width(im->cr, 1.0);
+                       gfx_line(im,
+                               X0 + boxH / 2, Y0,
+                               X0 + boxH / 2, Y0 - boxV,
+                               1.0, im->gdes[i].col);
+                       gfx_close_path(im);
+               } else if (im->dynamic_labels && im->gdes[i].gf == GF_LINE) { /* [/] */
+                       cairo_save(im->cr);
+                       cairo_new_path(im->cr);
+                       cairo_set_line_width(im->cr, im->gdes[i].linewidth);
+                       gfx_line(im,
+                               X0, Y0,
+                               X0 + boxH, Y0 - boxV,
+                               im->gdes[i].linewidth, im->gdes[i].col);
+                       gfx_close_path(im);
+               } else {
+               /* make sure transparent colors show up the same way as in the graph */
+                       gfx_new_area(im,
+                                    X0, Y0 - boxV,
+                                    X0, Y0, X0 + boxH, Y0, im->graph_col[GRC_BACK]);
+                       gfx_add_point(im, X0 + boxH, Y0 - boxV);
+                       gfx_close_path(im);
+                       gfx_new_area(im, X0, Y0 - boxV, X0,
+                                    Y0, X0 + boxH, Y0, im->gdes[i].col);
+                       gfx_add_point(im, X0 + boxH, Y0 - boxV);
+                       gfx_close_path(im);
+                       cairo_save(im->cr);
+                       cairo_new_path(im->cr);
+                       cairo_set_line_width(im->cr, 1.0);
+                       X1 = X0 + boxH;
+                       Y1 = Y0 - boxV;
+                       gfx_line_fit(im, &X0, &Y0);
+                       gfx_line_fit(im, &X1, &Y1);
+                       cairo_move_to(im->cr, X0, Y0);
+                       cairo_line_to(im->cr, X1, Y0);
+                       cairo_line_to(im->cr, X1, Y1);
+                       cairo_line_to(im->cr, X0, Y1);
+                       cairo_close_path(im->cr);
+                       cairo_set_source_rgba(im->cr,
+                                             im->graph_col[GRC_FRAME].red,
+                                             im->graph_col[GRC_FRAME].green,
+                                             im->graph_col[GRC_FRAME].blue,
+                                             im->graph_col[GRC_FRAME].alpha);
+               }
+                if (im->gdes[i].dash) {
+                    /* make box borders in legend dashed if the graph is dashed */
+                    double    dashes[] = {
+                        3.0
+                    };
+                    cairo_set_dash(im->cr, dashes, 1, 0.0);
+                }
+                cairo_stroke(im->cr);
+                cairo_restore(im->cr);
             }
         }
     }
@@ -2502,6 +2818,8 @@ int lazy_check(
 
     if (im->lazy == 0)
         return 0;       /* no lazy option */
+    if (strlen(im->graphfile) == 0)
+        return 0;       /* inmemory option */
     if (stat(im->graphfile, &imgstat) != 0)
         return 0;       /* can't stat */
     /* one pixel in the existing graph is more then what we would
@@ -2510,7 +2828,7 @@ int lazy_check(
         return 0;
     if ((fd = fopen(im->graphfile, "rb")) == NULL)
         return 0;       /* the file does not exist */
-    switch (im->canvas->imgformat) {
+    switch (im->imgformat) {
     case IF_PNG:
         size = PngSize(fd, &(im->ximg), &(im->yimg));
         break;
@@ -2521,84 +2839,46 @@ int lazy_check(
     return size;
 }
 
-#ifdef WITH_PIECHART
-void pie_part(
-    image_desc_t *im,
-    gfx_color_t color,
-    double PieCenterX,
-    double PieCenterY,
-    double Radius,
-    double startangle,
-    double endangle)
-{
-    gfx_node_t *node;
-    double    angle;
-    double    step = M_PI / 50; /* Number of iterations for the circle;
-                                 ** 10 is definitely too low, more than
-                                 ** 50 seems to be overkill
-                                 */
-
-    /* Strange but true: we have to work clockwise or else
-     ** anti aliasing nor transparency don't work.
-     **
-     ** This test is here to make sure we do it right, also
-     ** this makes the for...next loop more easy to implement.
-     ** The return will occur if the user enters a negative number
-     ** (which shouldn't be done according to the specs) or if the
-     ** programmers do something wrong (which, as we all know, never
-     ** happens anyway :)
-     */
-    if (endangle < startangle)
-        return;
-
-    /* Hidden feature: Radius decreases each full circle */
-    angle = startangle;
-    while (angle >= 2 * M_PI) {
-        angle -= 2 * M_PI;
-        Radius *= 0.8;
-    }
-
-    node = gfx_new_area(im->canvas,
-                        PieCenterX + sin(startangle) * Radius,
-                        PieCenterY - cos(startangle) * Radius,
-                        PieCenterX,
-                        PieCenterY,
-                        PieCenterX + sin(endangle) * Radius,
-                        PieCenterY - cos(endangle) * Radius, color);
-    for (angle = endangle; angle - startangle >= step; angle -= step) {
-        gfx_add_point(node,
-                      PieCenterX + sin(angle) * Radius,
-                      PieCenterY - cos(angle) * Radius);
-    }
-}
-
-#endif
 
 int graph_size_location(
-    image_desc_t *im,
-    int elements
-#ifdef WITH_PIECHART
-    ,
-    int piechart
-#endif
-    )
+    image_desc_t
+    *im,
+    int elements)
 {
     /* The actual size of the image to draw is determined from
      ** several sources.  The size given on the command line is
      ** the graph area but we need more as we have to draw labels
-     ** and other things outside the graph area
+     ** and other things outside the graph area. If the option
+     ** --full-size-mode is selected the size defines the total
+     ** image size and the size available for the graph is
+     ** calculated.
      */
 
-    int       Xvertical = 0, Ytitle = 0, Xylabel = 0, Xmain = 0, Ymain = 0,
-#ifdef WITH_PIECHART
-        Xpie = 0, Ypie = 0,
-#endif
-        Yxlabel = 0,
-#if 0
-        Xlegend = 0, Ylegend = 0,
-#endif
-        Xspacing = 15, Yspacing = 15, Ywatermark = 4;
+    /** +---+-----------------------------------+
+     ** | y |...............graph title.........|
+     ** |   +---+-------------------------------+
+     ** | a | y |                               |
+     ** | x |   |                               |
+     ** | i | a |                               |
+     ** | s | x |       main graph area         |
+     ** |   | i |                               |
+     ** | t | s |                               |
+     ** | i |   |                               |
+     ** | t | l |                               |
+     ** | l | b +-------------------------------+
+     ** | e | l |       x axis labels           |
+     ** +---+---+-------------------------------+
+     ** |....................legends............|
+     ** +---------------------------------------+
+     ** |                   watermark           |
+     ** +---------------------------------------+
+     */
 
+    int       Xvertical = 0, Xvertical2 = 0, Ytitle =
+        0, Xylabel = 0, Xmain = 0, Ymain =
+        0, Yxlabel = 0, Xspacing = 15, Yspacing = 15, Ywatermark = 4;
+
+    // no legends and no the shall be plotted it's easy
     if (im->extra_flags & ONLY_GRAPH) {
         im->xorigin = 0;
         im->ximg = im->xsize;
@@ -2608,30 +2888,23 @@ int graph_size_location(
         return 0;
     }
 
-    /** +---+--------------------------------------------+
-     ** | y |...............graph title..................|
-     ** |   +---+-------------------------------+--------+
-     ** | a | y |                               |        |
-     ** | x |   |                               |        |
-     ** | i | a |                               |  pie   |
-     ** | s | x |       main graph area         | chart  |
-     ** |   | i |                               |  area  |
-     ** | t | s |                               |        |
-     ** | i |   |                               |        |
-     ** | t | l |                               |        |
-     ** | l | b +-------------------------------+--------+
-     ** | e | l |       x axis labels           |        |
-     ** +---+---+-------------------------------+--------+
-     ** |....................legends.....................|
-     ** +------------------------------------------------+
-     ** |                   watermark                    |
-     ** +------------------------------------------------+
-     */
+    if(im->watermark[0] != '\0') {
+        Ywatermark = im->text_prop[TEXT_PROP_WATERMARK].size * 2;
+    }
 
+    // calculate the width of the left vertical legend
     if (im->ylegend[0] != '\0') {
         Xvertical = im->text_prop[TEXT_PROP_UNIT].size * 2;
     }
 
+    // calculate the width of the right vertical legend
+    if (im->second_axis_legend[0] != '\0') {
+        Xvertical2 = im->text_prop[TEXT_PROP_UNIT].size * 2;
+    }
+    else{
+        Xvertical2 = Xspacing;
+    }
+
     if (im->title[0] != '\0') {
         /* The title is placed "inbetween" two text lines so it
          ** automatically has some vertical spacing.  The horizontal
@@ -2640,108 +2913,113 @@ int graph_size_location(
         /* if necessary, reduce the font size of the title until it fits the image width */
         Ytitle = im->text_prop[TEXT_PROP_TITLE].size * 2.6 + 10;
     }
+    else{
+        // we have no title; get a little clearing from the top
+        Ytitle = 1.5 * Yspacing;
+    }
 
     if (elements) {
         if (im->draw_x_grid) {
+            // calculate the height of the horizontal labelling
             Yxlabel = im->text_prop[TEXT_PROP_AXIS].size * 2.5;
         }
         if (im->draw_y_grid || im->forceleftspace) {
-            Xylabel = gfx_get_text_width(im->canvas, 0,
-                                         im->text_prop[TEXT_PROP_AXIS].font,
-                                         im->text_prop[TEXT_PROP_AXIS].size,
-                                         im->tabwidth,
-                                         "0", 0) * im->unitslength;
+            // calculate the width of the vertical labelling
+            Xylabel =
+                gfx_get_text_width(im, 0,
+                                   im->text_prop[TEXT_PROP_AXIS].font_desc,
+                                   im->tabwidth, "0") * im->unitslength;
+        }
+    }
+
+    // add some space to the labelling
+    Xylabel += Xspacing;
+
+    /* If the legend is printed besides the graph the width has to be
+     ** calculated first. Placing the legend north or south of the
+     ** graph requires the width calculation first, so the legend is
+     ** skipped for the moment.
+     */
+    im->legendheight = 0;
+    im->legendwidth = 0;
+    if (!(im->extra_flags & NOLEGEND)) {
+        if(im->legendposition == WEST || im->legendposition == EAST){
+            if (leg_place(im, 1) == -1){
+                return -1;
+            }
         }
     }
 
     if (im->extra_flags & FULL_SIZE_MODE) {
+
         /* The actual size of the image to draw has been determined by the user.
          ** The graph area is the space remaining after accounting for the legend,
-         ** the watermark, the pie chart, the axis labels, and the title.
+         ** the watermark, the axis labels, and the title.
          */
-        im->xorigin = 0;
         im->ximg = im->xsize;
         im->yimg = im->ysize;
-        im->yorigin = im->ysize;
         Xmain = im->ximg;
         Ymain = im->yimg;
 
-        im->yorigin += Ytitle;
-
-#ifdef WITH_PIECHART
-        if (piechart) {
-            im->piesize = im->xsize < im->ysize ? im->xsize : im->ysize;
-            Xpie = im->piesize;
-            Ypie = im->piesize;
-        }
-#endif
-
         /* Now calculate the total size.  Insert some spacing where
            desired.  im->xorigin and im->yorigin need to correspond
            with the lower left corner of the main graph area or, if
            this one is not set, the imaginary box surrounding the
            pie chart area. */
-
         /* Initial size calculation for the main graph area */
-        Xmain = im->ximg - (Xylabel + 2 * Xspacing);
-        if (Xmain)
-            Xmain -= Xspacing;  /* put space between main graph area and right edge */
 
-#ifdef WITH_PIECHART
-        Xmain -= Xpie;  /* remove pie width from main graph area */
-        if (Xpie)
-            Xmain -= Xspacing;  /* put space between pie and main graph area */
-#endif
-
-        im->xorigin = Xspacing + Xylabel;
+        Xmain -= Xylabel;// + Xspacing;
+        if((im->legendposition == WEST || im->legendposition == EAST) && !(im->extra_flags & NOLEGEND) ){
+            Xmain -= im->legendwidth;// + Xspacing;
+        }
+        if (im->second_axis_scale != 0){
+            Xmain -= Xylabel;
+        }
+        if (!(im->extra_flags & NO_RRDTOOL_TAG)){
+            Xmain -= Xspacing;
+        }
 
-        /* the length of the title should not influence with width of the graph
-           if (Xtitle > im->ximg) im->ximg = Xtitle; */
+        Xmain -= Xvertical + Xvertical2;
 
-        if (Xvertical) {    /* unit description */
-            Xmain -= Xvertical;
-            im->xorigin += Xvertical;
+        /* limit the remaining space to 0 */
+        if(Xmain < 1){
+            Xmain = 1;
         }
         im->xsize = Xmain;
-        xtr(im, 0);
 
-        /* The vertical size of the image is known in advance.  The main graph area
-         ** (Ymain) and im->yorigin must be set according to the space requirements
-         ** of the legend and the axis labels.
-         */
+        /* Putting the legend north or south, the height can now be calculated */
+        if (!(im->extra_flags & NOLEGEND)) {
+            if(im->legendposition == NORTH || im->legendposition == SOUTH){
+                im->legendwidth = im->ximg;
+                if (leg_place(im, 0) == -1){
+                    return -1;
+                }
+            }
+        }
 
-        if (im->extra_flags & NOLEGEND)
-        {
-            /* set dimensions correctly if using full size mode with no legend */
-            im->yorigin = im->yimg - im->text_prop[TEXT_PROP_AXIS].size * 2.5 - Yspacing;
-            Ymain = im->yorigin;
-        } else
-        {
-            /* Determine where to place the legends onto the image.
-            ** Set Ymain and adjust im->yorigin to match the space requirements.
-            */
-            if (leg_place(im, &Ymain) == -1)
-               return -1;
+        if( (im->legendposition == NORTH || im->legendposition == SOUTH)  && !(im->extra_flags & NOLEGEND) ){
+            Ymain -=  Yxlabel + im->legendheight;
+        }
+        else{
+            Ymain -= Yxlabel;
         }
 
-#ifdef WITH_PIECHART
-        /* if (im->yimg < Ypie) im->yimg = Ypie; * not sure what do about this */
-#endif
+        /* reserve space for the title *or* some padding above the graph */
+        Ymain -= Ytitle;
 
-        /* remove title space *or* some padding above the graph from the main graph area */
-        if (Ytitle) {
-            Ymain -= Ytitle;
-        } else {
-            Ymain -= 1.5 * Yspacing;
+            /* reserve space for padding below the graph */
+        if (im->extra_flags & NOLEGEND) {
+            Ymain -= Yspacing;
         }
 
-        /* watermark doesn't seem to effect the vertical size of the main graph area, oh well! */
         if (im->watermark[0] != '\0') {
             Ymain -= Ywatermark;
         }
-
+        /* limit the remaining height to 0 */
+        if(Ymain < 1){
+            Ymain = 1;
+        }
         im->ysize = Ymain;
-
     } else {            /* dimension options -width and -height refer to the dimensions of the main graph area */
 
         /* The actual size of the image to draw is determined from
@@ -2750,272 +3028,340 @@ int graph_size_location(
          ** and other things outside the graph area.
          */
 
-        if (im->ylegend[0] != '\0') {
-            Xvertical = im->text_prop[TEXT_PROP_UNIT].size * 2;
-        }
-
-
-        if (im->title[0] != '\0') {
-            /* The title is placed "inbetween" two text lines so it
-             ** automatically has some vertical spacing.  The horizontal
-             ** spacing is added here, on each side.
-             */
-            /* don't care for the with of the title
-               Xtitle = gfx_get_text_width(im->canvas, 0,
-               im->text_prop[TEXT_PROP_TITLE].font,
-               im->text_prop[TEXT_PROP_TITLE].size,
-               im->tabwidth,
-               im->title, 0) + 2*Xspacing; */
-            Ytitle = im->text_prop[TEXT_PROP_TITLE].size * 2.6 + 10;
-        }
-
         if (elements) {
-            Xmain = im->xsize;
+            Xmain = im->xsize; // + Xspacing;
             Ymain = im->ysize;
         }
-#ifdef WITH_PIECHART
-        if (piechart) {
-            im->piesize = im->xsize < im->ysize ? im->xsize : im->ysize;
-            Xpie = im->piesize;
-            Ypie = im->piesize;
-        }
-#endif
-
-        /* Now calculate the total size.  Insert some spacing where
-           desired.  im->xorigin and im->yorigin need to correspond
-           with the lower left corner of the main graph area or, if
-           this one is not set, the imaginary box surrounding the
-           pie chart area. */
 
-        /* The legend width cannot yet be determined, as a result we
-         ** have problems adjusting the image to it.  For now, we just
-         ** forget about it at all; the legend will have to fit in the
-         ** size already allocated.
-         */
-        im->ximg = Xylabel + Xmain + 2 * Xspacing;
-
-#ifdef WITH_PIECHART
-        im->ximg += Xpie;
-#endif
-
-        if (Xmain)
-            im->ximg += Xspacing;
-#ifdef WITH_PIECHART
-        if (Xpie)
+        im->ximg = Xmain + Xylabel;
+        if (!(im->extra_flags & NO_RRDTOOL_TAG)){
             im->ximg += Xspacing;
-#endif
+        }
 
-        im->xorigin = Xspacing + Xylabel;
+        if( (im->legendposition == WEST || im->legendposition == EAST) && !(im->extra_flags & NOLEGEND) ){
+            im->ximg += im->legendwidth;// + Xspacing;
+        }
+        if (im->second_axis_scale != 0){
+            im->ximg += Xylabel;
+        }
 
-        /* the length of the title should not influence with width of the graph
-           if (Xtitle > im->ximg) im->ximg = Xtitle; */
+        im->ximg += Xvertical + Xvertical2;
 
-        if (Xvertical) {    /* unit description */
-            im->ximg += Xvertical;
-            im->xorigin += Xvertical;
+        if (!(im->extra_flags & NOLEGEND)) {
+            if(im->legendposition == NORTH || im->legendposition == SOUTH){
+                im->legendwidth = im->ximg;
+                if (leg_place(im, 0) == -1){
+                    return -1;
+                }
+            }
         }
-        xtr(im, 0);
-
-        /* The vertical size is interesting... we need to compare
-         ** the sum of {Ytitle, Ymain, Yxlabel, Ylegend, Ywatermark} with 
-         ** Yvertical however we need to know {Ytitle+Ymain+Yxlabel}
-         ** in order to start even thinking about Ylegend or Ywatermark.
-         **
-         ** Do it in three portions: First calculate the inner part,
-         ** then do the legend, then adjust the total height of the img,
-         ** adding space for a watermark if one exists;
-         */
-
-        /* reserve space for main and/or pie */
 
         im->yimg = Ymain + Yxlabel;
-
-#ifdef WITH_PIECHART
-        if (im->yimg < Ypie)
-            im->yimg = Ypie;
-#endif
-
-        im->yorigin = im->yimg - Yxlabel;
+        if( (im->legendposition == NORTH || im->legendposition == SOUTH)  && !(im->extra_flags & NOLEGEND) ){
+             im->yimg += im->legendheight;
+        }
 
         /* reserve space for the title *or* some padding above the graph */
         if (Ytitle) {
             im->yimg += Ytitle;
-            im->yorigin += Ytitle;
         } else {
             im->yimg += 1.5 * Yspacing;
-            im->yorigin += 1.5 * Yspacing;
         }
         /* reserve space for padding below the graph */
-        im->yimg += Yspacing;
-
-        /* Determine where to place the legends onto the image.
-         ** Adjust im->yimg to match the space requirements.
-         */
-        if (leg_place(im, 0) == -1)
-            return -1;
+        if (im->extra_flags & NOLEGEND) {
+            im->yimg += Yspacing;
+        }
 
         if (im->watermark[0] != '\0') {
             im->yimg += Ywatermark;
         }
     }
 
-#if 0
-    if (Xlegend > im->ximg) {
-        im->ximg = Xlegend;
-        /* reposition Pie */
+
+    /* In case of putting the legend in west or east position the first
+     ** legend calculation might lead to wrong positions if some items
+     ** are not aligned on the left hand side (e.g. centered) as the
+     ** legendwidth wight have been increased after the item was placed.
+     ** In this case the positions have to be recalculated.
+     */
+    if (!(im->extra_flags & NOLEGEND)) {
+        if(im->legendposition == WEST || im->legendposition == EAST){
+            if (leg_place(im, 0) == -1){
+                return -1;
+            }
+        }
     }
-#endif
 
-#ifdef WITH_PIECHART
-    /* The pie is placed in the upper right hand corner,
-     ** just below the title (if any) and with sufficient
-     ** padding.
+    /* After calculating all dimensions
+     ** it is now possible to calculate
+     ** all offsets.
      */
-    if (elements) {
-        im->pie_x = im->ximg - Xspacing - Xpie / 2;
-        im->pie_y = im->yorigin - Ymain + Ypie / 2;
-    } else {
-        im->pie_x = im->ximg / 2;
-        im->pie_y = im->yorigin - Ypie / 2;
+    switch(im->legendposition){
+        case NORTH:
+            im->xOriginTitle   = Xvertical + Xylabel + (im->xsize / 2);
+            im->yOriginTitle   = 0;
+
+            im->xOriginLegend  = 0;
+            im->yOriginLegend  = Ytitle;
+
+            im->xOriginLegendY = 0;
+            im->yOriginLegendY = Ytitle + im->legendheight + (Ymain / 2) + Yxlabel;
+
+            im->xorigin        = Xvertical + Xylabel;
+            im->yorigin        = Ytitle + im->legendheight + Ymain;
+
+            im->xOriginLegendY2 = Xvertical + Xylabel + Xmain;
+            if (im->second_axis_scale != 0){
+                im->xOriginLegendY2 += Xylabel;
+            }
+            im->yOriginLegendY2 = Ytitle + im->legendheight + (Ymain / 2) + Yxlabel;
+
+            break;
+
+        case WEST:
+            im->xOriginTitle   = im->legendwidth + Xvertical + Xylabel + im->xsize / 2;
+            im->yOriginTitle   = 0;
+
+            im->xOriginLegend  = 0;
+            im->yOriginLegend  = Ytitle;
+
+            im->xOriginLegendY = im->legendwidth;
+            im->yOriginLegendY = Ytitle + (Ymain / 2);
+
+            im->xorigin        = im->legendwidth + Xvertical + Xylabel;
+            im->yorigin        = Ytitle + Ymain;
+
+            im->xOriginLegendY2 = im->legendwidth + Xvertical + Xylabel + Xmain;
+            if (im->second_axis_scale != 0){
+                im->xOriginLegendY2 += Xylabel;
+            }
+            im->yOriginLegendY2 = Ytitle + (Ymain / 2);
+
+            break;
+
+        case SOUTH:
+            im->xOriginTitle   = Xvertical + Xylabel + im->xsize / 2;
+            im->yOriginTitle   = 0;
+
+            im->xOriginLegend  = 0;
+            im->yOriginLegend  = Ytitle + Ymain + Yxlabel;
+
+            im->xOriginLegendY = 0;
+            im->yOriginLegendY = Ytitle + (Ymain / 2);
+
+            im->xorigin        = Xvertical + Xylabel;
+            im->yorigin        = Ytitle + Ymain;
+
+            im->xOriginLegendY2 = Xvertical + Xylabel + Xmain;
+            if (im->second_axis_scale != 0){
+                im->xOriginLegendY2 += Xylabel;
+            }
+            im->yOriginLegendY2 = Ytitle + (Ymain / 2);
+
+            break;
+
+        case EAST:
+            im->xOriginTitle   = Xvertical + Xylabel + im->xsize / 2;
+            im->yOriginTitle   = 0;
+
+            im->xOriginLegend  = Xvertical + Xylabel + Xmain + Xvertical2;
+            if (im->second_axis_scale != 0){
+                im->xOriginLegend += Xylabel;
+            }
+            im->yOriginLegend  = Ytitle;
+
+            im->xOriginLegendY = 0;
+            im->yOriginLegendY = Ytitle + (Ymain / 2);
+
+            im->xorigin        = Xvertical + Xylabel;
+            im->yorigin        = Ytitle + Ymain;
+
+            im->xOriginLegendY2 = Xvertical + Xylabel + Xmain;
+            if (im->second_axis_scale != 0){
+                im->xOriginLegendY2 += Xylabel;
+            }
+            im->yOriginLegendY2 = Ytitle + (Ymain / 2);
+
+            if (!(im->extra_flags & NO_RRDTOOL_TAG)){
+                im->xOriginTitle    += Xspacing;
+                im->xOriginLegend   += Xspacing;
+                im->xOriginLegendY  += Xspacing;
+                im->xorigin         += Xspacing;
+                im->xOriginLegendY2 += Xspacing;
+            }
+            break;
     }
-#endif
 
+    xtr(im, 0);
     ytr(im, DNAN);
     return 0;
 }
 
-/* from http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm */
-/* yes we are loosing precision by doing tos with floats instead of doubles
-   but it seems more stable this way. */
-
+static cairo_status_t cairo_output(
+    void *closure,
+    const unsigned char
+    *data,
+    unsigned int length)
+{
+    image_desc_t *im = (image_desc_t*)closure;
+
+    im->rendered_image =
+        (unsigned char*)realloc(im->rendered_image, im->rendered_image_size + length);
+    if (im->rendered_image == NULL)
+        return CAIRO_STATUS_WRITE_ERROR;
+    memcpy(im->rendered_image + im->rendered_image_size, data, length);
+    im->rendered_image_size += length;
+    return CAIRO_STATUS_SUCCESS;
+}
 
 /* draw that picture thing ... */
 int graph_paint(
-    image_desc_t *im,
-    char ***calcpr)
+    image_desc_t *im)
 {
     int       i, ii;
     int       lazy = lazy_check(im);
-
-#ifdef WITH_PIECHART
-    int       piechart = 0;
-    double    PieStart = 0.0;
-#endif
-    FILE     *fo;
-    gfx_node_t *node;
-
     double    areazero = 0.0;
     graph_desc_t *lastgdes = NULL;
+    rrd_infoval_t info;
 
-    /* if we are lazy and there is nothing to PRINT ... quit now */
-    if (lazy && im->prt_c == 0)
-        return 0;
+//    PangoFontMap *font_map = pango_cairo_font_map_get_default();
 
     /* pull the data from the rrd files ... */
-
     if (data_fetch(im) == -1)
         return -1;
-
     /* evaluate VDEF and CDEF operations ... */
     if (data_calc(im) == -1)
         return -1;
-
-#ifdef WITH_PIECHART
-    /* check if we need to draw a piechart */
-    for (i = 0; i < im->gdes_c; i++) {
-        if (im->gdes[i].gf == GF_PART) {
-            piechart = 1;
-            break;
-        }
-    }
-#endif
-
     /* calculate and PRINT and GPRINT definitions. We have to do it at
      * this point because it will affect the length of the legends
-     * if there are no graph elements we stop here ... 
-     * if we are lazy, try to quit ... 
+     * if there are no graph elements (i==0) we stop here ...
+     * if we are lazy, try to quit ...
      */
-    i = print_calc(im, calcpr);
+    i = print_calc(im);
     if (i < 0)
         return -1;
-    if (((i == 0)
-#ifdef WITH_PIECHART
-         && (piechart == 0)
-#endif
-        ) || lazy)
-        return 0;
 
-#ifdef WITH_PIECHART
-    /* If there's only the pie chart to draw, signal this */
+    /* if we want and can be lazy ... quit now */
     if (i == 0)
-        piechart = 2;
-#endif
+        return 0;
 
 /**************************************************************
  *** Calculating sizes and locations became a bit confusing ***
  *** so I moved this into a separate function.              ***
  **************************************************************/
-    if (graph_size_location(im, i
-#ifdef WITH_PIECHART
-                            , piechart
-#endif
-        ) == -1)
+    if (graph_size_location(im, i) == -1)
         return -1;
 
+    info.u_cnt = im->xorigin;
+    grinfo_push(im, sprintf_alloc("graph_left"), RD_I_CNT, info);
+    info.u_cnt = im->yorigin - im->ysize;
+    grinfo_push(im, sprintf_alloc("graph_top"), RD_I_CNT, info);
+    info.u_cnt = im->xsize;
+    grinfo_push(im, sprintf_alloc("graph_width"), RD_I_CNT, info);
+    info.u_cnt = im->ysize;
+    grinfo_push(im, sprintf_alloc("graph_height"), RD_I_CNT, info);
+    info.u_cnt = im->ximg;
+    grinfo_push(im, sprintf_alloc("image_width"), RD_I_CNT, info);
+    info.u_cnt = im->yimg;
+    grinfo_push(im, sprintf_alloc("image_height"), RD_I_CNT, info);
+    info.u_cnt = im->start;
+    grinfo_push(im, sprintf_alloc("graph_start"), RD_I_CNT, info);
+    info.u_cnt = im->end;
+    grinfo_push(im, sprintf_alloc("graph_end"), RD_I_CNT, info);
+
+    /* if we want and can be lazy ... quit now */
+    if (lazy)
+        return 0;
+
     /* get actual drawing data and find min and max values */
     if (data_proc(im) == -1)
         return -1;
-
     if (!im->logarithmic) {
         si_unit(im);
     }
+
     /* identify si magnitude Kilo, Mega Giga ? */
     if (!im->rigid && !im->logarithmic)
         expand_range(im);   /* make sure the upper and lower limit are
                                sensible values */
 
-    if (!calc_horizontal_grid(im))
-        return -1;
+    info.u_val = im->minval;
+    grinfo_push(im, sprintf_alloc("value_min"), RD_I_VAL, info);
+    info.u_val = im->maxval;
+    grinfo_push(im, sprintf_alloc("value_max"), RD_I_VAL, info);
 
-    if (im->gridfit)
-        apply_gridfit(im);
 
+    if (!calc_horizontal_grid(im))
+        return -1;
+    /* reset precalc */
+    ytr(im, DNAN);
+/*   if (im->gridfit)
+     apply_gridfit(im); */
     /* the actual graph is created by going through the individual
        graph elements and then drawing them */
-
-    node = gfx_new_area(im->canvas,
-                        0, 0,
-                        0, im->yimg,
-                        im->ximg, im->yimg, im->graph_col[GRC_BACK]);
-
-    gfx_add_point(node, im->ximg, 0);
-
-#ifdef WITH_PIECHART
-    if (piechart != 2) {
-#endif
-        node = gfx_new_area(im->canvas,
-                            im->xorigin, im->yorigin,
-                            im->xorigin + im->xsize, im->yorigin,
-                            im->xorigin + im->xsize, im->yorigin - im->ysize,
-                            im->graph_col[GRC_CANVAS]);
-
-        gfx_add_point(node, im->xorigin, im->yorigin - im->ysize);
-
-        if (im->minval > 0.0)
-            areazero = im->minval;
-        if (im->maxval < 0.0)
-            areazero = im->maxval;
-#ifdef WITH_PIECHART
-    }
-#endif
-
-#ifdef WITH_PIECHART
-    if (piechart) {
-        pie_part(im, im->graph_col[GRC_CANVAS], im->pie_x, im->pie_y,
-                 im->piesize * 0.5, 0, 2 * M_PI);
-    }
-#endif
-
+    cairo_surface_destroy(im->surface);
+    switch (im->imgformat) {
+    case IF_PNG:
+        im->surface =
+            cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
+                                       im->ximg * im->zoom,
+                                       im->yimg * im->zoom);
+        break;
+    case IF_PDF:
+        im->gridfit = 0;
+        im->surface = strlen(im->graphfile)
+            ? cairo_pdf_surface_create(im->graphfile, im->ximg * im->zoom,
+                                       im->yimg * im->zoom)
+            : cairo_pdf_surface_create_for_stream
+            (&cairo_output, im, im->ximg * im->zoom, im->yimg * im->zoom);
+        break;
+    case IF_EPS:
+        im->gridfit = 0;
+        im->surface = strlen(im->graphfile)
+            ?
+            cairo_ps_surface_create(im->graphfile, im->ximg * im->zoom,
+                                    im->yimg * im->zoom)
+            : cairo_ps_surface_create_for_stream
+            (&cairo_output, im, im->ximg * im->zoom, im->yimg * im->zoom);
+        break;
+    case IF_SVG:
+        im->gridfit = 0;
+        im->surface = strlen(im->graphfile)
+            ?
+            cairo_svg_surface_create(im->
+                                     graphfile,
+                                     im->ximg * im->zoom, im->yimg * im->zoom)
+            : cairo_svg_surface_create_for_stream
+            (&cairo_output, im, im->ximg * im->zoom, im->yimg * im->zoom);
+        cairo_svg_surface_restrict_to_version
+            (im->surface, CAIRO_SVG_VERSION_1_1);
+        break;
+    };
+    cairo_destroy(im->cr);
+    im->cr = cairo_create(im->surface);
+    cairo_set_antialias(im->cr, im->graph_antialias);
+    cairo_scale(im->cr, im->zoom, im->zoom);
+//    pango_cairo_font_map_set_resolution(PANGO_CAIRO_FONT_MAP(font_map), 100);
+    gfx_new_area(im, 0, 0, 0, im->yimg,
+                 im->ximg, im->yimg, im->graph_col[GRC_BACK]);
+    gfx_add_point(im, im->ximg, 0);
+    gfx_close_path(im);
+    gfx_new_area(im, im->xorigin,
+                 im->yorigin,
+                 im->xorigin +
+                 im->xsize, im->yorigin,
+                 im->xorigin +
+                 im->xsize,
+                 im->yorigin - im->ysize, im->graph_col[GRC_CANVAS]);
+    gfx_add_point(im, im->xorigin, im->yorigin - im->ysize);
+    gfx_close_path(im);
+    cairo_rectangle(im->cr, im->xorigin, im->yorigin - im->ysize - 1.0,
+                    im->xsize, im->ysize + 2.0);
+    cairo_clip(im->cr);
+    if (im->minval > 0.0)
+        areazero = im->minval;
+    if (im->maxval < 0.0)
+        areazero = im->maxval;
     for (i = 0; i < im->gdes_c; i++) {
         switch (im->gdes[i].gf) {
         case GF_CDEF:
@@ -3024,6 +3370,7 @@ int graph_paint(
         case GF_PRINT:
         case GF_GPRINT:
         case GF_COMMENT:
+        case GF_TEXTALIGN:
         case GF_HRULE:
         case GF_VRULE:
         case GF_XPORT:
@@ -3031,24 +3378,25 @@ int graph_paint(
             break;
         case GF_TICK:
             for (ii = 0; ii < im->xsize; ii++) {
-                if (!isnan(im->gdes[i].p_data[ii]) &&
-                    im->gdes[i].p_data[ii] != 0.0) {
+                if (!isnan(im->gdes[i].p_data[ii])
+                    && im->gdes[i].p_data[ii] != 0.0) {
                     if (im->gdes[i].yrule > 0) {
-                        gfx_new_line(im->canvas,
-                                     im->xorigin + ii, im->yorigin,
-                                     im->xorigin + ii,
-                                     im->yorigin -
-                                     im->gdes[i].yrule * im->ysize, 1.0,
-                                     im->gdes[i].col);
+                        gfx_line(im,
+                                 im->xorigin + ii,
+                                 im->yorigin + 1.0,
+                                 im->xorigin + ii,
+                                 im->yorigin -
+                                 im->gdes[i].yrule *
+                                 im->ysize, 1.0, im->gdes[i].col);
                     } else if (im->gdes[i].yrule < 0) {
-                        gfx_new_line(im->canvas,
-                                     im->xorigin + ii,
-                                     im->yorigin - im->ysize,
-                                     im->xorigin + ii,
-                                     im->yorigin - (1 -
-                                                    im->gdes[i].yrule) *
-                                     im->ysize, 1.0, im->gdes[i].col);
-
+                        gfx_line(im,
+                                 im->xorigin + ii,
+                                 im->yorigin - im->ysize - 1.0,
+                                 im->xorigin + ii,
+                                 im->yorigin - im->ysize -
+                                                im->gdes[i].
+                                                yrule *
+                                 im->ysize, 1.0, im->gdes[i].col);
                     }
                 }
             }
@@ -3068,68 +3416,100 @@ int graph_paint(
             }           /* for */
 
             /* *******************************************************
-               a           ___. (a,t) 
+               a           ___. (a,t)
                |   |    ___
                ____|   |   |   |
                |       |___|
-               -------|--t-1--t--------------------------------      
+               -------|--t-1--t--------------------------------
 
-               if we know the value at time t was a then 
+               if we know the value at time t was a then
                we draw a square from t-1 to t with the value a.
 
                ********************************************************* */
-            if (im->gdes[i].col != 0x0) {
+            if (im->gdes[i].col.alpha != 0.0) {
                 /* GF_LINE and friend */
                 if (im->gdes[i].gf == GF_LINE) {
                     double    last_y = 0.0;
+                    int       draw_on = 0;
+
+                    cairo_save(im->cr);
+                    cairo_new_path(im->cr);
+                    cairo_set_line_width(im->cr, im->gdes[i].linewidth);
+                    if (im->gdes[i].dash) {
+                        cairo_set_dash(im->cr,
+                                       im->gdes[i].p_dashes,
+                                       im->gdes[i].ndash, im->gdes[i].offset);
+                    }
 
-                    node = NULL;
                     for (ii = 1; ii < im->xsize; ii++) {
                         if (isnan(im->gdes[i].p_data[ii])
                             || (im->slopemode == 1
                                 && isnan(im->gdes[i].p_data[ii - 1]))) {
-                            node = NULL;
+                            draw_on = 0;
                             continue;
                         }
-                        if (node == NULL) {
+                        if (draw_on == 0) {
                             last_y = ytr(im, im->gdes[i].p_data[ii]);
                             if (im->slopemode == 0) {
-                                node = gfx_new_line(im->canvas,
-                                                    ii - 1 + im->xorigin,
-                                                    last_y, ii + im->xorigin,
-                                                    last_y,
-                                                    im->gdes[i].linewidth,
-                                                    im->gdes[i].col);
+                                double    x = ii - 1 + im->xorigin;
+                                double    y = last_y;
+
+                                gfx_line_fit(im, &x, &y);
+                                cairo_move_to(im->cr, x, y);
+                                x = ii + im->xorigin;
+                                y = last_y;
+                                gfx_line_fit(im, &x, &y);
+                                cairo_line_to(im->cr, x, y);
                             } else {
-                                node = gfx_new_line(im->canvas,
-                                                    ii - 1 + im->xorigin,
-                                                    ytr(im,
-                                                        im->gdes[i].
-                                                        p_data[ii - 1]),
-                                                    ii + im->xorigin, last_y,
-                                                    im->gdes[i].linewidth,
-                                                    im->gdes[i].col);
+                                double    x = ii - 1 + im->xorigin;
+                                double    y =
+                                    ytr(im, im->gdes[i].p_data[ii - 1]);
+                                gfx_line_fit(im, &x, &y);
+                                cairo_move_to(im->cr, x, y);
+                                x = ii + im->xorigin;
+                                y = last_y;
+                                gfx_line_fit(im, &x, &y);
+                                cairo_line_to(im->cr, x, y);
                             }
+                            draw_on = 1;
                         } else {
-                            double    new_y = ytr(im, im->gdes[i].p_data[ii]);
+                            double    x1 = ii + im->xorigin;
+                            double    y1 = ytr(im, im->gdes[i].p_data[ii]);
 
                             if (im->slopemode == 0
-                                && !AlmostEqual2sComplement(new_y, last_y,
-                                                            4)) {
-                                gfx_add_point(node, ii - 1 + im->xorigin,
-                                              new_y);
+                                && !AlmostEqual2sComplement(y1, last_y, 4)) {
+                                double    x = ii - 1 + im->xorigin;
+                                double    y = y1;
+
+                                gfx_line_fit(im, &x, &y);
+                                cairo_line_to(im->cr, x, y);
                             };
-                            last_y = new_y;
-                            gfx_add_point(node, ii + im->xorigin, new_y);
+                            last_y = y1;
+                            gfx_line_fit(im, &x1, &y1);
+                            cairo_line_to(im->cr, x1, y1);
                         };
-
                     }
+                    cairo_set_source_rgba(im->cr,
+                                          im->gdes[i].
+                                          col.red,
+                                          im->gdes[i].
+                                          col.green,
+                                          im->gdes[i].
+                                          col.blue, im->gdes[i].col.alpha);
+                    cairo_set_line_cap(im->cr, CAIRO_LINE_CAP_ROUND);
+                    cairo_set_line_join(im->cr, CAIRO_LINE_JOIN_ROUND);
+                    cairo_stroke(im->cr);
+                    cairo_restore(im->cr);
                 } else {
                     int       idxI = -1;
-                    double   *foreY = malloc(sizeof(double) * im->xsize * 2);
-                    double   *foreX = malloc(sizeof(double) * im->xsize * 2);
-                    double   *backY = malloc(sizeof(double) * im->xsize * 2);
-                    double   *backX = malloc(sizeof(double) * im->xsize * 2);
+                    double   *foreY =
+                        (double *) malloc(sizeof(double) * im->xsize * 2);
+                    double   *foreX =
+                        (double *) malloc(sizeof(double) * im->xsize * 2);
+                    double   *backY =
+                        (double *) malloc(sizeof(double) * im->xsize * 2);
+                    double   *backX =
+                        (double *) malloc(sizeof(double) * im->xsize * 2);
                     int       drawem = 0;
 
                     for (ii = 0; ii <= im->xsize; ii++) {
@@ -3140,51 +3520,62 @@ int graph_paint(
                             int       lastI = 0;
 
                             while (cntI < idxI
-                                   && AlmostEqual2sComplement(foreY[lastI],
-                                                              foreY[cntI], 4)
-                                   && AlmostEqual2sComplement(foreY[lastI],
-                                                              foreY[cntI + 1],
-                                                              4)) {
+                                   &&
+                                   AlmostEqual2sComplement(foreY
+                                                           [lastI],
+                                                           foreY[cntI], 4)
+                                   &&
+                                   AlmostEqual2sComplement(foreY
+                                                           [lastI],
+                                                           foreY
+                                                           [cntI + 1], 4)) {
                                 cntI++;
                             }
-                            node = gfx_new_area(im->canvas,
-                                                backX[0], backY[0],
-                                                foreX[0], foreY[0],
-                                                foreX[cntI], foreY[cntI],
-                                                im->gdes[i].col);
+                            gfx_new_area(im,
+                                         backX[0], backY[0],
+                                         foreX[0], foreY[0],
+                                         foreX[cntI],
+                                         foreY[cntI], im->gdes[i].col);
                             while (cntI < idxI) {
                                 lastI = cntI;
                                 cntI++;
                                 while (cntI < idxI
                                        &&
-                                       AlmostEqual2sComplement(foreY[lastI],
+                                       AlmostEqual2sComplement(foreY
+                                                               [lastI],
                                                                foreY[cntI], 4)
                                        &&
-                                       AlmostEqual2sComplement(foreY[lastI],
-                                                               foreY[cntI +
-                                                                     1], 4)) {
+                                       AlmostEqual2sComplement(foreY
+                                                               [lastI],
+                                                               foreY
+                                                               [cntI
+                                                                + 1], 4)) {
                                     cntI++;
                                 }
-                                gfx_add_point(node, foreX[cntI], foreY[cntI]);
+                                gfx_add_point(im, foreX[cntI], foreY[cntI]);
                             }
-                            gfx_add_point(node, backX[idxI], backY[idxI]);
+                            gfx_add_point(im, backX[idxI], backY[idxI]);
                             while (idxI > 1) {
                                 lastI = idxI;
                                 idxI--;
                                 while (idxI > 1
                                        &&
-                                       AlmostEqual2sComplement(backY[lastI],
+                                       AlmostEqual2sComplement(backY
+                                                               [lastI],
                                                                backY[idxI], 4)
                                        &&
-                                       AlmostEqual2sComplement(backY[lastI],
-                                                               backY[idxI -
-                                                                     1], 4)) {
+                                       AlmostEqual2sComplement(backY
+                                                               [lastI],
+                                                               backY
+                                                               [idxI
+                                                                - 1], 4)) {
                                     idxI--;
                                 }
-                                gfx_add_point(node, backX[idxI], backY[idxI]);
+                                gfx_add_point(im, backX[idxI], backY[idxI]);
                             }
                             idxI = -1;
                             drawem = 0;
+                            gfx_close_path(im);
                         }
                         if (drawem != 0) {
                             drawem = 0;
@@ -3192,11 +3583,6 @@ int graph_paint(
                         }
                         if (ii == im->xsize)
                             break;
-
-                        /* keep things simple for now, just draw these bars
-                           do not try to build a big and complex area */
-
-
                         if (im->slopemode == 0 && ii == 0) {
                             continue;
                         }
@@ -3214,8 +3600,7 @@ int graph_paint(
                             drawem = 1;
                             continue;
                         }
-                        /* every area has to be wound clock-wise,
-                           so we have to make sur base remains base  */
+
                         if (ybase > ytop) {
                             double    extra = ytop;
 
@@ -3253,64 +3638,60 @@ int graph_paint(
             }
             lastgdes = &(im->gdes[i]);
             break;
-#ifdef WITH_PIECHART
-        case GF_PART:
-            if (isnan(im->gdes[i].yrule))   /* fetch variable */
-                im->gdes[i].yrule = im->gdes[im->gdes[i].vidx].vf.val;
-
-            if (finite(im->gdes[i].yrule)) {    /* even the fetched var can be NaN */
-                pie_part(im, im->gdes[i].col,
-                         im->pie_x, im->pie_y, im->piesize * 0.4,
-                         M_PI * 2.0 * PieStart / 100.0,
-                         M_PI * 2.0 * (PieStart + im->gdes[i].yrule) / 100.0);
-                PieStart += im->gdes[i].yrule;
-            }
-            break;
-#endif
         case GF_STACK:
             rrd_set_error
                 ("STACK should already be turned into LINE or AREA here");
             return -1;
             break;
-
         }               /* switch */
     }
-#ifdef WITH_PIECHART
-    if (piechart == 2) {
-        im->draw_x_grid = 0;
-        im->draw_y_grid = 0;
-    }
-#endif
-
+    cairo_reset_clip(im->cr);
 
     /* grid_paint also does the text */
     if (!(im->extra_flags & ONLY_GRAPH))
         grid_paint(im);
-
-
     if (!(im->extra_flags & ONLY_GRAPH))
         axis_paint(im);
-
     /* the RULES are the last thing to paint ... */
     for (i = 0; i < im->gdes_c; i++) {
 
         switch (im->gdes[i].gf) {
         case GF_HRULE:
             if (im->gdes[i].yrule >= im->minval
-                && im->gdes[i].yrule <= im->maxval)
-                gfx_new_line(im->canvas,
-                             im->xorigin, ytr(im, im->gdes[i].yrule),
-                             im->xorigin + im->xsize, ytr(im,
-                                                          im->gdes[i].yrule),
-                             1.0, im->gdes[i].col);
+                && im->gdes[i].yrule <= im->maxval) {
+                cairo_save(im->cr);
+                if (im->gdes[i].dash) {
+                    cairo_set_dash(im->cr,
+                                   im->gdes[i].p_dashes,
+                                   im->gdes[i].ndash, im->gdes[i].offset);
+                }
+                gfx_line(im, im->xorigin,
+                         ytr(im, im->gdes[i].yrule),
+                         im->xorigin + im->xsize,
+                         ytr(im, im->gdes[i].yrule), 1.0, im->gdes[i].col);
+                cairo_stroke(im->cr);
+                cairo_restore(im->cr);
+            }
             break;
         case GF_VRULE:
             if (im->gdes[i].xrule >= im->start
-                && im->gdes[i].xrule <= im->end)
-                gfx_new_line(im->canvas,
-                             xtr(im, im->gdes[i].xrule), im->yorigin,
-                             xtr(im, im->gdes[i].xrule),
-                             im->yorigin - im->ysize, 1.0, im->gdes[i].col);
+                && im->gdes[i].xrule <= im->end) {
+                cairo_save(im->cr);
+                if (im->gdes[i].dash) {
+                    cairo_set_dash(im->cr,
+                                   im->gdes[i].p_dashes,
+                                   im->gdes[i].ndash, im->gdes[i].offset);
+                }
+                gfx_line(im,
+                         xtr(im, im->gdes[i].xrule),
+                         im->yorigin, xtr(im,
+                                          im->
+                                          gdes[i].
+                                          xrule),
+                         im->yorigin - im->ysize, 1.0, im->gdes[i].col);
+                cairo_stroke(im->cr);
+                cairo_restore(im->cr);
+            }
             break;
         default:
             break;
@@ -3318,28 +3699,37 @@ int graph_paint(
     }
 
 
-    if (strcmp(im->graphfile, "-") == 0) {
-        fo = im->graphhandle ? im->graphhandle : stdout;
-#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
-        /* Change translation mode for stdout to BINARY */
-        _setmode(_fileno(fo), O_BINARY);
-#endif
-    } else {
-        if ((fo = fopen(im->graphfile, "wb")) == NULL) {
-            rrd_set_error("Opening '%s' for write: %s", im->graphfile,
-                          rrd_strerror(errno));
-            return (-1);
+    switch (im->imgformat) {
+    case IF_PNG:
+    {
+        cairo_status_t status;
+
+        status = strlen(im->graphfile) ?
+            cairo_surface_write_to_png(im->surface, im->graphfile)
+            : cairo_surface_write_to_png_stream(im->surface, &cairo_output,
+                                                im);
+
+        if (status != CAIRO_STATUS_SUCCESS) {
+            rrd_set_error("Could not save png to '%s'", im->graphfile);
+            return 1;
+        }
+        break;
+    }
+    default:
+        if (strlen(im->graphfile)) {
+            cairo_show_page(im->cr);
+        } else {
+            cairo_surface_finish(im->surface);
         }
+        break;
     }
-    gfx_render(im->canvas, im->ximg, im->yimg, 0x00000000, fo);
-    if (strcmp(im->graphfile, "-") != 0)
-        fclose(fo);
+
     return 0;
 }
 
 
 /*****************************************************
- * graph stuff 
+ * graph stuff
  *****************************************************/
 
 int gdes_alloc(
@@ -3347,9 +3737,9 @@ int gdes_alloc(
 {
 
     im->gdes_c++;
-    if ((im->gdes = (graph_desc_t *) rrd_realloc(im->gdes, (im->gdes_c)
-                                                 * sizeof(graph_desc_t))) ==
-        NULL) {
+    if ((im->gdes = (graph_desc_t *)
+         rrd_realloc(im->gdes, (im->gdes_c)
+                     * sizeof(graph_desc_t))) == NULL) {
         rrd_set_error("realloc graph_descs");
         return -1;
     }
@@ -3370,8 +3760,15 @@ int gdes_alloc(
     im->gdes[im->gdes_c - 1].data_first = 0;
     im->gdes[im->gdes_c - 1].p_data = NULL;
     im->gdes[im->gdes_c - 1].rpnp = NULL;
-    im->gdes[im->gdes_c - 1].shift = 0;
-    im->gdes[im->gdes_c - 1].col = 0x0;
+    im->gdes[im->gdes_c - 1].p_dashes = NULL;
+    im->gdes[im->gdes_c - 1].shift = 0.0;
+    im->gdes[im->gdes_c - 1].dash = 0;
+    im->gdes[im->gdes_c - 1].ndash = 0;
+    im->gdes[im->gdes_c - 1].offset = 0;
+    im->gdes[im->gdes_c - 1].col.red = 0.0;
+    im->gdes[im->gdes_c - 1].col.green = 0.0;
+    im->gdes[im->gdes_c - 1].col.blue = 0.0;
+    im->gdes[im->gdes_c - 1].col.alpha = 0.0;
     im->gdes[im->gdes_c - 1].legend[0] = '\0';
     im->gdes[im->gdes_c - 1].format[0] = '\0';
     im->gdes[im->gdes_c - 1].strftm = 0;
@@ -3379,7 +3776,6 @@ int gdes_alloc(
     im->gdes[im->gdes_c - 1].ds = -1;
     im->gdes[im->gdes_c - 1].cf_reduce = CF_AVERAGE;
     im->gdes[im->gdes_c - 1].cf = CF_AVERAGE;
-    im->gdes[im->gdes_c - 1].p_data = NULL;
     im->gdes[im->gdes_c - 1].yrule = DNAN;
     im->gdes[im->gdes_c - 1].xrule = 0;
     return 0;
@@ -3395,278 +3791,413 @@ int scan_for_col(
     int       inp, outp = 0;
 
     for (inp = 0; inp < len && input[inp] != ':' && input[inp] != '\0'; inp++) {
-        if (input[inp] == '\\' &&
-            input[inp + 1] != '\0' &&
-            (input[inp + 1] == '\\' || input[inp + 1] == ':')) {
+        if (input[inp] == '\\'
+            && input[inp + 1] != '\0'
+            && (input[inp + 1] == '\\' || input[inp + 1] == ':')) {
             output[outp++] = input[++inp];
         } else {
             output[outp++] = input[inp];
         }
     }
-    output[outp] = '\0';
-    return inp;
+    output[outp] = '\0';
+    return inp;
+}
+
+/* Now just a wrapper around rrd_graph_v */
+int rrd_graph(
+    int argc,
+    char **argv,
+    char ***prdata,
+    int *xsize,
+    int *ysize,
+    FILE * stream,
+    double *ymin,
+    double *ymax)
+{
+    int       prlines = 0;
+    rrd_info_t *grinfo = NULL;
+    rrd_info_t *walker;
+
+    grinfo = rrd_graph_v(argc, argv);
+    if (grinfo == NULL)
+        return -1;
+    walker = grinfo;
+    (*prdata) = NULL;
+    while (walker) {
+        if (strcmp(walker->key, "image_info") == 0) {
+            prlines++;
+            if (((*prdata) =
+                 (char**)rrd_realloc((*prdata),
+                             (prlines + 1) * sizeof(char *))) == NULL) {
+                rrd_set_error("realloc prdata");
+                return 0;
+            }
+            /* imginfo goes to position 0 in the prdata array */
+            (*prdata)[prlines - 1] = (char*)malloc((strlen(walker->value.u_str)
+                                             + 2) * sizeof(char));
+            strcpy((*prdata)[prlines - 1], walker->value.u_str);
+            (*prdata)[prlines] = NULL;
+        }
+        /* skip anything else */
+        walker = walker->next;
+    }
+    walker = grinfo;
+    *xsize = 0;
+    *ysize = 0;
+    *ymin = 0;
+    *ymax = 0;
+    while (walker) {
+        if (strcmp(walker->key, "image_width") == 0) {
+            *xsize = walker->value.u_cnt;
+        } else if (strcmp(walker->key, "image_height") == 0) {
+            *ysize = walker->value.u_cnt;
+        } else if (strcmp(walker->key, "value_min") == 0) {
+            *ymin = walker->value.u_val;
+        } else if (strcmp(walker->key, "value_max") == 0) {
+            *ymax = walker->value.u_val;
+        } else if (strncmp(walker->key, "print", 5) == 0) { /* keys are prdate[0..] */
+            prlines++;
+            if (((*prdata) =
+                 (char**)rrd_realloc((*prdata),
+                             (prlines + 1) * sizeof(char *))) == NULL) {
+                rrd_set_error("realloc prdata");
+                return 0;
+            }
+            (*prdata)[prlines - 1] = (char*)malloc((strlen(walker->value.u_str)
+                                             + 2) * sizeof(char));
+            (*prdata)[prlines] = NULL;
+            strcpy((*prdata)[prlines - 1], walker->value.u_str);
+        } else if (strcmp(walker->key, "image") == 0) {
+            if ( fwrite(walker->value.u_blo.ptr, walker->value.u_blo.size, 1,
+                   (stream ? stream : stdout)) == 0 && ferror(stream ? stream : stdout)){
+                rrd_set_error("writing image");
+                return 0;
+            }
+        }
+        /* skip anything else */
+        walker = walker->next;
+    }
+    rrd_info_free(grinfo);
+    return 0;
 }
 
+
 /* Some surgery done on this function, it became ridiculously big.
 ** Things moved:
 ** - initializing     now in rrd_graph_init()
 ** - options parsing  now in rrd_graph_options()
 ** - script parsing   now in rrd_graph_script()
 */
-int rrd_graph(
+rrd_info_t *rrd_graph_v(
     int argc,
-    char **argv,
-    char ***prdata,
-    int *xsize,
-    int *ysize,
-    FILE * stream,
-    double *ymin,
-    double *ymax)
+    char **argv)
 {
     image_desc_t im;
-
+    rrd_info_t *grinfo;
+    char *old_locale;
     rrd_graph_init(&im);
-    im.graphhandle = stream;
-
+    /* a dummy surface so that we can measure text sizes for placements */
+    old_locale = setlocale(LC_NUMERIC, "C");
     rrd_graph_options(argc, argv, &im);
     if (rrd_test_error()) {
+        rrd_info_free(im.grinfo);
         im_free(&im);
-        return -1;
+        return NULL;
+    }
+
+    if (optind >= argc) {
+        rrd_info_free(im.grinfo);
+        im_free(&im);
+        rrd_set_error("missing filename");
+        return NULL;
     }
 
     if (strlen(argv[optind]) >= MAXPATH) {
         rrd_set_error("filename (including path) too long");
+        rrd_info_free(im.grinfo);
         im_free(&im);
-        return -1;
+        return NULL;
     }
+
     strncpy(im.graphfile, argv[optind], MAXPATH - 1);
     im.graphfile[MAXPATH - 1] = '\0';
 
+    if (strcmp(im.graphfile, "-") == 0) {
+        im.graphfile[0] = '\0';
+    }
+
     rrd_graph_script(argc, argv, &im, 1);
+    setlocale(LC_NUMERIC, old_locale); /* reenable locale for rendering the graph */
+
     if (rrd_test_error()) {
+        rrd_info_free(im.grinfo);
         im_free(&im);
-        return -1;
+        return NULL;
     }
 
     /* Everything is now read and the actual work can start */
 
-    (*prdata) = NULL;
-    if (graph_paint(&im, prdata) == -1) {
+    if (graph_paint(&im) == -1) {
+        rrd_info_free(im.grinfo);
         im_free(&im);
-        return -1;
+        return NULL;
     }
 
+
     /* The image is generated and needs to be output.
      ** Also, if needed, print a line with information about the image.
      */
 
-    *xsize = im.ximg;
-    *ysize = im.yimg;
-    *ymin = im.minval;
-    *ymax = im.maxval;
     if (im.imginfo) {
+        rrd_infoval_t info;
+        char     *path;
         char     *filename;
 
-        if (!(*prdata)) {
-            /* maybe prdata is not allocated yet ... lets do it now */
-            if ((*prdata = calloc(2, sizeof(char *))) == NULL) {
-                rrd_set_error("malloc imginfo");
-                return -1;
-            };
-        }
-        if (((*prdata)[0] =
-             malloc((strlen(im.imginfo) + 200 +
-                     strlen(im.graphfile)) * sizeof(char)))
-            == NULL) {
-            rrd_set_error("malloc imginfo");
-            return -1;
-        }
-        filename = im.graphfile + strlen(im.graphfile);
-        while (filename > im.graphfile) {
-            if (*(filename - 1) == '/' || *(filename - 1) == '\\')
-                break;
-            filename--;
-        }
+        path = strdup(im.graphfile);
+        filename = basename(path);
+        info.u_str =
+            sprintf_alloc(im.imginfo,
+                          filename,
+                          (long) (im.zoom *
+                                  im.ximg), (long) (im.zoom * im.yimg));
+        grinfo_push(&im, sprintf_alloc("image_info"), RD_I_STR, info);
+        free(info.u_str);
+        free(path);
+    }
+    if (im.rendered_image) {
+        rrd_infoval_t img;
 
-        sprintf((*prdata)[0], im.imginfo, filename,
-                (long) (im.canvas->zoom * im.ximg),
-                (long) (im.canvas->zoom * im.yimg));
+        img.u_blo.size = im.rendered_image_size;
+        img.u_blo.ptr = im.rendered_image;
+        grinfo_push(&im, sprintf_alloc("image"), RD_I_BLO, img);
     }
+    grinfo = im.grinfo;
     im_free(&im);
-    return 0;
+    return grinfo;
+}
+
+static void
+rrd_set_font_desc (
+    image_desc_t *im,int prop,char *font, double size ){
+    if (font){
+        strncpy(im->text_prop[prop].font, font, sizeof(text_prop[prop].font) - 1);
+        im->text_prop[prop].font[sizeof(text_prop[prop].font) - 1] = '\0';
+        im->text_prop[prop].font_desc = pango_font_description_from_string( font );
+    };
+    if (size > 0){
+        im->text_prop[prop].size = size;
+    };
+    if (im->text_prop[prop].font_desc && im->text_prop[prop].size ){
+        pango_font_description_set_size(im->text_prop[prop].font_desc, im->text_prop[prop].size * PANGO_SCALE);
+    };
 }
 
 void rrd_graph_init(
-    image_desc_t *im)
+    image_desc_t
+    *im)
 {
     unsigned int i;
+    char     *deffont = getenv("RRD_DEFAULT_FONT");
+    static PangoFontMap *fontmap = NULL;
+    PangoContext *context;
 
 #ifdef HAVE_TZSET
     tzset();
 #endif
-#ifdef HAVE_SETLOCALE
-    setlocale(LC_TIME, "");
-#ifdef HAVE_MBSTOWCS
-    setlocale(LC_CTYPE, "");
-#endif
-#endif
-    im->yorigin = 0;
-    im->xorigin = 0;
-    im->minval = 0;
-    im->xlab_user.minsec = -1;
-    im->ximg = 0;
-    im->yimg = 0;
-    im->xsize = 400;
-    im->ysize = 100;
-    im->step = 0;
-    im->ylegend[0] = '\0';
-    im->title[0] = '\0';
-    im->watermark[0] = '\0';
-    im->minval = DNAN;
-    im->maxval = DNAN;
-    im->unitsexponent = 9999;
-    im->unitslength = 6;
-    im->forceleftspace = 0;
-    im->symbol = ' ';
-    im->viewfactor = 1.0;
+
+    im->base = 1000;
+    im->daemon_addr = NULL;
+    im->draw_x_grid = 1;
+    im->draw_y_grid = 1;
+    im->draw_3d_border = 2;
+    im->dynamic_labels = 0;
     im->extra_flags = 0;
-    im->rigid = 0;
+    im->font_options = cairo_font_options_create();
+    im->forceleftspace = 0;
+    im->gdes_c = 0;
+    im->gdes = NULL;
+    im->graph_antialias = CAIRO_ANTIALIAS_GRAY;
+    im->grid_dash_off = 1;
+    im->grid_dash_on = 1;
     im->gridfit = 1;
+    im->grinfo = (rrd_info_t *) NULL;
+    im->grinfo_current = (rrd_info_t *) NULL;
+    im->imgformat = IF_PNG;
     im->imginfo = NULL;
     im->lazy = 0;
-    im->slopemode = 0;
+    im->legenddirection = TOP_DOWN;
+    im->legendheight = 0;
+    im->legendposition = SOUTH;
+    im->legendwidth = 0;
     im->logarithmic = 0;
-    im->ygridstep = DNAN;
-    im->draw_x_grid = 1;
-    im->draw_y_grid = 1;
-    im->base = 1000;
+    im->maxval = DNAN;
+    im->minval = 0;
+    im->minval = DNAN;
     im->prt_c = 0;
-    im->gdes_c = 0;
-    im->gdes = NULL;
-    im->canvas = gfx_new_canvas();
-    im->grid_dash_on = 1;
-    im->grid_dash_off = 1;
+    im->rigid = 0;
+    im->rendered_image_size = 0;
+    im->rendered_image = NULL;
+    im->slopemode = 0;
+    im->step = 0;
+    im->symbol = ' ';
     im->tabwidth = 40.0;
+    im->title[0] = '\0';
+    im->unitsexponent = 9999;
+    im->unitslength = 6;
+    im->viewfactor = 1.0;
+    im->watermark[0] = '\0';
+    im->with_markup = 0;
+    im->ximg = 0;
+    im->xlab_user.minsec = -1;
+    im->xorigin = 0;
+    im->xOriginLegend = 0;
+    im->xOriginLegendY = 0;
+    im->xOriginLegendY2 = 0;
+    im->xOriginTitle = 0;
+    im->xsize = 400;
+    im->ygridstep = DNAN;
+    im->yimg = 0;
+    im->ylegend[0] = '\0';
+    im->second_axis_scale = 0; /* 0 disables it */
+    im->second_axis_shift = 0; /* no shift by default */
+    im->second_axis_legend[0] = '\0';
+    im->second_axis_format[0] = '\0';
+    im->yorigin = 0;
+    im->yOriginLegend = 0;
+    im->yOriginLegendY = 0;
+    im->yOriginLegendY2 = 0;
+    im->yOriginTitle = 0;
+    im->ysize = 100;
+    im->zoom = 1;
 
-    for (i = 0; i < DIM(graph_col); i++)
-        im->graph_col[i] = graph_col[i];
+    im->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 10, 10);
+    im->cr = cairo_create(im->surface);
 
-#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
-    {
-        char     *windir;
-        char      rrd_win_default_font[1000];
-
-        windir = getenv("windir");
-        /* %windir% is something like D:\windows or C:\winnt */
-        if (windir != NULL) {
-            strncpy(rrd_win_default_font, windir, 500);
-            rrd_win_default_font[500] = '\0';
-            strcat(rrd_win_default_font, "\\fonts\\");
-            strcat(rrd_win_default_font, RRD_DEFAULT_FONT);
-            for (i = 0; i < DIM(text_prop); i++) {
-                strncpy(text_prop[i].font, rrd_win_default_font,
-                        sizeof(text_prop[i].font) - 1);
-                text_prop[i].font[sizeof(text_prop[i].font) - 1] = '\0';
-            }
-        }
-    }
-#endif
-    {
-        char     *deffont;
-
-        deffont = getenv("RRD_DEFAULT_FONT");
-        if (deffont != NULL) {
-            for (i = 0; i < DIM(text_prop); i++) {
-                strncpy(text_prop[i].font, deffont,
-                        sizeof(text_prop[i].font) - 1);
-                text_prop[i].font[sizeof(text_prop[i].font) - 1] = '\0';
-            }
-        }
-    }
     for (i = 0; i < DIM(text_prop); i++) {
-        im->text_prop[i].size = text_prop[i].size;
-        strcpy(im->text_prop[i].font, text_prop[i].font);
+        im->text_prop[i].size = -1;
+        rrd_set_font_desc(im,i, deffont ? deffont : text_prop[i].font,text_prop[i].size);
     }
+
+    if (fontmap == NULL){
+        fontmap = pango_cairo_font_map_get_default();
+    }
+
+    context =  pango_cairo_font_map_create_context((PangoCairoFontMap*)fontmap);
+
+    pango_cairo_context_set_resolution(context, 100);
+
+    pango_cairo_update_context(im->cr,context);
+
+    im->layout = pango_layout_new(context);
+
+//  im->layout = pango_cairo_create_layout(im->cr);
+
+
+    cairo_font_options_set_hint_style
+        (im->font_options, CAIRO_HINT_STYLE_FULL);
+    cairo_font_options_set_hint_metrics
+        (im->font_options, CAIRO_HINT_METRICS_ON);
+    cairo_font_options_set_antialias(im->font_options, CAIRO_ANTIALIAS_GRAY);
+
+
+
+    for (i = 0; i < DIM(graph_col); i++)
+        im->graph_col[i] = graph_col[i];
+
+
 }
 
+
 void rrd_graph_options(
     int argc,
     char *argv[],
-    image_desc_t *im)
+    image_desc_t
+    *im)
 {
     int       stroff;
     char     *parsetime_error = NULL;
     char      scan_gtm[12], scan_mtm[12], scan_ltm[12], col_nam[12];
     time_t    start_tmp = 0, end_tmp = 0;
     long      long_tmp;
-    struct rrd_time_value start_tv, end_tv;
-    gfx_color_t color;
-
-    optind = 0;
-    opterr = 0;         /* initialize getopt */
-
-    parsetime("end-24h", &start_tv);
-    parsetime("now", &end_tv);
+    rrd_time_value_t start_tv, end_tv;
+    long unsigned int color;
 
     /* defines for long options without a short equivalent. should be bytes,
        and may not collide with (the ASCII value of) short options */
 #define LONGOPT_UNITS_SI 255
 
+/* *INDENT-OFF* */
+    struct option long_options[] = {
+        { "alt-autoscale",      no_argument,       0, 'A'},
+        { "imgformat",          required_argument, 0, 'a'},
+        { "font-smoothing-threshold", required_argument, 0, 'B'},
+        { "base",               required_argument, 0, 'b'},
+        { "color",              required_argument, 0, 'c'},
+        { "full-size-mode",     no_argument,       0, 'D'},
+        { "daemon",             required_argument, 0, 'd'},
+        { "slope-mode",         no_argument,       0, 'E'},
+        { "end",                required_argument, 0, 'e'},
+        { "force-rules-legend", no_argument,       0, 'F'},
+        { "imginfo",            required_argument, 0, 'f'},
+        { "graph-render-mode",  required_argument, 0, 'G'},
+        { "no-legend",          no_argument,       0, 'g'},
+        { "height",             required_argument, 0, 'h'},
+        { "no-minor",           no_argument,       0, 'I'},
+        { "interlaced",         no_argument,       0, 'i'},
+        { "alt-autoscale-min",  no_argument,       0, 'J'},
+        { "only-graph",         no_argument,       0, 'j'},
+        { "units-length",       required_argument, 0, 'L'},
+        { "lower-limit",        required_argument, 0, 'l'},
+        { "alt-autoscale-max",  no_argument,       0, 'M'},
+        { "zoom",               required_argument, 0, 'm'},
+        { "no-gridfit",         no_argument,       0, 'N'},
+        { "font",               required_argument, 0, 'n'},
+        { "logarithmic",        no_argument,       0, 'o'},
+        { "pango-markup",       no_argument,       0, 'P'},
+        { "font-render-mode",   required_argument, 0, 'R'},
+        { "rigid",              no_argument,       0, 'r'},
+        { "step",               required_argument, 0, 'S'},
+        { "start",              required_argument, 0, 's'},
+        { "tabwidth",           required_argument, 0, 'T'},
+        { "title",              required_argument, 0, 't'},
+        { "upper-limit",        required_argument, 0, 'u'},
+        { "vertical-label",     required_argument, 0, 'v'},
+        { "watermark",          required_argument, 0, 'W'},
+        { "width",              required_argument, 0, 'w'},
+        { "units-exponent",     required_argument, 0, 'X'},
+        { "x-grid",             required_argument, 0, 'x'},
+        { "alt-y-grid",         no_argument,       0, 'Y'},
+        { "y-grid",             required_argument, 0, 'y'},
+        { "lazy",               no_argument,       0, 'z'},
+        { "units",              required_argument, 0, LONGOPT_UNITS_SI},
+        { "alt-y-mrtg",         no_argument,       0, 1000},    /* this has no effect it is just here to save old apps from crashing when they use it */
+        { "disable-rrdtool-tag",no_argument,       0, 1001},
+        { "right-axis",         required_argument, 0, 1002},
+        { "right-axis-label",   required_argument, 0, 1003},
+        { "right-axis-format",  required_argument, 0, 1004},
+        { "legend-position",    required_argument, 0, 1005},
+        { "legend-direction",   required_argument, 0, 1006},
+        { "border",             required_argument, 0, 1007},
+        { "grid-dash",          required_argument, 0, 1008},
+        { "dynamic-labels",     no_argument,       0, 1009},
+        {  0, 0, 0, 0}
+};
+/* *INDENT-ON* */
+
+    optind = 0;
+    opterr = 0;         /* initialize getopt */
+    rrd_parsetime("end-24h", &start_tv);
+    rrd_parsetime("now", &end_tv);
     while (1) {
-        static struct option long_options[] = {
-            {"start", required_argument, 0, 's'},
-            {"end", required_argument, 0, 'e'},
-            {"x-grid", required_argument, 0, 'x'},
-            {"y-grid", required_argument, 0, 'y'},
-            {"vertical-label", required_argument, 0, 'v'},
-            {"width", required_argument, 0, 'w'},
-            {"height", required_argument, 0, 'h'},
-            {"full-size-mode", no_argument, 0, 'D'},
-            {"interlaced", no_argument, 0, 'i'},
-            {"upper-limit", required_argument, 0, 'u'},
-            {"lower-limit", required_argument, 0, 'l'},
-            {"rigid", no_argument, 0, 'r'},
-            {"base", required_argument, 0, 'b'},
-            {"logarithmic", no_argument, 0, 'o'},
-            {"color", required_argument, 0, 'c'},
-            {"font", required_argument, 0, 'n'},
-            {"title", required_argument, 0, 't'},
-            {"imginfo", required_argument, 0, 'f'},
-            {"imgformat", required_argument, 0, 'a'},
-            {"lazy", no_argument, 0, 'z'},
-            {"zoom", required_argument, 0, 'm'},
-            {"no-legend", no_argument, 0, 'g'},
-            {"force-rules-legend", no_argument, 0, 'F'},
-            {"only-graph", no_argument, 0, 'j'},
-            {"alt-y-grid", no_argument, 0, 'Y'},
-            {"no-minor", no_argument, 0, 'I'},
-            {"slope-mode", no_argument, 0, 'E'},
-            {"alt-autoscale", no_argument, 0, 'A'},
-            {"alt-autoscale-min", no_argument, 0, 'J'},
-            {"alt-autoscale-max", no_argument, 0, 'M'},
-            {"no-gridfit", no_argument, 0, 'N'},
-            {"units-exponent", required_argument, 0, 'X'},
-            {"units-length", required_argument, 0, 'L'},
-            {"units", required_argument, 0, LONGOPT_UNITS_SI},
-            {"step", required_argument, 0, 'S'},
-            {"tabwidth", required_argument, 0, 'T'},
-            {"font-render-mode", required_argument, 0, 'R'},
-            {"font-smoothing-threshold", required_argument, 0, 'B'},
-            {"watermark", required_argument, 0, 'W'},
-            {"alt-y-mrtg", no_argument, 0, 1000},   /* this has no effect it is just here to save old apps from crashing when they use it */
-            {0, 0, 0, 0}
-        };
         int       option_index = 0;
         int       opt;
         int       col_start, col_end;
 
         opt = getopt_long(argc, argv,
-                          "s:e:x:y:v:w:h:D:iu:l:rb:oc:n:m:t:f:a:I:zgjFYAMEX:L:S:T:NR:B:W:",
+                          "Aa:B:b:c:Dd:Ee:Ff:G:gh:IiJjL:l:Nn:Bb:oPR:rS:s:T:t:u:v:W:w:X:x:Yy:z",
                           long_options, &option_index);
-
         if (opt == EOF)
             break;
-
         switch (opt) {
         case 'I':
             im->extra_flags |= NOMINOR;
@@ -3689,9 +4220,36 @@ void rrd_graph_options(
         case 'g':
             im->extra_flags |= NOLEGEND;
             break;
+        case 1005:
+            if (strcmp(optarg, "north") == 0) {
+                im->legendposition = NORTH;
+            } else if (strcmp(optarg, "west") == 0) {
+                im->legendposition = WEST;
+            } else if (strcmp(optarg, "south") == 0) {
+                im->legendposition = SOUTH;
+            } else if (strcmp(optarg, "east") == 0) {
+                im->legendposition = EAST;
+            } else {
+                rrd_set_error("unknown legend-position '%s'", optarg);
+                return;
+            }
+            break;
+        case 1006:
+            if (strcmp(optarg, "topdown") == 0) {
+                im->legenddirection = TOP_DOWN;
+            } else if (strcmp(optarg, "bottomup") == 0) {
+                im->legenddirection = BOTTOM_UP;
+            } else {
+                rrd_set_error("unknown legend-position '%s'", optarg);
+                return;
+            }
+            break;
         case 'F':
             im->extra_flags |= FORCE_RULES_LEGEND;
             break;
+        case 1001:
+            im->extra_flags |= NO_RRDTOOL_TAG;
+            break;
         case LONGOPT_UNITS_SI:
             if (im->extra_flags & FORCE_UNITS) {
                 rrd_set_error("--units can only be used once!");
@@ -3720,14 +4278,17 @@ void rrd_graph_options(
         case 'N':
             im->gridfit = 0;
             break;
+        case 'P':
+            im->with_markup = 1;
+            break;
         case 's':
-            if ((parsetime_error = parsetime(optarg, &start_tv))) {
+            if ((parsetime_error = rrd_parsetime(optarg, &start_tv))) {
                 rrd_set_error("start time: %s", parsetime_error);
                 return;
             }
             break;
         case 'e':
-            if ((parsetime_error = parsetime(optarg, &end_tv))) {
+            if ((parsetime_error = rrd_parsetime(optarg, &end_tv))) {
                 rrd_set_error("end time: %s", parsetime_error);
                 return;
             }
@@ -3737,7 +4298,6 @@ void rrd_graph_options(
                 im->draw_x_grid = 0;
                 break;
             };
-
             if (sscanf(optarg,
                        "%10[A-Z]:%ld:%10[A-Z]:%ld:%10[A-Z]:%ld:%ld:%n",
                        scan_gtm,
@@ -3750,15 +4310,17 @@ void rrd_graph_options(
                 strncpy(im->xlab_form, optarg + stroff,
                         sizeof(im->xlab_form) - 1);
                 im->xlab_form[sizeof(im->xlab_form) - 1] = '\0';
-                if ((int) (im->xlab_user.gridtm = tmt_conv(scan_gtm)) == -1) {
+                if ((int)
+                    (im->xlab_user.gridtm = tmt_conv(scan_gtm)) == -1) {
                     rrd_set_error("unknown keyword %s", scan_gtm);
                     return;
-                } else if ((int) (im->xlab_user.mgridtm = tmt_conv(scan_mtm))
+                } else if ((int)
+                           (im->xlab_user.mgridtm = tmt_conv(scan_mtm))
                            == -1) {
                     rrd_set_error("unknown keyword %s", scan_mtm);
                     return;
-                } else if ((int) (im->xlab_user.labtm = tmt_conv(scan_ltm)) ==
-                           -1) {
+                } else if ((int)
+                           (im->xlab_user.labtm = tmt_conv(scan_ltm)) == -1) {
                     rrd_set_error("unknown keyword %s", scan_ltm);
                     return;
                 }
@@ -3775,7 +4337,6 @@ void rrd_graph_options(
                 im->draw_y_grid = 0;
                 break;
             };
-
             if (sscanf(optarg, "%lf:%d", &im->ygridstep, &im->ylabfact) == 2) {
                 if (im->ygridstep <= 0) {
                     rrd_set_error("grid step must be > 0");
@@ -3789,6 +4350,48 @@ void rrd_graph_options(
                 return;
             }
             break;
+        case 1007:
+            im->draw_3d_border = atoi(optarg);
+            break;
+        case 1008: /* grid-dash */
+            if(sscanf(optarg,
+                      "%lf:%lf",
+                      &im->grid_dash_on,
+                      &im->grid_dash_off) != 2) {
+                rrd_set_error("expected grid-dash format float:float");
+                return;
+            }
+            break;   
+        case 1009: /* enable dynamic labels */
+            im->dynamic_labels = 1;
+            break;         
+        case 1002: /* right y axis */
+
+            if(sscanf(optarg,
+                      "%lf:%lf",
+                      &im->second_axis_scale,
+                      &im->second_axis_shift) == 2) {
+                if(im->second_axis_scale==0){
+                    rrd_set_error("the second_axis_scale  must not be 0");
+                    return;
+                }
+            } else {
+                rrd_set_error("invalid right-axis format expected scale:shift");
+                return;
+            }
+            break;
+        case 1003:
+            strncpy(im->second_axis_legend,optarg,150);
+            im->second_axis_legend[150]='\0';
+            break;
+        case 1004:
+            if (bad_format(optarg)){
+                rrd_set_error("use either %le or %lf formats");
+                return;
+            }
+            strncpy(im->second_axis_format,optarg,150);
+            im->second_axis_format[150]='\0';
+            break;
         case 'v':
             strncpy(im->ylegend, optarg, 150);
             im->ylegend[150] = '\0';
@@ -3827,7 +4430,7 @@ void rrd_graph_options(
             im->extra_flags |= FULL_SIZE_MODE;
             break;
         case 'i':
-            im->canvas->interlaced = 1;
+            /* interlaced png not supported at the moment */
             break;
         case 'r':
             im->rigid = 1;
@@ -3836,7 +4439,8 @@ void rrd_graph_options(
             im->imginfo = optarg;
             break;
         case 'a':
-            if ((int) (im->canvas->imgformat = if_conv(optarg)) == -1) {
+            if ((int)
+                (im->imgformat = if_conv(optarg)) == -1) {
                 rrd_set_error("unsupported graphics format '%s'", optarg);
                 return;
             }
@@ -3847,7 +4451,6 @@ void rrd_graph_options(
         case 'E':
             im->slopemode = 1;
             break;
-
         case 'o':
             im->logarithmic = 1;
             break;
@@ -3860,15 +4463,21 @@ void rrd_graph_options(
 
                 switch (col_len) {
                 case 3:
-                    color = (((color & 0xF00) * 0x110000) |
-                             ((color & 0x0F0) * 0x011000) |
-                             ((color & 0x00F) * 0x001100) | 0x000000FF);
+                    color =
+                        (((color & 0xF00) * 0x110000) | ((color & 0x0F0) *
+                                                         0x011000) |
+                         ((color & 0x00F)
+                          * 0x001100)
+                         | 0x000000FF);
                     break;
                 case 4:
-                    color = (((color & 0xF000) * 0x11000) |
-                             ((color & 0x0F00) * 0x01100) |
-                             ((color & 0x00F0) * 0x00110) |
-                             ((color & 0x000F) * 0x00011)
+                    color =
+                        (((color & 0xF000) *
+                          0x11000) | ((color & 0x0F00) *
+                                      0x01100) | ((color &
+                                                   0x00F0) *
+                                                  0x00110) |
+                         ((color & 0x000F) * 0x00011)
                         );
                     break;
                 case 6:
@@ -3881,7 +4490,7 @@ void rrd_graph_options(
                     return;
                 }
                 if ((ci = grc_conv(col_nam)) != -1) {
-                    im->graph_col[ci] = color;
+                    im->graph_col[ci] = gfx_hex_to_col(color);
                 } else {
                     rrd_set_error("invalid color name '%s'", col_nam);
                     return;
@@ -3894,20 +4503,29 @@ void rrd_graph_options(
         case 'n':{
             char      prop[15];
             double    size = 1;
-            char      font[1024] = "";
+            int       end;
 
-            if (sscanf(optarg, "%10[A-Z]:%lf:%1000s", prop, &size, font) >= 2) {
+            if (sscanf(optarg, "%10[A-Z]:%lf%n", prop, &size, &end) >= 2) {
                 int       sindex, propidx;
 
                 if ((sindex = text_prop_conv(prop)) != -1) {
-                    for (propidx = sindex; propidx < TEXT_PROP_LAST;
-                         propidx++) {
+                    for (propidx = sindex;
+                         propidx < TEXT_PROP_LAST; propidx++) {
                         if (size > 0) {
-                            im->text_prop[propidx].size = size;
+                            rrd_set_font_desc(im,propidx,NULL,size);
                         }
-                        if (strlen(font) > 0) {
-                            strcpy(im->text_prop[propidx].font, font);
+                        if ((int) strlen(optarg) > end+2) {
+                            if (optarg[end] == ':') {
+                                rrd_set_font_desc(im,propidx,optarg + end + 1,0);
+                            } else {
+                                rrd_set_error
+                                    ("expected : after font size in '%s'",
+                                     optarg);
+                                return;
+                            }
                         }
+                        /* only run the for loop for DEFAULT (0) for
+                           all others, we break here. woodo programming */
                         if (propidx == sindex && sindex != 0)
                             break;
                     }
@@ -3922,8 +4540,8 @@ void rrd_graph_options(
             break;
         }
         case 'm':
-            im->canvas->zoom = atof(optarg);
-            if (im->canvas->zoom <= 0.0) {
+            im->zoom = atof(optarg);
+            if (im->zoom <= 0.0) {
                 rrd_set_error("zoom factor must be > 0");
                 return;
             }
@@ -3932,29 +4550,62 @@ void rrd_graph_options(
             strncpy(im->title, optarg, 150);
             im->title[150] = '\0';
             break;
-
         case 'R':
+            if (strcmp(optarg, "normal") == 0) {
+                cairo_font_options_set_antialias
+                    (im->font_options, CAIRO_ANTIALIAS_GRAY);
+                cairo_font_options_set_hint_style
+                    (im->font_options, CAIRO_HINT_STYLE_FULL);
+            } else if (strcmp(optarg, "light") == 0) {
+                cairo_font_options_set_antialias
+                    (im->font_options, CAIRO_ANTIALIAS_GRAY);
+                cairo_font_options_set_hint_style
+                    (im->font_options, CAIRO_HINT_STYLE_SLIGHT);
+            } else if (strcmp(optarg, "mono") == 0) {
+                cairo_font_options_set_antialias
+                    (im->font_options, CAIRO_ANTIALIAS_NONE);
+                cairo_font_options_set_hint_style
+                    (im->font_options, CAIRO_HINT_STYLE_FULL);
+            } else {
+                rrd_set_error("unknown font-render-mode '%s'", optarg);
+                return;
+            }
+            break;
+        case 'G':
             if (strcmp(optarg, "normal") == 0)
-                im->canvas->aa_type = AA_NORMAL;
-            else if (strcmp(optarg, "light") == 0)
-                im->canvas->aa_type = AA_LIGHT;
+                im->graph_antialias = CAIRO_ANTIALIAS_GRAY;
             else if (strcmp(optarg, "mono") == 0)
-                im->canvas->aa_type = AA_NONE;
+                im->graph_antialias = CAIRO_ANTIALIAS_NONE;
             else {
-                rrd_set_error("unknown font-render-mode '%s'", optarg);
+                rrd_set_error("unknown graph-render-mode '%s'", optarg);
                 return;
             }
             break;
-
         case 'B':
-            im->canvas->font_aa_threshold = atof(optarg);
+            /* not supported curently */
             break;
-
         case 'W':
             strncpy(im->watermark, optarg, 100);
             im->watermark[99] = '\0';
             break;
+        case 'd':
+        {
+            if (im->daemon_addr != NULL)
+            {
+                rrd_set_error ("You cannot specify --daemon "
+                        "more than once.");
+                return;
+            }
+
+            im->daemon_addr = strdup(optarg);
+            if (im->daemon_addr == NULL)
+            {
+              rrd_set_error("strdup failed");
+              return;
+            }
 
+            break;
+        }
         case '?':
             if (optopt != 0)
                 rrd_set_error("unknown option '%c'", optopt);
@@ -3962,33 +4613,39 @@ void rrd_graph_options(
                 rrd_set_error("unknown option '%s'", argv[optind - 1]);
             return;
         }
-    }
+    } /* while (1) */
 
-    if (optind >= argc) {
-        rrd_set_error("missing filename");
-        return;
+    {   /* try to connect to rrdcached */
+        int status = rrdc_connect(im->daemon_addr);
+        if (status != 0) return;
     }
 
-    if (im->logarithmic == 1 && im->minval <= 0) {
+    pango_cairo_context_set_font_options(pango_layout_get_context(im->layout), im->font_options);
+    pango_layout_context_changed(im->layout);
+
+
+
+    if (im->logarithmic && im->minval <= 0) {
         rrd_set_error
             ("for a logarithmic yaxis you must specify a lower-limit > 0");
         return;
     }
 
-    if (proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
-        /* error string is set in parsetime.c */
+    if (rrd_proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
+        /* error string is set in rrd_parsetime.c */
         return;
     }
 
     if (start_tmp < 3600 * 24 * 365 * 10) {
-        rrd_set_error("the first entry to fetch should be after 1980 (%ld)",
-                      start_tmp);
+        rrd_set_error
+            ("the first entry to fetch should be after 1980 (%ld)",
+             start_tmp);
         return;
     }
 
     if (end_tmp < start_tmp) {
-        rrd_set_error("start (%ld) should be less than end (%ld)",
-                      start_tmp, end_tmp);
+        rrd_set_error
+            ("start (%ld) should be less than end (%ld)", start_tmp, end_tmp);
         return;
     }
 
@@ -3998,7 +4655,8 @@ void rrd_graph_options(
 }
 
 int rrd_graph_color(
-    image_desc_t *im,
+    image_desc_t
+    *im,
     char *var,
     char *err,
     int optional)
@@ -4016,14 +4674,13 @@ int rrd_graph_color(
     } else {
         int       n = 0;
         char     *rest;
-        gfx_color_t col;
+        long unsigned int col;
 
         rest = strstr(color, ":");
         if (rest != NULL)
             n = rest - color;
         else
             n = strlen(color);
-
         switch (n) {
         case 7:
             sscanf(color, "#%6lx%n", &col, &n);
@@ -4040,7 +4697,7 @@ int rrd_graph_color(
         }
         if (rrd_test_error())
             return 0;
-        gdp->col = col;
+        gdp->col = gfx_hex_to_col(col);
         return n;
     }
 }
@@ -4059,11 +4716,9 @@ int bad_format(
             /* line cannot end with percent char */
             if (*ptr == '\0')
                 return 1;
-
             /* '%s', '%S' and '%%' are allowed */
             if (*ptr == 's' || *ptr == 'S' || *ptr == '%')
                 ptr++;
-
             /* %c is allowed (but use only with vdef!) */
             else if (*ptr == 'c') {
                 ptr++;
@@ -4075,7 +4730,6 @@ int bad_format(
                 /* optional padding character */
                 if (*ptr == ' ' || *ptr == '+' || *ptr == '-')
                     ptr++;
-
                 /* This should take care of 'm.n' with all three optional */
                 while (*ptr >= '0' && *ptr <= '9')
                     ptr++;
@@ -4083,7 +4737,6 @@ int bad_format(
                     ptr++;
                 while (*ptr >= '0' && *ptr <= '9')
                     ptr++;
-
                 /* Either 'le', 'lf' or 'lg' must follow here */
                 if (*ptr++ != 'l')
                     return 1;
@@ -4100,10 +4753,9 @@ int bad_format(
 
 
 int vdef_parse(
-    gdes,
-    str)
-    struct graph_desc_t *gdes;
-    const char *const str;
+    struct graph_desc_t
+    *gdes,
+    const char *const str)
 {
     /* A VDEF currently is either "func" or "param,func"
      * so the parsing is rather simple.  Change if needed.
@@ -4122,17 +4774,22 @@ int vdef_parse(
         if (n == (int) strlen(str)) {   /* matched */
             param = DNAN;
         } else {
-            rrd_set_error("Unknown function string '%s' in VDEF '%s'", str,
-                          gdes->vname);
+            rrd_set_error
+                ("Unknown function string '%s' in VDEF '%s'",
+                 str, gdes->vname);
             return -1;
         }
     }
     if (!strcmp("PERCENT", func))
         gdes->vf.op = VDEF_PERCENT;
+    else if (!strcmp("PERCENTNAN", func))
+        gdes->vf.op = VDEF_PERCENTNAN;
     else if (!strcmp("MAXIMUM", func))
         gdes->vf.op = VDEF_MAXIMUM;
     else if (!strcmp("AVERAGE", func))
         gdes->vf.op = VDEF_AVERAGE;
+    else if (!strcmp("STDEV", func))
+        gdes->vf.op = VDEF_STDEV;
     else if (!strcmp("MINIMUM", func))
         gdes->vf.op = VDEF_MINIMUM;
     else if (!strcmp("TOTAL", func))
@@ -4148,16 +4805,17 @@ int vdef_parse(
     else if (!strcmp("LSLCORREL", func))
         gdes->vf.op = VDEF_LSLCORREL;
     else {
-        rrd_set_error("Unknown function '%s' in VDEF '%s'\n", func,
-                      gdes->vname);
+        rrd_set_error
+            ("Unknown function '%s' in VDEF '%s'\n", func, gdes->vname);
         return -1;
     };
-
     switch (gdes->vf.op) {
     case VDEF_PERCENT:
+    case VDEF_PERCENTNAN:
         if (isnan(param)) { /* no parameter given */
-            rrd_set_error("Function '%s' needs parameter in VDEF '%s'\n",
-                          func, gdes->vname);
+            rrd_set_error
+                ("Function '%s' needs parameter in VDEF '%s'\n",
+                 func, gdes->vname);
             return -1;
         };
         if (param >= 0.0 && param <= 100.0) {
@@ -4165,13 +4823,15 @@ int vdef_parse(
             gdes->vf.val = DNAN;    /* undefined */
             gdes->vf.when = 0;  /* undefined */
         } else {
-            rrd_set_error("Parameter '%f' out of range in VDEF '%s'\n", param,
-                          gdes->vname);
+            rrd_set_error
+                ("Parameter '%f' out of range in VDEF '%s'\n",
+                 param, gdes->vname);
             return -1;
         };
         break;
     case VDEF_MAXIMUM:
     case VDEF_AVERAGE:
+    case VDEF_STDEV:
     case VDEF_MINIMUM:
     case VDEF_TOTAL:
     case VDEF_FIRST:
@@ -4184,8 +4844,9 @@ int vdef_parse(
             gdes->vf.val = DNAN;
             gdes->vf.when = 0;
         } else {
-            rrd_set_error("Function '%s' needs no parameter in VDEF '%s'\n",
-                          func, gdes->vname);
+            rrd_set_error
+                ("Function '%s' needs no parameter in VDEF '%s'\n",
+                 func, gdes->vname);
             return -1;
         };
         break;
@@ -4195,10 +4856,8 @@ int vdef_parse(
 
 
 int vdef_calc(
-    im,
-    gdi)
-    image_desc_t *im;
-    int gdi;
+    image_desc_t *im,
+    int gdi)
 {
     graph_desc_t *src, *dst;
     rrd_value_t *data;
@@ -4207,20 +4866,18 @@ int vdef_calc(
     dst = &im->gdes[gdi];
     src = &im->gdes[dst->vidx];
     data = src->data + src->ds;
-    steps = (src->end - src->start) / src->step;
 
+    steps = (src->end - src->start) / src->step;
 #if 0
-    printf("DEBUG: start == %lu, end == %lu, %lu steps\n", src->start,
-           src->end, steps);
+    printf
+        ("DEBUG: start == %lu, end == %lu, %lu steps\n",
+         src->start, src->end, steps);
 #endif
-
     switch (dst->vf.op) {
     case VDEF_PERCENT:{
         rrd_value_t *array;
         int       field;
-
-
-        if ((array = malloc(steps * sizeof(double))) == NULL) {
+        if ((array = (rrd_value_t*)malloc(steps * sizeof(double))) == NULL) {
             rrd_set_error("malloc VDEV_PERCENT");
             return -1;
         }
@@ -4228,18 +4885,45 @@ int vdef_calc(
             array[step] = data[step * src->ds_cnt];
         }
         qsort(array, step, sizeof(double), vdef_percent_compar);
-
-        field = (steps - 1) * dst->vf.param / 100;
+        field = round((dst->vf.param * (double)(steps - 1)) / 100.0);
         dst->vf.val = array[field];
         dst->vf.when = 0;   /* no time component */
         free(array);
 #if 0
         for (step = 0; step < steps; step++)
-            printf("DEBUG: %3li:%10.2f %c\n", step, array[step],
-                   step == field ? '*' : ' ');
+            printf("DEBUG: %3li:%10.2f %c\n",
+                   step, array[step], step == field ? '*' : ' ');
 #endif
     }
         break;
+    case VDEF_PERCENTNAN:{
+        rrd_value_t *array;
+        int       field;
+       /* count number of "valid" values */
+       int nancount=0;
+       for (step = 0; step < steps; step++) {
+         if (!isnan(data[step * src->ds_cnt])) { nancount++; }
+       }
+       /* and allocate it */
+        if ((array = (rrd_value_t*)malloc(nancount * sizeof(double))) == NULL) {
+            rrd_set_error("malloc VDEV_PERCENT");
+            return -1;
+        }
+       /* and fill it in */
+       field=0;
+        for (step = 0; step < steps; step++) {
+           if (!isnan(data[step * src->ds_cnt])) {
+                array[field] = data[step * src->ds_cnt];
+               field++;
+            }
+        }
+        qsort(array, nancount, sizeof(double), vdef_percent_compar);
+        field = round( dst->vf.param * (double)(nancount - 1) / 100.0);
+        dst->vf.val = array[field];
+        dst->vf.when = 0;   /* no time component */
+        free(array);
+    }
+        break;
     case VDEF_MAXIMUM:
         step = 0;
         while (step != steps && isnan(data[step * src->ds_cnt]))
@@ -4262,9 +4946,11 @@ int vdef_calc(
         }
         break;
     case VDEF_TOTAL:
+    case VDEF_STDEV:
     case VDEF_AVERAGE:{
         int       cnt = 0;
         double    sum = 0.0;
+        double    average = 0.0;
 
         for (step = 0; step < steps; step++) {
             if (finite(data[step * src->ds_cnt])) {
@@ -4276,9 +4962,19 @@ int vdef_calc(
             if (dst->vf.op == VDEF_TOTAL) {
                 dst->vf.val = sum * src->step;
                 dst->vf.when = 0;   /* no time component */
-            } else {
+            } else if (dst->vf.op == VDEF_AVERAGE) {
                 dst->vf.val = sum / cnt;
                 dst->vf.when = 0;   /* no time component */
+            } else {
+                average = sum / cnt;
+                sum = 0.0;
+                for (step = 0; step < steps; step++) {
+                    if (finite(data[step * src->ds_cnt])) {
+                        sum += pow((data[step * src->ds_cnt] - average), 2.0);
+                    };
+                }
+                dst->vf.val = pow(sum / cnt, 0.5);
+                dst->vf.when = 0;   /* no time component */
             };
         } else {
             dst->vf.val = DNAN;
@@ -4352,7 +5048,6 @@ int vdef_calc(
         SUMxy = 0;
         SUMxx = 0;
         SUMyy = 0;
-
         for (step = 0; step < steps; step++) {
             if (finite(data[step * src->ds_cnt])) {
                 cnt++;
@@ -4368,12 +5063,9 @@ int vdef_calc(
         y_intercept = (SUMy - slope * SUMx) / cnt;
         correl =
             (SUMxy -
-             (SUMx * SUMy) / cnt) / sqrt((SUMxx -
-                                          (SUMx * SUMx) / cnt) * (SUMyy -
-                                                                  (SUMy *
-                                                                   SUMy) /
-                                                                  cnt));
-
+             (SUMx * SUMy) / cnt) /
+            sqrt((SUMxx -
+                  (SUMx * SUMx) / cnt) * (SUMyy - (SUMy * SUMy) / cnt));
         if (cnt) {
             if (dst->vf.op == VDEF_LSLSLOPE) {
                 dst->vf.val = slope;
@@ -4385,7 +5077,6 @@ int vdef_calc(
                 dst->vf.val = correl;
                 dst->vf.when = 0;
             };
-
         } else {
             dst->vf.val = DNAN;
             dst->vf.when = 0;
@@ -4398,9 +5089,10 @@ int vdef_calc(
 
 /* NaN < -INF < finite_values < INF */
 int vdef_percent_compar(
-    a,
-    b)
-    const void *a, *b;
+    const void
+    *a,
+    const void
+    *b)
 {
     /* Equality is not returned; this doesn't hurt except
      * (maybe) for a little performance.
@@ -4411,7 +5103,6 @@ int vdef_percent_compar(
         return -1;
     if (isnan(*(double *) b))
         return 1;
-
     /* NaN doesn't reach this part so INF and -INF are extremes.
      * The sign from isinf() is compatible with the sign we return
      */
@@ -4419,10 +5110,21 @@ int vdef_percent_compar(
         return isinf(*(double *) a);
     if (isinf(*(double *) b))
         return isinf(*(double *) b);
-
     /* If we reach this, both values must be finite */
     if (*(double *) a < *(double *) b)
         return -1;
     else
         return 1;
 }
+
+void grinfo_push(
+    image_desc_t *im,
+    char *key,
+    rrd_info_type_t type,
+    rrd_infoval_t value)
+{
+    im->grinfo_current = rrd_info_push(im->grinfo_current, key, type, value);
+    if (im->grinfo == NULL) {
+        im->grinfo = im->grinfo_current;
+    }
+}