/// <summary> /// Implemented /// </summary> public void DrawLines(Pen pen, PointF[] points) { SvgPolylineElement pl = new SvgPolylineElement(points); pl.Style = new SvgStyle(pen); if (!_transforms.Result.IsIdentity) pl.Transform = new SvgTransformList(_transforms.Top.Clone()); _cur.AddChild(pl); DrawEndAnchors(pen, points[0], points[points.Length-1]); }
/// <summary> /// Implemented /// </summary> public void DrawLines(Pen pen, PointF[] points) { if (points.Length <= 1) return; if (IsEndAnchorSimple(pen.StartCap) && IsEndAnchorSimple(pen.EndCap)) { // This code works, but not for CustomLineCap style SvgPolylineElement pl = new SvgPolylineElement(points); pl.Style = new SvgStyle(pen); if (!_transforms.Result.IsIdentity) pl.Transform = new SvgTransformList(_transforms.Top.Clone()); _cur.AddChild(pl); DrawEndAnchors(pen, points[0], points[points.Length - 1]); return; } // GraphicsPaths used in the constructor of CustomLineCap // are private to the native GDI+ and for example the shape of AdjustableArrowCap // is completely private to the native GDI+ // // So in order to render the possibly any-shaped custom line caps we'll draw the line as GDI metafile and then reverse // engineer the GDI metafile drawing and convert it to corresponding SVG commands // Calculate the bounding rectangle var minX = points[0].X; var maxX = points[0].X; var minY = points[0].Y; var maxY = points[0].Y; for (var i = 1; i < points.Length; i++) { var point = points[i]; minX = Math.Min(minX, point.X); maxX = Math.Max(maxX, point.X); minY = Math.Min(minY, point.Y); maxY = Math.Max(maxY, point.Y); } var bounds = new RectangleF(minX, minY, maxX - minX + 1, maxY - minY + 1); // Make the rectangle 0-based where "zero" represents the original shift var zero = bounds.Location; bounds.Offset(-zero.X, -zero.Y); // Make the original point-path "zero"-based for (var i = 0; i < points.Length; i++) { points[i].X -= zero.X; points[i].Y -= zero.Y; } using (var metafileBuffer = new System.IO.MemoryStream()) { System.Drawing.Imaging.Metafile metafile = null; try { /* For discussion of tricky metafile details see: * - http://nicholas.piasecki.name/blog/2009/06/drawing-o-an-in-memory-metafile-in-c-sharp/ * - http://stackoverflow.com/a/1533053/2626313 */ using (var temporaryBitmap = new Bitmap(1, 1)) { using (var temporaryCanvas = Graphics.FromImage(temporaryBitmap)) { var hdc = temporaryCanvas.GetHdc(); metafile = new Metafile( metafileBuffer, hdc, bounds, MetafileFrameUnit.GdiCompatible, EmfType.EmfOnly); temporaryCanvas.ReleaseHdc(); } } using (var metafileCanvas = Graphics.FromImage(metafile)) { metafileCanvas.DrawLines(pen, points); } } finally { if (metafile != null) metafile.Dispose(); } metafileBuffer.Position = 0; var metafileIsEmpty = true; var parser = new MetafileTools.MetafileParser(); parser.EnumerateMetafile(metafileBuffer, pen.Width, zero, (PointF[] linePoints) => { metafileIsEmpty = false; SvgPolylineElement pl = new SvgPolylineElement(linePoints); pl.Style = new SvgStyle(pen); // Make it pretty pl.Style.Set("stroke-linecap", "round"); if (!_transforms.Result.IsIdentity) pl.Transform = new SvgTransformList(_transforms.Top.Clone()); _cur.AddChild(pl); }, (PointF[] linePoints, Brush fillBrush) => { // TODO: received shapes dont' have the vertex list "normalized" correctly // metafileIsEmpty = false; // FillPolygon(fillBrush, linePoints); }); if (metafileIsEmpty) { // TODO: metafile recording on OpenSUSE Linux with Mono 3.8.0 does not seem to work at all // as the supposed implementation in https://github.com/mono/libgdiplus/blob/master/src/graphics-metafile.c is // full of "TODO". In this case we should take a graceful fallback approach // Restore points array to the original values they had when entered the function for (var i = 0; i < points.Length; i++) { points[i].X += zero.X; points[i].Y += zero.Y; } SvgPolylineElement pl = new SvgPolylineElement(points); pl.Style = new SvgStyle(pen); if (!_transforms.Result.IsIdentity) pl.Transform = new SvgTransformList(_transforms.Top.Clone()); _cur.AddChild(pl); DrawEndAnchors(pen, points[0], points[points.Length - 1], ignoreUnsupportedLineCaps: true); } } }