
#include <math.h>
#include <sstream>
#include <iomanip>

//#include <atlenc.h>

#include "SVGCommonTypes.h"

namespace SVG
{

#ifndef M_PI
#define M_PI  3.14159265358979323846f
#endif

template <class TagTypes>
void SVGTreeParser<TagTypes>::Parse(xml_tree_t const & tree, SVGDrawObjects & objects, ImageBox & imageBox, ViewBox & viewBox, typename TagTypes::SVGObjectInit * pObjInit, ObjectInitCallback initCallback)
{
	pObjInit_ = pObjInit;
	initCallback_ = initCallback;

	Elements elms;
	ShapeAttrib sa;
	imageBox.SetRect(0, 0, 0, 0);

	xml_tag_t * tag = tree.root_tag;
	if (tag)
	{
		tag = tag->children;
	}

	// Get svg element
	while (tag)
	{
		if (tag->text  &&  !strcasecmp(tag->text, "svg"))
			break;

		tag = tag->next_tag;
	}

	if (tag)
	{
		xml_attribute_t * attrib = tag->attributes;

		//
		// Get viewBox
		//
		Values values;
		values["viewBox"] = Value(VT_String, "viewBox", false);
	
		ParseAttributes(attrib, values, sa.viewBox, elms);

		std::string::size_type pos = 0;
		sa.viewBox.left = GetNextNum(values["viewBox"].stringValue, pos);
		sa.viewBox.top = GetNextNum(values["viewBox"].stringValue, pos);
		sa.viewBox.right = GetNextNum(values["viewBox"].stringValue, pos);
		sa.viewBox.bottom = GetNextNum(values["viewBox"].stringValue, pos);
		// Transform to rect (in svg view box is set with top-left point and width and height)
		sa.viewBox.right += viewBox.left;
		sa.viewBox.bottom += viewBox.top;

		//
		// Get image box
		//
		values["x"] = Value(VT_Measure, "x", false);
		values["x"].value.measureValue = 0;
		values["y"] = Value(VT_Measure, "y", false);
		values["y"].value.measureValue = 0;
		values["width"] = Value(VT_Measure, "width", false);
		values["width"].value.measureValue = 0;
		values["height"] = Value(VT_Measure, "height", false);
		values["height"].value.measureValue = 0;

		ParseAttributes(attrib, values, sa.viewBox, elms);

		imageBox.left = (int)values["x"].value.measureValue;
		imageBox.top = (int)values["y"].value.measureValue;
		imageBox.right = (int)imageBox.left + (int)values["width"].value.measureValue;
		imageBox.bottom = (int)imageBox.top + (int)values["height"].value.measureValue;

		// If viewBox was not set then we should set it to current image box
		if (values["viewBox"].GetFoundValueType() != VT_String)
		{
			sa.viewBox.SetRect((float)imageBox.left, (float)imageBox.top, (float)imageBox.right, (float)imageBox.bottom);
		}

		// Parse svg tag
		// We should parse the first time to build element tree
		// (some elements can be linked before their appearence)
		ParseGroup(tag, elms, objects, sa, 0, false, false);
		objects.clear();
		ParseGroup(tag, elms, objects, sa, 0, false, false);
	}

	viewBox = sa.viewBox;
}

template <class TagTypes>
void SVGTreeParser<TagTypes>::ParseSVG(xml_tag_t * tag, Elements & elms, SVGDrawObjects & objects, ShapeAttrib const & sa)
{
	ImageBox imageBox;
	ViewBox viewBox;

	if (tag)
	{
		xml_attribute_t * attrib = tag->attributes;

		//
		// Get viewBox
		//
		Values values;
		values["viewBox"] = Value(VT_String, "viewBox", false);
	
		ParseAttributes(attrib, values, sa.viewBox, elms);

		std::string::size_type pos = 0;
		viewBox.left = GetNextNum(values["viewBox"].stringValue, pos);
		viewBox.top = GetNextNum(values["viewBox"].stringValue, pos);
		viewBox.right = GetNextNum(values["viewBox"].stringValue, pos);
		viewBox.bottom = GetNextNum(values["viewBox"].stringValue, pos);
		// Transform to rect (in svg view box is set with top-left point and width and height)
		viewBox.right += viewBox.left;
		viewBox.bottom += viewBox.top;

		//
		// Get image box
		//
		values["x"] = Value(VT_Measure, "x", false);
		values["x"].value.measureValue = 0;
		values["y"] = Value(VT_Measure, "y", false);
		values["y"].value.measureValue = 0;
		values["width"] = Value(VT_Measure, "width", false);
		values["width"].value.measureValue = 0;
		values["height"] = Value(VT_Measure, "height", false);
		values["height"].value.measureValue = 0;

		ParseAttributes(attrib, values, sa.viewBox, elms);

		imageBox.left = (int)values["x"].value.measureValue;
		imageBox.top = (int)values["y"].value.measureValue;
		imageBox.right = (int)imageBox.left + (int)values["width"].value.measureValue;
		imageBox.bottom = (int)imageBox.top + (int)values["height"].value.measureValue;

		// If viewBox was not set then we should set it to current image box
		if (values["viewBox"].GetFoundValueType() != VT_String)
		{
			viewBox.SetRect((float)imageBox.left, (float)imageBox.top, (float)imageBox.right, (float)imageBox.bottom);
		}

		// Parse svg tag
		ShapeAttrib saSVG = sa;
		saSVG.tm.SetIdentity();
		SVGDrawObjects objsSVG;
		ParseGroup(tag, elms, objsSVG, saSVG, 0, true, false);

		objsSVG.back()->GetShapeAttrib(saSVG);

		CoordSystemDef cs(imageBox);
		TransformCoords wt(cs, viewBox);
		TransformMatrixD tmSVG = wt.GetScreenTM();
		tmSVG.m[4] = (float)imageBox.left;
		tmSVG.m[5] = (float)imageBox.top;
		saSVG.tm = tmSVG * saSVG.tm;

		objsSVG.back()->SetShapeAttrib(saSVG);

		// Add clipping group
		saSVG = sa;
		saSVG.tm.SetIdentity();
		SVGGroupTag * clipGroup = new SVGGroupTag(saSVG);
		clipGroup->AddElement(objsSVG.back());

		// Create cliping
		SVGClipPathTag * clip = new SVGClipPathTag(PointD((float)imageBox.left, (float)imageBox.top), PointD((float)imageBox.right - (float)imageBox.left, (float)imageBox.bottom - (float)imageBox.top), 
			UT_UserSpaceOnUse, sa.viewBox);

		ShapeAttrib rectSa;
		SVGRectTag * clipRect = new SVGRectTag((float)imageBox.left, (float)imageBox.top, (float)imageBox.right - (float)imageBox.left, (float)imageBox.bottom - (float)imageBox.top,
			0.0f, 0.0f, rectSa);

        SharedPtr<ISVGDraw> svgDrawPtr(clipRect);
		clip->AddElement(svgDrawPtr);
		//

		clipGroup->GetShapeAttrib(saSVG);
		saSVG.clipPath.reset(clip);
		clipGroup->SetShapeAttrib(saSVG);

		objects.push_back(SharedPtr<ISVGDraw>(clipGroup));
	}
}

template <class TagTypes>
void SVGTreeParser<TagTypes>::ParseStyle(xml_tag_t* tag)
{
    if (!tag->children || !tag->children->text)
        return;

    std::string style = tag->children->text;

    //.st0{fill:#AB805F;}.st1{ fill:#7D5638; }.st2{ fill:url(#SVGID_1_);}

    std::string::size_type classNameStart = 0;
    std::string::size_type start = style.find("{");
    std::string::size_type end = style.find("}");
    while ((start != std::string::npos) && (end != std::string::npos))
    {
        std::string className;
        std::string internalStyle;
        
        className.assign(style, classNameStart, start - classNameStart);
        className = TrimBorderSpaces(className);

        internalStyle.assign(style, start + 1, end - start - 1);
        internalStyle = TrimBorderSpaces(internalStyle);

        styles_[className] = internalStyle;

        classNameStart = end + 1;
        start = style.find("{", classNameStart);
        end = style.find("}", classNameStart);
    }
}


template <class TagTypes>
void SVGTreeParser<TagTypes>::ParseAttributes(xml_attribute_t * attrib, Values & values, ViewBox const & viewBox, Elements const & elms)
{
	while (attrib)
	{
		if (attrib->value  &&  attrib->name)
		{
			std::string name(attrib->name), value(attrib->value);
			typename Values::iterator i = values.begin();
			for (; i != values.end(); ++i)
			{
				std::string valueOrig = value;
				bool isInStyle = false;
                
                if (name == "class")
                {
                    /* Look up style based on class
                     */

                    std::string className = "." + value;

                    if (styles_.find(className) != styles_.end())
                    {
                        name = "style";
                        value = styles_[className];
                    }
                }
                
				isInStyle = i->second.searchInStyleAttrib  &&  (name == "style")  &&  
					GetStyleValue(value, i->second.attribName, valueOrig);

				if (!(i->second.wasFound)  &&  (i->second.attribName == name  ||  isInStyle))
				{
					i->second.wasFound = true;
					i->second.stringValue = valueOrig;
					ValueType valueType = i->second.valueType;
					i->second.valueType = VT_Unknown;

					if (VT_String & valueType)
					{
						// Have set above
						i->second.valueType = VT_String;
					}

					// Should be after VT_String handling because VT_String handling is set default
					if (VT_None & valueType)
					{
						if (i->second.stringValue == "none")
						{
							i->second.valueType = VT_None;
						}
					}

					if (VT_FillRule & valueType)
					{
						if (GetFillRule(i->second.stringValue, i->second.value.fillRuleValue))
						{
							i->second.valueType = VT_FillRule;
						}
					}

					if (VT_StrokeLinecap & valueType)
					{
						if (GetStrokeLinecap(i->second.stringValue, i->second.value.strokeLinecapValue))
						{
							i->second.valueType = VT_StrokeLinecap;
						}
					}

					if (VT_StrokeLineJoin & valueType)
					{
						if (GetStrokeLineJoin(i->second.stringValue, i->second.value.strokeLineJoinValue))
						{
							i->second.valueType = VT_StrokeLineJoin;
						}
					}

					if (VT_Measure & valueType)
					{
						std::string::size_type pos = 0;
						if (GetMeasureValue(i->second.stringValue, pos, i->second.value.measureValue))
						{
							i->second.valueType = VT_Measure;
						}
					}

					if (VT_MeasureVector & valueType)
					{
						if (GetMeasureVector(i->second.stringValue, i->second.value.measureVectorValue))
						{
							i->second.valueType = VT_MeasureVector;
						}
					}

					if (VT_Opacity & valueType)
					{
						if (GetOpacity(i->second.stringValue, i->second.value.opacityValue))
						{
							i->second.valueType = VT_Opacity;
						}
					}

					if (VT_UnitType & valueType)
					{
						if (GetUnitType(i->second.stringValue, i->second.value.unitTypeValue))
						{
							i->second.valueType = VT_UnitType;
						}
					}

					if (VT_Color & valueType)
					{
						if (GetColor(i->second.stringValue, i->second.value.colorValue))
						{
							i->second.valueType = VT_Color;
						}
					}

					if (VT_BlendingMode & valueType)
					{
						if (GetBlendingMode(i->second.stringValue, i->second.value.bmValue))
						{
							i->second.valueType = VT_BlendingMode;
						}
					}

					if (VT_ColorSpace & valueType)
					{
						if (GetColorSpace(i->second.stringValue, i->second.value.colorSpaceValue))
						{
							i->second.valueType = VT_ColorSpace;
						}
					}

					if (VT_ObjectReference & valueType)
					{
						if (SetObject(i->second.stringValue.c_str(), elms, i->second.objValue))
						{
							i->second.valueType = VT_ObjectReference;
						}
					}

					if (VT_TransformMatrix & valueType)
					{
						if (SetMatrix(i->second.stringValue, i->second.value.transformMatrixValue))
						{
							i->second.valueType = VT_TransformMatrix;
						}
					}

					if (VT_GradientSpreadMethod & valueType)
					{
						if (GetGradientSpreadMethod(i->second.stringValue, i->second.value.spreadMethod))
						{
							i->second.valueType = VT_GradientSpreadMethod;
						}
					}

				}
			}
		}

		attrib = attrib->next_attribute;
	}
}

template <class TagTypes>
void SVGTreeParser<TagTypes>::ParseShapeAttributes(xml_attribute_t * attrib, ShapeAttrib & sa, std::string & id, Elements const & elms)
{
	Values values;
	values["id"] = Value(VT_String, "id", false);
	values["a:adobe-blending-mode"] = Value(VT_BlendingMode, "a:adobe-blending-mode", true);
	values["opacity"] = Value(VT_Opacity, "opacity", true);
	values["fill-opacity"] = Value(VT_Opacity, "fill-opacity", true);
	values["stroke-opacity"] = Value(VT_Opacity, "stroke-opacity", true);
	values["mask"] = Value(VT_ObjectReference, "mask", true);
	values["filter"] = Value(VT_ObjectReference, "filter", true);
	values["fill"] = Value((ValueType)(VT_ObjectReference | VT_Color | VT_None), "fill", true);
	values["stroke"] = Value((ValueType)(VT_ObjectReference | VT_Color | VT_None), "stroke", true);
	values["stroke-width"] = Value(VT_Measure, "stroke-width", true);
	values["color-interpolation"] = Value(VT_ColorSpace, "color-interpolation", true);
	values["transform"] = Value(VT_TransformMatrix, "transform", true);
	values["clip-path"] = Value(VT_ObjectReference, "clip-path", true);
	values["fill-rule"] = Value(VT_FillRule, "fill-rule", true);
	values["stroke-linecap"] = Value(VT_StrokeLinecap, "stroke-linecap", true);
	values["stroke-linejoin"] = Value(VT_StrokeLineJoin, "stroke-linejoin", true);
	values["stroke-miterlimit"] = Value(VT_Measure, "stroke-miterlimit", true);
	values["stroke-dasharray"] = Value((ValueType)(VT_String | VT_None), "stroke-dasharray", true);
	values["stroke-dashoffset"] = Value(VT_Measure, "stroke-dashoffset", true);
	values["display"] = Value(VT_None, "display", true);

	ParseAttributes(attrib, values, sa.viewBox, elms);

	// Assign values
	id = values["id"].stringValue;

	if (values["display"].GetFoundValueType() == VT_None)
	{
		sa.display = false;
	}

	if (values["a:adobe-blending-mode"].GetFoundValueType() == VT_BlendingMode)
	{
		sa.blendingMode = values["a:adobe-blending-mode"].value.bmValue;
	}

	if (values["opacity"].GetFoundValueType() == VT_Opacity)
	{
		sa.opacity = values["opacity"].value.opacityValue;
	}

	if (values["mask"].GetFoundValueType() == VT_ObjectReference)
	{
		sa.mask = values["mask"].objValue;
	}

	if (values["filter"].GetFoundValueType() == VT_ObjectReference)
	{
		sa.filter = values["filter"].objValue;
	}

	if (values["fill"].GetFoundValueType() == VT_None)
	{
		sa.wasSet.fill = 1;
		sa.isFilled = false;
	}
	else if (values["fill"].GetFoundValueType() == VT_ObjectReference)
	{
		sa.wasSet.fill = 1;
		sa.isFilled = true;
		sa.fill = values["fill"].objValue;
	}
	else if (values["fill"].GetFoundValueType() == VT_Color)
	{
		sa.wasSet.fill = 1;
		sa.isFilled = true;
		sa.fill.reset();
		sa.fillColor = values["fill"].value.colorValue;
	}

	if (values["stroke"].GetFoundValueType() == VT_None)
	{
		sa.wasSet.stroke = 1;
		sa.isStroked = false;
	}
	else if (values["stroke"].GetFoundValueType() == VT_ObjectReference)
	{
		sa.wasSet.stroke = 1;
		sa.isStroked = true;
		sa.stroke = values["stroke"].objValue;
	}
	else if (values["stroke"].GetFoundValueType() == VT_Color)
	{
		sa.wasSet.stroke = 1;
		sa.isStroked = true;
		sa.stroke.reset();
		sa.strokeColor = values["stroke"].value.colorValue;
	}

	if (values["stroke-width"].GetFoundValueType() == VT_Measure)
	{
		sa.wasSet.strokeWidth = 1;
		sa.strokeWidth = values["stroke-width"].value.measureValue;
	}

	if (values["fill-rule"].GetFoundValueType() == VT_FillRule)
	{
		sa.wasSet.fillRule = 1;
		sa.fillRule = values["fill-rule"].value.fillRuleValue;
	}

	if (values["fill-opacity"].GetFoundValueType() == VT_Opacity)
	{
		sa.wasSet.fillOpacity = 1;
		sa.fillOpacity = values["fill-opacity"].value.opacityValue;
	}

	if (values["stroke-opacity"].GetFoundValueType() == VT_Opacity)
	{
		sa.wasSet.strokeOpacity = 1;
		sa.strokeOpacity = values["stroke-opacity"].value.opacityValue;
	}

	if (values["stroke-linecap"].GetFoundValueType() == VT_StrokeLinecap)
	{
		sa.wasSet.strokeLinecap = 1;
		sa.strokeLinecap = values["stroke-linecap"].value.strokeLinecapValue;
	}

	if (values["stroke-linejoin"].GetFoundValueType() == VT_StrokeLineJoin)
	{
		sa.wasSet.strokeLineJoin = 1;
		sa.strokeLineJoin = values["stroke-linejoin"].value.strokeLineJoinValue;
	}

	if (values["stroke-miterlimit"].GetFoundValueType() == VT_Measure)
	{
		sa.wasSet.strokeMiterlimit = 1;
		sa.strokeMiterlimit = values["stroke-miterlimit"].value.measureValue;
	}

	if (values["stroke-dashoffset"].GetFoundValueType() == VT_Measure)
	{
		sa.wasSet.strokeDashOffset = 1;
		sa.strokeDashOffset = values["stroke-dashoffset"].value.measureValue;
	}

	if (values["stroke-dasharray"].GetFoundValueType() == VT_None)
	{
		sa.wasSet.strokeDashArray = 1;
		sa.strokeDashArray.clear();
	}

	if (values["stroke-dasharray"].GetFoundValueType() == VT_String)
	{
		sa.wasSet.strokeDashArray = 1;
		GetMeasureVector(values["stroke-dasharray"].stringValue, sa.strokeDashArray);

		if (sa.strokeDashArray.size() % 2)
		{
			sa.strokeDashArray.reserve(sa.strokeDashArray.size() * 2);
			MeasureFloatVector::iterator i = sa.strokeDashArray.begin();
			MeasureFloatVector::size_type j = 0, size = sa.strokeDashArray.size();
			for (j = 0; j < size; ++j, ++i)
			{
				sa.strokeDashArray.push_back(*i);
			}
		}
	}

	if (values["color-interpolation"].GetFoundValueType() == VT_ColorSpace)
	{
		// We support only sRGB color space in drawing of base shapes
		// (like all shapes has always color-interpolation="sRGB")
		sa.colorSpace = CS_sRGB;
	}

	if (values["transform"].GetFoundValueType() == VT_TransformMatrix)
	{
		sa.tm = values["transform"].value.transformMatrixValue;
	}

	if (values["clip-path"].GetFoundValueType() == VT_ObjectReference)
	{
		sa.clipPath = values["clip-path"].objValue;
	}
}

template <class TagTypes>
void SVGTreeParser<TagTypes>::ParseFilterPrimitiveBaseAttributes(xml_attribute_t * attrib, FilterPrimitiveBaseAttrib & fpba, Elements & elms, ViewBox const & viewBox)
{
	Values values;
	values["result"] = Value(VT_String, "result", false);
	values["in"] = Value(VT_String, "in", false);
	values["in2"] = Value(VT_String, "in2", false);
	values["color-interpolation-filters"] = Value(VT_ColorSpace, "color-interpolation-filters", true);

	ParseAttributes(attrib, values, viewBox, elms);
	fpba.result = TrimBorderSpaces(values["result"].stringValue);
	fpba.in = TrimBorderSpaces(values["in"].stringValue);
	fpba.in2 = TrimBorderSpaces(values["in2"].stringValue);

	if (values["color-interpolation-filters"].GetFoundValueType() == VT_ColorSpace)
	{
		fpba.colorSpace = values["color-interpolation-filters"].value.colorSpaceValue;
	}
}

template <class TagTypes>
void SVGTreeParser<TagTypes>::ParseGroup(xml_tag_t * tag, Elements & elms, SVGDrawObjects & objs, ShapeAttrib const & saCur, int level, bool asTag, bool onlyElements)
{
	ShapeAttrib sa;
	SVGDrawObjects objsGroup;
	std::string id;

	sa.viewBox = saCur.viewBox;

	ParseShapeAttributes(tag->attributes, sa, id, elms);
	sa.InheritFrom(saCur);

	tag = tag->children;
	while (tag)
	{
		if (tag->text)
		{
			if (*(tag->text) == 'g')
			{
				ParseGroup(tag, elms, objsGroup, sa, level + 1, true, false);
			}
			else if (!strcasecmp("switch", tag->text))
			{
				ParseGroup(tag, elms, objsGroup, sa, level + 1, false, false);
			}
			else if (!strcasecmp("ellipse", tag->text))
			{
				ParseEllipse(tag, elms, objsGroup, sa, level + 1);
			}
			else if (!strcasecmp("circle", tag->text))
			{
				ParseCircle(tag, elms, objsGroup, sa, level + 1);
			}
			else if (!strcasecmp("path", tag->text))
			{
				ParsePath(tag, elms, objsGroup, sa, level + 1);
			}
			else if (!strcasecmp("polygon", tag->text))
			{
				ParsePolygon(tag, elms, objsGroup, sa, level + 1);
			}
			else if (!strcasecmp("polyline", tag->text))
			{
				ParsePolyline(tag, elms, objsGroup, sa, level + 1);
			}
			else if (!strcasecmp("rect", tag->text))
			{
				ParseRect(tag, elms, objsGroup, sa, level + 1);
			}
			else if (!strcasecmp("line", tag->text))
			{
				ParseLine(tag, elms, objsGroup, sa, level + 1);
			}
			else if (!strcasecmp("image", tag->text))
			{
				ParseImage(tag, elms, objsGroup, sa, level + 1);
			}
			else if (!strcasecmp("filter", tag->text))
			{
				ParseFilter(tag, elms, sa.viewBox, level + 1);
			}
			else if (!strcasecmp("mask", tag->text))
			{
				ParseMask(tag, elms, sa.viewBox, level + 1);
			}
			else if (!strcasecmp("clipPath", tag->text))
			{
				ParseClipPath(tag, elms, sa.viewBox, level + 1);
			}
			else if (!strcasecmp("radialGradient", tag->text))
			{
				ParseRadialGradient(tag, elms, sa.viewBox, level + 1);
			}
			else if (!strcasecmp("linearGradient", tag->text))
			{
				ParseLinearGradient(tag, elms, sa.viewBox, level + 1);
			}
			else if (!strcasecmp("defs", tag->text))
			{
				// We parse it as a group, because <defs> content doesn't 
				//  include any rendering elements like circle. So no one object will be added, 
				//  just only effect objects will be.
				ParseGroup(tag, elms, objsGroup, sa, level + 1, true, true);
			}
			else if (!strcasecmp("use", tag->text))
			{
				ParseUse(tag, elms, objsGroup, sa, level + 1);
			}
			else if (!strcasecmp("svg", tag->text))
			{
				ParseSVG(tag, elms, objsGroup, sa);
			}
            else if (!strcasecmp("style", tag->text))
            {
                ParseStyle(tag);
            }
		}

		tag = tag->next_tag;
	}

	// Add group element
	if (!onlyElements &&  asTag  &&  !objsGroup.empty())
	{
		SharedPtr<ISVGDraw> sp(new typename TagTypes::SVGGroupTag(sa, id.c_str()));
		typename TagTypes::SVGGroupTag * gr = dynamic_cast<typename TagTypes::SVGGroupTag *>(sp.get());

		SVGDrawObjects::iterator i = objsGroup.begin();
		for (; i != objsGroup.end(); ++i)
		{
			gr->AddElement(*i);
		}

		// Init of object
		if (pObjInit_  &&  initCallback_)
		{
            SharedPtr<ISVGObject> svgObjPtr(gr);
			(pObjInit_->*initCallback_)(svgObjPtr);
		}

		objs.push_back(sp);
		elms.insert(Element(id, sp));
	}
	else if (!onlyElements  &&  !asTag)
	{
		SVGDrawObjects::iterator i = objsGroup.begin();
		for (; i != objsGroup.end(); ++i)
		{
			objs.push_back(*i);
		}
	}
}

template <class TagTypes>
void SVGTreeParser<TagTypes>::ParseEllipse(xml_tag_t * tag, Elements & elms, SVGDrawObjects & objs, ShapeAttrib const & saCur, int level)
{
	ShapeAttrib sa;
	std::string id;
	sa.viewBox = saCur.viewBox;

	ParseShapeAttributes(tag->attributes, sa, id, elms);
	sa.InheritFrom(saCur);

	// Parse tag specific attributes
	Values values;
	values["cx"] = Value(VT_Measure, "cx", false);
	values["cx"].value.measureValue = 0;
	values["cy"] = Value(VT_Measure, "cy", false);
	values["cy"].value.measureValue = 0;
	values["rx"] = Value(VT_Measure, "rx", false);
	values["rx"].value.measureValue = 0;
	values["ry"] = Value(VT_Measure, "ry", false);
	values["ry"].value.measureValue = 0;

	ParseAttributes(tag->attributes, values, sa.viewBox, elms);

	// Add object
	SharedPtr<ISVGDraw> sp(new typename TagTypes::SVGEllipseTag(values["cx"].value.measureValue, values["cy"].value.measureValue, 
		values["rx"].value.measureValue, values["ry"].value.measureValue, sa, id.c_str()));
	objs.push_back(sp);
	elms.insert(Element(id, sp));

	// Init of object
	if (pObjInit_  &&  initCallback_)
	{
        SharedPtr<ISVGObject> svgObjPtr(sp.get());
		(pObjInit_->*initCallback_)(svgObjPtr);
	}
}

template <class TagTypes>
void SVGTreeParser<TagTypes>::ParseUse(xml_tag_t * tag, Elements & elms, SVGDrawObjects & objs, ShapeAttrib const & saCur, int level)
{
	std::string id;

	// Parse tag specific attributes
	Values values;
	values["xlink:href"] = Value(VT_ObjectReference, "xlink:href", false);
	values["x"] = Value(VT_Measure, "x", false);
	values["x"].value.measureValue = 0;
	values["y"] = Value(VT_Measure, "y", false);
	values["y"].value.measureValue = 0;

	ParseAttributes(tag->attributes, values, saCur.viewBox, elms);

	// Get pointed object
	SharedPtr<ISVGObject> objTmp = values["xlink:href"].objValue;
	SharedPtr<ISVGDraw> obj = objTmp;
	if (obj.get())
	{
		ShapeAttrib sa;
		obj->GetShapeAttrib(sa);

		sa.viewBox = saCur.viewBox;
		ParseShapeAttributes(tag->attributes, sa, id, elms);
		sa.InheritFrom(saCur);

		// insert use reference object
		// Create object copy
		obj.reset(dynamic_cast<ISVGDraw *>(obj->Clone()));
		// Set updated shape attributes
		obj->InheritFrom(sa);

		// Fix transform
		ShapeAttrib sa2;
		obj->GetShapeAttrib(sa2);
		TransformMatrixD tmUse;
		tmUse.SetTranslate(values["x"].value.measureValue, values["y"].value.measureValue);
		sa2.tm = tmUse * sa.tm * sa2.tm;
		obj->SetShapeAttrib(sa2);

		// Add object to the list
		objs.push_back(obj);
		elms.insert(Element(id, obj));

		// Init of object
		if (pObjInit_  &&  initCallback_)
		{
            SharedPtr<ISVGObject> svgObjPtr(obj);
            (pObjInit_->*initCallback_)(svgObjPtr);
		}
	}
}

template <class TagTypes>
void SVGTreeParser<TagTypes>::ParseCircle(xml_tag_t * tag, Elements & elms, SVGDrawObjects & objs, ShapeAttrib const & saCur, int level)
{
	ShapeAttrib sa;
	std::string id;
	sa.viewBox = saCur.viewBox;

	ParseShapeAttributes(tag->attributes, sa, id, elms);
	sa.InheritFrom(saCur);

	// Parse tag specific attributes
	Values values;
	values["cx"] = Value(VT_Measure, "cx", false);
	values["cx"].value.measureValue = 0;
	values["cy"] = Value(VT_Measure, "cy", false);
	values["cy"].value.measureValue = 0;
	values["r"] = Value(VT_Measure, "r", false);
	values["r"].value.measureValue = 0;

	ParseAttributes(tag->attributes, values, sa.viewBox, elms);

	// Add object
	SharedPtr<ISVGDraw> sp(new typename TagTypes::SVGEllipseTag(values["cx"].value.measureValue, values["cy"].value.measureValue, 
		values["r"].value.measureValue, values["r"].value.measureValue, sa, id.c_str()));
	objs.push_back(sp);
	elms.insert(Element(id, sp));

	// Init of object
	if (pObjInit_  &&  initCallback_)
	{
        SharedPtr<ISVGObject> svgObjPtr(sp.get());
		(pObjInit_->*initCallback_)(svgObjPtr);
	}
}

template <class TagTypes>
void SVGTreeParser<TagTypes>::ParseLine(xml_tag_t * tag, Elements & elms, SVGDrawObjects & objs, ShapeAttrib const & saCur, int level)
{
	ShapeAttrib sa;
	std::string id;
	PathCommandList cmdList;
	sa.viewBox = saCur.viewBox;

	ParseShapeAttributes(tag->attributes, sa, id, elms);
	sa.InheritFrom(saCur);

	// Parse tag specific attributes
	Values values;
	values["x1"] = Value(VT_Measure, "x1", false);
	values["x1"].value.measureValue = 0;
	values["y1"] = Value(VT_Measure, "y1", false);
	values["y1"].value.measureValue = 0;
	values["x2"] = Value(VT_Measure, "x2", false);
	values["x2"].value.measureValue = 0;
	values["y2"] = Value(VT_Measure, "y2", false);
	values["y2"].value.measureValue = 0;

	ParseAttributes(tag->attributes, values, sa.viewBox, elms);

	// Add path commands
	PathCommand cmd;
	cmd.cmd = 'M';
	cmd.x1.x = values["x1"].value.measureValue;
	cmd.x1.y = values["y1"].value.measureValue;
	cmdList.push_back(cmd);

	cmd.cmd = 'L';
	cmd.x1.x = values["x2"].value.measureValue;
	cmd.x1.y = values["y2"].value.measureValue;
	cmdList.push_back(cmd);

	// Add object
	if (!cmdList.empty())
	{
		SharedPtr<ISVGDraw> sp(new typename TagTypes::SVGPathTag(cmdList, sa, id.c_str()));
		objs.push_back(sp);
		elms.insert(Element(id, sp));

		// Init of object
		if (pObjInit_  &&  initCallback_)
		{
            SharedPtr<ISVGObject> svgObjPtr(sp.get());
            (pObjInit_->*initCallback_)(svgObjPtr);
		}
	}
}

template <class TagTypes>
void SVGTreeParser<TagTypes>::ParseRect(xml_tag_t * tag, Elements & elms, SVGDrawObjects & objs, ShapeAttrib const & saCur, int level)
{
	ShapeAttrib sa;
	std::string id;
	PathCommandList cmdList;
	sa.viewBox = saCur.viewBox;

	ParseShapeAttributes(tag->attributes, sa, id, elms);
	sa.InheritFrom(saCur);

	// Parse tag specific attributes
	Values values;
	values["x"] = Value(VT_Measure, "x", false);
	values["x"].value.measureValue = 0;
	values["y"] = Value(VT_Measure, "y", false);
	values["y"].value.measureValue = 0;
	values["width"] = Value(VT_Measure, "width", false);
	values["width"].value.measureValue = 0;
	values["height"] = Value(VT_Measure, "height", false);
	values["height"].value.measureValue = 0;
	values["rx"] = Value(VT_Measure, "rx", false);
	values["rx"].value.measureValue = 0;
	values["ry"] = Value(VT_Measure, "ry", false);
	values["ry"].value.measureValue = 0;

	ParseAttributes(tag->attributes, values, sa.viewBox, elms);

	if (values["rx"].GetFoundValueType() == VT_Unknown)
	{
		values["rx"].value.measureValue = values["ry"].value.measureValue;
	}

	if (values["ry"].GetFoundValueType() == VT_Unknown)
	{
		values["ry"].value.measureValue = values["rx"].value.measureValue;
	}

	if (values["rx"].value.measureValue == 0.0f  ||  values["ry"].value.measureValue == 0.0f)
	{
		values["rx"].value.measureValue = 0.0f;
		values["ry"].value.measureValue = 0.0f;
	}

	if (values["rx"].value.measureValue * 2 > values["width"].value.measureValue)
	{
		values["rx"].value.measureValue = values["width"].value.measureValue / 2.0f;
	}

	if (values["ry"].value.measureValue * 2 > values["height"].value.measureValue)
	{
		values["ry"].value.measureValue = values["height"].value.measureValue / 2.0f;
	}

	SharedPtr<ISVGDraw> sp(new SVGRectTag(values["x"].value.measureValue, values["y"].value.measureValue, 
		values["width"].value.measureValue, values["height"].value.measureValue, 
		values["rx"].value.measureValue, values["ry"].value.measureValue, sa, id.c_str()));
	objs.push_back(sp);
	elms.insert(Element(id, sp));

	// Init of object
	if (pObjInit_  &&  initCallback_)
	{
        SharedPtr<ISVGObject> svgObjPtr(sp.get());
        (pObjInit_->*initCallback_)(svgObjPtr);
	}
}

template <class TagTypes>
void SVGTreeParser<TagTypes>::ParsePolyline(xml_tag_t * tag, Elements & elms, SVGDrawObjects & objs, ShapeAttrib const & saCur, int level)
{
	ShapeAttrib sa;
	std::string id;
	PathCommandList cmdList;

	sa.viewBox = saCur.viewBox;

	ParseShapeAttributes(tag->attributes, sa, id, elms);
	sa.InheritFrom(saCur);

	// Parse tag specific attributes
	Values values;
	values["points"] = Value(VT_String, "points", false);

	ParseAttributes(tag->attributes, values, sa.viewBox, elms);

	// Get polygon "points" attribute
	std::string points(values["points"].stringValue);
	std::string::size_type pos = 0, prevPos = 0;
	PathCommand cmd;

	// Move to polyline start point
	cmd.cmd = 'M';
	cmd.x1.x = GetNextNum(points, pos);
	cmd.x1.y = GetNextNum(points, pos);
	if (pos != 0)
	{
		cmdList.push_back(cmd);
		
		// Parse "points" attribute and get path command list
		while (prevPos != pos)
		{
			prevPos = pos;
			cmd.cmd = 'L';
			cmd.x1.x = GetNextNum(points, pos);
			cmd.x1.y = GetNextNum(points, pos);

			if (prevPos != pos)
			{
				cmdList.push_back(cmd);
			}
		}
	}

	// Add object
	if (!cmdList.empty())
	{
		SharedPtr<ISVGDraw> sp(new typename TagTypes::SVGPathTag(cmdList, sa, id.c_str()));
		objs.push_back(sp);
		elms.insert(Element(id, sp));

		// Init of object
		if (pObjInit_  &&  initCallback_)
		{
            SharedPtr<ISVGObject> svgObjPtr(sp.get());
            (pObjInit_->*initCallback_)(svgObjPtr);
		}
	}
}

template <class TagTypes>
void SVGTreeParser<TagTypes>::ParsePolygon(xml_tag_t * tag, Elements & elms, SVGDrawObjects & objs, ShapeAttrib const & saCur, int level)
{
	ShapeAttrib sa;
	std::string id;
	PathCommandList cmdList;

	sa.viewBox = saCur.viewBox;

	ParseShapeAttributes(tag->attributes, sa, id, elms);
	sa.InheritFrom(saCur);

	// Parse tag specific attributes
	Values values;
	values["points"] = Value(VT_String, "points", false);

	ParseAttributes(tag->attributes, values, sa.viewBox, elms);

	// Get polygon "points" attribute
	std::string points(values["points"].stringValue);
	std::string::size_type pos = 0, prevPos = 0;
	PathCommand cmd;

	// Move to polygon start point
	cmd.cmd = 'M';
	cmd.x1.x = GetNextNum(points, pos);
	cmd.x1.y = GetNextNum(points, pos);
	if (pos != 0)
	{
		cmdList.push_back(cmd);
		PointD startPoint = cmd.x1;

		// Parse "points" attribute and get path command list
		while (prevPos != pos)
		{
			prevPos = pos;
			cmd.cmd = 'L';
			cmd.x1.x = GetNextNum(points, pos);
			cmd.x1.y = GetNextNum(points, pos);

			if (prevPos != pos)
			{
				cmdList.push_back(cmd);
			}
		}

		// Close polygon
		cmd.cmd = 'L';
		cmd.x1 = startPoint;
		cmdList.push_back(cmd);

		cmd.cmd = 'Z';
		cmdList.push_back(cmd);
	}

	// Add object
	if (!cmdList.empty())
	{
		SharedPtr<ISVGDraw> sp(new typename TagTypes::SVGPathTag(cmdList, sa, id.c_str()));
		objs.push_back(sp);
		elms.insert(Element(id, sp));

		// Init of object
		if (pObjInit_  &&  initCallback_)
		{
            SharedPtr<ISVGObject> svgObjPtr(sp.get());
            (pObjInit_->*initCallback_)(svgObjPtr);
		}
	}
}

template <class TagTypes>
void SVGTreeParser<TagTypes>::ParseImage(xml_tag_t * tag, Elements & elms, SVGDrawObjects & objs, ShapeAttrib const & saCur, int level)
{
	ShapeAttrib sa;
	std::string id;
	std::string imageData;

	sa.viewBox = saCur.viewBox;

	ParseShapeAttributes(tag->attributes, sa, id, elms);
	sa.InheritFrom(saCur);

	// Parse tag specific attributes
	Values values;
	values["x"] = Value(VT_Measure, "x", false);
	values["x"].value.measureValue = 0;
	values["y"] = Value(VT_Measure, "y", false);
	values["y"].value.measureValue = 0;
	values["width"] = Value(VT_Measure, "width", false);
	values["width"].value.measureValue = 0;
	values["height"] = Value(VT_Measure, "height", false);
	values["height"].value.measureValue = 0;
	values["xlink:href"] = Value(VT_String, "xlink:href", false);

	ParseAttributes(tag->attributes, values, sa.viewBox, elms);

	PointD p(values["x"].value.measureValue, values["y"].value.measureValue), 
			wh(values["width"].value.measureValue, values["height"].value.measureValue);

	// Get image content
	std::string data(values["xlink:href"].stringValue);
	std::string::size_type pos = 0;
	if ((pos = data.find("base64,", 0)) != std::string::npos)
	{
		data.erase(0, pos + 7);
		Base64Decode(data, imageData);
	}

	// Add object
	SharedPtr<ISVGDraw> sp(new typename TagTypes::SVGImageTag(p, wh, imageData, sa, id.c_str()));
	objs.push_back(sp);
	elms.insert(Element(id, sp));

	// Init of object
	if (pObjInit_  &&  initCallback_)
	{
        SharedPtr<ISVGObject> svgObjPtr(sp.get());
        (pObjInit_->*initCallback_)(svgObjPtr);
	}
}

template <class TagTypes>
void SVGTreeParser<TagTypes>::ParsePath(xml_tag_t * tag, Elements & elms, SVGDrawObjects & objs, ShapeAttrib const & saCur, int level)
{
	ShapeAttrib sa;
	std::string id;
	PathCommandList cmdList;

	sa.viewBox = saCur.viewBox;

	ParseShapeAttributes(tag->attributes, sa, id, elms);
	sa.InheritFrom(saCur);

	// Parse tag specific attributes
	Values values;
	values["d"] = Value(VT_String, "d", false);

	ParseAttributes(tag->attributes, values, sa.viewBox, elms);

	// Store path "d" attribute
	std::string d(values["d"].stringValue);
	d = TrimBorderSpaces(d);
	std::string::size_type pos = 0, size = d.size();
	PathCommand cmd;

	// Parse "d" attribute and get path command list
	while (pos < size)
	{
		cmd.cmd = d[pos++];
		if (cmd.cmd == 'M'  ||  cmd.cmd == 'm'  ||  cmd.cmd == 'L'  ||  cmd.cmd == 'l')
		{
            cmd.x1.x = GetNextNum(d, pos);
            cmd.x1.y = GetNextNum(d, pos);
            cmdList.push_back(cmd);
            
            if (cmd.cmd == 'M')
                cmd.cmd = 'L';
            else if (cmd.cmd == 'm')
                cmd.cmd = 'l';
            
			while (pos < size  &&  !isalpha((unsigned char)d[pos]))
			{
				cmd.x1.x = GetNextNum(d, pos);
				cmd.x1.y = GetNextNum(d, pos);
				cmdList.push_back(cmd);
			}
		}
		else if (cmd.cmd == 'H'  ||  cmd.cmd == 'h')
		{
			while (pos < size  &&  !isalpha((unsigned char)d[pos]))
			{
				cmd.x1.x = GetNextNum(d, pos);
				cmdList.push_back(cmd);
			}
		}
		else if (cmd.cmd == 'V'  ||  cmd.cmd == 'v')
		{
			while (pos < size  &&  !isalpha((unsigned char)d[pos]))
			{
				cmd.x1.y = GetNextNum(d, pos);
				cmdList.push_back(cmd);
			}
		}
		else if (cmd.cmd == 'C'  ||  cmd.cmd == 'c')
		{
			while (pos < size  &&  !isalpha((unsigned char)d[pos]))
			{
				cmd.x1.x = GetNextNum(d, pos);
				cmd.x1.y = GetNextNum(d, pos);
				cmd.x2.x = GetNextNum(d, pos);
				cmd.x2.y = GetNextNum(d, pos);
				cmd.x3.x = GetNextNum(d, pos);
				cmd.x3.y = GetNextNum(d, pos);
				cmdList.push_back(cmd);
			}
		}
		else if (cmd.cmd == 'S'  ||  cmd.cmd == 's')
		{
			while (pos < size  &&  !isalpha((unsigned char)d[pos]))
			{
				cmd.x1.x = GetNextNum(d, pos);
				cmd.x1.y = GetNextNum(d, pos);
				cmd.x2.x = GetNextNum(d, pos);
				cmd.x2.y = GetNextNum(d, pos);
				cmdList.push_back(cmd);
			}
		}
		else if (cmd.cmd == 'A'  ||  cmd.cmd == 'a')
		{
			while (pos < size  &&  !isalpha((unsigned char)d[pos]))
			{
				cmd.rx = GetNextNum(d, pos);
				cmd.ry = GetNextNum(d, pos);
				cmd.x_axis_rotation = GetNextNum(d, pos);
				cmd.large_arc_flag = static_cast<bool>(GetNextNum(d, pos));
				cmd.sweep_flag = static_cast<bool>(GetNextNum(d, pos));
				cmd.x1.x = GetNextNum(d, pos);
				cmd.x1.y = GetNextNum(d, pos);
				cmdList.push_back(cmd);
			}
		}
		else if (cmd.cmd == 'Q'  ||  cmd.cmd == 'q')
		{
			while (pos < size  &&  !isalpha((unsigned char)d[pos]))
			{
				cmd.x1.x = GetNextNum(d, pos);
				cmd.x1.y = GetNextNum(d, pos);
				cmd.x2.x = GetNextNum(d, pos);
				cmd.x2.y = GetNextNum(d, pos);
				cmdList.push_back(cmd);
			}
		}
		else if (cmd.cmd == 'T'  ||  cmd.cmd == 't')
		{
			while (pos < size  &&  !isalpha((unsigned char)d[pos]))
			{
				cmd.x1.x = GetNextNum(d, pos);
				cmd.x1.y = GetNextNum(d, pos);
				cmdList.push_back(cmd);
			}
		}
		else if (cmd.cmd == 'Z'  ||  cmd.cmd == 'z')
		{
			cmdList.push_back(cmd);
		}
	}

	// Add object
	if (!cmdList.empty())
	{
		SharedPtr<ISVGDraw> sp(new typename TagTypes::SVGPathTag(cmdList, sa, id.c_str()));
		objs.push_back(sp);
		elms.insert(Element(id, sp));

		// Init of object
		if (pObjInit_  &&  initCallback_)
		{
            SharedPtr<ISVGObject> svgObjPtr(sp.get());
            (pObjInit_->*initCallback_)(svgObjPtr);
		}
	}
}

template <class TagTypes>
void SVGTreeParser<TagTypes>::ParseFilter(xml_tag_t * tag, Elements & elms, ViewBox const & viewBox, int level)
{
	ShapeAttrib sa;
	std::string id;

	sa.viewBox = viewBox;

	ParseShapeAttributes(tag->attributes, sa, id, elms);

	// Parse tag specific attributes
	Values values;
	values["x"] = Value(VT_Measure, "x", false);
	values["x"].value.measureValue = - 0.1f;
	values["x"].value.measureValue.isPercents = true;
	values["y"] = Value(VT_Measure, "y", false);
	values["y"].value.measureValue = - 0.1f;
	values["y"].value.measureValue.isPercents = true;
	values["width"] = Value(VT_Measure, "width", false);
	values["width"].value.measureValue = 1.2f;
	values["width"].value.measureValue.isPercents = true;
	values["height"] = Value(VT_Measure, "height", false);
	values["height"].value.measureValue = 1.2f;
	values["height"].value.measureValue.isPercents = true;

	values["filterUnits"] = Value(VT_UnitType, "filterUnits", false);
	values["filterUnits"].value.unitTypeValue = UT_ObjectBoundingBox;
	values["primitiveUnits"] = Value(VT_UnitType, "primitiveUnits", false);
	values["primitiveUnits"].value.unitTypeValue = UT_UserSpaceOnUse;

	ParseAttributes(tag->attributes, values, sa.viewBox, elms);

	PointD p(values["x"].value.measureValue, values["y"].value.measureValue), 
			wh(values["width"].value.measureValue, values["height"].value.measureValue);

	// Add filter primitives
	tag = tag->children;
	if (tag)
	{
		SharedPtr<ISVGObject> sp(new typename TagTypes::SVGFilterTag(p, wh, values["filterUnits"].value.unitTypeValue,
						values["primitiveUnits"].value.unitTypeValue, viewBox, id.c_str()));
		typename TagTypes::SVGFilterTag * pf = dynamic_cast<typename TagTypes::SVGFilterTag *>(sp.get());

		while (tag)
		{
			if (tag->text)
			{
				if (!strcasecmp("feFlood", tag->text))
				{
					FilterPrimitiveBaseAttrib fpba;

					// Parse base attributes
					ParseFilterPrimitiveBaseAttributes(tag->attributes, fpba, elms, viewBox); 

					// Parse tag specific attributes
					Values values;
					values["flood-color"] = Value(VT_Color, "flood-color", true);
					values["flood-color"].value.colorValue = 0;
					values["flood-opacity"] = Value(VT_Opacity, "flood-opacity", true);
					values["flood-opacity"].value.opacityValue = 255;

					ParseAttributes(tag->attributes, values, sa.viewBox, elms);

					// Add filter primitive
					pf->AddFeFlood(fpba.result, values["flood-color"].value.colorValue, values["flood-opacity"].value.opacityValue, fpba.colorSpace);
				}
				else if (!strcasecmp("feBlend", tag->text))
				{
					FilterPrimitiveBaseAttrib fpba;

					// Parse base attributes
					ParseFilterPrimitiveBaseAttributes(tag->attributes, fpba, elms, viewBox);

					// Parse tag specific attributes
					Values values;
					values["mode"] = Value(VT_BlendingMode, "mode", true);
					values["mode"].value.bmValue = BM_Normal;

					ParseAttributes(tag->attributes, values, sa.viewBox, elms);

					// Add filter primitive
					pf->AddFeBlend(fpba.result, values["mode"].value.bmValue, fpba.in, fpba.in2, fpba.colorSpace);
				}
				else if (!strcasecmp("feGaussianBlur", tag->text))
				{
					FilterPrimitiveBaseAttrib fpba;

					// Parse base attributes
					ParseFilterPrimitiveBaseAttributes(tag->attributes, fpba, elms, viewBox); 

					// Parse tag specific attributes
					Values values;
					values["stdDeviation"] = Value(VT_MeasureVector, "stdDeviation", false);

					ParseAttributes(tag->attributes, values, sa.viewBox, elms);

					if (values["stdDeviation"].value.measureVectorValue.empty())
					{
						values["stdDeviation"].value.measureVectorValue.push_back(0.0f);
					}
					if (values["stdDeviation"].value.measureVectorValue.size() == 1)
					{
						values["stdDeviation"].value.measureVectorValue.push_back(
														*(values["stdDeviation"].value.measureVectorValue.begin()));
					}

					// Add filter primitive
					pf->AddFeGaussianBlur(fpba.result, *(values["stdDeviation"].value.measureVectorValue.begin()), 
						*(++(values["stdDeviation"].value.measureVectorValue.begin())), fpba.in, fpba.colorSpace);
				}
				else if (!strcasecmp("feColorMatrix", tag->text))
				{
					FilterPrimitiveBaseAttrib fpba;
					SVGFilterTagFeColorMatrix::MatrixType matrixType = SVGFilterTagFeColorMatrix::MT_Unknown;
					SVGFilterTagFeColorMatrix::Matrix mtx;

					// Parse base attributes
					ParseFilterPrimitiveBaseAttributes(tag->attributes, fpba, elms, viewBox); 

					// Parse tag specific attributes
					Values values;
					values["type"] = Value(VT_String, "type", false);
					values["values"] = Value(VT_String, "values", false);

					ParseAttributes(tag->attributes, values, sa.viewBox, elms);

					if (TrimBorderSpaces(values["type"].stringValue) == "matrix")
					{
						matrixType = SVGFilterTagFeColorMatrix::MT_Matrix;
					}
				
					std::string val(values["values"].stringValue);
					std::string::size_type pos = 0;
					for (int i = 0; i < 4; ++i)
					{
						for (int j = 0; j < 5; ++j)
						{
							mtx.m[i][j] = GetNextNum(val, pos);
						}
					}

					// Add filter primitive
					pf->AddFeColorMatrix(fpba.result, fpba.in, matrixType, mtx, fpba.colorSpace);
				}
			}

			tag = tag->next_tag;
		}

		// Add object
		elms.insert(Element(id, sp));

		// Init of object
		if (pObjInit_  &&  initCallback_)
		{
            SharedPtr<ISVGObject> svgObjPtr(sp.get());
			(pObjInit_->*initCallback_)(svgObjPtr);
		}
	}
}

template <class TagTypes>
void SVGTreeParser<TagTypes>::ParseMask(xml_tag_t * tag, Elements & elms, ViewBox const & viewBox, int level)
{
	SVGDrawObjects objsGroup;
	ShapeAttrib sa;
	std::string id;

	sa.viewBox = viewBox;

	ParseShapeAttributes(tag->attributes, sa, id, elms);

	// Parse tag specific attributes
	Values values;
	values["x"] = Value(VT_Measure, "x", false);
	values["x"].value.measureValue = - 0.1f;
	values["x"].value.measureValue.isPercents = true;
	values["y"] = Value(VT_Measure, "y", false);
	values["y"].value.measureValue = - 0.1f;
	values["y"].value.measureValue.isPercents = true;
	values["width"] = Value(VT_Measure, "width", false);
	values["width"].value.measureValue = 1.2f;
	values["width"].value.measureValue.isPercents = true;
	values["height"] = Value(VT_Measure, "height", false);
	values["height"].value.measureValue = 1.2f;
	values["height"].value.measureValue.isPercents = true;

	values["maskUnits"] = Value(VT_UnitType, "maskUnits", false);
	values["maskUnits"].value.unitTypeValue = UT_ObjectBoundingBox;
	values["maskContentUnits"] = Value(VT_UnitType, "maskContentUnits", false);
	values["maskContentUnits"].value.unitTypeValue = UT_UserSpaceOnUse;

	ParseAttributes(tag->attributes, values, sa.viewBox, elms);

	PointD p(values["x"].value.measureValue, values["y"].value.measureValue), 
			wh(values["width"].value.measureValue, values["height"].value.measureValue);

	// Parse mask content
	ParseGroup(tag, elms, objsGroup, sa, level + 1, false, false);

	if (!objsGroup.empty())
	{
		// Add filter primitives
		SharedPtr<ISVGObject> sp(new typename TagTypes::SVGMaskTag(p, wh, values["maskUnits"].value.unitTypeValue,
										values["maskContentUnits"].value.unitTypeValue, viewBox, id.c_str()));
		typename TagTypes::SVGMaskTag * pf = dynamic_cast<typename TagTypes::SVGMaskTag *>(sp.get());

		SVGDrawObjects::iterator i = objsGroup.begin();
		for (; i != objsGroup.end(); ++i)
		{
			pf->AddElement(*i);
		}

		// Add object
		elms.insert(Element(id, sp));

		// Init of object
		if (pObjInit_  &&  initCallback_)
		{
            SharedPtr<ISVGObject> svgObjPtr(sp.get());
            (pObjInit_->*initCallback_)(svgObjPtr);
		}
	}
}

template <class TagTypes>
void SVGTreeParser<TagTypes>::ParseClipPath(xml_tag_t * tag, Elements & elms, ViewBox const & viewBox, int level)
{
	SVGDrawObjects objsGroup;
	ShapeAttrib sa;
	std::string id;
	PointD p(viewBox.right, viewBox.bottom), wh(0.0f, 0.0f);

	sa.viewBox = viewBox;
	ParseShapeAttributes(tag->attributes, sa, id, elms);

	// Get clipPath attributes
	Values values;
	values["clipPathUnits"] = Value(VT_UnitType, "clipPathUnits", false);
	values["clipPathUnits"].value.unitTypeValue = UT_UserSpaceOnUse;

	ParseAttributes(tag->attributes, values, sa.viewBox, elms);
	//

	// Parse clip path content
	ParseGroup(tag, elms, objsGroup, sa, level + 1, false, false);

	if (!objsGroup.empty())
	{
		// Get clipPath rect
		p.x = viewBox.left;
		p.y = viewBox.top;
		wh.x = viewBox.right - viewBox.left;
		wh.y = viewBox.bottom - viewBox.top;

		// Add clipPath content objects
		ShapeAttrib saObj;
		SharedPtr<ISVGObject> sp(new typename TagTypes::SVGClipPathTag(p, wh, values["clipPathUnits"].value.unitTypeValue, viewBox, id.c_str()));
		typename TagTypes::SVGClipPathTag * pf = dynamic_cast<typename TagTypes::SVGClipPathTag *>(sp.get());
		for (SVGDrawObjects::iterator i = objsGroup.begin(); i != objsGroup.end(); ++i)
		{
			SharedPtr<ISVGObject> obj((*i)->Clone());
			SharedPtr<ISVGDraw> objDraw(obj);
			objDraw->InheritFrom(sa);

			pf->AddElement(objDraw);
		}

		// Add object
		elms.insert(Element(id, sp));

		// Init of object
		if (pObjInit_  &&  initCallback_)
		{
            SharedPtr<ISVGObject> svgObjPtr(sp.get());
            (pObjInit_->*initCallback_)(svgObjPtr);
		}
	}
}

template <class TagTypes>
void SVGTreeParser<TagTypes>::ParseRadialGradient(xml_tag_t * tag, Elements & elms, ViewBox const & viewBox, int level)
{
	ShapeAttrib sa;
	std::string id;
	PointD f(0.0f, 0.0f);

	sa.viewBox = viewBox;

	ParseShapeAttributes(tag->attributes, sa, id, elms);

	// Parse tag specific attributes
	Values values;
	values["cx"] = Value(VT_Measure, "cx", false);
	values["cx"].value.measureValue = 0.5f;
	values["cx"].value.measureValue.isPercents = true;
	values["cy"] = Value(VT_Measure, "cy", false);
	values["cy"].value.measureValue = 0.5f;
	values["cy"].value.measureValue.isPercents = true;
	values["r"] = Value(VT_Measure, "r", false);
	values["r"].value.measureValue = 0.5f;
	values["r"].value.measureValue.isPercents = true;
	values["fx"] = Value(VT_Measure, "fx", false);
	values["fx"].value.measureValue = 0;
	values["fy"] = Value(VT_Measure, "fy", false);
	values["fy"].value.measureValue = 0;
	values["gradientTransform"] = Value(VT_TransformMatrix, "gradientTransform", false);
	values["gradientTransform"].value.transformMatrixValue.SetIdentity();
	values["xlink:href"] = Value(VT_ObjectReference, "xlink:href", false);
	values["gradientUnits"] = Value(VT_UnitType, "gradientUnits", false);
	values["gradientUnits"].value.unitTypeValue = UT_ObjectBoundingBox;

	ParseAttributes(tag->attributes, values, sa.viewBox, elms);

	if (values["fx"].GetFoundValueType() != VT_Measure)
	{
		f.x = values["cx"].value.measureValue;
	}
	else
	{
		f.x = values["fx"].value.measureValue;
	}

	if (values["fy"].GetFoundValueType() != VT_Measure)
	{
		f.y = values["cy"].value.measureValue;
	}
	else
	{
		f.y = values["fy"].value.measureValue;
	}

	// Add stop points
	tag = tag->children;
	SharedPtr<ISVGObject> sp(new typename TagTypes::SVGRadialGradientTag(values["cx"].value.measureValue, values["cy"].value.measureValue, 
								values["r"].value.measureValue, f, values["gradientUnits"].value.unitTypeValue,
								&(values["gradientTransform"].value.transformMatrixValue), id.c_str()));
	typename TagTypes::SVGRadialGradientTag * pf = dynamic_cast<typename TagTypes::SVGRadialGradientTag *>(sp.get());

	while (tag)
	{
		if (tag->text)
		{
			if (!strcasecmp("stop", tag->text))
			{
				// Parse tag specific attributes
				Values values;
				values["offset"] = Value(VT_Measure, "offset", false);
				values["offset"].value.measureValue = 0.0f;
				values["stop-color"] = Value(VT_Color, "stop-color", true);
				values["stop-color"].value.colorValue = 0;
				values["stop-opacity"] = Value(VT_Opacity, "stop-opacity", true);
				values["stop-opacity"].value.opacityValue = 255;

				ParseAttributes(tag->attributes, values, sa.viewBox, elms);

				// Add stop point
				pf->AddStopPoint(values["offset"].value.measureValue, 
					values["stop-color"].value.colorValue, values["stop-opacity"].value.opacityValue);
			}
		}

		tag = tag->next_tag;
	}

	// Add object
	elms.insert(Element(id, sp));

	// Init of object
	if (pObjInit_  &&  initCallback_)
	{
        SharedPtr<ISVGObject> svgObjPtr(sp.get());
        (pObjInit_->*initCallback_)(svgObjPtr);
	}

	// Inherit stop-points
	StopPoints stopPoints2;
	pf->GetStopPoints(stopPoints2);

	if (stopPoints2.empty()  &&  values["xlink:href"].GetFoundValueType() == VT_ObjectReference  &&  values["xlink:href"].objValue.get())
	{
		typename TagTypes::SVGRadialGradientTag * pf2 = dynamic_cast<typename TagTypes::SVGRadialGradientTag *>(values["xlink:href"].objValue.get());
		if (pf2)
		{
			pf2->GetStopPoints(stopPoints2);
			pf->SetStopPoints(stopPoints2);
		}

		typename TagTypes::SVGLinearGradientTag * pf3 = dynamic_cast<typename TagTypes::SVGLinearGradientTag *>(values["xlink:href"].objValue.get());
		if (pf3)
		{
			pf3->GetStopPoints(stopPoints2);
			pf->SetStopPoints(stopPoints2);
		}
	}
}

template <class TagTypes>
void SVGTreeParser<TagTypes>::ParseLinearGradient(xml_tag_t * tag, Elements & elms, ViewBox const & viewBox, int level)
{
	ShapeAttrib sa;
	std::string id;

	sa.viewBox = viewBox;

	ParseShapeAttributes(tag->attributes, sa, id, elms);

	// Parse tag specific attributes
	Values values;
	values["x1"] = Value(VT_Measure, "x1", false);
	values["x1"].value.measureValue = 0;
	values["x1"].value.measureValue.isPercents = true;
	values["y1"] = Value(VT_Measure, "y1", false);
	values["y1"].value.measureValue = 0;
	values["y1"].value.measureValue.isPercents = true;
	values["x2"] = Value(VT_Measure, "x2", false);
	values["x2"].value.measureValue = 1;
	values["x2"].value.measureValue.isPercents = true;
	values["y2"] = Value(VT_Measure, "y2", false);
	values["y2"].value.measureValue = 0;
	values["y2"].value.measureValue.isPercents = true;
	values["gradientTransform"] = Value(VT_TransformMatrix, "gradientTransform", false);
	values["gradientTransform"].value.transformMatrixValue.SetIdentity();
	values["xlink:href"] = Value(VT_ObjectReference, "xlink:href", false);
	values["gradientUnits"] = Value(VT_UnitType, "gradientUnits", false);
	values["gradientUnits"].value.unitTypeValue = UT_ObjectBoundingBox;
	values["spreadMethod"] = Value(VT_GradientSpreadMethod, "spreadMethod", false);
	values["spreadMethod"].value.spreadMethod = SM_Pad;

	ParseAttributes(tag->attributes, values, sa.viewBox, elms);

	PointD p1(values["x1"].value.measureValue, values["y1"].value.measureValue), 
		p2(values["x2"].value.measureValue, values["y2"].value.measureValue);

	// Add stop points
	tag = tag->children;
	SharedPtr<ISVGObject> sp(new typename TagTypes::SVGLinearGradientTag(p1, p2, values["gradientUnits"].value.unitTypeValue, 
								values["spreadMethod"].value.spreadMethod,
								&(values["gradientTransform"].value.transformMatrixValue), id.c_str()));
	typename TagTypes::SVGLinearGradientTag * pf = dynamic_cast<typename TagTypes::SVGLinearGradientTag *>(sp.get());

	while (tag)
	{
		if (tag->text)
		{
			if (!strcasecmp("stop", tag->text))
			{
				// Parse tag specific attributes
				Values values;
				values["offset"] = Value(VT_Measure, "offset", false);
				values["offset"].value.measureValue = 0.0f;
				values["stop-color"] = Value(VT_Color, "stop-color", true);
				values["stop-color"].value.colorValue = 0;
				values["stop-opacity"] = Value(VT_Opacity, "stop-opacity", true);
				values["stop-opacity"].value.opacityValue = 255;

				ParseAttributes(tag->attributes, values, sa.viewBox, elms);

				// Add stop point
				pf->AddStopPoint(values["offset"].value.measureValue, 
					values["stop-color"].value.colorValue, values["stop-opacity"].value.opacityValue);
			}
		}

		tag = tag->next_tag;
	}

	// Add object
	elms.insert(Element(id, sp));

	// Init of object
	if (pObjInit_  &&  initCallback_)
	{
        SharedPtr<ISVGObject> svgObjPtr(sp.get());
        (pObjInit_->*initCallback_)(svgObjPtr);
	}


	// Inherit stop-points
	StopPoints stopPoints2;
	pf->GetStopPoints(stopPoints2);

	if (stopPoints2.empty()  &&  values["xlink:href"].GetFoundValueType() == VT_ObjectReference  &&  values["xlink:href"].objValue.get())
	{
		typename TagTypes::SVGRadialGradientTag * pf2 = dynamic_cast<typename TagTypes::SVGRadialGradientTag *>(values["xlink:href"].objValue.get());
		if (pf2)
		{
			pf2->GetStopPoints(stopPoints2);
			pf->SetStopPoints(stopPoints2);
		}

		typename TagTypes::SVGLinearGradientTag * pf3 = dynamic_cast<typename TagTypes::SVGLinearGradientTag *>(values["xlink:href"].objValue.get());
		if (pf3)
		{
			pf3->GetStopPoints(stopPoints2);
			pf->SetStopPoints(stopPoints2);
		}
	}
}

template <class TagTypes>
float SVGTreeParser<TagTypes>::GetNextNum(std::string const & d, std::string::size_type & pos)
{
	if (pos >= d.size())
		return 0;

	// Skip spaces
	std::string::size_type size = d.size();
	while (pos < size  &&  isspace((unsigned char)d[pos]))
		++pos;

	// Get coord value
	std::string::size_type start = pos;

	// Skip value sign
	if (pos < size  &&  (d[pos] == '-'  ||  d[pos] == '+'))
	{
		++pos;
	}

	int numDPoints = 0; // zero or one decimal points, ie 0.6.5 should produce 0.6 and 0.5

	// Set position to the next number or space or text symbol
	while (pos < size  &&  d[pos] != ','  &&  d[pos] != '-'  &&  d[pos] != '+' && !isspace((unsigned char)d[pos]) && !isalpha((unsigned char)d[pos]) && d[pos] != '%')
	{
		if (d[pos] == '.' && ++numDPoints > 1)
			break;
		++pos;
	}

	// skip exponential part of number
	if (pos + 1 < size  &&  (d[pos] == 'e'  ||  d[pos] == 'E')  &&  
							(d[pos + 1] == '+'  ||  d[pos + 1] == '-'  ||  isdigit((unsigned char)d[pos + 1])))
	{
		++pos;
		if (d[pos] == '+'  ||  d[pos] == '-')
			++pos;

		while (pos < size  &&  isdigit((unsigned char)d[pos]))
			++pos;
	}

	float res = static_cast<float>(atof(d.substr(start).c_str()));

	// Skip spaces and other base delimeter symbols
	while (pos < size  &&  (isspace((unsigned char)d[pos])  ||  d[pos] == ','))
		++pos;

	return res;
}

template <class TagTypes>
bool SVGTreeParser<TagTypes>::GetMeasureVector(std::string const & d, MeasureFloatVector & v)
{
	v.clear();
	std::string::size_type pos = 0, pos2 = 1;

	while (pos != pos2)
	{
		pos2 = pos;
		MeasureFloat val;
		GetMeasureValue(d, pos, val);
		if (pos != pos2)
		{
			v.push_back(val);
		}
	}

	return true;
}

template <class TagTypes>
bool SVGTreeParser<TagTypes>::GetColor(std::string const & color2, ARGBColor & res)
{
	std::string color = TrimBorderSpaces(color2);

	struct ColorPair
	{
		std::string colorName;
		ARGBColor colorValue;
	};

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

	const int colorsSize = sizeof(colors) / sizeof(ColorPair);

	std::string::size_type pos = 0, size = color.size();

	if (pos < size)
	{
		if (color[pos] == '#')
		{
			if (color.substr(pos + 1).size() == 3)
			{
				// convert color of type FFF to FFFFFF
				color.insert(pos + 3, 1, color[pos + 3]);
				color.insert(pos + 2, 1, color[pos + 2]);
				color.insert(pos + 1, 1, color[pos + 1]);
			}

			std::stringstream ss;
			ss << std::hex << "0x" << color.c_str() + pos + 1;
			ss >> res;
			return true;
		}
		else if (!color.compare(0, 4, "rgb("))
		{
			ARGBColor r, g, b;
			
			std::string::size_type pos = 4;
			MeasureFloat f;
			GetMeasureValue(color, pos, f);
			r = ToABSeg<ARGBColor>((ARGBColor)(f.isPercents) ? (f * 255) : (float)(f), 0, 255);
			GetMeasureValue(color, pos, f);
			g = ToABSeg<ARGBColor>((ARGBColor)(f.isPercents) ? (f * 255) : (float)(f), 0, 255);
			GetMeasureValue(color, pos, f);
			b = ToABSeg<ARGBColor>((ARGBColor)(f.isPercents) ? (f * 255) : (float)(f), 0, 255);

			res = PACK_RGB(r, g, b);
			return true;
		}
		else if (isalpha((unsigned char)color[pos]))
		{
			for (int i = 0; i < colorsSize; ++i)
			{
				if (colors[i].colorName == color)
				{
					res = colors[i].colorValue;
					return true;
				}
			}
		}
	}

	return false;
}

template <class TagTypes>
bool SVGTreeParser<TagTypes>::GetColorSpace(std::string const & colorSpace2, ColorSpace & res)
{
	std::string colorSpace = TrimBorderSpaces(colorSpace2);
	if (colorSpace == "linearRGB")
	{
		res = CS_linearRGB;
		return true;
	}
	else if (colorSpace == "sRGB")
	{
		res = CS_sRGB;
		return true;
	}

	return false;
}

template <class TagTypes>
bool SVGTreeParser<TagTypes>::GetStyleValue(std::string const & style2, std::string const & name, std::string & value)
{
	std::string style = ";" + style2;
	//"flood-color:white;flood-opacity:1"
	std::string::size_type pos = style.find(";" + name + ":"), size = style.size();
	if (pos != std::string::npos)
	{
		pos += name.size() + 2;
		std::string::size_type start = pos;
		while (pos < size  &&  style[pos] != ';')
			++pos;

		if (start < size)
		{
			value.assign(style, start, pos - start);
			return true;
		}
	}

	return false;
}

template <class TagTypes>
bool SVGTreeParser<TagTypes>::GetBlendingMode(std::string const & mode2, BlendingMode & bm)
{
	std::string mode = TrimBorderSpaces(mode2);
	
	if (mode == "normal")
	{
		bm = BM_Normal;
		return true;
	}
	else if (mode == "multiply")
	{
		bm = BM_Multiply;
		return true;
	}
	else if (mode == "screen")
	{
		bm = BM_Screen;
		return true;
	}
	else if (mode == "hardLight")
	{
		bm = BM_HardLight;
		return true;
	}
	else if (mode == "softLight")
	{
		bm = BM_SoftLight;
		return true;
	}
	else if (mode == "luminosity")
	{
		bm = BM_Luminosity;
		return true;
	}
	else if (mode == "darken")
	{
		bm = BM_Darken;
		return true;
	}
	else if (mode == "colorDodge")
	{
		bm = BM_ColorDodge;
		return true;
	}

	return false;
}

template <class TagTypes>
bool SVGTreeParser<TagTypes>::GetFillRule(std::string const & fillRule2, FillRule & res)
{
	std::string fillRule = TrimBorderSpaces(fillRule2);
	if (fillRule == "nonzero")
	{
		res = FR_Nonzero;
		return true;
	}
	else if (fillRule == "evenodd")
	{
		res = FR_Evenodd;
		return true;
	}

	return false;
}

template <class TagTypes>
bool SVGTreeParser<TagTypes>::GetStrokeLinecap(std::string const & strokeLinecap2, StrokeLinecap & res)
{
	std::string strokeLinecap = TrimBorderSpaces(strokeLinecap2);
	if (strokeLinecap == "butt")
	{
		res = SL_Butt;
		return true;
	}
	else if (strokeLinecap == "round")
	{
		res = SL_Round;
		return true;
	}
	else if (strokeLinecap == "square")
	{
		res = SL_Square;
		return true;
	}

	return false;
}

template <class TagTypes>
bool SVGTreeParser<TagTypes>::GetStrokeLineJoin(std::string const & strokeLineJoin2, StrokeLineJoin & res)
{
	std::string strokeLineJoin = TrimBorderSpaces(strokeLineJoin2);
	if (strokeLineJoin == "miter")
	{
		res = SLJ_Miter;
		return true;
	}
	else if (strokeLineJoin == "round")
	{
		res = SLJ_Round;
		return true;
	}
	else if (strokeLineJoin == "bevel")
	{
		res = SLJ_Bevel;
		return true;
	}

	return false;
}

template <class TagTypes>
bool SVGTreeParser<TagTypes>::GetOpacity(std::string const & opacity2, Opacity & res)
{
	std::string opacity = TrimBorderSpaces(opacity2);

	if (opacity.size() > 1  &&  opacity[opacity.size() - 1] == '%')
	{	
		res = static_cast<Opacity>(ToABSeg<float>((float)atof(opacity.c_str()) / 100.0f, 0.0f, 1.0f) * 255);
	}
	else
	{
		res = static_cast<Opacity>(ToABSeg<float>((float)atof(opacity.c_str()), 0.0f, 1.0f) * 255);
	}

	return true;
}

template <class TagTypes>
bool SVGTreeParser<TagTypes>::GetUnitType(std::string const & unitType2, UnitType & res)
{
	std::string unitType = TrimBorderSpaces(unitType2);

	if (unitType == "userSpaceOnUse")
	{
		res = UT_UserSpaceOnUse;
		return true;
	}
	else if (unitType == "objectBoundingBox")
	{
		res = UT_ObjectBoundingBox;
		return true;
	}

	return false;
}

template <class TagTypes>
bool SVGTreeParser<TagTypes>::GetMeasureValue(std::string const & value, std::string::size_type & pos, MeasureFloat & res)
{
	std::string::size_type pos2 = pos;
	res = GetNextNum(value, pos);
	res.isPercents = false;

	if (pos + 1 < value.size())
	{
		std::string unit = value.substr(pos, 2);
		if (unit == "in")
		{
			res = res * 96;
			pos += 2;
		}
		else if (unit == "cm")
		{
			res = res  * 96 / 2.54f;
			pos += 2;
		}
		else if (unit == "mm")
		{
			res = res  * 96 / 25.4f;
			pos += 2;
		}
		else if (unit == "pt")
		{
			res = res  * 96 / 72.0f;
			pos += 2;
		}
		else if (unit == "pc")
		{
			res = res * 12 * 96 / 72.0f;
			pos += 2;
		}
	}

	if (pos < value.size() &&  value[pos] == '%')
	{
		res = res / 100.0f;
		res.isPercents = true;
		++pos;
	}

	return pos != pos2;
}

template <class TagTypes>
bool SVGTreeParser<TagTypes>::GetGradientSpreadMethod(std::string const & spreadMethod2, SpreadMethod & res)
{
	std::string spreadMethod = TrimBorderSpaces(spreadMethod2);

	if (spreadMethod == "pad")
	{
		res = SM_Pad;
		return true;
	}
	else if (spreadMethod == "reflect")
	{
		res = SM_Reflect;
		return true;
	}
	else if (spreadMethod == "repeat")
	{
		res = SM_Repeat;
		return true;
	}

	return false;
}

template <class TagTypes>
bool SVGTreeParser<TagTypes>::SetObject(char const * id, Elements const & elms, SharedPtr<ISVGObject> & obj)
{
	if (!id)
		return false;

	// Get element ID ("ELEMENT_ID" string) from url string ("url(#ELEMENT_ID)")
	std::string ids(id);
	ids = TrimBorderSpaces(ids);
	std::string::size_type pos = 0, len = 0;

	if (!ids.compare(0, 5, "url(#"))
	{
		pos = 5;
		len = ids.size() - 6;
	}
	else if (ids.size() > 1  &&  ids[0] == '#')
	{
		pos = 1;
		len = ids.size() - 1;
	}

	// Get object
	ids = ids.substr(pos, len);
	if (!ids.empty())
	{
		typename Elements::const_iterator found = elms.find(ids);
		if (found != elms.end())
		{
			obj = found->second;
			return true;
		}
	}

	return false;
}

template <class TagTypes>
bool SVGTreeParser<TagTypes>::SetMatrix(std::string const & matrix, TransformMatrixD & tm)
{
	//e.g., matrix == "matrix(0.7721 0 0 0.6066 -24.8807 -40.8226)"
	tm.SetIdentity();
	TransformMatrixD tmTr;
	tmTr.SetIdentity();

	std::string::size_type pos = 0, size = matrix.size(), pos2 = 0;

	while (pos < size)
	{
		TransformMatrixD tmTmp;
		tmTmp.SetIdentity();
		while (pos < size  &&  !isalpha((unsigned int)matrix[pos]))
			++pos;

		if (matrix.substr(pos, 7) == "matrix(")
		{
			pos += 7;
			for (int i = 0; i < 6; ++i)
			{
				tmTmp.m[i] = GetNextNum(matrix, pos);
			}

			tmTr.RightMultiply(tmTmp);
		}
		else if (matrix.substr(pos, 10) == "translate(")
		{
			pos += 10;
			tmTmp.m[4] = GetNextNum(matrix, pos);
			tmTmp.m[5] = GetNextNum(matrix, pos);

			tmTr.RightMultiply(tmTmp);
		}
		else if (matrix.substr(pos, 6) == "scale(")
		{
			pos += 6;
			tmTmp.m[0] = GetNextNum(matrix, pos);
			pos2 = pos;
			tmTmp.m[3] = GetNextNum(matrix, pos);

			if (pos2 == pos)
			{
				tmTmp.m[3] = tmTmp.m[0];
			}

			tmTr.RightMultiply(tmTmp);
		}
		else if (matrix.substr(pos, 7) == "rotate(")
		{
			float alpha = 0, cx = 0, cy = 0;
			pos += 7;

			alpha = GetNextNum(matrix, pos);
			cx = GetNextNum(matrix, pos);
			cy = GetNextNum(matrix, pos);

			tmTmp.SetRotate(alpha, cx, cy);
			tmTr.RightMultiply(tmTmp);
		}
		else if (matrix.substr(pos, 6) == "skewX(")
		{
			pos += 6;
			float alpha = GetNextNum(matrix, pos) * M_PI / 180.0f;
			tmTmp.SetMatrix(1, 0, tan(alpha), 1, 0, 0);
			tmTr.RightMultiply(tmTmp);
		}
		else if (matrix.substr(pos, 6) == "skewY(")
		{
			pos += 6;
			float alpha = GetNextNum(matrix, pos) * M_PI / 180.0f;
			tmTmp.SetMatrix(1, tan(alpha), 0, 1, 0, 0);
			tmTr.RightMultiply(tmTmp);
		}
		else
		{
			++pos;
		}
	}

	tm.LeftMultiply(tmTr);

	return true;
}


template <class TagTypes>
void SVGTreeParser<TagTypes>::Base64Decode(std::string const & input, std::string & output)
{
	const unsigned int rstr[] = {
		0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
		0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
		0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   62,   0,   0,   0,   63, 
		52,  53,  54,  55,  56,  57,  58,  59,  60,  61,   0,   0,   0,   0,   0,   0, 
		0,   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14, 
		15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,   0,   0,   0,   0,   0, 
		0,   26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40, 
		41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,   0,   0,   0,   0,   0,
		//
		0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
		0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
		0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
		0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
		0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
		0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
		0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
		0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0 
	};

	std::string::size_type i = 0;
	std::string::size_type l = input.size();

	output.clear();
	while (i < l)
	{
		while (i < l && isspace((unsigned char)input[i]))
				++i;
		if (i + 3 < l)
		{
			unsigned char b1 = (unsigned char)((rstr[(unsigned char)input[i]] << 2) |
							(rstr[(unsigned char)input[i + 1]] >> 4));
			output += b1;

			if (input[i + 2] != '=')
			{
				unsigned char b2 = (unsigned char)((rstr[(unsigned char)input[i + 1]] << 4) |
									(rstr[(unsigned char)input[i + 2]] >> 2));
				output += b2;
			}
			if (input[i + 3] != '=')
			{
					unsigned char b3 = (unsigned char)((rstr[(unsigned char)input[i + 2]] << 6) |
									rstr[(unsigned char)input[i + 3]]);
					output += b3;
			}
		}

		i += 4;
	}
}

template <class TagTypes>
std::string SVGTreeParser<TagTypes>::TrimBorderSpaces(std::string const & val)
{
	std::string res = val;
	while (!res.empty()  &&  isspace((unsigned char)res[0]))
	{
		res.erase(0, 1);
	}

	while (!res.empty()  &&  isspace((unsigned char)res[res.size() - 1]))
	{
		res.erase(res.size() - 1, 1);
	}

	return res;
}

}
