Graphviz  2.35.20130930.0449
gvrender.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 /*
15  * graphics code generator wrapper
16  *
17  * This library forms the socket for run-time loadable render plugins.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include <string.h>
25 #include "memory.h"
26 #include "const.h"
27 #include "macros.h"
28 #include "colorprocs.h"
29 #include "gvplugin_render.h"
30 #if WITH_CGRAPH
31 #include "cgraph.h"
32 #else
33 #include "graph.h"
34 #endif
35 #include "gvcint.h"
36 #include "geom.h"
37 #include "geomprocs.h"
38 #include "gvcproc.h"
39 
40 extern int emit_once(char *str);
41 extern shape_desc *find_user_shape(char *name);
42 extern boolean mapbool(char *s);
43 
44 #ifndef HAVE_STRCASECMP
45 extern int strcasecmp(const char *s1, const char *s2);
46 #endif
47 
48 /* storage for temporary hacks until client API is FP */
49 static pointf *AF;
50 static int sizeAF;
51 /* end hack */
52 
53 int gvrender_select(GVJ_t * job, const char *str)
54 {
55  GVC_t *gvc = job->gvc;
56  gvplugin_available_t *plugin;
57  gvplugin_installed_t *typeptr;
58 
59  gvplugin_load(gvc, API_device, str);
60 
61  /* When job is created, it is zeroed out.
62  * Some flags, such as OUTPUT_NOT_REQUIRED, may already be set,
63  * so don't reset.
64  */
65  /* job->flags = 0; */
66  plugin = gvc->api[API_device];
67  if (plugin) {
68  typeptr = plugin->typeptr;
69  job->device.engine = (gvdevice_engine_t *) (typeptr->engine);
70  job->device.features = (gvdevice_features_t *) (typeptr->features);
71  job->device.id = typeptr->id;
72  job->device.type = plugin->typestr;
73 
74  job->flags |= job->device.features->flags;
75  } else
76  return NO_SUPPORT; /* FIXME - should differentiate problem */
77 
78  /* The device plugin has a dependency on a render plugin,
79  * so the render plugin should be available as well now */
80  plugin = gvc->api[API_render];
81  if (plugin) {
82  typeptr = plugin->typeptr;
83  job->render.engine = (gvrender_engine_t *) (typeptr->engine);
84  job->render.features = (gvrender_features_t *) (typeptr->features);
85  job->render.type = plugin->typestr;
86 
87  job->flags |= job->render.features->flags;
88 
89  if (job->device.engine)
90  job->render.id = typeptr->id;
91  else
92  /* A null device engine indicates that the device id is also the renderer id
93  * and that the renderer doesn't need "device" functions.
94  * Device "features" settings are still available */
95  job->render.id = job->device.id;
96  return GVRENDER_PLUGIN;
97  }
98  job->render.engine = NULL;
99  return NO_SUPPORT; /* FIXME - should differentiate problem */
100 }
101 
103 {
104  gvrender_engine_t *gvre = job->render.engine;
105  int features = 0;
106 
107  if (gvre) {
108  features = job->render.features->flags;
109  }
110  return features;
111 }
112 
113 /* gvrender_begin_job:
114  * Return 0 on success
115  */
117 {
118  gvrender_engine_t *gvre = job->render.engine;
119 
120  if (gvdevice_initialize(job))
121  return 1;
122  if (gvre) {
123  if (gvre->begin_job)
124  gvre->begin_job(job);
125  }
126  return 0;
127 }
128 
130 {
131  gvrender_engine_t *gvre = job->render.engine;
132 
133  if (gvre) {
134  if (gvre->end_job)
135  gvre->end_job(job);
136  }
137  job->gvc->common.lib = NULL; /* FIXME - minimally this doesn't belong here */
138  gvdevice_finalize(job);
139 }
140 
141 /* font modifiers */
142 #define REGULAR 0
143 #define BOLD 1
144 #define ITALIC 2
145 
147 {
148  pointf rv, translation, scale;
149 
150  translation = job->translation;
151  scale.x = job->zoom * job->devscale.x;
152  scale.y = job->zoom * job->devscale.y;
153 
154  if (job->rotation) {
155  rv.x = -(p.y + translation.y) * scale.x;
156  rv.y = (p.x + translation.x) * scale.y;
157  } else {
158  rv.x = (p.x + translation.x) * scale.x;
159  rv.y = (p.y + translation.y) * scale.y;
160  }
161  return rv;
162 }
163 
164 /* transform an array of n points */
165 /* *AF and *af must be preallocated */
166 /* *AF can be the same as *af for inplace transforms */
167 pointf *gvrender_ptf_A(GVJ_t * job, pointf * af, pointf * AF, int n)
168 {
169  int i;
170  double t;
171  pointf translation, scale;
172 
173  translation = job->translation;
174  scale.x = job->zoom * job->devscale.x;
175  scale.y = job->zoom * job->devscale.y;
176 
177  if (job->rotation) {
178  for (i = 0; i < n; i++) {
179  t = -(af[i].y + translation.y) * scale.x;
180  AF[i].y = (af[i].x + translation.x) * scale.y;
181  AF[i].x = t;
182  }
183  } else {
184  for (i = 0; i < n; i++) {
185  AF[i].x = (af[i].x + translation.x) * scale.x;
186  AF[i].y = (af[i].y + translation.y) * scale.y;
187  }
188  }
189  return AF;
190 }
191 
192 static int gvrender_comparestr(const void *s1, const void *s2)
193 {
194  return strcmp(*(char **) s1, *(char **) s2);
195 }
196 
197 static void gvrender_resolve_color(gvrender_features_t * features,
198  char *name, gvcolor_t * color)
199 {
200  char *tok;
201  int rc;
202 
203  color->u.string = name;
204  color->type = COLOR_STRING;
205  tok = canontoken(name);
206  if (!features->knowncolors
207  ||
208  (bsearch
209  (&tok, features->knowncolors, features->sz_knowncolors,
210  sizeof(char *), gvrender_comparestr)) == NULL) {
211  /* if tok was not found in known_colors */
212  rc = colorxlate(name, color, features->color_type);
213  if (rc != COLOR_OK) {
214  if (rc == COLOR_UNKNOWN) {
215  char *missedcolor = gmalloc(strlen(name) + 16);
216  sprintf(missedcolor, "color %s", name);
217  if (emit_once(missedcolor))
218  agerr(AGWARN, "%s is not a known color.\n", name);
219  free(missedcolor);
220  } else {
221  agerr(AGERR, "error in colxlate()\n");
222  }
223  }
224  }
225 }
226 
228 {
229  /* GVC_t *gvc = job->gvc; */
230  gvrender_engine_t *gvre = job->render.engine;
231  /* char *s; */
232 
233  if (gvre) {
234  /* render specific init */
235  if (gvre->begin_graph)
236  gvre->begin_graph(job);
237 
238 #if 0
239  /* background color */
240  if (((s = agget(g, "bgcolor")) != 0) && s[0]) {
241  gvrender_resolve_color(job->render.features, s,
242  &(gvc->bgcolor));
243  if (gvre->resolve_color)
244  gvre->resolve_color(job, &(gvc->bgcolor));
245  }
246 #endif
247 
248  }
249 }
250 
252 {
253  gvrender_engine_t *gvre = job->render.engine;
254 
255  if (gvre) {
256  if (gvre->end_graph)
257  gvre->end_graph(job);
258  }
259  gvdevice_format(job);
260 }
261 
263 {
264  gvrender_engine_t *gvre = job->render.engine;
265 
266  if (gvre) {
267  if (gvre->begin_page)
268  gvre->begin_page(job);
269  }
270 }
271 
273 {
274  gvrender_engine_t *gvre = job->render.engine;
275 
276  if (gvre) {
277  if (gvre->end_page)
278  gvre->end_page(job);
279  }
280 }
281 
283 {
284  gvrender_engine_t *gvre = job->render.engine;
285 
286  if (gvre) {
287  if (gvre->begin_layer)
288  gvre->begin_layer(job, job->gvc->layerIDs[job->layerNum],
289  job->layerNum, job->numLayers);
290  }
291 }
292 
294 {
295  gvrender_engine_t *gvre = job->render.engine;
296 
297  if (gvre) {
298  if (gvre->end_layer)
299  gvre->end_layer(job);
300  }
301 }
302 
304 {
305  gvrender_engine_t *gvre = job->render.engine;
306 
307  if (gvre) {
308  if (gvre->begin_cluster)
309  gvre->begin_cluster(job);
310  }
311 }
312 
314 {
315  gvrender_engine_t *gvre = job->render.engine;
316 
317  if (gvre) {
318  if (gvre->end_cluster)
319  gvre->end_cluster(job);
320  }
321 }
322 
324 {
325  gvrender_engine_t *gvre = job->render.engine;
326 
327  if (gvre) {
328  if (gvre->begin_nodes)
329  gvre->begin_nodes(job);
330  }
331 }
332 
334 {
335  gvrender_engine_t *gvre = job->render.engine;
336 
337  if (gvre) {
338  if (gvre->end_nodes)
339  gvre->end_nodes(job);
340  }
341 }
342 
344 {
345  gvrender_engine_t *gvre = job->render.engine;
346 
347  if (gvre) {
348  if (gvre->begin_edges)
349  gvre->begin_edges(job);
350  }
351 }
352 
354 {
355  gvrender_engine_t *gvre = job->render.engine;
356 
357  if (gvre) {
358  if (gvre->end_edges)
359  gvre->end_edges(job);
360  }
361 }
362 
364 {
365  gvrender_engine_t *gvre = job->render.engine;
366 
367  if (gvre) {
368  if (gvre->begin_node)
369  gvre->begin_node(job);
370  }
371 }
372 
374 {
375  gvrender_engine_t *gvre = job->render.engine;
376 
377  if (gvre) {
378  if (gvre->end_node)
379  gvre->end_node(job);
380  }
381 }
382 
384 {
385  gvrender_engine_t *gvre = job->render.engine;
386 
387  if (gvre) {
388  if (gvre->begin_edge)
389  gvre->begin_edge(job);
390  }
391 }
392 
394 {
395  gvrender_engine_t *gvre = job->render.engine;
396 
397  if (gvre) {
398  if (gvre->end_edge)
399  gvre->end_edge(job);
400  }
401 }
402 
403 void gvrender_begin_anchor(GVJ_t * job, char *href, char *tooltip,
404  char *target, char *id)
405 {
406  gvrender_engine_t *gvre = job->render.engine;
407 
408  if (gvre) {
409  if (gvre->begin_anchor)
410  gvre->begin_anchor(job, href, tooltip, target, id);
411  }
412 }
413 
415 {
416  gvrender_engine_t *gvre = job->render.engine;
417 
418  if (gvre) {
419  if (gvre->end_anchor)
420  gvre->end_anchor(job);
421  }
422 }
423 
425 {
426  gvrender_engine_t *gvre = job->render.engine;
427 
428  if (gvre) {
429  if (gvre->begin_label)
430  gvre->begin_label(job, type);
431  }
432 }
433 
435 {
436  gvrender_engine_t *gvre = job->render.engine;
437 
438  if (gvre) {
439  if (gvre->end_label)
440  gvre->end_label(job);
441  }
442 }
443 
444 void gvrender_textpara(GVJ_t * job, pointf p, textpara_t * para)
445 {
446  gvrender_engine_t *gvre = job->render.engine;
447  pointf PF;
448 
449  if (para->str && para->str[0]
450  && (!job->obj /* because of xdgen non-conformity */
451  || job->obj->pen != PEN_NONE)) {
452  if (job->flags & GVRENDER_DOES_TRANSFORM)
453  PF = p;
454  else
455  PF = gvrender_ptf(job, p);
456  if (gvre) {
457  if (gvre->textpara)
458  gvre->textpara(job, PF, para);
459  }
460  }
461 }
462 
463 void gvrender_set_pencolor(GVJ_t * job, char *name)
464 {
465  gvrender_engine_t *gvre = job->render.engine;
466  gvcolor_t *color = &(job->obj->pencolor);
467  char *cp = NULL;
468 
469  if ((cp = strstr(name, ":"))) /* if its a color list, then use only first */
470  *cp = '\0';
471  if (gvre) {
472  gvrender_resolve_color(job->render.features, name, color);
473  if (gvre->resolve_color)
474  gvre->resolve_color(job, color);
475  }
476  if (cp) /* restore color list */
477  *cp = ':';
478 }
479 
480 void gvrender_set_fillcolor(GVJ_t * job, char *name)
481 {
482  gvrender_engine_t *gvre = job->render.engine;
483  gvcolor_t *color = &(job->obj->fillcolor);
484  char *cp = NULL;
485 
486  if ((cp = strstr(name, ":"))) /* if its a color list, then use only first */
487  *cp = '\0';
488  if (gvre) {
489  gvrender_resolve_color(job->render.features, name, color);
490  if (gvre->resolve_color)
491  gvre->resolve_color(job, color);
492  }
493  if (cp)
494  *cp = ':';
495 }
496 
497 void gvrender_set_gradient_vals (GVJ_t * job, char *stopcolor, int angle, float frac)
498 {
499  gvrender_engine_t *gvre = job->render.engine;
500  gvcolor_t *color = &(job->obj->stopcolor);
501 
502  if (gvre) {
503  gvrender_resolve_color(job->render.features, stopcolor, color);
504  if (gvre->resolve_color)
505  gvre->resolve_color(job, color);
506  }
507  job->obj->gradient_angle = angle;
508  job->obj->gradient_frac = frac;
509 }
510 
511 void gvrender_set_style(GVJ_t * job, char **s)
512 {
513  gvrender_engine_t *gvre = job->render.engine;
514  obj_state_t *obj = job->obj;
515  char *line, *p;
516 
517  obj->rawstyle = s;
518  if (gvre) {
519  if (s)
520  while ((p = line = *s++)) {
521  if (streq(line, "solid"))
522  obj->pen = PEN_SOLID;
523  else if (streq(line, "dashed"))
524  obj->pen = PEN_DASHED;
525  else if (streq(line, "dotted"))
526  obj->pen = PEN_DOTTED;
527  else if (streq(line, "invis") || streq(line, "invisible"))
528  obj->pen = PEN_NONE;
529  else if (streq(line, "bold"))
530  obj->penwidth = PENWIDTH_BOLD;
531  else if (streq(line, "setlinewidth")) {
532  while (*p)
533  p++;
534  p++;
535  obj->penwidth = atof(p);
536  } else if (streq(line, "filled"))
537  obj->fill = FILL_SOLID;
538  else if (streq(line, "unfilled"))
539  obj->fill = FILL_NONE;
540  else if (streq(line, "tapered"));
541  else {
542  agerr(AGWARN,
543  "gvrender_set_style: unsupported style %s - ignoring\n",
544  line);
545  }
546  }
547  }
548 }
549 
550 void gvrender_ellipse(GVJ_t * job, pointf * pf, int n, int filled)
551 {
552  gvrender_engine_t *gvre = job->render.engine;
553 
554  if (gvre) {
555  if (gvre->ellipse && job->obj->pen != PEN_NONE) {
556  pointf af[2];
557 
558  /* center */
559  af[0].x = (pf[0].x + pf[1].x) / 2.;
560  af[0].y = (pf[0].y + pf[1].y) / 2.;
561  /* corner */
562  af[1] = pf[1];
563 
564  if (!(job->flags & GVRENDER_DOES_TRANSFORM))
565  gvrender_ptf_A(job, af, af, 2);
566  gvre->ellipse(job, af, filled);
567  }
568  }
569 }
570 
571 void gvrender_polygon(GVJ_t * job, pointf * af, int n, int filled)
572 {
573  int noPoly = 0;
574  gvcolor_t save_pencolor;
575 
576  gvrender_engine_t *gvre = job->render.engine;
577  if (gvre) {
578  if (gvre->polygon && job->obj->pen != PEN_NONE) {
579  if (filled & NO_POLY) {
580  noPoly = 1;
581  filled &= ~NO_POLY;
582  save_pencolor = job->obj->pencolor;
583  job->obj->pencolor = job->obj->fillcolor;
584  }
585  if (job->flags & GVRENDER_DOES_TRANSFORM)
586  gvre->polygon(job, af, n, filled);
587  else {
588  if (sizeAF < n) {
589  sizeAF = n + 10;
590  AF = grealloc(AF, sizeAF * sizeof(pointf));
591  }
592  gvrender_ptf_A(job, af, AF, n);
593  gvre->polygon(job, AF, n, filled);
594  }
595  if (noPoly)
596  job->obj->pencolor = save_pencolor;
597  }
598  }
599 }
600 
601 
602 void gvrender_box(GVJ_t * job, boxf B, int filled)
603 {
604  pointf A[4];
605 
606  A[0] = B.LL;
607  A[2] = B.UR;
608  A[1].x = A[0].x;
609  A[1].y = A[2].y;
610  A[3].x = A[2].x;
611  A[3].y = A[0].y;
612 
613  gvrender_polygon(job, A, 4, filled);
614 }
615 
616 void gvrender_beziercurve(GVJ_t * job, pointf * af, int n,
617  int arrow_at_start, int arrow_at_end,
618  boolean filled)
619 {
620  gvrender_engine_t *gvre = job->render.engine;
621 
622  if (gvre) {
623  if (gvre->beziercurve && job->obj->pen != PEN_NONE) {
624  if (job->flags & GVRENDER_DOES_TRANSFORM)
625  gvre->beziercurve(job, af, n, arrow_at_start, arrow_at_end,
626  filled);
627  else {
628  if (sizeAF < n) {
629  sizeAF = n + 10;
630  AF = grealloc(AF, sizeAF * sizeof(pointf));
631  }
632  gvrender_ptf_A(job, af, AF, n);
633  gvre->beziercurve(job, AF, n, arrow_at_start, arrow_at_end,
634  filled);
635  }
636  }
637  }
638 }
639 
640 void gvrender_polyline(GVJ_t * job, pointf * af, int n)
641 {
642  gvrender_engine_t *gvre = job->render.engine;
643 
644  if (gvre) {
645  if (gvre->polyline && job->obj->pen != PEN_NONE) {
646  if (job->flags & GVRENDER_DOES_TRANSFORM)
647  gvre->polyline(job, af, n);
648  else {
649  if (sizeAF < n) {
650  sizeAF = n + 10;
651  AF = grealloc(AF, sizeAF * sizeof(pointf));
652  }
653  gvrender_ptf_A(job, af, AF, n);
654  gvre->polyline(job, AF, n);
655  }
656  }
657  }
658 }
659 
660 void gvrender_comment(GVJ_t * job, char *str)
661 {
662  gvrender_engine_t *gvre = job->render.engine;
663 
664  if (!str || !str[0])
665  return;
666 
667  if (gvre) {
668  if (gvre->comment)
669  gvre->comment(job, str);
670  }
671 }
672 
673 static imagescale_t get_imagescale(char *s)
674 {
675  if (*s == '\0')
676  return IMAGESCALE_FALSE;
677  if (!strcasecmp(s, "width"))
678  return IMAGESCALE_WIDTH;
679  if (!strcasecmp(s, "height"))
680  return IMAGESCALE_HEIGHT;
681  if (!strcasecmp(s, "both"))
682  return IMAGESCALE_BOTH;
683  if (mapbool(s))
684  return IMAGESCALE_TRUE;
685  return IMAGESCALE_FALSE;
686 }
687 
688 /* gvrender_usershape:
689  * Scale image to fill polygon bounding box according to "imagescale"
690  */
691 void gvrender_usershape(GVJ_t * job, char *name, pointf * a, int n,
692  boolean filled, char *imagescale)
693 {
694  gvrender_engine_t *gvre = job->render.engine;
695  usershape_t *us;
696  double iw, ih, pw, ph;
697  double scalex, scaley; /* scale factors */
698  boxf b; /* target box */
699  int i;
700  point isz;
701 
702  if (!(us = gvusershape_find(name))) {
703  if (find_user_shape(name)) {
704  if (gvre && gvre->library_shape)
705  gvre->library_shape(job, name, a, n, filled);
706  }
707  return;
708  }
709 
710  isz = gvusershape_size_dpi(us, job->dpi);
711  if ((isz.x <= 0) && (isz.y <= 0))
712  return;
713 
714  /* compute bb of polygon */
715  b.LL = b.UR = a[0];
716  for (i = 1; i < n; i++) {
717  EXPANDBP(b, a[i]);
718  }
719 
720  pw = b.UR.x - b.LL.x;
721  ph = b.UR.y - b.LL.y;
722  ih = (double) isz.y;
723  iw = (double) isz.x;
724 
725  scalex = pw / iw;
726  scaley = ph / ih;
727 
728  switch (get_imagescale(imagescale)) {
729  case IMAGESCALE_TRUE:
730  /* keep aspect ratio fixed by just using the smaller scale */
731  if (scalex < scaley) {
732  iw *= scalex;
733  ih *= scalex;
734  } else {
735  iw *= scaley;
736  ih *= scaley;
737  }
738  break;
739  case IMAGESCALE_WIDTH:
740  iw *= scalex;
741  break;
742  case IMAGESCALE_HEIGHT:
743  ih *= scaley;
744  break;
745  case IMAGESCALE_BOTH:
746  iw *= scalex;
747  ih *= scaley;
748  break;
749  case IMAGESCALE_FALSE:
750  default:
751  break;
752  }
753 
754  /* if image is smaller than target area then center it */
755  if (iw < pw) {
756  b.LL.x += (pw - iw) / 2.0;
757  b.UR.x -= (pw - iw) / 2.0;
758  }
759  if (ih < ph) {
760  b.LL.y += (ph - ih) / 2.0;
761  b.UR.y -= (ph - ih) / 2.0;
762  }
763 
764  /* convert from graph to device coordinates */
765  if (!(job->flags & GVRENDER_DOES_TRANSFORM)) {
766  b.LL = gvrender_ptf(job, b.LL);
767  b.UR = gvrender_ptf(job, b.UR);
768  }
769 
770  if (b.LL.x > b.UR.x) {
771  double d = b.LL.x;
772  b.LL.x = b.UR.x;
773  b.UR.x = d;
774  }
775  if (b.LL.y > b.UR.y) {
776  double d = b.LL.y;
777  b.LL.y = b.UR.y;
778  b.UR.y = d;
779  }
780  if (gvre) {
781  gvloadimage(job, us, b, filled, job->render.type);
782  }
783 }
784 
785 void gvrender_set_penwidth(GVJ_t * job, double penwidth)
786 {
787  gvrender_engine_t *gvre = job->render.engine;
788 
789  if (gvre) {
790  job->obj->penwidth = penwidth;
791  /*if (gvre->set_penwidth) gvre->set_penwidth(job, penwidth); */
792  }
793 }