X-Git-Url: http://gitweb.michael.orlitzky.com/?a=blobdiff_plain;f=src%2Fsvgtiny.c;h=8fa09a5bdd6e66ed2cdbdcb01d690af3f277e0f3;hb=b9b4c3c90eea9b00da17d0cbdce195d715fdb01a;hp=56f0028ccecc315ec3211f7dc275907a5adc4374;hpb=1e4fbffc36f3c7dc959936e3049b0ad8d5dde4d1;p=libsvgtiny.git diff --git a/src/svgtiny.c b/src/svgtiny.c index 56f0028..8fa09a5 100644 --- a/src/svgtiny.c +++ b/src/svgtiny.c @@ -2,10 +2,9 @@ * This file is part of Libsvgtiny * Licensed under the MIT License, * http://opensource.org/licenses/mit-license.php - * Copyright 2008 James Bursa + * Copyright 2008-2009 James Bursa */ -#define _GNU_SOURCE /* for strndup */ #include #include #include @@ -18,6 +17,11 @@ #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 +31,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, @@ -143,13 +149,15 @@ svgtiny_code svgtiny_parse_svg(xmlNode *svg, struct svgtiny_parse_state state) { float x, y, width, height; + xmlAttr *view_box; + xmlNode *child; svgtiny_parse_position_attributes(svg, state, &x, &y, &width, &height); svgtiny_parse_paint_attributes(svg, &state); svgtiny_parse_font_attributes(svg, &state); /* parse viewBox */ - xmlAttr *view_box = xmlHasProp(svg, (const xmlChar *) "viewBox"); + view_box = xmlHasProp(svg, (const xmlChar *) "viewBox"); if (view_box) { const char *s = (const char *) view_box->children->content; float min_x, min_y, vwidth, vheight; @@ -166,7 +174,7 @@ svgtiny_code svgtiny_parse_svg(xmlNode *svg, svgtiny_parse_transform_attributes(svg, &state); - for (xmlNode *child = svg->children; child; child = child->next) { + for (child = svg->children; child; child = child->next) { svgtiny_code code = svgtiny_OK; if (child->type == XML_ELEMENT_NODE) { @@ -183,6 +191,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) @@ -212,6 +222,11 @@ svgtiny_code svgtiny_parse_path(xmlNode *path, struct svgtiny_parse_state state) { char *s, *path_d; + float *p; + unsigned int i; + float last_x = 0, last_y = 0; + float last_cubic_x = 0, last_cubic_y = 0; + float last_quad_x = 0, last_quad_y = 0; svgtiny_parse_paint_attributes(path, &state); svgtiny_parse_transform_attributes(path, &state); @@ -225,22 +240,19 @@ svgtiny_code svgtiny_parse_path(xmlNode *path, } /* allocate space for path: it will never have more elements than d */ - float *p = malloc(sizeof p[0] * strlen(s)); + p = malloc(sizeof p[0] * strlen(s)); if (!p) return svgtiny_OUT_OF_MEMORY; /* parse d and build path */ - for (unsigned int i = 0; s[i]; i++) + for (i = 0; s[i]; i++) if (s[i] == ',') s[i] = ' '; - unsigned int i = 0; - float last_x = 0, last_y = 0; - float last_cubic_x = 0, last_cubic_y = 0; - float last_quad_x = 0, last_quad_y = 0; + i = 0; 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 +407,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); } @@ -417,13 +454,14 @@ svgtiny_code svgtiny_parse_rect(xmlNode *rect, struct svgtiny_parse_state state) { float x, y, width, height; + float *p; svgtiny_parse_position_attributes(rect, state, &x, &y, &width, &height); svgtiny_parse_paint_attributes(rect, &state); svgtiny_parse_transform_attributes(rect, &state); - float *p = malloc(13 * sizeof p[0]); + p = malloc(13 * sizeof p[0]); if (!p) return svgtiny_OUT_OF_MEMORY; @@ -452,10 +490,11 @@ 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; + float *p; + xmlAttr *attr; - for (xmlAttr *attr = circle->properties; attr; attr = attr->next) { + for (attr = circle->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) @@ -471,40 +510,128 @@ svgtiny_code svgtiny_parse_circle(xmlNode *circle, svgtiny_parse_paint_attributes(circle, &state); svgtiny_parse_transform_attributes(circle, &state); - float *p = malloc(32 * sizeof p[0]); + 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; + + 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 element node. + */ + +svgtiny_code svgtiny_parse_ellipse(xmlNode *ellipse, + struct svgtiny_parse_state state) +{ + float x = 0, y = 0, rx = -1, ry = -1; + float *p; + xmlAttr *attr; + + for (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; + + 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; @@ -520,8 +647,10 @@ svgtiny_code svgtiny_parse_line(xmlNode *line, struct svgtiny_parse_state state) { float x1 = 0, y1 = 0, x2 = 0, y2 = 0; + float *p; + xmlAttr *attr; - for (xmlAttr *attr = line->properties; attr; attr = attr->next) { + for (attr = line->properties; attr; attr = attr->next) { const char *name = (const char *) attr->name; const char *content = (const char *) attr->children->content; if (strcmp(name, "x1") == 0) @@ -540,7 +669,7 @@ svgtiny_code svgtiny_parse_line(xmlNode *line, svgtiny_parse_paint_attributes(line, &state); svgtiny_parse_transform_attributes(line, &state); - float *p = malloc(7 * sizeof p[0]); + p = malloc(7 * sizeof p[0]); if (!p) return svgtiny_OUT_OF_MEMORY; @@ -567,6 +696,8 @@ svgtiny_code svgtiny_parse_poly(xmlNode *poly, struct svgtiny_parse_state state, bool polygon) { char *s, *points; + float *p; + unsigned int i; svgtiny_parse_paint_attributes(poly, &state); svgtiny_parse_transform_attributes(poly, &state); @@ -581,17 +712,17 @@ svgtiny_code svgtiny_parse_poly(xmlNode *poly, } /* allocate space for path: it will never have more elements than s */ - float *p = malloc(sizeof p[0] * strlen(s)); + p = malloc(sizeof p[0] * strlen(s)); if (!p) { xmlFree(points); return svgtiny_OUT_OF_MEMORY; } /* parse s and build path */ - for (unsigned int i = 0; s[i]; i++) + for (i = 0; s[i]; i++) if (s[i] == ',') s[i] = ' '; - unsigned int i = 0; + i = 0; while (*s) { float x, y; int n; @@ -625,21 +756,23 @@ svgtiny_code svgtiny_parse_text(xmlNode *text, struct svgtiny_parse_state state) { float x, y, width, height; + float px, py; + xmlNode *child; svgtiny_parse_position_attributes(text, state, &x, &y, &width, &height); svgtiny_parse_font_attributes(text, &state); svgtiny_parse_transform_attributes(text, &state); - float px = state.ctm.a * x + state.ctm.c * y + state.ctm.e; - float py = state.ctm.b * x + state.ctm.d * y + state.ctm.f; + px = state.ctm.a * x + state.ctm.c * y + state.ctm.e; + py = state.ctm.b * x + state.ctm.d * y + state.ctm.f; /* state.ctm.e = px - state.origin_x; */ /* state.ctm.f = py - state.origin_y; */ /*struct css_style style = state.style; style.font_size.value.length.value *= state.ctm.a;*/ - for (xmlNode *child = text->children; child; child = child->next) { + for (child = text->children; child; child = child->next) { svgtiny_code code = svgtiny_OK; if (child->type == XML_TEXT_NODE) { @@ -673,12 +806,14 @@ void svgtiny_parse_position_attributes(const xmlNode *node, const struct svgtiny_parse_state state, float *x, float *y, float *width, float *height) { + xmlAttr *attr; + *x = 0; *y = 0; *width = state.viewport_width; *height = state.viewport_height; - for (xmlAttr *attr = node->properties; attr; attr = attr->next) { + for (attr = node->properties; attr; attr = attr->next) { const char *name = (const char *) attr->name; const char *content = (const char *) attr->children->content; if (strcmp(name, "x") == 0) @@ -744,7 +879,9 @@ float svgtiny_parse_length(const char *s, int viewport_size, void svgtiny_parse_paint_attributes(const xmlNode *node, struct svgtiny_parse_state *state) { - for (const xmlAttr *attr = node->properties; attr; attr = attr->next) { + const xmlAttr *attr; + + for (attr = node->properties; attr; attr = attr->next) { const char *name = (const char *) attr->name; const char *content = (const char *) attr->children->content; if (strcmp(name, "fill") == 0) @@ -779,8 +916,10 @@ void svgtiny_parse_paint_attributes(const xmlNode *node, s += 13; while (*s == ' ') s++; - state->stroke_width = svgtiny_parse_length(s, + value = strndup(s, strcspn(s, "; ")); + state->stroke_width = svgtiny_parse_length(value, state->viewport_width, *state); + free(value); } } } @@ -858,9 +997,11 @@ void svgtiny_parse_color(const char *s, svgtiny_colour *c, void svgtiny_parse_font_attributes(const xmlNode *node, struct svgtiny_parse_state *state) { + const xmlAttr *attr; + UNUSED(state); - for (const xmlAttr *attr = node->properties; attr; attr = attr->next) { + for (attr = node->properties; attr; attr = attr->next) { if (strcmp((const char *) attr->name, "font-size") == 0) { /*if (css_parse_length( (const char *) attr->children->content, @@ -907,8 +1048,9 @@ void svgtiny_parse_transform(char *s, float *ma, float *mb, float za, zb, zc, zd, ze, zf; float angle, x, y; int n; + unsigned int i; - for (unsigned int i = 0; s[i]; i++) + for (i = 0; s[i]; i++) if (s[i] == ',') s[i] = ' '; @@ -981,12 +1123,14 @@ void svgtiny_parse_transform(char *s, float *ma, float *mb, svgtiny_code svgtiny_add_path(float *p, unsigned int n, struct svgtiny_parse_state *state) { + struct svgtiny_shape *shape; + 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); + shape = svgtiny_add_shape(state); if (!shape) { free(p); return svgtiny_OUT_OF_MEMORY; @@ -1018,8 +1162,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; } @@ -1032,8 +1178,11 @@ 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) { - for (unsigned int j = 0; j != n; ) { + unsigned int j; + + for (j = 0; j != n; ) { unsigned int points = 0; + unsigned int k; switch ((int) p[j]) { case svgtiny_PATH_MOVE: case svgtiny_PATH_LINE: @@ -1049,7 +1198,7 @@ void svgtiny_transform_path(float *p, unsigned int n, assert(0); } j++; - for (unsigned int k = 0; k != points; k++) { + for (k = 0; k != points; k++) { float x0 = p[j], y0 = p[j + 1]; float x = state->ctm.a * x0 + state->ctm.c * y0 + state->ctm.e; @@ -1069,9 +1218,10 @@ void svgtiny_transform_path(float *p, unsigned int n, void svgtiny_free(struct svgtiny_diagram *svg) { + unsigned int i; assert(svg); - for (unsigned int i = 0; i != svg->shape_count; i++) { + for (i = 0; i != svg->shape_count; i++) { free(svg->shape[i].path); free(svg->shape[i].text); } @@ -1081,3 +1231,23 @@ void svgtiny_free(struct svgtiny_diagram *svg) free(svg); } +#ifndef HAVE_STRNDUP +char *svgtiny_strndup(const char *s, size_t n) +{ + size_t len; + char *s2; + + for (len = 0; len != n && s[len]; len++) + continue; + + s2 = malloc(len + 1); + if (s2 == NULL) + return NULL; + + memcpy(s2, s, len); + s2[len] = '\0'; + + return s2; +} +#endif +