]> gitweb.michael.orlitzky.com - libsvgtiny.git/commitdiff
Implement use element
authorVincent Sanders <vince@kyllikki.org>
Thu, 18 Jul 2024 21:38:26 +0000 (22:38 +0100)
committerVincent Sanders <vince@kyllikki.org>
Thu, 25 Jul 2024 20:48:08 +0000 (21:48 +0100)
src/svgtiny.c
src/svgtiny_gradient.c
src/svgtiny_internal.h
src/svgtiny_parse.c
src/svgtiny_strings.h
test/data/arcs02.mvg [new file with mode: 0644]
test/data/arcs02.svg
test/data/bad-grad.svg
test/data/use.mvg [new file with mode: 0644]
test/data/use.svg [new file with mode: 0644]

index 6400320d3fdb3b9586686bdbd216080e86ad11e8..ab49f8f4124669d54728743d9e44a4824ebb1408 100644 (file)
@@ -29,6 +29,8 @@
  */
 #define KAPPA 0.5522847498
 
+/* debug flag which enables printing of libdom parse messages to stderr */
+#undef PRINT_XML_PARSE_MSG
 
 #if (defined(_GNU_SOURCE) && !defined(__APPLE__) || defined(__amigaos4__) || defined(__HAIKU__) || (defined(_POSIX_C_SOURCE) && ((_POSIX_C_SOURCE - 0) >= 200809L)))
 #define HAVE_STRNDUP
@@ -38,6 +40,7 @@ char *svgtiny_strndup(const char *s, size_t n);
 #define strndup svgtiny_strndup
 #endif
 
+static svgtiny_code parse_element(dom_element *element, struct svgtiny_parse_state *state);
 
 #ifndef HAVE_STRNDUP
 char *svgtiny_strndup(const char *s, size_t n)
@@ -179,9 +182,24 @@ static void svgtiny_cleanup_state_local(struct svgtiny_parse_state *state)
 
 static void ignore_msg(uint32_t severity, void *ctx, const char *msg, ...)
 {
+#ifdef PRINT_XML_PARSE_MSG
+#include <stdarg.h>
+        va_list l;
+
+        UNUSED(ctx);
+
+        va_start(l, msg);
+
+        fprintf(stderr, "%"PRIu32": ", severity);
+        vfprintf(stderr, msg, l);
+        fprintf(stderr, "\n");
+
+       va_end(l);
+#else
        UNUSED(severity);
        UNUSED(ctx);
        UNUSED(msg);
+#endif
 }
 
 
@@ -324,6 +342,33 @@ svgtiny_add_path(float *p, unsigned int n, struct svgtiny_parse_state *state)
 }
 
 
+/**
+ * return svgtiny_OK if source is an ancestor of target else svgtiny_LIBDOM_ERROR
+ */
+static svgtiny_code is_ancestor_node(dom_node *source, dom_node *target)
+{
+       dom_node *parent;
+       dom_exception exc;
+
+       parent = dom_node_ref(target);
+       while (parent != NULL) {
+               dom_node *next = NULL;
+               if (parent == source) {
+                       dom_node_unref(parent);
+                       return svgtiny_OK;
+               }
+               exc = dom_node_get_parent_node(parent, &next);
+               dom_node_unref(parent);
+               if (exc != DOM_NO_ERR) {
+                       break;
+               }
+
+               parent = next;
+       }
+       return svgtiny_LIBDOM_ERROR;
+}
+
+
 /**
  * Parse a <path> element node.
  *
@@ -877,6 +922,46 @@ svgtiny_parse_text(dom_element *text, struct svgtiny_parse_state state)
 }
 
 
+/**
+ * Parse a <use> element node.
+ *
+ * https://www.w3.org/TR/SVG2/struct.html#UseElement
+ */
+static svgtiny_code
+svgtiny_parse_use(dom_element *use, struct svgtiny_parse_state state)
+{
+       svgtiny_code res;
+       dom_element *ref; /* referenced element */
+
+       svgtiny_setup_state_local(&state);
+
+       res = svgtiny_parse_element_from_href(use, &state, &ref);
+       if (res != svgtiny_OK) {
+               svgtiny_cleanup_state_local(&state);
+               return res;
+       }
+
+       if (ref != NULL) {
+               /* found the reference */
+
+               /**
+                * If the referenced element is a ancestor of the ‘use’ element,
+                * then this is an invalid circular reference and the ‘use’
+                * element is in error.
+                */
+               res = is_ancestor_node((dom_node *)ref, (dom_node *)use);
+               if (res != svgtiny_OK) {
+                       res = parse_element(ref, &state);
+               }
+               dom_node_unref(ref);
+       }
+
+       svgtiny_cleanup_state_local(&state);
+
+       return svgtiny_OK;
+}
+
+
 /**
  * Parse a <svg> or <g> element node.
  */
@@ -928,47 +1013,7 @@ svgtiny_parse_svg(dom_element *svg, struct svgtiny_parse_state state)
                        return svgtiny_LIBDOM_ERROR;
                }
                if (nodetype == DOM_ELEMENT_NODE) {
-                       dom_string *nodename;
-                       exc = dom_node_get_node_name(child, &nodename);
-                       if (exc != DOM_NO_ERR) {
-                               dom_node_unref(child);
-                               svgtiny_cleanup_state_local(&state);
-                               return svgtiny_LIBDOM_ERROR;
-                       }
-                       if (dom_string_caseless_isequal(state.interned_svg,
-                                                       nodename))
-                               code = svgtiny_parse_svg(child, state);
-                       else if (dom_string_caseless_isequal(state.interned_g,
-                                                            nodename))
-                               code = svgtiny_parse_svg(child, state);
-                       else if (dom_string_caseless_isequal(state.interned_a,
-                                                            nodename))
-                               code = svgtiny_parse_svg(child, state);
-                       else if (dom_string_caseless_isequal(state.interned_path,
-                                                            nodename))
-                               code = svgtiny_parse_path(child, state);
-                       else if (dom_string_caseless_isequal(state.interned_rect,
-                                                            nodename))
-                               code = svgtiny_parse_rect(child, state);
-                       else if (dom_string_caseless_isequal(state.interned_circle,
-                                                            nodename))
-                               code = svgtiny_parse_circle(child, state);
-                       else if (dom_string_caseless_isequal(state.interned_ellipse,
-                                                            nodename))
-                               code = svgtiny_parse_ellipse(child, state);
-                       else if (dom_string_caseless_isequal(state.interned_line,
-                                                            nodename))
-                               code = svgtiny_parse_line(child, state);
-                       else if (dom_string_caseless_isequal(state.interned_polyline,
-                                                            nodename))
-                               code = svgtiny_parse_poly(child, state, false);
-                       else if (dom_string_caseless_isequal(state.interned_polygon,
-                                                            nodename))
-                               code = svgtiny_parse_poly(child, state, true);
-                       else if (dom_string_caseless_isequal(state.interned_text,
-                                                            nodename))
-                               code = svgtiny_parse_text(child, state);
-                       dom_string_unref(nodename);
+                       code = parse_element(child, &state);
                }
                if (code != svgtiny_OK) {
                        dom_node_unref(child);
@@ -990,6 +1035,48 @@ svgtiny_parse_svg(dom_element *svg, struct svgtiny_parse_state state)
 }
 
 
+static svgtiny_code
+parse_element(dom_element *element, struct svgtiny_parse_state *state)
+{
+       dom_exception exc;
+       dom_string *nodename;
+       svgtiny_code code = svgtiny_OK;;
+
+       exc = dom_node_get_node_name(element, &nodename);
+       if (exc != DOM_NO_ERR) {
+               return svgtiny_LIBDOM_ERROR;
+       }
+
+       if (dom_string_caseless_isequal(state->interned_svg, nodename)) {
+               code = svgtiny_parse_svg(element, *state);
+       } else if (dom_string_caseless_isequal(state->interned_g, nodename)) {
+               code = svgtiny_parse_svg(element, *state);
+       } else if (dom_string_caseless_isequal(state->interned_a, nodename)) {
+               code = svgtiny_parse_svg(element, *state);
+       } else if (dom_string_caseless_isequal(state->interned_path, nodename)) {
+               code = svgtiny_parse_path(element, *state);
+       } else if (dom_string_caseless_isequal(state->interned_rect, nodename)) {
+               code = svgtiny_parse_rect(element, *state);
+       } else if (dom_string_caseless_isequal(state->interned_circle, nodename)) {
+               code = svgtiny_parse_circle(element, *state);
+       } else if (dom_string_caseless_isequal(state->interned_ellipse, nodename)) {
+               code = svgtiny_parse_ellipse(element, *state);
+       } else if (dom_string_caseless_isequal(state->interned_line, nodename)) {
+               code = svgtiny_parse_line(element, *state);
+       } else if (dom_string_caseless_isequal(state->interned_polyline, nodename)) {
+               code = svgtiny_parse_poly(element, *state, false);
+       } else if (dom_string_caseless_isequal(state->interned_polygon, nodename)) {
+               code = svgtiny_parse_poly(element, *state, true);
+       } else if (dom_string_caseless_isequal(state->interned_text, nodename)) {
+               code = svgtiny_parse_text(element, *state);
+       } else if (dom_string_caseless_isequal(state->interned_use, nodename)) {
+               code = svgtiny_parse_use(element, *state);
+       }
+       dom_string_unref(nodename);
+       return code;
+}
+
+
 static svgtiny_code
 initialise_parse_state(struct svgtiny_parse_state *state,
                       struct svgtiny_diagram *diagram,
index 9e5b7d2fa0c622dbc0d163a3fabf45e89bd06d34..ca961ccf7c5535ffff5e99bb734f9d2b6e6ef232 100644 (file)
@@ -185,16 +185,13 @@ svgtiny_parse_linear_gradient(dom_element *linear,
 {
        dom_string *attr;
        dom_exception exc;
+       svgtiny_code res;
+       dom_element *ref; /* referenced element */
 
-       exc = dom_element_get_attribute(linear, state->interned_href, &attr);
-       if (exc == DOM_NO_ERR && attr != NULL) {
-               if (dom_string_data(attr)[0] == (uint8_t) '#') {
-                       svgtiny_find_gradient(dom_string_data(attr) + 1,
-                                             dom_string_byte_length(attr) - 1,
-                                             grad,
-                                             state);
-               }
-               dom_string_unref(attr);
+       res = svgtiny_parse_element_from_href(linear, state, &ref);
+       if (res == svgtiny_OK && ref != NULL) {
+               svgtiny_update_gradient(ref, state, grad);
+               dom_node_unref(ref);
        }
 
        exc = dom_element_get_attribute(linear, state->interned_x1, &attr);
@@ -839,25 +836,18 @@ add_gradient_lines(struct svgtiny_parse_state *state,
        return svgtiny_OK;
 }
 
-
 /**
- * Find a gradient by id and parse it.
+ * update a gradient from a dom element
  */
 svgtiny_code
-svgtiny_find_gradient(const char *id,
-                     size_t idlen,
-                     struct svgtiny_parse_state_gradient *grad,
-                     struct svgtiny_parse_state *state)
+svgtiny_update_gradient(dom_element *grad_element,
+                       struct svgtiny_parse_state *state,
+                       struct svgtiny_parse_state_gradient *grad)
 {
-       dom_element *gradient;
-       dom_string *id_str, *name;
+       dom_string *name;
        dom_exception exc;
        svgtiny_code res = svgtiny_OK;
 
-#ifdef GRADIENT_DEBUG
-       fprintf(stderr, "svgtiny_find_gradient: id \"%.*s\"\n", (int)idlen, id);
-#endif
-
        grad->linear_gradient_stop_count = 0;
        if (grad->gradient_x1 != NULL)
                dom_string_unref(grad->gradient_x1);
@@ -879,30 +869,16 @@ svgtiny_find_gradient(const char *id,
        grad->gradient_transform.e = 0;
        grad->gradient_transform.f = 0;
 
-       exc = dom_string_create_interned((const uint8_t *) id, idlen, &id_str);
-       if (exc != DOM_NO_ERR)
-               return svgtiny_SVG_ERROR;
-
-       exc = dom_document_get_element_by_id(state->document, id_str, &gradient);
-       dom_string_unref(id_str);
-       if (exc != DOM_NO_ERR || gradient == NULL) {
-#ifdef GRADIENT_DEBUG
-               fprintf(stderr, "gradient \"%.*s\" not found\n", (int)idlen, id);
-#endif
-               return svgtiny_SVG_ERROR;
-       }
-
-       exc = dom_node_get_node_name(gradient, &name);
+       exc = dom_node_get_node_name(grad_element, &name);
        if (exc != DOM_NO_ERR) {
-               dom_node_unref(gradient);
                return svgtiny_SVG_ERROR;
        }
 
+       /* ensure element is a linear gradiant */
        if (dom_string_isequal(name, state->interned_linearGradient)) {
-               res = svgtiny_parse_linear_gradient(gradient, grad, state);
+               res = svgtiny_parse_linear_gradient(grad_element, grad, state);
        }
 
-       dom_node_unref(gradient);
        dom_string_unref(name);
 
 #ifdef GRADIENT_DEBUG
index 6a57140ab584553ee16c60fbff420932327b9097..2b2b25739f56638966dc8a24fc2b312868be1549 100644 (file)
@@ -96,6 +96,15 @@ void svgtiny_transform_path(float *p, unsigned int n,
                struct svgtiny_parse_state *state);
 
 /* svgtiny_parse.c */
+svgtiny_code svgtiny_parse_inline_style(dom_element *node,
+               struct svgtiny_parse_state *state,
+               struct svgtiny_parse_internal_operation *ops);
+svgtiny_code svgtiny_parse_attributes(dom_element *node,
+               struct svgtiny_parse_state *state,
+               struct svgtiny_parse_internal_operation *ops);
+
+svgtiny_code svgtiny_parse_element_from_href(dom_element *node,
+               struct svgtiny_parse_state *state, dom_element **element);
 svgtiny_code svgtiny_parse_poly_points(const char *data, size_t datalen,
                float *pointv, unsigned int *pointc);
 svgtiny_code svgtiny_parse_length(const char *text, size_t textlen,
@@ -107,26 +116,18 @@ svgtiny_code svgtiny_parse_color(const char *text, size_t textlen,
 svgtiny_code svgtiny_parse_viewbox(const char *text, size_t textlen,
                float viewport_width, float viewport_height,
                struct svgtiny_transformation_matrix *tm);
-svgtiny_code svgtiny_parse_inline_style(dom_element *node,
-               struct svgtiny_parse_state *state,
-               struct svgtiny_parse_internal_operation *ops);
-svgtiny_code svgtiny_parse_attributes(dom_element *node,
-               struct svgtiny_parse_state *state,
-               struct svgtiny_parse_internal_operation *ops);
 svgtiny_code svgtiny_parse_none(const char *cursor, const char *textend);
 svgtiny_code svgtiny_parse_number(const char *text, const char **textend,
                float *value);
 
-
 /* svgtiny_path.c */
 svgtiny_code svgtiny_parse_path_data(const char *text, size_t textlen,
                float **pointv, unsigned int *pointc);
 
 /* svgtiny_gradient.c */
-svgtiny_code svgtiny_find_gradient(const char *id,
-               size_t idlen,
-               struct svgtiny_parse_state_gradient *grad,
-               struct svgtiny_parse_state *state);
+svgtiny_code svgtiny_update_gradient(dom_element *grad_element,
+               struct svgtiny_parse_state *state,
+               struct svgtiny_parse_state_gradient *grad);
 svgtiny_code svgtiny_gradient_add_fill_path(float *p, unsigned int n,
                struct svgtiny_parse_state *state);
 svgtiny_code svgtiny_gradient_add_stroke_path(float *p, unsigned int n,
index 3d33cc73ce6cb7c87a6f465f8074eda6185ba6d9..af7b2287d43452ecc85c9c5c479bda1243e297ac 100644 (file)
@@ -18,6 +18,8 @@
 /* Source file generated by `gperf`. */
 #include "autogenerated_colors.c"
 
+/** xlink XML namespace https://www.w3.org/TR/xlink11/#att-method */
+#define XLINK_NS "http://www.w3.org/1999/xlink"
 
 #define SIGNIFICAND_MAX 100000000
 #define EXPONENT_MAX 38
@@ -339,6 +341,96 @@ static inline void advance_property_name(const char **cursor, const char *texten
        }
 }
 
+
+/**
+ * parse text to obtain identifier from a url with a fragment
+ *
+ * This limits url links to identifiers within the document.
+ */
+static inline svgtiny_code
+parse_url_fragment(const char **text,
+                  const char *textend,
+                  const char **idout,
+                  int *idlenout)
+{
+       const char *cursor = *text;
+       const char *idstart = cursor;
+       const char *idend = cursor;
+
+       advance_whitespace(&cursor, textend);
+
+       if (*cursor != '#') {
+               /* not a fragment id */
+               return svgtiny_SVG_ERROR;
+       }
+       cursor++;
+
+       idstart = cursor;
+       advance_id(&cursor, textend);
+       idend = cursor;
+
+       if ((idend - idstart) == 0) {
+               /* no id */
+               return svgtiny_SVG_ERROR;
+       }
+
+       advance_whitespace(&cursor, textend);
+
+       /* url syntax is correct update output */
+       *text = cursor;
+       *idout = idstart;
+       *idlenout = idend - idstart;
+
+       return svgtiny_OK;
+}
+
+
+/**
+ * get an element in the document from a url
+ */
+static svgtiny_code
+element_from_url(const char **url,
+                size_t urllen,
+                struct svgtiny_parse_state *state,
+                dom_element **element)
+{
+       svgtiny_code res;
+       dom_exception exc;
+       const char *cursor = *url;
+       const char *urlend = *url + urllen;
+       const char *id;
+       int idlen = 0;
+       dom_string *id_str;
+
+       /**
+        * \todo deal with illegal references (circular etc)
+        * https://svgwg.org/svg2-draft/linking.html#TermInvalidReference
+        *
+        * \todo parsing the fragment out of the url only implements same
+        * document references from fragments and might be extended.
+        * https://svgwg.org/svg2-draft/linking.html#TermSameDocumentURL
+        */
+       res = parse_url_fragment(&cursor, urlend, &id, &idlen);
+       if (res != svgtiny_OK) {
+               return res;
+       }
+
+       exc = dom_string_create_interned((const uint8_t *)id, idlen, &id_str);
+       if (exc != DOM_NO_ERR) {
+               return svgtiny_LIBDOM_ERROR;
+       }
+
+       exc = dom_document_get_element_by_id(state->document, id_str, element);
+       dom_string_unref(id_str);
+       if (exc != DOM_NO_ERR) {
+               return svgtiny_LIBDOM_ERROR;
+       }
+
+       *url = cursor;
+       return svgtiny_OK;
+}
+
+
 enum transform_type {
        TRANSFORM_UNK,
        TRANSFORM_MATRIX,
@@ -666,7 +758,7 @@ parse_hex_color(const char **cursor, const char *textend, svgtiny_colour *c)
                return svgtiny_SVG_ERROR;
        }
 
-       /* \todo do something with the alpha */
+       /** \todo do something with the alpha */
        UNUSED(a);
 
        *c = svgtiny_RGB(r, g, b);
@@ -761,7 +853,8 @@ parse_color_function(const char **cursorout,
 /**
  * parse a paint url
  *
- * /todo this does not cope with any url that is not a gradient paint server.
+ * /todo this does not cope with any url that is not a fragment which
+ *       identifies a gradient paint server.
  */
 static inline svgtiny_code
 parse_paint_url(const char **cursorout,
@@ -771,9 +864,8 @@ parse_paint_url(const char **cursorout,
                svgtiny_colour *c)
 {
        const char *cursor = *cursorout;
-       const char *idstart = cursor;
-       const char *idend = cursor;
        svgtiny_code res;
+       dom_element *ref; /* referenced element */
 
        if (grad == NULL) {
                return svgtiny_SVG_ERROR;
@@ -793,39 +885,29 @@ parse_paint_url(const char **cursorout,
                return svgtiny_SVG_ERROR;
        }
        cursor += 4;
-       advance_whitespace(&cursor, textend);
 
-       if (*cursor != '#') {
-               /* not a paint server */
-               return svgtiny_SVG_ERROR;
+       res = element_from_url(&cursor, textend - cursor, state, &ref);
+       if (res != svgtiny_OK){
+               return res;
        }
-       cursor++;
-
-       idstart = cursor;
-       advance_id(&cursor, textend);
-       idend = cursor;
-
-       if ((idend - idstart) == 0) {
-               /* no id */
+       if (ref == NULL) {
+               /* unable to find referenced element */
                return svgtiny_SVG_ERROR;
        }
 
-       advance_whitespace(&cursor, textend);
-
        if ((cursor >= textend) || (*cursor != ')')) {
                /* no close bracket on url */
+               dom_node_unref(ref);
                return svgtiny_SVG_ERROR;
        }
        cursor++;
 
-       /* url syntax is correct update cursor */
-       *cursorout = cursor;
-
        /* find and update gradient */
-       res = svgtiny_find_gradient(idstart, idend - idstart, grad, state);
+       res = svgtiny_update_gradient(ref, state, grad);
        if (res == svgtiny_OK) {
                *c = svgtiny_LINEAR_GRADIENT;
        }
+       dom_node_unref(ref);
 
        return res;
 }
@@ -1478,3 +1560,63 @@ svgtiny_parse_attributes(dom_element *node,
        }
        return svgtiny_OK;
 }
+
+
+/**
+ * parses href attribute contents as a url fragment and finds matching element
+ *
+ * \param node The node on which to examine teh href attribute for a url
+ * \param state The parse state
+ * \param element result element pointer or NULL if no matching element
+ * \return svgtiny_OK and element updated else error code
+ */
+svgtiny_code
+svgtiny_parse_element_from_href(dom_element *node,
+                               struct svgtiny_parse_state *state,
+                               dom_element **element)
+{
+       dom_exception exc;
+       dom_string *attr;
+       svgtiny_code res;
+       const char *url;
+
+       /* attempt to get href in default namespace */
+       exc = dom_element_get_attribute(node, state->interned_href, &attr);
+       if (exc != DOM_NO_ERR) {
+               return svgtiny_LIBDOM_ERROR;
+       }
+
+       if (attr == NULL) {
+               dom_string *xmlns_xlink;
+               exc = dom_string_create_interned((const uint8_t *)XLINK_NS,
+                                                strlen(XLINK_NS),
+                                                &xmlns_xlink);
+               if (exc != DOM_NO_ERR || xmlns_xlink == NULL) {
+                       return svgtiny_LIBDOM_ERROR;
+               }
+
+               /* attempt to get href attribute in xlink namespace */
+               exc = dom_element_get_attribute_ns(node,
+                                                  xmlns_xlink,
+                                                  state->interned_href,
+                                                  &attr);
+               dom_string_unref(xmlns_xlink);
+               if (exc != DOM_NO_ERR) {
+                       return svgtiny_LIBDOM_ERROR;
+               }
+               if (attr == NULL) {
+                       /* no href attribute */
+                       *element = NULL;
+                       return svgtiny_OK;
+               }
+       }
+
+       url = dom_string_data(attr);
+       res = element_from_url(&url,
+                              dom_string_byte_length(attr),
+                              state,
+                              element);
+
+       dom_string_unref(attr);
+       return res;
+}
index d075eb6fc0b7c330afe41e98862ac6d466ef39c4..c40755a2e9548b6f0c9cf3dc72756ea1f549aec7 100644 (file)
@@ -49,6 +49,7 @@ SVGTINY_STRING_ACTION(offset)
 SVGTINY_STRING_ACTION(gradientUnits)
 SVGTINY_STRING_ACTION(gradientTransform)
 SVGTINY_STRING_ACTION(userSpaceOnUse)
+SVGTINY_STRING_ACTION(use)
 SVGTINY_STRING_ACTION2(stroke_width,stroke-width)
 SVGTINY_STRING_ACTION2(stop_color,stop-color)
 SVGTINY_STRING_ACTION2(zero_percent,0%)
diff --git a/test/data/arcs02.mvg b/test/data/arcs02.mvg
new file mode 100644 (file)
index 0000000..caa11cb
--- /dev/null
@@ -0,0 +1,34 @@
+viewbox 0 0 425 186
+fill none stroke #0000ff stroke-width 1 path 'M 0.354331 0.354331 L 424.842 0.354331 L 424.842 185.669 L 0.354331 185.669 Z ' 
+fill none stroke #888888 stroke-width 1 path 'M 79.7244 44.2913 C 79.7244 54.0759 63.8605 62.0079 44.2913 62.0079 C 24.7222 62.0079 8.85827 54.0759 8.85827 44.2913 C 8.85827 34.5068 24.7222 26.5748 44.2913 26.5748 C 63.8605 26.5748 79.7244 34.5068 79.7244 44.2913 Z ' 
+fill none stroke #888888 stroke-width 1 path 'M 115.157 26.5748 C 115.157 36.3594 99.2935 44.2913 79.7244 44.2913 C 60.1553 44.2913 44.2913 36.3594 44.2913 26.5748 C 44.2913 16.7902 60.1553 8.85827 79.7244 8.85827 C 99.2935 8.85827 115.157 16.7902 115.157 26.5748 Z ' 
+fill #000000 stroke none stroke-width 1 text 12.4016 24.8032 'Arc start' 
+fill #000000 stroke none stroke-width 1 text 79.7244 51.378 'Arc end' 
+fill #000000 stroke none stroke-width 1 text 159.449 74.4095 'large-arc-flag=0' 
+fill #000000 stroke none stroke-width 1 text 159.449 88.5827 'sweep-flag=0' 
+fill none stroke #888888 stroke-width 1 path 'M 221.457 44.2913 C 221.457 54.0759 205.593 62.0079 186.024 62.0079 C 166.454 62.0079 150.591 54.0759 150.591 44.2913 C 150.591 34.5068 166.454 26.5748 186.024 26.5748 C 205.593 26.5748 221.457 34.5068 221.457 44.2913 Z ' 
+fill none stroke #888888 stroke-width 1 path 'M 256.89 26.5748 C 256.89 36.3594 241.026 44.2913 221.457 44.2913 C 201.888 44.2913 186.024 36.3594 186.024 26.5748 C 186.024 16.7902 201.888 8.85827 221.457 8.85827 C 241.026 8.85827 256.89 16.7902 256.89 26.5748 Z ' 
+fill #000000 stroke none stroke-width 1 text 154.134 24.8032 'Arc start' 
+fill #000000 stroke none stroke-width 1 text 221.457 51.378 'Arc end' 
+fill none stroke #ff0000 stroke-width 2 path 'M 186.024 26.5748 C 186.024 36.3594 201.888 44.2913 221.457 44.2913 ' 
+fill #000000 stroke none stroke-width 1 text 301.181 74.4095 'large-arc-flag=0' 
+fill #000000 stroke none stroke-width 1 text 301.181 88.5827 'sweep-flag=1' 
+fill none stroke #888888 stroke-width 1 path 'M 363.189 44.2913 C 363.189 54.0759 347.325 62.0079 327.756 62.0079 C 308.187 62.0079 292.323 54.0759 292.323 44.2913 C 292.323 34.5068 308.187 26.5748 327.756 26.5748 C 347.325 26.5748 363.189 34.5068 363.189 44.2913 Z ' 
+fill none stroke #888888 stroke-width 1 path 'M 398.622 26.5748 C 398.622 36.3594 382.758 44.2913 363.189 44.2913 C 343.62 44.2913 327.756 36.3594 327.756 26.5748 C 327.756 16.7902 343.62 8.85827 363.189 8.85827 C 382.758 8.85827 398.622 16.7902 398.622 26.5748 Z ' 
+fill #000000 stroke none stroke-width 1 text 295.866 24.8032 'Arc start' 
+fill #000000 stroke none stroke-width 1 text 363.189 51.378 'Arc end' 
+fill none stroke #ff0000 stroke-width 2 path 'M 327.756 26.5748 C 347.325 26.5748 363.189 34.5068 363.189 44.2913 ' 
+fill #000000 stroke none stroke-width 1 text 159.449 162.992 'large-arc-flag=1' 
+fill #000000 stroke none stroke-width 1 text 159.449 177.165 'sweep-flag=0' 
+fill none stroke #888888 stroke-width 1 path 'M 221.457 132.874 C 221.457 142.659 205.593 150.591 186.024 150.591 C 166.454 150.591 150.591 142.659 150.591 132.874 C 150.591 123.089 166.454 115.157 186.024 115.157 C 205.593 115.157 221.457 123.089 221.457 132.874 Z ' 
+fill none stroke #888888 stroke-width 1 path 'M 256.89 115.157 C 256.89 124.942 241.026 132.874 221.457 132.874 C 201.888 132.874 186.024 124.942 186.024 115.157 C 186.024 105.373 201.888 97.4409 221.457 97.4409 C 241.026 97.4409 256.89 105.373 256.89 115.157 Z ' 
+fill #000000 stroke none stroke-width 1 text 154.134 113.386 'Arc start' 
+fill #000000 stroke none stroke-width 1 text 221.457 139.961 'Arc end' 
+fill none stroke #ff0000 stroke-width 2 path 'M 186.024 115.157 C 166.454 115.157 150.591 123.089 150.591 132.874 C 150.591 142.659 166.454 150.591 186.024 150.591 C 205.593 150.591 221.457 142.659 221.457 132.874 ' 
+fill #000000 stroke none stroke-width 1 text 301.181 162.992 'large-arc-flag=1' 
+fill #000000 stroke none stroke-width 1 text 301.181 177.165 'sweep-flag=1' 
+fill none stroke #888888 stroke-width 1 path 'M 363.189 132.874 C 363.189 142.659 347.325 150.591 327.756 150.591 C 308.187 150.591 292.323 142.659 292.323 132.874 C 292.323 123.089 308.187 115.157 327.756 115.157 C 347.325 115.157 363.189 123.089 363.189 132.874 Z ' 
+fill none stroke #888888 stroke-width 1 path 'M 398.622 115.157 C 398.622 124.942 382.758 132.874 363.189 132.874 C 343.62 132.874 327.756 124.942 327.756 115.157 C 327.756 105.373 343.62 97.4409 363.189 97.4409 C 382.758 97.4409 398.622 105.373 398.622 115.157 Z ' 
+fill #000000 stroke none stroke-width 1 text 295.866 113.386 'Arc start' 
+fill #000000 stroke none stroke-width 1 text 363.189 139.961 'Arc end' 
+fill none stroke #ff0000 stroke-width 2 path 'M 327.756 115.157 C 327.756 105.373 343.62 97.4409 363.189 97.4409 C 382.758 97.4409 398.622 105.373 398.622 115.157 C 398.622 124.942 382.758 132.874 363.189 132.874 ' 
index f58f5d65f7cfa92852af9b696857a21e85f0a2dc..eabde9ca635a483554811d88b9baed775045b508 100644 (file)
@@ -21,7 +21,7 @@
 
     <g font-size="30" >
       <g transform="translate(0,0)">
-        <use xlink:href="#baseEllipses"/>
+        <use href="#baseEllipses"/>
       </g>
       <g transform="translate(400,0)">
         <text x="50" y="210">large-arc-flag=0</text>
index 26a725883cfbcb32ebcabacba2908be46aa84c24..43bc5ed44594f2664ddbea50c3bda40d36276f16 100644 (file)
@@ -1,6 +1,8 @@
 <svg viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg">
   <defs>
-    <linearGradient id="foo">
+    <linearGradient id="foo" href="#baz">
+    </linearGradient>
+    <linearGradient id="baz">
       <stop stop-color="#69f" offset="0"/>
       <stop style="stop-color:#468" offset="100%"/>
     </linearGradient>
diff --git a/test/data/use.mvg b/test/data/use.mvg
new file mode 100644 (file)
index 0000000..1804aa1
--- /dev/null
@@ -0,0 +1,3 @@
+viewbox 0 0 100 100
+fill #000000 stroke none stroke-width 1 path 'M 0 0 L 100 100 Z ' 
+fill #000000 stroke none stroke-width 1 path 'M 100 0 L 0 100 Z ' 
diff --git a/test/data/use.svg b/test/data/use.svg
new file mode 100644 (file)
index 0000000..fa01600
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version="1.0" standalone="no"?>
+<svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <g id="defcircularref">
+      <line x1="0" y1="0" x2="100" y2="100" />
+      <use href="#defcircularref"/>
+    </g>
+    <g id="noncircularref">
+      <line x1="100" y1="0" x2="0" y2="100" />
+    </g>
+  </defs>
+  <use href="#defcircularref"/>
+  <g id="circularref">
+    <use href="#noncircularref"/>
+    <use href="#circularref"/>
+  </g>
+  
+</svg>