2 * Hic Est Leo -- SVG Icon Embedder
4 * (c) 2014 Martin Mares <mj@ucw.cz>
10 #include <ucw/fastbuf.h>
11 #include <ucw-xml/xml.h>
18 #define HASH_NODE struct svg_icon
19 #define HASH_PREFIX(x) icon_##x
20 #define HASH_KEY_ENDSTRING name
21 #define HASH_WANT_LOOKUP
22 #define HASH_WANT_CLEANUP
23 #define HASH_ZERO_FILL
24 #define HASH_TABLE_DYNAMIC
25 #include <ucw/hashtable.h>
27 static void svg_icon_error(struct xml_context *ctx)
29 fprintf(stderr, "%s at %u: %s\n", (ctx->err_code < XML_ERR_ERROR) ? "warn" : "error", xml_row(ctx), ctx->err_msg);
32 struct svg_icon *svg_icon_load(struct svg *svg, const char *name)
34 struct svg_icon *icon = icon_lookup(svg->icon_hash, (char *) name);
37 icon->id = ++svg->icon_counter;
38 clist_init(&icon->patterns);
40 struct xml_context *ctx = xmalloc(sizeof(*ctx));
42 ctx->h_warn = ctx->h_error = ctx->h_fatal = svg_icon_error;
43 xml_push_fastbuf(ctx, bopen_file(name, O_RDONLY, NULL));
45 ctx->flags |= XML_ALLOC_TAGS | XML_ALLOC_CHARS;
48 die("Error reading icon %s: Fatal error in XML parser", name);
51 icon->root = ctx->dom;
53 struct xml_node *root = icon->root;
55 if (strcmp(root->name, "svg"))
56 die("Error reading icon %s: root element is <%s>, not <svg>", name, root->name);
58 struct xml_attr *a_width = xml_attr_find(ctx, root, "width");
59 struct xml_attr *a_height = xml_attr_find(ctx, root, "height");
60 if (!a_width || !a_height)
61 die("Error reading icon %s: cannot determine image dimensions", name);
62 icon->width = atof(a_width->val) / svg->scale;
63 icon->height = atof(a_height->val) / svg->scale;
64 if (icon->width < 0.01 || icon->height < 0.01)
65 die("Error reading icon %s: invalid icon dimensions", name);
66 // FIXME: What if dimensions are given in absolute units?
68 msg(L_DEBUG, "Loaded icon %s (%.5g x %.5g)", name, icon->width, icon->height);
72 static void svg_icon_unload(struct svg_icon *icon)
74 xml_cleanup(icon->ctx);
78 static bool is_white(int c)
80 return c == ' ' || c == '\t' || c == '\r' || c == '\n';
83 static void normalize_white(char *r)
104 static void svg_embed(struct svg *svg, struct svg_icon *icon, struct xml_node *n)
106 if (n->type == XML_NODE_CHARS)
108 char *t = mp_strdup(svg->pool, n->text);
112 svg_push_chars(svg)->name = n->text;
117 ASSERT(n->type == XML_NODE_ELEM);
119 svg_push_element(svg, n->name);
120 XML_ATTR_FOR_EACH(a, n)
123 if (!strcmp(a->name, "id"))
124 val = mp_printf(svg->pool, "i%u-%s", icon->id, val);
125 else if (!strcmp(a->name, "xlink:href") && val[0] == '#')
126 val = mp_printf(svg->pool, "#i%u-%s", icon->id, val+1);
127 // FIXME: Some attributes can have values "url(#id)"
128 svg_set_attr(svg, a->name, val);
130 XML_NODE_FOR_EACH(e, n)
131 svg_embed(svg, icon, e);
135 static void svg_icon_embed(struct svg *svg, struct svg_icon *icon)
137 svg_push_element(svg, "g");
138 svg_set_attr_format(svg, "id", "icon%u", icon->id);
140 svg_push_comment(svg)->name = mp_printf(svg->pool, "Embedded %s", icon->name);
143 svg_embed(svg, icon, icon->root);
147 void svg_icon_init(struct svg *svg)
149 svg->icon_hash = mp_alloc_zero(svg->pool, sizeof(struct icon_table));
150 icon_init(svg->icon_hash);
153 void svg_icon_cleanup(struct svg *svg)
155 HASH_FOR_ALL_DYNAMIC(icon, svg->icon_hash, icon)
157 svg_icon_unload(icon);
160 icon_cleanup(svg->icon_hash);
163 void svg_icon_put(struct svg *svg, struct svg_icon_request *sir)
165 struct svg_icon *icon = sir->icon;
166 double scale_x = sir->width / icon->width;
167 double scale_y = sir->height / icon->height;
170 // FIXME: More alignment modes?
171 double x = sir->x - sir->width / 2;
172 double y = sir->y - sir->height / 2;
174 svg_push_element(svg, "use");
175 svg_set_attr_format(svg, "xlink:href", "#icon%u", icon->id);
176 svg_set_attr_dimen(svg, "x", x / scale_x);
177 svg_set_attr_dimen(svg, "y", y / scale_y);
178 if (fabs(scale_x - 1) > 1e-10 || fabs(scale_y - 1) > 1e-10)
179 svg_set_attr_format(svg, "transform", "scale(%.5g %.5g)", scale_x, scale_y);
183 struct svg_pattern *svg_icon_to_pattern(struct svg *svg, struct svg_pattern_request *spr)
185 CLIST_FOR_EACH(struct svg_pattern *, patt, spr->icon->patterns)
187 if (fabs(patt->width - spr->width) < 1e-10 &&
188 fabs(patt->height - spr->height) < 1e-10)
192 struct svg_pattern *patt = mp_alloc_zero(svg->pool, sizeof(*patt));
193 clist_add_tail(&spr->icon->patterns, &patt->n);
194 patt->id = ++svg->pattern_counter;
195 patt->icon = spr->icon;
196 patt->width = spr->width;
197 patt->height = spr->height;
198 patt->paint_server = mp_printf(svg->pool, "url(#patt%u)", patt->id);
202 static void svg_pattern_embed(struct svg *svg, struct svg_pattern *patt)
204 struct svg_icon *icon = patt->icon;
206 svg_push_element(svg, "pattern");
207 svg_set_attr_format(svg, "id", "patt%u", patt->id);
208 svg_set_attr(svg, "patternUnits", "userSpaceOnUse");
209 svg_set_attr(svg, "patternContentUnits", "userSpaceOnUse");
210 svg_set_attr_dimen(svg, "width", patt->width);
211 svg_set_attr_dimen(svg, "height", patt->height);
213 struct svg_icon_request sir = {
215 .x = patt->width / 2,
216 .y = patt->height / 2,
217 .width = patt->width,
218 .height = patt->height,
220 svg_icon_put(svg, &sir);
225 void svg_icon_dump_library(struct svg *svg)
227 svg_push_element(svg, "defs");
228 HASH_FOR_ALL_DYNAMIC(icon, svg->icon_hash, icon)
230 svg_icon_embed(svg, icon);
231 CLIST_FOR_EACH(struct svg_pattern *, patt, icon->patterns)
232 svg_pattern_embed(svg, patt);