dom_string_unref(attr);
}
+ /* style attribute */
exc = dom_element_get_attribute(node, state->interned_style, &attr);
if (exc == DOM_NO_ERR && attr != NULL) {
- char *style = strndup(dom_string_data(attr),
- dom_string_byte_length(attr));
- const char *s;
- if ((s = strstr(style, "fill:"))) {
- s += 5;
- svgtiny_parse_paint(s,
- strcspn(s, "; "),
- &state->fill_grad,
- state,
- &state->fill);
- }
- if ((s = strstr(style, "stroke:"))) {
- s += 7;
- svgtiny_parse_paint(s,
- strcspn(s, "; "),
- &state->stroke_grad,
- state,
- &state->stroke);
- }
- if ((s = strstr(style, "stroke-width:"))) {
- float stroke_width;
- s += 13;
- svgtiny_parse_length(s,
- strcspn(s, "; "),
- state->viewport_width,
- &stroke_width);
- state->stroke_width = stroke_width;
- }
- free(style);
+ struct svgtiny_parse_inline_style_op styles[]={
+ {
+ state->interned_fill,
+ ISTYLEOP_PAINT,
+ &state->fill_grad,
+ &state->fill
+ }, {
+ state->interned_stroke,
+ ISTYLEOP_PAINT,
+ &state->stroke_grad,
+ &state->stroke
+ }, {
+ state->interned_stroke_width,
+ ISTYLEOP_INTLENGTH,
+ &state->viewport_width,
+ &state->stroke_width
+ },{
+ NULL, ISTYLEOP_NONE, NULL, NULL
+ },
+ };
+ svgtiny_parse_inline_style(dom_string_data(attr),
+ dom_string_byte_length(attr),
+ state,
+ styles);
dom_string_unref(attr);
}
}
state->interned_style,
&attr);
if (exc == DOM_NO_ERR && attr != NULL) {
- char *content = strndup(dom_string_data(attr),
- dom_string_byte_length(attr));
- const char *s;
- dom_string *value;
- if ((s = strstr(content, "stop-color:"))) {
- s += 11;
- while (*s == ' ')
- s++;
- exc = dom_string_create_interned(
- (const uint8_t *) s,
- strcspn(s, "; "),
- &value);
- if (exc == DOM_NO_ERR &&
- value != NULL) {
- svgtiny_parse_color(dom_string_data(value),
- dom_string_byte_length(value),
- &color);
- dom_string_unref(value);
- }
- }
- free(content);
+ struct svgtiny_parse_inline_style_op styles[]={
+ {
+ state->interned_stop_color,
+ ISTYLEOP_COLOR,
+ NULL,
+ &color
+ },{
+ NULL, ISTYLEOP_NONE, NULL, NULL
+ },
+ };
+ svgtiny_parse_inline_style(dom_string_data(attr),
+ dom_string_byte_length(attr),
+ state,
+ styles);
dom_string_unref(attr);
}
if (offset != -1 && color != svgtiny_TRANSPARENT) {
struct svgtiny_list;
+enum svgtiny_parse_inline_style_operation {
+ ISTYLEOP_NONE,
+ ISTYLEOP_PAINT,
+ ISTYLEOP_COLOR,
+ ISTYLEOP_LENGTH,
+ ISTYLEOP_INTLENGTH,
+};
+
+/**
+ * control structure for inline style parse
+ */
+struct svgtiny_parse_inline_style_op {
+ dom_string *key;
+ enum svgtiny_parse_inline_style_operation operation;
+ void *param;
+ void *value;
+};
+
/* svgtiny.c */
struct svgtiny_shape *svgtiny_add_shape(struct svgtiny_parse_state *state);
void svgtiny_transform_path(float *p, unsigned int n,
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(const char *text, size_t textlen,
+ struct svgtiny_parse_state *state,
+ struct svgtiny_parse_inline_style_op *styles);
/* svgtiny_gradient.c */
svgtiny_code svgtiny_find_gradient(const char *id,
}
}
+static inline void advance_property_name(const char **cursor, const char *textend)
+{
+ while ((*cursor) < textend) {
+ if (((**cursor < 0x30 /* 0 */) || (**cursor > 0x39 /* 9 */)) &&
+ ((**cursor < 0x41 /* A */) || (**cursor > 0x5A /* Z */)) &&
+ ((**cursor < 0x61 /* a */) || (**cursor > 0x7A /* z */)) &&
+ (**cursor != '-') &&
+ (**cursor != '_')) {
+ break;
+ }
+ (*cursor)++;
+ }
+}
enum transform_type {
TRANSFORM_UNK,
return svgtiny_OK;
}
+
+/**
+ * parse a declaration in a style
+ *
+ * https://www.w3.org/TR/CSS21/syndata.html#declaration
+ *
+ * \param declaration The declaration without any preceeding space
+ * \param end The end of the declaration string
+ * \param state parse state to pass on
+ * \param styleops The table of style operations to apply
+ *
+ * declaration is "<property name> : <property value>"
+ */
+static inline svgtiny_code
+parse_declaration(const char *declaration,
+ const char *end,
+ struct svgtiny_parse_state *state,
+ struct svgtiny_parse_inline_style_op *styleops)
+{
+ const char *cursor = declaration; /* text cursor */
+ size_t key_len; /* key length */
+ struct svgtiny_parse_inline_style_op *styleop;
+
+ /* declaration must be at least 3 characters long (ie "a:b") */
+ if ((end - declaration) < 3) {
+ return svgtiny_SVG_ERROR;
+ }
+
+ /* find end of key */
+ advance_property_name(&cursor, end);
+
+ if ((cursor - declaration) < 1) {
+ /* no key */
+ return svgtiny_SVG_ERROR;
+ }
+
+ key_len = cursor - declaration;
+
+ advance_whitespace(&cursor, end);
+
+ if ((cursor >= end) || (*cursor != ':')) {
+ /* no colon */
+ return svgtiny_SVG_ERROR;
+ }
+ cursor++; /* advance over colon */
+
+ advance_whitespace(&cursor, end);
+
+ /* search style operations for a match */
+ for (styleop = styleops; styleop->key != NULL; styleop++) {
+ if ((dom_string_byte_length(styleop->key) == key_len) &&
+ (memcmp(declaration, dom_string_data(styleop->key), key_len) == 0)) {
+ float parse_len;
+
+ switch (styleop->operation) {
+ case ISTYLEOP_PAINT:
+ svgtiny_parse_paint(cursor,
+ end - cursor,
+ styleop->param,
+ state,
+ styleop->value);
+ break;
+
+ case ISTYLEOP_COLOR:
+ svgtiny_parse_color(cursor,
+ end - cursor,
+ styleop->value);
+ break;
+
+ case ISTYLEOP_LENGTH:
+ svgtiny_parse_length(cursor,
+ end - cursor,
+ *((int *)styleop->param),
+ styleop->value);
+ break;
+
+ case ISTYLEOP_INTLENGTH:
+ svgtiny_parse_length(cursor,
+ end - cursor,
+ *((int *)styleop->param),
+ &parse_len);
+ *((int *)styleop->value) = parse_len;
+ break;
+
+ default:
+ break;
+ }
+ break; /* found the operation, stop iterating */
+ }
+ }
+
+ return svgtiny_OK;
+}
+
+/**
+ * parse an inline style
+ */
+svgtiny_code
+svgtiny_parse_inline_style(const char *text,
+ size_t textlen,
+ struct svgtiny_parse_state *state,
+ struct svgtiny_parse_inline_style_op *styles)
+{
+ const char *cursor = text; /* text cursor */
+ const char *textend = text + textlen;
+ const char *declaration_start;
+
+ while (cursor < textend) {
+ advance_whitespace(&cursor, textend);
+ declaration_start = cursor;
+ while (cursor < textend) {
+ if ((*cursor == ';') &&
+ (*(cursor - 1) != '\\')) {
+ break;
+ }
+ cursor++;
+ }
+ parse_declaration(declaration_start, cursor, state, styles);
+ cursor++;/* skip semicolon */
+ }
+ return svgtiny_OK;
+}
--- /dev/null
+viewbox 0 0 1000 1000
+fill #6598fd stroke none stroke-width 10 path 'M 100 100 L 140 100 L 100 900 Z '
+fill #6497fb stroke none stroke-width 10 path 'M 100 900 L 140 100 L 140 900 Z '
+fill #6395f7 stroke none stroke-width 10 path 'M 140 100 L 180 100 L 140 900 Z '
+fill #6394f5 stroke none stroke-width 10 path 'M 140 900 L 180 100 L 180 900 Z '
+fill #6293f1 stroke none stroke-width 10 path 'M 180 100 L 220 100 L 180 900 Z '
+fill #6192ef stroke none stroke-width 10 path 'M 180 900 L 220 100 L 220 900 Z '
+fill #6090eb stroke none stroke-width 10 path 'M 220 900 L 220 100 L 260 900 Z '
+fill #5f8fe9 stroke none stroke-width 10 path 'M 220 100 L 260 100 L 260 900 Z '
+fill #5e8de5 stroke none stroke-width 10 path 'M 260 900 L 260 100 L 300 900 Z '
+fill #5e8de3 stroke none stroke-width 10 path 'M 260 100 L 300 100 L 300 900 Z '
+fill #5c8bdf stroke none stroke-width 10 path 'M 300 900 L 300 100 L 340 900 Z '
+fill #5c8add stroke none stroke-width 10 path 'M 300 100 L 340 100 L 340 900 Z '
+fill #5b88d9 stroke none stroke-width 10 path 'M 340 900 L 340 100 L 380 900 Z '
+fill #5a88d7 stroke none stroke-width 10 path 'M 340 100 L 380 100 L 380 900 Z '
+fill #5986d3 stroke none stroke-width 10 path 'M 380 900 L 380 100 L 420 900 Z '
+fill #5885d1 stroke none stroke-width 10 path 'M 380 100 L 420 100 L 420 900 Z '
+fill #5783cd stroke none stroke-width 10 path 'M 420 900 L 420 100 L 460 900 Z '
+fill #5782cb stroke none stroke-width 10 path 'M 420 100 L 460 100 L 460 900 Z '
+fill #5681c7 stroke none stroke-width 10 path 'M 460 900 L 460 100 L 500 900 Z '
+fill #5580c5 stroke none stroke-width 10 path 'M 460 100 L 500 100 L 500 900 Z '
+fill #547ec1 stroke none stroke-width 10 path 'M 500 900 L 500 100 L 540 900 Z '
+fill #537dbf stroke none stroke-width 10 path 'M 500 100 L 540 100 L 540 900 Z '
+fill #527cbb stroke none stroke-width 10 path 'M 540 900 L 540 100 L 580 900 Z '
+fill #527bb9 stroke none stroke-width 10 path 'M 540 100 L 580 100 L 580 900 Z '
+fill #5179b5 stroke none stroke-width 10 path 'M 580 900 L 580 100 L 620 900 Z '
+fill #5078b3 stroke none stroke-width 10 path 'M 580 100 L 620 100 L 620 900 Z '
+fill #4f77af stroke none stroke-width 10 path 'M 620 900 L 620 100 L 660 900 Z '
+fill #4e76ad stroke none stroke-width 10 path 'M 620 100 L 660 100 L 660 900 Z '
+fill #4d74a9 stroke none stroke-width 10 path 'M 660 900 L 660 100 L 700 900 Z '
+fill #4d73a7 stroke none stroke-width 10 path 'M 660 100 L 700 100 L 700 900 Z '
+fill #4b71a3 stroke none stroke-width 10 path 'M 700 900 L 700 100 L 740 900 Z '
+fill #4b71a1 stroke none stroke-width 10 path 'M 700 100 L 740 100 L 740 900 Z '
+fill #4a6f9d stroke none stroke-width 10 path 'M 740 900 L 740 100 L 780 900 Z '
+fill #496e9b stroke none stroke-width 10 path 'M 740 100 L 780 100 L 780 900 Z '
+fill #486c97 stroke none stroke-width 10 path 'M 780 900 L 780 100 L 820 900 Z '
+fill #476b95 stroke none stroke-width 10 path 'M 780 100 L 820 100 L 820 900 Z '
+fill #466a91 stroke none stroke-width 10 path 'M 820 900 L 820 100 L 860 900 Z '
+fill #46698f stroke none stroke-width 10 path 'M 820 100 L 860 100 L 860 900 Z '
+fill #45678b stroke none stroke-width 10 path 'M 860 900 L 860 100 L 900 900 Z '
+fill #446689 stroke none stroke-width 10 path 'M 860 100 L 900 100 L 900 900 Z '
+fill none stroke #000000 stroke-width 10 path 'M 100 100 L 900 100 L 900 900 L 100 900 Z '
<defs>
<linearGradient id="foo">
<stop stop-color="#69f" offset="0"/>
- <stop stop-color="#468" offset="1"/>
+ <stop style="stop-color:#468" offset="100%"/>
</linearGradient>
</defs>
<path fill="url(#foo)" stroke='url(#bar)' d='M10 10 H 90 V 90 H 10 Z' />