/// <summary> /// Get a title for a specified svg element. /// </summary> private static string GetLayerTitle(SvgElement element, int maxLength = 32) { string elementName = element.GetName(); string layerName = null; if (element.ID != null) { layerName = element.ID; } if (string.IsNullOrEmpty(layerName) && element.CustomAttributes != null) { // get custom title attributes. foreach (var titleAttribute in AllowedTitles) { if (element.CustomAttributes.TryGetValue(titleAttribute, out string title)) { if (!string.IsNullOrEmpty(title)) { layerName = title; break; } } } } if (string.IsNullOrEmpty(layerName) && element.Children != null) { // Get child title tag SvgTitle title = element.Children.OfType <SvgTitle>().FirstOrDefault(); if (title != null && !string.IsNullOrEmpty(title.Content)) { layerName = title.Content; } } if (string.IsNullOrEmpty(layerName)) { // Generate more meanfull name for a svg use node. Add reference element name in a case if it's local document. if (element is SvgUse useElement && useElement.ReferencedElement != null && !string.IsNullOrEmpty(useElement.ReferencedElement.OriginalString)) { string str = useElement.ReferencedElement.OriginalString.Trim(); if (str.StartsWith("#")) { layerName = str.Substring(1); } } // Generate more meanfull name for a svg text. else if (element is SvgTextBase text && !string.IsNullOrEmpty(text.Text)) { layerName = text.Text; if (text.Text.Length > maxLength) { layerName = text.Text.Substring(0, maxLength) + "..."; } }
private void Build( SvgGroup workingGroup, IFlameGraphNode node, SvgUnit width, SvgUnit left, int depth, IFlameGraphNode parent) { // Reach the max depth if (depth > _maxDepth) { return; } // Adjust for space between columns. SvgUnit adjustedWidth = width - 1; if (adjustedWidth < 0) { return; } // Adjust height for space between rows SvgUnit top = Height - (depth + 1) * RowHeight; SvgUnit adjustedHeight = RowHeight - 1; Debug.Assert(adjustedHeight > 0); var unitGroup = new SvgGroup(); workingGroup.Children.Add(unitGroup); unitGroup.CustomAttributes.Add("class", "unit_g"); // Add tooltip for full content var title = new SvgTitle() { Content = node.Content }; // onclick="zoom(this)" unitGroup.CustomAttributes.Add("onclick", @"zoom(this)"); // onmouseover="s('sh_xfree (4 samples, 0.13%)')" unitGroup.CustomAttributes.Add("onmouseover", $@"s('{node.Content} ({node.Metric.ToString("0.00")} ms)')"); // onmouseout="c()" unitGroup.CustomAttributes.Add("onmouseout", @"c()"); unitGroup.Children.Add(title); // Create the rectangle: var rect = new SvgRectangle { Fill = new SvgColourServer(_getFrameBackground(node)), X = left, Y = top, CornerRadiusX = 1, CornerRadiusY = 1, Width = adjustedWidth, Height = adjustedHeight, }; unitGroup.Children.Add(rect); var text = new SvgText(GetFitText(node.Content, adjustedWidth)) { Y = { top + RowHeight - 5 }, X = { left + TextMargin }, FontSize = 12, FontWeight = SvgFontWeight.W500, Fill = new SvgColourServer(Color.Black), }; unitGroup.Children.Add(text); // Process children elements int?childrenCount = node.Children?.Count(); if (childrenCount.HasValue) { double metricsSum = node.Children.Sum(item => item.Metric); if (metricsSum == 0) { return; } double metricsRatio = metricsSum / node.Metric; Debug.Assert(metricsRatio <= 1); SvgUnit childrenWidth = (float)(width * metricsRatio); SvgUnit childLeft = left + (float)(width - childrenWidth) / 2; // Center the child foreach (var child in node.Children) { SvgUnit ratioWidth = (float)(child.Metric / metricsSum * childrenWidth); Build(workingGroup, child, ratioWidth, childLeft, depth + 1, node); childLeft += ratioWidth; } } }
void Render(SvgGroup parentGroup, SENode node, float screenX, float screenY, HashSet <String> cssClasses, out float width, out float height) { void AddClass(String cssClassx) { if (cssClasses == null) { return; } if (cssClasses.Contains(cssClassx) == false) { cssClasses.Add(cssClassx); } } //Debug.Assert((this.RenderTestPoint == null) || node.AllText().Contains(RenderTestPoint) == false); height = node.TextLines.Count * this.LineHeight + 2 * this.BorderMargin; width = node.Width / 15 + 2 * this.BorderMargin; AddClass(parentGroup.Class); SvgGroup g = this.doc.AddGroup(parentGroup); g.Class = parentGroup.Class; g.Transform = $"translate({this.ToPx(screenX)} {this.ToPx(screenY)})"; SvgRect square; if (node.HRef != null) { SvgHyperLink l = this.doc.AddHyperLink(g); l.Target = "_top"; l.HRef = node.HRef.ToString(); square = this.doc.AddRect(l); } else { square = this.doc.AddRect(g); } AddClass(node.Class); square.Class = node.Class; square.RX = this.ToPx(this.RectRx); square.RY = this.ToPx(this.RectRy); square.X = "0"; square.Y = "0"; square.Width = this.ToPx(width); square.Height = this.ToPx(height); float textY = this.BorderMargin + 1; foreach (SEText line in node.TextLines) { SvgText t; if (line.HRef != null) { SvgHyperLink l = this.doc.AddHyperLink(g); l.HRef = line.HRef; l.Target = "_top"; if (line.Title != null) { SvgTitle title = this.doc.AddTitle(l); title.Value = line.Title; } t = this.doc.AddText(l); } else { t = this.doc.AddText(g); } t.Class = GetClass(line.Class, node.Class); AddClass(t.Class); t.X = this.ToPx(this.BorderMargin + this.BorderWidth); t.Y = this.ToPx(textY); t.TextAnchor = "left"; t.Value = line.Text; textY += this.LineHeight; } }
void Render(SvgGroup parentGroup, SENode node, float screenX, float screenY, out float width, out float height) { const float CharMod = 0.7f; //Debug.Assert((this.RenderTestPoint == null) || node.AllText().Contains(RenderTestPoint) == false); height = node.TextLines.Count * this.LineHeight + 2 * this.BorderMargin; width = node.Width * CharMod + 2 * this.BorderMargin; SvgGroup g = this.doc.AddGroup(parentGroup); g.Transform = $"translate({this.ToPx(screenX)} {this.ToPx(screenY)})"; SvgRect square; if (node.HRef != null) { SvgHyperLink l = this.doc.AddHyperLink(g); l.Target = "_top"; l.HRef = node.HRef.ToString(); square = this.doc.AddRect(l); } else { square = this.doc.AddRect(g); } square.Stroke = Color.Black; square.StrokeWidth = this.ToPx(this.BorderWidth); square.RX = this.ToPx(this.RectRx); square.RY = this.ToPx(this.RectRy); square.X = "0"; square.Y = "0"; square.Width = this.ToPx(width); square.Height = this.ToPx(height); square.Fill = node.FillColor; float textX = this.BorderMargin; float textY = this.BorderMargin + 1; foreach (SEText line in node.TextLines) { SvgText t; if (line.HRef != null) { SvgHyperLink l = this.doc.AddHyperLink(g); l.HRef = line.HRef; l.Target = "_top"; if (line.Title != null) { SvgTitle title = this.doc.AddTitle(l); title.Value = line.Title; } t = this.doc.AddText(l); } else { t = this.doc.AddText(g); } t.X = this.ToPx(width / 2); t.Y = this.ToPx(textY); t.TextAnchor = "middle"; t.Value = line.Text; textY += this.LineHeight; } }
void ParseSvgElement(SvgElement el) { VectorPath path = null; if (el is SvgPolygon) { SvgPolygon p = (SvgPolygon)el; path = CreatePath(); int len = p.Points.Count; if (len % 2 != 0) { len--; } for (int i = 0; i < len; i += 2) { float x, y; x = p.Points[i].Value / ppmx; y = p.Points[i + 1].Value / ppmy; if (i == 0) { path.MoveTo(x, y); } else { path.LineTo(x, y); } } path.ClosePolygon(); } if (el is SvgPath) { SvgPath p = (SvgPath)el; path = CreatePath(); foreach (SvgElement e in p.Children) { if (e is SvgTitle) { SvgTitle title = (SvgTitle)e; path.Title = title.Content; } } string tag, side; tag = ""; side = ""; string guidStr = ""; path.Side = VectorPathSide.None; if (p.TryGetAttribute("gf-tag", out tag)) { path.Tag = Encoding.UTF8.GetString(Convert.FromBase64String(tag)); } try { if (p.TryGetAttribute("gf-guid", out guidStr)) { path.Guid = new Guid(guidStr); } } catch { } if (p.TryGetAttribute("gf-side", out side)) { if (side == "left") { path.Side = VectorPathSide.Left; } if (side == "right") { path.Side = VectorPathSide.Right; } } if (p.TryGetAttribute("gf-nome-peca", out string nomePeca)) { path.NomePeca = ""; try { path.NomePeca = Encoding.UTF8.GetString(Convert.FromBase64String(nomePeca)); } catch { } } path.ForceAngle = false; string force = ""; if (p.TryGetAttribute("gf-forceAngle", out force)) { bool pf = false; if (bool.TryParse(force, out pf)) { path.ForceAngle = pf; } } float sx, sy, ex, ey; foreach (SvgPathSegment seg in p.PathData) { sx = (seg.Start.X / ppmx); sy = (seg.Start.Y / ppmy); ex = (seg.End.X / ppmx); ey = (seg.End.Y / ppmy); if (seg is SvgLineSegment) { path.LineTo(ex, ey); } else if (seg is SvgCubicCurveSegment) { SvgCubicCurveSegment q = (SvgCubicCurveSegment)seg; path.CurveTo(ex, ey, (q.FirstControlPoint.X / ppmx), (q.FirstControlPoint.Y / ppmy), (q.SecondControlPoint.X / ppmx), (q.SecondControlPoint.Y / ppmy)); } else if (seg is SvgQuadraticCurveSegment) { SvgQuadraticCurveSegment q = (SvgQuadraticCurveSegment)seg; path.QCurveTo(ex, ey, (q.ControlPoint.X / ppmx), (q.ControlPoint.Y / ppmy)); } else if (seg is SvgClosePathSegment) { path.ClosePolygon(); } else if (seg is SvgMoveToSegment) { path.MoveTo(ex, ey); } else { } } } else { } if (path != null) { path.ClosePath(); } foreach (SvgElement n in el.Children) { ParseSvgElement(n); } }