diff --git a/extlib/libdxfrw b/extlib/libdxfrw
index a6f7c0a..a05d16e 160000
--- a/extlib/libdxfrw
+++ b/extlib/libdxfrw
@@ -1 +1 @@
-Subproject commit a6f7c0a9ff7b9dd847a1c673f7c414c3e10c4022
+Subproject commit a05d16eaa85c69d662d3b596ca82cbae56443295
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index a112f02..9e15e96 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -284,6 +284,7 @@ set(solvespace_SOURCES
     graphicswin.cpp
     group.cpp
     groupmesh.cpp
+    importdxf.cpp
     mesh.cpp
     modify.cpp
     mouse.cpp
diff --git a/src/dsc.h b/src/dsc.h
index ade8ce7..c61bbf5 100644
--- a/src/dsc.h
+++ b/src/dsc.h
@@ -492,13 +492,22 @@ public:
             (int)(255.1f * a));
     }
 
-    static RgbaColor FromPackedInt(uint32_t bgra) {
+    static RgbaColor FromPackedInt(uint32_t rgba) {
+        return From(
+            (int)((rgba)       & 0xff),
+            (int)((rgba >> 8)  & 0xff),
+            (int)((rgba >> 16) & 0xff),
+            (int)(255 - ((rgba >> 24) & 0xff)));
+    }
+
+    static RgbaColor FromPackedIntBGRA(uint32_t bgra) {
         return From(
-            (int)((bgra)       & 0xff),
-            (int)((bgra >> 8)  & 0xff),
             (int)((bgra >> 16) & 0xff),
+            (int)((bgra >> 8)  & 0xff),
+            (int)((bgra)       & 0xff),
             (int)(255 - ((bgra >> 24) & 0xff)));
     }
+
 };
 
 class BBox {
diff --git a/src/exportvector.cpp b/src/exportvector.cpp
index 7b318f3..0f301ad 100644
--- a/src/exportvector.cpp
+++ b/src/exportvector.cpp
@@ -195,27 +195,12 @@ public:
         }
     }
 
-    const char *lineTypeName(int stippleType) {
-        switch(stippleType) {
-            case Style::STIPPLE_CONTINUOUS:   return "CONTINUOUS";
-            case Style::STIPPLE_DASH:         return "DASHED";
-            case Style::STIPPLE_LONG_DASH:    return "DASHEDX2";
-            case Style::STIPPLE_DASH_DOT:     return "DASHDOT";
-            case Style::STIPPLE_DASH_DOT_DOT: return "DIVIDE";
-            case Style::STIPPLE_DOT:          return "DOT";
-            case Style::STIPPLE_FREEHAND:     return "CONTINUOUS";
-            case Style::STIPPLE_ZIGZAG:       return "CONTINUOUS";
-        }
-
-        return "CONTINUOUS";
-    }
-
     virtual void writeLTypes() {
         for(int i = 0; i <= Style::LAST_STIPPLE; i++) {
             DRW_LType type;
             // LibreCAD requires the line type to have one of these exact names,
             // or otherwise it overwrites it with its own (continuous) style.
-            type.name = lineTypeName(i);
+            type.name = DxfFileWriter::lineTypeName(i);
             double sw = 1.0;
             switch(i) {
                 case Style::STIPPLE_CONTINUOUS:
@@ -421,7 +406,7 @@ public:
         Style *s = Style::Get(hs);
         entity->color24 = s->Color(hs, true).ToPackedIntBGRA();
         entity->layer = s->DescriptionString();
-        entity->lineType = lineTypeName(s->stippleType);
+        entity->lineType = DxfFileWriter::lineTypeName(s->stippleType);
         entity->ltypeScale = Style::StippleScaleMm(s->h);
         entity->setWidthMm(Style::WidthMm(hs.v));
     }
@@ -671,6 +656,21 @@ bool DxfFileWriter::NeedToOutput(Constraint *c) {
     return false;
 }
 
+const char *DxfFileWriter::lineTypeName(int stippleType) {
+    switch(stippleType) {
+        case Style::STIPPLE_CONTINUOUS:   return "CONTINUOUS";
+        case Style::STIPPLE_DASH:         return "DASHED";
+        case Style::STIPPLE_LONG_DASH:    return "DASHEDX2";
+        case Style::STIPPLE_DASH_DOT:     return "DASHDOT";
+        case Style::STIPPLE_DASH_DOT_DOT: return "DIVIDE";
+        case Style::STIPPLE_DOT:          return "DOT";
+        case Style::STIPPLE_FREEHAND:     return "CONTINUOUS";
+        case Style::STIPPLE_ZIGZAG:       return "CONTINUOUS";
+    }
+
+    return "CONTINUOUS";
+}
+
 //-----------------------------------------------------------------------------
 // Routines for EPS output
 //-----------------------------------------------------------------------------
diff --git a/src/graphicswin.cpp b/src/graphicswin.cpp
index 83eeb3b..fa655ab 100644
--- a/src/graphicswin.cpp
+++ b/src/graphicswin.cpp
@@ -40,6 +40,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
 { 1, "Export 3d &Wireframe...",     MNU_EXPORT_WIREFRAME, 0,     TN, mFile },
 { 1, "Export Triangle &Mesh...",    MNU_EXPORT_MESH,    0,       TN, mFile },
 { 1, "Export &Surfaces...",         MNU_EXPORT_SURFACES,0,       TN, mFile },
+{ 1, "Im&port...",                  MNU_IMPORT         ,0,       TN, mFile },
 #ifndef __APPLE__
 { 1,  NULL,                         0,                  0,       TN, NULL  },
 { 1, "E&xit",                       MNU_EXIT,           C|'Q',   TN, mFile },
diff --git a/src/importdxf.cpp b/src/importdxf.cpp
new file mode 100644
index 0000000..c9ec7cc
--- /dev/null
+++ b/src/importdxf.cpp
@@ -0,0 +1,626 @@
+#include "solvespace.h"
+#include "libdxfrw.h"
+
+#ifdef WIN32
+// Conflicts with DRW::TEXT.
+#   undef TEXT
+#endif
+
+namespace SolveSpace {
+
+static std::string ToUpper(std::string str) {
+    std::transform(str.begin(), str.end(), str.begin(), ::toupper);
+    return str;
+}
+
+class DxfReadInterface : public DRW_Interface {
+public:
+    Vector blockX;
+    Vector blockY;
+    Vector blockT;
+
+    void invertXTransform() {
+        blockX.x = -blockX.x;
+        blockY.x = -blockY.x;
+        blockT.x = -blockT.x;
+    }
+
+    void multBlockTransform(double x, double y, double sx, double sy, double angle) {
+        Vector oldX = blockX;
+        Vector oldY = blockY;
+        Vector oldT = blockT;
+
+        Vector newX = Vector::From(sx, 0.0, 0.0).RotatedAbout(Vector::From(0.0, 0.0, 1.0), angle);
+        Vector newY = Vector::From(0.0, sy, 0.0).RotatedAbout(Vector::From(0.0, 0.0, 1.0), angle);
+        Vector newT = Vector::From(x, y, 0.0);
+
+        blockX = oldX.ScaledBy(newX.x).Plus(
+                 oldY.ScaledBy(newX.y));
+
+        blockY = oldX.ScaledBy(newY.x).Plus(
+                 oldY.ScaledBy(newY.y));
+
+        blockT = oldX.ScaledBy(newT.x).Plus(
+                 oldY.ScaledBy(newT.y)).Plus(oldT);
+    }
+
+    void clearBlockTransform() {
+        blockX = Vector::From(1.0, 0.0, 0.0);
+        blockY = Vector::From(0.0, 1.0, 0.0);
+        blockT = Vector::From(0.0, 0.0, 0.0);
+    }
+
+    Vector blockTransform(Vector v) {
+        Vector r = blockT;
+        r = r.Plus(blockX.ScaledBy(v.x));
+        r = r.Plus(blockY.ScaledBy(v.y));
+        return r;
+    }
+
+    void blockTransformArc(Vector *c, Vector *p0, Vector *p1) {
+        bool oldSign = p0->Minus(*c).Cross(p1->Minus(*c)).z > 0.0;
+
+        *c = blockTransform(*c);
+        *p0 = blockTransform(*p0);
+        *p1 = blockTransform(*p1);
+
+        bool newSign = p0->Minus(*c).Cross(p1->Minus(*c)).z > 0.0;
+        if(oldSign != newSign) std::swap(*p0, *p1);
+    }
+
+    Vector toVector(const DRW_Coord &c) {
+        Vector result = Vector::From(c.x, c.y, 0.0);
+        return blockTransform(result);
+    }
+
+    Vector toVector(const DRW_Vertex2D &c) {
+        Vector result = Vector::From(c.x, c.y, 0.0);
+        return blockTransform(result);
+    }
+
+    Vector toVector(const DRW_Vertex &c) {
+        Vector result = Vector::From(c.basePoint.x, c.basePoint.y, 0.0);
+        return blockTransform(result);
+    }
+
+    double angleTo(Vector v0, Vector v1) {
+        Vector d = v1.Minus(v0);
+        double a = atan2(d.y, d.x);
+        return M_PI + remainder(a - M_PI, 2 * M_PI);
+    }
+
+    Vector polar(double radius, double angle) {
+        return Vector::From(radius * cos(angle), radius * sin(angle), 0.0);
+    }
+
+    hRequest createBulge(Vector p0, Vector p1, double bulge) {
+        bool reversed = bulge < 0.0;
+        double alpha = atan(bulge) * 4.0;
+
+        Vector middle = p1.Plus(p0).ScaledBy(0.5);
+        double dist = p1.Minus(p0).Magnitude() / 2.0;
+        double angle = angleTo(p0, p1);
+
+        // alpha can't be 0.0 at this point
+        double radius = fabs(dist / sin(alpha / 2.0));
+        double wu = fabs(radius * radius - dist * dist);
+        double h = sqrt(wu);
+
+        if(bulge > 0.0) {
+            angle += M_PI_2;
+        } else {
+            angle -= M_PI_2;
+        }
+
+        if (fabs(alpha) > M_PI) {
+            h *= -1.0;
+        }
+
+        Vector center = polar(h, angle);
+        center = center.Plus(middle);
+
+        if(reversed) std::swap(p0, p1);
+        blockTransformArc(&center, &p0, &p1);
+
+        hRequest hr = SS.GW.AddRequest(Request::ARC_OF_CIRCLE, false);
+        SK.GetEntity(hr.entity(1))->PointForceTo(center);
+        SK.GetEntity(hr.entity(2))->PointForceTo(p0);
+        SK.GetEntity(hr.entity(3))->PointForceTo(p1);
+        return hr;
+    }
+
+    struct Block {
+        std::vector<std::unique_ptr<DRW_Entity>> entities;
+        DRW_Block data;
+    };
+
+    unsigned unknownEntities = 0;
+    std::map<std::string, hStyle> styles;
+    std::map<std::string, Block> blocks;
+    std::map<std::string, DRW_Layer> layers;
+    Block *readBlock = NULL;
+    const DRW_Insert *insertInsert = NULL;
+
+    template<class T>
+    bool addPendingBlockEntity(const T &e) {
+        if(readBlock == NULL) return false;
+        readBlock->entities.emplace_back(new T(e));
+        return true;
+    }
+
+    void addEntity(DRW_Entity *e) {
+        switch(e->eType) {
+            case DRW::POINT:
+                addPoint(*static_cast<DRW_Point *>(e));
+                break;
+            case DRW::LINE:
+                addLine(*static_cast<DRW_Line *>(e));
+                break;
+            case DRW::ARC:
+                addArc(*static_cast<DRW_Arc *>(e));
+                break;
+            case DRW::CIRCLE:
+                addCircle(*static_cast<DRW_Circle *>(e));
+                break;
+            case DRW::POLYLINE:
+                addPolyline(*static_cast<DRW_Polyline *>(e));
+                break;
+            case DRW::LWPOLYLINE:
+                addLWPolyline(*static_cast<DRW_LWPolyline *>(e));
+                break;
+            case DRW::SPLINE:
+                addSpline(static_cast<DRW_Spline *>(e));
+                break;
+            case DRW::INSERT:
+                addInsert(*static_cast<DRW_Insert *>(e));
+                break;
+            case DRW::TEXT:
+                addText(*static_cast<DRW_Text *>(e));
+                break;
+            case DRW::MTEXT:
+                addMText(*static_cast<DRW_MText *>(e));
+                break;
+            default:
+                unknownEntities++;
+        }
+    }
+
+    int dxfAlignToOrigin(DRW_Text::HAlign alignH, DRW_Text::VAlign alignV) {
+        int origin = 0;
+        switch(alignH) {
+            case DRW_Text::HLeft:
+                origin |= Style::ORIGIN_LEFT;
+                break;
+
+            case DRW_Text::HMiddle:
+            case DRW_Text::HCenter:
+                break;
+
+            case DRW_Text::HRight:
+                origin |= Style::ORIGIN_RIGHT;
+                break;
+
+            case DRW_Text::HAligned:
+            case DRW_Text::HFit:
+            default:
+                origin |= Style::ORIGIN_LEFT;
+                break;
+        }
+
+        switch(alignV) {
+            case DRW_Text::VBaseLine:
+            case DRW_Text::VBottom:
+                origin |= Style::ORIGIN_BOT;
+                break;
+
+            case DRW_Text::VMiddle:
+                break;
+
+            case DRW_Text::VTop:
+                origin |= Style::ORIGIN_TOP;
+                break;
+
+            default:
+                origin |= Style::ORIGIN_BOT;
+                break;
+        }
+
+        return origin;
+    }
+
+    DRW_Layer *getSourceLayer(const DRW_Entity *e) {
+        DRW_Layer *layer = NULL;
+        if(insertInsert != NULL) {
+            std::string l = insertInsert->layer;
+            auto bi = layers.find(l);
+            if(bi != layers.end()) layer = &bi->second;
+        } else {
+            std::string l = e->layer;
+            auto bi = layers.find(l);
+            if(bi != layers.end()) layer = &bi->second;
+        }
+        return layer;
+    }
+
+    int getColor(const DRW_Entity *e) {
+        int col = e->color;
+        if(col == DRW::ColorByBlock) {
+            if(insertInsert != NULL) {
+                col = insertInsert->color;
+            } else {
+                col = 7;
+            }
+        }
+        if(col == DRW::ColorByLayer) {
+            DRW_Layer *layer = getSourceLayer(e);
+            if(layer != NULL) {
+                col = layer->color;
+            } else {
+                col = 7;
+            }
+        }
+        return col;
+    }
+
+    DRW_LW_Conv::lineWidth getLineWidth(const DRW_Entity *e) {
+        DRW_LW_Conv::lineWidth result = e->lWeight;
+        if(result == DRW_LW_Conv::widthByBlock) {
+            if(insertInsert != NULL) {
+                result = insertInsert->lWeight;
+            } else {
+                result = DRW_LW_Conv::widthDefault;
+            }
+        }
+        if(result == DRW_LW_Conv::widthByLayer) {
+            DRW_Layer *layer = getSourceLayer(e);
+            if(layer != NULL) {
+                result = layer->lWeight;
+            } else {
+                result = DRW_LW_Conv::widthDefault;
+            }
+        }
+        return result;
+    }
+
+    std::string getLineType(const DRW_Entity *e) {
+        std::string  result = e->lineType;
+        if(result == "BYBLOCK") {
+            if(insertInsert != NULL) {
+                result = ToUpper(insertInsert->lineType);
+            } else {
+                result = "CONTINUOUS";
+            }
+        }
+        if(result == "BYLAYER") {
+            DRW_Layer *layer = getSourceLayer(e);
+            if(layer != NULL) {
+                result = ToUpper(layer->lineType);
+            } else {
+                result = "CONTINUOUS";
+            }
+        }
+        return result;
+    }
+
+    hStyle styleFor(const DRW_Entity *e) {
+        DRW_Layer *layer = NULL;
+        auto bi = layers.find(e->layer);
+        if(bi != layers.end()) layer = &bi->second;
+
+        // Color.
+        // TODO: which color to choose: index or RGB one?
+        int col = getColor(e);
+        RgbaColor c = RgbaColor::From(DRW::dxfColors[col][0],
+                                      DRW::dxfColors[col][1],
+                                      DRW::dxfColors[col][2]);
+
+        // Line width.
+        DRW_LW_Conv::lineWidth lw = getLineWidth(e);
+        double width = DRW_LW_Conv::lineWidth2dxfInt(e->lWeight) / 100.0;
+        if(width < 0.0) width = 1.0;
+
+        // Line stipple.
+        // TODO: Probably, we can load default autocad patterns and match it with ours.
+        std::string lineType = getLineType(e);
+        int stipple = Style::STIPPLE_CONTINUOUS;
+        for(int i = 0; i <= Style::LAST_STIPPLE; i++) {
+            if(lineType == DxfFileWriter::lineTypeName(i)) {
+                stipple = i;
+                break;
+            }
+        }
+
+        // Text properties.
+        DRW_Text::HAlign alignH = DRW_Text::HLeft;
+        DRW_Text::VAlign alignV = DRW_Text::VBaseLine;
+        double textAngle = 0.0;
+        double textHeight = Style::DefaultTextHeight();
+
+        if(e->eType == DRW::TEXT || e->eType == DRW::MTEXT) {
+            const DRW_Text *text = static_cast<const DRW_Text *>(e);
+            alignH = text->alignH;
+            alignV = text->alignV;
+            textHeight = text->height;
+            textAngle = text->angle;
+            // I have no idea why, but works
+            if(alignH == DRW_Text::HMiddle) {
+                alignV = DRW_Text::VMiddle;
+            }
+        }
+
+        // Unique identifier based on style properties.
+        std::string id = "@dxf";
+        if(lw != DRW_LW_Conv::widthDefault)
+            id += ssprintf("-w%.4g", width);
+        if(lineType != "CONTINUOUS")
+            id += ssprintf("-%s", lineType.c_str());
+        if(c.red != 0 || c.green != 0 || c.blue != 0)
+            id += ssprintf("-#%02x%02x%02x", c.red, c.green, c.blue);
+        if(textHeight != Style::DefaultTextHeight())
+            id += ssprintf("-h%.4g", textHeight);
+        if(textAngle != 0.0)
+            id += ssprintf("-a%.5g", textAngle);
+        if(alignH != DRW_Text::HLeft)
+            id += ssprintf("-oh%d", alignH);
+        if(alignV != DRW_Text::VBaseLine)
+            id += ssprintf("-ov%d", alignV);
+
+        auto si = styles.find(id);
+        if(si != styles.end()) {
+            return si->second;
+        }
+
+        hStyle hs = { Style::CreateCustomStyle(/*rememberForUndo=*/false) };
+        Style *s = Style::Get(hs);
+        if(lw != DRW_LW_Conv::widthDefault) {
+            s->widthAs = Style::UNITS_AS_MM;
+            s->width = width;
+            s->stippleScale = 1.0 + width * 2.0;
+        }
+        s->name = id;
+        s->stippleType = stipple;
+        if(c.red != 0 || c.green != 0 || c.blue != 0) s->color = c;
+        s->textHeightAs = Style::UNITS_AS_MM;
+        s->textHeight = textHeight;
+        s->textAngle = textAngle;
+        s->textOrigin = dxfAlignToOrigin(alignH, alignV);
+
+        styles.emplace(id, hs);
+        return hs;
+    }
+
+    void setStyle(hRequest hr, hStyle hs) {
+        Request *r = SK.GetRequest(hr);
+        r->style = hs;
+    }
+
+    virtual void addLayer(const DRW_Layer &data) {
+        layers.emplace(data.name, data);
+    }
+
+    virtual void addBlock(const DRW_Block &data) {
+        blocks.emplace(data.name, Block());
+        readBlock = &blocks[data.name];
+        readBlock->data = data;
+    }
+
+    virtual void endBlock() {
+        readBlock = NULL;
+    }
+
+    virtual void addPoint(const DRW_Point &data) {
+        if(data.space != DRW::ModelSpace) return;
+        if(addPendingBlockEntity<DRW_Point>(data)) return;
+
+        hRequest hr = SS.GW.AddRequest(Request::DATUM_POINT, false);
+        SK.GetEntity(hr.entity(0))->PointForceTo(toVector(data.basePoint));
+    }
+
+    virtual void addLine(const DRW_Line &data) {
+        if(data.space != DRW::ModelSpace) return;
+        if(addPendingBlockEntity<DRW_Line>(data)) return;
+
+        hRequest hr = SS.GW.AddRequest(Request::LINE_SEGMENT, false);
+        SK.GetEntity(hr.entity(1))->PointForceTo(toVector(data.basePoint));
+        SK.GetEntity(hr.entity(2))->PointForceTo(toVector(data.secPoint));
+        setStyle(hr, styleFor(&data));
+    }
+
+    virtual void addArc(const DRW_Arc &data) {
+        if(data.space != DRW::ModelSpace) return;
+        if(addPendingBlockEntity<DRW_Arc>(data)) return;
+
+        hRequest hr = SS.GW.AddRequest(Request::ARC_OF_CIRCLE, false);
+        double r = data.radious;
+        double sa = data.staangle;
+        double ea = data.endangle;
+        Vector c = Vector::From(data.basePoint.x, data.basePoint.y, 0.0);
+        Vector rvs = Vector::From(r * cos(sa), r * sin(sa), data.basePoint.z).Plus(c);
+        Vector rve = Vector::From(r * cos(ea), r * sin(ea), data.basePoint.z).Plus(c);
+
+        if(data.extPoint.z == -1.0) {
+            c.x = -c.x;
+            rvs.x = - rvs.x;
+            rve.x = - rve.x;
+            std::swap(rvs, rve);
+        }
+
+        blockTransformArc(&c, &rvs, &rve);
+
+        SK.GetEntity(hr.entity(1))->PointForceTo(c);
+        SK.GetEntity(hr.entity(2))->PointForceTo(rvs);
+        SK.GetEntity(hr.entity(3))->PointForceTo(rve);
+        setStyle(hr, styleFor(&data));
+    }
+
+    virtual void addCircle(const DRW_Circle &data) {
+        if(data.space != DRW::ModelSpace) return;
+        if(addPendingBlockEntity<DRW_Circle>(data)) return;
+
+        hRequest hr = SS.GW.AddRequest(Request::CIRCLE, false);
+        SK.GetEntity(hr.entity(1))->PointForceTo(toVector(data.basePoint));
+        SK.GetEntity(hr.entity(64))->DistanceForceTo(data.radious);
+        setStyle(hr, styleFor(&data));
+    }
+
+    virtual void addLWPolyline(const DRW_LWPolyline &data)  {
+        if(data.space != DRW::ModelSpace) return;
+        if(addPendingBlockEntity<DRW_LWPolyline>(data)) return;
+
+        size_t vNum = data.vertlist.size();
+
+        // Check for closed polyline.
+        if((data.flags & 1) != 1) vNum--;
+
+        // Correct coordinate system for the case where z=-1, as described in
+        // http://paulbourke.net/dataformats/dxf/dxf10.html.
+        bool needSwapX = data.extPoint.z == -1.0;
+
+        for(size_t i = 0; i < vNum; i++) {
+            DRW_Vertex2D c0 = *data.vertlist[i];
+            DRW_Vertex2D c1 = *data.vertlist[(i + 1) % data.vertlist.size()];
+
+            if(needSwapX) {
+                c0.x = -c0.x;
+                c1.x = -c1.x;
+                c0.bulge = -c0.bulge;
+            }
+
+            Vector p0 = Vector::From(c0.x, c0.y, 0.0);
+            Vector p1 = Vector::From(c1.x, c1.y, 0.0);
+            hStyle hs = styleFor(&data);
+
+            if(EXACT(data.vertlist[i]->bulge == 0.0)) {
+                hRequest hr = SS.GW.AddRequest(Request::LINE_SEGMENT, false);
+                SK.GetEntity(hr.entity(1))->PointForceTo(blockTransform(p0));
+                SK.GetEntity(hr.entity(2))->PointForceTo(blockTransform(p1));
+                setStyle(hr, hs);
+            } else {
+                hRequest hr = createBulge(p0, p1, c0.bulge);
+                setStyle(hr, hs);
+            }
+        }
+    }
+
+    virtual void addPolyline(const DRW_Polyline &data) {
+        if(data.space != DRW::ModelSpace) return;
+        if(addPendingBlockEntity<DRW_Polyline>(data)) return;
+
+        int vNum = data.vertlist.size();
+
+        // Check for closed polyline.
+        if((data.flags & 1) != 1) vNum--;
+
+        // Correct coordinate system for the case where z=-1, as described in
+        // http://paulbourke.net/dataformats/dxf/dxf10.html.
+        bool needSwapX = data.extPoint.z == -1.0;
+
+        for(int i = 0; i < vNum; i++) {
+            DRW_Coord c0 = data.vertlist[i]->basePoint;
+            DRW_Coord c1 = data.vertlist[(i + 1) % data.vertlist.size()]->basePoint;
+
+            double bulge = data.vertlist[i]->bulge;
+            if(needSwapX) {
+                c0.x = -c0.x;
+                c1.x = -c1.x;
+                bulge = -bulge;
+            }
+
+            Vector p0 = Vector::From(c0.x, c0.y, 0.0);
+            Vector p1 = Vector::From(c1.x, c1.y, 0.0);
+            hStyle hs = styleFor(&data);
+
+            if(EXACT(bulge == 0.0)) {
+                hRequest hr = SS.GW.AddRequest(Request::LINE_SEGMENT, false);
+                SK.GetEntity(hr.entity(1))->PointForceTo(blockTransform(p0));
+                SK.GetEntity(hr.entity(2))->PointForceTo(blockTransform(p1));
+                setStyle(hr, hs);
+            } else {
+                hRequest hr = createBulge(p0, p1, bulge);
+                setStyle(hr, hs);
+            }
+        }
+    }
+
+    virtual void addSpline(const DRW_Spline *data) {
+        if(data->space != DRW::ModelSpace) return;
+        if(data->degree != 3) return;
+        if(addPendingBlockEntity<DRW_Spline>(*data)) return;
+
+        hRequest hr = SS.GW.AddRequest(Request::CUBIC, false);
+        for(int i = 0; i < 4; i++) {
+            SK.GetEntity(hr.entity(i + 1))->PointForceTo(toVector(*data->controllist[i]));
+        }
+        setStyle(hr, styleFor(data));
+    }
+
+    virtual void addInsert(const DRW_Insert &data) {
+        if(data.space != DRW::ModelSpace) return;
+        if(addPendingBlockEntity<DRW_Insert>(data)) return;
+
+        auto bi = blocks.find(data.name);
+        if(bi == blocks.end()) oops();
+        Block *block = &bi->second;
+
+        // Push transform.
+        Vector x = blockX;
+        Vector y = blockY;
+        Vector t = blockT;
+
+        const DRW_Insert *oldInsert = insertInsert;
+        insertInsert = &data;
+
+        if(data.extPoint.z == -1.0) invertXTransform();
+        multBlockTransform(data.basePoint.x, data.basePoint.y, data.xscale, data.yscale, data.angle);
+        for(auto &e : block->entities) {
+            addEntity(&*e);
+        }
+
+        insertInsert = oldInsert;
+
+        // Pop transform.
+        blockX = x;
+        blockY = y;
+        blockT = t;
+    }
+
+    virtual void addMText(const DRW_MText &data) {
+        if(data.space != DRW::ModelSpace) return;
+        if(addPendingBlockEntity<DRW_MText>(data)) return;
+
+        DRW_MText text = data;
+        text.secPoint = text.basePoint;
+        addText(text);
+    }
+
+    virtual void addText(const DRW_Text &data) {
+        if(data.space != DRW::ModelSpace) return;
+        if(addPendingBlockEntity<DRW_Text>(data)) return;
+
+        Constraint c = {};
+        c.group         = SS.GW.activeGroup;
+        c.workplane     = SS.GW.ActiveWorkplane();
+        c.type          = Constraint::COMMENT;
+        if(data.alignH == DRW_Text::HLeft && data.alignV == DRW_Text::VBaseLine) {
+            c.disp.offset   = toVector(data.basePoint);
+        } else {
+            c.disp.offset   = toVector(data.secPoint);
+        }
+        c.comment       = data.text;
+        c.disp.style    = styleFor(&data);
+        Constraint::AddConstraint(&c, false);
+    }
+};
+
+void ImportDxf(const std::string &filename) {
+    dxfRW dxf(filename.c_str());
+    DxfReadInterface interface;
+    interface.clearBlockTransform();
+    dxf.read(&interface, false);
+    if(interface.unknownEntities > 0) {
+        Message(ssprintf("%u DXF entities of unknown type were ignored.",
+                         interface.unknownEntities).c_str());
+    }
+}
+
+}
diff --git a/src/sketch.h b/src/sketch.h
index d39e476..38c16d5 100644
--- a/src/sketch.h
+++ b/src/sketch.h
@@ -830,7 +830,7 @@ public:
     static void LoadFactoryDefaults(void);
 
     static void AssignSelectionToStyle(uint32_t v);
-    static uint32_t CreateCustomStyle(void);
+    static uint32_t CreateCustomStyle(bool rememberForUndo = true);
 
     static RgbaColor RewriteColor(RgbaColor rgb);
 
diff --git a/src/solvespace.cpp b/src/solvespace.cpp
index 139970c..12acc28 100644
--- a/src/solvespace.cpp
+++ b/src/solvespace.cpp
@@ -541,6 +541,16 @@ void SolveSpaceUI::MenuFile(int id) {
             break;
         }
 
+        case GraphicsWindow::MNU_IMPORT: {
+            std::string importFile;
+            if(!GetOpenFile(&importFile, CnfThawString("", "ImportFormat"),
+                            ImportableFileFilter)) break;
+            CnfFreezeString(Extension(importFile), "ImportFormat");
+
+            ImportDxf(importFile);
+            break;
+        }
+
         case GraphicsWindow::MNU_EXIT:
             if(!SS.OkayToStartNewFile()) break;
             SS.Exit();
diff --git a/src/solvespace.h b/src/solvespace.h
index 757c3ff..46c597f 100644
--- a/src/solvespace.h
+++ b/src/solvespace.h
@@ -19,6 +19,7 @@
 #include <algorithm>
 #include <memory>
 #include <string>
+#include <locale>
 #include <vector>
 #include <unordered_map>
 #include <map>
@@ -195,6 +196,11 @@ const FileFilter Vector3dFileFilter[] = {
     { "DXF file (AutoCAD 2007)",    { "dxf" } },
     { NULL }
 };
+// All Importable formats
+const FileFilter ImportableFileFilter[] = {
+    { "AutoCAD DXF and DWG files",  { "dxf", "dwg" } },
+    { NULL }
+};
 // Comma-separated value, like a spreadsheet would use
 const FileFilter CsvFileFilter[] = {
     { "CSV",                        { "csv" } },
@@ -534,6 +540,9 @@ public:
     void FinishAndCloseFile(void);
     bool HasCanvasSize(void) { return false; }
     bool NeedToOutput(Constraint *c);
+
+    static const char *lineTypeName(int stippleType);
+
 };
 class EpsFileWriter : public VectorFileWriter {
 public:
@@ -942,6 +951,8 @@ public:
     }
 };
 
+void ImportDxf(const std::string &file);
+
 extern SolveSpaceUI SS;
 extern Sketch SK;
 
diff --git a/src/style.cpp b/src/style.cpp
index 75abffb..91caaea 100644
--- a/src/style.cpp
+++ b/src/style.cpp
@@ -122,8 +122,8 @@ void Style::FreezeDefaultStyles(void) {
     }
 }
 
-uint32_t Style::CreateCustomStyle(void) {
-    SS.UndoRemember();
+uint32_t Style::CreateCustomStyle(bool rememberForUndo) {
+    if(rememberForUndo) SS.UndoRemember();
     uint32_t vs = max((uint32_t)Style::FIRST_CUSTOM, SK.style.MaximumId() + 1);
     hStyle hs = { vs };
     (void)Style::Get(hs);
diff --git a/src/ui.h b/src/ui.h
index 15f64dd..08c32e7 100644
--- a/src/ui.h
+++ b/src/ui.h
@@ -348,6 +348,7 @@ public:
         MNU_EXPORT_VIEW,
         MNU_EXPORT_SECTION,
         MNU_EXPORT_WIREFRAME,
+        MNU_IMPORT,
         MNU_EXIT,
         // View
         MNU_ZOOM_IN,