*/
#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
#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)
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
}
}
+/**
+ * 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.
*
}
+/**
+ * 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.
*/
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);
}
+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,
{
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);
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);
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
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,
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,
/* 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
}
}
+
+/**
+ * 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,
return svgtiny_SVG_ERROR;
}
- /* \todo do something with the alpha */
+ /** \todo do something with the alpha */
UNUSED(a);
*c = svgtiny_RGB(r, g, b);
/**
* 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,
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;
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;
}
}
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;
+}
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%)
--- /dev/null
+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 '
<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>
<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>
--- /dev/null
+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 '
--- /dev/null
+<?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>