diff --git a/OpenNest.IO/CadImportResult.cs b/OpenNest.IO/CadImportResult.cs
index 9b0cf39..f3d7555 100644
--- a/OpenNest.IO/CadImportResult.cs
+++ b/OpenNest.IO/CadImportResult.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using ACadSharp;
using OpenNest.Bending;
using OpenNest.Geometry;
@@ -38,5 +39,11 @@ namespace OpenNest.IO
/// Default drawing name (filename without extension, unless overridden).
///
public string Name { get; set; }
+
+ ///
+ /// The raw CAD document from the source file. Available for callers
+ /// that need access to non-geometry entities (e.g., text annotations).
+ ///
+ public CadDocument Document { get; set; }
}
}
diff --git a/OpenNest.IO/CadImporter.cs b/OpenNest.IO/CadImporter.cs
index 921ab1b..6afcea2 100644
--- a/OpenNest.IO/CadImporter.cs
+++ b/OpenNest.IO/CadImporter.cs
@@ -47,6 +47,7 @@ namespace OpenNest.IO
Bounds = dxf.Entities.GetBoundingBox(),
SourcePath = path,
Name = options.Name ?? Path.GetFileNameWithoutExtension(path),
+ Document = dxf.Document,
};
}
diff --git a/OpenNest/Controls/CadText.cs b/OpenNest/Controls/CadText.cs
new file mode 100644
index 0000000..d2d5174
--- /dev/null
+++ b/OpenNest/Controls/CadText.cs
@@ -0,0 +1,16 @@
+using System.Drawing;
+using OpenNest.Geometry;
+
+namespace OpenNest.Controls
+{
+ public class CadText
+ {
+ public Vector Position { get; set; }
+ public string Value { get; set; }
+ public double Height { get; set; }
+ public double Rotation { get; set; }
+ public string LayerName { get; set; }
+ public StringAlignment HAlign { get; set; }
+ public StringAlignment VAlign { get; set; }
+ }
+}
diff --git a/OpenNest/Controls/EntityView.cs b/OpenNest/Controls/EntityView.cs
index 71593da..53f62d4 100644
--- a/OpenNest/Controls/EntityView.cs
+++ b/OpenNest/Controls/EntityView.cs
@@ -29,12 +29,14 @@ namespace OpenNest.Controls
public List SimplifierToleranceRight { get; set; }
public List OriginalEntities { get; set; }
public bool ShowEntityLabels { get; set; }
+ public List Texts { get; set; } = new List();
private readonly Pen gridPen = new Pen(Color.FromArgb(70, 70, 70));
private readonly Dictionary penCache = new Dictionary();
private readonly Font labelFont = new Font("Segoe UI", 7f);
private readonly SolidBrush labelBrush = new SolidBrush(Color.FromArgb(220, 255, 255, 200));
private readonly SolidBrush labelBackBrush = new SolidBrush(Color.FromArgb(33, 40, 48));
+ private readonly SolidBrush textBrush = new SolidBrush(Color.FromArgb(180, 200, 200, 200));
public event EventHandler LinePicked;
public event EventHandler PickCancelled;
@@ -116,6 +118,8 @@ namespace OpenNest.Controls
DrawEntity(e.Graphics, entity, pen);
}
+ DrawTexts(e.Graphics);
+
if (ShowEntityLabels)
DrawEntityLabels(e.Graphics);
@@ -408,6 +412,7 @@ namespace OpenNest.Controls
labelFont.Dispose();
labelBrush.Dispose();
labelBackBrush.Dispose();
+ textBrush.Dispose();
}
base.Dispose(disposing);
}
@@ -474,6 +479,34 @@ namespace OpenNest.Controls
diameter);
}
+ private void DrawTexts(Graphics g)
+ {
+ if (Texts == null || Texts.Count == 0)
+ return;
+
+ using var sf = new StringFormat();
+
+ foreach (var text in Texts)
+ {
+ var pos = PointWorldToGraph(text.Position);
+ var fontSize = LengthWorldToGui(text.Height);
+ if (fontSize < 2f) continue;
+
+ var state = g.Save();
+ g.TranslateTransform(pos.X, pos.Y);
+
+ if (text.Rotation != 0)
+ g.RotateTransform((float)OpenNest.Math.Angle.ToDegrees(text.Rotation));
+
+ sf.Alignment = text.HAlign;
+ sf.LineAlignment = text.VAlign;
+
+ using var font = new Font("Segoe UI", fontSize, GraphicsUnit.Pixel);
+ g.DrawString(text.Value, font, textBrush, 0, 0, sf);
+ g.Restore(state);
+ }
+ }
+
private void DrawPoint(Graphics g, Vector pt, Pen pen)
{
var pt1 = PointWorldToGraph(pt);
diff --git a/OpenNest/Controls/FileListControl.cs b/OpenNest/Controls/FileListControl.cs
index 28f7df0..49e9d9c 100644
--- a/OpenNest/Controls/FileListControl.cs
+++ b/OpenNest/Controls/FileListControl.cs
@@ -22,6 +22,7 @@ namespace OpenNest.Controls
public HashSet SuppressedEntityIds { get; set; }
public Box Bounds { get; set; }
public int EntityCount { get; set; }
+ public List Texts { get; set; } = new();
}
public class FileListControl : Control
diff --git a/OpenNest/Forms/CadConverterForm.cs b/OpenNest/Forms/CadConverterForm.cs
index b069363..33a9e90 100644
--- a/OpenNest/Forms/CadConverterForm.cs
+++ b/OpenNest/Forms/CadConverterForm.cs
@@ -92,7 +92,8 @@ namespace OpenNest.Forms
Customer = string.Empty,
Bends = result.Bends,
Bounds = result.Bounds,
- EntityCount = result.Entities.Count
+ EntityCount = result.Entities.Count,
+ Texts = ExtractTexts(result.Document),
};
if (InvokeRequired)
@@ -152,6 +153,7 @@ namespace OpenNest.Forms
entityView1.Entities.Clear();
entityView1.Entities.AddRange(item.Entities);
entityView1.Bends = item.Bends ?? new List();
+ entityView1.Texts = item.Texts ?? new List();
item.Entities.ForEach(e => e.IsVisible = true);
if (item.Entities.Any(e => e.Layer != null))
@@ -804,6 +806,102 @@ namespace OpenNest.Forms
#endregion
+ private static List ExtractTexts(ACadSharp.CadDocument doc)
+ {
+ var texts = new List();
+ if (doc == null) return texts;
+
+ foreach (var entity in doc.Entities)
+ {
+ switch (entity)
+ {
+ case ACadSharp.Entities.MText mtext:
+ var (mh, mv) = MapAttachmentPoint(mtext.AttachmentPoint);
+ texts.Add(new CadText
+ {
+ Position = new Vector(mtext.InsertPoint.X, mtext.InsertPoint.Y),
+ Value = ReplaceControlCodes(StripMTextFormatting(mtext.Value)),
+ Height = mtext.Height,
+ Rotation = mtext.Rotation,
+ LayerName = mtext.Layer?.Name,
+ HAlign = mh,
+ VAlign = mv,
+ });
+ break;
+
+ case ACadSharp.Entities.TextEntity text:
+ var useAlignment = text.HorizontalAlignment != 0
+ || text.VerticalAlignment != 0;
+ var pt = useAlignment ? text.AlignmentPoint : text.InsertPoint;
+ var ha = text.HorizontalAlignment switch
+ {
+ ACadSharp.Entities.TextHorizontalAlignment.Center => System.Drawing.StringAlignment.Center,
+ ACadSharp.Entities.TextHorizontalAlignment.Right => System.Drawing.StringAlignment.Far,
+ _ => System.Drawing.StringAlignment.Near,
+ };
+ texts.Add(new CadText
+ {
+ Position = new Vector(pt.X, pt.Y),
+ Value = ReplaceControlCodes(text.Value),
+ Height = text.Height,
+ Rotation = text.Rotation,
+ LayerName = text.Layer?.Name,
+ HAlign = ha,
+ });
+ break;
+ }
+ }
+
+ return texts;
+ }
+
+ private static (System.Drawing.StringAlignment h, System.Drawing.StringAlignment v) MapAttachmentPoint(
+ ACadSharp.Entities.AttachmentPointType apt)
+ {
+ var h = apt switch
+ {
+ ACadSharp.Entities.AttachmentPointType.TopCenter
+ or ACadSharp.Entities.AttachmentPointType.MiddleCenter
+ or ACadSharp.Entities.AttachmentPointType.BottomCenter => System.Drawing.StringAlignment.Center,
+ ACadSharp.Entities.AttachmentPointType.TopRight
+ or ACadSharp.Entities.AttachmentPointType.MiddleRight
+ or ACadSharp.Entities.AttachmentPointType.BottomRight => System.Drawing.StringAlignment.Far,
+ _ => System.Drawing.StringAlignment.Near,
+ };
+ var v = apt switch
+ {
+ ACadSharp.Entities.AttachmentPointType.MiddleLeft
+ or ACadSharp.Entities.AttachmentPointType.MiddleCenter
+ or ACadSharp.Entities.AttachmentPointType.MiddleRight => System.Drawing.StringAlignment.Center,
+ ACadSharp.Entities.AttachmentPointType.BottomLeft
+ or ACadSharp.Entities.AttachmentPointType.BottomCenter
+ or ACadSharp.Entities.AttachmentPointType.BottomRight => System.Drawing.StringAlignment.Far,
+ _ => System.Drawing.StringAlignment.Near,
+ };
+ return (h, v);
+ }
+
+ private static string StripMTextFormatting(string text)
+ {
+ if (string.IsNullOrEmpty(text)) return text;
+ var result = System.Text.RegularExpressions.Regex.Replace(text, @"\\[A-Za-z][^;]*;", "");
+ result = result.Replace("{", "").Replace("}", "");
+ return result.Trim();
+ }
+
+ private static string ReplaceControlCodes(string text)
+ {
+ if (string.IsNullOrEmpty(text)) return text;
+ return text
+ .Replace("%%p", "±")
+ .Replace("%%P", "±")
+ .Replace("%%d", "°")
+ .Replace("%%D", "°")
+ .Replace("%%c", "⌀")
+ .Replace("%%C", "⌀")
+ .Replace("%%%", "%");
+ }
+
private void filterPanel_Paint(object sender, PaintEventArgs e)
{