]> gitweb.michael.orlitzky.com - libsvgtiny.git/blob - src/svgtiny_css.c
src/svgtiny_strings.h: intern the universal selector string "*"
[libsvgtiny.git] / src / svgtiny_css.c
1 #include <libcss/libcss.h>
2
3 #include "svgtiny.h"
4 #include "svgtiny_internal.h"
5
6 static css_error node_name(void *pw, void *node, css_qname *qname);
7 static css_error node_classes(void *pw, void *node,
8 lwc_string ***classes, uint32_t *n_classes);
9 static css_error node_id(void *pw, void *node, lwc_string **id);
10 static css_error named_parent_node(void *pw, void *node,
11 const css_qname *qname, void **parent);
12 static css_error named_sibling_node(void *pw, void *node,
13 const css_qname *qname, void **sibling);
14 static css_error named_generic_sibling_node(void *pw, void *node,
15 const css_qname *qname, void **sibling);
16 static css_error parent_node(void *pw, void *node, void **parent);
17 static css_error sibling_node(void *pw, void *node, void **sibling);
18
19
20 /**
21 * Resolve a relative URL to an absolute one by doing nothing. This is
22 * the simplest possible implementation of a URL resolver, needed for
23 * parsing CSS.
24 */
25 css_error svgtiny_resolve_url(void *pw,
26 const char *base, lwc_string *rel, lwc_string **abs)
27 {
28 UNUSED(pw);
29 UNUSED(base);
30
31 /* Copy the relative URL to the absolute one (the return
32 value) */
33 *abs = lwc_string_ref(rel);
34 return CSS_OK;
35 }
36
37 /**
38 * Create a stylesheet with the default set of params.
39 *
40 * \param sheet A stylesheet pointer, passed in by reference, that
41 * we use to store the newly-created stylesheet.
42 * \param inline_style True if this stylesheet represents an inline
43 * style, and false otherwise.
44 *
45 * \return The return value from css_stylesheet_create() is returned.
46 */
47 css_error svgtiny_create_stylesheet(css_stylesheet **sheet,
48 bool inline_style)
49 {
50 css_stylesheet_params params;
51
52 params.params_version = CSS_STYLESHEET_PARAMS_VERSION_1;
53 params.level = CSS_LEVEL_DEFAULT;
54 params.charset = NULL;
55 params.url = "";
56 params.title = NULL;
57 params.allow_quirks = false;
58 params.inline_style = inline_style;
59 params.resolve = svgtiny_resolve_url;
60 params.resolve_pw = NULL;
61 params.import = NULL;
62 params.import_pw = NULL;
63 params.color = NULL;
64 params.color_pw = NULL;
65 params.font = NULL;
66 params.font_pw = NULL;
67
68 return css_stylesheet_create(&params, sheet);
69 }
70
71
72 /**************************/
73 /* libcss select handlers */
74 /**************************/
75 /*
76 * From here on we implement the "select handler "API defined in
77 * libcss's include/libcss/select.h and discussed briefly in its
78 * docs/API document.
79 */
80
81
82 /**
83 * Retrieve the given node's name
84 *
85 * \param pw Pointer to the current SVG parser state
86 * \param node Libdom SVG node
87 * \param qname Address at which to store the node name
88 *
89 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
90 */
91 css_error node_name(void *pw, void *node, css_qname *qname)
92 {
93 dom_string *name;
94 dom_exception err;
95 struct svgtiny_parse_state *state;
96
97 err = dom_node_get_node_name((dom_node *)node, &name);
98 if (err != DOM_NO_ERR) {
99 return CSS_NOMEM;
100 }
101
102 state = (struct svgtiny_parse_state *)pw;
103 qname->ns = lwc_string_ref(state->interned_svg_xmlns);
104
105 err = dom_string_intern(name, &qname->name);
106 if (err != DOM_NO_ERR) {
107 dom_string_unref(name);
108 return CSS_NOMEM;
109 }
110
111 dom_string_unref(name);
112
113 return CSS_OK;
114 }
115
116
117 /**
118 * Retrieve the given node's classes
119 *
120 * \param pw Pointer to the current SVG parser state
121 * \param node Libdom SVG node
122 * \param classes Address at which to store the class name array
123 * \param n_classes Address at which to store the length of the class
124 * name array
125 *
126 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
127 *
128 * \note CSS_NOMEM is not possible in practice as of libdom-0.4.1,
129 * because the underlying libdom function never fails
130 */
131 css_error node_classes(void *pw, void *node,
132 lwc_string ***classes, uint32_t *n_classes)
133 {
134 UNUSED(pw);
135 dom_exception err;
136
137 err = dom_element_get_classes((dom_node *)node, classes, n_classes);
138
139 /* The implementation does not do it, but the documentation
140 for dom_element_get_classes() says that a DOM_NO_MEM_ERR is
141 possible here, so we handle it to be on the safe side. */
142 if (err != DOM_NO_ERR) {
143 return CSS_NOMEM;
144 }
145
146 return CSS_OK;
147 }
148
149
150 /**
151 * Retrieve the given node's id
152 *
153 * \param pw Pointer to the current SVG parser state
154 * \param node Libdom SVG node
155 * \param id Address at which to store the id
156 *
157 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
158 */
159 css_error node_id(void *pw, void *node, lwc_string **id)
160 {
161 dom_string *attr;
162 dom_exception err;
163 struct svgtiny_parse_state *state;
164
165 /* Begin with the assumption that this node has no id */
166 *id = NULL;
167
168 state = (struct svgtiny_parse_state *)pw;
169 err = dom_element_get_attribute((dom_node *)node,
170 state->interned_id, &attr);
171 if (err != DOM_NO_ERR) {
172 return CSS_NOMEM;
173 }
174 else if (attr == NULL) {
175 /* The node has no id attribute and our return value
176 is already set to NULL so we're done */
177 return CSS_OK;
178 }
179
180 /* If we found an id attribute (a dom_string), intern it into
181 an lwc_string that we can return, and then cleanup the
182 dom_string. */
183 err = dom_string_intern(attr, id);
184 if (err != DOM_NO_ERR) {
185 dom_string_unref(attr);
186 return CSS_NOMEM;
187 }
188 dom_string_unref(attr);
189 return CSS_OK;
190 }
191
192
193
194 /**
195 * Find the first parent of the given element having the given name
196 *
197 * \param pw Pointer to the current SVG parser state
198 * \param node Libdom SVG node
199 * \param qname Name of the parent node to search for
200 * \param parent Address at which to store the parent node pointer
201 *
202 * \return Always returns CSS_OK
203 *
204 * \post If a suitable element is found, a pointer to it will be
205 * stored at the address pointed to by \a parent; otherwise,
206 * NULL will be stored at the address pointed to by \a parent
207 */
208 css_error named_parent_node(void *pw, void *node,
209 const css_qname *qname, void **parent)
210 {
211 UNUSED(pw);
212 /* dom_element_named_parent_node() was invented to implement
213 * this select handler so there isn't much for us to do except
214 * call it. It's OK if node isn't an element, libdom checks
215 * for it. */
216 dom_element_named_parent_node((dom_element *)node,
217 qname->name,
218 (struct dom_element **)parent);
219
220 /* Implementation detail: dom_element_named_parent_node()
221 * increments the reference count of the parent element before
222 * returning it to us. According to docs/RefCnt in the libdom
223 * repository, this will prevent the parent element from being
224 * destroyed if it is pruned from the DOM. That sounds good,
225 * since we don't want to be using a pointer to an object that
226 * has been destroyed... but we also have no way of later
227 * decrementing the reference count ourselves, and don't want
228 * to make the returned node eternal. Decrementing the
229 * reference counter now allows it to be destroyed when the
230 * DOM no longer needs it, and so long as no other parts of
231 * libsvgtiny are messing with the DOM during parsing, that
232 * shouldn't (ha ha) cause any problems. */
233 dom_node_unref(*parent);
234
235 return CSS_OK;
236 }
237
238
239 /**
240 * Find the "next-sibling" of the given element having the given name
241 *
242 * This search corresponds to the "+ foo" combinator in CSS and will
243 * find only "foo" element nodes that immediately precede the given
244 * node under the same parent in the DOM. In CSS the tree is viewed
245 * top-down and in libdom it is viewed from the bottom-up; as a result
246 * "next" and "previous" are sometimes backwards. This is case-sensitive.
247 *
248 * \param pw Pointer to the current SVG parser state
249 * \param node Libdom SVG node
250 * \param qname Name of the sibling node to search for
251 * \param sibling Address at which to store the sibling node pointer
252 *
253 * \return Always returns CSS_OK
254 *
255 * \post If a suitable element is found, a pointer to it will be
256 * stored at the address pointed to by \a sibling; otherwise,
257 * NULL will be stored at the address pointed to by \a sibling
258 */
259 css_error named_sibling_node(void *pw, void *node,
260 const css_qname *qname, void **sibling)
261 {
262 UNUSED(pw);
263 dom_node *n = node; /* the current node */
264 dom_node *prev; /* the previous node */
265 dom_exception err;
266 dom_node_type type;
267 dom_string *name;
268
269 *sibling = NULL; /* default to nothing found */
270
271 /* Begin the search; the first iteration we do outside of the
272 * loop. Implementation detil: dom_node_get_previous_sibling()
273 * increments the reference counter on the returned node. A
274 * comment within named_parent_node() explains why we
275 * decrement it ASAP. */
276 err = dom_node_get_previous_sibling(n, &n);
277 if (err != DOM_NO_ERR) {
278 return CSS_OK;
279 }
280
281 while (n != NULL) {
282 /* We're looking for the first ELEMENT sibling */
283 err = dom_node_get_node_type(n, &type);
284 if (err != DOM_NO_ERR) {
285 dom_node_unref(n);
286 return CSS_OK;
287 }
288
289 if (type == DOM_ELEMENT_NODE) {
290 /* We found an element node, does it have the
291 * right name? */
292 err = dom_node_get_node_name(n, &name);
293 if (err != DOM_NO_ERR) {
294 dom_node_unref(n);
295 return CSS_OK;
296 }
297
298 if (dom_string_lwc_isequal(name,
299 qname->name)) {
300 /* The name is right, return it */
301 *sibling = n;
302 }
303
304 /* There's only one next-sibling element node
305 * and we've already found it, so if its name
306 * wasn't right, we return the default value
307 * of NULL below */
308 dom_string_unref(name);
309 dom_node_unref(n);
310 return CSS_OK;
311 }
312
313 /* Not an element node, so we move on the the previous
314 * previous sibling */
315 err = dom_node_get_previous_sibling(n, &prev);
316 if (err != DOM_NO_ERR) {
317 dom_node_unref(n);
318 return CSS_OK;
319 }
320
321 dom_node_unref(n);
322 n = prev;
323 }
324
325 return CSS_OK;
326 }
327
328
329 /**
330 * Find the first "subsequent-sibling" of the given element having the
331 * given name
332 *
333 * This search corresponds to the "~ foo" combinator in CSS and will
334 * find only "foo" element nodes that precede the given node (under
335 * the same parent) in the DOM. In CSS the tree is viewed top-down and
336 * in libdom it is viewed from the bottom-up; as a result "next" and
337 * "previous" are sometimes backwards. This is case-sensitive.
338 *
339 * \param pw Pointer to the current SVG parser state
340 * \param node Libdom SVG node
341 * \param qname Name of the sibling node to search for
342 * \param sibling Address at which to store the sibling node pointer
343 *
344 * \return Always returns CSS_OK
345 *
346 * \post If a suitable element is found, a pointer to it will be
347 * stored at the address pointed to by \a sibling; otherwise,
348 * NULL will be stored at the address pointed to by \a sibling
349 */
350 css_error named_generic_sibling_node(void *pw, void *node,
351 const css_qname *qname, void **sibling)
352 {
353 UNUSED(pw);
354 dom_node *n = node; /* the current node */
355 dom_node *prev; /* the previous node */
356 dom_exception err;
357 dom_node_type type;
358 dom_string *name;
359
360
361 *sibling = NULL; /* default to nothing found */
362
363 /* Begin the search; the first iteration we do outside of the
364 * loop. Implementation detil: dom_node_get_previous_sibling()
365 * increments the reference counter on the returned node. A
366 * comment within named_parent_node() explains why we
367 * decrement it ASAP. */
368 err = dom_node_get_previous_sibling(n, &n);
369 if (err != DOM_NO_ERR) {
370 return CSS_OK;
371 }
372
373 while (n != NULL) {
374 err = dom_node_get_node_type(n, &type);
375 if (err != DOM_NO_ERR) {
376 dom_node_unref(n);
377 return CSS_OK;
378 }
379
380 if (type == DOM_ELEMENT_NODE) {
381 /* We only want ELEMENT nodes */
382 err = dom_node_get_node_name(n, &name);
383 if (err != DOM_NO_ERR) {
384 dom_node_unref(n);
385 return CSS_OK;
386 }
387
388 if (dom_string_lwc_isequal(name,
389 qname->name)) {
390 /* Found one. Save it and stop the search */
391 dom_string_unref(name);
392 dom_node_unref(n);
393 *sibling = n;
394 return CSS_OK;
395 }
396
397 dom_string_unref(name);
398 }
399
400 /* This sibling wasn't an element with the desired
401 name, so move on to the previous sibling */
402 err = dom_node_get_previous_sibling(n, &prev);
403 if (err != DOM_NO_ERR) {
404 dom_node_unref(n);
405 return CSS_OK;
406 }
407
408 dom_node_unref(n);
409 n = prev;
410 }
411
412 return CSS_OK;
413 }
414
415
416 /**
417 * Return a pointer to the given node's parent
418 *
419 * \param pw Pointer to the current SVG parser state
420 * \param node Libdom SVG node
421 * \param parent Address at which to store the node's parent pointer
422 *
423 * \return Always returns CSS_OK
424 */
425 css_error parent_node(void *pw, void *node, void **parent)
426 {
427 UNUSED(pw);
428 /* Libdom basically implements this for us */
429 dom_element_parent_node(node, (struct dom_element **)parent);
430
431 /* See the comment in named_parent_node() for why we decrement
432 * this reference counter here. */
433 dom_node_unref(*parent);
434
435 return CSS_OK;
436 }
437
438
439 /**
440 * Find the "next-sibling" of the given element
441 *
442 * This search corresponds "+ *" in CSS and will find the first
443 * element node that immediately precedes the given node under the
444 * same parent in the DOM. In CSS the tree is viewed top-down and in
445 * libdom it is viewed from the bottom-up; as a result "next" and
446 * "previous" are sometimes backwards.
447 *
448 * \param pw Pointer to the current SVG parser state
449 * \param node Libdom SVG node
450 * \param sibling Address at which to store the sibling node pointer
451 *
452 * \return Always returns CSS_OK
453 *
454 * \post If a suitable element is found, a pointer to it will be
455 * stored at the address pointed to by \a sibling; otherwise,
456 * NULL will be stored at the address pointed to by \a sibling
457 */
458 css_error sibling_node(void *pw, void *node, void **sibling)
459 {
460 UNUSED(pw);
461 dom_node *n = node; /* the current node */
462 dom_node *prev; /* the previous node */
463 dom_exception err;
464 dom_node_type type;
465
466 *sibling = NULL; /* default to nothing found */
467
468 /* Begin the search; the first iteration we do outside of the
469 * loop. Implementation detil: dom_node_get_previous_sibling()
470 * increments the reference counter on the returned node. A
471 * comment within named_parent_node() explains why we
472 * decrement it ASAP. */
473 err = dom_node_get_previous_sibling(n, &n);
474 if (err != DOM_NO_ERR) {
475 return CSS_OK;
476 }
477
478 while (n != NULL) {
479 err = dom_node_get_node_type(n, &type);
480 if (err != DOM_NO_ERR) {
481 dom_node_unref(n);
482 return CSS_OK;
483 }
484
485 if (type == DOM_ELEMENT_NODE) {
486 /* We found a sibling node that is also an
487 element and that's all we wanted. */
488 *sibling = n;
489 dom_node_unref(n);
490 return CSS_OK;
491 }
492
493 /* This sibling node was not an element; move on to
494 the previous sibling */
495 err = dom_node_get_previous_sibling(n, &prev);
496 if (err != DOM_NO_ERR) {
497 dom_node_unref(n);
498 return CSS_OK;
499 }
500
501 dom_node_unref(n);
502 n = prev;
503 }
504
505 return CSS_OK;
506 }