Changes in rrd_graph; see NEWS
[rrdtool.git] / src / rrd_graph.c
index 0f6f588..7908ad4 100644 (file)
@@ -174,13 +174,13 @@ enum gf_en gf_conv(char *string){
     conv_if(DEF,GF_DEF)
     conv_if(CDEF,GF_CDEF)
     conv_if(VDEF,GF_VDEF)
+    conv_if(PART,GF_PART)
     
     return (-1);
 }
 
 enum if_en if_conv(char *string){
     
-    conv_if(GIF,IF_GIF)
     conv_if(PNG,IF_PNG)
 
     return (-1);
@@ -803,7 +803,7 @@ printf("DEBUG: value from vdef is %f\n",im->gdes[ptr].vf.val);
                             * further save step size and data source
                             * count of this rra
                             */ 
-                           im->gdes[gdi].rpnp[rpi].data = im->gdes[ptr].data; 
+                            im->gdes[gdi].rpnp[rpi].data =  im->gdes[ptr].data + im->gdes[ptr].ds;
                            im->gdes[gdi].rpnp[rpi].step = im->gdes[ptr].step;
                            im->gdes[gdi].rpnp[rpi].ds_cnt = im->gdes[ptr].ds_cnt;
 
@@ -815,6 +815,17 @@ printf("DEBUG: value from vdef is %f\n",im->gdes[ptr].vf.val);
                    } /* if OP_VARIABLE */
                } /* loop through all rpi */
 
+                /* move the data pointers to the correct period */
+                for(rpi=0;im->gdes[gdi].rpnp[rpi].op != OP_END;rpi++){
+                    if(im->gdes[gdi].rpnp[rpi].op == OP_VARIABLE){
+                        long ptr = im->gdes[gdi].rpnp[rpi].ptr;
+                        if(im->gdes[gdi].start > im->gdes[ptr].start) {
+                            im->gdes[gdi].rpnp[rpi].data += im->gdes[gdi].rpnp[rpi].ds_cnt;
+                        }
+                     }
+                }
+        
+
                if(steparray == NULL){
                    rrd_set_error("rpn expressions without DEF"
                                " or CDEF variables are not supported");
@@ -937,6 +948,7 @@ data_proc( image_desc_t *im ){
            case GF_DEF:               
            case GF_CDEF:
            case GF_VDEF:
+           case GF_PART:
                break;
            }
        }
@@ -1217,6 +1229,7 @@ print_calc(image_desc_t *im, char ***prdata)
        case GF_DEF:
        case GF_CDEF:       
        case GF_VDEF:       
+       case GF_PART:
            break;
        }
     }
@@ -1267,7 +1280,7 @@ leg_place(image_desc_t *im)
           leg_cc--;
           im->gdes[i].legend[leg_cc]='\0';
        }
-       if (leg_cc != 0 ){          
+       if (leg_cc != 0 ){
           legspace[i]=(prt_fctn=='g' ? 0 : interleg);
           
           if (fill > 0){ 
@@ -1342,7 +1355,7 @@ leg_place(image_desc_t *im)
            mark = ii;
        }          
     }
-    im->ygif = leg_y+6;
+    im->ygif = leg_y;
     free(legspace);
   }
   return 0;
@@ -1641,6 +1654,8 @@ vertical_grid(
        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 (ti < im->start || ti > im->end) continue;
 
 #if HAVE_STRFTIME
        strftime(graph_label,99,im->xlab_user.stst,localtime(&tilab));
@@ -1703,24 +1718,27 @@ grid_paint(
     )
 {   
     long i;
-    int boxH=8, boxV=8;
     int res=0;
     double x0,x1,x2,x3,y0,y1,y2,y3; /* points for filled graph and more*/
     gfx_node_t *node;
-    
 
     /* draw 3d border */
-    node = gfx_new_area (canvas, 0,im->ygif, 0,0, im->xgif, 0,im->graph_col[GRC_SHADEA]);
+    node = gfx_new_area (canvas, 0,im->ygif,
+                                 2,im->ygif-2,
+                                 2,2,im->graph_col[GRC_SHADEA]);
     gfx_add_point( node , im->xgif - 2, 2 );
-    gfx_add_point( node , 2,2 );
-    gfx_add_point( node , 2,im->ygif-2 );
-    gfx_add_point( node , 0,im->ygif );
+    gfx_add_point( node , im->xgif, 0 );
+    gfx_add_point( node , 0,0 );
+/*    gfx_add_point( node , 0,im->ygif ); */
    
-    node =  gfx_new_area (canvas, 0,im->ygif, im->xgif,im->ygif, im->xgif,0,im->graph_col[GRC_SHADEB]);
-    gfx_add_point( node , im->xgif - 2, 2 );
-    gfx_add_point( node , im->xgif-2,im->ygif-2 );
-    gfx_add_point( node , 2,im->ygif-2 );
-    gfx_add_point( node , 0,im->ygif );
+    node =  gfx_new_area (canvas, 2,im->ygif-2,
+                                  im->xgif-2,im->ygif-2,
+                                  im->xgif - 2, 2,
+                                 im->graph_col[GRC_SHADEB]);
+    gfx_add_point( node ,   im->xgif,0);
+    gfx_add_point( node ,   im->xgif,im->ygif);
+    gfx_add_point( node ,   0,im->ygif);
+/*    gfx_add_point( node , 0,im->ygif ); */
    
    
     if (im->draw_x_grid == 1 )
@@ -1746,68 +1764,85 @@ grid_paint(
     }
 
     /* yaxis description */
-    gfx_new_text( canvas,
-                 7, (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, 270.0,
-                 GFX_H_CENTER, GFX_V_CENTER,
-                 im->ylegend);
+       #if 0
+           gfx_new_text( canvas,
+                         7, (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, 270.0,
+                         GFX_H_CENTER, GFX_V_CENTER,
+                         im->ylegend);
+       #else
+           /* horrible hack until we can actually print vertically */
+           {
+               int n;
+               int l=strlen(im->ylegend);
+               char s[2];
+               for (n=0;n<strlen(im->ylegend);n++) {
+                   s[0]=im->ylegend[n];
+                   s[1]='\0';
+                   gfx_new_text(canvas,7,im->text_prop[TEXT_PROP_AXIS].size*(l-n),
+                       im->graph_col[GRC_FONT],
+                       im->text_prop[TEXT_PROP_AXIS].font,
+                       im->text_prop[TEXT_PROP_AXIS].size, im->tabwidth, 270.0,
+                       GFX_H_CENTER, GFX_V_CENTER,
+                       s);
+               }
+           }
+       #endif
    
     /* graph title */
     gfx_new_text( canvas,
-                 im->xgif/2, im->text_prop[TEXT_PROP_TITLE].size*1.5,
+                 im->xgif/2, im->text_prop[TEXT_PROP_TITLE].size,
                  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);
 
-   /* graph labels */
-   if( !(im->extra_flags & NOLEGEND) ) {
+    /* graph labels */
+    if( !(im->extra_flags & NOLEGEND) ) {
       for(i=0;i<im->gdes_c;i++){
-        if(im->gdes[i].legend[0] =='\0')
-          continue;
+       if(im->gdes[i].legend[0] =='\0')
+           continue;
         
-        if(im->gdes[i].gf != GF_GPRINT && im->gdes[i].gf != GF_COMMENT){
-           x0 = im->gdes[i].leg_x;
-           y0 = im->gdes[i].leg_y+1.0;
-           x1 = x0+boxH;
-           x2 = x0+boxH;
-           x3 = x0;
-           y1 = y0;
-           y2 = y0+boxV;
-           y3 = y0+boxV;
-           node = gfx_new_area(canvas, x0,y0,x1,y1,x2,y2 ,im->gdes[i].col);
-           gfx_add_point ( node, x3, y3 );
-           gfx_add_point ( node, x0, y0 );
-           node = gfx_new_line(canvas, x0,y0,x1,y1 ,GRIDWIDTH, im->graph_col[GRC_FRAME]);
-           gfx_add_point ( node, x2, y2 );
-           gfx_add_point ( node, x3, y3 );
-           gfx_add_point ( node, x0, y0 );
-           
-           gfx_new_text ( canvas, x0+boxH+6,  (y0+y2) / 2.0,
-                          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_LEFT, GFX_V_CENTER,
-                          im->gdes[i].legend );
-           
-        } else {
-           x0 = im->gdes[i].leg_x;
-           y0 = im->gdes[i].leg_y;
-               
-           gfx_new_text ( canvas, x0,  (y0+y2) / 2.0,
-                          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_LEFT, GFX_V_BOTTOM,
-                          im->gdes[i].legend );
-           
-        }
-      }
-   }
-}
+       /* im->gdes[i].leg_y is the bottom of the legend */
+               x0 = im->gdes[i].leg_x;
+               y0 = im->gdes[i].leg_y;
+               /* Box needed? */
+               if (       im->gdes[i].gf != GF_GPRINT
+                       && im->gdes[i].gf != GF_COMMENT) {
+                   int boxH, boxV;
+
+                   boxH = gfx_get_text_width(0,
+                               im->text_prop[TEXT_PROP_AXIS].font,
+                               im->text_prop[TEXT_PROP_AXIS].size,
+                               im->tabwidth,"M") * 1.25;
+                   boxV = boxH;
+
+                   node = gfx_new_area(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(canvas,
+                               x0,y0-boxV, x0,y0,
+                               1,0x000000FF);
+                   gfx_add_point(node,x0+boxH,y0);
+                   gfx_add_point(node,x0+boxH,y0-boxV);
+                   gfx_add_point(node,x0,y0-boxV);
+                   x0 += boxH / 1.25 * 2;
+               }
+               gfx_new_text ( canvas, x0, 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_LEFT, GFX_V_BOTTOM,
+                                  im->gdes[i].legend );
+             }
+          }
+       }
 
 
 /*****************************************************
@@ -1830,9 +1865,6 @@ int lazy_check(image_desc_t *im){
     if ((fd = fopen(im->graphfile,"rb")) == NULL) 
       return 0; /* the file does not exist */
     switch (im->imgformat) {
-    case IF_GIF:
-          size = GifSize(fd,&(im->xgif),&(im->ygif));
-          break;
     case IF_PNG:
           size = PngSize(fd,&(im->xgif),&(im->ygif));
           break;
@@ -1841,6 +1873,192 @@ int lazy_check(image_desc_t *im){
     return size;
 }
 
+void
+pie_part(gfx_canvas_t *canvas, 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(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 );
+    }
+}
+
+int
+graph_size_location(image_desc_t *im, int elements, int piechart )
+{
+    /* 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
+    */
+
+    /* +-+-------------------------------------------+
+    ** |l|.................title.....................|
+    ** |e+--+-------------------------------+--------+
+    ** |b| b|                               |        |
+    ** |a| a|                               |  pie   |
+    ** |l| l|          main graph area      | chart  |
+    ** |.| .|                               |  area  |
+    ** |t| y|                               |        |
+    ** |r+--+-------------------------------+--------+
+    ** |e|  | x-axis labels                 |        |
+    ** |v+--+-------------------------------+--------+
+    ** | |..............legends......................|
+    ** +-+-------------------------------------------+
+    */
+    int Xvertical=0,   Yvertical=0,
+       Xtitle   =0,    Ytitle   =0,
+       Xylabel  =0,    Yylabel  =0,
+       Xmain    =0,    Ymain    =0,
+       Xpie     =0,    Ypie     =0,
+       Xxlabel  =0,    Yxlabel  =0,
+       Xlegend  =0,    Ylegend  =0,
+       Xspacing =10,   Yspacing =10;
+
+    if (im->ylegend[0] != '\0') {
+       Xvertical = im->text_prop[TEXT_PROP_LEGEND].size *2;
+       Yvertical = im->text_prop[TEXT_PROP_LEGEND].size * (strlen(im->ylegend)+1);
+    }
+
+    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.
+       */
+       Xtitle = gfx_get_text_width(0,
+               im->text_prop[TEXT_PROP_TITLE].font,
+               im->text_prop[TEXT_PROP_TITLE].size,
+               im->tabwidth,
+               im->title) + 2*Xspacing;
+       Ytitle = im->text_prop[TEXT_PROP_TITLE].size*2;
+    }
+
+    if (elements) {
+       Xmain=im->xsize;
+       Ymain=im->ysize;
+       if (im->draw_x_grid) {
+           Xxlabel=Xmain;
+           Yxlabel=im->text_prop[TEXT_PROP_LEGEND].size *2;
+       }
+       if (im->draw_y_grid) {
+           Xylabel=im->text_prop[TEXT_PROP_LEGEND].size *6;
+           Yylabel=Ymain;
+       }
+    }
+
+    if (piechart) {
+       im->piesize=im->xsize<im->ysize?im->xsize:im->ysize;
+       Xpie=im->piesize;
+       Ypie=im->piesize;
+    }
+
+    /* 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->xgif = Xylabel + Xmain + Xpie + Xspacing;
+    if (Xmain) im->xgif += Xspacing;
+    if (Xpie) im->xgif += Xspacing;
+    im->xorigin = Xspacing + Xylabel;
+    if (Xtitle > im->xgif) im->xgif = Xtitle;
+    if (Xvertical) {
+       im->xgif += Xvertical;
+       im->xorigin += Xvertical;
+    }
+    xtr(im,0);
+
+    /* The vertical size is interesting... we need to compare
+    ** the sum of {Ytitle, Ymain, Yxlabel, Ylegend} with Yvertical
+    ** however we need to know {Ytitle+Ymain+Yxlabel} in order to
+    ** start even thinking about Ylegend.
+    **
+    ** Do it in three portions: First calculate the inner part,
+    ** then do the legend, then adjust the total height of the img.
+    */
+
+    /* reserve space for main and/or pie */
+    im->ygif = Ymain + Yxlabel;
+    if (im->ygif < Ypie) im->ygif = Ypie;
+    im->yorigin = im->ygif - Yxlabel;
+    /* reserve space for the title *or* some padding above the graph */
+    if (Ytitle) {
+       im->ygif += Ytitle;
+       im->yorigin += Ytitle;
+    } else {
+       im->ygif += Yspacing;
+       im->yorigin += Yspacing;
+    }
+    /* reserve space for padding below the graph */
+    im->ygif += Yspacing;
+    ytr(im,DNAN);
+
+    /* Determine where to place the legends onto the image.
+    ** Adjust im->ygif to match the space requirements.
+    */
+    if(leg_place(im)==-1)
+       return -1;
+
+    /* last of three steps: check total height of image */
+    if (im->ygif < Yvertical) im->ygif = Yvertical;
+
+#if 0
+    if (Xlegend > im->xgif) {
+       im->xgif = Xlegend;
+       /* reposition Pie */
+#endif
+
+    /* The pie is placed in the upper right hand corner,
+    ** just below the title (if any) and with sufficient
+    ** padding.
+    */
+    im->pie_x = im->xgif - Xspacing - Xpie/2;
+    im->pie_y = im->yorigin-Ymain+Ypie/2;
+
+    return 0;
+}
 
 /* draw that picture thing ... */
 int
@@ -1848,6 +2066,8 @@ graph_paint(image_desc_t *im, char ***calcpr)
 {
   int i,ii;
   int lazy =     lazy_check(im);
+  int piechart = 0;
+  double PieStart=0.0;
   FILE  *fo;
   gfx_canvas_t *canvas;
   gfx_node_t *node;
@@ -1868,6 +2088,14 @@ graph_paint(image_desc_t *im, char ***calcpr)
   if(data_calc(im)==-1)
     return -1;
   
+  /* 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;
+    }
+  }
+
   /* 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 ... 
@@ -1875,7 +2103,10 @@ graph_paint(image_desc_t *im, char ***calcpr)
    */
   i=print_calc(im,calcpr);
   if(i<0) return -1;
-  if(i==0 || lazy) return 0;
+  if(((i==0)&&(piechart==0)) || lazy) return 0;
+
+  /* If there's only the pie chart to draw, signal this */
+  if (i==0) piechart=2;
   
   /* get actual drawing data and find min and max values*/
   if(data_proc(im)==-1)
@@ -1886,30 +2117,12 @@ graph_paint(image_desc_t *im, char ***calcpr)
   if(!im->rigid && ! im->logarithmic)
     expand_range(im);   /* make sure the upper and lower limit are
                            sensible values */
-  
-  /* init xtr and ytr */
-  /* determine the actual size of the gif to draw. The size given
-     on the cmdline is the graph area. But we need more as we have
-     draw labels and other things outside the graph area */
-  
-  
-  im->xorigin = 10 + 9 *  im->text_prop[TEXT_PROP_LEGEND].size;
-
-  xtr(im,0); 
-  
-  im->yorigin = 10 + im->ysize;
 
-  ytr(im,DNAN);
-  
-  if(im->title[0] != '\0')
-    im->yorigin += im->text_prop[TEXT_PROP_TITLE].size*3+4;
-  
-  im->xgif=20+im->xsize + im->xorigin;
-  im->ygif= im->yorigin+2* im->text_prop[TEXT_PROP_LEGEND].size;
-  
-  /* determine where to place the legends onto the graphics.
-     and set im->ygif to match space requirements for text */
-  if(leg_place(im)==-1)
+/**************************************************************
+ *** Calculating sizes and locations became a bit confusing ***
+ *** so I moved this into a separate function.              ***
+ **************************************************************/
+  if(graph_size_location(im,i,piechart)==-1)
     return -1;
 
   canvas=gfx_new_canvas();
@@ -1925,24 +2138,28 @@ graph_paint(image_desc_t *im, char ***calcpr)
 
   gfx_add_point(node,0, im->ygif);
 
-  node=gfx_new_area ( canvas,
+  if (piechart != 2) {
+    node=gfx_new_area ( 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);
+    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;
   
-  if (im->minval > 0.0)
-    areazero = im->minval;
-  if (im->maxval < 0.0)
-    areazero = im->maxval;
-  
-  axis_paint(im,canvas);
+    axis_paint(im,canvas);
+  }
 
+  if (piechart) {
+    pie_part(canvas,im->graph_col[GRC_CANVAS],im->pie_x,im->pie_y,im->piesize*0.5,0,2*M_PI);
+  }
 
-  for(i=0;i<im->gdes_c;i++){    
+  for(i=0;i<im->gdes_c;i++){
     switch(im->gdes[i].gf){
     case GF_CDEF:
     case GF_VDEF:
@@ -2061,8 +2278,25 @@ graph_paint(image_desc_t *im, char ***calcpr)
       } 
       lastgdes = &(im->gdes[i]);                         
       break;
+    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(canvas,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;
     } /* switch */
   }
+  if (piechart==2) {
+    im->draw_x_grid=0;
+    im->draw_y_grid=0;
+  }
+  /* grid_paint also does the text */
   grid_paint(im,canvas);
   
   /* the RULES are the last thing to paint ... */
@@ -2070,7 +2304,6 @@ graph_paint(image_desc_t *im, char ***calcpr)
     
     switch(im->gdes[i].gf){
     case GF_HRULE:
-      printf("DEBUG: HRULE at %f\n",im->gdes[i].yrule);
       if(isnan(im->gdes[i].yrule)) { /* fetch variable */
         im->gdes[i].yrule = im->gdes[im->gdes[i].vidx].vf.val;
       };
@@ -2112,8 +2345,6 @@ graph_paint(image_desc_t *im, char ***calcpr)
     }
   }
   switch (im->imgformat) {
-  case IF_GIF:
-    break;
   case IF_PNG:
     gfx_render_png (canvas,im->xgif,im->ygif,im->zoom,0x0,fo);
     break;
@@ -2285,7 +2516,7 @@ rrd_graph_init(image_desc_t *im)
     im->gdes_c = 0;
     im->gdes = NULL;
     im->zoom = 1.0;
-    im->imgformat = IF_GIF; /* we default to GIF output */
+    im->imgformat = IF_PNG; /* we default to PNG output */
 
     for(i=0;i<DIM(graph_col);i++)
         im->graph_col[i]=graph_col[i];
@@ -2511,27 +2742,46 @@ rrd_graph_options(int argc, char *argv[],image_desc_t *im)
             }
             break;        
         case 'n':{
-          char *prop = "";
-          double size = 1;
-          char *font = "dummy";
-
-          if(sscanf(optarg,
-                    "%10[A-Z]:%lf:%s",
-                    prop,&size,font) == 3){
-            int sindex;
-            if((sindex=text_prop_conv(prop)) != -1){
-              im->text_prop[sindex].size=size;              
-              im->text_prop[sindex].font=font;
-              
-            }  else {
-              rrd_set_error("invalid color name '%s'",col_nam);
-            }
-          } else {
-            rrd_set_error("invalid text property format");
-            return;
-          }
-          break;          
-        }
+                       /* originally this used char *prop = "" and
+                       ** char *font = "dummy" however this results
+                       ** in a SEG fault, at least on RH7.1
+                       **
+                       ** The current implementation isn't proper
+                       ** either, font is never freed and prop uses
+                       ** a fixed width string
+                       */
+           char prop[100];
+           double size = 1;
+           char *font;
+
+           font=malloc(255);
+           if(sscanf(optarg,
+                               "%10[A-Z]:%lf:%s",
+                               prop,&size,font) == 3){
+               int sindex;
+               if((sindex=text_prop_conv(prop)) != -1){
+                   im->text_prop[sindex].size=size;              
+                   im->text_prop[sindex].font=font;
+                   if (sindex==0) { /* the default */
+                       im->text_prop[TEXT_PROP_TITLE].size=size;
+                       im->text_prop[TEXT_PROP_TITLE].font=font;
+                       im->text_prop[TEXT_PROP_AXIS].size=size;
+                       im->text_prop[TEXT_PROP_AXIS].font=font;
+                       im->text_prop[TEXT_PROP_UNIT].size=size;
+                       im->text_prop[TEXT_PROP_UNIT].font=font;
+                       im->text_prop[TEXT_PROP_LEGEND].size=size;
+                       im->text_prop[TEXT_PROP_LEGEND].font=font;
+                   }
+               } else {
+                   rrd_set_error("invalid fonttag '%s'",prop);
+                   return;
+               }
+           } else {
+               rrd_set_error("invalid text property format");
+               return;
+           }
+           break;          
+       }
         case 'm':
            im->zoom= atof(optarg);
            if (im->zoom <= 0.0) {
@@ -2612,7 +2862,7 @@ rrd_graph_script(int argc, char *argv[], image_desc_t *im)
        /* function:newvname=string[:ds-name:CF]        for xDEF
        ** function:vname[#color[:string]]              for LINEx,AREA,STACK
        ** function:vname#color[:num[:string]]          for TICK
-       ** function:vname-or-num#color[:string]         for xRULE
+       ** function:vname-or-num#color[:string]         for xRULE,PART
        ** function:vname:CF:string                     for xPRINT
        ** function:string                              for COMMENT
        */
@@ -2641,6 +2891,7 @@ rrd_graph_script(int argc, char *argv[], image_desc_t *im)
                if (rrd_graph_legend(gdp,&line[argstart])==0)
                    rrd_set_error("Cannot parse comment in line: %s",line);
                break;
+           case GF_PART:
            case GF_VRULE:
            case GF_HRULE:
                j=k=l=m=0;