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