Graphviz  2.29.20120524.0446
lib/common/psusershape.c
Go to the documentation of this file.
00001 /* $Id$ $Revision$ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /*************************************************************************
00005  * Copyright (c) 2011 AT&T Intellectual Property 
00006  * All rights reserved. This program and the accompanying materials
00007  * are made available under the terms of the Eclipse Public License v1.0
00008  * which accompanies this distribution, and is available at
00009  * http://www.eclipse.org/legal/epl-v10.html
00010  *
00011  * Contributors: See CVS logs. Details at http://www.graphviz.org/
00012  *************************************************************************/
00013 
00014 #ifndef WIN32
00015 #include <unistd.h>
00016 #endif
00017 
00018 #include <sys/stat.h>
00019 
00020 #include "render.h"
00021 #include "gvio.h"
00022 
00023 static int N_EPSF_files;
00024 static Dict_t *EPSF_contents;
00025 
00026 static void ps_image_free(Dict_t * dict, usershape_t * p, Dtdisc_t * disc)
00027 {
00028     free(p->data);
00029 }
00030 
00031 static Dtdisc_t ImageDictDisc = {
00032     offsetof(usershape_t, name),/* key */
00033     -1,                         /* size */
00034     0,                          /* link offset */
00035     NIL(Dtmake_f),
00036     (Dtfree_f) ps_image_free,
00037     NIL(Dtcompar_f),
00038     NIL(Dthash_f),
00039     NIL(Dtmemory_f),
00040     NIL(Dtevent_f)
00041 };
00042 
00043 static usershape_t *user_init(const char *str)
00044 {
00045     char *contents;
00046     char line[BUFSIZ];
00047     FILE *fp;
00048     struct stat statbuf;
00049     int saw_bb, must_inline, rc;
00050     int lx, ly, ux, uy;
00051     usershape_t *us;
00052 
00053     if (!EPSF_contents)
00054         EPSF_contents = dtopen(&ImageDictDisc, Dtoset);
00055 
00056     us = dtmatch(EPSF_contents, str);
00057     if (us)
00058         return us;
00059 
00060     if (!(fp = fopen(str, "r"))) {
00061         agerr(AGWARN, "couldn't open epsf file %s\n", str);
00062         return NULL;
00063     }
00064     /* try to find size */
00065     saw_bb = must_inline = FALSE;
00066     while (fgets(line, sizeof(line), fp)) {
00067         if (sscanf
00068             (line, "%%%%BoundingBox: %d %d %d %d", &lx, &ly, &ux, &uy) == 4) {
00069             saw_bb = TRUE;
00070         }
00071         if ((line[0] != '%') && strstr(line,"read")) must_inline = TRUE;
00072         if (saw_bb && must_inline) break;
00073     }
00074 
00075     if (saw_bb) {
00076         us = GNEW(usershape_t);
00077         us->x = lx;
00078         us->y = ly;
00079         us->w = ux - lx;
00080         us->y = uy - ly;
00081         us->name = str;
00082         us->macro_id = N_EPSF_files++;
00083         fstat(fileno(fp), &statbuf);
00084         contents = us->data = N_GNEW(statbuf.st_size + 1, char);
00085         fseek(fp, 0, SEEK_SET);
00086         rc = fread(contents, statbuf.st_size, 1, fp);
00087         contents[statbuf.st_size] = '\0';
00088         dtinsert(EPSF_contents, us);
00089         us->must_inline = must_inline;
00090     } else {
00091         agerr(AGWARN, "BoundingBox not found in epsf file %s\n", str);
00092         us = NULL;
00093     }
00094     fclose(fp);
00095     return us;
00096 }
00097 
00098 void epsf_init(node_t * n)
00099 {
00100     epsf_t *desc;
00101     const char *str;
00102     usershape_t *us;
00103     int dx, dy;
00104 
00105     if ((str = safefile(agget(n, "shapefile")))) {
00106         us = user_init(str);
00107         if (!us)
00108             return;
00109         dx = us->w;
00110         dy = us->h;
00111         ND_width(n) = PS2INCH(dx);
00112         ND_height(n) = PS2INCH(dy);
00113         ND_shape_info(n) = desc = NEW(epsf_t);
00114         desc->macro_id = us->macro_id;
00115         desc->offset.x = -us->x - (dx) / 2;
00116         desc->offset.y = -us->y - (dy) / 2;
00117     } else
00118         agerr(AGWARN, "shapefile not set or not found for epsf node %s\n", agnameof(n));
00119 }
00120 
00121 void epsf_free(node_t * n)
00122 {
00123 
00124     if (ND_shape_info(n))
00125         free(ND_shape_info(n));
00126 }
00127 
00128 
00129 /* cat_libfile:
00130  * Write library files onto the given file pointer.
00131  * arglib is an NULL-terminated array of char*
00132  * Each non-trivial entry should be the name of a file to be included.
00133  * stdlib is an NULL-terminated array of char*
00134  * Each of these is a line of a standard library to be included.
00135  * If any item in arglib is the empty string, the stdlib is not used.
00136  * The stdlib is printed first, if used, followed by the user libraries.
00137  * We check that for web-safe file usage.
00138  */
00139 void cat_libfile(GVJ_t * job, const char **arglib, const char **stdlib)
00140 {
00141     FILE *fp;
00142     const char **s, *bp, *p, *path;
00143     int i;
00144     boolean use_stdlib = TRUE;
00145 
00146     /* check for empty string to turn off stdlib */
00147     if (arglib) {
00148         for (i = 0; use_stdlib && ((p = arglib[i])); i++) {
00149             if (*p == '\0')
00150                 use_stdlib = FALSE;
00151         }
00152     }
00153     if (use_stdlib)
00154         for (s = stdlib; *s; s++) {
00155             gvputs(job, *s);
00156             gvputs(job, "\n");
00157         }
00158     if (arglib) {
00159         for (i = 0; (p = arglib[i]) != 0; i++) {
00160             if (*p == '\0')
00161                 continue;       /* ignore empty string */
00162             path = safefile(p);    /* make sure filename is okay */
00163             if (!path) {
00164                 agerr(AGWARN, "can't find library file %s\n", p);
00165             }
00166             else if ((fp = fopen(path, "r"))) {
00167                 while ((bp = Fgets(fp)))
00168                     gvputs(job, bp);
00169                 gvputs(job, "\n"); /* append a newline just in case */
00170                 fclose (fp);
00171             } else
00172                 agerr(AGWARN, "can't open library file %s\n", path);
00173         }
00174     }
00175 }
00176 
00177 #define FILTER_EPSF 1
00178 #ifdef FILTER_EPSF
00179 /* this removes EPSF DSC comments that, when nested in another
00180  * document, cause errors in Ghostview and other Postscript
00181  * processors (although legal according to the Adobe EPSF spec).
00182  *
00183  * N.B. PostScript lines can end with \n, \r or \r\n.
00184  */
00185 void epsf_emit_body(GVJ_t *job, usershape_t *us)
00186 {
00187     char *p;
00188     char c;
00189     p = us->data;
00190     while (*p) {
00191         /* skip %%EOF lines */
00192         if ((p[0] == '%') && (p[1] == '%')
00193                 && (!strncasecmp(&p[2], "EOF", 3)
00194                 || !strncasecmp(&p[2], "BEGIN", 5)
00195                 || !strncasecmp(&p[2], "END", 3)
00196                 || !strncasecmp(&p[2], "TRAILER", 7)
00197         )) {
00198             /* check for *p since last line might not end in '\n' */
00199             while ((c = *p) && (c != '\r') && (c != '\n')) p++;
00200             if ((*p == '\r') && (*(p+1) == '\n')) p += 2;
00201             else if (*p) p++;
00202             continue;
00203         }
00204         /* output line */
00205         while ((c = *p) && (c != '\r') && (c != '\n')) {
00206             gvputc(job, c);
00207             p++;
00208         }
00209         if ((*p == '\r') && (*(p+1) == '\n')) p += 2;
00210         else if (*p) p++;
00211         gvputc(job, '\n');
00212     }
00213 }
00214 #else
00215 void epsf_emit_body(GVJ_t *job, usershape_t *us)
00216 {
00217         gvputs(job, us->data);
00218 }
00219 #endif
00220 
00221 void epsf_define(GVJ_t *job)
00222 {
00223     usershape_t *us;
00224 
00225     if (!EPSF_contents)
00226         return;
00227     for (us = dtfirst(EPSF_contents); us; us = dtnext(EPSF_contents, us)) {
00228         if (us->must_inline)
00229             continue;
00230         gvprintf(job, "/user_shape_%d {\n", us->macro_id);
00231         gvputs(job, "%%BeginDocument:\n");
00232         epsf_emit_body(job, us);
00233         gvputs(job, "%%EndDocument\n");
00234         gvputs(job, "} bind def\n");
00235     }
00236 }
00237 
00238 enum {ASCII, LATIN1, NONLATIN};
00239 
00240 /* charsetOf:
00241  * Assuming legal utf-8 input, determine if
00242  * the character value range is ascii, latin-1 or otherwise.
00243  */
00244 static int
00245 charsetOf (char* s)
00246 {
00247     int r = ASCII;
00248     unsigned char c;
00249 
00250     while ((c = *(unsigned char*)s++)) {
00251         if (c < 0x7F) 
00252             continue;
00253         else if ((c & 0xFC) == 0xC0) {
00254             r = LATIN1;
00255             s++; /* eat second byte */
00256         }
00257         else return NONLATIN;
00258     }
00259     return r;
00260 }
00261 
00262 char *ps_string(char *ins, int latin)
00263 {
00264     char *s;
00265     char *base;
00266     static agxbuf  xb;
00267     static int warned;
00268     int rc;
00269 
00270     if (latin)
00271         base = utf8ToLatin1 (ins);
00272     else {
00273         switch (charsetOf (ins)) {
00274         case ASCII :
00275             base = ins;
00276             break;
00277         case LATIN1 :
00278             base = utf8ToLatin1 (ins);
00279             break;
00280         case NONLATIN :
00281             if (!warned) {
00282                 agerr (AGWARN, "UTF-8 input uses non-Latin1 characters which cannot be handled by this PostScript driver\n");
00283                 warned = 1;
00284             }
00285             base = ins;
00286             break;
00287         default:
00288             base = ins;
00289             break;
00290         }
00291     }
00292 
00293     if (xb.buf == NULL)
00294         agxbinit (&xb, 0, NULL);
00295 
00296     rc = agxbputc (&xb, LPAREN);
00297     s = base;
00298     while (*s) {
00299         if ((*s == LPAREN) || (*s == RPAREN) || (*s == '\\'))
00300             rc = agxbputc (&xb, '\\');
00301         rc = agxbputc (&xb, *s++);
00302     }
00303     agxbputc (&xb, RPAREN);
00304     if (base != ins) free (base);
00305     s = agxbuse(&xb);
00306     return s;
00307 }