DXF
Bidirectional DXF (Drawing Exchange Format) support: parse DXF drawings into strongly-typed
Geometry<Point> values and write geometries back out to CAD-compatible DXF files, with
color/fill styling and coordinate-reference-system awareness. Output is AutoCAD 2000 (AC1015)
format. Ships in the IRI.Maptor.Core.Spatial package; WPF integration lives in
IRI.Maptor.Presentation.Wpf.
Supported capabilities
| Capability | Supported | Implemented in |
|---|---|---|
| Read (DXF → geometries) | Yes | DxfReader.Read/ReadFromFile, preview via GetPreviewAsync |
| Read with CAD context (layer/entity/color/annotation) | Yes | DxfReader.ReadFeatures/ReadFeaturesFromFile → DxfFeature |
| Write (geometries → DXF) | Yes | DxfWriter.Write/WriteToFile/WriteToFileAsync, ToDxf/SaveAsDxfAsync extensions |
| Styling (true color, transparency, line width) | Yes (write) | DxfColorInfo, RgbColor |
| CRS embedding/detection (ESRI WKT) | Yes | DxfWriter (XRECORD), DxfReader (SRID auto-detection) |
| WPF import dialog / brush-based export | Yes | DxfOpenDialogView, GeometryExtensions (Jab.Wpf) |
All reading logic is centralized in DxfReader and all writing logic in DxfWriter
(IRI.Maptor.Core.Spatial.IO.Dxf); the WPF extension methods only convert visual parameters to
DxfColorInfo and delegate.
Reading
using IRI.Maptor.Core.Spatial.IO.Dxf;
// Read from file. Pass a defaultSrid, or null to let the reader decide.
List<Geometry<Point>> geometries = await DxfReader.ReadFromFile(@"C:\input\plan.dxf", defaultSrid: null);
// Read from an in-memory DXF string.
List<Geometry<Point>> fromString = DxfReader.Read(dxfContent, defaultSrid: 4326);
// Read with CAD context: source DXF layer, entity type, resolved color, annotation flag.
List<DxfFeature> features = await DxfReader.ReadFeaturesFromFile(@"C:\input\plan.dxf", defaultSrid: null);
CAD context and annotation separation (ReadFeatures)
Read returns bare geometries; ReadFeatures wraps each one in a DxfFeature carrying the CAD
context needed to treat a DXF as GIS data:
DxfLayerName— the DXF layer of the source entity (group 8).EntityType— the DXF entity name (LINE,LWPOLYLINE,INSERT,TEXT, …).Color— resolved to#RRGGBB: entity true color (420) wins, then an explicit ACI color (62, converted through the standard AutoCAD palette viaDxfAciColor), then the color of the entity's layer from the LAYER table (ByLayer/ByBlock/absent).Text— the content ofTEXT/MTEXT/ATTRIBentities.IsAnnotation—truefor drawing decoration as opposed to real-world features:TEXT/MTEXT/ATTRIB(as Points),LEADER(as LineString),DIMENSION(its anonymous block expanded),SOLID/TRACE(arrowheads),3DFACE(3D visualization facets),HATCH(fill boundaries),WIPEOUT(masking frames),INSERTof anonymous (*-prefixed) blocks — which includes dynamic-block instances (*U…) and Civil 3D label blocks — and anything on theDEFPOINTSlayer. Callers use this flag to route annotation into separate layers (e.g.-Polyline-other,-Polygon-other) so the main point/line/polygon layers hold only real features.
Polygon-with-hole reassembly pools real and annotation rings separately (an arrowhead inside a parcel never becomes a hole of the parcel), and each reassembled polygon keeps the CAD context of the entity that contributed its exterior ring.
SRID precedence
When resolving the coordinate system, DxfReader uses, in order:
- The caller-supplied
defaultSrid(when non-null and non-zero). - Otherwise, an SRID auto-detected from an embedded
GEOGCS/PROJCSWKT string in the file. - Otherwise,
SridHelper.GeodeticWGS84(EPSG:4326) as a fallback.
Preview
GetPreviewAsync extracts the detected SRID and a small sample of coordinates — handy for import
dialogs that let the user confirm the projection before loading the whole file.
DxfPreviewResult preview = await DxfReader.GetPreviewAsync(@"C:\input\plan.dxf", maxSamplePoints: 50);
int detectedSrid = preview.DetectedSrid; // 0 when none embedded
IReadOnlyList<Point> samples = preview.SamplePoints;
Supported entities (reading)
| DXF entity | Result (GeometryType) |
Notes |
|---|---|---|
POINT |
Point | |
LINE |
LineString | Two points (start/end) |
LWPOLYLINE |
LineString or Polygon | Polygon when the closed flag (70, bit 0) is set and ≥ 3 points |
POLYLINE / VERTEX |
LineString or Polygon | Polygon when the closed flag (bit 0) is set |
CIRCLE |
Polygon | Approximated with 32 segments |
ARC |
LineString | Approximated with 32 segments |
ELLIPSE |
Polygon or LineString | Polygon for a full ellipse, LineString for an elliptical arc; 32 segments |
SPLINE |
LineString or Polygon | Fit points used directly when present, otherwise de Boor sampling of the (rational) B-spline; Polygon when the closed flag is set |
SOLID / TRACE |
Polygon | Zigzag corner order (1,2,4,3) reassembled into a ring; triangles supported; always annotation |
3DFACE |
Polygon | XY projection of the face; always annotation (3D visualization facet, not a mapped feature) |
INSERT |
Point + expanded block geometry | See Block references below |
TEXT / MTEXT / ATTRIB |
Point | Insertion point; text content in DxfFeature.Text; always annotation |
LEADER |
LineString | Callout/arrow path from its WCS vertices; always annotation |
DIMENSION |
expanded block geometry | The anonymous *D block (group 2) is expanded in place; always annotation |
HATCH |
Polygon | Boundary paths (polyline paths with bulges flattened; edge paths with line edges and sampled arc edges); always annotation |
WIPEOUT |
Polygon | Masking frame from the clip boundary (image space → world via insertion + U/V vectors); always annotation |
| (other) | — | Unknown entities (MLEADER, RAY, XLINE, …) are skipped |
The closed flag is tested bitwise: AutoCAD commonly writes 70 = 129 (closed + plinegen), which is
just as closed as 70 = 1. If a closed polyline repeats its first vertex as its last, the duplicate
is dropped, because CreatePolygonOrMultiPolygon expects rings whose closing point is not repeated.
Closed rings are reassembled into polygons-with-holes / multipolygons via
Geometry<Point>.CreatePolygonOrMultiPolygon.
Block references (INSERT)
The BLOCKS section is parsed into block definitions, and every INSERT in the ENTITIES section is
expanded: the referenced block's geometry is translated by the block base point, scaled (41/42),
rotated (50), and placed at the insertion point — nested block references are expanded recursively
(depth cap 8, resolution memoized per block name), and MINSERT column/row arrays (70/71/44/45) are
repeated on their grid.
Each INSERT contributes:
- a Point feature at its insertion location — matching how ArcMap populates its CAD point feature class from block references (symbol positions: lamps, poles, …); and
- the expanded block geometry, merged into at most one multi-part feature per geometry class (MultiPoint / MultiLineString / MultiPolygon). A block reference is one symbol, so its internals stay one feature instead of flooding the layer — and a symbol's concentric rings become sibling polygon parts rather than being pulled into the file-wide polygon/hole reassembly.
An INSERT referencing an undefined block still emits its insertion Point.
Object Coordinate System (extrusion direction)
Planar DXF entities (LWPOLYLINE, 2D POLYLINE, CIRCLE, ARC, SOLID, INSERT) store their
coordinates in an Object Coordinate System defined by the extrusion direction (group codes
210/220/230, default (0,0,1)). The common non-default case is (0,0,-1) — a plane mirrored
about the Y axis, which AutoCAD emits for arcs and polylines drawn clockwise or through a mirror
operation. DxfReader negates the X of such entities to bring them back into world coordinates;
ignoring this puts them at -x instead of x, which for projected data (e.g. UTM eastings) throws
the layer extent hundreds of kilometres wide and makes the real features collapse to a sub-pixel
speck when the map zooms to the layer.
True-3D entities (POINT, LINE, ELLIPSE, SPLINE, 3DFACE) store WCS coordinates and are
never mirrored. Entities with an arbitrary (tilted) extrusion axis are read as-is; the full
Arbitrary Axis Algorithm is not implemented.
Writing
From IRI.Maptor.Core.Spatial (core)
using IRI.Maptor.Core.Spatial.IO.Dxf;
using IRI.Maptor.Extensions; // ToDxf / SaveAsDxfAsync
// Simplest: extension methods
string dxf = geometry.ToDxf(); // get DXF text
await geometry.SaveAsDxfAsync(@"C:\output\line.dxf"); // write to file
// Write a single geometry (optionally with color)
await DxfWriter.WriteToFileAsync(geometry, @"C:\output\geometry.dxf");
var colorInfo = new DxfColorInfo(
strokeColor: new RgbColor(255, 0, 0), // red stroke
fillColor: new RgbColor(255, 255, 0, 128), // yellow fill with alpha
strokeThickness: 2.0,
opacity: 0.8);
await DxfWriter.WriteToFileAsync(geometry, @"C:\output\colored.dxf", colorInfo);
Writing multiple geometries
A whole collection can be written into a single DXF file; all geometries share the same
ENTITIES section.
var geometries = new List<Geometry<Point>> { polygon1, line1, point1 };
// Uniform styling for all geometries
var colorInfo = new DxfColorInfo(
strokeColor: new RgbColor(0, 0, 0),
fillColor: new RgbColor(255, 0, 0));
await DxfWriter.WriteToFileAsync(geometries, @"C:\output\all.dxf", colorInfo);
// Per-geometry styling (returns the DXF text as well)
DxfWriter.WriteToFile(geometries, @"C:\output\styled.dxf", geom => GetColorForGeometry(geom));
// In-memory DXF strings (no file written)
string oneDxf = DxfWriter.Write(polygon1, colorInfo);
string manyDxf = DxfWriter.Write(geometries, colorInfo);
From IRI.Maptor.Presentation.Wpf (WPF)
using IRI.Maptor.Extensions;
using IRI.Maptor.Presentation.Wpf;
using System.Windows.Media;
// Using VisualParameters
var visualParams = new VisualParameters(
fill: new SolidColorBrush(Colors.LightBlue),
stroke: new SolidColorBrush(Colors.DarkBlue),
strokeThickness: 2.0,
opacity: 0.8);
await geometry.WriteToDxfFileAsync(@"C:\output\myGeometry.dxf", visualParams);
// Using individual brush parameters
await geometry.WriteToDxfFileAsync(
@"C:\output\geometry.dxf",
stroke: Brushes.Red,
fill: Brushes.Yellow,
strokeThickness: 1.5,
opacity: 1.0);
// Get DXF string without saving
string dxfContent = geometry.AsDxf(visualParams);
Supported geometry types (writing)
| Geometry type | DXF entity | Stroke | Fill | Notes |
|---|---|---|---|---|
| Point | POINT | Yes | – | Uses stroke color |
| LineString | LWPOLYLINE | Yes | – | Open polyline |
| Polygon | LWPOLYLINE + HATCH | Yes | Yes | Outline + solid fill |
| MultiPoint | Multiple POINTs | Yes | – | Each point separately |
| MultiLineString | Multiple LWPOLYLINEs | Yes | – | Each line separately |
| MultiPolygon | Multiple entities | Yes | Yes | Each polygon with outline/fill |
| GeometryCollection | Mixed | Yes | Yes | Recursive processing |
Color support
public class DxfColorInfo
{
public RgbColor? StrokeColor { get; set; } // outline color
public RgbColor? FillColor { get; set; } // fill color (polygons)
public double StrokeThickness { get; set; } // line width (default 1.0)
public double Opacity { get; set; } // 0.0 to 1.0 (default 1.0)
}
public struct RgbColor
{
public byte R, G, B, A; // A = alpha channel
public RgbColor(byte r, byte g, byte b, byte a = 255)
}
Colors are emitted using DXF group codes:
- Group code 420 — true color (24-bit RGB):
(R << 16) | (G << 8) | B - Group code 440 — transparency (0–255): alpha channel adjusted by opacity
- Group code 43 — line width: stroke thickness for polylines
Polygons are exported with both an LWPOLYLINE (closed outline, stroke color) and a HATCH (solid
fill, fill color); interior rings (holes) are handled in both. A stroke thickness of 0 means
"no width specification" (CAD default). Alpha values are clamped to 0–255 and opacity to 0.0–1.0.
Coordinate reference system
- Export: the geometry's SRID is embedded as an ESRI WKT string inside an
XRECORD(under anESRI_PRJentry), so files open with the correct projection in ArcMap and other GIS tools. - Import:
DxfReaderscans for embeddedGEOGCS/PROJCSWKT and resolves it back to an SRID (see SRID precedence).
WPF import dialog
IRI.Maptor.Presentation.Wpf ships a ready-made import dialog, DxfOpenDialogView, exposed through the
dialog service:
DxfOpenDialogResult? result = await DialogService.ShowDxfOpenDialogAsync();
if (result is null)
return; // user cancelled
List<Geometry<Point>> geometries =
await DxfReader.ReadFromFile(result.FilePath, result.SelectedSrid);
DxfOpenDialogResult is a simple record: record DxfOpenDialogResult(string FilePath, int SelectedSrid).
The dialog provides a DXF file picker, a live coordinate preview, SRID auto-detection (with
controls locked when the file carries a coordinate system), and a coordinate-system chooser
(WGS84, Web Mercator, or UTM with zone and hemisphere selection).
Format details
- Version:
AC1015(AutoCAD 2000), readable by modern CAD applications. - Coordinate precision: 6 decimal places.
- Handle generation: hexadecimal sequential IDs.
Limitations
- Single layer. The writer places all entities on the default layer
"0"; there is no multi-layer authoring, and layer names are not round-tripped on read. - Geometry-only.
TEXT/MTEXT,SPLINE,ELLIPSE, andINSERT/blocks are not parsed or written — supported content is point/line/polygon geometry (plusARC/CIRCLEon read). - Transparency support varies across DXF viewers (group code 440).
ReadFromFile/GetPreviewAsyncthrowFileNotFoundExceptionwhen the path does not exist.