From: Vincent Sanders Date: Thu, 18 Jul 2024 21:38:26 +0000 (+0100) Subject: Implement use element X-Git-Url: https://gitweb.michael.orlitzky.com/?a=commitdiff_plain;h=452addd085cb1bcdea0375f88f92cd36e839c8e9;p=libsvgtiny.git Implement use element --- diff --git a/src/svgtiny.c b/src/svgtiny.c index 6400320..ab49f8f 100644 --- a/src/svgtiny.c +++ b/src/svgtiny.c @@ -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 + 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 element node. * @@ -877,6 +922,46 @@ svgtiny_parse_text(dom_element *text, struct svgtiny_parse_state state) } +/** + * Parse a 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 or 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, diff --git a/src/svgtiny_gradient.c b/src/svgtiny_gradient.c index 9e5b7d2..ca961cc 100644 --- a/src/svgtiny_gradient.c +++ b/src/svgtiny_gradient.c @@ -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 diff --git a/src/svgtiny_internal.h b/src/svgtiny_internal.h index 6a57140..2b2b257 100644 --- a/src/svgtiny_internal.h +++ b/src/svgtiny_internal.h @@ -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, diff --git a/src/svgtiny_parse.c b/src/svgtiny_parse.c index 3d33cc7..af7b228 100644 --- a/src/svgtiny_parse.c +++ b/src/svgtiny_parse.c @@ -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; +} diff --git a/src/svgtiny_strings.h b/src/svgtiny_strings.h index d075eb6..c40755a 100644 --- a/src/svgtiny_strings.h +++ b/src/svgtiny_strings.h @@ -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 index 0000000..caa11cb --- /dev/null +++ b/test/data/arcs02.mvg @@ -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 ' diff --git a/test/data/arcs02.svg b/test/data/arcs02.svg index f58f5d6..eabde9c 100644 --- a/test/data/arcs02.svg +++ b/test/data/arcs02.svg @@ -21,7 +21,7 @@ - + large-arc-flag=0 diff --git a/test/data/bad-grad.svg b/test/data/bad-grad.svg index 26a7258..43bc5ed 100644 --- a/test/data/bad-grad.svg +++ b/test/data/bad-grad.svg @@ -1,6 +1,8 @@ - + + + diff --git a/test/data/use.mvg b/test/data/use.mvg new file mode 100644 index 0000000..1804aa1 --- /dev/null +++ b/test/data/use.mvg @@ -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 index 0000000..fa01600 --- /dev/null +++ b/test/data/use.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + +