Graphviz  2.41.20171026.1811
gvrender_core_svg.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 /* Comments on the SVG coordinate system (SN 8 Dec 2006):
15  The initial <svg> element defines the SVG coordinate system so
16  that the graphviz canvas (in units of points) fits the intended
17  absolute size in inches. After this, the situation should be
18  that "px" = "pt" in SVG, so we can dispense with stating units.
19  Also, the input units (such as fontsize) should be preserved
20  without scaling in the output SVG (as long as the graph size
21  was not constrained.)
22  */
23 
24 #include "config.h"
25 
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <ctype.h>
30 
31 #include "macros.h"
32 #include "const.h"
33 
34 #include "gvplugin_render.h"
35 #include "agxbuf.h"
36 #include "utils.h"
37 #include "gvplugin_device.h"
38 #include "gvio.h"
39 #include "gvcint.h"
40 
41 typedef enum { FORMAT_SVG, FORMAT_SVGZ, } format_type;
42 
43 /* SVG dash array */
44 static char *sdasharray = "5,2";
45 /* SVG dot array */
46 static char *sdotarray = "1,5";
47 
48 #ifndef HAVE_STRCASECMP
49 extern int strcasecmp(const char *s1, const char *s2);
50 #endif
51 
52 static void svg_bzptarray(GVJ_t * job, pointf * A, int n)
53 {
54  int i;
55  char c;
56 
57  c = 'M'; /* first point */
58 #if EDGEALIGN
59  if (A[0].x <= A[n-1].x) {
60 #endif
61  for (i = 0; i < n; i++) {
62  gvprintf(job, "%c", c);
63  gvprintdouble(job, A[i].x);
64  gvputs(job, ",");
65  gvprintdouble(job, -A[i].y);
66  if (i == 0)
67  c = 'C'; /* second point */
68  else
69  c = ' '; /* remaining points */
70  }
71 #if EDGEALIGN
72  } else {
73  for (i = n-1; i >= 0; i--) {
74  gvprintf(job, "%c", c);
75  gvprintdouble(job, A[i].x);
76  gvputs(job, ",");
77  gvprintdouble(job, -A[i].y);
78  if (i == 0)
79  c = 'C'; /* second point */
80  else
81  c = ' '; /* remaining points */
82  }
83  }
84 #endif
85 }
86 
87 static void svg_print_id_class(GVJ_t * job, char* id, char* idx, char* kind, void* obj)
88 {
89  char* str;
90 
91  gvputs(job, "<g id=\"");
92  gvputs(job, xml_string(id));
93  if (idx)
94  gvprintf (job, "_%s", xml_string(idx));
95  gvprintf(job, "\" class=\"%s", kind);
96  if ((str = agget(obj, "class")) && *str) {
97  gvputs(job, " ");
98  gvputs(job, xml_string(str));
99  }
100  gvputs(job, "\"");
101 }
102 
103 static void svg_print_color(GVJ_t * job, gvcolor_t color)
104 {
105  switch (color.type) {
106  case COLOR_STRING:
107  gvputs(job, color.u.string);
108  break;
109  case RGBA_BYTE:
110  if (color.u.rgba[3] == 0) /* transparent */
111  gvputs(job, "transparent");
112  else
113  gvprintf(job, "#%02x%02x%02x",
114  color.u.rgba[0], color.u.rgba[1], color.u.rgba[2]);
115  break;
116  default:
117  assert(0); /* internal error */
118  }
119 }
120 
121 static void svg_grstyle(GVJ_t * job, int filled, int gid)
122 {
123  obj_state_t *obj = job->obj;
124 
125  gvputs(job, " fill=\"");
126  if (filled == GRADIENT) {
127  gvprintf(job, "url(#l_%d)", gid);
128  } else if (filled == RGRADIENT) {
129  gvprintf(job, "url(#r_%d)", gid);
130  } else if (filled) {
131  svg_print_color(job, obj->fillcolor);
132  if (obj->fillcolor.type == RGBA_BYTE
133  && obj->fillcolor.u.rgba[3] > 0
134  && obj->fillcolor.u.rgba[3] < 255)
135  gvprintf(job, "\" fill-opacity=\"%f",
136  ((float) obj->fillcolor.u.rgba[3] / 255.0));
137  } else {
138  gvputs(job, "none");
139  }
140  gvputs(job, "\" stroke=\"");
141  svg_print_color(job, obj->pencolor);
142  if (obj->penwidth != PENWIDTH_NORMAL) {
143  gvputs(job, "\" stroke-width=\"");
144  gvprintdouble(job, obj->penwidth);
145  }
146  if (obj->pen == PEN_DASHED) {
147  gvprintf(job, "\" stroke-dasharray=\"%s", sdasharray);
148  } else if (obj->pen == PEN_DOTTED) {
149  gvprintf(job, "\" stroke-dasharray=\"%s", sdotarray);
150  }
151  if (obj->pencolor.type == RGBA_BYTE && obj->pencolor.u.rgba[3] > 0
152  && obj->pencolor.u.rgba[3] < 255)
153  gvprintf(job, "\" stroke-opacity=\"%f",
154  ((float) obj->pencolor.u.rgba[3] / 255.0));
155 
156  gvputs(job, "\"");
157 }
158 
159 static void svg_comment(GVJ_t * job, char *str)
160 {
161  gvputs(job, "<!-- ");
162  gvputs(job, xml_string(str));
163  gvputs(job, " -->\n");
164 }
165 
166 static void svg_begin_job(GVJ_t * job)
167 {
168  char *s;
169  gvputs(job,
170  "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
171  if ((s = agget(job->gvc->g, "stylesheet")) && s[0]) {
172  gvputs(job, "<?xml-stylesheet href=\"");
173  gvputs(job, s);
174  gvputs(job, "\" type=\"text/css\"?>\n");
175  }
176 #if 0
177  gvputs(job, "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\"\n");
178  gvputs(job,
179  " \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\"");
180  /* This is to work around a bug in the SVG 1.0 DTD */
181  gvputs(job,
182  " [\n <!ATTLIST svg xmlns:xlink CDATA #FIXED \"http://www.w3.org/1999/xlink\">\n]");
183 #else
184  gvputs(job, "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n");
185  gvputs(job,
186  " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
187 #endif
188 
189  gvputs(job, "<!-- Generated by ");
190  gvputs(job, xml_string(job->common->info[0]));
191  gvputs(job, " version ");
192  gvputs(job, xml_string(job->common->info[1]));
193  gvputs(job, " (");
194  gvputs(job, xml_string(job->common->info[2]));
195  gvputs(job, ")\n");
196  gvputs(job, " -->\n");
197 }
198 
199 static void svg_begin_graph(GVJ_t * job)
200 {
201  obj_state_t *obj = job->obj;
202 
203  gvputs(job, "<!--");
204  if (agnameof(obj->u.g)[0]) {
205  gvputs(job, " Title: ");
206  gvputs(job, xml_string(agnameof(obj->u.g)));
207  }
208  gvprintf(job, " Pages: %d -->\n",
209  job->pagesArraySize.x * job->pagesArraySize.y);
210 
211  gvprintf(job, "<svg width=\"%dpt\" height=\"%dpt\"\n",
212  job->width, job->height);
213  gvprintf(job, " viewBox=\"%.2f %.2f %.2f %.2f\"",
214  job->canvasBox.LL.x,
215  job->canvasBox.LL.y,
216  job->canvasBox.UR.x,
217  job->canvasBox.UR.y);
218  /* namespace of svg */
219  gvputs(job, " xmlns=\"http://www.w3.org/2000/svg\"");
220  /* namespace of xlink */
221  gvputs(job, " xmlns:xlink=\"http://www.w3.org/1999/xlink\"");
222  gvputs(job, ">\n");
223 }
224 
225 static void svg_end_graph(GVJ_t * job)
226 {
227  gvputs(job, "</svg>\n");
228 }
229 
230 static void svg_begin_layer(GVJ_t * job, char *layername, int layerNum,
231  int numLayers)
232 {
233  obj_state_t *obj = job->obj;
234 
235  svg_print_id_class(job, layername, NULL, "layer", obj->u.g);
236  gvputs(job, ">\n");
237 }
238 
239 static void svg_end_layer(GVJ_t * job)
240 {
241  gvputs(job, "</g>\n");
242 }
243 
244 /* svg_begin_page:
245  * Currently, svg output does not support pages.
246  * FIX: If implemented, we must guarantee the id is unique.
247  */
248 static void svg_begin_page(GVJ_t * job)
249 {
250  obj_state_t *obj = job->obj;
251 
252  /* its really just a page of the graph, but its still a graph,
253  * and it is the entire graph if we're not currently paging */
254  svg_print_id_class(job, obj->id, NULL, "graph", obj->u.g);
255  gvputs(job, " transform=\"scale(");
256  gvprintdouble(job, job->scale.x);
257  gvputs(job, " ");
258  gvprintdouble(job, job->scale.y);
259  gvprintf(job, ") rotate(%d) translate(", -job->rotation);
260  gvprintdouble(job, job->translation.x);
261  gvputs(job, " ");
262  gvprintdouble(job, -job->translation.y);
263  gvputs(job, ")\">\n");
264  /* default style */
265  if (agnameof(obj->u.g)[0]) {
266  gvputs(job, "<title>");
267  gvputs(job, xml_string(agnameof(obj->u.g)));
268  gvputs(job, "</title>\n");
269  }
270 }
271 
272 static void svg_end_page(GVJ_t * job)
273 {
274  gvputs(job, "</g>\n");
275 }
276 
277 static void svg_begin_cluster(GVJ_t * job)
278 {
279  obj_state_t *obj = job->obj;
280 
281  svg_print_id_class(job, obj->id, NULL, "cluster", obj->u.sg);
282  gvputs(job, ">\n");
283  gvputs(job, "<title>");
284  gvputs(job, xml_string(agnameof(obj->u.g)));
285  gvputs(job, "</title>\n");
286 }
287 
288 static void svg_end_cluster(GVJ_t * job)
289 {
290  gvputs(job, "</g>\n");
291 }
292 
293 static void svg_begin_node(GVJ_t * job)
294 {
295  obj_state_t *obj = job->obj;
296  char* idx;
297 
298  if (job->layerNum > 1)
299  idx = job->gvc->layerIDs[job->layerNum];
300  else
301  idx = NULL;
302  svg_print_id_class(job, obj->id, idx, "node", obj->u.n);
303  gvputs(job, ">\n");
304  gvputs(job, "<title>");
305  gvputs(job, xml_string(agnameof(obj->u.n)));
306  gvputs(job, "</title>\n");
307 }
308 
309 static void svg_end_node(GVJ_t * job)
310 {
311  gvputs(job, "</g>\n");
312 }
313 
314 static void svg_begin_edge(GVJ_t * job)
315 {
316  obj_state_t *obj = job->obj;
317  char *ename;
318 
319  svg_print_id_class(job, obj->id, NULL, "edge", obj->u.e);
320  gvputs(job, ">\n");
321 
322  gvputs(job, "<title>");
323  ename = strdup_and_subst_obj("\\E", (void *) (obj->u.e));
324  gvputs(job, xml_string(ename));
325  free(ename);
326  gvputs(job, "</title>\n");
327 }
328 
329 static void svg_end_edge(GVJ_t * job)
330 {
331  gvputs(job, "</g>\n");
332 }
333 
334 static void
335 svg_begin_anchor(GVJ_t * job, char *href, char *tooltip, char *target,
336  char *id)
337 {
338  gvputs(job, "<g");
339  if (id) {
340  gvputs(job, " id=\"a_");
341  gvputs(job, xml_string(id));
342  gvputs(job, "\"");
343  }
344  gvputs(job, ">");
345 
346  gvputs(job, "<a");
347 #if 0
348  /* the svg spec implies this can be omitted: http://www.w3.org/TR/SVG/linking.html#Links */
349  gvputs(job, " xlink:type=\"simple\"");
350 #endif
351  if (href && href[0]) {
352  gvputs(job, " xlink:href=\"");
353  gvputs(job, href);
354  gvputs(job, "\"");
355  }
356 #if 0
357  /* linking to itself, just so that it can have a xlink:link in the anchor, seems wrong.
358  * it changes the behavior in browsers, the link apears in the bottom information bar
359  */
360  else {
361  assert(id && id[0]); /* there should always be an id available */
362  gvputs(job, " xlink:href=\"#");
363  gvputs(job, xml_url_string(href));
364  gvputs(job, "\"");
365  }
366 #endif
367  if (tooltip && tooltip[0]) {
368  gvputs(job, " xlink:title=\"");
369  gvputs(job, xml_string0(tooltip, 1));
370  gvputs(job, "\"");
371  }
372  if (target && target[0]) {
373  gvputs(job, " target=\"");
374  gvputs(job, xml_string(target));
375  gvputs(job, "\"");
376  }
377  gvputs(job, ">\n");
378 }
379 
380 static void svg_end_anchor(GVJ_t * job)
381 {
382  gvputs(job, "</a>\n");
383  gvputs(job, "</g>\n");
384 }
385 
386 static void svg_textspan(GVJ_t * job, pointf p, textspan_t * span)
387 {
388  obj_state_t *obj = job->obj;
389  PostscriptAlias *pA;
390  char *family = NULL, *weight = NULL, *stretch = NULL, *style = NULL;
391  unsigned int flags;
392 
393  gvputs(job, "<text");
394  switch (span->just) {
395  case 'l':
396  gvputs(job, " text-anchor=\"start\"");
397  break;
398  case 'r':
399  gvputs(job, " text-anchor=\"end\"");
400  break;
401  default:
402  case 'n':
403  gvputs(job, " text-anchor=\"middle\"");
404  break;
405  }
406  p.y += span->yoffset_centerline;
407  if (!obj->labeledgealigned) {
408  gvputs(job, " x=\"");
409  gvprintdouble(job, p.x);
410  gvputs(job, "\" y=\"");
411  gvprintdouble(job, -p.y);
412  gvputs(job, "\"");
413  }
414  pA = span->font->postscript_alias;
415  if (pA) {
416  switch (GD_fontnames(job->gvc->g)) {
417  case PSFONTS:
418  family = pA->name;
419  weight = pA->weight;
420  style = pA->style;
421  break;
422  case SVGFONTS:
423  family = pA->svg_font_family;
424  weight = pA->svg_font_weight;
425  style = pA->svg_font_style;
426  break;
427  default:
428  case NATIVEFONTS:
429  family = pA->family;
430  weight = pA->weight;
431  style = pA->style;
432  break;
433  }
434  stretch = pA->stretch;
435 
436  gvprintf(job, " font-family=\"%s", family);
437  if (pA->svg_font_family)
438  gvprintf(job, ",%s", pA->svg_font_family);
439  gvputs(job, "\"");
440  if (weight)
441  gvprintf(job, " font-weight=\"%s\"", weight);
442  if (stretch)
443  gvprintf(job, " font-stretch=\"%s\"", stretch);
444  if (style)
445  gvprintf(job, " font-style=\"%s\"", style);
446  } else
447  gvprintf(job, " font-family=\"%s\"", span->font->name);
448  if ((span->font) && (flags = span->font->flags)) {
449  if ((flags & HTML_BF) && !weight)
450  gvprintf(job, " font-weight=\"bold\"");
451  if ((flags & HTML_IF) && !style)
452  gvprintf(job, " font-style=\"italic\"");
453  if ((flags & (HTML_UL|HTML_S|HTML_OL))) {
454  int comma = 0;
455  gvprintf(job, " text-decoration=\"");
456  if ((flags & HTML_UL)) {
457  gvprintf(job, "underline");
458  comma = 1;
459  }
460  if ((flags & HTML_OL)) {
461  gvprintf(job, "%soverline", (comma?",":""));
462  comma = 1;
463  }
464  if ((flags & HTML_S))
465  gvprintf(job, "%sline-through", (comma?",":""));
466  gvprintf(job, "\"");
467  }
468  if ((flags & HTML_SUP))
469  gvprintf(job, " baseline-shift=\"super\"");
470  if ((flags & HTML_SUB))
471  gvprintf(job, " baseline-shift=\"sub\"");
472  }
473 
474  gvprintf(job, " font-size=\"%.2f\"", span->font->size);
475  switch (obj->pencolor.type) {
476  case COLOR_STRING:
477  if (strcasecmp(obj->pencolor.u.string, "black"))
478  gvprintf(job, " fill=\"%s\"", obj->pencolor.u.string);
479  break;
480  case RGBA_BYTE:
481  gvprintf(job, " fill=\"#%02x%02x%02x\"",
482  obj->pencolor.u.rgba[0], obj->pencolor.u.rgba[1],
483  obj->pencolor.u.rgba[2]);
484  if (obj->pencolor.u.rgba[3] > 0 && obj->pencolor.u.rgba[3] < 255)
485  gvprintf(job, " fill-opacity=\"%f\"", ((float) obj->pencolor.u.rgba[3] / 255.0));
486  break;
487  default:
488  assert(0); /* internal error */
489  }
490  gvputs(job, ">");
491  if (obj->labeledgealigned) {
492  gvprintf(job, "<textPath xlink:href=\"#%s_p\" startOffset=\"50%%\">", xml_string(obj->id));
493  gvputs(job, "<tspan x=\"0\" dy=\"");
494  gvprintdouble(job, -p.y);
495  gvputs(job, "\">");
496  }
497  gvputs(job, xml_string0(span->str, TRUE));
498  if (obj->labeledgealigned)
499  gvprintf (job, "</tspan></textPath>");
500  gvputs(job, "</text>\n");
501 }
502 
503 /* svg_gradstyle
504  * Outputs the SVG statements that define the gradient pattern
505  */
506 static int svg_gradstyle(GVJ_t * job, pointf * A, int n)
507 {
508  pointf G[2];
509  float angle;
510  static int gradId;
511  int id = gradId++;
512 
513  obj_state_t *obj = job->obj;
514  angle = obj->gradient_angle * M_PI / 180; //angle of gradient line
515  G[0].x = G[0].y = G[1].x = G[1].y = 0.;
516  get_gradient_points(A, G, n, angle, 0); //get points on gradient line
517 
518  gvprintf(job,
519  "<defs>\n<linearGradient id=\"l_%d\" gradientUnits=\"userSpaceOnUse\" ", id);
520  gvputs(job, "x1=\"");
521  gvprintdouble(job, G[0].x);
522  gvputs(job, "\" y1=\"");
523  gvprintdouble(job, G[0].y);
524  gvputs(job, "\" x2=\"");
525  gvprintdouble(job, G[1].x);
526  gvputs(job, "\" y2=\"");
527  gvprintdouble(job, G[1].y);
528  gvputs(job, "\" >\n");
529  if (obj->gradient_frac > 0)
530  gvprintf(job, "<stop offset=\"%.03f\" style=\"stop-color:", obj->gradient_frac - 0.001);
531  else
532  gvputs(job, "<stop offset=\"0\" style=\"stop-color:");
533  svg_print_color(job, obj->fillcolor);
534  gvputs(job, ";stop-opacity:");
535  if (obj->fillcolor.type == RGBA_BYTE && obj->fillcolor.u.rgba[3] > 0
536  && obj->fillcolor.u.rgba[3] < 255)
537  gvprintf(job, "%f", ((float) obj->fillcolor.u.rgba[3] / 255.0));
538  else
539  gvputs(job, "1.");
540  gvputs(job, ";\"/>\n");
541  if (obj->gradient_frac > 0)
542  gvprintf(job, "<stop offset=\"%.03f\" style=\"stop-color:", obj->gradient_frac);
543  else
544  gvputs(job, "<stop offset=\"1\" style=\"stop-color:");
545  svg_print_color(job, obj->stopcolor);
546  gvputs(job, ";stop-opacity:");
547  if (obj->stopcolor.type == RGBA_BYTE && obj->stopcolor.u.rgba[3] > 0
548  && obj->stopcolor.u.rgba[3] < 255)
549  gvprintf(job, "%f", ((float) obj->stopcolor.u.rgba[3] / 255.0));
550  else
551  gvputs(job, "1.");
552  gvputs(job, ";\"/>\n</linearGradient>\n</defs>\n");
553  return id;
554 }
555 
556 /* svg_rgradstyle
557  * Outputs the SVG statements that define the radial gradient pattern
558  */
559 static int svg_rgradstyle(GVJ_t * job, pointf * A, int n)
560 {
561  /* pointf G[2]; */
562  float angle;
563  int ifx, ify;
564  static int rgradId;
565  int id = rgradId++;
566 
567  obj_state_t *obj = job->obj;
568  angle = obj->gradient_angle * M_PI / 180; //angle of gradient line
569  /* G[0].x = G[0].y = G[1].x = G[1].y; */
570  /* get_gradient_points(A, G, n, 0, 1); */
571  if (angle == 0.) {
572  ifx = ify = 50;
573  } else {
574  ifx = 50 * (1 + cos(angle));
575  ify = 50 * (1 - sin(angle));
576  }
577  gvprintf(job,
578  "<defs>\n<radialGradient id=\"r_%d\" cx=\"50%%\" cy=\"50%%\" r=\"75%%\" fx=\"%d%%\" fy=\"%d%%\">\n",
579  id, ifx, ify);
580  gvputs(job, "<stop offset=\"0\" style=\"stop-color:");
581  svg_print_color(job, obj->fillcolor);
582  gvputs(job, ";stop-opacity:");
583  if (obj->fillcolor.type == RGBA_BYTE && obj->fillcolor.u.rgba[3] > 0
584  && obj->fillcolor.u.rgba[3] < 255)
585  gvprintf(job, "%f", ((float) obj->fillcolor.u.rgba[3] / 255.0));
586  else
587  gvputs(job, "1.");
588  gvputs(job, ";\"/>\n");
589  gvputs(job, "<stop offset=\"1\" style=\"stop-color:");
590  svg_print_color(job, obj->stopcolor);
591  gvputs(job, ";stop-opacity:");
592  if (obj->stopcolor.type == RGBA_BYTE && obj->stopcolor.u.rgba[3] > 0
593  && obj->stopcolor.u.rgba[3] < 255)
594  gvprintf(job, "%f", ((float) obj->stopcolor.u.rgba[3] / 255.0));
595  else
596  gvputs(job, "1.");
597  gvputs(job, ";\"/>\n</radialGradient>\n</defs>\n");
598  return id;
599 }
600 
601 
602 static void svg_ellipse(GVJ_t * job, pointf * A, int filled)
603 {
604  int gid = 0;
605 
606  /* A[] contains 2 points: the center and corner. */
607  if (filled == GRADIENT) {
608  gid = svg_gradstyle(job, A, 2);
609  } else if (filled == (RGRADIENT)) {
610  gid = svg_rgradstyle(job, A, 2);
611  }
612  gvputs(job, "<ellipse");
613  svg_grstyle(job, filled, gid);
614  gvputs(job, " cx=\"");
615  gvprintdouble(job, A[0].x);
616  gvputs(job, "\" cy=\"");
617  gvprintdouble(job, -A[0].y);
618  gvputs(job, "\" rx=\"");
619  gvprintdouble(job, A[1].x - A[0].x);
620  gvputs(job, "\" ry=\"");
621  gvprintdouble(job, A[1].y - A[0].y);
622  gvputs(job, "\"/>\n");
623 }
624 
625 static void
626 svg_bezier(GVJ_t * job, pointf * A, int n, int arrow_at_start,
627  int arrow_at_end, int filled)
628 {
629  int gid = 0;
630  obj_state_t *obj = job->obj;
631 
632  if (filled == GRADIENT) {
633  gid = svg_gradstyle(job, A, n);
634  } else if (filled == (RGRADIENT)) {
635  gid = svg_rgradstyle(job, A, n);
636  }
637  gvputs(job, "<path");
638  if (obj->labeledgealigned) {
639  gvputs(job, " id=\"");
640  gvputs(job, xml_string(obj->id));
641  gvputs(job, "_p\" ");
642  }
643  svg_grstyle(job, filled, gid);
644  gvputs(job, " d=\"");
645  svg_bzptarray(job, A, n);
646  gvputs(job, "\"/>\n");
647 }
648 
649 static void svg_polygon(GVJ_t * job, pointf * A, int n, int filled)
650 {
651  int i, gid = 0;
652  if (filled == GRADIENT) {
653  gid = svg_gradstyle(job, A, n);
654  } else if (filled == (RGRADIENT)) {
655  gid = svg_rgradstyle(job, A, n);
656  }
657  gvputs(job, "<polygon");
658  svg_grstyle(job, filled, gid);
659  gvputs(job, " points=\"");
660  for (i = 0; i < n; i++) {
661  gvprintdouble(job, A[i].x);
662  gvputs(job, ",");
663  gvprintdouble(job, -A[i].y);
664  gvputs(job, " ");
665  }
666  /* repeat the first point because Adobe SVG is broken */
667  gvprintdouble(job, A[0].x);
668  gvputs(job, ",");
669  gvprintdouble(job, -A[0].y);
670  gvputs(job, "\"/>\n");
671 }
672 
673 static void svg_polyline(GVJ_t * job, pointf * A, int n)
674 {
675  int i;
676 
677  gvputs(job, "<polyline");
678  svg_grstyle(job, 0, 0);
679  gvputs(job, " points=\"");
680  for (i = 0; i < n; i++) {
681  gvprintdouble(job, A[i].x);
682  gvputs(job, ",");
683  gvprintdouble(job, -A[i].y);
684  gvputs(job, " ");
685  }
686  gvputs(job, "\"/>\n");
687 }
688 
689 /* color names from http://www.w3.org/TR/SVG/types.html */
690 /* NB. List must be LANG_C sorted */
691 static char *svg_knowncolors[] = {
692  "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure",
693  "beige", "bisque", "black", "blanchedalmond", "blue",
694  "blueviolet", "brown", "burlywood",
695  "cadetblue", "chartreuse", "chocolate", "coral",
696  "cornflowerblue", "cornsilk", "crimson", "cyan",
697  "darkblue", "darkcyan", "darkgoldenrod", "darkgray",
698  "darkgreen", "darkgrey", "darkkhaki", "darkmagenta",
699  "darkolivegreen", "darkorange", "darkorchid", "darkred",
700  "darksalmon", "darkseagreen", "darkslateblue", "darkslategray",
701  "darkslategrey", "darkturquoise", "darkviolet", "deeppink",
702  "deepskyblue", "dimgray", "dimgrey", "dodgerblue",
703  "firebrick", "floralwhite", "forestgreen", "fuchsia",
704  "gainsboro", "ghostwhite", "gold", "goldenrod", "gray",
705  "green", "greenyellow", "grey",
706  "honeydew", "hotpink", "indianred",
707  "indigo", "ivory", "khaki",
708  "lavender", "lavenderblush", "lawngreen", "lemonchiffon",
709  "lightblue", "lightcoral", "lightcyan", "lightgoldenrodyellow",
710  "lightgray", "lightgreen", "lightgrey", "lightpink",
711  "lightsalmon", "lightseagreen", "lightskyblue",
712  "lightslategray", "lightslategrey", "lightsteelblue",
713  "lightyellow", "lime", "limegreen", "linen",
714  "magenta", "maroon", "mediumaquamarine", "mediumblue",
715  "mediumorchid", "mediumpurple", "mediumseagreen",
716  "mediumslateblue", "mediumspringgreen", "mediumturquoise",
717  "mediumvioletred", "midnightblue", "mintcream",
718  "mistyrose", "moccasin",
719  "navajowhite", "navy", "oldlace",
720  "olive", "olivedrab", "orange", "orangered", "orchid",
721  "palegoldenrod", "palegreen", "paleturquoise",
722  "palevioletred", "papayawhip", "peachpuff", "peru", "pink",
723  "plum", "powderblue", "purple",
724  "red", "rosybrown", "royalblue",
725  "saddlebrown", "salmon", "sandybrown", "seagreen", "seashell",
726  "sienna", "silver", "skyblue", "slateblue", "slategray",
727  "slategrey", "snow", "springgreen", "steelblue",
728  "tan", "teal", "thistle", "tomato", "turquoise",
729  "violet",
730  "wheat", "white", "whitesmoke",
731  "yellow", "yellowgreen"
732 };
733 
735  svg_begin_job,
736  0, /* svg_end_job */
737  svg_begin_graph,
738  svg_end_graph,
739  svg_begin_layer,
740  svg_end_layer,
741  svg_begin_page,
742  svg_end_page,
743  svg_begin_cluster,
744  svg_end_cluster,
745  0, /* svg_begin_nodes */
746  0, /* svg_end_nodes */
747  0, /* svg_begin_edges */
748  0, /* svg_end_edges */
749  svg_begin_node,
750  svg_end_node,
751  svg_begin_edge,
752  svg_end_edge,
753  svg_begin_anchor,
754  svg_end_anchor,
755  0, /* svg_begin_anchor */
756  0, /* svg_end_anchor */
757  svg_textspan,
758  0, /* svg_resolve_color */
759  svg_ellipse,
760  svg_polygon,
761  svg_bezier,
762  svg_polyline,
763  svg_comment,
764  0, /* svg_library_shape */
765 };
766 
769  4., /* default pad - graph units */
770  svg_knowncolors, /* knowncolors */
771  sizeof(svg_knowncolors) / sizeof(char *), /* sizeof knowncolors */
772  RGBA_BYTE, /* color_type */
773 };
774 
777  {0., 0.}, /* default margin - points */
778  {0., 0.}, /* default page width, height - points */
779  {72., 72.}, /* default dpi */
780 };
781 
784  {0., 0.}, /* default margin - points */
785  {0., 0.}, /* default page width, height - points */
786  {72., 72.}, /* default dpi */
787 };
788 
790  {FORMAT_SVG, "svg", 1, &svg_engine, &render_features_svg},
791  {0, NULL, 0, NULL, NULL}
792 };
793 
795  {FORMAT_SVG, "svg:svg", 1, NULL, &device_features_svg},
796 #if HAVE_LIBZ
797  {FORMAT_SVGZ, "svgz:svg", 1, NULL, &device_features_svgz},
798 #endif
799  {0, NULL, 0, NULL, NULL}
800 };
void s1(graph_t *, node_t *)
Definition: stuff.c:686
#define PENWIDTH_NORMAL
Definition: gvcjob.h:40
gvcolor_t stopcolor
Definition: gvcjob.h:203
int rotation
Definition: gvcjob.h:328
union color_s::@10 u
#define HTML_S
Definition: textspan.h:29
#define HTML_IF
Definition: textspan.h:25
#define GVDEVICE_BINARY_FORMAT
Definition: gvcjob.h:93
char * weight
Definition: textspan.h:35
char * xml_url_string(char *s)
Definition: labels.c:572
pen_type pen
Definition: gvcjob.h:206
#define HTML_BF
Definition: textspan.h:24
double size
Definition: textspan.h:52
char * stretch
Definition: textspan.h:36
point pagesArraySize
Definition: gvcjob.h:313
#define assert(x)
Definition: cghdr.h:47
Definition: geom.h:28
unsigned int width
Definition: gvcjob.h:336
char * svg_font_style
Definition: textspan.h:41
graph_t * g
Definition: gvcint.h:106
Definition: color.h:34
gvcolor_t pencolor
Definition: gvcjob.h:203
char ** layerIDs
Definition: gvcint.h:128
Definition: gvcjob.h:271
char * name
Definition: textspan.h:49
int x
Definition: geom.h:26
char * strdup_and_subst_obj(char *str, void *obj)
Definition: labels.c:451
#define HTML_SUB
Definition: textspan.h:28
#define GVRENDER_DOES_TRANSFORM
Definition: gvcjob.h:97
obj_state_t * obj
Definition: gvcjob.h:278
char * agget(void *obj, char *name)
Definition: attr.c:428
#define GVRENDER_Y_GOES_DOWN
Definition: gvcjob.h:96
int gvputs(GVJ_t *job, const char *s)
Definition: gvdevice.c:270
char * str
Definition: textspan.h:59
color_type_t type
Definition: color.h:44
Definition: types.h:276
#define GVRENDER_DOES_TARGETS
Definition: gvcjob.h:107
unsigned int flags
Definition: textspan.h:53
#define GVRENDER_DOES_TOOLTIPS
Definition: gvcjob.h:106
#define GVDEVICE_DOES_LAYERS
Definition: gvcjob.h:90
boxf canvasBox
Definition: gvcjob.h:331
double y
Definition: geom.h:28
#define GD_fontnames(g)
Definition: types.h:411
char * xml_string(char *s)
Definition: labels.c:485
CGRAPH_API char * agnameof(void *)
Definition: id.c:143
#define HTML_OL
Definition: textspan.h:30
char * string
Definition: color.h:41
pointf scale
Definition: gvcjob.h:341
GVCOMMON_t * common
Definition: gvcjob.h:276
char ** info
Definition: gvcommon.h:22
double yoffset_centerline
Definition: textspan.h:63
#define GRADIENT
Definition: const.h:254
PostscriptAlias * postscript_alias
Definition: textspan.h:51
char * svg_font_weight
Definition: textspan.h:40
GVC_t * gvc
Definition: gvcjob.h:272
#define HTML_UL
Definition: textspan.h:26
gvplugin_installed_t gvdevice_svg_types[]
Definition: grammar.c:79
int gradient_angle
Definition: gvcjob.h:204
graph_t * g
Definition: gvcjob.h:195
int layerNum
Definition: gvcjob.h:311
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 RGRADIENT
Definition: const.h:255
float gradient_frac
Definition: gvcjob.h:205
pointf translation
Definition: gvcjob.h:342
graph_t * sg
Definition: gvcjob.h:196
double x
Definition: geom.h:28
#define HTML_SUP
Definition: textspan.h:27
int strcasecmp(const char *s1, const char *s2)
Definition: strcasecmp.c:21
char just
Definition: textspan.h:65
char * style
Definition: textspan.h:37
pointf LL
Definition: geom.h:35
#define GVDEVICE_COMPRESSED_FORMAT
Definition: gvcjob.h:94
union obj_state_s::@23 u
#define GVRENDER_DOES_LABELS
Definition: gvcjob.h:99
edge_t * e
Definition: gvcjob.h:198
#define M_PI
Definition: arith.h:77
char * svg_font_family
Definition: textspan.h:39
gvrender_engine_t svg_engine
agxbuf * str
Definition: htmlparse.c:85
gvplugin_installed_t gvrender_svg_types[]
char * id
Definition: gvcjob.h:220
gvcolor_t fillcolor
Definition: gvcjob.h:203
node_t * n
Definition: gvcjob.h:197
double penwidth
Definition: gvcjob.h:208
gvdevice_features_t device_features_svg
int y
Definition: geom.h:26
unsigned char rgba[4]
Definition: color.h:38
char * family
Definition: textspan.h:34
pointf UR
Definition: geom.h:35
unsigned int height
Definition: gvcjob.h:337
int labeledgealigned
Definition: gvcjob.h:244
#define GVDEVICE_DOES_TRUECOLOR
Definition: gvcjob.h:92
gvrender_features_t render_features_svg
void gvprintf(GVJ_t *job, const char *format,...)
Definition: gvdevice.c:389
textfont_t * font
Definition: textspan.h:60
gvdevice_features_t device_features_svgz
char * xml_string0(char *s, boolean raw)
Definition: labels.c:497
void get_gradient_points(pointf *A, pointf *G, int n, float angle, int flags)
Definition: utils.c:1827
#define TRUE
Definition: cgraph.h:38