+ switch (im->gdes[i].cf) {
+ case CF_HWPREDICT:
+ case CF_MHWPREDICT:
+ case CF_DEVPREDICT:
+ case CF_DEVSEASONAL:
+ case CF_SEASONAL:
+ case CF_AVERAGE:
+ validsteps++;
+ printval += im->gdes[vidx].data[ii];
+ break;
+ case CF_MINIMUM:
+ printval = min(printval, im->gdes[vidx].data[ii]);
+ break;
+ case CF_FAILURES:
+ case CF_MAXIMUM:
+ printval = max(printval, im->gdes[vidx].data[ii]);
+ break;
+ case CF_LAST:
+ printval = im->gdes[vidx].data[ii];
+ }
+ }
+ if (im->gdes[i].cf == CF_AVERAGE || im->gdes[i].cf > CF_LAST) {
+ if (validsteps > 1) {
+ printval = (printval / validsteps);
+ }
+ }
+ } /* prepare printval */
+
+ if ((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
+ * the value is 0, then do not set the symbol or magnification
+ * so next the calculation will be performed again. */
+ if (magfact < 0.0) {
+ auto_scale(im, &printval, &si_symb, &magfact);
+ if (printval == 0.0)
+ magfact = -1.0;
+ } else {
+ printval /= magfact;
+ }
+ *(++percent_s) = 's';
+ } else if (strstr(im->gdes[i].format, "%s") != NULL) {
+ auto_scale(im, &printval, &si_symb, &magfact);
+ }
+
+ if (im->gdes[i].gf == GF_PRINT) {
+ rrd_infoval_t prline;
+
+ if (im->gdes[i].strftm) {
+ 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);
+ return -1;
+ } else {
+ 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) {
+ 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
+ ("bad format for GPRINT in '%s'",
+ im->gdes[i].format);
+ return -1;
+ }
+#ifdef HAVE_SNPRINTF
+ 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);
+#endif
+ }
+ graphelement = 1;
+ }
+ break;
+ case GF_LINE:
+ case GF_AREA:
+ case GF_GRAD:
+ case GF_TICK:
+ graphelement = 1;
+ break;
+ case GF_HRULE:
+ if (isnan(im->gdes[i].yrule)) { /* we must set this here or the legend printer can not decide to print the legend */
+ im->gdes[i].yrule = im->gdes[vidx].vf.val;
+ };
+ graphelement = 1;
+ break;
+ case GF_VRULE:
+ if (im->gdes[i].xrule == 0) { /* again ... the legend printer needs it */
+ im->gdes[i].xrule = im->gdes[vidx].vf.when;
+ };
+ graphelement = 1;
+ break;
+ case GF_COMMENT:
+ case GF_TEXTALIGN:
+ case GF_DEF:
+ case GF_CDEF:
+ case GF_VDEF:
+#ifdef WITH_PIECHART
+ case GF_PART:
+#endif
+ case GF_SHIFT:
+ case GF_XPORT:
+ break;
+ case GF_STACK:
+ rrd_set_error
+ ("STACK should already be turned into LINE or AREA here");
+ return -1;
+ break;
+ }
+ }
+ return graphelement;
+}
+
+
+
+/* place legends with color spots */
+int leg_place(
+ image_desc_t *im,
+ 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 = 0; //im->yimg;
+ int leg_y_prev = 0; // im->yimg;
+ int leg_cc;
+ double glue = 0;
+ int i, ii, mark = 0;
+ char default_txtalign = TXA_JUSTIFIED; /*default line orientation */
+ int *legspace;
+ char *tab;
+ char saved_legend[FMT_LEG_LEN + 5];
+
+ if(calc_width){
+ legendwidth = 0;
+ }
+ else{
+ legendwidth = im->legendwidth - 2 * border;
+ }
+
+
+ if (!(im->extra_flags & NOLEGEND) && !(im->extra_flags & ONLY_GRAPH)) {
+ if ((legspace = (int*)malloc(im->gdes_c * sizeof(int))) == NULL) {
+ rrd_set_error("malloc for legspace");
+ return -1;
+ }
+
+ 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) {
+ 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))
+ 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))
+ 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 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';
+ } else {
+ prt_fctn = '\0';
+ }
+ /* only valid control codes */
+ if (prt_fctn != 'l' && prt_fctn != 'n' && /* a synonym for l */
+ prt_fctn != 'r' &&
+ prt_fctn != 'j' &&
+ prt_fctn != 'c' &&
+ prt_fctn != 'u' &&
+ 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);
+ return -1;
+ }
+ /* \n -> \l */
+ if (prt_fctn == 'n') {
+ prt_fctn = 'l';
+ }
+
+ /* remove exess space from the end of the legend for \g */
+ while (prt_fctn == 'g' &&
+ leg_cc > 0 && im->gdes[i].legend[leg_cc - 1] == ' ') {
+ leg_cc--;
+ im->gdes[i].legend[leg_cc] = '\0';
+ }
+
+ if (leg_cc != 0) {
+
+ /* no interleg space if string ends in \g */
+ legspace[i] = (prt_fctn == 'g' ? 0 : interleg);
+ if (fill > 0) {
+ fill += legspace[i];
+ }
+ 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;
+ }
+ /* who said there was a special tag ... ? */
+ if (prt_fctn == 'g') {
+ prt_fctn = '\0';
+ }
+
+ if (prt_fctn == '\0') {
+ 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:
+ 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 > legendwidth) {
+ if (leg_c > 1) {
+ /* go back one */
+ i--;
+ fill = fill_last;
+ leg_c--;
+ }
+ }
+ if (leg_c == 1 && prt_fctn == 'j') {
+ prt_fctn = 'l';
+ }
+ }
+
+ if (prt_fctn != '\0') {
+ leg_x = border;
+ if (leg_c >= 2 && prt_fctn == 'j') {
+ glue = (double)(legendwidth - fill) / (double)(leg_c - 1);
+ } else {
+ glue = 0;
+ }
+ if (prt_fctn == 'c')
+ leg_x = (double)(legendwidth - fill) / 2.0;
+ if (prt_fctn == 'r')
+ 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 + border;
+ leg_x +=
+ (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;
+ 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(calc_width){
+ strcpy(im->gdes[i].legend, saved_legend);
+ }
+ }
+
+ if(calc_width){
+ im->legendwidth = legendwidth + 2 * border;
+ }
+ else{
+ im->legendheight = leg_y + border * 0.6;
+ }
+ free(legspace);
+ }
+ return 0;
+}
+
+/* create a grid on the graph. it determines what to do
+ from the values of xsize, start and end */
+
+/* the xaxis labels are determined from the number of seconds per pixel
+ in the requested graph */
+
+int calc_horizontal_grid(
+ image_desc_t
+ *im)
+{
+ double range;
+ double scaledrange;
+ int pixel, i;
+ int gridind = 0;
+ int decimals, fractionals;
+
+ 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)) {
+ return 0;
+ }
+
+ /* find grid spaceing */
+ pixel = 1;
+ if (isnan(im->ygridstep)) {
+ if (im->extra_flags & ALTYGRID) {
+ /* find the value with max number of digits. Get number of digits */
+ decimals =
+ ceil(log10
+ (max(fabs(im->maxval), fabs(im->minval)) *
+ 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
+ && 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
+ || im->ygrid_scale.gridstep <
+ 1.8 * im->text_prop[TEXT_PROP_AXIS].size)
+ im->ygrid_scale.labfact = 2;
+ } else {
+ im->ygrid_scale.gridstep /= 5;
+ im->ygrid_scale.labfact = 5;
+ }
+ fractionals =
+ floor(log10
+ (im->ygrid_scale.gridstep *
+ (double) im->ygrid_scale.labfact * im->viewfactor /
+ im->magfact));
+ if (fractionals < 0) { /* small amplitude. */
+ int len = decimals - fractionals + 1;
+
+ if (im->unitslength < len + 2)
+ im->unitslength = len + 2;
+ 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" : ""));
+ }
+ } else { /* classic rrd grid */
+ for (i = 0; ylab[i].grid > 0; i++) {
+ pixel = im->ysize / (scaledrange / ylab[i].grid);
+ gridind = i;
+ if (pixel >= 5)
+ break;
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (pixel * ylab[gridind].lfac[i] >=
+ 1.8 * im->text_prop[TEXT_PROP_AXIS].size) {
+ im->ygrid_scale.labfact = ylab[gridind].lfac[i];
+ break;
+ }
+ }
+
+ im->ygrid_scale.gridstep = ylab[gridind].grid * im->magfact;
+ }