#if !defined(lint) static char rcs_id[] = "$Id: images.c,v 2.0.1.6 1998/03/25 11:01:42 stefan Stab $"; #endif /* ** GIF image drawing functions for http-analyze. ** ** The functions in this file use the GD library for fast GIF creation ** written by Thomas Boutell, . Please see ** http://www.boutell.com/gd/ for more details on the GD library. ** ** The functions in this source file are part of http-analyze, which is ** Copyright © 1996-1998 by Stefan Stapelberg, ** */ #include #include #include #include #if defined(unix) # include #else # if defined(WIN32) # include /* for the u_int, etc. types */ # endif # if defined(WIN32) || defined(NETWARE) # include /* for other windows/watcom stuff */ # include /* for the F_OK, etc. symbolic constants */ # endif #endif #include "config.h" #include "defs.h" #include "gd.h" #include "gdfontl.h" #include "gdfonts.h" #define FWID(arg) ((arg)->w) static gdPoint points[50]; static u_long c_hits[40]; static u_long c_files[40]; static u_long c_views[40]; static u_long c_sessions[40]; static u_long c_kbytes[40]; /* draw beveled borders */ static void makebevel(gdImagePtr const im, int const mx, int const my, int const sz, int const col1, int const col2, int const col3, int const col4) { int pt = 0; points[pt].x = 0; points[pt++].y = 0; points[pt].x = sz; points[pt++].y = sz; points[pt].x = mx-sz; points[pt++].y = sz; points[pt].x = mx; points[pt++].y = 0; gdImageFilledPolygon(im, points, pt, col1); pt = 0; points[pt].x = 0; points[pt++].y = 0; points[pt].x = sz; points[pt++].y = sz; points[pt].x = sz; points[pt++].y = my-sz; points[pt].x = 0; points[pt++].y = my; gdImageFilledPolygon(im, points, pt, col2); pt = 0; points[pt].x = 0; points[pt++].y = my; points[pt].x = sz; points[pt++].y = my-sz; points[pt].x = mx-sz; points[pt++].y = my-sz; points[pt].x = mx; points[pt++].y = my; gdImageFilledPolygon(im, points, pt, col3); pt = 0; points[pt].x = mx; points[pt++].y = my; points[pt].x = mx-sz; points[pt++].y = my-sz; points[pt].x = mx-sz; points[pt++].y = sz; points[pt].x = mx; points[pt++].y = 0; gdImageFilledPolygon(im, points, pt, col4); return; } /* add a bevel to space previously reserved */ static void addbevel(gdImagePtr const im, int const mx, int const my, int const sz, int const col1, int const col2, int const col3, int const col4) { makebevel(im, mx, my, sz, col1, col2, col3, col4); /*gdImageRectangle(im, 0, 0, mx, my, black);*/ return; } /* draw bars */ static void drawbars(gdImagePtr const im, u_long * const values, u_long const maxval, int bx, int const by, int const rx, int const ry, int const steps, int const size, int const bwid, int const fcol, int const bcol) { int idx, pt, top, left; int hdist = rx / size; bx += (hdist-bwid) / 2; for (idx=0; idx < steps; idx++) { top = by+ry+4-(values[idx]*ry/maxval); left = bx+(idx*hdist); if (ry+by-top < 2) continue; pt = 0; points[pt].x = left; points[pt++].y = ry+by+4; points[pt].x = left; points[pt++].y = top; points[pt].x = left+bwid; points[pt++].y = top; points[pt].x = left+bwid; points[pt++].y = ry+by+4; gdImageFilledPolygon(im, points, pt, fcol); gdImagePolygon(im, points, pt, bcol); } return; } /* draw bars by day or by hour */ int mn_bars(int const sizex, int const sizey, int const base, COUNTER * const cv, u_short const cvmax, u_short const units, char * const name, char * const label) { char lbuf[SMALLSIZE]; int idx, offset; u_long hits_max = 0L; u_long file_max = 0L; u_long view_max = 0L; u_long session_max = 0L; u_long kb_max = 0L; /* the image size */ int maxx = sizex-1; int maxy = sizey-1; /* the drawable region */ int basex = base; int basey = base; int rangex = maxx-(2*basex); int rangey = maxy-(2*basey); int bl1 = (rangey+1)/2; int bl2 = (rangey+1)/4; int hdist = rangex/(int)units; /* the font baselines */ int flx = basex-12; /* left X axis */ int fty = basey-18; /* top Y axis */ int fby = maxy-basey+4; /* bottom Y axis */ /* colors */ int white, grey1, grey2, grey3, black; int red, green, blue, magenta, orange; FILE *out; gdImagePtr im; for (idx=0; idx < (int)cvmax; idx++) { if ((c_hits[idx] = cv[idx].hits) > hits_max) hits_max = cv[idx].hits; if ((c_files[idx] = cv[idx].files) > file_max) file_max = cv[idx].files; if ((c_views[idx] = cv[idx].views) > view_max) view_max = cv[idx].views; if ((c_sessions[idx] = cv[idx].sessions) > session_max) session_max = cv[idx].sessions; if ((c_kbytes[idx] = (u_long)(cv[idx].bytes/1024.0)) > kb_max) kb_max = (u_long)(cv[idx].bytes/1024.0); } /* Create the image */ if ((im = gdImageCreate(sizex, sizey)) == NULL) return 0; /* first color allocated is background */ white = gdImageColorAllocate(im, 255, 255, 255); grey1 = gdImageColorAllocate(im, 242, 242, 242); grey2 = gdImageColorAllocate(im, 102, 102, 102); grey3 = gdImageColorAllocate(im, 153, 153, 153); black = gdImageColorAllocate(im, 0, 0, 0); red = gdImageColorAllocate(im, 255, 0, 0); green = gdImageColorAllocate(im, 0, 204, 0); blue = gdImageColorAllocate(im, 0, 0, 255); magenta = gdImageColorAllocate(im, 153, 0, 255); orange = gdImageColorAllocate(im, 255, 102, 0); if (hits_max == 0 || session_max == 0) { /* no hits ?!? */ (void) sprintf(lbuf, "No hits for this month!"); gdImageString(im, gdFontSmall, basex+(rangex-23*FWID(gdFontSmall))/2U, basey+(rangey/2), "No hits for this month!", black); gdImageInterlace(im, 1); /* make it interlaced */ if ((out = fopen(name, "wb")) != NULL) { gdImagePng(im, out); (void) fclose(out); } gdImageDestroy(im); return 1; } if (kb_max == 0) /* only 304's, no traffic so far */ kb_max = 1; if (file_max == 0) file_max = 1; if (view_max == 0) view_max = 1; if (units > 24) prmsg(0, "COMPUTING DAYS OF MONTH\n"); for (idx=0; idx < (int)units; idx++) { (void) sprintf(lbuf, "%2d", idx+1); gdImageString(im, gdFontSmall, basex+(idx*hdist)+FWID(gdFontSmall)/2, fby, lbuf, (units > 24 && wdtab[idx] > 4) ? blue : black); if (units > 24 && idx < cvmax) prmsg(0, "wdtab[idx=%d]=%d (%s)\n", idx, wdtab[idx], daynam[wdtab[idx]]); } (void) sprintf(lbuf, "%lu", kb_max); gdImageStringUp(im, gdFontSmall, flx, maxy-basey-bl2+strlen(lbuf)*FWID(gdFontSmall), lbuf, orange); (void) sprintf(lbuf, "%lu", session_max); gdImageStringUp(im, gdFontSmall, flx, maxy-basey-(2*bl2)+strlen(lbuf)*FWID(gdFontSmall), lbuf, red); (void) sprintf(lbuf, "%lu", view_max); gdImageStringUp(im, gdFontSmall, flx, maxy-basey-(2*bl2)-4*FWID(gdFontSmall), lbuf, magenta); (void) sprintf(lbuf, "%lu", hits_max); gdImageStringUp(im, gdFontSmall, flx, basey+strlen(lbuf)*FWID(gdFontSmall), lbuf, green); offset = basex; (void) sprintf(lbuf, "Hits"); gdImageString(im, gdFontLarge, offset, fty, lbuf, green); offset += 4*FWID(gdFontLarge); gdImageString(im, gdFontLarge, offset, fty, "/", black); offset += FWID(gdFontLarge); (void) sprintf(lbuf, "Files"); gdImageString(im, gdFontLarge, offset, fty, lbuf, blue); offset += 5*FWID(gdFontLarge); gdImageString(im, gdFontLarge, offset, fty, "/", black); offset += FWID(gdFontLarge); if (!nopageviews) { (void) sprintf(lbuf, "Pageviews"); gdImageString(im, gdFontLarge, offset, fty, lbuf, magenta); offset += 9*FWID(gdFontLarge); gdImageString(im, gdFontLarge, offset, fty, "/", black); offset += FWID(gdFontLarge); } (void) sprintf(lbuf, "Sessions"); gdImageString(im, gdFontLarge, offset, fty, lbuf, red); offset += 8*FWID(gdFontLarge); gdImageString(im, gdFontLarge, offset, fty, "/", black); offset += FWID(gdFontLarge); (void) sprintf(lbuf, "Kilobytes"); gdImageString(im, gdFontLarge, offset, fty, lbuf, orange); offset += 10*FWID(gdFontLarge); gdImageString(im, gdFontLarge, offset, fty, label, black); drawbars(im, c_hits, hits_max, basex-1, basey, rangex, bl1-4, cvmax, units, 6, green, black); drawbars(im, c_files, hits_max, basex+2, basey, rangex, bl1-4, cvmax, units, 6, blue, black); if (!nopageviews) drawbars(im, c_views, hits_max, basex+4, basey, rangex, bl1-4, cvmax, units, 6, magenta, black); drawbars(im, c_sessions, session_max, basex+1, basey+bl1, rangex, bl2-4, cvmax, units, 8, red, black); drawbars(im, c_kbytes, kb_max, basex+1, bl1+bl2+basey, rangex, bl2-4, cvmax, units, 8, orange, black); gdImageLine(im, basex, bl1+basey, maxx-basex+2, bl1+basey, black); gdImageLine(im, basex, bl1+bl2+basey, maxx-basex+2, bl1+bl2+basey, black); /* Draw a rectangle around the central area. */ gdImageRectangle(im, basex, basey, maxx-basex+2, maxy-basey, black); makebevel(im, maxx, maxy, 6, white, grey1, grey2, grey3); gdImageInterlace(im, 1); /* make it interlaced */ if ((out = fopen(name, "wb")) != NULL) { gdImagePng(im, out); (void) fclose(out); } gdImageDestroy(im); (void) memset((void *)c_hits, '\0', sizeof c_hits); (void) memset((void *)c_files, '\0', sizeof c_files); (void) memset((void *)c_views, '\0', sizeof c_views); (void) memset((void *)c_sessions, '\0', sizeof c_sessions); (void) memset((void *)c_kbytes, '\0', sizeof c_kbytes); return 1; } /* draw bars by weekday */ int wd_bars(int const sizex, int const sizey, int const base, COUNTER * const av, COUNTER * const cv, u_short const cvmax, char * const name) { char lbuf[SMALLSIZE]; int idx, ndx, offset; u_long hits_max[2] = { 0L, 0L }; u_long views_max[2] = { 0L, 0L }; /* the image size */ int maxx = sizex-1; int maxy = sizey-1; /* the drawable region */ int basex = base; int basey = base; int rangex = maxx-(2*basex)-3; int rangey = maxy-(2*basey); int hdist = rangex/18; /* the font baselines */ int flx = basex-12; /* left X axis */ int frx = maxx-basex; /* right X axis */ int fty = basey-18; /* top Y axis */ int fby = maxy-basey+4; /* bottom Y axis */ /* colors */ int white, grey1, grey2, grey3; int black, green, blue, magenta; FILE *out; gdImagePtr im; if ((im = gdImageCreate(sizex, sizey)) == NULL) return 0; /* first color allocated is background */ white = gdImageColorAllocate(im, 255, 255, 255); grey1 = gdImageColorAllocate(im, 242, 242, 242); grey2 = gdImageColorAllocate(im, 102, 102, 102); grey3 = gdImageColorAllocate(im, 153, 153, 153); black = gdImageColorAllocate(im, 0, 0, 0); green = gdImageColorAllocate(im, 0, 204, 0); blue = gdImageColorAllocate(im, 0, 0, 255); magenta = gdImageColorAllocate(im, 153, 0, 255); ndx = (int)(cvmax < 7 ? 0 : cvmax-7); prmsg(0, "COMPUTING AVERAGE WEEK, cvmax=%lu\n", cvmax); for (idx=0; idx < 7; idx++, ndx++) { if ((c_hits[idx] = av[idx].hits) > hits_max[0]) hits_max[0] = av[idx].hits; if ((c_views[idx] = av[idx].views) > views_max[0]) views_max[0] = av[idx].views; c_files[idx] = av[idx].files; if ((c_hits[wdtab[ndx]+7] = cv[ndx].hits) > hits_max[1]) hits_max[1] = cv[ndx].hits; if ((c_views[wdtab[ndx]+7] = cv[ndx].views) > views_max[1]) views_max[1] = cv[ndx].views; c_files[wdtab[ndx]+7] = cv[ndx].files; if (c_hits[wdtab[ndx]+7]) prmsg(0, "COMPUTING c_hits: wdtab[ndx=%d]=%d (%s) +7 = %d, c_hits[%d] = %lu\n", ndx, wdtab[ndx], daynam[wdtab[ndx]], wdtab[ndx]+7, wdtab[ndx]+7, c_hits[wdtab[ndx]+7]); else prmsg(0, "No hits for: wdtab[ndx=%d]=%d (%s) +7 = %d\n", ndx, wdtab[ndx], daynam[wdtab[ndx]], wdtab[ndx]+7); } offset = (maxx-20*FWID(gdFontLarge))/2; (void) sprintf(lbuf, "Hits"); gdImageString(im, gdFontLarge, offset, fty, lbuf, green); offset += 4*FWID(gdFontLarge); gdImageString(im, gdFontLarge, offset, fty, "/", black); offset += FWID(gdFontLarge); (void) sprintf(lbuf, "Files"); gdImageString(im, gdFontLarge, offset, fty, lbuf, blue); if (!nopageviews) { offset += 5*FWID(gdFontLarge); gdImageString(im, gdFontLarge, offset, fty, "/", black); offset += FWID(gdFontLarge); (void) sprintf(lbuf, "Pageviews"); gdImageString(im, gdFontLarge, offset, fty, lbuf, magenta); } gdImageString(im, gdFontLarge, basex, fty, "by weekday", black); gdImageString(im, gdFontLarge, frx-9*FWID(gdFontLarge), fty, "last week", black); offset = basex + hdist + FWID(gdFontSmall); ndx = (int)(cvmax < 7 ? 0 : cvmax-7); for (idx=0; idx < 7; idx++, ndx++) { (void) sprintf(lbuf, "%s", daynam[idx]); gdImageString(im, gdFontSmall, offset, fby, lbuf, idx>4 ? blue : black); gdImageString(im, gdFontSmall, offset+(9*hdist), fby, lbuf, idx>4 ? blue : black); offset += hdist; } if (hits_max[0]) { offset = basex+hdist; (void) sprintf(lbuf, "%lu", hits_max[0]); gdImageStringUp(im, gdFontSmall, flx, basey+strlen(lbuf)*FWID(gdFontSmall), lbuf, green); drawbars(im, c_hits, hits_max[0], offset-1, basey, (7*rangex)/18, rangey-4, 7, 7, 11, green, black); drawbars(im, c_files, hits_max[0], offset+3, basey, (7*rangex)/18, rangey-4, 7, 7, 11, blue, black); if (!nopageviews) { (void) sprintf(lbuf, "%lu", views_max[0]); gdImageStringUp(im, gdFontSmall, flx, fby-2*FWID(gdFontSmall), lbuf, magenta); drawbars(im, c_views, hits_max[0], offset+7, basey, (7*rangex)/18, rangey-4, 7, 7, 11, magenta, black); } } else gdImageString(im, gdFontSmall, basex+((7*rangex)/18-(8*FWID(gdFontSmall)))/2U, basey+(rangey/2), "No hits so far!", black); if (hits_max[1]) { offset = basex+10*hdist; (void) sprintf(lbuf, "%lu", hits_max[1]); gdImageStringUp(im, gdFontSmall, frx+2, basey+strlen(lbuf)*FWID(gdFontSmall), lbuf, green); drawbars(im, &c_hits[7], hits_max[1], offset-1, basey, (7*rangex)/18, rangey-4, 7, 7, 11, green, black); drawbars(im, &c_files[7], hits_max[1], offset+3, basey, (7*rangex)/18, rangey-4, 7, 7, 11, blue, black); if (!nopageviews) { (void) sprintf(lbuf, "%lu", views_max[1]); gdImageStringUp(im, gdFontSmall, frx+2, fby-2*FWID(gdFontSmall), lbuf, magenta); drawbars(im, &c_views[7], hits_max[1], offset+7, basey, (7*rangex)/18, rangey-4, 7, 7, 11, magenta, black); } } else gdImageString(im, gdFontSmall, basex+10*hdist+((7*rangex)/18-(8*FWID(gdFontSmall)))/2U, basey+(rangey/2), "No hits so far!", black); gdImageLine(im, maxx/2U, basey, maxx/2U, maxy-basey, black); /* Draw a rectangle around the central area. */ gdImageRectangle(im, basex, basey, maxx-basex, maxy-basey, black); makebevel(im, maxx, maxy, 6, white, grey1, grey2, grey3); gdImageInterlace(im, 1); /* make it interlaced */ if ((out = fopen(name, "wb")) != NULL) { gdImagePng(im, out); (void) fclose(out); } gdImageDestroy(im); (void) memset((void *)c_hits, '\0', sizeof c_hits); (void) memset((void *)c_views, '\0', sizeof c_views); return 1; } /* draw bars by hour */ int hr_bars(int const sizex, int const sizey, int const base, u_long * const cv, u_long const avg, char * const name) { char lbuf[SMALLSIZE]; int idx; u_long hits_max = 0L; /* the image size */ int maxx = sizex-1; int maxy = sizey-1; /* the drawable region */ int basex = base; int basey = base; int rangex = maxx-(2*basex)-3; int rangey = maxy-(2*basey); int hdist = rangex/24; /* the font baselines */ int flx = basex-12; /* left X axis */ int fty = basey-18; /* top Y axis */ int fby = maxy-basey+4; /* bottom Y axis */ /* colors */ int white, grey1, grey2, grey3, black, blue, orange; FILE *out; gdImagePtr im; if ((im = gdImageCreate(sizex, sizey)) == NULL) return 0; /* first color allocated is background */ white = gdImageColorAllocate(im, 255, 255, 255); grey1 = gdImageColorAllocate(im, 242, 242, 242); grey2 = gdImageColorAllocate(im, 102, 102, 102); grey3 = gdImageColorAllocate(im, 153, 153, 153); black = gdImageColorAllocate(im, 0, 0, 0); blue = gdImageColorAllocate(im, 0, 0, 255); orange = gdImageColorAllocate(im, 255, 102, 0); for (idx=0; idx < 24; idx++) if (cv[idx] > hits_max) hits_max = cv[idx]; gdImageString(im, gdFontLarge, basex, fty, "Average hits by hour", black); for (idx=0; idx < 24; idx++) { (void) sprintf(lbuf, "%2d", idx); gdImageString(im, gdFontSmall, basex+(idx*hdist)+FWID(gdFontSmall), fby, lbuf, black); } if (hits_max) { (void) sprintf(lbuf, "%lu", hits_max); gdImageStringUp(im, gdFontSmall, flx, basey+strlen(lbuf)*FWID(gdFontSmall), lbuf, orange); drawbars(im, cv, hits_max, basex+2, basey, rangex, rangey-4, 24, 24, 11, orange, black); /* Draw line for average hits */ if (avg > 24L) { int pos = maxy-basey-(((avg/24L)*rangey)/hits_max); (void) sprintf(lbuf, "%lu", (avg/24L)); gdImageStringUp(im, gdFontSmall, flx, pos+(strlen(lbuf)*FWID(gdFontSmall)), lbuf, blue); gdImageLine(im, basex, pos, maxx-basex, pos, blue); } } else gdImageString(im, gdFontSmall, basex+(rangex-8*FWID(gdFontSmall))/2U, basey+(rangey/2), "No hits so far!", black); /* Draw a rectangle around the central area. */ gdImageRectangle(im, basex, basey, maxx-basex, maxy-basey, black); makebevel(im, maxx, maxy, 6, white, grey1, grey2, grey3); gdImageInterlace(im, 1); /* make it interlaced */ if ((out = fopen(name, "wb")) != NULL) { gdImagePng(im, out); (void) fclose(out); } gdImageDestroy(im); return 1; } /* draw a graph */ static void drawgraph(gdImagePtr const im, u_long * const values, u_long const maxval, int const bx, int const by, int const rx, int const ry, int const steps, int const size, int const fcol, int const bcol) { int idx, pt, top, left; int hdist = rx / size; pt = 0; points[pt].x = bx; /* start point */ points[pt++].y = by+ry+4; for (idx=0; idx < steps; idx++) { /* compute pts in float precision */ top = (int)(by+ry+4-((float)values[idx]*ry/maxval)); left = bx+(idx*hdist); /* to avoid overflows */ points[pt].x = left; points[pt++].y = top; } points[pt].x = left; /* end point */ points[pt++].y = by+ry+4; gdImageFilledPolygon(im, points, pt, fcol); gdImagePolygon(im, points, pt, bcol); return; } /* draw graph by month */ int graph(COUNTER * const cv, char ** const mlabel, int sizex, int sizey, int const base, u_short const cyear, char * const name) { char lbuf[SMALLSIZE]; int idx, offset; u_long hits_max = 0L; u_long file_max = 0L; u_long view_max = 0L; u_long session_max = 0L; u_long kb_max = 0L; /* the image size */ int maxx = sizex-1; int maxy = sizey-1; /* the drawable region */ int basex = base; int basey = base; int rangex = maxx-(2*basex); int rangey = maxy-(2*basey); int bl1 = (rangey)/2; int bl2 = (rangey)/4; int hdist = rangex / 12; /* the font baselines */ int flx = basex-12; /* left X axis */ int fty = basey-18; /* top Y axis */ int fby = maxy-basey+4; /* bottom Y axis */ /* colors */ int white, grey1, grey2, grey3, black; int red, green, blue, magenta, orange; int labels = (base > 0); /* draw labels? */ FILE *out; gdImagePtr im; for (idx=0; idx < 13; idx++) { COUNTER *mp = cv+idx; if ((c_hits[idx] = mp->hits) > hits_max) hits_max = mp->hits; if ((c_files[idx] = mp->files) > file_max) file_max = mp->files; if ((c_views[idx] = mp->views) > view_max) view_max = mp->views; if ((c_sessions[idx] = mp->sessions) > session_max) session_max = mp->sessions; if ((c_kbytes[idx] = (u_long)(mp->bytes/1024.0)) > kb_max) kb_max = (u_long)(mp->bytes/1024.0); } if (!base && (hits_max == 0 || session_max == 0)) return 0; if (kb_max == 0) /* only 304's, no traffic so far */ kb_max = 1; if (file_max == 0) file_max = 1; if (view_max == 0) view_max = 1; if (!base) { /* reserve some space for bevel */ sizex += 9; sizey += 9; maxx += 9; maxy += 9; /* the drawable region */ basex = 5; basey = 5; } /* create the image, allocate colors */ if ((im = gdImageCreate(sizex, sizey)) == NULL) return 0; white = gdImageColorAllocate(im, 255, 255, 255); black = gdImageColorAllocate(im, 0, 0, 0); red = gdImageColorAllocate(im, 255, 0, 0); green = gdImageColorAllocate(im, 0, 204, 0); blue = gdImageColorAllocate(im, 0, 0, 255); magenta = gdImageColorAllocate(im, 153, 0, 255); orange = gdImageColorAllocate(im, 255, 102, 0); if (labels) { grey1 = gdImageColorAllocate(im, 242, 242, 242); grey2 = gdImageColorAllocate(im, 102, 102, 102); grey3 = gdImageColorAllocate(im, 153, 153, 153); } else { grey1 = gdImageColorAllocate(im, 63, 63, 63); grey2 = gdImageColorAllocate(im, 95, 95, 95); grey3 = gdImageColorAllocate(im, 215, 215, 215); } if (hits_max == 0 || session_max == 0) { /* no hits ?!? */ (void) sprintf(lbuf, "No hits for last 12 months!"); gdImageString(im, gdFontSmall, basex+(rangex-strlen(lbuf)*FWID(gdFontSmall))/2U, basey+(rangey/2), lbuf, black); gdImageRectangle(im, basex, basey, basex+12*hdist, maxy-basey, black); if (labels) makebevel(im, maxx, maxy, 6, white, grey1, grey2, grey3); else addbevel(im, maxx, maxy, 4, grey1, grey2, white, grey3); gdImageInterlace(im, 1); /* make it interlaced */ gdImageInterlace(im, 1); /* make it interlaced */ if ((out = fopen(name, "wb")) != NULL) { gdImagePng(im, out); (void) fclose(out); } gdImageDestroy(im); return 1; } if (labels) { for (idx=0; idx < 13; idx++) { if (!mlabel || !mlabel[idx]) continue; if (!idx) (void) sprintf(lbuf, "%3.3s %d", mlabel[idx], EPOCH(cyear-1U)); else if (idx == 12) (void) sprintf(lbuf, "%3.3s %d", mlabel[idx], EPOCH(cyear)); else (void) sprintf(lbuf, "%3.3s", mlabel[idx]); gdImageString(im, gdFontSmall, basex+(idx*hdist)-(strlen(lbuf)*FWID(gdFontSmall))/2U, fby, lbuf, black); } (void) sprintf(lbuf, "%lu", kb_max); gdImageStringUp(im, gdFontSmall, flx, maxy-basey-bl2+strlen(lbuf)*FWID(gdFontSmall), lbuf, orange); (void) sprintf(lbuf, "%lu", session_max); gdImageStringUp(im, gdFontSmall, flx, maxy-basey-(2*bl2)+strlen(lbuf)*FWID(gdFontSmall), lbuf, red); (void) sprintf(lbuf, "%lu", hits_max); gdImageStringUp(im, gdFontSmall, flx, basey+strlen(lbuf)*FWID(gdFontSmall), lbuf, green); if (!nopageviews) { (void) sprintf(lbuf, "%lu", view_max); gdImageStringUp(im, gdFontSmall, flx, maxy-basey-(2*bl2)-4*FWID(gdFontSmall), lbuf, magenta); } offset = basex; (void) sprintf(lbuf, "Hits"); gdImageString(im, gdFontLarge, offset, fty, lbuf, green); offset += 4*FWID(gdFontLarge); gdImageString(im, gdFontLarge, offset, fty, "/", black); offset += FWID(gdFontLarge); (void) sprintf(lbuf, "Files"); gdImageString(im, gdFontLarge, offset, fty, lbuf, blue); offset += 5*FWID(gdFontLarge); gdImageString(im, gdFontLarge, offset, fty, "/", black); offset += FWID(gdFontLarge); if (!nopageviews) { (void) sprintf(lbuf, "Pageviews"); gdImageString(im, gdFontLarge, offset, fty, lbuf, magenta); offset += 9*FWID(gdFontLarge); gdImageString(im, gdFontLarge, offset, fty, "/", black); offset += FWID(gdFontLarge); } (void) sprintf(lbuf, "Sessions"); gdImageString(im, gdFontLarge, offset, fty, lbuf, red); offset += 8*FWID(gdFontLarge); gdImageString(im, gdFontLarge, offset, fty, "/", black); offset += FWID(gdFontLarge); (void) sprintf(lbuf, "Kilobytes"); gdImageString(im, gdFontLarge, offset, fty, lbuf, orange); offset += 10*FWID(gdFontLarge); gdImageString(im, gdFontLarge, offset, fty, "by month", black); gdImageLine(im, basex, bl1+basey, basex+12*hdist, bl1+basey, black); gdImageLine(im, basex, bl1+bl2+basey, basex+12*hdist, bl1+bl2+basey, black); } drawgraph(im, c_hits, hits_max, basex, basey, rangex, bl1-4, 13, 12, green, black); drawgraph(im, c_files, hits_max, basex, basey, rangex, bl1-4, 13, 12, blue, black); if (!nopageviews) drawgraph(im, c_views, hits_max, basex, basey, rangex, bl1-4, 13, 12, magenta, black); drawgraph(im, c_sessions, session_max, basex, basey+bl1, rangex, bl2-4, 13, 12, red, black); drawgraph(im, c_kbytes, kb_max, basex, bl1+bl2+basey, rangex, bl2-4, 13, 12, orange, black); /* Draw a rectangle around the central area. */ gdImageRectangle(im, basex, basey, basex+12*hdist, maxy-basey, black); if (labels) makebevel(im, maxx, maxy, 6, white, grey1, grey2, grey3); else addbevel(im, maxx, maxy, 4, grey1, grey2, white, grey3); gdImageInterlace(im, 1); /* make it interlaced */ if ((out = fopen(name, "wb")) != NULL) { gdImagePng(im, out); (void) fclose(out); } gdImageDestroy(im); (void) memset((void *)c_hits, '\0', sizeof c_hits); (void) memset((void *)c_files, '\0', sizeof c_files); (void) memset((void *)c_views, '\0', sizeof c_views); (void) memset((void *)c_sessions, '\0', sizeof c_sessions); (void) memset((void *)c_kbytes, '\0', sizeof c_kbytes); return 1; } #if !defined(PI) # define PI 3.14159265358979323846 #endif #define RADIUS 120 #define MINPERCENT 1.4 #define MAX_ITEMS 14 #define COLOR_BG 0 #define COLOR_BLACK 1 #define COLOR_WHITE 2 #define COLOR_PWHITE 3 #define COLOR_IDX 4 #define NUM_COLORS 15 static int colors[NUM_COLORS]; static int tcolor[NUM_COLORS]; static struct { int x, y; int x2, y2; int fillcol; int textcol; char *name; } pts[MAX_ITEMS+1]; int c_chart(int const sizex, int const sizey, int const base, char * const name, COUNTRY ** const ccp, NLIST ** const top, size_t const max, u_long const total, char *label) { gdImagePtr im; double scale, ht; double percent; char lbuf[50]; int idx, pt; int tidx, cidx; int grey1, grey2, grey3; int leftb = (sizex-2*RADIUS)/2-4; int rightb = leftb+(2*RADIUS)+8; int oldx = 0, oldy = 0; u_long total_sum = 0L; u_long total_other = 0L; FILE *out; im = gdImageCreate(sizex, sizey); colors[COLOR_BG] = gdImageColorAllocate(im, 2, 2, 2); colors[COLOR_BLACK] = gdImageColorAllocate(im, 0, 0, 0); colors[COLOR_WHITE] = gdImageColorAllocate(im, 255, 255, 255); colors[COLOR_PWHITE] = gdImageColorAllocate(im, 220, 220, 220); grey1 = gdImageColorAllocate(im, 242, 242, 242); grey2 = gdImageColorAllocate(im, 102, 102, 102); grey3 = gdImageColorAllocate(im, 153, 153, 153); cidx = COLOR_IDX; colors[cidx] = gdImageColorAllocate(im, 0, 0, 154); /* navyblue */ tcolor[cidx++] = colors[COLOR_WHITE]; colors[cidx] = gdImageColorAllocate(im, 204, 0, 255); /* violet */ tcolor[cidx++] = colors[COLOR_WHITE]; colors[cidx] = gdImageColorAllocate(im, 52, 255, 0); /* green */ tcolor[cidx++] = colors[COLOR_BLACK]; colors[cidx] = gdImageColorAllocate(im, 52, 0, 0); /* brown */ tcolor[cidx++] = colors[COLOR_WHITE]; colors[cidx] = gdImageColorAllocate(im, 0, 204, 255); /* skyblue */ tcolor[cidx++] = colors[COLOR_BLACK]; colors[cidx] = gdImageColorAllocate(im, 255, 102, 0); /* orange */ tcolor[cidx++] = colors[COLOR_BLACK]; colors[cidx] = gdImageColorAllocate(im, 255, 154, 255); /* pink */ tcolor[cidx++] = colors[COLOR_BLACK]; colors[cidx] = gdImageColorAllocate(im, 0, 52, 0); /* darkgreen */ tcolor[cidx++] = colors[COLOR_WHITE]; colors[cidx] = gdImageColorAllocate(im, 255, 255, 0); /* yellow */ tcolor[cidx++] = colors[COLOR_BLACK]; colors[cidx] = gdImageColorAllocate(im, 255, 0, 0); /* red */ tcolor[cidx++] = colors[COLOR_BLACK]; colors[cidx] = gdImageColorAllocate(im, 154, 154, 0); /* olive */ tcolor[cidx++] = colors[COLOR_BLACK]; gdImageFilledRectangle(im, 0, 0, sizex-1, sizey-1, colors[COLOR_BG]); gdImageArc(im, sizex/2, sizey/2, RADIUS*2, RADIUS*2-60, 0, 360, colors[COLOR_BLACK]); gdImageFill(im, sizex/2, sizey/2, colors[COLOR_BLACK]); scale = total ? (PI*2)/total: 0.0; total_sum = total_other = 0L; cidx = tidx = COLOR_IDX; for (idx=0, pt=0; idx < max && pt < TABSIZE(pts); idx++) { if (!ccp && *top[idx]->str == '[') { if (idx != max-1) continue; } else if (idx >= MAX_ITEMS || total_other || (percent = ((ccp ? ccp[idx]->count : top[idx]->count)*100.0)/total) < MINPERCENT) { total_other += ccp ? ccp[idx]->count : top[idx]->count; total_sum += ccp ? ccp[idx]->count : top[idx]->count; if (idx != max-1) continue; } else { ht = total_sum + ((ccp ? ccp[idx]->count : top[idx]->count) / 2.0); total_sum += ccp ? ccp[idx]->count : top[idx]->count; } if (total_other) { if (!ccp && (total != total_sum)) { /* compensate for hidden items ([...]) */ total_other += total-total_sum; total_sum = total; } percent = (total_other*100.0)/total; ht = total_sum-total_other/2.0; (void) sprintf(lbuf, "Other %1.0f%%", percent); } else { char *tm; if (ccp) tm = ccp[idx]->name; else { tm = top[idx]->str; if (*tm == '.') tm++; else if (strneq(tm, "http://", 7)) tm += 7; else if (strneq(tm, "https://", 8)) tm += 8; } (void) sprintf(lbuf, "%.14s %1.0f%%", tm, percent); } if (idx == max-1 && cidx == COLOR_IDX) { cidx += 2; /* avoid same color for consecutive countries */ tidx += 2; } pts[pt].name = strsave(lbuf); /* check return code below */ pts[pt].x = (int)(cos((double)total_sum*scale)*RADIUS+(sizex/2)); pts[pt].y = (int)(sin((double)total_sum*scale)*RADIUS+(sizey/2)); pts[pt].x2 = (int)(cos((double)ht*scale)*(RADIUS/2)+(sizex/2)); pts[pt].y2 = (int)(sin((double)ht*scale)*(RADIUS/2)+(sizey/2)); pts[pt].fillcol = colors[cidx++]; pts[pt].textcol = tcolor[tidx++]; pt++; if (cidx == TABSIZE(colors)) cidx = COLOR_IDX; if (tidx == TABSIZE(tcolor)) tidx = COLOR_IDX; } idx = 0; gdImageLine(im, sizex/2, sizey/2, pts[idx].x, pts[idx].y, colors[COLOR_BG]); for (++idx; idx < pt; idx++) { gdImageLine(im, sizex/2, sizey/2, pts[idx].x, pts[idx].y, colors[COLOR_BG]); gdImageFillToBorder(im, pts[idx].x2, pts[idx].y2, colors[COLOR_BG], pts[idx].fillcol); } gdImageFillToBorder(im, pts[0].x2, pts[0].y2, colors[COLOR_BG], pts[0].fillcol); idx = (sizex-2*RADIUS)/2; gdImageArc(im, sizex/2, sizey/2, RADIUS*2, RADIUS*2-60, 0, 360, colors[COLOR_BLACK]); gdImageLine(im, idx, sizey/2, idx, sizey/2+12, colors[COLOR_BLACK]); gdImageLine(im, sizex-idx, sizey/2, sizex-idx, sizey/2+12, colors[COLOR_BLACK]); gdImageArc(im, sizex/2, sizey/2+12, RADIUS*2, RADIUS*2-60, 0, 180, colors[COLOR_BLACK]); gdImageFill(im, 0, 0, colors[COLOR_WHITE]); gdImageFill(im, sizex/2, sizey/2+RADIUS-20, colors[COLOR_BLACK]); for (idx=0; idx < pt; idx++) { int x, y, txtpos, len; if (!pts[idx].x) continue; x = (pts[idx].x2 <= sizex/2) ? leftb : rightb; if ((y=pts[idx].y2) <= sizey/2) { /* T */ y -= RADIUS/4; } else { /* B */ y += RADIUS/4; } if (oldx == x && oldy != 0) { if (x > sizex/2) { if (y < oldy + 12 || (y < oldy + 48) && (y > oldy + 12)) y = oldy + 12; } else if (y > oldy - 12 || (y > oldy - 48) && (y < oldy - 12)) { y = oldy - 12; } } oldx = x; oldy = y; txtpos = (x <= sizex/2) ? leftb-4 : rightb+4; gdImageLine(im, pts[idx].x2, pts[idx].y2, x, y, colors[COLOR_BLACK]); gdImageLine(im, x, y, txtpos, y, colors[COLOR_BLACK]); if (pts[idx].name != NULL) { len = strlen(pts[idx].name)*gdFontSmall->w; if (x <= sizex/2) { txtpos -= len; gdImageFilledRectangle(im, txtpos, y-5, txtpos+len, y+4, pts[idx].fillcol); } else gdImageFilledRectangle(im, txtpos, y-5, txtpos+len, y+4, pts[idx].fillcol); gdImageString(im, gdFontSmall, txtpos, y-gdFontSmall->h/2, pts[idx].name, pts[idx].textcol); free(pts[idx].name); } } makebevel(im, sizex-1, sizey-1, 6, colors[COLOR_WHITE], grey1, grey2, grey3); gdImageString(im, gdFontLarge, (sizex-strlen(label)*gdFontLarge->w)/2U, sizey-base/2-gdFontLarge->h-gdFontSmall->h+2, label, colors[COLOR_BLACK]); (void) sprintf(lbuf, "100%% = %lu hits", total_sum); gdImageString(im, gdFontSmall, (sizex-strlen(lbuf)*gdFontSmall->w)/2U, sizey-base/2-gdFontSmall->h+5, lbuf, colors[COLOR_BLACK]); gdImageInterlace(im, 1); /* make it interlaced */ if ((out = fopen(name, "wb")) != NULL) { gdImagePng(im, out); (void) fclose(out); } gdImageDestroy(im); return 1; } ICON_TAB icon_tab[] = { { "btn/sq_green.png", 0, 204, 0 }, { "btn/sq_blue.png", 0, 0, 255 }, { "btn/sq_red.png", 255, 0, 0 }, { "btn/sq_orange.png", 222, 102, 0 }, { "btn/sq_yellow.png", 242, 242, 0 }, { "btn/sq_magenta.png", 153, 0, 255 }, { "btn/sq_grey.png", 204, 204, 204 } }; BTN_TAB buttons[] = { /* various images */ { "btn/netstore_sw.png", "Netstore", 0, 0 }, { "btn/netstore_sb.png", "Netstore", 0, 0 }, { "btn/RAG_sw.png", NULL, 0, 0 }, { "btn/RAG_sb.png", NULL, 0, 0 }, { "btn/year_off.png", "summary", 0, 0 }, { "btn/totals_off.png", "totals", 0, 0 }, { "btn/days_off.png", "days", 0, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, { "btn/avload_off.png", "avload", 0, 0 }, { "btn/topurl_off.png", "topurl", 0, 0 }, { "btn/topdom_off.png", "topdom", 0, 0 }, { "btn/topuag_off.png", "topuag", 0, 0 }, { "btn/topref_off.png", "topref", 0, 0 }, { "btn/cntry_off.png", "country", 0, 0 }, { "btn/files_off.png", "files", 0, 0 }, { "btn/rfiles_off.png", "rfiles", 0, 0 }, { "btn/sites_off.png", "sites", 0, 0 }, { "btn/rsites_off.png", "rsites", 0, 0 }, { "btn/agents_off.png", "agents", 0, 0 }, { "btn/refers_off.png", "refers", 0, 0 } }; static void mkIcon(char * const fname, ICON_TAB * const tp) { FILE *out; gdImagePtr im; if ((im=gdImageCreate(10, 8)) != NULL) { (void) gdImageColorAllocate(im, tp->color[0], tp->color[1], tp->color[2]); gdImageInterlace(im, 1); if ((out=fopen(fname, "wb")) != NULL) { gdImagePng(im, out); (void) fclose(out); } gdImageDestroy(im); } return; } /* ** Check for required icon files. */ void checkForIcons(void) { int idx; /* check for icons, create them if necessary */ for (idx=0; idx < (int)TABSIZE(icon_tab); idx++) { if (access(icon_tab[idx].name, F_OK) < 0) mkIcon(icon_tab[idx].name, &icon_tab[idx]); } return; } /* ** Check for required logo files. */ int checkForLogos(char *ha_home) { FILE *fp; gdImagePtr ip; int c, idx, max = BTN_MAX; for (idx=0; idx < TABSIZE(buttons); idx++) { if (!buttons[idx].name) continue; if ((fp = fopen(buttons[idx].name, "rb")) != NULL) { ip = gdImageCreateFromPng(fp); (void) fclose(fp); buttons[idx].wid = gdImageSX(ip); buttons[idx].ht = gdImageSY(ip); c = gdImageGetPixel(ip, buttons[idx].wid/2, buttons[idx].ht/2); buttons[idx].r = ip->red[c]; buttons[idx].g = ip->green[c]; buttons[idx].b = ip->blue[c]; if (!idx) max = buttons[idx].r; gdImageDestroy(ip); } } if (max != BTN_CUSTOMW) { buttons[BTN_RAGSW].text = ha_home; buttons[BTN_RAGSB].text = ha_home; buttons[BTN_CUSTOMW] = buttons[BTN_RAGSW]; buttons[BTN_CUSTOMB] = buttons[BTN_RAGSB]; } else { buttons[BTN_RAGSW].text = ha_home; buttons[BTN_RAGSB].text = ha_home; buttons[BTN_NETSTORESW] = buttons[BTN_RAGSW]; buttons[BTN_NETSTORESB] = buttons[BTN_RAGSB]; buttons[BTN_NETSTORESW].text = buttons[BTN_NETSTORESB].text = "Netstore"; } return max; }