2 * Hic Est Leo -- SVG Generator
4 * (c) 2014 Martin Mares <mj@ucw.cz>
9 #include <ucw/mempool.h>
10 #include <ucw-xml/xml.h>
19 static void svg_start_tag(struct svg *svg, struct svg_element *e);
20 static void svg_escape_string(struct svg *svg, const char *str);
22 struct svg *svg_open(char *filename)
24 struct mempool *mp = mp_new(4096);
25 struct svg *svg = mp_alloc_zero(mp, sizeof(*svg));
28 svg->fb = bopen_file(filename, O_WRONLY | O_CREAT | O_TRUNC, NULL);
29 svg->scale = 90 / 25.4;
30 // FIXME: Use scale for all operations with dimensions?
31 GARY_INIT(svg->stack, 0);
33 svg->fb_pool = mp_alloc_zero(svg->pool, sizeof(*svg->fb_pool));
34 fbpool_init(svg->fb_pool);
38 bputsn(svg->fb, "<?xml version=\"1.0\" standalone=\"no\"?>");
39 bputsn(svg->fb, "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">");
41 svg_push_element(svg, "svg");
42 svg_set_attr(svg, "version", "1.1");
43 svg_set_attr(svg, "xmlns", "http://www.w3.org/2000/svg");
44 svg_set_attr(svg, "xmlns:xlink", "http://www.w3.org/1999/xlink");
49 void svg_close(struct svg *svg)
51 ASSERT(GARY_SIZE(svg->stack) == 1);
55 GARY_FREE(svg->stack);
56 svg_icon_cleanup(svg);
60 static inline struct svg_element *svg_element_this_or_null(struct svg *svg)
62 uns n = GARY_SIZE(svg->stack);
63 return n ? svg->stack[n-1] : NULL;
66 static inline struct svg_element *svg_element_this(struct svg *svg)
68 struct svg_element *e = svg_element_this_or_null(svg);
73 enum svg_indent_mode {
78 static void svg_indent_start(struct svg *svg, struct svg_element *e, enum svg_indent_mode mode)
80 if (e->indent < 0 || (e->flags & SVG_EF_FLAT) && mode != SVG_INDENT_OPEN)
82 for (int i=0; i<e->indent; i++)
86 static void svg_indent_end(struct svg *svg, struct svg_element *e, enum svg_indent_mode mode)
88 if (e->indent < 0 || (e->flags & SVG_EF_FLAT) && mode != SVG_INDENT_CLOSE)
93 static struct svg_element *svg_push_internal(struct svg *svg, uns type)
97 struct svg_element *parent = svg_element_this_or_null(svg);
100 ASSERT(parent->type == XML_NODE_ELEM);
101 parent->flags |= SVG_EF_HAS_CHILDREN;
102 if (!(parent->flags & SVG_EF_START_TAG))
103 svg_start_tag(svg, parent);
107 struct svg_element *e = mp_alloc(svg->pool, sizeof(*e));
111 clist_init(&e->attrs);
112 *GARY_PUSH(svg->stack) = e;
116 if (parent->indent < 0 || (parent->flags & SVG_EF_FLAT))
119 e->indent = parent->indent + 1;
127 struct svg_element *svg_push_element(struct svg *svg, const char *name)
129 struct svg_element *e = svg_push_internal(svg, XML_NODE_ELEM);
130 e->name = mp_strdup(svg->pool, name);
134 struct svg_element *svg_push_chars(struct svg *svg)
136 return svg_push_internal(svg, XML_NODE_CHARS);
139 struct svg_element *svg_push_comment(struct svg *svg)
141 return svg_push_internal(svg, XML_NODE_COMMENT);
144 void svg_pop(struct svg *svg)
146 struct svg_element *e = svg_element_this(svg);
147 ASSERT(!svg->str_fb);
152 if (!(e->flags & SVG_EF_START_TAG))
154 // Will recognize that the element has no children and an auto-closing tag
155 svg_start_tag(svg, e);
159 svg_indent_start(svg, e, SVG_INDENT_CLOSE);
160 bprintf(svg->fb, "</%s>", e->name);
161 svg_indent_end(svg, e, SVG_INDENT_CLOSE);
166 svg_indent_start(svg, e, SVG_INDENT_OPEN);
167 svg_escape_string(svg, e->name);
168 svg_indent_end(svg, e, SVG_INDENT_CLOSE);
170 case XML_NODE_COMMENT:
172 svg_indent_start(svg, e, SVG_INDENT_OPEN);
173 bputs(svg->fb, "<!-- ");
174 bputs(svg->fb, e->name);
175 bputs(svg->fb, " -->");
176 svg_indent_end(svg, e, SVG_INDENT_CLOSE);
183 GARY_POP(svg->stack);
186 static void svg_escape_string(struct svg *svg, const char *str)
188 for (const char *c = str; *c; c++)
192 bputs(svg->fb, """);
195 bputs(svg->fb, "'");
198 bputs(svg->fb, "<");
201 bputs(svg->fb, ">");
204 bputs(svg->fb, "&");
211 static void svg_start_tag(struct svg *svg, struct svg_element *e)
213 ASSERT(!(e->flags & SVG_EF_START_TAG));
214 e->flags |= SVG_EF_START_TAG;
216 svg_indent_start(svg, e, SVG_INDENT_OPEN);
218 bputs(svg->fb, e->name);
220 CLIST_FOR_EACH(struct svg_attr *, a, e->attrs)
223 bputs(svg->fb, a->key);
224 bputs(svg->fb, "=\"");
225 svg_escape_string(svg, a->val);
229 if (e->flags & SVG_EF_HAS_CHILDREN)
232 svg_indent_end(svg, e, SVG_INDENT_OPEN);
236 bputs(svg->fb, " />");
237 svg_indent_end(svg, e, SVG_INDENT_CLOSE);
241 void svg_set_attr_ref(struct svg *svg, const char *key, const char *val)
243 struct svg_element *e = svg_element_this(svg);
244 ASSERT(e->type == XML_NODE_ELEM);
245 ASSERT(!(e->flags & SVG_EF_START_TAG));
246 ASSERT(!svg->str_fb);
248 CLIST_FOR_EACH(struct svg_attr *, a, e->attrs)
249 if (!strcmp(a->key, key))
255 struct svg_attr *a = mp_alloc(svg->pool, sizeof(*a));
256 a->key = mp_strdup(svg->pool, key);
258 clist_add_tail(&e->attrs, &a->n);
261 void svg_set_attr(struct svg *svg, const char *key, const char *val)
263 svg_set_attr_ref(svg, key, mp_strdup(svg->pool, val));
266 void svg_set_attr_int(struct svg *svg, const char *key, int val)
268 svg_set_attr_ref(svg, key, mp_printf(svg->pool, "%d", val));
271 void svg_set_attr_float(struct svg *svg, const char *key, double val)
273 svg_set_attr_ref(svg, key, mp_printf(svg->pool, "%.6g", val));
276 char *svg_format_dimen(struct svg *svg, double val)
278 return mp_printf(svg->pool, "%.6g", val * svg->scale);
281 void svg_set_attr_dimen(struct svg *svg, const char *key, double val)
283 svg_set_attr_ref(svg, key, svg_format_dimen(svg, val));
286 void svg_set_attr_color(struct svg *svg, const char *key, color_t color)
288 svg_set_attr_ref(svg, key, mp_printf(svg->pool, "#%06x", color));
291 void svg_set_attr_format(struct svg *svg, const char *key, const char *fmt, ...)
295 svg_set_attr_ref(svg, key, mp_vprintf(svg->pool, fmt, args));
299 struct fastbuf *svg_fb_open(struct svg *svg)
301 ASSERT(!svg->str_fb);
302 fbpool_start(svg->fb_pool, svg->pool, 0);
303 svg->str_fb = (struct fastbuf *) svg->fb_pool;
307 const char *svg_fb_close(struct svg *svg)
310 bputc(svg->str_fb, 0);
311 const char *str = fbpool_end(svg->fb_pool);
316 void svg_fb_close_as_attr(struct svg *svg, const char *key)
318 const char *val = svg_fb_close(svg);
319 svg_set_attr_ref(svg, key, val);
322 struct svg_element *svg_push_path(struct svg *svg)
324 struct svg_element *e = svg_push_element(svg, "path");
327 // Construct the path, then call svg_path_end() and finally svg_pop()
328 // CAVEAT: Do not set attributes before calling svg_path_end()
332 void svg_path_end(struct svg *svg)
334 svg_fb_close_as_attr(svg, "d");
337 static void svg_path_printf(struct svg *svg, const char *fmt, ...)
341 if (btell(svg->str_fb))
342 bputc(svg->str_fb, ' ');
343 vbprintf(svg->str_fb, fmt, args);
347 void svg_path_move_to(struct svg *svg, double x, double y)
349 svg_path_printf(svg, "M %.6g %.6g", x * svg->scale, y * svg->scale);
352 void svg_path_move_to_rel(struct svg *svg, double x, double y)
354 svg_path_printf(svg, "m %.6g %.6g", x * svg->scale, y * svg->scale);
357 void svg_path_line_to(struct svg *svg, double x, double y)
359 svg_path_printf(svg, "L %.6g %.6g", x * svg->scale, y * svg->scale);
362 void svg_path_line_to_rel(struct svg *svg, double x, double y)
364 svg_path_printf(svg, "l %.6g %.6g", x * svg->scale, y * svg->scale);
367 void svg_path_close(struct svg *svg)
369 svg_path_printf(svg, "Z");