Beispiel #1
0
        public object Clone()
        {
            var units = new SvgUnitCollection
            {
                StringForEmptyValue = StringForEmptyValue
            };

            units.AddRange(this);
            return(units);
        }
Beispiel #2
0
        /// <summary>
        /// Renders the stroke of the <see cref="SvgVisualElement"/> to the specified <see cref="ISvgRenderer"/>
        /// </summary>
        /// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
        protected internal virtual bool RenderStroke(ISvgRenderer renderer)
        {
            if (Stroke != null && Stroke != SvgPaintServer.None && StrokeWidth > 0f)
            {
                var strokeWidth = StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this);
                using (var brush = Stroke.GetBrush(this, renderer, FixOpacityValue(StrokeOpacity), true))
                {
                    if (brush != null)
                    {
                        var path   = Path(renderer);
                        var bounds = path.GetBounds();
                        if (path.PointCount < 1)
                        {
                            return(false);
                        }
                        if (bounds.Width <= 0f && bounds.Height <= 0f)
                        {
                            switch (StrokeLineCap)
                            {
                            case SvgStrokeLineCap.Round:
                                using (var capPath = new GraphicsPath())
                                {
                                    capPath.AddEllipse(path.PathPoints[0].X - strokeWidth / 2f, path.PathPoints[0].Y - strokeWidth / 2f, strokeWidth, strokeWidth);
                                    renderer.FillPath(brush, capPath);
                                }
                                break;

                            case SvgStrokeLineCap.Square:
                                using (var capPath = new GraphicsPath())
                                {
                                    capPath.AddRectangle(new RectangleF(path.PathPoints[0].X - strokeWidth / 2f, path.PathPoints[0].Y - strokeWidth / 2f, strokeWidth, strokeWidth));
                                    renderer.FillPath(brush, capPath);
                                }
                                break;
                            }
                        }
                        else
                        {
                            using (var pen = new Pen(brush, strokeWidth))
                            {
                                if (StrokeDashArray != null && StrokeDashArray.Count > 0)
                                {
                                    var strokeDashArray = StrokeDashArray;
                                    if (strokeDashArray.Count % 2 != 0)
                                    {
                                        // handle odd dash arrays by repeating them once
                                        strokeDashArray = new SvgUnitCollection();
                                        strokeDashArray.AddRange(StrokeDashArray);
                                        strokeDashArray.AddRange(StrokeDashArray);
                                    }
                                    var dashOffset = StrokeDashOffset;

                                    strokeWidth = Math.Max(strokeWidth, 1f);

                                    /* divide by stroke width - GDI uses stroke width as unit.*/
                                    var dashPattern = strokeDashArray.Select(u => ((u.ToDeviceValue(renderer, UnitRenderingType.Other, this) <= 0f) ? 1f :
                                                                                   u.ToDeviceValue(renderer, UnitRenderingType.Other, this)) / strokeWidth).ToArray();
                                    var length = dashPattern.Length;

                                    if (StrokeLineCap == SvgStrokeLineCap.Round)
                                    {
                                        // to handle round caps, we have to adapt the dash pattern
                                        // by increasing the dash length by the stroke width - GDI draws the rounded
                                        // edge inside the dash line, SVG draws it outside the line
                                        var pattern = new float[length];
                                        var offset  = 1; // the values are already normalized to dash width
                                        for (var i = 0; i < length; i++)
                                        {
                                            pattern[i] = dashPattern[i] + offset;
                                            if (pattern[i] <= 0f)
                                            {
                                                // overlapping caps - remove the gap for simplicity, see #508
                                                if (i < length - 1)
                                                {
                                                    // add the next dash segment to the current one
                                                    dashPattern[i - 1] += dashPattern[i] + dashPattern[i + 1];
                                                    length             -= 2;
                                                    for (var k = i; k < length; k++)
                                                    {
                                                        dashPattern[k] = dashPattern[k + 2];
                                                    }

                                                    // and handle the combined segment again
                                                    i -= 2;
                                                }
                                                else if (i > 2)
                                                {
                                                    // add the last dash segment to the first one
                                                    // this will change the start point, so adapt the offset
                                                    var dashLength = dashPattern[i - 1] + dashPattern[i];
                                                    pattern[0] += dashLength;
                                                    length     -= 2;
                                                    dashOffset += dashLength * strokeWidth;
                                                }
                                                else
                                                {
                                                    // we have only one dash with the gap too small -
                                                    // do not use dash at all
                                                    length = 0;
                                                    break;
                                                }
                                            }
                                            offset *= -1; // increase dash length, decrease spaces
                                        }
                                        if (length > 0)
                                        {
                                            if (length < dashPattern.Length)
                                            {
                                                Array.Resize(ref pattern, length);
                                            }
                                            dashPattern = pattern;
                                            pen.DashCap = DashCap.Round;
                                        }
                                    }

                                    if (length > 0)
                                    {
                                        pen.DashPattern = dashPattern;

                                        if (dashOffset != 0f)
                                        {
                                            pen.DashOffset = ((dashOffset.ToDeviceValue(renderer, UnitRenderingType.Other, this) <= 0f) ? 1f :
                                                              dashOffset.ToDeviceValue(renderer, UnitRenderingType.Other, this)) / strokeWidth;
                                        }
                                    }
                                }
                                switch (StrokeLineJoin)
                                {
                                case SvgStrokeLineJoin.Bevel:
                                    pen.LineJoin = LineJoin.Bevel;
                                    break;

                                case SvgStrokeLineJoin.Round:
                                    pen.LineJoin = LineJoin.Round;
                                    break;

                                default:
                                    pen.LineJoin = LineJoin.Miter;
                                    break;
                                }
                                pen.MiterLimit = StrokeMiterLimit;
                                switch (StrokeLineCap)
                                {
                                case SvgStrokeLineCap.Round:
                                    pen.StartCap = LineCap.Round;
                                    pen.EndCap   = LineCap.Round;
                                    break;

                                case SvgStrokeLineCap.Square:
                                    pen.StartCap = LineCap.Square;
                                    pen.EndCap   = LineCap.Square;
                                    break;
                                }

                                renderer.DrawPath(pen, path);

                                return(true);
                            }
                        }
                    }
                }
            }

            return(false);
        }