#include "PainterSvg.h" NAMESPACE_UPP Color GetSvgColor(const char *color); String GetValueStringXml(String str, const char* var); Color GetColorXml(String text); class Svg2DTransform { public: Svg2DTransform() {Init();} Svg2DTransform(Svg2DTransform &transf) {*this = transf;} void Init() { transX = transY = 0; angle = 0; scaleX = scaleY = 1; } void Apply(Painter &sw) { sw.End(); sw.Begin(); sw.Translate(transX, transY); sw.Scale(scaleX, scaleY); sw.Rotate(angle); } void Translate(double x, double y) { transX += x; transY += y; } void Scale(double x, double y) { scaleX *= x; scaleY *= y; } void Rotate(double ang) { angle += ang; } Svg2DTransform &operator=(const Svg2DTransform &transf) { if (this == &transf) // Same object? return *this; transX = transf.transX; transY = transf.transY; angle = transf.angle; scaleX = transf.scaleX; scaleY = transf.scaleY; return *this; } private: double transX; double transY; double angle; double scaleX; double scaleY; }; class SvgStyle { public: SvgStyle() {Init();} SvgStyle(SvgStyle &transf) {*this = transf;} void Init() { strokeWidth = 1; strokeFill = Null; strokeColor = Black(); strokeOpacity = 1; } void Get(String str) { String value; Color color; color = GetColorXml(GetValueStringXml(str, "stroke:")); if (!color.IsNullInstance()) strokeColor = color; value = GetValueStringXml(str, "stroke-width:"); if (value != "") strokeWidth = atoi(value); color = GetColorXml(GetValueStringXml(str, "fill:")); if (!color.IsNullInstance()) strokeFill = color; value = GetValueStringXml(str, "opacity:"); if (value != "") strokeOpacity = atof(value); } void Apply(Painter &sw) { sw.Opacity(strokeOpacity); if (!strokeFill.IsNullInstance()) sw.Fill(strokeFill); sw.Stroke(strokeWidth, strokeColor); } SvgStyle &operator=(const SvgStyle &style) { if (this == &style) // Same object? return *this; strokeColor = style.strokeColor; strokeFill = style.strokeFill; strokeOpacity = style.strokeOpacity; strokeWidth = style.strokeWidth; return *this; } public: int strokeWidth; Color strokeFill; Color strokeColor; double strokeOpacity; }; Color GetSvgColor(const char *color) { struct Svg_color { const char *name; int r; int g; int b; } colors[] = { {"aliceblue", 240, 248, 255}, {"antiquewhite", 250, 235, 215}, {"aqua", 0, 255, 255}, {"aquamarine", 127, 255, 212}, {"azure", 240, 255, 255}, {"beige", 245, 245, 220}, {"bisque", 255, 228, 196}, {"black", 0, 0, 0}, {"blanchedalmond", 255, 235, 205}, {"blue", 0, 0, 255}, {"blueviolet", 138, 43, 226}, {"brown", 165, 42, 42}, {"burlywood", 222, 184, 135}, {"cadetblue", 95, 158, 160}, {"chartreuse", 127, 255, 0}, {"chocolate", 210, 105, 30}, {"coral", 255, 127, 80}, {"cornflowerblue", 100, 149, 237}, {"cornsilk", 255, 248, 220}, {"crimson", 237, 164, 61}, {"cyan", 0, 255, 255}, {"darkblue", 0, 0, 139}, {"darkcyan", 0, 139, 139}, {"darkgoldenrod", 184, 134, 11}, {"darkgray", 169, 169, 169}, {"darkgreen", 0, 100, 0}, {"darkkhaki", 189, 183, 107}, {"darkmagenta", 139, 0, 139}, {"darkolivegreen", 85, 107, 47}, {"darkorange", 255, 140, 0}, {"darkorchid", 153, 50, 204}, {"darkred", 139, 0, 0}, {"darksalmon", 233, 150, 122}, {"darkseagreen", 143, 188, 143}, {"darkslateblue", 72, 61, 139}, {"darkslategray", 47, 79, 79}, {"darkturquoise", 0, 206, 209}, {"darkviolet", 148, 0, 211}, {"deeppink", 255, 20, 147}, {"deepskyblue", 0, 191, 255}, {"dimgray", 105, 105, 105}, {"dodgerblue", 30, 144, 255}, {"firebrick", 178, 34, 34}, {"floralwhite", 255, 250, 240}, {"forestgreen", 34, 139, 34}, {"fuchsia", 255, 0, 255}, {"gainsboro", 220, 220, 220}, {"ghostwhite", 248, 248, 255}, {"gold", 255, 215, 0}, {"goldenrod", 218, 165, 32}, {"gray", 128, 128, 128}, {"green", 0, 128, 0}, {"greenyellow", 173, 255, 47}, {"honeydew", 240, 255, 240}, {"hotpink", 255, 105, 180}, {"indianred", 205, 92, 92}, {"indigo", 75, 0, 130}, {"ivory", 255, 255, 240}, {"khaki", 240, 230, 140}, {"lavender", 230, 230, 250}, {"lavenderblush", 255, 240, 245}, {"lawngreen", 124, 252, 0}, {"lemonchiffon", 255, 250, 205}, {"lightblue", 173, 216, 230}, {"lightcoral", 240, 128, 128}, {"lightcyan", 224, 255, 255}, {"lightgoldenrodyellow", 250, 250, 210}, {"lightgrey", 211, 211, 211}, {"lightgreen", 144, 238, 144}, {"lightpink", 255, 182, 193}, {"lightsalmon", 255, 160, 122}, {"lightseagreen", 32, 178, 170}, {"lightskyblue", 135, 206, 250}, {"lightslategray", 119, 136, 153}, {"lightsteelblue", 176, 196, 222}, {"lightyellow", 255, 255, 224}, {"lime", 0, 255, 0}, {"limegreen", 50, 205, 50}, {"linen", 250, 240, 230}, {"magenta", 255, 0, 255}, {"maroon", 128, 0, 0}, {"mediumaquamarine", 102, 205, 170}, {"mediumblue", 0, 0, 205}, {"mediumorchid", 186, 85, 211}, {"mediumpurple", 147, 112, 219}, {"mediumseagreen", 60, 179, 113}, {"mediumslateblue", 123, 104, 238}, {"mediumspringgreen", 0, 250, 154}, {"mediumturquoise", 72, 209, 204}, {"mediumvioletred", 199, 21, 133}, {"midnightblue", 25, 25, 112}, {"mintcream", 245, 255, 250}, {"mistyrose", 255, 228, 225}, {"moccasin", 255, 228, 181}, {"navajowhite", 255, 222, 173}, {"navy", 0, 0, 128}, {"oldlace", 253, 245, 230}, {"olive", 128, 128, 0}, {"olivedrab", 107, 142, 35}, {"orange", 255, 165, 0}, {"orangered", 255, 69, 0}, {"orchid", 218, 112, 214}, {"palegoldenrod", 238, 232, 170}, {"palegreen", 152, 251, 152}, {"paleturquoise", 175, 238, 238}, {"palevioletred", 219, 112, 147}, {"papayawhip", 255, 239, 213}, {"peachpuff", 255, 218, 185}, {"peru", 205, 133, 63}, {"pink", 255, 192, 203}, {"plum", 221, 160, 221}, {"powderblue", 176, 224, 230}, {"purple", 128, 0, 128}, {"red", 255, 0, 0}, {"rosybrown", 188, 143, 143}, {"royalblue", 65, 105, 225}, {"saddlebrown", 139, 69, 19}, {"salmon", 250, 128, 114}, {"sandybrown", 244, 164, 96}, {"seagreen", 46, 139, 87}, {"seashell", 255, 245, 238}, {"sienna", 160, 82, 45}, {"silver", 192, 192, 192}, {"skyblue", 135, 206, 235}, {"slateblue", 106, 90, 205}, {"slategray", 112, 128, 144}, {"snow", 255, 250, 250}, {"springgreen", 0, 255, 127}, {"steelblue", 70, 130, 180}, {"tan", 210, 180, 140}, {"teal", 0, 128, 128}, {"thistle", 216, 191, 216}, {"tomato", 255, 99, 71}, {"turquoise", 64, 224, 208}, {"violet", 238, 130, 238}, {"wheat", 245, 222, 179}, {"white", 255, 255, 255}, {"whitesmoke", 245, 245, 245}, {"yellow", 255, 255, 0}, {"yellowgreen", 154, 205, 50}, {""}}; for (int i = 0; colors[i].name[0] != '\0'; ++i) // Naive algorithm to get the color if (strcmp(color, colors[i].name) == 0) return Color(colors[i].r, colors[i].g, colors[i].b); return Null; } String GetValueStringXml(String str, const char* var) { int pos, endpos; if ((pos = str.Find(var)) >= 0) { pos += strlen(var); endpos = str.Find(';', pos+1); if (endpos == -1) endpos = str.GetCount(); return NormalizeSpaces(str.Mid(pos, endpos-pos)); } else return ""; } Color HtmlToColor(const char *str) { int col; sscanf(str+1, "%x", &col); return Color(col >> 16, (col >> 8) & 255, col & 255); } Color GetColorXml(String text) { text = ToLower(text); if (text == "none" || text.IsEmpty()) return Null; else if (text[0] == '#') return HtmlToColor(text); else return GetSvgColor(text); } Array GetTransformArgs(String str, const char *command) { Array args; int pos, endpos; if ((pos = str.Find(command)) < 0) return args; pos += strlen(command); pos = 1 + str.Find('(', pos); while (true) { if ((endpos = str.Find(',', pos)) < 0) break; double &d = args.Add(); d = atof(str.Mid(pos, endpos-pos)); pos = endpos+1; } if (args.GetCount() < 2) { if ((endpos = str.Find(')', pos)) >= 0) { double &d = args.Add(); d = atof(str.Mid(pos, endpos-pos)); } } return args; } void ApplyTransf(Svg2DTransform &transf, XmlParser &xp, int id) { Array args; args = GetTransformArgs(xp[id], "translate"); if (args.GetCount() == 1) transf.Translate(args[0], args[0]); else if (args.GetCount() == 2) transf.Translate(args[0], args[1]); args = GetTransformArgs(xp[id], "scale"); if (args.GetCount() == 1) transf.Scale(args[0], args[0]); else if (args.GetCount() == 2) transf.Scale(args[0], args[1]); } void SvgPaint_Rect(Painter& sw, XmlParser &xp, Svg2DTransform transf, SvgStyle style) { int x, y, width, height; for (int i = 0; i < xp.GetAttrCount(); ++i) { String attr = xp.GetAttr(i); if (attr == "x") x = atoi(xp[i]); else if (attr == "y") y = atoi(xp[i]); else if (attr == "width") width = atoi(xp[i]); else if (attr == "height") height = atoi(xp[i]); else if (attr == "style") style.Get(xp[i]); else if (attr == "transform") ApplyTransf(transf, xp, i); } transf.Apply(sw); sw.Rectangle(x, y, width, height); style.Apply(sw); } void SvgPaint_Image(Painter& sw, XmlParser &xp, Svg2DTransform transf, SvgStyle style, const char *svgFolder) { int x, y, width, height; String fileName; for (int i = 0; i < xp.GetAttrCount(); ++i) { String attr = xp.GetAttr(i); if (attr == "x") x = atoi(xp[i]); else if (attr == "y") y = atoi(xp[i]); else if (attr == "width") width = atoi(xp[i]); else if (attr == "height") height = atoi(xp[i]); else if (attr == "xlink:href") fileName = xp[i]; else if (attr == "style") style.Get(xp[i]); else if (attr == "transform") ApplyTransf(transf, xp, i); } if (!FileExists(fileName)) fileName = AppendFileName(svgFolder, fileName); transf.Apply(sw); Image img = StreamRaster::LoadFileAny(fileName); if (!IsNull(img)) sw.Rectangle(x, y, width, height).Fill(StreamRaster::LoadFileAny(fileName), x, y, width, 0).Stroke(0, Black()); else { sw.Rectangle(x, y, width, height).Fill(White()).Stroke(1, Black()); int wh = 20; sw.Move(x+5, y+5).Line(x+5+wh, y+5).Line(x+5+wh, y+5+wh).Line(x+5, y+5+wh).Line(x+5, y+5).Stroke(1, Black()); sw.Move(x+5+5, y+5+5).Line(x+5+wh-5, y+5+wh-5).Stroke(3, Red()); sw.Move(x+5+wh-5, y+5+5).Line(x+5+5, y+5+wh-5).Stroke(3, Red()); sw.Move(x, y); } style.Apply(sw); } void SvgPaint_Ellipse(Painter& sw, XmlParser &xp, Svg2DTransform transf, SvgStyle style) { int x, y, width, height; for (int i = 0; i < xp.GetAttrCount(); ++i) { String attr = xp.GetAttr(i); if (attr == "cx") x = atoi(xp[i]); else if (attr == "cy") y = atoi(xp[i]); else if (attr == "rx") width = atoi(xp[i]); else if (attr == "ry") height = atoi(xp[i]); else if (attr == "style") style.Get(xp[i]); else if (attr == "transform") ApplyTransf(transf, xp, i); } transf.Apply(sw); sw.Ellipse(x, y, width, height); style.Apply(sw); } void SvgPaint_Line(Painter& sw, XmlParser &xp, Svg2DTransform transf, SvgStyle style) { int x1, y1, x2, y2; for (int i = 0; i < xp.GetAttrCount(); ++i) { String attr = xp.GetAttr(i); if (attr == "x1") x1 = atoi(xp[i]); else if (attr == "y1") y1 = atoi(xp[i]); else if (attr == "x2") x2 = atoi(xp[i]); else if (attr == "y2") y2 = atoi(xp[i]); else if (attr == "style") style.Get(xp[i]); else if (attr == "transform") ApplyTransf(transf, xp, i); } transf.Apply(sw); sw.Move(x1, y1); sw.Line(x2, y2); style.Apply(sw); } Array GetPolygonPointsXml(String &strpoints) { Array points; int newpos, pos; char separator; if (strpoints.Find(',') >= 0) separator = ','; else separator = ' '; pos = newpos = 0; while (newpos != strpoints.GetCount()) { int x, y; if ((newpos = strpoints.Find(separator, pos)) < 0) break; x = atoi(strpoints.Mid(pos, newpos+1)); pos = newpos+1; if ((newpos = strpoints.Find(' ', pos+1 < strpoints.GetCount()? pos+1: pos)) < 0) // Jump possible spaces newpos = strpoints.GetCount(); y = atoi(strpoints.Mid(pos, newpos+1)); pos = newpos+1; Point &point = points.Add(); point.x = x; point.y = y; } return points; } void SvgPaint_Polygon(Painter& sw, XmlParser &xp, Svg2DTransform transf, SvgStyle style) { Array points; for (int i = 0; i < xp.GetAttrCount(); ++i) { String attr = xp.GetAttr(i); if (attr == "points") { String strpoints; strpoints = NormalizeSpaces(xp[i]); points = GetPolygonPointsXml(strpoints); } else if (attr == "style") style.Get(xp[i]); else if (attr == "transform") ApplyTransf(transf, xp, i); } transf.Apply(sw); sw.Move(points[0].x, points[0].y); for (int i = 1; i < points.GetCount(); ++i) sw.Line(points[i].x, points[i].y); style.Apply(sw); } void SvgPaint_Text(Painter& sw, XmlParser &xp, Svg2DTransform transf, SvgStyle style) { int x, y; String font; int factor = 20; // Factor to get better resolution text Font f(Font::ARIAL, 12); String str = xp.ReadText(); StringBuffer stext; stext = str; int i, j; for (i = j = 0; i < stext.GetCount(); ++i, ++j) { if (stext[i] == '\n') stext[j] = ' '; else if (stext[i] == '\r') --j; else stext[j] = stext[i]; } stext.SetCount(j); String text = stext; for (int i = 0; i < xp.GetAttrCount(); ++i) { String attr = xp.GetAttr(i); if (attr == "x") x = atoi(xp[i]); else if (attr == "y") y = atoi(xp[i]); else if (attr == "style") { style.Get(xp[i]); if (style.strokeFill.IsNullInstance()) style.strokeFill = style.strokeColor; String fontText; fontText = GetValueStringXml(xp[i], "font-family:"); if (fontText.Find("Roman") >= 0) f.Face(Font::ROMAN); else if (fontText.Find("Arial") >= 0) f.Face(Font::ARIAL); else if (fontText.Find("Courier") >= 0) f.Face(Font::COURIER); f.Height(atoi(GetValueStringXml(xp[i], "font-size:"))*factor); if (f.GetHeight() == 0) f.Height(12*factor); } else if (attr == "transform") { Array args; args = GetTransformArgs(xp[i], "translate"); if (!args.IsEmpty()) transf.Translate(args[0], args[1]); args = GetTransformArgs(xp[i], "scale"); if (!args.IsEmpty()) transf.Scale(args[0], args[1]); } } transf.Translate(x, y-f.GetHeight()/factor); transf.Scale(1./factor, 1./factor); transf.Apply(sw); sw.Text(0, 0, text, f); style.Apply(sw); } void SvgPaint_Path(Painter& sw, XmlParser &xp, Svg2DTransform transf, SvgStyle &style) { String path; for (int i = 0; i < xp.GetAttrCount(); ++i) { String attr = xp.GetAttr(i); if (attr == "d") path = xp[i]; else if (attr == "style") style.Get(xp[i]); else if (attr == "transform") ApplyTransf(transf, xp, i); } transf.Apply(sw); if (!path.IsEmpty()) sw.Path(path); style.Apply(sw); } void SvgPaint_G(Painter& sw, XmlParser &xp, Svg2DTransform &transf, SvgStyle &style) { for (int i = 0; i < xp.GetAttrCount(); ++i) { String attr = xp.GetAttr(i); if (attr == "style") style.Get(xp[i]); else if (attr == "transform") ApplyTransf(transf, xp, i); } } void ParseG(Painter& p, XmlParser &xp, Svg2DTransform transf, SvgStyle style, const char *svgFolder) { if(xp.TagE("rect")) SvgPaint_Rect(p, xp, transf, style); else if(xp.TagE("ellipse")) SvgPaint_Ellipse(p, xp, transf, style); else if(xp.TagE("line")) SvgPaint_Line(p, xp, transf, style); else if(xp.TagE("polygon")) SvgPaint_Polygon(p, xp, transf, style); else if(xp.TagE("polyline")) SvgPaint_Polygon(p, xp, transf, style); else if(xp.TagE("path")) SvgPaint_Path(p, xp, transf, style); else if(xp.TagE("image")) SvgPaint_Image(p, xp, transf, style, svgFolder); else if(xp.Tag("text")) { SvgPaint_Text(p, xp, transf, style); xp.PassEnd(); } else if(xp.Tag("g")) { Svg2DTransform transfG(transf); SvgStyle styleG(style); SvgPaint_G(p, xp, transfG, styleG); while(!xp.End()) ParseG(p, xp, transfG, styleG, svgFolder); } else xp.Skip(); } void ParseSVG(Painter& p, const char *svgFile, const char *svgFolder) { XmlParser xp(svgFile); while(!xp.IsTag()) xp.Skip(); xp.PassTag("svg"); Svg2DTransform transf; SvgStyle style; p.Begin(); while(!xp.End()) { transf.Init(); style.Init(); ParseG(p, xp, transf, style, svgFolder); } p.End(); } END_UPP_NAMESPACE