#include "svgtiny.h"
#include "svgtiny_internal.h"
-/* Source file generated by `gperf`. */
-#include "autogenerated_colors.c"
#define TAU 6.28318530717958647692
struct svgtiny_parse_state *state);
static svgtiny_code svgtiny_add_path(float *p, unsigned int n,
struct svgtiny_parse_state *state);
-static void _svgtiny_parse_color(const char *s, svgtiny_colour *c,
- struct svgtiny_parse_state_gradient *grad,
- struct svgtiny_parse_state *state);
/**
* rotate midpoint vector
}
}
+
/**
* Set the local externally-stored parts of a parse state.
* Call this in functions that made a new state on the stack.
svgtiny_grad_string_ref(&(state->stroke_grad));
}
+
/**
* Cleanup the local externally-stored parts of a parse state.
* Call this in functions that made a new state on the stack.
return NULL;
}
+
static void ignore_msg(uint32_t severity, void *ctx, const char *msg, ...)
{
UNUSED(severity);
UNUSED(msg);
}
+
/**
* Parse a block of memory into a svgtiny_diagram.
*/
}
-
/**
* Parse a <path> element node.
*
}
-
/**
* Parse a <polyline> or <polygon> element node.
*
dom_string *attr;
dom_exception exc;
+ /* fill color */
exc = dom_element_get_attribute(node, state->interned_fill, &attr);
if (exc == DOM_NO_ERR && attr != NULL) {
- svgtiny_parse_color(attr, &state->fill, &state->fill_grad, state);
+ svgtiny_parse_paint(dom_string_data(attr),
+ dom_string_byte_length(attr),
+ &state->fill_grad,
+ state,
+ &state->fill);
dom_string_unref(attr);
}
+ /* stroke color */
exc = dom_element_get_attribute(node, state->interned_stroke, &attr);
if (exc == DOM_NO_ERR && attr != NULL) {
- svgtiny_parse_color(attr, &state->stroke, &state->stroke_grad, state);
+ svgtiny_parse_paint(dom_string_data(attr),
+ dom_string_byte_length(attr),
+ &state->stroke_grad,
+ state,
+ &state->stroke);
dom_string_unref(attr);
}
+ /* stroke width */
exc = dom_element_get_attribute(node, state->interned_stroke_width, &attr);
if (exc == DOM_NO_ERR && attr != NULL) {
float stroke_width;
char *style = strndup(dom_string_data(attr),
dom_string_byte_length(attr));
const char *s;
- char *value;
if ((s = strstr(style, "fill:"))) {
s += 5;
- while (*s == ' ')
- s++;
- value = strndup(s, strcspn(s, "; "));
- _svgtiny_parse_color(value, &state->fill, &state->fill_grad, state);
- free(value);
+ svgtiny_parse_paint(s,
+ strcspn(s, "; "),
+ &state->fill_grad,
+ state,
+ &state->fill);
}
if ((s = strstr(style, "stroke:"))) {
s += 7;
- while (*s == ' ')
- s++;
- value = strndup(s, strcspn(s, "; "));
- _svgtiny_parse_color(value, &state->stroke, &state->stroke_grad, state);
- free(value);
+ svgtiny_parse_paint(s,
+ strcspn(s, "; "),
+ &state->stroke_grad,
+ state,
+ &state->stroke);
}
if ((s = strstr(style, "stroke-width:"))) {
- s += 13;
float stroke_width;
+ s += 13;
svgtiny_parse_length(s,
strcspn(s, "; "),
state->viewport_width,
}
-/**
- * Parse a colour.
- */
-
-static void _svgtiny_parse_color(const char *s, svgtiny_colour *c,
- struct svgtiny_parse_state_gradient *grad,
- struct svgtiny_parse_state *state)
-{
- unsigned int r, g, b;
- float rf, gf, bf;
- size_t len = strlen(s);
- char *id = 0, *rparen;
-
- if (len == 4 && s[0] == '#') {
- if (sscanf(s + 1, "%1x%1x%1x", &r, &g, &b) == 3)
- *c = svgtiny_RGB(r | r << 4, g | g << 4, b | b << 4);
-
- } else if (len == 7 && s[0] == '#') {
- if (sscanf(s + 1, "%2x%2x%2x", &r, &g, &b) == 3)
- *c = svgtiny_RGB(r, g, b);
-
- } else if (10 <= len && s[0] == 'r' && s[1] == 'g' && s[2] == 'b' &&
- s[3] == '(' && s[len - 1] == ')') {
- if (sscanf(s + 4, "%u,%u,%u", &r, &g, &b) == 3)
- *c = svgtiny_RGB(r, g, b);
- else if (sscanf(s + 4, "%f%%,%f%%,%f%%", &rf, &gf, &bf) == 3) {
- b = bf * 255 / 100;
- g = gf * 255 / 100;
- r = rf * 255 / 100;
- *c = svgtiny_RGB(r, g, b);
- }
-
- } else if (len == 4 && strcmp(s, "none") == 0) {
- *c = svgtiny_TRANSPARENT;
-
- } else if (5 < len && s[0] == 'u' && s[1] == 'r' && s[2] == 'l' &&
- s[3] == '(') {
- if (grad == NULL) {
- *c = svgtiny_RGB(0, 0, 0);
- } else if (s[4] == '#') {
- id = strdup(s + 5);
- if (!id)
- return;
- rparen = strchr(id, ')');
- if (rparen)
- *rparen = 0;
- svgtiny_find_gradient(id, grad, state);
- free(id);
- if (grad->linear_gradient_stop_count == 0)
- *c = svgtiny_TRANSPARENT;
- else if (grad->linear_gradient_stop_count == 1)
- *c = grad->gradient_stop[0].color;
- else
- *c = svgtiny_LINEAR_GRADIENT;
- }
-
- } else {
- const struct svgtiny_named_color *named_color;
- named_color = svgtiny_color_lookup(s, strlen(s));
- if (named_color)
- *c = named_color->color;
- }
-}
-
-void svgtiny_parse_color(dom_string *s, svgtiny_colour *c,
- struct svgtiny_parse_state_gradient *grad,
- struct svgtiny_parse_state *state)
-{
- dom_string_ref(s);
- _svgtiny_parse_color(dom_string_data(s), c, grad, state);
- dom_string_unref(s);
-}
-
/**
* Parse font attributes, if present.
*/
* Find a gradient by id and parse it.
*/
-void svgtiny_find_gradient(const char *id,
+svgtiny_code svgtiny_find_gradient(const char *id,
+ size_t idlen,
struct svgtiny_parse_state_gradient *grad,
struct svgtiny_parse_state *state)
{
dom_element *gradient;
dom_string *id_str, *name;
dom_exception exc;
+ svgtiny_code res = svgtiny_OK;
#ifdef GRADIENT_DEBUG
- fprintf(stderr, "svgtiny_find_gradient: id \"%s\"\n", id);
+ fprintf(stderr, "svgtiny_find_gradient: id \"%.*s\"\n", idlen, id);
#endif
grad->linear_gradient_stop_count = 0;
grad->gradient_transform.e = 0;
grad->gradient_transform.f = 0;
- exc = dom_string_create_interned((const uint8_t *) id,
- strlen(id), &id_str);
+ exc = dom_string_create_interned((const uint8_t *) id, idlen, &id_str);
if (exc != DOM_NO_ERR)
- return;
+ return svgtiny_SVG_ERROR;
- exc = dom_document_get_element_by_id(state->document, id_str,
- &gradient);
+ 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", id);
+ fprintf(stderr, "gradient \"%.*s\" not found\n", idlen, id);
#endif
- return;
+ return svgtiny_SVG_ERROR;
}
exc = dom_node_get_node_name(gradient, &name);
if (exc != DOM_NO_ERR) {
dom_node_unref(gradient);
- return;
+ return svgtiny_SVG_ERROR;
}
- if (dom_string_isequal(name, state->interned_linearGradient))
- svgtiny_parse_linear_gradient(gradient, grad, state);
+ if (dom_string_isequal(name, state->interned_linearGradient)) {
+ res = svgtiny_parse_linear_gradient(gradient, grad, state);
+ }
dom_node_unref(gradient);
dom_string_unref(name);
fprintf(stderr, "linear_gradient_stop_count %i\n",
grad->linear_gradient_stop_count);
#endif
+
+ return res;
}
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) '#') {
- char *s = strndup(dom_string_data(attr) + 1,
- dom_string_byte_length(attr) - 1);
- svgtiny_find_gradient(s, grad, state);
- free(s);
+ svgtiny_find_gradient(dom_string_data(attr) + 1,
+ dom_string_byte_length(attr) - 1,
+ grad,
+ state);
}
dom_string_unref(attr);
}
state->interned_stop_color,
&attr);
if (exc == DOM_NO_ERR && attr != NULL) {
- svgtiny_parse_color(attr, &color, NULL, state);
+ svgtiny_parse_color(dom_string_data(attr),
+ dom_string_byte_length(attr), &color);
dom_string_unref(attr);
}
exc = dom_element_get_attribute(stop,
&value);
if (exc == DOM_NO_ERR &&
value != NULL) {
- svgtiny_parse_color(value,
- &color,
- NULL,
- state);
+ svgtiny_parse_color(dom_string_data(value),
+ dom_string_byte_length(value),
+ &color);
dom_string_unref(value);
}
}
struct svgtiny_list;
/* svgtiny.c */
-void svgtiny_parse_color(dom_string *s, svgtiny_colour *c,
- struct svgtiny_parse_state_gradient *grad,
- struct svgtiny_parse_state *state);
struct svgtiny_shape *svgtiny_add_shape(struct svgtiny_parse_state *state);
void svgtiny_transform_path(float *p, unsigned int n,
struct svgtiny_parse_state *state);
int viewport_size, float *length);
svgtiny_code svgtiny_parse_transform(const char *text, size_t textlen,
struct svgtiny_transformation_matrix *tm);
+svgtiny_code svgtiny_parse_paint(const char *text, size_t textlen,
+ struct svgtiny_parse_state_gradient *grad,
+ struct svgtiny_parse_state *state,
+ svgtiny_colour *c);
+svgtiny_code svgtiny_parse_color(const char *text, size_t textlen,
+ svgtiny_colour *c);
/* svgtiny_gradient.c */
-void svgtiny_find_gradient(const char *id,
+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_add_path_linear_gradient(float *p, unsigned int n,
#include "svgtiny.h"
#include "svgtiny_internal.h"
+/* Source file generated by `gperf`. */
+#include "autogenerated_colors.c"
+
#define SIGNIFICAND_MAX 100000000
#define EXPONENT_MAX 38
#define EXPONENT_MIN -38
/**
- * skip SVG spec defined comma and whitespace
+ * advance cursor across SVG spec defined comma and whitespace
*
* \param cursor current cursor
* \param textend end of buffer
*/
static inline void advance_comma_whitespace(const char **cursor, const char *textend)
+{
+ advance_whitespace(cursor, textend);
+ if (((*cursor) < textend) && (**cursor == 0x2C /* , */)) {
+ (*cursor)++;
+ advance_whitespace(cursor, textend);
+ }
+}
+
+
+/**
+ * advance across hexidecimal characters
+ *
+ * \param cursor current cursor
+ * \param textend end of buffer
+ */
+static inline void advance_hex(const char **cursor, const char *textend)
+{
+ while ((*cursor) < textend) {
+ if (((**cursor < 0x30 /* 0 */) || (**cursor > 0x39 /* 9 */)) &&
+ ((**cursor < 0x41 /* A */) || (**cursor > 0x46 /* F */)) &&
+ ((**cursor < 0x61 /* a */) || (**cursor > 0x66 /* f */))) {
+ break;
+ }
+ (*cursor)++;
+ }
+}
+
+
+/**
+ * advance over SVG id
+ *
+ * \param cursor current cursor
+ * \param textend end of buffer
+ *
+ * https://www.w3.org/TR/SVG2/struct.html#IDAttribute
+ */
+static inline void advance_id(const char **cursor, const char *textend)
{
while((*cursor) < textend) {
- if ((**cursor != 0x20) &&
- (**cursor != 0x09) &&
- (**cursor != 0x0A) &&
- (**cursor != 0x0D) &&
- (**cursor != 0x2C /* , */)) {
+ if ((**cursor == 0x20) ||
+ (**cursor == 0x09) ||
+ (**cursor == 0x0A) ||
+ (**cursor == 0x0D) ||
+ (**cursor == ')')) {
break;
}
(*cursor)++;
return svgtiny_SVG_ERROR;
}
+
/**
* Parse and apply a transform attribute.
*
/* no comma or whitespace between transforms */
goto transform_parse_complete;
}
-
}
+
transform_parse_complete:
return svgtiny_OK;
}
+
+
+/**
+ * convert ascii hex digit to an integer
+ */
+static inline unsigned int hexd_to_int(const char *digit)
+{
+ unsigned int value = *digit;
+
+ if ((*digit >= 0x30 /* 0 */) && (*digit <= 0x39 /* 9 */)) {
+ value -= 0x30;
+ } else if ((*digit >= 0x41 /* A */) && (*digit <= 0x5A /* Z */) ) {
+ value -= 0x37;
+ } else if (((*digit >= 0x61 /* a */) && (*digit <= 0x7A /* z */))) {
+ value -= 0x57;
+ }
+ return value;
+}
+
+
+/**
+ * convert two ascii hex digits to an integer
+ */
+static inline unsigned int hexdd_to_int(const char *digits)
+{
+ return (hexd_to_int(digits) << 4) | hexd_to_int(digits + 1);
+}
+
+
+/**
+ * parse a hex colour
+ * https://www.w3.org/TR/css-color-4/#typedef-hex-color
+ */
+static inline svgtiny_code
+parse_hex_color(const char **cursor, const char *textend, svgtiny_colour *c)
+{
+ unsigned int r, g, b, a=0xff;
+ const char *tokstart;
+
+ /* hex-color */
+ if ((**cursor != '#') || ((textend - (*cursor)) < 4)) {
+ return svgtiny_SVG_ERROR;
+ }
+
+ (*cursor)++;
+ tokstart = *cursor;
+
+ advance_hex(cursor, textend);
+
+ switch ((*cursor) - tokstart) {
+ case 3:
+ r = hexd_to_int(tokstart);
+ g = hexd_to_int(tokstart + 1);
+ b = hexd_to_int(tokstart + 2);
+ r |= r << 4;
+ g |= g << 4;
+ b |= b << 4;
+ break;
+ case 4:
+ r = hexd_to_int(tokstart);
+ g = hexd_to_int(tokstart + 1);
+ b = hexd_to_int(tokstart + 2);
+ a = hexd_to_int(tokstart + 3);
+ r |= r << 4;
+ g |= g << 4;
+ b |= b << 4;
+ break;
+ case 6:
+ r = hexdd_to_int(tokstart);
+ g = hexdd_to_int(tokstart + 2);
+ b = hexdd_to_int(tokstart + 4);
+ *c = svgtiny_RGB(r, g, b);
+ break;
+ case 8:
+ r = hexdd_to_int(tokstart);
+ g = hexdd_to_int(tokstart + 2);
+ b = hexdd_to_int(tokstart + 4);
+ a = hexdd_to_int(tokstart + 6);
+ break;
+ default:
+ /* unparsable hex color */
+ *cursor = tokstart - 1; /* put cursor back */
+ return svgtiny_SVG_ERROR;
+ }
+
+ /* \todo do something with the alpha */
+ UNUSED(a);
+
+ *c = svgtiny_RGB(r, g, b);
+ return svgtiny_OK;
+}
+
+
+/**
+ * parse a color function
+ *
+ * https://www.w3.org/TR/css-color-5/#typedef-color-function
+ *
+ * The only actual supported color function is rgb
+ */
+static inline svgtiny_code
+parse_color_function(const char **cursorout,
+ const char *textend,
+ svgtiny_colour *c)
+{
+ const char *cursor = *cursorout;
+ const char *argend = cursor;
+ svgtiny_code res;
+ float argf[4];
+ int idx; /* argument index */
+
+ if ((textend - cursor) < 10) {
+ /* must be at least ten characters to be a valid function */
+ return svgtiny_SVG_ERROR;
+ }
+
+ if (((cursor[0] != 'r') && (cursor[0] != 'R')) ||
+ ((cursor[1] != 'g') && (cursor[1] != 'G')) ||
+ ((cursor[2] != 'b') && (cursor[2] != 'B')) ||
+ (cursor[3] != '('))
+ {
+ /* only function currently supported is rgb */
+ return svgtiny_SVG_ERROR;
+ }
+ cursor+=4;
+
+ for (idx = 0; idx < 4; idx++) {
+ res = svgtiny_parse_number(cursor, textend - cursor, &argend, &argf[idx]);
+ if (res != svgtiny_OK) {
+ break;
+ }
+ cursor = argend;
+ if (cursor >= textend) {
+ /* no more input */
+ break;
+ }
+ if (*cursor == '%') {
+ /* percentage */
+ argf[idx] = argf[idx] * 255 / 100;
+ cursor++;
+ }
+ /* value must be clamped */
+ if (argf[idx] < 0) {
+ argf[idx] = 0;
+ }
+ if (argf[idx] > 255) {
+ argf[idx] = 255;
+ }
+ /* advance cursor to next argument */
+ advance_whitespace(&cursor, textend);
+ if (cursor >= textend) {
+ /* no more input */
+ break;
+ }
+ if (*cursor == ')') {
+ /* close parenthesis, arguments are complete */
+ cursor++;
+ break;
+ }
+ if (*cursor == ',') {
+ /* skip optional comma */
+ cursor++;
+ }
+ }
+ if (idx < 2 || idx > 3) {
+ return svgtiny_SVG_ERROR;
+ }
+
+ *cursorout = cursor;
+ *c = svgtiny_RGB((unsigned int)argf[0],
+ (unsigned int)argf[1],
+ (unsigned int)argf[2]);
+ return svgtiny_OK;
+}
+
+
+/**
+ * parse a paint url
+ *
+ * /todo this does not cope with any url that is not a gradient paint server.
+ */
+static inline svgtiny_code
+parse_paint_url(const char **cursorout,
+ const char *textend,
+ struct svgtiny_parse_state_gradient *grad,
+ struct svgtiny_parse_state *state,
+ svgtiny_colour *c)
+{
+ const char *cursor = *cursorout;
+ const char *idstart = cursor;
+ const char *idend = cursor;
+ svgtiny_code res;
+
+ if (grad == NULL) {
+ return svgtiny_SVG_ERROR;
+ }
+
+ if ((textend - cursor) < 6) {
+ /* must be at least six characters to be a url */
+ return svgtiny_SVG_ERROR;
+ }
+
+ if (((cursor[0] != 'u') && (cursor[0] != 'U')) ||
+ ((cursor[1] != 'r') && (cursor[1] != 'R')) ||
+ ((cursor[2] != 'l') && (cursor[2] != 'L')) ||
+ (cursor[3] != '('))
+ {
+ /* only function currently supported is rgb */
+ return svgtiny_SVG_ERROR;
+ }
+ cursor += 4;
+ advance_whitespace(&cursor, textend);
+
+ if (*cursor != '#') {
+ /* not a paint server */
+ 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);
+
+ if ((cursor >= textend) || (*cursor != ')')) {
+ /* no close bracket on url */
+ 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);
+
+ if (res == svgtiny_OK) {
+ /* only set the color if the gradient was processed ok */
+ if (grad->linear_gradient_stop_count == 0) {
+ *c = svgtiny_TRANSPARENT;
+ } else if (grad->linear_gradient_stop_count == 1) {
+ *c = grad->gradient_stop[0].color;
+ } else {
+ *c = svgtiny_LINEAR_GRADIENT;
+ }
+ }
+
+ return res;
+}
+
+
+/**
+ * Parse a paint.
+ *
+ * https://www.w3.org/TR/SVG11/painting.html#SpecifyingPaint
+ * https://www.w3.org/TR/SVG2/painting.html#SpecifyingPaint
+ *
+ */
+svgtiny_code
+svgtiny_parse_paint(const char *text,
+ size_t textlen,
+ struct svgtiny_parse_state_gradient *grad,
+ struct svgtiny_parse_state *state,
+ svgtiny_colour *c)
+{
+ const char *cursor = text; /* cursor */
+ const char *textend = text+textlen;
+ svgtiny_code res;
+
+ advance_whitespace(&cursor, textend);
+
+ if (((textend - cursor) == 4) &&
+ cursor[0] == 'n' &&
+ cursor[1] == 'o' &&
+ cursor[2] == 'n' &&
+ cursor[3] == 'e') {
+ *c = svgtiny_TRANSPARENT;
+ return svgtiny_OK;
+ }
+
+ /* attempt to parse element as a paint url */
+ res = parse_paint_url(&cursor, textend, grad, state, c);
+ if (res == svgtiny_OK) {
+ return res;
+ }
+
+ return svgtiny_parse_color(cursor, textend - cursor, c);
+}
+
+
+/**
+ * Parse a color.
+ *
+ * https://www.w3.org/TR/SVG11/types.html#DataTypeColor
+ * https://www.w3.org/TR/css-color-5/#typedef-color
+ *
+ * <color> = <color-base> | currentColor | <system-color>
+ * <color-base> = <hex-color> | <color-function> | <named-color> | transparent
+ * <color-function> = <rgb()> | <rgba()> | <hsl()> | <hsla()> | <hwb()> |
+ * <lab()> | <lch()> | <oklab()> | <oklch()> | <color()>
+ *
+ * \todo this does not cope with currentColor or transparent and supports only
+ * the rgb color function.
+ */
+svgtiny_code
+svgtiny_parse_color(const char *text, size_t textlen, svgtiny_colour *c)
+{
+ const struct svgtiny_named_color *named_color;
+ const char *cursor = text; /* cursor */
+ const char *textend = text + textlen;
+ svgtiny_code res;
+
+ advance_whitespace(&cursor, textend);
+
+ /* attempt to parse element as a hex color */
+ res = parse_hex_color(&cursor, textend, c);
+ if (res == svgtiny_OK) {
+ return res;
+ }
+
+ /* attempt to parse element as a color function */
+ res = parse_color_function(&cursor, textend, c);
+ if (res == svgtiny_OK) {
+ return res;
+ }
+
+ named_color = svgtiny_color_lookup(text, textlen);
+ if (named_color) {
+ *c = named_color->color;
+ return svgtiny_OK;
+ }
+
+ /* did not parse as a color */
+ *c = svgtiny_RGB(0, 0, 0);
+ return svgtiny_SVG_ERROR;
+}
--- /dev/null
+viewbox 0 0 1200 1200
+fill #012345 stroke #89abcd stroke-width 10 path 'M 100 100 L 1100 100 L 1100 200 L 100 200 Z '
+fill #eeffaa stroke #ccddee stroke-width 10 path 'M 100 200 L 1100 200 L 1100 300 L 100 300 Z '
+fill #ff6677 stroke #bb6677 stroke-width 10 path 'M 100 300 L 1100 300 L 1100 400 L 100 400 Z '
+fill #0080ff stroke #ff7f00 stroke-width 10 path 'M 100 400 L 1100 400 L 1100 500 L 100 500 Z '
--- /dev/null
+<?xml version="1.0"?>
+<svg width="1200" height="1200" viewPort="0 0 1200 1200" version="1.1" xmlns="http://www.w3.org/2000/svg">
+ <rect fill="#01234567" stroke=" #89ABCD" x="100" y="100" width="1000" height="100" stroke-width="10" />
+ <rect fill="#EFab " stroke=" #cde " x="100" y="200" width="1000" height="100" stroke-width="10" />
+ <rect fill="#f67 " stroke="#bB6677 " x="100" y="300" width="1000" height="100" stroke-width="10" />
+ <rect fill="rgb(0,128,255)" stroke="RGB(100%,50%,0%)" x="100" y="400" width="1000" height="100" stroke-width="10" />
+</svg>