At layout time, the file type and image size are determined.
At render time, renderer-specific code is used to load, cache, and apply
the images to the output.
Usershapes in Cairo renderers are now supported.
libcommon_la_SOURCES = arrows.c colxlate.c fontmetrics.c \
- args.c gdusershape.c memory.c \
- globals.c htmllex.c htmlparse.y htmltable.c input.c \
- pointset.c postproc.c psusershape.c routespl.c splines.c \
- svgusershape.c timing.c labels.c ns.c shapes.c utils.c geom.c \
+ args.c memory.c globals.c htmllex.c htmlparse.y htmltable.c input.c \
+ pointset.c postproc.c routespl.c splines.c psusershape.c \
+ timing.c labels.c ns.c shapes.c utils.c geom.c \
output.c emit.c $(CODEGENS)
psgen.o psgen.lo : ps.h
dia_fputs(" </dia:object>\n");
}
-static void dia_user_shape(char *name, point * A, int n, int filled)
+static void dia_usershape(usershape_t *us, boxf b, point *A, int n, bool filled)
{
char *imagefile;
/* its invisible, don't draw */
return;
}
+
+#if 0
+/* FIXME */
imagefile = agget(Curnode, "shapefile");
+#else
+ imagefile = NULL;
+#endif
- if (imagefile == 0) {
+ if (! imagefile) {
dia_polygon(A, n, filled);
return;
}
dia_bezier, dia_polyline,
1, /* bezier_has_arrows */
dia_comment,
- 0, /* dia_textsize */
- dia_user_shape,
- 0 /* dia_usershapesize */
+ dia_usershape
};
case SVG:
job->flags = chkOrder(g) | GVRENDER_Y_GOES_DOWN;
break;
+ case GD: /* bitmap formats supported by gdgen.c */
+ case GD2:
+ case GIF:
+ case JPEG:
+ case PNG:
+ case WBMP:
+ case XBM:
+ job->flags = chkOrder(g) | GVRENDER_Y_GOES_DOWN;
+ break;
case ISMAP: case IMAP: case CMAP: case CMAPX:
/* output in breadth first graph walk order, but
* with nodes edges and nested clusters before
figptarray(A, n, 0); /* open shape */
}
-static void fig_user_shape(char *name, point * A, int n, int filled)
+static void fig_usershape(usershape_t *us, boxf b, point *A, int n, bool filled)
{
static bool onetime = TRUE;
if (onetime) {
fig_bezier, fig_polyline,
0, /* bezier_has_arrows */
fig_comment,
- 0, /* fig_textsize */
- fig_user_shape,
- 0 /* fig_usershapesize */
+ fig_usershape
};
#include <io.h>
#endif
-extern gdImagePtr gd_getshapeimage(char *name);
-extern void gd_freeusershapes(void);
-
static gdImagePtr im;
static int external_surface;
gdImageXbm(im, Output_file);
#endif
}
- gd_freeusershapes();
gdImageDestroy(im);
#ifdef MYTRACE
fprintf(stderr, "gd_end_graph_to_file\n");
}
}
-static void gd_user_shape(char *name, point * A, int n, int filled)
+static void gd_freeimage(void *data)
{
- gdImagePtr im2 = 0;
- pointf destul, destlr;
- pointf ul, lr; /* upper left, lower right */
- double sx, sy; /* target size */
- double scalex, scaley; /* scale factors */
- int i;
- char *shapeimagefile;
+ gdImageDestroy((gdImagePtr)data);
+}
- if (streq(name, "custom"))
- shapeimagefile = agget(Curnode, "shapefile");
- else
- shapeimagefile = name;
- im2 = gd_getshapeimage(shapeimagefile);
- if (im2) {
- pointf delta;
- /* 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);
- delta.x = destlr.x - destul.x;
- delta.y = destlr.y - destul.y;
- scalex = delta.x / (double) (im2->sx);
- scaley = delta.y / (double) (im2->sy);
- /* keep aspect ratio fixed by just using the smaller scale */
- if (scalex < scaley) {
- sx = im2->sx * scalex;
- sy = im2->sy * scalex;
- } else {
- sx = im2->sx * scaley;
- sy = im2->sy * scaley;
- }
- if (sx < delta.x)
- destul.x += (delta.x - sx) / 2.0;
- if (sy < delta.y)
- destul.y += (delta.y - sy) / 2.0;
- sx = ROUND(sx);
- sy = ROUND(sy);
- gdImageCopyResized(im, im2, ROUND(destul.x), ROUND(destul.y), 0, 0,
- sx, sy, im2->sx, im2->sy);
+static void gd_usershape(usershape_t *us, boxf b, point *A, int n, bool filled)
+{
+ gdImagePtr im2 = NULL;
+
+ if (us->data) {
+ if (us->datafree == gd_freeimage)
+ im2 = (gdImagePtr)(us->data); /* use cached data */
+ else {
+ us->datafree(us->data); /* free incompatible cache data */
+ us->data = NULL;
+ }
+ }
+ if (!im2) { /* read file into cache */
+ fseek(us->f, 0, SEEK_SET);
+ switch (us->type) {
+#ifdef HAVE_GD_PNG
+ case FT_PNG:
+ im2 = gdImageCreateFromPng(us->f);
+ break;
+#endif
+#ifdef HAVE_GD_GIF
+ case FT_GIF:
+ im2 = gdImageCreateFromGif(us->f);
+ break;
+#endif
+#ifdef HAVE_GD_JPEG
+ case FT_JPEG:
+ im2 = gdImageCreateFromJpeg(us->f);
+ break;
+#endif
+ default:
+ im2 = NULL;
+ }
+ if (im2) {
+ us->data = (void*)im2;
+ us->datafree = gd_freeimage;
+ }
}
+ if (im2)
+ gdImageCopyResized(im, im2, ROUND(b.LL.x), ROUND(b.LL.y), 0, 0,
+ ROUND(b.UR.x - b.LL.x), ROUND(b.UR.y - b.LL.y), us->w, us->h);
}
codegen_t GD_CodeGen = {
gd_bezier, gd_polyline,
0, /* bezier_has_arrows */
0, /* gd_comment */
- 0, /* gd_textsize */
- gd_user_shape,
- 0 /* gd_user_shape_size */
+ gd_usershape
};
+++ /dev/null
-/* $Id$ $Revision$ */
-/* vim:set shiftwidth=4 ts=8: */
-
-/**********************************************************
-* This software is part of the graphviz package *
-* http://www.graphviz.org/ *
-* *
-* Copyright (c) 1994-2004 AT&T Corp. *
-* and is licensed under the *
-* Common Public License, Version 1.0 *
-* by AT&T Corp. *
-* *
-* Information and Software Systems Research *
-* AT&T Research, Florham Park NJ *
-**********************************************************/
-
-#include "render.h"
-#include "gd.h"
-
-static Dict_t *ImageDict;
-
-static gdImagePtr loadshapeimage(char *name)
-{
- gdImagePtr rv = 0;
- char *shapeimagefile, *suffix;
- FILE *in = NULL;
-
- if ((shapeimagefile = safefile(name))) {
-#ifndef MSWIN32
- in = fopen(shapeimagefile, "r");
-#else
- in = fopen(shapeimagefile, "rb");
-#endif
- }
- if (!in)
- agerr(AGERR, "couldn't open image file %s\n", shapeimagefile);
- else {
- suffix = strrchr(shapeimagefile, '.');
- if (!suffix)
- suffix = shapeimagefile;
- else
- suffix++;
- if (!strcasecmp(suffix, "wbmp"))
- rv = gdImageCreateFromWBMP(in);
-#ifdef HAVE_GD_GIF
- else if (!strcasecmp(suffix, "gif"))
- rv = gdImageCreateFromGif(in);
-#endif
-#ifdef HAVE_GD_JPEG
- else if (!strcasecmp(suffix, "jpeg") || !strcasecmp(suffix, "jpg"))
- rv = gdImageCreateFromJpeg(in);
-#endif
-#ifdef HAVE_GD_PNG
- else if (!strcasecmp(suffix, "png"))
- rv = gdImageCreateFromPng(in);
-#endif
-#ifdef HAVE_GD_XPM
- else if (!strcasecmp(suffix, "xbm"))
- rv = gdImageCreateFromXbm(in);
-#endif
- 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);
- }
- 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)
-};
-
-gdImagePtr gd_getshapeimage(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 = loadshapeimage(name);
- dtinsert(ImageDict, val);
- }
- return val->im;
-}
-
-void gd_freeusershapes(void)
-{
- if (ImageDict) {
- dtclose(ImageDict);
- ImageDict = 0;
- }
-}
-
-point gd_image_size(graph_t * g, char *shapeimagefile)
-{
- point rv;
- gdImagePtr im;
- double dpi;
-
- dpi = GD_drawing(g)->dpi;
- if (dpi < 1.0)
- dpi = DEFAULT_DPI;
- im = gd_getshapeimage(shapeimagefile);
- if (im) {
- rv.x = im->sx * POINTS_PER_INCH / dpi;
- rv.y = im->sy * POINTS_PER_INCH / dpi;
- } else
- rv.x = rv.y = -1;
- return rv;
-}
output(buffer);
}
-static void hpgl_user_shape(char *name, point * A, int n, int filled)
+static void hpgl_usershape(usershape_t *us, boxf p, point *A, int n, bool filled)
{
static bool onetime = TRUE;
if (onetime) {
hpgl_bezier, hpgl_polyline,
0, /* bezier_has_arrows */
0, /* hpgl_comment */
- 0, /* hpgl_textsize */
- hpgl_user_shape,
- 0 /* hpgl_usershapesize */
+ hpgl_usershape
};
A[3].x = bb.UR.x;
A[3].y = bb.LL.y;
- gvrender_user_shape(job, cp->src, A, 4, 1);
+ gvrender_usershape(job, cp->src, A, 4, true);
}
static void
int rv;
b.LL.x = b.LL.y = 0;
- b.UR = image_size(env->g, img->src);
+ b.UR = gvusershape_size(env->g, img->src);
if ((b.UR.x == -1) && (b.UR.y == -1)) {
rv = 1;
b.UR.x = b.UR.y = 0;
#define YDIR(y) (Y_invert ? (Y_off - (y)) : (y))
#define YFDIR(y) (Y_invert ? (YF_off - (y)) : (y))
+
#endif
0, /* map_bezier */ 0, /* map_polyline */
0, /* bezier_has_arrows */
0, /* map_comment */
- 0, /* map_textsize */
- 0, /* map_user_shape */
- 0 /* map_usershapesize */
+ 0 /* map_usershape */
};
fprintf(Output_file, ">\n");
}
-static void mif_user_shape(char *name, point * A, int n, int filled)
+static void mif_usershape(usershape_t *us, boxf b, point *A, int n, bool filled)
{
static bool onetime = TRUE;
if (onetime) {
mif_bezier, mif_polyline,
0, /* bezier_has_arrows */
mif_comment,
- 0, /* mif_textsize */
- mif_user_shape,
- 0 /* mif_usershapesize */
+ mif_usershape
};
fprintf(Output_file, " withcolor %s;\n", S[SP].color);
}
-static void mp_user_shape(char *name, point * A, int sides, int filled)
+static void mp_usershape(usershape_t *us, boxf b, point *A, int n, bool filled)
{
int j;
fprintf(Output_file, "%%GV USER SHAPE [ ");
- for (j = 0; j < sides; j++)
+ for (j = 0; j < n; j++)
fprintf(Output_file, "%d %d ", A[j].x, A[j].y);
fprintf(Output_file, "%d %d ", A[0].x, A[0].y);
- fprintf(Output_file, "] %d %s %s ignored\n", sides,
- (filled ? "true" : "false"), name);
+ fprintf(Output_file, "] %d %s %s ignored\n", n,
+ (filled ? "true" : "false"), us->name);
}
codegen_t MP_CodeGen = {
mp_bezier, mp_polyline,
0, /* bezier_has_arrows */
mp_comment,
- 0, /* mp_textsize */
- mp_user_shape,
- 0 /* mp_usershapesize */
+ mp_usershape
};
point_list_out(A, n, FALSE);
}
-static void pic_user_shape(char *name, point * A, int sides, int filled)
+static void pic_usershape(usershape_t *us, boxf b, point *A, int n, bool filled)
{
+/* FIXME */
/* it's not at all clear what xxx_user_shape is supposed to do; in most xxxgen.c files it emits a message */
/* this defines the shape as a macro and then invokes the macro */
- fprintf(Output_file, "define %s {\n", name);
- fprintf(Output_file, "}\n%s\n", name);
+ fprintf(Output_file, "define %s {\n", us->name);
+ fprintf(Output_file, "}\n%s\n", us->name);
}
static void pic_bezier(point * A, int n, int arrow_at_start,
pic_bezier, pic_polyline,
0, /* bezier_has_arrows */
pic_comment,
- 0, /* pic_textsize */
- pic_user_shape,
- 0 /* pic_usershapesize */
+ pic_usershape
};
extern void epsf_define(FILE * of);
void epsf_emit_body(ps_image_t *img, FILE *of);
extern void ps_freeusershapes(void);
-extern ps_image_t *ps_usershape(char *shapeimagefile);
+extern ps_image_t *ps_usershape_to_image(char *shapeimagefile);
extern gdImagePtr gd_getshapeimage(char *name);
}
-/* ps_user_shape:
+static void ps_freeimage_gd (void *data)
+{
+ gdImageDestroy((gdImagePtr)data);
+}
+
+/* ps_usershape:
* Images for postscript are complicated by the old epsf shape, as
* well as user-defined shapes using postscript code.
* If the name is custom, we look for the image stored in the
* Else we assume name is the name of the image. This occurs when
* the image is part of an html label.
*/
-static void ps_user_shape(char *name, point * A, int sides, int filled)
+static void ps_usershape(usershape_t *us, boxf b, point *A, int n, bool filled)
{
int j;
- gdImagePtr bmimg;
+ gdImagePtr gd_img = NULL;
+ ps_image_t *ps_img = NULL;
point offset;
- char *shapeimagefile = NULL;
- char *suffix;
if (S[SP].invis)
return;
- if (streq(name, "custom")) {
- shapeimagefile = agget(Curnode, "shapefile");
- }
- else if (find_user_shape(name)) {
- if (filled) {
- ps_begin_context();
- ps_set_color(S[SP].fillcolor);
+
+ if (!us->f) {
+ if (find_user_shape(us->name)) {
+ if (filled) {
+ ps_begin_context();
+ ps_set_color(S[SP].fillcolor);
+ fprintf(Output_file, "[ ");
+ for (j = 0; j < n; j++)
+ fprintf(Output_file, "%d %d ", A[j].x, A[j].y);
+ fprintf(Output_file, "%d %d ", A[0].x, A[0].y);
+ fprintf(Output_file, "] %d true %s\n", n, us->name);
+ ps_end_context();
+ }
fprintf(Output_file, "[ ");
- for (j = 0; j < sides; j++)
- fprintf(Output_file, "%d %d ", A[j].x, A[j].y);
+ for (j = 0; j < n; j++)
+ fprintf(Output_file, "%d %d ", A[j].x, A[j].y);
fprintf(Output_file, "%d %d ", A[0].x, A[0].y);
- fprintf(Output_file, "] %d true %s\n", sides, name);
- ps_end_context();
+ fprintf(Output_file, "] %d false %s\n", n, us->name);
+ }
+ else { /* name not find by find_ser_shape */ }
+ return;
+ }
+
+ if (us->data) {
+ if (us->datafree == ps_freeimage_gd)
+ gd_img = (gdImagePtr)(us->data); /* use cached data */
+#if 0
+ else if (us->datafree == ps_freeimage_ps)
+ ps_img = (ps_image_t *)(us->data); /* use cached data */
+#endif
+ else {
+ us->datafree(us->data); /* free incompatible cache data */
+ us->data = NULL;
}
- fprintf(Output_file, "[ ");
- for (j = 0; j < sides; j++)
- fprintf(Output_file, "%d %d ", A[j].x, A[j].y);
- fprintf(Output_file, "%d %d ", A[0].x, A[0].y);
- fprintf(Output_file, "] %d false %s\n", sides, name);
+ }
+
+ if (!gd_img && !ps_img) { /* read file into cache */
+ fseek(us->f, 0, SEEK_SET);
+ switch (us->type) {
+#ifdef HAVE_GD_PNG
+ case FT_PNG:
+ gd_img = gdImageCreateFromPng(us->f);
+ break;
+#endif
+#ifdef HAVE_GD_GIF
+ case FT_GIF:
+ gd_img = gdImageCreateFromGif(us->f);
+ break;
+#endif
+#ifdef HAVE_GD_JPEG
+ case FT_JPEG:
+ gd_img = gdImageCreateFromJpeg(us->f);
+ break;
+#endif
+ case FT_PS:
+ case FT_EPS:
+ ps_img = ps_usershape_to_image(us->name);
+ break;
+ default:
+ break;
+ }
+ if (gd_img) {
+ us->data = (void*)gd_img;
+ us->datafree = ps_freeimage_gd;
+ }
+#if 0
+ if (ps_img) {
+ us->data = (void*)ps_img;
+ us->datafree = ps_freeimage_ps;
+ }
+#endif
+ }
+
+ if (ps_img) {
+ ps_begin_context();
+ offset.x = -ps_img->origin.x - (ps_img->size.x) / 2;
+ offset.y = -ps_img->origin.y - (ps_img->size.y) / 2;
+ fprintf(Output_file, "%d %d translate newpath\n",
+ ND_coord_i(Curnode).x + offset.x,
+ ND_coord_i(Curnode).y + offset.y);
+ if (ps_img->must_inline)
+ epsf_emit_body(ps_img, Output_file);
+ else
+ fprintf(Output_file, "user_shape_%d\n", ps_img->macro_id);
+ ps_end_context();
return;
}
- else
- shapeimagefile = name;
-
- assert (shapeimagefile);
- suffix = strrchr(shapeimagefile, '.');
- if (suffix) {
- suffix++;
- if (streq(suffix, "ps")) {
- ps_image_t *img = 0;
- img = ps_usershape(shapeimagefile);
- if (!img) /* problems would have been reported by image_size */
- return;
- ps_begin_context();
- offset.x = -img->origin.x - (img->size.x) / 2;
- offset.y = -img->origin.y - (img->size.y) / 2;
- fprintf(Output_file, "%d %d translate newpath\n",
- ND_coord_i(Curnode).x + offset.x,
- ND_coord_i(Curnode).y + offset.y);
- if (img->must_inline) epsf_emit_body(img,Output_file);
- else fprintf(Output_file,"user_shape_%d\n",img->macro_id);
- ps_end_context();
- }
- else if ((bmimg = gd_getshapeimage(shapeimagefile))) {
- point sz;
- sz.x = A[0].x - A[2].x;
- sz.y = A[0].y - A[2].y;
- writePSBitmap (bmimg, A[2], sz);
- }
- else { /* some other type of image */
- agerr(AGERR, "image type \"%s\" of file %s unsupported in PostScript output\n",
- suffix, shapeimagefile);
- }
+
+ if (gd_img) {
+ point sz;
+ sz.x = A[0].x - A[2].x;
+ sz.y = A[0].y - A[2].y;
+ writePSBitmap (gd_img, A[2], sz);
+ return;
}
- /* if !suffix, already reported by image_size */
+
+ /* some other type of image */
+ agerr(AGERR, "usershape %s is not supported in PostScript output\n", us->name);
}
codegen_t PS_CodeGen = {
ps_bezier, ps_polyline,
0, /* bezier_has_arrows */
ps_comment,
- 0, /* ps_textsize */
- ps_user_shape,
- 0 /* usershapesize */
+ ps_usershape
};
}
}
-ps_image_t *ps_usershape(char *shapeimagefile)
+ps_image_t *ps_usershape_to_image(char *shapeimagefile)
{
if (EPSF_contents) {
return dtmatch(EPSF_contents, shapeimagefile);
extern void global_def(char *,
Agsym_t * (*fun) (Agraph_t *, char *, char *));
extern int gvRenderJobs (GVC_t * gvc, graph_t * g);
- extern point image_size(graph_t * g, char *shapefile);
extern bool isPolygon(node_t *);
extern char *strdup_and_subst_graph(char *str, Agraph_t * g);
extern char *strdup_and_subst_node(char *str, Agnode_t * n);
}
else {
char *sfile = agget(n, "shapefile");
- imagesize = image_size(n->graph, sfile);
+ imagesize = gvusershape_size(n->graph, sfile);
if ((imagesize.x == -1) && (imagesize.y == -1)) {
agerr(AGERR,
"No or improper shapefile=\"%s\" for node \"%s\"\n",
static pointf *AF;
static int A_size;
bool filled;
- char *color;
+ char *color, *name;
poly = (polygon_t *) ND_shape_info(n);
vertices = poly->vertices;
A[i].y += ND_coord_i(n).y;
}
}
- gvrender_user_shape(job, ND_shape(n)->name, A, sides, filled);
+ name = ND_shape(n)->name;
+ if (streq(name, "custom"))
+ name = agget(n, "shapefile");
+ gvrender_usershape(job, name, A, sides, filled);
filled = FALSE;
}
/* if no boundary but filled, set boundary color to fill color */
svg_fputs("\"/>\n");
}
-static void svg_user_shape(char *name, point * A, int n, int filled)
+static void svg_usershape(usershape_t *us, boxf b, point *A, int n, bool filled)
{
int i;
point p;
/* its invisible, don't draw */
return;
}
- if (streq(name, "custom"))
- imagefile = agget(Curnode, "shapefile");
+ if (us->f)
+ imagefile = us->name;
else
- imagefile = name;
- if (imagefile == 0) {
+ imagefile = NULL;
+ if (! imagefile) {
svg_polygon(A, n, filled);
return;
}
*/
svg_fputs("<clipPath id=\"mypath");
- svg_name_fputs(name);
+ svg_name_fputs(us->name);
svg_name_fputs(Curnode->name);
svg_fputs("\">\n<polygon points=\"");
maxx = minx = svgpt(A[0]).x;
svg_printf
("\" width=\"%dpx\" height=\"%dpx\" preserveAspectRatio=\"xMidYMid meet\" x=\"%d\" y=\"%d\" clip-path=\"url(#mypath",
sz.x, sz.y, minx, miny);
- svg_name_fputs(name);
+ svg_name_fputs(us->name);
svg_name_fputs(Curnode->name);
svg_fputs(")\"/>\n");
}
svg_bezier, svg_polyline,
0, /* bezier_has_arrows */
svg_comment,
- 0, /* svg_textsize */
- svg_user_shape,
- 0 /* svg_usershape_size */
+ svg_usershape
};
+++ /dev/null
-/* $Id$ $Revision$ */
-/* vim:set shiftwidth=4 ts=8: */
-
-/**********************************************************
-* This software is part of the graphviz package *
-* http://www.graphviz.org/ *
-* *
-* Copyright (c) 1994-2004 AT&T Corp. *
-* and is licensed under the *
-* Common Public License, Version 1.0 *
-* by AT&T Corp. *
-* *
-* Information and Software Systems Research *
-* AT&T Research, Florham Park NJ *
-**********************************************************/
-
-#include "render.h"
-
-point svg_image_size(graph_t * g, char *shapeimagefile)
-{
- point rv;
-
- rv.x = rv.y = 0;
- return rv;
-}
int size;
} splinesf;
+ typedef enum { FT_BMP, FT_GIF, FT_PNG, FT_JPEG, FT_PDF, FT_PS, FT_EPS } imagetype_t;
+ typedef struct usershape_s {
+ char *name;
+ FILE *f;
+ imagetype_t type;
+ unsigned int w, h, dpi;
+ void *data; /* data loaded by a renderer */
+ void (*datafree)(void *data); /* renderer's function for freeing data */
+ } usershape_t;
typedef struct textline_t {
char *str; /* stored in utf-8 */
void (*polyline) (point * A, int n);
bool bezier_has_arrows;
void (*comment) (char *str);
- void (*textsize) (void); /* not used */
- void (*user_shape) (char *name, point * A, int sides, int filled);
- void (*usershapesize) (void); /* not used */
+ void (*usershape) (usershape_t *us, boxf b, point * A, int sides, bool filled);
};
struct codegen_info_s {
*/
}
-static void vrml_user_shape(char *name, point * A, int n, int filled)
+static void vrml_usershape(usershape_t *us, boxf b, point *A, int n, bool filled)
{
+/* FIXME */
vrml_polygon(A, n, filled);
}
vrml_bezier, vrml_polyline,
0, /* bezier_has_arrows */
0, /* comment */
- 0, /* vrml_textsize */
- vrml_user_shape,
- 0 /* vrml_usershapesize */
+ vrml_usershape
};
#endif /* HAVE_GD_PNG */
vtx_style();
}
-static void vtx_user_shape(char *name, point * A, int n, int filled)
+static void vtx_usershape(usershape_t *us, boxf b, point *A, int n, bool filled)
{
+/* FIXME */
int i;
pointf mp, max, min;
vtx_bezier, vtx_polyline,
1, /* bezier_has_arrows */
vtx_comment,
- 0, /* vtx_textsize */
- vtx_user_shape,
- 0 /* vtx_usershapesize */
+ vtx_usershape
};
xd_set_pencolor, xd_set_fillcolor, xd_set_style,
xd_ellipse, xd_polygon,
xd_bezier, xd_polyline,
- 0, /* xd_has_arrows */ 0, /* xd_comment */
- 0, /* xd_textsize */ 0, /* xd_user_shape */ 0 /* xd_usershapesize */
+ 0, /* xd_has_arrows */
+ 0, /* xd_comment */
+ 0 /* xd_usershape */
};
for (apis = library->apis; (types = apis->types); apis++) {
for (i = 0; types[i].type; i++) {
+ /* FIXME - should only install if dependencies on other plugins are satisfied */
+ /* e.g. "render" "gtk" depends on "device" "gtk" */
+ /* only need to check during actual loading, so no need to store dependencies in config */
gvplugin_install(gvc, apis->api, types[i].type,
types[i].quality, library->packagename, path, &types[i]);
}
extern bool gvtextlayout(GVC_t *gvc, textline_t *textline,
char *fontname, double fontsize, char **fontpath);
+/* usershapes */
+ extern point gvusershape_size(graph_t *g, char *name);
+ extern usershape_t *gvusershape_find(char *name);
+
/* device */
extern void gvdevice_initialize(GVC_t * gvc);
extern void gvrender_begin_context(GVJ_t * job);
extern void gvrender_end_context(GVJ_t * job);
extern void gvrender_begin_anchor(GVJ_t * job, char *href,
- char *tooltip, char *target);
+ char *tooltip, char *target);
extern void gvrender_end_anchor(GVJ_t * job);
extern void gvrender_set_font(GVJ_t * job, char *fontname,
- double fontsize);
+ double fontsize);
extern void gvrender_textline(GVJ_t * job, pointf p, textline_t * str);
extern void gvrender_set_pencolor(GVJ_t * job, char *name);
extern void gvrender_set_fillcolor(GVJ_t * job, char *name);
extern void gvrender_set_style(GVJ_t * job, char **s);
- extern void gvrender_ellipse(GVJ_t * job, point p, int rx, int ry, int filled);
- extern void gvrender_ellipsef(GVJ_t * job, pointf p, double rx, double ry, int filled);
- extern void gvrender_polygon(GVJ_t * job, point * A, int n, int filled);
- extern void gvrender_polygonf(GVJ_t * job, pointf * AF, int n, int filled);
+ extern void gvrender_ellipse(GVJ_t * job, point p,
+ int rx, int ry, bool filled);
+ extern void gvrender_ellipsef(GVJ_t * job, pointf p,
+ double rx, double ry, bool filled);
+ extern void gvrender_polygon(GVJ_t * job, point * A, int n, bool filled);
+ extern void gvrender_polygonf(GVJ_t * job, pointf * AF, int n, bool filled);
extern void gvrender_beziercurve(GVJ_t * job, pointf * AF, int n,
- int arrow_at_start, int arrow_at_end, int);
+ int arrow_at_start, int arrow_at_end, bool filled);
extern void gvrender_polyline(GVJ_t * job, point * A, int n);
extern void gvrender_polylinef(GVJ_t * job, pointf * AF, int n);
extern void gvrender_comment(GVJ_t * job, char *str);
- extern void gvrender_user_shape(GVJ_t * job, char *name, point * A,
- int sides, int filled);
+ extern void gvrender_usershape(GVJ_t * job, char *name, point * A, int n, bool filled);
/* layout */
#include "gvcint.h"
#include "gvcproc.h"
+#if 0
+/* This code is not used - see gvrender_select() in gvrender.c */
+
int gvdevice_select(GVJ_t * job, char *str)
{
GVC_t *gvc = job->gvc;
return features;
}
+#endif
+
void gvdevice_finalize(GVC_t * gvc)
{
GVJ_t *firstjob = gvc->active_jobs;
}
/* install a plugin description into the list of available plugins */
-/* list is alpha sorted by type, the quality sorted within the type,
+/* list is alpha sorted by type, then quality sorted within the type,
then, if qualities are the same, last install wins */
bool gvplugin_install(GVC_t * gvc, api_t api,
char *typestr, int quality, char *packagename, char *path,
int arrow_at_start, int arrow_at_end, int);
void (*polyline) (GVJ_t * job, pointf * A, int n);
void (*comment) (GVJ_t * job, char *comment);
- void (*user_shape) (GVJ_t * job, char *name, pointf * A, int sides,
- int filled);
+ void (*usershape) (GVJ_t * job, usershape_t *us, boxf b, bool filled);
};
#ifdef __cplusplus
#endif
struct gvtextlayout_engine_s {
- void (*textlayout) (textline_t *textline, char *fontname, double fontsize, char* fontpath);
+ void (*textlayout) (textline_t *textline, char *fontname, double fontsize, char** fontpath);
};
#ifdef __cplusplus
if (device) {
plugin = gvplugin_load(gvc, API_device, device);
if (! plugin)
- return NO_SUPPORT;
+ return NO_SUPPORT; /* FIXME - should differentiate no device from no renderer */
typeptr = plugin->typeptr;
job->device.engine = (gvdevice_engine_t *) (typeptr->engine);
job->device.features =
{
pointf rv;
- if (job->render.features->flags & GVRENDER_DOES_TRANSFORM)
+ if (job->render.features &&
+ (job->render.features->flags & GVRENDER_DOES_TRANSFORM))
return p;
if (job->rotation) {
#endif
}
-void gvrender_ellipsef(GVJ_t * job, pointf pf, double rx, double ry, int filled)
+void gvrender_ellipsef(GVJ_t * job, pointf pf, double rx, double ry, bool filled)
{
gvrender_engine_t *gvre = job->render.engine;
#endif
}
-void gvrender_ellipse(GVJ_t * job, point p, int rx, int ry, int filled)
+void gvrender_ellipse(GVJ_t * job, point p, int rx, int ry, bool filled)
{
gvrender_engine_t *gvre = job->render.engine;
#endif
}
-void gvrender_polygonf(GVJ_t * job, pointf * af, int n, int filled)
+void gvrender_polygonf(GVJ_t * job, pointf * af, int n, bool filled)
{
int i;
gvrender_engine_t *gvre = job->render.engine;
#endif
}
-void gvrender_polygon(GVJ_t * job, point * A, int n, int filled)
+void gvrender_polygon(GVJ_t * job, point * A, int n, bool filled)
{
gvrender_engine_t *gvre = job->render.engine;
}
void gvrender_beziercurve(GVJ_t * job, pointf * af, int n,
- int arrow_at_start, int arrow_at_end, int filled)
+ int arrow_at_start, int arrow_at_end, bool filled)
{
gvrender_engine_t *gvre = job->render.engine;
#endif
}
-void gvrender_user_shape(GVJ_t * job, char *name, point * a, int n, int filled)
+void gvrender_usershape(GVJ_t * job, char *name, point * a, int n, bool filled)
{
gvrender_engine_t *gvre = job->render.engine;
+ usershape_t *us;
+ double pw, ph, tw, th; /* poly width, height, target width, height */
+ double scalex, scaley; /* scale factors */
+ boxf b; /* target box */
+ int i;
- if (gvre && gvre->user_shape) {
- int i;
- if (sizeAF < n) {
- sizeAF = n+10;
- AF = grealloc(AF, sizeAF * sizeof(pointf));
- }
- for (i = 0; i < n; i++)
- P2PF(a[i], AF[i]);
- gvre->user_shape(job, name, AF, n, filled);
- }
+ if (! (us = gvusershape_find(name)))
+ return;
+
+ if (sizeAF < n) {
+ sizeAF = n+10;
+ AF = grealloc(AF, sizeAF * sizeof(pointf));
+ }
+ for (i = 0; i < n; i++)
+ AF[i] = gvrender_pt(job, a[i]);
+
+ /* compute bb of polygon */
+ b.LL = b.UR = AF[0];
+ for (i = 1; i < n; i++) {
+ EXPANDBP(b, AF[i]);
+ }
+ pw = b.UR.x - b.LL.x;
+ ph = b.UR.y - b.LL.y;
+ scalex = pw / (double) (us->w);
+ scaley = ph / (double) (us->h);
+
+ /* keep aspect ratio fixed by just using the smaller scale */
+ if (scalex < scaley) {
+ tw = us->w * scalex;
+ th = us->h * scalex;
+ } else {
+ tw = us->w * scaley;
+ th = us->h * scaley;
+ }
+ /* if image is smaller than target area then center it */
+ if (tw < pw)
+ b.LL.x += (pw - tw) / 2.0;
+ if (th < ph)
+ b.LL.y += (ph - th) / 2.0;
+
+ if (gvre && gvre->usershape)
+ gvre->usershape(job, us, b, filled);
#ifndef DISABLE_CODEGENS
else {
- codegen_t *cg = job->codegen;
+ codegen_t *cg = job->codegen;
- if (cg && cg->user_shape)
- cg->user_shape(name, a, n, filled);
+ if (cg && cg->usershape)
+ cg->usershape(us, b, a, n, filled);
}
#endif
}
#include "config.h"
#endif
-
#include "const.h"
#include "types.h"
#include "gvplugin_textlayout.h"
* AT&T Research, Florham Park NJ *
**********************************************************/
-/*
- * usershape engine wrapper
- */
-
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
-#ifdef HAVE_STRINGS_H
-#include <strings.h>
-#endif
-#ifdef HAVE_STRING_H
+#include <stddef.h>
+#include <stdlib.h>
#include <string.h>
-#endif
-#ifndef HAVE_STRCASECMP
- extern int strcasecmp(const char *s1, const char *s2);
-#endif
-#ifndef HAVE_STRNCASECMP
- extern int strncasecmp(const char *s1, const char *s2, unsigned int n);
-#endif
-#include "macros.h"
-#include "const.h"
#include "types.h"
#include "graph.h"
+#include "utils.h"
+
+static Dict_t *ImageDict;
+
+typedef struct imagerec_s {
+ Dtlink_t link;
+ usershape_t us;
+} imagerec_t;
+
+typedef struct {
+ char *template;
+ int size, type;
+} knowntype_t;
+
+#define HDRLEN 20
+
+#define PNG_MAGIC "\x89PNG\x0D\x0A\x1A\x0A"
+#define PS_MAGIC "%!PS-Adobe-"
+#define BMP_MAGIC "BM"
+#define GIF_MAGIC "GIF8"
+#define JPEG_MAGIC "\xFF\xD8\xFF\xE0"
+#define PDF_MAGIC "%PDF-"
+#define EPS_MAGIC "\xC5\xD0\xD3\xC6"
+
+knowntype_t knowntypes[] = {
+ { PNG_MAGIC, sizeof(PNG_MAGIC)-1, FT_PNG, },
+ { PS_MAGIC, sizeof(PS_MAGIC)-1, FT_PS, },
+ { BMP_MAGIC, sizeof(BMP_MAGIC)-1, FT_BMP, },
+ { GIF_MAGIC, sizeof(GIF_MAGIC)-1, FT_GIF, },
+ { JPEG_MAGIC, sizeof(JPEG_MAGIC)-1, FT_JPEG, },
+ { PDF_MAGIC, sizeof(PDF_MAGIC)-1, FT_PDF, },
+ { EPS_MAGIC, sizeof(EPS_MAGIC)-1, FT_EPS, },
+};
-extern point gd_image_size(graph_t * g, char *shapeimagefile);
-extern point ps_image_size(graph_t * g, char *shapeimagefile);
-extern point svg_image_size(graph_t * g, char *shapeimagefile);
-extern point quartz_image_size(graph_t * g, char *shapeimagefile);
-
-/*
- * This routine extracts the usershape size from known filetypes,
- * it does not check that the output renderer knows how to render
- * this file type
- * Returns the size required for the shape in points;
- * returns (-1,-1) on error;
- */
-point image_size(graph_t * g, char *shapefile)
+static int imagetype (usershape_t *us)
+{
+ char header[HDRLEN];
+ int i;
+
+ if (fread(header, 1, HDRLEN, us->f) != HDRLEN)
+ return -1;
+ for (i = 0; i < sizeof(knowntypes) / sizeof(knowntype_t); i++) {
+ if (!memcmp (header, knowntypes[i].template, knowntypes[i].size))
+ return (us->type = knowntypes[i].type);
+ }
+ return -1;
+}
+
+static bool get_int_lsb_first (FILE *f, unsigned int sz, unsigned int *val)
+{
+ int ch, i;
+
+ *val = 0;
+ for (i = 0; i < sz; i++) {
+ ch = fgetc(f);
+ if (feof(f))
+ return false;
+ *val |= (ch << 8*i);
+ }
+ return true;
+}
+
+static bool get_int_msb_first (FILE *f, unsigned int sz, unsigned int *val)
+{
+ int ch, i;
+
+ *val = 0;
+ for (i = 0; i < sz; i++) {
+ ch = fgetc(f);
+ if (feof(f))
+ return false;
+ *val <<= 8;
+ *val |= ch;
+ }
+ return true;
+}
+
+static void png_size (usershape_t *us)
+{
+ unsigned int w, h;
+
+ us->dpi = DEFAULT_DPI;
+ fseek(us->f, 16, SEEK_SET);
+ if (get_int_msb_first(us->f, 4, &w) && get_int_msb_first(us->f, 4, &h)) {
+ us->w = w;
+ us->h = h;
+ }
+}
+
+static void gif_size (usershape_t *us)
+{
+ unsigned int w, h;
+
+ us->dpi = DEFAULT_DPI;
+ fseek(us->f, 6, SEEK_SET);
+ if (get_int_lsb_first(us->f, 2, &w) && get_int_lsb_first(us->f, 2, &h)) {
+ us->w = w;
+ us->h = h;
+ }
+}
+
+static void bmp_size (usershape_t *us) {
+ unsigned int size_x_msw, size_x_lsw, size_y_msw, size_y_lsw;
+
+ us->dpi = DEFAULT_DPI;
+ fseek (us->f, 16, SEEK_SET);
+ if ( get_int_lsb_first (us->f, 2, &size_x_msw) &&
+ get_int_lsb_first (us->f, 2, &size_x_lsw) &&
+ get_int_lsb_first (us->f, 2, &size_y_msw) &&
+ get_int_lsb_first (us->f, 2, &size_y_lsw) ) {
+ us->w = size_x_msw << 16 | size_x_lsw;
+ us->h = size_y_msw << 16 | size_y_lsw;
+ }
+}
+
+static void jpeg_size (usershape_t *us) {
+ unsigned int marker, length, size_x, size_y, junk;
+
+ /* These are the markers that follow 0xff in the file.
+ * Other markers implicitly have a 2-byte length field that follows.
+ */
+ static char standalone_markers [] = {
+ 0x01, /* Temporary */
+ 0xd0, 0xd1, 0xd2, 0xd3, /* Reset */
+ 0xd4, 0xd5, 0xd6,
+ 0xd7,
+ 0xd8, /* Start of image */
+ 0xd9, /* End of image */
+ 0
+ };
+
+ us->dpi = DEFAULT_DPI;
+ while (true) {
+ /* Now we must be at a 0xff or at a series of 0xff's.
+ * If that is not the case, or if we're at EOF, then there's
+ * a parsing error.
+ */
+ if (! get_int_msb_first (us->f, 1, &marker))
+ return;
+
+ if (marker == 0xff)
+ continue;
+
+ /* Ok.. marker now read. If it is not a stand-alone marker,
+ * then continue. If it's a Start Of Frame (0xc?), then we're there.
+ * If it's another marker with a length field, then skip ahead
+ * over that length field.
+ */
+
+ /* A stand-alone... */
+ if (strchr (standalone_markers, marker))
+ continue;
+
+ /* Incase of a 0xc0 marker: */
+ if (marker == 0xc0) {
+ /* Skip length and 2 lengths. */
+ if ( get_int_msb_first (us->f, 3, &junk) &&
+ get_int_msb_first (us->f, 2, &size_x) &&
+ get_int_msb_first (us->f, 2, &size_y) ) {
+
+ /* Store length. */
+ us->h = size_x;
+ us->w = size_y;
+ }
+ return;
+ }
+
+ /* Incase of a 0xc2 marker: */
+ if (marker == 0xc2) {
+ /* Skip length and one more byte */
+ if (! get_int_msb_first (us->f, 3, &junk))
+ return;
+
+ /* Get length and store. */
+ if ( get_int_msb_first (us->f, 2, &size_x) &&
+ get_int_msb_first (us->f, 2, &size_y) ) {
+ us->h = size_x;
+ us->w = size_y;
+ }
+ return;
+ }
+
+ /* Any other marker is assumed to be followed by 2 bytes length. */
+ if (! get_int_msb_first (us->f, 2, &length))
+ return;
+
+ fseek (us->f, length - 2, SEEK_CUR);
+ }
+}
+
+static void ps_size (usershape_t *us)
+{
+ char line[BUFSIZ];
+ bool saw_bb;
+ int lx, ly, ux, uy;
+
+ us->dpi = POINTS_PER_INCH;
+ fseek(us->f, 0, SEEK_SET);
+ saw_bb = false;
+ while (fgets(line, sizeof(line), us->f)) {
+ if (sscanf (line, "%%%%BoundingBox: %d %d %d %d", &lx, &ly, &ux, &uy) == 4) {
+ saw_bb = true;
+ break;
+ }
+ }
+ if (saw_bb) {
+ us->w = ux - lx;
+ us->h = uy - ly;
+ }
+}
+
+static void imagerec_close (Dict_t * dict, Void_t * p, Dtdisc_t * disc)
+{
+ imagerec_t *val = (imagerec_t *)p;
+
+ if (val->us.f)
+ fclose(val->us.f);
+ if (val->us.data && val->us.datafree)
+ val->us.datafree(val->us.data);
+ free (val);
+}
+
+static Dtdisc_t ImageDictDisc = {
+ offsetof(imagerec_t, us.name), /* key */
+ -1, /* size */
+ 0, /* link offset */
+ NIL(Dtmake_f),
+ imagerec_close,
+ NIL(Dtcompar_f),
+ NIL(Dthash_f),
+ NIL(Dtmemory_f),
+ NIL(Dtevent_f)
+};
+
+static imagerec_t *imagerec_open (char *name)
+{
+ imagerec_t probe, *val;
+ usershape_t *us;
+ char *fn;
+
+ if (!ImageDict)
+ ImageDict = dtopen(&ImageDictDisc, Dttree);
+
+ probe.us.name = name;
+ val = dtsearch(ImageDict, &probe);
+ if (!val) {
+ val = malloc(sizeof(imagerec_t));
+ if (!val)
+ return NULL;
+ us = &(val->us);
+ us->name = name;
+ us->w = us->h = 0;
+ us->data = us->datafree = NULL;
+
+ if ((fn = safefile(name))) {
+#ifndef MSWIN32
+ us->f = fopen(fn, "r");
+#else
+ us->f = fopen(fn, "rb");
+#endif
+ }
+ if (us->f) {
+ switch(imagetype(us)) {
+ case FT_GIF:
+ gif_size(us);
+ break;
+ case FT_PNG:
+ png_size(us);
+ break;
+ case FT_BMP:
+ bmp_size(us);
+ break;
+ case FT_JPEG:
+ jpeg_size(us);
+ break;
+ case FT_PS:
+ ps_size(us);
+ break;
+ case FT_PDF: /* no pdf_size code available */
+ case FT_EPS: /* no eps_size code available */
+ default:
+ break;
+ }
+ }
+ dtinsert(ImageDict, val);
+ }
+
+ return val;
+}
+
+point gvusershape_size(graph_t * g, char *name)
{
- char *suffix;
point rv;
+ imagerec_t *val;
+ double dpi;
+
+ dpi = GD_drawing(g)->dpi;
+ if (dpi < 1.0)
+ dpi = POINTS_PER_INCH;
/* no shape file, no shape size */
- if (!shapefile || (*shapefile == '\0')) {
+ if (!name || (*name == '\0')) {
rv.x = rv.y = -1;
return rv;
}
- if (!strncasecmp(shapefile, "http://", 7)) {
+ if (!strncasecmp(name, "http://", 7)) {
rv.x = rv.y = 0;
return rv; /* punt on obvious web addresses */
}
- suffix = strrchr(shapefile, '.');
- if (!suffix)
- suffix = shapefile;
- else
- suffix++;
- if (!strcasecmp(suffix, "wbmp")
-#ifdef HAVE_GD_GIF
- || !strcasecmp(suffix, "gif")
-#endif
-#ifdef HAVE_GD_JPEG
- || !strcasecmp(suffix, "jpeg") || !strcasecmp(suffix, "jpg")
-#endif
-#ifdef HAVE_GD_PNG
- || !strcasecmp(suffix, "png")
-#endif
-#ifdef HAVE_GD_XPM
- || !strcasecmp(suffix, "xbm")
-/* FIXME - is "xpm" also supported by gd for input ? */
-#endif
- ) {
- rv = gd_image_size(g, shapefile);
- } else
-#ifdef QUARTZ_RENDER
- if (Output_lang == QPDF || Output_lang == QEPDF ||
- (Output_lang >= QBM_FIRST && Output_lang <= QBM_LAST))
- return quartz_image_size(g, shapefile);
- else
-#endif
-
- if (!strcasecmp(suffix, "ps")) {
- rv = ps_image_size(g, shapefile);
- } else if (!strcasecmp(suffix, "svg")) {
- rv = svg_image_size(g, shapefile);
- } else {
- agerr(AGERR, "shapefile \"%s\" suffix not recognized\n",
- shapefile);
+ if ((val = imagerec_open (name))) {
+ rv.x = val->us.w * dpi / val->us.dpi;
+ rv.y = val->us.h * dpi / val->us.dpi;
+ }
+ else {
rv.x = rv.y = -1;
}
+
return rv;
}
+
+usershape_t *gvusershape_find(char *name)
+{
+ imagerec_t probe, *val;
+
+ probe.us.name = name;
+ val = dtsearch(ImageDict, &probe);
+ if (!val)
+ return NULL;
+ return &(val->us);
+}
}
}
-static void tk_user_shape(char *name, point * A, int n, int filled)
+static void tk_usershape(usershape_t *us, boxf b, point *A, int n, bool filled)
{
+/* FIXME */
static bool onetime = TRUE;
if (onetime) {
fprintf(stderr, "custom shapes not available with this driver\n");
tk_bezier, tk_polyline,
0, /* tk_arrows */
0, /* tk_comment */
- 0, /* tk_textsize */
- tk_user_shape,
- 0 /* tk_usershapesize */
+ tk_usershape
};