X-Git-Url: https://git.octo.it/?p=rrdtool.git;a=blobdiff_plain;f=src%2Frrd_graph.c;h=d1343b989e0d2aa4a4200c9a6dcc31c04285d803;hp=f33cfc3dd8b981f1f820cb3494f6dc237a8c7bb4;hb=591bd42a2c2ef80a7e1753e00001ea0633978660;hpb=657d850f957a2dd703e3aab2d7cde4b0f9711c15 diff --git a/src/rrd_graph.c b/src/rrd_graph.c index f33cfc3..d1343b9 100644 --- a/src/rrd_graph.c +++ b/src/rrd_graph.c @@ -1,15 +1,18 @@ /**************************************************************************** - * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007 + * RRDtool 1.3.2 Copyright by Tobi Oetiker, 1997-2008 **************************************************************************** * rrd__graph.c produce graphs from data in rrdfiles ****************************************************************************/ #include +#include #ifdef WIN32 #include "strftime.h" +#include "plbasename.h" #endif + #include "rrd_tool.h" #if defined(WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__) @@ -26,6 +29,7 @@ #endif #include "rrd_graph.h" +#include "rrd_client.h" /* some constant definitions */ @@ -33,19 +37,21 @@ #ifndef RRD_DEFAULT_FONT /* there is special code later to pick Cour.ttf when running on windows */ -#define RRD_DEFAULT_FONT "DejaVuSansMono-Roman.ttf" +#define RRD_DEFAULT_FONT "DejaVu Sans Mono,Bitstream Vera Sans Mono,monospace,Courier" #endif text_prop_t text_prop[] = { - {8.0, RRD_DEFAULT_FONT} + {8.0, RRD_DEFAULT_FONT,NULL} , /* default */ - {9.0, RRD_DEFAULT_FONT} + {9.0, RRD_DEFAULT_FONT,NULL} , /* title */ - {7.0, RRD_DEFAULT_FONT} + {7.0, RRD_DEFAULT_FONT,NULL} , /* axis */ - {8.0, RRD_DEFAULT_FONT} + {8.0, RRD_DEFAULT_FONT,NULL} , /* unit */ - {8.0, RRD_DEFAULT_FONT} /* legend */ + {8.0, RRD_DEFAULT_FONT,NULL} /* legend */ + , + {5.5, RRD_DEFAULT_FONT,NULL} /* watermark */ }; xlab_t xlab[] = { @@ -61,7 +67,7 @@ xlab_t xlab[] = { , {60, 0, TMT_MINUTE, 30, TMT_HOUR, 2, TMT_HOUR, 2, 0, "%H:%M"} , - {60, 24 * 3600, TMT_MINUTE, 30, TMT_HOUR, 2, TMT_HOUR, 4, 0, "%a %H:%M"} + {60, 24 * 3600, TMT_MINUTE, 30, TMT_HOUR, 2, TMT_HOUR, 6, 0, "%a %H:%M"} , {180, 0, TMT_HOUR, 1, TMT_HOUR, 6, TMT_HOUR, 6, 0, "%H:%M"} , @@ -140,16 +146,17 @@ ylab_t ylab[] = { gfx_color_t graph_col[] = /* default colors */ -{ 0xFFFFFFFF, /* canvas */ - 0xF0F0F0FF, /* background */ - 0xD0D0D0FF, /* shade A */ - 0xA0A0A0FF, /* shade B */ - 0x90909080, /* grid */ - 0xE0505080, /* major grid */ - 0x000000FF, /* font */ - 0x802020FF, /* arrow */ - 0x202020FF, /* axis */ - 0x000000FF /* frame */ +{ + {1.00, 1.00, 1.00, 1.00}, /* canvas */ + {0.95, 0.95, 0.95, 1.00}, /* background */ + {0.81, 0.81, 0.81, 1.00}, /* shade A */ + {0.62, 0.62, 0.62, 1.00}, /* shade B */ + {0.56, 0.56, 0.56, 0.75}, /* grid */ + {0.87, 0.31, 0.31, 0.60}, /* major grid */ + {0.00, 0.00, 0.00, 1.00}, /* font */ + {0.50, 0.12, 0.12, 1.00}, /* arrow */ + {0.12, 0.12, 0.12, 1.00}, /* axis */ + {0.00, 0.00, 0.00, 1.00} /* frame */ }; @@ -200,16 +207,6 @@ double ytr( yval = im->yorigin - pixie * (log10(value) - log10(im->minval)); } } - /* make sure we don't return anything too unreasonable. GD lib can - get terribly slow when drawing lines outside its scope. This is - especially problematic in connection with the rigid option */ - if (!im->rigid) { - /* keep yval as-is */ - } else if (yval > im->yorigin) { - yval = im->yorigin + 0.00001; - } else if (yval < im->yorigin - im->ysize) { - yval = im->yorigin - im->ysize - 0.00001; - } return yval; } @@ -225,81 +222,80 @@ enum gf_en gf_conv( char *string) { - conv_if(PRINT, GF_PRINT) - conv_if(GPRINT, GF_GPRINT) - conv_if(COMMENT, GF_COMMENT) - conv_if(HRULE, GF_HRULE) - conv_if(VRULE, GF_VRULE) - conv_if(LINE, GF_LINE) - conv_if(AREA, GF_AREA) - conv_if(STACK, GF_STACK) - conv_if(TICK, GF_TICK) - conv_if(DEF, GF_DEF) - conv_if(CDEF, GF_CDEF) - conv_if(VDEF, GF_VDEF) -#ifdef WITH_PIECHART - conv_if(PART, GF_PART) -#endif - conv_if(XPORT, GF_XPORT) - conv_if(SHIFT, GF_SHIFT) - - return (-1); + conv_if(PRINT, GF_PRINT); + conv_if(GPRINT, GF_GPRINT); + conv_if(COMMENT, GF_COMMENT); + conv_if(HRULE, GF_HRULE); + conv_if(VRULE, GF_VRULE); + conv_if(LINE, GF_LINE); + conv_if(AREA, GF_AREA); + conv_if(STACK, GF_STACK); + conv_if(TICK, GF_TICK); + conv_if(TEXTALIGN, GF_TEXTALIGN); + conv_if(DEF, GF_DEF); + conv_if(CDEF, GF_CDEF); + conv_if(VDEF, GF_VDEF); + conv_if(XPORT, GF_XPORT); + conv_if(SHIFT, GF_SHIFT); + + return (enum gf_en)(-1); } enum gfx_if_en if_conv( char *string) { - conv_if(PNG, IF_PNG) - conv_if(SVG, IF_SVG) - conv_if(EPS, IF_EPS) - conv_if(PDF, IF_PDF) + conv_if(PNG, IF_PNG); + conv_if(SVG, IF_SVG); + conv_if(EPS, IF_EPS); + conv_if(PDF, IF_PDF); - return (-1); + return (enum gfx_if_en)(-1); } enum tmt_en tmt_conv( char *string) { - conv_if(SECOND, TMT_SECOND) - conv_if(MINUTE, TMT_MINUTE) - conv_if(HOUR, TMT_HOUR) - conv_if(DAY, TMT_DAY) - conv_if(WEEK, TMT_WEEK) - conv_if(MONTH, TMT_MONTH) - conv_if(YEAR, TMT_YEAR) - return (-1); + conv_if(SECOND, TMT_SECOND); + conv_if(MINUTE, TMT_MINUTE); + conv_if(HOUR, TMT_HOUR); + conv_if(DAY, TMT_DAY); + conv_if(WEEK, TMT_WEEK); + conv_if(MONTH, TMT_MONTH); + conv_if(YEAR, TMT_YEAR); + return (enum tmt_en)(-1); } enum grc_en grc_conv( char *string) { - conv_if(BACK, GRC_BACK) - conv_if(CANVAS, GRC_CANVAS) - conv_if(SHADEA, GRC_SHADEA) - conv_if(SHADEB, GRC_SHADEB) - conv_if(GRID, GRC_GRID) - conv_if(MGRID, GRC_MGRID) - conv_if(FONT, GRC_FONT) - conv_if(ARROW, GRC_ARROW) - conv_if(AXIS, GRC_AXIS) - conv_if(FRAME, GRC_FRAME) - - return -1; + conv_if(BACK, GRC_BACK); + conv_if(CANVAS, GRC_CANVAS); + conv_if(SHADEA, GRC_SHADEA); + conv_if(SHADEB, GRC_SHADEB); + conv_if(GRID, GRC_GRID); + conv_if(MGRID, GRC_MGRID); + conv_if(FONT, GRC_FONT); + conv_if(ARROW, GRC_ARROW); + conv_if(AXIS, GRC_AXIS); + conv_if(FRAME, GRC_FRAME); + + return (enum grc_en)(-1); } enum text_prop_en text_prop_conv( char *string) { - conv_if(DEFAULT, TEXT_PROP_DEFAULT) - conv_if(TITLE, TEXT_PROP_TITLE) - conv_if(AXIS, TEXT_PROP_AXIS) - conv_if(UNIT, TEXT_PROP_UNIT) - conv_if(LEGEND, TEXT_PROP_LEGEND) - return -1; + conv_if(DEFAULT, TEXT_PROP_DEFAULT); + conv_if(TITLE, TEXT_PROP_TITLE); + conv_if(AXIS, TEXT_PROP_AXIS); + conv_if(UNIT, TEXT_PROP_UNIT); + conv_if(LEGEND, TEXT_PROP_LEGEND); + conv_if(WATERMARK, TEXT_PROP_WATERMARK); + return (enum text_prop_en)(-1); } @@ -309,9 +305,14 @@ int im_free( image_desc_t *im) { unsigned long i, ii; + cairo_status_t status = (cairo_status_t) 0; if (im == NULL) return 0; + + if (im->daemon_addr != NULL) + free(im->daemon_addr); + for (i = 0; i < (unsigned) im->gdes_c; i++) { if (im->gdes[i].data_first) { /* careful here, because a single pointer can occur several times */ @@ -322,11 +323,36 @@ int im_free( free(im->gdes[i].ds_namv); } } + /* free allocated memory used for dashed lines */ + if (im->gdes[i].p_dashes != NULL) + free(im->gdes[i].p_dashes); + free(im->gdes[i].p_data); free(im->gdes[i].rpnp); } free(im->gdes); - gfx_destroy(im->canvas); + if (im->font_options) + cairo_font_options_destroy(im->font_options); + + if (im->cr) { + status = cairo_status(im->cr); + cairo_destroy(im->cr); + } + if (im->rendered_image) { + free(im->rendered_image); + } + + if (im->layout) { + g_object_unref (im->layout); + } + + if (im->surface) + cairo_surface_destroy(im->surface); + + if (status) + fprintf(stderr, "OOPS: Cairo has issues it can't even die: %s\n", + cairo_status_to_string(status)); + return 0; } @@ -403,7 +429,7 @@ void si_unit( if (im->unitsexponent != 9999) { /* unitsexponent = 9, 6, 3, 0, -3, -6, -9, etc */ - viewdigits = floor(im->unitsexponent / 3); + viewdigits = floor((double)(im->unitsexponent / 3)); } else { viewdigits = digits; } @@ -520,6 +546,7 @@ void expand_range( #endif } + void apply_gridfit( image_desc_t *im) { @@ -704,6 +731,7 @@ void reduce_data( else { switch (cf) { case CF_HWPREDICT: + case CF_MHWPREDICT: case CF_DEVSEASONAL: case CF_DEVPREDICT: case CF_SEASONAL: @@ -729,6 +757,7 @@ void reduce_data( } else { switch (cf) { case CF_HWPREDICT: + case CF_MHWPREDICT: case CF_DEVSEASONAL: case CF_DEVPREDICT: case CF_SEASONAL: @@ -812,6 +841,36 @@ int data_fetch( if (!skip) { unsigned long ft_step = im->gdes[i].step; /* ft_step will record what we got from fetch */ + /* Flush the file if + * - a connection to the daemon has been established + * - this is the first occurrence of that RRD file + */ + if (rrdc_is_connected(im->daemon_addr)) + { + int status; + + status = 0; + for (ii = 0; ii < i; ii++) + { + if (strcmp (im->gdes[i].rrd, im->gdes[ii].rrd) == 0) + { + status = 1; + break; + } + } + + if (status == 0) + { + status = rrdc_flush (im->gdes[i].rrd); + if (status != 0) + { + rrd_set_error ("rrdc_flush (%s) failed with status %i.", + im->gdes[i].rrd, status); + return (-1); + } + } + } /* if (rrdc_is_connected()) */ + if ((rrd_fetch_fn(im->gdes[i].rrd, im->gdes[i].cf, &im->gdes[i].start, @@ -882,7 +941,7 @@ long find_var( return -1; } -/* find the largest common denominator for all the numbers +/* find the greatest common divisor for all the numbers in the 0 terminated num array */ long lcd( long *num) @@ -996,7 +1055,7 @@ int data_calc( /* add one entry to the array that keeps track of the step sizes of the * data sources going into the CDEF. */ if ((steparray = - rrd_realloc(steparray, + (long*)rrd_realloc(steparray, (++stepcnt + 1) * sizeof(*steparray))) == NULL) { rrd_set_error("realloc steparray"); @@ -1064,7 +1123,7 @@ int data_calc( */ im->gdes[gdi].step = lcd(steparray); free(steparray); - if ((im->gdes[gdi].data = malloc(((im->gdes[gdi].end - + if ((im->gdes[gdi].data = (rrd_value_t*)malloc(((im->gdes[gdi].end - im->gdes[gdi].start) / im->gdes[gdi].step) * sizeof(double))) == NULL) { @@ -1100,6 +1159,43 @@ int data_calc( return 0; } +/* from http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm */ +/* yes we are loosing precision by doing tos with floats instead of doubles + but it seems more stable this way. */ + +static int AlmostEqual2sComplement( + float A, + float B, + int maxUlps) +{ + + int aInt = *(int *) &A; + int bInt = *(int *) &B; + int intDiff; + + /* Make sure maxUlps is non-negative and small enough that the + default NAN won't compare as equal to anything. */ + + /* assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024); */ + + /* Make aInt lexicographically ordered as a twos-complement int */ + + if (aInt < 0) + aInt = 0x80000000l - aInt; + + /* Make bInt lexicographically ordered as a twos-complement int */ + + if (bInt < 0) + bInt = 0x80000000l - bInt; + + intDiff = abs(aInt - bInt); + + if (intDiff <= maxUlps) + return 1; + + return 0; +} + /* massage data so, that we get one value for each x coordinate in the graph */ int data_proc( image_desc_t *im) @@ -1117,7 +1213,7 @@ int data_proc( for (i = 0; i < im->gdes_c; i++) { if ((im->gdes[i].gf == GF_LINE) || (im->gdes[i].gf == GF_AREA) || (im->gdes[i].gf == GF_TICK)) { - if ((im->gdes[i].p_data = malloc((im->xsize + 1) + if ((im->gdes[i].p_data = (rrd_value_t*)malloc((im->xsize + 1) * sizeof(rrd_value_t))) == NULL) { rrd_set_error("malloc data_proc"); return -1; @@ -1200,24 +1296,29 @@ int data_proc( lets set these to dummy values then ... */ if (im->logarithmic) { - if (isnan(minval)) - minval = 0.2; - if (isnan(maxval)) + if (isnan(minval) || isnan(maxval) || maxval <= 0) { + minval = 0.0; /* catching this right away below */ maxval = 5.1; + } + /* in logarithm mode, where minval is smaller or equal + to 0 make the beast just way smaller than maxval */ + if (minval <= 0) { + minval = maxval / 10e8; + } } else { - if (isnan(minval)) + if (isnan(minval) || isnan(maxval)) { minval = 0.0; - if (isnan(maxval)) maxval = 1.0; + } } - /* adjust min and max values */ + /* adjust min and max values given by the user */ + /* for logscale we add something on top */ if (isnan(im->minval) - /* don't adjust low-end with log scale *//* why not? */ || ((!im->rigid) && im->minval > minval) ) { if (im->logarithmic) - im->minval = minval * 0.5; + im->minval = minval / 2.0; else im->minval = minval; } @@ -1229,19 +1330,24 @@ int data_proc( else im->maxval = maxval; } + /* make sure min is smaller than max */ if (im->minval > im->maxval) { - im->minval = 0.99 * im->maxval; + if (im->minval > 0) + im->minval = 0.99 * im->maxval; + else + im->minval = 1.01 * im->maxval; } /* make sure min and max are not equal */ - if (im->minval == im->maxval) { - im->maxval *= 1.01; - if (!im->logarithmic) { - im->minval *= 0.99; - } + if (AlmostEqual2sComplement(im->minval, im->maxval, 4)) { + if (im->maxval > 0) + im->maxval *= 1.01; + else + im->maxval *= 0.99; + /* make sure min and max are not both zero */ - if (im->maxval == 0.0) { + if (AlmostEqual2sComplement(im->maxval, 0, 4)) { im->maxval = 1.0; } } @@ -1370,8 +1476,7 @@ time_t find_next_time( /* calculate values required for PRINT and GPRINT functions */ int print_calc( - image_desc_t *im, - char ***prdata) + image_desc_t *im) { long i, ii, validsteps; double printval; @@ -1382,24 +1487,16 @@ int print_calc( double magfact = -1; char *si_symb = ""; char *percent_s; - int prlines = 1; + int prline_cnt = 0; /* wow initializing tmvdef is quite a task :-) */ time_t now = time(NULL); localtime_r(&now, &tmvdef); - if (im->imginfo) - prlines++; for (i = 0; i < im->gdes_c; i++) { vidx = im->gdes[i].vidx; switch (im->gdes[i].gf) { case GF_PRINT: - prlines++; - if (((*prdata) = - rrd_realloc((*prdata), prlines * sizeof(char *))) == NULL) { - rrd_set_error("realloc prdata"); - return 0; - } case GF_GPRINT: /* PRINT and GPRINT can now print VDEF generated values. * There's no need to do any calculations on them as these @@ -1425,6 +1522,7 @@ int print_calc( switch (im->gdes[i].cf) { case CF_HWPREDICT: + case CF_MHWPREDICT: case CF_DEVPREDICT: case CF_DEVSEASONAL: case CF_SEASONAL: @@ -1469,44 +1567,44 @@ int print_calc( } if (im->gdes[i].gf == GF_PRINT) { - (*prdata)[prlines - 2] = - malloc((FMT_LEG_LEN + 2) * sizeof(char)); - (*prdata)[prlines - 1] = NULL; + rrd_infoval_t prline; + if (im->gdes[i].strftm) { - strftime((*prdata)[prlines - 2], FMT_LEG_LEN, - im->gdes[i].format, &tmvdef); + prline.u_str = (char*)malloc((FMT_LEG_LEN + 2) * sizeof(char)); + strftime(prline.u_str, + FMT_LEG_LEN, im->gdes[i].format, &tmvdef); + } else if (bad_format(im->gdes[i].format)) { + rrd_set_error + ("bad format for PRINT in '%s'", im->gdes[i].format); + return -1; } else { - if (bad_format(im->gdes[i].format)) { - rrd_set_error("bad format for PRINT in '%s'", - im->gdes[i].format); - return -1; - } -#ifdef HAVE_SNPRINTF - snprintf((*prdata)[prlines - 2], FMT_LEG_LEN, - im->gdes[i].format, printval, si_symb); -#else - sprintf((*prdata)[prlines - 2], im->gdes[i].format, - printval, si_symb); -#endif + prline.u_str = + sprintf_alloc(im->gdes[i].format, printval, si_symb); } + grinfo_push(im, + sprintf_alloc + ("print[%ld]", prline_cnt++), RD_I_STR, prline); + free(prline.u_str); } else { /* GF_GPRINT */ if (im->gdes[i].strftm) { - strftime(im->gdes[i].legend, FMT_LEG_LEN, - im->gdes[i].format, &tmvdef); + strftime(im->gdes[i].legend, + FMT_LEG_LEN, im->gdes[i].format, &tmvdef); } else { if (bad_format(im->gdes[i].format)) { - rrd_set_error("bad format for GPRINT in '%s'", - im->gdes[i].format); + rrd_set_error + ("bad format for GPRINT in '%s'", + im->gdes[i].format); return -1; } #ifdef HAVE_SNPRINTF - snprintf(im->gdes[i].legend, FMT_LEG_LEN - 2, + snprintf(im->gdes[i].legend, + FMT_LEG_LEN - 2, im->gdes[i].format, printval, si_symb); #else - sprintf(im->gdes[i].legend, im->gdes[i].format, printval, - si_symb); + sprintf(im->gdes[i].legend, + im->gdes[i].format, printval, si_symb); #endif } graphelement = 1; @@ -1530,6 +1628,7 @@ int print_calc( graphelement = 1; break; case GF_COMMENT: + case GF_TEXTALIGN: case GF_DEF: case GF_CDEF: case GF_VDEF: @@ -1552,50 +1651,57 @@ int print_calc( /* place legends with color spots */ int leg_place( - image_desc_t *im) + image_desc_t *im, + int *gY) { /* 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; int leg_c = 0; - int leg_x = border, leg_y = im->yimg; + double leg_x = border; + int leg_y = im->yimg; int leg_y_prev = im->yimg; int leg_cc; - int glue = 0; + double glue = 0; int i, ii, mark = 0; - char prt_fctn; /*special printfunctions */ + char default_txtalign = TXA_JUSTIFIED; /*default line orientation */ int *legspace; + char *tab; if (!(im->extra_flags & NOLEGEND) & !(im->extra_flags & ONLY_GRAPH)) { - if ((legspace = malloc(im->gdes_c * sizeof(int))) == NULL) { + if ((legspace = (int*)malloc(im->gdes_c * sizeof(int))) == NULL) { rrd_set_error("malloc for legspace"); return -1; } for (i = 0; i < im->gdes_c; i++) { + char prt_fctn; /*special printfunctions */ fill_last = fill; - - /* hid legends for rules which are not displayed */ + /* hide legends for rules which are not displayed */ + if (im->gdes[i].gf == GF_TEXTALIGN) { + default_txtalign = im->gdes[i].txtalign; + } if (!(im->extra_flags & FORCE_RULES_LEGEND)) { - if (im->gdes[i].gf == GF_HRULE && - (im->gdes[i].yrule < im->minval - || im->gdes[i].yrule > im->maxval)) + if (im->gdes[i].gf == GF_HRULE + && (im->gdes[i].yrule < + im->minval || im->gdes[i].yrule > im->maxval)) im->gdes[i].legend[0] = '\0'; - - if (im->gdes[i].gf == GF_VRULE && - (im->gdes[i].xrule < im->start - || im->gdes[i].xrule > im->end)) + if (im->gdes[i].gf == GF_VRULE + && (im->gdes[i].xrule < + im->start || im->gdes[i].xrule > im->end)) im->gdes[i].legend[0] = '\0'; } + /* turn \\t into tab */ + while ((tab = strstr(im->gdes[i].legend, "\\t"))) { + memmove(tab, tab + 1, strlen(tab)); + tab[0] = (char) 9; + } leg_cc = strlen(im->gdes[i].legend); - - /* is there a controle code ant the end of the legend string ? */ - /* and it is not a tab \\t */ - if (leg_cc >= 2 && im->gdes[i].legend[leg_cc - 2] == '\\' - && im->gdes[i].legend[leg_cc - 1] != 't') { + /* is there a controle code at the end of the legend string ? */ + if (leg_cc >= 2 && im->gdes[i].legend[leg_cc - 2] == '\\') { prt_fctn = im->gdes[i].legend[leg_cc - 1]; leg_cc -= 2; im->gdes[i].legend[leg_cc] = '\0'; @@ -1607,38 +1713,40 @@ int leg_place( prt_fctn != 'r' && prt_fctn != 'j' && prt_fctn != 'c' && - prt_fctn != 's' && - prt_fctn != 't' && prt_fctn != '\0' && prt_fctn != 'g') { + prt_fctn != 's' && prt_fctn != '\0' && prt_fctn != 'g') { free(legspace); - rrd_set_error("Unknown control code at the end of '%s\\%c'", - im->gdes[i].legend, prt_fctn); + rrd_set_error + ("Unknown control code at the end of '%s\\%c'", + im->gdes[i].legend, prt_fctn); return -1; - } - - /* remove exess space */ + /* \n -> \l */ if (prt_fctn == 'n') { prt_fctn = 'l'; } + /* remove exess space from the end of the legend for \g */ while (prt_fctn == 'g' && leg_cc > 0 && im->gdes[i].legend[leg_cc - 1] == ' ') { leg_cc--; im->gdes[i].legend[leg_cc] = '\0'; } + if (leg_cc != 0) { - legspace[i] = (prt_fctn == 'g' ? 0 : interleg); + /* no interleg space if string ends in \g */ + legspace[i] = (prt_fctn == 'g' ? 0 : interleg); if (fill > 0) { - /* no interleg space if string ends in \g */ fill += legspace[i]; } - fill += gfx_get_text_width(im->canvas, fill + border, - im->text_prop[TEXT_PROP_LEGEND]. - font, - im->text_prop[TEXT_PROP_LEGEND]. - size, im->tabwidth, - im->gdes[i].legend, 0); + fill += + gfx_get_text_width(im, + fill + border, + im-> + text_prop + [TEXT_PROP_LEGEND]. + font_desc, + im->tabwidth, im->gdes[i].legend); leg_c++; } else { legspace[i] = 0; @@ -1647,10 +1755,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) { @@ -1658,11 +1781,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'; } } @@ -1670,32 +1792,30 @@ int leg_place( if (prt_fctn != '\0') { leg_x = border; if (leg_c >= 2 && prt_fctn == 'j') { - glue = (im->ximg - fill - 2 * border) / (leg_c - 1); + glue = (double)(im->ximg - fill - 2 * border) / (double)(leg_c - 1); } else { glue = 0; } if (prt_fctn == 'c') - leg_x = (im->ximg - fill) / 2.0; + leg_x = (double)(im->ximg - fill) / 2.0; if (prt_fctn == 'r') leg_x = im->ximg - 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; leg_x += - gfx_get_text_width(im->canvas, leg_x, - im->text_prop[TEXT_PROP_LEGEND]. - font, - im->text_prop[TEXT_PROP_LEGEND]. - size, im->tabwidth, - im->gdes[ii].legend, 0) - + legspace[ii] + (double)gfx_get_text_width(im, leg_x, + im-> + text_prop + [TEXT_PROP_LEGEND]. + font_desc, + im->tabwidth, im->gdes[ii].legend) + +(double)legspace[ii] + glue; } leg_y_prev = leg_y; - /* only add y space if there was text on the line */ if (leg_x > border || prt_fctn == 's') leg_y += im->text_prop[TEXT_PROP_LEGEND].size * 1.8; if (prt_fctn == 's') @@ -1705,10 +1825,21 @@ int leg_place( mark = ii; } } - im->yimg = leg_y_prev; - /* if we did place some legends we have to add vertical space */ - if (leg_y != im->yimg) { - im->yimg += im->text_prop[TEXT_PROP_LEGEND].size * 1.8; + + if (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; + } + 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; } free(legspace); } @@ -1721,10 +1852,9 @@ int leg_place( /* the xaxis labels are determined from the number of seconds per pixel in the requested graph */ - - int calc_horizontal_grid( - image_desc_t *im) + image_desc_t + *im) { double range; double scaledrange; @@ -1735,7 +1865,6 @@ int calc_horizontal_grid( im->ygrid_scale.labfact = 2; range = im->maxval - im->minval; scaledrange = range / im->magfact; - /* does the scale of this graph make it impossible to put lines on it? If so, give up. */ if (isnan(scaledrange)) { @@ -1753,22 +1882,23 @@ int calc_horizontal_grid( im->viewfactor / im->magfact)); if (decimals <= 0) /* everything is small. make place for zero */ decimals = 1; - im->ygrid_scale.gridstep = pow((double) 10, floor(log10(range * im->viewfactor / im->magfact))) / im->viewfactor * im->magfact; - if (im->ygrid_scale.gridstep == 0) /* range is one -> 0.1 is reasonable scale */ im->ygrid_scale.gridstep = 0.1; /* should have at least 5 lines but no more then 15 */ - if (range / im->ygrid_scale.gridstep < 5) + if (range / im->ygrid_scale.gridstep < 5 + && im->ygrid_scale.gridstep >= 30) im->ygrid_scale.gridstep /= 10; if (range / im->ygrid_scale.gridstep > 15) im->ygrid_scale.gridstep *= 10; if (range / im->ygrid_scale.gridstep > 5) { im->ygrid_scale.labfact = 1; - if (range / im->ygrid_scale.gridstep > 8) + if (range / im->ygrid_scale.gridstep > 8 + || im->ygrid_scale.gridstep < + 1.8 * im->text_prop[TEXT_PROP_AXIS].size) im->ygrid_scale.labfact = 2; } else { im->ygrid_scale.gridstep /= 5; @@ -1784,27 +1914,28 @@ int calc_horizontal_grid( if (im->unitslength < len + 2) im->unitslength = len + 2; - sprintf(im->ygrid_scale.labfmt, "%%%d.%df%s", len, + sprintf(im->ygrid_scale.labfmt, + "%%%d.%df%s", len, -fractionals, (im->symbol != ' ' ? " %c" : "")); } else { int len = decimals + 1; if (im->unitslength < len + 2) im->unitslength = len + 2; - sprintf(im->ygrid_scale.labfmt, "%%%d.0f%s", len, - (im->symbol != ' ' ? " %c" : "")); + sprintf(im->ygrid_scale.labfmt, + "%%%d.0f%s", len, (im->symbol != ' ' ? " %c" : "")); } - } else { + } else { /* classic rrd grid */ for (i = 0; ylab[i].grid > 0; i++) { pixel = im->ysize / (scaledrange / ylab[i].grid); gridind = i; - if (pixel > 7) + if (pixel >= 5) break; } for (i = 0; i < 4; i++) { if (pixel * ylab[gridind].lfac[i] >= - 2.5 * im->text_prop[TEXT_PROP_AXIS].size) { + 1.8 * im->text_prop[TEXT_PROP_AXIS].size) { im->ygrid_scale.labfact = ylab[gridind].lfac[i]; break; } @@ -1820,7 +1951,8 @@ int calc_horizontal_grid( } int draw_horizontal_grid( - image_desc_t *im) + image_desc_t + *im) { int i; double scaledstep; @@ -1828,21 +1960,24 @@ int draw_horizontal_grid( int nlabels = 0; double X0 = im->xorigin; double X1 = im->xorigin + im->xsize; - int sgrid = (int) (im->minval / im->ygrid_scale.gridstep - 1); int egrid = (int) (im->maxval / im->ygrid_scale.gridstep + 1); double MaxY; - + double second_axis_magfact = 0; + char *second_axis_symb = ""; + scaledstep = - im->ygrid_scale.gridstep / (double) im->magfact * - (double) im->viewfactor; + im->ygrid_scale.gridstep / + (double) im->magfact * (double) im->viewfactor; MaxY = scaledstep * (double) egrid; for (i = sgrid; i <= egrid; i++) { - double Y0 = ytr(im, im->ygrid_scale.gridstep * i); - double YN = ytr(im, im->ygrid_scale.gridstep * (i + 1)); + double Y0 = ytr(im, + im->ygrid_scale.gridstep * i); + double YN = ytr(im, + im->ygrid_scale.gridstep * (i + 1)); - if (floor(Y0 + 0.5) >= im->yorigin - im->ysize - && floor(Y0 + 0.5) <= im->yorigin) { + if (floor(Y0 + 0.5) >= + im->yorigin - im->ysize && floor(Y0 + 0.5) <= im->yorigin) { /* Make sure at least 2 grid labels are shown, even if it doesn't agree with the chosen settings. Add a label if required by settings, or if there is only one label so far and the next grid line is out of bounds. */ @@ -1851,7 +1986,8 @@ int draw_horizontal_grid( && (YN < im->yorigin - im->ysize || YN > im->yorigin))) { if (im->symbol == ' ') { if (im->extra_flags & ALTYGRID) { - sprintf(graph_label, im->ygrid_scale.labfmt, + sprintf(graph_label, + im->ygrid_scale.labfmt, scaledstep * (double) i); } else { if (MaxY < 10) { @@ -1866,7 +2002,8 @@ int draw_horizontal_grid( char sisym = (i == 0 ? ' ' : im->symbol); if (im->extra_flags & ALTYGRID) { - sprintf(graph_label, im->ygrid_scale.labfmt, + sprintf(graph_label, + im->ygrid_scale.labfmt, scaledstep * (double) i, sisym); } else { if (MaxY < 10) { @@ -1879,27 +2016,67 @@ 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); - + if (im->second_axis_scale != 0){ + char graph_label_right[100]; + double sval = im->ygrid_scale.gridstep*(double)i*im->second_axis_scale+im->second_axis_shift; + if (im->second_axis_format[0] == '\0'){ + if (!second_axis_magfact){ + double dummy = im->ygrid_scale.gridstep*(double)(sgrid+egrid)/2.0*im->second_axis_scale+im->second_axis_shift; + auto_scale(im,&dummy,&second_axis_symb,&second_axis_magfact); + } + sval /= second_axis_magfact; + + if(MaxY < 10) { + sprintf(graph_label_right,"%5.1f %s",sval,second_axis_symb); + } else { + sprintf(graph_label_right,"%5.0f %s",sval,second_axis_symb); + } + } + else { + sprintf(graph_label_right,im->second_axis_format,sval); + } + gfx_text ( im, + X1+7, Y0, + im->graph_col[GRC_FONT], + im->text_prop[TEXT_PROP_AXIS].font_desc, + im->tabwidth,0.0, GFX_H_LEFT, GFX_V_CENTER, + graph_label_right ); + } + + gfx_text(im, + X0 - + im-> + text_prop[TEXT_PROP_AXIS]. + size, Y0, + im->graph_col[GRC_FONT], + im-> + text_prop[TEXT_PROP_AXIS]. + font_desc, + im->tabwidth, 0.0, + GFX_H_RIGHT, GFX_V_CENTER, graph_label); + gfx_line(im, X0 - 2, Y0, X0, Y0, + MGRIDWIDTH, im->graph_col[GRC_MGRID]); + gfx_line(im, X1, Y0, X1 + 2, Y0, + MGRIDWIDTH, im->graph_col[GRC_MGRID]); + gfx_dashed_line(im, X0 - 2, Y0, + X1 + 2, Y0, + MGRIDWIDTH, + im-> + graph_col + [GRC_MGRID], + im->grid_dash_on, im->grid_dash_off); } else if (!(im->extra_flags & NOMINOR)) { - gfx_new_dashed_line(im->canvas, - X0 - 1, Y0, - X1 + 1, Y0, - GRIDWIDTH, im->graph_col[GRC_GRID], - im->grid_dash_on, im->grid_dash_off); - + gfx_line(im, + X0 - 2, Y0, + X0, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]); + gfx_line(im, X1, Y0, X1 + 2, Y0, + GRIDWIDTH, im->graph_col[GRC_GRID]); + gfx_dashed_line(im, X0 - 1, Y0, + X1 + 1, Y0, + GRIDWIDTH, + im-> + graph_col[GRC_GRID], + im->grid_dash_on, im->grid_dash_off); } } } @@ -1917,7 +2094,7 @@ double frexp10( double mnt; int iexp; - iexp = floor(log(fabs(x)) / log(10)); + iexp = floor(log((double)fabs(x)) / log((double)10)); mnt = x / pow(10.0, iexp); if (mnt >= 10.0) { iexp++; @@ -1927,52 +2104,37 @@ double frexp10( return mnt; } -static int AlmostEqual2sComplement( - float A, - float B, - int maxUlps) -{ - - int aInt = *(int *) &A; - int bInt = *(int *) &B; - int intDiff; - - /* Make sure maxUlps is non-negative and small enough that the - default NAN won't compare as equal to anything. */ - - /* assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024); */ - - /* Make aInt lexicographically ordered as a twos-complement int */ - - if (aInt < 0) - aInt = 0x80000000l - aInt; - - /* Make bInt lexicographically ordered as a twos-complement int */ - - if (bInt < 0) - bInt = 0x80000000l - bInt; - - intDiff = abs(aInt - bInt); - - if (intDiff <= maxUlps) - return 1; - - return 0; -} /* logaritmic horizontal grid */ int horizontal_log_grid( - image_desc_t *im) + image_desc_t + *im) { double yloglab[][10] = { - {1.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, - {1.0, 5.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, - {1.0, 2.0, 5.0, 7.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0}, - {1.0, 2.0, 4.0, 6.0, 8.0, 10., 0.0, 0.0, 0.0, 0.0}, - {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} /* last line */ + { + 1.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0}, { + 1.0, 5.0, 10., 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0}, { + 1.0, 2.0, 5.0, 7.0, 10., 0.0, 0.0, + 0.0, 0.0, 0.0}, { + 1.0, 2.0, 4.0, + 6.0, 8.0, 10., + 0.0, + 0.0, 0.0, 0.0}, { + 1.0, + 2.0, + 3.0, + 4.0, + 5.0, + 6.0, + 7.0, + 8.0, + 9.0, + 10.}, + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} /* last line */ }; - int i, j, val_exp, min_exp; double nex; /* number of decades in data */ double logscale; /* scale in logarithmic space */ @@ -1986,7 +2148,6 @@ int horizontal_log_grid( nex = log10(im->maxval / im->minval); logscale = im->ysize / nex; - /* major spacing for data with high dynamic range */ while (logscale * exfrac < 3 * im->text_prop[TEXT_PROP_LEGEND].size) { if (exfrac == 1) @@ -2001,11 +2162,11 @@ int horizontal_log_grid( mid++; for (i = 0; yloglab[mid][i + 1] < 10.0; i++); mspac = logscale * log10(10.0 / yloglab[mid][i]); - } while (mspac > 2 * im->text_prop[TEXT_PROP_LEGEND].size - && yloglab[mid][0] > 0); + } + while (mspac > + 2 * im->text_prop[TEXT_PROP_LEGEND].size && yloglab[mid][0] > 0); if (mid) mid--; - /* find first value in yloglab */ for (flab = 0; yloglab[mid][flab] < 10 @@ -2017,10 +2178,8 @@ int horizontal_log_grid( val_exp = tmp; if (val_exp % exfrac) val_exp += abs(-val_exp % exfrac); - X0 = im->xorigin; X1 = im->xorigin + im->xsize; - /* draw grid */ pre_value = DNAN; while (1) { @@ -2028,20 +2187,21 @@ int horizontal_log_grid( value = yloglab[mid][flab] * pow(10.0, val_exp); if (AlmostEqual2sComplement(value, pre_value, 4)) break; /* it seems we are not converging */ - pre_value = value; - Y0 = ytr(im, value); if (floor(Y0 + 0.5) <= im->yorigin - im->ysize) break; - /* major grid line */ - gfx_new_dashed_line(im->canvas, - X0 - 2, Y0, - X1 + 2, Y0, - MGRIDWIDTH, im->graph_col[GRC_MGRID], - im->grid_dash_on, im->grid_dash_off); - + gfx_line(im, + X0 - 2, Y0, X0, Y0, MGRIDWIDTH, im->graph_col[GRC_MGRID]); + gfx_line(im, X1, Y0, X1 + 2, Y0, + MGRIDWIDTH, im->graph_col[GRC_MGRID]); + gfx_dashed_line(im, X0 - 2, Y0, + X1 + 2, Y0, + MGRIDWIDTH, + im-> + graph_col + [GRC_MGRID], im->grid_dash_on, im->grid_dash_off); /* label */ if (im->extra_flags & FORCE_UNITS_SI) { int scale; @@ -2054,24 +2214,52 @@ int horizontal_log_grid( else pvalue = pow(10.0, ((val_exp + 1) % 3) + 2); pvalue *= yloglab[mid][flab]; - - if (((scale + si_symbcenter) < (int) sizeof(si_symbol)) && - ((scale + si_symbcenter) >= 0)) + if (((scale + si_symbcenter) < (int) sizeof(si_symbol)) + && ((scale + si_symbcenter) >= 0)) symbol = si_symbol[scale + si_symbcenter]; else symbol = '?'; - sprintf(graph_label, "%3.0f %c", pvalue, symbol); - } else + } else { sprintf(graph_label, "%3.0e", value); - gfx_new_text(im->canvas, - X0 - im->text_prop[TEXT_PROP_AXIS].size, Y0, - im->graph_col[GRC_FONT], - im->text_prop[TEXT_PROP_AXIS].font, - im->text_prop[TEXT_PROP_AXIS].size, - im->tabwidth, 0.0, GFX_H_RIGHT, GFX_V_CENTER, - graph_label); - + } + if (im->second_axis_scale != 0){ + char graph_label_right[100]; + double sval = value*im->second_axis_scale+im->second_axis_shift; + if (im->second_axis_format[0] == '\0'){ + if (im->extra_flags & FORCE_UNITS_SI) { + double mfac = 1; + char *symb = ""; + auto_scale(im,&sval,&symb,&mfac); + sprintf(graph_label_right,"%4.0f %s", sval,symb); + } + else { + sprintf(graph_label_right,"%3.0e", sval); + } + } + else { + sprintf(graph_label_right,im->second_axis_format,sval); + } + + gfx_text ( im, + X1+7, Y0, + im->graph_col[GRC_FONT], + im->text_prop[TEXT_PROP_AXIS].font_desc, + im->tabwidth,0.0, GFX_H_LEFT, GFX_V_CENTER, + graph_label_right ); + } + + gfx_text(im, + X0 - + im-> + text_prop[TEXT_PROP_AXIS]. + size, Y0, + im->graph_col[GRC_FONT], + im-> + text_prop[TEXT_PROP_AXIS]. + font_desc, + im->tabwidth, 0.0, + GFX_H_RIGHT, GFX_V_CENTER, graph_label); /* minor grid */ if (mid < 4 && exfrac == 1) { /* find first and last minor line behind current major line @@ -2093,34 +2281,42 @@ int horizontal_log_grid( value = i * pow(10.0, min_exp); if (value < im->minval) continue; - Y0 = ytr(im, value); if (floor(Y0 + 0.5) <= im->yorigin - im->ysize) break; - /* draw lines */ - gfx_new_dashed_line(im->canvas, - X0 - 1, Y0, - X1 + 1, Y0, - GRIDWIDTH, im->graph_col[GRC_GRID], - im->grid_dash_on, im->grid_dash_off); + gfx_line(im, + X0 - 2, Y0, + X0, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]); + gfx_line(im, X1, Y0, X1 + 2, Y0, + GRIDWIDTH, im->graph_col[GRC_GRID]); + gfx_dashed_line(im, X0 - 1, Y0, + X1 + 1, Y0, + GRIDWIDTH, + im-> + graph_col[GRC_GRID], + im->grid_dash_on, im->grid_dash_off); } } else if (exfrac > 1) { for (i = val_exp - exfrac / 3 * 2; i < val_exp; i += exfrac / 3) { value = pow(10.0, i); if (value < im->minval) continue; - Y0 = ytr(im, value); if (floor(Y0 + 0.5) <= im->yorigin - im->ysize) break; - /* draw lines */ - gfx_new_dashed_line(im->canvas, - X0 - 1, Y0, - X1 + 1, Y0, - GRIDWIDTH, im->graph_col[GRC_GRID], - im->grid_dash_on, im->grid_dash_off); + gfx_line(im, + X0 - 2, Y0, + X0, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]); + gfx_line(im, X1, Y0, X1 + 2, Y0, + GRIDWIDTH, im->graph_col[GRC_GRID]); + gfx_dashed_line(im, X0 - 1, Y0, + X1 + 1, Y0, + GRIDWIDTH, + im-> + graph_col[GRC_GRID], + im->grid_dash_on, im->grid_dash_off); } } @@ -2152,17 +2348,20 @@ int horizontal_log_grid( value = i * pow(10.0, min_exp); if (value < im->minval) continue; - Y0 = ytr(im, value); if (floor(Y0 + 0.5) <= im->yorigin - im->ysize) break; - /* draw lines */ - gfx_new_dashed_line(im->canvas, - X0 - 1, Y0, - X1 + 1, Y0, - GRIDWIDTH, im->graph_col[GRC_GRID], - im->grid_dash_on, im->grid_dash_off); + gfx_line(im, + X0 - 2, Y0, X0, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]); + gfx_line(im, X1, Y0, X1 + 2, Y0, + GRIDWIDTH, im->graph_col[GRC_GRID]); + gfx_dashed_line(im, X0 - 1, Y0, + X1 + 1, Y0, + GRIDWIDTH, + im-> + graph_col[GRC_GRID], + im->grid_dash_on, im->grid_dash_off); } } /* fancy minor gridlines */ @@ -2171,17 +2370,20 @@ int horizontal_log_grid( value = pow(10.0, i); if (value < im->minval) continue; - Y0 = ytr(im, value); if (floor(Y0 + 0.5) <= im->yorigin - im->ysize) break; - /* draw lines */ - gfx_new_dashed_line(im->canvas, - X0 - 1, Y0, - X1 + 1, Y0, - GRIDWIDTH, im->graph_col[GRC_GRID], - im->grid_dash_on, im->grid_dash_off); + gfx_line(im, + X0 - 2, Y0, X0, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]); + gfx_line(im, X1, Y0, X1 + 2, Y0, + GRIDWIDTH, im->graph_col[GRC_GRID]); + gfx_dashed_line(im, X0 - 1, Y0, + X1 + 1, Y0, + GRIDWIDTH, + im-> + graph_col[GRC_GRID], + im->grid_dash_on, im->grid_dash_off); } } @@ -2201,16 +2403,15 @@ void vertical_grid( /* the type of time grid is determined by finding the number of seconds per pixel in the graph */ - - if (im->xlab_user.minsec == -1) { factor = (im->end - im->start) / im->xsize; xlab_sel = 0; - while (xlab[xlab_sel + 1].minsec != -1 - && xlab[xlab_sel + 1].minsec <= factor) { + while (xlab[xlab_sel + 1].minsec != + -1 && xlab[xlab_sel + 1].minsec <= factor) { xlab_sel++; } /* pick the last one */ - while (xlab[xlab_sel - 1].minsec == xlab[xlab_sel].minsec + while (xlab[xlab_sel - 1].minsec == + xlab[xlab_sel].minsec && xlab[xlab_sel].length > (im->end - im->start)) { xlab_sel--; } /* go back to the smallest size */ @@ -2227,16 +2428,21 @@ void vertical_grid( /* y coords are the same for every line ... */ Y0 = im->yorigin; Y1 = im->yorigin - im->ysize; - - /* paint the minor grid */ if (!(im->extra_flags & NOMINOR)) { for (ti = find_first_time(im->start, - im->xlab_user.gridtm, - im->xlab_user.gridst), - timajor = find_first_time(im->start, - im->xlab_user.mgridtm, - im->xlab_user.mgridst); + im-> + xlab_user. + gridtm, + im-> + xlab_user. + gridst), + timajor = + find_first_time(im->start, + im->xlab_user. + mgridtm, + im->xlab_user. + mgridst); ti < im->end; ti = find_next_time(ti, im->xlab_user.gridtm, im->xlab_user.gridst) @@ -2246,23 +2452,33 @@ void vertical_grid( continue; while (timajor < ti) { timajor = find_next_time(timajor, - im->xlab_user.mgridtm, - im->xlab_user.mgridst); + im-> + xlab_user. + mgridtm, im->xlab_user.mgridst); } if (ti == timajor) continue; /* skip as falls on major grid line */ X0 = xtr(im, ti); - gfx_new_dashed_line(im->canvas, X0, Y0 + 1, X0, Y1 - 1, GRIDWIDTH, - im->graph_col[GRC_GRID], - im->grid_dash_on, im->grid_dash_off); - + gfx_line(im, X0, Y1 - 2, X0, Y1, + GRIDWIDTH, im->graph_col[GRC_GRID]); + gfx_line(im, X0, Y0, X0, Y0 + 2, + GRIDWIDTH, im->graph_col[GRC_GRID]); + gfx_dashed_line(im, X0, Y0 + 1, X0, + Y1 - 1, GRIDWIDTH, + im-> + graph_col[GRC_GRID], + im->grid_dash_on, im->grid_dash_off); } } /* paint the major grid */ for (ti = find_first_time(im->start, - im->xlab_user.mgridtm, - im->xlab_user.mgridst); + im-> + xlab_user. + mgridtm, + im-> + xlab_user. + mgridst); ti < im->end; ti = find_next_time(ti, im->xlab_user.mgridtm, im->xlab_user.mgridst) ) { @@ -2270,37 +2486,49 @@ void vertical_grid( if (ti < im->start || ti > im->end) continue; X0 = xtr(im, ti); - gfx_new_dashed_line(im->canvas, X0, Y0 + 3, X0, Y1 - 2, MGRIDWIDTH, - im->graph_col[GRC_MGRID], - im->grid_dash_on, im->grid_dash_off); - + gfx_line(im, X0, Y1 - 2, X0, Y1, + MGRIDWIDTH, im->graph_col[GRC_MGRID]); + gfx_line(im, X0, Y0, X0, Y0 + 3, + MGRIDWIDTH, im->graph_col[GRC_MGRID]); + gfx_dashed_line(im, X0, Y0 + 3, X0, + Y1 - 2, MGRIDWIDTH, + im-> + graph_col + [GRC_MGRID], im->grid_dash_on, im->grid_dash_off); } /* paint the labels below the graph */ - for (ti = find_first_time(im->start - im->xlab_user.precis / 2, - im->xlab_user.labtm, - im->xlab_user.labst); - ti <= im->end - im->xlab_user.precis / 2; + for (ti = + find_first_time(im->start - + im->xlab_user. + precis / 2, + im->xlab_user. + labtm, + im->xlab_user. + labst); + ti <= + im->end - + im->xlab_user.precis / 2; ti = find_next_time(ti, im->xlab_user.labtm, im->xlab_user.labst) ) { tilab = ti + im->xlab_user.precis / 2; /* correct time for the label */ /* are we inside the graph ? */ if (tilab < im->start || tilab > im->end) continue; - #if HAVE_STRFTIME localtime_r(&tilab, &tm); strftime(graph_label, 99, im->xlab_user.stst, &tm); #else # error "your libc has no strftime I guess we'll abort the exercise here." #endif - gfx_new_text(im->canvas, - xtr(im, tilab), - Y0 + im->text_prop[TEXT_PROP_AXIS].size * 1.4 + 5, - im->graph_col[GRC_FONT], - im->text_prop[TEXT_PROP_AXIS].font, - im->text_prop[TEXT_PROP_AXIS].size, im->tabwidth, 0.0, - GFX_H_CENTER, GFX_V_BOTTOM, graph_label); - + gfx_text(im, + xtr(im, tilab), + Y0 + 3, + im->graph_col[GRC_FONT], + im-> + text_prop[TEXT_PROP_AXIS]. + font_desc, + im->tabwidth, 0.0, + GFX_H_CENTER, GFX_V_TOP, graph_label); } } @@ -2310,29 +2538,41 @@ void axis_paint( image_desc_t *im) { /* draw x and y axis */ - /* gfx_new_line ( im->canvas, im->xorigin+im->xsize,im->yorigin, + /* gfx_line ( im->canvas, im->xorigin+im->xsize,im->yorigin, im->xorigin+im->xsize,im->yorigin-im->ysize, GRIDWIDTH, im->graph_col[GRC_AXIS]); - gfx_new_line ( im->canvas, im->xorigin,im->yorigin-im->ysize, + gfx_line ( im->canvas, im->xorigin,im->yorigin-im->ysize, im->xorigin+im->xsize,im->yorigin-im->ysize, GRIDWIDTH, im->graph_col[GRC_AXIS]); */ - gfx_new_line(im->canvas, im->xorigin - 4, im->yorigin, - im->xorigin + im->xsize + 4, im->yorigin, - MGRIDWIDTH, im->graph_col[GRC_AXIS]); - - gfx_new_line(im->canvas, im->xorigin, im->yorigin + 4, - im->xorigin, im->yorigin - im->ysize - 4, - MGRIDWIDTH, im->graph_col[GRC_AXIS]); - - + gfx_line(im, im->xorigin - 4, + im->yorigin, + im->xorigin + im->xsize + + 4, im->yorigin, MGRIDWIDTH, im->graph_col[GRC_AXIS]); + gfx_line(im, im->xorigin, + im->yorigin + 4, + im->xorigin, + im->yorigin - im->ysize - + 4, MGRIDWIDTH, im->graph_col[GRC_AXIS]); /* arrow for X and Y axis direction */ - gfx_new_area(im->canvas, im->xorigin + im->xsize + 2, im->yorigin - 2, im->xorigin + im->xsize + 2, im->yorigin + 3, im->xorigin + im->xsize + 7, im->yorigin + 0.5, /* LINEOFFSET */ + gfx_new_area(im, im->xorigin + im->xsize + 2, im->yorigin - 3, im->xorigin + im->xsize + 2, im->yorigin + 3, im->xorigin + im->xsize + 7, im->yorigin, /* horyzontal */ im->graph_col[GRC_ARROW]); - - gfx_new_area(im->canvas, im->xorigin - 2, im->yorigin - im->ysize - 2, im->xorigin + 3, im->yorigin - im->ysize - 2, im->xorigin + 0.5, im->yorigin - im->ysize - 7, /* LINEOFFSET */ + gfx_close_path(im); + gfx_new_area(im, im->xorigin - 3, im->yorigin - im->ysize - 2, im->xorigin + 3, im->yorigin - im->ysize - 2, im->xorigin, im->yorigin - im->ysize - 7, /* vertical */ im->graph_col[GRC_ARROW]); + gfx_close_path(im); + if (im->second_axis_scale != 0){ + gfx_line ( im, im->xorigin+im->xsize,im->yorigin+4, + im->xorigin+im->xsize,im->yorigin-im->ysize-4, + MGRIDWIDTH, im->graph_col[GRC_AXIS]); + gfx_new_area ( im, + im->xorigin+im->xsize-2, im->yorigin-im->ysize-2, + im->xorigin+im->xsize+3, im->yorigin-im->ysize-2, + im->xorigin+im->xsize, im->yorigin-im->ysize-7, /* LINEOFFSET */ + im->graph_col[GRC_ARROW]); + gfx_close_path(im); + } } @@ -2342,28 +2582,24 @@ 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) vertical_grid(im); - if (im->draw_y_grid == 1) { if (im->logarithmic) { res = horizontal_log_grid(im); @@ -2375,47 +2611,75 @@ void grid_paint( if (!res) { char *nodata = "No Data found"; - gfx_new_text(im->canvas, im->ximg / 2, - (2 * im->yorigin - im->ysize) / 2, - im->graph_col[GRC_FONT], - im->text_prop[TEXT_PROP_AXIS].font, - im->text_prop[TEXT_PROP_AXIS].size, im->tabwidth, - 0.0, GFX_H_CENTER, GFX_V_CENTER, nodata); + gfx_text(im, im->ximg / 2, + (2 * im->yorigin - + im->ysize) / 2, + im->graph_col[GRC_FONT], + im-> + text_prop[TEXT_PROP_AXIS]. + font_desc, + im->tabwidth, 0.0, + GFX_H_CENTER, GFX_V_CENTER, nodata); } } /* yaxis unit description */ - gfx_new_text(im->canvas, - 10, (im->yorigin - im->ysize / 2), + if (im->ylegend[0] != '\0'){ + gfx_text(im, + 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); - + 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->graph_col[GRC_FONT], + im->text_prop[TEXT_PROP_UNIT].font_desc, + im->tabwidth, + RRDGRAPH_YLEGEND_ANGLE, + GFX_H_CENTER, GFX_V_CENTER, + im->second_axis_legend); + } + /* graph title */ - gfx_new_text(im->canvas, - im->ximg / 2, im->text_prop[TEXT_PROP_TITLE].size * 1.3 + 4, - im->graph_col[GRC_FONT], - im->text_prop[TEXT_PROP_TITLE].font, - im->text_prop[TEXT_PROP_TITLE].size, im->tabwidth, 0.0, - GFX_H_CENTER, GFX_V_CENTER, im->title); + gfx_text(im, + im->ximg / 2, 6, + im->graph_col[GRC_FONT], + im-> + text_prop[TEXT_PROP_TITLE]. + font_desc, + im->tabwidth, 0.0, GFX_H_CENTER, GFX_V_TOP, im->title); /* rrdtool 'logo' */ - gfx_new_text(im->canvas, - im->ximg - 7, 7, - (im->graph_col[GRC_FONT] & 0xffffff00) | 0x00000044, - im->text_prop[TEXT_PROP_AXIS].font, - 5.5, im->tabwidth, 270, - GFX_H_RIGHT, GFX_V_TOP, "RRDTOOL / TOBI OETIKER"); - + if (!(im->extra_flags & NO_RRDTOOL_TAG)){ + water_color = im->graph_col[GRC_FONT]; + water_color.alpha = 0.3; + gfx_text(im, im->ximg - 4, 5, + water_color, + im-> + text_prop[TEXT_PROP_WATERMARK]. + font_desc, im->tabwidth, + -90, GFX_H_LEFT, GFX_V_TOP, "RRDTOOL / TOBI OETIKER"); + } /* graph watermark */ if (im->watermark[0] != '\0') { - gfx_new_text(im->canvas, - im->ximg / 2, im->yimg - 6, - (im->graph_col[GRC_FONT] & 0xffffff00) | 0x00000044, - im->text_prop[TEXT_PROP_AXIS].font, - 5.5, im->tabwidth, 0, - GFX_H_CENTER, GFX_V_BOTTOM, im->watermark); + gfx_text(im, + im->ximg / 2, im->yimg - 6, + water_color, + im-> + text_prop[TEXT_PROP_WATERMARK]. + font_desc, im->tabwidth, 0, + GFX_H_CENTER, GFX_V_BOTTOM, im->watermark); } /* graph labels */ @@ -2423,46 +2687,76 @@ void grid_paint( for (i = 0; i < im->gdes_c; i++) { if (im->gdes[i].legend[0] == '\0') continue; - /* im->gdes[i].leg_y is the bottom of the legend */ X0 = im->gdes[i].leg_x; Y0 = im->gdes[i].leg_y; - gfx_new_text(im->canvas, X0, Y0, - im->graph_col[GRC_FONT], - im->text_prop[TEXT_PROP_LEGEND].font, - im->text_prop[TEXT_PROP_LEGEND].size, - im->tabwidth, 0.0, GFX_H_LEFT, GFX_V_BOTTOM, - im->gdes[i].legend); + gfx_text(im, X0, Y0, + im->graph_col[GRC_FONT], + im-> + text_prop + [TEXT_PROP_LEGEND].font_desc, + im->tabwidth, 0.0, + GFX_H_LEFT, GFX_V_BOTTOM, im->gdes[i].legend); /* The legend for GRAPH items starts with "M " to have enough space for the box */ if (im->gdes[i].gf != GF_PRINT && im->gdes[i].gf != GF_GPRINT && im->gdes[i].gf != GF_COMMENT) { - int boxH, boxV; - - boxH = gfx_get_text_width(im->canvas, 0, - im->text_prop[TEXT_PROP_LEGEND]. - font, - im->text_prop[TEXT_PROP_LEGEND]. - size, im->tabwidth, "o", 0) * 1.2; - boxV = boxH * 1.1; - + double boxH, boxV; + double X1, Y1; + + boxH = gfx_get_text_width(im, 0, + im-> + text_prop + [TEXT_PROP_LEGEND]. + font_desc, + im->tabwidth, "o") * 1.2; + boxV = boxH; + /* shift the box up a bit */ + Y0 -= boxV * 0.4; /* 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); } } } @@ -2482,6 +2776,8 @@ int lazy_check( if (im->lazy == 0) return 0; /* no lazy option */ + if (strlen(im->graphfile) == 0) + return 0; /* inmemory option */ if (stat(im->graphfile, &imgstat) != 0) return 0; /* can't stat */ /* one pixel in the existing graph is more then what we would @@ -2490,7 +2786,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; @@ -2501,67 +2797,11 @@ 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 graph_size_location( + image_desc_t + *im, + int elements) { /* The actual size of the image to draw is determined from ** several sources. The size given on the command line is @@ -2569,31 +2809,9 @@ int graph_size_location( ** 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......................| - ** +-+-------------------------------------------+ - ** | watermark | - ** +---------------------------------------------+ - */ - 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; + 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; @@ -2604,281 +2822,388 @@ int graph_size_location( return 0; } + /** +---+-----------------------------------+ + ** | y |...............graph title.........| + ** | +---+-------------------------------+ + ** | a | y | | + ** | x | | | + ** | i | a | | + ** | s | x | main graph area | + ** | | i | | + ** | t | s | | + ** | i | | | + ** | t | l | | + ** | l | b +-------------------------------+ + ** | e | l | x axis labels | + ** +---+---+-------------------------------+ + ** |....................legends............| + ** +---------------------------------------+ + ** | watermark | + ** +---------------------------------------+ + */ + if (im->ylegend[0] != '\0') { Xvertical = im->text_prop[TEXT_PROP_UNIT].size * 2; } - if (im->title[0] != '\0') { /* The title is placed "inbetween" two text lines so it ** automatically has some vertical spacing. The horizontal ** spacing is added here, on each side. */ - /* don't care for the with of the title - Xtitle = gfx_get_text_width(im->canvas, 0, - im->text_prop[TEXT_PROP_TITLE].font, - im->text_prop[TEXT_PROP_TITLE].size, - im->tabwidth, - im->title, 0) + 2*Xspacing; */ + /* 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; } if (elements) { - Xmain = im->xsize; - Ymain = im->ysize; if (im->draw_x_grid) { Yxlabel = im->text_prop[TEXT_PROP_AXIS].size * 2.5; } if (im->draw_y_grid || im->forceleftspace) { - Xylabel = gfx_get_text_width(im->canvas, 0, - im->text_prop[TEXT_PROP_AXIS].font, - im->text_prop[TEXT_PROP_AXIS].size, - im->tabwidth, - "0", 0) * im->unitslength; + Xylabel = + gfx_get_text_width(im, 0, + im-> + text_prop + [TEXT_PROP_AXIS]. + font_desc, + im->tabwidth, "0") * im->unitslength; } } -#ifdef WITH_PIECHART - if (piechart) { - im->piesize = im->xsize < im->ysize ? im->xsize : im->ysize; - Xpie = im->piesize; - Ypie = im->piesize; - } -#endif - /* Now calculate the total size. Insert some spacing where - desired. im->xorigin and im->yorigin need to correspond - with the lower left corner of the main graph area or, if - this one is not set, the imaginary box surrounding the - pie chart area. */ + 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; + } - /* 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; + /* adjust space for second axis */ + if (im->second_axis_scale != 0){ + Xmain -= Xylabel + 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; + } -#ifdef WITH_PIECHART - im->ximg += Xpie; -#endif + im->xsize = Xmain; - if (Xmain) - im->ximg += Xspacing; -#ifdef WITH_PIECHART - if (Xpie) - im->ximg += Xspacing; -#endif - - im->xorigin = Xspacing + Xylabel; + 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; + } + 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; + } - /* 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; - } - 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; - */ + /* 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 main and/or pie */ + /* watermark doesn't seem to effect the vertical size of the main graph area, oh well! */ + if (im->watermark[0] != '\0') { + Ymain -= Ywatermark; + } - im->yimg = Ymain + Yxlabel; + im->ysize = Ymain; + } else { /* dimension options -width and -height refer to the dimensions of the main graph area */ -#ifdef WITH_PIECHART - if (im->yimg < Ypie) - im->yimg = Ypie; -#endif + /* 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. + */ - im->yorigin = im->yimg - Yxlabel; + if (im->ylegend[0] != '\0') { + Xvertical = im->text_prop[TEXT_PROP_UNIT].size * 2; + } - /* 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) == -1) - return -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. + */ + /* 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; + } - if (im->watermark[0] != '\0') { - im->yimg += Ywatermark; - } -#if 0 - if (Xlegend > im->ximg) { - im->ximg = Xlegend; - /* reposition Pie */ - } -#endif + if (elements) { + Xmain = im->xsize; + Ymain = im->ysize; + } + /* Now calculate the total size. Insert some spacing where + desired. im->xorigin and im->yorigin need to correspond + with the lower left corner of the main graph area or, if + this one is not set, the imaginary box surrounding the + pie chart area. */ + + /* The legend width cannot yet be determined, as a result we + ** have problems adjusting the image to it. For now, we just + ** forget about it at all; the legend will have to fit in the + ** size already allocated. + */ + im->ximg = Xylabel + Xmain + 2 * Xspacing; -#ifdef WITH_PIECHART - /* 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; + if (im->second_axis_scale != 0){ + im->ximg += Xylabel + Xspacing; + } + if (im->extra_flags & NO_RRDTOOL_TAG){ + im->ximg -= Xspacing; + } + + 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->watermark[0] != '\0') { + im->yimg += Ywatermark; + } } -#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_output( + void *closure, + const unsigned char + *data, + unsigned int length) +{ + image_desc_t *im = (image_desc_t*)closure; + + im->rendered_image = + (unsigned char*)realloc(im->rendered_image, im->rendered_image_size + length); + if (im->rendered_image == NULL) + return CAIRO_STATUS_WRITE_ERROR; + memcpy(im->rendered_image + im->rendered_image_size, data, length); + im->rendered_image_size += length; + return CAIRO_STATUS_SUCCESS; +} /* draw that picture thing ... */ int graph_paint( - image_desc_t *im, - char ***calcpr) + image_desc_t *im) { int i, ii; int lazy = lazy_check(im); - -#ifdef WITH_PIECHART - int piechart = 0; - double PieStart = 0.0; -#endif - FILE *fo; - gfx_node_t *node; - double areazero = 0.0; graph_desc_t *lastgdes = NULL; + rrd_infoval_t info; - /* if we are lazy and there is nothing to PRINT ... quit now */ - if (lazy && im->prt_c == 0) - return 0; +// PangoFontMap *font_map = pango_cairo_font_map_get_default(); + /* if we want and can be lazy ... quit now */ + if (lazy) { + info.u_cnt = im->ximg; + grinfo_push(im, sprintf_alloc("image_width"), RD_I_CNT, info); + info.u_cnt = im->yimg; + grinfo_push(im, sprintf_alloc("image_height"), RD_I_CNT, info); + return 0; + } /* pull the data from the rrd files ... */ - if (data_fetch(im) == -1) return -1; - /* evaluate VDEF and CDEF operations ... */ if (data_calc(im) == -1) return -1; - -#ifdef WITH_PIECHART - /* check if we need to draw a piechart */ - for (i = 0; i < im->gdes_c; i++) { - if (im->gdes[i].gf == GF_PART) { - piechart = 1; - break; - } - } -#endif - /* calculate and PRINT and GPRINT definitions. We have to do it at * this point because it will affect the length of the legends - * if there are no graph elements we stop here ... + * if there are no graph elements (i==0) we stop here ... * if we are lazy, try to quit ... */ - i = print_calc(im, calcpr); + i = print_calc(im); if (i < 0) return -1; - if (((i == 0) -#ifdef WITH_PIECHART - && (piechart == 0) -#endif - ) || lazy) + + 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) == -1) + return -1; + + info.u_cnt = im->xorigin; + grinfo_push(im, sprintf_alloc("graph_left"), RD_I_CNT, info); + info.u_cnt = im->yorigin - im->ysize; + grinfo_push(im, sprintf_alloc("graph_top"), RD_I_CNT, info); + info.u_cnt = im->xsize; + grinfo_push(im, sprintf_alloc("graph_width"), RD_I_CNT, info); + info.u_cnt = im->ysize; + grinfo_push(im, sprintf_alloc("graph_height"), RD_I_CNT, info); + info.u_cnt = im->ximg; + grinfo_push(im, sprintf_alloc("image_width"), RD_I_CNT, info); + info.u_cnt = im->yimg; + grinfo_push(im, sprintf_alloc("image_height"), RD_I_CNT, info); /* get actual drawing data and find min and max values */ if (data_proc(im) == -1) return -1; - if (!im->logarithmic) { si_unit(im); } + /* identify si magnitude Kilo, Mega Giga ? */ if (!im->rigid && !im->logarithmic) expand_range(im); /* make sure the upper and lower limit are sensible values */ - if (!calc_horizontal_grid(im)) - return -1; - - if (im->gridfit) - apply_gridfit(im); + info.u_val = im->minval; + grinfo_push(im, sprintf_alloc("value_min"), RD_I_VAL, info); + info.u_val = im->maxval; + grinfo_push(im, sprintf_alloc("value_max"), RD_I_VAL, info); - -/************************************************************** - *** 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 (!calc_horizontal_grid(im)) return -1; - + /* reset precalc */ + ytr(im, DNAN); +/* if (im->gridfit) + apply_gridfit(im); */ /* the actual graph is created by going through the individual graph elements and then drawing them */ - - node = gfx_new_area(im->canvas, - 0, 0, - 0, im->yimg, - im->ximg, im->yimg, im->graph_col[GRC_BACK]); - - gfx_add_point(node, im->ximg, 0); - -#ifdef WITH_PIECHART - if (piechart != 2) { -#endif - node = gfx_new_area(im->canvas, - im->xorigin, im->yorigin, - im->xorigin + im->xsize, im->yorigin, - im->xorigin + im->xsize, im->yorigin - im->ysize, - im->graph_col[GRC_CANVAS]); - - gfx_add_point(node, im->xorigin, im->yorigin - im->ysize); - - if (im->minval > 0.0) - areazero = im->minval; - if (im->maxval < 0.0) - areazero = im->maxval; -#ifdef WITH_PIECHART - } -#endif - -#ifdef WITH_PIECHART - if (piechart) { - pie_part(im, im->graph_col[GRC_CANVAS], im->pie_x, im->pie_y, - im->piesize * 0.5, 0, 2 * M_PI); - } -#endif - + cairo_surface_destroy(im->surface); + switch (im->imgformat) { + case IF_PNG: + im->surface = + cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + im->ximg * im->zoom, + im->yimg * im->zoom); + break; + case IF_PDF: + im->gridfit = 0; + im->surface = strlen(im->graphfile) + ? cairo_pdf_surface_create(im->graphfile, im->ximg * im->zoom, + im->yimg * im->zoom) + : cairo_pdf_surface_create_for_stream + (&cairo_output, im, im->ximg * im->zoom, im->yimg * im->zoom); + break; + case IF_EPS: + im->gridfit = 0; + im->surface = strlen(im->graphfile) + ? + cairo_ps_surface_create(im->graphfile, im->ximg * im->zoom, + im->yimg * im->zoom) + : cairo_ps_surface_create_for_stream + (&cairo_output, im, im->ximg * im->zoom, im->yimg * im->zoom); + break; + case IF_SVG: + im->gridfit = 0; + im->surface = strlen(im->graphfile) + ? + cairo_svg_surface_create(im-> + graphfile, + im->ximg * im->zoom, im->yimg * im->zoom) + : cairo_svg_surface_create_for_stream + (&cairo_output, im, im->ximg * im->zoom, im->yimg * im->zoom); + cairo_svg_surface_restrict_to_version + (im->surface, CAIRO_SVG_VERSION_1_1); + break; + }; + cairo_destroy(im->cr); + im->cr = cairo_create(im->surface); + cairo_set_antialias(im->cr, im->graph_antialias); + cairo_scale(im->cr, im->zoom, im->zoom); +// pango_cairo_font_map_set_resolution(PANGO_CAIRO_FONT_MAP(font_map), 100); + gfx_new_area(im, 0, 0, 0, im->yimg, + im->ximg, im->yimg, im->graph_col[GRC_BACK]); + gfx_add_point(im, im->ximg, 0); + gfx_close_path(im); + gfx_new_area(im, im->xorigin, + im->yorigin, + im->xorigin + + im->xsize, im->yorigin, + im->xorigin + + im->xsize, + im->yorigin - im->ysize, im->graph_col[GRC_CANVAS]); + gfx_add_point(im, im->xorigin, im->yorigin - im->ysize); + gfx_close_path(im); + cairo_rectangle(im->cr, im->xorigin, im->yorigin - im->ysize - 1.0, + im->xsize, im->ysize + 2.0); + cairo_clip(im->cr); + if (im->minval > 0.0) + areazero = im->minval; + if (im->maxval < 0.0) + areazero = im->maxval; for (i = 0; i < im->gdes_c; i++) { switch (im->gdes[i].gf) { case GF_CDEF: @@ -2887,6 +3212,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: @@ -2894,24 +3220,25 @@ int graph_paint( break; case GF_TICK: for (ii = 0; ii < im->xsize; ii++) { - if (!isnan(im->gdes[i].p_data[ii]) && - im->gdes[i].p_data[ii] != 0.0) { + if (!isnan(im->gdes[i].p_data[ii]) + && im->gdes[i].p_data[ii] != 0.0) { if (im->gdes[i].yrule > 0) { - gfx_new_line(im->canvas, - im->xorigin + ii, im->yorigin, - im->xorigin + ii, - im->yorigin - - im->gdes[i].yrule * im->ysize, 1.0, - im->gdes[i].col); + gfx_line(im, + im->xorigin + ii, + im->yorigin, + 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); } } } @@ -2941,58 +3268,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++) { @@ -3003,51 +3362,62 @@ int graph_paint( int lastI = 0; while (cntI < idxI - && AlmostEqual2sComplement(foreY[lastI], - foreY[cntI], 4) - && AlmostEqual2sComplement(foreY[lastI], - foreY[cntI + 1], - 4)) { + && + AlmostEqual2sComplement(foreY + [lastI], + foreY[cntI], 4) + && + AlmostEqual2sComplement(foreY + [lastI], + foreY + [cntI + 1], 4)) { cntI++; } - node = gfx_new_area(im->canvas, - backX[0], backY[0], - foreX[0], foreY[0], - foreX[cntI], foreY[cntI], - im->gdes[i].col); + gfx_new_area(im, + backX[0], backY[0], + foreX[0], foreY[0], + foreX[cntI], + foreY[cntI], im->gdes[i].col); while (cntI < idxI) { lastI = cntI; cntI++; while (cntI < idxI && - AlmostEqual2sComplement(foreY[lastI], + AlmostEqual2sComplement(foreY + [lastI], foreY[cntI], 4) && - AlmostEqual2sComplement(foreY[lastI], - foreY[cntI + - 1], 4)) { + AlmostEqual2sComplement(foreY + [lastI], + foreY + [cntI + + 1], 4)) { cntI++; } - gfx_add_point(node, foreX[cntI], foreY[cntI]); + gfx_add_point(im, foreX[cntI], foreY[cntI]); } - gfx_add_point(node, backX[idxI], backY[idxI]); + gfx_add_point(im, backX[idxI], backY[idxI]); while (idxI > 1) { lastI = idxI; idxI--; while (idxI > 1 && - AlmostEqual2sComplement(backY[lastI], + AlmostEqual2sComplement(backY + [lastI], backY[idxI], 4) && - AlmostEqual2sComplement(backY[lastI], - backY[idxI - - 1], 4)) { + AlmostEqual2sComplement(backY + [lastI], + backY + [idxI + - 1], 4)) { idxI--; } - gfx_add_point(node, backX[idxI], backY[idxI]); + gfx_add_point(im, backX[idxI], backY[idxI]); } idxI = -1; drawem = 0; + gfx_close_path(im); } if (drawem != 0) { drawem = 0; @@ -3055,11 +3425,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; } @@ -3077,8 +3442,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; @@ -3116,64 +3480,60 @@ int graph_paint( } lastgdes = &(im->gdes[i]); break; -#ifdef WITH_PIECHART - case GF_PART: - if (isnan(im->gdes[i].yrule)) /* fetch variable */ - im->gdes[i].yrule = im->gdes[im->gdes[i].vidx].vf.val; - - if (finite(im->gdes[i].yrule)) { /* even the fetched var can be NaN */ - pie_part(im, im->gdes[i].col, - im->pie_x, im->pie_y, im->piesize * 0.4, - M_PI * 2.0 * PieStart / 100.0, - M_PI * 2.0 * (PieStart + im->gdes[i].yrule) / 100.0); - PieStart += im->gdes[i].yrule; - } - break; -#endif case GF_STACK: rrd_set_error ("STACK should already be turned into LINE or AREA here"); return -1; break; - } /* switch */ } -#ifdef WITH_PIECHART - if (piechart == 2) { - im->draw_x_grid = 0; - im->draw_y_grid = 0; - } -#endif - + cairo_reset_clip(im->cr); /* grid_paint also does the text */ if (!(im->extra_flags & ONLY_GRAPH)) grid_paint(im); - - if (!(im->extra_flags & ONLY_GRAPH)) axis_paint(im); - /* the RULES are the last thing to paint ... */ for (i = 0; i < im->gdes_c; i++) { switch (im->gdes[i].gf) { case GF_HRULE: if (im->gdes[i].yrule >= im->minval - && im->gdes[i].yrule <= im->maxval) - gfx_new_line(im->canvas, - im->xorigin, ytr(im, im->gdes[i].yrule), - im->xorigin + im->xsize, ytr(im, - im->gdes[i].yrule), - 1.0, im->gdes[i].col); + && im->gdes[i].yrule <= im->maxval) { + cairo_save(im->cr); + if (im->gdes[i].dash) { + cairo_set_dash(im->cr, + im->gdes[i].p_dashes, + im->gdes[i].ndash, im->gdes[i].offset); + } + gfx_line(im, im->xorigin, + ytr(im, im->gdes[i].yrule), + im->xorigin + im->xsize, + ytr(im, im->gdes[i].yrule), 1.0, im->gdes[i].col); + cairo_stroke(im->cr); + cairo_restore(im->cr); + } break; case GF_VRULE: if (im->gdes[i].xrule >= im->start - && im->gdes[i].xrule <= im->end) - gfx_new_line(im->canvas, - xtr(im, im->gdes[i].xrule), im->yorigin, - xtr(im, im->gdes[i].xrule), - im->yorigin - im->ysize, 1.0, im->gdes[i].col); + && im->gdes[i].xrule <= im->end) { + cairo_save(im->cr); + if (im->gdes[i].dash) { + cairo_set_dash(im->cr, + im->gdes[i].p_dashes, + im->gdes[i].ndash, im->gdes[i].offset); + } + gfx_line(im, + xtr(im, im->gdes[i].xrule), + im->yorigin, xtr(im, + im-> + gdes[i]. + xrule), + im->yorigin - im->ysize, 1.0, im->gdes[i].col); + cairo_stroke(im->cr); + cairo_restore(im->cr); + } break; default: break; @@ -3181,22 +3541,31 @@ int graph_paint( } - if (strcmp(im->graphfile, "-") == 0) { - fo = im->graphhandle ? im->graphhandle : stdout; -#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__) - /* Change translation mode for stdout to BINARY */ - _setmode(_fileno(fo), O_BINARY); -#endif - } else { - if ((fo = fopen(im->graphfile, "wb")) == NULL) { - rrd_set_error("Opening '%s' for write: %s", im->graphfile, - rrd_strerror(errno)); - return (-1); + switch (im->imgformat) { + case IF_PNG: + { + cairo_status_t status; + + status = strlen(im->graphfile) ? + cairo_surface_write_to_png(im->surface, im->graphfile) + : cairo_surface_write_to_png_stream(im->surface, &cairo_output, + im); + + if (status != CAIRO_STATUS_SUCCESS) { + rrd_set_error("Could not save png to '%s'", im->graphfile); + return 1; + } + break; + } + default: + if (strlen(im->graphfile)) { + cairo_show_page(im->cr); + } else { + cairo_surface_finish(im->surface); } + break; } - gfx_render(im->canvas, im->ximg, im->yimg, 0x00000000, fo); - if (strcmp(im->graphfile, "-") != 0) - fclose(fo); + return 0; } @@ -3210,9 +3579,9 @@ int gdes_alloc( { im->gdes_c++; - if ((im->gdes = (graph_desc_t *) rrd_realloc(im->gdes, (im->gdes_c) - * sizeof(graph_desc_t))) == - NULL) { + if ((im->gdes = (graph_desc_t *) + rrd_realloc(im->gdes, (im->gdes_c) + * sizeof(graph_desc_t))) == NULL) { rrd_set_error("realloc graph_descs"); return -1; } @@ -3233,8 +3602,15 @@ int gdes_alloc( im->gdes[im->gdes_c - 1].data_first = 0; im->gdes[im->gdes_c - 1].p_data = NULL; im->gdes[im->gdes_c - 1].rpnp = NULL; - im->gdes[im->gdes_c - 1].shift = 0; - im->gdes[im->gdes_c - 1].col = 0x0; + im->gdes[im->gdes_c - 1].p_dashes = NULL; + im->gdes[im->gdes_c - 1].shift = 0.0; + im->gdes[im->gdes_c - 1].dash = 0; + im->gdes[im->gdes_c - 1].ndash = 0; + im->gdes[im->gdes_c - 1].offset = 0; + im->gdes[im->gdes_c - 1].col.red = 0.0; + im->gdes[im->gdes_c - 1].col.green = 0.0; + im->gdes[im->gdes_c - 1].col.blue = 0.0; + im->gdes[im->gdes_c - 1].col.alpha = 0.0; im->gdes[im->gdes_c - 1].legend[0] = '\0'; im->gdes[im->gdes_c - 1].format[0] = '\0'; im->gdes[im->gdes_c - 1].strftm = 0; @@ -3242,7 +3618,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; @@ -3258,9 +3633,9 @@ int scan_for_col( int inp, outp = 0; for (inp = 0; inp < len && input[inp] != ':' && input[inp] != '\0'; inp++) { - if (input[inp] == '\\' && - input[inp + 1] != '\0' && - (input[inp + 1] == '\\' || input[inp + 1] == ':')) { + if (input[inp] == '\\' + && input[inp + 1] != '\0' + && (input[inp + 1] == '\\' || input[inp + 1] == ':')) { output[outp++] = input[++inp]; } else { output[outp++] = input[inp]; @@ -3270,12 +3645,7 @@ int scan_for_col( return inp; } -/* Some surgery done on this function, it became ridiculously big. -** Things moved: -** - initializing now in rrd_graph_init() -** - options parsing now in rrd_graph_options() -** - script parsing now in rrd_graph_script() -*/ +/* Now just a wrapper around rrd_graph_v */ int rrd_graph( int argc, char **argv, @@ -3286,83 +3656,186 @@ int rrd_graph( double *ymin, double *ymax) { - image_desc_t im; + int prlines = 0; + rrd_info_t *grinfo = NULL; + rrd_info_t *walker; - rrd_graph_init(&im); - im.graphhandle = stream; + grinfo = rrd_graph_v(argc, argv); + if (grinfo == NULL) + return -1; + walker = grinfo; + (*prdata) = NULL; + while (walker) { + if (strcmp(walker->key, "image_info") == 0) { + prlines++; + if (((*prdata) = + (char**)rrd_realloc((*prdata), + (prlines + 1) * sizeof(char *))) == NULL) { + rrd_set_error("realloc prdata"); + return 0; + } + /* imginfo goes to position 0 in the prdata array */ + (*prdata)[prlines - 1] = (char*)malloc((strlen(walker->value.u_str) + + 2) * sizeof(char)); + strcpy((*prdata)[prlines - 1], walker->value.u_str); + (*prdata)[prlines] = NULL; + } + /* skip anything else */ + walker = walker->next; + } + walker = grinfo; + *xsize = 0; + *ysize = 0; + *ymin = 0; + *ymax = 0; + while (walker) { + if (strcmp(walker->key, "image_width") == 0) { + *xsize = walker->value.u_int; + } else if (strcmp(walker->key, "image_height") == 0) { + *ysize = walker->value.u_int; + } else if (strcmp(walker->key, "value_min") == 0) { + *ymin = walker->value.u_val; + } else if (strcmp(walker->key, "value_max") == 0) { + *ymax = walker->value.u_val; + } else if (strncmp(walker->key, "print", 5) == 0) { /* keys are prdate[0..] */ + prlines++; + if (((*prdata) = + (char**)rrd_realloc((*prdata), + (prlines + 1) * sizeof(char *))) == NULL) { + rrd_set_error("realloc prdata"); + return 0; + } + (*prdata)[prlines - 1] = (char*)malloc((strlen(walker->value.u_str) + + 2) * sizeof(char)); + (*prdata)[prlines] = NULL; + strcpy((*prdata)[prlines - 1], walker->value.u_str); + } else if (strcmp(walker->key, "image") == 0) { + fwrite(walker->value.u_blo.ptr, walker->value.u_blo.size, 1, + (stream ? stream : stdout)); + } + /* skip anything else */ + walker = walker->next; + } + rrd_info_free(grinfo); + return 0; +} + +/* Some surgery done on this function, it became ridiculously big. +** Things moved: +** - initializing now in rrd_graph_init() +** - options parsing now in rrd_graph_options() +** - script parsing now in rrd_graph_script() +*/ +rrd_info_t *rrd_graph_v( + int argc, + char **argv) +{ + image_desc_t im; + rrd_info_t *grinfo; + rrd_graph_init(&im); + /* a dummy surface so that we can measure text sizes for placements */ + rrd_graph_options(argc, argv, &im); if (rrd_test_error()) { + rrd_info_free(im.grinfo); im_free(&im); - return -1; + return NULL; + } + + if (optind >= argc) { + rrd_info_free(im.grinfo); + im_free(&im); + rrd_set_error("missing filename"); + return NULL; } if (strlen(argv[optind]) >= MAXPATH) { rrd_set_error("filename (including path) too long"); + rrd_info_free(im.grinfo); im_free(&im); - return -1; + return NULL; } + strncpy(im.graphfile, argv[optind], MAXPATH - 1); im.graphfile[MAXPATH - 1] = '\0'; + if (strcmp(im.graphfile, "-") == 0) { + im.graphfile[0] = '\0'; + } + rrd_graph_script(argc, argv, &im, 1); if (rrd_test_error()) { + rrd_info_free(im.grinfo); im_free(&im); - return -1; + return NULL; } /* Everything is now read and the actual work can start */ - (*prdata) = NULL; - if (graph_paint(&im, prdata) == -1) { + if (graph_paint(&im) == -1) { + rrd_info_free(im.grinfo); im_free(&im); - return -1; + return NULL; } + /* The image is generated and needs to be output. ** Also, if needed, print a line with information about the image. */ - *xsize = im.ximg; - *ysize = im.yimg; - *ymin = im.minval; - *ymax = im.maxval; if (im.imginfo) { + rrd_infoval_t info; + char *path; char *filename; - if (!(*prdata)) { - /* maybe prdata is not allocated yet ... lets do it now */ - if ((*prdata = calloc(2, sizeof(char *))) == NULL) { - rrd_set_error("malloc imginfo"); - return -1; - }; - } - if (((*prdata)[0] = - malloc((strlen(im.imginfo) + 200 + - strlen(im.graphfile)) * sizeof(char))) - == NULL) { - rrd_set_error("malloc imginfo"); - return -1; - } - filename = im.graphfile + strlen(im.graphfile); - while (filename > im.graphfile) { - if (*(filename - 1) == '/' || *(filename - 1) == '\\') - break; - filename--; - } + path = strdup(im.graphfile); + filename = basename(path); + info.u_str = + sprintf_alloc(im.imginfo, + filename, + (long) (im.zoom * + im.ximg), (long) (im.zoom * im.yimg)); + grinfo_push(&im, sprintf_alloc("image_info"), RD_I_STR, info); + free(info.u_str); + free(path); + } + if (im.rendered_image) { + rrd_infoval_t img; - sprintf((*prdata)[0], im.imginfo, filename, - (long) (im.canvas->zoom * im.ximg), - (long) (im.canvas->zoom * im.yimg)); + img.u_blo.size = im.rendered_image_size; + img.u_blo.ptr = im.rendered_image; + grinfo_push(&im, sprintf_alloc("image"), RD_I_BLO, img); } + grinfo = im.grinfo; im_free(&im); - return 0; + return grinfo; +} + +static void +rrd_set_font_desc ( + image_desc_t *im,int prop,char *font, double size ){ + if (font){ + strncpy(im->text_prop[prop].font, font, sizeof(text_prop[prop].font) - 1); + im->text_prop[prop].font[sizeof(text_prop[prop].font) - 1] = '\0'; + im->text_prop[prop].font_desc = pango_font_description_from_string( font ); + }; + if (size > 0){ + im->text_prop[prop].size = size; + }; + if (im->text_prop[prop].font_desc && im->text_prop[prop].size ){ + pango_font_description_set_size(im->text_prop[prop].font_desc, im->text_prop[prop].size * PANGO_SCALE); + }; } void rrd_graph_init( - image_desc_t *im) + image_desc_t + *im) { unsigned int i; + char *deffont = getenv("RRD_DEFAULT_FONT"); + static PangoFontMap *fontmap = NULL; + PangoContext *context; #ifdef HAVE_TZSET tzset(); @@ -3373,162 +3846,181 @@ void rrd_graph_init( setlocale(LC_CTYPE, ""); #endif #endif - im->yorigin = 0; - im->xorigin = 0; - im->minval = 0; - im->xlab_user.minsec = -1; - im->ximg = 0; - im->yimg = 0; - im->xsize = 400; - im->ysize = 100; - im->step = 0; - im->ylegend[0] = '\0'; - im->title[0] = '\0'; - im->watermark[0] = '\0'; - im->minval = DNAN; - im->maxval = DNAN; - im->unitsexponent = 9999; - im->unitslength = 6; - im->forceleftspace = 0; - im->symbol = ' '; - im->viewfactor = 1.0; + im->base = 1000; + im->daemon_addr = NULL; + im->draw_x_grid = 1; + im->draw_y_grid = 1; im->extra_flags = 0; - im->rigid = 0; + im->font_options = cairo_font_options_create(); + im->forceleftspace = 0; + im->gdes_c = 0; + im->gdes = NULL; + im->graph_antialias = CAIRO_ANTIALIAS_GRAY; + im->grid_dash_off = 1; + im->grid_dash_on = 1; im->gridfit = 1; + im->grinfo = (rrd_info_t *) NULL; + im->grinfo_current = (rrd_info_t *) NULL; + im->imgformat = IF_PNG; im->imginfo = NULL; im->lazy = 0; - im->slopemode = 0; im->logarithmic = 0; - im->ygridstep = DNAN; - im->draw_x_grid = 1; - im->draw_y_grid = 1; - im->base = 1000; + im->maxval = DNAN; + im->minval = 0; + im->minval = DNAN; im->prt_c = 0; - im->gdes_c = 0; - im->gdes = NULL; - im->canvas = gfx_new_canvas(); - im->grid_dash_on = 1; - im->grid_dash_off = 1; + im->rigid = 0; + im->rendered_image_size = 0; + im->rendered_image = NULL; + im->slopemode = 0; + im->step = 0; + im->symbol = ' '; im->tabwidth = 40.0; + im->title[0] = '\0'; + im->unitsexponent = 9999; + im->unitslength = 6; + im->viewfactor = 1.0; + im->watermark[0] = '\0'; + im->with_markup = 0; + im->ximg = 0; + im->xlab_user.minsec = -1; + im->xorigin = 0; + im->xsize = 400; + im->ygridstep = DNAN; + im->yimg = 0; + im->ylegend[0] = '\0'; + im->second_axis_scale = 0; /* 0 disables it */ + im->second_axis_shift = 0; /* no shift by default */ + im->second_axis_legend[0] = '\0'; + im->second_axis_format[0] = '\0'; + im->yorigin = 0; + im->ysize = 100; + im->zoom = 1; - for (i = 0; i < DIM(graph_col); i++) - im->graph_col[i] = graph_col[i]; + im->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 10, 10); + im->cr = cairo_create(im->surface); -#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__) - { - char *windir; - char rrd_win_default_font[1000]; - - windir = getenv("windir"); - /* %windir% is something like D:\windows or C:\winnt */ - if (windir != NULL) { - strncpy(rrd_win_default_font, windir, 500); - rrd_win_default_font[500] = '\0'; - strcat(rrd_win_default_font, "\\fonts\\"); - strcat(rrd_win_default_font, RRD_DEFAULT_FONT); - for (i = 0; i < DIM(text_prop); i++) { - strncpy(text_prop[i].font, rrd_win_default_font, - sizeof(text_prop[i].font) - 1); - text_prop[i].font[sizeof(text_prop[i].font) - 1] = '\0'; - } - } - } -#endif - { - char *deffont; - - deffont = getenv("RRD_DEFAULT_FONT"); - if (deffont != NULL) { - for (i = 0; i < DIM(text_prop); i++) { - strncpy(text_prop[i].font, deffont, - sizeof(text_prop[i].font) - 1); - text_prop[i].font[sizeof(text_prop[i].font) - 1] = '\0'; - } - } - } for (i = 0; i < DIM(text_prop); i++) { - im->text_prop[i].size = text_prop[i].size; - strcpy(im->text_prop[i].font, text_prop[i].font); + im->text_prop[i].size = -1; + rrd_set_font_desc(im,i, deffont ? deffont : text_prop[i].font,text_prop[i].size); + } + + if (fontmap == NULL){ + fontmap = pango_cairo_font_map_get_default(); } + + context = pango_cairo_font_map_create_context((PangoCairoFontMap*)fontmap); + + pango_cairo_context_set_resolution(context, 100); + + pango_cairo_update_context(im->cr,context); + + im->layout = pango_layout_new(context); + +// im->layout = pango_cairo_create_layout(im->cr); + + + cairo_font_options_set_hint_style + (im->font_options, CAIRO_HINT_STYLE_FULL); + cairo_font_options_set_hint_metrics + (im->font_options, CAIRO_HINT_METRICS_ON); + cairo_font_options_set_antialias(im->font_options, CAIRO_ANTIALIAS_GRAY); + + + + for (i = 0; i < DIM(graph_col); i++) + im->graph_col[i] = graph_col[i]; + + } + void rrd_graph_options( int argc, char *argv[], - image_desc_t *im) + image_desc_t + *im) { int stroff; char *parsetime_error = NULL; char scan_gtm[12], scan_mtm[12], scan_ltm[12], col_nam[12]; time_t start_tmp = 0, end_tmp = 0; long long_tmp; - struct rrd_time_value start_tv, end_tv; - gfx_color_t color; - - optind = 0; - opterr = 0; /* initialize getopt */ - - parsetime("end-24h", &start_tv); - parsetime("now", &end_tv); + rrd_time_value_t start_tv, end_tv; + long unsigned int color; + 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 +/* *INDENT-OFF* */ + 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'}, + {"disable-rrdtool-tag", no_argument, 0, 1001}, + {"right-axis", required_argument, 0, 1002}, + {"right-axis-label", required_argument, 0, 1003}, + {"right-axis-format", required_argument, 0, 1004}, + { "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 */ + { "pango-markup", no_argument, 0, 'P'}, + { "daemon", required_argument, 0, 'd'}, + { 0, 0, 0, 0} +}; +/* *INDENT-ON* */ + + optind = 0; + opterr = 0; /* initialize getopt */ + rrd_parsetime("end-24h", &start_tv); + rrd_parsetime("now", &end_tv); while (1) { - static struct option long_options[] = { - {"start", required_argument, 0, 's'}, - {"end", required_argument, 0, 'e'}, - {"x-grid", required_argument, 0, 'x'}, - {"y-grid", required_argument, 0, 'y'}, - {"vertical-label", required_argument, 0, 'v'}, - {"width", required_argument, 0, 'w'}, - {"height", required_argument, 0, 'h'}, - {"interlaced", no_argument, 0, 'i'}, - {"upper-limit", required_argument, 0, 'u'}, - {"lower-limit", required_argument, 0, 'l'}, - {"rigid", no_argument, 0, 'r'}, - {"base", required_argument, 0, 'b'}, - {"logarithmic", no_argument, 0, 'o'}, - {"color", required_argument, 0, 'c'}, - {"font", required_argument, 0, 'n'}, - {"title", required_argument, 0, 't'}, - {"imginfo", required_argument, 0, 'f'}, - {"imgformat", required_argument, 0, 'a'}, - {"lazy", no_argument, 0, 'z'}, - {"zoom", required_argument, 0, 'm'}, - {"no-legend", no_argument, 0, 'g'}, - {"force-rules-legend", no_argument, 0, 'F'}, - {"only-graph", no_argument, 0, 'j'}, - {"alt-y-grid", no_argument, 0, 'Y'}, - {"no-minor", no_argument, 0, 'I'}, - {"slope-mode", no_argument, 0, 'E'}, - {"alt-autoscale", no_argument, 0, 'A'}, - {"alt-autoscale-min", no_argument, 0, 'J'}, - {"alt-autoscale-max", no_argument, 0, 'M'}, - {"no-gridfit", no_argument, 0, 'N'}, - {"units-exponent", required_argument, 0, 'X'}, - {"units-length", required_argument, 0, 'L'}, - {"units", required_argument, 0, LONGOPT_UNITS_SI}, - {"step", required_argument, 0, 'S'}, - {"tabwidth", required_argument, 0, 'T'}, - {"font-render-mode", required_argument, 0, 'R'}, - {"font-smoothing-threshold", required_argument, 0, 'B'}, - {"watermark", required_argument, 0, 'W'}, - {"alt-y-mrtg", no_argument, 0, 1000}, /* this has no effect it is just here to save old apps from crashing when they use it */ - {0, 0, 0, 0} - }; int option_index = 0; int opt; int col_start, col_end; opt = getopt_long(argc, argv, - "s:e:x:y:v:w:h:iu:l:rb:oc:n:m:t:f:a:I:zgjFYAMEX:L:S:T:NR:B:W:", + "s:e:x:y:v:w:h:D:iu:l:rb:oc:n:m:t:f:a:I:zgjFYAMEX:L:S:T:NR:B:W:kPd:", long_options, &option_index); - if (opt == EOF) break; - switch (opt) { case 'I': im->extra_flags |= NOMINOR; @@ -3554,9 +4046,13 @@ void rrd_graph_options( case 'F': im->extra_flags |= FORCE_RULES_LEGEND; break; + case 1001: + im->extra_flags |= NO_RRDTOOL_TAG; + break; case LONGOPT_UNITS_SI: if (im->extra_flags & FORCE_UNITS) { rrd_set_error("--units can only be used once!"); + setlocale(LC_NUMERIC, old_locale); return; } if (strcmp(optarg, "si") == 0) @@ -3574,22 +4070,29 @@ 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; break; + case 'P': + im->with_markup = 1; + break; case 's': - if ((parsetime_error = parsetime(optarg, &start_tv))) { + if ((parsetime_error = rrd_parsetime(optarg, &start_tv))) { rrd_set_error("start time: %s", parsetime_error); return; } break; case 'e': - if ((parsetime_error = parsetime(optarg, &end_tv))) { + if ((parsetime_error = rrd_parsetime(optarg, &end_tv))) { rrd_set_error("end time: %s", parsetime_error); return; } @@ -3599,7 +4102,6 @@ void rrd_graph_options( im->draw_x_grid = 0; break; }; - if (sscanf(optarg, "%10[A-Z]:%ld:%10[A-Z]:%ld:%10[A-Z]:%ld:%ld:%n", scan_gtm, @@ -3612,15 +4114,17 @@ void rrd_graph_options( strncpy(im->xlab_form, optarg + stroff, sizeof(im->xlab_form) - 1); im->xlab_form[sizeof(im->xlab_form) - 1] = '\0'; - if ((int) (im->xlab_user.gridtm = tmt_conv(scan_gtm)) == -1) { + if ((int) + (im->xlab_user.gridtm = tmt_conv(scan_gtm)) == -1) { rrd_set_error("unknown keyword %s", scan_gtm); return; - } else if ((int) (im->xlab_user.mgridtm = tmt_conv(scan_mtm)) + } else if ((int) + (im->xlab_user.mgridtm = tmt_conv(scan_mtm)) == -1) { rrd_set_error("unknown keyword %s", scan_mtm); return; - } else if ((int) (im->xlab_user.labtm = tmt_conv(scan_ltm)) == - -1) { + } else if ((int) + (im->xlab_user.labtm = tmt_conv(scan_ltm)) == -1) { rrd_set_error("unknown keyword %s", scan_ltm); return; } @@ -3637,8 +4141,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; @@ -3647,19 +4152,51 @@ void rrd_graph_options( return; } } else { + setlocale(LC_NUMERIC, old_locale); rrd_set_error("invalid y-grid format"); return; } break; + case 1002: /* right y axis */ + + if(sscanf(optarg, + "%lf:%lf", + &im->second_axis_scale, + &im->second_axis_shift) == 2) { + if(im->second_axis_scale==0){ + rrd_set_error("the second_axis_scale must not be 0"); + return; + } + } else { + rrd_set_error("invalid right-axis format expected scale:shift"); + return; + } + break; + case 1003: + strncpy(im->second_axis_legend,optarg,150); + im->second_axis_legend[150]='\0'; + break; + case 1004: + if (bad_format(optarg)){ + rrd_set_error("use either %le or %lf formats"); + return; + } + strncpy(im->second_axis_format,optarg,150); + im->second_axis_format[150]='\0'; + break; case 'v': strncpy(im->ylegend, optarg, 150); im->ylegend[150] = '\0'; 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); @@ -3685,8 +4222,11 @@ void rrd_graph_options( } im->ysize = long_tmp; break; + case 'D': + 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; @@ -3695,7 +4235,8 @@ void rrd_graph_options( im->imginfo = optarg; break; case 'a': - if ((int) (im->canvas->imgformat = if_conv(optarg)) == -1) { + if ((int) + (im->imgformat = if_conv(optarg)) == -1) { rrd_set_error("unsupported graphics format '%s'", optarg); return; } @@ -3706,7 +4247,6 @@ void rrd_graph_options( case 'E': im->slopemode = 1; break; - case 'o': im->logarithmic = 1; break; @@ -3719,15 +4259,21 @@ void rrd_graph_options( switch (col_len) { case 3: - color = (((color & 0xF00) * 0x110000) | - ((color & 0x0F0) * 0x011000) | - ((color & 0x00F) * 0x001100) | 0x000000FF); + color = + (((color & 0xF00) * 0x110000) | ((color & 0x0F0) * + 0x011000) | + ((color & 0x00F) + * 0x001100) + | 0x000000FF); break; case 4: - color = (((color & 0xF000) * 0x11000) | - ((color & 0x0F00) * 0x01100) | - ((color & 0x00F0) * 0x00110) | - ((color & 0x000F) * 0x00011) + color = + (((color & 0xF000) * + 0x11000) | ((color & 0x0F00) * + 0x01100) | ((color & + 0x00F0) * + 0x00110) | + ((color & 0x000F) * 0x00011) ); break; case 6: @@ -3740,7 +4286,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; @@ -3753,20 +4299,31 @@ void rrd_graph_options( case 'n':{ char prop[15]; double size = 1; - char font[1024] = ""; + int end; - if (sscanf(optarg, "%10[A-Z]:%lf:%1000s", prop, &size, font) >= 2) { + old_locale = setlocale(LC_NUMERIC, "C"); + if (sscanf(optarg, "%10[A-Z]:%lf%n", prop, &size, &end) >= 2) { int sindex, propidx; + setlocale(LC_NUMERIC, old_locale); if ((sindex = text_prop_conv(prop)) != -1) { - for (propidx = sindex; propidx < TEXT_PROP_LAST; - propidx++) { + for (propidx = sindex; + propidx < TEXT_PROP_LAST; propidx++) { if (size > 0) { - im->text_prop[propidx].size = size; + rrd_set_font_desc(im,propidx,NULL,size); } - if (strlen(font) > 0) { - strcpy(im->text_prop[propidx].font, font); + if ((int) strlen(optarg) > end+2) { + if (optarg[end] == ':') { + rrd_set_font_desc(im,propidx,optarg + end + 1,0); + } else { + rrd_set_error + ("expected : after font size in '%s'", + optarg); + return; + } } + /* only run the for loop for DEFAULT (0) for + all others, we break here. woodo programming */ if (propidx == sindex && sindex != 0) break; } @@ -3775,14 +4332,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; } @@ -3791,29 +4351,62 @@ void rrd_graph_options( strncpy(im->title, optarg, 150); im->title[150] = '\0'; break; - case 'R': + if (strcmp(optarg, "normal") == 0) { + cairo_font_options_set_antialias + (im->font_options, CAIRO_ANTIALIAS_GRAY); + cairo_font_options_set_hint_style + (im->font_options, CAIRO_HINT_STYLE_FULL); + } else if (strcmp(optarg, "light") == 0) { + cairo_font_options_set_antialias + (im->font_options, CAIRO_ANTIALIAS_GRAY); + cairo_font_options_set_hint_style + (im->font_options, CAIRO_HINT_STYLE_SLIGHT); + } else if (strcmp(optarg, "mono") == 0) { + cairo_font_options_set_antialias + (im->font_options, CAIRO_ANTIALIAS_NONE); + cairo_font_options_set_hint_style + (im->font_options, CAIRO_HINT_STYLE_FULL); + } else { + rrd_set_error("unknown font-render-mode '%s'", optarg); + return; + } + break; + case 'G': if (strcmp(optarg, "normal") == 0) - im->canvas->aa_type = AA_NORMAL; - else if (strcmp(optarg, "light") == 0) - im->canvas->aa_type = AA_LIGHT; + im->graph_antialias = CAIRO_ANTIALIAS_GRAY; else if (strcmp(optarg, "mono") == 0) - im->canvas->aa_type = AA_NONE; + im->graph_antialias = CAIRO_ANTIALIAS_NONE; else { - rrd_set_error("unknown font-render-mode '%s'", optarg); + rrd_set_error("unknown graph-render-mode '%s'", optarg); return; } break; - case 'B': - im->canvas->font_aa_threshold = atof(optarg); + /* not supported curently */ break; - case 'W': strncpy(im->watermark, optarg, 100); im->watermark[99] = '\0'; break; + case 'd': + { + if (im->daemon_addr != NULL) + { + rrd_set_error ("You cannot specify --daemon " + "more than once."); + return; + } + + im->daemon_addr = strdup(optarg); + if (im->daemon_addr == NULL) + { + rrd_set_error("strdup failed"); + return; + } + break; + } case '?': if (optopt != 0) rrd_set_error("unknown option '%c'", optopt); @@ -3821,33 +4414,39 @@ void rrd_graph_options( rrd_set_error("unknown option '%s'", argv[optind - 1]); return; } - } + } /* while (1) */ - if (optind >= argc) { - rrd_set_error("missing filename"); - return; + { /* try to connect to rrdcached */ + int status = rrdc_connect(im->daemon_addr); + if (status != 0) return; } + + pango_cairo_context_set_font_options(pango_layout_get_context(im->layout), im->font_options); + pango_layout_context_changed(im->layout); + + - if (im->logarithmic == 1 && im->minval <= 0) { + if (im->logarithmic && im->minval <= 0) { rrd_set_error ("for a logarithmic yaxis you must specify a lower-limit > 0"); return; } - if (proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) { - /* error string is set in parsetime.c */ + if (rrd_proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) { + /* error string is set in rrd_parsetime.c */ return; } if (start_tmp < 3600 * 24 * 365 * 10) { - rrd_set_error("the first entry to fetch should be after 1980 (%ld)", - start_tmp); + rrd_set_error + ("the first entry to fetch should be after 1980 (%ld)", + start_tmp); return; } if (end_tmp < start_tmp) { - rrd_set_error("start (%ld) should be less than end (%ld)", - start_tmp, end_tmp); + rrd_set_error + ("start (%ld) should be less than end (%ld)", start_tmp, end_tmp); return; } @@ -3857,7 +4456,8 @@ void rrd_graph_options( } int rrd_graph_color( - image_desc_t *im, + image_desc_t + *im, char *var, char *err, int optional) @@ -3875,14 +4475,13 @@ int rrd_graph_color( } else { int n = 0; char *rest; - gfx_color_t col; + long unsigned int col; rest = strstr(color, ":"); if (rest != NULL) n = rest - color; else n = strlen(color); - switch (n) { case 7: sscanf(color, "#%6lx%n", &col, &n); @@ -3899,7 +4498,7 @@ int rrd_graph_color( } if (rrd_test_error()) return 0; - gdp->col = col; + gdp->col = gfx_hex_to_col(col); return n; } } @@ -3918,11 +4517,9 @@ int bad_format( /* line cannot end with percent char */ if (*ptr == '\0') return 1; - /* '%s', '%S' and '%%' are allowed */ if (*ptr == 's' || *ptr == 'S' || *ptr == '%') ptr++; - /* %c is allowed (but use only with vdef!) */ else if (*ptr == 'c') { ptr++; @@ -3934,7 +4531,6 @@ int bad_format( /* optional padding character */ if (*ptr == ' ' || *ptr == '+' || *ptr == '-') ptr++; - /* This should take care of 'm.n' with all three optional */ while (*ptr >= '0' && *ptr <= '9') ptr++; @@ -3942,7 +4538,6 @@ int bad_format( ptr++; while (*ptr >= '0' && *ptr <= '9') ptr++; - /* Either 'le', 'lf' or 'lg' must follow here */ if (*ptr++ != 'l') return 1; @@ -3959,10 +4554,9 @@ int bad_format( int vdef_parse( - gdes, - str) - struct graph_desc_t *gdes; - const char *const str; + struct graph_desc_t + *gdes, + const char *const str) { /* A VDEF currently is either "func" or "param,func" * so the parsing is rather simple. Change if needed. @@ -3970,9 +4564,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", ¶m, func, &n); + setlocale(LC_NUMERIC, old_locale); if (n == (int) strlen(str)) { /* matched */ ; } else { @@ -3981,8 +4578,9 @@ int vdef_parse( if (n == (int) strlen(str)) { /* matched */ param = DNAN; } else { - rrd_set_error("Unknown function string '%s' in VDEF '%s'", str, - gdes->vname); + rrd_set_error + ("Unknown function string '%s' in VDEF '%s'", + str, gdes->vname); return -1; } } @@ -3992,6 +4590,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)) @@ -4007,16 +4607,16 @@ int vdef_parse( else if (!strcmp("LSLCORREL", func)) gdes->vf.op = VDEF_LSLCORREL; else { - rrd_set_error("Unknown function '%s' in VDEF '%s'\n", func, - gdes->vname); + rrd_set_error + ("Unknown function '%s' in VDEF '%s'\n", func, gdes->vname); return -1; }; - switch (gdes->vf.op) { case VDEF_PERCENT: if (isnan(param)) { /* no parameter given */ - rrd_set_error("Function '%s' needs parameter in VDEF '%s'\n", - func, gdes->vname); + rrd_set_error + ("Function '%s' needs parameter in VDEF '%s'\n", + func, gdes->vname); return -1; }; if (param >= 0.0 && param <= 100.0) { @@ -4024,13 +4624,15 @@ int vdef_parse( gdes->vf.val = DNAN; /* undefined */ gdes->vf.when = 0; /* undefined */ } else { - rrd_set_error("Parameter '%f' out of range in VDEF '%s'\n", param, - gdes->vname); + rrd_set_error + ("Parameter '%f' out of range in VDEF '%s'\n", + param, gdes->vname); return -1; }; break; case VDEF_MAXIMUM: case VDEF_AVERAGE: + case VDEF_STDEV: case VDEF_MINIMUM: case VDEF_TOTAL: case VDEF_FIRST: @@ -4043,8 +4645,9 @@ int vdef_parse( gdes->vf.val = DNAN; gdes->vf.when = 0; } else { - rrd_set_error("Function '%s' needs no parameter in VDEF '%s'\n", - func, gdes->vname); + rrd_set_error + ("Function '%s' needs no parameter in VDEF '%s'\n", + func, gdes->vname); return -1; }; break; @@ -4054,10 +4657,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; @@ -4066,20 +4667,18 @@ int vdef_calc( dst = &im->gdes[gdi]; src = &im->gdes[dst->vidx]; data = src->data + src->ds; - steps = (src->end - src->start) / src->step; + steps = (src->end - src->start) / src->step; #if 0 - printf("DEBUG: start == %lu, end == %lu, %lu steps\n", src->start, - src->end, steps); + printf + ("DEBUG: start == %lu, end == %lu, %lu steps\n", + src->start, src->end, steps); #endif - switch (dst->vf.op) { case VDEF_PERCENT:{ rrd_value_t *array; int field; - - - if ((array = malloc(steps * sizeof(double))) == NULL) { + if ((array = (rrd_value_t*)malloc(steps * sizeof(double))) == NULL) { rrd_set_error("malloc VDEV_PERCENT"); return -1; } @@ -4087,15 +4686,14 @@ int vdef_calc( array[step] = data[step * src->ds_cnt]; } qsort(array, step, sizeof(double), vdef_percent_compar); - field = (steps - 1) * dst->vf.param / 100; dst->vf.val = array[field]; dst->vf.when = 0; /* no time component */ free(array); #if 0 for (step = 0; step < steps; step++) - printf("DEBUG: %3li:%10.2f %c\n", step, array[step], - step == field ? '*' : ' '); + printf("DEBUG: %3li:%10.2f %c\n", + step, array[step], step == field ? '*' : ' '); #endif } break; @@ -4121,9 +4719,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])) { @@ -4135,9 +4735,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; @@ -4211,7 +4821,6 @@ int vdef_calc( SUMxy = 0; SUMxx = 0; SUMyy = 0; - for (step = 0; step < steps; step++) { if (finite(data[step * src->ds_cnt])) { cnt++; @@ -4227,12 +4836,9 @@ int vdef_calc( y_intercept = (SUMy - slope * SUMx) / cnt; correl = (SUMxy - - (SUMx * SUMy) / cnt) / sqrt((SUMxx - - (SUMx * SUMx) / cnt) * (SUMyy - - (SUMy * - SUMy) / - cnt)); - + (SUMx * SUMy) / cnt) / + sqrt((SUMxx - + (SUMx * SUMx) / cnt) * (SUMyy - (SUMy * SUMy) / cnt)); if (cnt) { if (dst->vf.op == VDEF_LSLSLOPE) { dst->vf.val = slope; @@ -4244,7 +4850,6 @@ int vdef_calc( dst->vf.val = correl; dst->vf.when = 0; }; - } else { dst->vf.val = DNAN; dst->vf.when = 0; @@ -4257,9 +4862,10 @@ int vdef_calc( /* NaN < -INF < finite_values < INF */ int vdef_percent_compar( - a, - b) - const void *a, *b; + const void + *a, + const void + *b) { /* Equality is not returned; this doesn't hurt except * (maybe) for a little performance. @@ -4270,7 +4876,6 @@ int vdef_percent_compar( return -1; if (isnan(*(double *) b)) return 1; - /* NaN doesn't reach this part so INF and -INF are extremes. * The sign from isinf() is compatible with the sign we return */ @@ -4278,10 +4883,21 @@ int vdef_percent_compar( return isinf(*(double *) a); if (isinf(*(double *) b)) return isinf(*(double *) b); - /* If we reach this, both values must be finite */ if (*(double *) a < *(double *) b) return -1; else return 1; } + +void grinfo_push( + image_desc_t *im, + char *key, + rrd_info_type_t type, + rrd_infoval_t value) +{ + im->grinfo_current = rrd_info_push(im->grinfo_current, key, type, value); + if (im->grinfo == NULL) { + im->grinfo = im->grinfo_current; + } +}