]> granicus.if.org Git - graphviz/commitdiff
Rework of usershape code into a layout part and a renderer part.
authorellson <devnull@localhost>
Sun, 21 May 2006 00:34:56 +0000 (00:34 +0000)
committerellson <devnull@localhost>
Sun, 21 May 2006 00:34:56 +0000 (00:34 +0000)
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.

33 files changed:
lib/common/Makefile.am
lib/common/diagen.c
lib/common/emit.c
lib/common/figgen.c
lib/common/gdgen.c
lib/common/gdusershape.c [deleted file]
lib/common/hpglgen.c
lib/common/htmltable.c
lib/common/macros.h
lib/common/mapgen.c
lib/common/mifgen.c
lib/common/mpgen.c
lib/common/picgen.c
lib/common/psgen.c
lib/common/psusershape.c
lib/common/render.h
lib/common/shapes.c
lib/common/svggen.c
lib/common/svgusershape.c [deleted file]
lib/common/types.h
lib/common/vrmlgen.c
lib/common/vtxgen.c
lib/common/xdgen.c
lib/gvc/gvconfig.c
lib/gvc/gvcproc.h
lib/gvc/gvdevice.c
lib/gvc/gvplugin.c
lib/gvc/gvplugin_render.h
lib/gvc/gvplugin_textlayout.h
lib/gvc/gvrender.c
lib/gvc/gvtextlayout.c
lib/gvc/gvusershape.c
tclpkg/tcldot/tkgen.c

index 9fda0d1ecc1d388bb2777cb4f5c55cd1cf4beeb9..78abfdaacc333e46fe0139edd4bf8fbf7865330b 100644 (file)
@@ -23,10 +23,9 @@ endif
 
 
 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
index b7c31c0ec2150bc287b8662a373b1d8503b367a3..deea14295128b58c737df2cb1d6a050bbce8acd4 100644 (file)
@@ -920,7 +920,7 @@ static void dia_polyline(point * A, int n)
     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;
 
@@ -928,9 +928,15 @@ static void dia_user_shape(char *name, point * A, int n, int filled)
        /* 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;
     }
@@ -955,7 +961,5 @@ codegen_t DIA_CodeGen = {
     dia_bezier, dia_polyline,
     1,                         /* bezier_has_arrows */
     dia_comment,
-    0,                         /* dia_textsize */
-    dia_user_shape,
-    0                          /* dia_usershapesize */
+    dia_usershape
 };
index 84203322ec2ee08644df854f1f54ef64484859f3..e69fca86f03ea21271374c4c6fb6f350183cc793 100644 (file)
@@ -93,6 +93,15 @@ static void init_job_flags(GVJ_t * job, graph_t * g)
     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
index e7cfeb0e05b2c8bc844b7d34b9cf9369b93e4c54..085fb6e0795ba2c9269f3ad3ca7ce35d722e2a53 100644 (file)
@@ -598,7 +598,7 @@ static void fig_polyline(point * A, int n)
     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) {
@@ -626,7 +626,5 @@ codegen_t FIG_CodeGen = {
     fig_bezier, fig_polyline,
     0,                         /* bezier_has_arrows */
     fig_comment,
-    0,                         /* fig_textsize */
-    fig_user_shape,
-    0                          /* fig_usershapesize */
+    fig_usershape
 };
index cc61642274035bb1d9b0179e76214e66152c246f..944020592ab95e5695d4a25c00a946633d32ce98 100644 (file)
@@ -22,9 +22,6 @@
 #include <io.h>
 #endif
 
-extern gdImagePtr gd_getshapeimage(char *name);
-extern void gd_freeusershapes(void);
-
 static gdImagePtr im;
 static int external_surface;
 
@@ -356,7 +353,6 @@ static void gd_end_graph(void)
        gdImageXbm(im, Output_file);
 #endif
     }
-    gd_freeusershapes();
     gdImageDestroy(im);
 #ifdef MYTRACE
     fprintf(stderr, "gd_end_graph_to_file\n");
@@ -929,59 +925,52 @@ static void gd_polyline(point * A, int 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 = {
@@ -1003,7 +992,5 @@ 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
 };
diff --git a/lib/common/gdusershape.c b/lib/common/gdusershape.c
deleted file mode 100644 (file)
index 72dd62d..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-/* $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;
-}
index ff7971065458d958aca660382de1cce936481811..c6e413b80c78687699c455b7f34f1a1e20311440 100644 (file)
@@ -825,7 +825,7 @@ static void hpgl_polyline(point * A, int n)
     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) {
@@ -853,7 +853,5 @@ codegen_t HPGL_CodeGen = {
     hpgl_bezier, hpgl_polyline,
     0,                         /* bezier_has_arrows */
     0,                         /* hpgl_comment */
-    0,                         /* hpgl_textsize */
-    hpgl_user_shape,
-    0                          /* hpgl_usershapesize */
+    hpgl_usershape
 };
index 2e456f21d501eb9f5b9addae0d0bde6ffef9cada..e2eabc86f968ff288c7808e7cc2f439f390bf39d 100644 (file)
@@ -394,7 +394,7 @@ emit_html_img(GVJ_t * job, htmlimg_t * cp, htmlenv_t * env, void *obj)
     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
@@ -852,7 +852,7 @@ static int size_html_img(htmlimg_t * img, htmlenv_t * env)
     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;
index 8d84eea9fbb7ffa960940db8d0103c6de0e1fb4e..ae6426611b2fa15db18557ec4978db1e2cfc669b 100644 (file)
@@ -44,4 +44,5 @@
 
 #define YDIR(y) (Y_invert ? (Y_off - (y)) : (y))
 #define YFDIR(y) (Y_invert ? (YF_off - (y)) : (y))
+
 #endif
index 43b561cd68597b85dfccc6861b1244c199628051..a1b1e415b54b8b349fbe50c80aaa00c7abfe7432 100644 (file)
@@ -1116,7 +1116,5 @@ codegen_t MAP_CodeGen = {
     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 */
 };
index 861c91f2011c355d92524cd50f02ecac5fb45966..70fdcd815c981e7477b419544d3efc71a301960d 100644 (file)
@@ -547,7 +547,7 @@ static void mif_polyline(point * A, int n)
     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) {
@@ -575,7 +575,5 @@ codegen_t MIF_CodeGen = {
     mif_bezier, mif_polyline,
     0,                         /* bezier_has_arrows */
     mif_comment,
-    0,                         /* mif_textsize */
-    mif_user_shape,
-    0                          /* mif_usershapesize */
+    mif_usershape
 };
index 1496f86d24bce48e98c6bd495d6ee68c04d7a80a..a8c94919a5d7be3dc360eb64c017cc4a3d22c5c3 100644 (file)
@@ -246,15 +246,15 @@ static void mp_polyline(point * A, int n)
     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 = {
@@ -276,7 +276,5 @@ 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
 };
index 1c694cd5e3c5efe4e6910a770749389ce6217a69..b33ff3e804d0633842e60a9539e9bac43e743129 100644 (file)
@@ -554,12 +554,13 @@ static void pic_polyline(point * A, int n)
     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,
@@ -614,7 +615,5 @@ codegen_t PIC_CodeGen = {
     pic_bezier, pic_polyline,
     0,                         /* bezier_has_arrows */
     pic_comment,
-    0,                         /* pic_textsize */
-    pic_user_shape,
-    0                          /* pic_usershapesize */
+    pic_usershape
 };
index 4d531a96e05eb4d9973fbd8a9bac6abb933bef88..ca6d9df2b75be351df70ea3980261d3fc4ff6c60 100644 (file)
@@ -35,7 +35,7 @@
 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);
 
@@ -534,7 +534,12 @@ static void writePSBitmap (gdImagePtr im, point p, point sz)
 
 }
 
-/* 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
@@ -543,71 +548,113 @@ static void writePSBitmap (gdImagePtr im, point p, point sz)
  * 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 = {
@@ -629,7 +676,5 @@ codegen_t PS_CodeGen = {
     ps_bezier, ps_polyline,
     0,                         /* bezier_has_arrows */
     ps_comment,
-    0,                         /* ps_textsize */
-    ps_user_shape,
-    0                          /* usershapesize */
+    ps_usershape
 };
index b85303ccf54a5143ac364dbffc1b2ef576f14fbd..1892b71656e50c46075336aa4869ebbed2754251 100644 (file)
@@ -221,7 +221,7 @@ void ps_freeusershapes(void)
     }
 }
 
-ps_image_t *ps_usershape(char *shapeimagefile)
+ps_image_t *ps_usershape_to_image(char *shapeimagefile)
 {
     if (EPSF_contents) {
        return dtmatch(EPSF_contents, shapeimagefile);
index 3ec506f8c70ac0337f6b9ba0ca184d276ad463d4..f858f99ee201b2c63be4b178fda56c2b160a250f 100644 (file)
@@ -126,7 +126,6 @@ extern "C" {
     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);
index 1bf59ba12ab9d747b168f82a847447b94f9776ea..7bd38633fd53605f6a341b9e4e8c76957c68eea0 100644 (file)
@@ -564,7 +564,7 @@ static void poly_init(node_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",
@@ -1200,7 +1200,7 @@ static void poly_gencode(GVJ_t * job, node_t * 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;
@@ -1285,7 +1285,10 @@ static void poly_gencode(GVJ_t * job, node_t * n)
                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 */
index 2a4e0c07edf57a652bad0a55b9a359670fe7ba7c..761580754ac4804a6dca6804d4060a513b583bc7 100644 (file)
@@ -883,7 +883,7 @@ static void svg_polyline(point * A, int n)
     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;
@@ -896,11 +896,11 @@ static void svg_user_shape(char *name, point * A, int n, int filled)
        /* 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;
     }
@@ -914,7 +914,7 @@ static void svg_user_shape(char *name, point * A, int n, int filled)
 */
 
     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;
@@ -943,7 +943,7 @@ static void svg_user_shape(char *name, point * A, int n, int filled)
     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");
 }
@@ -967,7 +967,5 @@ codegen_t SVG_CodeGen = {
     svg_bezier, svg_polyline,
     0,                         /* bezier_has_arrows */
     svg_comment,
-    0,                         /* svg_textsize */
-    svg_user_shape,
-    0                          /* svg_usershape_size */
+    svg_usershape
 };
diff --git a/lib/common/svgusershape.c b/lib/common/svgusershape.c
deleted file mode 100644 (file)
index a4f3ecb..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/* $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;
-}
index d95863f02f7adccd2251c2555d621c471cfae8e8..9f62419669b057a35d1943dcc72c3b8074e99d1e 100644 (file)
@@ -114,7 +114,16 @@ extern "C" {
        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 */
@@ -231,9 +240,7 @@ extern "C" {
        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 {
index b2f4d35ccce41d4117060b71c7a2137e24a6f6bf..109ac14741940d81d5eda51dab92e00c07e760fd 100644 (file)
@@ -1022,8 +1022,9 @@ static void vrml_polyline(point * A, int n)
 */
 }
 
-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);
 }
 
@@ -1046,8 +1047,6 @@ codegen_t VRML_CodeGen = {
     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 */
index 26d36b94c107b8500421b6914011c4b2cdb99bd9..f326bdbf9faa77a10c770eef1f7c47ce7bf86987 100644 (file)
@@ -598,8 +598,9 @@ static void vtx_polyline(point * A, int n)
     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;
 
@@ -645,7 +646,5 @@ codegen_t VTX_CodeGen = {
     vtx_bezier, vtx_polyline,
     1,                         /* bezier_has_arrows */
     vtx_comment,
-    0,                         /* vtx_textsize */
-    vtx_user_shape,
-    0                          /* vtx_usershapesize */
+    vtx_usershape
 };
index bb435861bbf86f7049201b705f35d71da9238d27..8093631322271618ede57b38510dbb47c17032f3 100644 (file)
@@ -331,6 +331,7 @@ codegen_t XDot_CodeGen = {
     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 */
 };
index bc87e137cef778a75993743e9a847d78bce32824..f813ade02b16d940d0a2ec954c1f7dd4214b46a0 100644 (file)
@@ -195,6 +195,9 @@ static void gvconfig_plugin_install_from_library(GVC_t * gvc, char *path, gvplug
 
     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]);
         }
index 541e12de8d60d0f52aec18cac110457c39fdf82a..26561706cdd64fd20c72d999d7d7bfd4f5efdcc7 100644 (file)
@@ -60,6 +60,10 @@ extern "C" {
     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);
@@ -92,25 +96,26 @@ extern "C" {
     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 */
 
index 4eb0f1d3813c6731c56601af8575a05f89a586cd..86cbf43aaefac102f726ddf6aac95848d8edcd4c 100644 (file)
@@ -32,6 +32,9 @@
 #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;
@@ -73,6 +76,8 @@ int gvdevice_features(GVJ_t * job)
     return features;
 }
 
+#endif
+
 void gvdevice_finalize(GVC_t * gvc)
 {
     GVJ_t *firstjob = gvc->active_jobs;
index c6b9bbf631dc6149a07806fc4fb594027e857e71..d93ade18bbec00a7e4d04e0ce4b9947f5b431ded 100644 (file)
@@ -60,7 +60,7 @@ char *gvplugin_api_name(api_t api)
 }
 
 /* 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,
index aba4aa4b45d159e0f384c1bc138e13ea46e70a24..9d95f33df79a9f5b198763d1b8e1f518f64e0c5c 100644 (file)
@@ -56,8 +56,7 @@ extern "C" {
                             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
index 14d772cf2833d2972bf2c08e3a872dcd401bd7e1..52741d6757f30c36dc332c40b5e1347452f89947 100644 (file)
@@ -25,7 +25,7 @@ extern "C" {
 #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
index d92a41a86b5ca47268ba2fec0d44d8417c9ef8ac..85509615e1f6c76866ea53137e85476b99ffb1a9 100644 (file)
@@ -81,7 +81,7 @@ int gvrender_select(GVJ_t * job, char *str)
            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 =
@@ -177,7 +177,8 @@ static pointf gvrender_ptf(GVJ_t *job, pointf p)
 {
     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) {
@@ -786,7 +787,7 @@ void gvrender_set_style(GVJ_t * job, char **s)
 #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;
 
@@ -819,7 +820,7 @@ void gvrender_ellipsef(GVJ_t * job, pointf pf, double rx, double ry, int filled)
 #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;
 
@@ -851,7 +852,7 @@ void gvrender_ellipse(GVJ_t * job, point p, int rx, int ry, int filled)
 #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;
@@ -883,7 +884,7 @@ void gvrender_polygonf(GVJ_t * job, pointf * af, int n, int filled)
 #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;
 
@@ -910,7 +911,7 @@ void gvrender_polygon(GVJ_t * job, point * A, int n, int filled)
 }
 
 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;
 
@@ -1020,26 +1021,57 @@ void gvrender_comment(GVJ_t * job, char *str)
 #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
 }
index b889bcbfd345961fbbdd778fdf872022264e2498..186b5e3648133f0c459b7d961b5e895aee0f1887 100644 (file)
@@ -22,7 +22,6 @@
 #include "config.h"
 #endif
 
-
 #include "const.h"
 #include "types.h"
 #include "gvplugin_textlayout.h"
index ed49a039756387a2fb9ca4aa296238fae3b7bda6..c7cc5cb7f3b6716359a11df023f58ddf76f14756 100644 (file)
 *              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);
+}
index 106d971be59d97d26314bd17e8e8db65778d933b..b2c8f7a1f9f78867f909df3daf595cb8787fe98a 100644 (file)
@@ -521,8 +521,9 @@ static void tk_polyline(point * A, int n)
     }
 }
 
-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");
@@ -550,7 +551,5 @@ codegen_t TK_CodeGen = {
     tk_bezier, tk_polyline,
     0,                         /* tk_arrows */
     0,                         /* tk_comment */
-    0,                         /* tk_textsize */
-    tk_user_shape,
-    0                          /* tk_usershapesize */
+    tk_usershape
 };