]> gitweb.michael.orlitzky.com - libsvgtiny.git/blobdiff - src/svgtiny.c
C89: Fix for gcc2 "warning: comma at end of enumerator list".
[libsvgtiny.git] / src / svgtiny.c
index ab6b6dd5ea30f282320fa33bcccd5fff437a5be3..be20b20abf1201d356291e313297bdf0bf747592 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of Libsvgtiny
  * Licensed under the MIT License,
  *                http://opensource.org/licenses/mit-license.php
- * Copyright 2008 James Bursa <james@semichrome.net>
+ * Copyright 2008-2009 James Bursa <james@semichrome.net>
  */
 
 #define _GNU_SOURCE  /* for strndup */
 #include "svgtiny.h"
 #include "svgtiny_internal.h"
 
+#ifndef M_PI
+#define M_PI           3.14159265358979323846
+#endif
+
+#define KAPPA          0.5522847498
 
 static svgtiny_code svgtiny_parse_svg(xmlNode *svg,
                struct svgtiny_parse_state state);
@@ -27,6 +32,8 @@ 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_ellipse(xmlNode *ellipse,
+               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,
@@ -183,6 +190,8 @@ svgtiny_code svgtiny_parse_svg(xmlNode *svg,
                                code = svgtiny_parse_rect(child, state);
                        else if (strcmp(name, "circle") == 0)
                                code = svgtiny_parse_circle(child, state);
+                       else if (strcmp(name, "ellipse") == 0)
+                               code = svgtiny_parse_ellipse(child, state);
                        else if (strcmp(name, "line") == 0)
                                code = svgtiny_parse_line(child, state);
                        else if (strcmp(name, "polyline") == 0)
@@ -240,7 +249,7 @@ svgtiny_code svgtiny_parse_path(xmlNode *path,
        while (*s) {
                char command[2];
                int plot_command;
-               float x, y, x1, y1, x2, y2;
+               float x, y, x1, y1, x2, y2, rx, ry, rotation, large_arc, sweep;
                int n;
 
                /* moveto (M, m), lineto (L, l) (2 arguments) */
@@ -395,14 +404,39 @@ svgtiny_code svgtiny_parse_path(xmlNode *path,
                        } while (sscanf(s, "%f %f %n",
                                        &x, &y, &n) == 2);
 
+               /* elliptical arc (A, a) (7 arguments) */
+               } else if (sscanf(s, " %1[Aa] %f %f %f %f %f %f %f %n", command,
+                               &rx, &ry, &rotation, &large_arc, &sweep,
+                               &x, &y, &n) == 8) {
+                       do {
+                               p[i++] = svgtiny_PATH_LINE;
+                               if (*command == 'a') {
+                                       x += last_x;
+                                       y += last_y;
+                               }
+                               p[i++] = last_cubic_x = last_quad_x = last_x
+                                               = x;
+                               p[i++] = last_cubic_y = last_quad_y = last_y
+                                               = y;
+                               s += n;
+                       } while (sscanf(s, "%f %f %f %f %f %f %f %n",
+                               &rx, &ry, &rotation, &large_arc, &sweep,
+                               &x, &y, &n) == 7);
+
                } else {
-                       /*LOG(("parse failed at \"%s\"", s));*/
+                       fprintf(stderr, "parse failed at \"%s\"\n", s);
                        break;
                }
        }
 
        xmlFree(path_d);
 
+       if (i <= 4) {
+               /* no real segments in path */
+               free(p);
+               return svgtiny_OK;
+       }
+
        return svgtiny_add_path(p, i, &state);
 }
 
@@ -452,8 +486,7 @@ svgtiny_code svgtiny_parse_rect(xmlNode *rect,
 svgtiny_code svgtiny_parse_circle(xmlNode *circle,
                struct svgtiny_parse_state state)
 {
-       float x = 0, y = 0, r = 0;
-       const float kappa = 0.5522847498;
+       float x = 0, y = 0, r = -1;
 
        for (xmlAttr *attr = circle->properties; attr; attr = attr->next) {
                const char *name = (const char *) attr->name;
@@ -471,40 +504,126 @@ svgtiny_code svgtiny_parse_circle(xmlNode *circle,
        svgtiny_parse_paint_attributes(circle, &state);
        svgtiny_parse_transform_attributes(circle, &state);
 
+       if (r < 0) {
+               state.diagram->error_line = circle->line;
+               state.diagram->error_message = "circle: r missing or negative";
+               return svgtiny_SVG_ERROR;
+       }
+       if (r == 0)
+               return svgtiny_OK;
+
        float *p = malloc(32 * sizeof p[0]);
        if (!p)
                return svgtiny_OUT_OF_MEMORY;
 
        p[0] = svgtiny_PATH_MOVE;
-       p[1] = x - r;
+       p[1] = x + r;
        p[2] = y;
        p[3] = svgtiny_PATH_BEZIER;
-       p[4] = x - r;
-       p[5] = y + r * kappa;
-       p[6] = x - r * kappa;
+       p[4] = x + r;
+       p[5] = y + r * KAPPA;
+       p[6] = x + r * KAPPA;
        p[7] = y + r;
        p[8] = x;
        p[9] = y + r;
        p[10] = svgtiny_PATH_BEZIER;
-       p[11] = x + r * kappa;
+       p[11] = x - r * KAPPA;
        p[12] = y + r;
-       p[13] = x + r;
-       p[14] = y + r * kappa;
-       p[15] = x + r;
+       p[13] = x - r;
+       p[14] = y + r * KAPPA;
+       p[15] = x - r;
        p[16] = y;
        p[17] = svgtiny_PATH_BEZIER;
-       p[18] = x + r;
-       p[19] = y - r * kappa;
-       p[20] = x + r * kappa;
+       p[18] = x - r;
+       p[19] = y - r * KAPPA;
+       p[20] = x - r * KAPPA;
        p[21] = y - r;
        p[22] = x;
        p[23] = y - r;
        p[24] = svgtiny_PATH_BEZIER;
-       p[25] = x - r * kappa;
+       p[25] = x + r * KAPPA;
        p[26] = y - r;
-       p[27] = x - r;
-       p[28] = y - r * kappa;
-       p[29] = x - r;
+       p[27] = x + r;
+       p[28] = y - r * KAPPA;
+       p[29] = x + r;
+       p[30] = y;
+       p[31] = svgtiny_PATH_CLOSE;
+       
+       return svgtiny_add_path(p, 32, &state);
+}
+
+
+/**
+ * Parse an <ellipse> element node.
+ */
+
+svgtiny_code svgtiny_parse_ellipse(xmlNode *ellipse,
+               struct svgtiny_parse_state state)
+{
+       float x = 0, y = 0, rx = -1, ry = -1;
+
+       for (xmlAttr *attr = ellipse->properties; attr; attr = attr->next) {
+               const char *name = (const char *) attr->name;
+               const char *content = (const char *) attr->children->content;
+               if (strcmp(name, "cx") == 0)
+                       x = svgtiny_parse_length(content,
+                                       state.viewport_width, state);
+               else if (strcmp(name, "cy") == 0)
+                       y = svgtiny_parse_length(content,
+                                       state.viewport_height, state);
+               else if (strcmp(name, "rx") == 0)
+                       rx = svgtiny_parse_length(content,
+                                       state.viewport_width, state);
+               else if (strcmp(name, "ry") == 0)
+                       ry = svgtiny_parse_length(content,
+                                       state.viewport_width, state);
+        }
+       svgtiny_parse_paint_attributes(ellipse, &state);
+       svgtiny_parse_transform_attributes(ellipse, &state);
+
+       if (rx < 0 || ry < 0) {
+               state.diagram->error_line = ellipse->line;
+               state.diagram->error_message = "ellipse: rx or ry missing "
+                               "or negative";
+               return svgtiny_SVG_ERROR;
+       }
+       if (rx == 0 || ry == 0)
+               return svgtiny_OK;
+
+       float *p = malloc(32 * sizeof p[0]);
+       if (!p)
+               return svgtiny_OUT_OF_MEMORY;
+
+       p[0] = svgtiny_PATH_MOVE;
+       p[1] = x + rx;
+       p[2] = y;
+       p[3] = svgtiny_PATH_BEZIER;
+       p[4] = x + rx;
+       p[5] = y + ry * KAPPA;
+       p[6] = x + rx * KAPPA;
+       p[7] = y + ry;
+       p[8] = x;
+       p[9] = y + ry;
+       p[10] = svgtiny_PATH_BEZIER;
+       p[11] = x - rx * KAPPA;
+       p[12] = y + ry;
+       p[13] = x - rx;
+       p[14] = y + ry * KAPPA;
+       p[15] = x - rx;
+       p[16] = y;
+       p[17] = svgtiny_PATH_BEZIER;
+       p[18] = x - rx;
+       p[19] = y - ry * KAPPA;
+       p[20] = x - rx * KAPPA;
+       p[21] = y - ry;
+       p[22] = x;
+       p[23] = y - ry;
+       p[24] = svgtiny_PATH_BEZIER;
+       p[25] = x + rx * KAPPA;
+       p[26] = y - ry;
+       p[27] = x + rx;
+       p[28] = y - ry * KAPPA;
+       p[29] = x + rx;
        p[30] = y;
        p[31] = svgtiny_PATH_CLOSE;
        
@@ -709,6 +828,8 @@ float svgtiny_parse_length(const char *s, int viewport_size,
        float n = atof((const char *) s);
        float font_size = 20; /*css_len2px(&state.style.font_size.value.length, 0);*/
 
+       UNUSED(state);
+
        if (unit[0] == 0) {
                return n;
        } else if (unit[0] == '%') {
@@ -807,7 +928,7 @@ void svgtiny_parse_color(const char *s, svgtiny_colour *c,
 
        } else if (10 <= len && s[0] == 'r' && s[1] == 'g' && s[2] == 'b' &&
                        s[3] == '(' && s[len - 1] == ')') {
-               if (sscanf(s + 4, "%i,%i,%i", &r, &g, &b) == 3)
+               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;
@@ -856,6 +977,8 @@ void svgtiny_parse_color(const char *s, svgtiny_colour *c,
 void svgtiny_parse_font_attributes(const xmlNode *node,
                struct svgtiny_parse_state *state)
 {
+       UNUSED(state);
+
        for (const xmlAttr *attr = node->properties; attr; attr = attr->next) {
                if (strcmp((const char *) attr->name, "font-size") == 0) {
                        /*if (css_parse_length(
@@ -1014,8 +1137,10 @@ 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 = lroundf((float) state->stroke_width *
+                       (state->ctm.a + state->ctm.d) / 2.0);
+       if (0 < state->stroke_width && shape->stroke_width == 0)
+               shape->stroke_width = 1;
 
        return shape;
 }