* 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 } },
};
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) {
while (*ptr && (*ptr == ' ') || (*ptr == ','))
ptr++;
sscanf(ptr, "%f", &point.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);
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 *_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);
}