diff --git a/src/rrd_graph.c b/src/rrd_graph.c
index b6d5bebb4b88dd4c01bd682a2a5535aed1875334..8417291bc2f05a5691cc44a906215e609cccc81a 100644 (file)
--- a/src/rrd_graph.c
+++ b/src/rrd_graph.c
/****************************************************************************
- * RRDtool 1.3.2 Copyright by Tobi Oetiker, 1997-2008
+ * RRDtool 1.4.3 Copyright by Tobi Oetiker, 1997-2010
****************************************************************************
* rrd__graph.c produce graphs from data in rrdfiles
****************************************************************************/
#ifdef WIN32
#include "strftime.h"
#endif
+
#include "rrd_tool.h"
+/* for basename */
+#ifdef HAVE_LIBGEN_H
+# include <libgen.h>
+#else
+#include "plbasename.h"
+#endif
+
#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
#include <io.h>
#include <fcntl.h>
#endif
-#ifdef HAVE_TIME_H
#include <time.h>
-#endif
-#ifdef HAVE_LOCALE_H
#include <locale.h>
+
+#ifdef HAVE_LANGINFO_H
+#include <langinfo.h>
#endif
#include "rrd_graph.h"
, /* unit */
{8.0, RRD_DEFAULT_FONT,NULL} /* legend */
,
- {5.5, RRD_DEFAULT_FONT,NULL} /* watermark */
+ {5.5, RRD_DEFAULT_FONT,NULL} /* watermark */
};
+char week_fmt[128] = "Week %V";
+
xlab_t xlab[] = {
{0, 0, TMT_SECOND, 30, TMT_MINUTE, 5, TMT_MINUTE, 5, 0, "%H:%M"}
,
,
{2400, 0, TMT_HOUR, 12, TMT_DAY, 1, TMT_DAY, 2, 24 * 3600, "%a"}
,
- {3600, 0, TMT_DAY, 1, TMT_WEEK, 1, TMT_WEEK, 1, 7 * 24 * 3600, "Week %V"}
+ {3600, 0, TMT_DAY, 1, TMT_WEEK, 1, TMT_WEEK, 1, 7 * 24 * 3600, week_fmt}
,
- {3 * 3600, 0, TMT_WEEK, 1, TMT_MONTH, 1, TMT_WEEK, 2, 7 * 24 * 3600,
- "Week %V"}
+ {3 * 3600, 0, TMT_WEEK, 1, TMT_MONTH, 1, TMT_WEEK, 2, 7 * 24 * 3600, week_fmt}
,
{6 * 3600, 0, TMT_MONTH, 1, TMT_MONTH, 1, TMT_MONTH, 1, 30 * 24 * 3600,
"%b"}
conv_if(VRULE, GF_VRULE);
conv_if(LINE, GF_LINE);
conv_if(AREA, GF_AREA);
+ conv_if(GRAD, GF_GRAD);
conv_if(STACK, GF_STACK);
conv_if(TICK, GF_TICK);
conv_if(TEXTALIGN, GF_TEXTALIGN);
conv_if(XPORT, GF_XPORT);
conv_if(SHIFT, GF_SHIFT);
- return (-1);
+ return (enum gf_en)(-1);
}
enum gfx_if_en if_conv(
conv_if(EPS, IF_EPS);
conv_if(PDF, IF_PDF);
- return (-1);
+ return (enum gfx_if_en)(-1);
}
enum tmt_en tmt_conv(
conv_if(WEEK, TMT_WEEK);
conv_if(MONTH, TMT_MONTH);
conv_if(YEAR, TMT_YEAR);
- return (-1);
+ return (enum tmt_en)(-1);
}
enum grc_en grc_conv(
conv_if(AXIS, GRC_AXIS);
conv_if(FRAME, GRC_FRAME);
- return -1;
+ return (enum grc_en)(-1);
}
enum text_prop_en text_prop_conv(
conv_if(UNIT, TEXT_PROP_UNIT);
conv_if(LEGEND, TEXT_PROP_LEGEND);
conv_if(WATERMARK, TEXT_PROP_WATERMARK);
- return -1;
+ return (enum text_prop_en)(-1);
}
image_desc_t *im)
{
unsigned long i, ii;
- cairo_status_t status = 0;
+ cairo_status_t status = (cairo_status_t) 0;
if (im == NULL)
return 0;
free(im->gdes[i].rpnp);
}
free(im->gdes);
+
+ for (i = 0; i < DIM(text_prop);i++){
+ pango_font_description_free(im->text_prop[i].font_desc);
+ im->text_prop[i].font_desc = NULL;
+ }
+
if (im->font_options)
cairo_font_options_destroy(im->font_options);
status = cairo_status(im->cr);
cairo_destroy(im->cr);
}
+
+
if (im->rendered_image) {
free(im->rendered_image);
}
if (status)
fprintf(stderr, "OOPS: Cairo has issues it can't even die: %s\n",
cairo_status_to_string(status));
-
+
return 0;
}
}
}
+/* power prefixes */
static char si_symbol[] = {
+ 'y', /* 10e-24 Yocto */
+ 'z', /* 10e-21 Zepto */
'a', /* 10e-18 Atto */
'f', /* 10e-15 Femto */
'p', /* 10e-12 Pico */
'T', /* 10e12 Tera */
'P', /* 10e15 Peta */
'E', /* 10e18 Exa */
+ 'Z', /* 10e21 Zeta */
+ 'Y' /* 10e24 Yotta */
};
-static const int si_symbcenter = 6;
+static const int si_symbcenter = 8;
/* find SI magnitude symbol for the numbers on the y-axis*/
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;
}
ytr(im, DNAN); /* reset precalc */
log10_range = log10(im->maxval) - log10(im->minval);
}
- /* make sure first y=10^x gridline is located on
- integer pixel position by moving scale slightly
+ /* make sure first y=10^x gridline is located on
+ integer pixel position by moving scale slightly
downwards (sub-pixel movement) */
ypix = ytr(im, ya) + im->ysize; /* add im->ysize so it always is positive */
ypixfrac = ypix - floor(ypix);
}
-/* get the data required for the graphs from the
+/* get the data required for the graphs from the
relevant rrds ... */
int data_fetch(
}
if (!skip) {
unsigned long ft_step = im->gdes[i].step; /* ft_step will record what we got from fetch */
+ const char *rrd_daemon;
+ int status;
- /* 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))
+ if (im->gdes[i].daemon[0] != 0)
+ rrd_daemon = im->gdes[i].daemon;
+ else
+ rrd_daemon = im->daemon_addr;
+
+ /* "daemon" may be NULL. ENV_RRDCACHED_ADDRESS is evaluated in that
+ * case. If "daemon" holds the same value as in the previous
+ * iteration, no actual new connection is established - the
+ * existing connection is re-used. */
+ rrdc_connect (rrd_daemon);
+
+ /* If connecting was successfull, use the daemon to query the data.
+ * If there is no connection, for example because no daemon address
+ * was specified, (try to) use the local file directly. */
+ if (rrdc_is_connected (rrd_daemon))
{
- 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,
- &im->gdes[i].end,
- &ft_step,
- &im->gdes[i].ds_cnt,
- &im->gdes[i].ds_namv,
- &im->gdes[i].data)) == -1) {
- return -1;
+ status = rrdc_fetch (im->gdes[i].rrd,
+ cf_to_string (im->gdes[i].cf),
+ &im->gdes[i].start,
+ &im->gdes[i].end,
+ &ft_step,
+ &im->gdes[i].ds_cnt,
+ &im->gdes[i].ds_namv,
+ &im->gdes[i].data);
+ if (status != 0)
+ return (status);
+ }
+ else
+ {
+ if ((rrd_fetch_fn(im->gdes[i].rrd,
+ im->gdes[i].cf,
+ &im->gdes[i].start,
+ &im->gdes[i].end,
+ &ft_step,
+ &im->gdes[i].ds_cnt,
+ &im->gdes[i].ds_namv,
+ &im->gdes[i].data)) == -1) {
+ return -1;
+ }
}
im->gdes[i].data_first = 1;
+ /* must reduce to at least im->step
+ otherwhise we end up with more data than we can handle in the
+ chart and visibility of data will be random */
+ im->gdes[i].step = max(im->gdes[i].step,im->step);
if (ft_step < im->gdes[i].step) {
reduce_data(im->gdes[i].cf_reduce,
ft_step,
/* evaluate the expressions in the CDEF functions */
/*************************************************************
- * CDEF stuff
+ * CDEF stuff
*************************************************************/
long find_var_wrapper(
/* 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");
*/
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) {
{
long i, ii;
double pixstep = (double) (im->end - im->start)
- / (double) im->xsize; /* how much time
+ / (double) im->xsize; /* how much time
passes in one pixel */
double paintval;
double minval = DNAN, maxval = DNAN;
/* memory for the processed data */
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].gf == GF_LINE)
+ || (im->gdes[i].gf == GF_AREA)
+ || (im->gdes[i].gf == GF_TICK)
+ || (im->gdes[i].gf == GF_GRAD)
+ ) {
+ 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;
switch (im->gdes[ii].gf) {
case GF_LINE:
case GF_AREA:
+ case GF_GRAD:
case GF_TICK:
if (!im->gdes[ii].stack)
paintval = 0.0;
} else
if (((long int) gr_time >=
(long int) im->gdes[vidx].start)
- && ((long int) gr_time <=
+ && ((long int) gr_time <
(long int) im->gdes[vidx].end)) {
value = im->gdes[vidx].data[(unsigned long)
floor((double)
minval = 0.0; /* catching this right away below */
maxval = 5.1;
}
- /* in logarithm mode, where minval is smaller or equal
+ /* 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;
return 0;
}
-
+static int find_first_weekday(void){
+ static int first_weekday = -1;
+ if (first_weekday == -1){
+#ifdef HAVE__NL_TIME_WEEK_1STDAY
+ /* according to http://sourceware.org/ml/libc-locales/2009-q1/msg00011.html */
+ long week_1stday_l = (long) nl_langinfo (_NL_TIME_WEEK_1STDAY);
+ if (week_1stday_l == 19971130) first_weekday = 0; /* Sun */
+ else if (week_1stday_l == 19971201) first_weekday = 1; /* Mon */
+ else first_weekday = 1; /* we go for a monday default */
+#else
+ first_weekday = 1;
+#endif
+ }
+ return first_weekday;
+}
/* identify the point where the first gridline, label ... gets placed */
tm. tm_sec = 0;
tm. tm_min = 0;
tm. tm_hour = 0;
- tm. tm_mday -= tm.tm_wday - 1; /* -1 because we want the monday */
+ tm. tm_mday -= tm.tm_wday - find_first_weekday();
- if (tm.tm_wday == 0)
- tm. tm_mday -= 7; /* we want the *previous* monday */
+ if (tm.tm_wday == 0 && find_first_weekday() > 0)
+ tm. tm_mday -= 7; /* we want the *previous* week */
break;
case TMT_MONTH:
localtime_r(¤t, &tm);
+ int limit = 2;
+ switch (baseint) {
+ case TMT_SECOND: limit = 7200; break;
+ case TMT_MINUTE: limit = 120; break;
+ case TMT_HOUR: limit = 2; break;
+ default: limit = 2; break;
+ }
do {
switch (baseint) {
case TMT_SECOND:
tm. tm_year += basestep;
}
madetime = mktime(&tm);
- } while (madetime == -1); /* this is necessary to skip impssible times
+ } while (madetime == -1 && limit-- >= 0); /* this is necessary to skip impossible times
like the daylight saving time skips */
return madetime;
}
} /* prepare printval */
- if ((percent_s = strstr(im->gdes[i].format, "%S")) != NULL) {
+ if (!im->gdes[i].strftm && (percent_s = strstr(im->gdes[i].format, "%S")) != NULL) {
/* Magfact is set to -1 upon entry to print_calc. If it
* is still less than 0, then we need to run auto_scale.
* Otherwise, put the value into the correct units. If
printval /= magfact;
}
*(++percent_s) = 's';
- } else if (strstr(im->gdes[i].format, "%s") != NULL) {
+ } else if (!im->gdes[i].strftm && strstr(im->gdes[i].format, "%s") != NULL) {
auto_scale(im, &printval, &si_symb, &magfact);
}
rrd_infoval_t prline;
if (im->gdes[i].strftm) {
- prline.u_str = malloc((FMT_LEG_LEN + 2) * sizeof(char));
- strftime(prline.u_str,
- FMT_LEG_LEN, im->gdes[i].format, &tmvdef);
+ prline.u_str = (char*)malloc((FMT_LEG_LEN + 2) * sizeof(char));
+ if (im->gdes[vidx].vf.never == 1) {
+ time_clean(prline.u_str, im->gdes[i].format);
+ } else {
+ 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);
/* GF_GPRINT */
if (im->gdes[i].strftm) {
- strftime(im->gdes[i].legend,
- FMT_LEG_LEN, im->gdes[i].format, &tmvdef);
+ if (im->gdes[vidx].vf.never == 1) {
+ time_clean(im->gdes[i].legend, im->gdes[i].format);
+ } else {
+ strftime(im->gdes[i].legend,
+ FMT_LEG_LEN, im->gdes[i].format, &tmvdef);
+ }
} else {
if (bad_format(im->gdes[i].format)) {
rrd_set_error
break;
case GF_LINE:
case GF_AREA:
+ case GF_GRAD:
case GF_TICK:
graphelement = 1;
break;
}
+
/* place legends with color spots */
int leg_place(
image_desc_t *im,
- int *gY)
+ int calc_width)
{
/* graph labels */
int interleg = im->text_prop[TEXT_PROP_LEGEND].size * 2.0;
int border = im->text_prop[TEXT_PROP_LEGEND].size * 2.0;
int fill = 0, fill_last;
+ double legendwidth; // = im->ximg - 2 * border;
int leg_c = 0;
double leg_x = border;
- int leg_y = im->yimg;
- int leg_y_prev = im->yimg;
+ int leg_y = 0; //im->yimg;
+ int leg_y_prev = 0; // im->yimg;
int leg_cc;
double glue = 0;
int i, ii, mark = 0;
- char prt_fctn; /*special printfunctions */
char default_txtalign = TXA_JUSTIFIED; /*default line orientation */
int *legspace;
char *tab;
+ char saved_legend[FMT_LEG_LEN + 5];
- if (!(im->extra_flags & NOLEGEND) & !(im->extra_flags & ONLY_GRAPH)) {
- if ((legspace = malloc(im->gdes_c * sizeof(int))) == NULL) {
+ if(calc_width){
+ legendwidth = 0;
+ }
+ else{
+ legendwidth = im->legendwidth - 2 * border;
+ }
+
+
+ if (!(im->extra_flags & NOLEGEND) && !(im->extra_flags & ONLY_GRAPH)) {
+ if ((legspace = (int*)malloc(im->gdes_c * sizeof(int))) == NULL) {
rrd_set_error("malloc for legspace");
return -1;
}
- if (im->extra_flags & FULL_SIZE_MODE)
- leg_y = leg_y_prev =
- leg_y - (int) (im->text_prop[TEXT_PROP_LEGEND].size * 1.8);
for (i = 0; i < im->gdes_c; i++) {
+ char prt_fctn; /*special printfunctions */
+ if(calc_width){
+ strcpy(saved_legend, im->gdes[i].legend);
+ }
+
fill_last = fill;
/* hide legends for rules which are not displayed */
if (im->gdes[i].gf == GF_TEXTALIGN) {
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 ? */
+ /* 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;
prt_fctn != 'r' &&
prt_fctn != 'j' &&
prt_fctn != 'c' &&
+ prt_fctn != 'u' &&
+ prt_fctn != '.' &&
prt_fctn != 's' && prt_fctn != '\0' && prt_fctn != 'g') {
free(legspace);
rrd_set_error
if (prt_fctn == 'n') {
prt_fctn = 'l';
}
+ /* \. is a null operation to allow strings ending in \x */
+ if (prt_fctn == '.') {
+ prt_fctn = '\0';
+ }
/* remove exess space from the end of the legend for \g */
while (prt_fctn == 'g' &&
}
if (prt_fctn == '\0') {
- if (i == im->gdes_c - 1 || fill > im->ximg - 2 * border) {
+ if(calc_width && (fill > legendwidth)){
+ legendwidth = fill;
+ }
+ if (i == im->gdes_c - 1 || fill > legendwidth) {
/* just one legend item is left right or center */
switch (default_txtalign) {
case TXA_RIGHT:
}
}
/* is it time to place the legends ? */
- if (fill > im->ximg - 2 * border) {
+ if (fill > legendwidth) {
if (leg_c > 1) {
/* go back one */
i--;
}
}
-
if (prt_fctn != '\0') {
leg_x = border;
if (leg_c >= 2 && prt_fctn == 'j') {
- glue = (double)(im->ximg - fill - 2 * border) / (double)(leg_c - 1);
+ glue = (double)(legendwidth - fill) / (double)(leg_c - 1);
} else {
glue = 0;
}
if (prt_fctn == 'c')
- leg_x = (double)(im->ximg - fill) / 2.0;
+ leg_x = border + (double)(legendwidth - fill) / 2.0;
if (prt_fctn == 'r')
- leg_x = im->ximg - fill - border;
+ leg_x = legendwidth - fill + border;
for (ii = mark; ii <= i; ii++) {
if (im->gdes[ii].legend[0] == '\0')
continue; /* skip empty legends */
im->gdes[ii].leg_x = leg_x;
- im->gdes[ii].leg_y = leg_y;
+ im->gdes[ii].leg_y = leg_y + border;
leg_x +=
(double)gfx_get_text_width(im, leg_x,
im->
+ glue;
}
leg_y_prev = leg_y;
- if (im->extra_flags & FULL_SIZE_MODE) {
- /* only add y space if there was text on the line */
- if (leg_x > border || prt_fctn == 's')
- leg_y -= im->text_prop[TEXT_PROP_LEGEND].size * 1.8;
- if (prt_fctn == 's')
- leg_y += im->text_prop[TEXT_PROP_LEGEND].size;
- } else {
- if (leg_x > border || prt_fctn == 's')
- leg_y += im->text_prop[TEXT_PROP_LEGEND].size * 1.8;
- if (prt_fctn == 's')
- leg_y -= im->text_prop[TEXT_PROP_LEGEND].size;
+ if (leg_x > border || prt_fctn == 's')
+ leg_y += im->text_prop[TEXT_PROP_LEGEND].size * 1.8;
+ if (prt_fctn == 's')
+ leg_y -= im->text_prop[TEXT_PROP_LEGEND].size;
+ if (prt_fctn == 'u')
+ leg_y -= im->text_prop[TEXT_PROP_LEGEND].size *1.8;
+
+ if(calc_width && (fill > legendwidth)){
+ legendwidth = fill;
}
fill = 0;
leg_c = 0;
mark = ii;
}
- }
- if (im->extra_flags & FULL_SIZE_MODE) {
- if (leg_y != leg_y_prev) {
- *gY = leg_y - im->text_prop[TEXT_PROP_LEGEND].size * 1.8;
- im->yorigin =
- leg_y - im->text_prop[TEXT_PROP_LEGEND].size * 1.8;
+ if(calc_width){
+ strcpy(im->gdes[i].legend, saved_legend);
}
- } else {
- im->yimg =
- leg_y - im->text_prop[TEXT_PROP_LEGEND].size * 1.8 +
- border * 0.6;
+ }
+
+ if(calc_width){
+ im->legendwidth = legendwidth + 2 * border;
+ }
+ else{
+ im->legendheight = leg_y + border * 0.6;
}
free(legspace);
}
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 /
}
}
nlabels++;
+ if (im->second_axis_scale != 0){
+ char graph_label_right[100];
+ double sval = im->ygrid_scale.gridstep*(double)i*im->second_axis_scale+im->second_axis_shift;
+ if (im->second_axis_format[0] == '\0'){
+ if (!second_axis_magfact){
+ double dummy = im->ygrid_scale.gridstep*(double)(sgrid+egrid)/2.0*im->second_axis_scale+im->second_axis_shift;
+ auto_scale(im,&dummy,&second_axis_symb,&second_axis_magfact);
+ }
+ sval /= second_axis_magfact;
+
+ 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->
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++;
else
symbol = '?';
sprintf(graph_label, "%3.0f %c", pvalue, symbol);
- } else
+ } else {
sprintf(graph_label, "%3.0e", value);
+ }
+ 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->
mgridtm,
im->xlab_user.
mgridst);
- ti < im->end;
+ ti < im->end && ti != -1;
ti =
find_next_time(ti, im->xlab_user.gridtm, im->xlab_user.gridst)
) {
/* are we inside the graph ? */
if (ti < im->start || ti > im->end)
continue;
- while (timajor < ti) {
+ while (timajor < ti && timajor != -1) {
timajor = find_next_time(timajor,
im->
xlab_user.
mgridtm, im->xlab_user.mgridst);
}
+ if (timajor == -1) break; /* fail in case of problems with time increments */
if (ti == timajor)
continue; /* skip as falls on major grid line */
X0 = xtr(im, ti);
im->
xlab_user.
mgridst);
- ti < im->end;
+ ti < im->end && ti != -1;
ti = find_next_time(ti, im->xlab_user.mgridtm, im->xlab_user.mgridst)
) {
/* are we inside the graph ? */
labtm,
im->xlab_user.
labst);
- ti <=
+ (ti <=
im->end -
- im->xlab_user.precis / 2;
+ im->xlab_user.precis / 2) && ti != -1;
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 */
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);
+ }
+
}
void grid_paint(
double X0, Y0; /* points for filled graph and more */
struct gfx_color_t water_color;
- /* draw 3d border */
- 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_3d_border > 0) {
+ /* draw 3d border */
+ i = im->draw_3d_border;
+ gfx_new_area(im, 0, im->yimg,
+ i, im->yimg - i, i, i, im->graph_col[GRC_SHADEA]);
+ gfx_add_point(im, im->ximg - i, i);
+ gfx_add_point(im, im->ximg, 0);
+ gfx_add_point(im, 0, 0);
+ gfx_close_path(im);
+ gfx_new_area(im, i, im->yimg - i,
+ im->ximg - i,
+ im->yimg - i, im->ximg - i, i, im->graph_col[GRC_SHADEB]);
+ gfx_add_point(im, im->ximg, 0);
+ gfx_add_point(im, im->ximg, im->yimg);
+ gfx_add_point(im, 0, im->yimg);
+ gfx_close_path(im);
+ }
if (im->draw_x_grid == 1)
vertical_grid(im);
if (im->draw_y_grid == 1) {
}
/* yaxis unit description */
- gfx_text(im,
- 10,
- (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->ylegend);
+ if (im->ylegend[0] != '\0'){
+ gfx_text(im,
+ im->xOriginLegendY+10,
+ im->yOriginLegendY,
+ im->graph_col[GRC_FONT],
+ im->
+ text_prop[TEXT_PROP_UNIT].
+ font_desc,
+ im->tabwidth,
+ RRDGRAPH_YLEGEND_ANGLE, GFX_H_CENTER, GFX_V_CENTER, im->ylegend);
+
+ }
+ if (im->second_axis_legend[0] != '\0'){
+ gfx_text( im,
+ im->xOriginLegendY2+10,
+ im->yOriginLegendY2,
+ im->graph_col[GRC_FONT],
+ im->text_prop[TEXT_PROP_UNIT].font_desc,
+ im->tabwidth,
+ RRDGRAPH_YLEGEND_ANGLE,
+ GFX_H_CENTER, GFX_V_CENTER,
+ im->second_axis_legend);
+ }
+
/* graph title */
gfx_text(im,
- im->ximg / 2, 6,
+ im->xOriginTitle, im->yOriginTitle+6,
im->graph_col[GRC_FONT],
im->
text_prop[TEXT_PROP_TITLE].
font_desc,
im->tabwidth, 0.0, GFX_H_CENTER, GFX_V_TOP, im->title);
/* rrdtool 'logo' */
- 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");
+ if (!(im->extra_flags & NO_RRDTOOL_TAG)){
+ water_color = im->graph_col[GRC_FONT];
+ water_color.alpha = 0.3;
+ double xpos = im->legendposition == EAST ? im->xOriginLegendY : im->ximg - 4;
+ gfx_text(im, xpos, 5,
+ water_color,
+ im->
+ text_prop[TEXT_PROP_WATERMARK].
+ font_desc, im->tabwidth,
+ -90, GFX_H_LEFT, GFX_V_TOP, "RRDTOOL / TOBI OETIKER");
+ }
/* graph watermark */
if (im->watermark[0] != '\0') {
+ water_color = im->graph_col[GRC_FONT];
+ water_color.alpha = 0.3;
gfx_text(im,
im->ximg / 2, im->yimg - 6,
water_color,
}
/* graph labels */
- if (!(im->extra_flags & NOLEGEND) & !(im->extra_flags & ONLY_GRAPH)) {
+ if (!(im->extra_flags & NOLEGEND) && !(im->extra_flags & ONLY_GRAPH)) {
for (i = 0; i < im->gdes_c; i++) {
if (im->gdes[i].legend[0] == '\0')
continue;
/* im->gdes[i].leg_y is the bottom of the legend */
- X0 = im->gdes[i].leg_x;
- Y0 = im->gdes[i].leg_y;
+ X0 = im->xOriginLegend + im->gdes[i].leg_x;
+ Y0 = im->legenddirection == TOP_DOWN ? im->yOriginLegend + im->gdes[i].leg_y : im->yOriginLegend + im->legendheight - im->gdes[i].leg_y;
gfx_text(im, X0, Y0,
im->graph_col[GRC_FONT],
im->
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 */
- 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->dynamic_labels && im->gdes[i].gf == GF_HRULE) { /* [-] */
+ cairo_save(im->cr);
+ cairo_new_path(im->cr);
+ cairo_set_line_width(im->cr, 1.0);
+ gfx_line(im,
+ X0, Y0 - boxV / 2,
+ X0 + boxH, Y0 - boxV / 2,
+ 1.0, im->gdes[i].col);
+ gfx_close_path(im);
+ } else if (im->dynamic_labels && im->gdes[i].gf == GF_VRULE) { /* [|] */
+ cairo_save(im->cr);
+ cairo_new_path(im->cr);
+ cairo_set_line_width(im->cr, 1.0);
+ gfx_line(im,
+ X0 + boxH / 2, Y0,
+ X0 + boxH / 2, Y0 - boxV,
+ 1.0, im->gdes[i].col);
+ gfx_close_path(im);
+ } else if (im->dynamic_labels && im->gdes[i].gf == GF_LINE) { /* [/] */
+ cairo_save(im->cr);
+ cairo_new_path(im->cr);
+ cairo_set_line_width(im->cr, im->gdes[i].linewidth);
+ gfx_line(im,
+ X0, Y0,
+ X0 + boxH, Y0 - boxV,
+ im->gdes[i].linewidth, im->gdes[i].col);
+ gfx_close_path(im);
+ } else {
+ /* make sure transparent colors show up the same way as in the graph */
+ gfx_new_area(im,
+ X0, Y0 - boxV,
+ X0, Y0, X0 + boxH, Y0, im->graph_col[GRC_BACK]);
+ gfx_add_point(im, X0 + boxH, Y0 - boxV);
+ gfx_close_path(im);
+ gfx_new_area(im, X0, Y0 - boxV, X0,
+ Y0, X0 + boxH, Y0, im->gdes[i].col);
+ gfx_add_point(im, X0 + boxH, Y0 - boxV);
+ gfx_close_path(im);
+ cairo_save(im->cr);
+ cairo_new_path(im->cr);
+ cairo_set_line_width(im->cr, 1.0);
+ X1 = X0 + boxH;
+ Y1 = Y0 - boxV;
+ gfx_line_fit(im, &X0, &Y0);
+ gfx_line_fit(im, &X1, &Y1);
+ cairo_move_to(im->cr, X0, Y0);
+ cairo_line_to(im->cr, X1, Y0);
+ cairo_line_to(im->cr, X1, Y1);
+ cairo_line_to(im->cr, X0, Y1);
+ cairo_close_path(im->cr);
+ cairo_set_source_rgba(im->cr,
+ im->graph_col[GRC_FRAME].red,
+ im->graph_col[GRC_FRAME].green,
+ im->graph_col[GRC_FRAME].blue,
+ im->graph_col[GRC_FRAME].alpha);
+ }
if (im->gdes[i].dash) {
/* make box borders in legend dashed if the graph is dashed */
double dashes[] = {
/* The actual size of the image to draw is determined from
** several sources. The size given on the command line is
** the graph area but we need more as we have to draw labels
- ** and other things outside the graph area
+ ** and other things outside the graph area. If the option
+ ** --full-size-mode is selected the size defines the total
+ ** image size and the size available for the graph is
+ ** calculated.
+ */
+
+ /** +---+-----------------------------------+
+ ** | y |...............graph title.........|
+ ** | +---+-------------------------------+
+ ** | a | y | |
+ ** | x | | |
+ ** | i | a | |
+ ** | s | x | main graph area |
+ ** | | i | |
+ ** | t | s | |
+ ** | i | | |
+ ** | t | l | |
+ ** | l | b +-------------------------------+
+ ** | e | l | x axis labels |
+ ** +---+---+-------------------------------+
+ ** |....................legends............|
+ ** +---------------------------------------+
+ ** | watermark |
+ ** +---------------------------------------+
*/
- int Xvertical = 0, Ytitle =
+ int Xvertical = 0, Xvertical2 = 0, Ytitle =
0, Xylabel = 0, Xmain = 0, Ymain =
0, Yxlabel = 0, Xspacing = 15, Yspacing = 15, Ywatermark = 4;
+ // no legends and no the shall be plotted it's easy
if (im->extra_flags & ONLY_GRAPH) {
im->xorigin = 0;
im->ximg = im->xsize;
im->yimg = im->ysize;
im->yorigin = im->ysize;
+ xtr(im, 0);
ytr(im, DNAN);
return 0;
}
- /** +---+--------------------------------------------+
- ** | y |...............graph title..................|
- ** | +---+-------------------------------+--------+
- ** | a | y | | |
- ** | x | | | |
- ** | i | a | | pie |
- ** | s | x | main graph area | chart |
- ** | | i | | area |
- ** | t | s | | |
- ** | i | | | |
- ** | t | l | | |
- ** | l | b +-------------------------------+--------+
- ** | e | l | x axis labels | |
- ** +---+---+-------------------------------+--------+
- ** |....................legends.....................|
- ** +------------------------------------------------+
- ** | watermark |
- ** +------------------------------------------------+
- */
+ if(im->watermark[0] != '\0') {
+ Ywatermark = im->text_prop[TEXT_PROP_WATERMARK].size * 2;
+ }
+ // calculate the width of the left vertical legend
if (im->ylegend[0] != '\0') {
Xvertical = im->text_prop[TEXT_PROP_UNIT].size * 2;
}
+ // calculate the width of the right vertical legend
+ if (im->second_axis_legend[0] != '\0') {
+ Xvertical2 = im->text_prop[TEXT_PROP_UNIT].size * 2;
+ }
+ else{
+ Xvertical2 = Xspacing;
+ }
+
if (im->title[0] != '\0') {
/* The title is placed "inbetween" two text lines so it
** automatically has some vertical spacing. The horizontal
/* if necessary, reduce the font size of the title until it fits the image width */
Ytitle = im->text_prop[TEXT_PROP_TITLE].size * 2.6 + 10;
}
+ else{
+ // we have no title; get a little clearing from the top
+ Ytitle = Yspacing;
+ }
if (elements) {
if (im->draw_x_grid) {
+ // calculate the height of the horizontal labelling
Yxlabel = im->text_prop[TEXT_PROP_AXIS].size * 2.5;
}
if (im->draw_y_grid || im->forceleftspace) {
+ // calculate the width of the vertical labelling
Xylabel =
gfx_get_text_width(im, 0,
- im->
- text_prop
- [TEXT_PROP_AXIS].
- font_desc,
+ im->text_prop[TEXT_PROP_AXIS].font_desc,
im->tabwidth, "0") * im->unitslength;
}
}
+ // add some space to the labelling
+ Xylabel += Xspacing;
+
+ /* If the legend is printed besides the graph the width has to be
+ ** calculated first. Placing the legend north or south of the
+ ** graph requires the width calculation first, so the legend is
+ ** skipped for the moment.
+ */
+ im->legendheight = 0;
+ im->legendwidth = 0;
+ if (!(im->extra_flags & NOLEGEND)) {
+ if(im->legendposition == WEST || im->legendposition == EAST){
+ if (leg_place(im, 1) == -1){
+ return -1;
+ }
+ }
+ }
+
if (im->extra_flags & FULL_SIZE_MODE) {
+
/* The actual size of the image to draw has been determined by the user.
** The graph area is the space remaining after accounting for the legend,
- ** the watermark, the pie chart, the axis labels, and the title.
+ ** the watermark, the axis labels, and the title.
*/
- im->xorigin = 0;
im->ximg = im->xsize;
im->yimg = im->ysize;
- im->yorigin = im->ysize;
Xmain = im->ximg;
Ymain = im->yimg;
- im->yorigin += Ytitle;
+
/* Now calculate the total size. Insert some spacing where
desired. im->xorigin and im->yorigin need to correspond
with the lower left corner of the main graph area or, if
this one is not set, the imaginary box surrounding the
pie chart area. */
/* Initial size calculation for the main graph area */
- Xmain = im->ximg - (Xylabel + 2 * Xspacing);
- if (Xmain)
- Xmain -= Xspacing; /* put space between main graph area and right edge */
- 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 */
- Xmain -= Xvertical;
- im->xorigin += Xvertical;
+
+ Xmain -= Xylabel;// + Xspacing;
+ if((im->legendposition == WEST || im->legendposition == EAST) && !(im->extra_flags & NOLEGEND) ){
+ Xmain -= im->legendwidth;// + Xspacing;
+ }
+ if (im->second_axis_scale != 0){
+ Xmain -= Xylabel;
+ }
+ if (!(im->extra_flags & NO_RRDTOOL_TAG)){
+ Xmain -= Xspacing;
+ }
+
+ Xmain -= Xvertical + Xvertical2;
+
+ /* limit the remaining space to 0 */
+ if(Xmain < 1){
+ Xmain = 1;
}
im->xsize = Xmain;
- xtr(im, 0);
- /* The vertical size of the image is known in advance. The main graph area
- ** (Ymain) and im->yorigin must be set according to the space requirements
- ** of the legend and the axis labels.
- */
- if (im->extra_flags & NOLEGEND) {
- /* set dimensions correctly if using full size mode with no legend */
- im->yorigin =
- im->yimg -
- im->text_prop[TEXT_PROP_AXIS].size * 2.5 - Yspacing;
- Ymain = im->yorigin;
- } else {
- /* Determine where to place the legends onto the image.
- ** Set Ymain and adjust im->yorigin to match the space requirements.
- */
- if (leg_place(im, &Ymain) == -1)
- return -1;
+
+ /* Putting the legend north or south, the height can now be calculated */
+ if (!(im->extra_flags & NOLEGEND)) {
+ if(im->legendposition == NORTH || im->legendposition == SOUTH){
+ im->legendwidth = im->ximg;
+ if (leg_place(im, 0) == -1){
+ return -1;
+ }
+ }
}
+ if( (im->legendposition == NORTH || im->legendposition == SOUTH) && !(im->extra_flags & NOLEGEND) ){
+ Ymain -= Yxlabel + im->legendheight;
+ }
+ else{
+ Ymain -= Yxlabel;
+ }
- /* 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 the title *or* some padding above the graph */
+ Ymain -= Ytitle;
+
+ /* reserve space for padding below the graph */
+ if (im->extra_flags & NOLEGEND) {
+ Ymain -= 0.5*Yspacing;
}
- /* watermark doesn't seem to effect the vertical size of the main graph area, oh well! */
if (im->watermark[0] != '\0') {
Ymain -= Ywatermark;
}
-
+ /* limit the remaining height to 0 */
+ if(Ymain < 1){
+ Ymain = 1;
+ }
im->ysize = Ymain;
} else { /* dimension options -width and -height refer to the dimensions of the main graph area */
** and other things outside the graph area.
*/
- if (im->ylegend[0] != '\0') {
- Xvertical = im->text_prop[TEXT_PROP_UNIT].size * 2;
+ if (elements) {
+ Xmain = im->xsize; // + Xspacing;
+ Ymain = im->ysize;
}
-
- if (im->title[0] != '\0') {
- /* The title is placed "inbetween" two text lines so it
- ** automatically has some vertical spacing. The horizontal
- ** spacing is added here, on each side.
- */
- /* don't care for the with of the title
- Xtitle = gfx_get_text_width(im->canvas, 0,
- im->text_prop[TEXT_PROP_TITLE].font,
- im->text_prop[TEXT_PROP_TITLE].size,
- im->tabwidth,
- im->title, 0) + 2*Xspacing; */
- Ytitle = im->text_prop[TEXT_PROP_TITLE].size * 2.6 + 10;
+ im->ximg = Xmain + Xylabel;
+ if (!(im->extra_flags & NO_RRDTOOL_TAG)){
+ im->ximg += Xspacing;
}
- if (elements) {
- Xmain = im->xsize;
- Ymain = im->ysize;
+ if( (im->legendposition == WEST || im->legendposition == EAST) && !(im->extra_flags & NOLEGEND) ){
+ im->ximg += im->legendwidth;// + Xspacing;
+ }
+ if (im->second_axis_scale != 0){
+ im->ximg += Xylabel;
}
- /* 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;
- 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;
+ im->ximg += Xvertical + Xvertical2;
+
+ if (!(im->extra_flags & NOLEGEND)) {
+ if(im->legendposition == NORTH || im->legendposition == SOUTH){
+ im->legendwidth = im->ximg;
+ if (leg_place(im, 0) == -1){
+ return -1;
+ }
+ }
}
- xtr(im, 0);
- /* The vertical size is interesting... we need to compare
- ** the sum of {Ytitle, Ymain, Yxlabel, Ylegend, Ywatermark} with
- ** Yvertical however we need to know {Ytitle+Ymain+Yxlabel}
- ** in order to start even thinking about Ylegend or Ywatermark.
- **
- ** Do it in three portions: First calculate the inner part,
- ** then do the legend, then adjust the total height of the img,
- ** adding space for a watermark if one exists;
- */
- /* reserve space for main and/or pie */
+
im->yimg = Ymain + Yxlabel;
- im->yorigin = im->yimg - Yxlabel;
+ if( (im->legendposition == NORTH || im->legendposition == SOUTH) && !(im->extra_flags & NOLEGEND) ){
+ im->yimg += im->legendheight;
+ }
+
/* reserve space for the title *or* some padding above the graph */
if (Ytitle) {
im->yimg += Ytitle;
- im->yorigin += Ytitle;
} else {
im->yimg += 1.5 * Yspacing;
- im->yorigin += 1.5 * Yspacing;
}
/* reserve space for padding below the graph */
- im->yimg += Yspacing;
- /* Determine where to place the legends onto the image.
- ** Adjust im->yimg to match the space requirements.
- */
- if (leg_place(im, 0) == -1)
- return -1;
+ if (im->extra_flags & NOLEGEND) {
+ im->yimg += 0.5*Yspacing;
+ }
+
if (im->watermark[0] != '\0') {
im->yimg += Ywatermark;
}
}
+
+ /* In case of putting the legend in west or east position the first
+ ** legend calculation might lead to wrong positions if some items
+ ** are not aligned on the left hand side (e.g. centered) as the
+ ** legendwidth wight have been increased after the item was placed.
+ ** In this case the positions have to be recalculated.
+ */
+ if (!(im->extra_flags & NOLEGEND)) {
+ if(im->legendposition == WEST || im->legendposition == EAST){
+ if (leg_place(im, 0) == -1){
+ return -1;
+ }
+ }
+ }
+
+ /* After calculating all dimensions
+ ** it is now possible to calculate
+ ** all offsets.
+ */
+ switch(im->legendposition){
+ case NORTH:
+ im->xOriginTitle = (im->ximg / 2);
+ im->yOriginTitle = 0;
+
+ im->xOriginLegend = 0;
+ im->yOriginLegend = Ytitle;
+
+ im->xOriginLegendY = 0;
+ im->yOriginLegendY = Ytitle + im->legendheight + (Ymain / 2) + Yxlabel;
+
+ im->xorigin = Xvertical + Xylabel;
+ im->yorigin = Ytitle + im->legendheight + Ymain;
+
+ im->xOriginLegendY2 = Xvertical + Xylabel + Xmain;
+ if (im->second_axis_scale != 0){
+ im->xOriginLegendY2 += Xylabel;
+ }
+ im->yOriginLegendY2 = Ytitle + im->legendheight + (Ymain / 2) + Yxlabel;
+
+ break;
+
+ case WEST:
+ im->xOriginTitle = im->legendwidth + im->xsize / 2;
+ im->yOriginTitle = 0;
+
+ im->xOriginLegend = 0;
+ im->yOriginLegend = Ytitle;
+
+ im->xOriginLegendY = im->legendwidth;
+ im->yOriginLegendY = Ytitle + (Ymain / 2);
+
+ im->xorigin = im->legendwidth + Xvertical + Xylabel;
+ im->yorigin = Ytitle + Ymain;
+
+ im->xOriginLegendY2 = im->legendwidth + Xvertical + Xylabel + Xmain;
+ if (im->second_axis_scale != 0){
+ im->xOriginLegendY2 += Xylabel;
+ }
+ im->yOriginLegendY2 = Ytitle + (Ymain / 2);
+
+ break;
+
+ case SOUTH:
+ im->xOriginTitle = im->ximg / 2;
+ im->yOriginTitle = 0;
+
+ im->xOriginLegend = 0;
+ im->yOriginLegend = Ytitle + Ymain + Yxlabel;
+
+ im->xOriginLegendY = 0;
+ im->yOriginLegendY = Ytitle + (Ymain / 2);
+
+ im->xorigin = Xvertical + Xylabel;
+ im->yorigin = Ytitle + Ymain;
+
+ im->xOriginLegendY2 = Xvertical + Xylabel + Xmain;
+ if (im->second_axis_scale != 0){
+ im->xOriginLegendY2 += Xylabel;
+ }
+ im->yOriginLegendY2 = Ytitle + (Ymain / 2);
+
+ break;
+
+ case EAST:
+ im->xOriginTitle = im->xsize / 2;
+ im->yOriginTitle = 0;
+
+ im->xOriginLegend = Xvertical + Xylabel + Xmain + Xvertical2;
+ if (im->second_axis_scale != 0){
+ im->xOriginLegend += Xylabel;
+ }
+ im->yOriginLegend = Ytitle;
+
+ im->xOriginLegendY = 0;
+ im->yOriginLegendY = Ytitle + (Ymain / 2);
+
+ im->xorigin = Xvertical + Xylabel;
+ im->yorigin = Ytitle + Ymain;
+
+ im->xOriginLegendY2 = Xvertical + Xylabel + Xmain;
+ if (im->second_axis_scale != 0){
+ im->xOriginLegendY2 += Xylabel;
+ }
+ im->yOriginLegendY2 = Ytitle + (Ymain / 2);
+
+ if (!(im->extra_flags & NO_RRDTOOL_TAG)){
+ im->xOriginTitle += Xspacing;
+ im->xOriginLegend += Xspacing;
+ im->xOriginLegendY += Xspacing;
+ im->xorigin += Xspacing;
+ im->xOriginLegendY2 += Xspacing;
+ }
+ break;
+ }
+
+ xtr(im, 0);
ytr(im, DNAN);
return 0;
}
*data,
unsigned int length)
{
- image_desc_t *im = closure;
+ image_desc_t *im = (image_desc_t*)closure;
im->rendered_image =
- realloc(im->rendered_image, im->rendered_image_size + length);
+ (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);
// 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;
return -1;
/* 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 (i==0) we stop here ...
- * if we are lazy, try to quit ...
+ * if there are no graph elements (i==0) we stop here ...
+ * if we are lazy, try to quit ...
*/
i = print_calc(im);
if (i < 0)
return -1;
- if ((i == 0) || lazy)
+ /* if we want and can be lazy ... quit now */
+ if (i == 0)
return 0;
/**************************************************************
grinfo_push(im, sprintf_alloc("image_width"), RD_I_CNT, info);
info.u_cnt = im->yimg;
grinfo_push(im, sprintf_alloc("image_height"), RD_I_CNT, info);
+ info.u_cnt = im->start;
+ grinfo_push(im, sprintf_alloc("graph_start"), RD_I_CNT, info);
+ info.u_cnt = im->end;
+ grinfo_push(im, sprintf_alloc("graph_end"), RD_I_CNT, info);
+
+ /* if we want and can be lazy ... quit now */
+ if (lazy)
+ return 0;
/* get actual drawing data and find min and max values */
if (data_proc(im) == -1)
info.u_val = im->maxval;
grinfo_push(im, sprintf_alloc("value_max"), RD_I_VAL, info);
+
if (!calc_horizontal_grid(im))
return -1;
/* reset precalc */
if (im->gdes[i].yrule > 0) {
gfx_line(im,
im->xorigin + ii,
- im->yorigin,
+ im->yorigin + 1.0,
im->xorigin + ii,
im->yorigin -
im->gdes[i].yrule *
} else if (im->gdes[i].yrule < 0) {
gfx_line(im,
im->xorigin + ii,
- im->yorigin - im->ysize,
+ im->yorigin - im->ysize - 1.0,
im->xorigin + ii,
- im->yorigin - (1 -
+ im->yorigin - im->ysize -
im->gdes[i].
- yrule) *
+ yrule *
im->ysize, 1.0, im->gdes[i].col);
}
}
break;
case GF_LINE:
case GF_AREA:
- /* fix data points at oo and -oo */
+ case GF_GRAD: {
+ rrd_value_t diffval = im->maxval - im->minval;
+ rrd_value_t maxlimit = im->maxval + 9 * diffval;
+ rrd_value_t minlimit = im->minval - 9 * diffval;
for (ii = 0; ii < im->xsize; ii++) {
+ /* fix data points at oo and -oo */
if (isinf(im->gdes[i].p_data[ii])) {
if (im->gdes[i].p_data[ii] > 0) {
im->gdes[i].p_data[ii] = im->maxval;
} else {
im->gdes[i].p_data[ii] = im->minval;
}
-
}
+ /* some versions of cairo go unstable when trying
+ to draw way out of the canvas ... lets not even try */
+ if (im->gdes[i].p_data[ii] > maxlimit) {
+ im->gdes[i].p_data[ii] = maxlimit;
+ }
+ if (im->gdes[i].p_data[ii] < minlimit) {
+ im->gdes[i].p_data[ii] = minlimit;
+ }
} /* for */
/* *******************************************************
- a ___. (a,t)
+ a ___. (a,t)
| | ___
____| | | |
| |___|
- -------|--t-1--t--------------------------------
+ -------|--t-1--t--------------------------------
- if we know the value at time t was a then
+ if we know the value at time t was a then
we draw a square from t-1 to t with the value a.
********************************************************* */
cairo_stroke(im->cr);
cairo_restore(im->cr);
} else {
+ double lastx=0;
+ double lasty=0;
int idxI = -1;
double *foreY =
(double *) malloc(sizeof(double) * im->xsize * 2);
[cntI + 1], 4)) {
cntI++;
}
- gfx_new_area(im,
- backX[0], backY[0],
- foreX[0], foreY[0],
- foreX[cntI],
- foreY[cntI], im->gdes[i].col);
- while (cntI < idxI) {
+ if (im->gdes[i].gf != GF_GRAD) {
+ gfx_new_area(im,
+ backX[0], backY[0],
+ foreX[0], foreY[0],
+ foreX[cntI],
+ foreY[cntI], im->gdes[i].col);
+ } else {
+ lastx = foreX[cntI];
+ lasty = foreY[cntI];
+ }
+ while (cntI < idxI) {
lastI = cntI;
cntI++;
while (cntI < idxI
+ 1], 4)) {
cntI++;
}
- gfx_add_point(im, foreX[cntI], foreY[cntI]);
+ if (im->gdes[i].gf != GF_GRAD) {
+ gfx_add_point(im, foreX[cntI], foreY[cntI]);
+ } else {
+ gfx_add_rect_fadey(im,
+ lastx, foreY[0],
+ foreX[cntI], foreY[cntI], lasty,
+ im->gdes[i].col,
+ im->gdes[i].col2,
+ im->gdes[i].gradheight
+ );
+ lastx = foreX[cntI];
+ lasty = foreY[cntI];
+ }
}
- gfx_add_point(im, backX[idxI], backY[idxI]);
+ if (im->gdes[i].gf != GF_GRAD) {
+ gfx_add_point(im, backX[idxI], backY[idxI]);
+ } else {
+ gfx_add_rect_fadey(im,
+ lastx, foreY[0],
+ backX[idxI], backY[idxI], lasty,
+ im->gdes[i].col,
+ im->gdes[i].col2,
+ im->gdes[i].gradheight);
+ lastx = backX[idxI];
+ lasty = backY[idxI];
+ }
while (idxI > 1) {
lastI = idxI;
idxI--;
- 1], 4)) {
idxI--;
}
- gfx_add_point(im, backX[idxI], backY[idxI]);
+ if (im->gdes[i].gf != GF_GRAD) {
+ gfx_add_point(im, backX[idxI], backY[idxI]);
+ } else {
+ gfx_add_rect_fadey(im,
+ lastx, foreY[0],
+ backX[idxI], backY[idxI], lasty,
+ im->gdes[i].col,
+ im->gdes[i].col2,
+ im->gdes[i].gradheight);
+ lastx = backX[idxI];
+ lasty = backY[idxI];
+ }
}
idxI = -1;
drawem = 0;
- gfx_close_path(im);
+ if (im->gdes[i].gf != GF_GRAD)
+ gfx_close_path(im);
}
if (drawem != 0) {
drawem = 0;
}
lastgdes = &(im->gdes[i]);
break;
+ } /* GF_AREA, GF_LINE, GF_GRAD */
case GF_STACK:
rrd_set_error
("STACK should already be turned into LINE or AREA here");
/*****************************************************
- * graph stuff
+ * graph stuff
*****************************************************/
int gdes_alloc(
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].col2.red = 0.0;
+ im->gdes[im->gdes_c - 1].col2.green = 0.0;
+ im->gdes[im->gdes_c - 1].col2.blue = 0.0;
+ im->gdes[im->gdes_c - 1].col2.alpha = 0.0;
+ im->gdes[im->gdes_c - 1].gradheight = 50.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;
im->gdes[im->gdes_c - 1].cf = CF_AVERAGE;
im->gdes[im->gdes_c - 1].yrule = DNAN;
im->gdes[im->gdes_c - 1].xrule = 0;
+ im->gdes[im->gdes_c - 1].daemon[0] = 0;
return 0;
}
if (strcmp(walker->key, "image_info") == 0) {
prlines++;
if (((*prdata) =
- rrd_realloc((*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] = malloc((strlen(walker->value.u_str)
+ (*prdata)[prlines - 1] = (char*)malloc((strlen(walker->value.u_str)
+ 2) * sizeof(char));
strcpy((*prdata)[prlines - 1], walker->value.u_str);
(*prdata)[prlines] = NULL;
*ymax = 0;
while (walker) {
if (strcmp(walker->key, "image_width") == 0) {
- *xsize = walker->value.u_int;
+ *xsize = walker->value.u_cnt;
} else if (strcmp(walker->key, "image_height") == 0) {
- *ysize = walker->value.u_int;
+ *ysize = walker->value.u_cnt;
} else if (strcmp(walker->key, "value_min") == 0) {
*ymin = walker->value.u_val;
} else if (strcmp(walker->key, "value_max") == 0) {
} else if (strncmp(walker->key, "print", 5) == 0) { /* keys are prdate[0..] */
prlines++;
if (((*prdata) =
- rrd_realloc((*prdata),
+ (char**)rrd_realloc((*prdata),
(prlines + 1) * sizeof(char *))) == NULL) {
rrd_set_error("realloc prdata");
return 0;
}
- (*prdata)[prlines - 1] = malloc((strlen(walker->value.u_str)
+ (*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));
+ if ( fwrite(walker->value.u_blo.ptr, walker->value.u_blo.size, 1,
+ (stream ? stream : stdout)) == 0 && ferror(stream ? stream : stdout)){
+ rrd_set_error("writing image");
+ return 0;
+ }
}
/* skip anything else */
walker = walker->next;
{
image_desc_t im;
rrd_info_t *grinfo;
+ char *old_locale;
rrd_graph_init(&im);
/* a dummy surface so that we can measure text sizes for placements */
-
+ old_locale = setlocale(LC_NUMERIC, NULL);
+ setlocale(LC_NUMERIC, "C");
rrd_graph_options(argc, argv, &im);
if (rrd_test_error()) {
+ setlocale(LC_NUMERIC, old_locale); /* reenable locale */
rrd_info_free(im.grinfo);
im_free(&im);
return NULL;
}
if (optind >= argc) {
+ setlocale(LC_NUMERIC, old_locale); /* reenable locale */
rrd_info_free(im.grinfo);
im_free(&im);
rrd_set_error("missing filename");
}
if (strlen(argv[optind]) >= MAXPATH) {
+ setlocale(LC_NUMERIC, old_locale); /* reenable locale */
rrd_set_error("filename (including path) too long");
rrd_info_free(im.grinfo);
im_free(&im);
}
rrd_graph_script(argc, argv, &im, 1);
+ setlocale(LC_NUMERIC, old_locale); /* reenable locale for rendering the graph */
+
if (rrd_test_error()) {
rrd_info_free(im.grinfo);
im_free(&im);
if (im.imginfo) {
rrd_infoval_t info;
+ char *path;
char *filename;
- 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,
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;
return grinfo;
}
-static void
+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 );
+ 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';
+ /* if we already got one, drop it first */
+ pango_font_description_free(im->text_prop[prop].font_desc);
+ im->text_prop[prop].font_desc = pango_font_description_from_string( font );
};
- if (size > 0){
+ if (size > 0){
im->text_prop[prop].size = size;
};
if (im->text_prop[prop].font_desc && im->text_prop[prop].size ){
#ifdef HAVE_TZSET
tzset();
#endif
-#ifdef HAVE_SETLOCALE
- setlocale(LC_TIME, "");
-#ifdef HAVE_MBSTOWCS
- setlocale(LC_CTYPE, "");
-#endif
-#endif
+
im->base = 1000;
im->daemon_addr = NULL;
im->draw_x_grid = 1;
im->draw_y_grid = 1;
+ im->draw_3d_border = 2;
+ im->dynamic_labels = 0;
im->extra_flags = 0;
im->font_options = cairo_font_options_create();
im->forceleftspace = 0;
im->imgformat = IF_PNG;
im->imginfo = NULL;
im->lazy = 0;
+ im->legenddirection = TOP_DOWN;
+ im->legendheight = 0;
+ im->legendposition = SOUTH;
+ im->legendwidth = 0;
im->logarithmic = 0;
im->maxval = DNAN;
im->minval = 0;
im->minval = DNAN;
+ im->magfact = 1;
im->prt_c = 0;
im->rigid = 0;
im->rendered_image_size = 0;
im->ximg = 0;
im->xlab_user.minsec = -1;
im->xorigin = 0;
+ im->xOriginLegend = 0;
+ im->xOriginLegendY = 0;
+ im->xOriginLegendY2 = 0;
+ im->xOriginTitle = 0;
im->xsize = 400;
im->ygridstep = DNAN;
im->yimg = 0;
im->ylegend[0] = '\0';
+ im->second_axis_scale = 0; /* 0 disables it */
+ im->second_axis_shift = 0; /* no shift by default */
+ im->second_axis_legend[0] = '\0';
+ im->second_axis_format[0] = '\0';
im->yorigin = 0;
+ im->yOriginLegend = 0;
+ im->yOriginLegendY = 0;
+ im->yOriginLegendY2 = 0;
+ im->yOriginTitle = 0;
im->ysize = 100;
im->zoom = 1;
- im->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 10, 10);
+ im->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 10, 10);
im->cr = cairo_create(im->surface);
for (i = 0; i < DIM(text_prop); i++) {
im->text_prop[i].size = -1;
+ im->text_prop[i].font_desc = NULL;
rrd_set_font_desc(im,i, deffont ? deffont : text_prop[i].font,text_prop[i].size);
}
pango_cairo_update_context(im->cr,context);
im->layout = pango_layout_new(context);
+ g_object_unref (context);
// im->layout = pango_cairo_create_layout(im->cr);
long long_tmp;
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 */
/* *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'},
+ { "alt-autoscale", no_argument, 0, 'A'},
+ { "imgformat", required_argument, 0, 'a'},
+ { "font-smoothing-threshold", required_argument, 0, 'B'},
{ "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'},
+ { "full-size-mode", no_argument, 0, 'D'},
+ { "daemon", required_argument, 0, 'd'},
+ { "slope-mode", no_argument, 0, 'E'},
+ { "end", required_argument, 0, 'e'},
+ { "force-rules-legend", no_argument, 0, 'F'},
{ "imginfo", required_argument, 0, 'f'},
- { "imgformat", required_argument, 0, 'a'},
- { "lazy", no_argument, 0, 'z'},
- { "zoom", required_argument, 0, 'm'},
+ { "graph-render-mode", required_argument, 0, 'G'},
{ "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'},
+ { "height", required_argument, 0, 'h'},
+ { "no-minor", no_argument, 0, 'I'},
+ { "interlaced", no_argument, 0, 'i'},
{ "alt-autoscale-min", no_argument, 0, 'J'},
+ { "only-graph", no_argument, 0, 'j'},
+ { "units-length", required_argument, 0, 'L'},
+ { "lower-limit", required_argument, 0, 'l'},
{ "alt-autoscale-max", no_argument, 0, 'M'},
+ { "zoom", required_argument, 0, 'm'},
{ "no-gridfit", no_argument, 0, 'N'},
- { "units-exponent", required_argument, 0, 'X'},
- { "units-length", required_argument, 0, 'L'},
- { "units", required_argument, 0, LONGOPT_UNITS_SI},
+ { "font", required_argument, 0, 'n'},
+ { "logarithmic", no_argument, 0, 'o'},
+ { "pango-markup", no_argument, 0, 'P'},
+ { "font-render-mode", required_argument, 0, 'R'},
+ { "rigid", no_argument, 0, 'r'},
{ "step", required_argument, 0, 'S'},
+ { "start", required_argument, 0, 's'},
{ "tabwidth", required_argument, 0, 'T'},
- { "font-render-mode", required_argument, 0, 'R'},
- { "graph-render-mode", required_argument, 0, 'G'},
- { "font-smoothing-threshold", required_argument, 0, 'B'},
+ { "title", required_argument, 0, 't'},
+ { "upper-limit", required_argument, 0, 'u'},
+ { "vertical-label", required_argument, 0, 'v'},
{ "watermark", required_argument, 0, 'W'},
+ { "width", required_argument, 0, 'w'},
+ { "units-exponent", required_argument, 0, 'X'},
+ { "x-grid", required_argument, 0, 'x'},
+ { "alt-y-grid", no_argument, 0, 'Y'},
+ { "y-grid", required_argument, 0, 'y'},
+ { "lazy", no_argument, 0, 'z'},
+ { "units", required_argument, 0, LONGOPT_UNITS_SI},
{ "alt-y-mrtg", no_argument, 0, 1000}, /* this has no effect it is just here to save old apps from crashing when they use it */
- { "pango-markup", no_argument, 0, 'P'},
- { "daemon", required_argument, 0, 'd'},
+ { "disable-rrdtool-tag",no_argument, 0, 1001},
+ { "right-axis", required_argument, 0, 1002},
+ { "right-axis-label", required_argument, 0, 1003},
+ { "right-axis-format", required_argument, 0, 1004},
+ { "legend-position", required_argument, 0, 1005},
+ { "legend-direction", required_argument, 0, 1006},
+ { "border", required_argument, 0, 1007},
+ { "grid-dash", required_argument, 0, 1008},
+ { "dynamic-labels", no_argument, 0, 1009},
+ { "week-fmt", required_argument, 0, 1010},
{ 0, 0, 0, 0}
};
/* *INDENT-ON* */
int col_start, col_end;
opt = getopt_long(argc, argv,
- "s:e:x:y:v:w:h:D:iu:l:rb:oc:n:m:t:f:a:I:zgjFYAMEX:L:S:T:NR:B:W:kPd:",
+ "Aa:B:b:c:Dd:Ee:Ff:G:gh:IiJjL:l:Mm:Nn:oPR:rS:s:T:t:u:v:W:w:X:x:Yy:z",
long_options, &option_index);
if (opt == EOF)
break;
case 'g':
im->extra_flags |= NOLEGEND;
break;
+ case 1005:
+ if (strcmp(optarg, "north") == 0) {
+ im->legendposition = NORTH;
+ } else if (strcmp(optarg, "west") == 0) {
+ im->legendposition = WEST;
+ } else if (strcmp(optarg, "south") == 0) {
+ im->legendposition = SOUTH;
+ } else if (strcmp(optarg, "east") == 0) {
+ im->legendposition = EAST;
+ } else {
+ rrd_set_error("unknown legend-position '%s'", optarg);
+ return;
+ }
+ break;
+ case 1006:
+ if (strcmp(optarg, "topdown") == 0) {
+ im->legenddirection = TOP_DOWN;
+ } else if (strcmp(optarg, "bottomup") == 0) {
+ im->legenddirection = BOTTOM_UP;
+ } else {
+ rrd_set_error("unknown legend-position '%s'", optarg);
+ return;
+ }
+ break;
case 'F':
im->extra_flags |= FORCE_RULES_LEGEND;
break;
+ case 1001:
+ im->extra_flags |= NO_RRDTOOL_TAG;
+ break;
case LONGOPT_UNITS_SI:
if (im->extra_flags & FORCE_UNITS) {
rrd_set_error("--units can only be used once!");
- setlocale(LC_NUMERIC, old_locale);
return;
}
if (strcmp(optarg, "si") == 0)
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;
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;
return;
}
} else {
- setlocale(LC_NUMERIC, old_locale);
rrd_set_error("invalid y-grid format");
return;
}
break;
+ case 1007:
+ im->draw_3d_border = atoi(optarg);
+ break;
+ case 1008: /* grid-dash */
+ if(sscanf(optarg,
+ "%lf:%lf",
+ &im->grid_dash_on,
+ &im->grid_dash_off) != 2) {
+ rrd_set_error("expected grid-dash format float:float");
+ return;
+ }
+ break;
+ case 1009: /* enable dynamic labels */
+ im->dynamic_labels = 1;
+ break;
+ case 1010:
+ strncpy(week_fmt,optarg,sizeof week_fmt);
+ week_fmt[(sizeof week_fmt)-1]='\0';
+ 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);
double size = 1;
int end;
- 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++) {
if (size > 0) {
- rrd_set_font_desc(im,propidx,NULL,size);
+ rrd_set_font_desc(im,propidx,NULL,size);
}
if ((int) strlen(optarg) > end+2) {
if (optarg[end] == ':') {
- rrd_set_font_desc(im,propidx,optarg + end + 1,0);
+ rrd_set_font_desc(im,propidx,optarg + end + 1,0);
} else {
rrd_set_error
("expected : after font size in '%s'",
return;
}
} else {
- setlocale(LC_NUMERIC, old_locale);
rrd_set_error("invalid text property format");
return;
}
break;
}
case 'm':
- 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;
}
} /* while (1) */
- { /* 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);
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 {
}
if (!strcmp("PERCENT", func))
gdes->vf.op = VDEF_PERCENT;
+ else if (!strcmp("PERCENTNAN", func))
+ gdes->vf.op = VDEF_PERCENTNAN;
else if (!strcmp("MAXIMUM", func))
gdes->vf.op = VDEF_MAXIMUM;
else if (!strcmp("AVERAGE", func))
};
switch (gdes->vf.op) {
case VDEF_PERCENT:
+ case VDEF_PERCENTNAN:
if (isnan(param)) { /* no parameter given */
rrd_set_error
("Function '%s' needs parameter in VDEF '%s'\n",
gdes->vf.param = param;
gdes->vf.val = DNAN; /* undefined */
gdes->vf.when = 0; /* undefined */
+ gdes->vf.never = 1;
} else {
rrd_set_error
("Parameter '%f' out of range in VDEF '%s'\n",
gdes->vf.param = DNAN;
gdes->vf.val = DNAN;
gdes->vf.when = 0;
+ gdes->vf.never = 1;
} else {
rrd_set_error
("Function '%s' needs no parameter in VDEF '%s'\n",
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;
}
array[step] = data[step * src->ds_cnt];
}
qsort(array, step, sizeof(double), vdef_percent_compar);
- field = (steps - 1) * dst->vf.param / 100;
+ field = round((dst->vf.param * (double)(steps - 1)) / 100.0);
dst->vf.val = array[field];
dst->vf.when = 0; /* no time component */
+ dst->vf.never = 1;
free(array);
#if 0
for (step = 0; step < steps; step++)
printf("DEBUG: %3li:%10.2f %c\n",
step, array[step], step == field ? '*' : ' ');
#endif
+ }
+ break;
+ case VDEF_PERCENTNAN:{
+ rrd_value_t *array;
+ int field;
+ /* count number of "valid" values */
+ int nancount=0;
+ for (step = 0; step < steps; step++) {
+ if (!isnan(data[step * src->ds_cnt])) { nancount++; }
+ }
+ /* and allocate it */
+ if ((array = (rrd_value_t*)malloc(nancount * sizeof(double))) == NULL) {
+ rrd_set_error("malloc VDEV_PERCENT");
+ return -1;
+ }
+ /* and fill it in */
+ field=0;
+ for (step = 0; step < steps; step++) {
+ if (!isnan(data[step * src->ds_cnt])) {
+ array[field] = data[step * src->ds_cnt];
+ field++;
+ }
+ }
+ qsort(array, nancount, sizeof(double), vdef_percent_compar);
+ field = round( dst->vf.param * (double)(nancount - 1) / 100.0);
+ dst->vf.val = array[field];
+ dst->vf.when = 0; /* no time component */
+ dst->vf.never = 1;
+ free(array);
}
break;
case VDEF_MAXIMUM:
if (step == steps) {
dst->vf.val = DNAN;
dst->vf.when = 0;
+ dst->vf.never = 1;
} else {
dst->vf.val = data[step * src->ds_cnt];
dst->vf.when = src->start + (step + 1) * src->step;
+ dst->vf.never = 0;
}
while (step != steps) {
if (finite(data[step * src->ds_cnt])) {
if (data[step * src->ds_cnt] > dst->vf.val) {
dst->vf.val = data[step * src->ds_cnt];
dst->vf.when = src->start + (step + 1) * src->step;
+ dst->vf.never = 0;
}
}
step++;
if (dst->vf.op == VDEF_TOTAL) {
dst->vf.val = sum * src->step;
dst->vf.when = 0; /* no time component */
+ dst->vf.never = 1;
} else if (dst->vf.op == VDEF_AVERAGE) {
dst->vf.val = sum / cnt;
dst->vf.when = 0; /* no time component */
+ dst->vf.never = 1;
} else {
average = sum / cnt;
sum = 0.0;
}
dst->vf.val = pow(sum / cnt, 0.5);
dst->vf.when = 0; /* no time component */
+ dst->vf.never = 1;
};
} else {
dst->vf.val = DNAN;
dst->vf.when = 0;
+ dst->vf.never = 1;
}
}
break;
if (step == steps) {
dst->vf.val = DNAN;
dst->vf.when = 0;
+ dst->vf.never = 1;
} else {
dst->vf.val = data[step * src->ds_cnt];
dst->vf.when = src->start + (step + 1) * src->step;
+ dst->vf.never = 0;
}
while (step != steps) {
if (finite(data[step * src->ds_cnt])) {
if (data[step * src->ds_cnt] < dst->vf.val) {
dst->vf.val = data[step * src->ds_cnt];
dst->vf.when = src->start + (step + 1) * src->step;
+ dst->vf.never = 0;
}
}
step++;
if (step == steps) { /* all entries were NaN */
dst->vf.val = DNAN;
dst->vf.when = 0;
+ dst->vf.never = 1;
} else {
dst->vf.val = data[step * src->ds_cnt];
dst->vf.when = src->start + step * src->step;
+ dst->vf.never = 0;
}
break;
case VDEF_LAST:
if (step < 0) { /* all entries were NaN */
dst->vf.val = DNAN;
dst->vf.when = 0;
+ dst->vf.never = 1;
} else {
dst->vf.val = data[step * src->ds_cnt];
dst->vf.when = src->start + (step + 1) * src->step;
+ dst->vf.never = 0;
}
break;
case VDEF_LSLSLOPE:
if (dst->vf.op == VDEF_LSLSLOPE) {
dst->vf.val = slope;
dst->vf.when = 0;
+ dst->vf.never = 1;
} else if (dst->vf.op == VDEF_LSLINT) {
dst->vf.val = y_intercept;
dst->vf.when = 0;
+ dst->vf.never = 1;
} else if (dst->vf.op == VDEF_LSLCORREL) {
dst->vf.val = correl;
dst->vf.when = 0;
+ dst->vf.never = 1;
};
} else {
dst->vf.val = DNAN;
dst->vf.when = 0;
+ dst->vf.never = 1;
}
}
break;
im->grinfo = im->grinfo_current;
}
}
+
+
+void time_clean(
+ char *result,
+ char *format)
+{
+ int j, jj;
+
+/* Handling based on
+ - ANSI C99 Specifications http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf
+ - Single UNIX Specification version 2 http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html
+ - POSIX:2001/Single UNIX Specification version 3 http://www.opengroup.org/onlinepubs/009695399/functions/strftime.html
+ - POSIX:2008 Specifications http://www.opengroup.org/onlinepubs/9699919799/functions/strftime.html
+ Specifications tells
+ "If a conversion specifier is not one of the above, the behavior is undefined."
+
+ C99 tells
+ "A conversion specifier consists of a % character, possibly followed by an E or O modifier character (described below), followed by a character that determines the behavior of the conversion specifier.
+
+ POSIX:2001 tells
+ "A conversion specification consists of a '%' character, possibly followed by an E or O modifier, and a terminating conversion specifier character that determines the conversion specification's behavior."
+
+ POSIX:2008 introduce more complexe behavior that are not handled here.
+
+ According to this, this code will replace:
+ - % followed by @ by a %@
+ - % followed by by a %SPACE
+ - % followed by . by a %.
+ - % followed by % by a %
+ - % followed by t by a TAB
+ - % followed by E then anything by '-'
+ - % followed by O then anything by '-'
+ - % followed by anything else by at least one '-'. More characters may be added to better fit expected output length
+*/
+
+ jj = 0;
+ for(j = 0; (j < FMT_LEG_LEN - 1) && (jj < FMT_LEG_LEN); j++) { /* we don't need to parse the last char */
+ if (format[j] == '%') {
+ if ((format[j+1] == 'E') || (format[j+1] == 'O')) {
+ result[jj++] = '-';
+ j+=2; /* We skip next 2 following char */
+ } else if ((format[j+1] == 'C') || (format[j+1] == 'd') ||
+ (format[j+1] == 'g') || (format[j+1] == 'H') ||
+ (format[j+1] == 'I') || (format[j+1] == 'm') ||
+ (format[j+1] == 'M') || (format[j+1] == 'S') ||
+ (format[j+1] == 'U') || (format[j+1] == 'V') ||
+ (format[j+1] == 'W') || (format[j+1] == 'y')) {
+ result[jj++] = '-';
+ if (jj < FMT_LEG_LEN) {
+ result[jj++] = '-';
+ }
+ j++; /* We skip the following char */
+ } else if (format[j+1] == 'j') {
+ result[jj++] = '-';
+ if (jj < FMT_LEG_LEN - 1) {
+ result[jj++] = '-';
+ result[jj++] = '-';
+ }
+ j++; /* We skip the following char */
+ } else if ((format[j+1] == 'G') || (format[j+1] == 'Y')) {
+ /* Assuming Year on 4 digit */
+ result[jj++] = '-';
+ if (jj < FMT_LEG_LEN - 2) {
+ result[jj++] = '-';
+ result[jj++] = '-';
+ result[jj++] = '-';
+ }
+ j++; /* We skip the following char */
+ } else if (format[j+1] == 'R') {
+ result[jj++] = '-';
+ if (jj < FMT_LEG_LEN - 3) {
+ result[jj++] = '-';
+ result[jj++] = ':';
+ result[jj++] = '-';
+ result[jj++] = '-';
+ }
+ j++; /* We skip the following char */
+ } else if (format[j+1] == 'T') {
+ result[jj++] = '-';
+ if (jj < FMT_LEG_LEN - 6) {
+ result[jj++] = '-';
+ result[jj++] = ':';
+ result[jj++] = '-';
+ result[jj++] = '-';
+ result[jj++] = ':';
+ result[jj++] = '-';
+ result[jj++] = '-';
+ }
+ j++; /* We skip the following char */
+ } else if (format[j+1] == 'F') {
+ result[jj++] = '-';
+ if (jj < FMT_LEG_LEN - 8) {
+ result[jj++] = '-';
+ result[jj++] = '-';
+ result[jj++] = '-';
+ result[jj++] = '-';
+ result[jj++] = '-';
+ result[jj++] = '-';
+ result[jj++] = '-';
+ result[jj++] = '-';
+ result[jj++] = '-';
+ }
+ j++; /* We skip the following char */
+ } else if (format[j+1] == 'D') {
+ result[jj++] = '-';
+ if (jj < FMT_LEG_LEN - 6) {
+ result[jj++] = '-';
+ result[jj++] = '/';
+ result[jj++] = '-';
+ result[jj++] = '-';
+ result[jj++] = '/';
+ result[jj++] = '-';
+ result[jj++] = '-';
+ }
+ j++; /* We skip the following char */
+ } else if (format[j+1] == 'n') {
+ result[jj++] = '\r';
+ result[jj++] = '\n';
+ j++; /* We skip the following char */
+ } else if (format[j+1] == 't') {
+ result[jj++] = '\t';
+ j++; /* We skip the following char */
+ } else if (format[j+1] == '%') {
+ result[jj++] = '%';
+ j++; /* We skip the following char */
+ } else if (format[j+1] == ' ') {
+ if (jj < FMT_LEG_LEN - 1) {
+ result[jj++] = '%';
+ result[jj++] = ' ';
+ }
+ j++; /* We skip the following char */
+ } else if (format[j+1] == '.') {
+ if (jj < FMT_LEG_LEN - 1) {
+ result[jj++] = '%';
+ result[jj++] = '.';
+ }
+ j++; /* We skip the following char */
+ } else if (format[j+1] == '@') {
+ if (jj < FMT_LEG_LEN - 1) {
+ result[jj++] = '%';
+ result[jj++] = '@';
+ }
+ j++; /* We skip the following char */
+ } else {
+ result[jj++] = '-';
+ j++; /* We skip the following char */
+ }
+ } else {
+ result[jj++] = format[j];
+ }
+ }
+ result[jj] = '\0'; /* We must force the end of the string */
+}