//
// Copyright (c) 2009 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty.  In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
//    claim that you wrote the original software. If you use this software
//    in a product, an acknowledgment in the product documentation would be
//    appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//    misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//

// The SVG parser is based on Anti-Graim Geometry SVG example
// Copyright (C) 2002-2004 Maxim Shemanarev (McSeem)

#include "nanosvg.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>

#ifndef M_PI
	#define M_PI 3.14159265358979323846264338327
#endif

#ifdef _MSC_VER
	#pragma warning (disable: 4996) // Switch off security warnings
#endif

// Simple XML parser

#define TAG 1
#define CONTENT 2
#define MAX_ATTRIBS 256

static void parseContent(char* s,
						 void (*contentCb)(void* ud, const char* s),
						 void* ud)
{
	// Trim start white spaces
	while (*s && isspace(*s)) s++;
	if (!*s) return;
	
	if (contentCb)
		(*contentCb)(ud, s);
}

static void parseElement(char* s,
						 void (*startelCb)(void* ud, const char* el, const char** attr),
						 void (*endelCb)(void* ud, const char* el),
						 void* ud)
{
	const char* attr[MAX_ATTRIBS];
	int nattr = 0;
	char* name;
	int start = 0;
	int end = 0;
	
	// Skip white space after the '<'
	while (*s && isspace(*s)) s++;

	// Check if the tag is end tag
	if (*s == '/')
	{
		s++;
		end = 1;
	}
	else
	{
		start = 1;
	}

	// Skip comments, data and preprocessor stuff.
	if (!*s || *s == '?' || *s == '!')
		return;

	// Get tag name
	name = s;
	while (*s && !isspace(*s)) s++;
	if (*s) { *s++ = '\0'; }

	// Get attribs
	while (!end && *s && nattr < MAX_ATTRIBS-1)
	{
		// Skip white space before the attrib name
		while (*s && isspace(*s)) s++;
		if (!*s) break;
		if (*s == '/')
		{
			end = 1;
			break;
		}
		attr[nattr++] = s;
		// Find end of the attrib name.
		while (*s && !isspace(*s) && *s != '=') s++;
		if (*s) { *s++ = '\0'; }
		// Skip until the beginning of the value.
		while (*s && *s != '\"') s++;
		if (!*s) break;
		s++;
		// Store value and find the end of it.
		attr[nattr++] = s;
		while (*s && *s != '\"') s++;
		if (*s) { *s++ = '\0'; }
	}
	
	// List terminator
	attr[nattr++] = 0;
	attr[nattr++] = 0;

	// Call callbacks.
	if (start && startelCb)
		(*startelCb)(ud, name, attr);
	if (end && endelCb)
		(*endelCb)(ud, name);
}

int parsexml(char* input,
			 void (*startelCb)(void* ud, const char* el, const char** attr),
			 void (*endelCb)(void* ud, const char* el),
			 void (*contentCb)(void* ud, const char* s),
			 void* ud)
{
	char* s = input;
	char* mark = s;
	int state = CONTENT;
	while (*s)
	{
		if (*s == '<' && state == CONTENT)
		{
			// Start of a tag
			*s++ = '\0';
			parseContent(mark, contentCb, ud);
			mark = s;
			state = TAG;
		}
		else if (*s == '>' && state == TAG)
		{
			// Start of a content or new tag.
			*s++ = '\0';
			parseElement(mark, startelCb, endelCb, ud);
			mark = s;
			state = CONTENT;
		}
		else
			s++;
	}
	
	return 1;
}


/* Simple SVG parser. */

#define SVG_MAX_ATTR 128

struct SVGAttrib
{
	float xform[6];
	unsigned int fillColor;
	unsigned int strokeColor;
	float fillOpacity;
	float strokeOpacity;
	float strokeWidth;
	char hasFill;
	char hasStroke;
	char visible;
};

struct SVGParser
{
	struct SVGAttrib attr[SVG_MAX_ATTR];
	int attrHead;
	float* buf;
	int nbuf;
	int cbuf;
	struct SVGPath* plist;
	char pathFlag;
	char defsFlag;
	float tol;
};

static void xformSetIdentity(float* t)
{
	t[0] = 1.0f; t[1] = 0.0f;
	t[2] = 0.0f; t[3] = 1.0f;
	t[4] = 0.0f; t[5] = 0.0f;
}

static void xformSetTranslation(float* t, float tx, float ty)
{
	t[0] = 1.0f; t[1] = 0.0f;
	t[2] = 0.0f; t[3] = 1.0f;
	t[4] = tx; t[5] = ty;
}

static void xformSetScale(float* t, float sx, float sy)
{
	t[0] = sx; t[1] = 0.0f;
	t[2] = 0.0f; t[3] = sy;
	t[4] = 0.0f; t[5] = 0.0f;
}

static void xformMultiply(float* t, float* s)
{
	float t0 = t[0] * s[0] + t[1] * s[2];
	float t2 = t[2] * s[0] + t[3] * s[2];
	float t4 = t[4] * s[0] + t[5] * s[2] + s[4];
	t[1] = t[0] * s[1] + t[1] * s[3];
	t[3] = t[2] * s[1] + t[3] * s[3];
	t[5] = t[4] * s[1] + t[5] * s[3] + s[5];
	t[0] = t0;
	t[2] = t2;
	t[4] = t4;
}

static void xformPremultiply(float* t, float* s)
{
	float s2[6];
	memcpy(s2, s, sizeof(float)*6);
	xformMultiply(s2, t);
	memcpy(t, s2, sizeof(float)*6);
}

static struct SVGParser* svgCreateParser()
{
	struct SVGParser* p;
	p = (struct SVGParser*)malloc(sizeof(struct SVGParser));
	if (!p)
		return NULL;
	memset(p, 0, sizeof(struct SVGParser));

	// Init style
	xformSetIdentity(p->attr[0].xform);
	p->attr[0].fillColor = 0;
	p->attr[0].strokeColor = 0;
	p->attr[0].fillOpacity = 1;
	p->attr[0].strokeOpacity = 1;
	p->attr[0].strokeWidth = 1;
	p->attr[0].hasFill = 0;
	p->attr[0].hasStroke = 0;
	p->attr[0].visible = 1;

	return p;
}

static void svgDeleteParser(struct SVGParser* p)
{
	struct SVGPath* path;
	struct SVGPath* next;
	path = p->plist;
	while (path)
	{
		next = path->next;
		if (path->pts)
			free(path->pts);
		free(path);
		path = next;
	}
	if (p->buf)
		free(p->buf);
	free(p);
}

static void svgResetPath(struct SVGParser* p)
{
	p->nbuf = 0;
}

static void svgPathPoint(struct SVGParser* p, float x, float y)
{
	int cap;
	float* buf;
	if (p->nbuf+1 > p->cbuf)
	{
		cap = p->cbuf ? p->cbuf*2 : 8;
		buf = (float*)malloc(cap*2*sizeof(float));
		if (!buf) return;
		if (p->nbuf)
			memcpy(buf, p->buf, p->nbuf*2*sizeof(float));
		if (p->buf)
			free(p->buf);
		p->buf = buf;
		p->cbuf = cap;
	}
	p->buf[p->nbuf*2+0] = x;
	p->buf[p->nbuf*2+1] = y;
	p->nbuf++;
}

static struct SVGAttrib* svgGetAttr(struct SVGParser* p)
{
	return &p->attr[p->attrHead];
}

static void svgPushAttr(struct SVGParser* p)
{
	if (p->attrHead < SVG_MAX_ATTR-1)
	{
		p->attrHead++;
		memcpy(&p->attr[p->attrHead], &p->attr[p->attrHead-1], sizeof(struct SVGAttrib));
	}
}

static void svgPopAttr(struct SVGParser* p)
{
	if (p->attrHead > 0)
		p->attrHead--;
}

static void svgCreatePath(struct SVGParser* p, char closed)
{
	float* t;
	float* pt;
	struct SVGAttrib* attr;
	struct SVGPath* path;
	int i;
	
	if (!p)
		return;

	if (!p->nbuf)
	{
		return;
	}

	attr = svgGetAttr(p);

	path = (struct SVGPath*)malloc(sizeof(struct SVGPath));
	if (!path)
		return;
	memset(path, 0, sizeof(struct SVGPath));
	path->pts = (float*)malloc(p->nbuf*2*sizeof(float));
	if (!path->pts)
	{
		free(path);
		return;
	}
	path->closed = closed;
	path->npts = p->nbuf;
	
	path->next = p->plist;
	p->plist = path;

	// Transform path.
	t = attr->xform;
	for (i = 0; i < p->nbuf; ++i)
	{
		pt = &p->buf[i*2];
		path->pts[i*2+0] = pt[0]*t[0] + pt[1]*t[2] + t[4];
		path->pts[i*2+1] = pt[0]*t[1] + pt[1]*t[3] + t[5];
	}
	
	path->hasFill = attr->hasFill;
	path->hasStroke = attr->hasStroke;
	path->strokeWidth = attr->strokeWidth * t[0];

	path->fillColor = attr->fillColor;
	if (path->hasFill)
		path->fillColor |= (unsigned int)(attr->fillOpacity*255) << 24;
	
	path->strokeColor = attr->strokeColor;
	if (path->hasStroke)
		path->strokeColor |= (unsigned int)(attr->strokeOpacity*255) << 24;
}

static int isnum(char c)
{
	return strchr("0123456789+-.eE", c) != 0;
}

/*static const char* parsePathFloats(const char* s, float* arg, int n)
{
	char num[64];
	const char* start;
	int nnum;
	int i = 0;
	while (*s && i < n)
	{
		// Skip white spaces and commas
		while (*s && (isspace(*s) || *s == ',')) s++;
		if (!*s) break;
		start = s;
		nnum = 0;
		while (*s && isnum(*s))
		{
			if (nnum < 63) num[nnum++] = *s;
			s++;
		}
		num[nnum] = '\0';
		arg[i++] = (float)atof(num);
	}
	return s;
}*/


static const char* getNextPathItem(const char* s, char* it)
{
	int i = 0;
	it[0] = '\0';
	// Skip white spaces and commas
	while (*s && (isspace(*s) || *s == ',')) s++;
	if (!*s) return s;
	if (*s == '-' || *s == '+' || isnum(*s))
	{
		while (*s == '-' || *s == '+')
		{
			if (i < 63) it[i++] = *s;
			s++;
		}
		while (*s && *s != '-' && *s != '+' && isnum(*s))
		{
			if (i < 63) it[i++] = *s;
			s++;
		}
		it[i] = '\0';
	}
	else
	{
		it[0] = *s++;
		it[1] = '\0';
		return s;
	}
	return s;
}


static unsigned int parseColor(const char* str)
{
	unsigned c = 0;
	while(*str == ' ') ++str;
	if (*str == '#')
		sscanf(str + 1, "%x", &c);
	return c;
}

static float parseFloat(const char* str)
{
	while (*str == ' ') ++str;
	return (float)atof(str);
}

static int parseTransformArgs(const char* str, float* args, int maxNa, int* na)
{
	const char* end;
	const char* ptr;
	
	*na = 0;
	ptr = str;
	while (*ptr && *ptr != '(') ++ptr;
	if (*ptr == 0)
		return 1;
	end = ptr;
	while (*end && *end != ')') ++end;
	if (*end == 0)
		return 1;
	
	while (ptr < end)
	{
		if (isnum(*ptr))
		{
			if (*na >= maxNa) return 0;
			args[(*na)++] = (float)atof(ptr);
			while (ptr < end && isnum(*ptr)) ++ptr;
		}
		else
		{
			++ptr;
		}
	}
	return (int)(end - str);
}

static int svgParseMatrix(struct SVGParser* p, const char* str)
{
	float t[6];
	int na = 0;
	int len = parseTransformArgs(str, t, 6, &na);
	if (na != 6) return len;
	xformPremultiply(svgGetAttr(p)->xform, t);
	return len;
}

static int svgParseTranslate(struct SVGParser* p, const char* str)
{
	float args[2];
	float t[6];
	int na = 0;
	int len = parseTransformArgs(str, args, 2, &na);
	if (na == 1) args[1] = 0.0;
	xformSetTranslation(t, args[0], args[1]);
	xformPremultiply(svgGetAttr(p)->xform, t);
	return len;
}

static int svgParseScale(struct SVGParser* p, const char* str)
{
	float args[2];
	int na = 0;
	float t[6];
	int len = parseTransformArgs(str, args, 2, &na);
	if (na == 1) args[1] = args[0];
	xformSetScale(t, args[0], args[1]);
	xformPremultiply(svgGetAttr(p)->xform, t);
	return len;
}

static void svgParseTransform(struct SVGParser* p, const char* str)
{
	while (*str)
	{
		if (strncmp(str, "matrix", 6) == 0)
			str += svgParseMatrix(p, str);
		else if (strncmp(str, "translate", 9) == 0)
			str += svgParseTranslate(p, str);
		else if (strncmp(str, "scale", 5) == 0)
			str += svgParseScale(p, str);
		else
			++str;
	}
}

static void svgParseStyle(struct SVGParser* p, const char* str);

static int svgParseAttr(struct SVGParser* p, const char* name, const char* value)
{
	struct SVGAttrib* attr = svgGetAttr(p);
	if (!attr) return 0;
	
	if (strcmp(name, "style") == 0)
	{
		svgParseStyle(p, value);
	}
	else if (strcmp(name, "display") == 0)
	{
		if (strcmp(value, "none") == 0)
			attr->visible = 0;
		else
			attr->visible = 1;
	}
	else if (strcmp(name, "fill") == 0)
	{
		if (strcmp(value, "none") == 0)
		{
			attr->hasFill = 0;
		}
		else
		{
			attr->hasFill = 1;
			attr->fillColor = parseColor(value);
		}
	}
	else if (strcmp(name, "fill-opacity") == 0)
	{
		attr->fillOpacity = parseFloat(value);
	}
	else if (strcmp(name, "stroke") == 0)
	{
		if (strcmp(value, "none") == 0)
		{
			attr->hasStroke = 0;
		}
		else
		{
			attr->hasStroke = 1;
			attr->strokeColor = parseColor(value);
		}
	}
	else if (strcmp(name, "stroke-width") == 0)
	{
		attr->strokeWidth = parseFloat(value);
	}
	else if (strcmp(name, "stroke-opacity") == 0)
	{
		attr->strokeOpacity = parseFloat(value);
	}
	else if (strcmp(name, "transform") == 0)
	{
		svgParseTransform(p, value);
	}
	else
	{
		return 0;
	}
	return 1;
}

static int svgParseNameValue(struct SVGParser* p, const char* start, const char* end)
{
	const char* str;
	const char* val;
	char name[512];
	char value[512];
	int n;
	
	str = start;
	while (str < end && *str != ':') ++str;
	
	val = str;
	
	// Right Trim
	while (str > start &&  (*str == ':' || isspace(*str))) --str;
	++str;
	
	n = (int)(str - start);
	if (n > 511) n = 511;
	if (n) memcpy(name, start, n);
	name[n] = 0;
	
	while (val < end && (*val == ':' || isspace(*val))) ++val;
	
	n = (int)(end - val);
	if (n > 511) n = 511;
	if (n) memcpy(value, val, n);
	value[n] = 0;
	
	return svgParseAttr(p, name, value);
}

static void svgParseStyle(struct SVGParser* p, const char* str)
{
	const char* start;
	const char* end;
	
	while (*str)
	{
		// Left Trim
		while(*str && isspace(*str)) ++str;
		start = str;
		while(*str && *str != ';') ++str;
		end = str;
		
		// Right Trim
		while (end > start &&  (*end == ';' || isspace(*end))) --end;
		++end;
		
		svgParseNameValue(p, start, end);
		if (*str) ++str;
	}
}

static void svgParseAttribs(struct SVGParser* p, const char** attr)
{
	int i;
	for (i = 0; attr[i]; i += 2)
	{
		if (strcmp(attr[i], "style") == 0)
			svgParseStyle(p, attr[i + 1]);
		else
			svgParseAttr(p, attr[i], attr[i + 1]);
	}
}

static int getArgsPerElement(char cmd)
{
	switch (tolower(cmd))
	{
		case 'v':
		case 'h':
			return 1;
		case 'm':
		case 'l':
		case 't':
			return 2;
		case 'q':
		case 's':
			return 4;
		case 'c':
			return 6;
		case 'a':
			return 7;
	}
	return 0;
}

static float distPtSeg(float x, float y, float px, float py, float qx, float qy)
{
	float pqx, pqy, dx, dy, d, t;
	pqx = qx-px;
	pqy = qy-py;
	dx = x-px;
	dy = y-py;
	d = pqx*pqx + pqy*pqy;
	t = pqx*dx + pqy*dy;
	if (d > 0) t /= d;
	if (t < 0) t = 0;
	else if (t > 1) t = 1;
	dx = px + t*pqx - x;
	dy = py + t*pqy - y;
	return dx*dx + dy*dy;
}

static void cubicBezRec(struct SVGParser* p,
						float x1, float y1, float x2, float y2, 
						float x3, float y3, float x4, float y4,
						int level)
{
	float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234;
	float d;
	
	if (level > 12) return;

	x12 = (x1+x2)*0.5f;
	y12 = (y1+y2)*0.5f;
	x23 = (x2+x3)*0.5f;
	y23 = (y2+y3)*0.5f;
	x34 = (x3+x4)*0.5f;
	y34 = (y3+y4)*0.5f;
	x123 = (x12+x23)*0.5f;
	y123 = (y12+y23)*0.5f;
	x234 = (x23+x34)*0.5f;
	y234 = (y23+y34)*0.5f;
	x1234 = (x123+x234)*0.5f;
	y1234 = (y123+y234)*0.5f;

	d = distPtSeg(x1234, y1234, x1,y1, x4,y4);
	if (level > 0 && d < p->tol*p->tol)
	{
		svgPathPoint(p, x1234, y1234);
		return;
	}
	
	cubicBezRec(p, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1); 
	cubicBezRec(p, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1); 
}

static void cubicBez(struct SVGParser* p,
					 float x1, float y1, float cx1, float cy1,
					 float cx2, float cy2, float x2, float y2)
{
	cubicBezRec(p, x1,y1, cx1,cy1, cx2,cy2, x2,y2, 0); 
	svgPathPoint(p, x2, y2);
}

static void quadBezRec(struct SVGParser* p,
					   float x1, float y1, float x2, float y2, float x3, float y3,
					   int level)
{
	float x12,y12,x23,y23,x123,y123,d;
	
	if (level > 12) return;
	
	x12 = (x1+x2)*0.5f;                
	y12 = (y1+y2)*0.5f;
	x23 = (x2+x3)*0.5f;
	y23 = (y2+y3)*0.5f;
	x123 = (x12+x23)*0.5f;
	y123 = (y12+y23)*0.5f;

	d = distPtSeg(x123, y123, x1,y1, x3,y3);
	if (level > 0 && d < p->tol*p->tol)
	{
		svgPathPoint(p, x123, y123);
		return;
	}
	
	quadBezRec(p, x1,y1, x12,y12, x123,y123, level+1); 
	quadBezRec(p, x123,y123, x23,y23, x3,y3, level+1); 
}

static void quadBez(struct SVGParser* p,
					float x1, float y1, float cx, float cy, float x2, float y2)
{
	quadBezRec(p, x1,y1, cx,cy, x2,y2, 0); 
	svgPathPoint(p, x2, y2);
}

static void pathLineTo(struct SVGParser* p, float* cpx, float* cpy, float* args, int rel)
{
	if (rel)
	{
		*cpx += args[0];
		*cpy += args[1];
	}
	else
	{
		*cpx = args[0];
		*cpy = args[1];
	}
	svgPathPoint(p, *cpx, *cpy);
}

static void pathHLineTo(struct SVGParser* p, float* cpx, float* cpy, float* args, int rel)
{
	if (rel)
		*cpx += args[0];
	else
		*cpx = args[0];
	svgPathPoint(p, *cpx, *cpy);
}

static void pathVLineTo(struct SVGParser* p, float* cpx, float* cpy, float* args, int rel)
{
	if (rel)
		*cpy += args[0];
	else
		*cpy = args[0];
	svgPathPoint(p, *cpx, *cpy);
}

static void pathCubicBezTo(struct SVGParser* p, float* cpx, float* cpy,
						   float* cpx2, float* cpy2, float* args, int rel)
{
	float x1, y1, x2, y2, cx1, cy1, cx2, cy2;
	
	x1 = *cpx;
	y1 = *cpy;
	if (rel)
	{
		cx1 = *cpx + args[0];
		cy1 = *cpy + args[1];
		cx2 = *cpx + args[2];
		cy2 = *cpy + args[3];
		x2 = *cpx + args[4];
		y2 = *cpy + args[5];
	}
	else
	{
		cx1 = args[0];
		cy1 = args[1];
		cx2 = args[2];
		cy2 = args[3];
		x2 = args[4];
		y2 = args[5];
	}

	cubicBez(p, x1,y1, cx1,cy1, cx2,cy2, x2,y2);
	
	*cpx2 = cx2;
	*cpy2 = cy2;
	*cpx = x2;
	*cpy = y2;
}

static void pathCubicBezShortTo(struct SVGParser* p, float* cpx, float* cpy,
								float* cpx2, float* cpy2, float* args, int rel)
{
	float x1, y1, x2, y2, cx1, cy1, cx2, cy2;
	
	x1 = *cpx;
	y1 = *cpy;
	if (rel)
	{
		cx2 = *cpx + args[0];
		cy2 = *cpy + args[1];
		x2 = *cpx + args[2];
		y2 = *cpy + args[3];
	}
	else
	{
		cx2 = args[0];
		cy2 = args[1];
		x2 = args[2];
		y2 = args[3];
	}
	
	cx1 = 2*x1 - *cpx2;
	cy1 = 2*y1 - *cpy2;
	
	cubicBez(p, x1,y1, cx1,cy1, cx2,cy2, x2,y2);
	
	*cpx2 = cx2;
	*cpy2 = cy2;
	*cpx = x2;
	*cpy = y2;
}

static void pathQuadBezTo(struct SVGParser* p, float* cpx, float* cpy,
						  float* cpx2, float* cpy2, float* args, int rel)
{
	float x1, y1, x2, y2, cx, cy;
	
	x1 = *cpx;
	y1 = *cpy;
	if (rel)
	{
		cx = *cpx + args[0];
		cy = *cpy + args[1];
		x2 = *cpx + args[2];
		y2 = *cpy + args[3];
	}
	else
	{
		cx = args[0];
		cy = args[1];
		x2 = args[2];
		y2 = args[3];
	}
	
	quadBez(p, x1,y1, cx,cy, x2,y2);
	
	*cpx2 = cx;
	*cpy2 = cy;
	*cpx = x2;
	*cpy = y2;
}

static void pathQuadBezShortTo(struct SVGParser* p, float* cpx, float* cpy,
							   float* cpx2, float* cpy2, float* args, int rel)
{
	float x1, y1, x2, y2, cx, cy;
	
	x1 = *cpx;
	y1 = *cpy;
	if (rel)
	{
		x2 = *cpx + args[0];
		y2 = *cpy + args[1];
	}
	else
	{
		x2 = args[0];
		y2 = args[1];
	}

	cx = 2*x1 - *cpx2;
	cy = 2*y1 - *cpy2;

	quadBez(p, x1,y1, cx,cy, x2,y2);
	
	*cpx2 = cx;
	*cpy2 = cy;
	*cpx = x2;
	*cpy = y2;
}

static void svgParsePath(struct SVGParser* p, const char** attr)
{
	const char* s;
	char cmd;
	float args[10];
	int nargs;
	int rargs;
	float cpx, cpy, cpx2, cpy2;
	const char* tmp[4];
	char closedFlag;
	int i;
	char item[64];
	
	for (i = 0; attr[i]; i += 2)
	{
		if (strcmp(attr[i], "d") == 0)
		{
			s = attr[i + 1];

			svgResetPath(p);
			closedFlag = 0;
			nargs = 0;
			
			while (*s)
			{
				s = getNextPathItem(s, item);
				if (!*item) break;
				
				if (isnum(item[0]))
				{
					if (nargs < 10)
						args[nargs++] = (float)atof(item);
					if (nargs >= rargs)
					{
						switch (cmd)
						{
							case 'm':
							case 'M':
							case 'l':
							case 'L':
								pathLineTo(p, &cpx, &cpy, args, (cmd == 'm' || cmd == 'l') ? 1 : 0);
								break;
							case 'H':
							case 'h':
								pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0);
								break;
							case 'V':
							case 'v':
								pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0);
								break;
							case 'C':
							case 'c':
								pathCubicBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'c' ? 1 : 0);
								break;
							case 'S':
							case 's':
								pathCubicBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0);
								break;
							case 'Q':
							case 'q':
								pathQuadBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'q' ? 1 : 0);
								break;
							case 'T':
							case 't':
								pathQuadBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0);
								break;
							default:
								if (nargs >= 2)
								{
									cpx = args[nargs-2];
									cpy = args[nargs-1];
								}
								break;
						}
						nargs = 0;
					}
				}
				else
				{
					cmd = item[0];
					rargs = getArgsPerElement(cmd);
					if (cmd == 'M' || cmd == 'm')
					{
						// Commit path.
						if (p->nbuf)
							svgCreatePath(p, closedFlag);
						// Start new subpath.
						svgResetPath(p);
						closedFlag = 0;
						nargs = 0;
						cpx = 0; cpy = 0;
					}
					else if (cmd == 'Z' || cmd == 'z')
					{
						closedFlag = 1;
						// Commit path.
						if (p->nbuf)
							svgCreatePath(p, closedFlag);
						// Start new subpath.
						svgResetPath(p);
						closedFlag = 0;
						nargs = 0;
					}
				}
			}
			
			// Commit path.
			if (p->nbuf)
				svgCreatePath(p, closedFlag);
			
		}
		else
		{
			tmp[0] = attr[i];
			tmp[1] = attr[i + 1];
			tmp[2] = 0;
			tmp[3] = 0;
			svgParseAttribs(p, tmp);
		}
	}
}

static void svgParseRect(struct SVGParser* p, const char** attr)
{
	float x = 0.0f;
	float y = 0.0f;
	float w = 0.0f;
	float h = 0.0f;
	int i;
	
	for (i = 0; attr[i]; i += 2)
	{
		if (!svgParseAttr(p, attr[i], attr[i + 1]))
		{
			if (strcmp(attr[i], "x") == 0) x = parseFloat(attr[i+1]);
			if (strcmp(attr[i], "y") == 0) y = parseFloat(attr[i+1]);
			if (strcmp(attr[i], "width") == 0) w = parseFloat(attr[i+1]);
			if (strcmp(attr[i], "height") == 0) h = parseFloat(attr[i+1]);
		}
	}
	
	if (w != 0.0f && h != 0.0f)
	{
		svgResetPath(p);

		svgPathPoint(p, x, y);
		svgPathPoint(p, x+w, y);
		svgPathPoint(p, x+w, y+h);
		svgPathPoint(p, x, y+h);
		
		svgCreatePath(p, 1);
	}
}

static void svgParseCircle(struct SVGParser* p, const char** attr)
{
	float cx = 0.0f;
	float cy = 0.0f;
	float r = 0.0f;
	float da;
	int i,n;
	float x,y,u;
	
	for (i = 0; attr[i]; i += 2)
	{
		if (!svgParseAttr(p, attr[i], attr[i + 1]))
		{
			if (strcmp(attr[i], "cx") == 0) cx = parseFloat(attr[i+1]);
			if (strcmp(attr[i], "cy") == 0) cy = parseFloat(attr[i+1]);
			if (strcmp(attr[i], "r") == 0) r = fabsf(parseFloat(attr[i+1]));
		}
	}
	
	if (r != 0.0f)
	{
		svgResetPath(p);

		da = acosf(r/(r+p->tol))*2;
		n = (int)ceilf(M_PI*2/da);

		da = (float)(M_PI*2)/n;
		for (i = 0; i < n; ++i)
		{
			u = i*da;
			x = cx + cosf(u)*r;
			y = cy + sinf(u)*r;
			svgPathPoint(p, x, y);
		}
		
		svgCreatePath(p, 1);
	}
}

static void svgParseLine(struct SVGParser* p, const char** attr)
{
	float x1 = 0.0;
	float y1 = 0.0;
	float x2 = 0.0;
	float y2 = 0.0;
	int i;
	
	for (i = 0; attr[i]; i += 2)
	{
		if (!svgParseAttr(p, attr[i], attr[i + 1]))
		{
			if (strcmp(attr[i], "x1") == 0) x1 = parseFloat(attr[i + 1]);
			if (strcmp(attr[i], "y1") == 0) y1 = parseFloat(attr[i + 1]);
			if (strcmp(attr[i], "x2") == 0) x2 = parseFloat(attr[i + 1]);
			if (strcmp(attr[i], "y2") == 0) y2 = parseFloat(attr[i + 1]);
		}
	}
	
	svgResetPath(p);
	
	svgPathPoint(p, x1, y1);
	svgPathPoint(p, x2, y2);
	
	svgCreatePath(p, 0);
}

static void svgParsePoly(struct SVGParser* p, const char** attr, int closeFlag)
{
	int i;
	const char* s;
	float args[2];
	int nargs;
	char item[64];
	
	svgResetPath(p);
	
	for (i = 0; attr[i]; i += 2)
	{
		if (!svgParseAttr(p, attr[i], attr[i + 1]))
		{
			if (strcmp(attr[i], "points") == 0) 
			{
				s = attr[i + 1];
				nargs = 0;
				while (*s)
				{
					s = getNextPathItem(s, item);
					args[nargs++] = (float)atof(item);
					if (nargs >= 2)
					{
						svgPathPoint(p, args[0], args[1]);
						nargs = 0;
					}
				}
			}
		}
	}
	
	svgCreatePath(p, closeFlag);
}

static void svgStartElement(void* ud, const char* el, const char** attr)
{
	struct SVGParser* p = (struct SVGParser*)ud;
	
	// Skip everything in defs
	if (p->defsFlag)
		return;
	
	if (strcmp(el, "g") == 0)
	{
		svgPushAttr(p);
		svgParseAttribs(p, attr);
	}
	else if (strcmp(el, "path") == 0)
	{
		if (p->pathFlag)	// Do not allow nested paths.
			return;
		svgPushAttr(p);
		svgParsePath(p, attr);
		p->pathFlag = 1;
		svgPopAttr(p);
	}
	else if (strcmp(el, "rect") == 0) 
	{
		svgPushAttr(p);
		svgParseRect(p, attr);
		svgPopAttr(p);
	}
	else if (strcmp(el, "circle") == 0) 
	{
		svgPushAttr(p);
		svgParseCircle(p, attr);
		svgPopAttr(p);
	}
	else if (strcmp(el, "line") == 0) 
	{
		svgPushAttr(p);
		svgParseLine(p, attr);
		svgPopAttr(p);
	}
	else if (strcmp(el, "polyline") == 0) 
	{
		svgPushAttr(p);
		svgParsePoly(p, attr, 0);
		svgPopAttr(p);
	}
	else if (strcmp(el, "polygon") == 0) 
	{
		svgPushAttr(p);
		svgParsePoly(p, attr, 1);
		svgPopAttr(p);
	}
	else if (strcmp(el, "defs") == 0)
	{
		p->defsFlag = 1;
	}
}

static void svgEndElement(void* ud, const char* el)
{
	struct SVGParser* p = (struct SVGParser*)ud;
	
	if (strcmp(el, "g") == 0)
	{
		svgPopAttr(p);
	}
	else if (strcmp(el, "path") == 0)
	{
		p->pathFlag = 0;
	}
	else if (strcmp(el, "defs") == 0)
	{
		p->defsFlag = 0;
	}
}

static void svgContent(void* ud, const char* s)
{
	// empty
}

struct SVGPath* svgParse(char* input)
{
	struct SVGParser* p;
	struct SVGPath* ret = 0;
	
	p = svgCreateParser();
	if (!p)
		return 0;

	p->tol = 1.0f;

	parsexml(input, svgStartElement, svgEndElement, svgContent, p);

	if (p->buf)
	{
		free(p->buf);
		p->buf = NULL;
		p->nbuf = 0;
		p->cbuf = 0;
	}

	ret = p->plist;
	p->plist = 0;
	
	svgDeleteParser(p);

	return ret;
}

struct SVGPath* svgParseFromFile(const char* filename)
{
	FILE* fp;
	int size;
	char* data;
	struct SVGPath* plist;

	fp = fopen(filename, "rb");
	if (!fp) return 0;
	fseek(fp, 0, SEEK_END);
	size = ftell(fp);
	fseek(fp, 0, SEEK_SET);
	data = (char*)malloc(size+1);
	fread(data, size, 1, fp);
	data[size] = '\0';	// Must be null terminated.
	fclose(fp);
	plist = svgParse(data);
	free(data);
	return plist;
}

void svgDelete(struct SVGPath* plist)
{
	struct SVGPath* path;
	struct SVGPath* next;
	if (!plist)
		return;
	path = plist;
	while (path)
	{
		next = path->next;
		if (path->pts)
			free(path->pts);
		free(path);
		path = next;
	}
}
