X-Git-Url: https://gitweb.michael.orlitzky.com/?a=blobdiff_plain;f=src%2Fsvgtiny.c;h=137f2b065d9afed42440342a3c24d781e8bcf112;hb=461d400085eba848898d74c92a820b5e3e1d6c49;hp=ab6b6dd5ea30f282320fa33bcccd5fff437a5be3;hpb=15cdb30e3125ce542289fe385f559799e5abf220;p=libsvgtiny.git diff --git a/src/svgtiny.c b/src/svgtiny.c index ab6b6dd..137f2b0 100644 --- a/src/svgtiny.c +++ b/src/svgtiny.c @@ -2,10 +2,10 @@ * 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 + * Copyright 2012 Daniel Silverstone */ -#define _GNU_SOURCE /* for strndup */ #include #include #include @@ -13,37 +13,48 @@ #include #include #include -#include -#include + +#include +#include + #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, +static svgtiny_code svgtiny_parse_svg(dom_element *svg, struct svgtiny_parse_state state); -static svgtiny_code svgtiny_parse_path(xmlNode *path, +static svgtiny_code svgtiny_parse_path(dom_element *path, struct svgtiny_parse_state state); -static svgtiny_code svgtiny_parse_rect(xmlNode *rect, +static svgtiny_code svgtiny_parse_rect(dom_element *rect, struct svgtiny_parse_state state); -static svgtiny_code svgtiny_parse_circle(xmlNode *circle, +static svgtiny_code svgtiny_parse_circle(dom_element *circle, struct svgtiny_parse_state state); -static svgtiny_code svgtiny_parse_line(xmlNode *line, +static svgtiny_code svgtiny_parse_ellipse(dom_element *ellipse, struct svgtiny_parse_state state); -static svgtiny_code svgtiny_parse_poly(xmlNode *poly, +static svgtiny_code svgtiny_parse_line(dom_element *line, + struct svgtiny_parse_state state); +static svgtiny_code svgtiny_parse_poly(dom_element *poly, struct svgtiny_parse_state state, bool polygon); -static svgtiny_code svgtiny_parse_text(xmlNode *text, +static svgtiny_code svgtiny_parse_text(dom_element *text, struct svgtiny_parse_state state); -static void svgtiny_parse_position_attributes(const xmlNode *node, +static void svgtiny_parse_position_attributes(const dom_element *node, const struct svgtiny_parse_state state, float *x, float *y, float *width, float *height); -static void svgtiny_parse_paint_attributes(const xmlNode *node, +static void svgtiny_parse_paint_attributes(const dom_element *node, struct svgtiny_parse_state *state); -static void svgtiny_parse_font_attributes(const xmlNode *node, +static void svgtiny_parse_font_attributes(const dom_element *node, struct svgtiny_parse_state *state); -static void svgtiny_parse_transform_attributes(xmlNode *node, +static void svgtiny_parse_transform_attributes(dom_element *node, 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 *state); /** @@ -54,18 +65,21 @@ struct svgtiny_diagram *svgtiny_create(void) { struct svgtiny_diagram *diagram; - diagram = malloc(sizeof *diagram); + diagram = calloc(sizeof(*diagram), 1); if (!diagram) return 0; - diagram->shape = 0; - diagram->shape_count = 0; - diagram->error_line = 0; - diagram->error_message = 0; - return diagram; + free(diagram); + return NULL; } +static void ignore_msg(uint32_t severity, void *ctx, const char *msg, ...) +{ + UNUSED(severity); + UNUSED(ctx); + UNUSED(msg); +} /** * Parse a block of memory into a svgtiny_diagram. @@ -75,8 +89,13 @@ svgtiny_code svgtiny_parse(struct svgtiny_diagram *diagram, const char *buffer, size_t size, const char *url, int viewport_width, int viewport_height) { - xmlDoc *document; - xmlNode *svg; + dom_document *document; + dom_exception exc; + dom_xml_parser *parser; + dom_xml_error err; + dom_element *svg; + dom_string *svg_name; + lwc_string *svg_name_lwc; struct svgtiny_parse_state state; float x, y, width, height; svgtiny_code code; @@ -85,26 +104,80 @@ svgtiny_code svgtiny_parse(struct svgtiny_diagram *diagram, assert(buffer); assert(url); - /* parse XML to tree */ - document = xmlReadMemory(buffer, size, url, 0, - XML_PARSE_NONET | XML_PARSE_COMPACT); - if (!document) - return svgtiny_LIBXML_ERROR; + UNUSED(url); + + parser = dom_xml_parser_create(NULL, NULL, + ignore_msg, NULL, &document); - /*xmlDebugDumpDocument(stderr, document);*/ + if (parser == NULL) + return svgtiny_LIBDOM_ERROR; + + err = dom_xml_parser_parse_chunk(parser, (uint8_t *)buffer, size); + if (err != DOM_XML_OK) { + dom_node_unref(document); + dom_xml_parser_destroy(parser); + return svgtiny_LIBDOM_ERROR; + } + + err = dom_xml_parser_completed(parser); + if (err != DOM_XML_OK) { + dom_node_unref(document); + dom_xml_parser_destroy(parser); + return svgtiny_LIBDOM_ERROR; + } + + /* We're done parsing, drop the parser. + * We now own the document entirely. + */ + dom_xml_parser_destroy(parser); /* find root element */ - svg = xmlDocGetRootElement(document); - if (!svg) - return svgtiny_NOT_SVG; - if (strcmp((const char *) svg->name, "svg") != 0) + exc = dom_document_get_document_element(document, &svg); + if (exc != DOM_NO_ERR) { + dom_node_unref(document); + return svgtiny_LIBDOM_ERROR; + } + exc = dom_node_get_node_name(svg, &svg_name); + if (exc != DOM_NO_ERR) { + dom_node_unref(svg); + dom_node_unref(document); + return svgtiny_LIBDOM_ERROR; + } + if (lwc_intern_string("svg", 3 /* SLEN("svg") */, + &svg_name_lwc) != lwc_error_ok) { + dom_string_unref(svg_name); + dom_node_unref(svg); + dom_node_unref(document); + return svgtiny_LIBDOM_ERROR; + } + if (!dom_string_caseless_lwc_isequal(svg_name, svg_name_lwc)) { + lwc_string_unref(svg_name_lwc); + dom_string_unref(svg_name); + dom_node_unref(svg); + dom_node_unref(document); return svgtiny_NOT_SVG; + } + + lwc_string_unref(svg_name_lwc); + dom_string_unref(svg_name); /* get graphic dimensions */ + memset(&state, 0, sizeof(state)); state.diagram = diagram; state.document = document; state.viewport_width = viewport_width; state.viewport_height = viewport_height; + +#define SVGTINY_STRING_ACTION2(s,n) \ + if (dom_string_create_interned((const uint8_t *) #n, \ + strlen(#n), &state.interned_##s) \ + != DOM_NO_ERR) { \ + code = svgtiny_LIBDOM_ERROR; \ + goto cleanup; \ + } +#include "svgtiny_strings.h" +#undef SVGTINY_STRING_ACTION2 + svgtiny_parse_position_attributes(svg, state, &x, &y, &width, &height); diagram->width = width; diagram->height = height; @@ -128,9 +201,23 @@ svgtiny_code svgtiny_parse(struct svgtiny_diagram *diagram, /* parse tree */ code = svgtiny_parse_svg(svg, state); - /* free XML tree */ - xmlFreeDoc(document); - + dom_node_unref(svg); + dom_node_unref(document); + +cleanup: + if (state.gradient_x1 != NULL) + dom_string_unref(state.gradient_x1); + if (state.gradient_x2 != NULL) + dom_string_unref(state.gradient_x2); + if (state.gradient_y1 != NULL) + dom_string_unref(state.gradient_y1); + if (state.gradient_y2 != NULL) + dom_string_unref(state.gradient_y2); +#define SVGTINY_STRING_ACTION2(s,n) \ + if (state.interned_##s != NULL) \ + dom_string_unref(state.interned_##s); +#include "svgtiny_strings.h" +#undef SVGTINY_STRING_ACTION2 return code; } @@ -139,19 +226,27 @@ svgtiny_code svgtiny_parse(struct svgtiny_diagram *diagram, * Parse a or element node. */ -svgtiny_code svgtiny_parse_svg(xmlNode *svg, +svgtiny_code svgtiny_parse_svg(dom_element *svg, struct svgtiny_parse_state state) { float x, y, width, height; + dom_string *view_box; + dom_element *child; + dom_exception exc; 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"); + exc = dom_element_get_attribute(svg, state.interned_viewBox, + &view_box); + if (exc != DOM_NO_ERR) { + return svgtiny_LIBDOM_ERROR; + } + if (view_box) { - const char *s = (const char *) view_box->children->content; + char *s = strndup(dom_string_data(view_box), + dom_string_byte_length(view_box)); float min_x, min_y, vwidth, vheight; if (sscanf(s, "%f,%f,%f,%f", &min_x, &min_y, &vwidth, &vheight) == 4 || @@ -162,39 +257,79 @@ svgtiny_code svgtiny_parse_svg(xmlNode *svg, state.ctm.e += -min_x * state.ctm.a; state.ctm.f += -min_y * state.ctm.d; } + free(s); + dom_string_unref(view_box); } svgtiny_parse_transform_attributes(svg, &state); - for (xmlNode *child = svg->children; child; child = child->next) { + exc = dom_node_get_first_child(svg, (dom_node **) (void *) &child); + if (exc != DOM_NO_ERR) { + return svgtiny_LIBDOM_ERROR; + } + while (child != NULL) { + dom_element *next; + dom_node_type nodetype; svgtiny_code code = svgtiny_OK; - if (child->type == XML_ELEMENT_NODE) { - const char *name = (const char *) child->name; - if (strcmp(name, "svg") == 0) + exc = dom_node_get_node_type(child, &nodetype); + if (exc != DOM_NO_ERR) { + dom_node_unref(child); + return svgtiny_LIBDOM_ERROR; + } + if (nodetype == DOM_ELEMENT_NODE) { + dom_string *nodename; + exc = dom_node_get_node_name(child, &nodename); + if (exc != DOM_NO_ERR) { + dom_node_unref(child); + return svgtiny_LIBDOM_ERROR; + } + if (dom_string_caseless_isequal(state.interned_svg, + nodename)) code = svgtiny_parse_svg(child, state); - else if (strcmp(name, "g") == 0) + else if (dom_string_caseless_isequal(state.interned_g, + nodename)) code = svgtiny_parse_svg(child, state); - else if (strcmp(name, "a") == 0) + else if (dom_string_caseless_isequal(state.interned_a, + nodename)) code = svgtiny_parse_svg(child, state); - else if (strcmp(name, "path") == 0) + else if (dom_string_caseless_isequal(state.interned_path, + nodename)) code = svgtiny_parse_path(child, state); - else if (strcmp(name, "rect") == 0) + else if (dom_string_caseless_isequal(state.interned_rect, + nodename)) code = svgtiny_parse_rect(child, state); - else if (strcmp(name, "circle") == 0) + else if (dom_string_caseless_isequal(state.interned_circle, + nodename)) code = svgtiny_parse_circle(child, state); - else if (strcmp(name, "line") == 0) + else if (dom_string_caseless_isequal(state.interned_ellipse, + nodename)) + code = svgtiny_parse_ellipse(child, state); + else if (dom_string_caseless_isequal(state.interned_line, + nodename)) code = svgtiny_parse_line(child, state); - else if (strcmp(name, "polyline") == 0) + else if (dom_string_caseless_isequal(state.interned_polyline, + nodename)) code = svgtiny_parse_poly(child, state, false); - else if (strcmp(name, "polygon") == 0) + else if (dom_string_caseless_isequal(state.interned_polygon, + nodename)) code = svgtiny_parse_poly(child, state, true); - else if (strcmp(name, "text") == 0) + else if (dom_string_caseless_isequal(state.interned_text, + nodename)) code = svgtiny_parse_text(child, state); + dom_string_unref(nodename); } - - if (code != svgtiny_OK) + if (code != svgtiny_OK) { + dom_node_unref(child); return code; + } + exc = dom_node_get_next_sibling(child, + (dom_node **) (void *) &next); + dom_node_unref(child); + if (exc != DOM_NO_ERR) { + return svgtiny_LIBDOM_ERROR; + } + child = next; } return svgtiny_OK; @@ -208,39 +343,57 @@ svgtiny_code svgtiny_parse_svg(xmlNode *svg, * http://www.w3.org/TR/SVG11/paths#PathElement */ -svgtiny_code svgtiny_parse_path(xmlNode *path, +svgtiny_code svgtiny_parse_path(dom_element *path, struct svgtiny_parse_state state) { + dom_string *path_d_str; + dom_exception exc; 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); /* read d attribute */ - s = path_d = (char *) xmlGetProp(path, (const xmlChar *) "d"); - if (!s) { - state.diagram->error_line = path->line; + exc = dom_element_get_attribute(path, state.interned_d, &path_d_str); + if (exc != DOM_NO_ERR) { + state.diagram->error_line = -1; /* path->line; */ + state.diagram->error_message = "path: error retrieving d attribute"; + return svgtiny_SVG_ERROR; + } + + if (path_d_str == NULL) { + state.diagram->error_line = -1; /* path->line; */ state.diagram->error_message = "path: missing d attribute"; return svgtiny_SVG_ERROR; } + s = path_d = strndup(dom_string_data(path_d_str), + dom_string_byte_length(path_d_str)); + dom_string_unref(path_d_str); + if (s == NULL) { + return svgtiny_OUT_OF_MEMORY; + } /* allocate space for path: it will never have more elements than d */ - float *p = malloc(sizeof p[0] * strlen(s)); - if (!p) + p = malloc(sizeof p[0] * strlen(s)); + if (!p) { + free(path_d); 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,13 +548,38 @@ 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); + free(path_d); + + if (i <= 4) { + /* no real segments in path */ + free(p); + return svgtiny_OK; + } return svgtiny_add_path(p, i, &state); } @@ -413,17 +591,18 @@ svgtiny_code svgtiny_parse_path(xmlNode *path, * http://www.w3.org/TR/SVG11/shapes#RectElement */ -svgtiny_code svgtiny_parse_rect(xmlNode *rect, +svgtiny_code svgtiny_parse_rect(dom_element *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; @@ -449,62 +628,180 @@ svgtiny_code svgtiny_parse_rect(xmlNode *rect, * Parse a element node. */ -svgtiny_code svgtiny_parse_circle(xmlNode *circle, +svgtiny_code svgtiny_parse_circle(dom_element *circle, struct svgtiny_parse_state state) { - float x = 0, y = 0, r = 0; - const float kappa = 0.5522847498; - - for (xmlAttr *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) - 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, "r") == 0) - r = svgtiny_parse_length(content, - state.viewport_width, state); - } + float x = 0, y = 0, r = -1; + float *p; + dom_string *attr; + dom_exception exc; + + exc = dom_element_get_attribute(circle, state.interned_cx, &attr); + if (exc != DOM_NO_ERR) + return svgtiny_LIBDOM_ERROR; + if (attr != NULL) { + x = svgtiny_parse_length(attr, state.viewport_width, state); + } + dom_string_unref(attr); + + exc = dom_element_get_attribute(circle, state.interned_cy, &attr); + if (exc != DOM_NO_ERR) + return svgtiny_LIBDOM_ERROR; + if (attr != NULL) { + y = svgtiny_parse_length(attr, state.viewport_height, state); + } + dom_string_unref(attr); + + exc = dom_element_get_attribute(circle, state.interned_r, &attr); + if (exc != DOM_NO_ERR) + return svgtiny_LIBDOM_ERROR; + if (attr != NULL) { + r = svgtiny_parse_length(attr, state.viewport_width, state); + } + dom_string_unref(attr); + 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 = -1; /* 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(dom_element *ellipse, + struct svgtiny_parse_state state) +{ + float x = 0, y = 0, rx = -1, ry = -1; + float *p; + dom_string *attr; + dom_exception exc; + + exc = dom_element_get_attribute(ellipse, state.interned_cx, &attr); + if (exc != DOM_NO_ERR) + return svgtiny_LIBDOM_ERROR; + if (attr != NULL) { + x = svgtiny_parse_length(attr, state.viewport_width, state); + } + dom_string_unref(attr); + + exc = dom_element_get_attribute(ellipse, state.interned_cy, &attr); + if (exc != DOM_NO_ERR) + return svgtiny_LIBDOM_ERROR; + if (attr != NULL) { + y = svgtiny_parse_length(attr, state.viewport_height, state); + } + dom_string_unref(attr); + + exc = dom_element_get_attribute(ellipse, state.interned_rx, &attr); + if (exc != DOM_NO_ERR) + return svgtiny_LIBDOM_ERROR; + if (attr != NULL) { + rx = svgtiny_parse_length(attr, state.viewport_width, state); + } + dom_string_unref(attr); + + exc = dom_element_get_attribute(ellipse, state.interned_ry, &attr); + if (exc != DOM_NO_ERR) + return svgtiny_LIBDOM_ERROR; + if (attr != NULL) { + ry = svgtiny_parse_length(attr, state.viewport_width, state); + } + dom_string_unref(attr); + + svgtiny_parse_paint_attributes(ellipse, &state); + svgtiny_parse_transform_attributes(ellipse, &state); + + if (rx < 0 || ry < 0) { + state.diagram->error_line = -1; /* 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; @@ -516,31 +813,50 @@ svgtiny_code svgtiny_parse_circle(xmlNode *circle, * Parse a element node. */ -svgtiny_code svgtiny_parse_line(xmlNode *line, +svgtiny_code svgtiny_parse_line(dom_element *line, struct svgtiny_parse_state state) { float x1 = 0, y1 = 0, x2 = 0, y2 = 0; + float *p; + dom_string *attr; + dom_exception exc; + + exc = dom_element_get_attribute(line, state.interned_x1, &attr); + if (exc != DOM_NO_ERR) + return svgtiny_LIBDOM_ERROR; + if (attr != NULL) { + x1 = svgtiny_parse_length(attr, state.viewport_width, state); + } + dom_string_unref(attr); + + exc = dom_element_get_attribute(line, state.interned_y1, &attr); + if (exc != DOM_NO_ERR) + return svgtiny_LIBDOM_ERROR; + if (attr != NULL) { + y1 = svgtiny_parse_length(attr, state.viewport_height, state); + } + dom_string_unref(attr); + + exc = dom_element_get_attribute(line, state.interned_x2, &attr); + if (exc != DOM_NO_ERR) + return svgtiny_LIBDOM_ERROR; + if (attr != NULL) { + x2 = svgtiny_parse_length(attr, state.viewport_width, state); + } + dom_string_unref(attr); + + exc = dom_element_get_attribute(line, state.interned_y2, &attr); + if (exc != DOM_NO_ERR) + return svgtiny_LIBDOM_ERROR; + if (attr != NULL) { + y2 = svgtiny_parse_length(attr, state.viewport_height, state); + } + dom_string_unref(attr); - for (xmlAttr *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) - x1 = svgtiny_parse_length(content, - state.viewport_width, state); - else if (strcmp(name, "y1") == 0) - y1 = svgtiny_parse_length(content, - state.viewport_height, state); - else if (strcmp(name, "x2") == 0) - x2 = svgtiny_parse_length(content, - state.viewport_width, state); - else if (strcmp(name, "y2") == 0) - y2 = svgtiny_parse_length(content, - state.viewport_height, state); - } 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; @@ -563,35 +879,48 @@ svgtiny_code svgtiny_parse_line(xmlNode *line, * http://www.w3.org/TR/SVG11/shapes#PolygonElement */ -svgtiny_code svgtiny_parse_poly(xmlNode *poly, +svgtiny_code svgtiny_parse_poly(dom_element *poly, struct svgtiny_parse_state state, bool polygon) { + dom_string *points_str; + dom_exception exc; char *s, *points; + float *p; + unsigned int i; svgtiny_parse_paint_attributes(poly, &state); svgtiny_parse_transform_attributes(poly, &state); - - /* read points attribute */ - s = points = (char *) xmlGetProp(poly, (const xmlChar *) "points"); - if (!s) { - state.diagram->error_line = poly->line; + + exc = dom_element_get_attribute(poly, state.interned_points, + &points_str); + if (exc != DOM_NO_ERR) + return svgtiny_LIBDOM_ERROR; + + if (points_str == NULL) { + state.diagram->error_line = -1; /* poly->line; */ state.diagram->error_message = "polyline/polygon: missing points attribute"; return svgtiny_SVG_ERROR; } + s = points = strndup(dom_string_data(points_str), + dom_string_byte_length(points_str)); + dom_string_unref(points_str); + /* read points attribute */ + if (s == NULL) + return svgtiny_OUT_OF_MEMORY; /* 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); + free(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; @@ -611,7 +940,7 @@ svgtiny_code svgtiny_parse_poly(xmlNode *poly, if (polygon) p[i++] = svgtiny_PATH_CLOSE; - xmlFree(points); + free(points); return svgtiny_add_path(p, i, &state); } @@ -621,44 +950,81 @@ svgtiny_code svgtiny_parse_poly(xmlNode *poly, * Parse a or element node. */ -svgtiny_code svgtiny_parse_text(xmlNode *text, +svgtiny_code svgtiny_parse_text(dom_element *text, struct svgtiny_parse_state state) { float x, y, width, height; + float px, py; + dom_node *child; + dom_exception exc; 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) { + + exc = dom_node_get_first_child(text, &child); + if (exc != DOM_NO_ERR) + return svgtiny_LIBDOM_ERROR; + while (child != NULL) { + dom_node *next; + dom_node_type nodetype; svgtiny_code code = svgtiny_OK; - if (child->type == XML_TEXT_NODE) { + exc = dom_node_get_node_type(child, &nodetype); + if (exc != DOM_NO_ERR) { + dom_node_unref(child); + return svgtiny_LIBDOM_ERROR; + } + if (nodetype == DOM_ELEMENT_NODE) { + dom_string *nodename; + exc = dom_node_get_node_name(child, &nodename); + if (exc != DOM_NO_ERR) { + dom_node_unref(child); + return svgtiny_LIBDOM_ERROR; + } + if (dom_string_caseless_isequal(nodename, + state.interned_tspan)) + code = svgtiny_parse_text((dom_element *)child, + state); + dom_string_unref(nodename); + } else if (nodetype == DOM_TEXT_NODE) { struct svgtiny_shape *shape = svgtiny_add_shape(&state); - if (!shape) + dom_string *content; + if (shape == NULL) { + dom_node_unref(child); return svgtiny_OUT_OF_MEMORY; - shape->text = strdup((const char *) child->content); + } + exc = dom_text_get_whole_text(child, &content); + if (exc != DOM_NO_ERR) { + dom_node_unref(child); + return svgtiny_LIBDOM_ERROR; + } + shape->text = strndup(dom_string_data(content), + dom_string_byte_length(content)); + dom_string_unref(content); shape->text_x = px; shape->text_y = py; state.diagram->shape_count++; - - } else if (child->type == XML_ELEMENT_NODE && - strcmp((const char *) child->name, - "tspan") == 0) { - code = svgtiny_parse_text(child, state); } - if (!code != svgtiny_OK) + if (code != svgtiny_OK) { + dom_node_unref(child); return code; + } + exc = dom_node_get_next_sibling(child, &next); + dom_node_unref(child); + if (exc != DOM_NO_ERR) + return svgtiny_LIBDOM_ERROR; + child = next; } return svgtiny_OK; @@ -669,30 +1035,42 @@ svgtiny_code svgtiny_parse_text(xmlNode *text, * Parse x, y, width, and height attributes, if present. */ -void svgtiny_parse_position_attributes(const xmlNode *node, +void svgtiny_parse_position_attributes(const dom_element *node, const struct svgtiny_parse_state state, float *x, float *y, float *width, float *height) { + dom_string *attr; + dom_exception exc; + *x = 0; *y = 0; *width = state.viewport_width; *height = state.viewport_height; - for (xmlAttr *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) - *x = svgtiny_parse_length(content, - state.viewport_width, state); - else if (strcmp(name, "y") == 0) - *y = svgtiny_parse_length(content, - state.viewport_height, state); - else if (strcmp(name, "width") == 0) - *width = svgtiny_parse_length(content, - state.viewport_width, state); - else if (strcmp(name, "height") == 0) - *height = svgtiny_parse_length(content, - state.viewport_height, state); + exc = dom_element_get_attribute(node, state.interned_x, &attr); + if (exc == DOM_NO_ERR && attr != NULL) { + *x = svgtiny_parse_length(attr, state.viewport_width, state); + dom_string_unref(attr); + } + + exc = dom_element_get_attribute(node, state.interned_y, &attr); + if (exc == DOM_NO_ERR && attr != NULL) { + *y = svgtiny_parse_length(attr, state.viewport_height, state); + dom_string_unref(attr); + } + + exc = dom_element_get_attribute(node, state.interned_width, &attr); + if (exc == DOM_NO_ERR && attr != NULL) { + *width = svgtiny_parse_length(attr, state.viewport_width, + state); + dom_string_unref(attr); + } + + exc = dom_element_get_attribute(node, state.interned_height, &attr); + if (exc == DOM_NO_ERR && attr != NULL) { + *height = svgtiny_parse_length(attr, state.viewport_height, + state); + dom_string_unref(attr); } } @@ -701,14 +1079,16 @@ void svgtiny_parse_position_attributes(const xmlNode *node, * Parse a length as a number of pixels. */ -float svgtiny_parse_length(const char *s, int viewport_size, - const struct svgtiny_parse_state state) +static float _svgtiny_parse_length(const char *s, int viewport_size, + const struct svgtiny_parse_state state) { int num_length = strspn(s, "0123456789+-."); const char *unit = s + num_length; 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] == '%') { @@ -734,53 +1114,77 @@ float svgtiny_parse_length(const char *s, int viewport_size, return 0; } +float svgtiny_parse_length(dom_string *s, int viewport_size, + const struct svgtiny_parse_state state) +{ + char *ss = strndup(dom_string_data(s), dom_string_byte_length(s)); + float ret = _svgtiny_parse_length(ss, viewport_size, state); + free(ss); + return ret; +} /** * Parse paint attributes, if present. */ -void svgtiny_parse_paint_attributes(const xmlNode *node, +void svgtiny_parse_paint_attributes(const dom_element *node, struct svgtiny_parse_state *state) { - for (const xmlAttr *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) - svgtiny_parse_color(content, &state->fill, state); - else if (strcmp(name, "stroke") == 0) - svgtiny_parse_color(content, &state->stroke, state); - else if (strcmp(name, "stroke-width") == 0) - state->stroke_width = svgtiny_parse_length(content, - state->viewport_width, *state); - else if (strcmp(name, "style") == 0) { - const char *style = (const char *) - attr->children->content; - 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); - free(value); - } - if ((s = strstr(style, "stroke:"))) { - s += 7; - while (*s == ' ') - s++; - value = strndup(s, strcspn(s, "; ")); - svgtiny_parse_color(value, &state->stroke, state); - free(value); - } - if ((s = strstr(style, "stroke-width:"))) { - s += 13; - while (*s == ' ') - s++; - state->stroke_width = svgtiny_parse_length(s, + dom_string *attr; + dom_exception exc; + + exc = dom_element_get_attribute(node, state->interned_fill, &attr); + if (exc == DOM_NO_ERR && attr != NULL) { + svgtiny_parse_color(attr, &state->fill, state); + dom_string_unref(attr); + } + + exc = dom_element_get_attribute(node, state->interned_stroke, &attr); + if (exc == DOM_NO_ERR && attr != NULL) { + svgtiny_parse_color(attr, &state->stroke, state); + dom_string_unref(attr); + } + + exc = dom_element_get_attribute(node, state->interned_stroke_width, &attr); + if (exc == DOM_NO_ERR && attr != NULL) { + state->stroke_width = svgtiny_parse_length(attr, state->viewport_width, *state); - } + dom_string_unref(attr); + } + + 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; + char *value; + if ((s = strstr(style, "fill:"))) { + s += 5; + while (*s == ' ') + s++; + value = strndup(s, strcspn(s, "; ")); + _svgtiny_parse_color(value, &state->fill, state); + free(value); + } + if ((s = strstr(style, "stroke:"))) { + s += 7; + while (*s == ' ') + s++; + value = strndup(s, strcspn(s, "; ")); + _svgtiny_parse_color(value, &state->stroke, state); + free(value); } + if ((s = strstr(style, "stroke-width:"))) { + s += 13; + while (*s == ' ') + s++; + value = strndup(s, strcspn(s, "; ")); + state->stroke_width = _svgtiny_parse_length(value, + state->viewport_width, *state); + free(value); + } + free(style); + dom_string_unref(attr); } } @@ -789,7 +1193,7 @@ void svgtiny_parse_paint_attributes(const xmlNode *node, * Parse a colour. */ -void svgtiny_parse_color(const char *s, svgtiny_colour *c, +static void _svgtiny_parse_color(const char *s, svgtiny_colour *c, struct svgtiny_parse_state *state) { unsigned int r, g, b; @@ -807,7 +1211,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; @@ -848,15 +1252,30 @@ void svgtiny_parse_color(const char *s, svgtiny_colour *c, } } +void svgtiny_parse_color(dom_string *s, svgtiny_colour *c, + struct svgtiny_parse_state *state) +{ + char *ss = strndup(dom_string_data(s), dom_string_byte_length(s)); + _svgtiny_parse_color(ss, c, state); + free(ss); +} /** * Parse font attributes, if present. */ -void svgtiny_parse_font_attributes(const xmlNode *node, +void svgtiny_parse_font_attributes(const dom_element *node, struct svgtiny_parse_state *state) { - for (const xmlAttr *attr = node->properties; attr; attr = attr->next) { + /* TODO: Implement this, it never used to be */ + UNUSED(node); + UNUSED(state); +#ifdef WRITTEN_THIS_PROPERLY + const xmlAttr *attr; + + UNUSED(state); + + 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, @@ -867,6 +1286,7 @@ void svgtiny_parse_font_attributes(const xmlNode *node, }*/ } } +#endif } @@ -876,18 +1296,23 @@ void svgtiny_parse_font_attributes(const xmlNode *node, * http://www.w3.org/TR/SVG11/coords#TransformAttribute */ -void svgtiny_parse_transform_attributes(xmlNode *node, +void svgtiny_parse_transform_attributes(dom_element *node, struct svgtiny_parse_state *state) { char *transform; - - /* parse transform */ - transform = (char *) xmlGetProp(node, (const xmlChar *) "transform"); - if (transform) { + dom_string *attr; + dom_exception exc; + + exc = dom_element_get_attribute(node, state->interned_transform, + &attr); + if (exc == DOM_NO_ERR && attr != NULL) { + transform = strndup(dom_string_data(attr), + dom_string_byte_length(attr)); svgtiny_parse_transform(transform, &state->ctm.a, &state->ctm.b, &state->ctm.c, &state->ctm.d, &state->ctm.e, &state->ctm.f); - xmlFree(transform); + free(transform); + dom_string_unref(attr); } } @@ -903,8 +1328,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] = ' '; @@ -977,12 +1403,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; @@ -1014,8 +1442,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; } @@ -1028,8 +1458,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: @@ -1045,7 +1478,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; @@ -1065,9 +1498,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); } @@ -1077,3 +1511,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 +