]> mj.ucw.cz Git - leo.git/blob - css.c
Do not crash on broken multipolygons
[leo.git] / css.c
1 /*
2  *      Hic Est Leo -- MapCSS Stylesheets
3  *
4  *      (c) 2014 Martin Mares <mj@ucw.cz>
5  */
6
7 #include <ucw/lib.h>
8 #include <ucw/mempool.h>
9
10 #include <stdio.h>
11
12 #include "leo.h"
13 #include "style.h"
14 #include "css.h"
15
16 void css_dump(struct css_sheet *ss)
17 {
18   printf("Style sheet <%s>:\n", ss->filename);
19   CLIST_FOR_EACH(struct css_rule *, r, ss->rules)
20     css_dump_rule(r);
21 }
22
23 void css_dump_rule(struct css_rule *r)
24 {
25   printf("Rule:\n");
26   CLIST_FOR_EACH(struct css_selector *, s, r->selectors)
27     css_dump_selector(s);
28   CLIST_FOR_EACH(struct css_action *, a, r->actions)
29     css_dump_action(a);
30 }
31
32 void css_dump_selector(struct css_selector *s)
33 {
34   printf("\tSelector:\n");
35   CLIST_FOR_EACH(struct css_path *, p, s->path)
36     {
37       printf("\t\tMatch type=%u layer=%u role=%u mod=%u\n", p->type, p->layer, p->role, p->modifiers);
38       CLIST_FOR_EACH(struct css_condition *, c, p->conditions)
39         {
40           printf("\t\t\tCondition %u key=%s val=%s\n", c->op, osm_key_decode(c->key), osm_val_decode(c->val));
41         }
42     }
43 }
44
45 void css_dump_action(struct css_action *a)
46 {
47   printf("\tAction: ");
48   style_dump_prop(&a->prop);
49 }
50
51 static bool css_match_condition(struct css_condition *c, struct osm_object *o)
52 {
53   osm_val_t val = osm_obj_find_tag(o, c->key);
54
55   switch (c->op)
56     {
57     case COND_OP_EQ:
58       return (val == c->val);   // FIXME: Case sensitivity?
59     case COND_OP_NE:
60       return (val != c->val);
61     case COND_OP_LT:
62       // FIXME
63       return 0;
64     case COND_OP_GT:
65       // FIXME
66       return 0;
67     case COND_OP_LE:
68       // FIXME
69       return 0;
70     case COND_OP_GE:
71       // FIXME
72       return 0;
73     case COND_OP_IS_SET:
74       return val;
75     case COND_OP_IS_UNSET:
76       return !val;
77     case COND_OP_IS_TRUE:
78       return (val == VALUE_1 || val == VALUE_YES || val == VALUE_TRUE);
79     case COND_OP_IS_FALSE:
80       return !(val == VALUE_1 || val == VALUE_YES || val == VALUE_TRUE);
81     default:
82       ASSERT(0);
83     }
84 }
85
86 static bool css_match_path_component(struct css_path *p, struct osm_object *o)
87 {
88   switch (p->type)
89     {
90     case CSS_TYPE_ANY:
91       break;
92     case CSS_TYPE_NODE:
93     case CSS_TYPE_WAY:
94     case CSS_TYPE_RELATION:
95       if (o->type != p->type)
96         return 0;
97       break;
98     case CSS_TYPE_AREA:
99       if (o->type == OSM_TYPE_WAY && osm_way_cyclic_p((struct osm_way *) o))
100         break;
101       if (o->type == OSM_TYPE_MULTIPOLYGON)
102         break;
103       return 0;
104     default:
105       return 0;
106     }
107
108   if (p->modifiers & PATH_MOD_NEVER)
109     return 0;
110
111   CLIST_FOR_EACH(struct css_condition *, c, p->conditions)
112     if (!css_match_condition(c, o))
113       return 0;
114
115   return 1;
116 }
117
118 static bool css_match_path(struct css_selector *s, struct css_path *p, struct osm_object *o);
119
120 static bool css_match_path_subtree(struct css_selector *s, struct css_path *p, struct osm_object *o)
121 {
122   CLIST_FOR_EACH(struct osm_ref *, ref, o->backrefs)
123     if (css_match_path(s, p, ref->o))
124       return 1;
125
126   CLIST_FOR_EACH(struct osm_ref *, ref, o->backrefs)
127     if (css_match_path_subtree(s, p, ref->o))
128       return 1;
129
130   return 0;
131 }
132
133 static bool css_match_path(struct css_selector *s, struct css_path *p, struct osm_object *o)
134 {
135   if (!css_match_path_component(p, o))
136     return 0;
137
138   p = clist_prev(&s->path, &p->n);
139   if (!p)
140     return 1;
141
142   return css_match_path_subtree(s, p, o);
143 }
144
145 static bool css_match_selector(struct css_selector *s, struct style_results *r)
146 {
147   struct css_path *p = clist_tail(&s->path);
148   ASSERT(p);
149   return css_match_path(s, p, r->obj);
150 }
151
152 static void css_apply_action(struct css_action *sa, struct style_results *r, layer_t layer)
153 {
154   style_set_by_layer(r, layer, &sa->prop);
155 }
156
157 void css_apply(struct css_sheet *ss, struct style_results *r)
158 {
159   CLIST_FOR_EACH(struct css_rule *, sr, ss->rules)
160     {
161       layer_t last_layer = ~0U;
162       CLIST_FOR_EACH(struct css_selector *, sel, sr->selectors)
163         {
164           if (css_match_selector(sel, r))
165             {
166               // XXX: Rules with selectors in multiple layers could be further optimized
167               struct css_path *cp = clist_tail(&sel->path);
168               layer_t layer = cp ? cp->layer : STYLE_LAYER_DEFAULT;
169               if (layer != last_layer)
170                 {
171                   last_layer = layer;
172                   CLIST_FOR_EACH(struct css_action *, sa, sr->actions)
173                     css_apply_action(sa, r, layer);
174                 }
175             }
176         }
177     }
178 }