root/src/tests/kits/interface/picture/SVGViewView.cpp
/*
 * Copyright 2006, Haiku Inc.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Marc Flerackers (mflerackers@androme.be)
 */


#include "SVGViewView.h"

#include <strings.h>


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 } },
        { "grey",                               { 128, 128, 128, 255 } },
        { "green",                              { 0,   128, 0,   255 } },
        { "greenyellow",                { 173, 255, 47,  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 } },
        { NULL          ,                       { 0,   0,         0, 255 } },
};

// Globals ---------------------------------------------------------------------

// Svg2PictureView class -------------------------------------------------------
Svg2PictureView::Svg2PictureView(BRect frame, const char *filename)
    :   BView(frame, "", B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS),
        fFileName(filename),
        fPicture(NULL)
{
        fPicture = new BPicture();
}


Svg2PictureView::~Svg2PictureView()
{
        delete fPicture;
}


void
Svg2PictureView::AttachedToWindow()
{
        BeginPicture(fPicture);

        bool done = false;
        FILE *file = fopen(fFileName.String(), "rb");
        if (file) {
                XML_Parser parser = XML_ParserCreate("UTF-8");
                XML_SetUserData(parser, this);
                XML_SetElementHandler(parser, (XML_StartElementHandler)_StartElement, (XML_EndElementHandler)_EndElement);
                XML_SetCharacterDataHandler(parser, (XML_CharacterDataHandler)_CharacterDataHandler);

                while (!done) {
                        char buf[256];
                        size_t len = fread(buf, 1, sizeof(buf), file);
                        done = len < sizeof(buf);
                        if (!XML_Parse(parser, buf, len, done))
                                break;
                }

                XML_ParserFree(parser);
                fclose(file);
        }
        fPicture = EndPicture();
}


void
Svg2PictureView::Draw(BRect updateRect)
{
        if (fPicture)
                DrawPicture(fPicture);
}


//------------------------------------------------------------------------------
bool Svg2PictureView::HasAttribute(const XML_Char **attributes, const char *name) {
    while (*attributes && strcasecmp(*attributes, name) != 0)
        attributes += 2;

    return (*attributes);
}
//------------------------------------------------------------------------------
float Svg2PictureView::GetFloatAttribute(const XML_Char **attributes, const char *name) {
    while (*attributes && strcasecmp(*attributes, name) != 0)
        attributes += 2;

        if (*attributes)
                return atof(*(attributes + 1));
        else
                return 0;
}
//------------------------------------------------------------------------------
const char *Svg2PictureView::GetStringAttribute(const XML_Char **attributes, const char *name) {
    while (*attributes && strcasecmp(*attributes, name) != 0)
        attributes += 2;

        if (*attributes)
                return *(attributes + 1);
        else
                return NULL;
}
//------------------------------------------------------------------------------
rgb_color Svg2PictureView::GetColorAttribute(const XML_Char **attributes, const char *name, uint8 alpha) {
    const char *attr = GetStringAttribute(attributes, name);

        if (!attr)
                return colors[0].color;

        int red, green, blue;

        if (attr[0] == '#') {
                if (strlen(attr) == 4) {
                        sscanf(attr, "#%1X%1X%1X", &red, &green, &blue);
                        red = (red << 4) + red;
                        green = (green << 4) + green;
                        blue = (blue << 4) + blue;
                }
                else
                        sscanf(attr, "#%2X%2X%2X", &red, &green, &blue);

                rgb_color color;

                color.red = red;
                color.green = green;
                color.blue = blue;
                color.alpha = alpha;

                return color;
        }

        if (sscanf(attr, "rgb(%d, %d, %d)", &red, &green, &blue) == 3) {
                rgb_color color;

                color.red = red;
                color.green = green;
                color.blue = blue;
                color.alpha = alpha;

                return color;
        }

        float redf, greenf, bluef;

        if (sscanf(attr, "rgb(%f%%, %f%%, %f%%)", &redf, &greenf, &bluef) == 3) {
                rgb_color color;

                color.red = (int32)(redf * 2.55f);
                color.green = (int32)(greenf * 2.55f);
                color.blue = (int32)(bluef * 2.55f);
                color.alpha = alpha;

                return color;
        }

        if (strcasecmp(attr, "url")) {
                const char *grad = strchr(attr, '#');

                if (grad) {
                        for (int32 i = 0; i < fGradients.CountItems(); i++) {
                                named_color *item = (named_color*)fGradients.ItemAt(i);

                                if (strstr(grad, item->name)) {
                                        rgb_color color = item->color;
                                        color.alpha = alpha;
                                        return color;
                                }
                        }
                }
        }

        for (int32 i = 0; colors[i].name != NULL; i++)
                if (strcasecmp(colors[i].name, attr) == 0) {
                        rgb_color color = colors[i].color;
                        color.alpha = alpha;
                        return color;
                }

        rgb_color color = colors[0].color;
        color.alpha = alpha;
        return color;
}
//------------------------------------------------------------------------------
void Svg2PictureView::GetPolygonAttribute(const XML_Char **attributes, const char *name, BShape &shape) {
        const char *attr = NULL;

        while (*attributes && strcasecmp(*attributes, name) != 0)
        attributes += 2;

    if (*attributes)
                attr = *(attributes + 1);

        if (!attr)
                return;

        char *ptr = const_cast<char*>(attr);
        BPoint point;
        bool first = true;

        while (*ptr) {
                // Skip white space and ','
                while (*ptr && (*ptr == ' ') || (*ptr == ','))
                        ptr++;

                sscanf(ptr, "%f", &point.x);

                // Skip x
                while (*ptr && *ptr != ',')
                        ptr++;
                if (!*ptr || !*(ptr + 1))
                        break;
                ptr++;

                sscanf(ptr, "%f", &point.y);

                if (first)
                {
                        shape.MoveTo(point);
                        first = false;
                }
                else
                        shape.LineTo(point);

                // Skip y
                while (*ptr && (*ptr != ' ') && (*ptr != ','))
                        ptr++;
        }
}
//------------------------------------------------------------------------------
void Svg2PictureView::GetMatrixAttribute(const XML_Char **attributes, const char *name, BMatrix *matrix) {
        const char *attr = NULL;

        while (*attributes && strcasecmp(*attributes, name) != 0)
        attributes += 2;

    if (*attributes)
                attr = *(attributes + 1);

        if (!attr)
                return;

        char *ptr = (char*)attr;

        while (*ptr) {
                while (*ptr == ' ')
                        ptr++;

                char *transform_name = ptr;

                while (*ptr != '(')
                        ptr++;

                if (strncmp(transform_name, "translate", 9) == 0) {
                        float x, y;

                        if (sscanf(ptr, "(%f %f)", &x, &y) != 2)
                                sscanf(ptr, "(%f,%f)", &x, &y);

                        matrix->Translate(x, y);
                }
                else if (strncmp(transform_name, "rotate", 6) == 0) {
                        float angle;

                        sscanf(ptr, "(%f)", &angle);

                        matrix->Rotate(angle);
                }
                else if (strncmp(transform_name, "scale", 5) == 0) {
                        float sx, sy;

                        if (sscanf(ptr, "(%f,%f)", &sx, &sy) == 2)
                                matrix->Scale(sx, sy);
                        else
                        {
                                sscanf(ptr, "(%f)", &sx);
                                matrix->Scale(sx, sx);
                        }
                }
                else if (strncmp(transform_name, "skewX", 5) == 0) {
                        float angle;

                        sscanf(ptr, "(%f)", &angle);

                        matrix->SkewX(angle);
                }
                else if (strncmp(transform_name, "skewY", 5) == 0) {
                        float angle;

                        sscanf(ptr, "(%f)", &angle);

                        matrix->SkewY(angle);
                }

                while (*ptr != ')')
                        ptr++;

                ptr++;
        }
}
//------------------------------------------------------------------------------
double CalcVectorAngle(double ux, double uy, double vx, double vy) {
        double ta = atan2(uy, ux);
        double tb = atan2(vy, vx);

        if (tb >= ta)
                return tb - ta;

        return 6.28318530718 - (ta - tb);
}
//------------------------------------------------------------------------------
char *SkipFloat(char *string) {
        if (*string == '-')
                string++;

        int32 len = strspn(string, "1234567890.");

        return string + len;
}
//------------------------------------------------------------------------------
char *FindFloat(char *string) {
        return strpbrk(string, "1234567890-.");
}
//------------------------------------------------------------------------------
float GetFloat(char **string) {
        *string = FindFloat(*string);
        float f = atof(*string);
        *string = SkipFloat(*string);
        return f;
}
//------------------------------------------------------------------------------
void Svg2PictureView::GetShapeAttribute(const XML_Char **attributes, const char *name, BShape &shape) {
        const char *attr = GetStringAttribute(attributes, name);

        if (!attr)
                return;

        char *ptr = const_cast<char*>(attr);
        float x, y, x1, y1, x2, y2, rx, ry, angle;
        bool largeArcFlag, sweepFlag;
        char command, prevCommand = 0;
        BPoint prevCtlPt;
        BPoint pos, startPos;
        bool canMove = true;

        while (*ptr) {
                ptr = strpbrk(ptr, "ZzMmLlCcQqAaHhVvSsTt");

                if (ptr == NULL)
                        break;

                command = *ptr;

                switch (command) {
                        case 'Z':
                        case 'z':
                        {
                                pos.Set(startPos.x, startPos.y);
                                canMove = true;
                                shape.Close();
                                ptr++;
                                break;
                        }
                        case 'M':
                        {
                                x = GetFloat(&ptr);
                                y = GetFloat(&ptr);

                                pos.Set(x, y);
                                if (canMove)
                                        startPos = pos;
                                shape.MoveTo(pos);
                                break;
                        }
                        case 'm':
                        {
                                x = GetFloat(&ptr);
                                y = GetFloat(&ptr);

                                pos.x += x;
                                pos.y += y;
                                if (canMove)
                                        startPos = pos;
                                shape.MoveTo(pos);
                                break;
                        }
                        case 'L':
                        {
                                x = GetFloat(&ptr);
                                y = GetFloat(&ptr);

                                pos.Set(x, y);
                                canMove = false;
                                shape.LineTo(pos);
                                break;
                        }
                        case 'l':
                        {
                                x = GetFloat(&ptr);
                                y = GetFloat(&ptr);

                                pos.x += x;
                                pos.y += y;
                                canMove = false;
                                shape.LineTo(pos);
                                break;
                        }
                        case 'C':
                        case 'c':
                        {
                                if (command == 'C') {
                                        x1 = GetFloat(&ptr);
                                        y1 = GetFloat(&ptr);
                                        x2 = GetFloat(&ptr);
                                        y2 = GetFloat(&ptr);
                                        x = GetFloat(&ptr);
                                        y = GetFloat(&ptr);
                                }
                                else {
                                        x1 = GetFloat(&ptr);
                                        y1 = GetFloat(&ptr);
                                        x2 = GetFloat(&ptr);
                                        y2 = GetFloat(&ptr);
                                        x = GetFloat(&ptr);
                                        y = GetFloat(&ptr);

                                        x1 += pos.x;
                                        y1 += pos.y;
                                        x2 += pos.x;
                                        y2 += pos.y;
                                        x += pos.x;
                                        y += pos.y;
                                }

                                BPoint controlPoints[3];

                                controlPoints[0].Set(x1, y1);
                                controlPoints[1].Set(x2, y2);
                                controlPoints[2].Set(x, y);

                                pos.Set(x, y);
                                prevCtlPt = controlPoints[1];
                                canMove = false;
                                shape.BezierTo(controlPoints);
                                break;
                        }
                        case 'Q':
                        case 'q':
                        {
                                if (command == 'Q') {
                                    x1 = GetFloat(&ptr);
                                        y1 = GetFloat(&ptr);
                                        x = GetFloat(&ptr);
                                        y = GetFloat(&ptr);
                                }
                                else {
                                        x1 = GetFloat(&ptr);
                                        y1 = GetFloat(&ptr);
                                        x = GetFloat(&ptr);
                                        y = GetFloat(&ptr);

                                        x1 += pos.x;
                                        y1 += pos.y;
                                        x += pos.x;
                                        y += pos.y;
                                }

                                BPoint controlPoints[3];

                                controlPoints[0].Set(pos.x + 2.0f / 3.0f * (x1 - pos.x),
                                        pos.y + 2.0f / 3.0f * (y1 - pos.y));
                                controlPoints[1].Set(x1 + 1.0f / 3.0f * (x - x1),
                                        y1  + 1.0f / 3.0f * (y - y1));
                                controlPoints[2].Set(x, y);

                                pos.Set(x, y);
                                prevCtlPt.Set(x1, y1);
                                canMove = false;
                                shape.BezierTo(controlPoints);
                                break;
                        }
                        case 'A':
                        case 'a':
                        {
                                x1 = pos.x;
                                y1 = pos.y;

                                if (command == 'A') {
                                    rx = GetFloat(&ptr);
                                        ry = GetFloat(&ptr);
                                        angle = GetFloat(&ptr);
                                        largeArcFlag = GetFloat(&ptr);
                                        sweepFlag = GetFloat(&ptr);
                                        x = GetFloat(&ptr);
                                        y = GetFloat(&ptr);

                                        x2 = x;
                                        y2 = y;
                                }
                                else {
                                        rx = GetFloat(&ptr);
                                        ry = GetFloat(&ptr);
                                        angle = GetFloat(&ptr);
                                        largeArcFlag = GetFloat(&ptr);
                                        sweepFlag = GetFloat(&ptr);
                                        x = GetFloat(&ptr);
                                        y = GetFloat(&ptr);

                                        x2 = x + pos.x;
                                        y2 = y + pos.y;
                                }

                                const double pi = 3.14159265359;
                                const double radPerDeg = pi / 180.0;

                                if (x1 == x2 && y1 == y2)
                                        break;

                                if (rx == 0.0f || ry == 0.0f) {
                                        shape.LineTo(BPoint((float)x2, (float)y2));
                                        break;
                                }

                                if (rx < 0.0)
                                        rx = -rx;

                                if (ry < 0.0)
                                        ry = -ry;

                                double sinPhi = sin(angle * radPerDeg);
                                double cosPhi = cos(angle * radPerDeg);

                                double x1dash =  cosPhi * (x1 - x2) / 2.0 +
                                        sinPhi * (y1 - y2) / 2.0;
                                double y1dash = -sinPhi * (x1 - x2) / 2.0 +
                                        cosPhi * (y1 - y2) / 2.0;

                                double root, numerator = rx * rx * ry * ry - rx * rx * y1dash * y1dash -
                                        ry * ry * x1dash * x1dash;

                                if (numerator < 0.0) {
                                        double s = (float)sqrt(1.0 - numerator / (rx * rx * ry * ry));

                                        rx *= s;
                                        ry *= s;
                                        root = 0.0;
                                }
                                else {
                                        root = (largeArcFlag == sweepFlag ? -1.0 : 1.0) *
                                                sqrt(numerator /
                                                (rx * rx * y1dash * y1dash + ry * ry * x1dash * x1dash));
                                }

                                double cxdash = root * rx * y1dash / ry, cydash = -root * ry * x1dash / rx;

                                double cx = cosPhi * cxdash - sinPhi * cydash + (x1 + x2) / 2.0;
                                double cy = sinPhi * cxdash + cosPhi * cydash + (y1 + y2) / 2.0;

                                double theta1 = CalcVectorAngle(1.0, 0.0, (x1dash - cxdash) / rx,
                                        (y1dash - cydash) / ry ),
                                        dtheta = CalcVectorAngle((x1dash - cxdash) / rx,
                                                (y1dash - cydash) / ry, (-x1dash - cxdash) / rx,
                                                (-y1dash - cydash) / ry);

                                if (!sweepFlag && dtheta > 0)
                                        dtheta -= 2.0 * pi;
                                else if (sweepFlag && dtheta < 0)
                                        dtheta += 2.0 * pi;

                                int segments = (int)ceil (fabs(dtheta / (pi / 2.0)));
                                double delta = dtheta / segments;
                                double t = 8.0/3.0 * sin(delta / 4.0) * sin( delta / 4.0) /
                                        sin(delta / 2.0);

                                BPoint controlPoints[3];

                                for (int n = 0; n < segments; ++n) {
                                        double cosTheta1 = cos(theta1);
                                        double sinTheta1 = sin(theta1);
                                        double theta2 = theta1 + delta;
                                        double cosTheta2 = cos(theta2);
                                        double sinTheta2 = sin(theta2);

                                        double xe = cosPhi * rx * cosTheta2 - sinPhi * ry * sinTheta2 + cx;
                                        double ye = sinPhi * rx * cosTheta2 + cosPhi * ry * sinTheta2 + cy;

                                        double dx1 = t * (-cosPhi * rx * sinTheta1 - sinPhi * ry * cosTheta1);
                                        double dy1 = t * (-sinPhi * rx * sinTheta1 + cosPhi * ry * cosTheta1);

                                        double dxe = t * (cosPhi * rx * sinTheta2 + sinPhi * ry * cosTheta2);
                                        double dye = t * (sinPhi * rx * sinTheta2 - cosPhi * ry * cosTheta2);

                                        controlPoints[0].Set((float)(x1 + dx1), (float)(y1 + dy1));
                                        controlPoints[1].Set((float)(xe + dxe), (float)(ye + dye));
                                        controlPoints[2].Set((float)xe, (float)ye );

                                        shape.BezierTo(controlPoints);

                                        theta1 = theta2;
                                        x1 = (float)xe;
                                        y1 = (float)ye;
                                }

                                pos.Set(x2, y2);
                                break;
                        }
                        case 'H':
                        {
                                x = GetFloat(&ptr);

                                pos.x = x;
                                canMove = false;
                                shape.LineTo(pos);
                                break;
                        }
                        case 'h':
                        {
                                x = GetFloat(&ptr);

                                pos.x += x;
                                canMove = false;
                                shape.LineTo(pos);
                                break;
                        }
                        case 'V':
                        {
                                y = GetFloat(&ptr);

                                pos.y = y;
                                canMove = false;
                                shape.LineTo(pos);
                                break;
                        }
                        case 'v':
                        {
                                y = GetFloat(&ptr);

                                pos.y += y;
                                canMove = false;
                                shape.LineTo(pos);
                                break;
                        }
                        case 'S':
                        case 's':
                        {
                                if (command == 'S') {
                                        x2 = GetFloat(&ptr);
                                        y2 = GetFloat(&ptr);
                                        x = GetFloat(&ptr);
                                        y = GetFloat(&ptr);
                                }
                                else {
                                        x2 = GetFloat(&ptr);
                                        y2 = GetFloat(&ptr);
                                        x = GetFloat(&ptr);
                                        y = GetFloat(&ptr);

                                        x2 += pos.x;
                                        y2 += pos.y;
                                        x += pos.x;
                                        y += pos.y;
                                }

                                if (prevCommand == 'C' || prevCommand == 'c' ||
                                        prevCommand == 'S' || prevCommand == 's') {
                                        x1 = prevCtlPt.x  + 2 * (pos.x - prevCtlPt.x);
                                        y1 = prevCtlPt.y  + 2 * (pos.y - prevCtlPt.y);
                                }
                                else {
                                        x1 = pos.x;
                                        y1 = pos.y;
                                }

                                BPoint controlPoints[3];

                                controlPoints[0].Set(x1, y1);
                                controlPoints[1].Set(x2, y2);
                                controlPoints[2].Set(x, y);

                                pos.Set(x, y);
                                prevCtlPt.Set(x2, y2);
                                canMove = false;
                                shape.BezierTo(controlPoints);
                                break;
                        }
                        case 'T':
                        case 't':
                        {
                                if (command == 'T') {
                                        x = GetFloat(&ptr);
                                        y = GetFloat(&ptr);
                                }
                                else {
                                        x = GetFloat(&ptr);
                                        y = GetFloat(&ptr);

                                        x += pos.x;
                                        y += pos.y;
                                }

                                if (prevCommand == 'Q' || prevCommand == 'q' ||
                                        prevCommand == 'T' || prevCommand == 't') {
                                        x1 = prevCtlPt.x  + 2 * (pos.x - prevCtlPt.x);
                                        y1 = prevCtlPt.y  + 2 * (pos.y - prevCtlPt.y);
                                }
                                else {
                                        x1 = pos.x;
                                        y1 = pos.y;
                                }

                                BPoint controlPoints[3];

                                controlPoints[0].Set(pos.x + 2.0f / 3.0f * (x1 - pos.x),
                                        pos.y + 2.0f / 3.0f * (y1 - pos.y));
                                controlPoints[1].Set(x1 + 1.0f / 3.0f * (x - x1),
                                        y1  + 1.0f / 3.0f * (y - y1));
                                controlPoints[2].Set(x, y);

                                pos.Set(x, y);
                                prevCtlPt.Set(x1, y1);
                                canMove = false;
                                shape.BezierTo(controlPoints);
                                break;
                        }
                }

                prevCommand = command;
        }
}
//------------------------------------------------------------------------------
void Svg2PictureView::CheckAttributes(const XML_Char **attributes) {
        uint8 alpha = fState.fStrokeColor.alpha;

        if (HasAttribute(attributes, "opacity")) {
            float opacity = GetFloatAttribute(attributes, "opacity");
                fState.fStrokeColor.alpha = (uint8)(opacity * alpha);
                fState.fFlags |= STROKE_FLAG;
                fState.fFillColor.alpha = (uint8)(opacity * alpha);
                fState.fFlags |= FILL_FLAG;
        }

        if (HasAttribute(attributes, "color")) {
        fState.fCurrentColor = GetColorAttribute(attributes, "color", fState.fCurrentColor.alpha);
        }

        if (HasAttribute(attributes, "stroke")) {
            const char *stroke = GetStringAttribute(attributes, "stroke");
                if (strcasecmp(stroke, "none") == 0)
                        fState.fStroke = false;
        else if (strcasecmp(stroke, "currentColor") == 0) {
            fState.fStrokeColor = fState.fCurrentColor;
                        fState.fStroke = true;
        }
                else {
                        fState.fStrokeColor = GetColorAttribute(attributes, "stroke", fState.fFillColor.alpha);
                        fState.fStroke = true;
                        SetHighColor(fState.fStrokeColor);
                }
                fState.fFlags |= STROKE_FLAG;
        }

        if (HasAttribute(attributes, "stroke-opacity")) {
                fState.fStrokeColor.alpha = (uint8)(GetFloatAttribute(attributes, "stroke-opacity") * alpha);
                fState.fFlags |= STROKE_FLAG;
        }

        if (HasAttribute(attributes, "fill")) {
            const char *fill = GetStringAttribute(attributes, "fill");
                if (strcasecmp(fill, "none") == 0)
                        fState.fFill = false;
                else if (strcasecmp(fill, "currentColor") == 0) {
                    fState.fFillColor = fState.fCurrentColor;
                        fState.fFill = true;
                }
                else {
                        fState.fFillColor = GetColorAttribute(attributes, "fill", fState.fFillColor.alpha);
                        fState.fFill = true;
                }
                fState.fFlags |= FILL_FLAG;
        }

        if (HasAttribute(attributes, "fill-opacity")) {
                fState.fFillColor.alpha = (uint8)(GetFloatAttribute(attributes, "fill-opacity") * alpha);
                fState.fFlags |= FILL_FLAG;
        }

        if (HasAttribute(attributes, "stroke-width")) {
                fState.fStrokeWidth = GetFloatAttribute(attributes, "stroke-width");
                SetPenSize(fState.fStrokeWidth);
                fState.fFlags |= STROKE_WIDTH_FLAG;
        }

        if (HasAttribute(attributes, "stroke-linecap")) {
            const char *stroke_linecap = GetStringAttribute(attributes, "stroke-linecap");

                if (strcasecmp(stroke_linecap, "but") == 0)
                        fState.fLineCap = B_BUTT_CAP;
                else if (strcasecmp(stroke_linecap, "round") == 0)
                        fState.fLineCap = B_ROUND_CAP;
                else if (strcasecmp(stroke_linecap, "square") == 0)
                        fState.fLineCap = B_SQUARE_CAP;

                SetLineMode(fState.fLineCap, LineJoinMode(), LineMiterLimit());
                fState.fFlags |= LINE_MODE_FLAG;
        }

        if (HasAttribute(attributes, "stroke-linejoin")) {
            const char *stroke_linejoin = GetStringAttribute(attributes, "stroke-linejoin");

                if (strcasecmp(stroke_linejoin, "miter") == 0)
                        fState.fLineJoin = B_MITER_JOIN;
                else if (strcasecmp(stroke_linejoin, "round") == 0)
                        fState.fLineJoin = B_ROUND_JOIN;
                else if (strcasecmp(stroke_linejoin, "bevel") == 0)
                        fState.fLineJoin = B_BEVEL_JOIN;

                SetLineMode(LineCapMode(), fState.fLineJoin, LineMiterLimit());
                fState.fFlags |= LINE_MODE_FLAG;
        }

        if (HasAttribute(attributes, "stroke-miterlimit")) {
                fState.fLineMiterLimit = GetFloatAttribute(attributes, "stroke-miterlimit");
                SetLineMode(LineCapMode(), LineJoinMode(), fState.fLineMiterLimit);
                fState.fFlags |= LINE_MODE_FLAG;
        }

        if (HasAttribute(attributes, "font-size")) {
                fState.fFontSize = GetFloatAttribute(attributes, "font-size");
                SetFontSize(fState.fFontSize);
                fState.fFlags |= FONT_SIZE_FLAG;
        }

        if (HasAttribute(attributes, "transform")) {
                BMatrix matrix;
                GetMatrixAttribute(attributes, "transform", &matrix);
                fState.fMatrix *= matrix;
                fState.fFlags |= MATRIX_FLAG;
        }
}
//------------------------------------------------------------------------------
void Svg2PictureView::StartElement(const XML_Char *name, const XML_Char **attributes) {
    Push();
    CheckAttributes(attributes);

    if (strcasecmp(name, "circle") == 0) {
        BPoint c(GetFloatAttribute(attributes, "cx"), GetFloatAttribute(attributes, "cy"));
        float r = GetFloatAttribute(attributes, "r");

        if (fState.fFill) {
            SetHighColor(fState.fFillColor);
            FillEllipse(c, r, r);
            SetHighColor(fState.fStrokeColor);
        }
        if (fState.fStroke)
            StrokeEllipse(c, r, r);
    }
    else if (strcasecmp(name, "ellipse") == 0) {
        BPoint c(GetFloatAttribute(attributes, "cx"), GetFloatAttribute(attributes, "cy"));
        float rx = GetFloatAttribute(attributes, "rx");
        float ry = GetFloatAttribute(attributes, "ry");

        if (fState.fFill) {
            SetHighColor(fState.fFillColor);
            FillEllipse(c, rx, ry);
            SetHighColor(fState.fStrokeColor);
        }
        if (fState.fStroke)
            StrokeEllipse(c, rx, ry);
    }
    else if (strcasecmp(name, "image") == 0) {
        BPoint topLeft(GetFloatAttribute(attributes, "x"), GetFloatAttribute(attributes, "y"));
        BPoint bottomRight(topLeft.x + GetFloatAttribute(attributes, "width"),
            topLeft.y + GetFloatAttribute(attributes, "height"));

        fState.fMatrix.Transform(&topLeft);
        fState.fMatrix.Transform(&bottomRight);

        const char *href = GetStringAttribute(attributes, "xlink:href");

        if (href) {
            BBitmap *bitmap = BTranslationUtils::GetBitmap(href);

            if (bitmap) {
                DrawBitmap(bitmap, BRect(topLeft, bottomRight));
                delete bitmap;
            }
        }
    }
    else if (strcasecmp(name, "line") == 0){
        BPoint from(GetFloatAttribute(attributes, "x1"), GetFloatAttribute(attributes, "y1"));
        BPoint to(GetFloatAttribute(attributes, "x2"), GetFloatAttribute(attributes, "y2"));

        fState.fMatrix.Transform(&from);
        fState.fMatrix.Transform(&to);

        StrokeLine(from, to);
    }
    else if (strcasecmp(name, "linearGradient") == 0) {
        fGradient = new named_gradient;

        fGradient->name = strdup(GetStringAttribute(attributes, "id"));
        fGradient->color.red = 0;
        fGradient->color.green = 0;
        fGradient->color.blue = 0;
        fGradient->color.alpha = 255;
        fGradient->started = false;
    }
    else if (strcasecmp(name, "path") == 0) {
        BShape shape;
        GetShapeAttribute(attributes, "d", shape);
        fState.fMatrix.Transform(shape);

        if (fState.fFill) {
            SetHighColor(fState.fFillColor);
            FillShape(&shape);
            SetHighColor(fState.fStrokeColor);
        }
        if (fState.fStroke)
            StrokeShape(&shape);
    }
    else if (strcasecmp(name, "polygon") == 0) {
        BShape shape;
        GetPolygonAttribute(attributes, "points", shape);
        shape.Close();
        fState.fMatrix.Transform(shape);

        if (fState.fFill) {
            SetHighColor(fState.fFillColor);
            FillShape(&shape);
            SetHighColor(fState.fStrokeColor);
        }
        if (fState.fStroke)
            StrokeShape(&shape);
    }
    else if (strcasecmp(name, "polyline") == 0) {
        BShape shape;
        GetPolygonAttribute(attributes, "points", shape);
        fState.fMatrix.Transform(shape);

        if (fState.fFill) {
            SetHighColor(fState.fFillColor);
            FillShape(&shape);
            SetHighColor(fState.fStrokeColor);
        }
        if (fState.fStroke)
            StrokeShape(&shape);
    }
    else if (strcasecmp(name, "radialGradient") == 0) {
        fGradient = new named_gradient;

        fGradient->name = strdup(GetStringAttribute(attributes, "id"));
        fGradient->color.red = 0;
        fGradient->color.green = 0;
        fGradient->color.blue = 0;
        fGradient->color.alpha = 255;
        fGradient->started = false;
    }
    else if (strcasecmp(name, "stop") == 0) {
        rgb_color color = GetColorAttribute(attributes, "stop-color", 255);

        if (fGradient) {
            if (fGradient->started) {
                fGradient->color.red = (int8)(((int32)fGradient->color.red + (int32)color.red) / 2);
                fGradient->color.green = (int8)(((int32)fGradient->color.green + (int32)color.green) / 2);
                fGradient->color.blue = (int8)(((int32)fGradient->color.blue + (int32)color.blue) / 2);
            }
            else {
                fGradient->color = color;
                fGradient->started = true;
            }
        }
    }
    else if (strcasecmp(name, "rect") == 0) {
        BPoint points[4];

        points[0].x = points[3].x = GetFloatAttribute(attributes, "x");
        points[0].y= points[1].y = GetFloatAttribute(attributes, "y");
        points[1].x = points[2].x = points[0].x + GetFloatAttribute(attributes, "width");
        points[2].y = points[3].y = points[0].y + GetFloatAttribute(attributes, "height");

        /*const char *_rx = element->Attribute("rx");
        const char *_ry = element->Attribute("ry");

        if (_rx || _ry)
        {
            float rx, ry;

            if (_rx)
            {
                rx = atof(_rx);

                if (_ry)
                    ry = atof(_ry);
                else
                    ry = rx;
            }
            else
                rx = ry = atof(_ry);

            if (fState.fFill)
            {
                SetHighColor(fState.fFillColor);
                FillRoundRect(rect, rx, ry);
                SetHighColor(fState.fStrokeColor);
            }
            if (fState.fStroke)
                StrokeRoundRect(rect, rx, ry);
        }
        else
        {
            if (fState.fFill)
            {
                SetHighColor(fState.fFillColor);
                FillRect(rect);
                SetHighColor(fState.fStrokeColor);
            }
            if (fState.fStroke)
                StrokeRect(rect);
        }*/

        BShape shape;

        shape.MoveTo(points[0]);
        shape.LineTo(points[1]);
        shape.LineTo(points[2]);
        shape.LineTo(points[3]);
        shape.Close();

        fState.fMatrix.Transform(shape);

        if (fState.fFill)
        {
            SetHighColor(fState.fFillColor);
            FillShape(&shape);
            SetHighColor(fState.fStrokeColor);
        }
        if (fState.fStroke)
            StrokeShape(&shape);
    }
    else if (strcasecmp(name, "text") == 0) {
        fTextPosition.Set(GetFloatAttribute(attributes, "x"), GetFloatAttribute(attributes, "y"));
        fState.fMatrix.Transform(&fTextPosition);
    }
}
//------------------------------------------------------------------------------
void Svg2PictureView::EndElement(const XML_Char *name) {
    if (strcasecmp(name, "linearGradient") == 0) {
        if (fGradient)
            fGradients.AddItem(fGradient);
        fGradient = NULL;
    }
    else if (strcasecmp(name, "radialGradient") == 0) {
        if (fGradient)
            fGradients.AddItem(fGradient);
        fGradient = NULL;
    }
    else if (strcasecmp(name, "text") == 0) {
        if (fState.fFill)
        {
            SetHighColor(fState.fFillColor);
            DrawString(fText.String(), fTextPosition);
            SetHighColor(fState.fStrokeColor);
        }
        if (fState.fStroke)
            DrawString(fText.String(), fTextPosition);
        printf("%f, %f\n", fTextPosition.x, fTextPosition.y);
    }

    Pop();
}
//------------------------------------------------------------------------------
void Svg2PictureView::CharacterDataHandler(const XML_Char *s, int len) {
    fText.SetTo(s, len);
}
//------------------------------------------------------------------------------
void Svg2PictureView::Push() {
        _state_ *state = new _state_(fState);

        fStack.AddItem(state);
}
//------------------------------------------------------------------------------
void Svg2PictureView::Pop() {
        if (fStack.CountItems() == 0)
                printf("Unbalanced Push/Pop\n");

        _state_ *state = (_state_*)fStack.LastItem();

        if (fState.fFlags & STROKE_FLAG)
        {
                if (state->fStroke)
                        SetHighColor(state->fStrokeColor);
        }

        if (fState.fFlags & FILL_FLAG)
        {
                if (state->fFill)
                        SetHighColor(state->fFillColor);
        }

        if (fState.fFlags & STROKE_WIDTH_FLAG)
                SetPenSize(state->fStrokeWidth);

        if (fState.fFlags & LINE_MODE_FLAG)
                SetLineMode(state->fLineCap, state->fLineJoin, state->fLineMiterLimit);

        if (fState.fFlags & FONT_SIZE_FLAG)
                SetFontSize(state->fFontSize);

        fState = *state;

        fStack.RemoveItem(state);
        delete state;
}
//------------------------------------------------------------------------------
void Svg2PictureView::_StartElement(Svg2PictureView *view, const XML_Char *name, const XML_Char **attributes) {
    view->StartElement(name, attributes);
}
//------------------------------------------------------------------------------
void Svg2PictureView::_EndElement(Svg2PictureView *view, const XML_Char *name) {
    view->EndElement(name);
}
//------------------------------------------------------------------------------
void Svg2PictureView::_CharacterDataHandler(Svg2PictureView *view, const XML_Char *s, int len) {
    view->CharacterDataHandler(s, len);
}
//------------------------------------------------------------------------------