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