]> gitweb.michael.orlitzky.com - libsvgtiny.git/blobdiff - svgtiny.c
Add scale argument to svgtiny_test and svgtiny_display.
[libsvgtiny.git] / svgtiny.c
index 2847af4f84c4d8222f3dfff8e50424e52eec6738..10635ac933020e894a7507eb2d2b42ed0508610c 100644 (file)
--- a/svgtiny.c
+++ b/svgtiny.c
 #include <libxml/parser.h>
 #include <libxml/debugXML.h>
 #include "svgtiny.h"
+#include "svgtiny_internal.h"
 
 
-struct svgtiny_parse_state {
-       struct svgtiny_diagram *diagram;
-       xmlDoc *document;
-
-       float viewport_width;
-       float viewport_height;
-
-       /* current transformation matrix */
-       struct {
-               float a, b, c, d, e, f;
-       } ctm;
-
-       /*struct css_style style;*/
-
-       /* paint attributes */
-       svgtiny_colour fill;
-       svgtiny_colour stroke;
-       int stroke_width;
-};
-
-
-static bool svgtiny_parse_svg(xmlNode *svg, struct svgtiny_parse_state state);
-static bool svgtiny_parse_path(xmlNode *path, struct svgtiny_parse_state state);
-static bool svgtiny_parse_rect(xmlNode *rect, struct svgtiny_parse_state state);
-static bool svgtiny_parse_circle(xmlNode *circle,
+static svgtiny_code svgtiny_parse_svg(xmlNode *svg,
+               struct svgtiny_parse_state state);
+static svgtiny_code svgtiny_parse_path(xmlNode *path,
+               struct svgtiny_parse_state state);
+static svgtiny_code svgtiny_parse_rect(xmlNode *rect,
+               struct svgtiny_parse_state state);
+static svgtiny_code svgtiny_parse_circle(xmlNode *circle,
+               struct svgtiny_parse_state state);
+static svgtiny_code svgtiny_parse_line(xmlNode *line,
+               struct svgtiny_parse_state state);
+static svgtiny_code svgtiny_parse_poly(xmlNode *poly,
+               struct svgtiny_parse_state state, bool polygon);
+static svgtiny_code svgtiny_parse_text(xmlNode *text,
                struct svgtiny_parse_state state);
-static bool svgtiny_parse_line(xmlNode *line, struct svgtiny_parse_state state);
-static bool svgtiny_parse_poly(xmlNode *poly, struct svgtiny_parse_state state,
-               bool polygon);
-static bool svgtiny_parse_text(xmlNode *text, struct svgtiny_parse_state state);
 static void svgtiny_parse_position_attributes(const xmlNode *node,
                const struct svgtiny_parse_state state,
                float *x, float *y, float *width, float *height);
-static float svgtiny_parse_length(const char *s, int viewport_size,
-               const struct svgtiny_parse_state state);
 static void svgtiny_parse_paint_attributes(const xmlNode *node,
                struct svgtiny_parse_state *state);
-static void svgtiny_parse_color(const char *s, svgtiny_colour *c,
-               struct svgtiny_parse_state *state);
 static void svgtiny_parse_font_attributes(const xmlNode *node,
                struct svgtiny_parse_state *state);
 static void svgtiny_parse_transform_attributes(xmlNode *node,
                struct svgtiny_parse_state *state);
-static struct svgtiny_shape *svgtiny_add_shape(
-               struct svgtiny_parse_state *state);
-static void svgtiny_transform_path(float *p, unsigned int n,
+static svgtiny_code svgtiny_add_path(float *p, unsigned int n,
                struct svgtiny_parse_state *state);
 
 
+/**
+ * Create a new svgtiny_diagram structure.
+ */
+
 struct svgtiny_diagram *svgtiny_create(void)
 {
        struct svgtiny_diagram *diagram;
@@ -77,11 +60,17 @@ struct svgtiny_diagram *svgtiny_create(void)
 
        diagram->shape = 0;
        diagram->shape_count = 0;
+       diagram->error_line = 0;
+       diagram->error_message = 0;
 
        return diagram;
 }
 
 
+/**
+ * Parse a block of memory into a svgtiny_diagram.
+ */
+
 svgtiny_code svgtiny_parse(struct svgtiny_diagram *diagram,
                const char *buffer, size_t size, const char *url,
                int viewport_width, int viewport_height)
@@ -89,6 +78,8 @@ svgtiny_code svgtiny_parse(struct svgtiny_diagram *diagram,
        xmlDoc *document;
        xmlNode *svg;
        struct svgtiny_parse_state state;
+       float x, y, width, height;
+       svgtiny_code code;
 
        assert(diagram);
        assert(buffer);
@@ -96,8 +87,7 @@ svgtiny_code svgtiny_parse(struct svgtiny_diagram *diagram,
 
        /* parse XML to tree */
        document = xmlReadMemory(buffer, size, url, 0,
-                       XML_PARSE_NONET | XML_PARSE_COMPACT |
-                       XML_PARSE_DTDVALID /* needed for xmlGetID to work */);
+                       XML_PARSE_NONET | XML_PARSE_COMPACT);
        if (!document)
                return svgtiny_LIBXML_ERROR;
 
@@ -111,7 +101,6 @@ svgtiny_code svgtiny_parse(struct svgtiny_diagram *diagram,
                return svgtiny_NOT_SVG;
 
        /* get graphic dimensions */
-       float x, y, width, height;
        state.diagram = diagram;
        state.document = document;
        state.viewport_width = viewport_width;
@@ -120,6 +109,7 @@ svgtiny_code svgtiny_parse(struct svgtiny_diagram *diagram,
        diagram->width = width;
        diagram->height = height;
 
+       /* set up parsing state */
        state.viewport_width = width;
        state.viewport_height = height;
        state.ctm.a = 1; /*(float) viewport_width / (float) width;*/
@@ -133,12 +123,15 @@ svgtiny_code svgtiny_parse(struct svgtiny_diagram *diagram,
        state.fill = 0x000000;
        state.stroke = svgtiny_TRANSPARENT;
        state.stroke_width = 1;
+       state.linear_gradient_stop_count = 0;
 
-       svgtiny_parse_svg(svg, state);
+       /* parse tree */
+       code = svgtiny_parse_svg(svg, state);
 
+       /* free XML tree */
        xmlFreeDoc(document);
 
-       return svgtiny_OK;
+       return code;
 }
 
 
@@ -146,7 +139,8 @@ svgtiny_code svgtiny_parse(struct svgtiny_diagram *diagram,
  * Parse a <svg> or <g> element node.
  */
 
-bool svgtiny_parse_svg(xmlNode *svg, struct svgtiny_parse_state state)
+svgtiny_code svgtiny_parse_svg(xmlNode *svg,
+               struct svgtiny_parse_state state)
 {
        float x, y, width, height;
 
@@ -173,47 +167,49 @@ bool svgtiny_parse_svg(xmlNode *svg, struct svgtiny_parse_state state)
        svgtiny_parse_transform_attributes(svg, &state);
 
        for (xmlNode *child = svg->children; child; child = child->next) {
-               bool ok = true;
+               svgtiny_code code = svgtiny_OK;
 
                if (child->type == XML_ELEMENT_NODE) {
                        const char *name = (const char *) child->name;
                        if (strcmp(name, "svg") == 0)
-                               ok = svgtiny_parse_svg(child, state);
+                               code = svgtiny_parse_svg(child, state);
                        else if (strcmp(name, "g") == 0)
-                               ok = svgtiny_parse_svg(child, state);
+                               code = svgtiny_parse_svg(child, state);
                        else if (strcmp(name, "a") == 0)
-                               ok = svgtiny_parse_svg(child, state);
+                               code = svgtiny_parse_svg(child, state);
                        else if (strcmp(name, "path") == 0)
-                               ok = svgtiny_parse_path(child, state);
+                               code = svgtiny_parse_path(child, state);
                        else if (strcmp(name, "rect") == 0)
-                               ok = svgtiny_parse_rect(child, state);
+                               code = svgtiny_parse_rect(child, state);
                        else if (strcmp(name, "circle") == 0)
-                               ok = svgtiny_parse_circle(child, state);
+                               code = svgtiny_parse_circle(child, state);
                        else if (strcmp(name, "line") == 0)
-                               ok = svgtiny_parse_line(child, state);
+                               code = svgtiny_parse_line(child, state);
                        else if (strcmp(name, "polyline") == 0)
-                               ok = svgtiny_parse_poly(child, state, false);
+                               code = svgtiny_parse_poly(child, state, false);
                        else if (strcmp(name, "polygon") == 0)
-                               ok = svgtiny_parse_poly(child, state, true);
+                               code = svgtiny_parse_poly(child, state, true);
                        else if (strcmp(name, "text") == 0)
-                               ok = svgtiny_parse_text(child, state);
+                               code = svgtiny_parse_text(child, state);
                }
 
-               if (!ok)
-                       return false;
+               if (code != svgtiny_OK)
+                       return code;
        }
 
-       return true;
+       return svgtiny_OK;
 }
 
 
+
 /**
  * Parse a <path> element node.
  *
  * http://www.w3.org/TR/SVG11/paths#PathElement
  */
 
-bool svgtiny_parse_path(xmlNode *path, struct svgtiny_parse_state state)
+svgtiny_code svgtiny_parse_path(xmlNode *path,
+               struct svgtiny_parse_state state)
 {
        char *s, *path_d;
 
@@ -223,16 +219,15 @@ bool svgtiny_parse_path(xmlNode *path, struct svgtiny_parse_state state)
        /* read d attribute */
        s = path_d = (char *) xmlGetProp(path, (const xmlChar *) "d");
        if (!s) {
-               /*LOG(("path missing d attribute"));*/
-               return false;
+               state.diagram->error_line = path->line;
+               state.diagram->error_message = "path: missing d attribute";
+               return svgtiny_SVG_ERROR;
        }
 
        /* allocate space for path: it will never have more elements than d */
        float *p = malloc(sizeof p[0] * strlen(s));
-       if (!p) {
-               /*LOG(("out of memory"));*/
-               return false;
-       }
+       if (!p)
+               return svgtiny_OUT_OF_MEMORY;
 
        /* parse d and build path */
        for (unsigned int i = 0; s[i]; i++)
@@ -408,18 +403,7 @@ bool svgtiny_parse_path(xmlNode *path, struct svgtiny_parse_state state)
 
        xmlFree(path_d);
 
-       svgtiny_transform_path(p, i, &state);
-
-       struct svgtiny_shape *shape = svgtiny_add_shape(&state);
-       if (!shape) {
-               free(p);
-               return false;
-       }
-       shape->path = p;
-       shape->path_length = i;
-       state.diagram->shape_count++;
-
-       return true;
+       return svgtiny_add_path(p, i, &state);
 }
 
 
@@ -429,7 +413,8 @@ bool svgtiny_parse_path(xmlNode *path, struct svgtiny_parse_state state)
  * http://www.w3.org/TR/SVG11/shapes#RectElement
  */
 
-bool svgtiny_parse_rect(xmlNode *rect, struct svgtiny_parse_state state)
+svgtiny_code svgtiny_parse_rect(xmlNode *rect,
+               struct svgtiny_parse_state state)
 {
        float x, y, width, height;
 
@@ -440,7 +425,7 @@ bool svgtiny_parse_rect(xmlNode *rect, struct svgtiny_parse_state state)
 
        float *p = malloc(13 * sizeof p[0]);
        if (!p)
-               return false;
+               return svgtiny_OUT_OF_MEMORY;
 
        p[0] = svgtiny_PATH_MOVE;
        p[1] = x;
@@ -456,18 +441,7 @@ bool svgtiny_parse_rect(xmlNode *rect, struct svgtiny_parse_state state)
        p[11] = y + height;
        p[12] = svgtiny_PATH_CLOSE;
 
-       svgtiny_transform_path(p, 13, &state);
-
-       struct svgtiny_shape *shape = svgtiny_add_shape(&state);
-       if (!shape) {
-               free(p);
-               return false;
-       }
-       shape->path = p;
-       shape->path_length = 13;
-       state.diagram->shape_count++;
-
-       return true;
+       return svgtiny_add_path(p, 13, &state);
 }
 
 
@@ -475,7 +449,8 @@ bool svgtiny_parse_rect(xmlNode *rect, struct svgtiny_parse_state state)
  * Parse a <circle> element node.
  */
 
-bool svgtiny_parse_circle(xmlNode *circle, struct svgtiny_parse_state state)
+svgtiny_code svgtiny_parse_circle(xmlNode *circle,
+               struct svgtiny_parse_state state)
 {
        float x = 0, y = 0, r = 0;
        const float kappa = 0.5522847498;
@@ -498,7 +473,7 @@ bool svgtiny_parse_circle(xmlNode *circle, struct svgtiny_parse_state state)
 
        float *p = malloc(32 * sizeof p[0]);
        if (!p)
-               return false;
+               return svgtiny_OUT_OF_MEMORY;
 
        p[0] = svgtiny_PATH_MOVE;
        p[1] = x - r;
@@ -533,18 +508,7 @@ bool svgtiny_parse_circle(xmlNode *circle, struct svgtiny_parse_state state)
        p[30] = y;
        p[31] = svgtiny_PATH_CLOSE;
        
-       svgtiny_transform_path(p, 32, &state);
-
-       struct svgtiny_shape *shape = svgtiny_add_shape(&state);
-       if (!shape) {
-               free(p);
-               return false;
-       }
-       shape->path = p;
-       shape->path_length = 32;
-       state.diagram->shape_count++;
-
-       return true;
+       return svgtiny_add_path(p, 32, &state);
 }
 
 
@@ -552,7 +516,8 @@ bool svgtiny_parse_circle(xmlNode *circle, struct svgtiny_parse_state state)
  * Parse a <line> element node.
  */
 
-bool svgtiny_parse_line(xmlNode *line, struct svgtiny_parse_state state)
+svgtiny_code svgtiny_parse_line(xmlNode *line,
+               struct svgtiny_parse_state state)
 {
        float x1 = 0, y1 = 0, x2 = 0, y2 = 0;
 
@@ -577,7 +542,7 @@ bool svgtiny_parse_line(xmlNode *line, struct svgtiny_parse_state state)
 
        float *p = malloc(7 * sizeof p[0]);
        if (!p)
-               return false;
+               return svgtiny_OUT_OF_MEMORY;
 
        p[0] = svgtiny_PATH_MOVE;
        p[1] = x1;
@@ -587,18 +552,7 @@ bool svgtiny_parse_line(xmlNode *line, struct svgtiny_parse_state state)
        p[5] = y2;
        p[6] = svgtiny_PATH_CLOSE;
 
-       svgtiny_transform_path(p, 7, &state);
-
-       struct svgtiny_shape *shape = svgtiny_add_shape(&state);
-       if (!shape) {
-               free(p);
-               return false;
-       }
-       shape->path = p;
-       shape->path_length = 7;
-       state.diagram->shape_count++;
-
-       return true;
+       return svgtiny_add_path(p, 7, &state);
 }
 
 
@@ -609,26 +563,28 @@ bool svgtiny_parse_line(xmlNode *line, struct svgtiny_parse_state state)
  * http://www.w3.org/TR/SVG11/shapes#PolygonElement
  */
 
-bool svgtiny_parse_poly(xmlNode *poly, struct svgtiny_parse_state state,
-               bool polygon)
+svgtiny_code svgtiny_parse_poly(xmlNode *poly,
+               struct svgtiny_parse_state state, bool polygon)
 {
        char *s, *points;
 
        svgtiny_parse_paint_attributes(poly, &state);
        svgtiny_parse_transform_attributes(poly, &state);
 
-       /* read d attribute */
+       /* read points attribute */
        s = points = (char *) xmlGetProp(poly, (const xmlChar *) "points");
        if (!s) {
-               /*LOG(("poly missing d attribute"));*/
-               return false;
+               state.diagram->error_line = poly->line;
+               state.diagram->error_message =
+                               "polyline/polygon: missing points attribute";
+               return svgtiny_SVG_ERROR;
        }
 
        /* allocate space for path: it will never have more elements than s */
        float *p = malloc(sizeof p[0] * strlen(s));
        if (!p) {
-               /*LOG(("out of memory"));*/
-               return false;
+               xmlFree(points);
+               return svgtiny_OUT_OF_MEMORY;
        }
 
        /* parse s and build path */
@@ -657,18 +613,7 @@ bool svgtiny_parse_poly(xmlNode *poly, struct svgtiny_parse_state state,
 
        xmlFree(points);
 
-       svgtiny_transform_path(p, i, &state);
-
-       struct svgtiny_shape *shape = svgtiny_add_shape(&state);
-       if (!shape) {
-               free(p);
-               return false;
-       }
-       shape->path = p;
-       shape->path_length = i;
-       state.diagram->shape_count++;
-
-       return true;
+       return svgtiny_add_path(p, i, &state);
 }
 
 
@@ -676,7 +621,8 @@ bool svgtiny_parse_poly(xmlNode *poly, struct svgtiny_parse_state state,
  * Parse a <text> or <tspan> element node.
  */
 
-bool svgtiny_parse_text(xmlNode *text, struct svgtiny_parse_state state)
+svgtiny_code svgtiny_parse_text(xmlNode *text,
+               struct svgtiny_parse_state state)
 {
        float x, y, width, height;
 
@@ -694,12 +640,12 @@ bool svgtiny_parse_text(xmlNode *text, struct svgtiny_parse_state state)
        style.font_size.value.length.value *= state.ctm.a;*/
 
        for (xmlNode *child = text->children; child; child = child->next) {
-               bool ok = true;
+               svgtiny_code code = svgtiny_OK;
 
                if (child->type == XML_TEXT_NODE) {
                        struct svgtiny_shape *shape = svgtiny_add_shape(&state);
                        if (!shape)
-                               return false;
+                               return svgtiny_OUT_OF_MEMORY;
                        shape->text = strdup((const char *) child->content);
                        shape->text_x = px;
                        shape->text_y = py;
@@ -708,14 +654,14 @@ bool svgtiny_parse_text(xmlNode *text, struct svgtiny_parse_state state)
                } else if (child->type == XML_ELEMENT_NODE &&
                                strcmp((const char *) child->name,
                                        "tspan") == 0) {
-                       ok = svgtiny_parse_text(child, state);
+                       code = svgtiny_parse_text(child, state);
                }
 
-               if (!ok)
-                       return false;
+               if (!code != svgtiny_OK)
+                       return code;
        }
 
-       return true;
+       return svgtiny_OK;
 }
 
 
@@ -850,7 +796,6 @@ void svgtiny_parse_color(const char *s, svgtiny_colour *c,
        float rf, gf, bf;
        size_t len = strlen(s);
        char *id = 0, *rparen;
-       xmlAttr *id_attr;
 
        if (len == 4 && s[0] == '#') {
                if (sscanf(s + 1, "%1x%1x%1x", &r, &g, &b) == 3)
@@ -883,15 +828,16 @@ void svgtiny_parse_color(const char *s, svgtiny_colour *c,
                        rparen = strchr(id, ')');
                        if (rparen)
                                *rparen = 0;
-                       id_attr = xmlGetID(state->document,
-                                       (const xmlChar *) id);
-                       if (!id_attr) {
-                               fprintf(stderr, "id \"%s\" not found\n", id);
-                               free(id);
-                               return;
-                       }
-                       fprintf(stderr, "id \"%s\" at %p\n", id, id_attr);
+                       svgtiny_find_gradient(id, state);
                        free(id);
+                       fprintf(stderr, "linear_gradient_stop_count %i\n",
+                                       state->linear_gradient_stop_count);
+                       if (state->linear_gradient_stop_count == 0)
+                               *c = svgtiny_TRANSPARENT;
+                       else if (state->linear_gradient_stop_count == 1)
+                               *c = state->gradient_stop[0].color;
+                       else
+                               *c = svgtiny_LINEAR_GRADIENT;
                }
 
        } else {
@@ -933,84 +879,119 @@ void svgtiny_parse_font_attributes(const xmlNode *node,
 void svgtiny_parse_transform_attributes(xmlNode *node,
                struct svgtiny_parse_state *state)
 {
-       char *transform, *s;
+       char *transform;
+
+       /* parse transform */
+       transform = (char *) xmlGetProp(node, (const xmlChar *) "transform");
+       if (transform) {
+               svgtiny_parse_transform(transform, &state->ctm.a, &state->ctm.b,
+                               &state->ctm.c, &state->ctm.d,
+                               &state->ctm.e, &state->ctm.f);
+               xmlFree(transform);
+       }
+}
+
+
+/**
+ * Parse a transform string.
+ */
+
+void svgtiny_parse_transform(char *s, float *ma, float *mb,
+               float *mc, float *md, float *me, float *mf)
+{
        float a, b, c, d, e, f;
-       float ctm_a, ctm_b, ctm_c, ctm_d, ctm_e, ctm_f;
+       float za, zb, zc, zd, ze, zf;
        float angle, x, y;
        int n;
 
-       /* parse transform */
-       s = transform = (char *) xmlGetProp(node,
-                       (const xmlChar *) "transform");
-       if (transform) {
-               for (unsigned int i = 0; transform[i]; i++)
-                       if (transform[i] == ',')
-                               transform[i] = ' ';
-
-               while (*s) {
-                       a = d = 1;
-                       b = c = 0;
-                       e = f = 0;
-                       if (sscanf(s, "matrix (%f %f %f %f %f %f) %n",
+       for (unsigned int i = 0; s[i]; i++)
+               if (s[i] == ',')
+                       s[i] = ' ';
+
+       while (*s) {
+               a = d = 1;
+               b = c = 0;
+               e = f = 0;
+               if (sscanf(s, "matrix (%f %f %f %f %f %f) %n",
                                        &a, &b, &c, &d, &e, &f, &n) == 6)
-                               ;
-                       else if (sscanf(s, "translate (%f %f) %n",
+                       ;
+               else if (sscanf(s, "translate (%f %f) %n",
                                        &e, &f, &n) == 2)
-                               ;
-                       else if (sscanf(s, "translate (%f) %n",
+                       ;
+               else if (sscanf(s, "translate (%f) %n",
                                        &e, &n) == 1)
-                               ;
-                       else if (sscanf(s, "scale (%f %f) %n",
+                       ;
+               else if (sscanf(s, "scale (%f %f) %n",
                                        &a, &d, &n) == 2)
-                               ;
-                       else if (sscanf(s, "scale (%f) %n",
+                       ;
+               else if (sscanf(s, "scale (%f) %n",
                                        &a, &n) == 1)
-                               d = a;
-                       else if (sscanf(s, "rotate (%f %f %f) %n",
+                       d = a;
+               else if (sscanf(s, "rotate (%f %f %f) %n",
                                        &angle, &x, &y, &n) == 3) {
-                               angle = angle / 180 * M_PI;
-                               a = cos(angle);
-                               b = sin(angle);
-                               c = -sin(angle);
-                               d = cos(angle);
-                               e = -x * cos(angle) + y * sin(angle) + x;
-                               f = -x * sin(angle) - y * cos(angle) + y;
-                       } else if (sscanf(s, "rotate (%f) %n",
+                       angle = angle / 180 * M_PI;
+                       a = cos(angle);
+                       b = sin(angle);
+                       c = -sin(angle);
+                       d = cos(angle);
+                       e = -x * cos(angle) + y * sin(angle) + x;
+                       f = -x * sin(angle) - y * cos(angle) + y;
+               } else if (sscanf(s, "rotate (%f) %n",
                                        &angle, &n) == 1) {
-                               angle = angle / 180 * M_PI;
-                               a = cos(angle);
-                               b = sin(angle);
-                               c = -sin(angle);
-                               d = cos(angle);
-                       } else if (sscanf(s, "skewX (%f) %n",
+                       angle = angle / 180 * M_PI;
+                       a = cos(angle);
+                       b = sin(angle);
+                       c = -sin(angle);
+                       d = cos(angle);
+               } else if (sscanf(s, "skewX (%f) %n",
                                        &angle, &n) == 1) {
-                               angle = angle / 180 * M_PI;
-                               c = tan(angle);
-                       } else if (sscanf(s, "skewY (%f) %n",
+                       angle = angle / 180 * M_PI;
+                       c = tan(angle);
+               } else if (sscanf(s, "skewY (%f) %n",
                                        &angle, &n) == 1) {
-                               angle = angle / 180 * M_PI;
-                               b = tan(angle);
-                       } else
-                               break;
-                       ctm_a = state->ctm.a * a + state->ctm.c * b;
-                       ctm_b = state->ctm.b * a + state->ctm.d * b;
-                       ctm_c = state->ctm.a * c + state->ctm.c * d;
-                       ctm_d = state->ctm.b * c + state->ctm.d * d;
-                       ctm_e = state->ctm.a * e + state->ctm.c * f +
-                                       state->ctm.e;
-                       ctm_f = state->ctm.b * e + state->ctm.d * f +
-                                       state->ctm.f;
-                       state->ctm.a = ctm_a;
-                       state->ctm.b = ctm_b;
-                       state->ctm.c = ctm_c;
-                       state->ctm.d = ctm_d;
-                       state->ctm.e = ctm_e;
-                       state->ctm.f = ctm_f;
-                       s += n;
-               }
+                       angle = angle / 180 * M_PI;
+                       b = tan(angle);
+               } else
+                       break;
+               za = *ma * a + *mc * b;
+               zb = *mb * a + *md * b;
+               zc = *ma * c + *mc * d;
+               zd = *mb * c + *md * d;
+               ze = *ma * e + *mc * f + *me;
+               zf = *mb * e + *md * f + *mf;
+               *ma = za;
+               *mb = zb;
+               *mc = zc;
+               *md = zd;
+               *me = ze;
+               *mf = zf;
+               s += n;
+       }
+}
 
-               xmlFree(transform);
+
+/**
+ * Add a path to the svgtiny_diagram.
+ */
+
+svgtiny_code svgtiny_add_path(float *p, unsigned int n,
+               struct svgtiny_parse_state *state)
+{
+       if (state->fill == svgtiny_LINEAR_GRADIENT)
+               return svgtiny_add_path_linear_gradient(p, n, state);
+
+       svgtiny_transform_path(p, n, state);
+
+       struct svgtiny_shape *shape = svgtiny_add_shape(state);
+       if (!shape) {
+               free(p);
+               return svgtiny_OUT_OF_MEMORY;
        }
+       shape->path = p;
+       shape->path_length = n;
+       state->diagram->shape_count++;
+
+       return svgtiny_OK;
 }
 
 
@@ -1033,13 +1014,17 @@ struct svgtiny_shape *svgtiny_add_shape(struct svgtiny_parse_state *state)
        shape->text = 0;
        shape->fill = state->fill;
        shape->stroke = state->stroke;
-       shape->stroke_width = state->stroke_width *
-                       (state->ctm.a + state->ctm.d) / 2;
+       shape->stroke_width = 1; /*state->stroke_width *
+                       (state->ctm.a + state->ctm.d) / 2;*/
 
        return shape;
 }
 
 
+/**
+ * Apply the current transformation matrix to a path.
+ */
+
 void svgtiny_transform_path(float *p, unsigned int n,
                struct svgtiny_parse_state *state)
 {
@@ -1074,6 +1059,10 @@ void svgtiny_transform_path(float *p, unsigned int n,
 }
 
 
+/**
+ * Free all memory used by a diagram.
+ */
+
 void svgtiny_free(struct svgtiny_diagram *svg)
 {
        assert(svg);