⛏️ index : haiku.git

author Adrien Destugues <pulkomandy@pulkomandy.tk> 2020-05-10 17:14:39.0 +02:00:00
committer waddlesplash <waddlesplash@gmail.com> 2020-05-22 14:08:38.0 +00:00:00
commit
8fd4dad78c5ce70849a62902b4aec00a2cd4ebb0 [patch]
tree
b01908ca9929f633f09d0e37f9bb5edfec23b3f1
parent
f151ff0b0966e841a14843e734a8d4e66b6cb6f9
download
8fd4dad78c5ce70849a62902b4aec00a2cd4ebb0.tar.gz

Icon-O-Matic: replace SVG parser with nanosvg

The parser was based on an old example from agg. NanoSVG is originally
based on the same code, but has lots of bugfixes. So it makes sense
to use it.

Nanosvg revision 25241c5a8f8451d41ab1b02ab2d865b01600d949

Fixes #5955, #8586, #13021.

Change-Id: I38ff9aa4e1d403c41979ebe42f7b45d4500a870c
Reviewed-on: https://review.haiku-os.org/c/haiku/+/2661
Reviewed-by: waddlesplash <waddlesplash@gmail.com>
(cherry picked from commit 2a3b385efba22655c4d6ff61ed59203283392ad7)
Reviewed-on: https://review.haiku-os.org/c/haiku/+/2774

Diff

 src/apps/icon-o-matic/Jamfile                               |    3 ---
 src/libs/icon/style/GradientTransformable.cpp               |    2 ++
 src/libs/icon/transformable/Transformable.cpp               |    9 +++++++++
 src/libs/icon/transformable/Transformable.h                 |    2 ++
 src/apps/icon-o-matic/import_export/svg/DocumentBuilder.cpp |  836 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
 src/apps/icon-o-matic/import_export/svg/DocumentBuilder.h   |  208 +++++---------------------------------------------------------------------------
 src/apps/icon-o-matic/import_export/svg/PathTokenizer.cpp   |  157 --------------------------------------------------------------------------------
 src/apps/icon-o-matic/import_export/svg/PathTokenizer.h     |  106 --------------------------------------------------------------------------------
 src/apps/icon-o-matic/import_export/svg/SVGGradients.cpp    |  199 --------------------------------------------------------------------------------
 src/apps/icon-o-matic/import_export/svg/SVGGradients.h      |   81 --------------------------------------------------------------------------------
 src/apps/icon-o-matic/import_export/svg/SVGImporter.cpp     |   23 +++++++++++------------
 src/apps/icon-o-matic/import_export/svg/SVGParser.cpp       | 1152 --------------------------------------------------------------------------------
 src/apps/icon-o-matic/import_export/svg/SVGParser.h         |  105 --------------------------------------------------------------------------------
 src/apps/icon-o-matic/import_export/svg/nanosvg.h           | 2975 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 14 files changed, 3167 insertions(+), 2691 deletions(-)

diff --git a/src/apps/icon-o-matic/Jamfile b/src/apps/icon-o-matic/Jamfile
index 14a10e0..b2d19f4 100644
--- a/src/apps/icon-o-matic/Jamfile
+++ b/src/apps/icon-o-matic/Jamfile
@@ -233,11 +233,8 @@

	# import_export/svg
	DocumentBuilder.cpp
	PathTokenizer.cpp
	SVGExporter.cpp
	SVGGradients.cpp
	SVGImporter.cpp
	SVGParser.cpp

	# shape
	PathManipulator.cpp
diff --git a/src/libs/icon/style/GradientTransformable.cpp b/src/libs/icon/style/GradientTransformable.cpp
index 0ce4c3f..2bfc42d 100644
--- a/src/libs/icon/style/GradientTransformable.cpp
+++ b/src/libs/icon/style/GradientTransformable.cpp
@@ -601,6 +601,8 @@
			   step->color.blue,
			   step->color.alpha);
	}

	Transformable::PrintToStream();
}

// _MakeEmpty
diff --git a/src/libs/icon/transformable/Transformable.cpp b/src/libs/icon/transformable/Transformable.cpp
index a2c2cfe..ac76b6b 100644
--- a/src/libs/icon/transformable/Transformable.cpp
+++ b/src/libs/icon/transformable/Transformable.cpp
@@ -319,3 +319,12 @@
	// default implementation doesn't care
}


void
Transformable::PrintToStream() const
{
	double matrix[6];
	store_to(matrix);
	printf("Transform:\n%f\t%f\t%f\n%f\t%f\t%f\n",
		matrix[0], matrix[2], matrix[4], matrix[1], matrix[3], matrix[5]);
}
diff --git a/src/libs/icon/transformable/Transformable.h b/src/libs/icon/transformable/Transformable.h
index aac1740..9d7518e 100644
--- a/src/libs/icon/transformable/Transformable.h
+++ b/src/libs/icon/transformable/Transformable.h
@@ -70,6 +70,8 @@
	virtual	void				TransformationChanged();
		// hook function that is called when the transformation
		// is changed for some reason
	
	virtual void				PrintToStream() const;
};


diff --git a/src/apps/icon-o-matic/import_export/svg/DocumentBuilder.cpp b/src/apps/icon-o-matic/import_export/svg/DocumentBuilder.cpp
index 39fd6d5..186eeb2 100644
--- a/src/apps/icon-o-matic/import_export/svg/DocumentBuilder.cpp
+++ b/src/apps/icon-o-matic/import_export/svg/DocumentBuilder.cpp
@@ -6,21 +6,8 @@
 *		Stephan Aßmus <superstippi@gmx.de>
 */

//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.2
// Copyright (C) 2002-2004 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
//		  mcseemagg@yahoo.com
//		  http://www.antigrain.com
//----------------------------------------------------------------------------

#define NANOSVG_IMPLEMENTATION
#include "DocumentBuilder.h"

#include <new>
@@ -28,8 +15,6 @@

#include <Bitmap.h>

#include <agg_bounding_rect.h>

#include "AutoDeleter.h"
#include "GradientTransformable.h"
#include "Icon.h"
@@ -39,166 +24,24 @@
#include "StrokeTransformer.h"
#include "Style.h"
#include "StyleContainer.h"
#include "SVGGradients.h"
#include "SVGImporter.h"
#include "VectorPath.h"

using std::nothrow;
#include <agg_math_stroke.h>
#include <agg_trans_affine.h>

namespace agg {
namespace svg {
using std::nothrow;

// constructor
DocumentBuilder::DocumentBuilder()
	: fGradients(20),
	  fCurrentGradient(NULL),
	  fWidth(0),
DocumentBuilder::DocumentBuilder(NSVGimage* source)
	: fWidth(0),
	  fHeight(0),
	  fViewBox(0.0, 0.0, -1.0, -1.0),
	  fTitle("")
{
}


// remove_all
void
DocumentBuilder::remove_all()
{
	fPathStorage.remove_all();
	fAttributesStorage.remove_all();
	fAttributesStack.remove_all();
	fTransform.reset();
}

// begin_path
void
DocumentBuilder::begin_path()
{
	push_attr();
	unsigned idx = fPathStorage.start_new_path();
	fAttributesStorage.add(path_attributes(cur_attr(), idx));
}

// end_path
void
DocumentBuilder::end_path()
{
	if (fAttributesStorage.size() == 0) {
		throw exception("end_path: The path was not begun");
	}
	path_attributes attr = cur_attr();
	unsigned idx = fAttributesStorage[fAttributesStorage.size() - 1].index;
	attr.index = idx;
	fAttributesStorage[fAttributesStorage.size() - 1] = attr;
	pop_attr();
}

// move_to
void
DocumentBuilder::move_to(double x, double y, bool rel)		  // M, m
{
	if (rel)
		fPathStorage.move_rel(x, y);
	else
		fPathStorage.move_to(x, y);
}

// line_to
void
DocumentBuilder::line_to(double x,  double y, bool rel)		 // L, l
{
	if (rel)
		fPathStorage.line_rel(x, y);
	else
		fPathStorage.line_to(x, y);
}

// hline_to
void
DocumentBuilder::hline_to(double x, bool rel)				   // H, h
{
	if (rel)
		fPathStorage.hline_rel(x);
	else
		fPathStorage.hline_to(x);
}

// vline_to
void
DocumentBuilder::vline_to(double y, bool rel)				   // V, v
{
	if (rel)
		fPathStorage.vline_rel(y);
	else
		fPathStorage.vline_to(y);
}

// curve3
void
DocumentBuilder::curve3(double x1, double y1,				   // Q, q
						double x,  double y, bool rel)
{
	if (rel)
		fPathStorage.curve3_rel(x1, y1, x, y);
	else
		fPathStorage.curve3(x1, y1, x, y);
}

// curve3
void
DocumentBuilder::curve3(double x, double y, bool rel)		   // T, t
{
	if (rel)
		fPathStorage.curve3_rel(x, y);
	else
		fPathStorage.curve3(x, y);
}

// curve4
void
DocumentBuilder::curve4(double x1, double y1,				   // C, c
						double x2, double y2,
						double x,  double y, bool rel)
{
	if (rel) {
		fPathStorage.curve4_rel(x1, y1, x2, y2, x, y);
	} else {
		fPathStorage.curve4(x1, y1, x2, y2, x, y);
	}
}

// curve4
void
DocumentBuilder::curve4(double x2, double y2,				   // S, s
						double x,  double y, bool rel)
{
	if (rel) {
		fPathStorage.curve4_rel(x2, y2, x, y);
	} else {
		fPathStorage.curve4(x2, y2, x, y);
	}
}

// elliptical_arc
void
DocumentBuilder::elliptical_arc(double rx, double ry, double angle,
							    bool large_arc_flag, bool sweep_flag,
							    double x, double y, bool rel)
	  fTitle(""),
	  fSource(source)
{
	angle = angle / 180.0 * pi;
	if (rel) {
		fPathStorage.arc_rel(rx, ry, angle, large_arc_flag, sweep_flag, x, y);
	} else {
		fPathStorage.arc_to(rx, ry, angle, large_arc_flag, sweep_flag, x, y);
	}
}

// close_subpath
void
DocumentBuilder::close_subpath()
{
	fPathStorage.end_poly(path_flags_close);
}

// SetTitle
void
@@ -214,244 +57,6 @@
	fWidth = width;
	fHeight = height;
	fViewBox = viewBox;
}

// cur_attr
path_attributes&
DocumentBuilder::cur_attr()
{
	if (fAttributesStack.size() == 0) {
		throw exception("cur_attr: Attribute stack is empty");
	}
	return fAttributesStack[fAttributesStack.size() - 1];
}

// push_attr
void
DocumentBuilder::push_attr()
{
//printf("DocumentBuilder::push_attr() (size: %d)\n", fAttributesStack.size());
	fAttributesStack.add(fAttributesStack.size() ? fAttributesStack[fAttributesStack.size() - 1]
												 : path_attributes());
}

// pop_attr
void
DocumentBuilder::pop_attr()
{
//printf("DocumentBuilder::pop_attr() (size: %d)\n", fAttributesStack.size());
	if (fAttributesStack.size() == 0) {
		throw exception("pop_attr: Attribute stack is empty");
	}
	fAttributesStack.remove_last();
}

// fill
void
DocumentBuilder::fill(const rgba8& f)
{
	path_attributes& attr = cur_attr();
	attr.fill_color = f;
	attr.fill_flag = true;
}

// stroke
void
DocumentBuilder::stroke(const rgba8& s)
{
	path_attributes& attr = cur_attr();
	attr.stroke_color = s;
	attr.stroke_flag = true;
}

// even_odd
void
DocumentBuilder::even_odd(bool flag)
{
	cur_attr().even_odd_flag = flag;
}

// stroke_width
void
DocumentBuilder::stroke_width(double w)
{
	path_attributes& attr = cur_attr();
	attr.stroke_width = w;
	attr.stroke_flag = true;
}

// fill_none
void
DocumentBuilder::fill_none()
{
	cur_attr().fill_flag = false;
}

// fill_url
void
DocumentBuilder::fill_url(const char* url)
{
	sprintf(cur_attr().fill_url, "%s", url);
}

// stroke_none
void
DocumentBuilder::stroke_none()
{
	cur_attr().stroke_flag = false;
}

// stroke_url
void
DocumentBuilder::stroke_url(const char* url)
{
	sprintf(cur_attr().stroke_url, "%s", url);
}

// opacity
void
DocumentBuilder::opacity(double op)
{
	cur_attr().opacity *= op;
//printf("opacity: %.1f\n", cur_attr().opacity);
}

// fill_opacity
void
DocumentBuilder::fill_opacity(double op)
{
	cur_attr().fill_color.opacity(op);
//	cur_attr().opacity *= op;
}

// stroke_opacity
void
DocumentBuilder::stroke_opacity(double op)
{
	cur_attr().stroke_color.opacity(op);
//	cur_attr().opacity *= op;
}

// line_join
void
DocumentBuilder::line_join(line_join_e join)
{
	cur_attr().line_join = join;
}

// line_cap
void
DocumentBuilder::line_cap(line_cap_e cap)
{
	cur_attr().line_cap = cap;
}

// miter_limit
void
DocumentBuilder::miter_limit(double ml)
{
	cur_attr().miter_limit = ml;
}

// transform
trans_affine&
DocumentBuilder::transform()
{
	return cur_attr().transform;
}

// parse_path
void
DocumentBuilder::parse_path(PathTokenizer& tok)
{
	char lastCmd = 0;
	while(tok.next()) {
		double arg[10];
		char cmd = tok.last_command();
		unsigned i;
		switch(cmd) {
			case 'M': case 'm':
				arg[0] = tok.last_number();
				arg[1] = tok.next(cmd);
				if (lastCmd != cmd)
					move_to(arg[0], arg[1], cmd == 'm');
				else
					line_to(arg[0], arg[1], lastCmd == 'm');
				break;

			case 'L': case 'l':
				arg[0] = tok.last_number();
				arg[1] = tok.next(cmd);
				line_to(arg[0], arg[1], cmd == 'l');
				break;

			case 'V': case 'v':
				vline_to(tok.last_number(), cmd == 'v');
				break;

			case 'H': case 'h':
				hline_to(tok.last_number(), cmd == 'h');
				break;

			case 'Q': case 'q':
				arg[0] = tok.last_number();
				for(i = 1; i < 4; i++) {
					arg[i] = tok.next(cmd);
				}
				curve3(arg[0], arg[1], arg[2], arg[3], cmd == 'q');
				break;

			case 'T': case 't':
				arg[0] = tok.last_number();
				arg[1] = tok.next(cmd);
				curve3(arg[0], arg[1], cmd == 't');
				break;

			case 'C': case 'c':
				arg[0] = tok.last_number();
				for(i = 1; i < 6; i++) {
					arg[i] = tok.next(cmd);
				}
				curve4(arg[0], arg[1], arg[2], arg[3], arg[4], arg[5], cmd == 'c');
				break;

			case 'S': case 's':
				arg[0] = tok.last_number();
				for(i = 1; i < 4; i++) {
					arg[i] = tok.next(cmd);
				}
				curve4(arg[0], arg[1], arg[2], arg[3], cmd == 's');
				break;

			case 'A': case 'a': {
				arg[0] = tok.last_number();
				for(i = 1; i < 3; i++) {
					arg[i] = tok.next(cmd);
				}
				bool large_arc_flag = (bool)tok.next(cmd);
				bool sweep_flag = (bool)tok.next(cmd);
				for(i = 3; i < 5; i++) {
					arg[i] = tok.next(cmd);
				}
				elliptical_arc(arg[0], arg[1], arg[2],
							   large_arc_flag, sweep_flag,
							   arg[3], arg[4], cmd == 'a');
				break;
			}

			case 'Z': case 'z':
				close_subpath();
				break;

			default:
			{
				char buf[100];
				sprintf(buf, "parse_path: Invalid path command '%c'", cmd);
				throw exception(buf);
			}
		}
		lastCmd = cmd;
	}
}

// #pragma mark -
@@ -461,22 +66,10 @@
DocumentBuilder::GetIcon(Icon* icon, SVGImporter* importer,
						 const char* fallbackName)
{
	double xMin;
	double yMin;
	double xMax;
	double yMax;

	int32 pathCount = fAttributesStorage.size();

	agg::conv_transform<agg::path_storage> transformedPaths(
		fPathStorage, fTransform);
	agg::bounding_rect(transformedPaths, *this, 0, pathCount,
					   &xMin, &yMin, &xMax, &yMax);

	xMin = floor(xMin);
	yMin = floor(yMin);
	xMax = ceil(xMax);
	yMax = ceil(yMax);
	double xMin = 0;
	double yMin = 0;
	double xMax = ceil(fSource->width);
	double yMax = ceil(fSource->height);

	BRect bounds;
	if (fViewBox.IsValid()) {
@@ -497,7 +90,7 @@
bounds.PrintToStream();
	}

	float size = min_c(bounds.Width() + 1.0, bounds.Height() + 1.0);
	float size = max_c(bounds.Width(), bounds.Height());
	double scale = 64.0 / size;
printf("scale: %f\n", scale);

@@ -509,16 +102,12 @@
//		icon->SetName(fTitle.String());
//	else
//		icon->SetName(fallbackName);

	for (int32 i = 0; i < pathCount; i++) {

		path_attributes& attributes = fAttributesStorage[i];

		if (attributes.fill_flag)
			_AddShape(attributes, false, transform, icon);

		if (attributes.stroke_flag)
			_AddShape(attributes, true, transform, icon);
	for (NSVGshape* shape = fSource->shapes; shape != NULL; shape = shape->next) {
		if (shape->fill.type != NSVG_PAINT_NONE)
			_AddShape(shape, false, transform, icon);
		if (shape->stroke.type != NSVG_PAINT_NONE)
			_AddShape(shape, true, transform, icon);
	}

	// clean up styles and paths (remove duplicates)
@@ -541,212 +130,61 @@
	}

	return B_OK;
}

// StartGradient
void
DocumentBuilder::StartGradient(bool radial)
{
	if (fCurrentGradient) {
		fprintf(stderr, "DocumentBuilder::StartGradient() - ERROR: "
						"previous gradient (%s) not finished!\n",
						fCurrentGradient->ID());
	}

	if (radial)
		fCurrentGradient = new SVGRadialGradient();
	else
		fCurrentGradient = new SVGLinearGradient();

	_AddGradient(fCurrentGradient);
}

// EndGradient
void
DocumentBuilder::EndGradient()
{
	if (fCurrentGradient) {
//		fCurrentGradient->PrintToStream();
	} else {
		fprintf(stderr, "DocumentBuilder::EndGradient() - "
				"ERROR: no gradient started!\n");
	}
	fCurrentGradient = NULL;
}

// #pragma mark -

// _AddGradient
void
DocumentBuilder::_AddGradient(SVGGradient* gradient)
{
	if (gradient) {
		fGradients.AddItem((void*)gradient);
	}
}

// _GradientAt
SVGGradient*
DocumentBuilder::_GradientAt(int32 index) const
{
	return (SVGGradient*)fGradients.ItemAt(index);
}

// _FindGradient
SVGGradient*
DocumentBuilder::_FindGradient(const char* name) const
{
	for (int32 i = 0; SVGGradient* g = _GradientAt(i); i++) {
		if (strcmp(g->ID(), name) == 0)
			return g;
	}
	return NULL;
}


// AddVertexSource
template<class VertexSource>
status_t
AddPathsFromVertexSource(Icon* icon, Shape* shape,
						 VertexSource& source, int32 index)
AddPathsFromVertexSource(Icon* icon, Shape* shape, NSVGshape* svgShape)
{
//printf("AddPathsFromVertexSource(pathID = %ld)\n", index);

	// start with the first path
	VectorPath* path = new (nothrow) VectorPath();
	if (!path || !icon->Paths()->AddPath(path)) {
		delete path;
		return B_NO_MEMORY;
	}
	for (NSVGpath* svgPath = svgShape->paths; svgPath != NULL;
		svgPath = svgPath->next) {
		VectorPath* path = new (nothrow) VectorPath();
		if (!path || !icon->Paths()->AddPath(path)) {
			delete path;
			return B_NO_MEMORY;
		}

	if (!shape->Paths()->AddPath(path))
		return B_NO_MEMORY;
		if (!shape->Paths()->AddPath(path))
			return B_NO_MEMORY;

	source.rewind(index);
	double x1 = 0, y1 = 0;
	unsigned cmd = source.vertex(&x1, &y1);
	bool keepGoing = true;
	int32 subPath = 0;
	while (keepGoing) {
		if (agg::is_next_poly(cmd)) {
//printf("next polygon\n");
			if (agg::is_end_poly(cmd)) {
//printf("  end polygon\n");
				path->SetClosed(true);
				subPath++;
			} else {
//printf("  not end polygon\n");
			}
		path->SetClosed(svgPath->closed);

 			if (agg::is_stop(cmd)) {
//printf("  stop = true\n");
 				keepGoing = false;
			} else {
				if (subPath > 0) {
//printf("  new subpath\n");
					path->CleanUp();
					if (path->CountPoints() == 0) {
//printf("  path with no points!\n");
						icon->Paths()->RemovePath(path);
						shape->Paths()->RemovePath(path);
						path->ReleaseReference();
					}
					path = new (nothrow) VectorPath();
					if (!path || !icon->Paths()->AddPath(path)) {
						delete path;
						return B_NO_MEMORY;
					}
					if (!shape->Paths()->AddPath(path))
						return B_NO_MEMORY;
				}
			}
		}
		switch (cmd) {
			case agg::path_cmd_move_to:
//printf("move to (%.2f, %.2f) (subPath: %ld)\n", x1, y1, subPath);
				if (path->CountPoints() > 0) {
					// cannot MoveTo on a path that has already points!
					path->CleanUp();
					path = new (nothrow) VectorPath();
					if (!path || !icon->Paths()->AddPath(path)) {
						delete path;
						return B_NO_MEMORY;
					}
					if (!shape->Paths()->AddPath(path))
						return B_NO_MEMORY;
				}
				if (!path->AddPoint(BPoint(x1, y1)))
					return B_NO_MEMORY;
				path->SetInOutConnected(path->CountPoints() - 1, false);
				break;
		int pointCount = svgPath->npts;
		float* points = svgPath->pts;

			case agg::path_cmd_line_to:
//printf("line to (%.2f, %.2f) (subPath: %ld)\n", x1, y1, subPath);
				if (!path->AddPoint(BPoint(x1, y1)))
					return B_NO_MEMORY;
				path->SetInOutConnected(path->CountPoints() - 1, false);
				break;
		// First entry in the points list is always a "move" to the path
		// starting point
		if (!path->AddPoint(BPoint(points[0], points[1])))
			return B_NO_MEMORY;
		path->SetInOutConnected(path->CountPoints() - 1, false);

			case agg::path_cmd_curve3: {
				double x2 = 0, y2 = 0;
				cmd = source.vertex(&x2, &y2);
//printf("curve3 (%.2f, %.2f)\n", x1, y1);
//printf("	   (%.2f, %.2f)\n", x2, y2);

				// convert to curve4 for easier editing
				int32 start = path->CountPoints() - 1;
				BPoint from;
				path->GetPointAt(start, from);

				double cx2 = (1.0/3.0) * from.x + (2.0/3.0) * x1;
				double cy2 = (1.0/3.0) * from.y + (2.0/3.0) * y1;
				double cx3 = (2.0/3.0) * x1 + (1.0/3.0) * x2;
				double cy3 = (2.0/3.0) * y1 + (1.0/3.0) * y2;

				path->SetPointOut(start, BPoint(cx2, cy2));

				if (!path->AddPoint(BPoint(x2, y2)))
					return B_NO_MEMORY;

				int32 end = path->CountPoints() - 1;
				path->SetInOutConnected(end, false);
				path->SetPointIn(end, BPoint(cx3, cy3));
				break;
			}
		pointCount--;
		points += 2;

			case agg::path_cmd_curve4: {
				double x2 = 0, y2 = 0;
				double x3 = 0, y3 = 0;
				cmd = source.vertex(&x2, &y2);
				cmd = source.vertex(&x3, &y3);
		while (pointCount > 0) {
			BPoint vector1(points[0], points[1]);
			BPoint vector2(points[2], points[3]);
			BPoint endPoint(points[4], points[5]);

				if (!path->AddPoint(BPoint(x3, y3)))
					return B_NO_MEMORY;
			if (!path->AddPoint(endPoint))
				return B_NO_MEMORY;

				int32 start = path->CountPoints() - 2;
				int32 end = path->CountPoints() - 1;
			int32 start = path->CountPoints() - 2;
			int32 end = path->CountPoints() - 1;

//printf("curve4 [%ld] (%.2f, %.2f) -> [%ld] (%.2f, %.2f) -> (%.2f, %.2f)\n", start, x1, y1, end, x2, y2, x3, y3);
			path->SetInOutConnected(end, false);
			path->SetPointOut(start, vector1);
			path->SetPointIn(end, vector2);

				path->SetInOutConnected(end, false);
				path->SetPointOut(start, BPoint(x1, y1));
				path->SetPointIn(end, BPoint(x2, y2));
				break;
			}
			default:
//printf("unkown command\n");
				break;
			pointCount -= 3;
			points += 6;
		}
		cmd = source.vertex(&x1, &y1);
	}
//path->PrintToStream();
	path->CleanUp();
	if (path->CountPoints() == 0) {
//printf("path with no points!\n");
		icon->Paths()->RemovePath(path);
		shape->Paths()->RemovePath(path);
		path->ReleaseReference();
	}

	// FIXME handle closed vs open paths

	return B_OK;
}
@@ -754,7 +192,7 @@

// _AddShape
status_t
DocumentBuilder::_AddShape(path_attributes& attributes, bool outline,
DocumentBuilder::_AddShape(NSVGshape* svgShape, bool outline,
						   const Transformable& transform, Icon* icon)
{
	Shape* shape = new (nothrow) Shape(NULL);
@@ -763,20 +201,43 @@
		return B_NO_MEMORY;
	}

	if (AddPathsFromVertexSource(icon, shape, fPathStorage, attributes.index) < B_OK)
	if (AddPathsFromVertexSource(icon, shape, svgShape) < B_OK)
		printf("failed to convert from vertex source\n");

	shape->multiply(attributes.transform);
	shape->SetName(svgShape->id);
	shape->Multiply(transform);

	StrokeTransformer* stroke = NULL;
	NSVGpaint* paint = NULL;
	if (outline) {
		stroke = new (nothrow) StrokeTransformer(shape->VertexSource());
		paint = &svgShape->stroke;

		if (stroke) {
			stroke->width(attributes.stroke_width);
			stroke->line_cap(attributes.line_cap);
			stroke->line_join(attributes.line_join);
			stroke->width(svgShape->strokeWidth);
			switch(svgShape->strokeLineCap) {
				case NSVG_CAP_BUTT:
					stroke->line_cap(agg::butt_cap);
					break;
				case NSVG_CAP_ROUND:
					stroke->line_cap(agg::round_cap);
					break;
				case NSVG_CAP_SQUARE:
					stroke->line_cap(agg::square_cap);
					break;
			}

			switch(svgShape->strokeLineJoin) {
				case NSVG_JOIN_MITER:
					stroke->line_join(agg::miter_join);
					break;
				case NSVG_JOIN_ROUND:
					stroke->line_join(agg::round_join);
					break;
				case NSVG_JOIN_BEVEL:
					stroke->line_join(agg::bevel_join);
					break;
			}
		}

		if (!shape->AddTransformer(stroke)) {
@@ -784,45 +245,83 @@
			stroke = NULL;
		}
	} else {
//		if (attributes.even_odd_flag)
//			shape->SetFillingRule(FILL_MODE_EVEN_ODD);
//		else
//			shape->SetFillingRule(FILL_MODE_NON_ZERO);
	}


	Gradient* gradient = NULL;
	SVGGradient* g = NULL;
	const char* url = outline ? attributes.stroke_url : attributes.fill_url;
	if (url[0] != 0) {
		g = _FindGradient(url);
		if (g != NULL)
			gradient = g->GetGradient(shape->Bounds());
		paint = &svgShape->fill;
#if 0 // FIXME filling rule are missing from Shape class

		if (svgShape->fillRule == NSVG_FILLRULE_EVENODD)
			shape->SetFillingRule(FILL_MODE_EVEN_ODD);
		else
			shape->SetFillingRule(FILL_MODE_NON_ZERO);
#endif
	}

	Gradient gradient(true);
	rgb_color color;
	switch(paint->type) {
		case NSVG_PAINT_COLOR:
			color.red = paint->color & 0xFF;
			color.green = (paint->color >> 8) & 0xFF;
			color.blue = (paint->color >> 16) & 0xFF;
			color.alpha = (paint->color >> 24) & 0xFF;
			break;
		case NSVG_PAINT_LINEAR_GRADIENT:
		{
			gradient.SetType(GRADIENT_LINEAR);
			// The base gradient axis in Icon-O-Matic is a horizontal line from
			// (-64, 0) to (64, 0). The base gradient axis used by nanosvg is
			// a vertical line from (0, 0) to (0, 1). This initial transform
			// converts from one space to the other.
			agg::trans_affine baseTransform(0, 1.0/128.0, -1.0/128.0, 0,
				-0.5, 0.5);
			gradient.multiply(baseTransform);
			break;
		}
		case NSVG_PAINT_RADIAL_GRADIENT:
		{
			gradient.SetType(GRADIENT_CIRCULAR);
			agg::trans_affine baseTransform(0, 1.0/64.0, -1.0/64.0, 0,
				0, 0);
			gradient.multiply(baseTransform);
			break;
		}
	}

	ObjectDeleter<Gradient> gradientDeleter(gradient);

	rgb_color color;
	if (paint->type != NSVG_PAINT_COLOR) {
		gradient.SetInterpolation(INTERPOLATION_LINEAR);
		agg::trans_affine gradientTransform(
			paint->gradient->xform[0], paint->gradient->xform[1],
			paint->gradient->xform[2], paint->gradient->xform[3],
			paint->gradient->xform[4], paint->gradient->xform[5]
		);

		// The transform from nanosvg converts a screen space coordinate into
		// a gradient offset. It is the inverse of what we need at this stage,
		// so we just invert it and multiply our base transform with it.
		gradient.multiply_inv(gradientTransform);

		// Finally, scale the gradient according to the global scaling to fit
		// the 64x64 box we work in.
		gradient.Multiply(*shape);

		for (int i = 0; i < paint->gradient->nstops; i++) {
			rgb_color stopColor;
			stopColor.red = paint->gradient->stops[i].color & 0xFF;
			stopColor.green = (paint->gradient->stops[i].color >> 8) & 0xFF;
			stopColor.blue = (paint->gradient->stops[i].color >> 16) & 0xFF;
			stopColor.alpha = (paint->gradient->stops[i].color >> 24) & 0xFF;
			gradient.AddColor(stopColor, paint->gradient->stops[i].offset);
		}

	BGradient::ColorStop* step;
	if (gradient && (step = gradient->ColorAt(0))) {
		color.red		= step->color.red;
		color.green		= step->color.green;
		color.blue		= step->color.blue;
	} else {
		if (outline) {
			color.red	= attributes.stroke_color.r;
			color.green	= attributes.stroke_color.g;
			color.blue	= attributes.stroke_color.b;
			color.alpha = (uint8)(attributes.stroke_color.a * attributes.opacity);
		} else {
			color.red	= attributes.fill_color.r;
			color.green	= attributes.fill_color.g;
			color.blue	= attributes.fill_color.b;
			color.alpha = (uint8)(attributes.fill_color.a * attributes.opacity);
		BGradient::ColorStop* step = gradient.ColorAt(0);
		if (step) {
			color.red		= step->color.red;
			color.green		= step->color.green;
			color.blue		= step->color.blue;
			color.alpha		= step->color.alpha;
		}
	}

	color.alpha = (uint8)(color.alpha * svgShape->opacity);

	Style* style = new (nothrow) Style(color);
	if (!style || !icon->Styles()->AddStyle(style)) {
		delete style;
@@ -831,31 +330,22 @@

	// NOTE: quick hack to freeze all transformations (only works because
	// paths and styles are not used by multiple shapes!!)
//	if (modifiers() & B_COMMAND_KEY) {
		int32 pathCount = shape->Paths()->CountPaths();
		for (int32 i = 0; i < pathCount; i++) {
			VectorPath* path = shape->Paths()->PathAtFast(i);
			path->ApplyTransform(*shape);
		}
		if (gradient)
			gradient->Multiply(*shape);
	int32 pathCount = shape->Paths()->CountPaths();
	for (int32 i = 0; i < pathCount; i++) {
		VectorPath* path = shape->Paths()->PathAtFast(i);
		path->ApplyTransform(*shape);
	}

		if (stroke)
			stroke->width(stroke->width() * shape->scale());
	if (stroke)
		stroke->width(stroke->width() * shape->scale());

		shape->Reset();
//	}
	if (paint->type != NSVG_PAINT_COLOR)
		style->SetGradient(&gradient);

	if (gradient) {
		style->SetGradient(gradient);
		style->SetName(g->ID());
	}
	shape->Reset();

	shape->SetStyle(style);

	return B_OK;
}

} // namespace svg
} // namespace agg

diff --git a/src/apps/icon-o-matic/import_export/svg/DocumentBuilder.h b/src/apps/icon-o-matic/import_export/svg/DocumentBuilder.h
index 6874f61..55760e6 100644
--- a/src/apps/icon-o-matic/import_export/svg/DocumentBuilder.h
+++ b/src/apps/icon-o-matic/import_export/svg/DocumentBuilder.h
@@ -6,41 +6,17 @@
 *		Stephan Aßmus <superstippi@gmx.de>
 */

//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.2
// Copyright (C) 2002-2004 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
//		  mcseemagg@yahoo.com
//		  http://www.antigrain.com
//----------------------------------------------------------------------------

#ifndef DOCUMENT_BUILD_H
#define DOCUMENT_BUILD_H

#include <stdio.h>

#include <List.h>
#include <Rect.h>
#include <String.h>

#include <agg_array.h>
#include <agg_color_rgba.h>
#include <agg_conv_transform.h>
#include <agg_conv_stroke.h>
#include <agg_conv_contour.h>
#include <agg_conv_curve.h>
#include <agg_path_storage.h>
#include <agg_rasterizer_scanline_aa.h>

#include "IconBuild.h"
#include "PathTokenizer.h"
#include "nanosvg.h"


class SVGImporter;
@@ -52,207 +28,33 @@

_USING_ICON_NAMESPACE

namespace agg {
namespace svg {

class SVGGradient;

// Basic path attributes
struct path_attributes {

	unsigned		index;
	rgba8			fill_color;
	rgba8			stroke_color;
	double			opacity;
	bool			fill_flag;
	bool			stroke_flag;
	bool			even_odd_flag;
	line_join_e		line_join;
	line_cap_e		line_cap;
	double			miter_limit;
	double			stroke_width;
	trans_affine	transform;

	char			stroke_url[64];
	char			fill_url[64];

	// Empty constructor
	path_attributes() :
		index			(0),
		fill_color		(rgba(0,0,0)),
		stroke_color	(rgba(0,0,0)),
		opacity			(1.0),
		fill_flag		(true),
		stroke_flag		(false),
		even_odd_flag	(false),
		line_join		(miter_join),
		line_cap		(butt_cap),
		miter_limit		(4.0),
		stroke_width	(1.0),
		transform		()
	{
		stroke_url[0] = 0;
		fill_url[0] = 0;
	}

	// Copy constructor
	path_attributes(const path_attributes& attr) :
		index			(attr.index),
		fill_color		(attr.fill_color),
		stroke_color	(attr.stroke_color),
		opacity			(attr.opacity),
		fill_flag		(attr.fill_flag),
		stroke_flag		(attr.stroke_flag),
		even_odd_flag	(attr.even_odd_flag),
		line_join		(attr.line_join),
		line_cap		(attr.line_cap),
		miter_limit		(attr.miter_limit),
		stroke_width	(attr.stroke_width),
		transform		(attr.transform)
	{
		sprintf(stroke_url, "%s", attr.stroke_url);
		sprintf(fill_url, "%s", attr.fill_url);
	}

	// Copy constructor with new index value
	path_attributes(const path_attributes& attr, unsigned idx) :
		index			(idx),
		fill_color		(attr.fill_color),
		stroke_color	(attr.stroke_color),
		fill_flag		(attr.fill_flag),
		stroke_flag		(attr.stroke_flag),
		even_odd_flag	(attr.even_odd_flag),
		line_join		(attr.line_join),
		line_cap		(attr.line_cap),
		miter_limit		(attr.miter_limit),
		stroke_width	(attr.stroke_width),
		transform		(attr.transform)
	{
		sprintf(stroke_url, "%s", attr.stroke_url);
		sprintf(fill_url, "%s", attr.fill_url);
	}
};

class DocumentBuilder {
 public:

	typedef pod_bvector<path_attributes>		attr_storage;

								DocumentBuilder();

			void				remove_all();

	// Use these functions as follows:
	// begin_path() when the XML tag <path> comes ("start_element" handler)
	// parse_path() on "d=" tag attribute
	// end_path() when parsing of the entire tag is done.
			void				begin_path();
			void				parse_path(PathTokenizer& tok);
			void				end_path();

	// The following functions are essentially a "reflection" of
	// the respective SVG path commands.
			void				move_to(double x, double y, bool rel = false);	// M, m
			void				line_to(double x,  double y, bool rel = false);	// L, l
			void				hline_to(double x, bool rel = false);			// H, h
			void				vline_to(double y, bool rel = false);			// V, v
			void				curve3(double x1, double y1,					// Q, q
									   double x,  double y, bool rel = false);
			void				curve3(double x, double y, bool rel = false);	// T, t
			void				curve4(double x1, double y1,					// C, c
									   double x2, double y2,
									   double x,  double y, bool rel = false);
			void				curve4(double x2, double y2,					// S, s
									   double x,  double y, bool rel = false);
			void				elliptical_arc(double rx, double ry,
											   double angle,
											   bool large_arc_flag,
											   bool sweep_flag,
											   double x, double y,
											   bool rel = false);				// A, a
			void				close_subpath();								// Z, z

/*			template<class VertexSource>
			void				add_path(VertexSource& vs,
										 unsigned path_id = 0,
										 bool solid_path = true)
								{
									fPathStorage.add_path(vs, path_id, solid_path);
								}*/
								DocumentBuilder(NSVGimage* image);

			void				SetTitle(const char* title);
			void				SetDimensions(uint32 width, uint32 height, BRect viewBox);


			// Call these functions on <g> tag (start_element, end_element respectively)
			void				push_attr();
			void				pop_attr();

			// Attribute setting functions.
			void				fill(const rgba8& f);
			void				stroke(const rgba8& s);
			void				even_odd(bool flag);
			void				stroke_width(double w);
			void				fill_none();
			void				fill_url(const char* url);
			void				stroke_none();
			void				stroke_url(const char* url);
			void				opacity(double op);
			void				fill_opacity(double op);
			void				stroke_opacity(double op);
			void				line_join(line_join_e join);
			void				line_cap(line_cap_e cap);
			void				miter_limit(double ml);
			trans_affine&		transform();

/*			// Make all polygons CCW-oriented
			void				arrange_orientations()
			{
				fPathStorage.arrange_orientations_all_paths(path_flags_ccw);
			}*/

			unsigned			operator [](unsigned idx)
	        {
	            fTransform = fAttributesStorage[idx].transform;
	            return fAttributesStorage[idx].index;
	        }

			status_t			GetIcon(Icon* icon,
										SVGImporter* importer,
										const char* fallbackName);

			void				StartGradient(bool radial = false);
			void				EndGradient();
			SVGGradient*		CurrentGradient() const
									{ return fCurrentGradient; }

 private:
			void				_AddGradient(SVGGradient* gradient);
			SVGGradient*		_GradientAt(int32 index) const;
			SVGGradient*		_FindGradient(const char* name) const;
			status_t			_AddShape(path_attributes& attributes,
			status_t			_AddShape(NSVGshape* svgShape,
										  bool outline,
										  const Transformable& transform,
										  Icon* icon);

			path_attributes&	cur_attr();

			path_storage		fPathStorage;
			attr_storage		fAttributesStorage;
			attr_storage		fAttributesStack;

			trans_affine		fTransform;

			BList				fGradients;
			SVGGradient*		fCurrentGradient;

			uint32				fWidth;
			uint32				fHeight;
			BRect				fViewBox;
			BString				fTitle;
};

} // namespace svg
} // namespace agg
			NSVGimage*			fSource;
};

#endif // DOCUMENT_BUILD_H
diff --git a/src/apps/icon-o-matic/import_export/svg/PathTokenizer.cpp b/src/apps/icon-o-matic/import_export/svg/PathTokenizer.cpp
deleted file mode 100644
index 38999ad..0000000 100644
--- a/src/apps/icon-o-matic/import_export/svg/PathTokenizer.cpp
+++ /dev/null
@@ -1,157 +1,0 @@
/*
 * Copyright 2006, Haiku. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Stephan Aßmus <superstippi@gmx.de>
 */

//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.2
// Copyright (C) 2002-2004 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software 
// is granted provided this copyright notice appears in all copies. 
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
//		  mcseemagg@yahoo.com
//		  http://www.antigrain.com

#include "PathTokenizer.h"

#include <string.h>
#include <stdlib.h>
#include <stdio.h>


namespace agg {	
namespace svg {

// globals
const char PathTokenizer::sCommands[]   = "+-MmZzLlHhVvCcSsQqTtAaFfPp";
const char PathTokenizer::sNumeric[]	= ".Ee0123456789";
const char PathTokenizer::sSeparators[] = " ,\t\n\r";

// constructor
PathTokenizer::PathTokenizer()
	: fPath(0),
	  fLastNumber(0.0),
	  fLastCommand(0)
{
	init_char_mask(fCommandsMask,   sCommands);
	init_char_mask(fNumericMask,	sNumeric);
	init_char_mask(fSeparatorsMask, sSeparators);
}

// set_path_str
void
PathTokenizer::set_path_str(const char* str)
{
	fPath = str;
	fLastCommand = 0;
	fLastNumber = 0.0;
}

// next
bool
PathTokenizer::next()
{
	if(fPath == 0) return false;

	// Skip all white spaces and other garbage
	while (*fPath && !is_command(*fPath) && !isNumeric(*fPath))  {
		if (!is_separator(*fPath)) {
			char buf[100];
			sprintf(buf, "PathTokenizer::next : Invalid Character %c", *fPath);
			throw exception(buf);
		}
		fPath++;
	}

	if (*fPath == 0) return false;

	if (is_command(*fPath)) {
		// Check if the command is a numeric sign character
		if(*fPath == '-' || *fPath == '+')
		{
			return parse_number();
		}
		fLastCommand = *fPath++;
		while(*fPath && is_separator(*fPath)) fPath++;
		if(*fPath == 0) return true;
	}
	return parse_number();
}

// next
double
PathTokenizer::next(char cmd)
{
	if (!next()) throw exception("parse_path: Unexpected end of path");
	if (last_command() != cmd) {
		char buf[100];
		sprintf(buf, "parse_path: Command %c: bad or missing parameters", cmd);
		throw exception(buf);
	}
	return last_number();
}

// init_char_mask
void
PathTokenizer::init_char_mask(char* mask, const char* char_set)
{
	memset(mask, 0, 256/8);
	while (*char_set) {
		unsigned c = unsigned(*char_set++) & 0xFF;
		mask[c >> 3] |= 1 << (c & 7);
	}
}

// contains
inline bool
PathTokenizer::contains(const char* mask, unsigned c) const
{
	return (mask[(c >> 3) & (256 / 8 - 1)] & (1 << (c & 7))) != 0;
}

// is_command
inline bool
PathTokenizer::is_command(unsigned c) const
{
	return contains(fCommandsMask, c);
}

// isNumeric
inline bool
PathTokenizer::isNumeric(unsigned c) const
{
	return contains(fNumericMask, c);
}

// is_separator
inline bool
PathTokenizer::is_separator(unsigned c) const
{
	return contains(fSeparatorsMask, c);
}

// parse_number
bool
PathTokenizer::parse_number()
{
	char* end;
	fLastNumber = strtod(fPath, &end);
	fPath = end;
	return true;
}


} //namespace svg
} //namespace agg




diff --git a/src/apps/icon-o-matic/import_export/svg/PathTokenizer.h b/src/apps/icon-o-matic/import_export/svg/PathTokenizer.h
deleted file mode 100644
index 4cde3b8..0000000 100644
--- a/src/apps/icon-o-matic/import_export/svg/PathTokenizer.h
+++ /dev/null
@@ -1,106 +1,0 @@
/*
 * Copyright 2006, Haiku. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Stephan Aßmus <superstippi@gmx.de>
 */

//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.2
// Copyright (C) 2002-2004 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software 
// is granted provided this copyright notice appears in all copies. 
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
//		  mcseemagg@yahoo.com
//		  http://www.antigrain.com
//----------------------------------------------------------------------------

#ifndef PATH_TOKENIZER_H
#define PATH_TOKENIZER_H

#include "SVGException.h"

namespace agg { 
namespace svg {
	// SVG path tokenizer. 
	// Example:
	//
	// agg::svg::PathTokenizer tok;
	//
	// tok.set_str("M-122.304 84.285L-122.304 84.285 122.203 86.179 ");
	// while(tok.next())
	// {
	//	 printf("command='%c' number=%f\n", 
	//			 tok.last_command(), 
	//			 tok.last_number());
	// }
	//
	// The tokenizer does all the routine job of parsing the SVG paths.
	// It doesn't recognize any graphical primitives, it even doesn't know
	// anything about pairs of coordinates (X,Y). The purpose of this class 
	// is to tokenize the numeric values and commands. SVG paths can 
	// have single numeric values for Horizontal or Vertical line_to commands 
	// as well as more than two coordinates (4 or 6) for Bezier curves 
	// depending on the semantics of the command.
	// The behaviour is as follows:
	//
	// Each call to next() returns true if there's new command or new numeric
	// value or false when the path ends. How to interpret the result
	// depends on the sematics of the command. For example, command "C" 
	// (cubic Bezier curve) implies 6 floating point numbers preceded by this 
	// command. If the command assumes no arguments (like z or Z) the 
	// the last_number() values won't change, that is, last_number() always
	// returns the last recognized numeric value, so does last_command().

class PathTokenizer {
 public:
								PathTokenizer();

			void				set_path_str(const char* str);
			bool				next();

			double				next(char cmd);
		
			char				last_command() const
									{ return fLastCommand; }
			double				last_number() const
									{ return fLastNumber; }


 private:
	static	void				init_char_mask(char* mask,
											   const char* char_set);

	inline	bool				contains(const char* mask, unsigned c) const;
	inline	bool				is_command(unsigned c) const;
	inline	bool				isNumeric(unsigned c) const;
	inline	bool				is_separator(unsigned c) const;

			bool				parse_number();

			char				fSeparatorsMask[256 / 8];
			char				fCommandsMask[256 / 8];
			char				fNumericMask[256 / 8];
		
			const char*			fPath;
			double				fLastNumber;
			char				fLastCommand;
		
	static	const char			sCommands[];
	static	const char			sNumeric[];
	static	const char			sSeparators[];
};



} //namespace svg
} //namespace agg


#endif // PATH_TOKENIZER_H
diff --git a/src/apps/icon-o-matic/import_export/svg/SVGGradients.cpp b/src/apps/icon-o-matic/import_export/svg/SVGGradients.cpp
deleted file mode 100644
index 43a9509..0000000 100644
--- a/src/apps/icon-o-matic/import_export/svg/SVGGradients.cpp
+++ /dev/null
@@ -1,199 +1,0 @@
/*
 * Copyright 2006, Haiku. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Stephan Aßmus <superstippi@gmx.de>
 */


#include <stdio.h>
#include <stdlib.h>

#include "GradientTransformable.h"

#include "SVGGradients.h"

namespace agg {
namespace svg {

// constructor
SVGGradient::SVGGradient()
	: BMessage(),
	  fGradient(NULL),
	  fID(""),
	  fGradientUnits(UNSPECIFIED)
{
}

// destructor
SVGGradient::~SVGGradient()
{
}

// SetID
void
SVGGradient::SetID(const char* id)
{
	fID = id;
}

// ID
const char*
SVGGradient::ID() const
{
	return fID.String();
}

// AddStop
void
SVGGradient::AddStop(float offset, rgba8 color)
{
	if (!fGradient)
		fGradient = MakeGradient();

	rgb_color c = { color.r, color.g, color.b, color.a };
	fGradient->AddColor(c, offset);
}

// SetTransformation
void
SVGGradient::SetTransformation(const trans_affine& transform)
{
//printf("SVGGradient::SetTransformation()\n");
	if (!fGradient)
		fGradient = MakeGradient();

	fGradient->multiply(transform);
}

// GetGradient
Gradient*
SVGGradient::GetGradient(BRect objectBounds)
{
	if (fGradient) {
		Gradient* gradient = new Gradient(*fGradient);

		IdentifyGradientUnits();
		if (fGradientUnits == OBJECT_BOUNDING_BOX) {
			gradient->FitToBounds(objectBounds);
		}

		return gradient;
	}
	return NULL;
}

// IdentifyGradientUnits
void
SVGGradient::IdentifyGradientUnits()
{
	if (fGradientUnits == UNSPECIFIED) {
		const char* units;
		if (FindString("gradientUnits", &units) >= B_OK) {
			if (strcmp(units, "objectBoundingBox") == 0)
				fGradientUnits = OBJECT_BOUNDING_BOX;
		}
	}
}

// constructor
SVGLinearGradient::SVGLinearGradient()
	: SVGGradient()
{
}

// destructor
SVGLinearGradient::~SVGLinearGradient()
{
}

// MakeGradient
Gradient*
SVGLinearGradient::MakeGradient() const
{
//printf("SVGLinearGradient::MakeGradient()\n");
//PrintToStream();

	Gradient* gradient = new Gradient(true);
	gradient->SetType(GRADIENT_LINEAR);
	gradient->SetInterpolation(INTERPOLATION_LINEAR);
	// setup the gradient transform 
	BPoint start(-64.0, -64.0);
	BPoint end(64.0, -64.0);
	BString coordinate;
	if (FindString("x1", &coordinate) >= B_OK)
		start.x = atof(coordinate.String());
	if (FindString("y1", &coordinate) >= B_OK)
		start.y = atof(coordinate.String());
	if (FindString("x2", &coordinate) >= B_OK)
		end.x = atof(coordinate.String());
	if (FindString("y2", &coordinate) >= B_OK)
		end.y = atof(coordinate.String());

	// the transformed parallelogram
	double parl[6];
	parl[0] = start.x;
	parl[1] = start.y;
	parl[2] = end.x;
	parl[3] = end.y;
	parl[4] = end.x - (end.y - start.y);
	parl[5] = end.y + (end.x - start.x);

	trans_affine transform(-64.0, -64.0, 64.0, 64.0, parl);
	gradient->multiply(transform);

	return gradient;
}

// constructor
SVGRadialGradient::SVGRadialGradient()
	: SVGGradient()
{
}

// destructor
SVGRadialGradient::~SVGRadialGradient()
{
}

// MakeGradient
Gradient*
SVGRadialGradient::MakeGradient() const
{
//printf("SVGRadialGradient::MakeGradient()\n");
	// TODO: handle userSpaceOnUse/objectBoundingBox
	Gradient* gradient = new Gradient(true);
	gradient->SetType(GRADIENT_CIRCULAR);
	gradient->SetInterpolation(INTERPOLATION_LINEAR);

	double cx = 0.0;
	double cy = 0.0;
	double r = 100.0;

	BString value;
	if (FindString("cx", &value) >= B_OK)
		cx = atof(value.String());
	if (FindString("cy", &value) >= B_OK)
		cy = atof(value.String());
	if (FindString("r", &value) >= B_OK)
		r = atof(value.String());

	// the transformed parallelogram
	double parl[6];
	parl[0] = cx - r;
	parl[1] = cy - r;
	parl[2] = cx + r;
	parl[3] = cy - r;
	parl[4] = cx + r;
	parl[5] = cy + r;

	trans_affine transform(-64.0, -64.0, 64.0, 64.0, parl);
	gradient->multiply(transform);

	return gradient;
}

} // namespace svg
} // namespace agg

diff --git a/src/apps/icon-o-matic/import_export/svg/SVGGradients.h b/src/apps/icon-o-matic/import_export/svg/SVGGradients.h
deleted file mode 100644
index c510ecb..0000000 100644
--- a/src/apps/icon-o-matic/import_export/svg/SVGGradients.h
+++ /dev/null
@@ -1,81 +1,0 @@
/*
 * Copyright 2006-2007, Haiku. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Stephan Aßmus <superstippi@gmx.de>
 */
#ifndef SVG_GRADIENTS_H
#define SVG_GRADIENTS_H


#include <agg_color_rgba.h>
#include <agg_trans_affine.h>

#include <Message.h>
#include <String.h>

#include "IconBuild.h"


_BEGIN_ICON_NAMESPACE
	class Gradient;
_END_ICON_NAMESPACE

namespace agg {
namespace svg {

class SVGGradient : public BMessage {
 public:
							SVGGradient();
	virtual					~SVGGradient();

			void			SetID(const char* id);
			const char*		ID() const;

	virtual	void			AddStop(float offset, rgba8 color);
			void			SetTransformation(const trans_affine& transform);

			Gradient*		GetGradient(BRect objectBounds);

 protected:
	virtual	Gradient*		MakeGradient() const = 0;
			void			IdentifyGradientUnits();

 private:

	enum {
		UNSPECIFIED = 0,
		USER_SPACE_ON_USE,
		OBJECT_BOUNDING_BOX,
	};

			Gradient*		fGradient;
			BString			fID;
			uint32			fGradientUnits;
};

class SVGLinearGradient : public SVGGradient {
 public:
							SVGLinearGradient();
	virtual					~SVGLinearGradient();

 protected:
	virtual	Gradient*		MakeGradient() const;

};

class SVGRadialGradient : public SVGGradient {
 public:
							SVGRadialGradient();
	virtual					~SVGRadialGradient();

 protected:
	virtual	Gradient*		MakeGradient() const;

};

} // namespace svg
} // namespace agg

#endif // SVG_GRADIENTS_H
diff --git a/src/apps/icon-o-matic/import_export/svg/SVGImporter.cpp b/src/apps/icon-o-matic/import_export/svg/SVGImporter.cpp
index 280ac80..534b36c 100644
--- a/src/apps/icon-o-matic/import_export/svg/SVGImporter.cpp
+++ b/src/apps/icon-o-matic/import_export/svg/SVGImporter.cpp
@@ -18,7 +18,7 @@
#include <Path.h>

#include "DocumentBuilder.h"
#include "SVGParser.h"
#include "nanosvg.h"


#undef B_TRANSLATION_CONTEXT
@@ -69,23 +69,22 @@
		return B_ERROR;
	}

	try {
		agg::svg::DocumentBuilder builder;
		agg::svg::Parser parser(builder);
		parser.parse(path.Path());
		ret = builder.GetIcon(icon, this, ref->name);
	} catch(agg::svg::exception& e) {
	NSVGimage* svg = nsvgParseFromFile(path.Path(), "px", 96);
	if (svg == NULL) {
		char error[1024];
		sprintf(error, B_TRANSLATE("Failed to open the file '%s' as "
					   "an SVG document.\n\n"
					   "Error: %s"), ref->name, e.msg());
			"an SVG document.\n\n"), ref->name);
		BAlert* alert = new BAlert(B_TRANSLATE("load error"),
								   error, B_TRANSLATE("OK"), NULL, NULL,
								   B_WIDTH_AS_USUAL, B_WARNING_ALERT);
			error, B_TRANSLATE("OK"), NULL, NULL,
			B_WIDTH_AS_USUAL, B_WARNING_ALERT);
		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
		alert->Go(NULL);
		ret = B_ERROR;
		return B_ERROR;
	}

	DocumentBuilder builder(svg);
	ret = builder.GetIcon(icon, this, ref->name);
	nsvgDelete(svg);

	return ret;
}
diff --git a/src/apps/icon-o-matic/import_export/svg/SVGParser.cpp b/src/apps/icon-o-matic/import_export/svg/SVGParser.cpp
deleted file mode 100644
index fcd182d..0000000 100644
--- a/src/apps/icon-o-matic/import_export/svg/SVGParser.cpp
+++ /dev/null
@@ -1,1152 +1,0 @@
/*
 * Copyright 2006, Haiku. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Stephan Aßmus <superstippi@gmx.de>
 */

//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.2
// Copyright (C) 2002-2004 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software 
// is granted provided this copyright notice appears in all copies. 
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
//		  mcseemagg@yahoo.com
//		  http://www.antigrain.com
//----------------------------------------------------------------------------

#include "SVGParser.h"

#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include <expat.h>

#include "SVGGradients.h"

namespace agg {
namespace svg {

struct named_color
{
	char  name[22];
	int8u r, g, b, a;
};

named_color colors[] = 
{
	{ "aliceblue",240,248,255, 255 },
	{ "antiquewhite",250,235,215, 255 },
	{ "aqua",0,255,255, 255 },
	{ "aquamarine",127,255,212, 255 },
	{ "azure",240,255,255, 255 },
	{ "beige",245,245,220, 255 },
	{ "bisque",255,228,196, 255 },
	{ "black",0,0,0, 255 },
	{ "blanchedalmond",255,235,205, 255 },
	{ "blue",0,0,255, 255 },
	{ "blueviolet",138,43,226, 255 },
	{ "brown",165,42,42, 255 },
	{ "burlywood",222,184,135, 255 },
	{ "cadetblue",95,158,160, 255 },
	{ "chartreuse",127,255,0, 255 },
	{ "chocolate",210,105,30, 255 },
	{ "coral",255,127,80, 255 },
	{ "cornflowerblue",100,149,237, 255 },
	{ "cornsilk",255,248,220, 255 },
	{ "crimson",220,20,60, 255 },
	{ "cyan",0,255,255, 255 },
	{ "darkblue",0,0,139, 255 },
	{ "darkcyan",0,139,139, 255 },
	{ "darkgoldenrod",184,134,11, 255 },
	{ "darkgray",169,169,169, 255 },
	{ "darkgreen",0,100,0, 255 },
	{ "darkgrey",169,169,169, 255 },
	{ "darkkhaki",189,183,107, 255 },
	{ "darkmagenta",139,0,139, 255 },
	{ "darkolivegreen",85,107,47, 255 },
	{ "darkorange",255,140,0, 255 },
	{ "darkorchid",153,50,204, 255 },
	{ "darkred",139,0,0, 255 },
	{ "darksalmon",233,150,122, 255 },
	{ "darkseagreen",143,188,143, 255 },
	{ "darkslateblue",72,61,139, 255 },
	{ "darkslategray",47,79,79, 255 },
	{ "darkslategrey",47,79,79, 255 },
	{ "darkturquoise",0,206,209, 255 },
	{ "darkviolet",148,0,211, 255 },
	{ "deeppink",255,20,147, 255 },
	{ "deepskyblue",0,191,255, 255 },
	{ "dimgray",105,105,105, 255 },
	{ "dimgrey",105,105,105, 255 },
	{ "dodgerblue",30,144,255, 255 },
	{ "firebrick",178,34,34, 255 },
	{ "floralwhite",255,250,240, 255 },
	{ "forestgreen",34,139,34, 255 },
	{ "fuchsia",255,0,255, 255 },
	{ "gainsboro",220,220,220, 255 },
	{ "ghostwhite",248,248,255, 255 },
	{ "gold",255,215,0, 255 },
	{ "goldenrod",218,165,32, 255 },
	{ "gray",128,128,128, 255 },
	{ "green",0,128,0, 255 },
	{ "greenyellow",173,255,47, 255 },
	{ "grey",128,128,128, 255 },
	{ "honeydew",240,255,240, 255 },
	{ "hotpink",255,105,180, 255 },
	{ "indianred",205,92,92, 255 },
	{ "indigo",75,0,130, 255 },
	{ "ivory",255,255,240, 255 },
	{ "khaki",240,230,140, 255 },
	{ "lavender",230,230,250, 255 },
	{ "lavenderblush",255,240,245, 255 },
	{ "lawngreen",124,252,0, 255 },
	{ "lemonchiffon",255,250,205, 255 },
	{ "lightblue",173,216,230, 255 },
	{ "lightcoral",240,128,128, 255 },
	{ "lightcyan",224,255,255, 255 },
	{ "lightgoldenrodyellow",250,250,210, 255 },
	{ "lightgray",211,211,211, 255 },
	{ "lightgreen",144,238,144, 255 },
	{ "lightgrey",211,211,211, 255 },
	{ "lightpink",255,182,193, 255 },
	{ "lightsalmon",255,160,122, 255 },
	{ "lightseagreen",32,178,170, 255 },
	{ "lightskyblue",135,206,250, 255 },
	{ "lightslategray",119,136,153, 255 },
	{ "lightslategrey",119,136,153, 255 },
	{ "lightsteelblue",176,196,222, 255 },
	{ "lightyellow",255,255,224, 255 },
	{ "lime",0,255,0, 255 },
	{ "limegreen",50,205,50, 255 },
	{ "linen",250,240,230, 255 },
	{ "magenta",255,0,255, 255 },
	{ "maroon",128,0,0, 255 },
	{ "mediumaquamarine",102,205,170, 255 },
	{ "mediumblue",0,0,205, 255 },
	{ "mediumorchid",186,85,211, 255 },
	{ "mediumpurple",147,112,219, 255 },
	{ "mediumseagreen",60,179,113, 255 },
	{ "mediumslateblue",123,104,238, 255 },
	{ "mediumspringgreen",0,250,154, 255 },
	{ "mediumturquoise",72,209,204, 255 },
	{ "mediumvioletred",199,21,133, 255 },
	{ "midnightblue",25,25,112, 255 },
	{ "mintcream",245,255,250, 255 },
	{ "mistyrose",255,228,225, 255 },
	{ "moccasin",255,228,181, 255 },
	{ "navajowhite",255,222,173, 255 },
	{ "navy",0,0,128, 255 },
	{ "oldlace",253,245,230, 255 },
	{ "olive",128,128,0, 255 },
	{ "olivedrab",107,142,35, 255 },
	{ "orange",255,165,0, 255 },
	{ "orangered",255,69,0, 255 },
	{ "orchid",218,112,214, 255 },
	{ "palegoldenrod",238,232,170, 255 },
	{ "palegreen",152,251,152, 255 },
	{ "paleturquoise",175,238,238, 255 },
	{ "palevioletred",219,112,147, 255 },
	{ "papayawhip",255,239,213, 255 },
	{ "peachpuff",255,218,185, 255 },
	{ "peru",205,133,63, 255 },
	{ "pink",255,192,203, 255 },
	{ "plum",221,160,221, 255 },
	{ "powderblue",176,224,230, 255 },
	{ "purple",128,0,128, 255 },
	{ "red",255,0,0, 255 },
	{ "rosybrown",188,143,143, 255 },
	{ "royalblue",65,105,225, 255 },
	{ "saddlebrown",139,69,19, 255 },
	{ "salmon",250,128,114, 255 },
	{ "sandybrown",244,164,96, 255 },
	{ "seagreen",46,139,87, 255 },
	{ "seashell",255,245,238, 255 },
	{ "sienna",160,82,45, 255 },
	{ "silver",192,192,192, 255 },
	{ "skyblue",135,206,235, 255 },
	{ "slateblue",106,90,205, 255 },
	{ "slategray",112,128,144, 255 },
	{ "slategrey",112,128,144, 255 },
	{ "snow",255,250,250, 255 },
	{ "springgreen",0,255,127, 255 },
	{ "steelblue",70,130,180, 255 },
	{ "tan",210,180,140, 255 },
	{ "teal",0,128,128, 255 },
	{ "thistle",216,191,216, 255 },
	{ "tomato",255,99,71, 255 },
	{ "turquoise",64,224,208, 255 },
	{ "violet",238,130,238, 255 },
	{ "wheat",245,222,179, 255 },
	{ "white",255,255,255, 255 },
	{ "whitesmoke",245,245,245, 255 },
	{ "yellow",255,255,0, 255 },
	{ "yellowgreen",154,205,50, 255 },
	{ "zzzzzzzzzzz",0,0,0, 0 }
}; 



// cmp_color
int
cmp_color(const void* p1, const void* p2)
{
	return strcmp(((named_color*)p1)->name, ((named_color*)p2)->name);
}

// parse_color
rgba8
parse_color(const char* str)
{
	while(*str == ' ') ++str;
	if (*str == '#') {
		str++;
		int32 length = strlen(str);
		unsigned c = 0;
		if (length == 3) {
			// if there are only 3 byte, than it means that we
			// need to expand the color (#f60 -> #ff6600)
			// TODO: There must be an easier way...
			char expanded[7];
			expanded[0] = *str;
			expanded[1] = *str++;
			expanded[2] = *str;
			expanded[3] = *str++;
			expanded[4] = *str;
			expanded[5] = *str++;
			expanded[6] = 0;
			sscanf(expanded, "%x", &c);
		} else {
			sscanf(str, "%x", &c);
		}
		return rgb8_packed(c);
	} else {
		named_color c;
		unsigned len = strlen(str);
		if(len > sizeof(c.name) - 1)
		{
			throw exception("parse_color: Invalid color name '%s'", str);
		}
		strcpy(c.name, str);
		const void* p = bsearch(&c, 
								colors, 
								sizeof(colors) / sizeof(colors[0]), 
								sizeof(colors[0]), 
								cmp_color);
		if(p == 0)
		{
			throw exception("parse_color: Invalid color name '%s'", str);
		}
		const named_color* pc = (const named_color*)p;
		return rgba8(pc->r, pc->g, pc->b, pc->a);
	}
}

// parse_double
double
parse_double(const char* str)
{
	while(*str == ' ') ++str;
	double value = atof(str);
	// handle percent
	int32 length = strlen(str);
	if (str[length - 1] == '%')
		value /= 100.0;
	return value;
}

// parse_url
char*
parse_url(const char* str)
{
	const char* begin = str;
	while (*begin != '#')
		begin++;

	begin++;
	const char* end = begin;
	while (*end != ')')
		end++;

	end--;

	int32 length = end - begin + 2;
	char* result = new char[length];
	memcpy(result, begin, length - 1);
	result[length - 1] = 0;

	return result;
}


// #pragma mark -

// constructor
Parser::Parser(DocumentBuilder& builder)
	: fBuilder(builder),
	  fPathTokenizer(),
	  fBuffer(new char[buf_size]),
	  fTitle(new char[256]),
	  fTitleLength(0),

	  fTitleFlag(false),
	  fPathFlag(false),

	  fAttrName(new char[128]),
	  fAttrValue(new char[1024]),
	  fAttrNameLength(127),
	  fAttrValueLength(1023),

	  fTagsIgnored(false)
{
	fTitle[0] = 0;
}

// destructor
Parser::~Parser()
{
	delete[] fAttrValue;
	delete[] fAttrName;
	delete[] fBuffer;
	delete[] fTitle;
}

// parse
void
Parser::parse(const char* pathToFile)
{
	char msg[1024];
	XML_Parser p = XML_ParserCreate(NULL);
	if (p == 0) {
		throw exception("Couldn't allocate memory for Parser");
	}

	XML_SetUserData(p, this);
	XML_SetElementHandler(p, start_element, end_element);
	XML_SetCharacterDataHandler(p, content);

	FILE* fd = fopen(pathToFile, "r");
	if (fd == 0) {
		sprintf(msg, "Couldn't open file %s", pathToFile);
		XML_ParserFree(p);
		throw exception(msg);
	}

	bool done = false;
	do {
		size_t len = fread(fBuffer, 1, buf_size, fd);
		done = len < buf_size;
		if (!XML_Parse(p, fBuffer, len, done)) {
			sprintf(msg, "%s at line %ld\n",
					XML_ErrorString(XML_GetErrorCode(p)),
					XML_GetCurrentLineNumber(p));
			fclose(fd);
			XML_ParserFree(p);
			throw exception(msg);
		}
	} while (!done);

	fclose(fd);
	XML_ParserFree(p);

	char* ts = fTitle;
	while (*ts) {
		if (*ts < ' ') *ts = ' ';
		++ts;
	}
}

// start_element
void
Parser::start_element(void* data, const char* el, const char** attr)
{
// printf("Parser::start_element(%s)\n", el);
	Parser& self = *(Parser*)data;

	if (strcmp(el, "svg") == 0)
	{
		self.parse_svg(attr);
	}
	else
	if (strcmp(el, "title") == 0)
	{
		self.fTitleFlag = true;
	}
	else
	if (strcmp(el, "g") == 0)
	{
		self.fBuilder.push_attr();
		self.parse_attr(attr);
	}
	else
	if (strcmp(el, "path") == 0)
	{
		if (self.fPathFlag) {
			throw exception("start_element: Nested path");
		}
		self.fBuilder.begin_path();
		self.parse_path(attr);
		self.fBuilder.end_path();
		self.fPathFlag = true;
	}
	else
	if (strcmp(el, "circle") == 0)
	{
		self.parse_circle(attr);
	}
	else
	if (strcmp(el, "ellipse") == 0)
	{
		self.parse_ellipse(attr);
	}
	else
	if (strcmp(el, "rect") == 0)
	{
		self.parse_rect(attr);
	}
	else
	if (strcmp(el, "line") == 0)
	{
		self.parse_line(attr);
	}
	else
	if (strcmp(el, "polyline") == 0)
	{
		self.parse_poly(attr, false);
	}
	else
	if (strcmp(el, "polygon") == 0)
	{
		self.parse_poly(attr, true);
	}
	else
	if (strcmp(el, "linearGradient") == 0 || strcmp(el, "radialGradient") == 0)
	{
		self.parse_gradient(attr, strcmp(el, "radialGradient") == 0);
	}
	else
	if (strcmp(el, "stop") == 0)
	{
		self.parse_gradient_stop(attr);
	}
	//else
	//if(strcmp(el, "<OTHER_ELEMENTS>") == 0) 
	//{
	//}
	// . . .
	else
	{
		fprintf(stderr, "SVGParser igoring tag: \"%s\"\n", el);
		self.fTagsIgnored = true;
	}
} 

// end_element
void
Parser::end_element(void* data, const char* el)
{
	Parser& self = *(Parser*)data;

	if (strcmp(el, "title") == 0)
	{
		self.fTitleFlag = false;
		self.fBuilder.SetTitle(self.fTitle);
	}
	else
	if (strcmp(el, "g") == 0)
	{
		self.fBuilder.pop_attr();
	}
	else
	if (strcmp(el, "path") == 0)
	{
		self.fPathFlag = false;
	}
	else
	if (strcmp(el, "linearGradient") == 0 || strcmp(el, "radialGradient") == 0)
	{
		self.fBuilder.EndGradient();
	}
	//else
	//if(strcmp(el, "<OTHER_ELEMENTS>") == 0) 
	//{
	//}
	// . . .
}

// content
void
Parser::content(void* data, const char* s, int len)
{
	Parser& self = *(Parser*)data;

	// fTitleFlag signals that the <title> tag is being parsed now.
	// The following code concatenates the pieces of content of the <title> tag.
	if(self.fTitleFlag)
	{
		if(len + self.fTitleLength > 255) len = 255 - self.fTitleLength;
		if(len > 0) 
		{
			memcpy(self.fTitle + self.fTitleLength, s, len);
			self.fTitleLength += len;
			self.fTitle[self.fTitleLength] = 0;
		}
	}
}

// parse_svg
void Parser::parse_svg(const char** attr)
{
	double width = 0.0;
	double height = 0.0;
	BRect viewBox(0.0, 0.0, -1.0, -1.0);

	for (int i = 0; attr[i]; i += 2) {
		if (strcmp(attr[i], "width") == 0)
		{
			width = parse_double(attr[i + 1]);
		}
		else
		if (strcmp(attr[i], "height") == 0)
		{
			height = parse_double(attr[i + 1]);
		}
		else
		if (strcmp(attr[i], "viewBox") == 0)
		{
			fPathTokenizer.set_path_str(attr[i + 1]);
			if(!fPathTokenizer.next())
			{
				throw exception("parse_svg (viewBox): Too few coordinates");
			}
			viewBox.left = fPathTokenizer.last_number();
			if(!fPathTokenizer.next())
			{
				throw exception("parse_svg (viewBox): Too few coordinates");
			}
			viewBox.top = fPathTokenizer.last_number();
			if(!fPathTokenizer.next())
			{
				throw exception("parse_svg (viewBox): Too few coordinates");
			}
			viewBox.right = fPathTokenizer.last_number();
			if(!fPathTokenizer.next())
			{
				throw exception("parse_svg (viewBox): Too few coordinates");
			}
			viewBox.bottom = fPathTokenizer.last_number();
		}
	}
	if (width >= 0.0 && height >= 0.0) {
		fBuilder.SetDimensions((uint32)ceil(width), (uint32)ceil(height), viewBox);
	} else {
		throw exception("parse_svg: Invalid width or height\n");
	}
}

// parse_attr
void Parser::parse_attr(const char** attr)
{
	for (int i = 0; attr[i]; i += 2) {
		if (strcmp(attr[i], "style") == 0) {
			parse_style(attr[i + 1]);
		} else {
			parse_attr(attr[i], attr[i + 1]);
		}
	}
}

// parse_path
void Parser::parse_path(const char** attr)
{
	int i;

	for(i = 0; attr[i]; i += 2)
	{
		// The <path> tag can consist of the path itself ("d=") 
		// as well as of other parameters like "style=", "transform=", etc.
		// In the last case we simply rely on the function of parsing 
		// attributes (see 'else' branch).
		if(strcmp(attr[i], "d") == 0)
		{
			fPathTokenizer.set_path_str(attr[i + 1]);
			fBuilder.parse_path(fPathTokenizer);
		}
		else
		{
			// Create a temporary single pair "name-value" in order
			// to avoid multiple calls for the same attribute.
			const char* tmp[4];
			tmp[0] = attr[i];
			tmp[1] = attr[i + 1];
			tmp[2] = 0;
			tmp[3] = 0;
			parse_attr(tmp);
		}
	}
}

// parse_attr
bool
Parser::parse_attr(const char* name, const char* value)
{
	if(strcmp(name, "style") == 0) {
		parse_style(value);
	} else
	if(strcmp(name, "opacity") == 0) {
		fBuilder.opacity(parse_double(value));
	} else
	if(strcmp(name, "fill") == 0) {
		if(strcmp(value, "none") == 0) {
			fBuilder.fill_none();
		} else if (strncmp(value, "url", 3) == 0) {
			char* url = parse_url(value);
			fBuilder.fill_url(url);
			delete[] url;
		} else {
			fBuilder.fill(parse_color(value));
		}
	} else
	if(strcmp(name, "fill-opacity") == 0) {
		fBuilder.fill_opacity(parse_double(value));
	} else
	if(strcmp(name, "fill-rule") == 0) {
		fBuilder.even_odd(strcmp(value, "evenodd") == 0);
	} else
	if(strcmp(name, "stroke") == 0) {
		if(strcmp(value, "none") == 0) {
			fBuilder.stroke_none();
		} else if (strncmp(value, "url", 3) == 0) {
			char* url = parse_url(value);
			fBuilder.stroke_url(url);
			delete[] url;
		} else {
			fBuilder.stroke(parse_color(value));
		}
	} else
	if(strcmp(name, "stroke-width") == 0) {
		fBuilder.stroke_width(parse_double(value));
	} else
	if(strcmp(name, "stroke-linecap") == 0) {
		if(strcmp(value, "butt") == 0)		fBuilder.line_cap(butt_cap);
		else if(strcmp(value, "round") == 0)  fBuilder.line_cap(round_cap);
		else if(strcmp(value, "square") == 0) fBuilder.line_cap(square_cap);
	} else
	if(strcmp(name, "stroke-linejoin") == 0) {
		if(strcmp(value, "miter") == 0)	  fBuilder.line_join(miter_join);
		else if(strcmp(value, "round") == 0) fBuilder.line_join(round_join);
		else if(strcmp(value, "bevel") == 0) fBuilder.line_join(bevel_join);
	} else
	if(strcmp(name, "stroke-miterlimit") == 0) {
		fBuilder.miter_limit(parse_double(value));
	} else
	if(strcmp(name, "stroke-opacity") == 0) {
		fBuilder.stroke_opacity(parse_double(value));
	} else
	if(strcmp(name, "transform") == 0) {
		fBuilder.transform().premultiply(parse_transform(value));
	} else
	if (strcmp(name, "stop-color") == 0) {
		fGradientStopColor = parse_color(value);
	} else
	if (strcmp(name, "stop-opacity") == 0) {
		fGradientStopColor.opacity(parse_double(value));
	}
	//else
	//if(strcmp(el, "<OTHER_ATTRIBUTES>") == 0) 
	//{
	//}
	// . . .
	else
	{
		return false;
	}
	return true;
}

// copy_name
void Parser::copy_name(const char* start, const char* end)
{
	unsigned len = unsigned(end - start);
	if(fAttrNameLength == 0 || len > fAttrNameLength)
	{
		delete [] fAttrName;
		fAttrName = new char[len + 1];
		fAttrNameLength = len;
	}
	if(len) memcpy(fAttrName, start, len);
	fAttrName[len] = 0;
}

// copy_value
void Parser::copy_value(const char* start, const char* end)
{
	unsigned len = unsigned(end - start);
	if(fAttrValueLength == 0 || len > fAttrValueLength)
	{
		delete [] fAttrValue;
		fAttrValue = new char[len + 1];
		fAttrValueLength = len;
	}
	if(len) memcpy(fAttrValue, start, len);
	fAttrValue[len] = 0;
}

// parse_name_value
bool Parser::parse_name_value(const char* nv_start, const char* nv_end)
{
	const char* str = nv_start;
	while(str < nv_end && *str != ':') ++str;

	const char* val = str;

	// Right Trim
	while(str > nv_start && 
		(*str == ':' || isspace(*str))) --str;
	++str;

	copy_name(nv_start, str);

	while(val < nv_end && (*val == ':' || isspace(*val))) ++val;
	
	copy_value(val, nv_end);
	return parse_attr(fAttrName, fAttrValue);
}

// parse_style
void Parser::parse_style(const char* str)
{
	while(*str)
	{
		// Left Trim
		while(*str && isspace(*str)) ++str;
		const char* nv_start = str;
		while(*str && *str != ';') ++str;
		const char* nv_end = str;

		// Right Trim
		while(nv_end > nv_start && 
			(*nv_end == ';' || isspace(*nv_end))) --nv_end;
		++nv_end;

		parse_name_value(nv_start, nv_end);
		if(*str) ++str;
	}

}

// parse_circle
void
Parser::parse_circle(const char** attr)
{
	int i;
	double cx = 0.0;
	double cy = 0.0;
	double r = 0.0;

	fBuilder.begin_path();
	for(i = 0; attr[i]; i += 2) {
		if (!parse_attr(attr[i], attr[i + 1])) {
			if(strcmp(attr[i], "cx") == 0)	cx = parse_double(attr[i + 1]);
			if(strcmp(attr[i], "cy") == 0)	cy = parse_double(attr[i + 1]);
			if(strcmp(attr[i], "r") == 0)	r = parse_double(attr[i + 1]);
		}
	}


	if (r != 0.0) {
		if (r < 0.0) throw exception("parse_circle: Invalid radius: %f", r);

		fBuilder.move_to(cx, cy - r);
		fBuilder.curve4(cx + r * 0.56, cy - r,
						cx + r, cy - r * 0.56,
						cx + r, cy);
		fBuilder.curve4(cx + r, cy + r * 0.56,
						cx + r * 0.56, cy + r,
						cx, cy + r);
		fBuilder.curve4(cx - r * 0.56, cy + r,
						cx - r, cy + r * 0.56,
						cx - r, cy);
		fBuilder.curve4(cx - r, cy - r * 0.56,
						cx - r * 0.56, cy - r,
						cx, cy - r);
		fBuilder.close_subpath();
	}
	fBuilder.end_path();
}

// parse_ellipse
void
Parser::parse_ellipse(const char** attr)
{
	int i;
	double cx = 0.0;
	double cy = 0.0;
	double rx = 0.0;
	double ry = 0.0;

	fBuilder.begin_path();
	for(i = 0; attr[i]; i += 2) {
		if (!parse_attr(attr[i], attr[i + 1])) {
			if(strcmp(attr[i], "cx") == 0)	cx = parse_double(attr[i + 1]);
			if(strcmp(attr[i], "cy") == 0)	cy = parse_double(attr[i + 1]);
			if(strcmp(attr[i], "rx") == 0)	rx = parse_double(attr[i + 1]);
			if(strcmp(attr[i], "ry") == 0)	ry = parse_double(attr[i + 1]);
		}
	}


	if (rx != 0.0 && ry != 0.0) {
		if (rx < 0.0) throw exception("parse_ellipse: Invalid x-radius: %f", rx);
		if (ry < 0.0) throw exception("parse_ellipse: Invalid y-radius: %f", ry);

		fBuilder.move_to(cx, cy - ry);
		fBuilder.curve4(cx + rx * 0.56, cy - ry,
						cx + rx, cy - ry * 0.56,
						cx + rx, cy);
		fBuilder.curve4(cx + rx, cy + ry * 0.56,
						cx + rx * 0.56, cy + ry,
						cx, cy + ry);
		fBuilder.curve4(cx - rx * 0.56, cy + ry,
						cx - rx, cy + ry * 0.56,
						cx - rx, cy);
		fBuilder.curve4(cx - rx, cy - ry * 0.56,
						cx - rx * 0.56, cy - ry,
						cx, cy - ry);
		fBuilder.close_subpath();
	}
	fBuilder.end_path();
}

// parse_rect
void
Parser::parse_rect(const char** attr)
{
	int i;
	double x = 0.0;
	double y = 0.0;
	double w = 0.0;
	double h = 0.0;

	fBuilder.begin_path();
	for(i = 0; attr[i]; i += 2)
	{
		if(!parse_attr(attr[i], attr[i + 1]))
		{
			if(strcmp(attr[i], "x") == 0)	  x = parse_double(attr[i + 1]);
			if(strcmp(attr[i], "y") == 0)	  y = parse_double(attr[i + 1]);
			if(strcmp(attr[i], "width") == 0)  w = parse_double(attr[i + 1]);
			if(strcmp(attr[i], "height") == 0) h = parse_double(attr[i + 1]);
			// rx - to be implemented 
			// ry - to be implemented
		}
	}


	if(w != 0.0 && h != 0.0)
	{
		if(w < 0.0) throw exception("parse_rect: Invalid width: %f", w);
		if(h < 0.0) throw exception("parse_rect: Invalid height: %f", h);

		fBuilder.move_to(x,	 y);
		fBuilder.line_to(x + w, y);
		fBuilder.line_to(x + w, y + h);
		fBuilder.line_to(x,	 y + h);
		fBuilder.close_subpath();
	}
	fBuilder.end_path();
}

// parse_line
void
Parser::parse_line(const char** attr)
{
	int i;
	double x1 = 0.0;
	double y1 = 0.0;
	double x2 = 0.0;
	double y2 = 0.0;

	fBuilder.begin_path();
	for(i = 0; attr[i]; i += 2)
	{
		if(!parse_attr(attr[i], attr[i + 1]))
		{
			if(strcmp(attr[i], "x1") == 0) x1 = parse_double(attr[i + 1]);
			if(strcmp(attr[i], "y1") == 0) y1 = parse_double(attr[i + 1]);
			if(strcmp(attr[i], "x2") == 0) x2 = parse_double(attr[i + 1]);
			if(strcmp(attr[i], "y2") == 0) y2 = parse_double(attr[i + 1]);
		}
	}

	fBuilder.move_to(x1, y1);
	fBuilder.line_to(x2, y2);
	fBuilder.end_path();
}

// parse_poly
void
Parser::parse_poly(const char** attr, bool close_flag)
{
	int i;
	double x = 0.0;
	double y = 0.0;

	fBuilder.begin_path();
	for (i = 0; attr[i]; i += 2) {
		if (!parse_attr(attr[i], attr[i + 1])) {
			if (strcmp(attr[i], "points") == 0) {
				fPathTokenizer.set_path_str(attr[i + 1]);
				if (!fPathTokenizer.next())
					throw exception("parse_poly: Too few coordinates");
				x = fPathTokenizer.last_number();
				if (!fPathTokenizer.next())
					throw exception("parse_poly: Too few coordinates");
				y = fPathTokenizer.last_number();
				fBuilder.move_to(x, y);
				while (fPathTokenizer.next()) {
					x = fPathTokenizer.last_number();
					if (!fPathTokenizer.next())
						throw exception("parse_poly: Odd number of coordinates");
					y = fPathTokenizer.last_number();
					fBuilder.line_to(x, y);
				}
			}
		}
	}
	if (close_flag)
		fBuilder.close_subpath();
	fBuilder.end_path();
}

// parse_transform
trans_affine
Parser::parse_transform(const char* str)
{
	trans_affine transform;
	while (*str) {
		if (islower(*str)) {
			if (strncmp(str, "matrix", 6) == 0)		str += parse_matrix(str, transform);	else 
			if (strncmp(str, "translate", 9) == 0)	str += parse_translate(str, transform);	else 
			if (strncmp(str, "rotate", 6) == 0)		str += parse_rotate(str, transform);	else 
			if (strncmp(str, "scale", 5) == 0)		str += parse_scale(str, transform);		else 
			if (strncmp(str, "skewX", 5) == 0)		str += parse_skew_x(str, transform);	else 
			if (strncmp(str, "skewY", 5) == 0)		str += parse_skew_y(str, transform);	else
			{
				++str;
			}
		}
		else
		{
			++str;
		}
	}
	return transform;
}

// parse_gradient
void
Parser::parse_gradient(const char** attr, bool radial)
{
//	printf("Parser::parse_gradient(%s)\n", attr[0]);

	fBuilder.StartGradient(radial);

	for (int32 i = 0; attr[i]; i += 2)
	{
/*		if(!parse_attr(attr[i], attr[i + 1]))
		{*/
			if (strcmp(attr[i], "id") == 0)
				fBuilder.CurrentGradient()->SetID(attr[i + 1]);
			else if(strcmp(attr[i], "gradientTransform") == 0) {
				fBuilder.CurrentGradient()->SetTransformation(parse_transform(attr[i + 1]));
			} else
				fBuilder.CurrentGradient()->AddString(attr[i], attr[i + 1]);
/*		}*/
	}
}

// parse_gradient_stop
void
Parser::parse_gradient_stop(const char** attr)
{
//	printf("Parser::parse_gradient_stop(%s)\n", attr[0]);

	float offset = 0.0;
	rgba8 color;
	for (int32 i = 0; attr[i]; i += 2) {
		if (strcmp(attr[i], "offset") == 0) {
			offset = parse_double(attr[i + 1]);
		} else
		if (strcmp(attr[i], "style") == 0) {
			parse_style(attr[i + 1]);
			// here we get a bit hacky, in order not to change too much code at once...
			// historically, parse_style() was for parsing path attributes only, but
			// it comes in handy here as well, and I added "stop-color" and "stop-opacity"
			// to parse_name_value(). It remembers the color in "fGradientStopColor".
			// The color will of course be broken if the "style" attribute did not contain
			// any valid stuff.
			color = fGradientStopColor;
		} else
		if (strcmp(attr[i], "stop-color") == 0) {
			color = parse_color(attr[i + 1]);
		} else
		if (strcmp(attr[i], "stop-opacity") == 0) {
			color.opacity(parse_double(attr[i + 1]));
		}
	}

//	printf("  offset: %f, color: %d, %d, %d, %d\n", offset, color.r, color.g, color.b, color.a);

	if (SVGGradient* gradient = fBuilder.CurrentGradient()) {
		gradient->AddStop(offset, color);
	} else {
		throw exception("parse_gradient_stop() outside of gradient tag!\n");
	}
}

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

// parse_transform_args
static unsigned
parse_transform_args(const char* str, 
					 double* args, 
					 unsigned max_na, 
					 unsigned* na)
{
	*na = 0;
	const char* ptr = str;
	while(*ptr && *ptr != '(') ++ptr;
	if(*ptr == 0)
	{
		throw exception("parse_transform_args: Invalid syntax");
	}
	const char* end = ptr;
	while(*end && *end != ')') ++end;
	if(*end == 0)
	{
		throw exception("parse_transform_args: Invalid syntax");
	}

	while(ptr < end)
	{
		if(is_numeric(*ptr))
		{
			if(*na >= max_na)
			{
				throw exception("parse_transform_args: Too many arguments");
			}
			args[(*na)++] = atof(ptr);
			while(ptr < end && is_numeric(*ptr)) ++ptr;
		}
		else
		{
			++ptr;
		}
	}
	return unsigned(end - str);
}

// parse_matrix
unsigned
Parser::parse_matrix(const char* str, trans_affine& transform)
{
	double args[6];
	unsigned na = 0;
	unsigned len = parse_transform_args(str, args, 6, &na);
	if(na != 6)
	{
		throw exception("parse_matrix: Invalid number of arguments");
	}
	transform.premultiply(trans_affine(args[0], args[1], args[2], args[3], args[4], args[5]));
	return len;
}

// parse_translate
unsigned
Parser::parse_translate(const char* str, trans_affine& transform)
{
	double args[2];
	unsigned na = 0;
	unsigned len = parse_transform_args(str, args, 2, &na);
	if(na == 1) args[1] = 0.0;
	transform.premultiply(trans_affine_translation(args[0], args[1]));
	return len;
}

// parse_rotate
unsigned
Parser::parse_rotate(const char* str, trans_affine& transform)
{
	double args[3];
	unsigned na = 0;
	unsigned len = parse_transform_args(str, args, 3, &na);
	if(na == 1) 
	{
		transform.premultiply(trans_affine_rotation(deg2rad(args[0])));
	}
	else if(na == 3)
	{
		trans_affine t = trans_affine_translation(-args[1], -args[2]);
		t *= trans_affine_rotation(deg2rad(args[0]));
		t *= trans_affine_translation(args[1], args[2]);
		transform.premultiply(t);
	}
	else
	{
		throw exception("parse_rotate: Invalid number of arguments");
	}
	return len;
}

// parse_scale
unsigned Parser::parse_scale(const char* str, trans_affine& transform)
{
	double args[2];
	unsigned na = 0;
	unsigned len = parse_transform_args(str, args, 2, &na);
	if(na == 1) args[1] = args[0];
	transform.premultiply(trans_affine_scaling(args[0], args[1]));
	return len;
}

// parse_skew_x
unsigned
Parser::parse_skew_x(const char* str, trans_affine& transform)
{
	double arg;
	unsigned na = 0;
	unsigned len = parse_transform_args(str, &arg, 1, &na);
	transform.premultiply(trans_affine_skewing(deg2rad(arg), 0.0));
	return len;
}

// parse_skew_y
unsigned
Parser::parse_skew_y(const char* str, trans_affine& transform)
{
	double arg;
	unsigned na = 0;
	unsigned len = parse_transform_args(str, &arg, 1, &na);
	transform.premultiply(trans_affine_skewing(0.0, deg2rad(arg)));
	return len;
}


} // namespace svg
} // namespace agg



diff --git a/src/apps/icon-o-matic/import_export/svg/SVGParser.h b/src/apps/icon-o-matic/import_export/svg/SVGParser.h
deleted file mode 100644
index 7ce6c81..0000000 100644
--- a/src/apps/icon-o-matic/import_export/svg/SVGParser.h
+++ /dev/null
@@ -1,105 +1,0 @@
/*
 * Copyright 2006, Haiku. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Stephan Aßmus <superstippi@gmx.de>
 */

//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.2
// Copyright (C) 2002-2004 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software 
// is granted provided this copyright notice appears in all copies. 
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
//		  mcseemagg@yahoo.com
//		  http://www.antigrain.com
//----------------------------------------------------------------------------

#ifndef SVG_PARSER_H
#define SVG_PARSER_H

#include "PathTokenizer.h"
#include "DocumentBuilder.h"

namespace agg {
namespace svg {

class Parser {
	enum { buf_size = BUFSIZ };
 public:

								Parser(DocumentBuilder& builder);
	virtual						~Parser();

			void				parse(const char* pathToFile);
			const char*			title() const
									{ return fTitle; }

			bool				TagsIgnored() const
									{ return fTagsIgnored; }

 private:
	// XML event handlers
	static	void				start_element(void* data, const char* el,
											  const char** attr);
	static	void				end_element(void* data, const char* el);
	static	void				content(void* data, const char* s, int len);

			void				parse_svg(const char** attr);
			void				parse_attr(const char** attr);
			void				parse_path(const char** attr);
			void				parse_poly(const char** attr, bool close_flag);
			void				parse_circle(const char** attr);
			void				parse_ellipse(const char** attr);
			void				parse_rect(const char** attr);
			void				parse_line(const char** attr);
			void				parse_style(const char* str);
			trans_affine		parse_transform(const char* str);

			void				parse_gradient(const char** attr, bool radial);
			void				parse_gradient_stop(const char** attr);
		
			unsigned			parse_matrix(const char* str, trans_affine& transform);
			unsigned			parse_translate(const char* str, trans_affine& transform);
			unsigned			parse_rotate(const char* str, trans_affine& transform);
			unsigned			parse_scale(const char* str, trans_affine& transform);
			unsigned			parse_skew_x(const char* str, trans_affine& transform);
			unsigned			parse_skew_y(const char* str, trans_affine& transform);
			
			bool				parse_attr(const char* name,
										   const char* value);
			bool				parse_name_value(const char* nv_start,
												 const char* nv_end);
			void				copy_name(const char* start, const char* end);
			void				copy_value(const char* start, const char* end);
	
private:
			DocumentBuilder&	fBuilder;
			PathTokenizer		fPathTokenizer;
			char*				fBuffer;
			char*				fTitle;
			unsigned			fTitleLength;

			bool				fTitleFlag;
			bool				fPathFlag;

			char*				fAttrName;
			char*				fAttrValue;
			unsigned			fAttrNameLength;
			unsigned			fAttrValueLength;

			rgba8				fGradientStopColor;

			bool				fTagsIgnored;
};

} // namespace svg
} // namespace agg

#endif // SVG_PARSER_H
diff --git a/src/apps/icon-o-matic/import_export/svg/nanosvg.h b/src/apps/icon-o-matic/import_export/svg/nanosvg.h
new file mode 100644
index 0000000..e5f6900 100644
--- /dev/null
+++ b/src/apps/icon-o-matic/import_export/svg/nanosvg.h
@@ -1,0 +1,2975 @@
/*
 * Copyright (c) 2013-14 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-Grain Geometry 2.4 SVG example
 * Copyright (C) 2002-2004 Maxim Shemanarev (McSeem) (http://www.antigrain.com/)
 *
 * Arc calculation code based on canvg (https://code.google.com/p/canvg/)
 *
 * Bounding box calculation based on http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
 *
 */

#ifndef NANOSVG_H
#define NANOSVG_H

#ifndef NANOSVG_CPLUSPLUS
#ifdef __cplusplus
extern "C" {
#endif
#endif

// NanoSVG is a simple stupid single-header-file SVG parse. The output of the parser is a list of cubic bezier shapes.
//
// The library suits well for anything from rendering scalable icons in your editor application to prototyping a game.
//
// NanoSVG supports a wide range of SVG features, but something may be missing, feel free to create a pull request!
//
// The shapes in the SVG images are transformed by the viewBox and converted to specified units.
// That is, you should get the same looking data as your designed in your favorite app.
//
// NanoSVG can return the paths in few different units. For example if you want to render an image, you may choose
// to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you may want to use millimeters.
//
// The units passed to NanoSVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'.
// DPI (dots-per-inch) controls how the unit conversion is done.
//
// If you don't know or care about the units stuff, "px" and 96 should get you going.


/* Example Usage:
	// Load SVG
	NSVGimage* image;
	image = nsvgParseFromFile("test.svg", "px", 96);
	printf("size: %f x %f\n", image->width, image->height);
	// Use...
	for (NSVGshape *shape = image->shapes; shape != NULL; shape = shape->next) {
		for (NSVGpath *path = shape->paths; path != NULL; path = path->next) {
			for (int i = 0; i < path->npts-1; i += 3) {
				float* p = &path->pts[i*2];
				drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]);
			}
		}
	}
	// Delete
	nsvgDelete(image);
*/

enum NSVGpaintType {
	NSVG_PAINT_NONE = 0,
	NSVG_PAINT_COLOR = 1,
	NSVG_PAINT_LINEAR_GRADIENT = 2,
	NSVG_PAINT_RADIAL_GRADIENT = 3
};

enum NSVGspreadType {
	NSVG_SPREAD_PAD = 0,
	NSVG_SPREAD_REFLECT = 1,
	NSVG_SPREAD_REPEAT = 2
};

enum NSVGlineJoin {
	NSVG_JOIN_MITER = 0,
	NSVG_JOIN_ROUND = 1,
	NSVG_JOIN_BEVEL = 2
};

enum NSVGlineCap {
	NSVG_CAP_BUTT = 0,
	NSVG_CAP_ROUND = 1,
	NSVG_CAP_SQUARE = 2
};

enum NSVGfillRule {
	NSVG_FILLRULE_NONZERO = 0,
	NSVG_FILLRULE_EVENODD = 1
};

enum NSVGflags {
	NSVG_FLAGS_VISIBLE = 0x01
};

typedef struct NSVGgradientStop {
	unsigned int color;
	float offset;
} NSVGgradientStop;

typedef struct NSVGgradient {
	float xform[6];
	char spread;
	float fx, fy;
	int nstops;
	NSVGgradientStop stops[1];
} NSVGgradient;

typedef struct NSVGpaint {
	char type;
	union {
		unsigned int color;
		NSVGgradient* gradient;
	};
} NSVGpaint;

typedef struct NSVGpath
{
	float* pts;					// Cubic bezier points: x0,y0, [cpx1,cpx1,cpx2,cpy2,x1,y1], ...
	int npts;					// Total number of bezier points.
	char closed;				// Flag indicating if shapes should be treated as closed.
	float bounds[4];			// Tight bounding box of the shape [minx,miny,maxx,maxy].
	struct NSVGpath* next;		// Pointer to next path, or NULL if last element.
} NSVGpath;

typedef struct NSVGshape
{
	char id[64];				// Optional 'id' attr of the shape or its group
	NSVGpaint fill;				// Fill paint
	NSVGpaint stroke;			// Stroke paint
	float opacity;				// Opacity of the shape.
	float strokeWidth;			// Stroke width (scaled).
	float strokeDashOffset;		// Stroke dash offset (scaled).
	float strokeDashArray[8];			// Stroke dash array (scaled).
	char strokeDashCount;				// Number of dash values in dash array.
	char strokeLineJoin;		// Stroke join type.
	char strokeLineCap;			// Stroke cap type.
	float miterLimit;			// Miter limit
	char fillRule;				// Fill rule, see NSVGfillRule.
	unsigned char flags;		// Logical or of NSVG_FLAGS_* flags
	float bounds[4];			// Tight bounding box of the shape [minx,miny,maxx,maxy].
	NSVGpath* paths;			// Linked list of paths in the image.
	struct NSVGshape* next;		// Pointer to next shape, or NULL if last element.
} NSVGshape;

typedef struct NSVGimage
{
	float width;				// Width of the image.
	float height;				// Height of the image.
	NSVGshape* shapes;			// Linked list of shapes in the image.
} NSVGimage;

// Parses SVG file from a file, returns SVG image as paths.
NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi);

// Parses SVG file from a null terminated string, returns SVG image as paths.
// Important note: changes the string.
NSVGimage* nsvgParse(char* input, const char* units, float dpi);

// Duplicates a path.
NSVGpath* nsvgDuplicatePath(NSVGpath* p);

// Deletes an image.
void nsvgDelete(NSVGimage* image);

#ifndef NANOSVG_CPLUSPLUS
#ifdef __cplusplus
}
#endif
#endif

#endif // NANOSVG_H

#ifdef NANOSVG_IMPLEMENTATION

#include <string.h>
#include <stdlib.h>
#include <math.h>

#define NSVG_PI (3.14159265358979323846264338327f)
#define NSVG_KAPPA90 (0.5522847493f)	// Length proportional to radius of a cubic bezier handle for 90deg arcs.

#define NSVG_ALIGN_MIN 0
#define NSVG_ALIGN_MID 1
#define NSVG_ALIGN_MAX 2
#define NSVG_ALIGN_NONE 0
#define NSVG_ALIGN_MEET 1
#define NSVG_ALIGN_SLICE 2

#define NSVG_NOTUSED(v) do { (void)(1 ? (void)0 : ( (void)(v) ) ); } while(0)
#define NSVG_RGB(r, g, b) (((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16))

#ifdef _MSC_VER
	#pragma warning (disable: 4996) // Switch off security warnings
	#pragma warning (disable: 4100) // Switch off unreferenced formal parameter warnings
	#ifdef __cplusplus
	#define NSVG_INLINE inline
	#else
	#define NSVG_INLINE
	#endif
#else
	#define NSVG_INLINE inline
#endif


static int nsvg__isspace(char c)
{
	return strchr(" \t\n\v\f\r", c) != 0;
}

static int nsvg__isdigit(char c)
{
	return c >= '0' && c <= '9';
}

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

static NSVG_INLINE float nsvg__minf(float a, float b) { return a < b ? a : b; }
static NSVG_INLINE float nsvg__maxf(float a, float b) { return a > b ? a : b; }


// Simple XML parser

#define NSVG_XML_TAG 1
#define NSVG_XML_CONTENT 2
#define NSVG_XML_MAX_ATTRIBS 256

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

	if (contentCb)
		(*contentCb)(ud, s);
}

static void nsvg__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[NSVG_XML_MAX_ATTRIBS];
	int nattr = 0;
	char* name;
	int start = 0;
	int end = 0;
	char quote;

	// Skip white space after the '<'
	while (*s && nsvg__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 && !nsvg__isspace(*s)) s++;
	if (*s) { *s++ = '\0'; }

	// Get attribs
	while (!end && *s && nattr < NSVG_XML_MAX_ATTRIBS-3) {
		char* name = NULL;
		char* value = NULL;

		// Skip white space before the attrib name
		while (*s && nsvg__isspace(*s)) s++;
		if (!*s) break;
		if (*s == '/') {
			end = 1;
			break;
		}
		name = s;
		// Find end of the attrib name.
		while (*s && !nsvg__isspace(*s) && *s != '=') s++;
		if (*s) { *s++ = '\0'; }
		// Skip until the beginning of the value.
		while (*s && *s != '\"' && *s != '\'') s++;
		if (!*s) break;
		quote = *s;
		s++;
		// Store value and find the end of it.
		value = s;
		while (*s && *s != quote) s++;
		if (*s) { *s++ = '\0'; }

		// Store only well formed attributes
		if (name && value) {
			attr[nattr++] = name;
			attr[nattr++] = value;
		}
	}

	// List terminator
	attr[nattr++] = 0;
	attr[nattr++] = 0;

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

int nsvg__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 = NSVG_XML_CONTENT;
	while (*s) {
		if (*s == '<' && state == NSVG_XML_CONTENT) {
			// Start of a tag
			*s++ = '\0';
			nsvg__parseContent(mark, contentCb, ud);
			mark = s;
			state = NSVG_XML_TAG;
		} else if (*s == '>' && state == NSVG_XML_TAG) {
			// Start of a content or new tag.
			*s++ = '\0';
			nsvg__parseElement(mark, startelCb, endelCb, ud);
			mark = s;
			state = NSVG_XML_CONTENT;
		} else {
			s++;
		}
	}

	return 1;
}


/* Simple SVG parser. */

#define NSVG_MAX_ATTR 128

enum NSVGgradientUnits {
	NSVG_USER_SPACE = 0,
	NSVG_OBJECT_SPACE = 1
};

#define NSVG_MAX_DASHES 8

enum NSVGunits {
	NSVG_UNITS_USER,
	NSVG_UNITS_PX,
	NSVG_UNITS_PT,
	NSVG_UNITS_PC,
	NSVG_UNITS_MM,
	NSVG_UNITS_CM,
	NSVG_UNITS_IN,
	NSVG_UNITS_PERCENT,
	NSVG_UNITS_EM,
	NSVG_UNITS_EX
};

typedef struct NSVGcoordinate {
	float value;
	int units;
} NSVGcoordinate;

typedef struct NSVGlinearData {
	NSVGcoordinate x1, y1, x2, y2;
} NSVGlinearData;

typedef struct NSVGradialData {
	NSVGcoordinate cx, cy, r, fx, fy;
} NSVGradialData;

typedef struct NSVGgradientData
{
	char id[64];
	char ref[64];
	char type;
	union {
		NSVGlinearData linear;
		NSVGradialData radial;
	};
	char spread;
	char units;
	float xform[6];
	int nstops;
	NSVGgradientStop* stops;
	struct NSVGgradientData* next;
} NSVGgradientData;

typedef struct NSVGattrib
{
	char id[64];
	float xform[6];
	unsigned int fillColor;
	unsigned int strokeColor;
	float opacity;
	float fillOpacity;
	float strokeOpacity;
	char fillGradient[64];
	char strokeGradient[64];
	float strokeWidth;
	float strokeDashOffset;
	float strokeDashArray[NSVG_MAX_DASHES];
	int strokeDashCount;
	char strokeLineJoin;
	char strokeLineCap;
	float miterLimit;
	char fillRule;
	float fontSize;
	unsigned int stopColor;
	float stopOpacity;
	float stopOffset;
	char hasFill;
	char hasStroke;
	char visible;
} NSVGattrib;

typedef struct NSVGparser
{
	NSVGattrib attr[NSVG_MAX_ATTR];
	int attrHead;
	float* pts;
	int npts;
	int cpts;
	NSVGpath* plist;
	NSVGimage* image;
	NSVGgradientData* gradients;
	NSVGshape* shapesTail;
	float viewMinx, viewMiny, viewWidth, viewHeight;
	int alignX, alignY, alignType;
	float dpi;
	char pathFlag;
	char defsFlag;
} NSVGparser;

static void nsvg__xformIdentity(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 nsvg__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 nsvg__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 nsvg__xformSetSkewX(float* t, float a)
{
	t[0] = 1.0f; t[1] = 0.0f;
	t[2] = tanf(a); t[3] = 1.0f;
	t[4] = 0.0f; t[5] = 0.0f;
}

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

static void nsvg__xformSetRotation(float* t, float a)
{
	float cs = cosf(a), sn = sinf(a);
	t[0] = cs; t[1] = sn;
	t[2] = -sn; t[3] = cs;
	t[4] = 0.0f; t[5] = 0.0f;
}

static void nsvg__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 nsvg__xformInverse(float* inv, float* t)
{
	double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1];
	if (det > -1e-6 && det < 1e-6) {
		nsvg__xformIdentity(t);
		return;
	}
	invdet = 1.0 / det;
	inv[0] = (float)(t[3] * invdet);
	inv[2] = (float)(-t[2] * invdet);
	inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet);
	inv[1] = (float)(-t[1] * invdet);
	inv[3] = (float)(t[0] * invdet);
	inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet);
}

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

static void nsvg__xformPoint(float* dx, float* dy, float x, float y, float* t)
{
	*dx = x*t[0] + y*t[2] + t[4];
	*dy = x*t[1] + y*t[3] + t[5];
}

static void nsvg__xformVec(float* dx, float* dy, float x, float y, float* t)
{
	*dx = x*t[0] + y*t[2];
	*dy = x*t[1] + y*t[3];
}

#define NSVG_EPSILON (1e-12)

static int nsvg__ptInBounds(float* pt, float* bounds)
{
	return pt[0] >= bounds[0] && pt[0] <= bounds[2] && pt[1] >= bounds[1] && pt[1] <= bounds[3];
}


static double nsvg__evalBezier(double t, double p0, double p1, double p2, double p3)
{
	double it = 1.0-t;
	return it*it*it*p0 + 3.0*it*it*t*p1 + 3.0*it*t*t*p2 + t*t*t*p3;
}

static void nsvg__curveBounds(float* bounds, float* curve)
{
	int i, j, count;
	double roots[2], a, b, c, b2ac, t, v;
	float* v0 = &curve[0];
	float* v1 = &curve[2];
	float* v2 = &curve[4];
	float* v3 = &curve[6];

	// Start the bounding box by end points
	bounds[0] = nsvg__minf(v0[0], v3[0]);
	bounds[1] = nsvg__minf(v0[1], v3[1]);
	bounds[2] = nsvg__maxf(v0[0], v3[0]);
	bounds[3] = nsvg__maxf(v0[1], v3[1]);

	// Bezier curve fits inside the convex hull of it's control points.
	// If control points are inside the bounds, we're done.
	if (nsvg__ptInBounds(v1, bounds) && nsvg__ptInBounds(v2, bounds))
		return;

	// Add bezier curve inflection points in X and Y.
	for (i = 0; i < 2; i++) {
		a = -3.0 * v0[i] + 9.0 * v1[i] - 9.0 * v2[i] + 3.0 * v3[i];
		b = 6.0 * v0[i] - 12.0 * v1[i] + 6.0 * v2[i];
		c = 3.0 * v1[i] - 3.0 * v0[i];
		count = 0;
		if (fabs(a) < NSVG_EPSILON) {
			if (fabs(b) > NSVG_EPSILON) {
				t = -c / b;
				if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON)
					roots[count++] = t;
			}
		} else {
			b2ac = b*b - 4.0*c*a;
			if (b2ac > NSVG_EPSILON) {
				t = (-b + sqrt(b2ac)) / (2.0 * a);
				if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON)
					roots[count++] = t;
				t = (-b - sqrt(b2ac)) / (2.0 * a);
				if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON)
					roots[count++] = t;
			}
		}
		for (j = 0; j < count; j++) {
			v = nsvg__evalBezier(roots[j], v0[i], v1[i], v2[i], v3[i]);
			bounds[0+i] = nsvg__minf(bounds[0+i], (float)v);
			bounds[2+i] = nsvg__maxf(bounds[2+i], (float)v);
		}
	}
}

static NSVGparser* nsvg__createParser()
{
	NSVGparser* p;
	p = (NSVGparser*)malloc(sizeof(NSVGparser));
	if (p == NULL) goto error;
	memset(p, 0, sizeof(NSVGparser));

	p->image = (NSVGimage*)malloc(sizeof(NSVGimage));
	if (p->image == NULL) goto error;
	memset(p->image, 0, sizeof(NSVGimage));

	// Init style
	nsvg__xformIdentity(p->attr[0].xform);
	memset(p->attr[0].id, 0, sizeof p->attr[0].id);
	p->attr[0].fillColor = NSVG_RGB(0,0,0);
	p->attr[0].strokeColor = NSVG_RGB(0,0,0);
	p->attr[0].opacity = 1;
	p->attr[0].fillOpacity = 1;
	p->attr[0].strokeOpacity = 1;
	p->attr[0].stopOpacity = 1;
	p->attr[0].strokeWidth = 1;
	p->attr[0].strokeLineJoin = NSVG_JOIN_MITER;
	p->attr[0].strokeLineCap = NSVG_CAP_BUTT;
	p->attr[0].miterLimit = 4;
	p->attr[0].fillRule = NSVG_FILLRULE_NONZERO;
	p->attr[0].hasFill = 1;
	p->attr[0].visible = 1;

	return p;

error:
	if (p) {
		if (p->image) free(p->image);
		free(p);
	}
	return NULL;
}

static void nsvg__deletePaths(NSVGpath* path)
{
	while (path) {
		NSVGpath *next = path->next;
		if (path->pts != NULL)
			free(path->pts);
		free(path);
		path = next;
	}
}

static void nsvg__deletePaint(NSVGpaint* paint)
{
	if (paint->type == NSVG_PAINT_LINEAR_GRADIENT || paint->type == NSVG_PAINT_RADIAL_GRADIENT)
		free(paint->gradient);
}

static void nsvg__deleteGradientData(NSVGgradientData* grad)
{
	NSVGgradientData* next;
	while (grad != NULL) {
		next = grad->next;
		free(grad->stops);
		free(grad);
		grad = next;
	}
}

static void nsvg__deleteParser(NSVGparser* p)
{
	if (p != NULL) {
		nsvg__deletePaths(p->plist);
		nsvg__deleteGradientData(p->gradients);
		nsvgDelete(p->image);
		free(p->pts);
		free(p);
	}
}

static void nsvg__resetPath(NSVGparser* p)
{
	p->npts = 0;
}

static void nsvg__addPoint(NSVGparser* p, float x, float y)
{
	if (p->npts+1 > p->cpts) {
		p->cpts = p->cpts ? p->cpts*2 : 8;
		p->pts = (float*)realloc(p->pts, p->cpts*2*sizeof(float));
		if (!p->pts) return;
	}
	p->pts[p->npts*2+0] = x;
	p->pts[p->npts*2+1] = y;
	p->npts++;
}

static void nsvg__moveTo(NSVGparser* p, float x, float y)
{
	if (p->npts > 0) {
		p->pts[(p->npts-1)*2+0] = x;
		p->pts[(p->npts-1)*2+1] = y;
	} else {
		nsvg__addPoint(p, x, y);
	}
}

static void nsvg__lineTo(NSVGparser* p, float x, float y)
{
	float px,py, dx,dy;
	if (p->npts > 0) {
		px = p->pts[(p->npts-1)*2+0];
		py = p->pts[(p->npts-1)*2+1];
		dx = x - px;
		dy = y - py;
		nsvg__addPoint(p, px + dx/3.0f, py + dy/3.0f);
		nsvg__addPoint(p, x - dx/3.0f, y - dy/3.0f);
		nsvg__addPoint(p, x, y);
	}
}

static void nsvg__cubicBezTo(NSVGparser* p, float cpx1, float cpy1, float cpx2, float cpy2, float x, float y)
{
	nsvg__addPoint(p, cpx1, cpy1);
	nsvg__addPoint(p, cpx2, cpy2);
	nsvg__addPoint(p, x, y);
}

static NSVGattrib* nsvg__getAttr(NSVGparser* p)
{
	return &p->attr[p->attrHead];
}

static void nsvg__pushAttr(NSVGparser* p)
{
	if (p->attrHead < NSVG_MAX_ATTR-1) {
		p->attrHead++;
		memcpy(&p->attr[p->attrHead], &p->attr[p->attrHead-1], sizeof(NSVGattrib));
	}
}

static void nsvg__popAttr(NSVGparser* p)
{
	if (p->attrHead > 0)
		p->attrHead--;
}

static float nsvg__actualOrigX(NSVGparser* p)
{
	return p->viewMinx;
}

static float nsvg__actualOrigY(NSVGparser* p)
{
	return p->viewMiny;
}

static float nsvg__actualWidth(NSVGparser* p)
{
	return p->viewWidth;
}

static float nsvg__actualHeight(NSVGparser* p)
{
	return p->viewHeight;
}

static float nsvg__actualLength(NSVGparser* p)
{
	float w = nsvg__actualWidth(p), h = nsvg__actualHeight(p);
	return sqrtf(w*w + h*h) / sqrtf(2.0f);
}

static float nsvg__convertToPixels(NSVGparser* p, NSVGcoordinate c, float orig, float length)
{
	NSVGattrib* attr = nsvg__getAttr(p);
	switch (c.units) {
		case NSVG_UNITS_USER:		return c.value;
		case NSVG_UNITS_PX:			return c.value;
		case NSVG_UNITS_PT:			return c.value / 72.0f * p->dpi;
		case NSVG_UNITS_PC:			return c.value / 6.0f * p->dpi;
		case NSVG_UNITS_MM:			return c.value / 25.4f * p->dpi;
		case NSVG_UNITS_CM:			return c.value / 2.54f * p->dpi;
		case NSVG_UNITS_IN:			return c.value * p->dpi;
		case NSVG_UNITS_EM:			return c.value * attr->fontSize;
		case NSVG_UNITS_EX:			return c.value * attr->fontSize * 0.52f; // x-height of Helvetica.
		case NSVG_UNITS_PERCENT:	return orig + c.value / 100.0f * length;
		default:					return c.value;
	}
	return c.value;
}

static NSVGgradientData* nsvg__findGradientData(NSVGparser* p, const char* id)
{
	NSVGgradientData* grad = p->gradients;
	while (grad) {
		if (strcmp(grad->id, id) == 0)
			return grad;
		grad = grad->next;
	}
	return NULL;
}

static NSVGgradient* nsvg__createGradient(NSVGparser* p, const char* id, const float* localBounds, char* paintType)
{
	NSVGattrib* attr = nsvg__getAttr(p);
	NSVGgradientData* data = NULL;
	NSVGgradientData* ref = NULL;
	NSVGgradientStop* stops = NULL;
	NSVGgradient* grad;
	float ox, oy, sw, sh, sl;
	int nstops = 0;

	data = nsvg__findGradientData(p, id);
	if (data == NULL) return NULL;

	// TODO: use ref to fill in all unset values too.
	ref = data;
	while (ref != NULL) {
		if (stops == NULL && ref->stops != NULL) {
			stops = ref->stops;
			nstops = ref->nstops;
			break;
		}
		ref = nsvg__findGradientData(p, ref->ref);
	}
	if (stops == NULL) return NULL;

	grad = (NSVGgradient*)malloc(sizeof(NSVGgradient) + sizeof(NSVGgradientStop)*(nstops-1));
	if (grad == NULL) return NULL;

	// The shape width and height.
	if (data->units == NSVG_OBJECT_SPACE) {
		ox = localBounds[0];
		oy = localBounds[1];
		sw = localBounds[2] - localBounds[0];
		sh = localBounds[3] - localBounds[1];
	} else {
		ox = nsvg__actualOrigX(p);
		oy = nsvg__actualOrigY(p);
		sw = nsvg__actualWidth(p);
		sh = nsvg__actualHeight(p);
	}
	sl = sqrtf(sw*sw + sh*sh) / sqrtf(2.0f);

	if (data->type == NSVG_PAINT_LINEAR_GRADIENT) {
		float x1, y1, x2, y2, dx, dy;
		x1 = nsvg__convertToPixels(p, data->linear.x1, ox, sw);
		y1 = nsvg__convertToPixels(p, data->linear.y1, oy, sh);
		x2 = nsvg__convertToPixels(p, data->linear.x2, ox, sw);
		y2 = nsvg__convertToPixels(p, data->linear.y2, oy, sh);
		// Calculate transform aligned to the line
		dx = x2 - x1;
		dy = y2 - y1;
		grad->xform[0] = dy; grad->xform[1] = -dx;
		grad->xform[2] = dx; grad->xform[3] = dy;
		grad->xform[4] = x1; grad->xform[5] = y1;
	} else {
		float cx, cy, fx, fy, r;
		cx = nsvg__convertToPixels(p, data->radial.cx, ox, sw);
		cy = nsvg__convertToPixels(p, data->radial.cy, oy, sh);
		fx = nsvg__convertToPixels(p, data->radial.fx, ox, sw);
		fy = nsvg__convertToPixels(p, data->radial.fy, oy, sh);
		r = nsvg__convertToPixels(p, data->radial.r, 0, sl);
		// Calculate transform aligned to the circle
		grad->xform[0] = r; grad->xform[1] = 0;
		grad->xform[2] = 0; grad->xform[3] = r;
		grad->xform[4] = cx; grad->xform[5] = cy;
		grad->fx = fx / r;
		grad->fy = fy / r;
	}

	nsvg__xformMultiply(grad->xform, data->xform);
	nsvg__xformMultiply(grad->xform, attr->xform);

	grad->spread = data->spread;
	memcpy(grad->stops, stops, nstops*sizeof(NSVGgradientStop));
	grad->nstops = nstops;

	*paintType = data->type;

	return grad;
}

static float nsvg__getAverageScale(float* t)
{
	float sx = sqrtf(t[0]*t[0] + t[2]*t[2]);
	float sy = sqrtf(t[1]*t[1] + t[3]*t[3]);
	return (sx + sy) * 0.5f;
}

static void nsvg__getLocalBounds(float* bounds, NSVGshape *shape, float* xform)
{
	NSVGpath* path;
	float curve[4*2], curveBounds[4];
	int i, first = 1;
	for (path = shape->paths; path != NULL; path = path->next) {
		nsvg__xformPoint(&curve[0], &curve[1], path->pts[0], path->pts[1], xform);
		for (i = 0; i < path->npts-1; i += 3) {
			nsvg__xformPoint(&curve[2], &curve[3], path->pts[(i+1)*2], path->pts[(i+1)*2+1], xform);
			nsvg__xformPoint(&curve[4], &curve[5], path->pts[(i+2)*2], path->pts[(i+2)*2+1], xform);
			nsvg__xformPoint(&curve[6], &curve[7], path->pts[(i+3)*2], path->pts[(i+3)*2+1], xform);
			nsvg__curveBounds(curveBounds, curve);
			if (first) {
				bounds[0] = curveBounds[0];
				bounds[1] = curveBounds[1];
				bounds[2] = curveBounds[2];
				bounds[3] = curveBounds[3];
				first = 0;
			} else {
				bounds[0] = nsvg__minf(bounds[0], curveBounds[0]);
				bounds[1] = nsvg__minf(bounds[1], curveBounds[1]);
				bounds[2] = nsvg__maxf(bounds[2], curveBounds[2]);
				bounds[3] = nsvg__maxf(bounds[3], curveBounds[3]);
			}
			curve[0] = curve[6];
			curve[1] = curve[7];
		}
	}
}

static void nsvg__addShape(NSVGparser* p)
{
	NSVGattrib* attr = nsvg__getAttr(p);
	float scale = 1.0f;
	NSVGshape* shape;
	NSVGpath* path;
	int i;

	if (p->plist == NULL)
		return;

	shape = (NSVGshape*)malloc(sizeof(NSVGshape));
	if (shape == NULL) goto error;
	memset(shape, 0, sizeof(NSVGshape));

	memcpy(shape->id, attr->id, sizeof shape->id);
	scale = nsvg__getAverageScale(attr->xform);
	shape->strokeWidth = attr->strokeWidth * scale;
	shape->strokeDashOffset = attr->strokeDashOffset * scale;
	shape->strokeDashCount = (char)attr->strokeDashCount;
	for (i = 0; i < attr->strokeDashCount; i++)
		shape->strokeDashArray[i] = attr->strokeDashArray[i] * scale;
	shape->strokeLineJoin = attr->strokeLineJoin;
	shape->strokeLineCap = attr->strokeLineCap;
	shape->miterLimit = attr->miterLimit;
	shape->fillRule = attr->fillRule;
	shape->opacity = attr->opacity;

	shape->paths = p->plist;
	p->plist = NULL;

	// Calculate shape bounds
	shape->bounds[0] = shape->paths->bounds[0];
	shape->bounds[1] = shape->paths->bounds[1];
	shape->bounds[2] = shape->paths->bounds[2];
	shape->bounds[3] = shape->paths->bounds[3];
	for (path = shape->paths->next; path != NULL; path = path->next) {
		shape->bounds[0] = nsvg__minf(shape->bounds[0], path->bounds[0]);
		shape->bounds[1] = nsvg__minf(shape->bounds[1], path->bounds[1]);
		shape->bounds[2] = nsvg__maxf(shape->bounds[2], path->bounds[2]);
		shape->bounds[3] = nsvg__maxf(shape->bounds[3], path->bounds[3]);
	}

	// Set fill
	if (attr->hasFill == 0) {
		shape->fill.type = NSVG_PAINT_NONE;
	} else if (attr->hasFill == 1) {
		shape->fill.type = NSVG_PAINT_COLOR;
		shape->fill.color = attr->fillColor;
		shape->fill.color |= (unsigned int)(attr->fillOpacity*255) << 24;
	} else if (attr->hasFill == 2) {
		float inv[6], localBounds[4];
		nsvg__xformInverse(inv, attr->xform);
		nsvg__getLocalBounds(localBounds, shape, inv);
		shape->fill.gradient = nsvg__createGradient(p, attr->fillGradient, localBounds, &shape->fill.type);
		if (shape->fill.gradient == NULL) {
			shape->fill.type = NSVG_PAINT_NONE;
		}
	}

	// Set stroke
	if (attr->hasStroke == 0) {
		shape->stroke.type = NSVG_PAINT_NONE;
	} else if (attr->hasStroke == 1) {
		shape->stroke.type = NSVG_PAINT_COLOR;
		shape->stroke.color = attr->strokeColor;
		shape->stroke.color |= (unsigned int)(attr->strokeOpacity*255) << 24;
	} else if (attr->hasStroke == 2) {
		float inv[6], localBounds[4];
		nsvg__xformInverse(inv, attr->xform);
		nsvg__getLocalBounds(localBounds, shape, inv);
		shape->stroke.gradient = nsvg__createGradient(p, attr->strokeGradient, localBounds, &shape->stroke.type);
		if (shape->stroke.gradient == NULL)
			shape->stroke.type = NSVG_PAINT_NONE;
	}

	// Set flags
	shape->flags = (attr->visible ? NSVG_FLAGS_VISIBLE : 0x00);

	// Add to tail
	if (p->image->shapes == NULL)
		p->image->shapes = shape;
	else
		p->shapesTail->next = shape;
	p->shapesTail = shape;

	return;

error:
	if (shape) free(shape);
}

static void nsvg__addPath(NSVGparser* p, char closed)
{
	NSVGattrib* attr = nsvg__getAttr(p);
	NSVGpath* path = NULL;
	float bounds[4];
	float* curve;
	int i;

	if (p->npts < 4)
		return;

	if (closed)
		nsvg__lineTo(p, p->pts[0], p->pts[1]);

	path = (NSVGpath*)malloc(sizeof(NSVGpath));
	if (path == NULL) goto error;
	memset(path, 0, sizeof(NSVGpath));

	path->pts = (float*)malloc(p->npts*2*sizeof(float));
	if (path->pts == NULL) goto error;
	path->closed = closed;
	path->npts = p->npts;

	// Transform path.
	for (i = 0; i < p->npts; ++i)
		nsvg__xformPoint(&path->pts[i*2], &path->pts[i*2+1], p->pts[i*2], p->pts[i*2+1], attr->xform);

	// Find bounds
	for (i = 0; i < path->npts-1; i += 3) {
		curve = &path->pts[i*2];
		nsvg__curveBounds(bounds, curve);
		if (i == 0) {
			path->bounds[0] = bounds[0];
			path->bounds[1] = bounds[1];
			path->bounds[2] = bounds[2];
			path->bounds[3] = bounds[3];
		} else {
			path->bounds[0] = nsvg__minf(path->bounds[0], bounds[0]);
			path->bounds[1] = nsvg__minf(path->bounds[1], bounds[1]);
			path->bounds[2] = nsvg__maxf(path->bounds[2], bounds[2]);
			path->bounds[3] = nsvg__maxf(path->bounds[3], bounds[3]);
		}
	}

	path->next = p->plist;
	p->plist = path;

	return;

error:
	if (path != NULL) {
		if (path->pts != NULL) free(path->pts);
		free(path);
	}
}

// We roll our own string to float because the std library one uses locale and messes things up.
static double nsvg__atof(const char* s)
{
	char* cur = (char*)s;
	char* end = NULL;
	double res = 0.0, sign = 1.0;
	long long intPart = 0, fracPart = 0;
	char hasIntPart = 0, hasFracPart = 0;

	// Parse optional sign
	if (*cur == '+') {
		cur++;
	} else if (*cur == '-') {
		sign = -1;
		cur++;
	}

	// Parse integer part
	if (nsvg__isdigit(*cur)) {
		// Parse digit sequence
		intPart = strtoll(cur, &end, 10);
		if (cur != end) {
			res = (double)intPart;
			hasIntPart = 1;
			cur = end;
		}
	}

	// Parse fractional part.
	if (*cur == '.') {
		cur++; // Skip '.'
		if (nsvg__isdigit(*cur)) {
			// Parse digit sequence
			fracPart = strtoll(cur, &end, 10);
			if (cur != end) {
				res += (double)fracPart / pow(10.0, (double)(end - cur));
				hasFracPart = 1;
				cur = end;
			}
		}
	}

	// A valid number should have integer or fractional part.
	if (!hasIntPart && !hasFracPart)
		return 0.0;

	// Parse optional exponent
	if (*cur == 'e' || *cur == 'E') {
		long expPart = 0;
		cur++; // skip 'E'
		expPart = strtol(cur, &end, 10); // Parse digit sequence with sign
		if (cur != end) {
			res *= pow(10.0, (double)expPart);
		}
	}

	return res * sign;
}


static const char* nsvg__parseNumber(const char* s, char* it, const int size)
{
	const int last = size-1;
	int i = 0;

	// sign
	if (*s == '-' || *s == '+') {
		if (i < last) it[i++] = *s;
		s++;
	}
	// integer part
	while (*s && nsvg__isdigit(*s)) {
		if (i < last) it[i++] = *s;
		s++;
	}
	if (*s == '.') {
		// decimal point
		if (i < last) it[i++] = *s;
		s++;
		// fraction part
		while (*s && nsvg__isdigit(*s)) {
			if (i < last) it[i++] = *s;
			s++;
		}
	}
	// exponent
	if ((*s == 'e' || *s == 'E') && (s[1] != 'm' && s[1] != 'x')) {
		if (i < last) it[i++] = *s;
		s++;
		if (*s == '-' || *s == '+') {
			if (i < last) it[i++] = *s;
			s++;
		}
		while (*s && nsvg__isdigit(*s)) {
			if (i < last) it[i++] = *s;
			s++;
		}
	}
	it[i] = '\0';

	return s;
}

static const char* nsvg__getNextPathItem(const char* s, char* it)
{
	it[0] = '\0';
	// Skip white spaces and commas
	while (*s && (nsvg__isspace(*s) || *s == ',')) s++;
	if (!*s) return s;
	if (*s == '-' || *s == '+' || *s == '.' || nsvg__isdigit(*s)) {
		s = nsvg__parseNumber(s, it, 64);
	} else {
		// Parse command
		it[0] = *s++;
		it[1] = '\0';
		return s;
	}

	return s;
}

static unsigned int nsvg__parseColorHex(const char* str)
{
	unsigned int c = 0, r = 0, g = 0, b = 0;
	int n = 0;
	str++; // skip #
	// Calculate number of characters.
	while(str[n] && !nsvg__isspace(str[n]))
		n++;
	if (n == 6) {
		sscanf(str, "%x", &c);
	} else if (n == 3) {
		sscanf(str, "%x", &c);
		c = (c&0xf) | ((c&0xf0) << 4) | ((c&0xf00) << 8);
		c |= c<<4;
	}
	r = (c >> 16) & 0xff;
	g = (c >> 8) & 0xff;
	b = c & 0xff;
	return NSVG_RGB(r,g,b);
}

static unsigned int nsvg__parseColorRGB(const char* str)
{
	int r = -1, g = -1, b = -1;
	char s1[32]="", s2[32]="";
	sscanf(str + 4, "%d%[%%, \t]%d%[%%, \t]%d", &r, s1, &g, s2, &b);
	if (strchr(s1, '%')) {
		return NSVG_RGB((r*255)/100,(g*255)/100,(b*255)/100);
	} else {
		return NSVG_RGB(r,g,b);
	}
}

typedef struct NSVGNamedColor {
	const char* name;
	unsigned int color;
} NSVGNamedColor;

NSVGNamedColor nsvg__colors[] = {

	{ "red", NSVG_RGB(255, 0, 0) },
	{ "green", NSVG_RGB( 0, 128, 0) },
	{ "blue", NSVG_RGB( 0, 0, 255) },
	{ "yellow", NSVG_RGB(255, 255, 0) },
	{ "cyan", NSVG_RGB( 0, 255, 255) },
	{ "magenta", NSVG_RGB(255, 0, 255) },
	{ "black", NSVG_RGB( 0, 0, 0) },
	{ "grey", NSVG_RGB(128, 128, 128) },
	{ "gray", NSVG_RGB(128, 128, 128) },
	{ "white", NSVG_RGB(255, 255, 255) },

#ifdef NANOSVG_ALL_COLOR_KEYWORDS
	{ "aliceblue", NSVG_RGB(240, 248, 255) },
	{ "antiquewhite", NSVG_RGB(250, 235, 215) },
	{ "aqua", NSVG_RGB( 0, 255, 255) },
	{ "aquamarine", NSVG_RGB(127, 255, 212) },
	{ "azure", NSVG_RGB(240, 255, 255) },
	{ "beige", NSVG_RGB(245, 245, 220) },
	{ "bisque", NSVG_RGB(255, 228, 196) },
	{ "blanchedalmond", NSVG_RGB(255, 235, 205) },
	{ "blueviolet", NSVG_RGB(138, 43, 226) },
	{ "brown", NSVG_RGB(165, 42, 42) },
	{ "burlywood", NSVG_RGB(222, 184, 135) },
	{ "cadetblue", NSVG_RGB( 95, 158, 160) },
	{ "chartreuse", NSVG_RGB(127, 255, 0) },
	{ "chocolate", NSVG_RGB(210, 105, 30) },
	{ "coral", NSVG_RGB(255, 127, 80) },
	{ "cornflowerblue", NSVG_RGB(100, 149, 237) },
	{ "cornsilk", NSVG_RGB(255, 248, 220) },
	{ "crimson", NSVG_RGB(220, 20, 60) },
	{ "darkblue", NSVG_RGB( 0, 0, 139) },
	{ "darkcyan", NSVG_RGB( 0, 139, 139) },
	{ "darkgoldenrod", NSVG_RGB(184, 134, 11) },
	{ "darkgray", NSVG_RGB(169, 169, 169) },
	{ "darkgreen", NSVG_RGB( 0, 100, 0) },
	{ "darkgrey", NSVG_RGB(169, 169, 169) },
	{ "darkkhaki", NSVG_RGB(189, 183, 107) },
	{ "darkmagenta", NSVG_RGB(139, 0, 139) },
	{ "darkolivegreen", NSVG_RGB( 85, 107, 47) },
	{ "darkorange", NSVG_RGB(255, 140, 0) },
	{ "darkorchid", NSVG_RGB(153, 50, 204) },
	{ "darkred", NSVG_RGB(139, 0, 0) },
	{ "darksalmon", NSVG_RGB(233, 150, 122) },
	{ "darkseagreen", NSVG_RGB(143, 188, 143) },
	{ "darkslateblue", NSVG_RGB( 72, 61, 139) },
	{ "darkslategray", NSVG_RGB( 47, 79, 79) },
	{ "darkslategrey", NSVG_RGB( 47, 79, 79) },
	{ "darkturquoise", NSVG_RGB( 0, 206, 209) },
	{ "darkviolet", NSVG_RGB(148, 0, 211) },
	{ "deeppink", NSVG_RGB(255, 20, 147) },
	{ "deepskyblue", NSVG_RGB( 0, 191, 255) },
	{ "dimgray", NSVG_RGB(105, 105, 105) },
	{ "dimgrey", NSVG_RGB(105, 105, 105) },
	{ "dodgerblue", NSVG_RGB( 30, 144, 255) },
	{ "firebrick", NSVG_RGB(178, 34, 34) },
	{ "floralwhite", NSVG_RGB(255, 250, 240) },
	{ "forestgreen", NSVG_RGB( 34, 139, 34) },
	{ "fuchsia", NSVG_RGB(255, 0, 255) },
	{ "gainsboro", NSVG_RGB(220, 220, 220) },
	{ "ghostwhite", NSVG_RGB(248, 248, 255) },
	{ "gold", NSVG_RGB(255, 215, 0) },
	{ "goldenrod", NSVG_RGB(218, 165, 32) },
	{ "greenyellow", NSVG_RGB(173, 255, 47) },
	{ "honeydew", NSVG_RGB(240, 255, 240) },
	{ "hotpink", NSVG_RGB(255, 105, 180) },
	{ "indianred", NSVG_RGB(205, 92, 92) },
	{ "indigo", NSVG_RGB( 75, 0, 130) },
	{ "ivory", NSVG_RGB(255, 255, 240) },
	{ "khaki", NSVG_RGB(240, 230, 140) },
	{ "lavender", NSVG_RGB(230, 230, 250) },
	{ "lavenderblush", NSVG_RGB(255, 240, 245) },
	{ "lawngreen", NSVG_RGB(124, 252, 0) },
	{ "lemonchiffon", NSVG_RGB(255, 250, 205) },
	{ "lightblue", NSVG_RGB(173, 216, 230) },
	{ "lightcoral", NSVG_RGB(240, 128, 128) },
	{ "lightcyan", NSVG_RGB(224, 255, 255) },
	{ "lightgoldenrodyellow", NSVG_RGB(250, 250, 210) },
	{ "lightgray", NSVG_RGB(211, 211, 211) },
	{ "lightgreen", NSVG_RGB(144, 238, 144) },
	{ "lightgrey", NSVG_RGB(211, 211, 211) },
	{ "lightpink", NSVG_RGB(255, 182, 193) },
	{ "lightsalmon", NSVG_RGB(255, 160, 122) },
	{ "lightseagreen", NSVG_RGB( 32, 178, 170) },
	{ "lightskyblue", NSVG_RGB(135, 206, 250) },
	{ "lightslategray", NSVG_RGB(119, 136, 153) },
	{ "lightslategrey", NSVG_RGB(119, 136, 153) },
	{ "lightsteelblue", NSVG_RGB(176, 196, 222) },
	{ "lightyellow", NSVG_RGB(255, 255, 224) },
	{ "lime", NSVG_RGB( 0, 255, 0) },
	{ "limegreen", NSVG_RGB( 50, 205, 50) },
	{ "linen", NSVG_RGB(250, 240, 230) },
	{ "maroon", NSVG_RGB(128, 0, 0) },
	{ "mediumaquamarine", NSVG_RGB(102, 205, 170) },
	{ "mediumblue", NSVG_RGB( 0, 0, 205) },
	{ "mediumorchid", NSVG_RGB(186, 85, 211) },
	{ "mediumpurple", NSVG_RGB(147, 112, 219) },
	{ "mediumseagreen", NSVG_RGB( 60, 179, 113) },
	{ "mediumslateblue", NSVG_RGB(123, 104, 238) },
	{ "mediumspringgreen", NSVG_RGB( 0, 250, 154) },
	{ "mediumturquoise", NSVG_RGB( 72, 209, 204) },
	{ "mediumvioletred", NSVG_RGB(199, 21, 133) },
	{ "midnightblue", NSVG_RGB( 25, 25, 112) },
	{ "mintcream", NSVG_RGB(245, 255, 250) },
	{ "mistyrose", NSVG_RGB(255, 228, 225) },
	{ "moccasin", NSVG_RGB(255, 228, 181) },
	{ "navajowhite", NSVG_RGB(255, 222, 173) },
	{ "navy", NSVG_RGB( 0, 0, 128) },
	{ "oldlace", NSVG_RGB(253, 245, 230) },
	{ "olive", NSVG_RGB(128, 128, 0) },
	{ "olivedrab", NSVG_RGB(107, 142, 35) },
	{ "orange", NSVG_RGB(255, 165, 0) },
	{ "orangered", NSVG_RGB(255, 69, 0) },
	{ "orchid", NSVG_RGB(218, 112, 214) },
	{ "palegoldenrod", NSVG_RGB(238, 232, 170) },
	{ "palegreen", NSVG_RGB(152, 251, 152) },
	{ "paleturquoise", NSVG_RGB(175, 238, 238) },
	{ "palevioletred", NSVG_RGB(219, 112, 147) },
	{ "papayawhip", NSVG_RGB(255, 239, 213) },
	{ "peachpuff", NSVG_RGB(255, 218, 185) },
	{ "peru", NSVG_RGB(205, 133, 63) },
	{ "pink", NSVG_RGB(255, 192, 203) },
	{ "plum", NSVG_RGB(221, 160, 221) },
	{ "powderblue", NSVG_RGB(176, 224, 230) },
	{ "purple", NSVG_RGB(128, 0, 128) },
	{ "rosybrown", NSVG_RGB(188, 143, 143) },
	{ "royalblue", NSVG_RGB( 65, 105, 225) },
	{ "saddlebrown", NSVG_RGB(139, 69, 19) },
	{ "salmon", NSVG_RGB(250, 128, 114) },
	{ "sandybrown", NSVG_RGB(244, 164, 96) },
	{ "seagreen", NSVG_RGB( 46, 139, 87) },
	{ "seashell", NSVG_RGB(255, 245, 238) },
	{ "sienna", NSVG_RGB(160, 82, 45) },
	{ "silver", NSVG_RGB(192, 192, 192) },
	{ "skyblue", NSVG_RGB(135, 206, 235) },
	{ "slateblue", NSVG_RGB(106, 90, 205) },
	{ "slategray", NSVG_RGB(112, 128, 144) },
	{ "slategrey", NSVG_RGB(112, 128, 144) },
	{ "snow", NSVG_RGB(255, 250, 250) },
	{ "springgreen", NSVG_RGB( 0, 255, 127) },
	{ "steelblue", NSVG_RGB( 70, 130, 180) },
	{ "tan", NSVG_RGB(210, 180, 140) },
	{ "teal", NSVG_RGB( 0, 128, 128) },
	{ "thistle", NSVG_RGB(216, 191, 216) },
	{ "tomato", NSVG_RGB(255, 99, 71) },
	{ "turquoise", NSVG_RGB( 64, 224, 208) },
	{ "violet", NSVG_RGB(238, 130, 238) },
	{ "wheat", NSVG_RGB(245, 222, 179) },
	{ "whitesmoke", NSVG_RGB(245, 245, 245) },
	{ "yellowgreen", NSVG_RGB(154, 205, 50) },
#endif
};

static unsigned int nsvg__parseColorName(const char* str)
{
	int i, ncolors = sizeof(nsvg__colors) / sizeof(NSVGNamedColor);

	for (i = 0; i < ncolors; i++) {
		if (strcmp(nsvg__colors[i].name, str) == 0) {
			return nsvg__colors[i].color;
		}
	}

	return NSVG_RGB(128, 128, 128);
}

static unsigned int nsvg__parseColor(const char* str)
{
	size_t len = 0;
	while(*str == ' ') ++str;
	len = strlen(str);
	if (len >= 1 && *str == '#')
		return nsvg__parseColorHex(str);
	else if (len >= 4 && str[0] == 'r' && str[1] == 'g' && str[2] == 'b' && str[3] == '(')
		return nsvg__parseColorRGB(str);
	return nsvg__parseColorName(str);
}

static float nsvg__parseOpacity(const char* str)
{
	float val = nsvg__atof(str);
	if (val < 0.0f) val = 0.0f;
	if (val > 1.0f) val = 1.0f;
	return val;
}

static float nsvg__parseMiterLimit(const char* str)
{
	float val = nsvg__atof(str);
	if (val < 0.0f) val = 0.0f;
	return val;
}

static int nsvg__parseUnits(const char* units)
{
	if (units[0] == 'p' && units[1] == 'x')
		return NSVG_UNITS_PX;
	else if (units[0] == 'p' && units[1] == 't')
		return NSVG_UNITS_PT;
	else if (units[0] == 'p' && units[1] == 'c')
		return NSVG_UNITS_PC;
	else if (units[0] == 'm' && units[1] == 'm')
		return NSVG_UNITS_MM;
	else if (units[0] == 'c' && units[1] == 'm')
		return NSVG_UNITS_CM;
	else if (units[0] == 'i' && units[1] == 'n')
		return NSVG_UNITS_IN;
	else if (units[0] == '%')
		return NSVG_UNITS_PERCENT;
	else if (units[0] == 'e' && units[1] == 'm')
		return NSVG_UNITS_EM;
	else if (units[0] == 'e' && units[1] == 'x')
		return NSVG_UNITS_EX;
	return NSVG_UNITS_USER;
}

static NSVGcoordinate nsvg__parseCoordinateRaw(const char* str)
{
	NSVGcoordinate coord = {0, NSVG_UNITS_USER};
	char buf[64];
	coord.units = nsvg__parseUnits(nsvg__parseNumber(str, buf, 64));
	coord.value = nsvg__atof(buf);
	return coord;
}

static NSVGcoordinate nsvg__coord(float v, int units)
{
	NSVGcoordinate coord = {v, units};
	return coord;
}

static float nsvg__parseCoordinate(NSVGparser* p, const char* str, float orig, float length)
{
	NSVGcoordinate coord = nsvg__parseCoordinateRaw(str);
	return nsvg__convertToPixels(p, coord, orig, length);
}

static int nsvg__parseTransformArgs(const char* str, float* args, int maxNa, int* na)
{
	const char* end;
	const char* ptr;
	char it[64];

	*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 (*ptr == '-' || *ptr == '+' || *ptr == '.' || nsvg__isdigit(*ptr)) {
			if (*na >= maxNa) return 0;
			ptr = nsvg__parseNumber(ptr, it, 64);
			args[(*na)++] = (float)nsvg__atof(it);
		} else {
			++ptr;
		}
	}
	return (int)(end - str);
}


static int nsvg__parseMatrix(float* xform, const char* str)
{
	float t[6];
	int na = 0;
	int len = nsvg__parseTransformArgs(str, t, 6, &na);
	if (na != 6) return len;
	memcpy(xform, t, sizeof(float)*6);
	return len;
}

static int nsvg__parseTranslate(float* xform, const char* str)
{
	float args[2];
	float t[6];
	int na = 0;
	int len = nsvg__parseTransformArgs(str, args, 2, &na);
	if (na == 1) args[1] = 0.0;

	nsvg__xformSetTranslation(t, args[0], args[1]);
	memcpy(xform, t, sizeof(float)*6);
	return len;
}

static int nsvg__parseScale(float* xform, const char* str)
{
	float args[2];
	int na = 0;
	float t[6];
	int len = nsvg__parseTransformArgs(str, args, 2, &na);
	if (na == 1) args[1] = args[0];
	nsvg__xformSetScale(t, args[0], args[1]);
	memcpy(xform, t, sizeof(float)*6);
	return len;
}

static int nsvg__parseSkewX(float* xform, const char* str)
{
	float args[1];
	int na = 0;
	float t[6];
	int len = nsvg__parseTransformArgs(str, args, 1, &na);
	nsvg__xformSetSkewX(t, args[0]/180.0f*NSVG_PI);
	memcpy(xform, t, sizeof(float)*6);
	return len;
}

static int nsvg__parseSkewY(float* xform, const char* str)
{
	float args[1];
	int na = 0;
	float t[6];
	int len = nsvg__parseTransformArgs(str, args, 1, &na);
	nsvg__xformSetSkewY(t, args[0]/180.0f*NSVG_PI);
	memcpy(xform, t, sizeof(float)*6);
	return len;
}

static int nsvg__parseRotate(float* xform, const char* str)
{
	float args[3];
	int na = 0;
	float m[6];
	float t[6];
	int len = nsvg__parseTransformArgs(str, args, 3, &na);
	if (na == 1)
		args[1] = args[2] = 0.0f;
	nsvg__xformIdentity(m);

	if (na > 1) {
		nsvg__xformSetTranslation(t, -args[1], -args[2]);
		nsvg__xformMultiply(m, t);
	}

	nsvg__xformSetRotation(t, args[0]/180.0f*NSVG_PI);
	nsvg__xformMultiply(m, t);

	if (na > 1) {
		nsvg__xformSetTranslation(t, args[1], args[2]);
		nsvg__xformMultiply(m, t);
	}

	memcpy(xform, m, sizeof(float)*6);

	return len;
}

static void nsvg__parseTransform(float* xform, const char* str)
{
	float t[6];
	nsvg__xformIdentity(xform);
	while (*str)
	{
		if (strncmp(str, "matrix", 6) == 0)
			str += nsvg__parseMatrix(t, str);
		else if (strncmp(str, "translate", 9) == 0)
			str += nsvg__parseTranslate(t, str);
		else if (strncmp(str, "scale", 5) == 0)
			str += nsvg__parseScale(t, str);
		else if (strncmp(str, "rotate", 6) == 0)
			str += nsvg__parseRotate(t, str);
		else if (strncmp(str, "skewX", 5) == 0)
			str += nsvg__parseSkewX(t, str);
		else if (strncmp(str, "skewY", 5) == 0)
			str += nsvg__parseSkewY(t, str);
		else{
			++str;
			continue;
		}

		nsvg__xformPremultiply(xform, t);
	}
}

static void nsvg__parseUrl(char* id, const char* str)
{
	int i = 0;
	str += 4; // "url(";
	if (*str == '#')
		str++;
	while (i < 63 && *str != ')') {
		id[i] = *str++;
		i++;
	}
	id[i] = '\0';
}

static char nsvg__parseLineCap(const char* str)
{
	if (strcmp(str, "butt") == 0)
		return NSVG_CAP_BUTT;
	else if (strcmp(str, "round") == 0)
		return NSVG_CAP_ROUND;
	else if (strcmp(str, "square") == 0)
		return NSVG_CAP_SQUARE;
	// TODO: handle inherit.
	return NSVG_CAP_BUTT;
}

static char nsvg__parseLineJoin(const char* str)
{
	if (strcmp(str, "miter") == 0)
		return NSVG_JOIN_MITER;
	else if (strcmp(str, "round") == 0)
		return NSVG_JOIN_ROUND;
	else if (strcmp(str, "bevel") == 0)
		return NSVG_JOIN_BEVEL;
	// TODO: handle inherit.
	return NSVG_JOIN_MITER;
}

static char nsvg__parseFillRule(const char* str)
{
	if (strcmp(str, "nonzero") == 0)
		return NSVG_FILLRULE_NONZERO;
	else if (strcmp(str, "evenodd") == 0)
		return NSVG_FILLRULE_EVENODD;
	// TODO: handle inherit.
	return NSVG_FILLRULE_NONZERO;
}

static const char* nsvg__getNextDashItem(const char* s, char* it)
{
	int n = 0;
	it[0] = '\0';
	// Skip white spaces and commas
	while (*s && (nsvg__isspace(*s) || *s == ',')) s++;
	// Advance until whitespace, comma or end.
	while (*s && (!nsvg__isspace(*s) && *s != ',')) {
		if (n < 63)
			it[n++] = *s;
		s++;
	}
	it[n++] = '\0';
	return s;
}

static int nsvg__parseStrokeDashArray(NSVGparser* p, const char* str, float* strokeDashArray)
{
	char item[64];
	int count = 0, i;
	float sum = 0.0f;

	// Handle "none"
	if (str[0] == 'n')
		return 0;

	// Parse dashes
	while (*str) {
		str = nsvg__getNextDashItem(str, item);
		if (!*item) break;
		if (count < NSVG_MAX_DASHES)
			strokeDashArray[count++] = fabsf(nsvg__parseCoordinate(p, item, 0.0f, nsvg__actualLength(p)));
	}

	for (i = 0; i < count; i++)
		sum += strokeDashArray[i];
	if (sum <= 1e-6f)
		count = 0;

	return count;
}

static void nsvg__parseStyle(NSVGparser* p, const char* str);

static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value)
{
	float xform[6];
	NSVGattrib* attr = nsvg__getAttr(p);
	if (!attr) return 0;

	if (strcmp(name, "style") == 0) {
		nsvg__parseStyle(p, value);
	} else if (strcmp(name, "display") == 0) {
		if (strcmp(value, "none") == 0)
			attr->visible = 0;
		// Don't reset ->visible on display:inline, one display:none hides the whole subtree

	} else if (strcmp(name, "fill") == 0) {
		if (strcmp(value, "none") == 0) {
			attr->hasFill = 0;
		} else if (strncmp(value, "url(", 4) == 0) {
			attr->hasFill = 2;
			nsvg__parseUrl(attr->fillGradient, value);
		} else {
			attr->hasFill = 1;
			attr->fillColor = nsvg__parseColor(value);
		}
	} else if (strcmp(name, "opacity") == 0) {
		attr->opacity = nsvg__parseOpacity(value);
	} else if (strcmp(name, "fill-opacity") == 0) {
		attr->fillOpacity = nsvg__parseOpacity(value);
	} else if (strcmp(name, "stroke") == 0) {
		if (strcmp(value, "none") == 0) {
			attr->hasStroke = 0;
		} else if (strncmp(value, "url(", 4) == 0) {
			attr->hasStroke = 2;
			nsvg__parseUrl(attr->strokeGradient, value);
		} else {
			attr->hasStroke = 1;
			attr->strokeColor = nsvg__parseColor(value);
		}
	} else if (strcmp(name, "stroke-width") == 0) {
		attr->strokeWidth = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p));
	} else if (strcmp(name, "stroke-dasharray") == 0) {
		attr->strokeDashCount = nsvg__parseStrokeDashArray(p, value, attr->strokeDashArray);
	} else if (strcmp(name, "stroke-dashoffset") == 0) {
		attr->strokeDashOffset = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p));
	} else if (strcmp(name, "stroke-opacity") == 0) {
		attr->strokeOpacity = nsvg__parseOpacity(value);
	} else if (strcmp(name, "stroke-linecap") == 0) {
		attr->strokeLineCap = nsvg__parseLineCap(value);
	} else if (strcmp(name, "stroke-linejoin") == 0) {
		attr->strokeLineJoin = nsvg__parseLineJoin(value);
	} else if (strcmp(name, "stroke-miterlimit") == 0) {
		attr->miterLimit = nsvg__parseMiterLimit(value);
	} else if (strcmp(name, "fill-rule") == 0) {
		attr->fillRule = nsvg__parseFillRule(value);
	} else if (strcmp(name, "font-size") == 0) {
		attr->fontSize = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p));
	} else if (strcmp(name, "transform") == 0) {
		nsvg__parseTransform(xform, value);
		nsvg__xformPremultiply(attr->xform, xform);
	} else if (strcmp(name, "stop-color") == 0) {
		attr->stopColor = nsvg__parseColor(value);
	} else if (strcmp(name, "stop-opacity") == 0) {
		attr->stopOpacity = nsvg__parseOpacity(value);
	} else if (strcmp(name, "offset") == 0) {
		attr->stopOffset = nsvg__parseCoordinate(p, value, 0.0f, 1.0f);
	} else if (strcmp(name, "id") == 0) {
		strncpy(attr->id, value, 63);
		attr->id[63] = '\0';
	} else {
		return 0;
	}
	return 1;
}

static int nsvg__parseNameValue(NSVGparser* 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 == ':' || nsvg__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 == ':' || nsvg__isspace(*val))) ++val;

	n = (int)(end - val);
	if (n > 511) n = 511;
	if (n) memcpy(value, val, n);
	value[n] = 0;

	return nsvg__parseAttr(p, name, value);
}

static void nsvg__parseStyle(NSVGparser* p, const char* str)
{
	const char* start;
	const char* end;

	while (*str) {
		// Left Trim
		while(*str && nsvg__isspace(*str)) ++str;
		start = str;
		while(*str && *str != ';') ++str;
		end = str;

		// Right Trim
		while (end > start &&  (*end == ';' || nsvg__isspace(*end))) --end;
		++end;

		nsvg__parseNameValue(p, start, end);
		if (*str) ++str;
	}
}

static void nsvg__parseAttribs(NSVGparser* p, const char** attr)
{
	int i;
	for (i = 0; attr[i]; i += 2)
	{
		if (strcmp(attr[i], "style") == 0)
			nsvg__parseStyle(p, attr[i + 1]);
		else
			nsvg__parseAttr(p, attr[i], attr[i + 1]);
	}
}

static int nsvg__getArgsPerElement(char cmd)
{
	switch (cmd) {
		case 'v':
		case 'V':
		case 'h':
		case 'H':
			return 1;
		case 'm':
		case 'M':
		case 'l':
		case 'L':
		case 't':
		case 'T':
			return 2;
		case 'q':
		case 'Q':
		case 's':
		case 'S':
			return 4;
		case 'c':
		case 'C':
			return 6;
		case 'a':
		case 'A':
			return 7;
	}
	return 0;
}

static void nsvg__pathMoveTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
{
	if (rel) {
		*cpx += args[0];
		*cpy += args[1];
	} else {
		*cpx = args[0];
		*cpy = args[1];
	}
	nsvg__moveTo(p, *cpx, *cpy);
}

static void nsvg__pathLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
{
	if (rel) {
		*cpx += args[0];
		*cpy += args[1];
	} else {
		*cpx = args[0];
		*cpy = args[1];
	}
	nsvg__lineTo(p, *cpx, *cpy);
}

static void nsvg__pathHLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
{
	if (rel)
		*cpx += args[0];
	else
		*cpx = args[0];
	nsvg__lineTo(p, *cpx, *cpy);
}

static void nsvg__pathVLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
{
	if (rel)
		*cpy += args[0];
	else
		*cpy = args[0];
	nsvg__lineTo(p, *cpx, *cpy);
}

static void nsvg__pathCubicBezTo(NSVGparser* p, float* cpx, float* cpy,
								 float* cpx2, float* cpy2, float* args, int rel)
{
	float x2, y2, cx1, cy1, cx2, cy2;

	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];
	}

	nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);

	*cpx2 = cx2;
	*cpy2 = cy2;
	*cpx = x2;
	*cpy = y2;
}

static void nsvg__pathCubicBezShortTo(NSVGparser* 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;

	nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);

	*cpx2 = cx2;
	*cpy2 = cy2;
	*cpx = x2;
	*cpy = y2;
}

static void nsvg__pathQuadBezTo(NSVGparser* p, float* cpx, float* cpy,
								float* cpx2, float* cpy2, float* args, int rel)
{
	float x1, y1, x2, y2, cx, cy;
	float cx1, cy1, cx2, cy2;

	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];
	}

	// Convert to cubic bezier
	cx1 = x1 + 2.0f/3.0f*(cx - x1);
	cy1 = y1 + 2.0f/3.0f*(cy - y1);
	cx2 = x2 + 2.0f/3.0f*(cx - x2);
	cy2 = y2 + 2.0f/3.0f*(cy - y2);

	nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);

	*cpx2 = cx;
	*cpy2 = cy;
	*cpx = x2;
	*cpy = y2;
}

static void nsvg__pathQuadBezShortTo(NSVGparser* p, float* cpx, float* cpy,
									 float* cpx2, float* cpy2, float* args, int rel)
{
	float x1, y1, x2, y2, cx, cy;
	float cx1, cy1, cx2, cy2;

	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;

	// Convert to cubix bezier
	cx1 = x1 + 2.0f/3.0f*(cx - x1);
	cy1 = y1 + 2.0f/3.0f*(cy - y1);
	cx2 = x2 + 2.0f/3.0f*(cx - x2);
	cy2 = y2 + 2.0f/3.0f*(cy - y2);

	nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);

	*cpx2 = cx;
	*cpy2 = cy;
	*cpx = x2;
	*cpy = y2;
}

static float nsvg__sqr(float x) { return x*x; }
static float nsvg__vmag(float x, float y) { return sqrtf(x*x + y*y); }

static float nsvg__vecrat(float ux, float uy, float vx, float vy)
{
	return (ux*vx + uy*vy) / (nsvg__vmag(ux,uy) * nsvg__vmag(vx,vy));
}

static float nsvg__vecang(float ux, float uy, float vx, float vy)
{
	float r = nsvg__vecrat(ux,uy, vx,vy);
	if (r < -1.0f) r = -1.0f;
	if (r > 1.0f) r = 1.0f;
	return ((ux*vy < uy*vx) ? -1.0f : 1.0f) * acosf(r);
}

static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
{
	// Ported from canvg (https://code.google.com/p/canvg/)
	float rx, ry, rotx;
	float x1, y1, x2, y2, cx, cy, dx, dy, d;
	float x1p, y1p, cxp, cyp, s, sa, sb;
	float ux, uy, vx, vy, a1, da;
	float x, y, tanx, tany, a, px = 0, py = 0, ptanx = 0, ptany = 0, t[6];
	float sinrx, cosrx;
	int fa, fs;
	int i, ndivs;
	float hda, kappa;

	rx = fabsf(args[0]);				// y radius
	ry = fabsf(args[1]);				// x radius
	rotx = args[2] / 180.0f * NSVG_PI;		// x rotation angle
	fa = fabsf(args[3]) > 1e-6 ? 1 : 0;	// Large arc
	fs = fabsf(args[4]) > 1e-6 ? 1 : 0;	// Sweep direction
	x1 = *cpx;							// start point
	y1 = *cpy;
	if (rel) {							// end point
		x2 = *cpx + args[5];
		y2 = *cpy + args[6];
	} else {
		x2 = args[5];
		y2 = args[6];
	}

	dx = x1 - x2;
	dy = y1 - y2;
	d = sqrtf(dx*dx + dy*dy);
	if (d < 1e-6f || rx < 1e-6f || ry < 1e-6f) {
		// The arc degenerates to a line
		nsvg__lineTo(p, x2, y2);
		*cpx = x2;
		*cpy = y2;
		return;
	}

	sinrx = sinf(rotx);
	cosrx = cosf(rotx);

	// Convert to center point parameterization.
	// http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
	// 1) Compute x1', y1'
	x1p = cosrx * dx / 2.0f + sinrx * dy / 2.0f;
	y1p = -sinrx * dx / 2.0f + cosrx * dy / 2.0f;
	d = nsvg__sqr(x1p)/nsvg__sqr(rx) + nsvg__sqr(y1p)/nsvg__sqr(ry);
	if (d > 1) {
		d = sqrtf(d);
		rx *= d;
		ry *= d;
	}
	// 2) Compute cx', cy'
	s = 0.0f;
	sa = nsvg__sqr(rx)*nsvg__sqr(ry) - nsvg__sqr(rx)*nsvg__sqr(y1p) - nsvg__sqr(ry)*nsvg__sqr(x1p);
	sb = nsvg__sqr(rx)*nsvg__sqr(y1p) + nsvg__sqr(ry)*nsvg__sqr(x1p);
	if (sa < 0.0f) sa = 0.0f;
	if (sb > 0.0f)
		s = sqrtf(sa / sb);
	if (fa == fs)
		s = -s;
	cxp = s * rx * y1p / ry;
	cyp = s * -ry * x1p / rx;

	// 3) Compute cx,cy from cx',cy'
	cx = (x1 + x2)/2.0f + cosrx*cxp - sinrx*cyp;
	cy = (y1 + y2)/2.0f + sinrx*cxp + cosrx*cyp;

	// 4) Calculate theta1, and delta theta.
	ux = (x1p - cxp) / rx;
	uy = (y1p - cyp) / ry;
	vx = (-x1p - cxp) / rx;
	vy = (-y1p - cyp) / ry;
	a1 = nsvg__vecang(1.0f,0.0f, ux,uy);	// Initial angle
	da = nsvg__vecang(ux,uy, vx,vy);		// Delta angle

//	if (vecrat(ux,uy,vx,vy) <= -1.0f) da = NSVG_PI;
//	if (vecrat(ux,uy,vx,vy) >= 1.0f) da = 0;

	if (fs == 0 && da > 0)
		da -= 2 * NSVG_PI;
	else if (fs == 1 && da < 0)
		da += 2 * NSVG_PI;

	// Approximate the arc using cubic spline segments.
	t[0] = cosrx; t[1] = sinrx;
	t[2] = -sinrx; t[3] = cosrx;
	t[4] = cx; t[5] = cy;

	// Split arc into max 90 degree segments.
	// The loop assumes an iteration per end point (including start and end), this +1.
	ndivs = (int)(fabsf(da) / (NSVG_PI*0.5f) + 1.0f);
	hda = (da / (float)ndivs) / 2.0f;
	kappa = fabsf(4.0f / 3.0f * (1.0f - cosf(hda)) / sinf(hda));
	if (da < 0.0f)
		kappa = -kappa;

	for (i = 0; i <= ndivs; i++) {
		a = a1 + da * ((float)i/(float)ndivs);
		dx = cosf(a);
		dy = sinf(a);
		nsvg__xformPoint(&x, &y, dx*rx, dy*ry, t); // position
		nsvg__xformVec(&tanx, &tany, -dy*rx * kappa, dx*ry * kappa, t); // tangent
		if (i > 0)
			nsvg__cubicBezTo(p, px+ptanx,py+ptany, x-tanx, y-tany, x, y);
		px = x;
		py = y;
		ptanx = tanx;
		ptany = tany;
	}

	*cpx = x2;
	*cpy = y2;
}

static void nsvg__parsePath(NSVGparser* p, const char** attr)
{
	const char* s = NULL;
	char cmd = '\0';
	float args[10];
	int nargs;
	int rargs = 0;
	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];
		} else {
			tmp[0] = attr[i];
			tmp[1] = attr[i + 1];
			tmp[2] = 0;
			tmp[3] = 0;
			nsvg__parseAttribs(p, tmp);
		}
	}

	if (s) {
		nsvg__resetPath(p);
		cpx = 0; cpy = 0;
		cpx2 = 0; cpy2 = 0;
		closedFlag = 0;
		nargs = 0;

		while (*s) {
			s = nsvg__getNextPathItem(s, item);
			if (!*item) break;
			if (nsvg__isnum(item[0])) {
				if (nargs < 10)
					args[nargs++] = (float)nsvg__atof(item);
				if (nargs >= rargs) {
					switch (cmd) {
						case 'm':
						case 'M':
							nsvg__pathMoveTo(p, &cpx, &cpy, args, cmd == 'm' ? 1 : 0);
							// Moveto can be followed by multiple coordinate pairs,
							// which should be treated as linetos.
							cmd = (cmd == 'm') ? 'l' : 'L';
							rargs = nsvg__getArgsPerElement(cmd);
							cpx2 = cpx; cpy2 = cpy;
							break;
						case 'l':
						case 'L':
							nsvg__pathLineTo(p, &cpx, &cpy, args, cmd == 'l' ? 1 : 0);
							cpx2 = cpx; cpy2 = cpy;
							break;
						case 'H':
						case 'h':
							nsvg__pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0);
							cpx2 = cpx; cpy2 = cpy;
							break;
						case 'V':
						case 'v':
							nsvg__pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0);
							cpx2 = cpx; cpy2 = cpy;
							break;
						case 'C':
						case 'c':
							nsvg__pathCubicBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'c' ? 1 : 0);
							break;
						case 'S':
						case 's':
							nsvg__pathCubicBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0);
							break;
						case 'Q':
						case 'q':
							nsvg__pathQuadBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'q' ? 1 : 0);
							break;
						case 'T':
						case 't':
							nsvg__pathQuadBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 't' ? 1 : 0);
							break;
						case 'A':
						case 'a':
							nsvg__pathArcTo(p, &cpx, &cpy, args, cmd == 'a' ? 1 : 0);
							cpx2 = cpx; cpy2 = cpy;
							break;
						default:
							if (nargs >= 2) {
								cpx = args[nargs-2];
								cpy = args[nargs-1];
								cpx2 = cpx; cpy2 = cpy;
							}
							break;
					}
					nargs = 0;
				}
			} else {
				cmd = item[0];
				rargs = nsvg__getArgsPerElement(cmd);
				if (cmd == 'M' || cmd == 'm') {
					// Commit path.
					if (p->npts > 0)
						nsvg__addPath(p, closedFlag);
					// Start new subpath.
					nsvg__resetPath(p);
					closedFlag = 0;
					nargs = 0;
				} else if (cmd == 'Z' || cmd == 'z') {
					closedFlag = 1;
					// Commit path.
					if (p->npts > 0) {
						// Move current point to first point
						cpx = p->pts[0];
						cpy = p->pts[1];
						cpx2 = cpx; cpy2 = cpy;
						nsvg__addPath(p, closedFlag);
					}
					// Start new subpath.
					nsvg__resetPath(p);
					nsvg__moveTo(p, cpx, cpy);
					closedFlag = 0;
					nargs = 0;
				}
			}
		}
		// Commit path.
		if (p->npts)
			nsvg__addPath(p, closedFlag);
	}

	nsvg__addShape(p);
}

static void nsvg__parseRect(NSVGparser* p, const char** attr)
{
	float x = 0.0f;
	float y = 0.0f;
	float w = 0.0f;
	float h = 0.0f;
	float rx = -1.0f; // marks not set
	float ry = -1.0f;
	int i;

	for (i = 0; attr[i]; i += 2) {
		if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
			if (strcmp(attr[i], "x") == 0) x = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
			if (strcmp(attr[i], "y") == 0) y = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
			if (strcmp(attr[i], "width") == 0) w = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p));
			if (strcmp(attr[i], "height") == 0) h = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p));
			if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p)));
			if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p)));
		}
	}

	if (rx < 0.0f && ry > 0.0f) rx = ry;
	if (ry < 0.0f && rx > 0.0f) ry = rx;
	if (rx < 0.0f) rx = 0.0f;
	if (ry < 0.0f) ry = 0.0f;
	if (rx > w/2.0f) rx = w/2.0f;
	if (ry > h/2.0f) ry = h/2.0f;

	if (w != 0.0f && h != 0.0f) {
		nsvg__resetPath(p);

		if (rx < 0.00001f || ry < 0.0001f) {
			nsvg__moveTo(p, x, y);
			nsvg__lineTo(p, x+w, y);
			nsvg__lineTo(p, x+w, y+h);
			nsvg__lineTo(p, x, y+h);
		} else {
			// Rounded rectangle
			nsvg__moveTo(p, x+rx, y);
			nsvg__lineTo(p, x+w-rx, y);
			nsvg__cubicBezTo(p, x+w-rx*(1-NSVG_KAPPA90), y, x+w, y+ry*(1-NSVG_KAPPA90), x+w, y+ry);
			nsvg__lineTo(p, x+w, y+h-ry);
			nsvg__cubicBezTo(p, x+w, y+h-ry*(1-NSVG_KAPPA90), x+w-rx*(1-NSVG_KAPPA90), y+h, x+w-rx, y+h);
			nsvg__lineTo(p, x+rx, y+h);
			nsvg__cubicBezTo(p, x+rx*(1-NSVG_KAPPA90), y+h, x, y+h-ry*(1-NSVG_KAPPA90), x, y+h-ry);
			nsvg__lineTo(p, x, y+ry);
			nsvg__cubicBezTo(p, x, y+ry*(1-NSVG_KAPPA90), x+rx*(1-NSVG_KAPPA90), y, x+rx, y);
		}

		nsvg__addPath(p, 1);

		nsvg__addShape(p);
	}
}

static void nsvg__parseCircle(NSVGparser* p, const char** attr)
{
	float cx = 0.0f;
	float cy = 0.0f;
	float r = 0.0f;
	int i;

	for (i = 0; attr[i]; i += 2) {
		if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
			if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
			if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
			if (strcmp(attr[i], "r") == 0) r = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualLength(p)));
		}
	}

	if (r > 0.0f) {
		nsvg__resetPath(p);

		nsvg__moveTo(p, cx+r, cy);
		nsvg__cubicBezTo(p, cx+r, cy+r*NSVG_KAPPA90, cx+r*NSVG_KAPPA90, cy+r, cx, cy+r);
		nsvg__cubicBezTo(p, cx-r*NSVG_KAPPA90, cy+r, cx-r, cy+r*NSVG_KAPPA90, cx-r, cy);
		nsvg__cubicBezTo(p, cx-r, cy-r*NSVG_KAPPA90, cx-r*NSVG_KAPPA90, cy-r, cx, cy-r);
		nsvg__cubicBezTo(p, cx+r*NSVG_KAPPA90, cy-r, cx+r, cy-r*NSVG_KAPPA90, cx+r, cy);

		nsvg__addPath(p, 1);

		nsvg__addShape(p);
	}
}

static void nsvg__parseEllipse(NSVGparser* p, const char** attr)
{
	float cx = 0.0f;
	float cy = 0.0f;
	float rx = 0.0f;
	float ry = 0.0f;
	int i;

	for (i = 0; attr[i]; i += 2) {
		if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
			if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
			if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
			if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p)));
			if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p)));
		}
	}

	if (rx > 0.0f && ry > 0.0f) {

		nsvg__resetPath(p);

		nsvg__moveTo(p, cx+rx, cy);
		nsvg__cubicBezTo(p, cx+rx, cy+ry*NSVG_KAPPA90, cx+rx*NSVG_KAPPA90, cy+ry, cx, cy+ry);
		nsvg__cubicBezTo(p, cx-rx*NSVG_KAPPA90, cy+ry, cx-rx, cy+ry*NSVG_KAPPA90, cx-rx, cy);
		nsvg__cubicBezTo(p, cx-rx, cy-ry*NSVG_KAPPA90, cx-rx*NSVG_KAPPA90, cy-ry, cx, cy-ry);
		nsvg__cubicBezTo(p, cx+rx*NSVG_KAPPA90, cy-ry, cx+rx, cy-ry*NSVG_KAPPA90, cx+rx, cy);

		nsvg__addPath(p, 1);

		nsvg__addShape(p);
	}
}

static void nsvg__parseLine(NSVGparser* 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 (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
			if (strcmp(attr[i], "x1") == 0) x1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
			if (strcmp(attr[i], "y1") == 0) y1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
			if (strcmp(attr[i], "x2") == 0) x2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
			if (strcmp(attr[i], "y2") == 0) y2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
		}
	}

	nsvg__resetPath(p);

	nsvg__moveTo(p, x1, y1);
	nsvg__lineTo(p, x2, y2);

	nsvg__addPath(p, 0);

	nsvg__addShape(p);
}

static void nsvg__parsePoly(NSVGparser* p, const char** attr, int closeFlag)
{
	int i;
	const char* s;
	float args[2];
	int nargs, npts = 0;
	char item[64];

	nsvg__resetPath(p);

	for (i = 0; attr[i]; i += 2) {
		if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
			if (strcmp(attr[i], "points") == 0) {
				s = attr[i + 1];
				nargs = 0;
				while (*s) {
					s = nsvg__getNextPathItem(s, item);
					args[nargs++] = (float)nsvg__atof(item);
					if (nargs >= 2) {
						if (npts == 0)
							nsvg__moveTo(p, args[0], args[1]);
						else
							nsvg__lineTo(p, args[0], args[1]);
						nargs = 0;
						npts++;
					}
				}
			}
		}
	}

	nsvg__addPath(p, (char)closeFlag);

	nsvg__addShape(p);
}

static void nsvg__parseSVG(NSVGparser* p, const char** attr)
{
	int i;
	for (i = 0; attr[i]; i += 2) {
		if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
			if (strcmp(attr[i], "width") == 0) {
				p->image->width = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f);
			} else if (strcmp(attr[i], "height") == 0) {
				p->image->height = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f);
			} else if (strcmp(attr[i], "viewBox") == 0) {
				const char *s = attr[i + 1];
				char buf[64];
				s = nsvg__parseNumber(s, buf, 64);
				p->viewMinx = nsvg__atof(buf);
				while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++;
				if (!*s) return;
				s = nsvg__parseNumber(s, buf, 64);
				p->viewMiny = nsvg__atof(buf);
				while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++;
				if (!*s) return;
				s = nsvg__parseNumber(s, buf, 64);
				p->viewWidth = nsvg__atof(buf);
				while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++;
				if (!*s) return;
				s = nsvg__parseNumber(s, buf, 64);
				p->viewHeight = nsvg__atof(buf);
			} else if (strcmp(attr[i], "preserveAspectRatio") == 0) {
				if (strstr(attr[i + 1], "none") != 0) {
					// No uniform scaling
					p->alignType = NSVG_ALIGN_NONE;
				} else {
					// Parse X align
					if (strstr(attr[i + 1], "xMin") != 0)
						p->alignX = NSVG_ALIGN_MIN;
					else if (strstr(attr[i + 1], "xMid") != 0)
						p->alignX = NSVG_ALIGN_MID;
					else if (strstr(attr[i + 1], "xMax") != 0)
						p->alignX = NSVG_ALIGN_MAX;
					// Parse X align
					if (strstr(attr[i + 1], "yMin") != 0)
						p->alignY = NSVG_ALIGN_MIN;
					else if (strstr(attr[i + 1], "yMid") != 0)
						p->alignY = NSVG_ALIGN_MID;
					else if (strstr(attr[i + 1], "yMax") != 0)
						p->alignY = NSVG_ALIGN_MAX;
					// Parse meet/slice
					p->alignType = NSVG_ALIGN_MEET;
					if (strstr(attr[i + 1], "slice") != 0)
						p->alignType = NSVG_ALIGN_SLICE;
				}
			}
		}
	}
}

static void nsvg__parseGradient(NSVGparser* p, const char** attr, char type)
{
	int i;
	NSVGgradientData* grad = (NSVGgradientData*)malloc(sizeof(NSVGgradientData));
	if (grad == NULL) return;
	memset(grad, 0, sizeof(NSVGgradientData));
	grad->units = NSVG_OBJECT_SPACE;
	grad->type = type;
	if (grad->type == NSVG_PAINT_LINEAR_GRADIENT) {
		grad->linear.x1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT);
		grad->linear.y1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT);
		grad->linear.x2 = nsvg__coord(100.0f, NSVG_UNITS_PERCENT);
		grad->linear.y2 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT);
	} else if (grad->type == NSVG_PAINT_RADIAL_GRADIENT) {
		grad->radial.cx = nsvg__coord(50.0f, NSVG_UNITS_PERCENT);
		grad->radial.cy = nsvg__coord(50.0f, NSVG_UNITS_PERCENT);
		grad->radial.r = nsvg__coord(50.0f, NSVG_UNITS_PERCENT);
	}

	nsvg__xformIdentity(grad->xform);

	for (i = 0; attr[i]; i += 2) {
		if (strcmp(attr[i], "id") == 0) {
			strncpy(grad->id, attr[i+1], 63);
			grad->id[63] = '\0';
		} else if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
			if (strcmp(attr[i], "gradientUnits") == 0) {
				if (strcmp(attr[i+1], "objectBoundingBox") == 0)
					grad->units = NSVG_OBJECT_SPACE;
				else
					grad->units = NSVG_USER_SPACE;
			} else if (strcmp(attr[i], "gradientTransform") == 0) {
				nsvg__parseTransform(grad->xform, attr[i + 1]);
			} else if (strcmp(attr[i], "cx") == 0) {
				grad->radial.cx = nsvg__parseCoordinateRaw(attr[i + 1]);
			} else if (strcmp(attr[i], "cy") == 0) {
				grad->radial.cy = nsvg__parseCoordinateRaw(attr[i + 1]);
			} else if (strcmp(attr[i], "r") == 0) {
				grad->radial.r = nsvg__parseCoordinateRaw(attr[i + 1]);
			} else if (strcmp(attr[i], "fx") == 0) {
				grad->radial.fx = nsvg__parseCoordinateRaw(attr[i + 1]);
			} else if (strcmp(attr[i], "fy") == 0) {
				grad->radial.fy = nsvg__parseCoordinateRaw(attr[i + 1]);
			} else if (strcmp(attr[i], "x1") == 0) {
				grad->linear.x1 = nsvg__parseCoordinateRaw(attr[i + 1]);
			} else if (strcmp(attr[i], "y1") == 0) {
				grad->linear.y1 = nsvg__parseCoordinateRaw(attr[i + 1]);
			} else if (strcmp(attr[i], "x2") == 0) {
				grad->linear.x2 = nsvg__parseCoordinateRaw(attr[i + 1]);
			} else if (strcmp(attr[i], "y2") == 0) {
				grad->linear.y2 = nsvg__parseCoordinateRaw(attr[i + 1]);
			} else if (strcmp(attr[i], "spreadMethod") == 0) {
				if (strcmp(attr[i+1], "pad") == 0)
					grad->spread = NSVG_SPREAD_PAD;
				else if (strcmp(attr[i+1], "reflect") == 0)
					grad->spread = NSVG_SPREAD_REFLECT;
				else if (strcmp(attr[i+1], "repeat") == 0)
					grad->spread = NSVG_SPREAD_REPEAT;
			} else if (strcmp(attr[i], "xlink:href") == 0) {
				const char *href = attr[i+1];
				strncpy(grad->ref, href+1, 62);
				grad->ref[62] = '\0';
			}
		}
	}

	grad->next = p->gradients;
	p->gradients = grad;
}

static void nsvg__parseGradientStop(NSVGparser* p, const char** attr)
{
	NSVGattrib* curAttr = nsvg__getAttr(p);
	NSVGgradientData* grad;
	NSVGgradientStop* stop;
	int i, idx;

	curAttr->stopOffset = 0;
	curAttr->stopColor = 0;
	curAttr->stopOpacity = 1.0f;

	for (i = 0; attr[i]; i += 2) {
		nsvg__parseAttr(p, attr[i], attr[i + 1]);
	}

	// Add stop to the last gradient.
	grad = p->gradients;
	if (grad == NULL) return;

	grad->nstops++;
	grad->stops = (NSVGgradientStop*)realloc(grad->stops, sizeof(NSVGgradientStop)*grad->nstops);
	if (grad->stops == NULL) return;

	// Insert
	idx = grad->nstops-1;
	for (i = 0; i < grad->nstops-1; i++) {
		if (curAttr->stopOffset < grad->stops[i].offset) {
			idx = i;
			break;
		}
	}
	if (idx != grad->nstops-1) {
		for (i = grad->nstops-1; i > idx; i--)
			grad->stops[i] = grad->stops[i-1];
	}

	stop = &grad->stops[idx];
	stop->color = curAttr->stopColor;
	stop->color |= (unsigned int)(curAttr->stopOpacity*255) << 24;
	stop->offset = curAttr->stopOffset;
}

static void nsvg__startElement(void* ud, const char* el, const char** attr)
{
	NSVGparser* p = (NSVGparser*)ud;

	if (p->defsFlag) {
		// Skip everything but gradients in defs
		if (strcmp(el, "linearGradient") == 0) {
			nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT);
		} else if (strcmp(el, "radialGradient") == 0) {
			nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT);
		} else if (strcmp(el, "stop") == 0) {
			nsvg__parseGradientStop(p, attr);
		}
		return;
	}

	if (strcmp(el, "g") == 0) {
		nsvg__pushAttr(p);
		nsvg__parseAttribs(p, attr);
	} else if (strcmp(el, "path") == 0) {
		if (p->pathFlag)	// Do not allow nested paths.
			return;
		nsvg__pushAttr(p);
		nsvg__parsePath(p, attr);
		nsvg__popAttr(p);
	} else if (strcmp(el, "rect") == 0) {
		nsvg__pushAttr(p);
		nsvg__parseRect(p, attr);
		nsvg__popAttr(p);
	} else if (strcmp(el, "circle") == 0) {
		nsvg__pushAttr(p);
		nsvg__parseCircle(p, attr);
		nsvg__popAttr(p);
	} else if (strcmp(el, "ellipse") == 0) {
		nsvg__pushAttr(p);
		nsvg__parseEllipse(p, attr);
		nsvg__popAttr(p);
	} else if (strcmp(el, "line") == 0)  {
		nsvg__pushAttr(p);
		nsvg__parseLine(p, attr);
		nsvg__popAttr(p);
	} else if (strcmp(el, "polyline") == 0)  {
		nsvg__pushAttr(p);
		nsvg__parsePoly(p, attr, 0);
		nsvg__popAttr(p);
	} else if (strcmp(el, "polygon") == 0)  {
		nsvg__pushAttr(p);
		nsvg__parsePoly(p, attr, 1);
		nsvg__popAttr(p);
	} else  if (strcmp(el, "linearGradient") == 0) {
		nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT);
	} else if (strcmp(el, "radialGradient") == 0) {
		nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT);
	} else if (strcmp(el, "stop") == 0) {
		nsvg__parseGradientStop(p, attr);
	} else if (strcmp(el, "defs") == 0) {
		p->defsFlag = 1;
	} else if (strcmp(el, "svg") == 0) {
		nsvg__parseSVG(p, attr);
	}
}

static void nsvg__endElement(void* ud, const char* el)
{
	NSVGparser* p = (NSVGparser*)ud;

	if (strcmp(el, "g") == 0) {
		nsvg__popAttr(p);
	} else if (strcmp(el, "path") == 0) {
		p->pathFlag = 0;
	} else if (strcmp(el, "defs") == 0) {
		p->defsFlag = 0;
	}
}

static void nsvg__content(void* ud, const char* s)
{
	NSVG_NOTUSED(ud);
	NSVG_NOTUSED(s);
	// empty
}

static void nsvg__imageBounds(NSVGparser* p, float* bounds)
{
	NSVGshape* shape;
	shape = p->image->shapes;
	if (shape == NULL) {
		bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0.0;
		return;
	}
	bounds[0] = shape->bounds[0];
	bounds[1] = shape->bounds[1];
	bounds[2] = shape->bounds[2];
	bounds[3] = shape->bounds[3];
	for (shape = shape->next; shape != NULL; shape = shape->next) {
		bounds[0] = nsvg__minf(bounds[0], shape->bounds[0]);
		bounds[1] = nsvg__minf(bounds[1], shape->bounds[1]);
		bounds[2] = nsvg__maxf(bounds[2], shape->bounds[2]);
		bounds[3] = nsvg__maxf(bounds[3], shape->bounds[3]);
	}
}

static float nsvg__viewAlign(float content, float container, int type)
{
	if (type == NSVG_ALIGN_MIN)
		return 0;
	else if (type == NSVG_ALIGN_MAX)
		return container - content;
	// mid
	return (container - content) * 0.5f;
}

static void nsvg__scaleGradient(NSVGgradient* grad, float tx, float ty, float sx, float sy)
{
	float t[6];
	nsvg__xformSetTranslation(t, tx, ty);
	nsvg__xformMultiply (grad->xform, t);

	nsvg__xformSetScale(t, sx, sy);
	nsvg__xformMultiply (grad->xform, t);
}

static void nsvg__scaleToViewbox(NSVGparser* p, const char* units)
{
	NSVGshape* shape;
	NSVGpath* path;
	float tx, ty, sx, sy, us, bounds[4], t[6], avgs;
	int i;
	float* pt;

	// Guess image size if not set completely.
	nsvg__imageBounds(p, bounds);

	if (p->viewWidth == 0) {
		if (p->image->width > 0) {
			p->viewWidth = p->image->width;
		} else {
			p->viewMinx = bounds[0];
			p->viewWidth = bounds[2] - bounds[0];
		}
	}
	if (p->viewHeight == 0) {
		if (p->image->height > 0) {
			p->viewHeight = p->image->height;
		} else {
			p->viewMiny = bounds[1];
			p->viewHeight = bounds[3] - bounds[1];
		}
	}
	if (p->image->width == 0)
		p->image->width = p->viewWidth;
	if (p->image->height == 0)
		p->image->height = p->viewHeight;

	tx = -p->viewMinx;
	ty = -p->viewMiny;
	sx = p->viewWidth > 0 ? p->image->width / p->viewWidth : 0;
	sy = p->viewHeight > 0 ? p->image->height / p->viewHeight : 0;
	// Unit scaling
	us = 1.0f / nsvg__convertToPixels(p, nsvg__coord(1.0f, nsvg__parseUnits(units)), 0.0f, 1.0f);

	// Fix aspect ratio
	if (p->alignType == NSVG_ALIGN_MEET) {
		// fit whole image into viewbox
		sx = sy = nsvg__minf(sx, sy);
		tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx;
		ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy;
	} else if (p->alignType == NSVG_ALIGN_SLICE) {
		// fill whole viewbox with image
		sx = sy = nsvg__maxf(sx, sy);
		tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx;
		ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy;
	}

	// Transform
	sx *= us;
	sy *= us;
	avgs = (sx+sy) / 2.0f;
	for (shape = p->image->shapes; shape != NULL; shape = shape->next) {
		shape->bounds[0] = (shape->bounds[0] + tx) * sx;
		shape->bounds[1] = (shape->bounds[1] + ty) * sy;
		shape->bounds[2] = (shape->bounds[2] + tx) * sx;
		shape->bounds[3] = (shape->bounds[3] + ty) * sy;
		for (path = shape->paths; path != NULL; path = path->next) {
			path->bounds[0] = (path->bounds[0] + tx) * sx;
			path->bounds[1] = (path->bounds[1] + ty) * sy;
			path->bounds[2] = (path->bounds[2] + tx) * sx;
			path->bounds[3] = (path->bounds[3] + ty) * sy;
			for (i =0; i < path->npts; i++) {
				pt = &path->pts[i*2];
				pt[0] = (pt[0] + tx) * sx;
				pt[1] = (pt[1] + ty) * sy;
			}
		}

		if (shape->fill.type == NSVG_PAINT_LINEAR_GRADIENT || shape->fill.type == NSVG_PAINT_RADIAL_GRADIENT) {
			nsvg__scaleGradient(shape->fill.gradient, tx,ty, sx,sy);
			memcpy(t, shape->fill.gradient->xform, sizeof(float)*6);
			nsvg__xformInverse(shape->fill.gradient->xform, t);
		}
		if (shape->stroke.type == NSVG_PAINT_LINEAR_GRADIENT || shape->stroke.type == NSVG_PAINT_RADIAL_GRADIENT) {
			nsvg__scaleGradient(shape->stroke.gradient, tx,ty, sx,sy);
			memcpy(t, shape->stroke.gradient->xform, sizeof(float)*6);
			nsvg__xformInverse(shape->stroke.gradient->xform, t);
		}

		shape->strokeWidth *= avgs;
		shape->strokeDashOffset *= avgs;
		for (i = 0; i < shape->strokeDashCount; i++)
			shape->strokeDashArray[i] *= avgs;
	}
}

NSVGimage* nsvgParse(char* input, const char* units, float dpi)
{
	NSVGparser* p;
	NSVGimage* ret = 0;

	p = nsvg__createParser();
	if (p == NULL) {
		return NULL;
	}
	p->dpi = dpi;

	nsvg__parseXML(input, nsvg__startElement, nsvg__endElement, nsvg__content, p);

	// Scale to viewBox
	nsvg__scaleToViewbox(p, units);

	ret = p->image;
	p->image = NULL;

	nsvg__deleteParser(p);

	return ret;
}

NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi)
{
	FILE* fp = NULL;
	size_t size;
	char* data = NULL;
	NSVGimage* image = NULL;

	fp = fopen(filename, "rb");
	if (!fp) goto error;
	fseek(fp, 0, SEEK_END);
	size = ftell(fp);
	fseek(fp, 0, SEEK_SET);
	data = (char*)malloc(size+1);
	if (data == NULL) goto error;
	if (fread(data, 1, size, fp) != size) goto error;
	data[size] = '\0';	// Must be null terminated.
	fclose(fp);
	image = nsvgParse(data, units, dpi);
	free(data);

	return image;

error:
	if (fp) fclose(fp);
	if (data) free(data);
	if (image) nsvgDelete(image);
	return NULL;
}

NSVGpath* nsvgDuplicatePath(NSVGpath* p)
{
    NSVGpath* res = NULL;

    if (p == NULL)
        return NULL;

    res = (NSVGpath*)malloc(sizeof(NSVGpath));
    if (res == NULL) goto error;
    memset(res, 0, sizeof(NSVGpath));

    res->pts = (float*)malloc(p->npts*2*sizeof(float));
    if (res->pts == NULL) goto error;
    memcpy(res->pts, p->pts, p->npts * sizeof(float) * 2);
    res->npts = p->npts;

    memcpy(res->bounds, p->bounds, sizeof(p->bounds));

    res->closed = p->closed;

    return res;

error:
    if (res != NULL) {
        free(res->pts);
        free(res);
    }
    return NULL;
}

void nsvgDelete(NSVGimage* image)
{
	NSVGshape *snext, *shape;
	if (image == NULL) return;
	shape = image->shapes;
	while (shape != NULL) {
		snext = shape->next;
		nsvg__deletePaths(shape->paths);
		nsvg__deletePaint(&shape->fill);
		nsvg__deletePaint(&shape->stroke);
		free(shape);
		shape = snext;
	}
	free(image);
}

#endif