Graphviz  2.41.20170921.2350
gvrender_core_ps.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 #include "config.h"
15 
16 #include <stdlib.h>
17 #include <string.h>
18 
19 #include "gvplugin_render.h"
20 #include "gvplugin_device.h"
21 #include "gvio.h"
22 #include "agxbuf.h"
23 #include "utils.h"
24 #include "ps.h"
25 
26 /* for CHAR_LATIN1 */
27 #include "const.h"
28 
29 /*
30  * J$: added `pdfmark' URL embedding. PostScript rendered from
31  * dot files with URL attributes will get active PDF links
32  * from Adobe's Distiller.
33  */
34 #define PDFMAX 14400 /* Maximum size of PDF page */
35 
37 
38 static int isLatin1;
39 static char setupLatin1;
40 
41 static void psgen_begin_job(GVJ_t * job)
42 {
43  gvputs(job, "%!PS-Adobe-3.0");
44  if (job->render.id == FORMAT_EPS)
45  gvputs(job, " EPSF-3.0\n");
46  else
47  gvputs(job, "\n");
48  gvprintf(job, "%%%%Creator: %s version %s (%s)\n",
49  job->common->info[0], job->common->info[1], job->common->info[2]);
50 }
51 
52 static void psgen_end_job(GVJ_t * job)
53 {
54  gvputs(job, "%%Trailer\n");
55  if (job->render.id != FORMAT_EPS)
56  gvprintf(job, "%%%%Pages: %d\n", job->common->viewNum);
57  if (job->common->show_boxes == NULL)
58  if (job->render.id != FORMAT_EPS)
59  gvprintf(job, "%%%%BoundingBox: %d %d %d %d\n",
60  job->boundingBox.LL.x, job->boundingBox.LL.y,
61  job->boundingBox.UR.x, job->boundingBox.UR.y);
62  gvputs(job, "end\nrestore\n");
63  gvputs(job, "%%EOF\n");
64 }
65 
66 static void psgen_begin_graph(GVJ_t * job)
67 {
68  obj_state_t *obj = job->obj;
69 
70  setupLatin1 = FALSE;
71 
72  if (job->common->viewNum == 0) {
73  gvprintf(job, "%%%%Title: %s\n", agnameof(obj->u.g));
74  if (job->render.id != FORMAT_EPS)
75  gvputs(job, "%%Pages: (atend)\n");
76  else
77  gvputs(job, "%%Pages: 1\n");
78  if (job->common->show_boxes == NULL) {
79  if (job->render.id != FORMAT_EPS)
80  gvputs(job, "%%BoundingBox: (atend)\n");
81  else
82  gvprintf(job, "%%%%BoundingBox: %d %d %d %d\n",
84  job->pageBoundingBox.UR.x, job->pageBoundingBox.UR.y);
85  }
86  gvputs(job, "%%EndComments\nsave\n");
87  /* include shape library */
88  cat_libfile(job, job->common->lib, ps_txt);
89  /* include epsf */
90  epsf_define(job);
91  if (job->common->show_boxes) {
92  const char* args[2];
93  args[0] = job->common->show_boxes[0];
94  args[1] = NULL;
95  cat_libfile(job, NULL, args);
96  }
97  }
98  isLatin1 = (GD_charset(obj->u.g) == CHAR_LATIN1 ? CHAR_LATIN1 : -1);
99  /* We always setup Latin1. The charset info is always output,
100  * and installing it is cheap. With it installed, we can then
101  * rely on ps_string to convert UTF-8 characters whose encoding
102  * is in the range of Latin-1 into the Latin-1 equivalent and
103  * get the expected PostScript output.
104  */
105  if (!setupLatin1) {
106  gvputs(job, "setupLatin1\n"); /* as defined in ps header */
107  setupLatin1 = TRUE;
108  }
109  /* Set base URL for relative links (for Distiller >= 3.0) */
110  if (obj->url)
111  gvprintf(job, "[ {Catalog} << /URI << /Base %s >> >>\n"
112  "/PUT pdfmark\n", ps_string(obj->url,isLatin1));
113 }
114 
115 static void psgen_begin_layer(GVJ_t * job, char *layername, int layerNum, int numLayers)
116 {
117  gvprintf(job, "%d %d setlayer\n", layerNum, numLayers);
118 }
119 
120 static void psgen_begin_page(GVJ_t * job)
121 {
122  box pbr = job->pageBoundingBox;
123 
124  gvprintf(job, "%%%%Page: %d %d\n",
125  job->common->viewNum + 1, job->common->viewNum + 1);
126  if (job->common->show_boxes == NULL)
127  gvprintf(job, "%%%%PageBoundingBox: %d %d %d %d\n",
128  pbr.LL.x, pbr.LL.y, pbr.UR.x, pbr.UR.y);
129  gvprintf(job, "%%%%PageOrientation: %s\n",
130  (job->rotation ? "Landscape" : "Portrait"));
131  if (job->render.id == FORMAT_PS2)
132  gvprintf(job, "<< /PageSize [%d %d] >> setpagedevice\n",
133  pbr.UR.x, pbr.UR.y);
134  gvprintf(job, "%d %d %d beginpage\n",
135  job->pagesArrayElem.x, job->pagesArrayElem.y, job->numPages);
136  if (job->common->show_boxes == NULL)
137  gvprintf(job, "gsave\n%d %d %d %d boxprim clip newpath\n",
138  pbr.LL.x, pbr.LL.y, pbr.UR.x-pbr.LL.x, pbr.UR.y-pbr.LL.y);
139  gvprintf(job, "%g %g set_scale %d rotate %g %g translate\n",
140  job->scale.x, job->scale.y,
141  job->rotation,
142  job->translation.x, job->translation.y);
143 
144  /* Define the size of the PS canvas */
145  if (job->render.id == FORMAT_PS2) {
146  if (pbr.UR.x >= PDFMAX || pbr.UR.y >= PDFMAX)
147  job->common->errorfn("canvas size (%d,%d) exceeds PDF limit (%d)\n"
148  "\t(suggest setting a bounding box size, see dot(1))\n",
149  pbr.UR.x, pbr.UR.y, PDFMAX);
150  gvprintf(job, "[ /CropBox [%d %d %d %d] /PAGES pdfmark\n",
151  pbr.LL.x, pbr.LL.y, pbr.UR.x, pbr.UR.y);
152  }
153 }
154 
155 static void psgen_end_page(GVJ_t * job)
156 {
157  if (job->common->show_boxes) {
158  gvputs(job, "0 0 0 edgecolor\n");
159  cat_libfile(job, NULL, job->common->show_boxes + 1);
160  }
161  /* the showpage is really a no-op, but at least one PS processor
162  * out there needs to see this literal token. endpage does the real work.
163  */
164  gvputs(job, "endpage\nshowpage\ngrestore\n");
165  gvputs(job, "%%PageTrailer\n");
166  gvprintf(job, "%%%%EndPage: %d\n", job->common->viewNum);
167 }
168 
169 static void psgen_begin_cluster(GVJ_t * job)
170 {
171  obj_state_t *obj = job->obj;
172 
173  gvprintf(job, "%% %s\n", agnameof(obj->u.g));
174 
175  gvputs(job, "gsave\n");
176 }
177 
178 static void psgen_end_cluster(GVJ_t * job)
179 {
180  gvputs(job, "grestore\n");
181 }
182 
183 static void psgen_begin_node(GVJ_t * job)
184 {
185  gvputs(job, "gsave\n");
186 }
187 
188 static void psgen_end_node(GVJ_t * job)
189 {
190  gvputs(job, "grestore\n");
191 }
192 
193 static void
194 psgen_begin_edge(GVJ_t * job)
195 {
196  gvputs(job, "gsave\n");
197 }
198 
199 static void psgen_end_edge(GVJ_t * job)
200 {
201  gvputs(job, "grestore\n");
202 }
203 
204 static void psgen_begin_anchor(GVJ_t *job, char *url, char *tooltip, char *target, char *id)
205 {
206  obj_state_t *obj = job->obj;
207 
208  if (url && obj->url_map_p) {
209  gvputs(job, "[ /Rect [ ");
210  gvprintpointflist(job, obj->url_map_p, 2);
211  gvputs(job, " ]\n");
212  gvprintf(job, " /Border [ 0 0 0 ]\n"
213  " /Action << /Subtype /URI /URI %s >>\n"
214  " /Subtype /Link\n"
215  "/ANN pdfmark\n",
216  ps_string(url, isLatin1));
217  }
218 }
219 
220 static void
221 ps_set_pen_style(GVJ_t *job)
222 {
223  double penwidth = job->obj->penwidth;
224  char *p, *line, **s = job->obj->rawstyle;
225 
226  gvprintdouble(job, penwidth);
227  gvputs(job," setlinewidth\n");
228 
229  while (s && (p = line = *s++)) {
230  if (strcmp(line, "setlinewidth") == 0)
231  continue;
232  while (*p)
233  p++;
234  p++;
235  while (*p) {
236  gvprintf(job,"%s ", p);
237  while (*p)
238  p++;
239  p++;
240  }
241  if (strcmp(line, "invis") == 0)
242  job->obj->penwidth = 0;
243  gvprintf(job, "%s\n", line);
244  }
245 }
246 
247 static void ps_set_color(GVJ_t *job, gvcolor_t *color)
248 {
249  char *objtype;
250 
251  if (color) {
252  switch (job->obj->type) {
253  case ROOTGRAPH_OBJTYPE:
254  case CLUSTER_OBJTYPE:
255  objtype = "graph";
256  break;
257  case NODE_OBJTYPE:
258  objtype = "node";
259  break;
260  case EDGE_OBJTYPE:
261  objtype = "edge";
262  break;
263  default:
264  objtype = "sethsb";
265  break;
266  }
267  gvprintf(job, "%.5g %.5g %.5g %scolor\n",
268  color->u.HSVA[0], color->u.HSVA[1], color->u.HSVA[2], objtype);
269  }
270 }
271 
272 static void psgen_textspan(GVJ_t * job, pointf p, textspan_t * span)
273 {
274  char *str;
275 
276  if (job->obj->pencolor.u.HSVA[3] < .5)
277  return; /* skip transparent text */
278 
279  ps_set_color(job, &(job->obj->pencolor));
280  gvprintdouble(job, span->font->size);
281  gvprintf(job, " /%s set_font\n", span->font->name);
282  str = ps_string(span->str,isLatin1);
283  switch (span->just) {
284  case 'r':
285  p.x -= span->size.x;
286  break;
287  case 'l':
288  p.x -= 0.0;
289  break;
290  case 'n':
291  default:
292  p.x -= span->size.x / 2.0;
293  break;
294  }
295  p.y += span->yoffset_centerline;
296  gvprintpointf(job, p);
297  gvputs(job, " moveto ");
298  gvprintdouble(job, span->size.x);
299  gvprintf(job, " %s alignedtext\n", str);
300 }
301 
302 static void psgen_ellipse(GVJ_t * job, pointf * A, int filled)
303 {
304  /* A[] contains 2 points: the center and corner. */
305  pointf AA[2];
306 
307  AA[0] = A[0];
308  AA[1].x = A[1].x - A[0].x;
309  AA[1].y = A[1].y - A[0].y;
310 
311  if (filled && job->obj->fillcolor.u.HSVA[3] > .5) {
312  ps_set_color(job, &(job->obj->fillcolor));
313  gvprintpointflist(job, AA, 2);
314  gvputs(job, " ellipse_path fill\n");
315  }
316  if (job->obj->pencolor.u.HSVA[3] > .5) {
317  ps_set_pen_style(job);
318  ps_set_color(job, &(job->obj->pencolor));
319  gvprintpointflist(job, AA, 2);
320  gvputs(job, " ellipse_path stroke\n");
321  }
322 }
323 
324 static void
325 psgen_bezier(GVJ_t * job, pointf * A, int n, int arrow_at_start,
326  int arrow_at_end, int filled)
327 {
328  int j;
329 
330  if (filled && job->obj->fillcolor.u.HSVA[3] > .5) {
331  ps_set_color(job, &(job->obj->fillcolor));
332  gvputs(job, "newpath ");
333  gvprintpointf(job, A[0]);
334  gvputs(job, " moveto\n");
335  for (j = 1; j < n; j += 3) {
336  gvprintpointflist(job, &A[j], 3);
337  gvputs(job, " curveto\n");
338  }
339  gvputs(job, "closepath fill\n");
340  }
341  if (job->obj->pencolor.u.HSVA[3] > .5) {
342  ps_set_pen_style(job);
343  ps_set_color(job, &(job->obj->pencolor));
344  gvputs(job, "newpath ");
345  gvprintpointf(job, A[0]);
346  gvputs(job, " moveto\n");
347  for (j = 1; j < n; j += 3) {
348  gvprintpointflist(job, &A[j], 3);
349  gvputs(job, " curveto\n");
350  }
351  gvputs(job, "stroke\n");
352  }
353 }
354 
355 static void psgen_polygon(GVJ_t * job, pointf * A, int n, int filled)
356 {
357  int j;
358 
359  if (filled && job->obj->fillcolor.u.HSVA[3] > .5) {
360  ps_set_color(job, &(job->obj->fillcolor));
361  gvputs(job, "newpath ");
362  gvprintpointf(job, A[0]);
363  gvputs(job, " moveto\n");
364  for (j = 1; j < n; j++) {
365  gvprintpointf(job, A[j]);
366  gvputs(job, " lineto\n");
367  }
368  gvputs(job, "closepath fill\n");
369  }
370  if (job->obj->pencolor.u.HSVA[3] > .5) {
371  ps_set_pen_style(job);
372  ps_set_color(job, &(job->obj->pencolor));
373  gvputs(job, "newpath ");
374  gvprintpointf(job, A[0]);
375  gvputs(job, " moveto\n");
376  for (j = 1; j < n; j++) {
377  gvprintpointf(job, A[j]);
378  gvputs(job, " lineto\n");
379  }
380  gvputs(job, "closepath stroke\n");
381  }
382 }
383 
384 static void psgen_polyline(GVJ_t * job, pointf * A, int n)
385 {
386  int j;
387 
388  if (job->obj->pencolor.u.HSVA[3] > .5) {
389  ps_set_pen_style(job);
390  ps_set_color(job, &(job->obj->pencolor));
391  gvputs(job, "newpath ");
392  gvprintpointf(job, A[0]);
393  gvputs(job, " moveto\n");
394  for (j = 1; j < n; j++) {
395  gvprintpointf(job, A[j]);
396  gvputs(job, " lineto\n");
397  }
398  gvputs(job, "stroke\n");
399  }
400 }
401 
402 static void psgen_comment(GVJ_t * job, char *str)
403 {
404  gvputs(job, "% ");
405  gvputs(job, str);
406  gvputs(job, "\n");
407 }
408 
409 static void psgen_library_shape(GVJ_t * job, char *name, pointf * A, int n, int filled)
410 {
411  if (filled && job->obj->fillcolor.u.HSVA[3] > .5) {
412  ps_set_color(job, &(job->obj->fillcolor));
413  gvputs(job, "[ ");
414  gvprintpointflist(job, A, n);
415  gvputs(job, " ");
416  gvprintpointf(job, A[0]);
417  gvprintf(job, " ] %d true %s\n", n, name);
418  }
419  if (job->obj->pencolor.u.HSVA[3] > .5) {
420  ps_set_pen_style(job);
421  ps_set_color(job, &(job->obj->pencolor));
422  gvputs(job, "[ ");
423  gvprintpointflist(job, A, n);
424  gvputs(job, " ");
425  gvprintpointf(job, A[0]);
426  gvprintf(job, " ] %d false %s\n", n, name);
427  }
428 }
429 
430 static gvrender_engine_t psgen_engine = {
431  psgen_begin_job,
432  psgen_end_job,
433  psgen_begin_graph,
434  0, /* psgen_end_graph */
435  psgen_begin_layer,
436  0, /* psgen_end_layer */
437  psgen_begin_page,
438  psgen_end_page,
439  psgen_begin_cluster,
440  psgen_end_cluster,
441  0, /* psgen_begin_nodes */
442  0, /* psgen_end_nodes */
443  0, /* psgen_begin_edges */
444  0, /* psgen_end_edges */
445  psgen_begin_node,
446  psgen_end_node,
447  psgen_begin_edge,
448  psgen_end_edge,
449  psgen_begin_anchor,
450  0, /* psgen_end_anchor */
451  0, /* psgen_begin_label */
452  0, /* psgen_end_label */
453  psgen_textspan,
454  0, /* psgen_resolve_color */
455  psgen_ellipse,
456  psgen_polygon,
457  psgen_bezier,
458  psgen_polyline,
459  psgen_comment,
460  psgen_library_shape,
461 };
462 
463 static gvrender_features_t render_features_ps = {
468  4., /* default pad - graph units */
469  NULL, /* knowncolors */
470  0, /* sizeof knowncolors */
471  HSVA_DOUBLE, /* color_type */
472 };
473 
474 static gvdevice_features_t device_features_ps = {
476  | GVDEVICE_DOES_LAYERS, /* flags */
477  {36.,36.}, /* default margin - points */
478  {612.,792.}, /* default page width, height - points */
479  {72.,72.}, /* default dpi */
480 };
481 
482 static gvdevice_features_t device_features_eps = {
483  0, /* flags */
484  {36.,36.}, /* default margin - points */
485  {612.,792.}, /* default page width, height - points */
486  {72.,72.}, /* default dpi */
487 };
488 
490  {FORMAT_PS, "ps", 1, &psgen_engine, &render_features_ps},
491  {0, NULL, 0, NULL, NULL}
492 };
493 
495  {FORMAT_PS, "ps:ps", 1, NULL, &device_features_ps},
496  {FORMAT_PS2, "ps2:ps", 1, NULL, &device_features_ps},
497  {FORMAT_EPS, "eps:ps", 1, NULL, &device_features_eps},
498  {0, NULL, 0, NULL, NULL}
499 };
char ** rawstyle
Definition: gvcjob.h:209
pointf size
Definition: textspan.h:64
gvplugin_installed_t gvdevice_ps_types[]
int rotation
Definition: gvcjob.h:328
box boundingBox
Definition: gvcjob.h:339
union color_s::@10 u
#define GVDEVICE_DOES_PAGES
Definition: gvcjob.h:89
void gvprintpointflist(GVJ_t *job, pointf *p, int n)
Definition: gvdevice.c:585
double size
Definition: textspan.h:52
Definition: geom.h:28
const char ** lib
Definition: gvcommon.h:28
Definition: color.h:34
gvcolor_t pencolor
Definition: gvcjob.h:203
Definition: gvcjob.h:271
char * name
Definition: textspan.h:49
point UR
Definition: geom.h:33
char * ps_string(char *ins, int chset)
Definition: psusershape.c:269
int x
Definition: geom.h:26
#define GVRENDER_DOES_MAP_RECTANGLE
Definition: gvcjob.h:101
#define GVRENDER_DOES_TRANSFORM
Definition: gvcjob.h:97
obj_state_t * obj
Definition: gvcjob.h:278
int gvputs(GVJ_t *job, const char *s)
Definition: gvdevice.c:270
box pageBoundingBox
Definition: gvcjob.h:338
char * str
Definition: textspan.h:59
int viewNum
Definition: gvcommon.h:31
gvplugin_active_render_t render
Definition: gvcjob.h:294
#define GVDEVICE_DOES_LAYERS
Definition: gvcjob.h:90
void epsf_define(GVJ_t *job)
Definition: psusershape.c:221
double y
Definition: geom.h:28
const char ** show_boxes
Definition: gvcommon.h:27
void cat_libfile(GVJ_t *job, const char **arglib, const char **stdlib)
Definition: psusershape.c:139
pointf * url_map_p
Definition: gvcjob.h:249
CGRAPH_API char * agnameof(void *)
Definition: id.c:143
obj_type type
Definition: gvcjob.h:193
char * url
Definition: gvcjob.h:219
pointf scale
Definition: gvcjob.h:341
GVCOMMON_t * common
Definition: gvcjob.h:276
char ** info
Definition: gvcommon.h:22
point pagesArrayElem
Definition: gvcjob.h:317
void gvprintpointf(GVJ_t *job, pointf p)
Definition: gvdevice.c:573
double yoffset_centerline
Definition: textspan.h:63
point LL
Definition: geom.h:33
Definition: grammar.c:79
#define PDFMAX
graph_t * g
Definition: gvcjob.h:195
void(* errorfn)(const char *fmt,...)
Definition: gvcommon.h:26
double HSVA[4]
Definition: color.h:37
#define GVRENDER_NO_WHITE_BG
Definition: gvcjob.h:109
void gvprintdouble(GVJ_t *job, double num)
Definition: gvdevice.c:557
format_type
#define GVRENDER_DOES_MAPS
Definition: gvcjob.h:100
#define NULL
Definition: logic.h:39
#define GD_charset(g)
Definition: types.h:371
pointf translation
Definition: gvcjob.h:342
double x
Definition: geom.h:28
int numPages
Definition: gvcjob.h:318
#define CHAR_LATIN1
Definition: const.h:205
char just
Definition: textspan.h:65
gvplugin_installed_t gvrender_ps_types[]
union obj_state_s::@23 u
agxbuf * str
Definition: htmlparse.c:85
gvcolor_t fillcolor
Definition: gvcjob.h:203
double penwidth
Definition: gvcjob.h:208
int y
Definition: geom.h:26
#define FALSE
Definition: cgraph.h:35
void gvprintf(GVJ_t *job, const char *format,...)
Definition: gvdevice.c:389
textfont_t * font
Definition: textspan.h:60
Definition: geom.h:33
#define TRUE
Definition: cgraph.h:38