Graphviz  2.41.20170921.2350
psusershape.c
Go to the documentation of this file.
1 /* $Id$ $Revision$ */
2 /* vim:set shiftwidth=4 ts=8: */
3 
4 /*************************************************************************
5  * Copyright (c) 2011 AT&T Intellectual Property
6  * All rights reserved. This program and the accompanying materials
7  * are made available under the terms of the Eclipse Public License v1.0
8  * which accompanies this distribution, and is available at
9  * http://www.eclipse.org/legal/epl-v10.html
10  *
11  * Contributors: See CVS logs. Details at http://www.graphviz.org/
12  *************************************************************************/
13 
14 #ifndef _WIN32
15 #include <unistd.h>
16 #endif
17 
18 #include <sys/stat.h>
19 
20 #include "render.h"
21 #include "gvio.h"
22 
23 static int N_EPSF_files;
24 static Dict_t *EPSF_contents;
25 
26 static void ps_image_free(Dict_t * dict, usershape_t * p, Dtdisc_t * disc)
27 {
28  free(p->data);
29 }
30 
31 static Dtdisc_t ImageDictDisc = {
32  offsetof(usershape_t, name),/* key */
33  -1, /* size */
34  0, /* link offset */
35  NIL(Dtmake_f),
36  (Dtfree_f) ps_image_free,
37  NIL(Dtcompar_f),
38  NIL(Dthash_f),
39  NIL(Dtmemory_f),
40  NIL(Dtevent_f)
41 };
42 
43 static usershape_t *user_init(const char *str)
44 {
45  char *contents;
46  char line[BUFSIZ];
47  FILE *fp;
48  struct stat statbuf;
49  int saw_bb, must_inline, rc;
50  int lx, ly, ux, uy;
51  usershape_t *us;
52 
53  if (!EPSF_contents)
54  EPSF_contents = dtopen(&ImageDictDisc, Dtoset);
55 
56  us = dtmatch(EPSF_contents, str);
57  if (us)
58  return us;
59 
60  if (!(fp = fopen(str, "r"))) {
61  agerr(AGWARN, "couldn't open epsf file %s\n", str);
62  return NULL;
63  }
64  /* try to find size */
65  saw_bb = must_inline = FALSE;
66  while (fgets(line, sizeof(line), fp)) {
67  if (sscanf
68  (line, "%%%%BoundingBox: %d %d %d %d", &lx, &ly, &ux, &uy) == 4) {
69  saw_bb = TRUE;
70  }
71  if ((line[0] != '%') && strstr(line,"read")) must_inline = TRUE;
72  if (saw_bb && must_inline) break;
73  }
74 
75  if (saw_bb) {
76  us = GNEW(usershape_t);
77  us->x = lx;
78  us->y = ly;
79  us->w = ux - lx;
80  us->y = uy - ly;
81  us->name = str;
82  us->macro_id = N_EPSF_files++;
83  fstat(fileno(fp), &statbuf);
84  contents = us->data = N_GNEW(statbuf.st_size + 1, char);
85  fseek(fp, 0, SEEK_SET);
86  rc = fread(contents, statbuf.st_size, 1, fp);
87  contents[statbuf.st_size] = '\0';
88  dtinsert(EPSF_contents, us);
89  us->must_inline = must_inline;
90  } else {
91  agerr(AGWARN, "BoundingBox not found in epsf file %s\n", str);
92  us = NULL;
93  }
94  fclose(fp);
95  return us;
96 }
97 
98 void epsf_init(node_t * n)
99 {
100  epsf_t *desc;
101  const char *str;
102  usershape_t *us;
103  int dx, dy;
104 
105  if ((str = safefile(agget(n, "shapefile")))) {
106  us = user_init(str);
107  if (!us)
108  return;
109  dx = us->w;
110  dy = us->h;
111  ND_width(n) = PS2INCH(dx);
112  ND_height(n) = PS2INCH(dy);
113  ND_shape_info(n) = desc = NEW(epsf_t);
114  desc->macro_id = us->macro_id;
115  desc->offset.x = -us->x - (dx) / 2;
116  desc->offset.y = -us->y - (dy) / 2;
117  } else
118  agerr(AGWARN, "shapefile not set or not found for epsf node %s\n", agnameof(n));
119 }
120 
121 void epsf_free(node_t * n)
122 {
123 
124  if (ND_shape_info(n))
125  free(ND_shape_info(n));
126 }
127 
128 
129 /* cat_libfile:
130  * Write library files onto the given file pointer.
131  * arglib is an NULL-terminated array of char*
132  * Each non-trivial entry should be the name of a file to be included.
133  * stdlib is an NULL-terminated array of char*
134  * Each of these is a line of a standard library to be included.
135  * If any item in arglib is the empty string, the stdlib is not used.
136  * The stdlib is printed first, if used, followed by the user libraries.
137  * We check that for web-safe file usage.
138  */
139 void cat_libfile(GVJ_t * job, const char **arglib, const char **stdlib)
140 {
141  FILE *fp;
142  const char **s, *bp, *p, *path;
143  int i;
144  boolean use_stdlib = TRUE;
145 
146  /* check for empty string to turn off stdlib */
147  if (arglib) {
148  for (i = 0; use_stdlib && ((p = arglib[i])); i++) {
149  if (*p == '\0')
150  use_stdlib = FALSE;
151  }
152  }
153  if (use_stdlib)
154  for (s = stdlib; *s; s++) {
155  gvputs(job, *s);
156  gvputs(job, "\n");
157  }
158  if (arglib) {
159  for (i = 0; (p = arglib[i]) != 0; i++) {
160  if (*p == '\0')
161  continue; /* ignore empty string */
162  path = safefile(p); /* make sure filename is okay */
163  if (!path) {
164  agerr(AGWARN, "can't find library file %s\n", p);
165  }
166  else if ((fp = fopen(path, "r"))) {
167  while ((bp = Fgets(fp)))
168  gvputs(job, bp);
169  gvputs(job, "\n"); /* append a newline just in case */
170  fclose (fp);
171  } else
172  agerr(AGWARN, "can't open library file %s\n", path);
173  }
174  }
175 }
176 
177 #define FILTER_EPSF 1
178 #ifdef FILTER_EPSF
179 /* this removes EPSF DSC comments that, when nested in another
180  * document, cause errors in Ghostview and other Postscript
181  * processors (although legal according to the Adobe EPSF spec).
182  *
183  * N.B. PostScript lines can end with \n, \r or \r\n.
184  */
186 {
187  char *p;
188  char c;
189  p = us->data;
190  while (*p) {
191  /* skip %%EOF lines */
192  if ((p[0] == '%') && (p[1] == '%')
193  && (!strncasecmp(&p[2], "EOF", 3)
194  || !strncasecmp(&p[2], "BEGIN", 5)
195  || !strncasecmp(&p[2], "END", 3)
196  || !strncasecmp(&p[2], "TRAILER", 7)
197  )) {
198  /* check for *p since last line might not end in '\n' */
199  while ((c = *p) && (c != '\r') && (c != '\n')) p++;
200  if ((*p == '\r') && (*(p+1) == '\n')) p += 2;
201  else if (*p) p++;
202  continue;
203  }
204  /* output line */
205  while ((c = *p) && (c != '\r') && (c != '\n')) {
206  gvputc(job, c);
207  p++;
208  }
209  if ((*p == '\r') && (*(p+1) == '\n')) p += 2;
210  else if (*p) p++;
211  gvputc(job, '\n');
212  }
213 }
214 #else
215 void epsf_emit_body(GVJ_t *job, usershape_t *us)
216 {
217  gvputs(job, us->data);
218 }
219 #endif
220 
221 void epsf_define(GVJ_t *job)
222 {
223  usershape_t *us;
224 
225  if (!EPSF_contents)
226  return;
227  for (us = dtfirst(EPSF_contents); us; us = dtnext(EPSF_contents, us)) {
228  if (us->must_inline)
229  continue;
230  gvprintf(job, "/user_shape_%d {\n", us->macro_id);
231  gvputs(job, "%%BeginDocument:\n");
232  epsf_emit_body(job, us);
233  gvputs(job, "%%EndDocument\n");
234  gvputs(job, "} bind def\n");
235  }
236 }
237 
239 
240 /* charsetOf:
241  * Assuming legal utf-8 input, determine if
242  * the character value range is ascii, latin-1 or otherwise.
243  */
244 static int
245 charsetOf (char* s)
246 {
247  int r = ASCII;
248  unsigned char c;
249 
250  while ((c = *(unsigned char*)s++)) {
251  if (c < 0x7F)
252  continue;
253  else if ((c & 0xFC) == 0xC0) {
254  r = LATIN1;
255  s++; /* eat second byte */
256  }
257  else return NONLATIN;
258  }
259  return r;
260 }
261 
262 /* ps_string:
263  * internally, strings are always utf8. If chset is CHAR_LATIN1, we know
264  * all of the values can be represented by latin-1; if chset is
265  * CHAR_UTF8, we use the string as is; otherwise, we test to see if the
266  * string is ascii, latin-1 or non-latin, and translate to latin-l if
267  * possible.
268  */
269 char *ps_string(char *ins, int chset)
270 {
271  char *s;
272  char *base;
273  static agxbuf xb;
274  static int warned;
275 
276  switch (chset) {
277  case CHAR_UTF8 :
278  base = ins;
279  break;
280  case CHAR_LATIN1 :
281  base = utf8ToLatin1 (ins);
282  break;
283  default :
284  switch (charsetOf (ins)) {
285  case ASCII :
286  base = ins;
287  break;
288  case LATIN1 :
289  base = utf8ToLatin1 (ins);
290  break;
291  case NONLATIN :
292  if (!warned) {
293  agerr (AGWARN, "UTF-8 input uses non-Latin1 characters which cannot be handled by this PostScript driver\n");
294  warned = 1;
295  }
296  base = ins;
297  break;
298  default:
299  base = ins;
300  break;
301  }
302  }
303 
304  if (xb.buf == NULL)
305  agxbinit (&xb, 0, NULL);
306 
307  agxbputc (&xb, LPAREN);
308  s = base;
309  while (*s) {
310  if ((*s == LPAREN) || (*s == RPAREN) || (*s == '\\'))
311  agxbputc (&xb, '\\');
312  agxbputc (&xb, *s++);
313  }
314  agxbputc (&xb, RPAREN);
315  if (base != ins) free (base);
316  s = agxbuse(&xb);
317  return s;
318 }
int(* Dtcompar_f)(Dt_t *, void *, void *, Dtdisc_t *)
Definition: cdt.h:40
unsigned int(* Dthash_f)(Dt_t *, void *, Dtdisc_t *)
Definition: cdt.h:41
unsigned char * buf
Definition: agxbuf.h:35
int gvputc(GVJ_t *job, int c)
Definition: gvdevice.c:280
#define RPAREN
Definition: const.h:19
void *(* Dtmake_f)(Dt_t *, void *, Dtdisc_t *)
Definition: cdt.h:38
#define agxbuse(X)
Definition: agxbuf.h:83
CDT_API Dtmethod_t * Dtoset
Definition: cdt.h:166
void * data
Definition: usershape.h:61
Definition: render.h:54
Definition: cdt.h:80
#define dtfirst(d)
Definition: cdt.h:254
boolean must_inline
Definition: usershape.h:55
int agerr(agerrlevel_t level, const char *fmt,...)
Definition: agerror.c:141
Definition: gvcjob.h:271
#define ND_shape_info(n)
Definition: types.h:535
char * ps_string(char *ins, int chset)
Definition: psusershape.c:269
int x
Definition: geom.h:26
Definition: cgraph.h:388
CDT_API Dt_t * dtopen(Dtdisc_t *, Dtmethod_t *)
Definition: dtopen.c:9
#define LPAREN
Definition: const.h:18
#define agxbputc(X, C)
Definition: agxbuf.h:77
#define PS2INCH(a_points)
Definition: geom.h:69
char * agget(void *obj, char *name)
Definition: attr.c:428
int macro_id
Definition: usershape.h:54
struct path path
int gvputs(GVJ_t *job, const char *s)
Definition: gvdevice.c:270
void epsf_init(node_t *n)
Definition: psusershape.c:98
#define NIL(t)
Definition: dthdr.h:13
void epsf_define(GVJ_t *job)
Definition: psusershape.c:221
#define CHAR_UTF8
Definition: const.h:204
void cat_libfile(GVJ_t *job, const char **arglib, const char **stdlib)
Definition: psusershape.c:139
int strncasecmp(const char *s1, const char *s2, unsigned int n)
Definition: strncasecmp.c:20
CGRAPH_API char * agnameof(void *)
Definition: id.c:143
int macro_id
Definition: render.h:55
#define ND_height(n)
Definition: types.h:504
#define dtmatch(d, o)
Definition: cdt.h:261
#define dtnext(d, o)
Definition: cdt.h:255
void epsf_free(node_t *n)
Definition: psusershape.c:121
void *(* Dtmemory_f)(Dt_t *, void *, size_t, Dtdisc_t *)
Definition: cdt.h:36
char * Fgets(FILE *fp)
Definition: utils.c:282
void agxbinit(agxbuf *xb, unsigned int hint, unsigned char *init)
Definition: agxbuf.c:25
Definition: grammar.c:79
void epsf_emit_body(GVJ_t *job, usershape_t *us)
Definition: psusershape.c:185
#define dtinsert(d, o)
Definition: cdt.h:262
#define ND_width(n)
Definition: types.h:542
const char * safefile(const char *filename)
Definition: utils.c:376
#define NULL
Definition: logic.h:39
#define GNEW(t)
Definition: memory.h:37
char * utf8ToLatin1(char *s)
Definition: utils.c:1601
#define CHAR_LATIN1
Definition: const.h:205
int(* Dtevent_f)(Dt_t *, int, void *, Dtdisc_t *)
Definition: cdt.h:42
void(* Dtfree_f)(Dt_t *, void *, Dtdisc_t *)
Definition: cdt.h:39
Definition: cdt.h:99
agxbuf * str
Definition: htmlparse.c:85
Definition: agxbuf.h:34
int y
Definition: geom.h:26
const char * name
Definition: usershape.h:53
#define N_GNEW(n, t)
Definition: agxbuf.c:20
#define FALSE
Definition: cgraph.h:35
point offset
Definition: render.h:56
void gvprintf(GVJ_t *job, const char *format,...)
Definition: gvdevice.c:389
#define NEW(t)
Definition: memory.h:35
#define TRUE
Definition: cgraph.h:38