2 * Hic Est Leo -- SVG Generator
4 * (c) 2014 Martin Mares <mj@ucw.cz>
11 #include <ucw/mempool.h>
12 #include <ucw-xml/xml.h>
18 static void svg_start_tag(struct svg *svg, struct svg_element *e);
19 static void svg_escape_string(struct svg *svg, const char *str);
21 struct svg *svg_open(char *filename)
23 struct mempool *mp = mp_new(4096);
24 struct svg *svg = mp_alloc_zero(mp, sizeof(*svg));
27 svg->fb = bopen_file(filename, O_WRONLY | O_CREAT | O_TRUNC, NULL);
28 svg->scale = 90 / 25.4;
29 // FIXME: Use scale for all operations with dimensions?
30 GARY_INIT(svg->stack, 0);
32 svg->fb_pool = mp_alloc_zero(svg->pool, sizeof(*svg->fb_pool));
33 fbpool_init(svg->fb_pool);
37 bputsn(svg->fb, "<?xml version=\"1.0\" standalone=\"no\"?>");
38 bputsn(svg->fb, "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">");
40 svg_push_element(svg, "svg");
41 svg_set_attr(svg, "version", "1.1");
42 svg_set_attr(svg, "xmlns", "http://www.w3.org/2000/svg");
43 svg_set_attr(svg, "xmlns:xlink", "http://www.w3.org/1999/xlink");
48 void svg_close(struct svg *svg)
50 ASSERT(GARY_SIZE(svg->stack) == 1);
54 GARY_FREE(svg->stack);
55 svg_icon_cleanup(svg);
59 static inline struct svg_element *svg_element_this_or_null(struct svg *svg)
61 uns n = GARY_SIZE(svg->stack);
62 return n ? svg->stack[n-1] : NULL;
65 static inline struct svg_element *svg_element_this(struct svg *svg)
67 struct svg_element *e = svg_element_this_or_null(svg);
72 enum svg_indent_mode {
77 static void svg_indent_start(struct svg *svg, struct svg_element *e, enum svg_indent_mode mode)
79 if (e->indent < 0 || (e->flags & SVG_EF_FLAT) && mode != SVG_INDENT_OPEN)
81 for (int i=0; i<e->indent; i++)
85 static void svg_indent_end(struct svg *svg, struct svg_element *e, enum svg_indent_mode mode)
87 if (e->indent < 0 || (e->flags & SVG_EF_FLAT) && mode != SVG_INDENT_CLOSE)
92 static struct svg_element *svg_push_internal(struct svg *svg, uns type)
96 struct svg_element *parent = svg_element_this_or_null(svg);
99 ASSERT(parent->type == XML_NODE_ELEM);
100 parent->flags |= SVG_EF_HAS_CHILDREN;
101 if (!(parent->flags & SVG_EF_START_TAG))
102 svg_start_tag(svg, parent);
106 struct svg_element *e = mp_alloc(svg->pool, sizeof(*e));
110 clist_init(&e->attrs);
111 *GARY_PUSH(svg->stack) = e;
115 if (parent->indent < 0 || (parent->flags & SVG_EF_FLAT))
118 e->indent = parent->indent + 1;
126 struct svg_element *svg_push_element(struct svg *svg, const char *name)
128 struct svg_element *e = svg_push_internal(svg, XML_NODE_ELEM);
129 e->name = mp_strdup(svg->pool, name);
133 struct svg_element *svg_push_chars(struct svg *svg)
135 return svg_push_internal(svg, XML_NODE_CHARS);
138 struct svg_element *svg_push_comment(struct svg *svg)
140 return svg_push_internal(svg, XML_NODE_COMMENT);
143 void svg_pop(struct svg *svg)
145 struct svg_element *e = svg_element_this(svg);
146 ASSERT(!svg->str_fb);
151 if (!(e->flags & SVG_EF_START_TAG))
153 // Will recognize that the element has no children and an auto-closing tag
154 svg_start_tag(svg, e);
158 svg_indent_start(svg, e, SVG_INDENT_CLOSE);
159 bprintf(svg->fb, "</%s>", e->name);
160 svg_indent_end(svg, e, SVG_INDENT_CLOSE);
165 svg_indent_start(svg, e, SVG_INDENT_OPEN);
166 svg_escape_string(svg, e->name);
167 svg_indent_end(svg, e, SVG_INDENT_CLOSE);
169 case XML_NODE_COMMENT:
171 svg_indent_start(svg, e, SVG_INDENT_OPEN);
172 bputs(svg->fb, "<!-- ");
173 bputs(svg->fb, e->name);
174 bputs(svg->fb, " -->");
175 svg_indent_end(svg, e, SVG_INDENT_CLOSE);
182 GARY_POP(svg->stack);
185 static void svg_escape_string(struct svg *svg, const char *str)
187 for (const char *c = str; *c; c++)
191 bputs(svg->fb, """);
194 bputs(svg->fb, "'");
197 bputs(svg->fb, "<");
200 bputs(svg->fb, ">");
203 bputs(svg->fb, "&");
210 static void svg_start_tag(struct svg *svg, struct svg_element *e)
212 ASSERT(!(e->flags & SVG_EF_START_TAG));
213 e->flags |= SVG_EF_START_TAG;
215 svg_indent_start(svg, e, SVG_INDENT_OPEN);
217 bputs(svg->fb, e->name);
219 CLIST_FOR_EACH(struct svg_attr *, a, e->attrs)
222 bputs(svg->fb, a->key);
223 bputs(svg->fb, "=\"");
224 svg_escape_string(svg, a->val);
228 if (e->flags & SVG_EF_HAS_CHILDREN)
231 svg_indent_end(svg, e, SVG_INDENT_OPEN);
235 bputs(svg->fb, " />");
236 svg_indent_end(svg, e, SVG_INDENT_CLOSE);
240 void svg_set_attr_ref(struct svg *svg, const char *key, const char *val)
242 struct svg_element *e = svg_element_this(svg);
243 ASSERT(e->type == XML_NODE_ELEM);
244 ASSERT(!(e->flags & SVG_EF_START_TAG));
245 ASSERT(!svg->str_fb);
247 CLIST_FOR_EACH(struct svg_attr *, a, e->attrs)
248 if (!strcmp(a->key, key))
254 struct svg_attr *a = mp_alloc(svg->pool, sizeof(*a));
255 a->key = mp_strdup(svg->pool, key);
257 clist_add_tail(&e->attrs, &a->n);
260 void svg_set_attr(struct svg *svg, const char *key, const char *val)
262 svg_set_attr_ref(svg, key, mp_strdup(svg->pool, val));
265 void svg_set_attr_int(struct svg *svg, const char *key, int val)
267 svg_set_attr_ref(svg, key, mp_printf(svg->pool, "%d", val));
270 void svg_set_attr_float(struct svg *svg, const char *key, double val)
272 svg_set_attr_ref(svg, key, mp_printf(svg->pool, "%.6g", val));
275 char *svg_format_dimen(struct svg *svg, double val)
277 return mp_printf(svg->pool, "%.6g", val * svg->scale);
280 void svg_set_attr_dimen(struct svg *svg, const char *key, double val)
282 svg_set_attr_ref(svg, key, svg_format_dimen(svg, val));
285 void svg_set_attr_color(struct svg *svg, const char *key, color_t color)
287 svg_set_attr_ref(svg, key, mp_printf(svg->pool, "#%06x", color));
290 void svg_set_attr_format(struct svg *svg, const char *key, const char *fmt, ...)
294 svg_set_attr_ref(svg, key, mp_vprintf(svg->pool, fmt, args));
298 struct fastbuf *svg_fb_open(struct svg *svg)
300 ASSERT(!svg->str_fb);
301 fbpool_start(svg->fb_pool, svg->pool, 0);
302 svg->str_fb = (struct fastbuf *) svg->fb_pool;
306 const char *svg_fb_close(struct svg *svg)
309 bputc(svg->str_fb, 0);
310 const char *str = fbpool_end(svg->fb_pool);
315 void svg_fb_close_as_attr(struct svg *svg, const char *key)
317 const char *val = svg_fb_close(svg);
318 svg_set_attr_ref(svg, key, val);
321 struct svg_element *svg_push_path(struct svg *svg)
323 struct svg_element *e = svg_push_element(svg, "path");
326 // Construct the path, then call svg_path_end() and finally svg_pop()
327 // CAVEAT: Do not set attributes before calling svg_path_end()
331 void svg_path_end(struct svg *svg)
333 svg_fb_close_as_attr(svg, "d");
336 static void svg_path_printf(struct svg *svg, const char *fmt, ...)
340 if (btell(svg->str_fb))
341 bputc(svg->str_fb, ' ');
342 vbprintf(svg->str_fb, fmt, args);
346 void svg_path_move_to(struct svg *svg, double x, double y)
348 svg_path_printf(svg, "M %.6g %.6g", x * svg->scale, y * svg->scale);
351 void svg_path_move_to_rel(struct svg *svg, double x, double y)
353 svg_path_printf(svg, "m %.6g %.6g", x * svg->scale, y * svg->scale);
356 void svg_path_line_to(struct svg *svg, double x, double y)
358 svg_path_printf(svg, "L %.6g %.6g", x * svg->scale, y * svg->scale);
361 void svg_path_line_to_rel(struct svg *svg, double x, double y)
363 svg_path_printf(svg, "l %.6g %.6g", x * svg->scale, y * svg->scale);
366 void svg_path_close(struct svg *svg)
368 svg_path_printf(svg, "Z");