using System.Drawing; using System.Drawing.Drawing2D; using netDxf; using netDxf.Entities; using Point = System.Drawing.Point; namespace InABox.Dxf { public static class DxfUtils { public static event ProcessError OnProcessError; // Draw a rotated string at a particular position. private static void DrawRotatedTextAt(Graphics gr, float angle, string txt, float x, float y, Font the_font, Brush the_brush) { // Save the graphics state. var state = gr.Save(); gr.ResetTransform(); // Rotate. gr.RotateTransform(angle); // Translate to desired position. Be sure to append // the rotation so it occurs after the rotation. gr.TranslateTransform(x, y, MatrixOrder.Append); // Draw the text at the origin. gr.DrawString(txt, the_font, the_brush, 0, 0); // Restore the graphics state. gr.Restore(state); } private static void CheckPoints(double x, double y, ref double minX, ref double minY, ref double maxX, ref double maxY) { minX = x < minX ? x : minX; minY = y < minY ? y : minY; maxX = x > maxX ? x : maxX; maxY = y > maxY ? y : maxY; } private static float Scale(int value, float offset, double scale, int border) { return (value - offset) * (float)scale + border; } private static float Scale(double value, float offset, double scale, int border) { return ((float)value - offset) * (float)scale + border; } private static PointF VectorToPointF(Vector3 vector, PointF offset, float scale, Point border) { return new PointF(Scale(vector.X, offset.X, scale, border.X), Scale(vector.Y, offset.Y, scale, border.Y)); } private static PointF VectorToPointF(Vector2 vector, PointF offset, int scale, Point border) { return new PointF(Scale(vector.X, offset.X, scale, border.X), Scale(vector.Y, offset.Y, scale, border.Y)); } public delegate void ProcessError(string message); public static Bitmap ProcessImage(Stream stream, string caption = null, bool dimensionmarks = true) { var minX = double.MaxValue; var minY = double.MaxValue; var maxX = double.MinValue; var maxY = double.MinValue; var document = DxfDocument.Load(stream); // Lets Explode all the various bits into their component atoms // (i.e. everything is gonna be a line) List objects = new(); ProcessEntities(document.Entities.All, objects, new Vector3()); if (!objects.Any()) throw new Exception(string.Format("No Data Found in {0}!", caption)); foreach (var line in objects.OfType()) { CheckPoints(line.StartPoint.X, line.StartPoint.Y, ref minX, ref minY, ref maxX, ref maxY); CheckPoints(line.EndPoint.X, line.EndPoint.Y, ref minX, ref minY, ref maxX, ref maxY); } var height = (int)Math.Abs(maxY - minY); var width = (int)Math.Abs(maxX - minX); // Calculate the scaling factor to fit the image within the bounds float ratioX = (float)2048 / height; float ratioY = (float)2048 / width; var scale = Math.Min(ratioX, ratioY); var margin = 100; var offset = new PointF((float)minX, (float)minY); var border = new Point(margin, margin); var _result = new Bitmap((int)(width * scale) + (margin * 2), (int)(height * scale) + (margin * 2)); var _pen = new Pen(new SolidBrush(Color.Black), 3.0F); using (var _graphics = Graphics.FromImage(_result)) { Brush _brush = new SolidBrush(Color.White); _graphics.FillRectangle(_brush, 0, 0, _result.Width, _result.Height); foreach (var line in objects.OfType()) { try { var _start = VectorToPointF(line.StartPoint, offset, scale, border); var _end = VectorToPointF(line.EndPoint, offset, scale, border); _graphics.DrawLine(_pen, _start, _end); } catch (Exception e) { } } foreach (var text in objects.OfType().Where(x=>!string.IsNullOrWhiteSpace(x.Value))) { try { var font = new Font(text.Style.FontFamilyName, 12.0f, (FontStyle)text.Style.FontStyle); var dimensions = _graphics.MeasureString(text.Value, font); var anchor = VectorToPointF(text.Position, offset, scale, border); var textwidth = Scale(text.RectangleWidth, 0, scale, 0); float textRatioX = text.RectangleWidth == 0 ? int.MaxValue : textwidth / dimensions.Width; var textheight = Scale(text.Height, 0, scale, 0); float textRatioY = textheight / dimensions.Height; var textScale = Math.Min(textRatioX, textRatioY); font = new Font(text.Style.FontFamilyName, 12.0f * textScale, (FontStyle)text.Style.FontStyle); var topleft = (text.AttachmentPoint) switch { MTextAttachmentPoint.TopLeft => anchor, MTextAttachmentPoint.TopCenter => new PointF(anchor.X - (dimensions.Width / 2.0f), anchor.Y), MTextAttachmentPoint.TopRight => new PointF(anchor.X - dimensions.Width, anchor.Y), MTextAttachmentPoint.MiddleLeft => new PointF(anchor.X, anchor.Y - (textheight / 2.0f)), MTextAttachmentPoint.MiddleCenter => new PointF(anchor.X - (dimensions.Width / 2.0f), anchor.Y- (textheight / 2.0f)), MTextAttachmentPoint.MiddleRight => new PointF(anchor.X - dimensions.Width, anchor.Y- (textheight / 2.0f)), MTextAttachmentPoint.BottomLeft => anchor, MTextAttachmentPoint.BottomCenter => new PointF(anchor.X - (dimensions.Width / 2.0f), anchor.Y - textheight), MTextAttachmentPoint.BottomRight => new PointF(anchor.X - dimensions.Width, anchor.Y - textheight), }; //var rectangle = new RectangleF(topleft, new SizeF(dimensions.Width, textheight)); //_graphics.FillRectangle(new SolidBrush(Color.Yellow), rectangle); _graphics.DrawString(text.Value, font, new SolidBrush(Color.Navy),topleft); } catch (Exception e) { } } try { var font = new Font("Arial", 20.0F); var brush = new SolidBrush(Color.Blue); _pen = new Pen(brush); if (!string.IsNullOrWhiteSpace(caption)) { var crect = _graphics.MeasureString(caption, font); _graphics.DrawString(caption, font, brush, new PointF(_result.Width / 2.0F - crect.Width / 2.0F, 10)); } if (dimensionmarks) { var heightrect = _graphics.MeasureString(height.ToString(), font); _graphics.DrawLine(_pen, new PointF(_result.Width - (heightrect.Height + 10.0F), 100), new PointF(_result.Width - 30, 100)); _graphics.DrawLine(_pen, new PointF(_result.Width - (heightrect.Height + 10.0F), _result.Height - 100), new PointF(_result.Width - 30, _result.Height - 100)); _graphics.DrawLine(_pen, new PointF(_result.Width - (20.0F + heightrect.Height / 2.0F), 105), new PointF(_result.Width - (20.0F + heightrect.Height / 2.0F), _result.Height - 105)); var aX = _result.Width - (20.0F + heightrect.Height / 2.0F); float aY = 105; _graphics.FillPolygon(brush, new[] { new(aX, aY), new PointF(aX - 10.0F, aY + 10.0F), new PointF(aX + 10.0F, aY + 10.0F) }); aY = _result.Height - 105; _graphics.FillPolygon(brush, new[] { new(aX, aY), new PointF(aX - 10.0F, aY - 10.0F), new PointF(aX + 10.0F, aY - 10.0F) }); _graphics.FillRectangle(_brush, _result.Width - (heightrect.Height + 15.0F), _result.Height / 2.0F - heightrect.Width / 2.0F, heightrect.Height, heightrect.Width); DrawRotatedTextAt(_graphics, 270.0F, height.ToString(), _result.Width - (heightrect.Height + 15.0F), _result.Height / 2.0F + heightrect.Width / 2.0F, font, brush); var widthrect = _graphics.MeasureString(width.ToString(), font); _graphics.DrawLine(_pen, new PointF(100, _result.Height - (widthrect.Height + 10.0F)), new PointF(100, _result.Height - 30)); _graphics.DrawLine(_pen, new PointF(_result.Width - 100, _result.Height - (widthrect.Height + 10.0F)), new PointF(_result.Width - 100, _result.Height - 30)); _graphics.DrawLine(_pen, new PointF(105, _result.Height - (20.0F + widthrect.Height / 2.0F)), new PointF(_result.Width - 105, _result.Height - (20.0F + widthrect.Height / 2.0F))); aX = 105; aY = _result.Height - (20.0F + widthrect.Height / 2.0F); _graphics.FillPolygon(brush, new[] { new(aX, aY), new PointF(aX + 10.0F, aY - 10.0F), new PointF(aX + 10.0F, aY + 10.0F) }); aX = _result.Width - 105; _graphics.FillPolygon(brush, new[] { new(aX, aY), new PointF(aX - 10.0F, aY - 10.0F), new PointF(aX - 10.0F, aY + 10.0F) }); _graphics.FillRectangle(_brush, _result.Width / 2.0F - widthrect.Width / 2.0F, _result.Height - (widthrect.Height + 15.0F), widthrect.Width, widthrect.Height); _graphics.DrawString(width.ToString(), font, brush, new PointF(_result.Width / 2.0F - widthrect.Width / 2.0F, _result.Height - (widthrect.Height + 15.0F))); } } catch (Exception e) { } } return _result; } private static void ProcessEntities(IEnumerable entities, List results, Vector3 offset) { foreach (var entity in entities) { try { if (entity is Line l) AddLine(l, results, offset); else if (entity is Insert i) InsertToLines(i, results, offset); else if (entity is Polyline2D p2d) PolylineToLines(p2d, results, offset); else if (entity is Arc a) ArcToLines(a, results, offset); else if (entity is Ellipse e) EllipseToLines(e, results, offset); else if (entity is MText t) AddText(t, results, offset); else if (entity is Dimension d) DimensionToLines(d, results, offset); else { } } catch (Exception e) { } } } private static void DimensionToLines(Dimension dimension, List results, Vector3 offset) { //dimension. ProcessEntities(dimension.Block.Entities, results, offset); } private static void AddText(MText text, List results, Vector3 offset) { text.Position += offset; results.Add(text); } private static void AddLine(Line line, List results, Vector3 offset) { line.StartPoint += offset; line.EndPoint += offset; results.Add(line); } private static void EllipseToLines(Ellipse ellipse, List results, Vector3 offset) { var precision = Math.Max(2, (int)((ellipse.MajorAxis + ellipse.MinorAxis) * 2.0F * Math.PI)); var polyline = ellipse.ToPolyline2D(precision); var segments = polyline.Explode(); ProcessEntities(segments, results, offset); } private static void InsertToLines(Insert insert, List results, Vector3 offset) { ProcessEntities(insert.Block.Entities, results, insert.Position += offset); } private static void PolylineToLines(Polyline2D polyline, List results, Vector3 offset) { var components = polyline.Explode(); ProcessEntities(components, results, offset); } private static void ArcToLines(Arc arc, List results, Vector3 offset) { var precision = Math.Max(2, (int)(arc.Radius * 2.0F * Math.PI)); var polyline = arc.ToPolyline2D(precision); var segments = polyline.Explode(); ProcessEntities(segments, results, offset); } public static Bitmap? DXFToBitmap(string filename) { using var stream = new FileStream(filename, FileMode.Open, FileAccess.Read); try { return ProcessImage(stream, Path.GetFileNameWithoutExtension(filename)); } catch (Exception e) { OnProcessError?.Invoke(e.Message); return null; } } } }