support for dashed lines in graphs
[rrdtool.git] / src / rrd_graph.c
index 191404e..ac0f3ea 100644 (file)
@@ -1,5 +1,5 @@
 /****************************************************************************
- * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ * RRDtool 1.2.99907080300  Copyright by Tobi Oetiker, 1997-2007
  ****************************************************************************
  * rrd__graph.c  produce graphs from data in rrdfiles
  ****************************************************************************/
@@ -140,16 +140,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      */
 };
 
 
@@ -225,81 +226,79 @@ 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 (-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 (-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 (-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)
+    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;
+    return -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);
+    return -1;
 }
 
 
@@ -309,6 +308,7 @@ int im_free(
     image_desc_t *im)
 {
     unsigned long i, ii;
+    cairo_status_t status = 0;
 
     if (im == NULL)
         return 0;
@@ -322,11 +322,27 @@ 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->surface)
+        cairo_surface_destroy(im->surface);
+    if (status)
+        fprintf(stderr, "OOPS: Cairo has issuesm it can't even die: %s\n",
+                cairo_status_to_string(status));
+
     return 0;
 }
 
@@ -520,6 +536,7 @@ void expand_range(
 #endif
 }
 
+
 void apply_gridfit(
     image_desc_t *im)
 {
@@ -704,6 +721,7 @@ void reduce_data(
                 else {
                     switch (cf) {
                     case CF_HWPREDICT:
+                    case CF_MHWPREDICT:
                     case CF_DEVSEASONAL:
                     case CF_DEVPREDICT:
                     case CF_SEASONAL:
@@ -729,6 +747,7 @@ void reduce_data(
             } else {
                 switch (cf) {
                 case CF_HWPREDICT:
+                case CF_MHWPREDICT:
                 case CF_DEVSEASONAL:
                 case CF_DEVPREDICT:
                 case CF_SEASONAL:
@@ -1100,6 +1119,39 @@ int data_calc(
     return 0;
 }
 
+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)
@@ -1212,8 +1264,8 @@ int data_proc(
     }
 
     /* adjust min and max values */
+    /* 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)
@@ -1229,19 +1281,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;
         }
     }
@@ -1425,6 +1482,7 @@ int print_calc(
 
                     switch (im->gdes[i].cf) {
                     case CF_HWPREDICT:
+                    case CF_MHWPREDICT:
                     case CF_DEVPREDICT:
                     case CF_DEVSEASONAL:
                     case CF_SEASONAL:
@@ -1530,6 +1588,7 @@ int print_calc(
             graphelement = 1;
             break;
         case GF_COMMENT:
+        case GF_TEXTALIGN:
         case GF_DEF:
         case GF_CDEF:
         case GF_VDEF:
@@ -1566,6 +1625,7 @@ int leg_place(
     int       glue = 0;
     int       i, ii, mark = 0;
     char      prt_fctn; /*special printfunctions */
+    char      default_txtalign = TXA_JUSTIFIED; /*default line orientation */
     int      *legspace;
 
     if (!(im->extra_flags & NOLEGEND) & !(im->extra_flags & ONLY_GRAPH)) {
@@ -1583,6 +1643,10 @@ int leg_place(
 
             /* 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
@@ -1620,30 +1684,32 @@ int leg_place(
                 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) {
+
+                /* 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,
+                fill += gfx_get_text_width(im, fill + border,
                                            im->text_prop[TEXT_PROP_LEGEND].
                                            font,
                                            im->text_prop[TEXT_PROP_LEGEND].
                                            size, im->tabwidth,
-                                           im->gdes[i].legend, 0);
+                                           im->gdes[i].legend);
                 leg_c++;
             } else {
                 legspace[i] = 0;
@@ -1652,10 +1718,25 @@ 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 (i == im->gdes_c - 1 || fill > im->ximg - 2 * border) {
+                    /* 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 (leg_c > 1) {
@@ -1663,11 +1744,10 @@ int leg_place(
                         i--;
                         fill = fill_last;
                         leg_c--;
-                        prt_fctn = 'j';
-                    } else {
-                        prt_fctn = 'l';
                     }
-
+                }
+                if (leg_c == 1 && prt_fctn == 'j') {
+                    prt_fctn = 'l';
                 }
             }
 
@@ -1690,12 +1770,12 @@ int leg_place(
                     im->gdes[ii].leg_x = leg_x;
                     im->gdes[ii].leg_y = leg_y;
                     leg_x +=
-                        gfx_get_text_width(im->canvas, leg_x,
+                        gfx_get_text_width(im, leg_x,
                                            im->text_prop[TEXT_PROP_LEGEND].
                                            font,
                                            im->text_prop[TEXT_PROP_LEGEND].
                                            size, im->tabwidth,
-                                           im->gdes[ii].legend, 0)
+                                           im->gdes[ii].legend)
                         + legspace[ii]
                         + glue;
                 }
@@ -1900,25 +1980,37 @@ int draw_horizontal_grid(
                 }
                 nlabels++;
 
-                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);
+                gfx_text(im,
+                         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_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);
 
             }
         }
@@ -1947,38 +2039,10 @@ 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;
+/* 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. */
 
-    return 0;
-}
 
 /* logaritmic horizontal grid */
 int horizontal_log_grid(
@@ -2056,11 +2120,18 @@ int horizontal_log_grid(
             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) {
@@ -2084,13 +2155,12 @@ int horizontal_log_grid(
             sprintf(graph_label, "%3.0f %c", pvalue, symbol);
         } 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);
+        gfx_text(im,
+                 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);
 
         /* minor grid */
         if (mid < 4 && exfrac == 1) {
@@ -2119,11 +2189,17 @@ int horizontal_log_grid(
                     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) {
@@ -2136,11 +2212,17 @@ int horizontal_log_grid(
                     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);
             }
         }
 
@@ -2178,11 +2260,15 @@ int horizontal_log_grid(
                 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 */
@@ -2197,11 +2283,15 @@ int horizontal_log_grid(
                 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);
         }
     }
 
@@ -2272,9 +2362,13 @@ void vertical_grid(
             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);
 
         }
     }
@@ -2290,9 +2384,13 @@ 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 */
@@ -2313,13 +2411,13 @@ void vertical_grid(
 #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,
+                 im->text_prop[TEXT_PROP_AXIS].size, im->tabwidth, 0.0,
+                 GFX_H_CENTER, GFX_V_TOP, graph_label);
 
     }
 
@@ -2330,29 +2428,33 @@ 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_line(im, 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, 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_close_path(im);
 
-    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_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);
+
 
 }
 
@@ -2362,23 +2464,23 @@ void grid_paint(
     long      i;
     int       res = 0;
     double    X0, Y0;   /* points for filled graph and more */
-    gfx_node_t *node;
+    struct gfx_color_t water_color;
 
     /* 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 ); */
+    gfx_new_area(im, 0, im->yimg,
+                 2, im->yimg - 2, 2, 2, im->graph_col[GRC_SHADEA]);
+    gfx_add_point(im, im->ximg - 2, 2);
+    gfx_add_point(im, im->ximg, 0);
+    gfx_add_point(im, 0, 0);
+    gfx_close_path(im);
+
+    gfx_new_area(im, 2, im->yimg - 2,
+                 im->ximg - 2, im->yimg - 2,
+                 im->ximg - 2, 2, 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)
@@ -2395,47 +2497,48 @@ 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,
+                     im->text_prop[TEXT_PROP_AXIS].size, 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),
-                 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);
+    gfx_text(im,
+             10, (im->yorigin - im->ysize / 2),
+             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_CENTER, GFX_V_CENTER, im->ylegend);
 
     /* 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->ximg / 2, 6,
+             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_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");
+    water_color = im->graph_col[GRC_FONT];
+    water_color.alpha = 0.3;
+    gfx_text(im,
+             im->ximg - 4, 5,
+             water_color,
+             im->text_prop[TEXT_PROP_AXIS].font,
+             5.5, 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);
+        gfx_text(im,
+                 im->ximg / 2, im->yimg - 6,
+                 water_color,
+                 im->text_prop[TEXT_PROP_AXIS].font,
+                 5.5, im->tabwidth, 0,
+                 GFX_H_CENTER, GFX_V_BOTTOM, im->watermark);
     }
 
     /* graph labels */
@@ -2447,42 +2550,67 @@ void grid_paint(
             /* 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);
+            gfx_text(im, 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);
             /* 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;
+                double    boxH, boxV;
+                double    X1, Y1;
+
 
-                boxH = gfx_get_text_width(im->canvas, 0,
+                boxH = gfx_get_text_width(im, 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;
+                                          size, im->tabwidth, "o") * 1.2;
+                boxV = boxH;
+
+                /* shift the box up a bit */
+                Y0 -= boxV * 0.4;
 
                 /* 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);
+
+                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);
             }
         }
     }
@@ -2510,7 +2638,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,67 +2649,10 @@ 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
-    )
+    int elements)
 {
     /* The actual size of the image to draw is determined from
      ** several sources.  The size given on the command line is
@@ -2590,14 +2661,7 @@ int graph_size_location(
      */
 
     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;
+        Yxlabel = 0, Xspacing = 15, Yspacing = 15, Ywatermark = 4;
 
     if (im->extra_flags & ONLY_GRAPH) {
         im->xorigin = 0;
@@ -2646,11 +2710,10 @@ int graph_size_location(
             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,
+            Xylabel = gfx_get_text_width(im, 0,
                                          im->text_prop[TEXT_PROP_AXIS].font,
                                          im->text_prop[TEXT_PROP_AXIS].size,
-                                         im->tabwidth,
-                                         "0", 0) * im->unitslength;
+                                         im->tabwidth, "0") * im->unitslength;
         }
     }
 
@@ -2668,14 +2731,6 @@ int graph_size_location(
 
         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
@@ -2687,12 +2742,6 @@ int graph_size_location(
         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;
 
         /* the length of the title should not influence with width of the graph
@@ -2710,15 +2759,20 @@ int graph_size_location(
          ** of the legend and the axis labels.
          */
 
-        /* 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->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;
+        }
 
-#ifdef WITH_PIECHART
-        /* if (im->yimg < Ypie) im->yimg = Ypie; * not sure what do about this */
-#endif
 
         /* remove title space *or* some padding above the graph from the main graph area */
         if (Ytitle) {
@@ -2765,14 +2819,6 @@ int graph_size_location(
             Xmain = im->xsize;
             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
@@ -2786,16 +2832,8 @@ int graph_size_location(
          */
         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 += Xspacing;
-#endif
 
         im->xorigin = Xspacing + Xylabel;
 
@@ -2822,10 +2860,6 @@ int graph_size_location(
 
         im->yimg = Ymain + Yxlabel;
 
-#ifdef WITH_PIECHART
-        if (im->yimg < Ypie)
-            im->yimg = Ypie;
-#endif
 
         im->yorigin = im->yimg - Yxlabel;
 
@@ -2851,36 +2885,42 @@ int graph_size_location(
         }
     }
 
-#if 0
-    if (Xlegend > im->ximg) {
-        im->ximg = Xlegend;
-        /* reposition Pie */
-    }
-#endif
-
-#ifdef WITH_PIECHART
-    /* The pie is placed in the upper right hand corner,
-     ** just below the title (if any) and with sufficient
-     ** padding.
-     */
-    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;
-    }
-#endif
-
     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_write_func_filehandle(
+    void *closure,
+    const unsigned char *data,
+    unsigned int length)
+{
+    if (fwrite(data, length, 1, closure) != 1)
+        return CAIRO_STATUS_WRITE_ERROR;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t cairo_copy_to_buffer(
+    void *closure,
+    const unsigned char *data,
+    unsigned int length)
+{
+    image_desc_t *im = closure;
+
+    im->rendered_image =
+        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,
@@ -2889,16 +2929,12 @@ int graph_paint(
     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;
 
+    PangoFontMap *font_map = pango_cairo_font_map_get_default();
+
+
     /* if we are lazy and there is nothing to PRINT ... quit now */
     if (lazy && im->prt_c == 0)
         return 0;
@@ -2912,15 +2948,6 @@ int graph_paint(
     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
@@ -2930,28 +2957,14 @@ int graph_paint(
     i = print_calc(im, calcpr);
     if (i < 0)
         return -1;
-    if (((i == 0)
-#ifdef WITH_PIECHART
-         && (piechart == 0)
-#endif
-        ) || lazy)
+    if ((i == 0) || lazy)
         return 0;
 
-#ifdef WITH_PIECHART
-    /* If there's only the pie chart to draw, signal this */
-    if (i == 0)
-        piechart = 2;
-#endif
-
 /**************************************************************
  *** 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;
 
     /* get actual drawing data and find min and max values */
@@ -2969,44 +2982,79 @@ int graph_paint(
     if (!calc_horizontal_grid(im))
         return -1;
 
-    if (im->gridfit)
-        apply_gridfit(im);
+    /* 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 */
+    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_copy_to_buffer, 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_copy_to_buffer, 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_copy_to_buffer, im,
+                                                  im->ximg * im->zoom,
+                                                  im->yimg * im->zoom);
+        cairo_svg_surface_restrict_to_version(im->surface,
+                                              CAIRO_SVG_VERSION_1_1);
+        break;
+    };
+    im->cr = cairo_create(im->surface);
+    pango_cairo_font_map_set_resolution(PANGO_CAIRO_FONT_MAP(font_map), 100);
+    cairo_set_antialias(im->cr, im->graph_antialias);
+    cairo_scale(im->cr, im->zoom, im->zoom);
 
-    node = gfx_new_area(im->canvas,
-                        0, 0,
-                        0, im->yimg,
-                        im->ximg, im->yimg, im->graph_col[GRC_BACK]);
+    gfx_new_area(im,
+                 0, 0,
+                 0, im->yimg, im->ximg, im->yimg, im->graph_col[GRC_BACK]);
 
-    gfx_add_point(node, im->ximg, 0);
+    gfx_add_point(im, im->ximg, 0);
+    gfx_close_path(im);
 
-#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
+    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]);
 
-#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
+    gfx_add_point(im, im->xorigin, im->yorigin - im->ysize);
+    gfx_close_path(im);
+
+    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) {
@@ -3016,6 +3064,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:
@@ -3026,20 +3075,20 @@ int graph_paint(
                 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,
+                                 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,
+                                 im->xorigin + ii,
+                                 im->yorigin - (1 -
+                                                im->gdes[i].yrule) *
+                                 im->ysize, 1.0, im->gdes[i].col);
 
                     }
                 }
@@ -3070,58 +3119,90 @@ int graph_paint(
                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++) {
@@ -3139,11 +3220,11 @@ int graph_paint(
                                                               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++;
@@ -3157,9 +3238,9 @@ int graph_paint(
                                                                      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--;
@@ -3173,10 +3254,11 @@ int graph_paint(
                                                                      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;
@@ -3185,10 +3267,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;
                         }
@@ -3206,8 +3284,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;
 
@@ -3245,20 +3322,6 @@ 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");
@@ -3267,13 +3330,6 @@ int graph_paint(
 
         }               /* switch */
     }
-#ifdef WITH_PIECHART
-    if (piechart == 2) {
-        im->draw_x_grid = 0;
-        im->draw_y_grid = 0;
-    }
-#endif
-
 
     /* grid_paint also does the text */
     if (!(im->extra_flags & ONLY_GRAPH))
@@ -3290,19 +3346,33 @@ int graph_paint(
         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);
+                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);
+                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;
@@ -3310,22 +3380,38 @@ 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;
+
+        if (strlen(im->graphfile) == 0) {
+            status =
+                cairo_surface_write_to_png_stream(im->surface,
+                                                  &cairo_copy_to_buffer, im);
+        } else if (strcmp(im->graphfile, "-") == 0) {
+            status =
+                cairo_surface_write_to_png_stream(im->surface,
+                                                  &cairo_write_func_filehandle,
+                                                  (void *) stdout);
+        } else {
+            status = cairo_surface_write_to_png(im->surface, im->graphfile);
+        }
+
+        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;
 }
 
@@ -3362,8 +3448,12 @@ 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].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;
@@ -3371,7 +3461,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;
@@ -3418,6 +3507,10 @@ int rrd_graph(
     image_desc_t im;
 
     rrd_graph_init(&im);
+
+    /* a dummy surface so that we can measure text sizes for placements */
+    im.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 10, 10);
+    im.cr = cairo_create(im.surface);
     im.graphhandle = stream;
 
     rrd_graph_options(argc, argv, &im);
@@ -3426,11 +3519,17 @@ int rrd_graph(
         return -1;
     }
 
+    if (optind >= argc) {
+        rrd_set_error("missing filename");
+        return -1;
+    }
+
     if (strlen(argv[optind]) >= MAXPATH) {
         rrd_set_error("filename (including path) too long");
         im_free(&im);
         return -1;
     }
+
     strncpy(im.graphfile, argv[optind], MAXPATH - 1);
     im.graphfile[MAXPATH - 1] = '\0';
 
@@ -3481,13 +3580,66 @@ int rrd_graph(
         }
 
         sprintf((*prdata)[0], im.imginfo, filename,
-                (long) (im.canvas->zoom * im.ximg),
-                (long) (im.canvas->zoom * im.yimg));
+                (long) (im.zoom * im.ximg), (long) (im.zoom * im.yimg));
     }
     im_free(&im);
     return 0;
 }
 
+/* a simplified version of the above that just creates the graph in memory 
+   and returns a pointer to it. */
+
+unsigned char *rrd_graph_in_memory(
+    int argc,
+    char **argv,
+    char ***prdata,
+    int *xsize,
+    int *ysize,
+    double *ymin,
+    double *ymax,
+    size_t * img_size)
+{
+    image_desc_t im;
+
+    rrd_graph_init(&im);
+
+    /* a dummy surface so that we can measure text sizes for placements */
+    im.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 10, 10);
+    im.cr = cairo_create(im.surface);
+
+    rrd_graph_options(argc, argv, &im);
+    if (rrd_test_error()) {
+        im_free(&im);
+        return NULL;
+    }
+
+    rrd_graph_script(argc, argv, &im, 1);
+    if (rrd_test_error()) {
+        im_free(&im);
+        return NULL;
+    }
+
+    /* Everything is now read and the actual work can start */
+
+    /* by not assigning a name to im.graphfile data will be written to
+       newly allocated memory on im.rendered_image ... */
+
+    (*prdata) = NULL;
+    if (graph_paint(&im, prdata) == -1) {
+        im_free(&im);
+        return NULL;
+    }
+
+    *xsize = im.ximg;
+    *ysize = im.yimg;
+    *ymin = im.minval;
+    *ymax = im.maxval;
+    *img_size = im.rendered_image_size;
+    im_free(&im);
+
+    return im.rendered_image;
+}
+
 void rrd_graph_init(
     image_desc_t *im)
 {
@@ -3510,6 +3662,8 @@ void rrd_graph_init(
     im->yimg = 0;
     im->xsize = 400;
     im->ysize = 100;
+    im->rendered_image_size = 0;
+    im->rendered_image = NULL;
     im->step = 0;
     im->ylegend[0] = '\0';
     im->title[0] = '\0';
@@ -3521,6 +3675,10 @@ void rrd_graph_init(
     im->forceleftspace = 0;
     im->symbol = ' ';
     im->viewfactor = 1.0;
+    im->imgformat = IF_PNG;
+    im->graphfile[0] = '\0';
+    im->cr = NULL;
+    im->surface = NULL;
     im->extra_flags = 0;
     im->rigid = 0;
     im->gridfit = 1;
@@ -3535,10 +3693,19 @@ void rrd_graph_init(
     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->tabwidth = 40.0;
+    im->zoom = 1;
+    im->font_options = cairo_font_options_create();
+    im->graph_antialias = CAIRO_ANTIALIAS_GRAY;
+
+    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];
@@ -3592,7 +3759,56 @@ void rrd_graph_options(
     time_t    start_tmp = 0, end_tmp = 0;
     long      long_tmp;
     struct rrd_time_value start_tv, end_tv;
-    gfx_color_t color;
+    long unsigned int color;
+    char     *old_locale = "";
+
+    /* 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
+    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'},
+        {"graph-render-mode", required_argument, 0, 'G'},
+        {"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}
+    };
 
     optind = 0;
     opterr = 0;         /* initialize getopt */
@@ -3600,54 +3816,7 @@ void rrd_graph_options(
     parsetime("end-24h", &start_tv);
     parsetime("now", &end_tv);
 
-    /* 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
-
     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;
@@ -3687,6 +3856,7 @@ void rrd_graph_options(
         case LONGOPT_UNITS_SI:
             if (im->extra_flags & FORCE_UNITS) {
                 rrd_set_error("--units can only be used once!");
+                setlocale(LC_NUMERIC, old_locale);
                 return;
             }
             if (strcmp(optarg, "si") == 0)
@@ -3704,10 +3874,14 @@ void rrd_graph_options(
             im->forceleftspace = 1;
             break;
         case 'T':
+            old_locale = setlocale(LC_NUMERIC, "C");
             im->tabwidth = atof(optarg);
+            setlocale(LC_NUMERIC, old_locale);
             break;
         case 'S':
+            old_locale = setlocale(LC_NUMERIC, "C");
             im->step = atoi(optarg);
+            setlocale(LC_NUMERIC, old_locale);
             break;
         case 'N':
             im->gridfit = 0;
@@ -3767,8 +3941,9 @@ void rrd_graph_options(
                 im->draw_y_grid = 0;
                 break;
             };
-
+            old_locale = setlocale(LC_NUMERIC, "C");
             if (sscanf(optarg, "%lf:%d", &im->ygridstep, &im->ylabfact) == 2) {
+                setlocale(LC_NUMERIC, old_locale);
                 if (im->ygridstep <= 0) {
                     rrd_set_error("grid step must be > 0");
                     return;
@@ -3777,6 +3952,7 @@ void rrd_graph_options(
                     return;
                 }
             } else {
+                setlocale(LC_NUMERIC, old_locale);
                 rrd_set_error("invalid y-grid format");
                 return;
             }
@@ -3786,10 +3962,14 @@ void rrd_graph_options(
             im->ylegend[150] = '\0';
             break;
         case 'u':
+            old_locale = setlocale(LC_NUMERIC, "C");
             im->maxval = atof(optarg);
+            setlocale(LC_NUMERIC, old_locale);
             break;
         case 'l':
+            old_locale = setlocale(LC_NUMERIC, "C");
             im->minval = atof(optarg);
+            setlocale(LC_NUMERIC, old_locale);
             break;
         case 'b':
             im->base = atol(optarg);
@@ -3819,7 +3999,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;
@@ -3828,7 +4008,7 @@ 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;
             }
@@ -3873,7 +4053,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;
@@ -3888,9 +4068,11 @@ void rrd_graph_options(
             double    size = 1;
             char      font[1024] = "";
 
+            old_locale = setlocale(LC_NUMERIC, "C");
             if (sscanf(optarg, "%10[A-Z]:%lf:%1000s", prop, &size, font) >= 2) {
                 int       sindex, propidx;
 
+                setlocale(LC_NUMERIC, old_locale);
                 if ((sindex = text_prop_conv(prop)) != -1) {
                     for (propidx = sindex; propidx < TEXT_PROP_LAST;
                          propidx++) {
@@ -3908,14 +4090,17 @@ void rrd_graph_options(
                     return;
                 }
             } else {
+                setlocale(LC_NUMERIC, old_locale);
                 rrd_set_error("invalid text property format");
                 return;
             }
             break;
         }
         case 'm':
-            im->canvas->zoom = atof(optarg);
-            if (im->canvas->zoom <= 0.0) {
+            old_locale = setlocale(LC_NUMERIC, "C");
+            im->zoom = atof(optarg);
+            setlocale(LC_NUMERIC, old_locale);
+            if (im->zoom <= 0.0) {
                 rrd_set_error("zoom factor must be > 0");
                 return;
             }
@@ -3926,20 +4111,38 @@ void rrd_graph_options(
             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':
@@ -3956,11 +4159,6 @@ void rrd_graph_options(
         }
     }
 
-    if (optind >= argc) {
-        rrd_set_error("missing filename");
-        return;
-    }
-
     if (im->logarithmic == 1 && im->minval <= 0) {
         rrd_set_error
             ("for a logarithmic yaxis you must specify a lower-limit > 0");
@@ -4008,7 +4206,7 @@ int rrd_graph_color(
     } else {
         int       n = 0;
         char     *rest;
-        gfx_color_t col;
+        long unsigned int col;
 
         rest = strstr(color, ":");
         if (rest != NULL)
@@ -4032,7 +4230,7 @@ int rrd_graph_color(
         }
         if (rrd_test_error())
             return 0;
-        gdp->col = col;
+        gdp->col = gfx_hex_to_col(col);
         return n;
     }
 }
@@ -4092,10 +4290,8 @@ 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.
@@ -4103,9 +4299,12 @@ int vdef_parse(
     double    param;
     char      func[30];
     int       n;
+    char     *old_locale;
 
     n = 0;
+    old_locale = setlocale(LC_NUMERIC, "C");
     sscanf(str, "%le,%29[A-Z]%n", &param, func, &n);
+    setlocale(LC_NUMERIC, old_locale);
     if (n == (int) strlen(str)) {   /* matched */
         ;
     } else {
@@ -4125,6 +4324,8 @@ int vdef_parse(
         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))
@@ -4164,6 +4365,7 @@ int vdef_parse(
         break;
     case VDEF_MAXIMUM:
     case VDEF_AVERAGE:
+    case VDEF_STDEV:
     case VDEF_MINIMUM:
     case VDEF_TOTAL:
     case VDEF_FIRST:
@@ -4187,10 +4389,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;
@@ -4254,9 +4454,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])) {
@@ -4268,9 +4470,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;
@@ -4390,9 +4602,8 @@ 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.