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