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