[rrd-developers] patch for legends position

Melchior Rabe rrdtool at mrab.de
Mon Feb 9 17:47:35 CET 2009


Hello together,

at the moment the legend section is placed below the graph. This is in
the most cases fine as the time axis gets a much space as possible. But
I want to graph data that is a set of stacked values (up to 25) and also
want to print some informational numbers into the legend. In that case I
find it more convinient placing the legend on the side of the graph
(Possibly that might be also nice for the pie chart function...). I
created a patch that is introducing two new options:
--legend-position north|south|west|east (default: south)
--legend-direction topdown|bottomup (default: topdown)

The first option decides where the legend is placed and the second can
be used to reverse the vertical order of the legend items. In a stack
the first item (AREA, LINEx) is the bottommost and the following is
stacked on top of it. The legend is placed in the same sequence. That
means that the first item and the next one is placed (after a line
break) below the preceeding ones. So the graph is stacked bottomup while
the legend is "stacked" topdown. The --legend-direction is reversing the
vertical order of the legend items.


Some not so nice (but IMHO acceptable) side effects:

- Placing the legend on the side will result in a very wide graph with
all items in one line (as long as there is no manual line break
inserted). So the user has to take care for the line breaks. I think
that is acceptable, as any other solution I thought of (giving the width
of the legend or defining some ratio between legend and graph) was
leading to more parameters.

- Placing the legend items bottomup might lead to an incomplete first
line, because only the vertical alignment is inverted, but not the
sequence of the items e.g. like that:

topdown legend:
first   second third
forth   fifth  sixth
seventh

will lead to bottomup:
seventh
forth   fifth  sixth
first   second third

The patch is tested on WinXP32, but as there is only some painting
affected I do not expect any problems on other systems. I wrote a short
description with some example images (http://mrab.de/?p=64) and placed
the patch below also in a zip at
http://mrab.de/wp-content/uploads/rrdtool_patch_legend-position.zip

Any comments, hints and tips are appreciated!

Cheers,

Melchior

Patch for rrd_graph.h
----------------------- BEGIN ----------------------------------------
Index: rrd_graph.h
===================================================================
--- rrd_graph.h	(revision 1746)
+++ rrd_graph.h	(working copy)
@@ -81,6 +81,8 @@
    TEXT_PROP_LAST
};

+enum legend_pos{ NORTH = 0, WEST, SOUTH, EAST };
+enum legend_direction { TOP_DOWN = 0, BOTTOM_UP };

enum gfx_if_en { IF_PNG = 0, IF_SVG, IF_EPS, IF_PDF };
enum gfx_en { GFX_LINE = 0, GFX_AREA, GFX_TEXT };
@@ -231,6 +233,8 @@
                           reasonable probablility that the
                           existing one is out of date */
    int       slopemode;    /* connect the dots of the curve directly, not using a stair */
+    legend_pos legendposition; /* the position of the legend: north, west, south or east */
+    legend_direction legenddirection; /* The direction of the legend topdown or bottomup */
    int       logarithmic;  /* scale the yaxis logarithmic */
    double    force_scale_min;  /* Force a scale--min */
    double    force_scale_max;  /* Force a scale--max */
@@ -238,7 +242,12 @@
    /* status information */
    int       with_markup;
    long      xorigin, yorigin; /* where is (0,0) of the graph */
+    long      xOriginTitle, yOriginTitle; /* where is the origin of the title */
+    long      xOriginLegendY, yOriginLegendY; /* where is the origin of the y legend */
+    long      xOriginLegendY2, yOriginLegendY2; /* where is the origin of the second y legend */
+    long      xOriginLegend, yOriginLegend; /* where is the origin of the legend */
    long      ximg, yimg;   /* total size of the image */
+    long      legendwidth, legendheight; /* the calculated height and width of the legend */
    size_t    rendered_image_size;
    double    zoom;
    double    magfact;  /* numerical magnitude */ 
----------------------- END   ----------------------------------------

Patch for rrd_graph.c
----------------------- BEGIN ----------------------------------------
Index: rrd_graph.c
===================================================================
--- rrd_graph.c	(revision 1746)
+++ rrd_graph.c	(working copy)
@@ -1613,26 +1613,37 @@
}


+
/* place legends with color spots */
int leg_place(
    image_desc_t *im,
-    int *gY)
+    bool calc_width)
{
    /* graph labels */
    int       interleg = im->text_prop[TEXT_PROP_LEGEND].size * 2.0;
    int       border = im->text_prop[TEXT_PROP_LEGEND].size * 2.0;
    int       fill = 0, fill_last;
+    double    legendwidth; // = im->ximg - 2 * border;
    int       leg_c = 0;
    double    leg_x = border;
-    int       leg_y = im->yimg;
-    int       leg_y_prev = im->yimg;
+    int       leg_y = 0; //im->yimg;
+    int       leg_y_prev = 0; // im->yimg;
    int       leg_cc;
    double    glue = 0;
    int       i, ii, mark = 0;
    char      default_txtalign = TXA_JUSTIFIED; /*default line orientation */
    int      *legspace;
    char     *tab;
+    char      saved_legend[FMT_LEG_LEN + 5];

+    if(calc_width){
+        legendwidth = 0;
+    }
+    else{
+        legendwidth = im->legendwidth - 2 * border;
+    }
+
+
    if (!(im->extra_flags & NOLEGEND) & !(im->extra_flags & ONLY_GRAPH)) {
        if ((legspace = (int*)(malloc(im->gdes_c * sizeof(int)))) == NULL) {
            rrd_set_error("malloc for legspace");
@@ -1641,6 +1652,10 @@

        for (i = 0; i < im->gdes_c; i++) {
            char      prt_fctn; /*special printfunctions */
+            if(calc_width){
+                strcpy(saved_legend, im->gdes[i].legend);
+            }
+
            fill_last = fill;
            /* hide legends for rules which are not displayed */
            if (im->gdes[i].gf == GF_TEXTALIGN) {
@@ -1663,6 +1678,7 @@
                memmove(tab, tab + 1, strlen(tab));
                tab[0] = (char) 9;
            }
+
            leg_cc = strlen(im->gdes[i].legend);
            /* is there a controle code at the end of the legend string ? */
            if (leg_cc >= 2 && im->gdes[i].legend[leg_cc - 2] == '\\') {
@@ -1721,7 +1737,10 @@
            }

            if (prt_fctn == '\0') {
-                if (i == im->gdes_c - 1 || fill > im->ximg - 2 * border) {
+                if(calc_width && (fill > legendwidth)){
+                    legendwidth = fill;
+                }
+                if (i == im->gdes_c - 1 || fill > legendwidth) {
                    /* just one legend item is left right or center */
                    switch (default_txtalign) {
                    case TXA_RIGHT:
@@ -1739,7 +1758,7 @@
                    }
                }
                /* is it time to place the legends ? */
-                if (fill > im->ximg - 2 * border) {
+                if (fill > legendwidth) {
                    if (leg_c > 1) {
                        /* go back one */
                        i--;
@@ -1752,23 +1771,22 @@
                }
            }

-
            if (prt_fctn != '\0') {
                leg_x = border;
                if (leg_c >= 2 && prt_fctn == 'j') {
-                    glue = (double)(im->ximg - fill - 2 * border) / (double)(leg_c - 1);
+                    glue = (double)(legendwidth - fill) / (double)(leg_c - 1);
                } else {
                    glue = 0;
                }
                if (prt_fctn == 'c')
-                    leg_x = (double)(im->ximg - fill) / 2.0;
+                    leg_x = (double)(legendwidth - fill) / 2.0;
                if (prt_fctn == 'r')
-                    leg_x = im->ximg - fill - border;
+                    leg_x = legendwidth - fill - border;
                for (ii = mark; ii <= i; ii++) {
                    if (im->gdes[ii].legend[0] == '\0')
                        continue;   /* skip empty legends */
                    im->gdes[ii].leg_x = leg_x;
-                    im->gdes[ii].leg_y = leg_y;
+                    im->gdes[ii].leg_y = leg_y + border;
                    leg_x +=
                        (double)gfx_get_text_width(im, leg_x,
                                           im->
@@ -1784,27 +1802,26 @@
                    leg_y += im->text_prop[TEXT_PROP_LEGEND].size * 1.8;
                if (prt_fctn == 's')
                    leg_y -= im->text_prop[TEXT_PROP_LEGEND].size;
+
+                if(calc_width && (fill > legendwidth)){
+                    legendwidth = fill;
+                }
                fill = 0;
                leg_c = 0;
                mark = ii;
            }
-        }

-        if (im->extra_flags & FULL_SIZE_MODE) {
-            /* now for some backpaddeling. We have to shift up all the
-               legend items into the graph and tell the caller about the
-               space we used up. */
-            long shift_up = leg_y - im->yimg - im->text_prop[TEXT_PROP_LEGEND].size * 1.8 + border * 0.7;
-            for (i = 0; i < im->gdes_c; i++) {
-                im->gdes[i].leg_y -= shift_up;
+            if(calc_width){
+                strcpy(im->gdes[i].legend, saved_legend);
            }
-            im->yorigin = im->yorigin - leg_y + im->yimg - im->text_prop[TEXT_PROP_LEGEND].size * 1.8 - border;            
-            *gY = im->yorigin;
-        } else {
-            im->yimg =
-                leg_y - im->text_prop[TEXT_PROP_LEGEND].size * 1.8 +
-                border * 0.6;
        }
+
+        if(calc_width){
+            im->legendwidth = legendwidth + 2 * border;
+        }
+        else{
+            im->legendheight = leg_y + border * 0.6;
+        }
        free(legspace);
    }
    return 0;
@@ -2590,24 +2607,20 @@
    /* yaxis unit description */
    if (im->ylegend[0] != '\0'){
        gfx_text(im,
-                 10,
-                 (im->yorigin -
-                  im->ysize / 2),
+                 im->xOriginLegendY+10,
+                 im->yOriginLegendY,
                 im->graph_col[GRC_FONT],
                 im->
                 text_prop[TEXT_PROP_UNIT].
                 font_desc,
                 im->tabwidth,
                 RRDGRAPH_YLEGEND_ANGLE, GFX_H_CENTER, GFX_V_CENTER, im->ylegend);
+
    }
    if (im->second_axis_legend[0] != '\0'){
-            double Xylabel=gfx_get_text_width(im, 0,
-                        im->text_prop[TEXT_PROP_AXIS].font_desc,
-                        im->tabwidth,
-                        "0") * im->unitslength
-                    + im->text_prop[TEXT_PROP_UNIT].size *2;
            gfx_text( im,
-                  im->xorigin+im->xsize+Xylabel+8, (im->yorigin - im->ysize/2),
+                  im->xOriginLegendY2+10, 
+                  im->yOriginLegendY2,
                  im->graph_col[GRC_FONT],
                  im->text_prop[TEXT_PROP_UNIT].font_desc,
                  im->tabwidth, 
@@ -2618,7 +2631,7 @@
 
    /* graph title */
    gfx_text(im,
-             im->ximg / 2, 6,
+             im->xOriginTitle, im->yOriginTitle+6,
             im->graph_col[GRC_FONT],
             im->
             text_prop[TEXT_PROP_TITLE].
@@ -2628,7 +2641,8 @@
    if (!(im->extra_flags & NO_RRDTOOL_TAG)){
        water_color = im->graph_col[GRC_FONT];
        water_color.alpha = 0.3;
-        gfx_text(im, im->ximg - 4, 5,
+        double xpos = im->legendposition == EAST ? im->xOriginLegendY : im->ximg - 4;
+        gfx_text(im, xpos, 5,
                 water_color,
                 im->
                 text_prop[TEXT_PROP_WATERMARK].
@@ -2652,8 +2666,8 @@
            if (im->gdes[i].legend[0] == '\0')
                continue;
            /* im->gdes[i].leg_y is the bottom of the legend */
-            X0 = im->gdes[i].leg_x;
-            Y0 = im->gdes[i].leg_y;
+            X0 = im->xOriginLegend + im->gdes[i].leg_x;
+            Y0 = im->legenddirection == TOP_DOWN ? im->yOriginLegend + im->gdes[i].leg_y : im->yOriginLegend + im->legendheight - im->gdes[i].leg_y;
            gfx_text(im, X0, Y0,
                     im->graph_col[GRC_FONT],
                     im->
@@ -2770,22 +2784,12 @@
    /* The actual size of the image to draw is determined from
     ** several sources.  The size given on the command line is
     ** the graph area but we need more as we have to draw labels
-     ** and other things outside the graph area
+     ** and other things outside the graph area. If the option 
+     ** --full-size-mode is selected the size defines the total 
+     ** image size and the size available for the graph is 
+     ** calculated.
     */

-    int       Xvertical = 0, Ytitle =
-        0, Xylabel = 0, Xmain = 0, Ymain =
-        0, Yxlabel = 0, Xspacing = 15, Yspacing = 15, Ywatermark = 4;
-
-    if (im->extra_flags & ONLY_GRAPH) {
-        im->xorigin = 0;
-        im->ximg = im->xsize;
-        im->yimg = im->ysize;
-        im->yorigin = im->ysize;
-        ytr(im, DNAN);
-        return 0;
-    }
-
    /** +---+-----------------------------------+
     ** | y |...............graph title.........|
     ** |   +---+-------------------------------+
@@ -2806,10 +2810,33 @@
     ** +---------------------------------------+
     */

+    int       Xvertical = 0, Xvertical2 = 0, Ytitle =
+        0, Xylabel = 0, Xmain = 0, Ymain =
+        0, Yxlabel = 0, Xspacing = 15, Yspacing = 15, Ywatermark = 4;
+
+    // no legends and no the shall be plotted it's easy
+    if (im->extra_flags & ONLY_GRAPH) {
+        im->xorigin = 0;
+        im->ximg = im->xsize;
+        im->yimg = im->ysize;
+        im->yorigin = im->ysize;
+        ytr(im, DNAN);
+        return 0;
+    } 
+
+    // calculate the width of the left vertical legend
    if (im->ylegend[0] != '\0') {
        Xvertical = im->text_prop[TEXT_PROP_UNIT].size * 2;
    }

+    // calculate the width of the right vertical legend
+    if (im->second_axis_legend[0] != '\0') {
+        Xvertical2 = im->text_prop[TEXT_PROP_UNIT].size * 2;
+    }
+    else{
+        Xvertical2 = Xspacing;
+    }
+
    if (im->title[0] != '\0') {
        /* The title is placed "inbetween" two text lines so it
         ** automatically has some vertical spacing.  The horizontal
@@ -2818,92 +2845,112 @@
        /* if necessary, reduce the font size of the title until it fits the image width */
        Ytitle = im->text_prop[TEXT_PROP_TITLE].size * 2.6 + 10;
    }
+    else{
+        // we have no title; get a little clearing from the top
+        Ytitle = 1.5 * Yspacing;
+    }

    if (elements) {
        if (im->draw_x_grid) {
+            // calculate the height of the horizontal labelling
            Yxlabel = im->text_prop[TEXT_PROP_AXIS].size * 2.5;
        }
        if (im->draw_y_grid || im->forceleftspace) {
+            // calculate the width of the vertical labelling
            Xylabel =
                gfx_get_text_width(im, 0,
-                                   im->
-                                   text_prop
-                                   [TEXT_PROP_AXIS].
-                                   font_desc,
+                                   im->text_prop[TEXT_PROP_AXIS].font_desc,
                                   im->tabwidth, "0") * im->unitslength;
        }
    }

+    // add some space to the labelling
+    Xylabel += Xspacing;
+
+    /* If the legend is printed besides the graph the width has to be
+     ** calculated first. Placing the legend north or south of the 
+     ** graph requires the width calculation first, so the legend is 
+     ** skipped for the moment.
+     */
+    im->legendheight = 0;
+    im->legendwidth = 0;
+    if (!(im->extra_flags & NOLEGEND)) {
+        if(im->legendposition == WEST || im->legendposition == EAST){
+            if (leg_place(im, true) == -1){
+                return -1;
+            }
+        }
+    }
+
    if (im->extra_flags & FULL_SIZE_MODE) {
+
        /* The actual size of the image to draw has been determined by the user.
         ** The graph area is the space remaining after accounting for the legend,
         ** the watermark, the axis labels, and the title.
         */
-        im->xorigin = 0;
        im->ximg = im->xsize;
        im->yimg = im->ysize;
-        im->yorigin = im->ysize;
        Xmain = im->ximg;
        Ymain = im->yimg;
+
        /* Now calculate the total size.  Insert some spacing where
           desired.  im->xorigin and im->yorigin need to correspond
           with the lower left corner of the main graph area or, if
           this one is not set, the imaginary box surrounding the
           pie chart area. */
        /* Initial size calculation for the main graph area */
-        Xmain = im->ximg - Xylabel - 3 * Xspacing;

-        im->xorigin = Xspacing + Xylabel;
-
-        if (Xvertical) {    /* unit description */
-            Xmain -= Xvertical;
-            im->xorigin += Xvertical;
+        Xmain -= Xylabel;// + Xspacing;
+        if((im->legendposition == WEST || im->legendposition == EAST) && !(im->extra_flags & NOLEGEND) ){
+            Xmain -= im->legendwidth;// + Xspacing;
        }
-
-        /* adjust space for second axis */
        if (im->second_axis_scale != 0){
-            Xmain -= Xylabel + Xspacing;
+            Xmain -= Xylabel;
        }
-        if (im->extra_flags & NO_RRDTOOL_TAG){
-            Xmain += Xspacing;
+        if (!(im->extra_flags & NO_RRDTOOL_TAG)){
+            Xmain -= Xspacing;
        }
-        if (im->second_axis_legend[0] != '\0' ) {
-            Xmain -= im->text_prop[TEXT_PROP_UNIT].size * 1.5;
+
+        Xmain -= Xvertical + Xvertical2;
+
+        /* limit the remaining space to 0 */
+        if(Xmain < 1){
+            Xmain = 1;
        }
-
        im->xsize = Xmain;

-        xtr(im, 0);
-        /* The vertical size of the image is known in advance.  The main graph area
-         ** (Ymain) and im->yorigin must be set according to the space requirements
-         ** of the legend and the axis labels.
-         */
-        if (im->extra_flags & NOLEGEND) {
-            im->yorigin = im->yimg -
-                im->text_prop[TEXT_PROP_AXIS].size * 2.5 - Yspacing;
-            Ymain = im->yorigin;
+        /* Putting the legend north or south, the height can now be calculated */
+        if (!(im->extra_flags & NOLEGEND)) {
+            if(im->legendposition == NORTH || im->legendposition == SOUTH){
+                im->legendwidth = im->ximg;
+                if (leg_place(im, false) == -1){
+                    return -1;
+                }
+            }
        }
-        else {            
-            /* Determine where to place the legends onto the image.
-             ** Set Ymain and adjust im->yorigin to match the space requirements.
-             */
-            if (leg_place(im, &Ymain) == -1)
-                return -1;
+
+        if( (im->legendposition == NORTH || im->legendposition == SOUTH)  && !(im->extra_flags & NOLEGEND) ){
+            Ymain -=  Yxlabel + im->legendheight;
        }
+        else{
+            Ymain -= Yxlabel;
+        }
+        
+        /* reserve space for the title *or* some padding above the graph */
+        Ymain -= Ytitle;

-
-        /* remove title space *or* some padding above the graph from the main graph area */
-        if (Ytitle) {
-            Ymain -= Ytitle;
-        } else {
-            Ymain -= 1.5 * Yspacing;
+            /* reserve space for padding below the graph */
+        if (im->extra_flags & NOLEGEND) {
+            Ymain -= Yspacing;
        }

-        /* watermark doesn't seem to effect the vertical size of the main graph area, oh well! */
        if (im->watermark[0] != '\0') {
            Ymain -= Ywatermark;
        }
-
+        /* limit the remaining height to 0 */
+        if(Ymain < 1){
+            Ymain = 1;
+        }
        im->ysize = Ymain;
    } else {            /* dimension options -width and -height refer to the dimensions of the main graph area */

@@ -2913,94 +2960,171 @@
         ** and other things outside the graph area.
         */

-        if (im->ylegend[0] != '\0') {
-            Xvertical = im->text_prop[TEXT_PROP_UNIT].size * 2;
+        if (elements) {
+            Xmain = im->xsize; // + Xspacing;
+            Ymain = im->ysize;
        }

-
-        if (im->title[0] != '\0') {
-            /* The title is placed "inbetween" two text lines so it
-             ** automatically has some vertical spacing.  The horizontal
-             ** spacing is added here, on each side.
-             */
-            /* don't care for the with of the title
-               Xtitle = gfx_get_text_width(im->canvas, 0,
-               im->text_prop[TEXT_PROP_TITLE].font_desc,
-               im->tabwidth,
-               im->title, 0) + 2*Xspacing; */
-            Ytitle = im->text_prop[TEXT_PROP_TITLE].size * 2.6 + 10;
+        im->ximg = Xmain + Xylabel;
+        if (!(im->extra_flags & NO_RRDTOOL_TAG)){
+            im->ximg += Xspacing;
        }

-        if (elements) {
-            Xmain = im->xsize;
-            Ymain = im->ysize;
+        if( (im->legendposition == WEST || im->legendposition == EAST) && !(im->extra_flags & NOLEGEND) ){
+            im->ximg += im->legendwidth;// + Xspacing;
        }
-        /* 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. */
+        if (im->second_axis_scale != 0){
+            im->ximg += Xylabel;
+        }

-        /* The legend width cannot yet be determined, as a result we
-         ** have problems adjusting the image to it.  For now, we just
-         ** forget about it at all; the legend will have to fit in the
-         ** size already allocated.
-         */
-        im->ximg = Xylabel + Xmain + 2 * Xspacing;
+        im->ximg += Xvertical + Xvertical2;

-        if (im->second_axis_scale != 0){
-            im->ximg += Xylabel + Xspacing;
+        if (!(im->extra_flags & NOLEGEND)) {
+            if(im->legendposition == NORTH || im->legendposition == SOUTH){
+                im->legendwidth = im->ximg;
+                if (leg_place(im, false) == -1){
+                    return -1;
+                }
+            }
        }
-        if (im->extra_flags & NO_RRDTOOL_TAG){
-            im->ximg -= Xspacing;
+      
+        im->yimg = Ymain + Yxlabel;
+        if( (im->legendposition == NORTH || im->legendposition == SOUTH)  && !(im->extra_flags & NOLEGEND) ){
+             im->yimg += im->legendheight;
        }
        
-        if (Xmain)
-            im->ximg += Xspacing;
-        im->xorigin = Xspacing + Xylabel;
-        /* the length of the title should not influence with width of the graph
-           if (Xtitle > im->ximg) im->ximg = Xtitle; */
-        if (Xvertical) {    /* unit description */
-            im->ximg += Xvertical;
-            im->xorigin += Xvertical;
-        }
-        if (im->second_axis_legend[0] != '\0' ) {
-            im->ximg += Xvertical;
-        }
-      
-        xtr(im, 0);
-        /* The vertical size is interesting... we need to compare
-         ** the sum of {Ytitle, Ymain, Yxlabel, Ylegend, Ywatermark} with 
-         ** Yvertical however we need to know {Ytitle+Ymain+Yxlabel}
-         ** in order to start even thinking about Ylegend or Ywatermark.
-         **
-         ** Do it in three portions: First calculate the inner part,
-         ** then do the legend, then adjust the total height of the img,
-         ** adding space for a watermark if one exists;
-         */
-        /* reserve space for main and/or pie */
-        im->yimg = Ymain + Yxlabel;
-        im->yorigin = im->yimg - Yxlabel;
        /* reserve space for the title *or* some padding above the graph */
        if (Ytitle) {
            im->yimg += Ytitle;
-            im->yorigin += Ytitle;
        } else {
            im->yimg += 1.5 * Yspacing;
-            im->yorigin += 1.5 * Yspacing;
        }
        /* reserve space for padding below the graph */
-        im->yimg += Yspacing;
-        /* Determine where to place the legends onto the image.
-         ** Adjust im->yimg to match the space requirements.
-         */
-        if (leg_place(im, 0) == -1)
-            return -1;
+        if (im->extra_flags & NOLEGEND) {
+            im->yimg += Yspacing;
+        }
+
        if (im->watermark[0] != '\0') {
            im->yimg += Ywatermark;
        }
    }

+
+    /* In case of putting the legend in west or east position the first 
+     ** legend calculation might lead to wrong positions if some items 
+     ** are not aligned on the left hand side (e.g. centered) as the 
+     ** legendwidth wight have been increased after the item was placed.
+     ** In this case the positions have to be recalculated.
+     */
+    if (!(im->extra_flags & NOLEGEND)) {
+        if(im->legendposition == WEST || im->legendposition == EAST){
+            if (leg_place(im, false) == -1){
+                return -1;
+            }
+        }
+    }
+
+    /* After calculating all dimensions
+     ** it is now possible to calculate 
+     ** all offsets.
+     */
+    switch(im->legendposition){
+        case NORTH:
+            im->xOriginTitle   = Xvertical + Xylabel + (im->xsize / 2);
+            im->yOriginTitle   = 0;
+
+            im->xOriginLegend  = 0;
+            im->yOriginLegend  = Ytitle;
+
+            im->xOriginLegendY = 0;
+            im->yOriginLegendY = Ytitle + im->legendheight + (Ymain + Yxlabel) / 2;
+
+            im->xorigin        = Xvertical + Xylabel;
+            im->yorigin        = Ytitle + im->legendheight + Ymain;
+
+            im->xOriginLegendY2 = Xvertical + Xylabel + Xmain;
+            if (im->second_axis_scale != 0){
+                im->xOriginLegendY2 += Xylabel;
+            }
+            im->yOriginLegendY2 = Ytitle + im->legendheight + (Ymain + Yxlabel) / 2;
+
+            break;
+
+        case WEST:
+            im->xOriginTitle   = im->legendwidth + Xvertical + Xylabel + im->xsize / 2;
+            im->yOriginTitle   = 0;
+
+            im->xOriginLegend  = 0;
+            im->yOriginLegend  = Ytitle;
+
+            im->xOriginLegendY = im->legendwidth;
+            im->yOriginLegendY = (Ytitle + Ymain + Yxlabel) / 2;
+
+            im->xorigin        = im->legendwidth + Xvertical + Xylabel;
+            im->yorigin        = Ytitle + Ymain;
+
+            im->xOriginLegendY2 = im->legendwidth + Xvertical + Xylabel + Xmain;
+            if (im->second_axis_scale != 0){
+                im->xOriginLegendY2 += Xylabel;
+            }
+            im->yOriginLegendY2 = (Ytitle + Ymain + Yxlabel) / 2;
+
+            break;
+
+        case SOUTH:
+            im->xOriginTitle   = Xvertical + Xylabel + im->xsize / 2;
+            im->yOriginTitle   = 0;
+
+            im->xOriginLegend  = 0;
+            im->yOriginLegend  = Ytitle + Ymain + Yxlabel;
+
+            im->xOriginLegendY = 0;
+            im->yOriginLegendY = (Ytitle + Ymain + Yxlabel) / 2;
+
+            im->xorigin        = Xvertical + Xylabel;
+            im->yorigin        = Ytitle + Ymain;
+
+            im->xOriginLegendY2 = Xvertical + Xylabel + Xmain;
+            if (im->second_axis_scale != 0){
+                im->xOriginLegendY2 += Xylabel;
+            }
+            im->yOriginLegendY2 = (Ytitle + Ymain + Yxlabel) / 2;
+
+            break;
+
+        case EAST:
+            im->xOriginTitle   = Xvertical + Xylabel + im->xsize / 2;
+            im->yOriginTitle   = 0;
+
+            im->xOriginLegend  = Xvertical + Xylabel + Xmain + Xvertical2;
+            if (im->second_axis_scale != 0){
+                im->xOriginLegend += Xylabel;
+            }
+            im->yOriginLegend  = Ytitle;
+
+            im->xOriginLegendY = 0;
+            im->yOriginLegendY = (Ytitle + Ymain + Yxlabel) / 2;
+
+            im->xorigin        = Xvertical + Xylabel;
+            im->yorigin        = Ytitle + Ymain;
+
+            im->xOriginLegendY2 = Xvertical + Xylabel + Xmain;
+            if (im->second_axis_scale != 0){
+                im->xOriginLegendY2 += Xylabel;
+            }
+            im->yOriginLegendY2 = (Ytitle + Ymain + Yxlabel) / 2;
+
+            if (!(im->extra_flags & NO_RRDTOOL_TAG)){
+                im->xOriginTitle    += Xspacing;
+                im->xOriginLegend   += Xspacing;
+                im->xOriginLegendY  += Xspacing;
+                im->xorigin         += Xspacing;
+                im->xOriginLegendY2 += Xspacing;
+            }
+            break;
+    }
+
+    xtr(im, 0);
    ytr(im, DNAN);
    return 0;
}
@@ -3827,6 +3951,10 @@
    im->imgformat = IF_PNG;
    im->imginfo = NULL;
    im->lazy = 0;
+    im->legenddirection = TOP_DOWN;
+    im->legendheight = 0;
+    im->legendposition = SOUTH;
+    im->legendwidth = 0;
    im->logarithmic = 0;
    im->maxval = DNAN;
    im->minval = 0;
@@ -3848,6 +3976,10 @@
    im->ximg = 0;
    im->xlab_user.minsec = -1;
    im->xorigin = 0;
+    im->xOriginLegend = 0;
+    im->xOriginLegendY = 0;
+    im->xOriginLegendY2 = 0;
+    im->xOriginTitle = 0;
    im->xsize = 400;
    im->ygridstep = DNAN;
    im->yimg = 0;
@@ -3857,6 +3989,10 @@
    im->second_axis_legend[0] = '\0';
    im->second_axis_format[0] = '\0'; 
    im->yorigin = 0;
+    im->yOriginLegend = 0;
+    im->yOriginLegendY = 0;
+    im->yOriginLegendY2 = 0;
+    im->yOriginTitle = 0;
    im->ysize = 100;
    im->zoom = 1;

@@ -3941,6 +4077,8 @@
        { "lazy",               no_argument,       0, 'z'},
        { "zoom",               required_argument, 0, 'm'},
        { "no-legend",          no_argument,       0, 'g'},
+        { "legend-position",    required_argument, 0, 1005},
+        { "legend-direction",   required_argument, 0, 1006},
        { "force-rules-legend", no_argument,       0, 'F'},
        { "only-graph",         no_argument,       0, 'j'},
        { "alt-y-grid",         no_argument,       0, 'Y'},
@@ -4005,6 +4143,30 @@
        case 'g':
            im->extra_flags |= NOLEGEND;
            break;
+        case 1005:
+            if (strcmp(optarg, "north") == 0) {
+                im->legendposition = NORTH;
+            } else if (strcmp(optarg, "west") == 0) {
+                im->legendposition = WEST;
+            } else if (strcmp(optarg, "south") == 0) {
+                im->legendposition = SOUTH;
+            } else if (strcmp(optarg, "east") == 0) {
+                im->legendposition = EAST;
+            } else {
+                rrd_set_error("unknown legend-position '%s'", optarg);
+                return;
+            }
+            break;
+        case 1006:
+            if (strcmp(optarg, "topdown") == 0) {
+                im->legenddirection = TOP_DOWN;
+            } else if (strcmp(optarg, "bottomup") == 0) {
+                im->legenddirection = BOTTOM_UP;
+            } else {
+                rrd_set_error("unknown legend-position '%s'", optarg);
+                return;
+            }
+            break;
        case 'F':
            im->extra_flags |= FORCE_RULES_LEGEND;
            break; 
----------------------- END   ----------------------------------------




More information about the rrd-developers mailing list