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