#include "Controls4U.h" #include "DrawingCanvas.h" String GetValueStringXml(String str, const char* var); Color GetColorXml(String text); void ParseG(GraphElemList &elems, XmlParser &xp, Svg2DTransform transf, SvgStyle style, const char *svgFolder); void GraphElemList::Paint(Painter &sw, Svg2DTransform _transf, SvgStyle _style, bool firstLayer) { _transf += transf; _transf.Apply(sw); _style += style; _style.Apply(sw); for (int i = 0; i < elems.GetCount(); ++i) { elems[i].Paint(sw, _transf, _style, false); } if (firstLayer) PaintLimits(sw); } void GraphElemList::Clear() { elems.Clear(); } void SvgStyle::Set(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 SvgStyle::Apply(Painter &sw) { sw.Opacity(strokeOpacity); if (!strokeFill.IsNullInstance()) sw.Fill(strokeFill); sw.Stroke(strokeWidth, strokeColor); } SvgStyle &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; } SvgStyle &SvgStyle::operator+=(const SvgStyle &style) { if (this == &style) // Same object? return *this; if (!IsNull(style.strokeColor)) strokeColor = style.strokeColor; if (!IsNull(style.strokeFill)) strokeFill = style.strokeFill; if (!IsNull(style.strokeOpacity)) strokeOpacity = style.strokeOpacity; if (!IsNull(style.strokeWidth)) strokeWidth = style.strokeWidth; return *this; } 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.IsEmpty()) { if ((endpos = str.Find(')', pos)) >= 0) { double &d = args.Add(); d = atof(str.Mid(pos, endpos-pos)); } } return args; } void SvgGet_Rect(GraphElemList &elems, XmlParser &xp) { RectElem &elem = static_cast(elems.elems.Add(new RectElem())); for (int i = 0; i < xp.GetAttrCount(); ++i) { String attr = xp.GetAttr(i); if (attr == "x") elem.x = atoi(xp[i]); else if (attr == "y") elem.y = atoi(xp[i]); else if (attr == "width") elem.width = atoi(xp[i]); else if (attr == "height") elem.height = atoi(xp[i]); else if (attr == "style") elem.style.Set(xp[i]); else if (attr == "transform") { Array args; args = GetTransformArgs(xp[i], "translate"); if (!args.IsEmpty()) elem.transf.Translate(args[0], args[1]); args = GetTransformArgs(xp[i], "scale"); if (!args.IsEmpty()) elem.transf.Scale(args[0], args[1]); } } elem.SetLimits(); } void SvgGet_Image(GraphElemList &elems, XmlParser &xp, const char *svgFolder) { ImageElem &elem = static_cast(elems.elems.Add(new ImageElem())); for (int i = 0; i < xp.GetAttrCount(); ++i) { String attr = xp.GetAttr(i); if (attr == "x") elem.x = atoi(xp[i]); else if (attr == "y") elem.y = atoi(xp[i]); else if (attr == "width") elem.width = atoi(xp[i]); else if (attr == "height") elem.height = atoi(xp[i]); else if (attr == "xlink:href") { elem.fileName = xp[i]; if (!FileExists(elem.fileName)) elem.fileName = AppendFileName(svgFolder, elem.fileName); } else if (attr == "style") elem.style.Set(xp[i]); else if (attr == "transform") { Array args; args = GetTransformArgs(xp[i], "translate"); if (!args.IsEmpty()) elem.transf.Translate(args[0], args[1]); args = GetTransformArgs(xp[i], "scale"); if (!args.IsEmpty()) elem.transf.Scale(args[0], args[1]); } } elem.SetLimits(); } void SvgGet_Ellipse(GraphElemList &elems, XmlParser &xp) { EllipseElem &elem = static_cast(elems.elems.Add(new EllipseElem())); for (int i = 0; i < xp.GetAttrCount(); ++i) { String attr = xp.GetAttr(i); if (attr == "cx") elem.x = atoi(xp[i]); else if (attr == "cy") elem.y = atoi(xp[i]); else if (attr == "rx") elem.width = atoi(xp[i]); else if (attr == "ry") elem.height = atoi(xp[i]); else if (attr == "style") elem.style.Set(xp[i]); else if (attr == "transform") { Array args; args = GetTransformArgs(xp[i], "translate"); if (!args.IsEmpty()) elem.transf.Translate(args[0], args[1]); args = GetTransformArgs(xp[i], "scale"); if (!args.IsEmpty()) elem.transf.Scale(args[0], args[1]); } } elem.SetLimits(); } void SvgGet_Line(GraphElemList &elems, XmlParser &xp) { LineElem &elem = dynamic_cast(elems.elems.Add(new LineElem())); for (int i = 0; i < xp.GetAttrCount(); ++i) { String attr = xp.GetAttr(i); if (attr == "x1") elem.x1 = atoi(xp[i]); else if (attr == "y1") elem.y1 = atoi(xp[i]); else if (attr == "x2") elem.x2 = atoi(xp[i]); else if (attr == "y2") elem.y2 = atoi(xp[i]); else if (attr == "style") elem.style.Set(xp[i]); else if (attr == "transform") { Array args; args = GetTransformArgs(xp[i], "translate"); if (!args.IsEmpty()) elem.transf.Translate(args[0], args[1]); args = GetTransformArgs(xp[i], "scale"); if (!args.IsEmpty()) elem.transf.Scale(args[0], args[1]); } } elem.SetLimits(); } 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; Pointf &point = points.Add(); point.x = x; point.y = y; } return points; } void SvgGet_Polygon(GraphElemList &elems, XmlParser &xp) { PolygonElem &elem = static_cast(elems.elems.Add(new PolygonElem())); for (int i = 0; i < xp.GetAttrCount(); ++i) { String attr = xp.GetAttr(i); if (attr == "points") { String strpoints; strpoints = NormalizeSpaces(xp[i]); elem.points = GetPolygonPointsXml(strpoints); } else if (attr == "style") elem.style.Set(xp[i]); else if (attr == "transform") { Array args; args = GetTransformArgs(xp[i], "translate"); if (!args.IsEmpty()) elem.transf.Translate(args[0], args[1]); args = GetTransformArgs(xp[i], "scale"); if (!args.IsEmpty()) elem.transf.Scale(args[0], args[1]); } } elem.SetLimits(); } void SvgGet_Title(GraphElemList &elems, XmlParser &xp) { elems.title = xp.ReadText(); } void SvgGet_Text(GraphElemList &elems, XmlParser &xp) { TextElem &elem = static_cast(elems.elems.Add(new TextElem())); elem.text = xp.ReadText(); elem.text.Replace("\n", " "); elem.text.Replace("\r", ""); for (int i = 0; i < xp.GetAttrCount(); ++i) { String attr = xp.GetAttr(i); if (attr == "x") elem.x = atoi(xp[i]); else if (attr == "y") elem.y = atoi(xp[i]); else if (attr == "style") { elem.style.Set(xp[i]); if (elem.style.strokeColor.IsNullInstance()) elem.style.strokeColor = Black(); if (elem.style.strokeFill.IsNullInstance()) elem.style.strokeFill = elem.style.strokeColor; String fontText; fontText = GetValueStringXml(xp[i], "font-family:"); if (fontText.Find("Roman") >= 0) elem.f.Face(Font::ROMAN); else if (fontText.Find("Arial") >= 0) elem.f.Face(Font::ARIAL); else if (fontText.Find("Courier") >= 0) elem.f.Face(Font::COURIER); elem.f.Height(atoi(GetValueStringXml(xp[i], "font-size:"))*elem.factor); if (elem.f.GetHeight() == 0) elem.f.Height(12*elem.factor); } else if (attr == "transform") { Array args; args = GetTransformArgs(xp[i], "translate"); if (!args.IsEmpty()) elem.transf.Translate(args[0], args[1]); args = GetTransformArgs(xp[i], "scale"); if (!args.IsEmpty()) elem.transf.Scale(args[0], args[1]); } } elem.SetLimits(); } void SvgGet_Path(GraphElemList &elems, XmlParser &xp) { PathElem &elem = static_cast(elems.elems.Add(new PathElem())); for (int i = 0; i < xp.GetAttrCount(); ++i) { String attr = xp.GetAttr(i); if (attr == "d") elem.path = xp[i]; else if (attr == "style") elem.style.Set(xp[i]); else if (attr == "transform") { Array args; args = GetTransformArgs(xp[i], "translate"); if (!args.IsEmpty()) elem.transf.Translate(args[0], args[1]); args = GetTransformArgs(xp[i], "scale"); if (!args.IsEmpty()) elem.transf.Scale(args[0], args[1]); } } elem.SetLimits(); } void SvgGet_G(GraphElemList &elems, XmlParser &xp, const char *svgFolder) { GraphElemList &elem = static_cast(elems.elems.Add(new GraphElemList())); for (int i = 0; i < xp.GetAttrCount(); ++i) { String attr = xp.GetAttr(i); if (attr == "style") elem.style.Set(xp[i]); else if (attr == "transform") { Array args; args = GetTransformArgs(xp[i], "translate"); if (!args.IsEmpty()) elem.transf.Translate(args[0], args[1]); args = GetTransformArgs(xp[i], "scale"); if (!args.IsEmpty()) elem.transf.Scale(args[0], args[1]); } } while(!xp.End()) ParseG(elem, xp, elem.transf, elem.style, svgFolder); elem.SetLimits(); } void ParseG(GraphElemList &elems, XmlParser &xp, Svg2DTransform transf, SvgStyle style, const char *svgFolder) { if(xp.TagE("rect")) SvgGet_Rect(elems, xp); else if(xp.TagE("ellipse")) SvgGet_Ellipse(elems, xp); else if(xp.TagE("line")) SvgGet_Line(elems, xp); else if(xp.TagE("polygon")) SvgGet_Polygon(elems, xp); else if(xp.TagE("polyline")) SvgGet_Polygon(elems, xp); else if(xp.TagE("path")) SvgGet_Path(elems, xp); else if(xp.TagE("image")) SvgGet_Image(elems, xp, svgFolder); else if(xp.Tag("text")) { SvgGet_Text(elems, xp); xp.PassEnd(); } else if(xp.Tag("g")) SvgGet_G(elems, xp, svgFolder); else if(xp.Tag("title")) { SvgGet_Title(elems, xp); xp.PassEnd(); } else xp.Skip(); } bool ParseSVG(DrawingCanvas &canvas, const char *svgFile, const char *svgFolder) { XmlParser xp(svgFile); while(!xp.IsTag()) xp.Skip(); xp.PassTag("svg"); Svg2DTransform transf; SvgStyle style; style.SetStrokeFill(Black()).SetStrokeOpacity(1); //p.Begin(); while(!xp.End()) { transf.Init(); style.Init(); ParseG(canvas.elemList, xp, transf, style, svgFolder); } //p.End(); return true; } DrawingCanvas::DrawingCanvas() { transparent = false; translateX = translateY = 0; rotate = 0; scale = 1; quality = MODE_ANTIALIASED; // 1 or 2 MODE_NOAA = 1, MODE_SUBPIXEL = 2, linejoin = LINEJOIN_MITER; linecap = LINECAP_BUTT; transparent = false; opacity = 1; scaleFactor = 1.2; focusMove.focusMoving = false; selectionWindow.isSelected = false; } void DrawingCanvas::Paint(Draw& w) { Size sz = GetSize(); if(transparent) { for(int y = 0; y + 32 < sz.cy; y += 32) for(int x = 0; x + 32 < sz.cx; x += 32) w.DrawRect(x, y, 32, 32, (x ^ y) & 32 ? Color(254, 172, 120) : Color(124, 135, 253)); } ImageBuffer ib(sz); BufferPainter sw(ib, quality); DoPaint(sw); if (selectionWindow.isSelected) { sw.Rectangle(selectionWindow.selected.left, selectionWindow.selected.top, selectionWindow.selected.GetWidth(), selectionWindow.selected.GetHeight()) .Opacity(0.4).Dash("1").Stroke(1, Black()).Fill(LtGray()); //sw.RectPath(selectionWindow.selected).Dash("1").Stroke(0.5, Black()); } w.DrawImage(0, 0, ib); } void DrawingCanvas::DoPaint(Painter& sw) { sw.Translate(translateX, translateY); sw.Rotate(rotate); sw.Scale(scale, scale); sw.Opacity(opacity); sw.LineCap(linecap); sw.LineJoin(linejoin); { PAINTER_TIMING("FILL"); if(transparent) sw.Clear(RGBAZero()); else sw.Clear(White()); } Svg2DTransform trans; SvgStyle style; style.SetStrokeFill(Null).SetStrokeColor(Black()).SetStrokeOpacity(1).SetStrokeWidth(1); sw.Begin(); elemList.Paint(sw, trans, style, true); sw.End(); } bool LoadSvg(DrawingCanvas &canvas, String fileName) { if (!FileExists(fileName)) return false; return ParseSVG(canvas, LoadFileBOM(fileName), GetFileDirectory(fileName)); } void DrawingCanvas::MouseWheel(Point p, int zdelta, dword keyflags) { Size sz = GetSize(); double factor; if(zdelta > 0) factor = scaleFactor; else factor = 1/scaleFactor; scale *= factor; translateX = sz.cx*(1-factor)/2. + translateX*factor; translateY = sz.cy*(1-factor)/2. + translateY*factor; Refresh(); } void DrawingCanvas::MiddleDown(Point p, dword keyflags) { focusMove.lastFocusPoint = p; focusMove.focusMoving = true; } void DrawingCanvas::MiddleUp(Point p, dword keyflags) { focusMove.focusMoving = false; } void DrawingCanvas::MouseLeave() { focusMove.focusMoving = false; } void DrawingCanvas::MouseMove(Point p, dword keyflags) { if (focusMove.focusMoving) { translateX -= focusMove.lastFocusPoint.x - p.x; translateY -= focusMove.lastFocusPoint.y - p.y; focusMove.lastFocusPoint = p; Refresh(); } if (selectionWindow.isSelected) { selectionWindow.selected.right = p.x; selectionWindow.selected.bottom = p.y; Refresh(); } } void DrawingCanvas::LeftDown(Point p, dword keyflags) { selectionWindow.isSelected = true; selectionWindow.selected.left = selectionWindow.selected.right = p.x; selectionWindow.selected.top = selectionWindow.selected.bottom = p.y; } void DrawingCanvas::LeftUp(Point p, dword keyflags) { if (selectionWindow.isSelected) { Refresh(); selectionWindow.isSelected = false; } }