/* This software may only be used by you under license from AT&T Corp. ("AT&T"). A copy of AT&T's Source Code Agreement is available at AT&T's Internet website having the URL: If you received this software without first entering into a license with AT&T, you have an infringing copy of this software and cannot use it without violating AT&T's intellectual property rights. */ #pragma prototyped #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef _UWIN #ifndef DEFAULT_FONTPATH #define DEFAULT_FONTPATH "/win/fonts" #endif /* DEFAULT_FONTPATH */ #else #ifndef MSWIN32 #ifndef DEFAULT_FONTPATH #define DEFAULT_FONTPATH "/usr/share/ttf:/usr/local/share/ttf:/usr/share/fonts/ttf:/usr/local/share/fonts/ttf:/usr/lib/fonts:/usr/local/lib/fonts:/usr/lib/fonts/ttf:/usr/local/lib/fonts/ttf:/usr/common/graphviz/lib/fonts/ttf:/windows/fonts:/dos/windows/fonts:/usr/add-on/share/ttf:." #endif /* DEFAULT_FONTPATH */ #else #ifndef DEFAULT_FONTPATH #define DEFAULT_FONTPATH "%WINDIR%/FONTS;C:/WINDOWS/FONTS;C:/WINNT/Fonts;C:/winnt/fonts" #endif /* DEFAULT_FONTPATH */ #endif /* MSWIN32 */ #endif /* _UWIN */ #include "render.h" #include "gd.h" #include "utils.h" #include #ifdef MSWIN32 #include #endif #define SCALE (GD_RESOLUTION/72.0) #define BEZIERSUBDIVISION 10 /* fontsize at which text is omitted entirely */ #define FONTSIZE_MUCH_TOO_SMALL 0.15 /* fontsize at which text is rendered by a simple line */ #define FONTSIZE_TOO_SMALL 1.5 /* font modifiers */ #define REGULAR 0 #define BOLD 1 #define ITALIC 2 /* patterns */ #define P_SOLID 0 #define P_DOTTED 4 #define P_DASHED 11 #define P_NONE 15 /* bold line constant */ #define WIDTH_NORMAL 1 #define WIDTH_BOLD 3 static int N_pages; static point Pages; static double Scale; static pointf Offset; static double ArgScale; static int Rot; static box PB; static gdImagePtr im; static Dict_t *ImageDict; typedef struct context_t { int pencolor, fillcolor; char *fontfam, fontopt, font_was_set, pen, fill, penwidth; double fontsz; } context_t; #define MAXNEST 4 static context_t cstk[MAXNEST]; static int SP; static node_t *Curnode; static void gd_reset(void) { } int black, white, transparent; static void init_gd() { SP = 0; /* must create default background color first... */ /* we have to force the background to be filled for some reason */ white = gdImageColorResolve(im, gdRedMax,gdGreenMax,gdBlueMax); gdImageFilledRectangle(im, 0, 0, im->sx-1, im->sy-1, white); black = gdImageColorResolve(im, 0, 0, 0); /* transparent uses an rgb value very close to white so that formats like GIF that don't support transparency show a white background */ transparent = gdImageColorResolveAlpha(im, gdRedMax,gdGreenMax,gdBlueMax-1, gdAlphaTransparent); gdImageColorTransparent(im, transparent); cstk[0].pencolor = black; /* set pen black*/ cstk[0].fillcolor = black; /* set fill black*/ cstk[0].fontfam = "times"; /* font family name */ cstk[0].fontopt = REGULAR; /* modifier: REGULAR, BOLD or ITALIC */ cstk[0].pen = P_SOLID; /* pen pattern style, default is solid */ cstk[0].fill = P_NONE; cstk[0].penwidth = WIDTH_NORMAL; } pointf gdpt(pointf p) { pointf rv; if (Rot == 0) { rv.x = PB.LL.x + p.x * Scale + Offset.x; rv.y = PB.UR.y - 1 - p.y * Scale - Offset.y; } else { rv.x = PB.UR.x - 1 - p.y * Scale - Offset.x; rv.y = PB.UR.y - 1 - p.x * Scale - Offset.y; } return rv; } void gd_font(context_t* cp) { /* char *fw, *fa; fw = fa = "Regular"; switch (cp->fontopt) { case BOLD: fw = "Bold"; break; case ITALIC: fa = "Italic"; break; } */ } static void gd_begin_job(FILE *ofp, graph_t *g, char **lib, char *user, char *info[], point pages) { Pages = pages; N_pages = pages.x * pages.y; #ifdef MYTRACE fprintf(stderr,"gd_begin_job\n"); #endif } static void gd_end_job(void) { #ifdef MYTRACE fprintf(stderr,"gd_end_job\n"); #endif } void gd_begin_graph(graph_t* g, box bb, point pb) { PB.LL.x = PB.LL.y = 0; PB.UR.x = (bb.UR.x - bb.LL.x + 2*GD_drawing(g)->margin.x) * SCALE; PB.UR.y = (bb.UR.y - bb.LL.y + 2*GD_drawing(g)->margin.y) * SCALE; Offset.x = GD_drawing(g)->margin.x * SCALE; Offset.y = GD_drawing(g)->margin.y * SCALE; } static void gd_begin_graph_to_file(graph_t* g, box bb, point pb) { gd_begin_graph(g, bb, pb); if (Verbose) fprintf(stderr,"%s: allocating a %dK GD image\n", CmdName, (PB.UR.x - PB.LL.x - 1) * (PB.UR.y - PB.LL.y - 1) / 1024); if (mapbool(agget(g,"truecolor"))) im = gdImageCreateTrueColor((PB.UR.x - PB.LL.x - 1), (PB.UR.y - PB.LL.y - 1)); else im = gdImageCreate((PB.UR.x - PB.LL.x - 1), (PB.UR.y - PB.LL.y - 1)); init_gd(); #ifdef MYTRACE fprintf(stderr,"gd_begin_graph_to_file\n"); #endif } static void gd_begin_graph_to_memory(graph_t* g, box bb, point pb) { gd_begin_graph(g, bb, pb); if (Verbose) fprintf(stderr,"%s: using existing GD image\n", CmdName); im = *(gdImagePtr *)Output_file; init_gd(); #ifdef MYTRACE fprintf(stderr,"gd_begin_graph_to_memory\n"); #endif } static void gd_end_graph_to_file(void) { /* * Windows will do \n -> \r\n translations on stdout unless told otherwise. */ #ifdef HAVE_SETMODE #ifdef O_BINARY setmode(fileno(Output_file), O_BINARY); #endif #endif /* * Write IM to OUTFILE as a JFIF-formatted JPEG image, using quality * JPEG_QUALITY. If JPEG_QUALITY is in the range 0-100, increasing values * represent higher quality but also larger image size. If JPEG_QUALITY is * negative, the IJG JPEG library's default quality is used (which * should be near optimal for many applications). See the IJG JPEG * library documentation for more details. */ #define JPEG_QUALITY -1 if (Output_lang == GD) { gdImageGd(im, Output_file); #ifdef HAVE_LIBZ } else if (Output_lang == GD2) { #define GD2_CHUNKSIZE 128 #define GD2_RAW 1 #define GD2_COMPRESSED 2 gdImageGd2(im, Output_file, GD2_CHUNKSIZE, GD2_COMPRESSED); #endif #ifdef WITH_GIF } else if (Output_lang == GIF) { gdImageGif(im, Output_file); #endif #ifdef HAVE_LIBPNG #ifdef HAVE_LIBZ } else if (Output_lang == PNG) { gdImagePng(im, Output_file); #endif #endif #ifdef HAVE_LIBJPEG } else if (Output_lang == JPEG) { gdImageJpeg(im, Output_file, JPEG_QUALITY); #endif } else if (Output_lang == WBMP) { /* Use black for the foreground color for the B&W wbmp image. */ gdImageWBMP(im, black, Output_file); #ifdef HAVE_LIBXPM } else if (Output_lang == XBM) { gdImageXbm(im, Output_file); #endif } if (ImageDict) { dtclose(ImageDict); ImageDict = 0; } gdImageDestroy(im); #ifdef MYTRACE fprintf(stderr,"gd_end_graph_to_file\n"); #endif } static void gd_end_graph_to_memory(void) { /* leave image in memory to be handled by Gdtclft output routines */ #ifdef MYTRACE fprintf(stderr,"gd_end_graph_to_memory\n"); #endif } void gd_begin_page(graph_t *g, point page, double scale, int rot, point offset) { int page_number; point sz; ArgScale = scale; Scale = scale * SCALE; Rot = rot; page_number = page.x + page.y * Pages.x + 1; sz = sub_points(PB.UR, PB.LL); #ifdef MYTRACE fprintf(stderr,"gd_begin_page\n"); fprintf(stderr," page=%d,%d offset=%d,%d\n",page.x,page.y,offset.x,offset.y); fprintf(stderr," page_number=%d\n",page_number); #endif } void gd_end_page(void) { #ifdef MYTRACE fprintf(stderr,"gd_end_page\n"); #endif } static void gd_begin_cluster(graph_t* g) { } static void gd_end_cluster(void) { } static void gd_begin_nodes(void) { } static void gd_end_nodes(void) { } static void gd_begin_edges(void) { } static void gd_end_edges(void) { } static void gd_begin_node(node_t* n) { Curnode = n; } static void gd_end_node(void) { } static void gd_begin_edge(edge_t* e) { } static void gd_end_edge(void) { } static void gd_begin_context(void) { assert(SP + 1 < MAXNEST); cstk[SP + 1] = cstk[SP]; SP++; } static void gd_end_context(void) { int psp = SP - 1; assert(SP > 0); if (cstk[SP].font_was_set) gd_font(&(cstk[psp])); SP = psp; } static void gd_set_font(char* name, double size) { char *p, *q; context_t *cp; cp = &(cstk[SP]); cp->font_was_set = TRUE; cp->fontsz = ArgScale * size; p = strdup(name); if ((q = strchr(p, '-'))) { *q++ = 0; if (strcasecmp(q, "italic") == 0) cp->fontopt = ITALIC; else if (strcasecmp(q, "bold") == 0) cp->fontopt = BOLD; } cp->fontfam = p; gd_font(&cstk[SP]); } static int gd_resolve_color(char* name) { color_t color; if (!(strcmp(name,"transparent"))) { /* special case for "transparent" color */ return gdImageGetTransparent(im); } else { colorxlate(name,&color,RGBA_BYTE); return gdImageColorResolve(im,color.u.rgba[0],color.u.rgba[1],color.u.rgba[2]); } } static void gd_set_pencolor(char* name) { cstk[SP].pencolor = gd_resolve_color(name); } static void gd_set_fillcolor(char* name) { cstk[SP].fillcolor = gd_resolve_color(name); } static void gd_set_style(char** s) { char *line,*p; context_t *cp; cp = &(cstk[SP]); while ((p = line = *s++)) { if (streq(line, "solid")) cp->pen = P_SOLID; else if (streq(line, "dashed")) cp->pen = P_DASHED; else if (streq(line, "dotted")) cp->pen = P_DOTTED; else if (streq(line, "invis")) cp->pen = P_NONE; else if (streq(line, "bold")) cp->penwidth = WIDTH_BOLD; else if (streq(line, "setlinewidth")) { while (*p) p++; p++; cp->penwidth = atol(p); } else if (streq(line, "filled")) cp->fill = P_SOLID; else if (streq(line, "unfilled")) cp->fill = P_NONE; else agerr (AGWARN, "gd_set_style: unsupported style %s - ignoring\n", line); } } /* sometimes fonts are stored under a different name */ char * gd_alternate_fontlist(char *font) { char *fontlist; fontlist = font; if (strcasecmp(font,"Times-Roman")==0) fontlist = "Times-Roman Times_New_Roman Times-New-Roman TimesNewRoman Times times"; else if (strcasecmp(font,"Times-New-Roman")==0) fontlist = "Times-New-Roman Times_New_Roman TimesNewRoman Times-Roman Times times"; else if (strcasecmp(font,"Times_New_Roman")==0) fontlist = "Times_New_Roman Times-New-Roman TimesNewRoman Times-Roman Times times"; else if (strcasecmp(font,"TimesNewRoman")==0) fontlist = "TimesNewRoman Times_New_Roman Times-New-Roman Times-Roman Times times"; else if (strcasecmp(font,"Times")==0) fontlist = "Times times Times-Roman Times_New_Roman Times-New-Roman TimesNewRoman"; else if (strcasecmp(font,"Helvetica")==0) fontlist = "Helvetica arial"; else if (strcasecmp(font,"Arial")==0) fontlist = "Arial arial"; else if (strcasecmp(font,"arialb")==0) fontlist = "arialb Arial-Bold"; else if (strcasecmp(font, "ariali")==0) fontlist = "ariali Arial-Italic"; else if (strcasecmp(font,"Courier")==0) fontlist = "Courier cour Courier-New Courier_New"; else if (strcasecmp(font,"Courier-New")==0) fontlist = "Courier-New Courier_New Courier cour"; else if (strcasecmp(font,"Courier_New")==0) fontlist = "Courier_New Courier-New Courier cour"; return fontlist; } void gd_missingfont(char *err, char *fontreq) { static char *lastmissing = 0; static int n_errors = 0; char *p; if (n_errors >= 20) return; if ((lastmissing == 0) || (strcmp(lastmissing,fontreq))) { if (!(p=getenv("GDFONTPATH"))) p = DEFAULT_FONTPATH; agerr(AGERR, "%s : %s in %s\n",err,fontreq,p); if (lastmissing) free(lastmissing); lastmissing = strdup(fontreq); n_errors++; if (n_errors >= 20) agerr(AGWARN, "(font errors suppressed)\n"); } } extern gdFontPtr gdFontTiny, gdFontSmall, gdFontMediumBold, gdFontLarge, gdFontGiant; static void gd_textline(point p, textline_t *line) { char *fontlist, *err; pointf mp,ep; int brect[8]; char *str = line->str; double fontsz = cstk[SP].fontsz; if (cstk[SP].pen == P_NONE) return; fontlist = gd_alternate_fontlist(cstk[SP].fontfam); switch(line->just) { case 'l': mp.x = p.x; break; case 'r': mp.x = p.x - line->width; break; default: case 'n': mp.x = p.x - line->width / 2; break; } ep.y = mp.y = p.y; ep.x = mp.x + line->width; mp = gdpt(mp); if (fontsz <= FONTSIZE_MUCH_TOO_SMALL) { /* ignore entirely */ } else if (fontsz <= FONTSIZE_TOO_SMALL) { /* draw line in place of text */ ep = gdpt(ep); gdImageLine(im, ROUND(mp.x), ROUND(mp.y), ROUND(ep.x), ROUND(ep.y), cstk[SP].pencolor); } else { err = gdImageStringFT(im, brect, cstk[SP].pencolor, fontlist, fontsz, (Rot? 90.0 : 0.0) * PI / 180.0, ROUND(mp.x), ROUND(mp.y), str); if (err) { /* revert to builtin fonts */ gd_missingfont (err, cstk[SP].fontfam); if (fontsz <= 8.5) { gdImageString(im, gdFontTiny, ROUND(mp.x), ROUND(mp.y-9.), (unsigned char *)str, cstk[SP].pencolor); } else if (fontsz <= 9.5) { gdImageString(im, gdFontSmall, ROUND(mp.x), ROUND(mp.y-12.), (unsigned char *)str, cstk[SP].pencolor); } else if (fontsz <= 10.5) { gdImageString(im, gdFontMediumBold, ROUND(mp.x), ROUND(mp.y-13.), (unsigned char *)str, cstk[SP].pencolor); } else if (fontsz <= 11.5) { gdImageString(im, gdFontLarge, ROUND(mp.x), ROUND(mp.y-14.), (unsigned char *)str, cstk[SP].pencolor); } else { gdImageString(im, gdFontGiant, ROUND(mp.x), ROUND(mp.y-15.), (unsigned char *)str, cstk[SP].pencolor); } } } } point gd_textsize(char *str, char *fontname, double fontsz) { char *fontlist,*err; point rv; int brect[8]; fontlist = gd_alternate_fontlist(fontname); rv.x = rv.y = 0.0; if (fontlist && *str) { if (fontsz <= FONTSIZE_MUCH_TOO_SMALL) { /* ignore entirely */ rv.x = rv.y = 0; return rv; } else if (fontsz <= FONTSIZE_TOO_SMALL) { /* draw line in place of text */ /* fake a finite fontsize so that line length is calculated */ fontsz = FONTSIZE_TOO_SMALL; } /* call gdImageStringFT with null *im to get brect */ err = gdImageStringFT(NULL, brect, -1, fontlist, fontsz, 0, 0, 0, str); if (!err) { rv.x = (brect[4] - brect[0]); /* rv.y = (brect[5] - brect[1]); */ rv.y = (brect[5] - 0 ); /* ignore descenders */ rv.x /= SCALE; rv.y /= SCALE; } } return rv; } static void gd_bezier(point* A, int n, int arrow_at_start, int arrow_at_end) { pointf p0, p1, V[4]; int i, j, step; int style[20]; int pen, width; gdImagePtr brush = NULL; if (cstk[SP].pen != P_NONE) { if (cstk[SP].pen == P_DASHED) { for (i = 0; i < 10; i++) style[i] = cstk[SP].pencolor; for (; i < 20; i++) style[i] = gdTransparent; gdImageSetStyle(im, style, 20); pen = gdStyled; } else if (cstk[SP].pen == P_DOTTED) { for (i = 0; i < 2; i++) style[i] = cstk[SP].pencolor; for (; i < 12; i++) style[i] = gdTransparent; gdImageSetStyle(im, style, 12); pen = gdStyled; } else { pen = cstk[SP].pencolor; } #if 0 if (cstk[SP].penwidth != WIDTH_NORMAL) { width=cstk[SP].penwidth; brush = gdImageCreate(width,width); gdImagePaletteCopy(brush, im); gdImageFilledRectangle(brush, 0,0,width-1, width-1, cstk[SP].pencolor); gdImageSetBrush(im, brush); if (pen == gdStyled) pen = gdStyledBrushed; else pen = gdBrushed; } #else width = cstk[SP].penwidth; gdImageSetThickness(im, width); #endif V[3].x = A[0].x; V[3].y = A[0].y; for (i = 0; i+3 < n; i += 3) { V[0] = V[3]; for (j = 1; j <= 3; j++) { V[j].x = A[i+j].x; V[j].y = A[i+j].y; } p0 = gdpt(V[0]); for (step = 1; step <= BEZIERSUBDIVISION; step++) { p1 = gdpt(Bezier(V, 3, (double)step/BEZIERSUBDIVISION, NULL, NULL)); gdImageLine(im, ROUND(p0.x), ROUND(p0.y), ROUND(p1.x), ROUND(p1.y), pen); p0 = p1; } } if (brush) gdImageDestroy(brush); } } static void gd_polygon(point *A, int n, int filled) { pointf p; int i; gdPoint *points; int style[20]; int pen, width; gdImagePtr brush = NULL; if (cstk[SP].pen != P_NONE) { if (cstk[SP].pen == P_DASHED) { for (i = 0; i < 10; i++) style[i] = cstk[SP].pencolor; for (; i < 20; i++) style[i] = gdTransparent; gdImageSetStyle(im, style, 20); pen = gdStyled; } else if (cstk[SP].pen == P_DOTTED) { for (i = 0; i < 2; i++) style[i] = cstk[SP].pencolor; for (; i < 12; i++) style[i] = gdTransparent; gdImageSetStyle(im, style, 12); pen = gdStyled; } else { pen = cstk[SP].pencolor; } #if 1 /* use brush instead of Thickness to improve end butts */ gdImageSetThickness(im, WIDTH_NORMAL); if (cstk[SP].penwidth != WIDTH_NORMAL) { width=cstk[SP].penwidth * Scale; brush = gdImageCreate(width,width); gdImagePaletteCopy(brush, im); gdImageFilledRectangle(brush, 0,0,width-1, width-1, cstk[SP].pencolor); gdImageSetBrush(im, brush); if (pen == gdStyled) pen = gdStyledBrushed; else pen = gdBrushed; } #else width = cstk[SP].penwidth; gdImageSetThickness(im, width); #endif points = N_GNEW(n,gdPoint); for (i = 0; i < n; i++) { p.x = A[i].x; p.y = A[i].y; p = gdpt(p); points[i].x = ROUND(p.x); points[i].y = ROUND(p.y); } if (filled) gdImageFilledPolygon(im, points, n, cstk[SP].fillcolor); gdImagePolygon(im, points, n, pen); free(points); if (brush) gdImageDestroy(brush); } } static void gd_ellipse(point p, int rx, int ry, int filled) { pointf mp; int i; int style[40]; /* need 2* size for arcs, I don't know why */ int pen, width; gdImagePtr brush = NULL; if (cstk[SP].pen != P_NONE) { if (cstk[SP].pen == P_DASHED) { for (i = 0; i < 20; i++) style[i] = cstk[SP].pencolor; for (; i < 40; i++) style[i] = gdTransparent; gdImageSetStyle(im, style, 40); pen = gdStyled; } else if (cstk[SP].pen == P_DOTTED) { for (i = 0; i < 2; i++) style[i] = cstk[SP].pencolor; for (; i < 24; i++) style[i] = gdTransparent; gdImageSetStyle(im, style, 24); pen = gdStyled; } else { pen = cstk[SP].pencolor; } #if 1 /* use brush instead of Thickness to improve outline appearance */ gdImageSetThickness(im, WIDTH_NORMAL); if (cstk[SP].penwidth != WIDTH_NORMAL) { width = cstk[SP].penwidth; brush = gdImageCreate(width,width); gdImagePaletteCopy(brush, im); gdImageFilledRectangle(brush, 0,0,width-1, width-1, cstk[SP].pencolor); gdImageSetBrush(im, brush); if (pen == gdStyled) pen = gdStyledBrushed; else pen = gdBrushed; } #else width = cstk[SP].penwidth; gdImageSetThickness(im, width); #endif if (Rot) {int t; t = rx; rx = ry; ry = t;} mp.x = p.x; mp.y = p.y; mp = gdpt(mp); if (filled) { gdImageFilledEllipse(im, ROUND(mp.x), ROUND(mp.y), ROUND(Scale*(rx + rx)), ROUND(Scale*(ry + ry)), cstk[SP].fillcolor); } gdImageArc(im, ROUND(mp.x), ROUND(mp.y), ROUND(Scale*(rx + rx)), ROUND(Scale*(ry + ry)), 0, 360, pen); if (brush) gdImageDestroy(brush); } } static void gd_polyline(point* A, int n) { pointf p, p1; int i; int style[20]; int pen, width; gdImagePtr brush = NULL; if (cstk[SP].pen != P_NONE) { if (cstk[SP].pen == P_DASHED) { for (i = 0; i < 10; i++) style[i] = cstk[SP].pencolor; for (; i < 20; i++) style[i] = gdTransparent; gdImageSetStyle(im, style, 20); pen = gdStyled; } else if (cstk[SP].pen == P_DOTTED) { for (i = 0; i < 2; i++) style[i] = cstk[SP].pencolor; for (; i < 12; i++) style[i] = gdTransparent; gdImageSetStyle(im, style, 12); pen = gdStyled; } else { pen = cstk[SP].pencolor; } #if 0 if (cstk[SP].penwidth != WIDTH_NORMAL) { width = cstk[SP].penwidth; brush = gdImageCreate(width,width); gdImagePaletteCopy(brush, im); gdImageFilledRectangle(brush, 0,0,width-1,width-1,cstk[SP].pencolor); gdImageSetBrush(im, brush); if (pen == gdStyled) pen = gdStyledBrushed; else pen = gdBrushed; } #else width = cstk[SP].penwidth; gdImageSetThickness(im, width); #endif p.x = A[0].x; p.y = A[0].y; p = gdpt(p); for (i = 1; i < n; i++) { p1.x = A[i].x; p1.y = A[i].y; p1 = gdpt(p1); gdImageLine(im, ROUND(p.x), ROUND(p.y), ROUND(p1.x), ROUND(p1.y), pen); p.x = p1.x; p.y = p1.y; } if (brush) gdImageDestroy(brush); } } static gdImagePtr loadimage(char *name) { gdImagePtr rv = 0; char *imagefile,*suffix; FILE *in = NULL; char *cmd = 0; #ifdef HAVE_CURL char *curlopts; #endif imagefile = name; if (!imagefile) return 0; if (!strncmp(imagefile,"file:",5) && !httpcheck(imagefile)) { imagefile = &imagefile[5]; if (!strncmp(imagefile,"//",2)) imagefile = &imagefile[2]; #ifndef MSWIN32 in = fopen (imagefile, "r"); #else in = fopen (imagefile, "rb"); #endif cmd = N_GNEW(16,char); strcpy(cmd,"fopen()"); } #ifdef HAVE_CURL else { cmd = N_GNEW(strlen(imagefile) + strlen(CURLCMD) + 16,char); if (httpcheck || !(curlopts = agget(Curnode,"curlopts"))) curlopts = CURLOPTS; sprintf(cmd,"%s %s \"%s\"",CURLCMD,curlopts,imagefile); in = popen(cmd,"r"); } #endif if (!in) agerr(AGERR, "couldn't open image file %s via %s\n",name,cmd); else { suffix = strrchr(imagefile,'.'); if (!suffix) suffix = imagefile; else suffix++; if (!strcasecmp(suffix,"wbmp")) rv = gdImageCreateFromWBMP(in); #ifdef WITH_GIF else if (!strcasecmp(suffix,"gif")) rv = gdImageCreateFromGif(in); #endif #ifdef HAVE_LIBPNG else if (!strcasecmp(suffix,"png")) rv = gdImageCreateFromPng(in); #endif #ifdef HAVE_LIBJPEG else if (!strcasecmp(suffix,"jpeg")||!strcasecmp(suffix,"jpg")) rv = gdImageCreateFromJpeg(in); #endif else if (!strcasecmp(suffix,"xbm")) rv = gdImageCreateFromXbm(in); else agerr(AGERR, "image file %s suffix not recognized\n",name); fclose(in); if (!rv) agerr(AGERR, "image file %s contents were not recognized\n",name); } if (cmd) free(cmd); return rv; } typedef struct imagerec_s { Dtlink_t link; char *name; gdImagePtr im; } imagerec_t; static void imagerec_free(Dict_t *dict, Void_t *p, Dtdisc_t *disc) { gdImagePtr im = ((imagerec_t*)p)->im; if (im) gdImageDestroy(im); } static Dtdisc_t ImageDictDisc = { offsetof(imagerec_t,name), /* key */ -1, /* size */ 0, /* link offset */ NIL(Dtmake_f), imagerec_free, NIL(Dtcompar_f), NIL(Dthash_f), NIL(Dtmemory_f), NIL(Dtevent_f) }; static gdImagePtr getimage(char *name) { imagerec_t probe, *val; if (!name) return 0; /* cdt does not like NULL keys */ if (!ImageDict) ImageDict = dtopen(&ImageDictDisc,Dttree); probe.name = name; val = dtsearch(ImageDict,&probe); if (!val) { val = GNEW(imagerec_t); val->name = name; val->im = loadimage(name); dtinsert(ImageDict,val); } return val->im; } static void gd_user_shape(char *name, point *A, int n, int filled) { gdImagePtr im2 = 0; pointf destul, destlr; pointf ul, lr; /* upper left, lower right */ int i; im2 = getimage(agget(Curnode,"shapefile")); if (im2) { /* compute dest origin and size */ ul.x = lr.x = A[0].x; ul.y = lr.y = A[0].y; for (i = 1; i < n; i++) { if (ul.x > A[i].x) ul.x = A[i].x; if (ul.y < A[i].y) ul.y = A[i].y; if (lr.y > A[i].y) lr.y = A[i].y; if (lr.x < A[i].x) lr.x = A[i].x; } destul = gdpt(ul); destlr = gdpt(lr); gdImageCopyResized(im,im2,ROUND(destul.x),ROUND(destul.y),0,0,ROUND(destlr.x - destul.x),ROUND(destlr.y - destul.y),im2->sx,im2->sy); } } point gd_user_shape_size(node_t *n, char *imagefile) { point rv; gdImagePtr im; Curnode = n; im = getimage(imagefile); if (im) {rv.x = im->sx / SCALE; rv.y = im->sy / SCALE; } else rv.x = rv.y = 0; return rv; } codegen_t GD_CodeGen = { gd_reset, gd_begin_job, gd_end_job, gd_begin_graph_to_file, gd_end_graph_to_file, gd_begin_page, gd_end_page, gd_begin_cluster, gd_end_cluster, gd_begin_nodes, gd_end_nodes, gd_begin_edges, gd_end_edges, gd_begin_node, gd_end_node, gd_begin_edge, gd_end_edge, gd_begin_context, gd_end_context, gd_set_font, gd_textline, gd_set_pencolor, gd_set_fillcolor, gd_set_style, gd_ellipse, gd_polygon, gd_bezier, gd_polyline, 0 /* gd_arrowhead */, gd_user_shape, 0 /* gd_comment */, gd_textsize, gd_user_shape_size }; codegen_t memGD_CodeGen = { /* see tcldot */ gd_reset, gd_begin_job, gd_end_job, gd_begin_graph_to_memory, gd_end_graph_to_memory, gd_begin_page, gd_end_page, gd_begin_cluster, gd_end_cluster, gd_begin_nodes, gd_end_nodes, gd_begin_edges, gd_end_edges, gd_begin_node, gd_end_node, gd_begin_edge, gd_end_edge, gd_begin_context, gd_end_context, gd_set_font, gd_textline, gd_set_pencolor, gd_set_fillcolor, gd_set_style, gd_ellipse, gd_polygon, gd_bezier, gd_polyline, 0 /* gd_arrowhead */, gd_user_shape, 0 /* gd_comment */, gd_textsize, gd_user_shape_size };