noinst_LTLIBRARIES = libcommon_C.la
if WITH_CODEGENS
-CODEGENS = $(GD_CODEGENS) diagen.c figgen.c hpglgen.c mapgen.c mifgen.c mpgen.c \
+CODEGENS = $(GD_CODEGENS) diagen.c figgen.c hpglgen.c mifgen.c mpgen.c \
picgen.c vtxgen.c xdgen.c
endif
+++ /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"
-
-#include <fcntl.h>
-#ifdef MSWIN32
-#include <io.h>
-#endif
-
-static gdImagePtr im;
-static int external_surface;
-
-#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 */
-
-#define BEZIERSUBDIVISION 10
-
-/* 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 2
-
-/* static int N_pages; */
-/* static point Pages; */
-static pointf Dpi;
-static point Margin;
-static pointf CompScale;
-static int Rot;
-
-static point Viewport;
-static pointf GraphFocus;
-static double Zoom;
-
-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 int white, black, transparent;
-
-static int gd_resolve_color(char *name)
-{
- gvcolor_t color;
-
- if (!(strcmp(name, "transparent"))) {
- /* special case for "transparent" color */
- return transparent;
- } else {
- colorxlate(name, &color, RGBA_BYTE);
- /* seems gd alpha is "transparency" rather than the usual "opacity" */
- return gdImageColorResolveAlpha(im,
- color.u.rgba[0],
- color.u.rgba[1],
- color.u.rgba[2],
- (255 -
- color.u.rgba[3]) * gdAlphaMax /
- 255);
- }
-}
-
-static pointf gdpt(pointf p)
-{
- pointf rv;
-
- if (Rot == 0) {
- rv.x = (p.x - GraphFocus.x) * CompScale.x + Viewport.x / 2. + Margin.x;
- rv.y = -(p.y - GraphFocus.y) * CompScale.y + Viewport.y / 2. + Margin.y;
- } else {
- rv.x = -(p.y - GraphFocus.y) * CompScale.x + Viewport.x / 2. + Margin.x;
- rv.y = -(p.x - GraphFocus.x) * CompScale.y + Viewport.y / 2. + Margin.y;
- }
- return rv;
-}
-
-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
-}
-
-static void init1_gd(GVC_t * gvc, graph_t * g, box bb, point pb)
-{
- Dpi = gvc->job->dpi;
- Margin.x = ROUND(gvc->job->margin.x * Dpi.x / POINTS_PER_INCH);
- Margin.y = ROUND(gvc->job->margin.y * Dpi.y / POINTS_PER_INCH);
- Viewport.x = gvc->job->width;
- Viewport.y = gvc->job->height;
- Zoom = gvc->job->zoom;
- GraphFocus = gvc->job->focus;
- CompScale.x = Zoom * Dpi.x / POINTS_PER_INCH;
- CompScale.y = Zoom * Dpi.y / POINTS_PER_INCH;
-}
-
-static void init2_gd(gdImagePtr im)
-{
- SP = 0;
-
- white = gdImageColorResolveAlpha(im,
- gdRedMax, gdGreenMax, gdBlueMax,
- gdAlphaOpaque);
- black = gdImageColorResolveAlpha(im, 0, 0, 0, gdAlphaOpaque);
- transparent = gdImageColorResolveAlpha(im,
- gdRedMax - 1, gdGreenMax,
- gdBlueMax, gdAlphaTransparent);
- gdImageColorTransparent(im, transparent);
-
- cstk[0].pencolor = black; /* set pen 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;
-}
-
-static bool is_format_truecolor_capable(int Output_lang)
-{
- bool rv = FALSE;
-
- if (Output_lang == GD) {
- rv = TRUE;
-#ifdef HAVE_LIBZ
- } else if (Output_lang == GD2) {
- rv = TRUE;
-#endif
-#ifdef HAVE_GD_GIF
- } else if (Output_lang == GIF) {
- rv = FALSE;
-#endif
-#ifdef HAVE_GD_JPEG
- } else if (Output_lang == JPEG) {
- rv = TRUE;
-#endif
-#ifdef HAVE_GD_PNG
- } else if (Output_lang == PNG) {
- rv = TRUE;
-#endif
- } else if (Output_lang == WBMP) {
- rv = FALSE;
-#ifdef HAVE_GD_XPM
- } else if (Output_lang == XBM) {
- rv = FALSE;
-#endif
- }
- return rv;
-}
-
-static void gd_begin_graph(GVC_t * gvc, graph_t * g, box bb, point pb)
-{
- char *bgcolor_str = NULL;
- char *truecolor_str;
- bool truecolor_p = FALSE; /* try to use cheaper paletted mode */
- bool bg_transparent_p = FALSE;
- int bgcolor;
-
- external_surface = gvc->job->external_surface;
-
- init1_gd(gvc, g, bb, pb);
-
- if (external_surface) {
- im = (gdImagePtr)gvc->job->surface;
- } else {
- int width, height;
-
- truecolor_str = agget(g, "truecolor"); /* allow user to force truecolor */
- bgcolor_str = agget(g, "bgcolor");
-
- if (truecolor_str && truecolor_str[0])
- truecolor_p = mapbool(truecolor_str);
-
- if (bgcolor_str && strcmp(bgcolor_str, "transparent") == 0) {
- bg_transparent_p = TRUE;
- if (is_format_truecolor_capable(Output_lang))
- truecolor_p = TRUE; /* force truecolor */
- }
-
- if (GD_has_images(g))
- truecolor_p = TRUE; /* force truecolor */
-
- /* device size with margins all around */
- width = Viewport.x + 2 * Margin.x;
- height = Viewport.y + 2 * Margin.y;
-
- if (truecolor_p) {
- if (Verbose)
- fprintf(stderr, "%s: allocating a %dK TrueColor GD image\n",
- gvc->common.cmdname, ROUND(width * height * 4 / 1024.));
- im = gdImageCreateTrueColor(width, height);
- } else {
- if (Verbose)
- fprintf(stderr, "%s: allocating a %dK PaletteColor GD image\n",
- gvc->common.cmdname, ROUND(width * height / 1024.));
- im = gdImageCreate(width, height);
- }
- if (!im) {
- agerr(AGERR, "gdImageCreate returned NULL. Malloc problem?\n");
- return;
- }
- }
-
- init2_gd(im);
-
- if (! external_surface) {
- if (bgcolor_str && bgcolor_str[0])
- if (bg_transparent_p)
- bgcolor = transparent;
- else
- bgcolor = gd_resolve_color(bgcolor_str);
- else
- bgcolor = white;
-
- cstk[0].fillcolor = bgcolor;
-
- /* Blending must be off to lay a transparent bgcolor.
- Nothing to blend with anyway. */
- gdImageAlphaBlending(im, FALSE);
-
- gdImageFill(im, im->sx / 2, im->sy / 2, bgcolor);
-
- /* Blend everything else together,
- especially fonts over non-transparent backgrounds */
- gdImageAlphaBlending(im, TRUE);
- }
-
-#ifdef MYTRACE
- fprintf(stderr, "gd_begin_graph\n");
-#endif
-}
-
-static void gd_end_graph(void)
-{
- if (!im || external_surface)
- return;
-
-/*
- * 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
-
- /* Only save the alpha channel in outputs that support it if
- the base color was transparent. Otherwise everything
- was blended so there is no useful alpha info */
- if (im->trueColor) {
- if (is_format_truecolor_capable(Output_lang))
- gdImageSaveAlpha(im, TRUE);
- else
- gdImageTrueColorToPalette(im, 0, 256);
- }
- 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 HAVE_GD_GIF
- } else if (Output_lang == GIF) {
- gdImageGif(im, Output_file);
-#endif
-#ifdef HAVE_GD_JPEG
- } else if (Output_lang == JPEG) {
- /*
- * 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
- gdImageJpeg(im, Output_file, JPEG_QUALITY);
-#endif
-#ifdef HAVE_GD_PNG
- } else if (Output_lang == PNG) {
- gdImagePng(im, Output_file);
-#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_GD_XPM
- } else if (Output_lang == XBM) {
- gdImageXbm(im, Output_file);
-#endif
- }
- gdImageDestroy(im);
-#ifdef MYTRACE
- fprintf(stderr, "gd_end_graph_to_file\n");
-#endif
-}
-
-static void
-gd_begin_page(graph_t * g, point page, double scale, int rot, point offset)
-{
- if (!im)
- return;
-
-/* int page_number; */
-/* point sz; */
-
- Rot = rot;
-
-/* page_number = page.x + page.y * Pages.x + 1; */
-#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_node(node_t * n)
-{
- Curnode = n;
-}
-
-static void gd_end_node(void)
-{
- Curnode = NULL;
-}
-
-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);
- SP = psp;
-}
-
-static void gd_set_font(char *fontname, double fontsize)
-{
- context_t *cp;
-#if 0
- char *p;
- char *q;
-#endif
-
- cp = &(cstk[SP]);
- cp->fontsz = fontsize;
-#if 0
- p = strdup(fontname);
- if ((q = strchr(p, '-'))) {
- *q++ = 0;
- if (strcasecmp(q, "italic") == 0)
- cp->fontopt = ITALIC;
- else if (strcasecmp(q, "bold") == 0)
- cp->fontopt = BOLD;
- }
-#endif
- cp->fontfam = fontname;
-}
-
-static void gd_set_pencolor(char *name)
-{
- if (!im)
- return;
-
- cstk[SP].pencolor = gd_resolve_color(name);
-}
-
-static void gd_set_fillcolor(char *name)
-{
- if (!im)
- return;
-
- 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);
- }
-}
-
-void gd_missingfont(char *err, char *fontreq)
-{
- static char *lastmissing = 0;
- static int n_errors = 0;
-
- if (n_errors >= 20)
- return;
- if ((lastmissing == 0) || (strcmp(lastmissing, fontreq))) {
-#if HAVE_GD_FONTCONFIG
- agerr(AGERR, "%s : %s\n", err, fontreq);
-#else
- char *p = getenv("GDFONTPATH");
- if (!p)
- p = DEFAULT_FONTPATH;
- agerr(AGERR, "%s : %s in %s\n", err, fontreq, p);
-#endif
- 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_textpara(point p, textpara_t * para)
-{
- pointf mp, ep;
- double fontsz;
- gdFTStringExtra strex;
- int pencolor;
-#ifdef HAVE_GD_FREETYPE
- char *err;
- int brect[8];
-#endif
-
- if (!im)
- return;
-
- strex.flags = gdFTEX_RESOLUTION;
- strex.hdpi = strex.vdpi = Dpi.x * Zoom;
-
- if (cstk[SP].pen == P_NONE)
- return;
-
- pencolor = cstk[SP].pencolor;
- if (!im->trueColor && cstk[SP].fillcolor == transparent)
- pencolor = -pencolor; /* disable antialiasing */
-
- if (strstr(cstk[SP].fontfam, "/"))
- strex.flags |= gdFTEX_FONTPATHNAME;
- else
- strex.flags |= gdFTEX_FONTCONFIG;
-
- fontsz = cstk[SP].fontsz;
-
- switch (para->just) {
- case 'l':
- mp.x = p.x;
- break;
- case 'r':
- mp.x = p.x - para->width;
- break;
- default:
- case 'n':
- mp.x = p.x - para->width / 2;
- break;
- }
- ep.y = mp.y = p.y;
- ep.x = mp.x + para->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 {
-#ifdef HAVE_GD_FREETYPE
- gdFTUseFontConfig(1); /* use fontconfig if possible */
- err = gdImageStringFTEx(im, brect, pencolor,
- para->fontname, fontsz, Rot ? (PI / 2) : 0,
- ROUND(mp.x), ROUND(mp.y), para->str, &strex);
-#if 0
- gdImagePolygon(im, (gdPointPtr) brect, 4, cstk[SP].pencolor);
-#endif
-#if 0
- fprintf(stderr,
- "textpara: font=%s size=%g pos=%g,%g width=%g dpi=%d width/dpi=%g\n",
- para->fontname, fontsz, mp.x, mp.y, (double) (brect[4] - brect[0]),
- strex.hdpi,
- (((double) (brect[4] - brect[0])) / strex.hdpi));
-#endif
- if (err) {
- /* revert to builtin fonts */
- gd_missingfont(err, cstk[SP].fontfam);
-#endif
- mp.y += 2;
- if (fontsz <= 8.5) {
- gdImageString(im, gdFontTiny,
- ROUND(mp.x), ROUND(mp.y - 9.),
- (unsigned char *) para->str, cstk[SP].pencolor);
- } else if (fontsz <= 9.5) {
- gdImageString(im, gdFontSmall,
- ROUND(mp.x), ROUND(mp.y - 12.),
- (unsigned char *) para->str, cstk[SP].pencolor);
- } else if (fontsz <= 10.5) {
- gdImageString(im, gdFontMediumBold,
- ROUND(mp.x), ROUND(mp.y - 13.),
- (unsigned char *) para->str, cstk[SP].pencolor);
- } else if (fontsz <= 11.5) {
- gdImageString(im, gdFontLarge,
- ROUND(mp.x), ROUND(mp.y - 14.),
- (unsigned char *) para->str, cstk[SP].pencolor);
- } else {
- gdImageString(im, gdFontGiant,
- ROUND(mp.x), ROUND(mp.y - 15.),
- (unsigned char *) para->str, cstk[SP].pencolor);
- }
-#ifdef HAVE_GD_FREETYPE
- }
-#endif
- }
-}
-
-static void
-gd_bezier(point * A, int n, int arrow_at_start, int arrow_at_end, int filled)
-{
- pointf p, p0, p1, V[4];
- int i, j, step;
- int style[20];
- int pen, width;
- gdImagePtr brush = NULL;
- gdPoint F[4];
-
- if (!im)
- return;
-
- 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] = transparent;
- 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] = transparent;
- gdImageSetStyle(im, style, 12);
- pen = gdStyled;
- } else {
- pen = cstk[SP].pencolor;
- }
- width = cstk[SP].penwidth * CompScale.x;
- if (width < WIDTH_NORMAL)
- width = WIDTH_NORMAL; /* gd can't do thin lines */
- gdImageSetThickness(im, width);
- if (width > WIDTH_NORMAL) {
- 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;
- }
- p.x = A[0].x;
- p.y = A[0].y;
- p = gdpt(p);
- F[0].x = ROUND(p.x);
- F[0].y = ROUND(p.y);
- p.x = A[n-1].x;
- p.y = A[n-1].y;
- p = gdpt(p);
- F[3].x = ROUND(p.x);
- F[3].y = ROUND(p.y);
- 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);
- if (filled) {
- F[1].x = ROUND(p0.x);
- F[1].y = ROUND(p0.y);
- F[2].x = ROUND(p1.x);
- F[2].y = ROUND(p1.y);
- gdImageFilledPolygon(im, F, 4, cstk[SP].fillcolor);
- }
- 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 (!im)
- return;
-
- 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] = transparent;
- 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] = transparent;
- gdImageSetStyle(im, style, 12);
- pen = gdStyled;
- } else {
- pen = cstk[SP].pencolor;
- }
- width = cstk[SP].penwidth * CompScale.x;
- if (width < WIDTH_NORMAL)
- width = WIDTH_NORMAL; /* gd can't do thin lines */
- gdImageSetThickness(im, width);
- /* use brush instead of Thickness to improve end butts */
- if (width > WIDTH_NORMAL) {
- 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;
- }
- 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 (!im)
- return;
-
- 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] = transparent;
- 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] = transparent;
- gdImageSetStyle(im, style, 24);
- pen = gdStyled;
- } else {
- pen = cstk[SP].pencolor;
- }
- width = cstk[SP].penwidth * CompScale.x;
- if (width < WIDTH_NORMAL)
- width = WIDTH_NORMAL; /* gd can't do thin lines */
- gdImageSetThickness(im, width);
- /* use brush instead of Thickness to improve outline appearance */
- if (width > WIDTH_NORMAL) {
- 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;
- }
- 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(CompScale.x * (rx + rx)),
- ROUND(CompScale.y * (ry + ry)),
- cstk[SP].fillcolor);
- }
- gdImageArc(im, ROUND(mp.x), ROUND(mp.y),
- ROUND(CompScale.x * (rx + rx)),
- ROUND(CompScale.y * (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 (!im)
- return;
-
- 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] = transparent;
- 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] = transparent;
- gdImageSetStyle(im, style, 12);
- pen = gdStyled;
- } else {
- pen = cstk[SP].pencolor;
- }
- width = cstk[SP].penwidth * CompScale.x;
- if (width < WIDTH_NORMAL)
- width = WIDTH_NORMAL; /* gd can't do thin lines */
- gdImageSetThickness(im, width);
- if (width != WIDTH_NORMAL) {
- 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;
- }
- 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 void gd_freeimage(usershape_t *us)
-{
- gdImageDestroy((gdImagePtr)us->data);
-}
-
-static void gd_usershape(usershape_t *us, boxf b, point *A, int n, bool filled)
-{
- gdImagePtr im2 = NULL, im3;
-
- if (!us->f)
- return;
-
- if (us->data) {
- if (us->datafree == gd_freeimage)
- im2 = (gdImagePtr)(us->data); /* use cached data */
- else {
- us->datafree(us); /* 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) {
- if (Rot) {
- im3 = gdImageCreate(im2->sy, im2->sx); /* scratch image for rotation */
- gdImageCopyRotated(im3, im2, im3->sx / 2., im3->sy / 2.,
- 0, 0, im2->sx, im2->sy, Rot);
- gdImageCopyResized(im, im3, ROUND(b.LL.x), ROUND(b.LL.y), 0, 0,
- ROUND(b.UR.y - b.LL.y), ROUND(b.UR.x - b.LL.x), im3->sx, im3->sy);
- gdImageDestroy(im3);
- }
- else {
- 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), im2->sx, im2->sy);
- }
- }
-}
-
-codegen_t GD_CodeGen = {
- 0, /* gd_reset */
- gd_begin_job, gd_end_job,
- gd_begin_graph, gd_end_graph,
- gd_begin_page, gd_end_page,
- 0, /* gd_begin_layer */ 0, /* gd_end_layer */
- 0, /* gd_begin_cluster */ 0, /* gd_end_cluster */
- 0, /* gd_begin_nodes */ 0, /* gd_end_nodes */
- 0, /* gd_begin_edges */ 0, /* gd_end_edges */
- gd_begin_node, gd_end_node,
- 0, /* gd_begin_edge */ 0, /* gd_end_edge */
- gd_begin_context, gd_end_context,
- 0, /* gd_begin_anchor */ 0, /* gd_end_anchor */
- gd_set_font, gd_textpara,
- gd_set_pencolor, gd_set_fillcolor, gd_set_style,
- gd_ellipse, gd_polygon,
- gd_bezier, gd_polyline,
- 0, /* bezier_has_arrows */
- 0, /* gd_comment */
- 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 *
-**********************************************************/
-
-#define NEWANCHORS 0
-
-#include "render.h"
-#include "htmltable.h"
-
-/* IMAP font modifiers */
-#define REGULAR 0
-#define BOLD 1
-#define ITALIC 2
-
-/* IMAP patterns */
-#define P_SOLID 0
-#define P_NONE 15
-#define P_DOTTED 4 /* i wasn't sure about this */
-#define P_DASHED 11 /* or this */
-
-/* IMAP bold line constant */
-#define WIDTH_NORMAL 1
-#define WIDTH_BOLD 3
-
-/* static int N_pages; */
-/* static point Pages; */
-static int onetime = TRUE;
-static int isLatin1;
-
-typedef struct context_t {
- char color_ix, *fontfam, fontopt, font_was_set;
- char pen, fill, penwidth, style_was_set;
- double fontsz;
-} context_t;
-
-#ifdef OLD
-#define MAXNEST 4
-static context_t cstk[MAXNEST];
-static int SP;
-#endif
-
-static double Dpi;
-static double DevScale;
-static double CompScale;
-static int Rot;
-
-static point Viewport;
-static pointf GraphFocus;
-static double Zoom;
-
-/* isRect:
- * isRect function returns true when polygon has
- * regular rectangular shape. Rectangle is regular when
- * it is not skewed and distorted and orientation is almost zero
- */
-bool isRect(polygon_t * p)
-{
- return (p->sides == 4 && (ROUND(p->orientation) % 90) == 0
- && p->distortion == 0.0 && p->skew == 0.0);
-}
-
-/* pEllipse:
- * pEllipse function returns 'np' points from the circumference
- * of ellipse described by radii 'a' and 'b'.
- * Assumes 'np' is greater than zero.
- * 'np' should be at least 4 to sample polygon from ellipse
- */
-static pointf *pEllipse(double a, double b, int np)
-{
- double theta = 0.0;
- double deltheta = 2 * M_PI / np;
- int i;
- pointf *ps;
-
- ps = N_NEW(np, pointf);
- for (i = 0; i < np; i++) {
- ps[i].x = a * cos(theta);
- ps[i].y = b * sin(theta);
- theta += deltheta;
- }
- return ps;
-}
-
-static pointf mapptf(pointf p)
-{
- pointf rv;
-
- if (Rot == 0) {
- rv.x = (p.x - GraphFocus.x) * CompScale + Viewport.x / 2.;
- rv.y = -(p.y - GraphFocus.y) * CompScale + Viewport.y / 2.;
- } else {
- rv.x = -(p.y - GraphFocus.y) * CompScale + Viewport.x / 2.;
- rv.y = -(p.x - GraphFocus.x) * CompScale + Viewport.y / 2.;
- }
- /* apply clipping */
- if (rv.x < 0)
- rv.x = 0;
- if (rv.x > (double) Viewport.x)
- rv.x = (double) (Viewport.x - 1);
- if (rv.y < 0)
- rv.y = 0;
- if (rv.y > (double) Viewport.y)
- rv.y = (double) (Viewport.y - 1);
- return rv;
-}
-
-/*
- * isFilled function returns 1 if filled style has been set for node 'n'
- * otherwise returns 0. it accepts pointer to node_t as an argument
- */
-static int ifFilled(node_t * n)
-{
- char *style, *p, **pp;
- int r = 0;
- style = late_nnstring(n, N_style, "");
- if (style[0]) {
- pp = parse_style(style);
- while ((p = *pp)) {
- if (strcmp(p, "filled") == 0)
- r = 1;
- pp++;
- }
- }
- return r;
-}
-
-#if ! NEWANCHORS
-static void mapOutput (map_shape_t map_shape, point* pp, int nump,
- char* url, char *target, char *label, char *tooltip)
-{
- int i;
-
- if (Output_lang == IMAP && url && url[0]) {
- switch (map_shape) {
- case MAP_RECTANGLE:
- fprintf(Output_file, "rectangle %s %d %d %d %d\n", url,
- pp[0].x, pp[0].y, pp[1].x, pp[1].y);
- break;
- case MAP_CIRCLE:
- fprintf(Output_file, "circle %s %d %d %d %d\n", url,
- pp[0].x, pp[0].y, (pp[1].x+pp[0].x), pp[0].y);
- break;
- case MAP_POLYGON:
- fprintf(Output_file, "poly %s", url);
- for (i = 0; i < nump; i++)
- fprintf(Output_file, " %d,%d", pp[i].x, pp[i].y);
- fprintf(Output_file, "\n");
- break;
- default:
- assert(0);
- break;
- }
-
- } else if (Output_lang == ISMAP && url && url[0]) {
- switch (map_shape) {
- case MAP_RECTANGLE:
- fprintf(Output_file, "rectangle (%d,%d) (%d,%d) %s %s\n",
- pp[0].x, pp[0].y, pp[1].x, pp[1].y, url, label);
- break;
- default:
- assert(0);
- break;
- }
-
- } else if (Output_lang == CMAP || Output_lang == CMAPX) {
- switch (map_shape) {
- case MAP_RECTANGLE:
- fprintf(Output_file, "<area shape=\"rect\"");
- break;
- case MAP_CIRCLE:
- fprintf(Output_file, "<area shape=\"circle\"");
- break;
- case MAP_POLYGON:
- fprintf(Output_file, "<area shape=\"poly\"");
- break;
- default:
- assert(0);
- break;
- }
- if (url && url[0])
- fprintf(Output_file, " href=\"%s\"", xml_string(url));
- if (target && target[0])
- fprintf(Output_file, " target=\"%s\"", xml_string(target));
- if (tooltip && tooltip[0])
- fprintf(Output_file, " title=\"%s\"", xml_string(tooltip));
- /*
- * alt text is intended for the visually impaired, but such
- * folk are not likely to be clicking around on a graph anyway.
- * IE on the PC platform (but not on Macs) incorrectly
- * uses (non-empty) alt strings instead of title strings for tooltips.
- * To make tooltips work and avoid this IE issue,
- * while still satisfying usability guidelines
- * that require that there is always an alt string,
- * we generate just an empty alt string.
- */
- fprintf(Output_file, " alt=\"\"");
- fprintf(Output_file, " coords=\"");
-
- switch (map_shape) {
- case MAP_RECTANGLE:
- fprintf(Output_file, "%d,%d,%d,%d\"", pp[0].x, pp[0].y, pp[1].x, pp[1].y);
- break;
- case MAP_CIRCLE:
- fprintf(Output_file, "%d,%d,%d\"", pp[0].x, pp[0].y, pp[1].x);
- break;
- case MAP_POLYGON:
- fprintf(Output_file, "%d,%d", pp[0].x, pp[0].y);
- for (i = 1; i < nump; i++)
- fprintf(Output_file, " %d,%d", pp[i].x, pp[i].y);
- fprintf(Output_file, "\"");
- break;
- default:
- break;
- }
- if (Output_lang == CMAPX)
- fprintf(Output_file, " /");
- fprintf(Output_file, ">\n");
-
- } else if (Output_lang == POSTSCRIPT || Output_lang == PDF) {
- if (url && url[0]) {
- fprintf(Output_file, "[ /Rect [ %d %d %d %d ]\n"
- " /Border [ 0 0 0 ]\n"
- " /Action << /Subtype /URI /URI %s >>\n"
- " /Subtype /Link\n"
- "/ANN pdfmark\n",
- pp[0].x, pp[0].y, pp[1].x, pp[1].y, ps_string(url, isLatin1));
- }
- }
-}
-#endif
-
-/*
- * map_output_poly function generates the map file of requested format
- * currently imap, cmapx, ismap, cmap, postscipt and pdf formats are supported
- * This function maps the node.
- */
-static void
-map_output_poly(node_t * n, char *url,
- char *target, char *label, char *tooltip, int sample)
-{
- int sides, peri, nump, i, j, filled = 0, rect = 0, nshape;
- pointf *ppf, *vertices, ldimen;
- point *pp, coord;
- map_shape_t map_shape;
- polygon_t *poly = NULL;
-
- /* checking shape of node */
- nshape = shapeOf(n);
- /* node coordinate */
- coord = ND_coord_i(n);
- /* checking if filled style has been set for node */
- filled = ifFilled(n);
-
- if (nshape == SH_POLY || nshape == SH_POINT) {
- poly = (polygon_t *) ND_shape_info(n);
-
- /* checking if polygon is regular rectangle */
- if (isRect(poly) && (poly->peripheries || filled))
- rect = 1;
- }
-
- /* When node has polygon shape and requested output format is imap or cmapx,
- * we have used shape of polygon to map clickable region that is circle,
- * ellipse, polygon with n side, or point. For regular rectangular shape
- * we have use node's bounding box to map clickable region
- */
- if (poly && !rect && (Output_lang == IMAP || Output_lang == CMAPX)) {
-
- if (poly->sides < 3)
- sides = 1;
- else
- sides = poly->sides;
-
- if (poly->peripheries < 2)
- peri = 1;
- else
- peri = poly->peripheries;
-
- vertices = poly->vertices;
-
- /* use bounding box of text label for mapping
- * when polygon has no peripheries and node is not filled
- */
- if (poly->peripheries == 0 && !filled) {
- map_shape = MAP_RECTANGLE;
- nump = 2;
- ppf = N_NEW(nump, pointf);
- pp = N_NEW(nump, point);
- ldimen = ND_label(n)->dimen;
- ppf[0].x = coord.x - (ldimen.x) / 2.0;
- ppf[0].y = coord.y + (ldimen.y) / 2.0;
- ppf[1].x = coord.x + (ldimen.x) / 2.0;
- ppf[1].y = coord.y - (ldimen.y) / 2.0;
-
- }
- /* circle or ellipse */
- else if (poly->sides < 3 && poly->skew == 0.0
- && poly->distortion == 0.0) {
- if (poly->regular) {
- map_shape = MAP_CIRCLE;
- nump = 2; /* center of circle and radius */
- ppf = N_NEW(nump, pointf);
- pp = N_NEW(nump, point);
- ppf[0].x = coord.x;
- ppf[0].y = coord.y;
- ppf[1].x = vertices[peri - 1].x;
- ppf[1].y = vertices[peri - 1].y;
- }
- else { /* ellipse is treated as polygon */
- double a, b;
- map_shape = MAP_POLYGON;
- a = vertices[peri - 1].x;
- b = vertices[peri - 1].y;
- nump = sample;
- ppf = pEllipse(a, b, nump);
- pp = N_NEW(nump, point);
- for (i = 0; i < nump; i++) {
- ppf[i].x += coord.x;
- ppf[i].y += coord.y;
- }
- }
- /* all other polygonal shape */
- } else {
- int offset = (peri - 1)*(poly->sides);
- map_shape = MAP_POLYGON;
- /* distorted or skewed ellipses and circles are polygons with 120
- * sides. For mapping we convert them into polygon with sample sides
- */
- if (poly->sides == 120) {
- int delta = 120/sample;
- nump = sample;
- ppf = N_NEW(nump, pointf);
- pp = N_NEW(nump, point);
- for (i = 0, j = 0; j < nump; i += delta, j++) {
- ppf[j].x = coord.x + vertices[i + offset].x;
- ppf[j].y = coord.y + vertices[i + offset].y;
- }
- } else {
- nump = sides;
- ppf = N_NEW(nump, pointf);
- pp = N_NEW(nump, point);
- for (i = 0; i < nump; i++) {
- ppf[i].x = coord.x + vertices[i + offset].x;
- ppf[i].y = coord.y + vertices[i + offset].y;
- }
- }
- }
- } else {
- /* we have used node's bounding box to map clickable region
- * when requested output format is neither imap nor cmapx and for all
- * node shapes other than polygon ( except regular rectangle polygon )
- */
- map_shape = MAP_RECTANGLE;
- nump = 2;
- ppf = N_NEW(nump, pointf);
- pp = N_NEW(nump, point);
- ppf[0].x = ND_coord_i(n).x - ND_lw_i(n);
- ppf[0].y = ND_coord_i(n).y + (ND_ht_i(n) / 2);
- ppf[1].x = ND_coord_i(n).x + ND_rw_i(n);
- ppf[1].y = ND_coord_i(n).y - (ND_ht_i(n) / 2);
- }
-
- /* apply scaling and translation if necessary */
- if (Output_lang == ISMAP
- || Output_lang == IMAP
- || Output_lang == CMAP || Output_lang == CMAPX) {
- for (i = 0; i < nump; i++) {
- ppf[i] = mapptf(ppf[i]);
- }
- }
-
- for (i = 0; i < nump; i++) {
- pp[i].x = ROUND(ppf[i].x);
- pp[i].y = ROUND(ppf[i].y);
- }
-
-#if ! NEWANCHORS
- mapOutput (map_shape, pp, nump, url, target, label, tooltip);
-#endif
- free(ppf);
- free(pp);
-}
-
-static void
-map_output_rect(pointf p1, pointf p2, char *url, char *target, char *label,
- char *tooltip)
-{
- pointf ppf1, ppf2;
- point pp[2];
- double t;
-
- if (!(url && url[0]) && !(tooltip && tooltip[0]))
- return;
-
- /* apply scaling and translation if necessary */
- if (Output_lang == ISMAP
- || Output_lang == IMAP
- || Output_lang == CMAP || Output_lang == CMAPX) {
- ppf1 = mapptf(p1);
- ppf2 = mapptf(p2);
- } else {
- ppf1 = p1;
- ppf2 = p2;
- }
- pp[0].x = ROUND(ppf1.x);
- pp[0].y = ROUND(ppf1.y);
- pp[1].x = ROUND(ppf2.x);
- pp[1].y = ROUND(ppf2.y);
-
- /* suppress maps that are totally clipped in either x or y */
- if (pp[0].x == pp[1].x || pp[0].y == pp[1].y)
- return;
-
- /* fix up coordinate order */
- if (pp[1].x < pp[0].x) {
- t = pp[1].x;
- pp[1].x = pp[0].x;
- pp[0].x = t;
- }
- if (pp[1].y < pp[0].y) {
- t = pp[1].y;
- pp[1].y = pp[0].y;
- pp[0].y = t;
- }
-#if ! NEWANCHORS
- mapOutput (MAP_RECTANGLE, pp, 2, url, target, label, tooltip);
-#endif
-}
-
-/* radius of mouse-sensitive region around a point */
-#define FUZZ 3
-static void
-map_output_fuzzy_point(pointf p, char *url, char *target, char *label,
- char *tooltip)
-{
- pointf p1, p2;
-
- p1.x = p.x - FUZZ;
- p1.y = p.y - FUZZ;
- p2.x = p.x + FUZZ;
- p2.y = p.y + FUZZ;
-
- map_output_rect(p1, p2, url, target, label, tooltip);
-}
-
-static void map_reset(void)
-{
- onetime = TRUE;
-}
-
-static void init_imap(void)
-{
-#ifdef OLD
- SP = 0;
- cstk[0].color_ix = 0; /* IMAP color index 0-7 */
- 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 sold */
- cstk[0].fill = P_NONE;
- cstk[0].penwidth = WIDTH_NORMAL;
-#endif
-}
-
-static void doHTMLdata(htmldata_t * dp, point p, void *obj)
-{
- char *url = NULL, *target = NULL, *title = NULL;
- pointf p1, p2;
- int havetitle = 0;
-
- if ((url = dp->href) && url[0]) {
- switch (agobjkind(obj)) {
- case AGGRAPH:
- url = strdup_and_subst_graph(url, (graph_t *) obj);
- break;
- case AGNODE:
- url = strdup_and_subst_node(url, (node_t *) obj);
- break;
- case AGEDGE:
- url = strdup_and_subst_edge(url, (edge_t *) obj);
- break;
- }
- }
- target = dp->target;
- if ((title = dp->title) && title[0]) {
- havetitle++;
- switch (agobjkind(obj)) {
- case AGGRAPH:
- title = strdup_and_subst_graph(title, (graph_t *) obj);
- break;
- case AGNODE:
- title = strdup_and_subst_node(title, (node_t *) obj);
- break;
- case AGEDGE:
- title = strdup_and_subst_edge(title, (edge_t *) obj);
- break;
- }
- }
- if (url || title) {
- p1.x = p.x + dp->box.LL.x;
- p1.y = p.y + dp->box.LL.y;
- p2.x = p.x + dp->box.UR.x;
- p2.y = p.y + dp->box.UR.y;
- map_output_rect(p1, p2, url, target, "", title);
- }
- free(url);
- free(title);
-}
-
-/* forward declaration */
-static void doHTMLcell(htmlcell_t * cp, point p, void *obj);
-
-static void doHTMLtbl(htmltbl_t * tbl, point p, void *obj)
-{
- htmlcell_t **cells = tbl->u.n.cells;
- htmlcell_t *cp;
-
- while ((cp = *cells++))
- doHTMLcell(cp, p, obj);
- if (tbl->data.href)
- doHTMLdata(&tbl->data, p, obj);
-}
-
-static void doHTMLcell(htmlcell_t * cp, point p, void *obj)
-{
- if (cp->child.kind == HTML_TBL)
- doHTMLtbl(cp->child.u.tbl, p, obj);
- if (cp->data.href)
- doHTMLdata(&cp->data, p, obj);
-}
-
-static void doHTMLlabel(htmllabel_t * lbl, point p, void *obj)
-{
- if (lbl->kind == HTML_TBL) {
- doHTMLtbl(lbl->u.tbl, p, obj);
- }
-}
-
-static void
-map_begin_job(FILE * ofp, graph_t * g, char **lib, char *user,
- char *info[], point pages)
-{
- /* Pages = pages; */
- /* N_pages = pages.x * pages.y; */
- if (Output_lang == IMAP) {
- fprintf(Output_file, "base referer\n");
- } else if (Output_lang == ISMAP) {
-/* fprintf(Output_file,"base referer\n"); */
- } else if (Output_lang == CMAP || Output_lang == CMAPX) {
-/* fprintf(Output_file,"base referer\n"); */
- } else if (Output_lang == POSTSCRIPT || Output_lang == PDF) {
-/* fprintf(Output_file,"base referer\n"); */
- }
- isLatin1 = (GD_charset(g) == CHAR_LATIN1);
-}
-
-static void map_begin_graph(GVC_t * gvc, graph_t * g, box bb, point pb)
-{
- Dpi = GD_drawing(g)->dpi;
- if (Dpi < 1.0)
- Dpi = DEFAULT_DPI;
- DevScale = Dpi / POINTS_PER_INCH;
-
- Viewport.x = gvc->job->width;
- Viewport.y = gvc->job->height;
- if (Viewport.x) {
- Zoom = gvc->job->zoom;
- GraphFocus = gvc->job->focus;
- } else {
- Viewport.x =
- (bb.UR.x - bb.LL.x + 2 * GD_drawing(g)->margin.x) * DevScale +
- 2;
- Viewport.y =
- (bb.UR.y - bb.LL.y + 2 * GD_drawing(g)->margin.y) * DevScale +
- 2;
- GraphFocus.x = (GD_bb(g).UR.x - GD_bb(g).LL.x) / 2.;
- GraphFocus.y = (GD_bb(g).UR.y - GD_bb(g).LL.y) / 2.;
- Zoom = 1.0;
- }
- CompScale = Zoom * DevScale;
-
- if (onetime) {
- init_imap();
- onetime = FALSE;
- }
-}
-
-static graph_t *Root_Graph;
-static char *Default_URL;
-
-static void
-map_begin_page(graph_t * g, point page, double scale, int rot,
- point offset)
-{
- char *url, *name;
-
- Rot = rot;
- name = xml_string(g->name);
-
- if (Output_lang == CMAPX) {
- fprintf(Output_file, "<map id=\"%s\" name=\"%s\">\n", name, name);
- }
- Root_Graph = g;
- Default_URL = NULL;
- if (((url = agget(g, "href")) && url[0])
- || ((url = agget(g, "URL")) && url[0])) {
- if (Output_lang == IMAP) {
- fprintf(Output_file, "default %s\n", url);
- } else if (Output_lang == ISMAP) {
- fprintf(Output_file, "default %s %s\n", url, g->name);
- } else if (Output_lang == CMAP || Output_lang == CMAPX) {
- Default_URL = url;
- }
- }
-
-}
-
-static void map_end_page(void)
-{
- pointf Default_p1, Default_p2;
- char *Default_label;
- graph_t *g = Root_Graph;
-
- if (GD_label(g) && GD_label(g)->html)
- doHTMLlabel(GD_label(g)->u.html, GD_label(g)->p, (void *) g);
- if (Default_URL) {
- Default_URL = strdup_and_subst_graph(Default_URL, g);
- Default_label = g->name;
- Default_p1.x = GD_bb(g).LL.x;
- Default_p1.y = GD_bb(g).LL.y;
- Default_p2.x = GD_bb(g).UR.x;
- Default_p2.y = GD_bb(g).UR.y;
- map_output_rect(Default_p1, Default_p2, Default_URL, "",
- Default_label, "");
- free(Default_URL);
- }
- if (Output_lang == CMAPX) {
- fprintf(Output_file, "</map>\n");
- }
-}
-
-void map_begin_cluster(graph_t * g)
-{
- char *s, *url = NULL, *target = NULL, *title = NULL, *tooltip = NULL,
- *m_url = NULL, *m_target = NULL, *m_tooltip = NULL;
- pointf p1, p2;
-
- if (GD_label(g)) {
- if (GD_label(g)->html)
- doHTMLlabel(GD_label(g)->u.html, GD_label(g)->p, (void *) g);
- title = GD_label(g)->text;
- }
- if (((s = agget(g, "href")) && s[0])
- || ((s = agget(g, "URL")) && s[0]))
- m_url = url = strdup_and_subst_graph(s, g);
- if ((s = agget(g, "target")) && s[0])
- m_target = target = strdup_and_subst_graph(s, g);
- if ((s = agget(g, "tooltip")) && s[0])
- m_tooltip = tooltip = strdup_and_subst_graph(s, g);
- else
- tooltip = title;
- if (url || m_tooltip) {
- p1.x = GD_bb(g).LL.x;
- p1.y = GD_bb(g).LL.y;
- p2.x = GD_bb(g).UR.x;
- p2.y = GD_bb(g).UR.y;
- map_output_rect(p1, p2, url, target, title, tooltip);
- }
- free(m_target);
- free(m_tooltip);
- free(m_url);
-}
-
-void map_begin_node(node_t * n)
-{
- char *s, *url = NULL, *target = NULL, *tooltip = NULL,
- *m_url = NULL, *m_target = NULL, *m_tooltip = NULL;
-
- if (ND_label(n)->html)
- doHTMLlabel(ND_label(n)->u.html, ND_coord_i(n), (void *) n);
- if (((s = agget(n, "href")) && s[0]) || ((s = agget(n, "URL")) && s[0]))
- m_url = url = strdup_and_subst_node(s, n);
- if ((s = agget(n, "target")) && s[0])
- m_target = target = strdup_and_subst_node(s, n);
- if ((s = agget(n, "tooltip")) && s[0])
- m_tooltip = tooltip = strdup_and_subst_node(s, n);
- else
- tooltip = ND_label(n)->text;
-
- if (url || m_tooltip) {
- int sample = 0;
- char *p = agget(n, "samplepoints");
- if (p)
- sample = atoi(p);
- /* We want at least 4 points. For server-side maps, at most 100
- * points are allowed. To simplify things to fit with the 120 points
- * used for skewed ellipses, we set the bound at 60.
- */
- if ((sample < 4) || (sample > 60))
- sample = DFLT_SAMPLE;
- map_output_poly(n, url, target, ND_label(n)->text, tooltip, sample);
- }
-
- free(m_url);
- free(m_target);
- free(m_tooltip);
-}
-
-#define HW 2.0 /* maximum distance away from line, in points */
-
-/* check_control_points:
- * check_control_points function checks the size of quadrilateral
- * formed by four control points
- * returns 1 if four points are in line (or close to line)
- * else return 0
- */
-static int check_control_points(pointf *cp)
-{
- double dis1 = ptToLine2 (cp[0], cp[3], cp[1]);
- double dis2 = ptToLine2 (cp[0], cp[3], cp[2]);
- if (dis1 < HW*HW && dis2 < HW*HW)
- return 1;
- else
- return 0;
-}
-
-#ifdef DEBUG
-void psmapOutput (point* ps, int n)
-{
- int i;
- fprintf (stdout, "newpath %d %d moveto\n", ps[0].x, ps[0].y);
- for (i=1; i < n; i++)
- fprintf (stdout, "%d %d lineto\n", ps[i].x, ps[i].y);
- fprintf (stdout, "closepath stroke\n");
-}
-#endif
-
-typedef struct segitem_s {
- pointf p;
- struct segitem_s* next;
-} segitem_t;
-
-#define MARK_FIRST_SEG(L) ((L)->next = (segitem_t*)1)
-#define FIRST_SEG(L) ((L)->next == (segitem_t*)1)
-#define INIT_SEG(P,L) {(L)->next = 0; (L)->p = P;}
-
-static segitem_t*
-appendSeg (pointf p, segitem_t* lp)
-{
- segitem_t* s = GNEW(segitem_t);
- INIT_SEG (p, s);
- lp->next = s;
- return s;
-}
-
-/* map_bspline_poly:
- * Output the polygon determined by the n points in p1, followed
- * by the n points in p2 in reverse order. Assumes n <= 50.
- *
- * Note: when using psmapOutput for debugging, remove the calls to
- * mapptf, since for postscript the necessary parameters aren't defined and
- * mapptf always returns (0,0).
- */
-static void
-map_bspline_poly(int n, pointf* p1, pointf* p2,
- char *url, char* target, char* tooltip)
-{
- int i;
- pointf ppf;
- point rp[100];
- int last = 2*n-1;
-
- for (i = 0; i < n; i++) {
- ppf = mapptf(p1[i]);
- rp[i].x = ROUND(ppf.x);
- rp[i].y = ROUND(ppf.y);
- }
- for (i = 0; i < n; i++) {
- ppf = mapptf(p2[i]);
- rp[last-i].x = ROUND(ppf.x);
- rp[last-i].y = ROUND(ppf.y);
- }
- mapOutput (MAP_POLYGON, rp, last+1, url, target, NULL, tooltip);
-#ifdef DEBUG
- psmapOutput (rp, last+1);
-#endif
-}
-/* approx_bezier:
- * Approximate Bezier by line segments. If the four points are
- * almost colinear, as determined by check_control_points, we store
- * the segment cp[0]-cp[3]. Otherwise we split the Bezier into 2
- * and recurse.
- * Since 2 contiguous segments share an endpoint, we actually store
- * the segments as a list of points.
- * New points are appended to the list given by lp. The tail of the
- * list is returned.
- */
-static segitem_t*
-approx_bezier (pointf *cp, segitem_t* lp)
-{
- pointf sub_curves[8];
-
- if (check_control_points(cp)) {
- if (FIRST_SEG (lp)) INIT_SEG (cp[0], lp);
- lp = appendSeg (cp[3], lp);
- }
- else {
- Bezier (cp, 3, 0.5, sub_curves, sub_curves+4);
- lp = approx_bezier (sub_curves, lp);
- lp = approx_bezier (sub_curves+4, lp);
- }
- return lp;
-}
-
-/* bisect:
- * Return the angle of the bisector between the two rays
- * pp-cp and cp-np. The bisector returned is always to the
- * left of pp-cp-np.
- */
-static double
-bisect (pointf pp, pointf cp, pointf np)
-{
- double ang, theta, phi;
- theta = atan2(np.y - cp.y,np.x - cp.x);
- phi = atan2(pp.y - cp.y,pp.x - cp.x);
- ang = theta - phi;
- if (ang > 0) ang -= 2*M_PI;
-
- return (phi + ang/2.0);
-}
-
-/* mkSegPts:
- * Determine polygon points related to 2 segments prv-cur and cur-nxt.
- * The points lie on the bisector of the 2 segments, passing through cur,
- * and distance HW from cur. The points are stored in p1 and p2.
- * If p1 is NULL, we use the normal to cur-nxt.
- * If p2 is NULL, we use the normal to prv-cur.
- * Assume at least one of prv or nxt is non-NULL.
- */
-static void
-mkSegPts (segitem_t* prv, segitem_t* cur, segitem_t* nxt,
- pointf* p1, pointf* p2)
-{
- pointf cp, pp, np;
- double theta, delx, dely;
- pointf p;
-
- cp = cur->p;
- /* if prv or nxt are NULL, use the one given to create a collinear
- * prv or nxt. This could be more efficiently done with special case code,
- * but this way is more uniform.
- */
- if (prv) {
- pp = prv->p;
- if (nxt)
- np = nxt->p;
- else {
- np.x = 2*cp.x - pp.x;
- np.y = 2*cp.y - pp.y;
- }
- }
- else {
- np = nxt->p;
- pp.x = 2*cp.x - np.x;
- pp.y = 2*cp.y - np.y;
- }
- theta = bisect(pp,cp,np);
- delx = HW*cos(theta);
- dely = HW*sin(theta);
- p.x = cp.x + delx;
- p.y = cp.y + dely;
- *p1 = p;
- p.x = cp.x - delx;
- p.y = cp.y - dely;
- *p2 = p;
-}
-
-/* map_output_bspline:
- * Construct and output a closed polygon approximating the input
- * B-spline bp. We do this by first approximating bp by a sequence
- * of line segments. We then use the sequence of segments to determine
- * the polygon.
- * In cmapx, polygons are limited to 100 points, so we output polygons
- * in chunks of 100.
- */
-static void
-map_output_bspline (bezier* bp, char *url, char *target, char *tooltip)
-{
- segitem_t* segl = GNEW(segitem_t);
- segitem_t* segp = segl;
- segitem_t* segprev;
- segitem_t* segnext;
- int nc, j, k, cnt;
- pointf pts[4];
- pointf pt1[50], pt2[50];
-
- MARK_FIRST_SEG(segl);
- nc = (bp->size - 1)/3; /* nc is number of bezier curves */
- for (j = 0; j < nc; j++) {
- for (k = 0; k < 4; k++) {
- pts[k].x = (double)bp->list[3*j + k].x;
- pts[k].y = (double)bp->list[3*j + k].y;
- }
- segp = approx_bezier (pts, segp);
- }
-
- segp = segl;
- segprev = 0;
- cnt = 0;
- while (segp) {
- segnext = segp->next;
- mkSegPts (segprev, segp, segnext, pt1+cnt, pt2+cnt);
- cnt++;
- if ((segnext == NULL) || (cnt == 50)) {
- map_bspline_poly (cnt, pt1, pt2, url, target, tooltip);
- pt1[0] = pt1[cnt-1];
- pt2[0] = pt2[cnt-1];
- cnt = 1;
- }
- segprev = segp;
- segp = segnext;
- }
-
- /* free segl */
- while (segl) {
- segp = segl->next;
- free (segl);
- segl = segp;
- }
-}
-
-void map_begin_edge(edge_t* e)
-{
- /* strings */
- char *s, *label, *taillabel, *headlabel, *url, *tailurl, *headurl,
- *target, *tailtarget, *headtarget, *tooltip, *tailtooltip, *headtooltip,
- *m_url = NULL, *m_tailurl = NULL, *m_headurl = NULL,
- *m_target = NULL, *m_tailtarget = NULL, *m_headtarget = NULL,
- *m_tooltip = NULL, *m_tailtooltip = NULL, *m_headtooltip = NULL;
-
- textlabel_t *lab = NULL, *tlab = NULL, *hlab = NULL;
- pointf p, p1, p2;
- bezier bz;
-
- /* establish correct text for main edge label, URL, tooltip */
- label = NULL;
- if ((lab = ED_label(e))) {
- if (lab->html)
- doHTMLlabel(lab->u.html, lab->p, (void *) e);
- label = lab->text;
- }
- url = NULL;
- if (((s = agget(e, "href")) && s[0]) || ((s = agget(e, "URL")) && s[0]))
- m_url = url = strdup_and_subst_edge(s, e);
- tooltip = label;
- if ((s = agget(e, "tooltip")) && s[0])
- m_tooltip = tooltip = strdup_and_subst_edge(s, e);
- target = NULL;
- if ((s = agget(e, "target")) && s[0])
- m_target = target = strdup_and_subst_edge(s, e);
-
- /* establish correct text for tail label, URL, tooltip */
- taillabel = label;
- if ((tlab = ED_tail_label(e))) {
- if (tlab->html)
- doHTMLlabel(tlab->u.html, tlab->p, (void *) e);
- taillabel = tlab->text;
- }
- tailurl = url;
- if (((s = agget(e, "tailhref")) && s[0]) || ((s = agget(e, "tailURL")) && s[0]))
- m_tailurl = tailurl = strdup_and_subst_edge(s, e);
- tailtooltip = taillabel;
- if ((s = agget(e, "tailtooltip")) && s[0])
- m_tailtooltip = tailtooltip = strdup_and_subst_edge(s, e);
- tailtarget = target;
- if ((s = agget(e, "tailtarget")) && s[0])
- m_tailtarget = tailtarget = strdup_and_subst_edge(s, e);
-
- /* establish correct text for head label, URL, tooltip */
- headlabel = label;
- if ((hlab = ED_head_label(e))) {
- if (hlab->html)
- doHTMLlabel(hlab->u.html, hlab->p, (void *) e);
- headlabel = hlab->text;
- }
- headurl = url;
- if (((s = agget(e, "headhref")) && s[0]) || ((s = agget(e, "headURL")) && s[0]))
- m_headurl = headurl = strdup_and_subst_edge(s, e);
- headtooltip = headlabel;
- if ((s = agget(e, "headtooltip")) && s[0])
- m_headtooltip = headtooltip = strdup_and_subst_edge(s, e);
- headtarget = target;
- if ((s = agget(e, "headtarget")) && s[0])
- m_headtarget = headtarget = strdup_and_subst_edge(s, e);
-
- /* strings are now set - next we map the three labels */
-
- if (lab && (url || m_tooltip)) {
- /* map a rectangle around the edge label */
- p1.x = lab->p.x - lab->dimen.x / 2;
- p1.y = lab->p.y - lab->dimen.y / 2;
- p2.x = lab->p.x + lab->dimen.x / 2;
- p2.y = lab->p.y + lab->dimen.y / 2;
- map_output_rect(p1, p2, url, target, label, tooltip);
- }
-
- if (tlab && (tailurl || m_tailtooltip)) {
- /* map a rectangle around the edge taillabel */
- p1.x = tlab->p.x - tlab->dimen.x / 2;
- p1.y = tlab->p.y - tlab->dimen.y / 2;
- p2.x = tlab->p.x + tlab->dimen.x / 2;
- p2.y = tlab->p.y + tlab->dimen.y / 2;
- map_output_rect(p1, p2, tailurl, tailtarget, taillabel, tailtooltip);
- }
-
- if (hlab && (headurl || m_headtooltip)) {
- /* map a rectangle around the edge headlabel */
- p1.x = hlab->p.x - hlab->dimen.x / 2;
- p1.y = hlab->p.y - hlab->dimen.y / 2;
- p2.x = hlab->p.x + hlab->dimen.x / 2;
- p2.y = hlab->p.y + hlab->dimen.y / 2;
- map_output_rect(p1, p2, headurl, headtarget, headlabel, headtooltip);
- }
-
- /* finally we map the two ends of the edge where they touch nodes */
-
- /* process intersecion with tail node */
- if (ED_spl(e) && (tailurl || m_tailtooltip)) {
- bz = ED_spl(e)->list[0];
- if (bz.sflag) {
- /* Arrow at start of splines */
- p.x = bz.sp.x;
- p.y = bz.sp.y;
- } else {
- /* No arrow at start of splines */
- p.x = bz.list[0].x;
- p.y = bz.list[0].y;
- }
- map_output_fuzzy_point(p, tailurl, tailtarget, taillabel, tailtooltip);
- }
-
- /* process intersection with head node */
- if (ED_spl(e) && (headurl || m_headtooltip)) {
- bz = ED_spl(e)->list[ED_spl(e)->size - 1];
- if (bz.eflag) {
- /* Arrow at end of splines */
- p.x = bz.ep.x;
- p.y = bz.ep.y;
- } else {
- /* No arrow at end of splines */
- p.x = bz.list[bz.size - 1].x;
- p.y = bz.list[bz.size - 1].y;
- }
- map_output_fuzzy_point(p, headurl, headtarget, headlabel, headtooltip);
- }
-
- /* map the edge (spline) for format IMAP or CMAPX */
-
- if (ED_spl(e) && (url || m_tooltip) && (Output_lang == IMAP || Output_lang == CMAPX))
- {
- int i, ns;
- splines *spl;
-
- spl = ED_spl(e);
- ns = spl->size; /* number of splines */
- for (i = 0; i < ns; i++) {
- map_output_bspline (spl->list+i, url, target, tooltip);
- }
- }
-
- free(m_url);
- free(m_target);
- free(m_tailtarget);
- free(m_headtarget);
- free(m_tailurl);
- free(m_headurl);
- free(m_tooltip);
- free(m_tailtooltip);
- free(m_headtooltip);
-}
-
-static void map_begin_anchor(char *href, char *tooltip, char *target)
-{
-#if NEWANCHORS
- fprintf(Output_file, "<area");
- if (href && href[0])
- fprintf(Output_file, " href=\"%s\"", xml_string(href));
- if (tooltip && tooltip[0])
- fprintf(Output_file, " title=\"%s\"", xml_string(tooltip));
- if (target && target[0])
- fprintf(Output_file, " target=\"%s\"", xml_string(target));
-#endif
-}
-
-static void map_end_anchor(void)
-{
-#if NEWANCHORS
- fprintf(Output_file, "/>\n");
-#endif
-}
-
-static void map_polygon(point * A, int n, int filled)
-{
-#if NEWANCHORS
- int i;
- point p;
-
- fprintf(Output_file, " shape=\"polygon\" coords=\"");
- for (i = 0; i < n; i++) {
- p = mappt(A[i]);
- fprintf(Output_file, "%d,%d,", p.x, p.y);
- }
- p = mappt(A[0]);
- fprintf(Output_file, "%d,%d\"", p.x, p.y);
-#endif
-}
-
-static void map_ellipse(point p, int rx, int ry, int filled)
-{
-#if NEWANCHORS
- fprintf(Output_file, " shape=\"ellipse\"");
-#endif
-}
-
-
-codegen_t MAP_CodeGen = {
- map_reset,
- map_begin_job, 0, /* map_end_job */
- map_begin_graph, 0, /* map_end_graph */
- map_begin_page, map_end_page,
- 0, /* map_begin_layer */ 0, /* map_end_layer */
- map_begin_cluster, 0, /* map_end_cluster */
- 0, /* map_begin_nodes */ 0, /* map_end_nodes */
- 0, /* map_begin_edges */ 0, /* map_end_edges */
- map_begin_node, 0, /* map_end_node */
- map_begin_edge, 0, /* map_end_edge */
- 0, /* map_begin_context */ 0, /* map_end_context */
- map_begin_anchor, map_end_anchor,
- 0, /* map_set_font */ 0, /* map_textpara */
- 0, /* map_set_color */ 0, /* map_set_color */ 0, /* map_set_style */
- map_ellipse, map_polygon,
- 0, /* map_bezier */ 0, /* map_polyline */
- 0, /* bezier_has_arrows */
- 0, /* map_comment */
- 0 /* map_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 *
-**********************************************************/
-
-/*
- J$: added `pdfmark' URL embedding. PostScript rendered from
- dot files with URL attributes will get active PDF links
- from Adobe's Distiller.
- */
-#define PDFMAX 14400 /* Maximum size of PDF page */
-
-#include "render.h"
-#include "agxbuf.h"
-#include <sys/types.h>
-#include <sys/stat.h>
-#if HAVE_SYS_MMAN_H
-#include <sys/mman.h>
-#endif
-
-#ifdef HAVE_LIBGD
-#include "gd.h"
-#endif
-
-#ifndef MSWIN32
-#include <unistd.h>
-#endif
-
-extern void epsf_define(FILE * of);
-extern void epsf_emit_body(usershape_t *us, FILE *of);
-extern char *ps_string(char *ins, int latin);
-
-static int N_pages, Cur_page;
-/* static point Pages; */
-static box PB;
-static box DBB;
-static int onetime = TRUE;
-static int isLatin1;
-static char setupLatin1;
-static node_t *Curnode; /* for user shapes */
-
-static char *Fill = "fill\n";
-static char *Stroke = "stroke\n";
-static char *Newpath_Moveto = "newpath %d %d moveto\n";
-static char **U_lib;
-
-typedef struct grcontext_t {
- char *pencolor, *fillcolor, *font;
- char invis;
- double size;
-} grcontext_t;
-
-#define STACKSIZE 8
-static grcontext_t S[STACKSIZE];
-static int SP = 0;
-
-static void ps_reset(void)
-{
- onetime = TRUE;
-}
-
-static void
-ps_begin_job(FILE * ofp, graph_t * g, char **lib, char *user, char *info[],
- point pages)
-{
- /* Pages = pages; */
- U_lib = lib;
- /* wrong when drawing more than one than one graph - use (atend) */
- N_pages = pages.x * pages.y;
- if (onetime) {
- Cur_page = 0;
- fprintf(Output_file, "%%!PS-Adobe-2.0\n");
- fprintf(Output_file, "%%%%Creator: %s version %s (%s)\n",
- info[0], info[1], info[2]);
- fprintf(Output_file, "%%%%For: %s\n", user);
- fprintf(Output_file, "%%%%Title: %s\n", g->name);
- fprintf(Output_file, "%%%%Pages: (atend)\n");
- if (Show_boxes == NULL)
- fprintf(Output_file, "%%%%BoundingBox: (atend)\n");
- fprintf(Output_file, "%%%%EndComments\nsave\n");
- }
-
- /* remainder is emitted by first begin_graph */
-}
-
-static void ps_end_job(void)
-{
- fprintf(Output_file, "%%%%Trailer\n");
- fprintf(Output_file, "%%%%Pages: %d\n", Cur_page);
- if (Show_boxes == NULL)
- fprintf(Output_file, "%%%%BoundingBox: %d %d %d %d\n",
- DBB.LL.x, DBB.LL.y, DBB.UR.x, DBB.UR.y);
- fprintf(Output_file, "end\nrestore\n");
- fprintf(Output_file, "%%%%EOF\n");
- setupLatin1 = FALSE;
-}
-
-static void ps_comment(char *str)
-{
- fprintf(Output_file, "%% %s\n", str);
-}
-
-static void ps_begin_graph(GVC_t * gvc, graph_t * g, box bb, point pb)
-{
- char *s;
-
- PB = bb;
- if (onetime) {
- cat_preamble(gvc->job, U_lib);
- epsf_define(Output_file);
- if (Show_boxes) {
- char* args[2];
- args[0] = Show_boxes[0];
- args[1] = NULL;
- cat_libfile(Output_file, NULL, args);
- }
-
- /* Set base URL for relative links (for Distiller >= 3.0) */
- if (((s = agget(g, "href")) && s[0])
- || ((s = agget(g, "URL")) && s[0])) {
- fprintf(Output_file,
- "[ {Catalog} << /URI << /Base (%s) >> >>\n"
- "/PUT pdfmark\n", s);
- }
- }
-
- isLatin1 = (GD_charset(g) == CHAR_LATIN1);
- if (isLatin1 && !setupLatin1) {
- fprintf(Output_file, "setupLatin1\n"); /* as defined in ps header */
- setupLatin1 = TRUE;
- }
-}
-
-static void ps_end_graph(void)
-{
- onetime = FALSE;
-}
-
-static void
-ps_begin_page(graph_t * g, point page, double scale, int rot, point offset)
-{
- point sz;
- box pbr;
-
- sz = sub_points(PB.UR,PB.LL);
- Cur_page++;
- fprintf(Output_file, "%%%%Page: %d %d\n", Cur_page, Cur_page);
-
- if (rot) {
- pbr.LL.x = PB.LL.y;
- pbr.LL.y = PB.LL.x;
- pbr.UR.x = PB.UR.y;
- pbr.UR.y = PB.UR.x;
- }
- else {
- pbr = PB;
- }
- if (onetime) {
- DBB = pbr;
- }
- else {
- EXPANDBB(DBB, pbr);
- }
- if (Show_boxes == NULL) {
- fprintf(Output_file, "%%%%PageBoundingBox: %d %d %d %d\n",
- pbr.LL.x, pbr.LL.y, pbr.UR.x, pbr.UR.y);
- }
-
- fprintf(Output_file, "%%%%PageOrientation: %s\n",
- (rot ? "Landscape" : "Portrait"));
- if (Output_lang == PDF)
- fprintf(Output_file, "<< /PageSize [%d %d] >> setpagedevice\n",
- pbr.UR.x, pbr.UR.y);
- if (Show_boxes == NULL) {
- fprintf(Output_file, "gsave\n%d %d %d %d boxprim clip newpath\n",
- pbr.LL.x, pbr.LL.y, pbr.UR.x, pbr.UR.y);
- }
- fprintf(Output_file, "%d %d translate\n", PB.LL.x, PB.LL.y);
- if (rot)
- fprintf(Output_file, "gsave %d %d translate %d rotate\n",
- PB.UR.x - PB.LL.x, 0, rot);
- fprintf(Output_file, "%d %d %d beginpage\n", page.x, page.y, N_pages);
- if (rot)
- fprintf(Output_file, "grestore\n");
- fprintf(Output_file, "%g %g set_scale\n", scale, scale);
- fprintf(Output_file, "%d %d translate %d rotate\n",
- offset.x, offset.y, rot);
- assert(SP == 0);
- S[SP].font = S[SP].pencolor = S[SP].fillcolor = "";
- S[SP].size = 0.0;
-
- /* Define the size of the PS canvas */
- if (Output_lang == PDF) {
- if (sz.x > PDFMAX || sz.y > PDFMAX)
- agerr(AGWARN,
- "canvas size (%d,%d) exceeds PDF limit (%d)\n"
- "\t(suggest setting a bounding box size, see dot(1))\n",
- sz.x, sz.y, PDFMAX);
- fprintf(Output_file, "[ /CropBox [%d %d %d %d] /PAGE pdfmark\n",
- pbr.LL.x, pbr.LL.y, pbr.UR.x, pbr.UR.y);
- }
-}
-
-static void ps_end_page(void)
-{
- if (Show_boxes)
- cat_libfile(Output_file, NULL, Show_boxes+1);
-
- /* the showpage is really a no-op, but at least one PS processor
- * out there needs to see this literal token. endpage does the real work.
- */
- fprintf(Output_file, "endpage\nshowpage\ngrestore\n");
- fprintf(Output_file, "%%%%PageTrailer\n");
- fprintf(Output_file, "%%%%EndPage: %d\n", Cur_page);
- assert(SP == 0);
-}
-
-static void ps_begin_layer(char *s, int n, int Nlayers)
-{
- fprintf(Output_file, "%d %d setlayer\n", n, Nlayers);
-}
-
-static void ps_begin_cluster(graph_t * g)
-{
- fprintf(Output_file, "%% %s\n", g->name);
-
- /* Embed information for Distiller to generate hyperlinked PDF */
- map_begin_cluster(g);
-}
-
-static void ps_begin_node(node_t * n)
-{
- Curnode = n;
-
- /* Embed information for Distiller to generate hyperlinked PDF */
- map_begin_node(n);
-}
-
-static void ps_begin_edge(edge_t * e)
-{
- /* Embed information for Distiller, so it can generate hyperactive PDF */
- map_begin_edge(e);
-}
-
-
-static void ps_begin_context(void)
-{
- fprintf(Output_file, "gsave 10 dict begin\n");
- if (SP == STACKSIZE - 1)
- agerr(AGWARN, "psgen stk ovfl\n");
- else {
- SP++;
- S[SP] = S[SP - 1];
- }
-}
-
-static void ps_end_context(void)
-{
- if (SP == 0)
- agerr(AGWARN, "psgen stk undfl\n");
- else
- SP--;
- fprintf(Output_file, "end grestore\n");
-}
-
-static void ps_set_font(char *name, double size)
-{
- if (strcmp(S[SP].font, name) || (size != S[SP].size)) {
- fprintf(Output_file, "%.2f /%s set_font\n", size, name);
- S[SP].font = name;
- S[SP].size = size;
- }
-}
-
-static void ps_set_color(char *name)
-{
- static char *op[] = { "graph", "node", "edge", "sethsb" };
- gvcolor_t color;
-
- colorxlate(name, &color, HSV_DOUBLE);
- fprintf(Output_file, "%.3f %.3f %.3f %scolor\n",
- color.u.HSV[0], color.u.HSV[1], color.u.HSV[2], op[Obj]);
-}
-
-static void ps_set_pencolor(char *name)
-{
- if (streq(name, "transparent"))
- S[SP].pencolor = "";
- else if (strcmp(name, S[SP].pencolor)) {
- ps_set_color(name); /* change pen color immediately */
- S[SP].pencolor = name;
- }
-}
-
-static void ps_set_fillcolor(char *name)
-{
- /* defer changes to fill color to shape */
- if (streq(name, "transparent"))
- S[SP].fillcolor = "";
- else
- S[SP].fillcolor = name;
-}
-
-static void ps_set_style(char **s)
-{
- char *line, *p;
-
- while ((p = line = *s++)) {
- while (*p)
- p++;
- p++;
- while (*p) {
- fprintf(Output_file, "%s ", p);
- while (*p)
- p++;
- p++;
- }
- if (streq(line, "invis"))
- S[SP].invis = TRUE;
- fprintf(Output_file, "%s\n", line);
- }
-}
-
-static void ps_textpara(point p, textpara_t * para)
-{
- double adj;
-
- if (S[SP].invis)
- return;
- if (*S[SP].pencolor == '\0')
- return;
- if (para->xshow) {
- switch (para->just) {
- case 'l':
- break;
- case 'r':
- p.x -= para->width;
- break;
- default:
- case 'n':
- p.x -= para->width / 2;
- break;
- }
- fprintf(Output_file, "%d %d moveto\n%s\n[%s]\nxshow\n",
- p.x, p.y, ps_string(para->str,isLatin1), para->xshow);
- } else {
- switch (para->just) {
- case 'l':
- adj = 0.0;
- break;
- case 'r':
- adj = -1.0;
- break;
- default:
- case 'n':
- adj = -0.5;
- break;
- }
- fprintf(Output_file, "%d %d moveto %.1f %.1f %s alignedtext\n",
- p.x, p.y, para->width, adj, ps_string(para->str,isLatin1));
- }
-}
-
-static void
-ps_bezier(point * A, int n, int arrow_at_start, int arrow_at_end, int filled)
-{
- int j;
- if (S[SP].invis)
- return;
- if (filled && *S[SP].fillcolor) {
- ps_set_color(S[SP].fillcolor);
- fprintf(Output_file, Newpath_Moveto, A[0].x, A[0].y);
- for (j = 1; j < n; j += 3)
- fprintf(Output_file, "%d %d %d %d %d %d curveto\n",
- A[j].x, A[j].y, A[j + 1].x, A[j + 1].y, A[j + 2].x,
- A[j + 2].y);
- fprintf(Output_file, "closepath\n");
- fprintf(Output_file, Fill);
- if (*S[SP].pencolor)
- ps_set_color(S[SP].pencolor);
- }
- if (*S[SP].pencolor == '\0')
- return;
- if (arrow_at_start || arrow_at_end)
- agerr(AGERR, "ps_bezier illegal arrow args\n");
- fprintf(Output_file, Newpath_Moveto, A[0].x, A[0].y);
- for (j = 1; j < n; j += 3)
- fprintf(Output_file, "%d %d %d %d %d %d curveto\n",
- A[j].x, A[j].y, A[j + 1].x, A[j + 1].y, A[j + 2].x,
- A[j + 2].y);
- fprintf(Output_file, Stroke);
-}
-
-static void ps_polygon(point * A, int n, int filled)
-{
- int j;
-
- if (S[SP].invis)
- return;
- if (filled && *S[SP].fillcolor) {
- ps_set_color(S[SP].fillcolor);
- fprintf(Output_file, Newpath_Moveto, A[0].x, A[0].y);
- for (j = 1; j < n; j++)
- fprintf(Output_file, "%d %d lineto\n", A[j].x, A[j].y);
- fprintf(Output_file, "closepath\n");
- fprintf(Output_file, Fill);
- if (*S[SP].pencolor)
- ps_set_color(S[SP].pencolor);
- }
- if (*S[SP].pencolor == '\0')
- return;
- fprintf(Output_file, Newpath_Moveto, A[0].x, A[0].y);
- for (j = 1; j < n; j++)
- fprintf(Output_file, "%d %d lineto\n", A[j].x, A[j].y);
- fprintf(Output_file, "closepath\n");
- fprintf(Output_file, Stroke);
-}
-
-static void ps_ellipse(point p, int rx, int ry, int filled)
-{
- if (S[SP].invis)
- return;
- if (filled && *S[SP].fillcolor) {
- ps_set_color(S[SP].fillcolor);
- fprintf(Output_file, "%d %d %d %d ellipse_path\n", p.x, p.y, rx,
- ry);
- fprintf(Output_file, Fill);
- if (*S[SP].pencolor)
- ps_set_color(S[SP].pencolor);
- }
- if (*S[SP].pencolor == '\0')
- return;
- if (!filled || (filled && strcmp(S[SP].fillcolor, S[SP].pencolor))) {
- fprintf(Output_file, "%d %d %d %d ellipse_path\n", p.x, p.y, rx,
- ry);
- fprintf(Output_file, Stroke);
- }
-}
-
-static void ps_polyline(point * A, int n)
-{
- int j;
-
- if (S[SP].invis)
- return;
- if (*S[SP].pencolor == '\0')
- return;
- fprintf(Output_file, Newpath_Moveto, A[0].x, A[0].y);
- for (j = 1; j < n; j++)
- fprintf(Output_file, "%d %d lineto\n", A[j].x, A[j].y);
- fprintf(Output_file, Stroke);
-}
-
-#ifdef HAVE_LIBGD
-static void writePSBitmap (gdImagePtr im, boxf bb)
-{
- int X = im->sx, Y = im->sy, x, y, px;
-
- fprintf(Output_file, "gsave\n");
-
- /* this sets the position of the image */
- fprintf(Output_file, "%g %g translate %% lower-left coordinate\n", bb.LL.x, bb.LL.y);
-
- /* this sets the rendered size to fit the box */
- fprintf(Output_file,"%g %g scale\n", bb.UR.x - bb.LL.x, bb.UR.y - bb.LL.y);
-
- /* xsize ysize bits-per-sample [matrix] */
- fprintf(Output_file, "%d %d 8 [%d 0 0 %d 0 %d]\n", X, Y, X, -Y, Y);
-
- fprintf(Output_file, "{<\n");
- if (im->trueColor) {
- for (y = 0; y < Y; y++) {
- for (x = 0; x < X; x++) {
- px = gdImageTrueColorPixel(im, x, y);
- fprintf(Output_file, "%02x%02x%02x",
- gdTrueColorGetRed(px),
- gdTrueColorGetGreen(px),
- gdTrueColorGetBlue(px));
- }
- fprintf(Output_file, "\n");
- }
- }
- else {
- for (y = 0; y < Y; y++) {
- for (x = 0; x < X; x++) {
- px = gdImagePalettePixel(im, x, y);
- fprintf(Output_file, "%02x%02x%02x",
- im->red[px],
- im->green[px],
- im->blue[px]);
- }
- fprintf(Output_file, "\n");
- }
- }
-
- fprintf(Output_file, ">}\n");
- fprintf(Output_file, "false 3 colorimage\n");
-
- fprintf(Output_file, "grestore\n");
-}
-#endif
-
-static void ps_freeimage_gd (usershape_t *us)
-{
-#ifdef HAVE_LIBGD
- gdImageDestroy((gdImagePtr)us->data);
-#endif
-}
-
-static void ps_freeimage_ps (usershape_t *us)
-{
-#if HAVE_SYS_MMAN_H
- munmap(us->data, us->datasize);
-#else
- free(us->data);
-#endif
-}
-
-/* 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
- * current node's shapefile attribute.
- * Else we see if name is a user-defined postscript function
- * Else we assume name is the name of the image. This occurs when
- * the image is part of an html label.
- */
-static void ps_usershape(usershape_t *us, boxf b, point *A, int n, bool filled)
-{
- int j;
- shape_desc *shape;
-
- assert(us);
- assert(us->name);
-
- if (S[SP].invis)
- return;
-
- if (!us->f) {
- if ((shape = (shape_desc*)us->data)) { /* if we found the shape in a library then us->data was set */
- 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 < 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 false %s\n", n, us->name);
- }
- return;
- }
-
- if (us->data) {
- if ((us->datafree != ps_freeimage_gd)
- && (us->datafree != ps_freeimage_ps)) {
- us->datafree(us); /* free incompatible cache data */
- us->data = NULL;
- us->datafree = NULL;
- us->datasize = 0;
- }
- }
-
- if (! us->data) { /* read file into cache */
- int fd = fileno(us->f);
- struct stat statbuf;
-
- fseek(us->f, 0, SEEK_SET);
- switch (us->type) {
-#ifdef HAVE_LIBGD
-#ifdef HAVE_GD_PNG
- case FT_PNG:
- us->data = (void*)gdImageCreateFromPng(us->f);
- us->datafree = ps_freeimage_gd;
- break;
-#endif
-#ifdef HAVE_GD_GIF
- case FT_GIF:
- us->data = (void*)gdImageCreateFromGif(us->f);
- us->datafree = ps_freeimage_gd;
- break;
-#endif
-#ifdef HAVE_GD_JPEG
- case FT_JPEG:
- us->data = (void*)gdImageCreateFromJpeg(us->f);
- us->datafree = ps_freeimage_gd;
- break;
-#endif
-#endif
- case FT_PS:
- case FT_EPS:
- fstat(fd, &statbuf);
- us->datasize = statbuf.st_size;
-#if HAVE_SYS_MMAN_H
- us->data = mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
-#else
- us->data = malloc(statbuf.st_size);
- read(fd, us->data, statbuf.st_size);
-#endif
- us->datafree = ps_freeimage_ps;
- us->must_inline = true;
- break;
- default:
- break;
- }
- }
-
- if (us->data) {
- boxf bb;
- pointf p;
- double pw, ph, tw, th;
- double scalex, scaley;
- int i;
-
- /* compute bb of polygon */
- P2PF(A[0],p);
- bb.LL = bb.UR = p;
- for (i = 1; i < n; i++) {
- P2PF(A[i],p);
- EXPANDBP(bb, p);
- }
- 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;
- b.UR.x -= (pw - tw) / 2.0;
- }
- if (th < ph) {
- b.LL.y += (ph - th) / 2.0;
- b.UR.y -= (ph - th) / 2.0;
- }
- switch (us->type) {
-#ifdef HAVE_LIBGD
- case FT_PNG:
- case FT_GIF:
- case FT_JPEG:
- writePSBitmap ((gdImagePtr)us->data, bb);
- break;
-#endif
- case FT_PS:
- case FT_EPS:
- ps_begin_context();
- fprintf(Output_file, "%d %d translate newpath\n",
- ROUND(bb.LL.x - us->x), ROUND(bb.LL.y - us->y));
- if (us->must_inline)
- epsf_emit_body(us, Output_file);
- else
- fprintf(Output_file, "user_shape_%d\n", us->macro_id);
- ps_end_context();
- break;
- default:
- agerr(AGERR, "usershape %s is not supported in PostScript output\n", us->name);
- break;
- }
- }
-}
-
-codegen_t PS_CodeGen = {
- ps_reset,
- ps_begin_job, ps_end_job,
- ps_begin_graph, ps_end_graph,
- ps_begin_page, ps_end_page,
- ps_begin_layer, 0, /* ps_end_layer */
- ps_begin_cluster, 0, /* ps_end_cluster */
- 0, /* ps_begin_nodes */ 0, /* ps_end_nodes */
- 0, /* ps_begin_edges */ 0, /* ps_end_edges */
- ps_begin_node, 0, /* ps_end_node */
- ps_begin_edge, 0, /* ps_end_edge */
- ps_begin_context, ps_end_context,
- 0, /* ps_begin_anchor */ 0, /* ps_end_anchor */
- ps_set_font, ps_textpara,
- ps_set_pencolor, ps_set_fillcolor, ps_set_style,
- ps_ellipse, ps_polygon,
- ps_bezier, ps_polyline,
- 0, /* bezier_has_arrows */
- ps_comment,
- ps_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 *
-**********************************************************/
-
-
-/* this is a rough start at a working SVG driver.
-you can get image nodes by setting a node's
- [shape=webimage,shapefile="http://www.your.site.com/path/image.png"]
-(of course can also be a file: reference if you run locally.
-which causes a warning (FIX that) but the back end turns this into
-a custom shape. you can also set
-[ href = "http://some.place.com/whatever" ]
-to get clickable nodes (and edges for that matter).
-
-some major areas needing work:
-0. fonts including embedded font support. is SVG finished in this area?
-1. styles, including dotted/dashed lines, also "style function"
-passthrough in SVG similar to what we have in postscript.
-2. look at what happens in landscape mode, pagination? etc.
-3. allow arbitrary user transforms via graph "style" attribute.
-4. javascript hooks. particularly, look at this in the context
-of SVG animation for dynadag output (n.b. dynadag only in alpha release)
-5. image node improvement, e.g. set clip path to node boundary, and
-support scaling with fixed aspect ratio (this feature seems to be
-broken in the current Adobe SVG plugin for Windows).
-6. can we support arbitrary HTML for node contents?
-7. encode abstract graph as interleaved XML in some sort of graph XML dialect?
-8. accessibility features
-9. embed directions about getting plugin, if not present in browser
-
-Stephen North
-north@research.att.com
-*/
-
-#include <ctype.h>
-#include "render.h"
-#ifdef HAVE_LIBZ
-#include "zlib.h"
-#endif
-#ifdef MSWIN32
-#include <io.h>
-#include <stdarg.h>
-#endif
-
-/* SVG font modifiers */
-#define REGULAR 0
-#define BOLD 1
-#define ITALIC 2
-
-/* SVG patterns */
-#define P_SOLID 0
-#define P_NONE 15
-#define P_DOTTED 4 /* i wasn't sure about this */
-#define P_DASHED 11 /* or this */
-
-/* SVG bold line constant */
-#define WIDTH_NORMAL 1
-#define WIDTH_BOLD 3
-
-#ifdef TESTFAILED
-/* sodipodi doesn't understand style sheets */
-#define DEFAULT_STYLE\
-"<style type='text/css'>\n" \
-"<![CDATA[\n" \
-".node ellipse {fill:none;filter:URL(#MyFilter)}\n" \
-".node polygon {fill:none;filter:URL(#MyFilter)}\n" \
-".cluster polygon {fill:none;filter:URL(#MyFilter)}\n" \
-".edge path {fill:none;stroke:black;stroke-width:1;}\n" \
-"text {font-family:Times;stroke:black;stroke-width:.4;font-size:12px}\n" \
-"]]>\n" \
-"</style>"
-
-#define DEFAULT_FILTER \
-"<filter id=\"MyFilter\" x=\"-20%\" y=\"-20%\" width=\"160%\" height=\"160%\">\n" \
-"<feGaussianBlur in=\"SourceAlpha\" stdDeviation=\"4\" result=\"blur\"/>\n" \
-"<feOffset in=\"blur\" dx=\"4\" dy=\"4\" result=\"offsetBlur\"/>\n" \
-"<feSpecularLighting in=\"blur\" surfaceScale=\"5\" specularConstant=\"1\"\n" \
-"specularExponent=\"10\" style=\"lighting-color:white\" result=\"specOut\">\n" \
-"<fePointLight x=\"-5000\" y=\"-10000\" z=\"20000\"/>\n" \
-"</feSpecularLighting>\n" \
-"<feComposite in=\"specOut\" in2=\"SourceAlpha\" operator=\"in\" result=\"specOut\"/>\n" \
-"<feComposite in=\"SourceGraphic\" in2=\"specOut\" operator=\"arithmetic\"\n" \
-"k1=\"0\" k2=\"1\" k3=\"1\" k4=\"0\" result=\"litPaint\"/>\n" \
-"<feMerge>\n" \
-"<feMergeNode in=\"offsetBlur\"/>\n" \
-"<feMergeNode in=\"litPaint\"/>\n" \
-"</feMerge>\n" \
-"</filter>"
-#endif
-
-#define SCALE 1
-
-/* SVG dash array */
-static char *sdarray = "5,2";
-/* SVG dot array */
-static char *sdotarray = "1,5";
-
-static int N_pages;
-/* static point Pages; */
-static int onetime = TRUE;
-static int isLatin1;
-
-static pointf CompScale;
-static double FontScale;
-static int Rot;
-
-static point Margin;
-static point Viewport;
-static pointf GraphFocus;
-static double Zoom;
-
-static node_t *Curnode;
-
-typedef struct context_t {
- char *pencolor, *fillcolor, *fontfam, fontopt, font_was_set;
- char pen, fill, penwidth, style_was_set;
- double fontsz;
-} context_t;
-
-#define MAXNEST 4
-static context_t cstk[MAXNEST];
-static int SP;
-
-static char *op[] = { "graph", "node", "edge", "graph" };
-
-#ifdef HAVE_LIBZ
-static gzFile Zfile;
-#endif
-
-static void svg_fputs(char *s)
-{
- int len;
-
- len = strlen(s);
- switch (Output_lang) {
- case SVGZ:
-#ifdef HAVE_LIBZ
- gzwrite(Zfile, s, (unsigned) len);
- break;
-#else
- agerr(AGERR, "No libz support.\n");
- exit(1);
-#endif
- case SVG:
- fwrite(s, sizeof(char), (unsigned) len, Output_file);
- break;
- }
-}
-
-/* svg_printf:
- * Note that this function is unsafe due to the fixed buffer size.
- * It should only be used when the caller is sure the input will not
- * overflow the buffer. In particular, it should be avoided for
- * input coming from users. Also, if vsnprintf is available, the
- * code should check for return values to use it safely.
- */
-static void svg_printf(const char *format, ...)
-{
- char buf[BUFSIZ];
- va_list argp;
-
- va_start(argp, format);
-#ifdef HAVE_VSNPRINTF
- (void) vsnprintf(buf, sizeof(buf), format, argp);
-#else
- (void) vsprintf(buf, format, argp);
-#endif
- va_end(argp);
-
- svg_fputs(buf);
-}
-
-static void svg_reset(void)
-{
- onetime = TRUE;
-}
-
-static void init_svg(void)
-{
- SP = 0;
- cstk[0].pencolor = DEFAULT_COLOR; /* SVG pencolor */
- cstk[0].fillcolor = ""; /* SVG fillcolor */
- cstk[0].fontfam = DEFAULT_FONTNAME; /* font family name */
- cstk[0].fontsz = DEFAULT_FONTSIZE; /* font size */
- 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;
-}
-
-static point svgpt(point p)
-{
- point rv;
-
- if (Rot == 0) {
- rv.x = (p.x - GraphFocus.x) * CompScale.x + Viewport.x / 2. + Margin.x;
- rv.y = -(p.y - GraphFocus.y) * CompScale.y + Viewport.y / 2. + Margin.y;
- } else {
- rv.x = -(p.y - GraphFocus.y) * CompScale.x + Viewport.x / 2. + Margin.x;
- rv.y = -(p.x - GraphFocus.x) * CompScale.y + Viewport.y / 2. + Margin.y;
- }
- return rv;
-}
-
-static void svgbzptarray(point * A, int n)
-{
- int i;
- point p;
- char *c;
-
- c = "M"; /* first point */
- for (i = 0; i < n; i++) {
- p = svgpt(A[i]);
- svg_printf("%s%d,%d", c, p.x, p.y);
- if (i == 0)
- c = "C"; /* second point */
- else
- c = " "; /* remaining points */
- }
-}
-
-static int svg_comparestr(const void *s1, const void *s2)
-{
- return strcmp(*(char **) s1, *(char **) s2);
-}
-
-char *svg_resolve_color(char *name, int useKnown)
-{
-/* color names from http://www.w3.org/TR/SVG/types.html */
-/* NB. List must be LANG_C sorted */
- static char *svg_known_colors[] = {
- "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure",
- "beige", "bisque", "black", "blanchedalmond", "blue",
- "blueviolet", "brown", "burlywood",
- "cadetblue", "chartreuse", "chocolate", "coral",
- "cornflowerblue", "cornsilk", "crimson", "cyan",
- "darkblue", "darkcyan", "darkgoldenrod", "darkgray",
- "darkgreen", "darkgrey", "darkkhaki", "darkmagenta",
- "darkolivegreen", "darkorange", "darkorchid", "darkred",
- "darksalmon", "darkseagreen", "darkslateblue", "darkslategray",
- "darkslategrey", "darkturquoise", "darkviolet", "deeppink",
- "deepskyblue", "dimgray", "dimgrey", "dodgerblue",
- "firebrick", "floralwhite", "forestgreen", "fuchsia",
- "gainsboro", "ghostwhite", "gold", "goldenrod", "gray",
- "green", "greenyellow", "grey",
- "honeydew", "hotpink", "indianred",
- "indigo", "ivory", "khaki",
- "lavender", "lavenderblush", "lawngreen", "lemonchiffon",
- "lightblue", "lightcoral", "lightcyan", "lightgoldenrodyellow",
- "lightgray", "lightgreen", "lightgrey", "lightpink",
- "lightsalmon", "lightseagreen", "lightskyblue",
- "lightslategray", "lightslategrey", "lightsteelblue",
- "lightyellow", "lime", "limegreen", "linen",
- "magenta", "maroon", "mediumaquamarine", "mediumblue",
- "mediumorchid", "mediumpurple", "mediumseagreen",
- "mediumslateblue", "mediumspringgreen", "mediumturquoise",
- "mediumvioletred", "midnightblue", "mintcream",
- "mistyrose", "moccasin",
- "navajowhite", "navy", "oldlace",
- "olive", "olivedrab", "orange", "orangered", "orchid",
- "palegoldenrod", "palegreen", "paleturquoise",
- "palevioletred", "papayawhip", "peachpuff", "peru", "pink",
- "plum", "powderblue", "purple",
- "red", "rosybrown", "royalblue",
- "saddlebrown", "salmon", "sandybrown", "seagreen", "seashell",
- "sienna", "silver", "skyblue", "slateblue", "slategray",
- "slategrey", "snow", "springgreen", "steelblue",
- "tan", "teal", "thistle", "tomato", "turquoise",
- "violet",
- "wheat", "white", "whitesmoke",
- "yellow", "yellowgreen",
- };
-
- static char buf[SMALLBUF];
- char *tok;
- gvcolor_t color;
-
- tok = canontoken(name);
- if (!useKnown || (bsearch(&tok, svg_known_colors,
- sizeof(svg_known_colors) / sizeof(char *),
- sizeof(char *), svg_comparestr) == NULL)) {
- /* if tok was not found in known_colors */
- if (streq(tok, "transparent")) {
- tok = "none";
- } else {
- colorxlate(name, &color, RGBA_BYTE);
- sprintf(buf, "#%02x%02x%02x",
- color.u.rgba[0], color.u.rgba[1], color.u.rgba[2]);
- tok = buf;
- }
- }
- return tok;
-}
-
-static void svg_font(context_t * cp)
-{
- char *color, buf[BUFSIZ];
- int needstyle = 0;
-
- strcpy(buf, " style=\"");
- if (strcasecmp(cp->fontfam, DEFAULT_FONTNAME)) {
- sprintf(buf + strlen(buf), "font-family:%s;", cp->fontfam);
- needstyle++;
- }
- if (cp->fontsz != DEFAULT_FONTSIZE) {
- sprintf(buf + strlen(buf), "font-size:%.2fpt;", cp->fontsz * FontScale);
- needstyle++;
- }
- color = svg_resolve_color(cp->pencolor, 1);
- if ((strcasecmp(color, "black"))) {
- sprintf(buf + strlen(buf), "fill:%s;", color);
- needstyle++;
- }
- if (needstyle) {
- strcat(buf, "\"");
- svg_fputs(buf);
- }
-}
-
-
-static void svg_grstyle(context_t * cp, int filled)
-{
- svg_fputs(" style=\"");
- if (filled)
- svg_printf("fill:%s;", svg_resolve_color(cp->fillcolor, 1));
- else
- svg_fputs("fill:none;");
- svg_printf("stroke:%s;", svg_resolve_color(cp->pencolor, 1));
- if (cp->penwidth != WIDTH_NORMAL)
- svg_printf("stroke-width:%d;", cp->penwidth);
- if (cp->pen == P_DASHED) {
- svg_printf("stroke-dasharray:%s;", sdarray);
- } else if (cp->pen == P_DOTTED) {
- svg_printf("stroke-dasharray:%s;", sdotarray);
- }
- svg_fputs("\"");
-}
-
-/* xml_namestring:
- * Return xml_string applied to input.
- * If input is Latin1, first translate to UTF8
- */
-static char*
-xml_namestring (char* str)
-{
- if (isLatin1) {
- char* s = latin1ToUTF8 (str);
- str = xml_string(s);
- free (s);
- }
- else
- str = xml_string(str);
- return str;
-}
-
-/* svg_name_fputs:
- * Do fputs on input.
- * If input is Latin1, first translate to UTF8.
- */
-static void
-svg_name_fputs (char* str)
-{
- if (isLatin1) {
- char* s = latin1ToUTF8 (str);
- svg_fputs(s);
- free (s);
- }
- else
- svg_fputs(str);
-}
-
-static void svg_comment(char *str)
-{
-
- svg_fputs("<!-- ");
- svg_fputs(xml_namestring(str));
- svg_fputs(" -->\n");
-}
-
-/* isAscii:
- * Return true if all bytes in string are ascii.
- */
-static int isAscii (char* s)
-{
- int c;
-
- while ((c = *s++) && isascii(c)) ;
- return (c == '\0');
-}
-
-static void
-svg_begin_job(FILE * ofp, graph_t * g, char **lib, char *user,
- char *info[], point pages)
-{
- char *s;
-#if HAVE_LIBZ
- int fd;
-#endif
-
- switch (Output_lang) {
- case SVGZ:
-#if HAVE_LIBZ
- fd = dup(fileno(Output_file)); /* open dup so can gzclose
- independent of FILE close */
-#ifdef HAVE_SETMODE
-#ifdef O_BINARY
- /*
- * Windows will do \n -> \r\n translations on
- * stdout unless told otherwise.
- */
- setmode(fd, O_BINARY);
-#endif
-#endif
-
- Zfile = gzdopen(fd, "wb");
- if (!Zfile) {
- agerr(AGERR, "Error opening compressed output file\n");
- exit(1);
- }
- break;
-#else
- agerr(AGERR, "No libz support.\n");
- exit(1);
-#endif
- case SVG:
- break;
- }
-
-/* Pages = pages; */
- N_pages = pages.x * pages.y;
- svg_fputs
- ("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
- if ((s = agget(g, "stylesheet")) && s[0]) {
- svg_fputs("<?xml-stylesheet href=\"");
- svg_fputs(s);
- svg_fputs("\" type=\"text/css\"?>\n");
- }
- svg_fputs("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\"\n");
- svg_fputs
- (" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\"");
- /* This is to work around a bug in the SVG 1.0 DTD */
- svg_fputs
- (" [\n <!ATTLIST svg xmlns:xlink CDATA #FIXED \"http://www.w3.org/1999/xlink\">\n]");
- svg_fputs(">\n<!-- Generated by ");
- svg_fputs(xml_string(info[0]));
- svg_fputs(" version ");
- svg_fputs(xml_string(info[1]));
- svg_fputs(" (");
- svg_fputs(xml_string(info[2]));
- /* We assume utf-8 output. If user name contains any non-ascii bytes,
- * we don't put it in the output. Obviously, this could be made more
- * sophisticated, like checking that the name is utf-8 or doing some
- * conversion.
- */
- if (isAscii(user)) {
- svg_fputs(")\n For user: ");
- svg_fputs(xml_string(user));
- svg_fputs(" -->\n");
- }
- else
- svg_fputs(") -->\n");
-}
-
-static void svg_begin_graph(GVC_t * gvc, graph_t * g, box bb, point pb)
-{
- Viewport.x = gvc->job->width;
- Viewport.y = gvc->job->height;
- Margin.x = ROUND(gvc->job->margin.x * gvc->job->dpi.x / POINTS_PER_INCH);
- Margin.y = ROUND(gvc->job->margin.y * gvc->job->dpi.y / POINTS_PER_INCH);
-
- Zoom = gvc->job->zoom;
- GraphFocus = gvc->job->focus;
-
- CompScale.x = Zoom * gvc->job->dpi.x / POINTS_PER_INCH;
- CompScale.y = Zoom * gvc->job->dpi.y / POINTS_PER_INCH;
- FontScale = CompScale.y * 0.72;
-
- if (onetime) {
- init_svg();
- onetime = FALSE;
- }
- isLatin1 = (GD_charset(g) == CHAR_LATIN1);
- svg_fputs("<!-- Title: ");
- svg_fputs(xml_namestring(g->name));
- svg_printf(" Pages: %d -->\n", N_pages);
- if (ROUND(gvc->job->dpi.x) == POINTS_PER_INCH && ROUND(gvc->job->dpi.y) == POINTS_PER_INCH) {
- svg_printf("<svg width=\"%dpt\" height=\"%dpt\"\n",
- Viewport.x + Margin.x * 2, Viewport.y + Margin.y * 2);
- }
- else {
- svg_printf("<svg width=\"%dpx\" height=\"%dpx\"\n",
- ROUND(gvc->job->dpi.x * (Viewport.x + Margin.x * 2) / POINTS_PER_INCH),
- ROUND(gvc->job->dpi.y * (Viewport.y + Margin.y * 2) / POINTS_PER_INCH));
- }
- /* establish absolute units in points */
- svg_printf(" viewBox = \"%d %d %d %d\"\n", Margin.x, Margin.y, Viewport.x, Viewport.y);
- /* namespace of svg */
- svg_fputs(" xmlns=\"http://www.w3.org/2000/svg\"");
- /* namespace of xlink */
- svg_fputs(" xmlns:xlink=\"http://www.w3.org/1999/xlink\"");
- svg_fputs(">\n");
-}
-
-static void svg_end_graph(void)
-{
- svg_fputs("</svg>\n");
- switch (Output_lang) {
- case SVGZ:
-#ifdef HAVE_LIBZ
- gzclose(Zfile);
- break;
-#else
- agerr(AGERR, "No libz support\n");
- exit(1);
-#endif
- case SVG:
- break;
- }
-}
-
-static void
-svg_begin_page(graph_t * g, point page, double scale, int rot,
- point offset)
-{
- Rot = rot;
-
-#if 0
-fprintf(stderr,"Zoom=%g CompScale=%g,%f\n", Zoom, CompScale.x, CompScale.y);
-fprintf(stderr,"font-size=%g Zoom=%g\n", cstk[0].fontsz, Zoom);
-#endif
-
- /* its really just a page of the graph, but its still a graph,
- * and it is the entire graph if we're not currently paging */
- svg_printf("<g id=\"%s0\" class=\"graph\"", op[Obj]);
- /* default style */
- svg_fputs(" style=\"font-family:");
- svg_fputs(cstk[0].fontfam);
- svg_printf(";font-size:%.2fpt;\">\n", cstk[0].fontsz * FontScale);
- svg_fputs("<title>");
- svg_fputs(xml_namestring(g->name));
- svg_fputs("</title>\n");
-}
-
-static void svg_end_page(void)
-{
- svg_fputs("</g>\n");
-}
-
-static void svg_begin_layer(char *layerName, int n, int nLayers)
-{
- /* svg_printf("<g id=\"%s\" class=\"layer\">\n", xml_string(layerName)); */
- svg_fputs("<g id=\"");
- svg_fputs(xml_string(layerName));
- svg_fputs("\" class=\"layer\">\n");
-}
-
-static void svg_end_layer(void)
-{
- svg_fputs("</g>\n");
-}
-
-static void svg_begin_cluster(graph_t * g)
-{
- svg_printf("<g id=\"%s%d\" class=\"cluster\">", op[Obj],
- g->meta_node->id);
- svg_fputs("<title>");
- svg_fputs(xml_namestring(g->name));
- svg_fputs("</title>\n");
-}
-
-static void svg_end_cluster(void)
-{
- svg_fputs("</g>\n");
-}
-
-static void svg_begin_node(node_t * n)
-{
- Curnode = n;
- svg_printf("<g id=\"%s%d\" class=\"node\">", op[Obj], n->id);
- svg_fputs("<title>");
- svg_fputs(xml_namestring(n->name));
- svg_fputs("</title>\n");
-}
-
-static void svg_end_node(void)
-{
- svg_fputs("</g>\n");
-}
-
-static void svg_begin_edge(edge_t * e)
-{
- char *edgeop;
-
- svg_printf("<g id=\"%s%d\" class=\"edge\">", op[Obj], e->id);
- if (e->tail->graph->root->kind & AGFLAG_DIRECTED)
- edgeop = "->";
- else
- edgeop = "--";
- svg_fputs("<title>");
- svg_fputs(xml_namestring(e->tail->name));
- svg_fputs(edgeop);
- /* can't do this in single svg_printf because
- * xml_string's buffer gets reused. */
- svg_fputs(xml_namestring(e->head->name));
- svg_fputs("</title>\n");
-}
-
-static void svg_end_edge(void)
-{
- svg_fputs("</g>\n");
-}
-
-static void svg_begin_context(void)
-{
- assert(SP + 1 < MAXNEST);
- cstk[SP + 1] = cstk[SP];
- SP++;
-}
-
-static void svg_begin_anchor(char *href, char *tooltip, char *target)
-{
- svg_fputs("<a");
- if (href && href[0]) {
- svg_fputs(" xlink:href=\"");
- svg_fputs(xml_string(href));
- svg_fputs("\"");
- }
- if (tooltip && tooltip[0]) {
- svg_fputs(" xlink:title=\"");
- svg_fputs(xml_string(tooltip));
- svg_fputs("\"");
- }
- if (target && target[0]) {
- svg_fputs(" target=\"");
- svg_fputs(xml_string(target));
- svg_fputs("\"");
- }
- svg_fputs(">\n");
-}
-
-static void svg_end_anchor(void)
-{
- svg_fputs("</a>\n");
-}
-
-static void svg_end_context(void)
-{
- int psp = SP - 1;
- assert(SP > 0);
- /*free(cstk[psp].fontfam); */
- SP = psp;
-}
-
-static void svg_set_font(char *name, double size)
-{
- char *p;
-#if 0
-/* see below */
- char *q;
-#endif
- context_t *cp;
-
- cp = &(cstk[SP]);
- cp->font_was_set = TRUE;
- cp->fontsz = size;
- p = strdup(name);
-#if 0
-/*
- * this doesn't work as originally intended
- * fontnames can be things like: "Times-Roman"
- * where "Roman" is not an ITALIC or BOLD indicator.
- */
- 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;
-#else
- cp->fontfam = p;
-#endif
-}
-
-static void svg_set_pencolor(char *name)
-{
- cstk[SP].pencolor = name;
-}
-
-static void svg_set_fillcolor(char *name)
-{
- cstk[SP].fillcolor = name;
-}
-
-static void svg_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(AGERR,
- "svg_set_style: unsupported style %s - ignoring\n",
- line);
- }
- cp->style_was_set = TRUE;
- }
- /* if (cp->style_was_set) svg_style(cp); */
-}
-
-static void svg_textpara(point p, textpara_t * para)
-{
- char *anchor, *string;
- point mp;
- context_t *cp;
-
- string = xml_string(para->str);
- if (!string[0])
- return;
-
- cp = &(cstk[SP]);
- if (cp->pen == P_NONE) {
- /* its invisible, don't draw */
- return;
- }
-#if 0
-/* adobe svg doesn't like the dx propert */
- if (para->xshow) {
- anchor = "start";
- switch (para->just) {
- case 'l':
- break;
- case 'r':
- p.x -= para->width;
- break;
- default:
- case 'n':
- p.x -= para->width / 2;
- break;
- }
- } else {
-#endif
- switch (para->just) {
- case 'l':
- anchor = "start";
- break;
- case 'r':
- anchor = "end";
- break;
- default:
- case 'n':
- anchor = "middle";
- break;
- }
-#if 0
-/* adobe svg doesn't like the dx propert */
- }
-#endif
- mp = svgpt(p);
-
- svg_printf("<text text-anchor=\"%s\"", anchor);
- if (Rot) {
- svg_printf(" transform=\"rotate(-90 %d %d)\"", mp.x, mp.y);
- }
- svg_printf(" x=\"%d\" y=\"%d\"", mp.x, mp.y);
- svg_font(cp);
-#if 0
-/* adobe svg doesn't like the dx propert */
- if (line->xshow) {
- svg_fputs("\" dx=\"");
- svg_fputs(para->xshow);
- }
-#endif
- svg_fputs(">");
- svg_fputs(string);
- svg_fputs("</text>\n");
-}
-
-static void svg_ellipse(point p, int rx, int ry, int filled)
-{
- point mp;
-
- if (cstk[SP].pen == P_NONE) {
- /* its invisible, don't draw */
- return;
- }
- mp.x = p.x;
- mp.y = p.y;
- mp = svgpt(mp);
- svg_fputs("<ellipse");
- svg_grstyle(&cstk[SP], filled);
- svg_printf(" cx=\"%d\" cy=\"%d\"", mp.x, mp.y);
- if (Rot) {
- int t;
- t = rx;
- rx = ry;
- ry = t;
- }
- mp.x = CompScale.x * rx;
- mp.y = CompScale.y * ry;
- svg_printf(" rx=\"%d\" ry=\"%d\"/>\n", mp.x, mp.y);
-}
-
-static void
-svg_bezier(point * A, int n, int arrow_at_start, int arrow_at_end, int filled)
-{
- if (cstk[SP].pen == P_NONE) {
- /* its invisible, don't draw */
- return;
- }
- svg_fputs("<path");
- svg_grstyle(&cstk[SP], filled);
- svg_fputs(" d=\"");
- svgbzptarray(A, n);
- svg_fputs("\"/>\n");
-}
-
-static void svg_polygon(point * A, int n, int filled)
-{
- int i;
- point p;
-
- if (cstk[SP].pen == P_NONE) {
- /* its invisible, don't draw */
- return;
- }
- svg_fputs("<polygon");
- svg_grstyle(&cstk[SP], filled);
- svg_fputs(" points=\"");
- for (i = 0; i < n; i++) {
- p = svgpt(A[i]);
- svg_printf("%d,%d ", p.x, p.y);
- }
- /* because Adobe SVG is broken */
- p = svgpt(A[0]);
- svg_printf("%d,%d", p.x, p.y);
- svg_fputs("\"/>\n");
-}
-
-static void svg_polyline(point * A, int n)
-{
- int i;
- point p;
-
- if (cstk[SP].pen == P_NONE) {
- /* its invisible, don't draw */
- return;
- }
- svg_fputs("<polyline");
- svg_grstyle(&cstk[SP], 0);
- svg_fputs(" points=\"");
- for (i = 0; i < n; i++) {
- p = svgpt(A[i]);
- svg_printf("%d,%d ", p.x, p.y);
- }
- svg_fputs("\"/>\n");
-}
-
-static void svg_usershape(usershape_t *us, boxf b, point *A, int n, bool filled)
-{
- char *imagefile;
-
- if (cstk[SP].pen == P_NONE) {
- /* its invisible, don't draw */
- return;
- }
- if (us->f)
- imagefile = us->name;
- else
- imagefile = NULL;
- if (! imagefile) {
- svg_polygon(A, n, filled);
- return;
- }
-
- svg_fputs("<image xlink:href=\"");
- svg_name_fputs(imagefile);
- if (Rot) {
- svg_printf ("\" width=\"%gpx\" height=\"%fpx\" preserveAspectRatio=\"xMidYMid meet\" x=\"%g\" y=\"%g\"",
- b.UR.y - b.LL.y, b.UR.x - b.LL.x, b.LL.x, b.UR.y);
- svg_printf (" transform=\"rotate(-%d %g %g)\"",
- Rot, b.LL.x, b.UR.y);
- }
- else {
- svg_printf ("\" width=\"%gpx\" height=\"%fpx\" preserveAspectRatio=\"xMidYMid meet\" x=\"%g\" y=\"%g\"",
- b.UR.x - b.LL.x, b.UR.y - b.LL.y, b.LL.x, b.LL.y);
- }
- svg_fputs("/>\n");
-}
-
-codegen_t SVG_CodeGen = {
- svg_reset,
- svg_begin_job, 0, /* svg_end_job */
- svg_begin_graph, svg_end_graph,
- svg_begin_page, svg_end_page,
- svg_begin_layer, svg_end_layer,
- svg_begin_cluster, svg_end_cluster,
- 0, /* svg_begin_nodes */ 0, /* svg_end_nodes */
- 0, /* svg_begin_edges */ 0, /* svg_end_edges */
- svg_begin_node, svg_end_node,
- svg_begin_edge, svg_end_edge,
- svg_begin_context, svg_end_context,
- svg_begin_anchor, svg_end_anchor,
- svg_set_font, svg_textpara,
- svg_set_pencolor, svg_set_fillcolor, svg_set_style,
- svg_ellipse, svg_polygon,
- svg_bezier, svg_polyline,
- 0, /* bezier_has_arrows */
- svg_comment,
- 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"
-#include "gd.h"
-#include "pathutil.h"
-
-
-extern char *get_ttf_fontpath(char *fontreq, int warn);
-
-#ifdef HAVE_GD_PNG
-
-#ifndef MAXFLOAT
-#define MAXFLOAT 10000000.
-#endif
-
-#define NONE 0
-#define NODE 1
-#define EDGE 2
-#define CLST 3
-
-#define BEZIERSUBDIVISION 10
-
-/* font modifiers */
-#define REGULAR 0
-#define BOLD 1
-#define ITALIC 2
-
-/* patterns */
-#define P_SOLID 0
-#define P_NONE 15
-#define P_DOTTED 4 /* i wasn't sure about this */
-#define P_DASHED 11 /* or this */
-
-/* bold line constant */
-#define WIDTH_NORMAL 1
-#define WIDTH_BOLD 3
-
-typedef struct {
- unsigned char r, g, b;
-} Color;
-
-
-/* static int N_pages; */
-/* static point Pages; */
-static double Scale;
-static int Rot;
-static box BB;
-static double MinZ;
-/* static int onetime = TRUE; */
-static int Saw_skycolor;
-
-static gdImagePtr im;
-static FILE *PNGfile;
-static node_t *Curnode;
-static edge_t *Curedge;
-static int IsSegment; /* set true if edge is line segment */
-static double CylHt; /* height of cylinder part of edge */
-static double EdgeLen; /* length between centers of endpoints */
-static double HeadHt, TailHt; /* height of arrows */
-static double Fstz, Sndz; /* z values of tail and head points */
-
-typedef struct context_t {
- unsigned char pencolor_ix, fillcolor_ix;
- char *pencolor, *fillcolor;
- char *fontfam, fontopt, font_was_set;
- double r, g, b; /* fill color values */
- char pen, fill, penwidth;
- double fontsz;
-} context_t;
-
-#define MAXNEST 4
-static context_t cstk[MAXNEST];
-static int SP;
-
-static char *nodeURL(node_t * n, char *buf)
-{
- sprintf(buf, "node%d.png", n->id);
- return buf;
-}
-
-/* gdirname:
- * Returns directory pathname prefix
- * Code adapted from dgk
- */
-static char *gdirname(char *pathname)
-{
- char *last;
-
- /* go to end of path */
- for (last = pathname; *last; last++);
- /* back over trailing '/' */
- while (last > pathname && *--last == '/');
- /* back over non-slash chars */
- for (; last > pathname && *last != '/'; last--);
- if (last == pathname) {
- /* all '/' or "" */
- if (*pathname != '/')
- *last = '.';
- /* preserve // */
- else if (pathname[1] == '/')
- last++;
- } else {
- /* back over trailing '/' */
- for (; *last == '/' && last > pathname; last--);
- /* preserve // */
- if (last == pathname && *pathname == '/' && pathname[1] == '/')
- last++;
- }
- last++;
- *last = '\0';
-
- return pathname;
-}
-
-static char *nodefilename(node_t * n, char *buf)
-{
- static char *dir;
- static char disposable[1024];
- char junkbuf[1024];
-
- if (dir == 0) {
- if (Output_file_name)
- dir = gdirname(strcpy(disposable, Output_file_name));
- else
- dir = ".";
- }
- sprintf(buf, "%s/%s", dir, nodeURL(n, junkbuf));
- return buf;
-}
-
-static FILE *nodefile(node_t * n)
-{
- FILE *rv;
- char buf[1024];
-
- rv = fopen(nodefilename(n, buf), "wb");
- return rv;
-}
-
-static unsigned char vrml_resolve_color(char *name)
-{
- gvcolor_t color;
-
- if (!(strcmp(name, "transparent"))) {
- /* special case for "transparent" color */
- return gdImageColorResolve(im, 255, 255, 254);
- } else {
- colorxlate(name, &color, RGBA_BYTE);
- return gdImageColorResolve(im,
- color.u.rgba[0], color.u.rgba[1],
- color.u.rgba[2]);
- }
-}
-
-static void vrml_set_pencolor(char *name)
-{
- cstk[SP].pencolor = name;
-}
-
-static void vrml_set_fillcolor(char *name)
-{
- gvcolor_t color;
- cstk[SP].fillcolor = name;
- colorxlate(name, &color, RGBA_BYTE);
- cstk[SP].r = (double) color.u.rgba[0] / 255.0;
- cstk[SP].g = (double) color.u.rgba[1] / 255.0;
- cstk[SP].b = (double) color.u.rgba[2] / 255.0;
-}
-
-static void init_png(gdImagePtr im)
-{
- int transparent;
-
- if ((transparent = gdImageGetTransparent(im)) == -1) {
- transparent = gdImageColorResolve(im, 255, 255, 254);
- gdImageColorTransparent(im, transparent);
- }
-}
-
-static pointf vrml_node_point(point p)
-{
- pointf rv;
-
- /* make mp relative to PNG canvas */
- if (Rot == 0) {
- rv.x = (p.x - ND_coord_i(Curnode).x + ND_lw_i(Curnode)) * Scale;
- rv.y =
- (ND_coord_i(Curnode).y - p.y + ND_ht_i(Curnode) / 2) * Scale;
- } else {
- rv.x = (p.y - ND_coord_i(Curnode).y + ND_lw_i(Curnode)) * Scale;
- rv.y =
- (ND_coord_i(Curnode).x - p.x + ND_ht_i(Curnode) / 2) * Scale;
- }
- return rv;
-}
-
-static void vrml_font(context_t * cp)
-{
-/* FIX
- char *fw, *fa;
-
- fw = fa = "Regular";
- switch (cp->fontopt) {
- case BOLD:
- fw = "Bold";
- break;
- case ITALIC:
- fa = "Italic";
- break;
- }
-*/
-}
-
-/* warmed over VRML code starts here */
-
-static void vrml_begin_job(FILE * ofp, graph_t * g, char **lib, char *user,
- char *info[], point pages)
-{
- fprintf(Output_file, "#VRML V2.0 utf8\n");
-}
-
-static void vrml_begin_graph(GVC_t * gvc, graph_t * g, box bb, point pb)
-{
- g = g;
-
- Saw_skycolor = FALSE;
- MinZ = MAXDOUBLE;
- BB = bb;
- fprintf(Output_file, "Group { children [\n");
- fprintf(Output_file, " Transform {\n");
- fprintf(Output_file, " scale %.3f %.3f %.3f\n",
- .0278, .0278, .0278);
- fprintf(Output_file, " children [\n");
-
- SP = 0;
- cstk[0].fillcolor = "white";
- 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;
-}
-
-static void vrml_end_graph(void)
-{
- double d, z;
- box bb = BB;
-
- d = MAX(bb.UR.x - bb.LL.x,bb.UR.y - bb.LL.y);
- /* Roughly fill 3/4 view assuming FOV angle of PI/4.
- * Small graphs and non-square aspect ratios will upset this.
- */
- z = (0.6667*d)/tan(PI/8.0) + MinZ; /* fill 3/4 of view */
-
- if (!Saw_skycolor)
- fprintf(Output_file, " Background { skyColor 1 1 1 }\n");
- fprintf(Output_file, " ] }\n");
- fprintf(Output_file, " Viewpoint {position %.3f %.3f %.3f}\n",
- .0278 * (bb.UR.x + bb.LL.x) / 2.0,
- .0278 * (bb.UR.y + bb.LL.y) / 2.0, .0278 * z);
- fprintf(Output_file, "] }\n");
-}
-
-static void vrml_begin_page(graph_t * g, point page, double scale, int rot,
- point offset)
-{
-#if 0 /* scale not used */
- Scale = scale * (double) DEFAULT_DPI / POINTS_PER_INCH;
-#else
- Scale = (double) DEFAULT_DPI / POINTS_PER_INCH;
-#endif
- Rot = rot;
-}
-
-static void vrml_begin_node(node_t * n)
-{
- int width, height;
- double z;
-
- fprintf(Output_file, "# node %s\n", n->name);
- z = late_double(n, N_z, 0.0, -MAXFLOAT);
- if (z < MinZ) MinZ = z;
- if (shapeOf(n) != SH_POINT) {
- PNGfile = nodefile(n);
- width = (ND_lw_i(n) + ND_rw_i(n)) * Scale + 3;
- height = (ND_ht_i(n)) * Scale + 3;
- im = gdImageCreate(width, height);
- init_png(im);
- }
- Curnode = n;
-}
-
-static void vrml_end_node(void)
-{
- if (shapeOf(Curnode) != SH_POINT) {
- gdImagePng(im, PNGfile);
- gdImageDestroy(im);
- im = 0;
- fclose(PNGfile);
- }
-}
-
-static void vrml_begin_edge(edge_t * e)
-{
- Curedge = e;
- IsSegment = 0;
- fprintf(Output_file, "# edge %s -> %s\n", e->tail->name, e->head->name);
- fprintf(Output_file, " Group { children [\n");
-}
-
-static void
-finishSegment (void)
-{
- point p0 = ND_coord_i(Curedge->tail);
- point p1 = ND_coord_i(Curedge->head);
- double o_x, o_y, o_z;
- double x, y, y0, z, theta;
-
- o_x = ((double)(p0.x + p1.x))/2;
- o_y = ((double)(p0.y + p1.y))/2;
- o_z = (Fstz + Sndz)/2;
- /* Compute rotation */
- /* Pick end point with highest y */
- if (p0.y > p1.y) {
- x = p0.x;
- y = p0.y;
- z = Fstz;
- }
- else {
- x = p1.x;
- y = p1.y;
- z = Sndz;
- }
- /* Translate center to the origin */
- x -= o_x;
- y -= o_y;
- z -= o_z;
- if (p0.y > p1.y)
- theta = acos(2*y/EdgeLen) + PI;
- else
- theta = acos(2*y/EdgeLen);
- if (!x && !z) /* parallel to y-axis */
- x = 1;
-
- y0 = (HeadHt-TailHt)/2.0;
- fprintf(Output_file, " ]\n");
- fprintf(Output_file, " center 0 %f 0\n", y0);
- fprintf(Output_file, " rotation %f 0 %f %f\n", -z, x, -theta);
- fprintf(Output_file, " translation %.3f %.3f %.3f\n", o_x, o_y - y0, o_z);
- fprintf(Output_file, " }\n");
-}
-
-static void vrml_end_edge(void)
-{
- if (IsSegment) {
- finishSegment();
- }
- fprintf(Output_file, "] }\n");
-}
-
-static void vrml_begin_context(void)
-{
- assert(SP + 1 < MAXNEST);
- cstk[SP + 1] = cstk[SP];
- SP++;
-}
-
-static void vrml_end_context(void)
-{
- int psp = SP - 1;
- assert(SP > 0);
- if (cstk[SP].font_was_set)
- vrml_font(&(cstk[psp]));
- /* free(cstk[psp].fontfam); */
- SP = psp;
-}
-
-static void vrml_set_font(char *name, double size)
-{
- char *p, *q;
- context_t *cp;
-
- cp = &(cstk[SP]);
- cp->font_was_set = TRUE;
- cp->fontsz = 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;
- vrml_font(&cstk[SP]);
-}
-
-static void vrml_set_style(char **s)
-{
- char *line;
- context_t *cp;
-
- cp = &(cstk[SP]);
- while ((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, "bold"))
- cp->penwidth = WIDTH_BOLD;
- else if (streq(line, "invis"))
- cp->pen = P_NONE;
- else if (streq(line, "filled"))
- cp->fill = P_SOLID;
- else if (streq(line, "unfilled"))
- cp->fill = P_NONE;
- else {
- agerr(AGWARN,
- "vrml_set_style: unsupported style %s - ignoring\n",
- line);
- }
- }
-}
-
-static void vrml_textpara(point p, textpara_t * para)
-{
- char *fontlist, *err;
- pointf mp;
- int brect[8];
- extern gdFontPtr gdFontSmall;
-
- if (Obj != NODE)
- return;
- cstk[SP].pencolor_ix = vrml_resolve_color(cstk[SP].pencolor);
- fontlist = (char*)(para->layout); /* FIXME - kluge */
-
- switch (para->just) {
- case 'l':
- break;
- case 'r':
- p.x -= para->width;
- break;
- default:
- case 'n':
- p.x -= para->width / 2;
- break;
- }
-/* p.y += cstk[SP].fontsz*2/3; */
-
- mp = vrml_node_point(p);
-
- err = gdImageStringFT(im, brect, cstk[SP].pencolor_ix, fontlist,
- cstk[SP].fontsz, (Rot ? 90.0 : 0.0) * PI / 180.0,
- ROUND(mp.x), ROUND(mp.y), para->str);
- if (err) {
- /* revert to builtin fonts */
- gdImageString(im, gdFontSmall, ROUND(mp.x), ROUND(mp.y),
- (unsigned char *) para->str, cstk[SP].pencolor_ix);
- }
-}
-
-/* interpolate_zcoord:
- * Given 2 points in 3D p = (fst.x,fst.y,fstz) and q = (snd.x, snd.y, sndz),
- * and a point p1 in the xy plane lying on the line segment connecting
- * the projections of the p and q, find the z coordinate of p1 when it
- * is projected up onto the segment (p,q) in 3-space.
- *
- * Why the special case for ranks? Is the arithmetic really correct?
- */
-static double
-interpolate_zcoord(pointf p1, point fst, double fstz, point snd, double sndz)
-{
- double len, d, rv;
-
- if (fstz == sndz)
- return fstz;
- if (ND_rank(Curedge->tail) != ND_rank(Curedge->head)) {
- if (snd.y == fst.y)
- rv = (fstz + sndz) / 2.0;
- else
- rv = fstz + (sndz - fstz) * (p1.y - fst.y) / (snd.y - fst.y);
- }
- else {
- len = DIST(fst, snd);
- d = DIST(p1, fst)/len;
- rv = fstz + d*(sndz - fstz);
- }
- return rv;
-}
-
-/* collinear:
- * Return true if the 3 points starting at A are collinear.
- */
-static int
-collinear (point * A)
-{
- Ppoint_t a, b, c;
- double w;
-
- a.x = A->x;
- a.y = A->y;
- A++;
- b.x = A->x;
- b.y = A->y;
- A++;
- c.x = A->x;
- c.y = A->y;
-
- w = wind(a,b,c);
- return (fabs(w) <= 1);
-}
-
-/* straight:
- * Return true if bezier points are collinear
- * At present, just check with 4 points, the common case.
- */
-static int
-straight (point * A, int n)
-{
- if (n != 4) return 0;
- return (collinear(A) && collinear(A+1));
-}
-
-static void
-doSegment (point* A, point p0, double z0, point p1, double z1)
-{
- double d1, d0;
- double delx, dely, delz;
-
- delx = p0.x - p1.x;
- dely = p0.y - p1.y;
- delz = z0 - z1;
- EdgeLen = sqrt(delx*delx + dely*dely + delz*delz);
- d0 = DIST(A[0],p0);
- d1 = DIST(A[3],p1);
- CylHt = EdgeLen - d0 - d1;
- TailHt = HeadHt = 0;
-
- IsSegment = 1;
- fprintf(Output_file, "Transform {\n");
- fprintf(Output_file, " children [\n");
- fprintf(Output_file, " Shape {\n");
- fprintf(Output_file, " geometry Cylinder {\n");
- fprintf(Output_file, " bottom FALSE top FALSE\n");
- fprintf(Output_file, " height %f radius %d }\n", CylHt, cstk[SP].penwidth);
- fprintf(Output_file, " appearance Appearance {\n");
- fprintf(Output_file, " material Material {\n");
- fprintf(Output_file, " ambientIntensity 0.33\n");
- fprintf(Output_file, " diffuseColor %f %f %f\n",
- cstk[SP].r,cstk[SP].g,cstk[SP].b);
- fprintf(Output_file, " }\n");
- fprintf(Output_file, " }\n");
- fprintf(Output_file, " }\n");
-}
-
-static void
-vrml_bezier(point * A, int n, int arrow_at_start, int arrow_at_end, int filled)
-{
- pointf p1, V[4];
- int i, j, step;
- double fstz, sndz;
- context_t *cp;
-
- assert(Obj == EDGE);
-
- cp = &(cstk[SP]);
- if (cp->pen == P_NONE)
- return;
- fstz = Fstz = late_double(Curedge->tail, N_z, 0.0, -1000.0);
- sndz = Sndz = late_double(Curedge->head, N_z, 0.0, -MAXFLOAT);
- if (straight(A,n)) {
- doSegment (A, ND_coord_i(Curedge->tail),Fstz,ND_coord_i(Curedge->head),Sndz);
- return;
- }
-
- fprintf(Output_file, "Shape { geometry Extrusion {\n");
- fprintf(Output_file, " spine [");
- 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;
- }
- for (step = 0; step <= BEZIERSUBDIVISION; step++) {
- p1 = Bezier(V, 3, (double) step / BEZIERSUBDIVISION, NULL,
- NULL);
- fprintf(Output_file, " %.3f %.3f %.3f", p1.x, p1.y,
- interpolate_zcoord(p1, A[0], fstz, A[n - 1], sndz));
- }
- }
- fprintf(Output_file, " ]\n");
- fprintf(Output_file, " crossSection [ %d %d, %d %d, %d %d, %d %d ]\n",
- (cp->penwidth), (cp->penwidth), -(cp->penwidth),
- (cp->penwidth), -(cp->penwidth), -(cp->penwidth),
- (cp->penwidth), -(cp->penwidth));
- fprintf(Output_file, "}\n");
- fprintf(Output_file, " appearance DEF E%d Appearance {\n",
- Curedge->id);
- fprintf(Output_file, " material Material {\n");
- fprintf(Output_file, " ambientIntensity 0.33\n");
- fprintf(Output_file, " diffuseColor %.3f %.3f %.3f\n",
- cstk[SP].r, cstk[SP].g, cstk[SP].b);
- fprintf(Output_file, " }\n");
- fprintf(Output_file, " }\n");
- fprintf(Output_file, "}\n");
-}
-
-/* doArrowhead:
- * If edge is straight, we attach a cone to the edge as a group.
- */
-static void
-doArrowhead (point* A)
-{
- double rad, ht, y;
- pointf p0; /* center of triangle base */
- point tp,hp;
-
- p0.x = (A[0].x + A[2].x)/2.0;
- p0.y = (A[0].y + A[2].y)/2.0;
- rad = DIST(A[0],A[2])/2.0;
- ht = DIST(p0,A[1]);
-
- y = (CylHt + ht)/2.0;
-
- tp = ND_coord_i(Curedge->tail);
- hp = ND_coord_i(Curedge->head);
- fprintf(Output_file, "Transform {\n");
- if (DIST2(A[1], tp) < DIST2(A[1], hp)) {
- TailHt = ht;
- fprintf(Output_file, " translation 0 -%.3f 0\n", y);
- fprintf(Output_file, " rotation 0 0 1 %.3f\n", PI);
- }
- else {
- HeadHt = ht;
- fprintf(Output_file, " translation 0 %.3f 0\n", y);
- }
- fprintf(Output_file, " children [\n");
- fprintf(Output_file, " Shape {\n");
- fprintf(Output_file, " geometry Cone {bottomRadius %.3f height %.3f }\n",
- rad, ht);
- fprintf(Output_file, " appearance Appearance {\n");
- fprintf(Output_file, " material Material {\n");
- fprintf(Output_file, " ambientIntensity 0.33\n");
- fprintf(Output_file, " diffuseColor %f %f %f\n", cstk[SP].r,cstk[SP].g,cstk[SP].b);
- fprintf(Output_file, " }\n");
- fprintf(Output_file, " }\n");
- fprintf(Output_file, " }\n");
- fprintf(Output_file, " ]\n");
- fprintf(Output_file, "}\n");
-}
-
-static void vrml_polygon(point * A, int n, int filled)
-{
- pointf p, mp;
- int i;
- gdPoint *points;
- int style[20];
- int pen, width;
- gdImagePtr brush = NULL;
- double theta, z;
- node_t *endp;
- char somebuf[1024];
-
- switch (Obj) {
- case NONE: /* GRAPH */
- fprintf(Output_file, " Background { skyColor %.3f %.3f %.3f }\n",
- cstk[SP].r, cstk[SP].g, cstk[SP].b);
- Saw_skycolor = TRUE;
- break;
-
- case NODE:
-
- if (cstk[SP].pen != P_NONE) {
- cstk[SP].pencolor_ix = vrml_resolve_color(cstk[SP].pencolor);
- cstk[SP].fillcolor_ix = vrml_resolve_color(cstk[SP].fillcolor);
- if (cstk[SP].pen == P_DASHED) {
- for (i = 0; i < 10; i++)
- style[i] = cstk[SP].pencolor_ix;
- 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_ix;
- for (; i < 12; i++)
- style[i] = gdTransparent;
- gdImageSetStyle(im, style, 12);
- pen = gdStyled;
- } else {
- pen = cstk[SP].pencolor_ix;
- }
- 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_ix);
- gdImageSetBrush(im, brush);
- if (pen == gdStyled)
- pen = gdStyledBrushed;
- else
- pen = gdBrushed;
- }
- points = N_GNEW(n, gdPoint);
- for (i = 0; i < n; i++) {
- mp = vrml_node_point(A[i]);
- points[i].x = ROUND(mp.x);
- points[i].y = ROUND(mp.y);
- }
- if (filled)
- gdImageFilledPolygon(im, points, n, cstk[SP].fillcolor_ix);
- gdImagePolygon(im, points, n, pen);
- free(points);
- if (brush)
- gdImageDestroy(brush);
- }
-
- z = late_double(Curnode, N_z, 0.0, -MAXFLOAT);
-
- fprintf(Output_file, "Shape {\n");
- fprintf(Output_file, " appearance Appearance {\n");
- fprintf(Output_file, " material Material {\n");
- fprintf(Output_file, " ambientIntensity 0.33\n");
- fprintf(Output_file, " diffuseColor 1 1 1\n");
- fprintf(Output_file, " }\n");
- fprintf(Output_file, " texture ImageTexture { url \"%s\" }\n",
- nodeURL(Curnode, somebuf));
- fprintf(Output_file, " }\n");
- fprintf(Output_file, " geometry Extrusion {\n");
- fprintf(Output_file, " crossSection [");
- for (i = 0; i < n; i++) {
- p.x = A[i].x - ND_coord_i(Curnode).x;
- p.y = A[i].y - ND_coord_i(Curnode).y;
- fprintf(Output_file, " %.3f %.3f,", p.x, p.y);
- }
- p.x = A[0].x - ND_coord_i(Curnode).x;
- p.y = A[0].y - ND_coord_i(Curnode).y;
- fprintf(Output_file, " %.3f %.3f ]\n", p.x, p.y);
- fprintf(Output_file, " spine [ %d %d %.3f, %d %d %.3f ]\n",
- ND_coord_i(Curnode).x, ND_coord_i(Curnode).y, z - .01,
- ND_coord_i(Curnode).x, ND_coord_i(Curnode).y, z + .01);
- fprintf(Output_file, " }\n");
- fprintf(Output_file, "}\n");
- break;
-
- case EDGE:
- if (cstk[SP].pen == P_NONE)
- return;
- if (n != 3) {
- static int flag;
- if (!flag) {
- flag++;
- agerr(AGWARN,
- "vrml_polygon: non-triangle arrowheads not supported - ignoring\n");
- }
- }
- if (IsSegment) {
- doArrowhead (A);
- return;
- }
- p.x = p.y = 0.0;
- for (i = 0; i < n; i++) {
- p.x += A[i].x;
- p.y += A[i].y;
- }
- p.x = p.x / n;
- p.y = p.y / n;
-
- /* it is bad to know that A[1] is the aiming point, but we do */
- theta =
- atan2((A[0].y + A[2].y) / 2.0 - A[1].y,
- (A[0].x + A[2].x) / 2.0 - A[1].x) + PI / 2.0;
-
-
- /* this is gruesome, but how else can we get z coord */
- if (DIST2(p, ND_coord_i(Curedge->tail)) <
- DIST2(p, ND_coord_i(Curedge->head)))
- endp = Curedge->tail;
- else
- endp = Curedge->head;
- z = late_double(endp, N_z, 0.0, -MAXFLOAT);
-
- /* FIXME: arrow vector ought to follow z coord of bezier */
- fprintf(Output_file, "Transform {\n");
- fprintf(Output_file, " translation %.3f %.3f %.3f\n", p.x, p.y,
- z);
- fprintf(Output_file, " children [\n");
- fprintf(Output_file, " Transform {\n");
- fprintf(Output_file, " rotation 0 0 1 %.3f\n", theta);
- fprintf(Output_file, " children [\n");
- fprintf(Output_file, " Shape {\n");
- fprintf(Output_file,
- " geometry Cone {bottomRadius %.3f height %.3f }\n",
- cstk[SP].penwidth * 2.5, cstk[SP].penwidth * 10.0);
- fprintf(Output_file, " appearance USE E%d\n",
- Curedge->id);
- fprintf(Output_file, " }\n");
- fprintf(Output_file, " ]\n");
- fprintf(Output_file, " }\n");
- fprintf(Output_file, " ]\n");
- fprintf(Output_file, "}\n");
- break;
- default:
- break;
- }
-}
-
-/* doSphere:
- * Output sphere in VRML for point nodes.
- */
-static void
-doSphere (point p, int rx, int ry)
-{
- pointf mp;
- double z;
-
- if (!(strcmp(cstk[SP].fillcolor, "transparent"))) {
- return;
- }
-
- mp.x = ND_coord_i(Curnode).x;
- mp.y = ND_coord_i(Curnode).y;
-
- z = late_double(Curnode, N_z, 0.0, -MAXFLOAT);
-
- fprintf(Output_file, "Transform {\n");
- fprintf(Output_file, " translation %.3f %.3f %.3f\n", mp.x, mp.y,
- z);
- fprintf(Output_file, " scale %d %d %d\n", rx, rx, rx);
- fprintf(Output_file, " children [\n");
- fprintf(Output_file, " Transform {\n");
- fprintf(Output_file, " children [\n");
- fprintf(Output_file, " Shape {\n");
- fprintf(Output_file,
- " geometry Sphere { radius 1.0 }\n");
- fprintf(Output_file, " appearance Appearance {\n");
- fprintf(Output_file, " material Material {\n");
- fprintf(Output_file, " ambientIntensity 0.33\n");
- fprintf(Output_file, " diffuseColor %f %f %f\n",
- cstk[SP].r,cstk[SP].g,cstk[SP].b);
- fprintf(Output_file, " }\n");
- fprintf(Output_file, " }\n");
- fprintf(Output_file, " }\n");
- fprintf(Output_file, " ]\n");
- fprintf(Output_file, " }\n");
- fprintf(Output_file, " ]\n");
- fprintf(Output_file, "}\n");
-}
-
-static void vrml_ellipse(point p, int rx, int ry, int filled)
-{
- pointf mp;
- int i;
- node_t *endp;
- int style[40]; /* need 2* size for arcs, I don't know why */
- int pen, width;
- gdImagePtr brush = NULL;
- double z;
- char somebuf[1024];
-
- switch (Obj) {
- case NODE:
- if (shapeOf(Curnode) == SH_POINT) {
- doSphere (p, rx, ry);
- return;
- }
- cstk[SP].pencolor_ix = vrml_resolve_color(cstk[SP].pencolor);
- cstk[SP].fillcolor_ix = vrml_resolve_color(cstk[SP].fillcolor);
- if (cstk[SP].pen != P_NONE) {
- if (cstk[SP].pen == P_DASHED) {
- for (i = 0; i < 20; i++)
- style[i] = cstk[SP].pencolor_ix;
- 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_ix;
- for (; i < 24; i++)
- style[i] = gdTransparent;
- gdImageSetStyle(im, style, 24);
- pen = gdStyled;
- } else {
- pen = cstk[SP].pencolor_ix;
- }
- 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_ix);
- gdImageSetBrush(im, brush);
- if (pen == gdStyled)
- pen = gdStyledBrushed;
- else
- pen = gdBrushed;
- }
- mp = vrml_node_point(p);
-
- if (filled) {
- gdImageFilledEllipse(im, ROUND(mp.x), ROUND(mp.y),
- ROUND(Scale * (rx + rx)),
- ROUND(Scale * (ry + ry)),
- cstk[SP].fillcolor_ix);
- }
- gdImageArc(im, ROUND(mp.x), ROUND(mp.y),
- ROUND(Scale * (rx + rx)), ROUND(Scale * (ry + ry)),
- 0, 360, pen);
- if (brush)
- gdImageDestroy(brush);
- }
-
- mp.x = ND_coord_i(Curnode).x;
- mp.y = ND_coord_i(Curnode).y;
-
- z = late_double(Curnode, N_z, 0.0, -MAXFLOAT);
-
- fprintf(Output_file, "Transform {\n");
- fprintf(Output_file, " translation %.3f %.3f %.3f\n", mp.x, mp.y,
- z);
- fprintf(Output_file, " scale %d %d 1\n", rx, ry);
- fprintf(Output_file, " children [\n");
- fprintf(Output_file, " Transform {\n");
- fprintf(Output_file, " rotation 1 0 0 1.57\n");
- fprintf(Output_file, " children [\n");
- fprintf(Output_file, " Shape {\n");
- fprintf(Output_file,
- " geometry Cylinder { side FALSE }\n");
- fprintf(Output_file, " appearance Appearance {\n");
- fprintf(Output_file, " material Material {\n");
- fprintf(Output_file, " ambientIntensity 0.33\n");
- fprintf(Output_file, " diffuseColor 1 1 1\n");
- fprintf(Output_file, " }\n");
- fprintf(Output_file,
- " texture ImageTexture { url \"%s\" }\n",
- nodeURL(Curnode, somebuf));
- fprintf(Output_file, " }\n");
- fprintf(Output_file, " }\n");
- fprintf(Output_file, " ]\n");
- fprintf(Output_file, " }\n");
- fprintf(Output_file, " ]\n");
- fprintf(Output_file, "}\n");
- break;
- case EDGE:
- if (cstk[SP].pen == P_NONE)
- return;
- mp.x = (double) p.x;
- mp.y = (double) p.y;
- /* this is gruesome, but how else can we get z coord */
- if (DIST2(mp, ND_coord_i(Curedge->tail)) <
- DIST2(mp, ND_coord_i(Curedge->head)))
- endp = Curedge->tail;
- else
- endp = Curedge->head;
- z = late_double(endp, N_z, 0.0, -MAXFLOAT);
-
- fprintf(Output_file, "Transform {\n");
- fprintf(Output_file, " translation %.3f %.3f %.3f\n", mp.x, mp.y,
- z);
- fprintf(Output_file, " children [\n");
- fprintf(Output_file, " Shape {\n");
- fprintf(Output_file, " geometry Sphere {radius %.3f }\n",
- (double) rx);
- fprintf(Output_file, " appearance USE E%d\n", Curedge->id);
- fprintf(Output_file, " }\n");
- fprintf(Output_file, " ]\n");
- fprintf(Output_file, "}\n");
- break;
- default:
- break;
- }
-}
-
-static void vrml_polyline(point * A, int n)
-{
-/*
- pointf p, p1;
- int i;
-
- if (cstk[SP].pen != P_NONE) {
- p.x = A[0].x;
- p.y = A[0].y;
- for (i = 1; i < n; i++) {
- p1.x = A[i].x;
- p1.y = A[i].y;
-#ifdef NONEOFTHISEITHER
- if (cstk[SP].pen == P_DASHED) {
- gdImageDashedLine(im, ROUND(p.x), ROUND(p.y),
- ROUND(p1.x), ROUND(p1.y), cstk[SP].color_ix);
- } else {
- gdImageLine(im, ROUND(p.x), ROUND(p.y),
- ROUND(p1.x), ROUND(p1.y), cstk[SP].color_ix);
- }
-#endif
- p.x = p1.x;
- p.y = p1.y;
- }
- }
-*/
-}
-
-static void vrml_usershape(usershape_t *us, boxf b, point *A, int n, bool filled)
-{
-/* FIXME */
- vrml_polygon(A, n, filled);
-}
-
-codegen_t VRML_CodeGen = {
- 0, /* vrml_reset */
- vrml_begin_job, 0, /* vrml_end_job */
- vrml_begin_graph, vrml_end_graph,
- vrml_begin_page, 0, /* vrml_end_page */
- 0, /* vrml_begin_layer */ 0, /* vrml_end_layer */
- 0, /* vrml_begin_cluster */ 0, /* vrml_end_cluster */
- 0, /* vrml_begin_nodes */ 0, /* vrml_end_nodes */
- 0, /* vrml_begin_edges */ 0, /* vrml_end_edges */
- vrml_begin_node, vrml_end_node,
- vrml_begin_edge, vrml_end_edge,
- vrml_begin_context, vrml_end_context,
- 0, /* vrml_begin_anchor */ 0, /* vrml_end_anchor */
- vrml_set_font, vrml_textpara,
- vrml_set_pencolor, vrml_set_fillcolor, vrml_set_style,
- vrml_ellipse, vrml_polygon,
- vrml_bezier, vrml_polyline,
- 0, /* bezier_has_arrows */
- 0, /* comment */
- vrml_usershape
-};
-#endif /* HAVE_GD_PNG */